added API for webdav locking

Mon, 14 Mar 2016 17:18:33 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 14 Mar 2016 17:18:33 +0100
changeset 208
1fb26aca5093
parent 207
de23f8881e9f
child 209
d24761f1e07c

added API for webdav locking

no support for lock timeouts yet

libidav/methods.c file | annotate | diff | comparison | revisions
libidav/methods.h file | annotate | diff | comparison | revisions
libidav/resource.c file | annotate | diff | comparison | revisions
libidav/session.c file | annotate | diff | comparison | revisions
libidav/session.h file | annotate | diff | comparison | revisions
libidav/webdav.h file | annotate | diff | comparison | revisions
--- a/libidav/methods.c	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/methods.c	Mon Mar 14 17:18:33 2016 +0100
@@ -707,13 +707,22 @@
 
 CURLcode do_proppatch_request(
         CURL *handle,
+        char *lock,
         UcxBuffer *request,
         UcxBuffer *response)
-{
+{  
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
     
     struct curl_slist *headers = NULL;
-    headers = curl_slist_append(headers, "Content-Type: text/xml");
+    headers = curl_slist_append(headers, "Content-Type: text/xml"); 
+    if(lock) {
+        char *url = NULL;
+        curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
+        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        headers = curl_slist_append(headers, ltheader);
+        free(ltheader);
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    }
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
@@ -891,13 +900,20 @@
     return s*n;
 }
 
-CURLcode do_put_request(CURL *handle, void *data, dav_read_func read_func, size_t length) {
+CURLcode do_put_request(CURL *handle, char *lock, void *data, dav_read_func read_func, size_t length) {
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
-    curl_easy_setopt(handle, CURLOPT_PUT, 1L);  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
     
     // clear headers
     struct curl_slist *headers = NULL;
+    if(lock) {
+        char *url = NULL;
+        curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
+        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        headers = curl_slist_append(headers, ltheader);
+        free(ltheader);
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    }
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
     UcxBuffer *buf = NULL;
@@ -929,24 +945,42 @@
     return ret;
 }
 
-CURLcode do_delete_request(CURL *handle, UcxBuffer *response) {
+CURLcode do_delete_request(CURL *handle, char *lock, UcxBuffer *response) { 
     struct curl_slist *headers = NULL;
-    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    if(lock) {
+        char *url = NULL;
+        curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
+        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        headers = curl_slist_append(headers, ltheader);
+        free(ltheader);
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    } else {
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
+    }
     
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
-    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
     
     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     
     CURLcode ret = curl_easy_perform(handle);
+    curl_slist_free_all(headers);
     return ret;
 }
 
-CURLcode do_mkcol_request(CURL *handle) { 
+CURLcode do_mkcol_request(CURL *handle, char *lock) {
     struct curl_slist *headers = NULL;
-    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    if(lock) {
+        char *url = NULL;
+        curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
+        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        headers = curl_slist_append(headers, ltheader);
+        free(ltheader);
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
+    } else {
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
+    }
     
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL");
     curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
@@ -956,6 +990,7 @@
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
     
     CURLcode ret = curl_easy_perform(handle);
+    curl_slist_free_all(headers);
     return ret;
 }
 
@@ -978,7 +1013,7 @@
 }
 
 
-CURLcode do_copy_move_request(CURL *handle, char *dest, _Bool copy, _Bool override) {
+CURLcode do_copy_move_request(CURL *handle, char *dest, _Bool copy, _Bool override) { 
     if(copy) {
         curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "COPY");
     } else {
@@ -1055,6 +1090,11 @@
                     continue;
                 }
                 
+                if(xstreq(node->name, "activelock")) {
+                    node = node->children;
+                    continue;
+                }
+                
                 if(lockdiscovery) {
                     if(xstreq(node->name, "timeout")) {
                         timeout = util_xml_get_text(node);
@@ -1084,7 +1124,7 @@
     return ret;
 }
 
-CURLcode do_lock_request(CURL *handle, UcxBuffer *request, UcxBuffer *response) {
+CURLcode do_lock_request(CURL *handle, UcxBuffer *request, UcxBuffer *response) { 
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK");  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
     request->pos = 0;
@@ -1106,10 +1146,13 @@
     return ret;
 }
 
-CURLcode do_unlock_request(CURL *handle, char *locktoken) {
+CURLcode do_unlock_request(CURL *handle, char *locktoken) {   
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "UNLOCK");
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
     
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
+    
     // set lock-token header
     sstr_t ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken);
     struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr);
--- a/libidav/methods.h	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/methods.h	Mon Mar 14 17:18:33 2016 +0100
@@ -67,11 +67,13 @@
 
 CURLcode do_proppatch_request(
         CURL *handle,
+        char *lock,
         UcxBuffer *request,
         UcxBuffer *response);
 
 CURLcode do_put_request(
         CURL *handle,
+        char *lock,
         void *data,
         dav_read_func read_func,
         size_t length);
@@ -96,9 +98,9 @@
 UcxBuffer* create_proppatch_request(DavResourceData *data);
 UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name, char *hash);
 
-CURLcode do_delete_request(CURL *handle, UcxBuffer *response);
+CURLcode do_delete_request(CURL *handle, char *lock, UcxBuffer *response);
 
-CURLcode do_mkcol_request(CURL *handle);
+CURLcode do_mkcol_request(CURL *handle, char *lock);
 
 CURLcode do_head_request(CURL *handle);
 
--- a/libidav/resource.c	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/resource.c	Mon Mar 14 17:18:33 2016 +0100
@@ -557,7 +557,10 @@
     DavResourceData *data = res->data;
     
     util_set_url(sn, dav_resource_get_href(res));
-      
+    
+    DavLock *lock = dav_get_lock(sn, res->path);
+    char *locktoken = lock ? lock->token : NULL;
+    
     // store content
     if(data->content) {
         int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key;
@@ -575,10 +578,11 @@
                         buf,
                         (dav_read_func)ucx_buffer_read);
             }
-            
+              
             // put resource
             ret = do_put_request(
                     sn->handle,
+                    locktoken,
                     enc,
                     (dav_read_func)aes_read,
                     0);
@@ -604,6 +608,7 @@
         } else {
             ret = do_put_request(
                     sn->handle,
+                    locktoken,
                     data->content,
                     data->read,
                     data->length);
@@ -632,7 +637,7 @@
         UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
         //printf("request:\n%.*s\n\n", request->pos, request->space);
 
-        CURLcode ret = do_proppatch_request(sn->handle, request, response);
+        CURLcode ret = do_proppatch_request(sn->handle, locktoken, request, response);
         long status = 0;
         curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
         if(ret == CURLE_OK && status == 207) {
@@ -745,8 +750,11 @@
     CURL *handle = res->session->handle;
     util_set_url(res->session, dav_resource_get_href(res));
     
+    DavLock *lock = dav_get_lock(res->session, res->path);
+    char *locktoken = lock ? lock->token : NULL;
+    
     UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    CURLcode ret = do_delete_request(handle, response);
+    CURLcode ret = do_delete_request(handle, locktoken, response);
     long status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
@@ -765,6 +773,10 @@
 static int create_ancestors(DavSession *sn, char *href, char *path) {
     CURL *handle = sn->handle;
     CURLcode code;
+    
+    DavLock *lock = dav_get_lock(sn, path);
+    char *locktoken = lock ? lock->token : NULL;
+    
     long status = 0;
     int ret = 0;
     
@@ -777,7 +789,7 @@
     
     for(int i=0;i<2;i++) {
         util_set_url(sn, h);
-        code = do_mkcol_request(handle);
+        code = do_mkcol_request(handle, locktoken);
         curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
         if(status == 201) {
             // resource successfully created
@@ -816,11 +828,14 @@
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(res));
     
+    DavLock *lock = dav_get_lock(res->session, res->path);
+    char *locktoken = lock ? lock->token : NULL;
+    
     CURLcode code;
     if(res->iscollection) {
-        code = do_mkcol_request(handle);
+        code = do_mkcol_request(handle, locktoken);
     } else {
-        code = do_put_request(handle, "", NULL, 0); 
+        code = do_put_request(handle, locktoken, "", NULL, 0); 
     }
     long s = 0;
     curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &s);
@@ -925,7 +940,7 @@
     return dav_cp_mv_url(res, url, false, override);
 }
 
-char* dav_lock(DavResource *res) {
+int dav_lock(DavResource *res) {
     DavSession *sn = res->session;
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(res));
@@ -944,26 +959,50 @@
         LockDiscovery lock;
         if(parse_lock_response(sn, response, &lock)) {
             sn->error = DAV_ERROR;
-            return NULL;
+            return -1;
         }
         
+        DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout);
+        free(lock.locktoken);
         free(lock.timeout);
-        return lock.locktoken;
+        
+        int r = 0;
+        if(res->iscollection) {
+            r = dav_add_collection_lock(sn, res->path, l);
+        } else {
+            r = dav_add_resource_lock(sn, res->path, l);
+        }
+        
+        if(r == 0) {
+            return 0;
+        } else {
+            (void)dav_unlock(res);
+            sn->error = DAV_ERROR;
+            dav_destroy_lock(sn, l);
+            return -1;
+        }
     } else {
         dav_session_set_error(sn, ret, status);
-        return NULL;
+        return -1;
     }
 }
 
-int dav_unlock(DavResource *res, char *locktoken) {
+int dav_unlock(DavResource *res) {
     DavSession *sn = res->session;
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(res));
     
-    CURLcode ret = do_unlock_request(handle, locktoken);
+    DavLock *lock = dav_get_lock(res->session, res->path);
+    if(!lock) {
+        sn->error = DAV_ERROR;
+        return -1;
+    }
+    
+    CURLcode ret = do_unlock_request(handle, lock->token);
     long status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        dav_remove_lock(sn, res->path, lock);
         return 0;
     } else {
         dav_session_set_error(sn, ret, status);
@@ -983,7 +1022,8 @@
     UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
     
     util_set_url(sn, href);
-    CURLcode ret = do_proppatch_request(sn->handle, request, response);
+    // TODO: lock
+    CURLcode ret = do_proppatch_request(sn->handle, NULL, request, response);
     ucx_buffer_free(request);
     long status = 0;
     curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
--- a/libidav/session.c	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/session.c	Mon Mar 14 17:18:33 2016 +0100
@@ -65,6 +65,12 @@
     }
     sn->handle = curl_easy_init();
     curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L);
+    
+    // create lock manager
+    DavLockManager *locks = ucx_mempool_malloc(sn->mp, sizeof(DavLockManager));
+    locks->resource_locks = ucx_map_new_a(sn->mp->allocator, 16);
+    locks->collection_locks = NULL;
+    sn->locks = locks;
 
     // set proxy
     DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy
@@ -359,3 +365,126 @@
         ucx_map_sstr_put(sn->pathcache, path, href.ptr);
     }
 }
+
+
+DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout) {
+    DavLock *lock = dav_session_malloc(sn, sizeof(DavLock));
+    lock->path = NULL;
+    lock->token = dav_session_strdup(sn, token);
+    
+    // TODO: timeout
+    
+    return lock;
+}
+
+void dav_destroy_lock(DavSession *sn, DavLock *lock) {
+    dav_session_free(sn, lock->token);
+    if(lock->path) {
+        dav_session_free(sn, lock->path);
+    }
+    dav_session_free(sn, lock);
+}
+
+int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock) {
+    DavLockManager *locks = sn->locks;
+    if(ucx_map_cstr_get(locks->resource_locks, path)) {
+        return -1;
+    }
+    
+    ucx_map_cstr_put(locks->resource_locks, path, lock);
+    return 0;
+}
+
+static void insert_lock(DavSession *sn, UcxList *elm, UcxList *newelm) {
+    UcxList *next = elm->next;
+    if(next) {
+        next->prev = newelm;
+        newelm->next = next;
+    }
+    newelm->prev = elm;
+    elm->next = newelm;
+}
+
+int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock) {
+    DavLockManager *locks = sn->locks;
+    if(!locks->collection_locks) {
+        locks->collection_locks = ucx_list_append_a(
+                sn->mp->allocator,
+                NULL,
+                lock);
+        lock->path = dav_session_strdup(sn, path);
+        return 0;
+    }
+    
+    UcxList *elm = locks->collection_locks;
+    for(;;) {
+        DavLock *l = elm->data;
+        int cmp = strcmp(path, l->path);
+        if(cmp > 0) {
+            UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock);
+            lock->path = dav_session_strdup(sn, path);
+            insert_lock(sn, elm, newelm);
+        } else if(cmp == 0) {
+            return -1;
+        }
+        
+        if(elm->next) {
+            elm = elm->next;
+        } else {
+            UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock);
+            lock->path = dav_session_strdup(sn, path);
+            ucx_list_concat(elm, newelm);
+            break;
+        }
+    }
+    
+    return 0;
+}
+
+DavLock* dav_get_lock(DavSession *sn, char *path) {
+    DavLockManager *locks = sn->locks;
+    
+    DavLock *lock = ucx_map_cstr_get(locks->resource_locks, path);
+    if(lock) {
+        return lock;
+    }
+    
+    sstr_t p = sstr(path);
+    UCX_FOREACH(elm, locks->collection_locks) {
+        DavLock *cl = elm->data;
+        int cmd = strcmp(path, cl->path);
+        if(cmd == 0) {
+            return cl;
+        } else if(sstrprefix(p, sstr(cl->path)))  {
+            return cl;
+        } else if(cmd > 0) {
+            break;
+        }
+    }
+    
+    return NULL;
+}
+
+void dav_remove_lock(DavSession *sn, char *path, DavLock *lock) {
+    DavLockManager *locks = sn->locks;
+    
+    if(ucx_map_cstr_remove(locks->resource_locks, path)) {
+        return;
+    }
+    
+    UcxList *rm = NULL;
+    UCX_FOREACH(elm, locks->collection_locks) {
+        DavLock *cl = elm->data;
+        if(cl == lock) {
+            rm = elm;
+            break;
+        }
+    }
+    
+    if(rm) {
+        locks->collection_locks = ucx_list_remove_a(
+                sn->mp->allocator,
+                locks->collection_locks,
+                rm);
+    }
+}
--- a/libidav/session.h	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/session.h	Mon Mar 14 17:18:33 2016 +0100
@@ -65,7 +65,18 @@
     char *encrypted_name;
     int  exists;
 } DavPathCacheElement;
-//*/
+*/
+    
+typedef struct DavLock {
+    char *path;
+    char *token;
+    
+} DavLock;
+
+typedef struct DavLockManager {
+    UcxMap  *resource_locks;
+    UcxList *collection_locks;
+} DavLockManager;
 
 void dav_session_set_error(DavSession *sn, CURLcode c, int status);
 
@@ -77,6 +88,16 @@
 
 void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href);
 
+
+DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout);
+void dav_destroy_lock(DavSession *sn, DavLock *lock);
+
+int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock);
+int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock);
+
+DavLock* dav_get_lock(DavSession *sn, char *path);
+void dav_remove_lock(DavSession *sn, char *path, DavLock *lock);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/libidav/webdav.h	Mon Mar 14 11:54:55 2016 +0100
+++ b/libidav/webdav.h	Mon Mar 14 17:18:33 2016 +0100
@@ -42,6 +42,12 @@
 #endif
 
 typedef int DavBool;
+#ifndef TRUE
+#define TRUE 0
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
     
 typedef struct DavContext    DavContext;
 typedef struct DavProxy      DavProxy;
@@ -117,6 +123,7 @@
     UcxMempool    *mp;
     UcxMap        *pathcache;
     DavKey        *key;
+    void          *locks;
     uint32_t      flags;
     DavError      error;
     const char    *errorstr;
@@ -232,8 +239,8 @@
 int dav_copyto(DavResource *res, char *url, DavBool override);
 int dav_moveto(DavResource *res, char *url, DavBool override);
 
-char* dav_lock(DavResource *res);
-int dav_unlock(DavResource *res, char *locktoken);
+int dav_lock(DavResource *res);
+int dav_unlock(DavResource *res);
 
 char* dav_get_property(DavResource *res, char *name);
 char* dav_get_property_ns(DavResource *res, char *ns, char *name);

mercurial