diff -r 6518b035a9df -r 03076907b58a libidav/session.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/session.c Thu Jun 05 15:11:29 2014 +0200 @@ -0,0 +1,340 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 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 +#include +#include + +#include +#include + +#include "utils.h" +#include "session.h" +#include "resource.h" +#include "methods.h" + +DavSession* dav_session_new(DavContext *context, char *base_url) { + if(!base_url) { + return NULL; + } + sstr_t url = sstr(base_url); + if(url.length == 0) { + return NULL; + } + DavSession *sn = malloc(sizeof(DavSession)); + sn->mp = ucx_mempool_new(DAV_SESSION_MEMPOOL_SIZE); + sn->pathcache = ucx_map_new_a(sn->mp->allocator, DAV_PATH_CACHE_SIZE); + sn->key = NULL; + sn->errorstr = NULL; + sn->error = DAV_OK; + sn->flags = 0; + if(url.ptr[url.length - 1] == '/') { + sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url)); + sn->base_url = url.ptr; + } else { + char *url_str = malloc(url.length + 2); + memcpy(url_str, base_url, url.length); + url_str[url.length] = '/'; + url_str[url.length + 1] = '\0'; + sn->base_url = url_str; + } + sn->context = context; + sn->handle = curl_easy_init(); + //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); + //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); + curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L); + + // set proxy + DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy + : context->http_proxy; + + if (proxy->url) { + curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url); + if (proxy->username) { + curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME, + proxy->username); + if (proxy->password) { + curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD, + proxy->password); + } else { + // TODO: prompt + } + } + if(proxy->no_proxy) { + curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, + proxy->no_proxy); + } + } + + // set url + curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); + + context->sessions = ucx_list_append(context->sessions, sn); + + return sn; +} + +DavSession* dav_session_new_auth( + DavContext *context, + char *base_url, + char *user, + char *password) +{ + DavSession *sn = dav_session_new(context, base_url); + if(!sn) { + return NULL; + } + dav_session_set_auth(sn, user, password); + return sn; +} + +void dav_session_set_auth(DavSession *sn, char *user, char *password) { + if(user && password) { + size_t ulen = strlen(user); + size_t plen = strlen(password); + size_t upwdlen = ulen + plen + 2; + char *upwdbuf = malloc(upwdlen); + snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password); + curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); + free(upwdbuf); + } +} + +void dav_session_set_flags(DavSession *sn, uint32_t flags) { + sn->flags = flags; +} + +uint32_t dav_session_get_flags(DavSession *sn) { + return sn->flags; +} + +void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags) { + sn->key = key; + if(flags != 0) { + sn->flags |= flags; + } else { + sn->flags |= DAV_SESSION_ENCRYPT_CONTENT; + } +} + +void dav_session_set_error(DavSession *sn, CURLcode c, int status) { + if(status > 0) { + switch(status) { + default: sn->error = DAV_ERROR; break; + case 401: sn->error = DAV_UNAUTHORIZED; break; + case 403: sn->error = DAV_FORBIDDEN; break; + case 404: sn->error = DAV_NOT_FOUND; break; + case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break; + case 409: sn->error = DAV_CONFLICT; break; + } + } else { + sn->error = DAV_ERROR; + } + if(c != CURLE_OK) { + sn->errorstr = curl_easy_strerror(c); + } else { + sn->errorstr = NULL; + } +} + +void dav_session_destroy(DavSession *sn) { + // remove session from context + UcxList *sessions = sn->context->sessions; + ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL); + if(i > 0) { + UcxList *elm = ucx_list_get(sessions, i); + if(elm) { + sn->context->sessions = ucx_list_remove(sessions, elm); + } + } + + ucx_mempool_destroy(sn->mp); + curl_easy_cleanup(sn->handle); + free(sn); +} + + +void* dav_session_malloc(DavSession *sn, size_t size) { + return ucx_mempool_malloc(sn->mp, size); +} + +void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) { + return ucx_mempool_calloc(sn->mp, nelm, size); +} + +void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) { + return ucx_mempool_realloc(sn->mp, ptr, size); +} + +void dav_session_free(DavSession *sn, void *ptr) { + ucx_mempool_free(sn->mp, ptr); +} + +char* dav_session_strdup(DavSession *sn, char *str) { + return sstrdup_a(sn->mp->allocator, sstr(str)).ptr; +} + + +char* dav_session_create_plain_href(DavSession *sn, char *path) { + if(!DAV_ENCRYPT_NAME(sn)) { + // non encrypted file names + char *url = util_path_to_url(sn, path); + char *href = dav_session_strdup(sn, util_url_path(url)); + free(url); + return href; + } else { + return NULL; + } +} + +char* dav_session_get_href(DavSession *sn, char *path) { + if(DAV_ENCRYPT_NAME(sn)) { + sstr_t p = sstr(path); + UcxBuffer *href = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); + UcxBuffer *pbuf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); + int start = 0; + int begin = 0; + + // check path cache + char *cp = strdup(path); + //printf("cp: %s\n", cp); + while(strlen(cp) > 1) { + char *cached = ucx_map_cstr_get(sn->pathcache, cp); + if(cached) { + start = strlen(cp); + begin = start; + ucx_buffer_puts(href, cached); + break; + } else { + // check, if the parent path is cached + char *f = cp; + cp = util_parent_path(cp); + free(f); + } + } + free(cp); + if(href->pos == 0) { + // if there are no cached elements we have to add the base url path + // to the href buffer + ucx_buffer_puts(href, util_url_path(sn->base_url)); + } + + // create resource for name lookup + sstr_t rp = sstrdup(sstrn(path, start)); + DavResource *root = dav_resource_new(sn, rp.ptr); + resource_set_href(root, sstrn(href->space, href->pos)); + + // create request buffer for propfind requests + UcxBuffer *rqbuf = create_basic_propfind_request(); + + sstr_t remaining = sstrsubs(p, start); + size_t nelm = 0; + sstr_t *elms = sstrsplit(remaining, S("/"), &nelm); + DavResource *res = root; + ucx_buffer_puts(pbuf, res->path); + // iterate over all remaining path elements + for(int i=0;i 0) { + //printf("elm: %.*s\n", elm.length, elm.ptr); + DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr); + + // if necessary add a path separator + if(pbuf->space[pbuf->pos-1] != '/') { + if(href->space[href->pos-1] != '/') { + ucx_buffer_putc(href, '/'); + } + ucx_buffer_putc(pbuf, '/'); + } + // add last path/href to the cache + sstr_t pp = sstrn(pbuf->space, pbuf->size); + sstr_t hh = sstrn(href->space, href->size); + dav_session_cache_path(sn, pp, hh); + + ucx_buffer_write(elm.ptr, 1, elm.length, pbuf); + if(child) { + ucx_buffer_puts(href, util_resource_name(child->href)); + res = child; + } else { + //printf("random\n"); + char *random_name = util_random_str(); + ucx_buffer_puts(href, random_name); + free(random_name); + } + + } + } + + // if necessary add a path separator + if(p.ptr[p.length-1] == '/') { + if(href->space[href->pos-1] != '/') { + ucx_buffer_putc(href, '/'); + } + ucx_buffer_putc(pbuf, '/'); + } + // add the final path to the cache + sstr_t pp = sstrn(pbuf->space, pbuf->size); + sstr_t hh = sstrn(href->space, href->size); + dav_session_cache_path(sn, pp, hh); + + sstr_t href_str = sstrdup_a( + sn->mp->allocator, + sstrn(href->space, + href->size)); + + // cleanup + dav_resource_free_all(root); + ucx_buffer_free(pbuf); + ucx_buffer_free(href); + + return href_str.ptr; + } else { + return dav_session_create_plain_href(sn, path); + } +} + +DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name) { + if(res && !dav_propfind(sn, res, rqbuf, NULL, 0)) { + DavResource *child = res->children; + while(child) { + if(!strcmp(child->name, name)) { + return child; + } + child = child->next; + } + } + return NULL; +} + +void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href) { + char *elm = ucx_map_sstr_get(sn->pathcache, path); + if(!elm) { + href = sstrdup_a(sn->mp->allocator, href); + ucx_map_sstr_put(sn->pathcache, path, href.ptr); + } +}