Thu, 12 Dec 2024 16:46:43 +0100
fix compatibility with older libcurl versions
/* * 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 <cx/buffer.h> #include <cx/utils.h> #include <cx/linked_list.h> #include <cx/hash_map.h> #include <cx/compare.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 = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr, CX_STORE_POINTERS); cxDefineDestructor(context->sessions, dav_session_destructor); 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 = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); if(!context->namespaces) { dav_context_destroy(context); return NULL; } context->namespaceinfo = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); if(!context->namespaceinfo) { dav_context_destroy(context); } context->keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 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 // ctx->sessions destructor must be dav_session_destructor cxListDestroy(ctx->sessions); if(ctx->http_proxy) { free(ctx->http_proxy); } if(ctx->https_proxy) { free(ctx->https_proxy); } if(ctx->namespaces) { CxIterator i = cxMapIteratorValues(ctx->namespaces); cx_foreach(DavNamespace*, ns, i) { if(!ns) continue; if(ns->prefix) { free(ns->prefix); } if(ns->name) { free(ns->name); } free(ns); } cxMapDestroy(ctx->namespaces); } if(ctx->namespaceinfo) { // TODO: implement } if(ctx->keys) { CxIterator i = cxMapIteratorValues(ctx->keys); cx_foreach(DavKey*, key, i) { if(!key) continue; if(key->name) { free(key->name); } if(key->data) { free(key->data); } free(key); } cxMapDestroy(ctx->keys); } free(ctx); } #ifndef _WIN32 void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) { if (enable) { pthread_mutex_init(&ctx->mutex, NULL); } else { pthread_mutex_destroy(&ctx->mutex); } ctx->mtsafe = enable; } void dav_context_lock(DavContext *ctx) { if (ctx->mtsafe) { pthread_mutex_lock(&ctx->mutex); } } void dav_context_unlock(DavContext *ctx) { if (ctx->mtsafe) { pthread_mutex_unlock(&ctx->mutex); } } #else void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) { if (enable) { ctx->mutex = CreateMutex(NULL, FALSE, NULL); } else { CloseHandle(ctx->mutex); } ctx->mtsafe = enable; } void dav_context_lock(DavContext *ctx) { if (ctx->mtsafe) { WaitForSingleObject(ctx->mutex, INFINITE); } } void dav_context_unlock(DavContext *ctx) { if (ctx->mtsafe) { ReleaseMutex(ctx->mutex); } } #endif void dav_context_add_key(DavContext *context, DavKey *key) { dav_context_lock(context); cxMapPut(context->keys, cx_hash_key_str(key->name), key); dav_context_unlock(context); } DavKey* dav_context_get_key(DavContext *context, const char *name) { DavKey *key = NULL; dav_context_lock(context); if(name) { key = cxMapGet(context->keys, cx_hash_key_str(name)); } dav_context_unlock(context); return key; } 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); if (!p) { free(namespace); return 1; } char *n = strdup(name); if (!n) { free(namespace); free(p); return 1; } dav_context_lock(context); int err = 0; if(p && n) { namespace->prefix = p; namespace->name = n; err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace); } if(err) { free(namespace); if(p) free(p); if(n) free(n); } dav_context_unlock(context); return err; } DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) { dav_context_lock(context); DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key_str(prefix)); dav_context_unlock(context); return ns; } DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) { dav_context_lock(context); DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length)); dav_context_unlock(context); return ns; } int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) { dav_context_lock(context); CxHashKey hkey = cx_hash_key_str(ns); DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey); if(!info) { info = calloc(1, sizeof(DavNSInfo)); info->encrypt = encrypt; cxMapPut(context->namespaceinfo, hkey, info); } else { info->encrypt = encrypt; } dav_context_unlock(context); return 0; } int dav_namespace_is_encrypted(DavContext *context, const char *ns) { int ret = 0; dav_context_lock(context); DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns)); if(info) { ret = info->encrypt; } dav_context_unlock(context); return ret; } 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 *davns = dav_get_namespace_s( ctx, cx_strn(prefixed_name, pname-prefixed_name)); if(davns) { pns = davns->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, cx_strn(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, cx_str("D")); } } int dav_context_add_session(DavContext *context, DavSession *sn) { dav_context_lock(context); int ret = cxListAdd(context->sessions, sn); dav_context_unlock(context); return ret; } int dav_context_remove_session(DavContext *context, DavSession *sn) { int ret = 0; dav_context_lock(context); CxList *sessions = context->sessions; ssize_t i = cxListFind(sessions, sn); if(i >= 0) { cxListRemove(sessions, i); } else { ret = 1; } dav_context_unlock(context); return ret; } // 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) { const 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, const char *properties) { CURL *handle = sn->handle; DavResource *resource = dav_resource_new(sn, path); util_set_url(sn, dav_resource_get_href(resource)); CxList *proplist = NULL; if(properties) { proplist = parse_properties_string(sn->context, cx_str(properties)); } CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0); CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); //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; } cxBufferFree(rqbuf); cxBufferFree(rpbuf); if(proplist) { CxIterator i = cxListIterator(proplist); cx_foreach(DavProperty*, p, i) { free(p->name); } cxListDestroy(proplist); } return resource; } int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) { // clean resource properties DavResourceData *data = root->data; cxMapClear(data->properties); // TODO: free existing content CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(root)); CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 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; } cxBufferFree(rpbuf); return error; } CxList* parse_properties_string(DavContext *context, cxstring str) { CxList *proplist = cxLinkedListCreateSimple(sizeof(DavProperty)); CxStrtokCtx tok = cx_strtok(str, cx_str(","), INT_MAX); cxstring s; while(cx_strtok_next(&tok, &s)) { cxstring nsname = cx_strchr(s, ':'); if(nsname.length > 0) { cxstring nspre = cx_strsubsl(s, 0, nsname.ptr - s.ptr); nsname.ptr++; nsname.length--; DavProperty dp; cxstring pre = cx_strtrim(nspre); dp.ns = dav_get_namespace_s(context, pre); dp.name = cx_strdup(nsname).ptr; dp.value = NULL; if(dp.ns && dp.name) { cxListAdd(proplist, &dp); } else { free(dp.name); } } } return proplist; } DavResource* dav_query(DavSession *sn, char *query, ...) { DavQLStatement *stmt = dav_parse_statement(cx_str(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); if(result.status == -1) { if(result.result) { dav_resource_free(result.result); result.result = NULL; } } return result.result; } void dav_verbose_log( DavSession *sn, const char *method, const char *url, const char *request_body, size_t request_bodylen, int status, const char *response_body, size_t response_bodylen) { fprintf(stderr, "# method: %s url: %s status: %d\n", method, url, status); fprintf(stderr, "# request len: %d\n%.*s\n", (int)request_bodylen, (int)request_bodylen, request_body); fprintf(stderr, "# response len: %d\n%.*s\n", (int)response_bodylen, (int)response_bodylen, response_body); }