libidav/resource.c

changeset 43
03076907b58a
parent 42
6518b035a9df
child 70
88092b88ec00
--- 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;
+    }
+}

mercurial