UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "settings.h" 30 31 #include <cx/list.h> 32 #include <cx/array_list.h> 33 #include <cx/printf.h> 34 #include <libidav/utils.h> 35 36 37 38 #define SETTINGS_STATE_REPOLIST_SELECTED 1 39 #define SETTINGS_STATE_REPO_ENCRYPTION 20 40 #define SETTINGS_STATE_CREDENTIALS_SELECTED 30 41 #define SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED 31 42 #define SETTINGS_STATE_KEYS_SELECTED 40 43 44 #define SETTINGS_STATE_DISABLED 9999 45 46 47 static void repolist_activate(UiEvent *event, void *userdata) { 48 UiListSelection *selection = event->eventdata; 49 SettingsWindow *settings = event->window; 50 settings_edit_repository(settings, selection->rows[0]); 51 } 52 53 static void repolist_selection(UiEvent *event, void *userdata) { 54 UiListSelection *selection = event->eventdata; 55 SettingsWindow *settings = event->window; 56 if(selection->count > 0) { 57 ui_set_group(event->obj->ctx, SETTINGS_STATE_REPOLIST_SELECTED); 58 settings->selected_repo = selection->rows[0]; 59 } else { 60 ui_unset_group(event->obj->ctx, SETTINGS_STATE_REPOLIST_SELECTED); 61 } 62 } 63 64 static void repolist_edit(UiEvent *event, void *userdata) { 65 SettingsWindow *settings = event->window; 66 if(settings->selected_repo >= 0) { 67 settings_edit_repository(settings, settings->selected_repo); 68 } 69 } 70 71 static void repolist_add(UiEvent *event, void *userdata) { 72 SettingsWindow *settings = event->window; 73 settings->repo_new = TRUE; 74 settings->selected_repo = -1; 75 settings_clear_repository(settings); 76 // switch to editing tab 77 ui_set(settings->repo_tabview, 1); 78 } 79 80 static void repolist_remove(UiEvent *event, void *userdata) { 81 SettingsWindow *settings = event->window; 82 DavCfgRepository *repo = ui_list_get(settings->repos, settings->selected_repo); 83 if(!repo) { 84 fprintf(stderr, "Error: cannot get repository at index %d\n", settings->selected_repo); 85 return; 86 } 87 dav_repository_remove_and_free(settings->config, repo); 88 settings_update_repolist(settings); 89 settings->selected_repo = -1; 90 ui_unset_group(event->obj->ctx, SETTINGS_STATE_REPOLIST_SELECTED); 91 } 92 93 static void editrepo_go_back(UiEvent *event, void *userdata) { 94 SettingsWindow *settings = event->window; 95 settings_store_repository(settings); 96 ui_set(settings->repo_tabview, 0); 97 } 98 99 static void credentials_add(UiEvent *event, void *userdata) { 100 SettingsWindow *settings = event->window; 101 if(settings_credentials_save(settings)) { 102 return; 103 } 104 if(settings->credentials_list_needs_update) { 105 settings->credentials_ignore_selectionevent = TRUE; 106 ui_list_update(settings->credentials_users); 107 settings_reload_repo_credentials(settings); 108 settings->credentials_ignore_selectionevent = FALSE; 109 settings->credentials_list_needs_update = FALSE; 110 } 111 settings_credentials_clear(settings); 112 settings->credentials_new = TRUE; 113 PwdStore *pwd = settings->pwdstore; 114 if(!pwd->isdecrypted) { 115 settings_credentials_decrypt(settings); 116 return; 117 } 118 settings_credentials_select(settings, NULL); 119 } 120 121 static void addcred_onclick(UiEvent *event, void *userdata) { 122 SettingsNewCredentialsDialog *wdata = event->window; 123 if(event->intval == 4) { 124 char *id = ui_get(wdata->id); 125 char *user = ui_get(wdata->user); 126 char *password = ui_get(wdata->password); 127 int addlocation = ui_get(wdata->addlocation); 128 129 if(strlen(id) == 0) { 130 ui_set(wdata->message, "Missing identifier!"); 131 return; 132 } 133 if(strlen(user) == 0) { 134 ui_set(wdata->message, "Missing user!"); 135 return; 136 } 137 if(strlen(password) == 0) { 138 ui_set(wdata->message, "Missing password!"); 139 return; 140 } 141 142 if(pwdstore_has_id(wdata->settings->pwdstore, id)) { 143 ui_set(wdata->message, "Identifier already in use!"); 144 return; 145 } 146 147 CxList *locations = NULL; 148 if(addlocation) { 149 locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 150 cxListAdd(locations, strdup(wdata->url)); 151 } 152 pwdstore_put(wdata->settings->pwdstore, id, user, password); 153 pwdstore_put_index(wdata->settings->pwdstore, strdup(id), locations); 154 155 settings_reload_credentials(wdata->settings); 156 settings_reload_repo_credentials(wdata->settings); 157 158 CxList *list = wdata->settings->repo_credentials->data; 159 ssize_t index = cxListFind(list, id); 160 if(index >= 0) { 161 ui_list_setselection(wdata->settings->repo_credentials, index); 162 } 163 } 164 165 ui_close(event->obj); 166 } 167 168 // called by repo_edit 169 // create new credentials in dialog 170 static void credentials_new(UiEvent *event, void *userdata) { 171 SettingsWindow *settings = event->window; 172 173 UiObject *dialog = ui_dialog_window(event->obj, 174 .modal = UI_ON, 175 .title = "New Credentials", 176 .show_closebutton = UI_OFF, 177 .lbutton1 = "Cancel", 178 .rbutton4 = "Add", 179 .onclick = addcred_onclick); 180 181 SettingsNewCredentialsDialog *wdata = ui_malloc(dialog->ctx, sizeof(SettingsNewCredentialsDialog)); 182 wdata->settings = settings; 183 wdata->id = ui_string_new(dialog->ctx, NULL); 184 wdata->user = ui_string_new(dialog->ctx, NULL); 185 wdata->password = ui_string_new(dialog->ctx, NULL); 186 wdata->addlocation = ui_int_new(dialog->ctx, NULL); 187 wdata->message = ui_string_new(dialog->ctx, NULL); 188 dialog->window = wdata; 189 190 char *url = ui_get(settings->repo_url); 191 if(strlen(url) > 0) { 192 wdata->url = ui_strdup(dialog->ctx, url); 193 } else { 194 wdata->url = NULL; 195 } 196 197 ui_grid(dialog, .margin = 16, .columnspacing = 40, .rowspacing = 10) { 198 ui_llabel(dialog, .label = "Identifier"); 199 ui_textfield(dialog, .value = wdata->id, .hexpand = TRUE); 200 ui_newline(dialog); 201 202 ui_llabel(dialog, .label = "User"); 203 ui_textfield(dialog, .value = wdata->user, .hexpand = TRUE); 204 ui_newline(dialog); 205 206 ui_llabel(dialog, .label = "Password"); 207 ui_passwordfield(dialog, .value = wdata->password, .hexpand = TRUE); 208 ui_newline(dialog); 209 210 if(wdata->url) { 211 cxmutstr msg = cx_asprintf("Add URL %s to Credential Locations", url); 212 ui_checkbox(dialog, .label = msg.ptr, .value = wdata->addlocation, .colspan = 2); 213 ui_newline(dialog); 214 free(msg.ptr); 215 } 216 217 ui_llabel(dialog, .value = wdata->message, .colspan = 2); 218 } 219 220 ui_show(dialog); 221 } 222 223 static void credentials_remove(UiEvent *event, void *userdata) { 224 SettingsWindow *settings = event->window; 225 if(settings->credentials_selected_id) { 226 pwdstore_remove_entry(settings->pwdstore, settings->credentials_selected_id); 227 ui_list_remove(settings->credentials_users, settings->credentials_selected_index); 228 ui_list_update(settings->credentials_users); 229 settings_reload_repo_credentials(settings); 230 } 231 } 232 233 static void credentials_onselect(UiEvent *event, void *userdata) { 234 SettingsWindow *settings = event->window; 235 if(settings->credentials_ignore_selectionevent) { 236 return; 237 } 238 UiListSelection *sel = event->eventdata; 239 if(settings_credentials_save(settings)) { 240 return; 241 } 242 if(sel->count > 0) { 243 const char *id = ui_list_get(settings->credentials_users, sel->rows[0]); 244 settings->credentials_selected_index = sel->rows[0]; 245 settings_credentials_select(settings, id); 246 if(settings->credentials_list_needs_update) { 247 settings->credentials_ignore_selectionevent = TRUE; 248 ui_list_update(settings->credentials_users); 249 settings_reload_repo_credentials(settings); 250 ui_list_setselection(settings->credentials_users, sel->rows[0]); 251 settings->credentials_ignore_selectionevent = FALSE; 252 settings->credentials_list_needs_update = FALSE; 253 } 254 } else { 255 settings_credentials_clear(settings); 256 } 257 } 258 259 static void c_add_location(UiEvent *event, void *userdata) { 260 SettingsWindow *settings = event->window; 261 if(event->intval == 1) { 262 ui_list_append(settings->credentials_locations, ui_strdup(event->obj->ctx, event->eventdata)); 263 ui_list_update(settings->credentials_locations); 264 } 265 } 266 267 static void credentials_location_add(UiEvent *event, void *userdata) { 268 SettingsWindow *settings = event->window; 269 ui_dialog(event->obj, 270 .title = "Add Location", 271 .content = "New Location URL", 272 .input = TRUE, 273 .result = c_add_location, 274 .button1_label = "Add Location", 275 .closebutton_label = "Cancel"); 276 } 277 278 static void c_edit_location(UiEvent *event, void *userdata) { 279 SettingsWindow *settings = event->window; 280 if(event->intval == 1) { 281 CxList *list = settings->credentials_locations->data; 282 ssize_t i = cxListFind(list, userdata); 283 if(i >= 0) { 284 cxListRemove(list, i); 285 cxListInsert(list, i, ui_strdup(event->obj->ctx, event->eventdata)); 286 ui_list_update(settings->credentials_locations); 287 } 288 } 289 } 290 291 static void credentials_location_edit(UiEvent *event, void *userdata) { 292 SettingsWindow *settings = event->window; 293 char *location = ui_list_get(settings->credentials_locations, settings->credentials_location_selected_index); 294 if(!location) { 295 return; 296 } 297 ui_dialog(event->obj, 298 .title = "Edit Location", 299 .content = "Location URL", 300 .input_value = location, 301 .input = TRUE, 302 .result = c_edit_location, 303 .resultdata = location, 304 .button1_label = "Edit Location", 305 .closebutton_label = "Cancel"); 306 } 307 308 static void credentials_location_remove(UiEvent *event, void *userdata) { 309 SettingsWindow *settings = event->window; 310 if(settings->credentials_location_selected_index >= 0) { 311 CxList *list = settings->credentials_locations->data; 312 cxListRemove(list, settings->credentials_location_selected_index); 313 ui_list_update(settings->credentials_locations); 314 } 315 } 316 317 static void credentials_location_up(UiEvent *event, void *userdata) { 318 SettingsWindow *settings = event->window; 319 int index = settings->credentials_location_selected_index; 320 if(index >= 1) { 321 CxList *list = settings->credentials_locations->data; 322 cxListSwap(list, index, index-1); 323 ui_list_update(settings->credentials_locations); 324 ui_list_setselection(settings->credentials_locations, index-1); 325 } 326 } 327 328 static void credentials_location_down(UiEvent *event, void *userdata) { 329 SettingsWindow *settings = event->window; 330 int index = settings->credentials_location_selected_index; 331 if(index >= 0 && index + 1 < ui_list_count(settings->credentials_locations)) { 332 CxList *list = settings->credentials_locations->data; 333 cxListSwap(list, index, index+1); 334 ui_list_update(settings->credentials_locations); 335 ui_list_setselection(settings->credentials_locations, index+1); 336 } 337 } 338 339 static void credentials_location_onselect(UiEvent *event, void *userdata) { 340 SettingsWindow *settings = event->window; 341 UiListSelection *sel = event->eventdata; 342 if(sel->count > 0) { 343 settings->credentials_location_selected_index = sel->rows[0]; 344 ui_set_group(event->obj->ctx, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED); 345 } else { 346 settings->credentials_location_selected_index = -1; 347 ui_unset_group(event->obj->ctx, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED); 348 } 349 } 350 351 static void credentials_setmasterpw(UiEvent *event, void *userdata) { 352 if(event->intval == 1) { 353 SettingsWindow *settings = event->window; 354 char *pw = event->eventdata; 355 size_t pwlen = strlen(pw); 356 if(pwlen > 0) { 357 pwdstore_setpassword(settings->pwdstore, event->eventdata); 358 memset(pw, 0, pwlen); 359 if(!pwdstore_decrypt(settings->pwdstore)) { 360 settings_credentials_select(settings, NULL); 361 } else { 362 ui_dialog(event->obj, .title = "Error", .content = "Cannot decrypt Secret Store", .closebutton_label = "OK"); 363 } 364 } 365 } 366 } 367 368 void settings_credentials_decrypt(SettingsWindow *settings) { 369 ui_dialog(settings->obj, 370 .title = "Secret Store", 371 .content = "Master password", 372 .password = TRUE, 373 .result = credentials_setmasterpw, 374 .button1_label = "Decrypt Secret Store", 375 .closebutton_label = "Cancel"); 376 } 377 378 379 380 381 #define ADDKEY_DIALOG_STATE_NO_IMPORT 10 382 383 static void addkey_onclick(UiEvent *event, void *userdata) { 384 SettingsNewKeyDialog *wdata = event->window; 385 if(event->intval == 4) { 386 ui_set(wdata->message, ""); 387 388 // validate form 389 char *name = ui_get(wdata->name); 390 if(strlen(name) == 0) { 391 ui_set(wdata->message, "A name must be specified!"); 392 return; 393 } 394 char *file = ui_get(wdata->file); 395 if(strlen(file) == 0) { 396 ui_set(wdata->message, "A file path must be specified!"); 397 return; 398 } 399 UiListSelection sel = ui_list_getselection(wdata->type); 400 if(sel.count == 0) { 401 ui_set(wdata->message, "No type selected!"); 402 return; 403 } 404 DavCfgKeyType type = sel.rows[0]; 405 406 // generate key file 407 char *path = NULL; 408 char *cfg_path = NULL; 409 if(file[0] == '/') { 410 path = file; 411 } else { 412 cfg_path = config_file_path(file); 413 path = cfg_path; 414 } 415 FILE *f = fopen(path, "w"); 416 if(!f) { 417 cxmutstr msg = cx_asprintf("Error: cannot write file ''%s'': %s", path, strerror(errno)); 418 ui_set(wdata->message, msg.ptr); 419 free(msg.ptr); 420 free(cfg_path); 421 return; 422 } 423 free(cfg_path); 424 425 unsigned char key[32]; 426 dav_rand_bytes(key, 32); 427 428 size_t len; 429 switch(type) { 430 default: 431 case DAV_KEY_TYPE_AES256: len = 32; break; 432 case DAV_KEY_TYPE_AES128: len = 16; break; 433 } 434 size_t w = fwrite(key, 1, len, f); 435 fclose(f); 436 437 if(w != len) { 438 cxmutstr msg = cx_asprintf("Could not write key data: %s", strerror(errno)); 439 ui_set(wdata->message, msg.ptr); 440 free(msg.ptr); 441 return; 442 } 443 444 // add key to config 445 DavCfgKey *newkey = dav_key_new(wdata->settings->config); 446 const CxAllocator *a = wdata->settings->config->mp->allocator; 447 newkey->name.value = cx_strdup_a(a, cx_str(name)); 448 newkey->file.value = cx_strdup_a(a, cx_str(file)); 449 newkey->type = type; 450 451 dav_config_add_key(wdata->settings->config, newkey); 452 settings_reload_repo_keys(wdata->settings); 453 settings_reload_keys(wdata->settings); 454 455 if(wdata->add_to_repo && (wdata->settings->selected_repo >= 0 || wdata->settings->repo_new)) { 456 CxList *repo_keys = wdata->settings->repo_keys->data; 457 ssize_t index = cxListFind(repo_keys, name); 458 if(index >= 0) { 459 ui_list_setselection(wdata->settings->repo_keys, index); 460 } 461 } 462 } 463 464 ui_close(event->obj); 465 } 466 467 static void addkey_name_changed(UiEvent *event, void *userdata) { 468 SettingsNewKeyDialog *wdata = event->window; 469 char *name = ui_get(wdata->name); 470 cxmutstr file = cx_asprintf("keys/%s", name); 471 ui_set(wdata->file, file.ptr); 472 free(file.ptr); 473 } 474 475 static void keys_add(UiEvent *event, void *userdata) { 476 SettingsWindow *settings = event->window; 477 478 UiObject *dialog = ui_dialog_window(event->obj, 479 .modal = UI_ON, 480 .title = "Add Key", 481 .show_closebutton = UI_OFF, 482 .lbutton1 = "Cancel", 483 .rbutton4 = "Add", 484 .default_button = 4, 485 .onclick = addkey_onclick); 486 SettingsNewKeyDialog *wdata = ui_malloc(dialog->ctx, sizeof(SettingsNewKeyDialog)); 487 wdata->settings = settings; 488 wdata->name = ui_string_new(dialog->ctx, NULL); 489 wdata->file = ui_string_new(dialog->ctx, NULL); 490 wdata->import_path = ui_string_new(dialog->ctx, NULL); 491 wdata->message = ui_string_new(dialog->ctx, NULL); 492 wdata->secretstore = ui_int_new(dialog->ctx, NULL); 493 wdata->type = ui_list_new(dialog->ctx, NULL); 494 wdata->add_to_repo = userdata ? TRUE : FALSE; 495 dialog->window = wdata; 496 497 ui_list_append(wdata->type, "AES256"); 498 ui_list_append(wdata->type, "AES128"); 499 500 ui_grid(dialog, .margin = 16, .columnspacing = 40, .rowspacing = 10) { 501 /* 502 ui_hbox(dialog, .colspan = 2) { 503 ui_button(dialog, .label = "Import Key..."); 504 } 505 ui_newline(dialog); 506 ui_llabel(dialog, .value = wdata->import_path); 507 ui_newline(dialog); 508 */ 509 510 ui_llabel(dialog, .label = "Name"); 511 ui_textfield(dialog, .value = wdata->name, .onchange = addkey_name_changed, .hexpand = TRUE); 512 ui_newline(dialog); 513 514 ui_llabel(dialog, .label = "File"); 515 ui_textfield(dialog, .value = wdata->file); 516 ui_newline(dialog); 517 518 ui_llabel(dialog, .label = "Type"); 519 ui_combobox(dialog, .list = wdata->type, .groups = UI_GROUPS(ADDKEY_DIALOG_STATE_NO_IMPORT)); 520 ui_newline(dialog); 521 522 ui_llabel(dialog, .value = wdata->message, .colspan = 2); 523 } 524 525 ui_set_group(dialog->ctx, ADDKEY_DIALOG_STATE_NO_IMPORT); 526 ui_list_setselection(wdata->type, 0); 527 528 ui_show(dialog); 529 } 530 531 static void keys_remove(UiEvent *event, void *userdata) { 532 SettingsWindow *settings = event->window; 533 DavCfgKey *key = ui_list_get(settings->keys_list, settings->keys_selected_index); 534 if(key) { 535 dav_key_remove_and_free(settings->config, key); 536 ui_list_remove(settings->keys_list, settings->keys_selected_index); 537 ui_list_update(settings->keys_list); 538 settings_reload_repo_keys(settings); 539 } 540 } 541 542 static void keys_onselect(UiEvent *event, void *userdata) { 543 SettingsWindow *settings = event->window; 544 UiListSelection *sel = event->eventdata; 545 if(sel->count > 0) { 546 settings->keys_selected_index = sel->rows[0]; 547 DavCfgKey *key = ui_list_get(settings->keys_list, sel->rows[0]); 548 if(key) { 549 settings_edit_key(settings, key); 550 ui_set_group(event->obj->ctx, SETTINGS_STATE_KEYS_SELECTED); 551 } 552 } else { 553 settings->keys_selected_index = -1; 554 settings_clear_key(settings); 555 ui_unset_group(event->obj->ctx, SETTINGS_STATE_KEYS_SELECTED); 556 } 557 } 558 559 static void list_str_destructor(void *data, void *ptr) { 560 UiContext *ctx = data; 561 char *s = ptr; 562 ui_free(ctx, ptr); 563 } 564 565 static void secretstore_newmasterpw(UiEvent *event, void *userdata) { 566 SettingsWindow *settings = event->window; 567 if(event->intval == 1) { 568 pwdstore_setpassword(settings->pwdstore, event->eventdata); 569 set_pwdstore(settings->pwdstore); 570 pwdstore_save(settings->pwdstore); 571 settings->pwdstore = NULL; 572 } 573 ui_close(event->obj); 574 } 575 576 void settings_ok(UiEvent *event, void *userdata) { 577 SettingsWindow *settings = event->window; 578 // save any changed settings 579 settings_store_repository(settings); 580 settings_credentials_save(settings); 581 582 set_config(settings->config); 583 if(store_config()) { 584 ui_dialog(event->obj, .title = "Error", .content = "Cannot store settings", .closebutton_label = "OK"); 585 } 586 application_update_repolist(get_application()); settings->config = NULL; 587 if(settings->credentials_modified) { 588 if(settings->pwdstore->key) { 589 set_pwdstore(settings->pwdstore); 590 pwdstore_save(settings->pwdstore); 591 } else { 592 ui_dialog(event->obj, 593 .title = "Secret Store", 594 .content = "Master password", 595 .password = TRUE, 596 .result = secretstore_newmasterpw, 597 .button1_label = "Create Secret Store", 598 .closebutton_label = "Cancel", 599 .result = secretstore_newmasterpw); 600 return; 601 } 602 settings->pwdstore = NULL; 603 } 604 ui_close(event->obj); 605 } 606 607 void settings_close(UiEvent *event, void *userdata) { 608 SettingsWindow *settings = event->window; 609 if(settings->config) { 610 dav_config_free(settings->config); 611 } 612 if(settings->pwdstore) { 613 pwdstore_free(settings->pwdstore); 614 } 615 } 616 617 void settings_cancel(UiEvent *event, void *userdata) { 618 ui_close(event->obj); 619 } 620 621 void settings_window_open() { 622 DavConfig *config = load_config_file(); 623 if(!config) { 624 return; 625 } 626 PwdStore *pwdstore = get_pwdstore(); 627 pwdstore = pwdstore ? pwdstore_clone(pwdstore) : pwdstore_new(); 628 629 UiObject *obj = ui_simple_window("Settings", NULL); 630 ui_context_closefunc(obj->ctx, settings_close, NULL); 631 SettingsWindow *wdata = ui_malloc(obj->ctx, sizeof(SettingsWindow)); 632 memset(wdata, 0, sizeof(SettingsWindow)); 633 wdata->config = config; 634 wdata->pwdstore = pwdstore; 635 obj->window = wdata; 636 wdata->obj = obj; 637 settings_init(obj, wdata); 638 639 ui_tabview(obj, .tabview = UI_TABVIEW_NAVIGATION_TOP) { 640 ui_tab(obj, "General") { 641 ui_grid(obj, .margin = 10) { 642 ui_label(obj, .label = "TODO"); 643 } 644 } 645 646 ui_tab(obj, "Repositories") { 647 648 ui_tabview(obj, .value = wdata->repo_tabview, .tabview = UI_TABVIEW_INVISIBLE) { 649 ui_tab(obj, "list") { 650 ui_grid(obj, .margin = 16, .columnspacing = 10, .rowspacing = 10) { 651 ui_hbox(obj, .spacing = 4) { 652 ui_button(obj, .label = "Add", .onclick = repolist_add); 653 ui_button(obj, .label = "Edit", .onclick = repolist_edit, .groups = UI_GROUPS(SETTINGS_STATE_REPOLIST_SELECTED)); 654 ui_button(obj, .label = "Remove", .onclick = repolist_remove, .groups = UI_GROUPS(SETTINGS_STATE_REPOLIST_SELECTED)); 655 } 656 ui_newline(obj); 657 658 UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING, "URL", UI_STRING, "User", UI_STRING, "Encrypted", -1); 659 model->getvalue = (ui_getvaluefunc) settings_repolist_getvalue; 660 ui_table(obj, 661 .model = model, 662 .list = wdata->repos, 663 .multiselection = FALSE, 664 .onactivate = repolist_activate, 665 .onselection = repolist_selection, 666 .vexpand = TRUE, .hexpand = TRUE, .colspan = 3); 667 } 668 } 669 670 ui_tab(obj, "repo") { 671 ui_vbox(obj, .margin = 16, .spacing = 10) { 672 ui_hbox(obj, .fill = UI_OFF, .spacing = 4) { 673 ui_button(obj, .icon = UI_ICON_GO_BACK, .onclick = editrepo_go_back); 674 ui_label(obj, .label = "Repository List"); 675 } 676 677 ui_scrolledwindow(obj, .hexpand = TRUE, .vexpand = TRUE, .subcontainer = UI_CONTAINER_NO_SUB) { 678 ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10) { 679 ui_llabel(obj, .label = "Name"); 680 ui_textfield(obj, .value = wdata->repo_name, .width = 15); 681 ui_newline(obj); 682 ui_llabel(obj, .label = "URL"); 683 ui_textfield(obj, .value = wdata->repo_url, .hexpand = TRUE); 684 ui_newline(obj); 685 686 ui_llabel(obj, .label = "Credentials", .style = UI_LABEL_STYLE_TITLE, .colspan = 2); 687 ui_newline(obj); 688 ui_hbox(obj, .spacing = 4, .colspan = 2) { 689 ui_combobox(obj, .list = wdata->repo_credentials); 690 ui_button(obj, .label = "New Credentials", .onclick = credentials_new); 691 } 692 ui_newline(obj); 693 ui_expander(obj, .spacing = 10, .colspan = 2, .label = "Unencrypted User/Password", .margin = 10) { 694 ui_llabel(obj, .label = "Store the credentials unencrypted in the repository and not in the secret store", .style = UI_LABEL_STYLE_DIM); 695 ui_grid(obj, .rowspacing = 10, .columnspacing = 10, .fill = UI_OFF) { 696 ui_llabel(obj, .label = "User"); 697 ui_textfield(obj, .value = wdata->repo_user, .width = 15); 698 ui_newline(obj); 699 700 ui_llabel(obj, .label = "Password"); 701 ui_passwordfield(obj, .value = wdata->repo_password, .width = 15); 702 } 703 } 704 ui_newline(obj); 705 706 ui_llabel(obj, .label = "Encryption", .style = UI_LABEL_STYLE_TITLE, .colspan = 2); 707 ui_newline(obj); 708 709 ui_checkbox(obj, .label = "Enable client-side encryption", .value = wdata->repo_encryption, .colspan = 2, .enable_group = SETTINGS_STATE_REPO_ENCRYPTION); 710 ui_newline(obj); 711 ui_llabel(obj, .label = "Default key"); 712 ui_hbox(obj, .spacing = 4) { 713 ui_combobox(obj, .list = wdata->repo_keys, .groups = UI_GROUPS(SETTINGS_STATE_REPO_ENCRYPTION)); 714 ui_button(obj, .label = "Generate Key", .onclick = keys_add, .onclickdata = "repo", .groups = UI_GROUPS(SETTINGS_STATE_REPO_ENCRYPTION)); 715 } 716 ui_newline(obj); 717 718 ui_llabel(obj, .label = "TLS", .style = UI_LABEL_STYLE_TITLE, .colspan = 2); 719 ui_newline(obj); 720 721 ui_llabel(obj, .label = "Cert Path"); 722 ui_hbox0(obj) { 723 ui_textfield(obj, .value = wdata->repo_cacert, .width = 15); 724 } 725 ui_newline(obj); 726 727 ui_llabel(obj, .label = "TLS Version"); 728 ui_hbox0(obj) { 729 ui_combobox(obj, .list = wdata->repo_tls_versions); 730 } 731 ui_newline(obj); 732 ui_checkbox(obj, .label = "Disable TLS verification", .value = wdata->repo_disable_verification, .colspan = 2); 733 } 734 } 735 } 736 } 737 } 738 739 740 } 741 742 ui_tab(obj, "Sync Directories") { 743 ui_grid(obj, .margin = 10) { 744 ui_label(obj, .label = "TODO"); 745 } 746 } 747 748 ui_tab(obj, "Credentials") { 749 ui_hbox(obj, .margin = 16, .spacing = 30) { 750 ui_vbox(obj, .fill = UI_OFF, .spacing = 4) { 751 ui_hbox(obj, .fill = UI_OFF, .spacing = 4) { 752 ui_button(obj, .label = "Add", .onclick = credentials_add); 753 ui_button(obj, .label = "Remove", .onclick = credentials_remove, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED)); 754 } 755 ui_listview(obj, .list = wdata->credentials_users, .fill = UI_ON, .onselection = credentials_onselect); 756 } 757 758 ui_grid(obj, .columnspacing = 30, .rowspacing = 10) { 759 ui_llabel(obj, .label = "Identifier"); 760 ui_textfield(obj, .value = wdata->credentials_id, .hexpand = TRUE, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED)); 761 ui_newline(obj); 762 763 ui_llabel(obj, .label = "User"); 764 ui_textfield(obj, .value = wdata->credentials_user, .hexpand = TRUE, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED)); 765 ui_newline(obj); 766 767 ui_llabel(obj, .label = "Password"); 768 ui_passwordfield(obj, .value = wdata->credentials_password, .hexpand = TRUE, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED)); 769 ui_newline(obj); 770 771 772 ui_label(obj, .label = " "); 773 ui_newline(obj); 774 775 ui_llabel(obj, .style = UI_LABEL_STYLE_TITLE, .label = "Locations", .colspan = 2); 776 ui_newline(obj); 777 ui_llabel(obj, .style = UI_LABEL_STYLE_DIM, .label = "List of URLs for which these credentials should be used (optional)", .colspan = 2); 778 ui_newline(obj); 779 780 ui_hbox(obj, .colspan = 2, .vexpand = TRUE, .hexpand = TRUE, .spacing = 10) { 781 #ifndef UI_WINUI 782 ui_callback credentials_activate_callback = credentials_location_edit; 783 #else 784 ui_callback credentials_activate_callback = NULL; 785 #endif 786 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)); 787 ui_vbox(obj, .fill = UI_OFF, .spacing = 4) { 788 ui_button(obj, .label = "Add", .onclick = credentials_location_add, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED)); 789 ui_button(obj, .label = "Edit", .onclick = credentials_location_edit, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED)); 790 ui_button(obj, .label = "Remove", .onclick = credentials_location_remove, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED)); 791 ui_button(obj, .label = "Move Up", .onclick = credentials_location_up, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED)); 792 ui_button(obj, .label = "Move Down", .onclick = credentials_location_down, .groups = UI_GROUPS(SETTINGS_STATE_CREDENTIALS_SELECTED, SETTINGS_STATE_CREDENTIALS_LOCATION_SELECTED)); 793 } 794 } 795 } 796 } 797 } 798 799 ui_tab(obj, "Keys") { 800 ui_hbox(obj, .margin = 16, .spacing = 30) { 801 ui_vbox(obj, .fill = UI_OFF, .spacing = 4) { 802 ui_hbox(obj, .fill = UI_OFF, .spacing = 4) { 803 ui_button(obj, .label = "Add", .onclick = keys_add); 804 ui_button(obj, .label = "Remove", .onclick = keys_remove, .groups = UI_GROUPS(SETTINGS_STATE_KEYS_SELECTED)); 805 } 806 ui_listview(obj, .list = wdata->keys_list, .fill = UI_ON, .onselection = keys_onselect, .getvalue = keylist_getvalue); 807 } 808 809 ui_grid(obj, .columnspacing = 30, .rowspacing = 10) { 810 ui_llabel(obj, .label = "Identifier"); 811 ui_textfield(obj, .value = wdata->key_name, .groups = UI_GROUPS(SETTINGS_STATE_KEYS_SELECTED)); 812 ui_newline(obj); 813 ui_llabel(obj, .label = "Type"); 814 ui_textfield(obj, .value = wdata->key_type, .groups = UI_GROUPS(SETTINGS_STATE_DISABLED)); 815 ui_newline(obj); 816 ui_llabel(obj, .label = "File"); 817 ui_textfield(obj, .value = wdata->key_file, .groups = UI_GROUPS(SETTINGS_STATE_KEYS_SELECTED)); 818 } 819 } 820 } 821 822 /* 823 ui_tab(obj, "Properties") { 824 ui_grid(obj, .margin = 10) { 825 ui_label(obj, .label = "TODO"); 826 } 827 } 828 */ 829 } 830 831 ui_hbox(obj, .fill = UI_OFF, .margin = 10) { 832 ui_button(obj, .label = "Cancel", .onclick = settings_cancel); 833 ui_label(obj, .fill = UI_ON); 834 ui_button(obj, .label = "Save", .onclick = settings_ok); 835 } 836 837 838 ui_show(obj); 839 } 840 841 void settings_init(UiObject *obj, SettingsWindow *settings) { 842 settings->repos = ui_list_new(obj->ctx, NULL); 843 settings->repo_tabview = ui_int_new(obj->ctx, NULL); 844 845 settings->repo_name = ui_string_new(obj->ctx, NULL); 846 settings->repo_url = ui_string_new(obj->ctx, NULL); 847 settings->repo_user = ui_string_new(obj->ctx, NULL); 848 settings->repo_password = ui_string_new(obj->ctx, NULL); 849 settings->repo_cacert = ui_string_new(obj->ctx, NULL); 850 settings->repo_credentials = ui_list_new(obj->ctx, NULL); 851 settings->repo_keys = ui_list_new(obj->ctx, NULL); 852 settings->repo_tls_versions = ui_list_new(obj->ctx, NULL); 853 settings->repo_encryption = ui_int_new(obj->ctx, NULL); 854 settings->repo_disable_verification = ui_int_new(obj->ctx, NULL); 855 CxList *repo_keys = settings->repo_keys->data; 856 repo_keys->collection.cmpfunc = (cx_compare_func)strcmp; 857 858 ui_list_append(settings->repo_tls_versions, "Default"); 859 ui_list_append(settings->repo_tls_versions, "TLSv1.3"); 860 ui_list_append(settings->repo_tls_versions, "TLSv1.2"); 861 ui_list_append(settings->repo_tls_versions, "TLSv1.1"); 862 ui_list_append(settings->repo_tls_versions, "TLSv1.0"); 863 864 settings->credentials_selected_index = -1; 865 settings->credentials_users = ui_list_new(obj->ctx, NULL); 866 settings->credentials_locations = ui_list_new(obj->ctx, NULL); 867 settings->credentials_id = ui_string_new(obj->ctx, NULL); 868 settings->credentials_user = ui_string_new(obj->ctx, NULL); 869 settings->credentials_password = ui_string_new(obj->ctx, NULL); 870 CxList *credentials_users = settings->credentials_users->data; 871 CxList *credentials_locations = settings->credentials_locations->data; 872 credentials_users->collection.advanced_destructor = list_str_destructor; 873 credentials_users->collection.destructor_data = settings->obj->ctx; 874 credentials_users->collection.cmpfunc = (cx_compare_func)strcmp; 875 credentials_locations->collection.advanced_destructor = list_str_destructor; 876 credentials_locations->collection.destructor_data = settings->obj->ctx; 877 credentials_locations->collection.cmpfunc = (cx_compare_func)strcmp; 878 879 settings->keys_list = ui_list_new(obj->ctx, NULL); 880 settings->key_name = ui_string_new(obj->ctx, NULL); 881 settings->key_type = ui_string_new(obj->ctx, NULL); 882 settings->key_file = ui_string_new(obj->ctx, NULL); 883 settings->keys_selected_index = -1; 884 885 // load some list values, that can be reused 886 settings_update_repolist(settings); 887 settings_reload_repo_keys(settings); 888 settings_reload_credentials(settings); 889 settings_reload_repo_credentials(settings); 890 settings_reload_keys(settings); 891 892 settings->selected_repo = -1; 893 } 894 895 #define SETTINGS_SET_STRING(str, setting) if(setting.value.ptr) ui_set(str, setting.value.ptr); 896 897 /* ----------------------------- Repository ----------------------------- */ 898 899 void settings_edit_repository(SettingsWindow *settings, int repo_index) { 900 DavCfgRepository *repo = ui_list_get(settings->repos, repo_index); 901 if(!repo) { 902 fprintf(stderr, "Error: cannot get repository at index %d\n", repo_index); 903 return; 904 } 905 settings->selected_repo = repo_index; 906 907 // load plain string values 908 SETTINGS_SET_STRING(settings->repo_name, repo->name); 909 SETTINGS_SET_STRING(settings->repo_url, repo->url); 910 SETTINGS_SET_STRING(settings->repo_cacert, repo->cert); 911 SETTINGS_SET_STRING(settings->repo_user, repo->user); 912 // load decrypted password 913 if(repo->password.value.ptr) { 914 char *decoded_pw = util_base64decode(repo->password.value.ptr); 915 ui_set(settings->repo_password, decoded_pw); 916 size_t decoded_pw_len = strlen(decoded_pw); 917 memset(decoded_pw, 0, decoded_pw_len); 918 free(decoded_pw); 919 } 920 921 // select credentials dropdown value 922 CxList *cred = settings->repo_credentials->data; 923 cred->collection.cmpfunc = (cx_compare_func)strcmp; 924 ssize_t cred_index = repo->stored_user.value.ptr ? cxListFind(cred, repo->stored_user.value.ptr) : 0; 925 if(cred_index > 0) { 926 ui_list_setselection(settings->repo_credentials, cred_index); 927 } else { 928 ui_list_setselection(settings->repo_credentials, 0); 929 } 930 931 // load encryption value and default key value 932 ui_set(settings->repo_encryption, repo->full_encryption.value); 933 CxList *keys = settings->repo_keys->data; 934 keys->collection.cmpfunc = (cx_compare_func)strcmp; 935 ssize_t key_index = repo->default_key.value.ptr ? cxListFind(keys, repo->default_key.value.ptr) : 0; 936 if(key_index > 0) { 937 ui_list_setselection(settings->repo_keys, key_index); 938 } else { 939 ui_list_setselection(settings->repo_keys, 0); 940 } 941 942 // select tls version from dropdown menu 943 CxList *tlsVersions = settings->repo_tls_versions->data; 944 tlsVersions->collection.cmpfunc = (cx_compare_func)strcmp; 945 const char *tls_str = dav_tlsversion2str(repo->ssl_version.value); 946 if(!tls_str) tls_str = ""; 947 ssize_t tlsv_index = cxListFind(tlsVersions, tls_str); 948 if(tlsv_index > 0) { 949 ui_list_setselection(settings->repo_tls_versions, tlsv_index); 950 } else { 951 ui_list_setselection(settings->repo_tls_versions, 0); 952 } 953 954 if(!repo->verification.value) { 955 ui_set(settings->repo_disable_verification, TRUE); 956 } 957 958 959 // switch to editing tab 960 ui_set(settings->repo_tabview, 1); 961 } 962 963 /* 964 * sets a config value 965 * if new_value is null, the xml node is removed 966 */ 967 static void cfg_string_set_value_or_remove(DavConfig *config, CfgString *str, xmlNode *parent, cxstring new_value, const char *nodename) { 968 if(new_value.length == 0) { 969 new_value.ptr = NULL; 970 } 971 dav_cfg_string_set_value(config, str, parent, new_value, nodename); 972 if(!new_value.ptr) { 973 dav_cfg_string_remove(str); 974 } 975 } 976 977 /* 978 * return the selected list value, if it is not the default value at index 0 979 */ 980 static cxstring default_list_get_value(UiList *list) { 981 cxstring ret = { NULL, 0 }; 982 UiListSelection sel = ui_list_getselection(list); 983 if(sel.count > 0) { 984 int index = sel.rows[0]; 985 if(index > 0) { 986 ret = cx_str(ui_list_get(list, index)); 987 } 988 free(sel.rows); 989 } 990 return ret; 991 } 992 993 void settings_store_repository(SettingsWindow *settings) { 994 DavConfig *config = settings->config; 995 DavCfgRepository *repo; 996 if(settings->repo_new) { 997 settings->repo_new = FALSE; 998 char *name = ui_get(settings->repo_name); 999 if(strlen(name) == 0) { 1000 return; 1001 } 1002 repo = dav_repository_new(config); 1003 dav_config_add_repository(config, repo); 1004 } else if(settings->selected_repo >= 0) { 1005 repo = ui_list_get(settings->repos, settings->selected_repo); 1006 if(!repo) { 1007 fprintf(stderr, "Error: cannot get repository at index %d\n", settings->selected_repo); 1008 return; 1009 } 1010 } else { 1011 return; 1012 } 1013 1014 // always store name/url nodes 1015 dav_cfg_string_set_value(config, &repo->name, repo->node, cx_str(ui_get(settings->repo_name)), "name"); 1016 dav_cfg_string_set_value(config, &repo->url, repo->node, cx_str(ui_get(settings->repo_url)), "url"); 1017 // store user of remove node, if no user is configured 1018 cfg_string_set_value_or_remove(config, &repo->user, repo->node, cx_str(ui_get(settings->repo_user)), "user"); 1019 // store cert or remove node 1020 cfg_string_set_value_or_remove(config, &repo->cert, repo->node, cx_str(ui_get(settings->repo_cacert)), "cert"); 1021 1022 1023 // store password or remove node, if no password is configured 1024 char *pw = ui_get(settings->repo_password); 1025 size_t pwlen = strlen(pw); 1026 if(pwlen > 0) { 1027 char *pwenc = util_base64encode(pw, pwlen); 1028 memset(pw, 0, pwlen); 1029 dav_cfg_string_set_value(config, &repo->password, repo->node, cx_str(pwenc), "password"); 1030 free(pwenc); 1031 } else { 1032 // set password 0 NULL and remove password config node 1033 cfg_string_set_value_or_remove(config, &repo->password, repo->node, cx_strn(NULL, 0), "password"); 1034 } 1035 1036 // get the selected credentials and set "stored-user" 1037 // remove node if not needed 1038 cxstring stored_user = default_list_get_value(settings->repo_credentials); 1039 cfg_string_set_value_or_remove(config, &repo->stored_user, repo->node, stored_user, "stored-user"); 1040 1041 // adjust full-encryption node if necessary 1042 int encryption = ui_get(settings->repo_encryption); 1043 if(encryption || repo->full_encryption.node) { 1044 dav_cfg_bool_set_value(config, &repo->full_encryption, repo->node, encryption, "full-encryption"); 1045 } 1046 1047 // set default-key 1048 cxstring key = default_list_get_value(settings->repo_keys); 1049 cfg_string_set_value_or_remove(config, &repo->default_key, repo->node, key, "default-key"); 1050 1051 // if disable_verification is enabled, set repo verification to false 1052 // otherwise remove the node, because verification is enabled by default 1053 int disable_verification = ui_get(settings->repo_disable_verification); 1054 if(disable_verification) { 1055 dav_cfg_bool_set_value(config, &repo->verification, repo->node, !disable_verification, "verification"); 1056 } else { 1057 dav_cfg_bool_remove(&repo->verification); 1058 } 1059 1060 // set tls version if configured 1061 cxstring tlsversion_str = default_list_get_value(settings->repo_tls_versions); 1062 int tlsversion = dav_str2ssl_version(tlsversion_str.ptr); 1063 if(tlsversion >= 0) { 1064 dav_cfg_int_set_value(config, &repo->ssl_version, repo->node, tlsversion, "ssl-version"); 1065 } else { 1066 dav_cfg_int_remove(&repo->ssl_version); 1067 } 1068 1069 settings_update_repolist(settings); 1070 settings->selected_repo = -1; 1071 } 1072 1073 void settings_clear_repository(SettingsWindow *settings) { 1074 ui_set(settings->repo_name, ""); 1075 ui_set(settings->repo_url, ""); 1076 ui_set(settings->repo_user, ""); 1077 ui_set(settings->repo_password, ""); 1078 ui_set(settings->repo_cacert, ""); 1079 ui_list_setselection(settings->repo_credentials, 0); 1080 ui_list_setselection(settings->repo_keys, 0); 1081 ui_list_setselection(settings->repo_tls_versions, 0); 1082 ui_set(settings->repo_encryption, 0); 1083 ui_set(settings->repo_disable_verification, 0); 1084 } 1085 1086 void settings_update_repolist(SettingsWindow *settings) { 1087 DavConfig *config = settings->config; 1088 1089 ui_list_clear(settings->repos); 1090 1091 for (DavCfgRepository *repo = config->repositories; repo; repo = repo->next) { 1092 ui_list_append(settings->repos, repo); 1093 } 1094 1095 if(settings->repos->update) { 1096 ui_list_update(settings->repos); 1097 } 1098 } 1099 1100 void* settings_repolist_getvalue(DavCfgRepository *repo, int col) { 1101 switch(col) { 1102 case 0: { 1103 return repo->name.value.ptr; 1104 } 1105 case 1: { 1106 return repo->url.value.ptr; 1107 } 1108 case 2: { 1109 return repo->user.value.ptr ? repo->user.value.ptr : repo->stored_user.value.ptr; 1110 } 1111 case 3: { 1112 return repo->full_encryption.value ? "yes" : "no"; 1113 } 1114 } 1115 return NULL; 1116 } 1117 1118 void settings_reload_repo_keys(SettingsWindow *settings) { 1119 DavConfig *config = settings->config; 1120 DavCfgKey *key = config->keys; 1121 ui_list_clear(settings->repo_keys); 1122 ui_list_append(settings->repo_keys, "-"); 1123 while(key) { 1124 if(key->name.value.ptr) { 1125 ui_list_append(settings->repo_keys, key->name.value.ptr); 1126 } 1127 key = key->next; 1128 } 1129 ui_list_update(settings->repo_keys); 1130 } 1131 1132 void settings_reload_keys(SettingsWindow *settings) { 1133 DavConfig *config = settings->config; 1134 DavCfgKey *key = config->keys; 1135 ui_list_clear(settings->keys_list); 1136 while(key) { 1137 if(key->name.value.ptr) { 1138 ui_list_append(settings->keys_list, key); 1139 } 1140 key = key->next; 1141 } 1142 ui_list_update(settings->keys_list); 1143 } 1144 1145 const char* dav_tlsversion2str(int value) { 1146 if(value == CURL_SSLVERSION_TLSv1) { 1147 return "TLSv1"; 1148 } else if(value == CURL_SSLVERSION_SSLv2) { 1149 return "SSLv2"; 1150 } else if(value == CURL_SSLVERSION_SSLv3) { 1151 return "SSLv3"; 1152 } 1153 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7034 1154 else if(value == CURL_SSLVERSION_TLSv1_0) { 1155 return "TLSv1.0"; 1156 } else if(value == CURL_SSLVERSION_TLSv1_1) { 1157 return "TLSv1.1"; 1158 } else if(value == CURL_SSLVERSION_TLSv1_2) { 1159 return "TLSv1.2"; 1160 } 1161 #endif 1162 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7052 1163 else if(value == CURL_SSLVERSION_TLSv1_3) { 1164 return "TLSv1.3"; 1165 } 1166 #endif 1167 return NULL; 1168 } 1169 1170 1171 /* ----------------------------- Credentials ----------------------------- */ 1172 1173 void settings_reload_credentials(SettingsWindow *settings) { 1174 PwdStore *pwd = settings->pwdstore; 1175 1176 ui_list_clear(settings->credentials_users); 1177 1178 CxIterator i = cxListIterator(pwd->noloc); 1179 cx_foreach(PwdIndexEntry*, entry, i) { 1180 char *id = ui_strdup(settings->obj->ctx, entry->id); 1181 ui_list_append(settings->credentials_users, id); 1182 } 1183 i = cxListIterator(pwd->locations); 1184 cx_foreach(PwdIndexEntry*, entry, i) { 1185 char *id = ui_strdup(settings->obj->ctx, entry->id); 1186 ui_list_append(settings->credentials_users, id); 1187 } 1188 1189 ui_list_update(settings->credentials_users); 1190 } 1191 1192 void settings_reload_repo_credentials(SettingsWindow *settings) { 1193 PwdStore *pwd = settings->pwdstore; 1194 1195 ui_list_clear(settings->repo_credentials); 1196 1197 // repo_credentials needs an entry for selecting no credentials 1198 ui_list_append(settings->repo_credentials, "-"); 1199 1200 CxIterator i = cxListIterator(pwd->noloc); 1201 cx_foreach(PwdIndexEntry*, entry, i) { 1202 char *id = ui_strdup(settings->obj->ctx, entry->id); 1203 ui_list_append(settings->repo_credentials, id); 1204 } 1205 i = cxListIterator(pwd->locations); 1206 cx_foreach(PwdIndexEntry*, entry, i) { 1207 char *id = ui_strdup(settings->obj->ctx, entry->id); 1208 ui_list_append(settings->repo_credentials, id); 1209 } 1210 1211 ui_list_update(settings->repo_credentials); 1212 } 1213 1214 1215 void settings_credentials_select(SettingsWindow *settings, const char *id) { 1216 if(!id && !settings->credentials_selected_id && !settings->credentials_new) { 1217 fprintf(stderr, "Error: no credentials id selected\n"); 1218 return; 1219 } 1220 1221 PwdStore *pwd = settings->pwdstore; 1222 1223 if(id) { 1224 ui_free(settings->obj->ctx, settings->credentials_selected_id); 1225 settings->credentials_selected_id = ui_strdup(settings->obj->ctx, id); 1226 } 1227 1228 if(!pwd->isdecrypted) { 1229 settings_credentials_decrypt(settings); 1230 return; 1231 } 1232 1233 if(settings->credentials_new) { 1234 ui_free(settings->obj->ctx, settings->credentials_selected_id); 1235 settings->credentials_selected_id = NULL; 1236 settings->credentials_selected_index = ui_list_count(settings->credentials_users); 1237 ui_list_append(settings->credentials_users, ui_strdup(settings->obj->ctx, "new")); 1238 settings->credentials_ignore_selectionevent = TRUE; 1239 ui_list_update(settings->credentials_users); 1240 ui_list_setselection(settings->credentials_users, settings->credentials_selected_index); 1241 settings->credentials_ignore_selectionevent = FALSE; 1242 1243 ui_set(settings->credentials_id, "new"); 1244 ui_set(settings->credentials_user, ""); 1245 ui_set(settings->credentials_password, ""); 1246 ui_list_clear(settings->credentials_locations); 1247 ui_list_update(settings->credentials_locations); 1248 } else { 1249 PwdEntry *entry = cxMapGet(pwd->ids, settings->credentials_selected_id); 1250 PwdIndexEntry *index = cxMapGet(pwd->index, settings->credentials_selected_id); 1251 if(!entry) { 1252 fprintf(stderr, "Error: cannot get pwd entry %s\n", settings->credentials_selected_id); 1253 return; 1254 } 1255 if(!index) { 1256 fprintf(stderr, "Error: missing PwdIndexEntry. PwdStore may be broken.\n"); 1257 return; 1258 } 1259 1260 ui_set(settings->credentials_id, entry->id); 1261 ui_set(settings->credentials_user, entry->user); 1262 ui_set(settings->credentials_password, entry->password); 1263 1264 ui_list_clear(settings->credentials_locations); 1265 if(index->locations) { 1266 CxIterator i = cxListIterator(index->locations); 1267 cx_foreach(char *, loc, i) { 1268 ui_list_append(settings->credentials_locations, ui_strdup(settings->obj->ctx, loc)); 1269 } 1270 } 1271 } 1272 1273 ui_list_update(settings->credentials_locations); 1274 1275 ui_set_group(settings->obj->ctx, SETTINGS_STATE_CREDENTIALS_SELECTED); 1276 } 1277 1278 void settings_credentials_clear(SettingsWindow *settings) { 1279 ui_free(settings->obj->ctx, settings->credentials_selected_id); 1280 settings->credentials_selected_id = NULL; 1281 settings->credentials_new = FALSE; 1282 1283 ui_set(settings->credentials_id, ""); 1284 ui_set(settings->credentials_user, ""); 1285 ui_set(settings->credentials_password, ""); 1286 ui_list_clear(settings->credentials_locations); 1287 ui_list_update(settings->credentials_locations); 1288 1289 ui_unset_group(settings->obj->ctx, SETTINGS_STATE_CREDENTIALS_SELECTED); 1290 } 1291 1292 int settings_credentials_save(SettingsWindow *settings) { 1293 if(settings->credentials_selected_index == -1) { 1294 return 0; 1295 } 1296 1297 // check if the credentials list contains an element with the same id 1298 // if an index is found, it must match the selected index, otherwise 1299 // we have a duplicate identifier 1300 char *newid = ui_get(settings->credentials_id); 1301 if(strlen(newid) == 0) { 1302 return 0; // TODO: check if other fields are set 1303 } 1304 1305 ssize_t index = cxListFind(settings->credentials_users->data, newid); 1306 if(index >=0 && index != settings->credentials_selected_index) { 1307 cxmutstr s = cx_asprintf("Identifier %s already in use", newid); 1308 ui_dialog(settings->obj, .title = "Error", .content = s.ptr, .closebutton_label = "OK"); 1309 free(s.ptr); 1310 return 1; 1311 } 1312 1313 char *user = ui_get(settings->credentials_user); 1314 char *password = ui_get(settings->credentials_password); 1315 CxList *ui_locations = settings->credentials_locations->data; 1316 size_t numlocations = cxListSize(ui_locations); 1317 CxList *locations = NULL; 1318 if(numlocations > 0) { 1319 locations = cxArrayListCreateSimple(CX_STORE_POINTERS, numlocations); 1320 CxIterator i = cxListIterator(ui_locations); 1321 cx_foreach(char*, loc, i) { 1322 cxListAdd(locations, strdup(loc)); 1323 } 1324 } 1325 1326 // if the user id changed, mark the list as changed to update it later 1327 settings->credentials_list_needs_update = settings->credentials_new || strcmp(newid, settings->credentials_selected_id); 1328 if(settings->credentials_new || strcmp(newid, settings->credentials_selected_id ? settings->credentials_selected_id : "")) { 1329 settings->credentials_list_needs_update = TRUE; 1330 cxListRemove(settings->credentials_users->data, settings->credentials_selected_index); 1331 cxListInsert(settings->credentials_users->data, settings->credentials_selected_index, ui_strdup(settings->obj->ctx, newid)); 1332 } 1333 1334 // if the pwdstore already contains this id, remove it first 1335 if(settings->credentials_selected_id) { 1336 pwdstore_remove_entry(settings->pwdstore, settings->credentials_selected_id); 1337 } 1338 1339 pwdstore_put(settings->pwdstore, newid, user, password); 1340 pwdstore_put_index(settings->pwdstore, strdup(newid), locations); 1341 settings->credentials_modified = TRUE; 1342 settings->credentials_new = FALSE; 1343 1344 return 0; 1345 } 1346 1347 void *keylist_getvalue(void *data, int col) { 1348 DavCfgKey *key = data; 1349 return key->name.value.ptr; 1350 } 1351 1352 void settings_edit_key(SettingsWindow *settings, DavCfgKey *key) { 1353 if(key->name.value.ptr) { 1354 ui_set(settings->key_name, key->name.value.ptr); 1355 } 1356 const char *key_type; 1357 switch(key->type) { 1358 default: key_type = ""; break; 1359 case DAV_KEY_TYPE_AES256: { 1360 key_type = "AES256"; 1361 break; 1362 } 1363 case DAV_KEY_TYPE_AES128: { 1364 key_type = "AES128"; 1365 break; 1366 } 1367 case DAV_KEY_TYPE_UNKNOWN: { 1368 key_type = "unknown"; 1369 break; 1370 } 1371 } 1372 ui_set(settings->key_type, key_type); 1373 1374 if(key->file.value.ptr) { 1375 ui_set(settings->key_file, key->file.value.ptr); 1376 } 1377 } 1378 1379 void settings_clear_key(SettingsWindow *settings) { 1380 ui_set(settings->key_name, ""); 1381 ui_set(settings->key_type, ""); 1382 ui_set(settings->key_file, ""); 1383 } 1384 1385