mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-06-20 13:19:23 +00:00
Compare commits
16 Commits
bb08b19c0d
...
mode-butto
| Author | SHA1 | Date | |
|---|---|---|---|
| dfd4554a11 | |||
| 1b98297881 | |||
|
|
cacc8fcafe | ||
| ac07d39db8 | |||
|
|
e27aa77cec | ||
| cbc9f68dd6 | |||
| 944d0f5a70 | |||
| 9ded7ef830 | |||
| adb18b7ce3 | |||
|
|
8220e60257 | ||
| b09af655e7 | |||
| 5833f8a3d8 | |||
|
|
72132d5e88 | ||
|
|
4167c4db78 | ||
| 3e1f6b9137 | |||
| d1707d3c72 |
103
packaging/fedora/zoitechat.spec
Normal file
103
packaging/fedora/zoitechat.spec
Normal file
@@ -0,0 +1,103 @@
|
||||
Name: zoitechat
|
||||
Version: 2.18.1
|
||||
Release: %autorelease
|
||||
Summary: HexChat-based IRC client
|
||||
License: GPL-2.0-or-later WITH cryptsetup-OpenSSL-exception
|
||||
URL: https://github.com/ZoiteChat/zoitechat
|
||||
Source0: %{url}/archive/refs/tags/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: gcc
|
||||
BuildRequires: gettext
|
||||
BuildRequires: libappstream-glib
|
||||
BuildRequires: meson >= 0.55.0
|
||||
BuildRequires: perl
|
||||
BuildRequires: perl-devel
|
||||
BuildRequires: python3
|
||||
BuildRequires: python3-cffi
|
||||
BuildRequires: publicsuffix-list
|
||||
BuildRequires: xwayland-run
|
||||
BuildRequires: weston
|
||||
BuildRequires: pkgconfig(ayatana-appindicator3-0.1)
|
||||
BuildRequires: pkgconfig(dbus-glib-1)
|
||||
BuildRequires: pkgconfig(gio-2.0) >= 2.36.0
|
||||
BuildRequires: pkgconfig(gmodule-2.0)
|
||||
BuildRequires: pkgconfig(gtk+-3.0) >= 3.22
|
||||
BuildRequires: pkgconfig(iso-codes)
|
||||
BuildRequires: pkgconfig(libarchive)
|
||||
BuildRequires: pkgconfig(libcanberra) >= 0.22
|
||||
BuildRequires: pkgconfig(libpci)
|
||||
BuildRequires: pkgconfig(lua)
|
||||
BuildRequires: pkgconfig(openssl) >= 0.9.8
|
||||
BuildRequires: pkgconfig(python3)
|
||||
|
||||
Requires: hicolor-icon-theme
|
||||
Requires: iso-codes
|
||||
|
||||
%package devel
|
||||
Summary: Development files for ZoiteChat plugins
|
||||
Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description devel
|
||||
Development files for building ZoiteChat plugins.
|
||||
|
||||
%description
|
||||
ZoiteChat is a HexChat-based IRC client for Windows and UNIX-like operating
|
||||
systems.
|
||||
|
||||
%prep
|
||||
%autosetup -C
|
||||
|
||||
%build
|
||||
%meson \
|
||||
-Dtext-frontend=false \
|
||||
-Dwith-checksum=true \
|
||||
-Dwith-fishlim=true \
|
||||
-Dwith-lua=lua \
|
||||
-Dwith-perl=perl \
|
||||
-Dwith-python=python3 \
|
||||
-Dwith-sysinfo=true \
|
||||
-Dinstall-appdata=true \
|
||||
-Dinstall-plugin-metainfo=true
|
||||
%meson_build
|
||||
|
||||
%install
|
||||
%meson_install
|
||||
%find_lang %{name}
|
||||
|
||||
%check
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/net.zoite.Zoitechat.desktop
|
||||
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/net.zoite.Zoitechat.appdata.xml
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/net.zoite.Zoitechat*.metainfo.xml
|
||||
|
||||
xwfb-run -- /usr/bin/meson test -C %{_vpath_builddir} --num-processes %{_smp_build_ncpus} --print-errorlogs \
|
||||
"Theme Manager Dispatch Routing Tests" \
|
||||
"Validate net.zoite.Zoitechat.desktop" \
|
||||
"Theme GTK3 Settings Tests" \
|
||||
"Validate translations" \
|
||||
"Fishlim Tests"
|
||||
|
||||
%files -f %{name}.lang
|
||||
%license COPYING
|
||||
%doc readme.md troubleshooting.md
|
||||
%{_bindir}/zoitechat
|
||||
%{_datadir}/applications/net.zoite.Zoitechat.desktop
|
||||
%{_datadir}/dbus-1/services/org.zoitechat.service.service
|
||||
%{_datadir}/icons/hicolor/48x48/apps/net.zoite.Zoitechat.png
|
||||
%{_datadir}/icons/hicolor/scalable/apps/net.zoite.Zoitechat.svg
|
||||
%{_datadir}/metainfo/net.zoite.Zoitechat.appdata.xml
|
||||
%{_datadir}/metainfo/net.zoite.Zoitechat*.metainfo.xml
|
||||
%dir %{_libdir}/zoitechat
|
||||
%dir %{_libdir}/zoitechat/plugins
|
||||
%dir %{_libdir}/zoitechat/python
|
||||
%{_libdir}/zoitechat/plugins/*.so
|
||||
%{_libdir}/zoitechat/python/*.py
|
||||
%{_mandir}/man1/zoitechat.1*
|
||||
|
||||
%files devel
|
||||
%{_includedir}/zoitechat-plugin.h
|
||||
%{_libdir}/pkgconfig/zoitechat-plugin.pc
|
||||
|
||||
%changelog
|
||||
%autochangelog
|
||||
@@ -26,7 +26,7 @@
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;_USRDLL;FISHLIM_EXPORTS;HAVE_DH_SET0_PQG;HAVE_DH_GET0_KEY;HAVE_DH_SET0_KEY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(DepsRoot)\include;$(OpenSslInclude);$(Glib);$(Gtk);..\..\src\common;$(ZoiteChatLib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<ModuleDefinitionFile>fishlim.def</ModuleDefinitionFile>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "zoitechat-plugin.h"
|
||||
@@ -44,7 +45,7 @@ static const char plugin_version[] = "1.0.0";
|
||||
|
||||
static const char usage_setkey[] = "Usage: SETKEY [<nick or #channel>] [<mode>:]<password>, sets the key for a channel or nick. Modes: ECB, CBC";
|
||||
static const char usage_delkey[] = "Usage: DELKEY [<nick or #channel>], deletes the key for a channel or nick";
|
||||
static const char usage_keyx[] = "Usage: KEYX [<nick>], performs DH1080 key-exchange with <nick>";
|
||||
static const char usage_keyx[] = "Usage: KEYX [<nick>] [ECB|CBC], performs DH1080 key-exchange with <nick>";
|
||||
static const char usage_topic[] = "Usage: TOPIC+ <topic>, sets a new encrypted topic for the current channel";
|
||||
static const char usage_notice[] = "Usage: NOTICE+ <nick or #channel> <notice>";
|
||||
static const char usage_msg[] = "Usage: MSG+ <nick or #channel> <message>";
|
||||
@@ -52,6 +53,13 @@ static const char usage_msg[] = "Usage: MSG+ <nick or #channel> <message>";
|
||||
|
||||
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;
|
||||
static GtkWidget *fishlim_view;
|
||||
|
||||
|
||||
/**
|
||||
@@ -455,6 +463,314 @@ 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_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));
|
||||
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);
|
||||
fishlim_gui_send_channel_key(target, mode, key);
|
||||
gtk_entry_set_text(GTK_ENTRY(fishlim_key_entry), "");
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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("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);
|
||||
|
||||
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;
|
||||
fishlim_view = 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 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);
|
||||
|
||||
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 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();
|
||||
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);
|
||||
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);
|
||||
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, 4, 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("Private Message 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, 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();
|
||||
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, 6, 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
|
||||
*/
|
||||
@@ -490,6 +806,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");
|
||||
}
|
||||
@@ -533,8 +851,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 +883,10 @@ 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]);
|
||||
if (mode == FISH_CBC_MODE)
|
||||
zoitechat_printf(ph, "Warning: CBC may not work with older clients.");
|
||||
|
||||
g_free(pub_key);
|
||||
} else {
|
||||
@@ -789,6 +1119,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 +1148,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();
|
||||
|
||||
@@ -428,6 +428,7 @@ const struct prefs vars[] =
|
||||
{"gui_lagometer", P_OFFINT (hex_gui_lagometer), TYPE_INT},
|
||||
{"gui_lang", P_OFFINT (hex_gui_lang), TYPE_INT},
|
||||
{"gui_mode_buttons", P_OFFINT (hex_gui_mode_buttons), TYPE_BOOL},
|
||||
{"gui_mode_buttons_inline", P_OFFINT (hex_gui_mode_buttons_inline), TYPE_BOOL},
|
||||
{"gui_pane_divider_position", P_OFFINT (hex_gui_pane_divider_position), TYPE_INT},
|
||||
{"gui_pane_left_size", P_OFFINT (hex_gui_pane_left_size), TYPE_INT},
|
||||
{"gui_pane_right_size", P_OFFINT (hex_gui_pane_right_size), TYPE_INT},
|
||||
@@ -458,6 +459,7 @@ const struct prefs vars[] =
|
||||
{"gui_tab_utils", P_OFFINT (hex_gui_tab_utils), TYPE_BOOL},
|
||||
{"gui_throttlemeter", P_OFFINT (hex_gui_throttlemeter), TYPE_INT},
|
||||
{"gui_topicbar", P_OFFINT (hex_gui_topicbar), TYPE_BOOL},
|
||||
{"gui_topicbar_multiline", P_OFFINT (hex_gui_topicbar_multiline), TYPE_BOOL},
|
||||
{"gui_transparency", P_OFFINT (hex_gui_transparency), TYPE_INT},
|
||||
{"gui_tray", P_OFFINT (hex_gui_tray), TYPE_BOOL},
|
||||
{"gui_tray_away", P_OFFINT (hex_gui_tray_away), TYPE_BOOL},
|
||||
@@ -791,6 +793,7 @@ load_default_config(void)
|
||||
prefs.hex_gui_tab_scrollchans = 1;
|
||||
prefs.hex_gui_mouse_scroll_speed = 10;
|
||||
prefs.hex_gui_topicbar = 1;
|
||||
prefs.hex_gui_topicbar_multiline = 1;
|
||||
prefs.hex_gui_transparency = 255;
|
||||
prefs.hex_gui_tray = 1;
|
||||
prefs.hex_gui_tray_blink = 1;
|
||||
|
||||
@@ -1090,13 +1090,19 @@ clients_find_filename_foreach (gpointer key,
|
||||
gpointer user_data)
|
||||
{
|
||||
RemoteObject *obj = value;
|
||||
return g_str_equal (obj->filename, (char *)user_data);
|
||||
|
||||
return obj->filename != NULL && g_str_equal (obj->filename, user_data);
|
||||
}
|
||||
|
||||
static int
|
||||
unload_plugin_cb (char *word[], char *word_eol[], void *userdata)
|
||||
{
|
||||
RemoteObject *obj = g_hash_table_find (clients, clients_find_filename_foreach, word[2]);
|
||||
RemoteObject *obj;
|
||||
|
||||
if (word[2][0] == 0)
|
||||
return ZOITECHAT_EAT_NONE;
|
||||
|
||||
obj = g_hash_table_find (clients, clients_find_filename_foreach, word[2]);
|
||||
|
||||
if (obj != NULL)
|
||||
{
|
||||
|
||||
@@ -133,6 +133,7 @@ struct zoitechatprefs
|
||||
unsigned int hex_gui_input_style;
|
||||
unsigned int hex_gui_join_dialog;
|
||||
unsigned int hex_gui_mode_buttons;
|
||||
unsigned int hex_gui_mode_buttons_inline;
|
||||
unsigned int hex_gui_quit_dialog;
|
||||
/* unsigned int hex_gui_single; */
|
||||
unsigned int hex_gui_slist_fav;
|
||||
@@ -148,6 +149,7 @@ struct zoitechatprefs
|
||||
unsigned int hex_gui_tab_sort;
|
||||
unsigned int hex_gui_tab_utils;
|
||||
unsigned int hex_gui_topicbar;
|
||||
unsigned int hex_gui_topicbar_multiline;
|
||||
unsigned int hex_gui_tray;
|
||||
unsigned int hex_gui_tray_away;
|
||||
unsigned int hex_gui_tray_blink;
|
||||
|
||||
@@ -341,6 +341,7 @@ cv_tabs_init (chanview *cv)
|
||||
GtkWidget *box;
|
||||
GtkWidget *viewport;
|
||||
GtkWidget *outer;
|
||||
GtkWidget *tree;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
@@ -385,6 +386,11 @@ cv_tabs_init (chanview *cv)
|
||||
gtk_container_add (GTK_CONTAINER (viewport), box);
|
||||
gtk_widget_show (box);
|
||||
|
||||
tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cv->store));
|
||||
gtk_widget_set_name (tree, "zoitechat-tree");
|
||||
gtk_widget_set_no_show_all (tree, TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (outer), tree, 0, 0, 0);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), outer);
|
||||
}
|
||||
|
||||
|
||||
@@ -812,18 +812,19 @@ fe_set_topic (session *sess, char *topic, char *stripped_topic)
|
||||
{
|
||||
if (!sess->gui->is_tab || sess == current_tab)
|
||||
{
|
||||
GtkTextBuffer *topic_buffer;
|
||||
GtkTextIter start;
|
||||
|
||||
topic_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry));
|
||||
if (prefs.hex_text_stripcolor_topic)
|
||||
{
|
||||
gtk_text_buffer_set_text (
|
||||
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
|
||||
stripped_topic, -1);
|
||||
}
|
||||
gtk_text_buffer_set_text (topic_buffer, stripped_topic, -1);
|
||||
else
|
||||
{
|
||||
gtk_text_buffer_set_text (
|
||||
gtk_text_view_get_buffer (GTK_TEXT_VIEW (sess->gui->topic_entry)),
|
||||
topic, -1);
|
||||
}
|
||||
gtk_text_buffer_set_text (topic_buffer, topic, -1);
|
||||
|
||||
gtk_text_buffer_get_start_iter (topic_buffer, &start);
|
||||
gtk_text_buffer_place_cursor (topic_buffer, &start);
|
||||
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (sess->gui->topic_entry),
|
||||
&start, 0.0, FALSE, 0.0, 0.0);
|
||||
mg_set_topic_tip (sess);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#define ICON_FKEYS_DELETE "edit-delete"
|
||||
#define ICON_FKEYS_CANCEL "dialog-cancel"
|
||||
#define ICON_FKEYS_SAVE "document-save"
|
||||
#define ICON_FKEYS_RESET "edit-undo"
|
||||
|
||||
static void replace_handle (GtkWidget * wid);
|
||||
void key_check_replace_on_change (GtkEditable *editable, gpointer data);
|
||||
@@ -105,7 +106,10 @@ struct gcomp_data
|
||||
};
|
||||
|
||||
static int key_load_kbs (void);
|
||||
static int key_load_kbs_from_buffer (char *ibuf, off_t size, GSList **out_list);
|
||||
static int key_save_kbs (void);
|
||||
static void key_dialog_load (GtkListStore *store);
|
||||
static void key_dialog_reset (GtkWidget *wid, gpointer userdata);
|
||||
static int key_action_handle_command (GtkWidget * wid, GdkEventKey * evt,
|
||||
char *d1, char *d2,
|
||||
struct session *sess);
|
||||
@@ -890,6 +894,134 @@ key_dialog_add (GtkWidget *wid, gpointer userdata)
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
static char *
|
||||
key_binding_signature (const char *action, const char *data1, const char *data2)
|
||||
{
|
||||
return g_strdup_printf ("%s\\n%s\\n%s", action ? action : "", data1 ? data1 : "", data2 ? data2 : "");
|
||||
}
|
||||
|
||||
static int
|
||||
key_dialog_reset_count (GHashTable *table, const char *key)
|
||||
{
|
||||
return GPOINTER_TO_INT (g_hash_table_lookup (table, key));
|
||||
}
|
||||
|
||||
static void
|
||||
key_dialog_reset_increment (GHashTable *table, char *key)
|
||||
{
|
||||
g_hash_table_replace (table, key, GINT_TO_POINTER (key_dialog_reset_count (table, key) + 1));
|
||||
}
|
||||
|
||||
static void
|
||||
key_dialog_reset (GtkWidget *wid, gpointer userdata)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkListStore *custom_store;
|
||||
GtkTreeIter iter, custom_iter;
|
||||
GtkWidget *delete_button;
|
||||
GHashTable *default_counts, *seen_counts;
|
||||
GSList *list = NULL, *old_list, *default_iter;
|
||||
struct key_binding *kb;
|
||||
gboolean custom, keep;
|
||||
char *key, *accel, *action, *data1, *data2, *signature;
|
||||
|
||||
if (key_load_kbs_from_buffer (g_strdup (default_kb_cfg), strlen (default_kb_cfg), &list) != 0)
|
||||
return;
|
||||
|
||||
default_counts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
seen_counts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
for (default_iter = list; default_iter; default_iter = g_slist_next (default_iter))
|
||||
{
|
||||
kb = default_iter->data;
|
||||
signature = key_binding_signature (key_actions[kb->action].name, kb->data1, kb->data2);
|
||||
key_dialog_reset_increment (default_counts, signature);
|
||||
}
|
||||
|
||||
custom_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
|
||||
KEY_COLUMN, &key,
|
||||
ACCEL_COLUMN, &accel,
|
||||
ACTION_COLUMN, &action,
|
||||
D1_COLUMN, &data1,
|
||||
D2_COLUMN, &data2,
|
||||
CUSTOM_COLUMN, &custom,
|
||||
-1);
|
||||
signature = key_binding_signature (action, data1, data2);
|
||||
keep = custom || key_dialog_reset_count (seen_counts, signature) >= key_dialog_reset_count (default_counts, signature);
|
||||
if (!custom)
|
||||
key_dialog_reset_increment (seen_counts, g_strdup (signature));
|
||||
if (keep)
|
||||
{
|
||||
gtk_list_store_append (custom_store, &custom_iter);
|
||||
gtk_list_store_set (custom_store, &custom_iter,
|
||||
KEY_COLUMN, key,
|
||||
ACCEL_COLUMN, accel,
|
||||
ACTION_COLUMN, action,
|
||||
D1_COLUMN, data1,
|
||||
D2_COLUMN, data2,
|
||||
CUSTOM_COLUMN, TRUE,
|
||||
-1);
|
||||
}
|
||||
g_free (signature);
|
||||
g_free (key);
|
||||
g_free (accel);
|
||||
g_free (action);
|
||||
g_free (data1);
|
||||
g_free (data2);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
|
||||
}
|
||||
|
||||
old_list = keybind_list;
|
||||
keybind_list = list;
|
||||
gtk_list_store_clear (store);
|
||||
key_dialog_load (store);
|
||||
keybind_list = old_list;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (custom_store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (custom_store), &iter,
|
||||
KEY_COLUMN, &key,
|
||||
ACCEL_COLUMN, &accel,
|
||||
ACTION_COLUMN, &action,
|
||||
D1_COLUMN, &data1,
|
||||
D2_COLUMN, &data2,
|
||||
-1);
|
||||
gtk_list_store_append (store, &custom_iter);
|
||||
gtk_list_store_set (store, &custom_iter,
|
||||
KEY_COLUMN, key,
|
||||
ACCEL_COLUMN, accel,
|
||||
ACTION_COLUMN, action,
|
||||
D1_COLUMN, data1,
|
||||
D2_COLUMN, data2,
|
||||
CUSTOM_COLUMN, TRUE,
|
||||
-1);
|
||||
g_free (key);
|
||||
g_free (accel);
|
||||
g_free (action);
|
||||
g_free (data1);
|
||||
g_free (data2);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (custom_store), &iter));
|
||||
}
|
||||
|
||||
delete_button = g_object_get_data (G_OBJECT (key_dialog), "delete_button");
|
||||
if (delete_button)
|
||||
gtk_widget_set_sensitive (delete_button, FALSE);
|
||||
g_hash_table_destroy (default_counts);
|
||||
g_hash_table_destroy (seen_counts);
|
||||
g_object_unref (custom_store);
|
||||
g_slist_free_full (list, key_free);
|
||||
}
|
||||
|
||||
static void
|
||||
key_dialog_delete (GtkWidget *wid, gpointer userdata)
|
||||
{
|
||||
@@ -1128,6 +1260,8 @@ key_dialog_show ()
|
||||
NULL, _("Delete"));
|
||||
g_object_set_data (G_OBJECT (key_dialog), "delete_button", delete_button);
|
||||
gtk_widget_set_sensitive (delete_button, FALSE);
|
||||
gtkutil_button (box, ICON_FKEYS_RESET, NULL, key_dialog_reset,
|
||||
NULL, _("Reset"));
|
||||
gtkutil_button (box, ICON_FKEYS_CANCEL, NULL, key_dialog_close,
|
||||
NULL, _("Cancel"));
|
||||
gtkutil_button (box, ICON_FKEYS_SAVE, NULL, key_dialog_save,
|
||||
@@ -1235,41 +1369,14 @@ key_load_kbs_helper_mod (char *buf, GdkModifierType *out)
|
||||
}
|
||||
|
||||
static int
|
||||
key_load_kbs (void)
|
||||
key_load_kbs_from_buffer (char *ibuf, off_t size, GSList **out_list)
|
||||
{
|
||||
char *buf, *ibuf;
|
||||
struct stat st;
|
||||
char *buf;
|
||||
struct key_binding *kb = NULL;
|
||||
int fd, len, state = 0, pnt = 0;
|
||||
int len, state = 0, pnt = 0;
|
||||
guint keyval;
|
||||
GdkModifierType mod = 0;
|
||||
off_t size;
|
||||
|
||||
fd = zoitechat_open_file ("keybindings.conf", O_RDONLY, 0, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
ibuf = g_strdup (default_kb_cfg);
|
||||
size = strlen (default_kb_cfg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fstat (fd, &st) != 0)
|
||||
{
|
||||
close (fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ibuf = g_malloc(st.st_size);
|
||||
read (fd, ibuf, st.st_size);
|
||||
size = st.st_size;
|
||||
close (fd);
|
||||
}
|
||||
|
||||
if (keybind_list)
|
||||
{
|
||||
g_slist_free_full (keybind_list, key_free);
|
||||
keybind_list = NULL;
|
||||
}
|
||||
GSList *list = NULL;
|
||||
|
||||
while (buf_get_line (ibuf, &buf, &pnt, size))
|
||||
{
|
||||
@@ -1283,14 +1390,12 @@ key_load_kbs (void)
|
||||
case KBSTATE_MOD:
|
||||
kb = g_new0 (struct key_binding, 1);
|
||||
|
||||
/* New format */
|
||||
if (strncmp (buf, "ACCEL=", 6) == 0)
|
||||
{
|
||||
buf += 6;
|
||||
|
||||
gtk_accelerator_parse (buf, &keyval, &mod);
|
||||
|
||||
|
||||
kb->keyval = keyval;
|
||||
kb->mod = key_modifier_get_valid (mod);
|
||||
|
||||
@@ -1313,6 +1418,8 @@ key_load_kbs (void)
|
||||
if (keyval == 0)
|
||||
{
|
||||
g_free (ibuf);
|
||||
key_free (kb);
|
||||
g_slist_free_full (list, key_free);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -1329,6 +1436,8 @@ key_load_kbs (void)
|
||||
if (kb->action == KEY_MAX_ACTIONS + 1)
|
||||
{
|
||||
g_free (ibuf);
|
||||
key_free (kb);
|
||||
g_slist_free_full (list, key_free);
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -1346,6 +1455,8 @@ key_load_kbs (void)
|
||||
if (buf[0] != 'D')
|
||||
{
|
||||
g_free (ibuf);
|
||||
key_free (kb);
|
||||
g_slist_free_full (list, key_free);
|
||||
return 4;
|
||||
}
|
||||
|
||||
@@ -1366,7 +1477,6 @@ key_load_kbs (void)
|
||||
if (buf[2] == ':')
|
||||
{
|
||||
len = strlen (buf);
|
||||
/* Add one for the NULL, subtract 3 for the "Dx:" */
|
||||
len++;
|
||||
len -= 3;
|
||||
if (state == KBSTATE_DT1)
|
||||
@@ -1389,7 +1499,8 @@ key_load_kbs (void)
|
||||
continue;
|
||||
} else
|
||||
{
|
||||
keybind_list = g_slist_append (keybind_list, kb);
|
||||
list = g_slist_append (list, kb);
|
||||
kb = NULL;
|
||||
|
||||
state = KBSTATE_MOD;
|
||||
}
|
||||
@@ -1398,14 +1509,56 @@ key_load_kbs (void)
|
||||
}
|
||||
}
|
||||
g_free (ibuf);
|
||||
*out_list = list;
|
||||
return 0;
|
||||
|
||||
corrupt_file:
|
||||
g_free (ibuf);
|
||||
g_free (kb);
|
||||
key_free (kb);
|
||||
g_slist_free_full (list, key_free);
|
||||
return 5;
|
||||
}
|
||||
|
||||
static int
|
||||
key_load_kbs (void)
|
||||
{
|
||||
char *ibuf;
|
||||
struct stat st;
|
||||
int fd, result;
|
||||
off_t size;
|
||||
GSList *list = NULL;
|
||||
|
||||
fd = zoitechat_open_file ("keybindings.conf", O_RDONLY, 0, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
ibuf = g_strdup (default_kb_cfg);
|
||||
size = strlen (default_kb_cfg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fstat (fd, &st) != 0)
|
||||
{
|
||||
close (fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ibuf = g_malloc(st.st_size);
|
||||
read (fd, ibuf, st.st_size);
|
||||
size = st.st_size;
|
||||
close (fd);
|
||||
}
|
||||
|
||||
result = key_load_kbs_from_buffer (ibuf, size, &list);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
if (keybind_list)
|
||||
g_slist_free_full (keybind_list, key_free);
|
||||
keybind_list = list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1,
|
||||
char *d2, struct session *sess)
|
||||
|
||||
@@ -3042,7 +3042,7 @@ mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
|
||||
gui->key_entry = gtk_entry_new ();
|
||||
gtk_widget_set_name (gui->key_entry, "zoitechat-inputbox");
|
||||
gtk_entry_set_max_length (GTK_ENTRY (gui->key_entry), 23);
|
||||
gtk_widget_set_size_request (gui->key_entry, 115, 11);
|
||||
gtk_widget_set_size_request (gui->key_entry, 58, 11);
|
||||
gtk_box_pack_start (GTK_BOX (box), gui->key_entry, 0, 0, 0);
|
||||
mg_apply_emoji_fallback_widget (gui->key_entry);
|
||||
mg_apply_compact_mode_css (gui->key_entry);
|
||||
@@ -3059,7 +3059,8 @@ mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
|
||||
gui->limit_entry = gtk_entry_new ();
|
||||
gtk_widget_set_name (gui->limit_entry, "zoitechat-inputbox");
|
||||
gtk_entry_set_max_length (GTK_ENTRY (gui->limit_entry), 10);
|
||||
gtk_widget_set_size_request (gui->limit_entry, 30, 11);
|
||||
gtk_entry_set_width_chars (GTK_ENTRY (gui->limit_entry), 5);
|
||||
gtk_widget_set_size_request (gui->limit_entry, 45, 11);
|
||||
gtk_box_pack_start (GTK_BOX (box), gui->limit_entry, 0, 0, 0);
|
||||
mg_apply_emoji_fallback_widget (gui->limit_entry);
|
||||
mg_apply_compact_mode_css (gui->limit_entry);
|
||||
@@ -3193,8 +3194,11 @@ mg_topicbar_update_height (GtkWidget *topic)
|
||||
width -= margin_left + margin_right;
|
||||
if (width < 1)
|
||||
width = 1;
|
||||
pango_layout_set_width (layout, width * PANGO_SCALE);
|
||||
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
|
||||
if (prefs.hex_gui_topicbar_multiline && !prefs.hex_gui_mode_buttons_inline)
|
||||
{
|
||||
pango_layout_set_width (layout, width * PANGO_SCALE);
|
||||
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
|
||||
}
|
||||
|
||||
context = gtk_widget_get_pango_context (topic);
|
||||
metrics = pango_context_get_metrics (context,
|
||||
@@ -3206,7 +3210,8 @@ mg_topicbar_update_height (GtkWidget *topic)
|
||||
if (line_height <= 0)
|
||||
line_height = 16;
|
||||
|
||||
line_count = pango_layout_get_line_count (layout);
|
||||
line_count = prefs.hex_gui_topicbar_multiline && !prefs.hex_gui_mode_buttons_inline ?
|
||||
pango_layout_get_line_count (layout) : 1;
|
||||
if (line_count <= 0)
|
||||
line_count = 1;
|
||||
|
||||
@@ -3328,7 +3333,7 @@ mg_apply_session_font_prefs (session_gui *gui)
|
||||
static void
|
||||
mg_create_topicbar (session *sess, GtkWidget *box)
|
||||
{
|
||||
GtkWidget *vbox, *hbox, *mode_hbox, *topic, *bbox;
|
||||
GtkWidget *vbox, *hbox, *mode_hbox, *topic, *topic_scroll, *bbox;
|
||||
session_gui *gui = sess->gui;
|
||||
|
||||
gui->topic_bar = vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
|
||||
@@ -3342,7 +3347,9 @@ mg_create_topicbar (session *sess, GtkWidget *box)
|
||||
|
||||
gui->topic_entry = topic = gtk_text_view_new ();
|
||||
gtk_widget_set_name (topic, "zoitechat-topicbox");
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD_CHAR);
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic),
|
||||
prefs.hex_gui_topicbar_multiline && !prefs.hex_gui_mode_buttons_inline ?
|
||||
GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
|
||||
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4);
|
||||
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4);
|
||||
gtk_text_view_set_top_margin (GTK_TEXT_VIEW (topic), 4);
|
||||
@@ -3355,8 +3362,17 @@ mg_create_topicbar (session *sess, GtkWidget *box)
|
||||
G_CALLBACK (mg_topicbar_buffer_changed_cb), topic);
|
||||
g_signal_connect (G_OBJECT (topic), "size-allocate",
|
||||
G_CALLBACK (mg_topicbar_size_allocate_cb), NULL);
|
||||
topic_scroll = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_hexpand (topic_scroll, TRUE);
|
||||
gtk_widget_set_size_request (topic_scroll, 1, -1);
|
||||
gtk_widget_set_size_request (topic, 1, -1);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (topic_scroll),
|
||||
GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER);
|
||||
gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (topic_scroll), FALSE);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (topic_scroll), GTK_SHADOW_NONE);
|
||||
gtk_container_add (GTK_CONTAINER (topic_scroll), topic);
|
||||
mg_topicbar_update_height (topic);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), topic_scroll, TRUE, TRUE, 0);
|
||||
gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
|
||||
g_signal_connect (G_OBJECT (topic), "key-press-event",
|
||||
@@ -3373,9 +3389,13 @@ mg_create_topicbar (session *sess, GtkWidget *box)
|
||||
mg_create_dialogbuttons (bbox);
|
||||
|
||||
mode_hbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), mode_hbox, 0, 0, 0);
|
||||
if (prefs.hex_gui_mode_buttons_inline)
|
||||
gtk_box_pack_start (GTK_BOX (hbox), mode_hbox, 0, 0, 0);
|
||||
else
|
||||
gtk_box_pack_start (GTK_BOX (vbox), mode_hbox, 0, 0, 0);
|
||||
|
||||
gui->topicbutton_box = bbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
||||
gtk_widget_set_valign (bbox, GTK_ALIGN_CENTER);
|
||||
gtk_box_pack_end (GTK_BOX (mode_hbox), bbox, 0, 0, 0);
|
||||
mg_create_chanmodebuttons (gui, bbox);
|
||||
}
|
||||
|
||||
@@ -855,6 +855,7 @@ tray_toggle_visibility (gboolean force_hide)
|
||||
static int maximized;
|
||||
static int fullscreen;
|
||||
GtkWindow *win;
|
||||
WinStatus status;
|
||||
|
||||
if (!tray_backend_active)
|
||||
return FALSE;
|
||||
@@ -870,7 +871,9 @@ tray_toggle_visibility (gboolean force_hide)
|
||||
if (!win)
|
||||
return FALSE;
|
||||
|
||||
if (force_hide || gtk_widget_get_visible (GTK_WIDGET (win)))
|
||||
status = tray_get_window_status ();
|
||||
|
||||
if (force_hide || status != WS_HIDDEN)
|
||||
{
|
||||
if (prefs.hex_gui_tray_away)
|
||||
zoitechat_command (ph, "ALLSERV AWAY");
|
||||
@@ -890,8 +893,8 @@ tray_toggle_visibility (gboolean force_hide)
|
||||
gtk_window_maximize (win);
|
||||
if (fullscreen)
|
||||
gtk_window_fullscreen (win);
|
||||
gtk_widget_show (GTK_WIDGET (win));
|
||||
gtk_window_deiconify (win);
|
||||
gtk_widget_show (GTK_WIDGET (win));
|
||||
gtk_window_present (win);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ static gboolean color_change;
|
||||
static struct zoitechatprefs setup_prefs;
|
||||
static GtkWidget *cancel_button;
|
||||
static GtkWidget *font_dialog = NULL;
|
||||
static GtkWidget *setup_topicbar_multiline_toggle = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -185,6 +186,10 @@ static const setting appearance_settings[] =
|
||||
{ST_TOGGLR, N_("Show number of users"), P_OFFINTNL(hex_gui_win_ucount),0,0,0},
|
||||
{ST_TOGGLE, N_("Show nickname"), P_OFFINTNL(hex_gui_win_nick),0,0,0},
|
||||
|
||||
{ST_HEADER, N_("Topic Bar"),0,0,0},
|
||||
{ST_TOGGLE, N_("Place mode buttons beside the topic"), P_OFFINTNL(hex_gui_mode_buttons_inline), 0, 0, 0},
|
||||
{ST_TOGGLE, N_("Allow multi-line topics"), P_OFFINTNL(hex_gui_topicbar_multiline), 0, 0, 0},
|
||||
|
||||
{ST_END, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -833,6 +838,16 @@ setup_toggle_sensitive_cb (GtkToggleButton *but, GtkWidget *wid)
|
||||
gtk_widget_set_sensitive (wid, gtk_toggle_button_get_active (but));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_topicbar_inline_toggled_cb (GtkToggleButton *but, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
|
||||
if (setup_topicbar_multiline_toggle)
|
||||
gtk_widget_set_sensitive (setup_topicbar_multiline_toggle,
|
||||
!gtk_toggle_button_get_active (but));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_create_toggleR (GtkWidget *tab, int row, const setting *set)
|
||||
{
|
||||
@@ -859,6 +874,14 @@ setup_create_toggleL (GtkWidget *tab, int row, const setting *set)
|
||||
setup_get_int (&setup_prefs, set));
|
||||
g_signal_connect (G_OBJECT (wid), "toggled",
|
||||
G_CALLBACK (setup_toggle_cb), (gpointer)set);
|
||||
if (set->offset == STRUCT_OFFSET_INT (struct zoitechatprefs, hex_gui_mode_buttons_inline))
|
||||
g_signal_connect (G_OBJECT (wid), "toggled",
|
||||
G_CALLBACK (setup_topicbar_inline_toggled_cb), NULL);
|
||||
if (set->offset == STRUCT_OFFSET_INT (struct zoitechatprefs, hex_gui_topicbar_multiline))
|
||||
{
|
||||
setup_topicbar_multiline_toggle = wid;
|
||||
gtk_widget_set_sensitive (wid, !setup_prefs.hex_gui_mode_buttons_inline);
|
||||
}
|
||||
if (set->tooltip)
|
||||
gtk_widget_set_tooltip_text (wid, _(set->tooltip));
|
||||
setup_table_attach (tab, wid, 2, row==6 ? 6 : 4, row, row + 1, FALSE, FALSE,
|
||||
@@ -2286,6 +2309,8 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_lagometer))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_mode_buttons_inline))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_tab_icons))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_tab_closebuttons))
|
||||
@@ -2300,6 +2325,8 @@ setup_apply (struct zoitechatprefs *pr)
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_throttlemeter))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_topicbar_multiline))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_ulist_count))
|
||||
noapply = TRUE;
|
||||
if (DIFF (hex_gui_ulist_icons))
|
||||
|
||||
@@ -35,6 +35,7 @@ struct zoitechatprefs prefs;
|
||||
|
||||
static gboolean gtk_available;
|
||||
static char *temp_root;
|
||||
static char *xdg_data_home;
|
||||
static char *theme_parent_root;
|
||||
static char *theme_child_root;
|
||||
static char *theme_switch_root;
|
||||
@@ -205,6 +206,10 @@ setup_themes (void)
|
||||
|
||||
temp_root = g_dir_make_tmp ("zoitechat-theme-gtk3-settings-XXXXXX", NULL);
|
||||
g_assert_nonnull (temp_root);
|
||||
xdg_data_home = g_build_filename (temp_root, "data", NULL);
|
||||
g_assert_cmpint (g_mkdir_with_parents (xdg_data_home, 0700), ==, 0);
|
||||
g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE);
|
||||
|
||||
theme_parent_root = g_build_filename (temp_root, "parent", NULL);
|
||||
theme_child_root = g_build_filename (temp_root, "child", NULL);
|
||||
theme_switch_root = g_build_filename (temp_root, "switch", NULL);
|
||||
@@ -253,10 +258,12 @@ teardown_themes (void)
|
||||
g_free (theme_parent_root);
|
||||
g_free (theme_child_root);
|
||||
g_free (theme_switch_root);
|
||||
g_free (xdg_data_home);
|
||||
g_free (temp_root);
|
||||
theme_parent_root = NULL;
|
||||
theme_child_root = NULL;
|
||||
theme_switch_root = NULL;
|
||||
xdg_data_home = NULL;
|
||||
temp_root = NULL;
|
||||
}
|
||||
|
||||
@@ -328,8 +335,8 @@ main (int argc, char **argv)
|
||||
int rc;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
gtk_available = gtk_init_check (&argc, &argv);
|
||||
setup_themes ();
|
||||
gtk_available = gtk_init_check (&argc, &argv);
|
||||
|
||||
g_test_add_func ("/theme/gtk3/settings_layer_precedence", test_settings_layer_precedence);
|
||||
g_test_add_func ("/theme/gtk3/settings_restored_on_disable_and_switch", test_settings_restored_on_disable_and_switch);
|
||||
|
||||
Reference in New Issue
Block a user