diff -r 6518b035a9df -r 03076907b58a libidav/resource.c --- 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 #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; + } +}