From b09af655e7b318f1070b12149603d5dcd5190943 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Mon, 15 Jun 2026 22:56:50 -0600 Subject: [PATCH 1/3] Add FiSHLiM GTK key manager + context menu access --- plugins/fishlim/fishlim.vcxproj | 2 +- plugins/fishlim/keystore.c | 13 ++ plugins/fishlim/keystore.h | 1 + plugins/fishlim/meson.build | 2 +- plugins/fishlim/plugin_zoitechat.c | 264 ++++++++++++++++++++++++++++- 5 files changed, 277 insertions(+), 5 deletions(-) diff --git a/plugins/fishlim/fishlim.vcxproj b/plugins/fishlim/fishlim.vcxproj index a0c13d7f..c6c3df6d 100644 --- a/plugins/fishlim/fishlim.vcxproj +++ b/plugins/fishlim/fishlim.vcxproj @@ -26,7 +26,7 @@ WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;FISHLIM_EXPORTS;HAVE_DH_SET0_PQG;HAVE_DH_GET0_KEY;HAVE_DH_SET0_KEY;%(PreprocessorDefinitions) - $(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories) + $(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(Gtk);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories) fishlim.def diff --git a/plugins/fishlim/keystore.c b/plugins/fishlim/keystore.c index bce7ed77..8e64a6bd 100644 --- a/plugins/fishlim/keystore.c +++ b/plugins/fishlim/keystore.c @@ -194,6 +194,18 @@ static gboolean keyfile_save_to_file (GKeyFile *keyfile, char *filename) { } #endif + +gchar **keystore_get_targets(gsize *length) { + GKeyFile *keyfile; + gchar **groups; + + keyfile = getConfigFile(); + groups = g_key_file_get_groups(keyfile, length); + g_key_file_free(keyfile); + + return groups; +} + /** * Writes the key store file to disk. */ @@ -217,6 +229,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS /** * Sets a key in the key store file. */ + gboolean keystore_store_key(const char *nick, const char *key, enum fish_mode mode) { const char *password; char *encrypted; diff --git a/plugins/fishlim/keystore.h b/plugins/fishlim/keystore.h index 4af46693..b71d57e6 100644 --- a/plugins/fishlim/keystore.h +++ b/plugins/fishlim/keystore.h @@ -33,6 +33,7 @@ char *keystore_get_key(const char *nick, enum fish_mode *mode); gboolean keystore_store_key(const char *nick, const char *key, enum fish_mode mode); gboolean keystore_delete_nick(const char *nick); +gchar **keystore_get_targets(gsize *length); #endif diff --git a/plugins/fishlim/meson.build b/plugins/fishlim/meson.build index 19715457..03cb6c91 100644 --- a/plugins/fishlim/meson.build +++ b/plugins/fishlim/meson.build @@ -15,7 +15,7 @@ fishlim_sources = [ ] shared_module('fishlim', fishlim_sources, - dependencies: [libgio_dep, zoitechat_plugin_dep, libssl_dep], + dependencies: [libgio_dep, gtk_dep, zoitechat_plugin_dep, libssl_dep], c_args: ['-DOPENSSL_API_COMPAT=0x10100000L'], install: true, install_dir: plugindir, diff --git a/plugins/fishlim/plugin_zoitechat.c b/plugins/fishlim/plugin_zoitechat.c index 4b7cb4b5..89834316 100644 --- a/plugins/fishlim/plugin_zoitechat.c +++ b/plugins/fishlim/plugin_zoitechat.c @@ -27,6 +27,7 @@ #include "config.h" #include +#include #include #include #include "zoitechat-plugin.h" @@ -44,7 +45,7 @@ static const char plugin_version[] = "1.0.0"; static const char usage_setkey[] = "Usage: SETKEY [] [:], sets the key for a channel or nick. Modes: ECB, CBC"; static const char usage_delkey[] = "Usage: DELKEY [], deletes the key for a channel or nick"; -static const char usage_keyx[] = "Usage: KEYX [], performs DH1080 key-exchange with "; +static const char usage_keyx[] = "Usage: KEYX [] [ECB|CBC], performs DH1080 key-exchange with "; static const char usage_topic[] = "Usage: TOPIC+ , sets a new encrypted topic for the current channel"; static const char usage_notice[] = "Usage: NOTICE+ "; static const char usage_msg[] = "Usage: MSG+ "; @@ -52,6 +53,12 @@ static const char usage_msg[] = "Usage: MSG+ "; static zoitechat_plugin *ph; static GHashTable *pending_exchanges; +static GtkWidget *fishlim_dialog; +static GtkWidget *fishlim_target_entry; +static GtkWidget *fishlim_key_entry; +static GtkWidget *fishlim_mode_combo; +static GtkWidget *fishlim_status_label; +static GtkListStore *fishlim_store; /** @@ -455,6 +462,239 @@ cleanup: return ZOITECHAT_EAT_ALL; } + +static const char *fishlim_gui_target(void) { + const char *target; + + target = gtk_entry_get_text(GTK_ENTRY(fishlim_target_entry)); + if (target && *target) + return target; + + return zoitechat_get_info(ph, "channel"); +} + +static void fishlim_gui_refresh(void) { + GtkTreeIter iter; + gchar **targets; + gsize length, i; + const char *target; + char *key; + enum fish_mode mode; + + if (!fishlim_dialog) + return; + + gtk_list_store_clear(fishlim_store); + targets = keystore_get_targets(&length); + + for (i = 0; targets && i < length; i++) { + key = keystore_get_key(targets[i], &mode); + gtk_list_store_append(fishlim_store, &iter); + gtk_list_store_set(fishlim_store, &iter, 0, targets[i], 1, fish_modes[mode], 2, key ? "Stored" : "Unreadable", -1); + g_free(key); + } + + g_strfreev(targets); + + target = fishlim_gui_target(); + key = target ? keystore_get_key(target, &mode) : NULL; + + if (key) { + char *text = g_strdup_printf("Key stored for %s (%s)", target, fish_modes[mode]); + gtk_label_set_text(GTK_LABEL(fishlim_status_label), text); + gtk_combo_box_set_active(GTK_COMBO_BOX(fishlim_mode_combo), mode == FISH_CBC_MODE ? 1 : 0); + g_free(text); + g_free(key); + } else if (target && *target) { + char *text = g_strdup_printf("No key for %s", target); + gtk_label_set_text(GTK_LABEL(fishlim_status_label), text); + g_free(text); + } else { + gtk_label_set_text(GTK_LABEL(fishlim_status_label), "No target selected"); + } +} + +static void fishlim_gui_refresh_cb(GtkWidget *widget, gpointer data) { + fishlim_gui_refresh(); +} + +static void fishlim_gui_set(GtkWidget *widget, gpointer data) { + const char *target = fishlim_gui_target(); + const char *key = gtk_entry_get_text(GTK_ENTRY(fishlim_key_entry)); + const char *mode = gtk_combo_box_get_active(GTK_COMBO_BOX(fishlim_mode_combo)) == 1 ? "CBC" : "ECB"; + + if (!target || !*target || !key || !*key) { + zoitechat_printf(ph, "%s\n", usage_setkey); + return; + } + + zoitechat_commandf(ph, "SETKEY %s %s:%s", target, mode, key); + gtk_entry_set_text(GTK_ENTRY(fishlim_key_entry), ""); + fishlim_gui_refresh(); +} + +static void fishlim_gui_delete(GtkWidget *widget, gpointer data) { + const char *target = fishlim_gui_target(); + + if (!target || !*target) { + zoitechat_printf(ph, "%s\n", usage_delkey); + return; + } + + zoitechat_commandf(ph, "DELKEY %s", target); + fishlim_gui_refresh(); +} + +static char *fishlim_gui_prompt_target(const char *title, const char *initial) { + GtkWidget *dialog, *content, *entry; + char *target = NULL; + + dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(fishlim_dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, "_Cancel", GTK_RESPONSE_CANCEL, "_OK", GTK_RESPONSE_OK, NULL); + content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + entry = gtk_entry_new(); + if (initial && *initial) + gtk_entry_set_text(GTK_ENTRY(entry), initial); + gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); + gtk_box_pack_start(GTK_BOX(content), gtk_label_new("User"), FALSE, FALSE, 6); + gtk_box_pack_start(GTK_BOX(content), entry, FALSE, FALSE, 6); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + gtk_widget_show_all(dialog); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + const char *text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (text && *text) + target = g_strdup(text); + } + + gtk_widget_destroy(dialog); + return target; +} + +static void fishlim_gui_keyx(GtkWidget *widget, gpointer data) { + char *target; + const char *initial = fishlim_gui_target(); + const char *mode = gtk_combo_box_get_active(GTK_COMBO_BOX(fishlim_mode_combo)) == 1 ? "CBC" : "ECB"; + + if (!initial || !*initial || !irc_is_query(initial)) + initial = NULL; + + target = fishlim_gui_prompt_target("Key Exchange", initial); + if (!target) + return; + + zoitechat_commandf(ph, "KEYX %s %s", target, mode); + g_free(target); +} + +static void fishlim_gui_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { + GtkTreeModel *model = gtk_tree_view_get_model(tree); + GtkTreeIter iter; + char *target; + + if (!gtk_tree_model_get_iter(model, &iter, path)) + return; + + gtk_tree_model_get(model, &iter, 0, &target, -1); + gtk_entry_set_text(GTK_ENTRY(fishlim_target_entry), target); + g_free(target); + fishlim_gui_refresh(); +} + +static void fishlim_gui_destroy(GtkWidget *widget, gpointer data) { + fishlim_dialog = NULL; + fishlim_target_entry = NULL; + fishlim_key_entry = NULL; + fishlim_mode_combo = NULL; + fishlim_status_label = NULL; + fishlim_store = NULL; +} + +static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { + GtkWidget *content, *grid, *view, *scroll, *buttons, *button; + GtkCellRenderer *renderer; + const char *target; + + target = *word_eol[2] ? word_eol[2] : zoitechat_get_info(ph, "channel"); + + if (fishlim_dialog) { + if (target) + gtk_entry_set_text(GTK_ENTRY(fishlim_target_entry), target); + gtk_window_present(GTK_WINDOW(fishlim_dialog)); + return ZOITECHAT_EAT_ZOITECHAT; + } + + fishlim_dialog = gtk_dialog_new_with_buttons("FiSHLiM", NULL, GTK_DIALOG_DESTROY_WITH_PARENT, "_Close", GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(fishlim_dialog), 520, 360); + g_signal_connect(fishlim_dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL); + g_signal_connect(fishlim_dialog, "destroy", G_CALLBACK(fishlim_gui_destroy), NULL); + + content = gtk_dialog_get_content_area(GTK_DIALOG(fishlim_dialog)); + grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 8); + gtk_grid_set_column_spacing(GTK_GRID(grid), 8); + gtk_container_set_border_width(GTK_CONTAINER(grid), 12); + gtk_box_pack_start(GTK_BOX(content), grid, TRUE, TRUE, 0); + + fishlim_target_entry = gtk_entry_new(); + if (target) + gtk_entry_set_text(GTK_ENTRY(fishlim_target_entry), target); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Target"), 0, 0, 1, 1); + gtk_grid_attach(GTK_GRID(grid), fishlim_target_entry, 1, 0, 2, 1); + + fishlim_mode_combo = gtk_combo_box_text_new(); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(fishlim_mode_combo), "ECB"); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(fishlim_mode_combo), "CBC"); + gtk_combo_box_set_active(GTK_COMBO_BOX(fishlim_mode_combo), 1); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Mode"), 0, 1, 1, 1); + gtk_grid_attach(GTK_GRID(grid), fishlim_mode_combo, 1, 1, 2, 1); + + fishlim_key_entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(fishlim_key_entry), FALSE); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Key"), 0, 2, 1, 1); + gtk_grid_attach(GTK_GRID(grid), fishlim_key_entry, 1, 2, 2, 1); + + buttons = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout(GTK_BUTTON_BOX(buttons), GTK_BUTTONBOX_END); + gtk_grid_attach(GTK_GRID(grid), buttons, 0, 3, 3, 1); + + button = gtk_button_new_with_label("Set Key"); + g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_set), NULL); + gtk_container_add(GTK_CONTAINER(buttons), button); + + button = gtk_button_new_with_label("Delete Key"); + g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_delete), NULL); + gtk_container_add(GTK_CONTAINER(buttons), button); + + button = gtk_button_new_with_label("Key Exchange"); + g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_keyx), NULL); + gtk_container_add(GTK_CONTAINER(buttons), button); + + fishlim_status_label = gtk_label_new(NULL); + gtk_label_set_xalign(GTK_LABEL(fishlim_status_label), 0.0); + gtk_grid_attach(GTK_GRID(grid), fishlim_status_label, 0, 4, 3, 1); + + fishlim_store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(fishlim_store)); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Target", renderer, "text", 0, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Mode", renderer, "text", 1, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Status", renderer, "text", 2, NULL); + g_signal_connect(view, "row-activated", G_CALLBACK(fishlim_gui_row_activated), NULL); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_vexpand(scroll, TRUE); + gtk_container_add(GTK_CONTAINER(scroll), view); + gtk_grid_attach(GTK_GRID(grid), scroll, 0, 5, 3, 1); + + g_signal_connect(fishlim_target_entry, "changed", G_CALLBACK(fishlim_gui_refresh_cb), NULL); + fishlim_gui_refresh(); + + gtk_widget_show_all(fishlim_dialog); + return ZOITECHAT_EAT_ZOITECHAT; +} + /** * Command handler for /setkey */ @@ -533,8 +773,18 @@ static int handle_keyx(char *word[], char *word_eol[], void *userdata) { const char *target = word[2]; zoitechat_context *query_ctx = NULL; char *pub_key, *priv_key; + enum fish_mode mode = FISH_CBC_MODE; int ctx_type; + if (*word[3]) { + if (g_ascii_strcasecmp(word[3], "ECB") == 0) + mode = FISH_ECB_MODE; + else if (g_ascii_strcasecmp(word[3], "CBC") != 0) { + zoitechat_printf(ph, "%s", usage_keyx); + return ZOITECHAT_EAT_ALL; + } + } + if (*target) query_ctx = find_context_on_network(target); else { @@ -555,8 +805,8 @@ static int handle_keyx(char *word[], char *word_eol[], void *userdata) { if (dh1080_generate_key(&priv_key, &pub_key)) { g_hash_table_replace (pending_exchanges, g_ascii_strdown(target, -1), priv_key); - zoitechat_commandf(ph, "quote NOTICE %s :DH1080_INIT %s CBC", target, pub_key); - zoitechat_printf(ph, "Sent public key to %s (CBC), waiting for reply...", target); + zoitechat_commandf(ph, "quote NOTICE %s :DH1080_INIT %s%s", target, pub_key, (mode == FISH_CBC_MODE) ? " CBC" : ""); + zoitechat_printf(ph, "Sent public key to %s (%s), waiting for reply...", target, fish_modes[mode]); g_free(pub_key); } else { @@ -789,6 +1039,7 @@ int zoitechat_plugin_init(zoitechat_plugin *plugin_handle, *version = plugin_version; /* Register commands */ + zoitechat_hook_command(ph, "FISHLIM", ZOITECHAT_PRI_NORM, handle_fishlim, "Usage: FISHLIM, opens the FiSHLiM key manager", NULL); zoitechat_hook_command(ph, "SETKEY", ZOITECHAT_PRI_NORM, handle_setkey, usage_setkey, NULL); zoitechat_hook_command(ph, "DELKEY", ZOITECHAT_PRI_NORM, handle_delkey, usage_delkey, NULL); zoitechat_hook_command(ph, "KEYX", ZOITECHAT_PRI_NORM, handle_keyx, usage_keyx, NULL); @@ -817,12 +1068,19 @@ int zoitechat_plugin_init(zoitechat_plugin *plugin_handle, pending_exchanges = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + zoitechat_command(ph, "MENU ADD \"Window/FiSHLiM Key Manager\" \"FISHLIM\""); + zoitechat_command(ph, "MENU ADD \"$NICK/FiSHLiM Key Manager\" \"FISHLIM %s\""); + zoitechat_printf(ph, "%s plugin loaded\n", plugin_name); /* Return success */ return 1; } int zoitechat_plugin_deinit(void) { + zoitechat_command(ph, "MENU DEL \"Window/FiSHLiM Key Manager\""); + zoitechat_command(ph, "MENU DEL \"$NICK/FiSHLiM Key Manager\""); + if (fishlim_dialog) + gtk_widget_destroy(fishlim_dialog); g_clear_pointer(&pending_exchanges, g_hash_table_destroy); dh1080_deinit(); fish_deinit(); From adb18b7ce36a0cae8b90d93e321b1463122d9407 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Mon, 15 Jun 2026 23:26:59 -0600 Subject: [PATCH 2/3] Improve FiSHLiM key delete flow + CBC warnings --- plugins/fishlim/plugin_zoitechat.c | 62 ++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/plugins/fishlim/plugin_zoitechat.c b/plugins/fishlim/plugin_zoitechat.c index 89834316..e18366b5 100644 --- a/plugins/fishlim/plugin_zoitechat.c +++ b/plugins/fishlim/plugin_zoitechat.c @@ -59,6 +59,7 @@ static GtkWidget *fishlim_key_entry; static GtkWidget *fishlim_mode_combo; static GtkWidget *fishlim_status_label; static GtkListStore *fishlim_store; +static GtkWidget *fishlim_view; /** @@ -533,16 +534,54 @@ static void fishlim_gui_set(GtkWidget *widget, gpointer data) { fishlim_gui_refresh(); } +static gboolean fishlim_gui_confirm_delete(const char *target) { + GtkWidget *dialog; + char *message; + gboolean confirmed; + + message = g_strdup_printf("Delete the key for %s?", target); + dialog = gtk_message_dialog_new(GTK_WINDOW(fishlim_dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s", message); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "This cannot be undone."); + gtk_dialog_add_buttons(GTK_DIALOG(dialog), "_Cancel", GTK_RESPONSE_CANCEL, "_Delete", GTK_RESPONSE_ACCEPT, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL); + confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + gtk_widget_destroy(dialog); + g_free(message); + return confirmed; +} + +static char *fishlim_gui_selected_target(void) { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + char *target = NULL; + + if (!fishlim_view) + return NULL; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fishlim_view)); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + gtk_tree_model_get(model, &iter, 0, &target, -1); + + return target; +} + static void fishlim_gui_delete(GtkWidget *widget, gpointer data) { - const char *target = fishlim_gui_target(); + char *selected = fishlim_gui_selected_target(); + const char *target = selected ? selected : fishlim_gui_target(); if (!target || !*target) { zoitechat_printf(ph, "%s\n", usage_delkey); + g_free(selected); return; } - zoitechat_commandf(ph, "DELKEY %s", target); - fishlim_gui_refresh(); + if (fishlim_gui_confirm_delete(target)) { + zoitechat_commandf(ph, "DELKEY %s", target); + fishlim_gui_refresh(); + } + + g_free(selected); } static char *fishlim_gui_prompt_target(const char *title, const char *initial) { @@ -607,6 +646,7 @@ static void fishlim_gui_destroy(GtkWidget *widget, gpointer data) { fishlim_mode_combo = NULL; fishlim_status_label = NULL; fishlim_store = NULL; + fishlim_view = NULL; } static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { @@ -647,15 +687,16 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { gtk_combo_box_set_active(GTK_COMBO_BOX(fishlim_mode_combo), 1); gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Mode"), 0, 1, 1, 1); gtk_grid_attach(GTK_GRID(grid), fishlim_mode_combo, 1, 1, 2, 1); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("CBC may not work with older clients."), 1, 2, 2, 1); fishlim_key_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(fishlim_key_entry), FALSE); - gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Key"), 0, 2, 1, 1); - gtk_grid_attach(GTK_GRID(grid), fishlim_key_entry, 1, 2, 2, 1); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Key"), 0, 3, 1, 1); + gtk_grid_attach(GTK_GRID(grid), fishlim_key_entry, 1, 3, 2, 1); buttons = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); gtk_button_box_set_layout(GTK_BUTTON_BOX(buttons), GTK_BUTTONBOX_END); - gtk_grid_attach(GTK_GRID(grid), buttons, 0, 3, 3, 1); + gtk_grid_attach(GTK_GRID(grid), buttons, 0, 4, 3, 1); button = gtk_button_new_with_label("Set Key"); g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_set), NULL); @@ -671,10 +712,11 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { fishlim_status_label = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(fishlim_status_label), 0.0); - gtk_grid_attach(GTK_GRID(grid), fishlim_status_label, 0, 4, 3, 1); + gtk_grid_attach(GTK_GRID(grid), fishlim_status_label, 0, 5, 3, 1); fishlim_store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(fishlim_store)); + fishlim_view = view; renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Target", renderer, "text", 0, NULL); renderer = gtk_cell_renderer_text_new(); @@ -686,7 +728,7 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { scroll = gtk_scrolled_window_new(NULL, NULL); gtk_widget_set_vexpand(scroll, TRUE); gtk_container_add(GTK_CONTAINER(scroll), view); - gtk_grid_attach(GTK_GRID(grid), scroll, 0, 5, 3, 1); + gtk_grid_attach(GTK_GRID(grid), scroll, 0, 6, 3, 1); g_signal_connect(fishlim_target_entry, "changed", G_CALLBACK(fishlim_gui_refresh_cb), NULL); fishlim_gui_refresh(); @@ -730,6 +772,8 @@ static int handle_setkey(char *word[], char *word_eol[], void *userdata) { /* Set password */ if (keystore_store_key(nick, key, mode)) { zoitechat_printf(ph, "Stored key for %s (%s)\n", nick, fish_modes[mode]); + if (mode == FISH_CBC_MODE) + zoitechat_printf(ph, "Warning: CBC may not work with older clients.\n"); } else { zoitechat_printf(ph, "\00305Failed to store key in addon_fishlim.conf\n"); } @@ -807,6 +851,8 @@ static int handle_keyx(char *word[], char *word_eol[], void *userdata) { zoitechat_commandf(ph, "quote NOTICE %s :DH1080_INIT %s%s", target, pub_key, (mode == FISH_CBC_MODE) ? " CBC" : ""); zoitechat_printf(ph, "Sent public key to %s (%s), waiting for reply...", target, fish_modes[mode]); + if (mode == FISH_CBC_MODE) + zoitechat_printf(ph, "Warning: CBC may not work with older clients."); g_free(pub_key); } else { From 9ded7ef830abe8773d32489a9e3e8d25ecb6a692 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Tue, 16 Jun 2026 15:47:56 -0600 Subject: [PATCH 3/3] Clarify FiSHLiM key manager invite wording --- plugins/fishlim/plugin_zoitechat.c | 44 ++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/plugins/fishlim/plugin_zoitechat.c b/plugins/fishlim/plugin_zoitechat.c index e18366b5..bfb9d4b2 100644 --- a/plugins/fishlim/plugin_zoitechat.c +++ b/plugins/fishlim/plugin_zoitechat.c @@ -519,6 +519,39 @@ static void fishlim_gui_refresh_cb(GtkWidget *widget, gpointer data) { fishlim_gui_refresh(); } +static void fishlim_gui_send_channel_key(const char *channel, const char *mode, const char *key) { + GtkWidget *dialog, *content, *entry, *label; + char **users; + int i; + + if (!channel || !*channel || irc_is_query(channel)) + return; + + dialog = gtk_dialog_new_with_buttons("Share Channel Key", GTK_WINDOW(fishlim_dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, "_Skip", GTK_RESPONSE_CANCEL, "_Send", GTK_RESPONSE_OK, NULL); + content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + label = gtk_label_new("Send this channel key to users by private message. Enter users separated by commas."); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(content), label, FALSE, FALSE, 6); + entry = gtk_entry_new(); + gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "nick1, nick2, nick3"); + gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); + gtk_box_pack_start(GTK_BOX(content), entry, FALSE, FALSE, 6); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + gtk_widget_show_all(dialog); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + users = g_strsplit(gtk_entry_get_text(GTK_ENTRY(entry)), ",", -1); + for (i = 0; users && users[i]; i++) { + char *user = g_strstrip(users[i]); + if (*user) + zoitechat_commandf(ph, "msg %s Encrypted chat invite: join %s, open FiSHLiM Key Manager, add channel %s with %s key: %s. Key exchange is for private messages only.", user, channel, channel, mode, key); + } + g_strfreev(users); + } + + gtk_widget_destroy(dialog); +} + static void fishlim_gui_set(GtkWidget *widget, gpointer data) { const char *target = fishlim_gui_target(); const char *key = gtk_entry_get_text(GTK_ENTRY(fishlim_key_entry)); @@ -530,6 +563,7 @@ static void fishlim_gui_set(GtkWidget *widget, gpointer data) { } zoitechat_commandf(ph, "SETKEY %s %s:%s", target, mode, key); + fishlim_gui_send_channel_key(target, mode, key); gtk_entry_set_text(GTK_ENTRY(fishlim_key_entry), ""); fishlim_gui_refresh(); } @@ -594,7 +628,7 @@ static char *fishlim_gui_prompt_target(const char *title, const char *initial) { if (initial && *initial) gtk_entry_set_text(GTK_ENTRY(entry), initial); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); - gtk_box_pack_start(GTK_BOX(content), gtk_label_new("User"), FALSE, FALSE, 6); + gtk_box_pack_start(GTK_BOX(content), gtk_label_new("Private message user"), FALSE, FALSE, 6); gtk_box_pack_start(GTK_BOX(content), entry, FALSE, FALSE, 6); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); gtk_widget_show_all(dialog); @@ -663,7 +697,7 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { return ZOITECHAT_EAT_ZOITECHAT; } - fishlim_dialog = gtk_dialog_new_with_buttons("FiSHLiM", NULL, GTK_DIALOG_DESTROY_WITH_PARENT, "_Close", GTK_RESPONSE_CLOSE, NULL); + fishlim_dialog = gtk_dialog_new_with_buttons("FiSHLiM Key Manager", NULL, GTK_DIALOG_DESTROY_WITH_PARENT, "_Close", GTK_RESPONSE_CLOSE, NULL); gtk_window_set_default_size(GTK_WINDOW(fishlim_dialog), 520, 360); g_signal_connect(fishlim_dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL); g_signal_connect(fishlim_dialog, "destroy", G_CALLBACK(fishlim_gui_destroy), NULL); @@ -678,7 +712,7 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { fishlim_target_entry = gtk_entry_new(); if (target) gtk_entry_set_text(GTK_ENTRY(fishlim_target_entry), target); - gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Target"), 0, 0, 1, 1); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Target nick or channel"), 0, 0, 1, 1); gtk_grid_attach(GTK_GRID(grid), fishlim_target_entry, 1, 0, 2, 1); fishlim_mode_combo = gtk_combo_box_text_new(); @@ -687,7 +721,7 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { gtk_combo_box_set_active(GTK_COMBO_BOX(fishlim_mode_combo), 1); gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Mode"), 0, 1, 1, 1); gtk_grid_attach(GTK_GRID(grid), fishlim_mode_combo, 1, 1, 2, 1); - gtk_grid_attach(GTK_GRID(grid), gtk_label_new("CBC may not work with older clients."), 1, 2, 2, 1); + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("CBC may not work with older clients. Key exchange is private-message only."), 1, 2, 2, 1); fishlim_key_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(fishlim_key_entry), FALSE); @@ -706,7 +740,7 @@ static int handle_fishlim(char *word[], char *word_eol[], void *userdata) { g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_delete), NULL); gtk_container_add(GTK_CONTAINER(buttons), button); - button = gtk_button_new_with_label("Key Exchange"); + button = gtk_button_new_with_label("Private Message Key Exchange"); g_signal_connect(button, "clicked", G_CALLBACK(fishlim_gui_keyx), NULL); gtk_container_add(GTK_CONTAINER(buttons), button);