Thu, 05 Jun 2014 15:11:29 +0200
added file name encryption
--- 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; }