messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

commit 2fe6e07cc51acfb3573b68da956dd54577859df2
parent 13c5869a7ddfe895f9104e5f28530930d28b8eca
Author: Jacki <jacki@thejackimonster.de>
Date:   Sat, 17 Jan 2026 04:04:58 +0100

Implement using libsecret for automatic secret usage to encrypt/decrypt local keys

Signed-off-by: Jacki <jacki@thejackimonster.de>

Diffstat:
Mmeson.build | 1+
Msrc/application.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/application.h | 3++-
Msrc/event.c | 55++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/meson.build | 3++-
Asrc/secret.c | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/secret.h | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/accounts.c | 51++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/ui/messenger.c | 60++++++++++++++++++++++++++++++++++++++++++++++++------------
9 files changed, 695 insertions(+), 51 deletions(-)

diff --git a/meson.build b/meson.build @@ -54,6 +54,7 @@ messenger_gtk_deps = [ dependency('libnotify'), dependency('libqrencode'), dependency('libpipewire-0.3'), + dependency('libsecret-1'), declare_dependency(link_args: '-lunistring'), ] diff --git a/src/application.c b/src/application.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,8 +23,6 @@ */ #include "application.h" -#include "request.h" -#include "resources.h" #include <glib-2.0/glib.h> #include <gnunet/gnunet_common.h> @@ -40,6 +38,10 @@ #include <libportal-gtk3/portal-gtk3.h> #endif +#include "request.h" +#include "resources.h" +#include "secret.h" + static void _load_ui_stylesheets(MESSENGER_Application *app) { @@ -92,41 +94,69 @@ _application_accounts(gpointer user_data) } static void -_application_init(MESSENGER_Application *app) +_identity_secret_lookup(MESSENGER_Application *app, + const char *secret, + uint32_t secret_len, + gboolean success, + gboolean error, + gpointer user_data) { - g_assert(app); - - schedule_load_glib(&(app->ui.schedule)); + g_assert((app) && (user_data)); - ui_messenger_init(app, &(app->ui.messenger)); + char *identity = user_data; -#ifndef MESSENGER_APPLICATION_NO_PORTAL - if (app->portal) - app->parent = xdp_parent_new_gtk(GTK_WINDOW(app->ui.messenger.main_window)); -#endif + if (error) + { + fprintf(stderr, "ERROR: Looking up secret failed\n"); + } + else if ((success) && (secret) && (secret_len > 0)) + { + struct GNUNET_CHAT_Account *account; - GSourceFunc function = G_SOURCE_FUNC(_application_accounts); + application_chat_lock(app); + account = GNUNET_CHAT_find_account(app->chat.messenger.handle, identity); + GNUNET_CHAT_connect(app->chat.messenger.handle, account, secret, secret_len); + application_chat_unlock(app); - if (app->chat.identity) + app->init = util_idle_add(G_SOURCE_FUNC(_application_main_window), app); + return; + } + else { struct GNUNET_CHAT_Account *account; application_chat_lock(app); - account = GNUNET_CHAT_find_account( - app->chat.messenger.handle, - app->chat.identity - ); + account = GNUNET_CHAT_find_account(app->chat.messenger.handle, identity); + application_chat_unlock(app); if (account) { - GNUNET_CHAT_connect(app->chat.messenger.handle, account); - function = G_SOURCE_FUNC(_application_main_window); + secret_operation_generate(app, identity, &_identity_secret_lookup, identity); + return; } - - application_chat_unlock(app); } - app->init = util_idle_add(function, app); + app->init = util_idle_add(G_SOURCE_FUNC(_application_accounts), app); +} + +static void +_application_init(MESSENGER_Application *app) +{ + g_assert(app); + + schedule_load_glib(&(app->ui.schedule)); + + ui_messenger_init(app, &(app->ui.messenger)); + +#ifndef MESSENGER_APPLICATION_NO_PORTAL + if (app->portal) + app->parent = xdp_parent_new_gtk(GTK_WINDOW(app->ui.messenger.main_window)); +#endif + + if (app->chat.identity) + secret_operation_lookup(app, app->chat.identity, &_identity_secret_lookup, app->chat.identity); + else + app->init = util_idle_add(G_SOURCE_FUNC(_application_accounts), app); } static void @@ -235,6 +265,7 @@ application_init(MESSENGER_Application *app, notify_init(MESSENGER_APPLICATION_NAME); app->notifications = NULL; app->requests = NULL; + app->secrets = NULL; _load_ui_stylesheets(app); @@ -372,6 +403,20 @@ application_run(MESSENGER_Application *app) pthread_join(app->chat.tid, NULL); GList *list; + + // Get rid of open secret operations + list = app->secrets; + + while (list) + { + if (list->data) + { + secret_operation_cancel((MESSENGER_SecretOperation*) list->data); + secret_operation_destroy((MESSENGER_SecretOperation*) list->data); + } + + list = list->next; + } // Get rid of open requests list = app->requests; @@ -398,6 +443,9 @@ application_run(MESSENGER_Application *app) list = list->next; } + if (app->secrets) + g_list_free(app->secrets); + if (app->requests) g_list_free(app->requests); diff --git a/src/application.h b/src/application.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -89,6 +89,7 @@ typedef struct MESSENGER_Application GtkApplication *application; GList *notifications; GList *requests; + GList *secrets; guint init; #ifndef MESSENGER_APPLICATION_NO_PORTAL diff --git a/src/event.c b/src/event.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -24,22 +24,23 @@ #include "event.h" +#include <glib-2.0/glib.h> +#include <gnunet/gnunet_chat_lib.h> +#include <gnunet/gnunet_common.h> +#include <stdio.h> + #include "account.h" #include "application.h" #include "contact.h" #include "discourse.h" #include "file.h" +#include "secret.h" #include "ui.h" #include "ui/chat_entry.h" #include "ui/chat_title.h" #include "ui/message.h" -#include <glib-2.0/glib.h> -#include <gnunet/gnunet_chat_lib.h> -#include <gnunet/gnunet_common.h> -#include <stdio.h> - static void _close_notification(NotifyNotification* notification, gpointer user_data) @@ -434,6 +435,40 @@ event_cleanup_profile(MESSENGER_Application *app) GNUNET_CHAT_iterate_files(chat->handle, _cleanup_profile_files, NULL); } +static void +_account_secret_lookup(MESSENGER_Application *app, + const char *secret, + uint32_t secret_len, + gboolean success, + gboolean error, + gpointer user_data) +{ + g_assert((app) && (user_data)); + + struct GNUNET_CHAT_Account *account = user_data; + + if (error) + { + fprintf(stderr, "ERROR: Looking up secret failed\n"); + } + else if ((success) && (secret) && (secret_len > 0)) + { + application_chat_lock(app); + GNUNET_CHAT_connect(app->chat.messenger.handle, account, secret, secret_len); + application_chat_unlock(app); + } + else + { + const char *name; + + application_chat_lock(app); + name = GNUNET_CHAT_account_get_name(account); + application_chat_unlock(app); + + secret_operation_generate(app, name, &_account_secret_lookup, account); + } +} + void event_select_profile(MESSENGER_Application *app, struct GNUNET_CHAT_Context *context, @@ -441,12 +476,14 @@ event_select_profile(MESSENGER_Application *app, { g_assert((app) && (!context) && (msg)); - CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); - struct GNUNET_CHAT_Account *account = GNUNET_CHAT_message_get_account(msg); if (GNUNET_CHAT_KIND_CREATED_ACCOUNT == GNUNET_CHAT_message_get_kind(msg)) - GNUNET_CHAT_connect(chat->handle, account); + { + const char *name = GNUNET_CHAT_account_get_name(account); + + secret_operation_lookup(app, name, &_account_secret_lookup, account); + } } gboolean diff --git a/src/meson.build b/src/meson.build @@ -1,6 +1,6 @@ # # This file is part of GNUnet. -# Copyright (C) 2023--2024 GNUnet e.V. +# Copyright (C) 2023--2026 GNUnet e.V. # # GNUnet is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published @@ -32,6 +32,7 @@ messenger_gtk_sources = files([ 'request.c', 'request.h', 'resources.c', 'resources.h', 'schedule.c', 'schedule.h', + 'secret.c', 'secret.h', 'ui.c', 'ui.h', 'util.c', 'util.h', 'messenger_gtk.c', diff --git a/src/secret.c b/src/secret.c @@ -0,0 +1,365 @@ +/* + This file is part of GNUnet. + Copyright (C) 2026 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file secret.c + */ + +#include "secret.h" + +#include <glib-2.0/glib.h> +#include <gnunet/gnunet_chat_lib.h> +#include <gnunet/gnunet_common.h> +#include <string.h> +#include <libsecret/secret.h> +#include <gnunet/gnunet_util_lib.h> + +#ifndef MESSENGER_APPLICATION_ID +#define SECRET_APP_ID "org.gnunet.Messenger" +#else +#define SECRET_APP_ID MESSENGER_APPLICATION_ID +#endif + +const SecretSchema * +_secret_schema(void) +{ + static const SecretSchema schema = { + "org.gnunet.chat.AccountSecret", SECRET_SCHEMA_NONE, + { + { "name", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "app_id", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "NULL", 0 }, + } + }; + return &schema; +} + +char* +_secret_description(const char *name) +{ + char *desc; + + GNUNET_asprintf( + &desc, + "GNUnet Messenger account secret for identity %s", + name + ); + + return desc; +} + +MESSENGER_SecretOperation* +_secret_operation_new(MESSENGER_Application *application, + MESSENGER_SecretCallback callback, + gpointer user_data) +{ + g_assert(application); + + GCancellable *cancellable = g_cancellable_new(); + + if (!cancellable) + return NULL; + + MESSENGER_SecretOperation* op = g_malloc(sizeof(MESSENGER_SecretOperation)); + + op->application = application; + op->callback = callback; + op->cancellable = cancellable; + op->user_data = user_data; + op->secret = NULL; + op->secret_len = 0; + + application->secrets = g_list_append( + application->secrets, + op + ); + + return op; +} + +void +_secret_operation_callback(MESSENGER_SecretOperation *op, + gboolean success, + gboolean error) +{ + g_assert(op); + + if (op->callback) + { + op->callback( + op->application, + op->secret, + op->secret_len, + success, + error, + op->user_data + ); + } + + secret_operation_drop(op); +} + +void +_secret_lookup_callback(GNUNET_UNUSED GObject *source_object, + GAsyncResult *result, + gpointer data) +{ + GError *error = NULL; + gchar *password; + + MESSENGER_SecretOperation *op = data; + + password = secret_password_lookup_finish(result, &error); + + if (error) + _secret_operation_callback(op, FALSE, TRUE); + else if (password) + { + op->secret = GNUNET_strdup(password); + op->secret_len = g_utf8_strlen(password, -1); + + _secret_operation_callback(op, TRUE, FALSE); + + secret_password_free(password); + } + else + _secret_operation_callback(op, FALSE, FALSE); +} + +MESSENGER_SecretOperation* +secret_operation_lookup(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data) +{ + g_assert((application) && (name)); + + MESSENGER_SecretOperation *op = _secret_operation_new( + application, + callback, + user_data + ); + + if (!op) + return NULL; + + secret_password_lookup( + _secret_schema(), + op->cancellable, + &_secret_lookup_callback, + op, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + return op; +} + +void +_secret_store_callback(GNUNET_UNUSED GObject *source_object, + GAsyncResult *result, + gpointer data) +{ + GError *error = NULL; + gboolean success; + + MESSENGER_SecretOperation *op = data; + + success = secret_password_store_finish(result, &error); + + if (error) + _secret_operation_callback(op, FALSE, TRUE); + else + _secret_operation_callback(op, success, FALSE); +} + +MESSENGER_SecretOperation* +secret_operation_store(MESSENGER_Application *application, + const char *name, + const char *secret, + uint32_t secret_len, + MESSENGER_SecretCallback callback, + gpointer user_data) +{ + g_assert((application) && (name) && (secret)); + + if (strlen(secret) != secret_len) + return NULL; + + MESSENGER_SecretOperation *op = _secret_operation_new( + application, + callback, + user_data + ); + + if (!op) + return NULL; + + op->secret = GNUNET_strndup(secret, secret_len + 1); + op->secret_len = secret_len; + + secret_password_store( + _secret_schema(), + SECRET_COLLECTION_DEFAULT, + _secret_description(name), + secret, + op->cancellable, + &_secret_store_callback, + op, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + return op; +} + +MESSENGER_SecretOperation* +secret_operation_generate(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data) +{ + char new_secret [65]; + uint32_t secret_len; + + g_assert((application) && (name)); + + secret_len = 64; + + if (GNUNET_OK != GNUNET_CHAT_generate_secret(new_secret, secret_len)) + return NULL; + + new_secret[secret_len] = '\0'; + + MESSENGER_SecretOperation *op = secret_operation_store( + application, + name, + new_secret, + secret_len, + callback, + user_data + ); + + secret_password_wipe(new_secret); + return op; +} + +void +_secret_delete_callback(GNUNET_UNUSED GObject *source_object, + GAsyncResult *result, + gpointer data) +{ + GError *error = NULL; + gboolean success; + + MESSENGER_SecretOperation *op = data; + + success = secret_password_clear_finish(result, &error); + + if (error) + _secret_operation_callback(op, FALSE, TRUE); + else + _secret_operation_callback(op, success, FALSE); +} + +MESSENGER_SecretOperation* +secret_operation_delete(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data) +{ + g_assert((application) && (name)); + + MESSENGER_SecretOperation *op = _secret_operation_new( + application, + callback, + user_data + ); + + if (!op) + return NULL; + + secret_password_clear( + _secret_schema(), + op->cancellable, + &_secret_delete_callback, + op, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + return op; +} + +void +secret_operation_cancel(MESSENGER_SecretOperation *op) +{ + g_assert(op); + + if (!op->cancellable) + return; + + if (!g_cancellable_is_cancelled(op->cancellable)) + g_cancellable_cancel(op->cancellable); +} + +void +secret_operation_cleanup(MESSENGER_SecretOperation *op) +{ + g_assert(op); + + if (op->secret) + { + secret_password_wipe(op->secret); + GNUNET_free(op->secret); + } + + if (!op->cancellable) + return; + + g_object_unref(op->cancellable); + op->cancellable = NULL; +} + +void +secret_operation_drop(MESSENGER_SecretOperation *op) +{ + g_assert(op); + + if (op->application->secrets) + op->application->secrets = g_list_remove( + op->application->secrets, + op + ); + + secret_operation_destroy(op); +} + +void +secret_operation_destroy(MESSENGER_SecretOperation *op) +{ + g_assert(op); + + secret_operation_cleanup(op); + g_free(op); +} diff --git a/src/secret.h b/src/secret.h @@ -0,0 +1,114 @@ +/* + This file is part of GNUnet. + Copyright (C) 2026 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file secret.h + */ + +#ifndef SECRET_H_ +#define SECRET_H_ + +#include <gnunet/gnunet_chat_lib.h> +#include <stdbool.h> +#include <stdint.h> + +#include "application.h" + +typedef void (*MESSENGER_SecretCallback)( + MESSENGER_Application *application, + const char *secret, + uint32_t secret_len, + gboolean success, + gboolean error, + gpointer user_data +); + +typedef struct MESSENGER_SecretOperation { + MESSENGER_Application *application; + MESSENGER_SecretCallback callback; + GCancellable *cancellable; + gpointer user_data; + char *secret; + uint32_t secret_len; +} MESSENGER_SecretOperation; + +/** + * Lookup a secret from identity with + * a given name. + * + * @param[in] name Identity name + * @param[out] secret_len Length of secret + * @return Secret or NULL + */ +MESSENGER_SecretOperation* +secret_operation_lookup(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data); + +/** + * Stores a secret for identity with + * a given name. + * + * @param[in] name Identity name + * @param[in] secret Secret + * @param[in] secret_len Length of secret + * @return Whether the storage was successful + */ +MESSENGER_SecretOperation* +secret_operation_store(MESSENGER_Application *application, + const char *name, + const char *secret, + uint32_t secret_len, + MESSENGER_SecretCallback callback, + gpointer user_data); + +MESSENGER_SecretOperation* +secret_operation_generate(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data); + +/** + * Delete a secret from identity with + * a given name. + * + * @param[in] name Identity name + * @return Whether the deletion was successful + */ +MESSENGER_SecretOperation* +secret_operation_delete(MESSENGER_Application *application, + const char *name, + MESSENGER_SecretCallback callback, + gpointer user_data); + +void +secret_operation_cancel(MESSENGER_SecretOperation *op); + +void +secret_operation_cleanup(MESSENGER_SecretOperation *op); + +void +secret_operation_drop(MESSENGER_SecretOperation *op); + +void +secret_operation_destroy(MESSENGER_SecretOperation *op); + +#endif /* SECRET_H_ */ diff --git a/src/ui/accounts.c b/src/ui/accounts.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2022--2024 GNUnet e.V. + Copyright (C) 2022--2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -24,10 +24,13 @@ #include "accounts.h" +#include <gnunet/gnunet_chat_lib.h> + #include "account_entry.h" #include "../account.h" #include "../application.h" +#include "../secret.h" #include "../ui.h" static void @@ -54,6 +57,40 @@ _open_new_account_dialog(gpointer user_data) } static void +_account_secret_lookup(MESSENGER_Application *app, + const char *secret, + uint32_t secret_len, + gboolean success, + gboolean error, + gpointer user_data) +{ + g_assert((app) && (user_data)); + + struct GNUNET_CHAT_Account *account = user_data; + + if (error) + { + fprintf(stderr, "ERROR: Looking up secret failed\n"); + } + else if ((success) && (secret) && (secret_len > 0)) + { + application_chat_lock(app); + GNUNET_CHAT_connect(app->chat.messenger.handle, account, secret, secret_len); + application_chat_unlock(app); + } + else + { + const char *name; + + application_chat_lock(app); + name = GNUNET_CHAT_account_get_name(account); + application_chat_unlock(app); + + secret_operation_generate(app, name, &_account_secret_lookup, account); + } +} + +static void handle_accounts_listbox_row_activated(UNUSED GtkListBox* listbox, GtkListBoxRow* row, gpointer user_data) @@ -80,14 +117,18 @@ handle_accounts_listbox_row_activated(UNUSED GtkListBox* listbox, goto close_dialog; app->ui.state = MESSENGER_STATE_MAIN_WINDOW; - - application_chat_lock(app); - GNUNET_CHAT_connect(app->chat.messenger.handle, account); - application_chat_unlock(app); gtk_list_box_unselect_all(app->ui.messenger.accounts_listbox); gtk_widget_set_sensitive(GTK_WIDGET(app->ui.accounts.dialog), FALSE); + const char *name; + + application_chat_lock(app); + name = GNUNET_CHAT_account_get_name(account); + application_chat_unlock(app); + + secret_operation_lookup(app, name, &_account_secret_lookup, account); + close_dialog: gtk_window_close(GTK_WINDOW(app->ui.accounts.dialog)); } diff --git a/src/ui/messenger.c b/src/ui/messenger.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -24,7 +24,9 @@ #include "messenger.h" +#include <gnunet/gnunet_chat_lib.h> #include <gtk-3.0/gdk/gdkkeys.h> +#include <stdio.h> #include "account_entry.h" #include "chat_entry.h" @@ -39,6 +41,7 @@ #include "../account.h" #include "../application.h" +#include "../secret.h" #include "../ui.h" static void @@ -186,6 +189,43 @@ handle_account_details_button_click(UNUSED GtkButton* button, } static void +_account_secret_lookup(MESSENGER_Application *app, + const char *secret, + uint32_t secret_len, + gboolean success, + gboolean error, + gpointer user_data) +{ + g_assert((app) && (user_data)); + + struct GNUNET_CHAT_Account *account = user_data; + + if (error) + { + fprintf(stderr, "ERROR: Looking up secret failed\n"); + } + else if ((success) && (secret) && (secret_len > 0)) + { + _switch_details_revealer_visibility(&(app->ui.messenger), FALSE); + hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); + + application_chat_lock(app); + GNUNET_CHAT_connect(app->chat.messenger.handle, account, secret, secret_len); + application_chat_unlock(app); + } + else + { + const char *name; + + application_chat_lock(app); + name = GNUNET_CHAT_account_get_name(account); + application_chat_unlock(app); + + secret_operation_generate(app, name, &_account_secret_lookup, account); + } +} + +static void handle_accounts_listbox_row_activated(UNUSED GtkListBox* listbox, GtkListBoxRow* row, gpointer user_data) @@ -212,23 +252,19 @@ handle_accounts_listbox_row_activated(UNUSED GtkListBox* listbox, if (!account) return; - application_chat_lock(app); - - const struct GNUNET_CHAT_Account *current = GNUNET_CHAT_get_connected( - app->chat.messenger.handle - ); + const struct GNUNET_CHAT_Account *current; + const char *name; + + application_chat_lock(app); + current = GNUNET_CHAT_get_connected(app->chat.messenger.handle); + name = GNUNET_CHAT_account_get_name(account); application_chat_unlock(app); if (account == current) return; - _switch_details_revealer_visibility(&(app->ui.messenger), FALSE); - hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); - - application_chat_lock(app); - GNUNET_CHAT_connect(app->chat.messenger.handle, account); - application_chat_unlock(app); + secret_operation_lookup(app, name, &_account_secret_lookup, account); } static void