# HG changeset patch # User Olaf Wintermann # Date 1457972313 -3600 # Node ID 1fb26aca5093e4bd4cc9e0f103674daad7c9422c # Parent de23f8881e9fd6a95cf58f1005ef99ba83391187 added API for webdav locking no support for lock timeouts yet diff -r de23f8881e9f -r 1fb26aca5093 libidav/methods.c --- 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); diff -r de23f8881e9f -r 1fb26aca5093 libidav/methods.h --- 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); diff -r de23f8881e9f -r 1fb26aca5093 libidav/resource.c --- 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); diff -r de23f8881e9f -r 1fb26aca5093 libidav/session.c --- 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); + } +} diff -r de23f8881e9f -r 1fb26aca5093 libidav/session.h --- 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 diff -r de23f8881e9f -r 1fb26aca5093 libidav/webdav.h --- 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);