port more dav config code

Mon, 29 Jan 2024 12:09:24 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 29 Jan 2024 12:09:24 +0100
changeset 7
905ac52c910f
parent 6
09ac07345656
child 8
726b24766437

port more dav config code

application/application.c file | annotate | diff | comparison | revisions
application/application.h file | annotate | diff | comparison | revisions
application/config.c file | annotate | diff | comparison | revisions
application/config.h file | annotate | diff | comparison | revisions
application/window.c file | annotate | diff | comparison | revisions
application/window.h file | annotate | diff | comparison | revisions
--- 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);
 
 

mercurial