mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-09-24 16:37:12 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe9a2ee202 | ||
|
|
5e27dc7e9f | ||
|
|
69d083e3c0 | ||
|
|
2b95f9724e | ||
|
|
4066a07892 | ||
|
|
443ab58f65 | ||
|
|
e0116acbe4 | ||
|
|
91e9bf0207 | ||
|
|
aff0335f7e | ||
|
|
4d8baaa19b | ||
|
|
65452d80f1 |
@@ -1,6 +1,17 @@
|
|||||||
ZoiteChat ChangeLog
|
ZoiteChat ChangeLog
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
2.18.3 (2026-06-29)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Added IRCv3 support for message tags, echo-message, typing notifications, and replies.
|
||||||
|
- Restored the accept-invalid-cert TLS bypass behavior.
|
||||||
|
- Replaced legacy IPv4 APIs and added support for OpenSSL 4 APIs.
|
||||||
|
- Snapshot Python hooks during plugin unload for safer plugin shutdown.
|
||||||
|
- Fixed Windows GtkStatusIcon tray menu popup behavior.
|
||||||
|
- Fixed self-echoed private messages routing to the target tab.
|
||||||
|
- Fixed the lag ping timeout window.
|
||||||
|
|
||||||
2.18.2 (2026-06-20)
|
2.18.2 (2026-06-20)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,19 @@
|
|||||||
<id>zoitechat.desktop</id>
|
<id>zoitechat.desktop</id>
|
||||||
</provides>
|
</provides>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2026-06-29" version="2.18.3">
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Added IRCv3 support for message tags, echo-message, typing notifications, and replies.</li>
|
||||||
|
<li>Restored the accept-invalid-cert TLS bypass behavior.</li>
|
||||||
|
<li>Replaced legacy IPv4 APIs and added support for OpenSSL 4 APIs.</li>
|
||||||
|
<li>Snapshot Python hooks during plugin unload for safer plugin shutdown.</li>
|
||||||
|
<li>Fixed Windows GtkStatusIcon tray menu popup behavior.</li>
|
||||||
|
<li>Fixed self-echoed private messages routing to the target tab.</li>
|
||||||
|
<li>Fixed the lag ping timeout window.</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release date="2026-06-20" version="2.18.2">
|
<release date="2026-06-20" version="2.18.2">
|
||||||
<description>
|
<description>
|
||||||
<p>Security and connections:</p>
|
<p>Security and connections:</p>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
project('zoitechat', 'c',
|
project('zoitechat', 'c',
|
||||||
version: '2.18.2',
|
version: '2.18.3',
|
||||||
meson_version: '>= 0.55.0',
|
meson_version: '>= 0.55.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c17',
|
'c_std=c17',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: zoitechat
|
Name: zoitechat
|
||||||
Version: 2.18.2
|
Version: 2.18.3
|
||||||
Release: %autorelease
|
Release: %autorelease
|
||||||
Summary: HexChat-based IRC client
|
Summary: HexChat-based IRC client
|
||||||
License: GPL-2.0-or-later WITH cryptsetup-OpenSSL-exception
|
License: GPL-2.0-or-later WITH cryptsetup-OpenSSL-exception
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ else:
|
|||||||
if not hasattr(sys, 'argv'):
|
if not hasattr(sys, 'argv'):
|
||||||
sys.argv = ['<zoitechat>']
|
sys.argv = ['<zoitechat>']
|
||||||
|
|
||||||
VERSION = b'2.18.2'
|
VERSION = b'2.18.3'
|
||||||
PLUGIN_NAME = ffi.new('char[]', b'Python')
|
PLUGIN_NAME = ffi.new('char[]', b'Python')
|
||||||
PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
|
PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
|
||||||
PLUGIN_VERSION = ffi.new('char[]', VERSION)
|
PLUGIN_VERSION = ffi.new('char[]', VERSION)
|
||||||
@@ -173,7 +173,7 @@ class Plugin:
|
|||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
log('unloading', self.filename)
|
log('unloading', self.filename)
|
||||||
for hook in self.hooks:
|
for hook in list(self.hooks):
|
||||||
if hook.is_unload is True:
|
if hook.is_unload is True:
|
||||||
try:
|
try:
|
||||||
hook.callback(hook.userdata)
|
hook.callback(hook.userdata)
|
||||||
|
|||||||
+14
-1
@@ -1368,7 +1368,20 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
|
|||||||
{
|
{
|
||||||
if (ignore_check (word[1], IG_PRIV))
|
if (ignore_check (word[1], IG_PRIV))
|
||||||
return;
|
return;
|
||||||
inbound_privmsg (serv, nick, ip, text, tags_data->identified, tags_data);
|
if (serv->have_echo_message && !serv->p_cmp (nick, serv->nick))
|
||||||
|
{
|
||||||
|
session *target_sess = find_dialog (serv, to);
|
||||||
|
|
||||||
|
if (!target_sess)
|
||||||
|
target_sess = find_channel (serv, to);
|
||||||
|
if (target_sess)
|
||||||
|
inbound_chanmsg (serv, target_sess, target_sess->channel, nick, text, TRUE, tags_data->identified, tags_data);
|
||||||
|
else if (serv->front_session)
|
||||||
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_MSGSEND, serv->front_session, to, text, NULL, NULL, 0, tags_data->timestamp);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
inbound_privmsg (serv, nick, ip, text, tags_data->identified, tags_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-12
@@ -154,7 +154,7 @@ int
|
|||||||
_SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl)
|
_SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl)
|
||||||
{
|
{
|
||||||
X509 *peer_cert;
|
X509 *peer_cert;
|
||||||
X509_PUBKEY *key;
|
const X509_PUBKEY *key;
|
||||||
X509_ALGOR *algor = NULL;
|
X509_ALGOR *algor = NULL;
|
||||||
EVP_PKEY *peer_pkey;
|
EVP_PKEY *peer_pkey;
|
||||||
char notBefore[64];
|
char notBefore[64];
|
||||||
@@ -350,7 +350,7 @@ _SSL_close (SSL * ssl)
|
|||||||
{
|
{
|
||||||
SSL_set_shutdown (ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
SSL_set_shutdown (ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
||||||
SSL_free (ssl);
|
SSL_free (ssl);
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
#if !defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
|
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
|
||||||
ERR_remove_thread_state (NULL);
|
ERR_remove_thread_state (NULL);
|
||||||
#else
|
#else
|
||||||
@@ -507,9 +507,12 @@ _SSL_check_subject_altname (X509 *cert, const char *host)
|
|||||||
static int
|
static int
|
||||||
_SSL_check_common_name (X509 *cert, const char *host)
|
_SSL_check_common_name (X509 *cert, const char *host)
|
||||||
{
|
{
|
||||||
X509_NAME *name;
|
const X509_NAME *name;
|
||||||
char *common_name = NULL;
|
const X509_NAME_ENTRY *entry;
|
||||||
|
const ASN1_STRING *common_name_asn1;
|
||||||
|
const unsigned char *common_name;
|
||||||
int common_name_len;
|
int common_name_len;
|
||||||
|
int common_name_index;
|
||||||
int rv = -1;
|
int rv = -1;
|
||||||
GInetAddress *addr;
|
GInetAddress *addr;
|
||||||
|
|
||||||
@@ -517,16 +520,27 @@ _SSL_check_common_name (X509 *cert, const char *host)
|
|||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
common_name_len = X509_NAME_get_text_by_NID (name, NID_commonName, NULL, 0);
|
common_name_index = X509_NAME_get_index_by_NID (name, NID_commonName, -1);
|
||||||
if (common_name_len < 0)
|
if (common_name_index < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
common_name = g_malloc0 (common_name_len + 1);
|
entry = X509_NAME_get_entry (name, common_name_index);
|
||||||
|
if (entry == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
X509_NAME_get_text_by_NID (name, NID_commonName, common_name, common_name_len + 1);
|
common_name_asn1 = X509_NAME_ENTRY_get_data (entry);
|
||||||
|
if (common_name_asn1 == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#ifdef HAVE_ASN1_STRING_GET0_DATA
|
||||||
|
common_name = ASN1_STRING_get0_data (common_name_asn1);
|
||||||
|
#else
|
||||||
|
common_name = ASN1_STRING_data (common_name_asn1);
|
||||||
|
#endif
|
||||||
|
common_name_len = ASN1_STRING_length (common_name_asn1);
|
||||||
|
|
||||||
/* NUL bytes in CN? */
|
/* NUL bytes in CN? */
|
||||||
if (common_name_len != (int)strlen(common_name))
|
if (common_name_len != (int)strlen((const char *)common_name))
|
||||||
{
|
{
|
||||||
g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n");
|
g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n");
|
||||||
rv = -2;
|
rv = -2;
|
||||||
@@ -539,18 +553,17 @@ _SSL_check_common_name (X509 *cert, const char *host)
|
|||||||
* We don't want to attempt wildcard matching against IP
|
* We don't want to attempt wildcard matching against IP
|
||||||
* addresses, so perform a simple comparison here.
|
* addresses, so perform a simple comparison here.
|
||||||
*/
|
*/
|
||||||
if (g_strcmp0 (common_name, host) == 0)
|
if (g_strcmp0 ((const char *)common_name, host) == 0)
|
||||||
rv = 0;
|
rv = 0;
|
||||||
else
|
else
|
||||||
rv = -1;
|
rv = -1;
|
||||||
|
|
||||||
g_object_unref (addr);
|
g_object_unref (addr);
|
||||||
}
|
}
|
||||||
else if (_SSL_match_hostname (common_name, host) == 0)
|
else if (_SSL_match_hostname ((const char *)common_name, host) == 0)
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
g_free(common_name);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -380,6 +380,7 @@ lag_check (void)
|
|||||||
time_t now = time (0);
|
time_t now = time (0);
|
||||||
time_t lag;
|
time_t lag;
|
||||||
time_t ping_age;
|
time_t ping_age;
|
||||||
|
unsigned long ping_timeout;
|
||||||
|
|
||||||
tim = make_ping_time ();
|
tim = make_ping_time ();
|
||||||
|
|
||||||
@@ -389,8 +390,11 @@ lag_check (void)
|
|||||||
if (serv->connected && serv->end_of_motd)
|
if (serv->connected && serv->end_of_motd)
|
||||||
{
|
{
|
||||||
lag = now - serv->ping_recv;
|
lag = now - serv->ping_recv;
|
||||||
if (serv->lag_sent && prefs.hex_net_ping_timeout != 0 && lag > prefs.hex_net_ping_timeout && lag > 0)
|
ping_timeout = (unsigned long) prefs.hex_net_ping_timeout * 1000;
|
||||||
|
if (serv->lag_sent && prefs.hex_net_ping_timeout != 0
|
||||||
|
&& tim - serv->lag_sent > ping_timeout)
|
||||||
{
|
{
|
||||||
|
lag = (tim - serv->lag_sent) / 1000;
|
||||||
sprintf (tbuf, "%" G_GINT64_FORMAT, (gint64) lag);
|
sprintf (tbuf, "%" G_GINT64_FORMAT, (gint64) lag);
|
||||||
EMIT_SIGNAL (XP_TE_PINGTIMEOUT, serv->server_session, tbuf, NULL,
|
EMIT_SIGNAL (XP_TE_PINGTIMEOUT, serv->server_session, tbuf, NULL,
|
||||||
NULL, NULL, 0);
|
NULL, NULL, 0);
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ typedef struct _GtkStatusIcon GtkStatusIcon;
|
|||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#include <gdk/gdkwin32.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum /* current icon status */
|
typedef enum /* current icon status */
|
||||||
@@ -1092,6 +1095,119 @@ tray_menu_settings (GtkWidget * wid, gpointer none)
|
|||||||
setup_open ();
|
setup_open ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#define TRAY_WIN32_HIDE 1
|
||||||
|
#define TRAY_WIN32_AWAY 2
|
||||||
|
#define TRAY_WIN32_BACK 3
|
||||||
|
#define TRAY_WIN32_PREFS 4
|
||||||
|
#define TRAY_WIN32_QUIT 5
|
||||||
|
|
||||||
|
static WCHAR *
|
||||||
|
tray_win32_menu_label (const char *label)
|
||||||
|
{
|
||||||
|
char *plain;
|
||||||
|
char *src;
|
||||||
|
char *dst;
|
||||||
|
WCHAR *wide;
|
||||||
|
|
||||||
|
plain = g_strdup (label ? label : "");
|
||||||
|
for (src = plain, dst = plain; *src; src++)
|
||||||
|
{
|
||||||
|
if (*src == '_')
|
||||||
|
continue;
|
||||||
|
*dst++ = *src;
|
||||||
|
}
|
||||||
|
*dst = 0;
|
||||||
|
wide = g_utf8_to_utf16 (plain, -1, NULL, NULL, NULL);
|
||||||
|
g_free (plain);
|
||||||
|
|
||||||
|
return wide;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tray_win32_append_item (HMENU menu, UINT id, const char *label, gboolean enabled)
|
||||||
|
{
|
||||||
|
WCHAR *wide;
|
||||||
|
|
||||||
|
wide = tray_win32_menu_label (label);
|
||||||
|
AppendMenuW (menu, MF_STRING | (enabled ? MF_ENABLED : MF_GRAYED), id, wide);
|
||||||
|
g_free (wide);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HWND
|
||||||
|
tray_win32_get_hwnd (void)
|
||||||
|
{
|
||||||
|
GtkWindow *win;
|
||||||
|
GdkWindow *gdk_win;
|
||||||
|
|
||||||
|
win = GTK_WINDOW (zoitechat_get_info (ph, "gtkwin_ptr"));
|
||||||
|
if (!win)
|
||||||
|
return GetActiveWindow ();
|
||||||
|
|
||||||
|
gdk_win = gtk_widget_get_window (GTK_WIDGET (win));
|
||||||
|
if (!gdk_win)
|
||||||
|
return GetActiveWindow ();
|
||||||
|
|
||||||
|
return gdk_win32_window_get_handle (gdk_win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tray_win32_menu_cb (void)
|
||||||
|
{
|
||||||
|
HMENU menu;
|
||||||
|
POINT point;
|
||||||
|
UINT command;
|
||||||
|
HWND hwnd;
|
||||||
|
int away_status;
|
||||||
|
|
||||||
|
zoitechat_set_context (ph, zoitechat_find_context (ph, NULL, NULL));
|
||||||
|
|
||||||
|
menu = CreatePopupMenu ();
|
||||||
|
if (!menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
away_status = tray_find_away_status ();
|
||||||
|
tray_win32_append_item (menu, TRAY_WIN32_HIDE,
|
||||||
|
tray_get_window_status () == WS_HIDDEN ? _("_Restore Window") : _("_Hide Window"), TRUE);
|
||||||
|
AppendMenuW (menu, MF_SEPARATOR, 0, NULL);
|
||||||
|
tray_win32_append_item (menu, TRAY_WIN32_AWAY, _("_Away"), away_status != 1);
|
||||||
|
tray_win32_append_item (menu, TRAY_WIN32_BACK, _("_Back"), away_status != 2);
|
||||||
|
AppendMenuW (menu, MF_SEPARATOR, 0, NULL);
|
||||||
|
tray_win32_append_item (menu, TRAY_WIN32_PREFS, _("_Preferences"), TRUE);
|
||||||
|
AppendMenuW (menu, MF_SEPARATOR, 0, NULL);
|
||||||
|
tray_win32_append_item (menu, TRAY_WIN32_QUIT, _("_Quit"), TRUE);
|
||||||
|
|
||||||
|
GetCursorPos (&point);
|
||||||
|
hwnd = tray_win32_get_hwnd ();
|
||||||
|
if (hwnd)
|
||||||
|
SetForegroundWindow (hwnd);
|
||||||
|
command = TrackPopupMenu (menu, TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
|
||||||
|
point.x, point.y, 0, hwnd, NULL);
|
||||||
|
DestroyMenu (menu);
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case TRAY_WIN32_HIDE:
|
||||||
|
tray_toggle_visibility (FALSE);
|
||||||
|
break;
|
||||||
|
case TRAY_WIN32_AWAY:
|
||||||
|
tray_foreach_server (NULL, "away");
|
||||||
|
break;
|
||||||
|
case TRAY_WIN32_BACK:
|
||||||
|
tray_foreach_server (NULL, "back");
|
||||||
|
break;
|
||||||
|
case TRAY_WIN32_PREFS:
|
||||||
|
tray_menu_settings (NULL, NULL);
|
||||||
|
break;
|
||||||
|
case TRAY_WIN32_QUIT:
|
||||||
|
tray_menu_quit_cb (NULL, NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tray_menu_populate (GtkWidget *menu)
|
tray_menu_populate (GtkWidget *menu)
|
||||||
{
|
{
|
||||||
@@ -1219,6 +1335,15 @@ tray_window_visibility_cb (GtkWidget *widget, gpointer userdata)
|
|||||||
static void
|
static void
|
||||||
tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
||||||
{
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
(void)widget;
|
||||||
|
(void)button;
|
||||||
|
(void)time;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
tray_win32_menu_cb ();
|
||||||
|
return;
|
||||||
|
#else
|
||||||
static GtkWidget *menu;
|
static GtkWidget *menu;
|
||||||
|
|
||||||
(void)button;
|
(void)button;
|
||||||
@@ -1258,6 +1383,7 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
|||||||
if (event)
|
if (event)
|
||||||
gdk_event_free (event);
|
gdk_event_free (event);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2.18.2
|
2.18.3
|
||||||
|
|||||||
Reference in New Issue
Block a user