Wed, 20 Nov 2019 11:57:45 +0100
fix some split bugs and add tests
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2018 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 <libxml/tree.h> #include "utils.h" #include "webdav.h" #include "session.h" #include "methods.h" #include "ucx/buffer.h" #include "ucx/utils.h" #include "davqlparser.h" #include "davqlexec.h" DavContext* dav_context_new(void) { // initialize DavContext *context = calloc(1, sizeof(DavContext)); if(!context) { return NULL; } context->sessions = NULL; context->http_proxy = calloc(1, sizeof(DavProxy)); if(!context->http_proxy) { dav_context_destroy(context); return NULL; } context->https_proxy = calloc(1, sizeof(DavProxy)); if(!context->https_proxy) { dav_context_destroy(context); return NULL; } context->namespaces = ucx_map_new(16); if(!context->namespaces) { dav_context_destroy(context); return NULL; } context->namespaceinfo = ucx_map_new(16); if(!context->namespaceinfo) { dav_context_destroy(context); } context->keys = ucx_map_new(16); if(!context->keys) { dav_context_destroy(context); return NULL; } // add DAV: namespace if(dav_add_namespace(context, "D", "DAV:")) { dav_context_destroy(context); return NULL; } // add idav namespace if(dav_add_namespace(context, "idav", DAV_NS)) { dav_context_destroy(context); return NULL; } // add idavprops namespace if(dav_add_namespace(context, "idavprops", DAV_PROPS_NS)) { dav_context_destroy(context); return NULL; } return context; } void dav_context_destroy(DavContext *ctx) { // destroy all sessions assoziated with this context UcxList *elm = ctx->sessions; while(elm) { DavSession *sn = elm->data; elm = elm->next; dav_session_destroy(sn); } if(ctx->http_proxy) { free(ctx->http_proxy); } if(ctx->https_proxy) { free(ctx->https_proxy); } if(ctx->namespaces) { UcxMapIterator i = ucx_map_iterator(ctx->namespaces); UcxKey k; DavNamespace *ns; UCX_MAP_FOREACH(k, ns, i) { if(!ns) continue; if(ns->prefix) { free(ns->prefix); } if(ns->name) { free(ns->name); } free(ns); } ucx_map_free(ctx->namespaces); } if(ctx->namespaceinfo) { // TODO: implement } if(ctx->keys) { UcxMapIterator i = ucx_map_iterator(ctx->keys); UcxKey k; DavKey *key; UCX_MAP_FOREACH(k, key, i) { if(!key) continue; if(key->name) { free(key->name); } if(key->data) { free(key->data); } free(key); } ucx_map_free(ctx->keys); } free(ctx); } void dav_context_add_key(DavContext *context, DavKey *key) { ucx_map_cstr_put(context->keys, key->name, key); } DavKey* dav_context_get_key(DavContext *context, char *name) { if(name) { return ucx_map_cstr_get(context->keys, name); } return NULL; } int dav_add_namespace(DavContext *context, const char *prefix, const char *name) { DavNamespace *namespace = malloc(sizeof(DavNamespace)); if(!namespace) { return 1; } char *p = strdup(prefix); char *n = strdup(name); int err = 0; if(p && n) { namespace->prefix = p; namespace->name = n; err = ucx_map_cstr_put(context->namespaces, prefix, namespace); } if(err) { free(namespace); if(p) free(p); if(n) free(n); } return err; } DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) { return ucx_map_cstr_get(context->namespaces, prefix); } DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) { return ucx_map_sstr_get(context->namespaces, prefix); } int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) { DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); if(!info) { info = calloc(1, sizeof(DavNSInfo)); info->encrypt = encrypt; ucx_map_cstr_put(context->namespaceinfo, ns, info); } else { info->encrypt = encrypt; } return 0; } int dav_namespace_is_encrypted(DavContext *context, const char *ns) { DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); if(info) { return info->encrypt; } return 0; } void dav_get_property_namespace_str( DavContext *ctx, char *prefixed_name, char **ns, char **name) { // TODO: rewrite using dav_get_property_ns char *pname = strchr(prefixed_name, ':'); char *pns = "DAV:"; if(pname) { DavNamespace *ns = dav_get_namespace_s( ctx, sstrn(prefixed_name, pname-prefixed_name)); if(ns) { pns = ns->name; pname++; } else { pns = NULL; pname = NULL; } } else { pname = prefixed_name; } *ns = pns; *name = pname; } DavNamespace* dav_get_property_namespace( DavContext *ctx, char *prefixed_name, char **name) { char *pname = strchr(prefixed_name, ':'); if(pname) { DavNamespace *ns = dav_get_namespace_s( ctx, sstrn(prefixed_name, pname-prefixed_name)); if(ns) { *name = pname +1; return ns; } else { *name = NULL; return NULL; } } else { *name = prefixed_name; return dav_get_namespace_s(ctx, S("D")); } } // TODO: add sstr_t version of dav_get_property_ns void dav_set_effective_href(DavSession *sn, DavResource *resource) { char *eff_url; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &eff_url); if(eff_url) { char *href = util_url_path(eff_url); if(strcmp(href, resource->href)) { dav_session_free(sn, resource->href); resource->href = dav_session_strdup(sn, href); } } } DavResource* dav_get(DavSession *sn, char *path, char *properties) { CURL *handle = sn->handle; DavResource *resource = dav_resource_new(sn, path); util_set_url(sn, dav_resource_get_href(resource)); UcxList *proplist = NULL; if(properties) { proplist = parse_properties_string(sn->context, sstr(properties)); } UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0); UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); //fwrite(rqbuf->space, 1, rqbuf->size, stdout); //printf("\n"); CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { dav_set_effective_href(sn, resource); //printf("response\n%s\n", rpbuf->space); // TODO: use PropfindParser resource = parse_propfind_response(sn, resource, rpbuf); resource->exists = 1; sn->error = DAV_OK; } else { dav_session_set_error(sn, ret, status); dav_resource_free(resource); resource = NULL; } ucx_buffer_free(rqbuf); ucx_buffer_free(rpbuf); while(proplist) { DavProperty *p = proplist->data; free(p->name); free(p); UcxList *next = proplist->next; free(proplist); proplist = next; } return resource; } int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf) { // clean resource properties DavResourceData *data = root->data; ucx_map_clear(data->properties); // TODO: free existing content CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(root)); UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); DavResource *resource = root; CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf); long status = 0; long error = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { //printf("response\n%s\n", rpbuf->space); dav_set_effective_href(sn, resource); // TODO: use PropfindParser resource = parse_propfind_response(sn, resource, rpbuf); sn->error = DAV_OK; root->exists = 1; } else { dav_session_set_error(sn, ret, status); error = 1; } ucx_buffer_free(rpbuf); return error; } UcxList* parse_properties_string(DavContext *context, sstr_t str) { UcxList *proplist = NULL; ssize_t nprops = 0; sstr_t *props = sstrsplit(str, S(","), &nprops); for(int i=0;i<nprops;i++) { sstr_t s = props[i]; sstr_t nsname = sstrchr(s, ':'); if(nsname.length > 0) { sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr); nsname.ptr++; nsname.length--; DavProperty *dp = malloc(sizeof(DavProperty)); sstr_t pre = sstrtrim(nspre); dp->ns = dav_get_namespace_s(context, pre); dp->name = sstrdup(nsname).ptr; if(dp->ns && dp->name) { proplist = ucx_list_append(proplist, dp); } else { free(dp->name); free(dp); } } free(s.ptr); } free(props); return proplist; } DavResource* dav_query(DavSession *sn, char *query, ...) { DavQLStatement *stmt = dav_parse_statement(sstr(query)); if(!stmt) { sn->error = DAV_ERROR; return NULL; } if(stmt->errorcode != 0) { sn->error = DAV_QL_ERROR; dav_free_statement(stmt); return NULL; } va_list ap; va_start(ap, query); DavResult result = dav_statement_execv(sn, stmt, ap); va_end(ap); dav_free_statement(stmt); return result.result; }