#include "settings.h"
#include <cx/list.h>
#include <cx/array_list.h>
#include <cx/printf.h>
#include <libidav/utils.h>
#define SETTINGS_STATE_REPOLIST_SELECTED 1
#define SETTINGS_STATE_REPO_ENCRYPTION 20
#define SETTINGS_STATE_CREDENTIALS_SELECTED 30
#define SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED 31
#define SETTINGS_STATE_KEYS_SELECTED 40
#define SETTINGS_STATE_DISABLED 9999
static void repolist_activate(UiEvent *event,
void *userdata) {
UiListSelection *selection = event->eventdata;
SettingsWindow *settings = event->window;
settings_edit_repository(settings, selection->rows[
0]);
}
static void repolist_selection(UiEvent *event,
void *userdata) {
UiListSelection *selection = event->eventdata;
SettingsWindow *settings = event->window;
if(selection->count >
0) {
ui_set_group(event->obj->ctx,
SETTINGS_STATE_REPOLIST_SELECTED);
settings->selected_repo = selection->rows[
0];
}
else {
ui_unset_group(event->obj->ctx,
SETTINGS_STATE_REPOLIST_SELECTED);
}
}
static void repolist_edit(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings->selected_repo >=
0) {
settings_edit_repository(settings, settings->selected_repo);
}
}
static void repolist_add(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
settings->repo_new =
TRUE;
settings->selected_repo = -
1;
settings_clear_repository(settings);
ui_set(settings->repo_tabview,
1);
}
static void repolist_remove(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
DavCfgRepository *repo = ui_list_get(settings->repos, settings->selected_repo);
if(!repo) {
fprintf(stderr,
"Error: cannot get repository at index %d\n", settings->selected_repo);
return;
}
dav_repository_remove_and_free(settings->config, repo);
settings_update_repolist(settings);
settings->selected_repo = -
1;
ui_unset_group(event->obj->ctx,
SETTINGS_STATE_REPOLIST_SELECTED);
}
static void editrepo_go_back(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
settings_store_repository(settings);
ui_set(settings->repo_tabview,
0);
}
static void credentials_add(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings_credentials_save(settings)) {
return;
}
if(settings->credentials_list_needs_update) {
settings->credentials_ignore_selectionevent =
TRUE;
ui_list_update(settings->credentials_users);
settings_reload_repo_credentials(settings);
settings->credentials_ignore_selectionevent =
FALSE;
settings->credentials_list_needs_update =
FALSE;
}
settings_credentials_clear(settings);
settings->credentials_new =
TRUE;
PwdStore *pwd = settings->pwdstore;
if(!pwd->isdecrypted) {
settings_credentials_decrypt(settings);
return;
}
settings_credentials_select(settings,
NULL);
}
static void addcred_onclick(UiEvent *event,
void *userdata) {
SettingsNewCredentialsDialog *wdata = event->window;
if(event->intval ==
4) {
char *id = ui_get(wdata->id);
char *user = ui_get(wdata->user);
char *password = ui_get(wdata->password);
int addlocation = ui_get(wdata->addlocation);
if(strlen(id) ==
0) {
ui_set(wdata->message,
"Missing identifier!");
return;
}
if(strlen(user) ==
0) {
ui_set(wdata->message,
"Missing user!");
return;
}
if(strlen(password) ==
0) {
ui_set(wdata->message,
"Missing password!");
return;
}
if(pwdstore_has_id(wdata->settings->pwdstore, id)) {
ui_set(wdata->message,
"Identifier already in use!");
return;
}
CxList *locations =
NULL;
if(addlocation) {
locations = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
cxListAdd(locations, strdup(wdata->url));
}
pwdstore_put(wdata->settings->pwdstore, id, user, password);
pwdstore_put_index(wdata->settings->pwdstore, strdup(id), locations);
settings_reload_credentials(wdata->settings);
settings_reload_repo_credentials(wdata->settings);
CxList *list = wdata->settings->repo_credentials->data;
ssize_t index = cxListFind(list, id);
if(index >=
0) {
ui_list_setselection(wdata->settings->repo_credentials, index);
}
}
ui_close(event->obj);
}
static void credentials_new(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
UiObject *dialog = ui_dialog_window(event->obj,
.modal =
UI_ON,
.title =
"New Credentials",
.show_closebutton =
UI_OFF,
.lbutton1 =
"Cancel",
.rbutton4 =
"Add",
.onclick = addcred_onclick);
SettingsNewCredentialsDialog *wdata = ui_malloc(dialog->ctx,
sizeof(SettingsNewCredentialsDialog));
wdata->settings = settings;
wdata->id = ui_string_new(dialog->ctx,
NULL);
wdata->user = ui_string_new(dialog->ctx,
NULL);
wdata->password = ui_string_new(dialog->ctx,
NULL);
wdata->addlocation = ui_int_new(dialog->ctx,
NULL);
wdata->message = ui_string_new(dialog->ctx,
NULL);
dialog->window = wdata;
char *url = ui_get(settings->repo_url);
if(strlen(url) >
0) {
wdata->url = ui_strdup(dialog->ctx, url);
}
else {
wdata->url =
NULL;
}
ui_grid(dialog, .margin =
16, .columnspacing =
40, .rowspacing =
10) {
ui_llabel(dialog, .label =
"Identifier");
ui_textfield(dialog, .value = wdata->id, .hexpand =
TRUE);
ui_newline(dialog);
ui_llabel(dialog, .label =
"User");
ui_textfield(dialog, .value = wdata->user, .hexpand =
TRUE);
ui_newline(dialog);
ui_llabel(dialog, .label =
"Password");
ui_passwordfield(dialog, .value = wdata->password, .hexpand =
TRUE);
ui_newline(dialog);
if(wdata->url) {
cxmutstr msg = cx_asprintf(
"Add URL %s to Credential Locations", url);
ui_checkbox(dialog, .label = msg.ptr, .value = wdata->addlocation, .colspan =
2);
ui_newline(dialog);
free(msg.ptr);
}
ui_llabel(dialog, .value = wdata->message, .colspan =
2);
}
ui_show(dialog);
}
static void credentials_remove(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings->credentials_selected_id) {
pwdstore_remove_entry(settings->pwdstore, settings->credentials_selected_id);
ui_list_remove(settings->credentials_users, settings->credentials_selected_index);
ui_list_update(settings->credentials_users);
settings_reload_repo_credentials(settings);
}
}
static void credentials_onselect(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings->credentials_ignore_selectionevent) {
return;
}
UiListSelection *sel = event->eventdata;
if(settings_credentials_save(settings)) {
return;
}
if(sel->count >
0) {
const char *id = ui_list_get(settings->credentials_users, sel->rows[
0]);
settings->credentials_selected_index = sel->rows[
0];
settings_credentials_select(settings, id);
if(settings->credentials_list_needs_update) {
settings->credentials_ignore_selectionevent =
TRUE;
ui_list_update(settings->credentials_users);
settings_reload_repo_credentials(settings);
ui_list_setselection(settings->credentials_users, sel->rows[
0]);
settings->credentials_ignore_selectionevent =
FALSE;
settings->credentials_list_needs_update =
FALSE;
}
}
else {
settings_credentials_clear(settings);
}
}
static void c_add_location(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(event->intval ==
1) {
ui_list_append(settings->credentials_locations, ui_strdup(event->obj->ctx, event->eventdata));
ui_list_update(settings->credentials_locations);
}
}
static void credentials_location_add(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
ui_dialog(event->obj,
.title =
"Add Location",
.content =
"New Location URL",
.input =
TRUE,
.result = c_add_location,
.button1_label =
"Add Location",
.closebutton_label =
"Cancel");
}
static void c_edit_location(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(event->intval ==
1) {
CxList *list = settings->credentials_locations->data;
ssize_t i = cxListFind(list, userdata);
if(i >=
0) {
cxListRemove(list, i);
cxListInsert(list, i, ui_strdup(event->obj->ctx, event->eventdata));
ui_list_update(settings->credentials_locations);
}
}
}
static void credentials_location_edit(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
char *location = ui_list_get(settings->credentials_locations, settings->credentials_location_selected_index);
if(!location) {
return;
}
ui_dialog(event->obj,
.title =
"Edit Location",
.content =
"Location URL",
.input_value = location,
.input =
TRUE,
.result = c_edit_location,
.resultdata = location,
.button1_label =
"Edit Location",
.closebutton_label =
"Cancel");
}
static void credentials_location_remove(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings->credentials_location_selected_index >=
0) {
CxList *list = settings->credentials_locations->data;
cxListRemove(list, settings->credentials_location_selected_index);
ui_list_update(settings->credentials_locations);
}
}
static void credentials_location_up(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
int index = settings->credentials_location_selected_index;
if(index >=
1) {
CxList *list = settings->credentials_locations->data;
cxListSwap(list, index, index-
1);
ui_list_update(settings->credentials_locations);
ui_list_setselection(settings->credentials_locations, index-
1);
}
}
static void credentials_location_down(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
int index = settings->credentials_location_selected_index;
if(index >=
0 && index +
1 < ui_list_count(settings->credentials_locations)) {
CxList *list = settings->credentials_locations->data;
cxListSwap(list, index, index+
1);
ui_list_update(settings->credentials_locations);
ui_list_setselection(settings->credentials_locations, index+
1);
}
}
static void credentials_location_onselect(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
UiListSelection *sel = event->eventdata;
if(sel->count >
0) {
settings->credentials_location_selected_index = sel->rows[
0];
ui_set_group(event->obj->ctx,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED);
}
else {
settings->credentials_location_selected_index = -
1;
ui_unset_group(event->obj->ctx,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED);
}
}
static void credentials_setmasterpw(UiEvent *event,
void *userdata) {
if(event->intval ==
1) {
SettingsWindow *settings = event->window;
char *pw = event->eventdata;
size_t pwlen = strlen(pw);
if(pwlen >
0) {
pwdstore_setpassword(settings->pwdstore, event->eventdata);
memset(pw,
0, pwlen);
if(!pwdstore_decrypt(settings->pwdstore)) {
settings_credentials_select(settings,
NULL);
}
else {
ui_dialog(event->obj, .title =
"Error", .content =
"Cannot decrypt Secret Store", .closebutton_label =
"OK");
}
}
}
}
void settings_credentials_decrypt(SettingsWindow *settings) {
ui_dialog(settings->obj,
.title =
"Secret Store",
.content =
"Master password",
.password =
TRUE,
.result = credentials_setmasterpw,
.button1_label =
"Decrypt Secret Store",
.closebutton_label =
"Cancel");
}
#define ADDKEY_DIALOG_STATE_NO_IMPORT 10
static void addkey_onclick(UiEvent *event,
void *userdata) {
SettingsNewKeyDialog *wdata = event->window;
if(event->intval ==
4) {
ui_set(wdata->message,
"");
char *name = ui_get(wdata->name);
if(strlen(name) ==
0) {
ui_set(wdata->message,
"A name must be specified!");
return;
}
char *file = ui_get(wdata->file);
if(strlen(file) ==
0) {
ui_set(wdata->message,
"A file path must be specified!");
return;
}
UiListSelection sel = ui_list_getselection(wdata->type);
if(sel.count ==
0) {
ui_set(wdata->message,
"No type selected!");
return;
}
DavCfgKeyType type = sel.rows[
0];
char *path =
NULL;
char *cfg_path =
NULL;
if(file[
0] ==
'/') {
path = file;
}
else {
cfg_path = config_file_path(file);
path = cfg_path;
}
FILE *f = fopen(path,
"w");
if(!f) {
cxmutstr msg = cx_asprintf(
"Error: cannot write file ''%s'': %s", path, strerror(errno));
ui_set(wdata->message, msg.ptr);
free(msg.ptr);
free(cfg_path);
return;
}
free(cfg_path);
unsigned char key[
32];
dav_rand_bytes(key,
32);
size_t len;
switch(type) {
default:
case DAV_KEY_TYPE_AES256: len =
32;
break;
case DAV_KEY_TYPE_AES128: len =
16;
break;
}
size_t w = fwrite(key,
1, len, f);
fclose(f);
if(w != len) {
cxmutstr msg = cx_asprintf(
"Could not write key data: %s", strerror(errno));
ui_set(wdata->message, msg.ptr);
free(msg.ptr);
return;
}
DavCfgKey *newkey = dav_key_new(wdata->settings->config);
const CxAllocator *a = wdata->settings->config->mp->allocator;
newkey->name.value = cx_strdup_a(a, cx_str(name));
newkey->file.value = cx_strdup_a(a, cx_str(file));
newkey->type = type;
dav_config_add_key(wdata->settings->config, newkey);
settings_reload_repo_keys(wdata->settings);
settings_reload_keys(wdata->settings);
if(wdata->add_to_repo && (wdata->settings->selected_repo >=
0 || wdata->settings->repo_new)) {
CxList *repo_keys = wdata->settings->repo_keys->data;
ssize_t index = cxListFind(repo_keys, name);
if(index >=
0) {
ui_list_setselection(wdata->settings->repo_keys, index);
}
}
}
ui_close(event->obj);
}
static void addkey_name_changed(UiEvent *event,
void *userdata) {
SettingsNewKeyDialog *wdata = event->window;
char *name = ui_get(wdata->name);
cxmutstr file = cx_asprintf(
"keys/%s", name);
ui_set(wdata->file, file.ptr);
free(file.ptr);
}
static void keys_add(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
UiObject *dialog = ui_dialog_window(event->obj,
.modal =
UI_ON,
.title =
"Add Key",
.show_closebutton =
UI_OFF,
.lbutton1 =
"Cancel",
.rbutton4 =
"Add",
.default_button =
4,
.onclick = addkey_onclick);
SettingsNewKeyDialog *wdata = ui_malloc(dialog->ctx,
sizeof(SettingsNewKeyDialog));
wdata->settings = settings;
wdata->name = ui_string_new(dialog->ctx,
NULL);
wdata->file = ui_string_new(dialog->ctx,
NULL);
wdata->import_path = ui_string_new(dialog->ctx,
NULL);
wdata->message = ui_string_new(dialog->ctx,
NULL);
wdata->secretstore = ui_int_new(dialog->ctx,
NULL);
wdata->type = ui_list_new(dialog->ctx,
NULL);
wdata->add_to_repo = userdata ?
TRUE :
FALSE;
dialog->window = wdata;
ui_list_append(wdata->type,
"AES256");
ui_list_append(wdata->type,
"AES128");
ui_grid(dialog, .margin =
16, .columnspacing =
40, .rowspacing =
10) {
ui_llabel(dialog, .label =
"Name");
ui_textfield(dialog, .value = wdata->name, .onchange = addkey_name_changed, .hexpand =
TRUE);
ui_newline(dialog);
ui_llabel(dialog, .label =
"File");
ui_textfield(dialog, .value = wdata->file);
ui_newline(dialog);
ui_llabel(dialog, .label =
"Type");
ui_combobox(dialog, .list = wdata->type, .groups =
UI_GROUPS(
ADDKEY_DIALOG_STATE_NO_IMPORT));
ui_newline(dialog);
ui_llabel(dialog, .value = wdata->message, .colspan =
2);
}
ui_set_group(dialog->ctx,
ADDKEY_DIALOG_STATE_NO_IMPORT);
ui_list_setselection(wdata->type,
0);
ui_show(dialog);
}
static void keys_remove(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
DavCfgKey *key = ui_list_get(settings->keys_list, settings->keys_selected_index);
if(key) {
dav_key_remove_and_free(settings->config, key);
ui_list_remove(settings->keys_list, settings->keys_selected_index);
ui_list_update(settings->keys_list);
settings_reload_repo_keys(settings);
}
}
static void keys_onselect(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
UiListSelection *sel = event->eventdata;
if(sel->count >
0) {
settings->keys_selected_index = sel->rows[
0];
DavCfgKey *key = ui_list_get(settings->keys_list, sel->rows[
0]);
if(key) {
settings_edit_key(settings, key);
ui_set_group(event->obj->ctx,
SETTINGS_STATE_KEYS_SELECTED);
}
}
else {
settings->keys_selected_index = -
1;
settings_clear_key(settings);
ui_unset_group(event->obj->ctx,
SETTINGS_STATE_KEYS_SELECTED);
}
}
static void list_str_destructor(
void *data,
void *ptr) {
UiContext *ctx = data;
char *s = ptr;
ui_free(ctx, ptr);
}
static void secretstore_newmasterpw(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(event->intval ==
1) {
pwdstore_setpassword(settings->pwdstore, event->eventdata);
set_pwdstore(settings->pwdstore);
pwdstore_save(settings->pwdstore);
settings->pwdstore =
NULL;
}
ui_close(event->obj);
}
void settings_ok(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
settings_store_repository(settings);
settings_credentials_save(settings);
set_config(settings->config);
if(store_config()) {
ui_dialog(event->obj, .title =
"Error", .content =
"Cannot store settings", .closebutton_label =
"OK");
}
application_update_repolist(get_application()); settings->config =
NULL;
if(settings->credentials_modified) {
if(settings->pwdstore->key) {
set_pwdstore(settings->pwdstore);
pwdstore_save(settings->pwdstore);
}
else {
ui_dialog(event->obj,
.title =
"Secret Store",
.content =
"Master password",
.password =
TRUE,
.result = secretstore_newmasterpw,
.button1_label =
"Create Secret Store",
.closebutton_label =
"Cancel",
.result = secretstore_newmasterpw);
return;
}
settings->pwdstore =
NULL;
}
ui_close(event->obj);
}
void settings_close(UiEvent *event,
void *userdata) {
SettingsWindow *settings = event->window;
if(settings->config) {
dav_config_free(settings->config);
}
if(settings->pwdstore) {
pwdstore_free(settings->pwdstore);
}
}
void settings_cancel(UiEvent *event,
void *userdata) {
ui_close(event->obj);
}
void settings_window_open() {
DavConfig *config = load_config_file();
if(!config) {
return;
}
PwdStore *pwdstore = get_pwdstore();
pwdstore = pwdstore ? pwdstore_clone(pwdstore) : pwdstore_new();
UiObject *obj = ui_simple_window(
"Settings",
NULL);
ui_context_closefunc(obj->ctx, settings_close,
NULL);
SettingsWindow *wdata = ui_malloc(obj->ctx,
sizeof(SettingsWindow));
memset(wdata,
0,
sizeof(SettingsWindow));
wdata->config = config;
wdata->pwdstore = pwdstore;
obj->window = wdata;
wdata->obj = obj;
settings_init(obj, wdata);
ui_tabview(obj, .tabview =
UI_TABVIEW_NAVIGATION_TOP) {
ui_tab(obj,
"General") {
ui_grid(obj, .margin =
10) {
ui_label(obj, .label =
"TODO");
}
}
ui_tab(obj,
"Repositories") {
ui_tabview(obj, .value = wdata->repo_tabview, .tabview =
UI_TABVIEW_INVISIBLE) {
ui_tab(obj,
"list") {
ui_grid(obj, .margin =
16, .columnspacing =
10, .rowspacing =
10) {
ui_hbox(obj, .spacing =
4) {
ui_button(obj, .label =
"Add", .onclick = repolist_add);
ui_button(obj, .label =
"Edit", .onclick = repolist_edit, .groups =
UI_GROUPS(
SETTINGS_STATE_REPOLIST_SELECTED));
ui_button(obj, .label =
"Remove", .onclick = repolist_remove, .groups =
UI_GROUPS(
SETTINGS_STATE_REPOLIST_SELECTED));
}
ui_newline(obj);
UiModel* model = ui_model(obj->ctx,
UI_STRING,
"Name",
UI_STRING,
"URL",
UI_STRING,
"User",
UI_STRING,
"Encrypted", -
1);
model->getvalue = (ui_getvaluefunc) settings_repolist_getvalue;
ui_table(obj,
.model = model,
.list = wdata->repos,
.multiselection =
FALSE,
.onactivate = repolist_activate,
.onselection = repolist_selection,
.vexpand =
TRUE, .hexpand =
TRUE, .colspan =
3);
}
}
ui_tab(obj,
"repo") {
ui_vbox(obj, .margin =
16, .spacing =
10) {
ui_hbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_button(obj, .icon =
UI_ICON_GO_BACK, .onclick = editrepo_go_back);
ui_label(obj, .label =
"Repository List");
}
ui_scrolledwindow(obj, .hexpand =
TRUE, .vexpand =
TRUE, .subcontainer =
UI_CONTAINER_NO_SUB) {
ui_grid(obj, .margin =
10, .columnspacing =
10, .rowspacing =
10) {
ui_llabel(obj, .label =
"Name");
ui_textfield(obj, .value = wdata->repo_name, .width =
15);
ui_newline(obj);
ui_llabel(obj, .label =
"URL");
ui_textfield(obj, .value = wdata->repo_url, .hexpand =
TRUE);
ui_newline(obj);
ui_llabel(obj, .label =
"Credentials", .style =
UI_LABEL_STYLE_TITLE, .colspan =
2);
ui_newline(obj);
ui_hbox(obj, .spacing =
4, .colspan =
2) {
ui_combobox(obj, .list = wdata->repo_credentials);
ui_button(obj, .label =
"New Credentials", .onclick = credentials_new);
}
ui_newline(obj);
ui_expander(obj, .spacing =
10, .colspan =
2, .label =
"Unencrypted User/Password", .margin =
10) {
ui_llabel(obj, .label =
"Store the credentials unencrypted in the repository and not in the secret store", .style =
UI_LABEL_STYLE_DIM);
ui_grid(obj, .rowspacing =
10, .columnspacing =
10, .fill =
UI_OFF) {
ui_llabel(obj, .label =
"User");
ui_textfield(obj, .value = wdata->repo_user, .width =
15);
ui_newline(obj);
ui_llabel(obj, .label =
"Password");
ui_passwordfield(obj, .value = wdata->repo_password, .width =
15);
}
}
ui_newline(obj);
ui_llabel(obj, .label =
"Encryption", .style =
UI_LABEL_STYLE_TITLE, .colspan =
2);
ui_newline(obj);
ui_checkbox(obj, .label =
"Enable client-side encryption", .value = wdata->repo_encryption, .colspan =
2, .enable_group =
SETTINGS_STATE_REPO_ENCRYPTION);
ui_newline(obj);
ui_llabel(obj, .label =
"Default key");
ui_hbox(obj, .spacing =
4) {
ui_combobox(obj, .list = wdata->repo_keys, .groups =
UI_GROUPS(
SETTINGS_STATE_REPO_ENCRYPTION));
ui_button(obj, .label =
"Generate Key", .onclick = keys_add, .onclickdata =
"repo", .groups =
UI_GROUPS(
SETTINGS_STATE_REPO_ENCRYPTION));
}
ui_newline(obj);
ui_llabel(obj, .label =
"TLS", .style =
UI_LABEL_STYLE_TITLE, .colspan =
2);
ui_newline(obj);
ui_llabel(obj, .label =
"Cert Path");
ui_hbox0(obj) {
ui_textfield(obj, .value = wdata->repo_cacert, .width =
15);
}
ui_newline(obj);
ui_llabel(obj, .label =
"TLS Version");
ui_hbox0(obj) {
ui_combobox(obj, .list = wdata->repo_tls_versions);
}
ui_newline(obj);
ui_checkbox(obj, .label =
"Disable TLS verification", .value = wdata->repo_disable_verification, .colspan =
2);
}
}
}
}
}
}
ui_tab(obj,
"Sync Directories") {
ui_grid(obj, .margin =
10) {
ui_label(obj, .label =
"TODO");
}
}
ui_tab(obj,
"Credentials") {
ui_hbox(obj, .margin =
16, .spacing =
30) {
ui_vbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_hbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_button(obj, .label =
"Add", .onclick = credentials_add);
ui_button(obj, .label =
"Remove", .onclick = credentials_remove, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
}
ui_listview(obj, .list = wdata->credentials_users, .fill =
UI_ON, .onselection = credentials_onselect);
}
ui_grid(obj, .columnspacing =
30, .rowspacing =
10) {
ui_llabel(obj, .label =
"Identifier");
ui_textfield(obj, .value = wdata->credentials_id, .hexpand =
TRUE, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
ui_newline(obj);
ui_llabel(obj, .label =
"User");
ui_textfield(obj, .value = wdata->credentials_user, .hexpand =
TRUE, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
ui_newline(obj);
ui_llabel(obj, .label =
"Password");
ui_passwordfield(obj, .value = wdata->credentials_password, .hexpand =
TRUE, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
ui_newline(obj);
ui_label(obj, .label =
" ");
ui_newline(obj);
ui_llabel(obj, .style =
UI_LABEL_STYLE_TITLE, .label =
"Locations", .colspan =
2);
ui_newline(obj);
ui_llabel(obj, .style =
UI_LABEL_STYLE_DIM, .label =
"List of URLs for which these credentials should be used (optional)", .colspan =
2);
ui_newline(obj);
ui_hbox(obj, .colspan =
2, .vexpand =
TRUE, .hexpand =
TRUE, .spacing =
10) {
#ifndef UI_WINUI
ui_callback credentials_activate_callback = credentials_location_edit;
#else
ui_callback credentials_activate_callback =
NULL;
#endif
ui_listview(obj, .list = wdata->credentials_locations, .onactivate = credentials_activate_callback, .onselection = credentials_location_onselect, .colspan =
2, .fill =
UI_ON, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
ui_vbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_button(obj, .label =
"Add", .onclick = credentials_location_add, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED));
ui_button(obj, .label =
"Edit", .onclick = credentials_location_edit, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED));
ui_button(obj, .label =
"Remove", .onclick = credentials_location_remove, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED));
ui_button(obj, .label =
"Move Up", .onclick = credentials_location_up, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED));
ui_button(obj, .label =
"Move Down", .onclick = credentials_location_down, .groups =
UI_GROUPS(
SETTINGS_STATE_CREDENTIALS_SELECTED,
SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED));
}
}
}
}
}
ui_tab(obj,
"Keys") {
ui_hbox(obj, .margin =
16, .spacing =
30) {
ui_vbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_hbox(obj, .fill =
UI_OFF, .spacing =
4) {
ui_button(obj, .label =
"Add", .onclick = keys_add);
ui_button(obj, .label =
"Remove", .onclick = keys_remove, .groups =
UI_GROUPS(
SETTINGS_STATE_KEYS_SELECTED));
}
ui_listview(obj, .list = wdata->keys_list, .fill =
UI_ON, .onselection = keys_onselect, .getvalue = keylist_getvalue);
}
ui_grid(obj, .columnspacing =
30, .rowspacing =
10) {
ui_llabel(obj, .label =
"Identifier");
ui_textfield(obj, .value = wdata->key_name, .groups =
UI_GROUPS(
SETTINGS_STATE_KEYS_SELECTED));
ui_newline(obj);
ui_llabel(obj, .label =
"Type");
ui_textfield(obj, .value = wdata->key_type, .groups =
UI_GROUPS(
SETTINGS_STATE_DISABLED));
ui_newline(obj);
ui_llabel(obj, .label =
"File");
ui_textfield(obj, .value = wdata->key_file, .groups =
UI_GROUPS(
SETTINGS_STATE_KEYS_SELECTED));
}
}
}
}
ui_hbox(obj, .fill =
UI_OFF, .margin =
10) {
ui_button(obj, .label =
"Cancel", .onclick = settings_cancel);
ui_label(obj, .fill =
UI_ON);
ui_button(obj, .label =
"Save", .onclick = settings_ok);
}
ui_show(obj);
}
void settings_init(UiObject *obj, SettingsWindow *settings) {
settings->repos = ui_list_new(obj->ctx,
NULL);
settings->repo_tabview = ui_int_new(obj->ctx,
NULL);
settings->repo_name = ui_string_new(obj->ctx,
NULL);
settings->repo_url = ui_string_new(obj->ctx,
NULL);
settings->repo_user = ui_string_new(obj->ctx,
NULL);
settings->repo_password = ui_string_new(obj->ctx,
NULL);
settings->repo_cacert = ui_string_new(obj->ctx,
NULL);
settings->repo_credentials = ui_list_new(obj->ctx,
NULL);
settings->repo_keys = ui_list_new(obj->ctx,
NULL);
settings->repo_tls_versions = ui_list_new(obj->ctx,
NULL);
settings->repo_encryption = ui_int_new(obj->ctx,
NULL);
settings->repo_disable_verification = ui_int_new(obj->ctx,
NULL);
CxList *repo_keys = settings->repo_keys->data;
repo_keys->collection.cmpfunc = (cx_compare_func)strcmp;
ui_list_append(settings->repo_tls_versions,
"Default");
ui_list_append(settings->repo_tls_versions,
"TLSv1.3");
ui_list_append(settings->repo_tls_versions,
"TLSv1.2");
ui_list_append(settings->repo_tls_versions,
"TLSv1.1");
ui_list_append(settings->repo_tls_versions,
"TLSv1.0");
settings->credentials_selected_index = -
1;
settings->credentials_users = ui_list_new(obj->ctx,
NULL);
settings->credentials_locations = ui_list_new(obj->ctx,
NULL);
settings->credentials_id = ui_string_new(obj->ctx,
NULL);
settings->credentials_user = ui_string_new(obj->ctx,
NULL);
settings->credentials_password = ui_string_new(obj->ctx,
NULL);
CxList *credentials_users = settings->credentials_users->data;
CxList *credentials_locations = settings->credentials_locations->data;
credentials_users->collection.advanced_destructor = list_str_destructor;
credentials_users->collection.destructor_data = settings->obj->ctx;
credentials_users->collection.cmpfunc = (cx_compare_func)strcmp;
credentials_locations->collection.advanced_destructor = list_str_destructor;
credentials_locations->collection.destructor_data = settings->obj->ctx;
credentials_locations->collection.cmpfunc = (cx_compare_func)strcmp;
settings->keys_list = ui_list_new(obj->ctx,
NULL);
settings->key_name = ui_string_new(obj->ctx,
NULL);
settings->key_type = ui_string_new(obj->ctx,
NULL);
settings->key_file = ui_string_new(obj->ctx,
NULL);
settings->keys_selected_index = -
1;
settings_update_repolist(settings);
settings_reload_repo_keys(settings);
settings_reload_credentials(settings);
settings_reload_repo_credentials(settings);
settings_reload_keys(settings);
settings->selected_repo = -
1;
}
#define SETTINGS_SET_STRING(str, setting)
if(setting.value.ptr) ui_set(str, setting.value.ptr);
void settings_edit_repository(SettingsWindow *settings,
int repo_index) {
DavCfgRepository *repo = ui_list_get(settings->repos, repo_index);
if(!repo) {
fprintf(stderr,
"Error: cannot get repository at index %d\n", repo_index);
return;
}
settings->selected_repo = repo_index;
SETTINGS_SET_STRING(settings->repo_name, repo->name);
SETTINGS_SET_STRING(settings->repo_url, repo->url);
SETTINGS_SET_STRING(settings->repo_cacert, repo->cert);
SETTINGS_SET_STRING(settings->repo_user, repo->user);
if(repo->password.value.ptr) {
char *decoded_pw = util_base64decode(repo->password.value.ptr);
ui_set(settings->repo_password, decoded_pw);
size_t decoded_pw_len = strlen(decoded_pw);
memset(decoded_pw,
0, decoded_pw_len);
free(decoded_pw);
}
CxList *cred = settings->repo_credentials->data;
cred->collection.cmpfunc = (cx_compare_func)strcmp;
ssize_t cred_index = repo->stored_user.value.ptr ? cxListFind(cred, repo->stored_user.value.ptr) :
0;
if(cred_index >
0) {
ui_list_setselection(settings->repo_credentials, cred_index);
}
else {
ui_list_setselection(settings->repo_credentials,
0);
}
ui_set(settings->repo_encryption, repo->full_encryption.value);
CxList *keys = settings->repo_keys->data;
keys->collection.cmpfunc = (cx_compare_func)strcmp;
ssize_t key_index = repo->default_key.value.ptr ? cxListFind(keys, repo->default_key.value.ptr) :
0;
if(key_index >
0) {
ui_list_setselection(settings->repo_keys, key_index);
}
else {
ui_list_setselection(settings->repo_keys,
0);
}
CxList *tlsVersions = settings->repo_tls_versions->data;
tlsVersions->collection.cmpfunc = (cx_compare_func)strcmp;
const char *tls_str = dav_tlsversion2str(repo->ssl_version.value);
if(!tls_str) tls_str =
"";
ssize_t tlsv_index = cxListFind(tlsVersions, tls_str);
if(tlsv_index >
0) {
ui_list_setselection(settings->repo_tls_versions, tlsv_index);
}
else {
ui_list_setselection(settings->repo_tls_versions,
0);
}
if(!repo->verification.value) {
ui_set(settings->repo_disable_verification,
TRUE);
}
ui_set(settings->repo_tabview,
1);
}
static void cfg_string_set_value_or_remove(DavConfig *config, CfgString *str, xmlNode *parent, cxstring new_value,
const char *nodename) {
if(new_value.length ==
0) {
new_value.ptr =
NULL;
}
dav_cfg_string_set_value(config, str, parent, new_value, nodename);
if(!new_value.ptr) {
dav_cfg_string_remove(str);
}
}
static cxstring default_list_get_value(UiList *list) {
cxstring ret = {
NULL,
0 };
UiListSelection sel = ui_list_getselection(list);
if(sel.count >
0) {
int index = sel.rows[
0];
if(index >
0) {
ret = cx_str(ui_list_get(list, index));
}
free(sel.rows);
}
return ret;
}
void settings_store_repository(SettingsWindow *settings) {
DavConfig *config = settings->config;
DavCfgRepository *repo;
if(settings->repo_new) {
settings->repo_new =
FALSE;
char *name = ui_get(settings->repo_name);
if(strlen(name) ==
0) {
return;
}
repo = dav_repository_new(config);
dav_config_add_repository(config, repo);
}
else if(settings->selected_repo >=
0) {
repo = ui_list_get(settings->repos, settings->selected_repo);
if(!repo) {
fprintf(stderr,
"Error: cannot get repository at index %d\n", settings->selected_repo);
return;
}
}
else {
return;
}
dav_cfg_string_set_value(config, &repo->name, repo->node, cx_str(ui_get(settings->repo_name)),
"name");
dav_cfg_string_set_value(config, &repo->url, repo->node, cx_str(ui_get(settings->repo_url)),
"url");
cfg_string_set_value_or_remove(config, &repo->user, repo->node, cx_str(ui_get(settings->repo_user)),
"user");
cfg_string_set_value_or_remove(config, &repo->cert, repo->node, cx_str(ui_get(settings->repo_cacert)),
"cert");
char *pw = ui_get(settings->repo_password);
size_t pwlen = strlen(pw);
if(pwlen >
0) {
char *pwenc = util_base64encode(pw, pwlen);
memset(pw,
0, pwlen);
dav_cfg_string_set_value(config, &repo->password, repo->node, cx_str(pwenc),
"password");
free(pwenc);
}
else {
cfg_string_set_value_or_remove(config, &repo->password, repo->node, cx_strn(
NULL,
0),
"password");
}
cxstring stored_user = default_list_get_value(settings->repo_credentials);
cfg_string_set_value_or_remove(config, &repo->stored_user, repo->node, stored_user,
"stored-user");
int encryption = ui_get(settings->repo_encryption);
if(encryption || repo->full_encryption.node) {
dav_cfg_bool_set_value(config, &repo->full_encryption, repo->node, encryption,
"full-encryption");
}
cxstring key = default_list_get_value(settings->repo_keys);
cfg_string_set_value_or_remove(config, &repo->default_key, repo->node, key,
"default-key");
int disable_verification = ui_get(settings->repo_disable_verification);
if(disable_verification) {
dav_cfg_bool_set_value(config, &repo->verification, repo->node, !disable_verification,
"verification");
}
else {
dav_cfg_bool_remove(&repo->verification);
}
cxstring tlsversion_str = default_list_get_value(settings->repo_tls_versions);
int tlsversion = dav_str2ssl_version(tlsversion_str.ptr);
if(tlsversion >=
0) {
dav_cfg_int_set_value(config, &repo->ssl_version, repo->node, tlsversion,
"ssl-version");
}
else {
dav_cfg_int_remove(&repo->ssl_version);
}
settings_update_repolist(settings);
settings->selected_repo = -
1;
}
void settings_clear_repository(SettingsWindow *settings) {
ui_set(settings->repo_name,
"");
ui_set(settings->repo_url,
"");
ui_set(settings->repo_user,
"");
ui_set(settings->repo_password,
"");
ui_set(settings->repo_cacert,
"");
ui_list_setselection(settings->repo_credentials,
0);
ui_list_setselection(settings->repo_keys,
0);
ui_list_setselection(settings->repo_tls_versions,
0);
ui_set(settings->repo_encryption,
0);
ui_set(settings->repo_disable_verification,
0);
}
void settings_update_repolist(SettingsWindow *settings) {
DavConfig *config = settings->config;
ui_list_clear(settings->repos);
for (DavCfgRepository *repo = config->repositories; repo; repo = repo->next) {
ui_list_append(settings->repos, repo);
}
if(settings->repos->update) {
ui_list_update(settings->repos);
}
}
void* settings_repolist_getvalue(DavCfgRepository *repo,
int col) {
switch(col) {
case 0: {
return repo->name.value.ptr;
}
case 1: {
return repo->url.value.ptr;
}
case 2: {
return repo->user.value.ptr ? repo->user.value.ptr : repo->stored_user.value.ptr;
}
case 3: {
return repo->full_encryption.value ?
"yes" :
"no";
}
}
return NULL;
}
void settings_reload_repo_keys(SettingsWindow *settings) {
DavConfig *config = settings->config;
DavCfgKey *key = config->keys;
ui_list_clear(settings->repo_keys);
ui_list_append(settings->repo_keys,
"-");
while(key) {
if(key->name.value.ptr) {
ui_list_append(settings->repo_keys, key->name.value.ptr);
}
key = key->next;
}
ui_list_update(settings->repo_keys);
}
void settings_reload_keys(SettingsWindow *settings) {
DavConfig *config = settings->config;
DavCfgKey *key = config->keys;
ui_list_clear(settings->keys_list);
while(key) {
if(key->name.value.ptr) {
ui_list_append(settings->keys_list, key);
}
key = key->next;
}
ui_list_update(settings->keys_list);
}
const char* dav_tlsversion2str(
int value) {
if(value == CURL_SSLVERSION_TLSv1) {
return "TLSv1";
}
else if(value == CURL_SSLVERSION_SSLv2) {
return "SSLv2";
}
else if(value == CURL_SSLVERSION_SSLv3) {
return "SSLv3";
}
#if LIBCURL_VERSION_MAJOR *
1000 +
LIBCURL_VERSION_MINOR >=
7034
else if(value == CURL_SSLVERSION_TLSv1_0) {
return "TLSv1.0";
}
else if(value == CURL_SSLVERSION_TLSv1_1) {
return "TLSv1.1";
}
else if(value == CURL_SSLVERSION_TLSv1_2) {
return "TLSv1.2";
}
#endif
#if LIBCURL_VERSION_MAJOR *
1000 +
LIBCURL_VERSION_MINOR >=
7052
else if(value == CURL_SSLVERSION_TLSv1_3) {
return "TLSv1.3";
}
#endif
return NULL;
}
void settings_reload_credentials(SettingsWindow *settings) {
PwdStore *pwd = settings->pwdstore;
ui_list_clear(settings->credentials_users);
CxIterator i = cxListIterator(pwd->noloc);
cx_foreach(PwdIndexEntry*, entry, i) {
char *id = ui_strdup(settings->obj->ctx, entry->id);
ui_list_append(settings->credentials_users, id);
}
i = cxListIterator(pwd->locations);
cx_foreach(PwdIndexEntry*, entry, i) {
char *id = ui_strdup(settings->obj->ctx, entry->id);
ui_list_append(settings->credentials_users, id);
}
ui_list_update(settings->credentials_users);
}
void settings_reload_repo_credentials(SettingsWindow *settings) {
PwdStore *pwd = settings->pwdstore;
ui_list_clear(settings->repo_credentials);
ui_list_append(settings->repo_credentials,
"-");
CxIterator i = cxListIterator(pwd->noloc);
cx_foreach(PwdIndexEntry*, entry, i) {
char *id = ui_strdup(settings->obj->ctx, entry->id);
ui_list_append(settings->repo_credentials, id);
}
i = cxListIterator(pwd->locations);
cx_foreach(PwdIndexEntry*, entry, i) {
char *id = ui_strdup(settings->obj->ctx, entry->id);
ui_list_append(settings->repo_credentials, id);
}
ui_list_update(settings->repo_credentials);
}
void settings_credentials_select(SettingsWindow *settings,
const char *id) {
if(!id && !settings->credentials_selected_id && !settings->credentials_new) {
fprintf(stderr,
"Error: no credentials id selected\n");
return;
}
PwdStore *pwd = settings->pwdstore;
if(id) {
ui_free(settings->obj->ctx, settings->credentials_selected_id);
settings->credentials_selected_id = ui_strdup(settings->obj->ctx, id);
}
if(!pwd->isdecrypted) {
settings_credentials_decrypt(settings);
return;
}
if(settings->credentials_new) {
ui_free(settings->obj->ctx, settings->credentials_selected_id);
settings->credentials_selected_id =
NULL;
settings->credentials_selected_index = ui_list_count(settings->credentials_users);
ui_list_append(settings->credentials_users, ui_strdup(settings->obj->ctx,
"new"));
settings->credentials_ignore_selectionevent =
TRUE;
ui_list_update(settings->credentials_users);
ui_list_setselection(settings->credentials_users, settings->credentials_selected_index);
settings->credentials_ignore_selectionevent =
FALSE;
ui_set(settings->credentials_id,
"new");
ui_set(settings->credentials_user,
"");
ui_set(settings->credentials_password,
"");
ui_list_clear(settings->credentials_locations);
ui_list_update(settings->credentials_locations);
}
else {
PwdEntry *entry = cxMapGet(pwd->ids, settings->credentials_selected_id);
PwdIndexEntry *index = cxMapGet(pwd->index, settings->credentials_selected_id);
if(!entry) {
fprintf(stderr,
"Error: cannot get pwd entry %s\n", settings->credentials_selected_id);
return;
}
if(!index) {
fprintf(stderr,
"Error: missing PwdIndexEntry. PwdStore may be broken.\n");
return;
}
ui_set(settings->credentials_id, entry->id);
ui_set(settings->credentials_user, entry->user);
ui_set(settings->credentials_password, entry->password);
ui_list_clear(settings->credentials_locations);
if(index->locations) {
CxIterator i = cxListIterator(index->locations);
cx_foreach(
char *, loc, i) {
ui_list_append(settings->credentials_locations, ui_strdup(settings->obj->ctx, loc));
}
}
}
ui_list_update(settings->credentials_locations);
ui_set_group(settings->obj->ctx,
SETTINGS_STATE_CREDENTIALS_SELECTED);
}
void settings_credentials_clear(SettingsWindow *settings) {
ui_free(settings->obj->ctx, settings->credentials_selected_id);
settings->credentials_selected_id =
NULL;
settings->credentials_new =
FALSE;
ui_set(settings->credentials_id,
"");
ui_set(settings->credentials_user,
"");
ui_set(settings->credentials_password,
"");
ui_list_clear(settings->credentials_locations);
ui_list_update(settings->credentials_locations);
ui_unset_group(settings->obj->ctx,
SETTINGS_STATE_CREDENTIALS_SELECTED);
}
int settings_credentials_save(SettingsWindow *settings) {
if(settings->credentials_selected_index == -
1) {
return 0;
}
char *newid = ui_get(settings->credentials_id);
if(strlen(newid) ==
0) {
return 0;
}
ssize_t index = cxListFind(settings->credentials_users->data, newid);
if(index >=
0 && index != settings->credentials_selected_index) {
cxmutstr s = cx_asprintf(
"Identifier %s already in use", newid);
ui_dialog(settings->obj, .title =
"Error", .content = s.ptr, .closebutton_label =
"OK");
free(s.ptr);
return 1;
}
char *user = ui_get(settings->credentials_user);
char *password = ui_get(settings->credentials_password);
CxList *ui_locations = settings->credentials_locations->data;
size_t numlocations = cxListSize(ui_locations);
CxList *locations =
NULL;
if(numlocations >
0) {
locations = cxArrayListCreateSimple(
CX_STORE_POINTERS, numlocations);
CxIterator i = cxListIterator(ui_locations);
cx_foreach(
char*, loc, i) {
cxListAdd(locations, strdup(loc));
}
}
settings->credentials_list_needs_update = settings->credentials_new || strcmp(newid, settings->credentials_selected_id);
if(settings->credentials_new || strcmp(newid, settings->credentials_selected_id ? settings->credentials_selected_id :
"")) {
settings->credentials_list_needs_update =
TRUE;
cxListRemove(settings->credentials_users->data, settings->credentials_selected_index);
cxListInsert(settings->credentials_users->data, settings->credentials_selected_index, ui_strdup(settings->obj->ctx, newid));
}
if(settings->credentials_selected_id) {
pwdstore_remove_entry(settings->pwdstore, settings->credentials_selected_id);
}
pwdstore_put(settings->pwdstore, newid, user, password);
pwdstore_put_index(settings->pwdstore, strdup(newid), locations);
settings->credentials_modified =
TRUE;
settings->credentials_new =
FALSE;
return 0;
}
void *keylist_getvalue(
void *data,
int col) {
DavCfgKey *key = data;
return key->name.value.ptr;
}
void settings_edit_key(SettingsWindow *settings, DavCfgKey *key) {
if(key->name.value.ptr) {
ui_set(settings->key_name, key->name.value.ptr);
}
const char *key_type;
switch(key->type) {
default: key_type =
"";
break;
case DAV_KEY_TYPE_AES256: {
key_type =
"AES256";
break;
}
case DAV_KEY_TYPE_AES128: {
key_type =
"AES128";
break;
}
case DAV_KEY_TYPE_UNKNOWN: {
key_type =
"unknown";
break;
}
}
ui_set(settings->key_type, key_type);
if(key->file.value.ptr) {
ui_set(settings->key_file, key->file.value.ptr);
}
}
void settings_clear_key(SettingsWindow *settings) {
ui_set(settings->key_name,
"");
ui_set(settings->key_type,
"");
ui_set(settings->key_file,
"");
}