From 2aec64983960b3c825e68eab2a13b05281d90363 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Mon, 6 Jul 2026 09:58:16 +0200 Subject: [PATCH 1/2] fix(ui): Fix rendering corruption when wrapping long words When a single word is longer than the line layout limit (often happening with ad-hoc command URIs), `_win_print_wrapped` splits and prints the word char by char. We used `g_utf8_strncpy(copy, word_ch, 1)` to copy a single character. But `g_utf8_strncpy` does not null terminate the destination buffer if the character limit is reached before encountering a NUL. The result was stack garbage being passed to ncurses `waddstr` causing corrupted console rendering. Fixes: https://github.com/profanity-im/profanity/issues/1949 Signed-off-by: Michael Vetter --- src/ui/window.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ui/window.c b/src/ui/window.c index d849ebbb8..052e5a382 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -2002,8 +2002,11 @@ _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pa _win_indent(win, indent + pad_indent); } - gchar copy[wordi + 1]; - g_utf8_strncpy(copy, word_ch, 1); + gchar* next = g_utf8_next_char(word_ch); + gsize bytes_to_copy = next - word_ch; + gchar copy[bytes_to_copy + 1]; + memcpy(copy, word_ch, bytes_to_copy); + copy[bytes_to_copy] = '\0'; waddstr(win, copy); word_ch = g_utf8_next_char(word_ch); From 6754c7063f62b6d70958a417347e726c592b2730 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Mon, 6 Jul 2026 10:23:08 +0200 Subject: [PATCH 2/2] fix(ui): Use case insensitive window lookup for JIDs When users join MUCs or start chats, they may input mixed case strings (`Room@Conference.Example.Com`). The server normalize JIDs to lowercase, so incoming server responses and events use lowercase. JID based window lookups used `g_strcmp0` for case sensitive matching. When a server response arrived for a window initialized with mixed case the lookup failed. Ref: https://github.com/profanity-im/profanity/issues/1949 Signed-off-by: Michael Vetter --- src/common.c | 15 +++++++++++++++ src/common.h | 1 + src/ui/window_list.c | 6 +++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/common.c b/src/common.c index 65ec0db42..3c694f57c 100644 --- a/src/common.c +++ b/src/common.c @@ -445,6 +445,21 @@ str_xml_sanitize(const char* const str) return g_string_free(sanitized, FALSE); } +gint +str_ascii_casecmp0(const char* s1, const char* s2) +{ + if (s1 == s2) { + return 0; + } + if (s1 == NULL) { + return -1; + } + if (s2 == NULL) { + return 1; + } + return g_ascii_strcasecmp(s1, s2); +} + char* release_get_latest(void) { diff --git a/src/common.h b/src/common.h index cb3155e49..3c18c56dd 100644 --- a/src/common.h +++ b/src/common.h @@ -160,6 +160,7 @@ char* str_replace(const char* string, const char* substr, const char* replacemen gboolean strtoi_range(const char* str, int* saveptr, int min, int max, char** err_msg); int utf8_display_len(const char* const str); gchar* str_xml_sanitize(const char* const str); +gint str_ascii_casecmp0(const char* s1, const char* s2); gboolean string_matches_one_of(const char* what, const char* is, gboolean is_can_be_null, const char* first, ...) __attribute__((sentinel)); gboolean valid_tls_policy_option(const char* is); diff --git a/src/ui/window_list.c b/src/ui/window_list.c index 0fce68004..f99b61f52 100644 --- a/src/ui/window_list.c +++ b/src/ui/window_list.c @@ -101,7 +101,7 @@ wins_get_chat(const char* const barejid) ProfWin* window = curr->data; if (window->type == WIN_CHAT) { ProfChatWin* chatwin = (ProfChatWin*)window; - if (g_strcmp0(chatwin->barejid, barejid) == 0) { + if (str_ascii_casecmp0(chatwin->barejid, barejid) == 0) { return chatwin; } } @@ -147,7 +147,7 @@ wins_get_conf(const char* const roomjid) ProfWin* window = curr->data; if (window->type == WIN_CONFIG) { ProfConfWin* confwin = (ProfConfWin*)window; - if (g_strcmp0(confwin->roomjid, roomjid) == 0) { + if (str_ascii_casecmp0(confwin->roomjid, roomjid) == 0) { return confwin; } } @@ -166,7 +166,7 @@ wins_get_muc(const char* const roomjid) ProfWin* window = curr->data; if (window->type == WIN_MUC) { ProfMucWin* mucwin = (ProfMucWin*)window; - if (g_strcmp0(mucwin->roomjid, roomjid) == 0) { + if (str_ascii_casecmp0(mucwin->roomjid, roomjid) == 0) { return mucwin; } }