added file name encryption

Thu, 05 Jun 2014 15:11:29 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 05 Jun 2014 15:11:29 +0200
changeset 43
03076907b58a
parent 42
6518b035a9df
child 44
e4e23a88d4de

added file name encryption

dav/config.c file | annotate | diff | comparison | revisions
dav/config.h file | annotate | diff | comparison | revisions
dav/main.c file | annotate | diff | comparison | revisions
libidav/Makefile file | annotate | diff | comparison | revisions
libidav/crypto.c file | annotate | diff | comparison | revisions
libidav/davql.c file | annotate | diff | comparison | revisions
libidav/davql.h file | annotate | diff | comparison | revisions
libidav/methods.c file | annotate | diff | comparison | revisions
libidav/methods.h file | annotate | diff | comparison | revisions
libidav/resource.c file | annotate | diff | comparison | revisions
libidav/resource.h file | annotate | diff | comparison | revisions
libidav/session.c file | annotate | diff | comparison | revisions
libidav/session.h file | annotate | diff | comparison | revisions
libidav/utils.c file | annotate | diff | comparison | revisions
libidav/utils.h file | annotate | diff | comparison | revisions
libidav/webdav.c file | annotate | diff | comparison | revisions
libidav/webdav.h file | annotate | diff | comparison | revisions
osx.mk file | annotate | diff | comparison | revisions
ucx/string.c file | annotate | diff | comparison | revisions
--- a/dav/config.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/dav/config.c	Thu Jun 05 15:11:29 2014 +0200
@@ -111,8 +111,10 @@
 void load_repository(xmlNode *reponode) {
     xmlNode *node = reponode->children;
     Repository *repo = calloc(1, sizeof(Repository));
-    repo->store_key_property = true;
-    repo->decrypt = false;
+    repo->encrypt_name = false;
+    repo->encrypt_content = false;
+    repo->decrypt_name = false;
+    repo->decrypt_content = true;
     while(node) {
         if(node->type == XML_ELEMENT_NODE) {
             char *value = util_xml_get_text(node);
@@ -128,12 +130,37 @@
                 repo->password = util_base64decode(value);
             } else if(xstreq(node->name, "default-key")) {
                 repo->default_key = strdup(value);
-            } else if(xstreq(node->name, "encrypt")) {
-                repo->encrypt = util_getboolean(value);
-            } else if(xstreq(node->name, "decrypt")) {
-                repo->decrypt = util_getboolean(value);
-            } else if(xstreq(node->name, "store-key-property")) {
-                repo->store_key_property = util_getboolean(value);
+            } else if(xstreq(node->name, "full-encryption")) {
+                if(util_getboolean(value)) {
+                    repo->encrypt_name = true;
+                    repo->encrypt_content = true;
+                    repo->decrypt_name = true;
+                    repo->decrypt_content = true;
+                }
+            } else if(xstreq(node->name, "content-encryption")) {
+                if(util_getboolean(value)) {
+                    repo->encrypt_content = true;
+                    repo->decrypt_content = true;
+                } else {
+                    repo->encrypt_content = false;
+                }
+            } else if(xstreq(node->name, "decrypt-content")) {
+                repo->decrypt_content = util_getboolean(value);
+            } else if(xstreq(node->name, "decrypt-name")) {
+                repo->decrypt_name = util_getboolean(value);
+            } else if(xstreq(node->name, "encrypt") || xstreq(node->name, "store-key-property") || xstreq(node->name, "decrypt")) {
+                fprintf(stderr, "Error: config.xml contains deprecated elements\n");
+                fprintf(stderr, "The elements <encrypt>, <decrypt> and <store-key-property> are removed\n");
+                fprintf(stderr, "Use the following: \n\n");
+                fprintf(stderr, "<content-encryption>true</content-encryption>\n");
+                fprintf(stderr, "enables file content encryption and decryption\n\n");
+                fprintf(stderr, "<full-encryption>true</full-encryption>\n");
+                fprintf(stderr, "enables content and file name encryption/decryption\n\n");
+                fprintf(stderr, "<decrypt-content>$BOOL</decrypt-content>\n");
+                fprintf(stderr, "only enables/disables content decryption\n\n");
+                fprintf(stderr, "<decrypt-name>$BOOL</decrypt-name>\n");
+                fprintf(stderr, "only enables/disables name decryption\n\n");
+                exit(-1);
             }
         }
         node = node->next;
@@ -283,6 +310,24 @@
     return ucx_map_sstr_get(repos, name);
 }
 
+int get_repository_flags(Repository *repo) {
+    int flags = 0;
+    if(repo->decrypt_content) {
+        flags |= DAV_SESSION_DECRYPT_CONTENT;
+    }
+    if(repo->decrypt_name) {
+        flags |= DAV_SESSION_DECRYPT_NAME;
+    }
+    if(repo->encrypt_content) {
+        flags |= DAV_SESSION_ENCRYPT_CONTENT;
+    }
+    if(repo->encrypt_name) {
+        flags |= DAV_SESSION_ENCRYPT_NAME;
+    }
+    return flags;
+}
+
+
 Key* get_key(char *name) {
     if(!name) {
         return NULL;
--- a/dav/config.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/dav/config.h	Thu Jun 05 15:11:29 2014 +0200
@@ -58,9 +58,10 @@
     char *user;
     char *password;
     char *default_key;
-    bool encrypt;
-    bool decrypt;
-    bool store_key_property;
+    bool encrypt_content;
+    bool encrypt_name;
+    bool decrypt_content;
+    bool decrypt_name;
 };
 
 struct Proxy {
@@ -77,6 +78,8 @@
 sstr_t load_key_file(char *filename);
 
 Repository* get_repository(sstr_t name);
+int get_repository_flags(Repository *repo);
+DavSession *repository_session(Repository *repo);
 Key* get_key(char *name);
 Proxy* get_http_proxy();
 Proxy* get_https_proxy();
--- a/dav/main.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/dav/main.c	Thu Jun 05 15:11:29 2014 +0200
@@ -49,6 +49,11 @@
     // nothing
 }
 
+#include <libidav/session.h>
+void test() {
+    
+}
+
 int main(int argc, char **argv) {
     xmlGenericErrorFunc fnc = xmlerrorfnc;
     initGenericErrorDefaultFunc(&fnc);
@@ -56,6 +61,8 @@
     load_config(ctx);
     dav_add_namespace(ctx, "U", "http://www.uap-core.de/");
     
+    //test();
+    
     memcpy(ctx->http_proxy, get_http_proxy(), sizeof(Proxy));
     memcpy(ctx->https_proxy, get_https_proxy(), sizeof(Proxy));
     
@@ -103,9 +110,9 @@
     fprintf(stderr, "        list [-altR] [-u <date>] <url>\n");
     fprintf(
             stderr,
-            "        get [-pR] [-k <key>] [-o <file>] [-u <date>] <url>\n");
+            "        get [-pR] [-o <file>] [-u <date>] <url>\n");
     fprintf(stderr, "        put [-pR] [-k <key>] <url> <file>\n");
-    fprintf(stderr, "        mkdir <url>\n");
+    fprintf(stderr, "        mkdir [-p] [-k <key>] <url>\n");
     fprintf(stderr, "        remove <url>\n");
     fprintf(stderr, "        date [url]\n");
     fprintf(stderr, "\n");
@@ -202,8 +209,7 @@
         repo = calloc(1, sizeof(Repository));
         repo->name = "";
         repo->url = strdup(url);
-        repo->store_key_property = true;
-        repo->decrypt = true;
+        repo->decrypt_content = true;
         *path = strdup("/");
     }
     
@@ -252,10 +258,11 @@
     char *url = a->argv[0];
     char *path = NULL;
     //char *base = NULL;
-    DavSession *sn = NULL;
     Repository *repo = url2repo(url, &path);
     //base = util_concat_path(repo->url, path);
-    sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    dav_session_set_flags(sn, get_repository_flags(repo));
+    sn->key = dav_context_get_key(ctx, repo->default_key);
     
     char *update = cmd_getoption(a, "update");
     time_t t = 0;
@@ -263,31 +270,16 @@
         t = util_parse_lastmodified(update);
     }
     
+    int depth = cmd_getoption(a, "recursive") ? -1 : 1;
     int ret = -1;
     DavResource *ls;
     while(ret != 0) {
-        if(cmd_getoption(a, "recursive")) {
-            //printf("base: %s\n", base);
-            if(update) {
-                ls = dav_query(
-                        sn,
-                        "get U:crypto-key from %s* where lastmodified > %t",
-                        path,
-                        t);
-            } else {
-                ls = dav_query(sn, "get U:crypto-key from %s*", path);
-            }
-        } else {
-            if(update) {
-                ls = dav_query(
-                        sn,
-                        "get U:crypto-key from %s where lastmodified > %t",
-                        path,
-                        t);
-            } else {
-                ls = dav_query(sn, "get U:crypto-key from %s", path);
-            }
-        }
+        ls = dav_query(
+                sn,
+                "get - from %s where lastmodified > %t with depth %d",
+                path,
+                t,
+                depth);
         
         if(!ls) {
             if(sn->error == DAV_UNAUTHORIZED) {
@@ -429,7 +421,7 @@
     }
     char *keyprop = dav_get_property_ns(
             res,
-            "http://www.uap-core.de/",
+            DAV_NS,
             "crypto-key");
     if(keyprop) {
         flags[1] = 'c';
@@ -486,9 +478,10 @@
     
     char *url = a->argv[0];
     char *path = NULL;
-    DavSession *sn = NULL;
     Repository *repo = url2repo(url, &path);
-    sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    dav_session_set_flags(sn, get_repository_flags(repo));
+    sn->key = dav_context_get_key(ctx, repo->default_key);
     
     char *update = cmd_getoption(a, "update");
     time_t t = 0;
@@ -499,27 +492,14 @@
     int recursive = cmd_getoption(a, "recursive") ? 1 : 0;
     DavResource *res;
     
-    if(recursive) {
-        if(update) {
-            res = dav_query(
-                    sn,
-                    "get U:crypto-key from %s* where lastmodified > %t",
-                    path,
-                    t);
-        } else {
-            res = dav_query(sn, "get U:crypto-key from %s*", path);
-        }
-    } else {
-        if(update) {
-            res = dav_query(
-                    sn,
-                    "get U:crypto-key from %s where lastmodified > %t",
-                    path,
-                    t);
-        } else {
-            res = dav_query(sn, "get U:crypto-key from %s", path);
-        }
-    }
+    int depth = recursive ? -1 : 1;
+    res = dav_query(
+            sn,
+            "get - from %s where lastmodified > %t with depth %d",
+            path,
+            t,
+            depth);
+    
     if(!res) {
         print_resource_error(sn, path);
         return -1;
@@ -532,6 +512,13 @@
         return -1;
     }
     
+    // disable file decryption, if the -p option is specified
+    char *plain = cmd_getoption(a, "plain");
+    if(plain) {
+        int flags = sn->flags;
+        dav_session_set_flags(sn, flags ^ DAV_SESSION_DECRYPT_CONTENT);
+    }
+    
     /*
      * determine the output file
      * use stdout if the output file is -
@@ -598,62 +585,7 @@
         return -1;
     }
     
-    /*
-     * if the -p (plain) option is specified we don't decrypt files
-     * use a key specified with the -k (key) option, a key from the
-     * key property or the repository default key
-     */
-    void *out_stream = fout;
-    dav_write_func write_func = (dav_write_func)fwrite;
-    AESDecrypter *dec = NULL;
-    char *plain = cmd_getoption(a, "plain");
-    char *keyname = cmd_getoption(a, "key");
-    if(!plain) {
-        char *keyprop = dav_get_property_ns(
-                res,
-                "http://www.uap-core.de/",
-                "crypto-key");
-        Key *key = NULL;
-        char *kn = NULL;
-        if(keyname) {
-            kn = keyname;
-        } else if(keyprop) {
-            kn = keyprop;
-        } else if(repo && repo->decrypt) {
-            kn = repo->default_key;
-        }
-        if(kn) {
-            key = get_key(kn);
-            if(!key) {
-                fprintf(stderr, "Key %s not found!\n", kn);
-                // TODO: free
-                if(cmd_getoption(a, "recursive")) {
-                    // skip the file in recursive mode
-                    char *res_url = util_concat_path(
-                            res->session->base_url,
-                            res->path);
-                    printf("Skipping resource: %s\n", res_url);
-                    free(res_url);
-                    return 0;
-                } else {
-                    printf("Abort.\n");
-                    // abort
-                    return 1;
-                }
-            }
-        }
-        
-        if(key) {
-            dec = aes_decrypter_new(key, fout, (dav_write_func)fwrite);
-            out_stream = dec;
-            write_func = (dav_write_func)aes_write;
-        }
-    }
-    
-    int ret = dav_get_content(res, out_stream, write_func);
-    if(dec) {
-        aes_decrypter_close(dec);
-    }
+    int ret = dav_get_content(res, fout, (dav_write_func)fwrite);
     fclose(fout);
     if(ret && strcmp(out, "-")) {
         unlink(out);
@@ -672,9 +604,31 @@
     char *url = a->argv[0];
     char *file = a->argv[1];
     char *path = NULL;
-    DavSession *sn = NULL;
     Repository *repo = url2repo(url, &path);
-    sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    dav_session_set_flags(sn, get_repository_flags(repo));
+    sn->key = dav_context_get_key(ctx, repo->default_key);
+    
+    // disable file encryption if the -p option is specified
+    char *plain = cmd_getoption(a, "plain");
+    if(plain) {
+        int flags = sn->flags;
+        dav_session_set_flags(sn, flags ^ DAV_SESSION_ENCRYPT_CONTENT);
+        dav_session_set_flags(sn, flags ^ DAV_SESSION_ENCRYPT_NAME);
+    }
+    
+    // override the session key if the -k option is specified
+    char *keyname = cmd_getoption(a, "key");
+    if(keyname) {
+        DavKey *key = dav_context_get_key(ctx, keyname);
+        if(key) {
+            sn->key = key;
+        } else {
+            fprintf(stderr, "Key %s not found!\nAbort.\n", keyname);
+            // TODO: free
+            return -1;
+        }
+    }
     
     int ret;
     if(!strcmp(file, "-")) {
@@ -766,32 +720,12 @@
         free(path);
         path = newpath;
         res = dav_resource_new(sn, path);
+        int ret = put_file(repo, a, sn, res->path, NULL, in);
+        // TODO: free res
+        return ret;
     }
     
-    AESEncrypter *enc = NULL;
-    char *keyname = cmd_getoption(a, "key");
-    char *kn = NULL;
-    char *plain = cmd_getoption(a, "plain");
-    if(!plain && (keyname || repo)) {
-        kn = keyname ? keyname : repo->default_key;
-        if(kn) {
-            Key *key = get_key(kn);
-            if(!key) {
-                fprintf(stderr, "Key %s not found!\nAbort.\n", kn);
-                // TODO: free
-                return -1;
-            }
-            if(keyname || repo->encrypt) {
-                enc = aes_encrypter_new(key, in, (dav_read_func)fread);
-            }
-        }
-    }
-    if(enc) {
-        dav_set_content(res, enc, (dav_read_func)aes_read);
-        dav_set_property_ns(res, "http://www.uap-core.de/", "crypto-key", kn);
-    } else {
-        dav_set_content(res, in, (dav_read_func)fread);
-    }
+    dav_set_content(res, in, (dav_read_func)fread);
     
     if(dav_store(res)) {
         print_resource_error(sn, res->path);
@@ -802,9 +736,6 @@
         fclose(in);
         return -1;
     }
-    if(enc) {
-        aes_encrypter_close(enc);
-    }
     return 0;
 }
 
@@ -818,9 +749,10 @@
     
     char *url = a->argv[0];
     char *path = NULL;
-    DavSession *sn = NULL;
     Repository *repo = url2repo(url, &path);
-    sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    dav_session_set_flags(sn, get_repository_flags(repo));
+    sn->key = dav_context_get_key(ctx, repo->default_key);
     
     DavResource *res = dav_resource_new(sn, path);
     if(!res) {
@@ -846,9 +778,31 @@
     
     char *url = a->argv[0];
     char *path = NULL;
-    DavSession *sn = NULL;
     Repository *repo = url2repo(url, &path);
-    sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    dav_session_set_flags(sn, get_repository_flags(repo));
+    sn->key = dav_context_get_key(ctx, repo->default_key);
+    
+    // disable file encryption if the -p option is specified
+    char *plain = cmd_getoption(a, "plain");
+    if(plain) {
+        int flags = sn->flags;
+        dav_session_set_flags(sn, flags ^ DAV_SESSION_ENCRYPT_CONTENT);
+        dav_session_set_flags(sn, flags ^ DAV_SESSION_ENCRYPT_NAME);
+    }
+    
+    // override the session key if the -k option is specified
+    char *keyname = cmd_getoption(a, "key");
+    if(keyname) {
+        DavKey *key = dav_context_get_key(ctx, keyname);
+        if(key) {
+            sn->key = key;
+        } else {
+            fprintf(stderr, "Key %s not found!\nAbort.\n", keyname);
+            // TODO: free
+            return -1;
+        }
+    }
     
     DavResource *res = dav_resource_new(sn, path);
     if(!res) {
@@ -891,9 +845,10 @@
     } else if (a->argc == 1) {
         char *url = a->argv[0];
         char *path = NULL;
-        DavSession *sn = NULL;
         Repository *repo = url2repo(url, &path);
-        sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+        DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+        dav_session_set_flags(sn, get_repository_flags(repo));
+        sn->key = dav_context_get_key(ctx, repo->default_key);
 
         DavResource *res = dav_resource_new(sn, path);
         char *date = NULL;
@@ -909,6 +864,7 @@
         fprintf(stderr, "Too many arguments\n");
         return -1;
     }
+    return 0;
 }
 
 int cmd_sync(CmdArgs *a) {
--- a/libidav/Makefile	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/Makefile	Thu Jun 05 15:11:29 2014 +0200
@@ -30,6 +30,7 @@
 
 # list of source files
 SRC  = webdav.c
+SRC += session.c
 SRC += resource.c
 SRC += methods.c
 SRC += utils.c
--- a/libidav/crypto.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/crypto.c	Thu Jun 05 15:11:29 2014 +0200
@@ -196,40 +196,49 @@
 
 
 char* aes_encrypt(char *in, DavKey *key) {
-    char *iv = malloc(16);
+    unsigned char iv[16];
     if(!RAND_bytes(iv, 16)) {
-        free(iv);
         return NULL;
     }
     
     EVP_CIPHER_CTX ctx;
     EVP_CIPHER_CTX_init(&ctx);
     if(key->type == DAV_KEY_AES128) {
-        EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key->data, iv);
+        EVP_EncryptInit_ex(
+                &ctx,
+                EVP_aes_128_cbc(),
+                NULL,
+                (unsigned char*)key->data,
+                iv);
     } else if(key->type == DAV_KEY_AES256) {
-        EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key->data, iv);
+        EVP_EncryptInit_ex(
+                &ctx,
+                EVP_aes_256_cbc(),
+                NULL,
+                (unsigned char*)key->data,
+                iv);
     } else {
         return NULL;
     }
     
     int len = strlen(in);
     int buflen = len + 64;
-    char *buf = calloc(1, buflen);
+    unsigned char *buf = calloc(1, buflen);
     memcpy(buf, iv, 16);
     
     int l = buflen - 16;
-    EVP_EncryptUpdate(&ctx, buf + 16, &l, in, len);
+    EVP_EncryptUpdate(&ctx, buf + 16, &l, (unsigned char*)in, len);
     
     int f = 0;
     EVP_EncryptFinal_ex(&ctx, buf + 16 + l, &f);
-    char *out = util_base64encode(buf, 16 + l + f);
+    char *out = util_base64encode((char*)buf, 16 + l + f);
     free(buf);
     return out;
 }
 
 char* aes_decrypt(char *in, DavKey *key) {
     int len;
-    char *buf = util_base64decode_len(in, &len);
+    unsigned char *buf = (unsigned char*)util_base64decode_len(in, &len);
     
     EVP_CIPHER_CTX ctx;
     EVP_CIPHER_CTX_init(&ctx);
@@ -251,17 +260,15 @@
         return NULL;
     }
     
-    char *out = malloc(len + 1);
+    unsigned char *out = malloc(len + 1);
     int outlen = len;
-    char *in_buf = buf + 16;
+    unsigned char *in_buf = buf + 16;
     int inlen = len - 16;
-    int f = 0;
-    
-    
+    int f = 0; 
     
     EVP_DecryptUpdate(&ctx, out, &outlen, in_buf, inlen);
     EVP_DecryptFinal_ex(&ctx, out + outlen, &f);
     out[outlen + f] = '\0';
     free(buf);
-    return out;
+    return (char*)out;
 }
--- a/libidav/davql.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/davql.c	Thu Jun 05 15:11:29 2014 +0200
@@ -72,7 +72,9 @@
     
     sstr_t from_query = q;
     sstr_t cond = util_getsubstr_until_token(q, S("where"), &from_query);
-    
+    sstr_t with = util_getsubstr_until_token(cond, S("with"), &cond);
+    int depth = 1;
+       
     // insert variable values
     UcxBuffer *fbuf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
     int var = 0;
@@ -124,9 +126,19 @@
         ucx_list_free(ops);
     }
     
+    // with
+    if(with.ptr) {
+        if(dav_parse_with(with, &depth, ap)) {
+            // TODO: error
+            printf("parse error\n");
+            return NULL;
+        }
+    }
+    
     DavGetQuery *getquery = malloc(sizeof(DavGetQuery));
     getquery->properties = sstrdup(property_query);
     getquery->from = sstrn(fbuf->space, fbuf->pos);
+    getquery->depth = depth;
     if(condition) {
         getquery->condition = condition;
         getquery->condlen = oplen;
@@ -166,6 +178,57 @@
     return 0;
 }
 
+static int dav_str2depth(sstr_t str, int *depth) {
+    if(!sstrcmp(str, S("infinity"))) {
+        *depth = -1;
+    } else {
+        sstr_t cp = sstrdup(str); // terminate
+        *depth = atoi(cp.ptr);
+        free(cp.ptr);
+    }
+    return 0;
+}
+
+int dav_parse_with(sstr_t with, int *depth, va_list ap) {
+    int i;
+    for(i=0;i<with.length;i++) {
+        if(with.ptr[i] == ' ') {
+            break;
+        }
+    }
+    
+    sstr_t name = sstrsubsl(with, 0, i);
+    sstr_t value = sstrtrim(sstrsubs(with, i));
+    //printf("with {%.*s} {%.*s}\n", name.length, name.ptr, value.length, value.ptr);
+    
+    if(!sstrcmp(name, S("depth"))) {
+        if(value.length == 0) {
+            return 1;
+        } else if(value.ptr[0] == '%') {
+            switch(value.ptr[1]) {
+                default: return 1;
+                case 's': {
+                    sstr_t v = sstr(va_arg(ap, char*));
+                    if(dav_str2depth(value, depth)) {
+                        return 1;
+                    }
+                    break;
+                }
+                case 'd': {
+                    *depth = va_arg(ap, int);
+                    break;
+                }
+            }
+        } else {
+            if(dav_str2depth(value, depth)) {
+                return 1;
+            }
+        }
+    }
+    
+    return 0;
+}
+
 int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap) {
     sstr_t token;
     DavQOp *op1 = NULL; // level 1 operator
--- a/libidav/davql.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/davql.h	Thu Jun 05 15:11:29 2014 +0200
@@ -69,6 +69,7 @@
     sstr_t from;
     DavQOp *condition;
     size_t condlen;
+    int    depth;
 } DavGetQuery;
     
 DavQuery dav_ql_parse(char *query, va_list ap);
@@ -77,6 +78,7 @@
 
 int parse_path_query(sstr_t query, char **path, int *depth);
 
+int dav_parse_with(sstr_t with, int *depth, va_list ap);
 int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap);
 sstr_t condition_parser_next_token(sstr_t *str);
 int condition_operator_type(sstr_t token, int64_t *type);
--- a/libidav/methods.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/methods.c	Thu Jun 05 15:11:29 2014 +0200
@@ -33,6 +33,8 @@
 #include "utils.h"
 #include "methods.h"
 #include "davql.h"
+#include "crypto.h"
+#include "session.h"
 
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
@@ -80,16 +82,38 @@
     return buf;
 }
 
-UcxBuffer* create_propfind_request(UcxList *properties) {
+UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties) {
     UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
     sstr_t s;
     
+    int add_crypto_name = 1;
+    int add_crypto_key = 1;
+    char *crypto_ns = "idav";
     UcxMap *namespaces = ucx_map_new(8);
     UCX_FOREACH(elm, properties) {
         DavProperty *p = elm->data;
         if(strcmp(p->ns->name, "DAV:")) {
             ucx_map_cstr_put(namespaces, 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;
+            }
+        }
+    }
+    
+    DavNamespace idav_ns;
+    if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn)) {
+        idav_ns.prefix = "idav";
+        idav_ns.name = DAV_NS;
+        ucx_map_cstr_put(namespaces, "idav", &idav_ns);
     }
     
     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
@@ -129,6 +153,22 @@
     s = S("<D:resourcetype />\n");
     ucx_buffer_write(s.ptr, 1, s.length, buf);
     
+    // crypto properties
+    if(DAV_CRYPTO(sn)) {
+        if(add_crypto_name) {
+            ucx_buffer_putc(buf, '<');
+            ucx_buffer_puts(buf, crypto_ns);
+            s = S(":crypto-name />\n");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+        }
+        if(add_crypto_key) {
+            ucx_buffer_putc(buf, '<');
+            ucx_buffer_puts(buf, crypto_ns);
+            s = S(":crypto-key />\n");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+        }
+    }
+    
     // extra properties
     UCX_FOREACH(elm, properties) {
         DavProperty *prop = elm->data;
@@ -151,11 +191,44 @@
     return buf;
 }
 
+UcxBuffer* create_basic_propfind_request() {
+    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
+    sstr_t s;
+    
+    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);  
+    s = S(DAV_NS);
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = S("\" >\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // properties
+    s = S("<D:prop>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = S("<D:resourcetype />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = S("<i:crypto-key />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = S("<i:crypto-name />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = S("</D:prop>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // end
+    s = S("</D:propfind>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    return buf;
+}
+
 DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response, DavQOp *cond, size_t len) {
     char *url = NULL;
     curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
     if(!root) {
-        root = dav_resource_new_href(sn, util_url_path(url));
+        root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove
     }
     
     xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
@@ -181,7 +254,17 @@
 }
 
 int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t clen) {
-    DavResource *res = resource;
+    DavSession *sn = resource->session;
+    
+    //DavResource *res = resource;
+    DavResource *res = NULL;
+    char *href = NULL;
+    UcxList *properties = NULL; // 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) {
@@ -189,16 +272,16 @@
                 xmlNode *href_node = node->children;
                 if(href_node->type != XML_TEXT_NODE) {
                     // error
-                    resource->session->error = DAV_ERROR;
+                    sn->error = DAV_ERROR;
                     return 1;
                 }
-                char *href = (char*)href_node->content;
-                href = util_url_path(href);
+                //char *href = (char*)href_node->content;
+                href = util_url_path((char*)href_node->content);
                 if(xstreq(resource->href, href)) {
                     res = resource;
                 } else {
-                    res = dav_resource_new_href(resource->session, href);
-                    res->parent = resource;
+                    //res = dav_resource_new_href(resource->session, href);
+                    //res->parent = resource;
                 }
             } else if(xstreq(node->name, "propstat")) {
                 xmlNode *n = node->children;
@@ -212,12 +295,12 @@
                         } else if(xstreq(n->name, "status")) {
                             xmlNode *status_node = n->children;
                             if(status_node->type != XML_TEXT_NODE) {
-                                resource->session->error = DAV_ERROR;
+                                sn->error = DAV_ERROR;
                                 return 1;
                             }
                             sstr_t status_str = sstr((char*)status_node->content);
                             if(status_str.length < 13) {
-                                resource->session->error = DAV_ERROR;
+                                sn->error = DAV_ERROR;
                                 return 1;
                             }
                             status_str = sstrsubsl(status_str, 9, 3);
@@ -233,21 +316,19 @@
                     n = prop_node->children;
                     while(n) {
                         if(n->type == XML_ELEMENT_NODE) {
+                            properties = ucx_list_append(properties, n);
                             if(xstreq(n->name, "resourcetype")) {
                                 xmlNode *rsnode = n->children;
                                 if(rsnode && rsnode->type == XML_ELEMENT_NODE) {
                                     // TODO: this is a ugly lazy hack
-                                    resource_add_property(res, "DAV:", (char*)n->name, "collection");
-                                    res->iscollection = 1;
+                                    //resource_add_property(res, "DAV:", (char*)n->name, "collection");
+                                    iscollection = 1;
                                 }
-                            } else {
-                                xmlNode *content = n->children;
-                                if(content) {
-                                    resource_add_property(
-                                            res,
-                                            (char*)n->ns->href,
-                                            (char*)n->name,
-                                            (char*)content->content);
+                            } 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);
                                 }
                             }
                         }
@@ -260,6 +341,39 @@
         node = node->next;
     }
     
+    if(!res) {
+        // create new resource object
+        char *name = NULL;
+        if(DAV_DECRYPT_NAME(sn) && crypto_name) {
+            if(!crypto_key) {
+                // TODO: error
+                fprintf(stderr, "encrypted resource without key\n");
+            }
+            name = util_decrypt_str(sn, crypto_name, crypto_key);
+            if(!name) {
+                // TODO: error
+                fprintf(stderr, "decrypted name is null\n");
+            }
+        } else {
+            name = util_resource_name(href);
+        }
+        
+        href = dav_session_strdup(sn, href);
+        res = dav_resource_new_full(sn, resource->path, name, href);
+    }
+    res->iscollection = iscollection;
+    
+    // add properties
+    UCX_FOREACH(elm, properties) {
+        xmlNode *prop = elm->data;
+        
+        // TODO: add xml data instead of a string
+        char *text = util_xml_get_text(prop);
+        if(text) {
+            resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, text);
+        }
+    }
+    
     set_davprops(res);
     if(res != resource) {
         if(clen > 0) {
@@ -436,6 +550,41 @@
     return buf;
 }
 
+UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name) {
+    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
+    sstr_t s;
+    
+    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:set>\n<D:prop>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    if(DAV_ENCRYPT_NAME(sn)) {
+        s = S("<idav:crypto-name>");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        char *crname = aes_encrypt(name, key);
+        ucx_buffer_puts(buf, crname);
+        free(crname);
+        s = S("</idav:crypto-name>\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    
+    s = S("<idav:crypto-key>");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    ucx_buffer_puts(buf, key->name);
+    s = S("</idav:crypto-key>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    return buf;
+}
+
 /* ----------------------------- PUT ----------------------------- */
 
 static size_t dummy_write(void *buf, size_t s, size_t n, void *data) {
@@ -448,6 +597,10 @@
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
     curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
     
+    // clear headers
+    struct curl_slist *headers = NULL;
+    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    
     UcxBuffer *buf = NULL;
     if(!read_func) {
         buf = ucx_buffer_new(data, length, 0);
@@ -478,6 +631,9 @@
 }
 
 CURLcode do_delete_request(CURL *handle, UcxBuffer *response) {
+    struct curl_slist *headers = NULL;
+    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
     curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
@@ -491,6 +647,9 @@
 }
 
 CURLcode do_mkcol_request(CURL *handle) { 
+    struct curl_slist *headers = NULL;
+    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL");
     curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
--- a/libidav/methods.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/methods.h	Thu Jun 05 15:11:29 2014 +0200
@@ -53,12 +53,15 @@
         size_t length);
 
 UcxBuffer* create_allprop_propfind_request();
-UcxBuffer* create_propfind_request(UcxList *properties);
+UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties);
+UcxBuffer* create_basic_propfind_request();
+
 DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response, DavQOp *cond, size_t len);
 int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t len);
 void set_davprops(DavResource *res);
 
 UcxBuffer* create_proppatch_request(DavResourceData *data);
+UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name);
 
 CURLcode do_delete_request(CURL *handle, UcxBuffer *response);
 
--- a/libidav/resource.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/resource.c	Thu Jun 05 15:11:29 2014 +0200
@@ -32,6 +32,7 @@
 #include <libxml/tree.h>
 
 #include "utils.h"
+#include "session.h"
 #include "methods.h"
 #include "davql.h"
 #include "crypto.h"
@@ -43,16 +44,21 @@
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
 DavResource* dav_resource_new(DavSession *sn, char *path) {
-    char *url = util_path_to_url(sn, path);
-    char *href = util_url_path(url);
-    DavResource *res = dav_resource_new_href(sn, href);
-    free(url);
+    //char *href = util_url_path(url);
+    //DavResource *res = dav_resource_new_href(sn, href);
+    char *parent = util_parent_path(path);
+    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, char *name) {
     char *path = util_concat_path(parent->path, name);
-    DavResource *res = dav_resource_new(sn, path);
+    char *href = dav_session_create_plain_href(sn, path);
+    DavResource *res = dav_resource_new_full(sn, parent->path, name, href);
     free(path);
     return res;
 }
@@ -71,6 +77,100 @@
     return res;
 }
 
+DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href) {
+    DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
+    res->session = sn;
+    
+    // set name, path and href
+    sstr_t n = sstr(name);
+    res->name = sstrdup_a(sn->mp->allocator, n).ptr;
+    if(n.ptr[n.length-1] == '/') {
+        res->name[n.length-1] = '\0';
+    }
+    
+    char *path = util_concat_path(parent_path, name); 
+    res->path = dav_session_strdup(sn, path);
+    free(path);
+    
+    res->href = href;
+    
+    // initialize resource data
+    res->data = resource_data_new(sn);
+    
+    // cache href/path
+    if(href) {
+        dav_session_cache_path(sn, sstr(path), sstr(href));
+    }
+    
+    return res;
+}
+
+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;
+    UcxMapIterator i = ucx_map_iterator(data->properties);
+    UcxKey key;
+    char *property;
+    UCX_MAP_FOREACH(key, property, i) {
+        dav_session_free(sn, property);
+    }
+    ucx_map_free(data->properties);
+    
+    UCX_FOREACH(elm, data->set) {
+        DavProperty *p = elm->data;
+        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->value);
+        dav_session_free(sn, p);
+    }
+    
+    UCX_FOREACH(elm, data->remove) {
+        DavProperty *p = elm->data;
+        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->value);
+        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, sstr_t href) {
+    res->href = sstrdup_a(res->session->mp->allocator, href).ptr;
+}
+
 void resource_set_info(DavResource *res, char *href_str) {
     char *url_str = NULL;
     curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str);
@@ -112,6 +212,15 @@
     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_property(DavResource *res, char *ns, char *name, char *val) {
     if(!val) {
         return;
@@ -209,14 +318,14 @@
     DavProperty *property = dav_session_malloc(
             res->session,
             sizeof(DavProperty));
-    property->name = sstrdup_a(a, sstr(name)).ptr;
-    property->value = sstrdup_a(a, sstr(value)).ptr;
+    property->name = dav_session_strdup(res->session, name);
+    property->value = dav_session_strdup(res->session, value);
     
     DavNamespace *namespace = dav_session_malloc(
             res->session,
             sizeof(DavNamespace));
     namespace->prefix = NULL;
-    namespace->name = sstrdup_a(a, sstr(ns)).ptr;
+    namespace->name = dav_session_strdup(res->session, ns);
     property->ns = namespace;
     
     data->set = ucx_list_append_a(a, data->set, property);
@@ -251,92 +360,77 @@
 
 
 void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) {
-    DavSession *sn = res->session;
-    if((sn->flags & DAV_SESSION_ENCRYPT_FILE) == DAV_SESSION_ENCRYPT_FILE) {
-        AESEncrypter *enc = aes_encrypter_new(sn->key, stream, read_func);
-        DavResourceData *data = res->data;
-        data->content = enc;
-        data->read = (dav_read_func)aes_read;
-        data->length = 0;
-        dav_set_property_ns(
-                res,
-                "http://www.uap-core.de/",
-                "crypto-key",
-                sn->key->name);
-    } else {
-        DavResourceData *data = res->data;
-        data->content = stream;
-        data->read = read_func;
-        data->length = 0;
-    }
+    DavResourceData *data = res->data;
+    data->content = stream;
+    data->read = read_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 = content;
+    data->content = dav_session_malloc(sn, length);
+    memcpy(data->content, content, length);
     data->read = NULL;
     data->length = length;
 }
 
 
 int dav_load(DavResource *res) {
-    DavSession *sn = res->session;
-    DavResourceData *data = res->data;
-    // clean map
-    UcxKey key;
-    void *value;
-    UcxMapIterator i = ucx_map_iterator(data->properties);
-    UCX_MAP_FOREACH(key, value, i) {
-        ucx_map_remove(data->properties, key);
-    }
-    
-    util_set_url(sn, res->path);
-    
     UcxBuffer *rqbuf = create_allprop_propfind_request();
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    
-    //fwrite(rpbuf->space, 1, rpbuf->size, stdout);
-    //printf("\n");
-    
-    CURLcode ret = do_propfind_request(sn->handle, rqbuf, rpbuf);
-    int status = 0;
-    curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
-    if(ret == CURLE_OK) {
-        //printf("response\n%s\n", rpbuf->space);
-        // TODO: use parse_propfind_response()
-        xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, NULL, NULL, 0);
-        if(!doc) {
-            return 1;
-        }
-
-        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(res, node, NULL, 0);
-                }
-            }
-            node = node->next;
-        }
-        
-        set_davprops(res);
-    } else {
-        session_set_error(sn, ret, status);
-    }
-    return 0;
+    int ret = dav_propfind(res->session, res, rqbuf, NULL, 0);
+    ucx_buffer_free(rqbuf);
+    return ret;
 }
 
 int dav_store(DavResource *res) {
     DavSession *sn = res->session;
     DavResourceData *data = res->data;
     
-    util_set_url(sn, res->path);;
-    
+    util_set_url(sn, dav_resource_get_href(res));
+      
     // store content
     if(data->content) {
-        CURLcode ret = do_put_request(sn->handle, data->content, data->read, data->length);
+        int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key;
+        CURLcode ret;
+        if(encryption) {
+            AESEncrypter *enc = NULL;
+            UcxBuffer *buf = NULL;
+            if(data->read) {
+                enc = aes_encrypter_new(sn->key, data->content, data->read);
+            } else {
+                buf = ucx_buffer_new(data->content, data->length, 0);
+                buf->size = data->length;
+                enc = aes_encrypter_new(
+                        sn->key,
+                        buf,
+                        (dav_read_func)ucx_buffer_read);
+            }
+            
+            // create an empty resource
+            ret = do_put_request(
+                    sn->handle,
+                    enc,
+                    (dav_read_func)aes_read,
+                    0);
+            aes_encrypter_close(enc);
+            if(buf) {
+                ucx_buffer_free(buf);
+            }
+            
+            // add crypto properties
+            // TODO: store the properties later
+            if(resource_add_crypto_info(sn, res->href, res->name)) {
+                return 1;
+            }
+        } else {
+            ret = do_put_request(
+                    sn->handle,
+                    data->content,
+                    data->read,
+                    data->length);
+        }
+        
         int status = 0;
         curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status);
         if(ret == CURLE_OK && (status >= 200 && status < 300)) {
@@ -349,7 +443,7 @@
             data->read = NULL;
             data->length = 0;
         } else {
-            session_set_error(sn, ret, status);
+            dav_session_set_error(sn, ret, status);
             return 1;
         }
     }
@@ -369,7 +463,7 @@
             data->set = NULL;
             data->remove = NULL;
         } else {
-            session_set_error(sn, ret, status);
+            dav_session_set_error(sn, ret, status);
             return 1;
         }
     }
@@ -378,8 +472,23 @@
 }
 
 int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { 
-    CURL *handle = res->session->handle;
-    util_set_url(res->session, res->path);
+    DavSession *sn = res->session;
+    CURL *handle = sn->handle;
+    util_set_url(res->session, dav_resource_get_href(res));
+    
+    // check encryption
+    AESDecrypter *dec = NULL;
+    if(DAV_DECRYPT_CONTENT(sn)) {
+        char *keyname = dav_get_property_ns(res, DAV_NS, "crypto-key");
+        if(keyname) {
+            DavKey *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_SSL_VERIFYPEER, 0);
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
@@ -390,13 +499,18 @@
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream);
     
     CURLcode ret = curl_easy_perform(handle);
+    
+    if(dec) {
+        aes_decrypter_close(dec);
+    }
+    
     int status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
         res->session->error = DAV_OK;
         return 0;
     } else {
-        session_set_error(res->session, ret, status);
+        dav_session_set_error(res->session, ret, status);
         return 1;
     }
 }
@@ -413,7 +527,7 @@
 
 int dav_delete(DavResource *res) {
     CURL *handle = res->session->handle;
-    util_set_url(res->session, res->path);
+    util_set_url(res->session, dav_resource_get_href(res));
     
     UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
     CURLcode ret = do_delete_request(handle, response);
@@ -427,93 +541,111 @@
         
         return 0;
     } else {
-        session_set_error(res->session, ret, status);
+        dav_session_set_error(res->session, ret, status);
+        return 1;
+    }
+}
+
+static int create_ancestors(DavSession *sn, char *href, char *path) {
+    CURL *handle = sn->handle;
+    CURLcode code;
+    int 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(handle);
+        curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
+        if(status == 201) {
+            // resource successfully created
+            char *name = util_resource_name(p);
+            int len = strlen(name);
+            if(name[len - 1] == '/') {
+                name[len - 1] = '\0';
+            }
+            if(resource_add_crypto_info(sn, h, name)) {
+                // TODO: error
+            }
+            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));
+    
+    CURLcode code;
+    if(res->iscollection) {
+        code = do_mkcol_request(handle);
+    } else {
+        code = do_put_request(handle, "", NULL, 0); 
+    }
+    int 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
+        resource_add_crypto_info(sn, res->href, res->name); // TODO: check return type
+        
+        // do a minimal propfind request
+        UcxBuffer *rqbuf = create_propfind_request(sn, NULL);
+        int ret = dav_propfind(sn, res, rqbuf, NULL, 0);
+        ucx_buffer_free(rqbuf);
+        return ret;
+    } else {
+        dav_session_set_error(sn, code, s);
         return 1;
     }
 }
 
 int dav_create(DavResource *res) {
-    //char *url = util_concat_path(res->session->base_url, res->path);
-    char *parent = util_parent_path(res->path);
-    
-    DavSession *sn = res->session;
-    DavResource *parent_res = dav_get(sn, parent, NULL);
-    if(!parent_res && sn->error == DAV_NOT_FOUND) {
-        parent_res = dav_resource_new(sn, parent);
-        parent_res->iscollection = 1;
-        int r = dav_create(parent_res);
-        if(r) {
-            free(parent);
-            return r;
-        }
-    } else if(parent_res && !parent_res->iscollection) {
-        sn->error = DAV_FORBIDDEN;
-        return 1;
-    } else if(sn->error != DAV_OK) {
-        return 1;
+    int status;
+    if(!create_resource(res, &status)) {
+        // resource successfully created
+        return 0;
     }
     
-    CURL *handle = res->session->handle;
-    util_set_url(res->session, res->path);
-    free(parent);
-    
-    // create new collection or do an empty put request
-    CURLcode ret;
-    if(res->iscollection) {
-        ret = do_mkcol_request(handle);
-    } else {
-        ret = do_put_request(handle, "", NULL, 0); 
-    }
-    int status = 0;
-    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
-    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
-        res->session->error = DAV_OK;
-    } else {
-        session_set_error(res->session, ret, status);
-        return 1;
+    if(status == 403 || status == 409) {
+        // create intermediate collections
+        if(create_ancestors(res->session, res->href, res->path)) {
+            return 1;
+        }
     }
     
-    // do an minimal propfind request
-    UcxBuffer *rqbuf = create_propfind_request(NULL);
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    
-    //fwrite(rpbuf->space, 1, rpbuf->size, stdout);
-    //printf("\n");
-    
-    ret = do_propfind_request(handle, rqbuf, rpbuf);
-    status = 0;
-    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
-    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
-        //printf("response\n%s\n", rpbuf->space);
-        // TODO: use parse_propfind_response()
-        xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, NULL, NULL, 0);
-        if(!doc) {
-            return 1;
-        }
-
-        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(res, node, NULL, 0);
-                }
-            }
-            node = node->next;
-        }
-        
-        set_davprops(res);
-        return 0;
-    } else {
-        session_set_error(sn, ret, status);
-        return 1;
-    }
+    return create_resource(res, &status);
 }
 
 int dav_exists(DavResource *res) {
     DavSession *sn = res->session;
     CURL *handle = sn->handle;
-    util_set_url(sn, res->path);
+    util_set_url(sn, dav_resource_get_href(res));
     
     CURLcode ret = do_head_request(handle);
     int status = 0;
@@ -521,7 +653,30 @@
     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
         return 1;
     } else {
-        session_set_error(sn, ret, status);
+        dav_session_set_error(sn, ret, status);
         return 0;
     }
 }
+
+
+int resource_add_crypto_info(DavSession *sn, char *href, char *name) {
+    if(!DAV_IS_ENCRYPTED(sn)) {
+        return 0;
+    }
+    
+    UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name);
+    UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    
+    util_set_url(sn, href);
+    CURLcode ret = do_proppatch_request(sn->handle, request, response);
+    int status = 0;
+    curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && status == 207) {
+        // TODO: parse response
+        sn->error = DAV_OK;
+        return 0;
+    } else {
+        dav_session_set_error(sn, ret, status);
+        return 1;
+    }
+}
--- a/libidav/resource.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/resource.h	Thu Jun 05 15:11:29 2014 +0200
@@ -30,6 +30,7 @@
 #define	RESOURCE_H
 
 #include "webdav.h"
+#include <ucx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -56,12 +57,18 @@
     size_t    length;
 };
 
+DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href);
+
+void resource_set_href(DavResource *res, sstr_t href);
+
 void resource_set_info(DavResource *res, char *href_str);
 DavResourceData* resource_data_new(DavSession *sn);
 void resource_add_property(DavResource *res, char *ns, char *name, char *val);
 char* resource_get_property(DavResource *res, char *ns, char *name);
 void resource_add_child(DavResource *parent, DavResource *child);
 
+int resource_add_crypto_info(DavSession *sn, char *href, char *name);
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/session.c	Thu Jun 05 15:11:29 2014 +0200
@@ -0,0 +1,340 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 <ucx/buffer.h>
+#include <ucx/utils.h>
+
+#include "utils.h"
+#include "session.h"
+#include "resource.h"
+#include "methods.h"
+
+DavSession* dav_session_new(DavContext *context, char *base_url) {
+    if(!base_url) {
+        return NULL;
+    }
+    sstr_t url = sstr(base_url);
+    if(url.length == 0) {
+        return NULL;
+    }
+    DavSession *sn = malloc(sizeof(DavSession));
+    sn->mp = ucx_mempool_new(DAV_SESSION_MEMPOOL_SIZE);
+    sn->pathcache = ucx_map_new_a(sn->mp->allocator, DAV_PATH_CACHE_SIZE);
+    sn->key = NULL;
+    sn->errorstr = NULL;
+    sn->error = DAV_OK;
+    sn->flags = 0;
+    if(url.ptr[url.length - 1] == '/') {
+        sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url));
+        sn->base_url = url.ptr;
+    } else {
+        char *url_str = malloc(url.length + 2);
+        memcpy(url_str, base_url, url.length);
+        url_str[url.length]     = '/';
+        url_str[url.length + 1] = '\0';
+        sn->base_url = url_str;
+    }
+    sn->context = context;
+    sn->handle = curl_easy_init();
+    //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
+    //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
+    curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L);
+
+    // set proxy
+    DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy
+                                                  : context->http_proxy;
+    
+    if (proxy->url) {
+        curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url);
+        if (proxy->username) {
+            curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME,
+                proxy->username);
+            if (proxy->password) {
+                curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD,
+                    proxy->password);
+            } else {
+                // TODO: prompt
+            }
+        }
+        if(proxy->no_proxy) {
+            curl_easy_setopt(sn->handle, CURLOPT_NOPROXY,
+                proxy->no_proxy);
+        }
+    }
+    
+    // set url
+    curl_easy_setopt(sn->handle, CURLOPT_URL, base_url);
+    
+    context->sessions = ucx_list_append(context->sessions, sn);
+    
+    return sn;
+}
+
+DavSession* dav_session_new_auth(
+        DavContext *context,
+        char *base_url,
+        char *user,
+        char *password)
+{
+    DavSession *sn = dav_session_new(context, base_url);
+    if(!sn) {
+        return NULL;
+    }
+    dav_session_set_auth(sn, user, password);
+    return sn;
+}
+
+void dav_session_set_auth(DavSession *sn, char *user, char *password) {
+    if(user && password) {
+        size_t ulen = strlen(user);
+        size_t plen = strlen(password);
+        size_t upwdlen = ulen + plen + 2;
+        char *upwdbuf = malloc(upwdlen);
+        snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password);
+        curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf);
+        free(upwdbuf);
+    }
+}
+
+void dav_session_set_flags(DavSession *sn, uint32_t flags) {
+    sn->flags = flags;
+}
+
+uint32_t dav_session_get_flags(DavSession *sn) {
+    return sn->flags;
+}
+
+void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags) {
+    sn->key = key;
+    if(flags != 0) {
+        sn->flags |= flags;
+    } else {
+        sn->flags |= DAV_SESSION_ENCRYPT_CONTENT;
+    }
+}
+
+void dav_session_set_error(DavSession *sn, CURLcode c, int status) {
+    if(status > 0) {
+        switch(status) {
+            default: sn->error = DAV_ERROR; break;
+            case 401: sn->error = DAV_UNAUTHORIZED; break;
+            case 403: sn->error = DAV_FORBIDDEN; break;
+            case 404: sn->error = DAV_NOT_FOUND; break;
+            case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break;
+            case 409: sn->error = DAV_CONFLICT; break;
+        }
+    } else {
+        sn->error = DAV_ERROR;
+    }
+    if(c != CURLE_OK) {
+        sn->errorstr = curl_easy_strerror(c);
+    } else {
+        sn->errorstr = NULL;
+    }
+}
+
+void dav_session_destroy(DavSession *sn) { 
+    // remove session from context
+    UcxList *sessions = sn->context->sessions;
+    ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL);
+    if(i > 0)  {
+        UcxList *elm = ucx_list_get(sessions, i);
+        if(elm) {
+            sn->context->sessions = ucx_list_remove(sessions, elm);
+        }
+    }
+    
+    ucx_mempool_destroy(sn->mp);
+    curl_easy_cleanup(sn->handle);
+    free(sn);
+}
+
+
+void* dav_session_malloc(DavSession *sn, size_t size) {
+    return ucx_mempool_malloc(sn->mp, size);
+}
+
+void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) {
+    return ucx_mempool_calloc(sn->mp, nelm, size);
+}
+
+void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) {
+    return ucx_mempool_realloc(sn->mp, ptr, size);
+}
+
+void  dav_session_free(DavSession *sn, void *ptr) {
+    ucx_mempool_free(sn->mp, ptr);
+}
+
+char* dav_session_strdup(DavSession *sn, char *str) {
+    return sstrdup_a(sn->mp->allocator, sstr(str)).ptr;
+}
+
+
+char* dav_session_create_plain_href(DavSession *sn, char *path) {
+    if(!DAV_ENCRYPT_NAME(sn)) {
+        // non encrypted file names
+        char *url = util_path_to_url(sn, path);
+        char *href = dav_session_strdup(sn, util_url_path(url));
+        free(url);
+        return href;
+    } else {
+        return NULL;
+    }
+}
+
+char* dav_session_get_href(DavSession *sn, char *path) {
+    if(DAV_ENCRYPT_NAME(sn)) {
+        sstr_t p = sstr(path);
+        UcxBuffer *href = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
+        UcxBuffer *pbuf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
+        int start = 0;
+        int begin = 0;
+        
+        // check path cache
+        char *cp = strdup(path);
+        //printf("cp: %s\n", cp);
+        while(strlen(cp) > 1) {
+            char *cached = ucx_map_cstr_get(sn->pathcache, cp);
+            if(cached) {
+                start = strlen(cp);
+                begin = start;
+                ucx_buffer_puts(href, cached);
+                break;
+            } else {
+                // check, if the parent path is cached
+                char *f = cp;
+                cp = util_parent_path(cp);
+                free(f);
+            }
+        }
+        free(cp);
+        if(href->pos == 0) {
+            // if there are no cached elements we have to add the base url path
+            // to the href buffer
+            ucx_buffer_puts(href, util_url_path(sn->base_url));
+        }
+        
+        // create resource for name lookup
+        sstr_t rp = sstrdup(sstrn(path, start));
+        DavResource *root = dav_resource_new(sn, rp.ptr);
+        resource_set_href(root, sstrn(href->space, href->pos));
+        
+        // create request buffer for propfind requests
+        UcxBuffer *rqbuf = create_basic_propfind_request();
+        
+        sstr_t remaining = sstrsubs(p, start);
+        size_t nelm = 0;
+        sstr_t *elms = sstrsplit(remaining, S("/"), &nelm);
+        DavResource *res = root;
+        ucx_buffer_puts(pbuf, res->path);
+        // iterate over all remaining path elements
+        for(int i=0;i<nelm;i++) {
+            sstr_t elm = elms[i];
+            if(elm.length > 0) {
+                //printf("elm: %.*s\n", elm.length, elm.ptr);
+                DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr);
+                
+                // if necessary add a path separator
+                if(pbuf->space[pbuf->pos-1] != '/') {
+                    if(href->space[href->pos-1] != '/') {
+                        ucx_buffer_putc(href, '/');
+                    }
+                    ucx_buffer_putc(pbuf, '/');
+                }
+                // add last path/href to the cache
+                sstr_t pp = sstrn(pbuf->space, pbuf->size);
+                sstr_t hh = sstrn(href->space, href->size);
+                dav_session_cache_path(sn, pp, hh);
+                
+                ucx_buffer_write(elm.ptr, 1, elm.length, pbuf);
+                if(child) {
+                    ucx_buffer_puts(href, util_resource_name(child->href));
+                    res = child;
+                } else {
+                    //printf("random\n");
+                    char *random_name = util_random_str();
+                    ucx_buffer_puts(href, random_name);
+                    free(random_name);
+                }
+                
+            }
+        }
+        
+        // if necessary add a path separator
+        if(p.ptr[p.length-1] == '/') {
+            if(href->space[href->pos-1] != '/') {
+                ucx_buffer_putc(href, '/');
+            }
+            ucx_buffer_putc(pbuf, '/');
+        }
+        // add the final path to the cache
+        sstr_t pp = sstrn(pbuf->space, pbuf->size);
+        sstr_t hh = sstrn(href->space, href->size);
+        dav_session_cache_path(sn, pp, hh);
+        
+        sstr_t href_str = sstrdup_a(
+                sn->mp->allocator,
+                sstrn(href->space,
+                href->size));
+        
+        // cleanup
+        dav_resource_free_all(root);
+        ucx_buffer_free(pbuf);
+        ucx_buffer_free(href);
+        
+        return href_str.ptr;
+    } else {
+        return dav_session_create_plain_href(sn, path);
+    }
+}
+
+DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name) {   
+    if(res && !dav_propfind(sn, res, rqbuf, NULL, 0)) {
+        DavResource *child = res->children;
+        while(child) {
+            if(!strcmp(child->name, name)) {
+                return child;
+            }
+            child = child->next;
+        }
+    }
+    return NULL;
+}
+
+void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href) {
+    char *elm = ucx_map_sstr_get(sn->pathcache, path);
+    if(!elm) {
+        href = sstrdup_a(sn->mp->allocator, href);
+        ucx_map_sstr_put(sn->pathcache, path, href.ptr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/session.h	Thu Jun 05 15:11:29 2014 +0200
@@ -0,0 +1,85 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef DAV_SESSION_H
+#define	DAV_SESSION_H
+
+#include "webdav.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+// initial size of the session mempool
+#define DAV_SESSION_MEMPOOL_SIZE 1024
+// initial size of the path cache map
+#define DAV_PATH_CACHE_SIZE 32
+    
+#define DAV_ENCRYPT_NAME(sn) \
+    (((sn)->flags & DAV_SESSION_ENCRYPT_NAME) == DAV_SESSION_ENCRYPT_NAME)
+    
+#define DAV_DECRYPT_NAME(sn) \
+    (((sn)->flags & DAV_SESSION_DECRYPT_NAME) == DAV_SESSION_DECRYPT_NAME)
+    
+#define DAV_ENCRYPT_CONTENT(sn) \
+    (((sn)->flags & DAV_SESSION_ENCRYPT_CONTENT) == DAV_SESSION_ENCRYPT_CONTENT)
+    
+#define DAV_DECRYPT_CONTENT(sn) \
+    (((sn)->flags & DAV_SESSION_DECRYPT_CONTENT) == DAV_SESSION_DECRYPT_CONTENT)
+
+#define DAV_IS_ENCRYPTED(sn) \
+    (DAV_ENCRYPT_NAME(sn) || DAV_ENCRYPT_CONTENT(sn))
+    
+#define DAV_CRYPTO(sn) \
+    (DAV_ENCRYPT_NAME(sn) || DAV_DECRYPT_NAME(sn) || \
+     DAV_ENCRYPT_CONTENT(sn) || DAV_DECRYPT_CONTENT(sn))
+    
+/*
+typedef struct DavPathCacheElement {
+    char *name;
+    char *encrypted_name;
+    int  exists;
+} DavPathCacheElement;
+//*/
+
+void dav_session_set_error(DavSession *sn, CURLcode c, int status);
+
+char* dav_session_create_plain_href(DavSession *sn, char *path);
+
+char* dav_session_get_href(DavSession *sn, char *path);
+
+DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name);
+
+void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* DAV_SESSION_H */
+
--- a/libidav/utils.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/utils.c	Thu Jun 05 15:11:29 2014 +0200
@@ -32,6 +32,7 @@
 #include <string.h>
 #include <ucx/string.h>
 #include <ucx/buffer.h>
+#include <ucx/utils.h>
 #include <libxml/tree.h>
 #include <curl/curl.h>
 
@@ -40,8 +41,10 @@
 #include <openssl/evp.h>
 #include <openssl/bio.h>
 #include <openssl/buffer.h>
+#include <openssl/rand.h>
 
 #include "utils.h"
+#include "crypto.h"
 #include "webdav.h"
 
 
@@ -175,14 +178,21 @@
     return url.ptr;
 }
 
-void util_set_url(DavSession *sn, char *path) {
-    if(path) {
-        char *url = util_path_to_url(sn, path);
-        curl_easy_setopt(sn->handle, CURLOPT_URL, url);
-        free(url);
-    } else {
-        curl_easy_setopt(sn->handle, CURLOPT_URL, sn->base_url);
-    }
+void util_set_url(DavSession *sn, char *href) {
+    sstr_t base = sstr(sn->base_url);
+    sstr_t href_str = sstr(href);
+    
+    char *base_path = util_url_path(sn->base_url);
+    base.length -= strlen(base_path);
+    
+    sstr_t url;
+    url.length = base.length + href_str.length;
+    url.ptr = malloc(url.length + 1);
+    url.ptr[url.length] = '\0';
+    url = sstrncat(url, 2, base, href_str);
+    
+    curl_easy_setopt(sn->handle, CURLOPT_URL, url);
+    free(url.ptr);
 }
 
 char* util_path_to_url(DavSession *sn, char *path) {
@@ -200,7 +210,6 @@
     for(int i=0;i<ntk;i++) {
         sstr_t node = tks[i];
         if(node.length > 0) {
-            // TODO: implement file name encryption
             char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
             ucx_buffer_putc(url, '/');
             ucx_buffer_write(esc, 1, strlen(esc), url);
@@ -286,6 +295,51 @@
     return out;
 }
 
+char* util_encrypt_str(DavSession *sn, char *str, char *key) {
+    DavKey *k = dav_context_get_key(sn->context, key);
+    if(!k) {
+        // TODO: session error
+        return NULL;
+    }
+    
+    char *enc_str = aes_encrypt(str, k);
+    char *ret_str = dav_session_strdup(sn, enc_str);
+    free(enc_str);
+    return ret_str;
+}
+
+char* util_decrypt_str(DavSession *sn, char *str, char *key) {
+    DavKey *k = dav_context_get_key(sn->context, key);
+    if(!k) {
+        // TODO: session error
+        return NULL;
+    }
+    
+    char *dec_str = aes_decrypt(str, k);
+    char *ret_str = dav_session_strdup(sn, dec_str);
+    free(dec_str);
+    return ret_str;
+}
+
+char* util_random_str() {
+    unsigned char *str = malloc(25);
+    str[24] = '\0';
+    
+    sstr_t t = S(
+            "01234567890"
+            "abcdefghijklmnopqrstuvwxyz"
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    const unsigned char *table = (const unsigned char*)t.ptr;
+    
+    RAND_pseudo_bytes(str, 24);
+    for(int i=0;i<24;i++) {
+        int c = str[i] % t.length;
+        str[i] = table[c];
+    }
+    
+    return (char*)str;
+}
+
 /*
  * gets a substring from 0 to the appearance of the token
  * tokens are separated by space
--- a/libidav/utils.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/utils.h	Thu Jun 05 15:11:29 2014 +0200
@@ -61,7 +61,8 @@
 char* util_url_path(char *url);
 char* util_resource_name(char *url);
 char* util_concat_path(char *url_base, char *path);
-void util_set_url(DavSession *sn, char *path);
+void util_set_url(DavSession *sn, char *href);
+
 char* util_path_to_url(DavSession *sn, char *path);
 char* util_parent_path(char *path);
 
@@ -74,6 +75,11 @@
 char* util_base64decode_len(char *in, int *outlen);
 char* util_base64encode(char *in, size_t len);
 
+char* util_encrypt_str(DavSession *sn, char *str, char *key);
+char* util_decrypt_str(DavSession *sn, char *str, char *key);
+
+char* util_random_str();
+
 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub);
 
 #ifdef	__cplusplus
--- a/libidav/webdav.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/webdav.c	Thu Jun 05 15:11:29 2014 +0200
@@ -33,13 +33,13 @@
 
 #include "utils.h"
 #include "webdav.h"
+#include "session.h"
 #include "methods.h"
 #include "davql.h"
 #include "ucx/buffer.h"
 #include "ucx/utils.h"
 
 
-
 DavContext* dav_context_new() {
     DavContext *context = malloc(sizeof(DavContext));
     if(!context) {
@@ -69,6 +69,24 @@
         return NULL;
     }
     
+    DavNamespace *idavns = malloc(sizeof(DavNamespace));
+    if(!idavns) {
+        free(davns);
+        ucx_map_free(context->namespaces);
+        free(context);
+        return NULL;
+    }
+    idavns->prefix = "idav";
+    davns->name = DAV_NS;
+    if(ucx_map_cstr_put(context->namespaces, "idav", idavns)) {
+        free(davns);
+        free(idavns);
+        ucx_map_free(context->namespaces);
+        free(context);
+        return NULL;
+    }
+    
+    
     return context;
 }
 
@@ -93,7 +111,11 @@
 }
 
 DavKey* dav_context_get_key(DavContext *context, char *name) {
-    return ucx_map_cstr_get(context->keys, name);
+    if(name) {
+        return ucx_map_cstr_get(context->keys, name);
+    } else {
+        return NULL;
+    }
 }
 
 int dav_add_namespace(DavContext *context, char *prefix, char *name) {
@@ -135,155 +157,18 @@
     *name = pname;
 }
 
-DavSession* dav_session_new(DavContext *context, char *base_url) {
-    if(!base_url) {
-        return NULL;
-    }
-    sstr_t url = sstr(base_url);
-    if(url.length == 0) {
-        return NULL;
-    }
-    DavSession *sn = malloc(sizeof(DavSession));
-    sn->mp = ucx_mempool_new(1024);
-    sn->key = NULL;
-    sn->errorstr = NULL;
-    sn->error = DAV_OK;
-    sn->flags = 0;
-    if(url.ptr[url.length - 1] == '/') {
-        sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url));
-        sn->base_url = url.ptr;
-    } else {
-        char *url_str = malloc(url.length + 2);
-        memcpy(url_str, base_url, url.length);
-        url_str[url.length]     = '/';
-        url_str[url.length + 1] = '\0';
-        sn->base_url = url_str;
-    }
-    sn->context = context;
-    sn->handle = curl_easy_init();
-    //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
-    //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
-    curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L);
-
-    // set proxy
-    DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy
-                                                  : context->http_proxy;
-    
-    if (proxy->url) {
-        curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url);
-        if (proxy->username) {
-            curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME,
-                proxy->username);
-            if (proxy->password) {
-                curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD,
-                    proxy->password);
-            } else {
-                // TODO: prompt
-            }
-        }
-        if(proxy->no_proxy) {
-            curl_easy_setopt(sn->handle, CURLOPT_NOPROXY,
-                proxy->no_proxy);
-        }
-    }
-    
-    // set url
-    curl_easy_setopt(sn->handle, CURLOPT_URL, base_url);
-    
-    context->sessions = ucx_list_append(context->sessions, sn);
-    
-    return sn;
-}
-
-DavSession* dav_session_new_auth(
-        DavContext *context,
-        char *base_url,
-        char *user,
-        char *password)
-{
-    DavSession *sn = dav_session_new(context, base_url);
-    if(!sn) {
-        return NULL;
-    }
-    dav_session_set_auth(sn, user, password);
-    return sn;
-}
-
-void dav_session_set_auth(DavSession *sn, char *user, char *password) {
-    if(user && password) {
-        size_t ulen = strlen(user);
-        size_t plen = strlen(password);
-        size_t upwdlen = ulen + plen + 2;
-        char *upwdbuf = malloc(upwdlen);
-        snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password);
-        curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf);
-        free(upwdbuf);
-    }
-}
-
-void session_set_error(DavSession *sn, CURLcode c, int status) {
-    if(status > 0) {
-        switch(status) {
-            default: sn->error = DAV_ERROR; break;
-            case 401: sn->error = DAV_UNAUTHORIZED; break;
-            case 403: sn->error = DAV_FORBIDDEN; break;
-            case 404: sn->error = DAV_NOT_FOUND; break;
-            case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break;
-            case 409: sn->error = DAV_CONFLICT; break;
-        }
-    } else {
-        sn->error = DAV_ERROR;
-    }
-    if(c != CURLE_OK) {
-        sn->errorstr = curl_easy_strerror(c);
-    } else {
-        sn->errorstr = NULL;
-    }
-}
-
-void dav_session_destroy(DavSession *sn) { 
-    // remove session from context
-    UcxList *sessions = sn->context->sessions;
-    ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL);
-    if(i > 0)  {
-        UcxList *elm = ucx_list_get(sessions, i);
-        if(elm) {
-            sn->context->sessions = ucx_list_remove(sessions, elm);
-        }
-    }
-    
-    ucx_mempool_destroy(sn->mp);
-    curl_easy_cleanup(sn->handle);
-    free(sn);
-}
-
-
-void* dav_session_malloc(DavSession *sn, size_t size) {
-    return ucx_mempool_malloc(sn->mp, size);
-}
-
-void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) {
-    return ucx_mempool_calloc(sn->mp, nelm, size);
-}
-
-void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) {
-    return ucx_mempool_realloc(sn->mp, ptr, size);
-}
-
-void  dav_session_free(DavSession *sn, void *ptr) {
-    ucx_mempool_free(sn->mp, ptr);
-}
-
 
 DavResource* dav_get(DavSession *sn, char *path, char *properties) {  
     CURL *handle = sn->handle;
-    util_set_url(sn, path);
+    char *href = dav_session_get_href(sn, path);
+    util_set_url(sn, href);
+    dav_session_free(sn, href);
     
     UcxList *proplist = NULL;
     if(properties) {
         proplist = parse_properties_string(sn->context, sstr(properties));
     }
-    UcxBuffer *rqbuf = create_propfind_request(proplist);
+    UcxBuffer *rqbuf = create_propfind_request(sn, proplist);
     UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
     
     //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
@@ -298,30 +183,46 @@
         resource = parse_propfind_response(sn, NULL, rpbuf, NULL, 0);
         sn->error = DAV_OK;
     } else  {
-        session_set_error(sn, ret, status);
+        dav_session_set_error(sn, ret, status);
     }
+    
+    ucx_buffer_free(rqbuf);
+    ucx_buffer_free(rpbuf);
+    
     return resource;
 }
 
-DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path, DavQOp *cond, size_t len) {
+int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, DavQOp *cond, size_t len) {
+    // clean resource properties
+    DavResourceData *data = root->data;
+    if(data->properties->count > 0) {
+        UcxKey key;
+        void *value;
+        UcxMapIterator i = ucx_map_iterator(data->properties);
+        UCX_MAP_FOREACH(key, value, i) {
+            ucx_map_remove(data->properties, key);
+        }
+    }
+    
     CURL *handle = sn->handle;
-    util_set_url(sn, path);
-    
+    util_set_url(sn, dav_resource_get_href(root));
+     
     UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
     DavResource *resource = root;
     CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
     int status = 0;
+    int error = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && status == 207) {
         //printf("response\n%s\n", rpbuf->space); 
         resource = parse_propfind_response(sn, resource, rpbuf, cond, len);
         sn->error = DAV_OK;
     } else  {
-        session_set_error(sn, ret, status);
-        resource = NULL;
+        dav_session_set_error(sn, ret, status);
+        error = 1;
     }
     ucx_buffer_free(rpbuf);
-    return resource;
+    return error;
 }
 
 UcxList* propfind_stack_push(UcxList *stack, DavResource *children) {
@@ -337,27 +238,36 @@
 DavResource* dav_query_get(DavSession *sn, DavGetQuery *query) {
     char *path;
     int depth = 0;
+    /*
     if(parse_path_query(query->from, &path, &depth)) {
         sn->error = DAV_ERROR;
         return NULL;
     }
+    */
+    path = sstrdup(query->from).ptr;
     
     sstr_t ps = query->properties;
     UcxBuffer *rqbuf;
     if(!sstrcmp(ps, S("*"))) {
         rqbuf = create_allprop_propfind_request();
     } else if(!sstrcmp(ps, S("-"))) {
-        rqbuf = create_propfind_request(NULL);
+        rqbuf = create_propfind_request(sn, NULL);
     } else {
         UcxList *proplist = parse_properties_string(sn->context, ps);
-        rqbuf = create_propfind_request(proplist);
+        rqbuf = create_propfind_request(sn, proplist);
     }
     
     //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
     //printf("\n");
     
-    DavResource *resource = dav_propfind(sn, NULL, rqbuf, path, query->condition, query->condlen);
+    DavResource *resource = dav_resource_new(sn, path);
     free(path);
+    if(dav_propfind(sn, resource, rqbuf, query->condition, query->condlen)) {
+        dav_resource_free(resource);
+        resource = NULL;
+    }
+    ucx_buffer_free(rqbuf);
+    
     int error = 0;
     if(resource && depth == -1) {
         UcxList *stack = NULL; // stack with DavResource* elements
@@ -366,8 +276,7 @@
             DavResource *sr = stack->data; // get first element from the stack
             stack = ucx_list_remove(stack, stack); // remove first element
             // do propfind request for sr
-            sr = dav_propfind(sn, sr, rqbuf, sr->path, query->condition, query->condlen);
-            if(!sr) {
+            if(dav_propfind(sn, sr, rqbuf, query->condition, query->condlen)) {
                 error = 1;
                 printf("subrequest failed\n");
                 break;
@@ -420,6 +329,10 @@
             free_get_query(q.command_data);
             break;
         }
+        case DAV_QUERY_ERROR: {
+            // TODO
+            break;
+        }
     }
     return res;
 }
--- a/libidav/webdav.h	Tue Mar 18 13:59:02 2014 +0100
+++ b/libidav/webdav.h	Thu Jun 05 15:11:29 2014 +0200
@@ -67,10 +67,16 @@
 
 typedef enum DavError DavError;
 
-#define DAV_SESSION_ENCRYPT_FILE     0x0001
-#define DAV_SESSION_ENCRYPT_NAME     0x0002
-#define DAV_SESSION_DECRYPT_PATH     0x0004 
+#define DAV_SESSION_ENCRYPT_CONTENT     0x0001
+#define DAV_SESSION_ENCRYPT_NAME        0x0002
+#define DAV_SESSION_DECRYPT_CONTENT     0x0004
+#define DAV_SESSION_DECRYPT_NAME        0x0008
 
+#define DAV_SESSION_CONTENT_ENCRYPTION  0x000a
+#define DAV_SESSION_FULL_ENCRYPTION     0x000f
+
+
+#define DAV_NS "http://www.uap-core.de/"
 
 struct DavNamespace {
     char *prefix;
@@ -99,6 +105,7 @@
     CURL          *handle;
     char          *base_url;
     UcxMempool    *mp;
+    UcxMap        *pathcache;
     DavKey        *key;
     uint32_t      flags;
     DavError      error;
@@ -153,14 +160,17 @@
         char *user,
         char *password);
 void dav_session_set_auth(DavSession *sn, char *user, char *password);
+void dav_session_set_flags(DavSession *sn, uint32_t flags);
+uint32_t dav_session_get_flags(DavSession *sn);
+void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags);
 
-void session_set_error(DavSession *sn, CURLcode c, int status);
 void dav_session_destroy(DavSession *sn);
 
 void* dav_session_malloc(DavSession *sn, size_t size);
 void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size);
 void* dav_session_realloc(DavSession *sn, void *ptr, size_t size);
 void  dav_session_free(DavSession *sn, void *ptr);
+char* dav_session_strdup(DavSession *sn, char *str);
 
 
 DavResource* dav_get(DavSession *sn, char *path, char *properties);
@@ -184,6 +194,11 @@
 DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name);
 DavResource* dav_resource_new_href(DavSession *sn, char *href);
 
+void dav_resource_free(DavResource *res);
+void dav_resource_free_all(DavResource *res);
+
+char* dav_resource_get_href(DavResource *resource);
+
 DavResource* dav_create_child(DavResource *parent, char *name);
 int dav_delete(DavResource *res);
 int dav_create(DavResource *res);
@@ -203,6 +218,9 @@
 int dav_store(DavResource *res);
 int dav_get_content(DavResource *res, void *stream, dav_write_func write_func);
 
+// private
+int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, DavQOp *cond, size_t len);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/osx.mk	Tue Mar 18 13:59:02 2014 +0100
+++ b/osx.mk	Thu Jun 05 15:11:29 2014 +0200
@@ -26,12 +26,12 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-CC = gcc
-LD = gcc
+CC = cc
+LD = cc
 AR = ar
 RM = rm
 
-CFLAGS  = -std=gnu99 -g -c
+CFLAGS  = -g -c -Wno-deprecated -Wno-format
 COFLAGS = -o
 LDFLAGS = 
 LOFLAGS = -o
@@ -42,5 +42,5 @@
 LIB_EXT = a
 APP_EXT =
 
-DAV_CFLAGS = -I/usr/include/libxml2
+DAV_CFLAGS = `xml2-config --cflags`
 DAV_LDFLAGS = -lcurl -lxml2 -lssl -lcrypto
--- a/ucx/string.c	Tue Mar 18 13:59:02 2014 +0100
+++ b/ucx/string.c	Thu Jun 05 15:11:29 2014 +0200
@@ -97,7 +97,10 @@
 sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
     sstr_t new_sstr;
     if (start >= s.length) {
-        return s;
+        //return s;
+        new_sstr.ptr = NULL;
+        new_sstr.length = 0;
+        return new_sstr;
     }
     if (length > s.length-start) {
         length = s.length-start;
@@ -139,7 +142,7 @@
 
 sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t s, sstr_t d, size_t *n) {
     if (s.length == 0 || d.length == 0) {
-        *n = -1;
+        *n = 0;
         return NULL;
     }
 

mercurial