Mon, 29 Jan 2024 12:09:24 +0100
port more dav config code
--- a/application/application.c Mon Jan 29 10:41:00 2024 +0100 +++ b/application/application.c Mon Jan 29 12:09:24 2024 +0100 @@ -51,10 +51,10 @@ exit(-1); } - UiObject* win = window_create(); + UiObject *win = window_create(); - DavApp* app = application_create_app_document(); - UiContext* global = ui_global_context(); + DavApp *app = application_create_app_document(); + UiContext *global = ui_global_context(); ui_attach_document(global, app); ui_show(win); @@ -68,7 +68,7 @@ ui_toolbar_item("Home", .icon = "Home"); ui_toolbar_item("NewWindow", .icon = "NewWindow"); ui_toolbar_menu("Repo", .label = "Repository") { - ui_menu_itemlist(.varname = "repolist"); + ui_menu_itemlist(.varname = "repolist", .onselect = action_repo_selected); } ui_toolbar_item("Refresh", .icon = "Refresh"); ui_toolbar_item("NewFolder", .icon = "NewFolder"); @@ -76,8 +76,8 @@ ui_toolbar_item("Upload", .label = "Upload", .icon = "Upload"); ui_toolbar_item("Download", .icon = "SaveLocal"); ui_toolbar_item("Remove", .icon = "Delete"); - ui_toolbar_item("LocalBrowser", .icon = "DockLeft", .label = "Local Browser"); - ui_toolbar_item("PreviewPane", .icon = "DockRight"); + ui_toolbar_toggleitem("LocalBrowser", .icon = "DockLeft", .label = "Local Browser"); + ui_toolbar_toggleitem("PreviewPane", .icon = "DockRight"); ui_toolbar_appmenu() { ui_menuitem("TODO", NULL); @@ -101,8 +101,8 @@ DavApp* application_create_app_document(void) { - DavApp* doc = ui_document_new(sizeof(DavApp)); - UiContext* ctx = ui_document_context(doc); + DavApp *doc = ui_document_new(sizeof(DavApp)); + UiContext *ctx = ui_document_context(doc); doc->repos = ui_list_new(ctx, "repolist"); // create repo list @@ -111,15 +111,26 @@ return doc; } -void application_update_repolist(DavApp* app) { - DavConfig* config = get_config(); - DavCfgRepository* repo = config->repositories; +void application_update_repolist(DavApp *app) { + DavConfig *config = get_config(); + DavCfgRepository *repo = config->repositories; + // TODO: free list content ptr ui_list_clear(app->repos); - for (DavCfgRepository* repo = config->repositories; repo; repo = repo->next) { + for (DavCfgRepository *repo = config->repositories; repo; repo = repo->next) { // TODO: copy repo name ui_list_append(app->repos, repo->name.value.ptr); } } + + +DavContext* application_dav_context(void) { + return davctx; +} + + +void action_repo_selected(UiEvent *event, void *data) { + +}
--- a/application/application.h Mon Jan 29 10:41:00 2024 +0100 +++ b/application/application.h Mon Jan 29 12:09:24 2024 +0100 @@ -44,8 +44,7 @@ typedef struct DavApp { DavConfig *dav_config; - UiList* repos; - + UiList *repos; } DavApp; @@ -66,6 +65,11 @@ void application_update_repolist(DavApp *app); +DavContext* application_dav_context(void); + + +void action_repo_selected(UiEvent *event, void *data); + #ifdef __cplusplus } #endif
--- a/application/config.c Mon Jan 29 10:41:00 2024 +0100 +++ b/application/config.c Mon Jan 29 12:09:24 2024 +0100 @@ -326,3 +326,209 @@ 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); + locations->simple_destructor = (cx_destructor_func)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; +}
--- a/application/config.h Mon Jan 29 10:41:00 2024 +0100 +++ b/application/config.h Mon Jan 29 12:09:24 2024 +0100 @@ -64,6 +64,8 @@ int request_auth(DavSession* sn, void* userdata); +DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc); + #ifdef __cplusplus } #endif
--- a/application/window.c Mon Jan 29 10:41:00 2024 +0100 +++ b/application/window.c Mon Jan 29 12:09:24 2024 +0100 @@ -34,6 +34,12 @@ UiObject* window_create(void) { UiObject* obj = ui_window("iDAV", NULL); + MainWindow* wdata = ui_malloc(obj->ctx, sizeof(MainWindow)); + memset(wdata, 0, sizeof(MainWindow)); + obj->window = wdata; + + wdata->dav_queue = ui_threadpool_create(1); + // navigation bar ui_hbox(obj, .fill = UI_OFF, .margin = 8) { ui_button(obj, .icon = "Back");
--- a/application/window.h Mon Jan 29 10:41:00 2024 +0100 +++ b/application/window.h Mon Jan 29 12:09:24 2024 +0100 @@ -30,11 +30,20 @@ #define IDAV_WINDOW_H #include <ui/ui.h> +#include <libidav/webdav.h> #ifdef __cplusplus extern "C" { #endif +/* + * toplevel UiObject* window data + */ +typedef struct MainWindow { + DavSession *sn; + UiThreadpool *dav_queue; +} MainWindow; + UiObject* window_create(void);