From f55f7611c218afa9dae3d791816df62c2223364b Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Tue, 17 Mar 2026 11:10:55 +0100 Subject: [PATCH 1/3] The buffer API should use `unsigned` types. Either there are buffer entries or there are none. No need to have negative values in the API. Signed-off-by: Steffen Jaeckel --- src/ui/buffer.c | 6 +++--- src/ui/buffer.h | 6 +++--- src/ui/window.c | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ui/buffer.c b/src/ui/buffer.c index 6dd485f2..17736daf 100644 --- a/src/ui/buffer.c +++ b/src/ui/buffer.c @@ -50,7 +50,7 @@ buffer_create(void) return new_buff; } -int +unsigned int buffer_size(ProfBuff buffer) { return g_slist_length(buffer->entries); @@ -114,7 +114,7 @@ buffer_remove_entry_by_id(ProfBuff buffer, const char* const id) } void -buffer_remove_entry(ProfBuff buffer, int entry) +buffer_remove_entry(ProfBuff buffer, unsigned int entry) { GSList* node = g_slist_nth(buffer->entries, entry); ProfBuffEntry* e = node->data; @@ -142,7 +142,7 @@ buffer_mark_received(ProfBuff buffer, const char* const id) } ProfBuffEntry* -buffer_get_entry(ProfBuff buffer, int entry) +buffer_get_entry(ProfBuff buffer, unsigned int entry) { GSList* node = g_slist_nth(buffer->entries, entry); return node->data; diff --git a/src/ui/buffer.h b/src/ui/buffer.h index 6062d207..61840621 100644 --- a/src/ui/buffer.h +++ b/src/ui/buffer.h @@ -49,9 +49,9 @@ void buffer_free(ProfBuff buffer); void buffer_append(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos); void buffer_prepend(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos); void buffer_remove_entry_by_id(ProfBuff buffer, const char* const id); -void buffer_remove_entry(ProfBuff buffer, int entry); -int buffer_size(ProfBuff buffer); -ProfBuffEntry* buffer_get_entry(ProfBuff buffer, int entry); +void buffer_remove_entry(ProfBuff buffer, unsigned int entry); +unsigned int buffer_size(ProfBuff buffer); +ProfBuffEntry* buffer_get_entry(ProfBuff buffer, unsigned int entry); ProfBuffEntry* buffer_get_entry_by_id(ProfBuff buffer, const char* const id); gboolean buffer_mark_received(ProfBuff buffer, const char* const id); diff --git a/src/ui/window.c b/src/ui/window.c index dba4a728..12816eea 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -637,7 +637,7 @@ win_page_up(ProfWin* window, int scroll_size) iq_mam_request_older(chatwin); } - int buff_size = buffer_size(window->layout->buffer); + unsigned int buff_size = buffer_size(window->layout->buffer); int offset_entry_id = buff_size > 10 ? 10 : buff_size - 1; int offset = buffer_get_entry(window->layout->buffer, offset_entry_id)->y_end_pos; *page_start = offset - page_space; @@ -678,8 +678,8 @@ win_page_down(ProfWin* window, int scroll_size) // Scrolled down after reaching the bottom of the page if ((*page_start > total_rows - page_space || (*page_start == page_space && *page_start >= total_rows)) && window->type == WIN_CHAT) { - int bf_size = buffer_size(window->layout->buffer); - if (bf_size > 0) { + unsigned int bf_size = buffer_size(window->layout->buffer); + if (bf_size != 0) { ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1); auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time); GDateTime* now = g_date_time_new_now_local(); @@ -1954,10 +1954,10 @@ win_print_trackbar(ProfWin* window) void win_redraw(ProfWin* window) { - int size = buffer_size(window->layout->buffer); + unsigned int size = buffer_size(window->layout->buffer); werase(window->layout->win); - for (int i = 0; i < size; i++) { + for (unsigned int i = 0; i < size; i++) { ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i); e->y_start_pos = getcury(window->layout->win); @@ -2185,11 +2185,11 @@ win_handle_command_exec_result_note(ProfWin* window, const char* const type, con void win_insert_last_read_position_marker(ProfWin* window, char* id) { - int size = buffer_size(window->layout->buffer); + unsigned int size = buffer_size(window->layout->buffer); // TODO: this is somewhat costly. We should improve this later. // check if we already have a separator present - for (int i = 0; i < size; i++) { + for (unsigned int i = 0; i < size; i++) { ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i); // if yes, don't print a new one From 349de02b8c0660686ea365264120c04536594ec3 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Tue, 17 Mar 2026 17:29:18 +0100 Subject: [PATCH 2/3] Introduce `prof_date_time_format_iso8601()`. Instead of repeating the same pattern over and over, introduce a helper function that either outputs a formatted timestamp if one is given as argument or returns the current time if no argument is given. Signed-off-by: Steffen Jaeckel --- src/chatlog.c | 16 ++-------------- src/common.c | 14 ++++++++++++++ src/common.h | 2 ++ src/database.c | 16 ++-------------- src/log.c | 5 +---- src/ui/window.c | 4 +--- 6 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/chatlog.c b/src/chatlog.c index eec86e1f..5e83ff49 100644 --- a/src/chatlog.c +++ b/src/chatlog.c @@ -241,13 +241,7 @@ _chat_log_chat(const char* const login, const char* const other, const char* msg g_string_free(other_str, TRUE); } - if (timestamp == NULL) { - timestamp = g_date_time_new_now_local(); - } else { - g_date_time_ref(timestamp); - } - - auto_gchar gchar* date_fmt = g_date_time_format_iso8601(timestamp); + auto_gchar gchar* date_fmt = prof_date_time_format_iso8601(timestamp); FILE* chatlogp = fopen(dated_log->filename, "a"); g_chmod(dated_log->filename, S_IRUSR | S_IWUSR); if (chatlogp) { @@ -278,8 +272,6 @@ _chat_log_chat(const char* const login, const char* const other, const char* msg log_error("Error closing file %s, errno = %d", dated_log->filename, errno); } } - - g_date_time_unref(timestamp); } void @@ -347,9 +339,7 @@ _groupchat_log_chat(const gchar* const login, const gchar* const room, const gch g_hash_table_replace(logs, strdup(room), dated_log); } - GDateTime* dt_tmp = g_date_time_new_now_local(); - - auto_gchar gchar* date_fmt = g_date_time_format_iso8601(dt_tmp); + auto_gchar gchar* date_fmt = prof_date_time_format_iso8601(NULL); FILE* grpchatlogp = fopen(dated_log->filename, "a"); g_chmod(dated_log->filename, S_IRUSR | S_IWUSR); @@ -366,8 +356,6 @@ _groupchat_log_chat(const gchar* const login, const gchar* const room, const gch log_error("Error closing file %s, errno = %d", dated_log->filename, errno); } } - - g_date_time_unref(dt_tmp); } static char* diff --git a/src/common.c b/src/common.c index d693d671..44c2fcff 100644 --- a/src/common.c +++ b/src/common.c @@ -864,6 +864,20 @@ unique_filename_from_url(const char* url, const char* path) return unique_filename; } +/* This returns a timestamp formatted in ISO8601 format. + * Either it returns the current time if `dt` is NULL, or + * it returns the formatted time value passed in `dt`. + */ +gchar* +prof_date_time_format_iso8601(GDateTime* dt) +{ + GDateTime* dt_ = (dt == NULL) ? g_date_time_new_now_local() : dt; + gchar* ret = g_date_time_format_iso8601(dt_); + if (dt == NULL) + g_date_time_unref(dt_); + return ret; +} + /* build profanity version string. * example: 0.13.1dev.master.69d8c1f9 */ diff --git a/src/common.h b/src/common.h index 5101c281..0dccf9b5 100644 --- a/src/common.h +++ b/src/common.h @@ -187,6 +187,8 @@ gchar* get_expanded_path(const char* path); char* basename_from_url(const char* url); +gchar* prof_date_time_format_iso8601(GDateTime* dt); + gchar* prof_get_version(void); void prof_add_shutdown_routine(void (*routine)(void)); diff --git a/src/database.c b/src/database.c index 7311f8ec..7b59a5b7 100644 --- a/src/database.c +++ b/src/database.c @@ -306,8 +306,7 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s // Flip order when querying older pages gchar* sort1 = from_start ? "ASC" : "DESC"; gchar* sort2 = !flip ? "ASC" : "DESC"; - GDateTime* now = g_date_time_new_now_local(); - auto_gchar gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now); + auto_gchar gchar* end_date_fmt = end_time ? end_time : prof_date_time_format_iso8601(NULL); auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM (" "SELECT COALESCE(B.`message`, A.`message`) AS message, " "A.`timestamp`, A.`from_jid`, A.`from_resource`, A.`to_jid`, A.`to_resource`, A.`type`, A.`encryption`, A.`stanza_id` FROM `ChatLogs` AS A " @@ -320,8 +319,6 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s "ORDER BY `timestamp` %s;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, end_date_fmt, start_time, start_time, sort1, MESSAGES_TO_RETRIEVE, sort2); - g_date_time_unref(now); - if (!query) { log_error("Could not allocate memory"); return NULL; @@ -448,16 +445,7 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji } char* err_msg; - auto_gchar gchar* date_fmt = NULL; - - if (message->timestamp) { - date_fmt = g_date_time_format_iso8601(message->timestamp); - } else { - GDateTime* dt = g_date_time_new_now_local(); - date_fmt = g_date_time_format_iso8601(dt); - g_date_time_unref(dt); - } - + auto_gchar gchar* date_fmt = prof_date_time_format_iso8601(message->timestamp); const char* enc = _get_message_enc_str(message->enc); if (!type) { diff --git a/src/log.c b/src/log.c index 04fb6f53..3eeeef37 100644 --- a/src/log.c +++ b/src/log.c @@ -204,14 +204,11 @@ log_close(void) static void _log_msg(log_level_t level, const char* const area, const char* const msg) { - GDateTime* dt = g_date_time_new_now_local(); - char* level_str = _log_abbreviation_string_from_level(level); - auto_gchar gchar* date_fmt = g_date_time_format_iso8601(dt); + auto_gchar gchar* date_fmt = prof_date_time_format_iso8601(NULL); fprintf(logp, "%s: %08d: %s: %s: %s\n", date_fmt, prof_pid, area, level_str, msg); - g_date_time_unref(dt); fflush(logp); diff --git a/src/ui/window.c b/src/ui/window.c index 12816eea..fb61df1a 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -682,9 +682,7 @@ win_page_down(ProfWin* window, int scroll_size) if (bf_size != 0) { ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1); auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time); - GDateTime* now = g_date_time_new_now_local(); - gchar* end_date = g_date_time_format_iso8601(now); - g_date_time_unref(now); + gchar* end_date = prof_date_time_format_iso8601(NULL); if (*scroll_state != WIN_SCROLL_REACHED_BOTTOM && !chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE)) { *scroll_state = WIN_SCROLL_REACHED_BOTTOM; } else if (*scroll_state == WIN_SCROLL_REACHED_BOTTOM) { From 22408799569364e8def6571de8998aa51921955d Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Wed, 18 Mar 2026 13:05:14 +0100 Subject: [PATCH 3/3] Fix potential double free. Change `log_database_get_previous_chat()` to not take ownership of `end_time`, so it's clear that whoever passes it in, has to free it. Fixes: https://github.com/profanity-im/profanity/issues/2110 Signed-off-by: Steffen Jaeckel --- src/database.c | 4 ++-- src/database.h | 2 +- src/ui/chatwin.c | 7 ++++--- src/ui/ui.h | 2 +- src/ui/window.c | 4 +--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/database.c b/src/database.c index 7b59a5b7..60ec5f95 100644 --- a/src/database.c +++ b/src/database.c @@ -296,7 +296,7 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las // null the current time is used. from_start gets first few messages if true // otherwise the last ones. Flip flips the order of the results GSList* -log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip) +log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, const char* end_time, gboolean from_start, gboolean flip) { sqlite3_stmt* stmt = NULL; const Jid* myjid = connection_get_jid(); @@ -306,7 +306,7 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s // Flip order when querying older pages gchar* sort1 = from_start ? "ASC" : "DESC"; gchar* sort2 = !flip ? "ASC" : "DESC"; - auto_gchar gchar* end_date_fmt = end_time ? end_time : prof_date_time_format_iso8601(NULL); + auto_gchar gchar* end_date_fmt = end_time ? g_strdup(end_time) : prof_date_time_format_iso8601(NULL); auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM (" "SELECT COALESCE(B.`message`, A.`message`) AS message, " "A.`timestamp`, A.`from_jid`, A.`from_resource`, A.`to_jid`, A.`to_resource`, A.`type`, A.`encryption`, A.`stanza_id` FROM `ChatLogs` AS A " diff --git a/src/database.h b/src/database.h index d734282a..dc5312d3 100644 --- a/src/database.h +++ b/src/database.h @@ -21,7 +21,7 @@ void log_database_add_incoming(ProfMessage* message); void log_database_add_outgoing_chat(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc); void log_database_add_outgoing_muc(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc); void log_database_add_outgoing_muc_pm(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc); -GSList* log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip); +GSList* log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, const char* end_time, gboolean from_start, gboolean flip); ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last); void log_database_close(void); diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index f1c53349..1d1311e6 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -558,10 +558,11 @@ _chatwin_history(ProfChatWin* chatwin, const char* const contact_barejid) // first entry's timestamp in the buffer is used. Flip true to prepend to buffer. // Timestamps should be in iso8601 gboolean -chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip) +chatwin_db_history(ProfChatWin* chatwin, const char* start_time, const char* end_time, gboolean flip) { - if (!end_time) { - end_time = buffer_size(((ProfWin*)chatwin)->layout->buffer) == 0 ? NULL : g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)chatwin)->layout->buffer, 0)->time); + auto_gchar gchar* end_time_ = NULL; + if (!end_time && buffer_size(((ProfWin*)chatwin)->layout->buffer) != 0) { + end_time = end_time_ = g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)chatwin)->layout->buffer, 0)->time); } GSList* history = log_database_get_previous_chat(chatwin->barejid, start_time, end_time, !flip, flip); diff --git a/src/ui/ui.h b/src/ui/ui.h index 8a012931..f47bb906 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -119,7 +119,7 @@ void chatwin_set_incoming_char(ProfChatWin* chatwin, const char* const ch); void chatwin_unset_incoming_char(ProfChatWin* chatwin); void chatwin_set_outgoing_char(ProfChatWin* chatwin, const char* const ch); void chatwin_unset_outgoing_char(ProfChatWin* chatwin); -gboolean chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip); +gboolean chatwin_db_history(ProfChatWin* chatwin, const char* start_time, const char* end_time, gboolean flip); // MUC window ProfMucWin* mucwin_new(const char* const barejid); diff --git a/src/ui/window.c b/src/ui/window.c index fb61df1a..178a2c0b 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -682,11 +682,9 @@ win_page_down(ProfWin* window, int scroll_size) if (bf_size != 0) { ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1); auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time); - gchar* end_date = prof_date_time_format_iso8601(NULL); + auto_gchar gchar* end_date = prof_date_time_format_iso8601(NULL); if (*scroll_state != WIN_SCROLL_REACHED_BOTTOM && !chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE)) { *scroll_state = WIN_SCROLL_REACHED_BOTTOM; - } else if (*scroll_state == WIN_SCROLL_REACHED_BOTTOM) { - g_free(end_date); } int offset = last_entry->y_end_pos - 1;