Thu, 21 Dec 2017 19:48:27 +0100
davql: allow ANYWHERE keyword in SELECT statements
This may seem pointless, but users might want to be explicit about this and the grammar is more consistent.
This commit also adds some no-ops to the functions body of the SET parser, because some day the grammar might allow more clauses after the WHERE clause.
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 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 <stdbool.h> #include <libxml/tree.h> #include "utils.h" #include "session.h" #include "methods.h" #include "crypto.h" #include "ucx/buffer.h" #include "ucx/utils.h" #include "resource.h" #include "xml.h" #include "davqlexec.h" #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) DavResource* dav_resource_new(DavSession *sn, char *path) { //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); char *href = dav_session_create_plain_href(sn, path); DavResource *res = dav_resource_new_full(sn, parent->path, name, href); free(path); return res; } DavResource* dav_resource_new_href(DavSession *sn, char *href) { DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource)); res->session = sn; // set name, path and href resource_set_info(res, href); // initialize resource data res->data = resource_data_new(sn); return res; } DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href) { sstr_t n = sstr(name); // the name must not contain path separators if(n.length > 0) { for(int i=0;i<n.length-1;i++) { char c = n.ptr[i]; if(c == '/' || c == '\\') { n = sstr(util_resource_name(href)); break; } } } // remove trailing '/' if(n.length > 0 && n.ptr[n.length-1] == '/') { n.length--; } DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource)); res->session = sn; // set name, path and href res->name = sstrdup_a(sn->mp->allocator, n).ptr; char *path = util_concat_path(parent_path, name); res->path = dav_session_strdup(sn, 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)); } free(path); return res; } void resource_free_properties(DavSession *sn, UcxMap *properties) { UcxMapIterator i = ucx_map_iterator(properties); DavXmlNode *node; UCX_MAP_FOREACH(key, node, i) { // TODO: free everything dav_session_free(sn, node); } ucx_map_free(properties); } 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; resource_free_properties(sn, 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); } 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); sstr_t name = sstr(util_resource_name(href_str)); sstr_t href = sstr(href_str); sstr_t base_href = sstr(util_url_path(res->session->base_url)); sstr_t path = sstrsubs(href, base_href.length - 1); UcxAllocator *a = res->session->mp->allocator; CURL *handle = res->session->handle; int nlen = 0; char *uname = curl_easy_unescape(handle, name.ptr, name.length , &nlen); int plen = 0; char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen); res->name = sstrdup_a(a, sstrn(uname, nlen)).ptr; res->href = sstrdup_a(a, href).ptr; res->path = sstrdup_a(a, sstrn(upath, plen)).ptr; curl_free(uname); curl_free(upath); } DavResourceData* resource_data_new(DavSession *sn) { DavResourceData *data = ucx_mempool_malloc( sn->mp, sizeof(DavResourceData)); if(!data) { return NULL; } data->properties = ucx_map_new_a(sn->mp->allocator, 32); data->set = NULL; data->remove = NULL; data->content = NULL; data->read = NULL; data->length = 0; 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, xmlNode *val) { if(!val) { return; } UcxKey key = dav_property_key(ns, name); DavXmlNode *v = dav_convert_xml(res->session, val); ucx_map_put(((DavResourceData*)res->data)->properties, key, v); free(key.data); } void resource_add_string_property(DavResource *res, char *ns, char *name, char *val) { if(!val) { return; } UcxKey key = dav_property_key(ns, name); DavXmlNode *v = dav_text_node(res->session, val); ucx_map_put(((DavResourceData*)res->data)->properties, key, v); free(key.data); } DavXmlNode* resource_get_property(DavResource *res, char *ns, char *name) { UcxKey key = dav_property_key(ns, name); DavXmlNode *property = resource_get_property_k(res, key); free(key.data); return property; } DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key) { DavResourceData *data = (DavResourceData*)res->data; return ucx_map_get(data->properties, key); } UcxKey dav_property_key(char *ns, char *name) { return dav_property_key_a(ucx_default_allocator(), ns, name); } UcxKey dav_property_key_a(UcxAllocator *a, char *ns, char *name) { sstr_t ns_str = sstr(ns); sstr_t name_str = sstr(name); sstr_t key; key = sstrcat_a(a, 4, ns_str, S("\0"), name_str, S("\0")); return ucx_key(key.ptr, key.length); } void resource_add_child(DavResource *parent, DavResource *child) { child->next = NULL; if(parent->children) { DavResource *last = parent->children; while(last->next) { last = last->next; } last->next = child; child->prev = last; } else { child->prev = NULL; parent->children = child; } child->parent = parent; } static int resource_cmp(DavResource *res1, DavResource *res2, DavOrderCriterion *cr) { if(!(res1 && res2)) { return 0; } int ret; if(cr->type == 0) { switch(cr->column.resprop) { case DAVQL_RES_NAME: { ret = strcmp(res1->name, res2->name); break; } case DAVQL_RES_PATH: { ret = strcmp(res1->path, res2->path); break; } case DAVQL_RES_HREF: { ret = strcmp(res1->href, res2->href); break; } case DAVQL_RES_CONTENTLENGTH: { int c = res1->contentlength == res2->contentlength; ret = c ? 0 : (res1->contentlength < res2->contentlength?-1:1); break; } case DAVQL_RES_CONTENTTYPE: { ret = strcmp(res1->contenttype, res2->contenttype); break; } case DAVQL_RES_CREATIONDATE: { int c = res1->creationdate == res2->creationdate; ret = c ? 0 : (res1->creationdate < res2->creationdate?-1:1); break; } case DAVQL_RES_LASTMODIFIED: { int c = res1->lastmodified == res2->lastmodified; ret = c ? 0 : (res1->lastmodified < res2->lastmodified?-1:1); break; } case DAVQL_RES_ISCOLLECTION: { int c = res1->iscollection == res2->iscollection; ret = c ? 0 : (res1->iscollection < res2->iscollection?-1:1); break; } default: ret = 0; } } else if(cr->type == 1) { DavXmlNode *xvalue1 = resource_get_property_k(res1, cr->column.property); DavXmlNode *xvalue2 = resource_get_property_k(res2, cr->column.property); char *value1 = dav_xml_getstring(xvalue1); char *value2 = dav_xml_getstring(xvalue2); if(!value1) { ret = value2 ? -1 : 0; } else if(!value2) { ret = value1 ? 1 : 0; } else { ret = strcmp(value1, value2); } } else { return 0; } return cr->descending ? -ret : ret; } void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr) { if(!ordercr) { resource_add_child(parent, child); return; } child->parent = parent; if(!parent->children) { child->next = NULL; child->prev = NULL; parent->children = child; } else { DavResource *resource = parent->children; while(resource) { int r = 0; UCX_FOREACH(elm, ordercr) { DavOrderCriterion *cr = elm->data; r = resource_cmp(child, resource, cr); if(r != 0) { break; } } if(r < 0) { // insert child before resource child->prev = resource->prev; child->next = resource; if(resource->prev) { resource->prev->next = child; } else { parent->children = child; } resource->prev = child; break; } if(!resource->next) { // append child child->prev = resource; child->next = NULL; resource->next = child; break; } else { resource = resource->next; } } } } char* dav_get_string_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); if(!pns || !pname) { return NULL; } return dav_get_string_property_ns(res, pns, pname); } char* dav_get_string_property_ns(DavResource *res, char *ns, char *name) { DavXmlNode *prop = dav_get_property_ns(res, ns, name); if(!prop) { return NULL; } return dav_xml_getstring(prop); } DavXmlNode* dav_get_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); if(!pns || !pname) { return NULL; } return dav_get_property_ns(res, pns, pname); } DavXmlNode* dav_get_property_ns(DavResource *res, char *ns, char *name) { if(!ns || !name) { return NULL; } DavXmlNode *property = resource_get_property(res, ns, name); DavResourceData *data = res->data; // resource_get_property only returns persistent properties // check the remove and set list if(property) { // if the property is in the remove list, we return NULL UCX_FOREACH(elm, data->remove) { DavProperty *p = elm->data; if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return NULL; } } } // the set list contains property updates // we return an updated property if possible UCX_FOREACH(elm, data->set) { DavProperty *p = elm->data; if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return p->value; // TODO: fix } } // no property update return property; } void dav_set_property(DavResource *res, char *name, char *value) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); dav_set_property_ns(res, pns, pname, value); } void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { UcxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = dav_session_malloc( res->session, sizeof(DavProperty)); property->name = dav_session_strdup(res->session, name); property->value = dav_text_node(res->session, value); DavNamespace *namespace = dav_session_malloc( res->session, sizeof(DavNamespace)); namespace->prefix = NULL; namespace->name = dav_session_strdup(res->session, ns); property->ns = namespace; data->set = ucx_list_append_a(a, data->set, property); } void dav_remove_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); dav_remove_property_ns(res, pns, pname); } void dav_remove_property_ns(DavResource *res, char *ns, char *name) { DavResourceData *data = res->data; UcxAllocator *a = res->session->mp->allocator; DavProperty *property = dav_session_malloc( res->session, sizeof(DavProperty)); property->name = sstrdup_a(a, sstr(name)).ptr; property->value = NULL; DavNamespace *namespace = dav_session_malloc( res->session, sizeof(DavNamespace)); namespace->prefix = NULL; namespace->name = sstrdup_a(a, sstr(ns)).ptr; property->ns = namespace; data->remove = ucx_list_append_a(a, data->remove, property); } static int compare_propname(const void *a, const void *b) { const DavPropName *p1 = a; const DavPropName *p2 = b; int result = strcmp(p1->ns, p2->ns); if(result) { return result; } else { return strcmp(p1->name, p2->name); } } DavPropName* dav_get_property_names(DavResource *res, size_t *count) { DavResourceData *data = res->data; *count = data->properties->count; DavPropName *names = dav_session_calloc( res->session, *count, sizeof(DavPropName)); UcxMapIterator i = ucx_map_iterator(data->properties); void *value; int j = 0; UCX_MAP_FOREACH(key, value, i) { DavPropName *name = &names[j]; // the map key is namespace + '\0' + name name->ns = key.data; for(int k=0;j<key.len;k++) { if(((char*)key.data)[k] == '\0') { name->name = (char*)key.data + k + 1; break; } } j++; } qsort(names, *count, sizeof(DavPropName), compare_propname); return names; } void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { 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 = dav_session_malloc(sn, length); memcpy(data->content, content, length); data->read = NULL; data->length = length; } void dav_set_content_length(DavResource *res, size_t length) { DavResourceData *data = res->data; data->length = length; } int dav_load(DavResource *res) { UcxBuffer *rqbuf = create_allprop_propfind_request(); int ret = dav_propfind(res->session, res, rqbuf); ucx_buffer_free(rqbuf); return ret; } int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) { UcxMempool *mp = ucx_mempool_new(64); UcxList *proplist = NULL; for(size_t i=0;i<numprop;i++) { DavProperty *p = ucx_mempool_malloc(mp, sizeof(DavProperty)); p->name = properties[i].name; p->ns = ucx_mempool_malloc(mp, sizeof(DavNamespace)); p->ns->name = properties[i].ns; if(!strcmp(properties[i].ns, "DAV:")) { p->ns->prefix = "D"; } else { p->ns->prefix = ucx_asprintf(mp->allocator, "x%d", i).ptr; } p->value = NULL; proplist = ucx_list_append_a(mp->allocator, proplist, p); } UcxBuffer *rqbuf = create_propfind_request(res->session, proplist); int ret = dav_propfind(res->session, res, rqbuf); ucx_buffer_free(rqbuf); ucx_mempool_destroy(mp); return ret; } int dav_store(DavResource *res) { DavSession *sn = res->session; 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; 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); } // put resource ret = do_put_request( sn, locktoken, TRUE, enc, (dav_read_func)aes_read, 0); // get sha256 hash unsigned char sha[DAV_SHA256_DIGEST_LENGTH]; dav_get_hash(&enc->sha256, sha); char *enc_hash = aes_encrypt((char*)sha, DAV_SHA256_DIGEST_LENGTH, sn->key); 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, enc_hash)) { free(enc_hash); return 1; } resource_add_string_property(res, DAV_NS, "crypto-hash", enc_hash); free(enc_hash); } else { ret = do_put_request( sn, locktoken, TRUE, data->content, data->read, data->length); } long status = 0; curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { res->session->error = 0; // cleanup node data if(!data->read) { ucx_mempool_free(sn->mp, data->content); } data->content = NULL; data->read = NULL; data->length = 0; } else { dav_session_set_error(sn, ret, status); return 1; } } // store properties int r = 0; sn->error = DAV_OK; if(data->set || data->remove) { UcxBuffer *request = create_proppatch_request(data); 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, locktoken, request, response); long status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { //printf("%s\n", response->space); // TODO: parse response // TODO: cleanup node data correctly data->set = NULL; data->remove = NULL; } else { dav_session_set_error(sn, ret, status); r = -1; } ucx_buffer_free(request); ucx_buffer_free(response); } return r; } int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(res->session, dav_resource_get_href(res)); // check encryption AESDecrypter *dec = NULL; DavKey *key = NULL; if(DAV_DECRYPT_CONTENT(sn)) { char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); if(keyname) { 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_HTTPHEADER, NULL); curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(handle, CURLOPT_PUT, 0L); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc); curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); if(sn->get_progress) { curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, dav_session_get_progress); curl_easy_setopt(handle, CURLOPT_XFERINFODATA, res); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L); } long status = 0; CURLcode ret = dav_session_curl_perform(sn, &status); if(sn->get_progress) { curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, NULL); curl_easy_setopt(handle, CURLOPT_XFERINFODATA, NULL); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); } char *hash = NULL; if(dec) { aes_decrypter_shutdown(dec); // get final bytes // get hash unsigned char sha[DAV_SHA256_DIGEST_LENGTH]; dav_get_hash(&dec->sha256, sha); hash = util_hexstr(sha, DAV_SHA256_DIGEST_LENGTH); aes_decrypter_close(dec); } if(ret == CURLE_OK && (status >= 200 && status < 300)) { int verify_failed = 0; if(DAV_DECRYPT_CONTENT(sn) && key) { // try to verify the content char *res_hash = dav_get_string_property_ns(res, DAV_NS, "crypto-hash"); if(res_hash) { size_t len = 0; char *dec_hash = aes_decrypt(res_hash, &len, key); char *hex_hash = util_hexstr((unsigned char*)dec_hash, 32); if(strcmp(hash, hex_hash)) { verify_failed = 1; } free(dec_hash); free(hex_hash); } } if(hash) { free(hash); } if(verify_failed) { res->session->error = DAV_CONTENT_VERIFICATION_ERROR; return 1; } res->session->error = DAV_OK; return 0; } else { if(hash) { free(hash); } dav_session_set_error(res->session, ret, status); return 1; } } DavResource* dav_create_child(DavResource *parent, char *name) { DavResource *res = dav_resource_new_child(parent->session, parent, name); if(dav_create(res)) { dav_resource_free(res); return NULL; } else { return res; } } int dav_delete(DavResource *res) { 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(res->session, locktoken, response); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); int r = 0; if(ret == CURLE_OK && (status >= 200 && status < 300)) { res->session->error = DAV_OK; // TODO: parse response // TODO: free res } else { dav_session_set_error(res->session, ret, status); r = 1; } ucx_buffer_free(response); return r; } 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; 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(sn, locktoken); 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, NULL)) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Cannot set crypto properties for ancestor"); } 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)); DavLock *lock = dav_get_lock(res->session, res->path); char *locktoken = lock ? lock->token : NULL; CURLcode code; if(res->iscollection) { code = do_mkcol_request(sn, locktoken); } else { code = do_put_request(sn, locktoken, TRUE, "", NULL, 0); } long 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 if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) { // do a minimal propfind request UcxBuffer *rqbuf = create_propfind_request(sn, NULL); int ret = dav_propfind(sn, res, rqbuf); ucx_buffer_free(rqbuf); return ret; } else { return 1; } } else { dav_session_set_error(sn, code, s); return 1; } } int dav_create(DavResource *res) { int status; if(!create_resource(res, &status)) { // resource successfully created return 0; } if(status == 403 || status == 409) { // create intermediate collections if(create_ancestors(res->session, res->href, res->path)) { return 1; } } return create_resource(res, &status); } int dav_exists(DavResource *res) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); CURLcode ret = do_head_request(sn); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { return 1; } else { dav_session_set_error(sn, ret, status); return 0; } } static int dav_cp_mv_url(DavResource *res, char *desturl, _Bool copy, _Bool override) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(sn, res->path); char *locktoken = lock ? lock->token : NULL; CURLcode ret = do_copy_move_request(sn, desturl, locktoken, copy, override); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { return 0; } else { dav_session_set_error(sn, ret, status); return 1; } } static int dav_cp_mv(DavResource *res, char *newpath, _Bool copy, _Bool override) { char *dest = dav_session_get_href(res->session, newpath); char *desturl = util_get_url(res->session, dest); dav_session_free(res->session, dest); int ret = dav_cp_mv_url(res, desturl, copy, override); free(desturl); return ret; } int dav_copy(DavResource *res, char *newpath) { return dav_cp_mv(res, newpath, true, false); } int dav_move(DavResource *res, char *newpath) { return dav_cp_mv(res, newpath, false, false); } int dav_copy_o(DavResource *res, char *newpath, DavBool override) { return dav_cp_mv(res, newpath, true, override); } int dav_move_o(DavResource *res, char *newpath, DavBool override) { return dav_cp_mv(res, newpath, false, override); } int dav_copyto(DavResource *res, char *url, DavBool override) { return dav_cp_mv_url(res, url, true, override); } int dav_moveto(DavResource *res, char *url, DavBool override) { return dav_cp_mv_url(res, url, false, override); } int dav_lock(DavResource *res) { return dav_lock_t(res, 0); } int dav_lock_t(DavResource *res, time_t timeout) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); UcxBuffer *request = create_lock_request(); UcxBuffer *response = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); CURLcode ret = do_lock_request(sn, request, response, timeout); //printf("\nlock\n"); //printf("%.*s\n\n", request->size, request->space); //printf("%.*s\n\n", response->size, response->space); ucx_buffer_free(request); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { LockDiscovery lock; if(parse_lock_response(sn, response, &lock)) { sn->error = DAV_ERROR; ucx_buffer_free(response); return -1; } ucx_buffer_free(response); DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout); free(lock.locktoken); free(lock.timeout); 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); ucx_buffer_free(response); return -1; } } int dav_unlock(DavResource *res) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(res->session, res->path); if(!lock) { sn->error = DAV_ERROR; return -1; } CURLcode ret = do_unlock_request(sn, 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); } else { dav_session_set_error(sn, ret, status); return 1; } return 0; } int resource_add_crypto_info(DavSession *sn, char *href, char *name, char *hash) { if(!DAV_IS_ENCRYPTED(sn)) { return 0; } UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); util_set_url(sn, href); // TODO: lock CURLcode ret = do_proppatch_request(sn, NULL, request, response); ucx_buffer_free(request); long status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { // TODO: parse response sn->error = DAV_OK; ucx_buffer_free(response); return 0; } else { dav_session_set_error(sn, ret, status); ucx_buffer_free(response); return 1; } }