diff -r 3ca3acefc66a -r 1ce14068ef31 application/config.c --- a/application/config.c Mon Oct 21 15:45:12 2024 +0200 +++ b/application/config.c Wed Oct 23 10:37:43 2024 +0200 @@ -1,539 +1,369 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2024 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 -#include -#include - -#include "pwd.h" -#include "config.h" -#include "system.h" - -#include -#include - -#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) -#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b) - -#define print_error(lineno, ...) \ - do {\ - fprintf(stderr, "Error (config.xml line %u): ", lineno); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "Abort.\n"); \ - } while(0); -#define print_warning(lineno, ...) \ - do {\ - fprintf(stderr, "Warning (config.xml line %u): ", lineno); \ - fprintf(stderr, __VA_ARGS__); \ - } while(0); - -#ifdef _WIN32 -#define ENV_HOME getenv("USERPROFILE") -#else -#define ENV_HOME getenv("HOME") -#endif /* _WIN32 */ - -static CxMap* repos; -static CxMap* keys; - -static DavConfig* davconfig; -static PwdStore* pstore; - -static char* secretstore_unlock_cmd; -static char* secretstore_lock_cmd; - -int check_config_dir(void) { - char* file = util_concat_path(ENV_HOME, ".dav"); - int ret = 0; - if (util_mkdir(file, S_IRWXU)) { - if (errno != EEXIST) { - ret = 1; - } - } - free(file); - return ret; -} - -static DavContext* context; - -void create_default_config(char* file) { - xmlDoc* doc = xmlNewDoc(BAD_CAST "1.0"); - xmlNode* root = xmlNewNode(NULL, BAD_CAST "configuration"); - xmlDocSetRootElement(doc, root); - xmlSaveFormatFileEnc(file, doc, "UTF-8", 1); - xmlFreeDoc(doc); -} - -char* config_file_path(char* name) { - char* davd = util_concat_path(ENV_HOME, ".dav"); - if (!davd) { - return NULL; - } - char* path = util_concat_path(davd, name); - free(davd); - return path; -} - -cxmutstr config_load_file(const char* path) { - FILE* file = sys_fopen(path, "r"); - if (!file) { - return (cxmutstr) { NULL, 0 }; - } - - CxBuffer buf; - cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); - fclose(file); - - return cx_mutstrn(buf.space, buf.size); -} - -int load_config(DavContext *ctx) { - context = ctx; - // TODO: free the config somewhere - repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); - keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); - - char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); - pstore = pwdstore_open(pwfile); - free(pwfile); - - char *file = util_concat_path(ENV_HOME, ".dav/config.xml"); - - struct stat s; - if(stat(file, &s)) { - switch(errno) { - case ENOENT: { - davconfig = dav_config_new(NULL); - return 0; - } - default: { - perror("Cannot load config.xml"); - } - } - return 1; - } - - cxmutstr config_content = config_load_file(file); - int config_error; - davconfig = dav_config_load(config_content, &config_error); - free(config_content.ptr); - free(file); - - if(!davconfig) { - fprintf(stderr, "Cannot load config.xml\n"); - return 1; - } - - if(dav_config_register_namespaces(davconfig, ctx)) { - return 1; - } - - return dav_config_register_keys(davconfig, ctx, load_key_file); -} - -DavConfig* get_config(void) { - return davconfig; -} - -int store_config(void) { - if (check_config_dir()) { - return 1; - } - - CxBuffer* buf = dav_config2buf(davconfig); - if (!buf) { - return 1; - } - - char* file = util_concat_path(ENV_HOME, ".dav/config.xml"); - FILE* cout = sys_fopen(file, "w"); - if (!cout) { - cxBufferFree(buf); - return 1; - } - - // should only fail if we run out of disk space or something like that - // in that case, the config file is only destroyed - // could only be prevented, if we write to a temp file first and than - // rename it - fwrite(buf->space, buf->size, 1, cout); - - cxBufferFree(buf); - fclose(cout); - - return 0; -} - -void free_config(void) { - if (davconfig) { - dav_config_free(davconfig); - } -} - -cxmutstr load_key_file(const char* filename) { - cxmutstr k; - k.ptr = NULL; - k.length = 0; - - FILE* file = NULL; - if (filename[0] == '/') { - file = sys_fopen(filename, "r"); - } - else { - char* path = util_concat_path(ENV_HOME, ".dav/"); - char* p2 = util_concat_path(path, filename); - file = sys_fopen(p2, "r"); - free(path); - free(p2); - } - - if (!file) { - fprintf(stderr, "Error: cannot load keyfile %s\n", filename); - return k; - } - - char* data = malloc(256); - size_t r = fread(data, 1, 256, file); - k.ptr = data; - k.length = r; - - fclose(file); - return k; -} - -static char* get_attr_content(xmlNode* node) { - // TODO: remove code duplication (util_xml_get_text) - while (node) { - if (node->type == XML_TEXT_NODE) { - return (char*)node->content; - } - node = node->next; - } - return NULL; -} - -int load_namespace(const xmlNode* node) { - const char* prefix = NULL; - const char* uri = NULL; - - xmlAttr* attr = node->properties; - while (attr) { - if (attr->type == XML_ATTRIBUTE_NODE) { - char* value = get_attr_content(attr->children); - if (!value) { - print_error( - node->line, - "missing value for attribute %s\n", (char*)attr->name); - return 1; - } - if (xstreq(attr->name, "prefix")) { - prefix = value; - } - else if (xstreq(attr->name, "uri")) { - uri = value; - } - else { - print_error( - node->line, - "unexpected attribute %s\n", (char*)attr->name); - return 1; - } - } - attr = attr->next; - } - - if (!prefix) { - print_error(node->line, "missing prefix attribute\n"); - return 1; - } - if (!uri) { - print_error(node->line, "missing uri attribute\n"); - return 1; - } - - if (dav_get_namespace(context, prefix)) { - print_error(node->line, "namespace prefix '%s' already used\n", prefix); - return 1; - } - - return dav_add_namespace(context, prefix, uri); -} - -int load_secretstore(const xmlNode* node) { - // currently only one secretstore is supported - - if (!pstore) { - return 0; - } - - node = node->children; - int error = 0; - while (node) { - if (node->type == XML_ELEMENT_NODE) { - char* value = util_xml_get_text(node); - if (value) { - if (xstreq(node->name, "unlock-command")) { - pstore->unlock_cmd = strdup(value); - } - else if (xstreq(node->name, "lock-command")) { - pstore->lock_cmd = strdup(value); - } - } - } - node = node->next; - } - - return error; -} - -PwdStore* get_pwdstore(void) { - return pstore; -} - -int pwdstore_save(PwdStore* pwdstore) { - if (check_config_dir()) { - return 1; - } - - char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); - int ret = pwdstore_store(pwdstore, pwfile); - free(pwfile); - return ret; -} - - - -static int decrypt_secrets(PwdStore *secrets) { - char *ps_password = NULL; - if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) { - CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); - if(!util_exec_command(secrets->unlock_cmd, cmd_out)) { - // command successful, get first line from output without newline - // and use that as password for the secretstore - size_t len = 0; - for(size_t i=0;i<=cmd_out->size;i++) { - if(i == cmd_out->size || cmd_out->space[i] == '\n') { - len = i; - break; - } - } - if(len > 0) { - ps_password = malloc(len + 1); - memcpy(ps_password, cmd_out->space, len); - ps_password[len] = 0; - } - } - cxBufferFree(cmd_out); - } - - return 1; - // TODO - /* - if(!ps_password) { - ps_password = util_password_input("Master password: "); - if(!ps_password) { - return 1; - } - } - - if(pwdstore_setpassword(secrets, ps_password)) { - fprintf(stderr, "Error: cannot create key from password\n"); - return 1; - } - if(pwdstore_decrypt(secrets)) { - fprintf(stderr, "Error: cannot decrypt secrets store\n"); - return 1; - } - return 0; - */ -} - - -typedef struct CredLocation { - char *id; - char *location; -} CredLocation; - -static int cmp_url_cred_entry(CredLocation *e1, CredLocation *e2, void *n) { - return strcmp(e2->location, e1->location); -} - -static void free_cred_location(CredLocation *c) { - // c->id is not a copy, therefore we don't have to free it - free(c->location); - free(c); -} - -static int get_stored_credentials(char *credid, char **user, char **password) { - return 0; - // TODO - /* - if(!credid) { - return 0; - } - PwdStore *secrets = get_pwdstore(); - if(!secrets) { - fprintf(stderr, "Error: no secrets store available\n"); - return 0; - } - - if(pwdstore_has_id(secrets, credid)) { - if(!secrets->isdecrypted) { - if(decrypt_secrets(a, secrets)) { - return 0; - } - } - - PwdEntry *s_cred = pwdstore_get(secrets, credid); - if(s_cred) { - *user = s_cred->user; - *password = s_cred->password; - return 1; - } - } else { - fprintf(stderr, "Error: credentials id '%s' not found\n", credid); - } - - return 0; - */ -} - - -static int get_location_credentials(DavCfgRepository *repo, const char *path, char **user, char **password) { - PwdStore *secrets = get_pwdstore(); - if(!secrets) { - return 0; - } - - /* - * The list secrets->location contains urls or repo names as - * location strings. We need a list, that contains only urls - */ - CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS); - cxDefineDestructor(locations, free_cred_location); - CxIterator i = cxListIterator(secrets->locations); - cx_foreach(PwdIndexEntry*, e, i) { - CxIterator entry_iter = cxListIterator(e->locations); - cx_foreach(char *, loc, entry_iter) { - cxmutstr rpath; - DavCfgRepository *r = dav_config_url2repo_s(davconfig, cx_str(loc), &rpath); - CredLocation *urlentry = calloc(1, sizeof(CredLocation)); - urlentry->id = e->id; - urlentry->location = util_concat_path_s(cx_strcast(r->url.value), cx_strcast(rpath)).ptr; - cxListAdd(locations, urlentry); - free(rpath.ptr); - } - } - // the list must be sorted - cxListSort(locations); - - // create full request url string and remove protocol prefix - cxmutstr req_url_proto = util_concat_path_s(cx_strcast(repo->url.value), cx_str(path)); - cxstring req_url = cx_strcast(req_url_proto); - if(cx_strprefix(req_url, CX_STR("http://"))) { - req_url = cx_strsubs(req_url, 7); - } else if(cx_strprefix(req_url, CX_STR("https://"))) { - req_url = cx_strsubs(req_url, 8); - } - - // iterate over sorted locations and check if a location is a prefix - // of the requested url - char *id = NULL; - int ret = 0; - i = cxListIterator(locations); - cx_foreach(CredLocation*, cred, i) { - cxstring cred_url = cx_str(cred->location); - - // remove protocol prefix - if(cx_strprefix(cred_url, CX_STR("http://"))) { - cred_url = cx_strsubs(cred_url, 7); - } else if(cx_strprefix(cred_url, CX_STR("https://"))) { - cred_url = cx_strsubs(cred_url, 8); - } - - if(cx_strprefix(req_url, cred_url)) { - id = cred->id; - break; - } - } - - // if an id is found and we can access the decrypted secret store - // we can set the user/password - if(id && (secrets->isdecrypted || !decrypt_secrets(secrets))) { - PwdEntry *cred = pwdstore_get(secrets, id); - if(cred) { - *user = cred->user; - *password = cred->password; - ret = 1; - } - } - - free(req_url_proto.ptr); - cxListDestroy(locations); - - return ret; -} - - -DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc) { - cxmutstr decodedpw = dav_repository_get_decodedpassword(repo); - - char *user = repo->user.value.ptr; - char *password = decodedpw.ptr; - - if(!user && !password) { - if(!get_stored_credentials(repo->stored_user.value.ptr, &user, &password)) { - get_location_credentials(repo, path, &user, &password); - } - } - - DavSession *sn = dav_session_new_auth(ctx, repo->url.value.ptr, user, password); - if(password) { - free(password); - } - - sn->flags = dav_repository_get_flags(repo); - sn->key = dav_context_get_key(ctx, repo->default_key.value.ptr); - curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods); - curl_easy_setopt(sn->handle, CURLOPT_SSLVERSION, repo->ssl_version); - if(repo->cert.value.ptr) { - curl_easy_setopt(sn->handle, CURLOPT_CAINFO, repo->cert.value.ptr); - } - if(!repo->verification.value) { - curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYHOST, 0); - } - - return sn; -} +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "pwd.h" +#include "system.h" + +#include +#include + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) +#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b) + +#define print_error(lineno, ...) \ + do {\ + fprintf(stderr, "Error (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "Abort.\n"); \ + } while(0); +#define print_warning(lineno, ...) \ + do {\ + fprintf(stderr, "Warning (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + } while(0); + +#ifdef _WIN32 +#define ENV_HOME getenv("USERPROFILE") +#else +#define ENV_HOME getenv("HOME") +#endif /* _WIN32 */ + +static CxMap *repos; +static CxMap *keys; + +static DavConfig *davconfig; +static PwdStore *pstore; + +static char *secretstore_unlock_cmd; +static char *secretstore_lock_cmd; + +int check_config_dir(void) { + char *file = util_concat_path(ENV_HOME, ".dav"); + int ret = 0; + if(util_mkdir(file, S_IRWXU)) { + if(errno != EEXIST) { + ret = 1; + } + } + free(file); + return ret; +} + +static DavContext *context; + +void create_default_config(char *file) { + xmlDoc *doc = xmlNewDoc(BAD_CAST "1.0"); + xmlNode *root = xmlNewNode(NULL, BAD_CAST "configuration"); + xmlDocSetRootElement(doc, root); + xmlSaveFormatFileEnc(file, doc, "UTF-8", 1); + xmlFreeDoc(doc); +} + +char* config_file_path(char *name) { + char *davd = util_concat_path(ENV_HOME, ".dav"); + if(!davd) { + return NULL; + } + char *path = util_concat_path(davd, name); + free(davd); + return path; +} + +cxmutstr config_load_file(const char *path) { + FILE *file = sys_fopen(path, "r"); + if(!file) { + return (cxmutstr){NULL,0}; + } + + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); + fclose(file); + + return cx_mutstrn(buf.space, buf.size); +} + +int load_config(DavContext *ctx) { + context = ctx; + // TODO: free the config somewhere + repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + + char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); + pstore = pwdstore_open(pwfile); + free(pwfile); + + char *file = util_concat_path(ENV_HOME, ".dav/config.xml"); + + struct stat s; + if(stat(file, &s)) { + switch(errno) { + case ENOENT: { + davconfig = dav_config_new(NULL); + return 0; + } + default: { + perror("Cannot load config.xml"); + } + } + return 1; + } + + cxmutstr config_content = config_load_file(file); + int config_error; + davconfig = dav_config_load(config_content, &config_error); + free(config_content.ptr); + free(file); + + if(!davconfig) { + fprintf(stderr, "Cannot load config.xml\n"); + return 1; + } + + if(dav_config_register_namespaces(davconfig, ctx)) { + return 1; + } + + return dav_config_register_keys(davconfig, ctx, load_key_file); +} + +DavConfig* get_config(void) { + return davconfig; +} + +int store_config(void) { + if(check_config_dir()) { + return 1; + } + + CxBuffer *buf = dav_config2buf(davconfig); + if(!buf) { + return 1; + } + + char *file = util_concat_path(ENV_HOME, ".dav/config.xml"); + FILE *cout = sys_fopen(file, "w"); + if(!cout) { + cxBufferFree(buf); + return 1; + } + + // should only fail if we run out of disk space or something like that + // in that case, the config file is only destroyed + // could only be prevented, if we write to a temp file first and than + // rename it + fwrite(buf->space, buf->size, 1, cout); + + cxBufferFree(buf); + fclose(cout); + + return 0; +} + +void free_config(void) { + if(davconfig) { + dav_config_free(davconfig); + } +} + +cxmutstr load_key_file(const char *filename) { + cxmutstr k; + k.ptr = NULL; + k.length = 0; + + FILE *file = NULL; + if(filename[0] == '/') { + file = sys_fopen(filename, "r"); + } else { + char *path = util_concat_path(ENV_HOME, ".dav/"); + char *p2 = util_concat_path(path, filename); + file = sys_fopen(p2, "r"); + free(path); + free(p2); + } + + if(!file) { + fprintf(stderr, "Error: cannot load keyfile %s\n", filename); + return k; + } + + char *data = malloc(256); + size_t r = fread(data, 1, 256, file); + k.ptr = data; + k.length = r; + + fclose(file); + return k; +} + +PwdStore* get_pwdstore(void) { + return pstore; +} + +int pwdstore_save(PwdStore *pwdstore) { + if(check_config_dir()) { + return 1; + } + + char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); + int ret = pwdstore_store(pwdstore, pwfile); + free(pwfile); + return ret; +} + +typedef struct CredLocation { + char *id; + char *location; +} CredLocation; + +static int cmp_url_cred_entry(CredLocation *e1, CredLocation *e2, void *n) { + return strcmp(e2->location, e1->location); +} + +static void free_cred_location(CredLocation *c) { + // c->id is not a copy, therefore we don't have to free it + free(c->location); + free(c); +} + +int get_stored_credentials(char *credid, char **user, char **password) { + if(!credid) { + return 0; + } + + PwdStore *secrets = get_pwdstore(); + if(!secrets) { + fprintf(stderr, "Error: no secrets store available\n"); + return 0; + } + + if(pwdstore_has_id(secrets, credid)) { + if(!secrets->isdecrypted) { + if(pwdstore_decrypt_secrets(secrets)) { + return 0; + } + } + + PwdEntry *s_cred = pwdstore_get(secrets, credid); + if(s_cred) { + *user = s_cred->user; + *password = s_cred->password; + return 1; + } + } else { + fprintf(stderr, "Error: credentials id '%s' not found\n", credid); + } + + return 0; +} + + +int get_location_credentials(DavCfgRepository *repo, const char *path, char **user, char **password) { + PwdStore *secrets = get_pwdstore(); + if(!secrets) { + return 0; + } + + /* + * The list secrets->location contains urls or repo names as + * location strings. We need a list, that contains only urls + */ + CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS); + cxDefineDestructor(locations, free_cred_location); + CxIterator i = cxListIterator(secrets->locations); + cx_foreach(PwdIndexEntry*, e, i) { + CxIterator entry_iter = cxListIterator(e->locations); + cx_foreach(char *, loc, entry_iter) { + cxmutstr rpath; + DavCfgRepository *r = dav_config_url2repo_s(davconfig, cx_str(loc), &rpath); + CredLocation *urlentry = calloc(1, sizeof(CredLocation)); + urlentry->id = e->id; + urlentry->location = util_concat_path_s(cx_strcast(r->url.value), cx_strcast(rpath)).ptr; + cxListAdd(locations, urlentry); + free(rpath.ptr); + } + } + // the list must be sorted + cxListSort(locations); + + // create full request url string and remove protocol prefix + cxmutstr req_url_proto = util_concat_path_s(cx_strcast(repo->url.value), cx_str(path)); + cxstring req_url = cx_strcast(req_url_proto); + if(cx_strprefix(req_url, CX_STR("http://"))) { + req_url = cx_strsubs(req_url, 7); + } else if(cx_strprefix(req_url, CX_STR("https://"))) { + req_url = cx_strsubs(req_url, 8); + } + + // iterate over sorted locations and check if a location is a prefix + // of the requested url + char *id = NULL; + int ret = 0; + i = cxListIterator(locations); + cx_foreach(CredLocation*, cred, i) { + cxstring cred_url = cx_str(cred->location); + + // remove protocol prefix + if(cx_strprefix(cred_url, CX_STR("http://"))) { + cred_url = cx_strsubs(cred_url, 7); + } else if(cx_strprefix(cred_url, CX_STR("https://"))) { + cred_url = cx_strsubs(cred_url, 8); + } + + if(cx_strprefix(req_url, cred_url)) { + id = cred->id; + break; + } + } + + // if an id is found and we can access the decrypted secret store + // we can set the user/password + if(id && (secrets->isdecrypted || !pwdstore_decrypt_secrets(secrets))) { + PwdEntry *cred = pwdstore_get(secrets, id); + if(cred) { + *user = cred->user; + *password = cred->password; + ret = 1; + } + } + + free(req_url_proto.ptr); + cxListDestroy(locations); + + return ret; +}