diff --git a/src/database.c b/src/database.c index 80439b9ad..4cb886191 100644 --- a/src/database.c +++ b/src/database.c @@ -201,9 +201,9 @@ log_database_add_outgoing_muc_pm(const char* const id, const char* const barejid _log_database_add_outgoing("mucpm", id, barejid, message, replace_id, enc); } -// Get info (timestamp and stanza_id) of the first or last message in db +// Get info (timestamp and stanza_id) of the first or last message in db after or before from_timestamp ProfMessage* -log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last) +log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last, char* from_timestamp) { sqlite3_stmt* stmt = NULL; gchar* query; @@ -212,10 +212,10 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las if (!myjid) return NULL; - if (is_last) { - query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE (`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q') ORDER BY `timestamp` DESC LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid); + if (from_timestamp) { + query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE ((`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q')) AND `timestamp` %c '%q' ORDER BY `timestamp` %s LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, is_last ? '<' : '>', from_timestamp, is_last ? "DESC" : "ASC"); } else { - query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE (`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q') ORDER BY `timestamp` ASC LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid); + query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE (`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q') ORDER BY `timestamp` %s LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, is_last ? "DESC" : "ASC"); } if (!query) { @@ -250,7 +250,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, char* end_time, gboolean from_start, gboolean flip, gboolean limit_results) { sqlite3_stmt* stmt = NULL; const char* jid = connection_get_fulljid(); @@ -263,7 +263,36 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s gchar* sort2 = !flip ? "ASC" : "DESC"; GDateTime* now = g_date_time_new_now_local(); gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now); - auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM (SELECT COALESCE(B.`message`, A.`message`) AS message, A.`timestamp`, A.`from_jid`, A.`type`, A.`encryption` from `ChatLogs` AS A LEFT JOIN `ChatLogs` AS B ON A.`stanza_id` = B.`replace_id` WHERE A.`replace_id` = '' AND ((A.`from_jid` = '%q' AND A.`to_jid` = '%q') OR (A.`from_jid` = '%q' AND A.`to_jid` = '%q')) AND A.`timestamp` < '%q' AND (%Q IS NULL OR A.`timestamp` > %Q) ORDER BY A.`timestamp` %s LIMIT %d) ORDER BY `timestamp` %s;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, end_date_fmt, start_time, start_time, sort1, MESSAGES_TO_RETRIEVE, sort2); + auto_sqlite gchar* query; + + if (limit_results) { + query = sqlite3_mprintf( + "SELECT * FROM (SELECT COALESCE(B.`message`, A.`message`) AS message, A.`timestamp`, A.`from_jid`, A.`type`, A.`encryption` from `ChatLogs` AS A LEFT JOIN `ChatLogs` AS B ON A.`stanza_id` = B.`replace_id` WHERE A.`replace_id` = '' AND ((A.`from_jid` = '%q' AND A.`to_jid` = '%q') OR (A.`from_jid` = '%q' AND A.`to_jid` = '%q')) AND A.`timestamp` < '%q' AND (%Q IS NULL OR A.`timestamp` > %Q) ORDER BY A.`timestamp` %s LIMIT %d) ORDER BY `timestamp` %s;", + contact_barejid, + myjid->barejid, + myjid->barejid, + contact_barejid, + end_date_fmt, + start_time, + start_time, + sort1, + MESSAGES_TO_RETRIEVE, + sort2 + ); + } else { + query = sqlite3_mprintf( + "SELECT * FROM (SELECT COALESCE(B.`message`, A.`message`) AS message, A.`timestamp`, A.`from_jid`, A.`type`, A.`encryption` from `ChatLogs` AS A LEFT JOIN `ChatLogs` AS B ON A.`stanza_id` = B.`replace_id` WHERE A.`replace_id` = '' AND ((A.`from_jid` = '%q' AND A.`to_jid` = '%q') OR (A.`from_jid` = '%q' AND A.`to_jid` = '%q')) AND A.`timestamp` < '%q' AND (%Q IS NULL OR A.`timestamp` > %Q) ORDER BY A.`timestamp`) ORDER BY `timestamp` %s;", + contact_barejid, + myjid->barejid, + myjid->barejid, + contact_barejid, + end_date_fmt, + start_time, + start_time, + sort1, + sort2 + ); + } g_date_time_unref(now); g_free(end_date_fmt); diff --git a/src/database.h b/src/database.h index 1e73c874f..1483eeb6e 100644 --- a/src/database.h +++ b/src/database.h @@ -47,8 +47,8 @@ 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); -ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last); +GSList* log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip, gboolean limit_results); +ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last, char* from_timestamp); void log_database_close(void); #endif // DATABASE_H diff --git a/src/event/common.c b/src/event/common.c index 1844d2ffd..45dc93913 100644 --- a/src/event/common.c +++ b/src/event/common.c @@ -61,6 +61,7 @@ ev_disconnect_cleanup(void) ui_disconnected(); session_disconnect(); roster_destroy(); + iq_disco_items_on_disconnect(); iq_autoping_timer_cancel(); muc_invites_clear(); muc_confserver_clear(); diff --git a/src/event/server_events.c b/src/event/server_events.c index c10f69c37..3394c6a79 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -273,9 +273,21 @@ sv_ev_room_subject(const char* const room, const char* const nick, const char* c } } +static void +_log_muc(ProfMessage* message) +{ + if (message->enc == PROF_MSG_ENC_OMEMO) { + groupchat_log_omemo_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain); + } else { + groupchat_log_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain); + } + log_database_add_incoming(message); +} + void sv_ev_room_history(ProfMessage* message) { + _log_muc(message); if (prefs_get_boolean(PREF_NOTIFY_ROOM_OFFLINE)) { // check if this message was sent while we were offline. // if so, treat it as a new message rather than a history event. @@ -314,25 +326,9 @@ sv_ev_room_history(ProfMessage* message) } mucwin->last_msg_timestamp = g_date_time_new_now_local(); } - - gboolean younger = g_date_time_compare(mucwin->last_msg_timestamp, message->timestamp) < 0 ? TRUE : FALSE; - if (ev_is_first_connect() || younger) { - mucwin_history(mucwin, message); - } } } -static void -_log_muc(ProfMessage* message) -{ - if (message->enc == PROF_MSG_ENC_OMEMO) { - groupchat_log_omemo_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain); - } else { - groupchat_log_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain); - } - log_database_add_incoming(message); -} - void sv_ev_room_message(ProfMessage* message) { @@ -357,7 +353,9 @@ sv_ev_room_message(ProfMessage* message) GList* triggers = prefs_message_get_triggers(message->plain); _clean_incoming_message(message); - mucwin_incoming_msg(mucwin, message, mentions, triggers, TRUE); + if (!message->is_mam) { + mucwin_incoming_msg(mucwin, message, mentions, triggers, TRUE, FALSE); + } g_slist_free(mentions); @@ -375,7 +373,7 @@ sv_ev_room_message(ProfMessage* message) } // not currently on groupchat window - } else { + } else if (!message->is_mam) { status_bar_new(num, WIN_MUC, mucwin->roomjid); if ((g_strcmp0(mynick, message->from_jid->resourcepart) != 0) && (prefs_get_boolean(PREF_FLASH))) { @@ -400,7 +398,7 @@ sv_ev_room_message(ProfMessage* message) } mucwin->last_msg_timestamp = g_date_time_new_now_local(); - if (prefs_do_room_notify(is_current, mucwin->roomjid, mynick, message->from_jid->resourcepart, message->plain, mention, triggers != NULL)) { + if (prefs_do_room_notify(is_current, mucwin->roomjid, mynick, message->from_jid->resourcepart, message->plain, mention, triggers != NULL) && !message->is_mam) { Jid* jidp = jid_create(mucwin->roomjid); if (jidp) { notify_room_message(message->from_jid->resourcepart, jidp->localpart, num, message->plain); @@ -652,7 +650,7 @@ sv_ev_incoming_message(ProfMessage* message) if (prefs_get_boolean(PREF_MAM)) { win_print_loading_history(window); - iq_mam_request(chatwin, g_date_time_add_seconds(message->timestamp, 0)); // copy timestamp + iq_mam_request(window, g_date_time_add_seconds(message->timestamp, 0), FALSE); // copy timestamp } #ifdef HAVE_OMEMO diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index b470dbd66..b3eb74fac 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -146,7 +146,7 @@ chatwin_new(const char* const barejid) } if (prefs_get_boolean(PREF_MAM)) { - iq_mam_request(chatwin, NULL); + iq_mam_request(window, NULL, FALSE); win_print_loading_history(window); } @@ -575,7 +575,7 @@ static void _chatwin_history(ProfChatWin* chatwin, const char* const contact_barejid) { if (!chatwin->history_shown) { - GSList* history = log_database_get_previous_chat(contact_barejid, NULL, NULL, FALSE, FALSE); + GSList* history = log_database_get_previous_chat(contact_barejid, NULL, NULL, FALSE, FALSE, TRUE); GSList* curr = history; while (curr) { @@ -598,13 +598,13 @@ _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, char* end_time, gboolean flip, gboolean limit_results) { 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); } - GSList* history = log_database_get_previous_chat(chatwin->barejid, start_time, end_time, !flip, flip); + GSList* history = log_database_get_previous_chat(chatwin->barejid, start_time, end_time, !flip, flip, limit_results); gboolean has_items = g_slist_length(history) != 0; GSList* curr = history; diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 9b652f1c3..c2dd6260a 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -33,6 +33,7 @@ * */ +#include "xmpp/session.h" #define _XOPEN_SOURCE_EXTENDED #include "config.h" @@ -913,6 +914,7 @@ _inp_rl_win_pageup_handler(int count, int key) static int _inp_rl_win_pagedown_handler(int count, int key) { + session_lost_connection(); ProfWin* current = wins_get_current(); win_page_down(current); return 0; diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c index a2c314218..f958f61d6 100644 --- a/src/ui/mucwin.c +++ b/src/ui/mucwin.c @@ -36,6 +36,7 @@ #include "config.h" +#include "database.h" #include "ui.h" #include @@ -69,6 +70,13 @@ mucwin_new(const char* const barejid) } #endif + if (prefs_get_boolean(PREF_MAM)) { + iq_mam_request(window, NULL, FALSE); + win_print_loading_history(window); + } else if ((prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY))) { + mucwin_db_history(mucwin, NULL, NULL, TRUE, TRUE); + } + // Force redraw here to show correct offline users; before this point muc_members returns a wrong list ui_redraw_all_room_rosters(); return mucwin; @@ -363,7 +371,7 @@ mucwin_nick_change(ProfMucWin* mucwin, const char* const nick) } void -mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message) +mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message, gboolean flip) { assert(mucwin != NULL); @@ -372,7 +380,7 @@ mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message) GSList* mentions = get_mentions(prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD), prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE), message->plain, mynick); GList* triggers = prefs_message_get_triggers(message->plain); - mucwin_incoming_msg(mucwin, message, mentions, triggers, FALSE); + mucwin_incoming_msg(mucwin, message, mentions, triggers, FALSE, flip); g_slist_free(mentions); g_list_free_full(triggers, free); @@ -380,46 +388,108 @@ mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message) plugins_on_room_history_message(mucwin->roomjid, nick, message->plain, message->timestamp); } -static void -_mucwin_print_mention(ProfWin* window, const char* const message, const char* const from, const char* const mynick, GSList* mentions, const char* const ch, int flags) +gboolean +mucwin_db_history(ProfMucWin* mucwin, char* start_time, char* end_time, gboolean flip, gboolean limit_results) { - int last_pos = 0; - int pos; - GSList* curr = mentions; - glong mynick_len = g_utf8_strlen(mynick, -1); + if (!end_time) { + end_time = buffer_size(((ProfWin*)mucwin)->layout->buffer) == 0 ? NULL : g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)mucwin)->layout->buffer, 0)->time); + } + + GSList* history = log_database_get_previous_chat(mucwin->roomjid, start_time, end_time, !flip, flip, limit_results); + gboolean has_items = g_slist_length(history) != 0; + GSList* curr = history; while (curr) { - pos = GPOINTER_TO_INT(curr->data); + ProfMessage* msg = curr->data; + mucwin_history(mucwin, msg, flip); + curr = g_slist_next(curr); + } - auto_gchar gchar* before_str = g_utf8_substring(message, last_pos, pos); + g_slist_free_full(history, (GDestroyNotify)message_free); + win_redraw((ProfWin*)mucwin); - if (last_pos == 0 && strncmp(before_str, "/me ", 4) == 0) { - win_print_them(window, THEME_ROOMMENTION, ch, flags, ""); - win_append_highlight(window, THEME_ROOMMENTION, "*%s ", from); - win_append_highlight(window, THEME_ROOMMENTION, "%s", before_str + 4); + return has_items; +} + +static void +_mucwin_print_mention(ProfWin* window, const char* const message, const char* const from, const char* const mynick, GSList* mentions, const char* const ch, int flags, GDateTime* timestamp, gboolean flip) +{ + if (flip) { + int pos; + GSList* curr = g_slist_last(mentions); + glong mynick_len = g_utf8_strlen(mynick, -1); + int last_pos = GPOINTER_TO_INT(curr->data) + mynick_len; + + glong message_len = g_utf8_strlen(message, -1); + if (last_pos < message_len) { + // get tail without allocating a new string + gchar* rest = g_utf8_offset_to_pointer(message, last_pos); + win_appendln_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", rest); } else { - // print time and nick only once at beginning of the line - if (last_pos == 0) { - win_print_them(window, THEME_ROOMMENTION, ch, flags, from); - } - win_append_highlight(window, THEME_ROOMMENTION, "%s", before_str); + win_appendln_highlight(window, THEME_ROOMMENTION, timestamp, flip, ""); } - auto_gchar gchar* mynick_str = g_utf8_substring(message, pos, pos + mynick_len); - win_append_highlight(window, THEME_ROOMMENTION_TERM, "%s", mynick_str); + for (int i = g_slist_length(mentions) - 1; i >= 0; i --){ + curr = g_slist_nth(mentions, i); + pos = GPOINTER_TO_INT(curr->data); + last_pos = i == 0 ? 0 : pos + mynick_len; - last_pos = pos + mynick_len; + auto_gchar gchar* mynick_str = g_utf8_substring(message, pos, pos + mynick_len); + win_append_highlight(window, THEME_ROOMMENTION_TERM, timestamp, flip, "%s", mynick_str); - curr = g_slist_next(curr); - } + auto_gchar gchar* before_str = g_utf8_substring(message, last_pos, pos); - glong message_len = g_utf8_strlen(message, -1); - if (last_pos < message_len) { - // get tail without allocating a new string - gchar* rest = g_utf8_offset_to_pointer(message, last_pos); - win_appendln_highlight(window, THEME_ROOMMENTION, "%s", rest); + if (last_pos == 0 && strncmp(before_str, "/me ", 4) == 0) { + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", before_str + 4); + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "*%s ", from); + win_print_them(window, THEME_ROOMMENTION, timestamp, ch, flags, "", flip); + } else { + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", before_str); + // print time and nick only once at beginning of the line + if (last_pos == 0) { + win_print_them(window, THEME_ROOMMENTION, timestamp, ch, flags, from, flip); + } + } + } } else { - win_appendln_highlight(window, THEME_ROOMMENTION, ""); + int last_pos = 0; + int pos; + GSList* curr = mentions; + glong mynick_len = g_utf8_strlen(mynick, -1); + + while (curr) { + pos = GPOINTER_TO_INT(curr->data); + + auto_gchar gchar* before_str = g_utf8_substring(message, last_pos, pos); + + if (last_pos == 0 && strncmp(before_str, "/me ", 4) == 0) { + win_print_them(window, THEME_ROOMMENTION, timestamp, ch, flags, "", flip); + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "*%s ", from); + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", before_str + 4); + } else { + // print time and nick only once at beginning of the line + if (last_pos == 0) { + win_print_them(window, THEME_ROOMMENTION, timestamp, ch, flags, from, flip); + } + win_append_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", before_str); + } + + auto_gchar gchar* mynick_str = g_utf8_substring(message, pos, pos + mynick_len); + win_append_highlight(window, THEME_ROOMMENTION_TERM, timestamp, flip, "%s", mynick_str); + + last_pos = pos + mynick_len; + + curr = g_slist_next(curr); + } + + glong message_len = g_utf8_strlen(message, -1); + if (last_pos < message_len) { + // get tail without allocating a new string + gchar* rest = g_utf8_offset_to_pointer(message, last_pos); + win_appendln_highlight(window, THEME_ROOMMENTION, timestamp, flip, "%s", rest); + } else { + win_appendln_highlight(window, THEME_ROOMMENTION, timestamp, flip, ""); + } } } @@ -438,7 +508,7 @@ _cmp_trigger_weight(gconstpointer a, gconstpointer b) } static void -_mucwin_print_triggers(ProfWin* window, const char* const message, GList* triggers) +_mucwin_print_triggers(ProfWin* window, const char* const message, GList* triggers, GDateTime* timestamp, const gboolean flip) { GList* weighted_triggers = NULL; GList* curr = triggers; @@ -477,31 +547,59 @@ _mucwin_print_triggers(ProfWin* window, const char* const message, GList* trigge // no triggers found if (first_trigger_pos == -1) { - win_appendln_highlight(window, THEME_ROOMTRIGGER, "%s", message); + win_appendln_highlight(window, THEME_ROOMTRIGGER, timestamp, flip, "%s", message); } else { - if (first_trigger_pos > 0) { - char message_section[strlen(message) + 1]; + if (!flip) { + if (first_trigger_pos > 0) { + char message_section[strlen(message) + 1]; + int i = 0; + while (i < first_trigger_pos) { + message_section[i] = message[i]; + i++; + } + message_section[i] = '\0'; + win_append_highlight(window, THEME_ROOMTRIGGER, timestamp, flip, "%s", message_section); + } + char trigger_section[first_trigger_len + 1]; int i = 0; - while (i < first_trigger_pos) { - message_section[i] = message[i]; + while (i < first_trigger_len) { + trigger_section[i] = message[first_trigger_pos + i]; i++; } - message_section[i] = '\0'; - win_append_highlight(window, THEME_ROOMTRIGGER, "%s", message_section); - } - char trigger_section[first_trigger_len + 1]; - int i = 0; - while (i < first_trigger_len) { - trigger_section[i] = message[first_trigger_pos + i]; - i++; - } - trigger_section[i] = '\0'; + trigger_section[i] = '\0'; - if (first_trigger_pos + first_trigger_len < strlen(message)) { - win_append_highlight(window, THEME_ROOMTRIGGER_TERM, "%s", trigger_section); - _mucwin_print_triggers(window, &message[first_trigger_pos + first_trigger_len], triggers); + if (first_trigger_pos + first_trigger_len < strlen(message)) { + win_append_highlight(window, THEME_ROOMTRIGGER_TERM, timestamp, flip, "%s", trigger_section); + _mucwin_print_triggers(window, &message[first_trigger_pos + first_trigger_len], triggers, timestamp, flip); + } else { + win_appendln_highlight(window, THEME_ROOMTRIGGER_TERM, timestamp, flip, "%s", trigger_section); + } } else { - win_appendln_highlight(window, THEME_ROOMTRIGGER_TERM, "%s", trigger_section); + char trigger_section[first_trigger_len + 1]; + int i = 0; + while (i < first_trigger_len) { + trigger_section[i] = message[first_trigger_pos + i]; + i++; + } + trigger_section[i] = '\0'; + + if (first_trigger_pos + first_trigger_len < strlen(message)) { + _mucwin_print_triggers(window, &message[first_trigger_pos + first_trigger_len], triggers, timestamp, flip); + win_append_highlight(window, THEME_ROOMTRIGGER_TERM, timestamp, flip, "%s", trigger_section); + } else { + win_appendln_highlight(window, THEME_ROOMTRIGGER_TERM, timestamp, flip, "%s", trigger_section); + } + + if (first_trigger_pos > 0) { + char message_section[strlen(message) + 1]; + int i = 0; + while (i < first_trigger_pos) { + message_section[i] = message[i]; + i++; + } + message_section[i] = '\0'; + win_append_highlight(window, THEME_ROOMTRIGGER, timestamp, flip, "%s", message_section); + } } } } @@ -542,7 +640,7 @@ mucwin_outgoing_msg(ProfMucWin* mucwin, const char* const message, const char* c } void -mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection) +mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection, gboolean flip) { assert(mucwin != NULL); int flags = 0; @@ -577,17 +675,24 @@ mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList ch = strdup("-"); } - win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid); - wins_add_urls_ac(window, message, FALSE); - wins_add_quotes_ac(window, message->plain, FALSE); + if (!flip) { + win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid); + } + wins_add_urls_ac(window, message, flip); + wins_add_quotes_ac(window, message->plain, flip); if (g_slist_length(mentions) > 0) { - _mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags); + _mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags, message->timestamp, flip); } else if (triggers) { - win_print_them(window, THEME_ROOMTRIGGER, ch, flags, message->from_jid->resourcepart); - _mucwin_print_triggers(window, message->plain, triggers); + if (!flip) { + win_print_them(window, THEME_ROOMTRIGGER, message->timestamp, ch, flags, message->from_jid->resourcepart, flip); + _mucwin_print_triggers(window, message->plain, triggers, message->timestamp, flip); + } else { + _mucwin_print_triggers(window, message->plain, triggers, message->timestamp, flip); + win_print_them(window, THEME_ROOMTRIGGER, message->timestamp, ch, flags, message->from_jid->resourcepart, flip); + } } else { - win_println_incoming_muc_msg(window, ch, flags, message); + win_println_incoming_muc_msg(window, ch, flags, message, flip); } free(ch); diff --git a/src/ui/ui.h b/src/ui/ui.h index 79fafd75c..89b8ee015 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -145,7 +145,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, char* end_time, gboolean flip, gboolean limit_results); // MUC window ProfMucWin* mucwin_new(const char* const barejid); @@ -161,9 +161,10 @@ void mucwin_occupant_affiliation_change(ProfMucWin* mucwin, const char* const ni void mucwin_occupant_role_and_affiliation_change(ProfMucWin* mucwin, const char* const nick, const char* const role, const char* const affiliation, const char* const actor, const char* const reason); void mucwin_roster(ProfMucWin* mucwin, GList* occupants, const char* const presence); -void mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message); +void mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message, gboolean flip); +gboolean mucwin_db_history(ProfMucWin* mucwin, char* start_time, char* end_time, gboolean flip, gboolean limit_results); void mucwin_outgoing_msg(ProfMucWin* mucwin, const char* const message, const char* const id, prof_enc_t enc_mode, const char* const replace_id); -void mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection); +void mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection, gboolean flip); void mucwin_subject(ProfMucWin* mucwin, const char* const nick, const char* const subject); void mucwin_requires_config(ProfMucWin* mucwin); void mucwin_info(ProfMucWin* mucwin); @@ -396,8 +397,8 @@ void win_println_indent(ProfWin* window, int pad, const char* const message, ... void win_append(ProfWin* window, theme_item_t theme_item, const char* const message, ...); void win_appendln(ProfWin* window, theme_item_t theme_item, const char* const message, ...); -void win_append_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...); -void win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...); +void win_append_highlight(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const gboolean flip, const char* const message, ...); +void win_appendln_highlight(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const gboolean flip, const char* const message, ...); char* win_get_title(ProfWin* window); void win_show_occupant(ProfWin* window, Occupant* occupant); diff --git a/src/ui/window.c b/src/ui/window.c index 49f22ca7d..40cb0661c 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -35,6 +35,7 @@ */ #include "config.h" +#include "ui/win_types.h" #include "ui/window_list.h" #include @@ -70,7 +71,7 @@ #define CEILING(X) (X - (int)(X) > 0 ? (int)(X + 1) : (int)(X)) static void -_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...); +_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const gboolean flip, const char* const message, ...); static void _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const from, const char* const message, DeliveryReceipt* receipt); static void _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent); @@ -652,15 +653,29 @@ win_page_up(ProfWin* window) *page_start -= page_space; - if (*page_start == -page_space && window->type == WIN_CHAT) { - ProfChatWin* chatwin = (ProfChatWin*)window; + if (*page_start == -page_space) { ProfBuffEntry* first_entry = buffer_size(window->layout->buffer) != 0 ? buffer_get_entry(window->layout->buffer, 0) : NULL; + gboolean is_not_fetching = first_entry && !(first_entry->theme_item == THEME_ROOMINFO && g_strcmp0(first_entry->message, LOADING_MESSAGE) == 0); + + if (window->type == WIN_CHAT) { + ProfChatWin* chatwin = (ProfChatWin*)window; - // Don't do anything if still fetching mam messages - if (first_entry && !(first_entry->theme_item == THEME_ROOMINFO && g_strcmp0(first_entry->message, LOADING_MESSAGE) == 0)) { - if (!chatwin_db_history(chatwin, NULL, NULL, TRUE) && prefs_get_boolean(PREF_MAM)) { - win_print_loading_history(window); - iq_mam_request_older(chatwin); + // Don't do anything if still fetching mam messages + if (is_not_fetching) { + if (!chatwin_db_history(chatwin, NULL, NULL, TRUE, TRUE) && prefs_get_boolean(PREF_MAM)) { + win_print_loading_history(window); + iq_mam_request_older(window); + } + } + } else if (window->type == WIN_MUC) { + ProfMucWin* mucwin = (ProfMucWin*)window; + + // Don't do anything if still fetching mam messages + if (is_not_fetching) { + if (!mucwin_db_history(mucwin, NULL, NULL, TRUE, TRUE) && prefs_get_boolean(PREF_MAM)) { + win_print_loading_history(window); + iq_mam_request_older(window); + } } } } @@ -695,7 +710,7 @@ win_page_down(ProfWin* window) char* start = g_date_time_format_iso8601(buffer_get_entry(window->layout->buffer, bf_size - 1)->time); GDateTime* now = g_date_time_new_now_local(); char* end = g_date_time_format_iso8601(now); - chatwin_db_history((ProfChatWin*)window, start, end, FALSE); + chatwin_db_history((ProfChatWin*)window, start, end, FALSE, TRUE); g_free(start); g_date_time_unref(now); @@ -1430,7 +1445,7 @@ win_print_incoming(ProfWin* window, const char* const display_name_from, ProfMes } else { // Prevent duplicate messages when current client is sending a message or if it's mam if (g_strcmp0(message->from_jid->fulljid, connection_get_fulljid()) != 0 && !message->is_mam) { - _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, "%s", message->plain); + _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, FALSE, "%s", message->plain); } } @@ -1438,7 +1453,7 @@ win_print_incoming(ProfWin* window, const char* const display_name_from, ProfMes break; } case WIN_PRIVATE: - _win_printf(window, "-", 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, "%s", message->plain); + _win_printf(window, "-", 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, FALSE, "%s", message->plain); break; default: assert(FALSE); @@ -1447,18 +1462,18 @@ win_print_incoming(ProfWin* window, const char* const display_name_from, ProfMes } void -win_print_them(ProfWin* window, theme_item_t theme_item, const char* const show_char, int flags, const char* const them) +win_print_them(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const char* const show_char, int flags, const char* const them, gboolean flip) { - _win_printf(window, show_char, 0, NULL, flags | NO_ME | NO_EOL, theme_item, them, NULL, NULL, ""); + _win_printf(window, show_char, 0, timestamp , flags | NO_ME | NO_EOL, theme_item, them, NULL, NULL, flip, "", NULL); } void -win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message) +win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message, const gboolean flip) { if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) { _win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->fulljid); } else { - _win_printf(window, show_char, 0, message->timestamp, flags | NO_ME, THEME_TEXT_THEM, message->from_jid->resourcepart, message->from_jid->fulljid, message->id, "%s", message->plain); + _win_printf(window, show_char, 0, message->timestamp, flags | NO_ME, THEME_TEXT_THEM, message->from_jid->resourcepart, message->from_jid->fulljid, message->id, flip, "%s", message->plain); } inp_nonblocking(TRUE); @@ -1472,7 +1487,7 @@ win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const m if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && replace_id) { _win_correct(window, message, id, replace_id, me); } else { - _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, me, me, id, "%s", message); + _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, me, me, id, FALSE, "%s", message); } inp_nonblocking(TRUE); @@ -1489,7 +1504,7 @@ win_print_outgoing(ProfWin* window, const char* show_char, const char* const id, _win_correct(window, message, id, replace_id, myjid); } else { auto_char gchar* outgoing_str = prefs_get_string(PREF_OUTGOING_STAMP); - _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, outgoing_str, myjid, id, "%s", message); + _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, outgoing_str, myjid, id, FALSE, "%s", message); } inp_nonblocking(TRUE); @@ -1657,40 +1672,67 @@ win_appendln(ProfWin* window, theme_item_t theme_item, const char* const message } void -win_append_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...) +win_append_highlight(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const gboolean flip, const char* const message, ...) { - GDateTime* timestamp = g_date_time_new_now_local(); + gboolean hadTimestamp = TRUE; + + if (!timestamp) { + timestamp = g_date_time_new_now_local(); + hadTimestamp = FALSE; + } va_list arg; va_start(arg, message); GString* fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL); - _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL); + if (flip) { + buffer_prepend(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL); + win_redraw(window); + } else { + buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL); + _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL); + } inp_nonblocking(TRUE); - g_date_time_unref(timestamp); + + if (!hadTimestamp) { + g_date_time_unref(timestamp); + } g_string_free(fmt_msg, TRUE); va_end(arg); } void -win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...) +win_appendln_highlight(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const gboolean flip, const char* const message, ...) { - GDateTime* timestamp = g_date_time_new_now_local(); + gboolean hadTimestamp = TRUE; + + if (!timestamp) { + timestamp = g_date_time_new_now_local(); + hadTimestamp = FALSE; + } va_list arg; va_start(arg, message); GString* fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", NULL, fmt_msg->str, NULL, NULL); - _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL); + if (flip) { + buffer_prepend(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", NULL, fmt_msg->str, NULL, NULL); + win_redraw(window); + } else { + buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", NULL, fmt_msg->str, NULL, NULL); + _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL); + } + inp_nonblocking(TRUE); - g_date_time_unref(timestamp); + + if (!hadTimestamp) { + g_date_time_unref(timestamp); + } g_string_free(fmt_msg, TRUE); va_end(arg); @@ -1762,7 +1804,7 @@ win_newline(ProfWin* window) } static void -_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...) +_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const gboolean flip, const char* const message, ...) { if (timestamp == NULL) { timestamp = g_date_time_new_now_local(); @@ -1775,8 +1817,13 @@ _win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* t GString* fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, fmt_msg->str, NULL, message_id); - _win_print_internal(window, show_char, pad_indent, timestamp, flags, theme_item, display_from, fmt_msg->str, NULL); + if (flip) { + buffer_prepend(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, fmt_msg->str, NULL, message_id); + win_redraw(window); + } else { + buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, fmt_msg->str, NULL, message_id); + _win_print_internal(window, show_char, pad_indent, timestamp, flags, theme_item, display_from, fmt_msg->str, NULL); + } inp_nonblocking(TRUE); g_date_time_unref(timestamp); diff --git a/src/ui/window.h b/src/ui/window.h index 1c0a9007d..3e8bc1eff 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -65,11 +65,11 @@ void win_show_status_string(ProfWin* window, const char* const from, GDateTime* last_activity, const char* const pre, const char* const default_show); -void win_print_them(ProfWin* window, theme_item_t theme_item, const char* const show_char, int flags, const char* const them); +void win_print_them(ProfWin* window, theme_item_t theme_item, GDateTime* timestamp, const char* const show_char, int flags, const char* const them, const gboolean flip); void win_print_incoming(ProfWin* window, const char* const from, ProfMessage* message); void win_print_outgoing(ProfWin* window, const char* show_char, const char* const id, const char* const replace_id, const char* const message); void win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const char* const from, const char* const message, char* id, const char* const replace_id); -void win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message); +void win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message, const gboolean flip); void win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const me, const char* const id, const char* const replace_id, const char* const message); void win_print_history(ProfWin* window, const ProfMessage* const message); void win_print_old_history(ProfWin* window, const ProfMessage* const message); diff --git a/src/ui/window_list.c b/src/ui/window_list.c index 0a96b4610..fbbcc2ed2 100644 --- a/src/ui/window_list.c +++ b/src/ui/window_list.c @@ -47,6 +47,7 @@ #include "config/theme.h" #include "plugins/plugins.h" #include "ui/ui.h" +#include "ui/window.h" #include "ui/window_list.h" #include "xmpp/xmpp.h" #include "xmpp/roster_list.h" @@ -885,6 +886,12 @@ wins_lost_connection(void) ProfWin* window = curr->data; if (window->type != WIN_CONSOLE) { win_println(window, THEME_ERROR, "-", "Lost connection."); + ProfBuffEntry* first_message = buffer_size(window->layout->buffer) != 0 ? buffer_get_entry(window->layout->buffer, 0) : NULL; + gboolean is_fetching_mam = first_message && (first_message->theme_item == THEME_ROOMINFO && g_strcmp0(first_message->message, LOADING_MESSAGE) == 0); + + if (is_fetching_mam) { + buffer_remove_entry(window->layout->buffer, 0); + } // if current win, set current_win_dirty if (wins_is_current(window)) { @@ -925,6 +932,10 @@ wins_reestablished_connection(void) } #endif + if (prefs_get_boolean(PREF_MAM)) { + iq_mam_request(window, g_date_time_new_now_local(), TRUE); + } + // if current win, set current_win_dirty if (wins_is_current(window)) { win_update_virtual(window); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 159ba6096..70f5de4da 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -34,6 +34,7 @@ */ #include "config.h" +#include "ui/win_types.h" #ifdef HAVE_GIT_VERSION #include "gitversion.h" @@ -110,14 +111,16 @@ typedef struct mam_rsm_userdata char* start_datestr; char* end_datestr; gboolean fetch_next; - ProfChatWin* win; + gboolean is_reconnect; + ProfWin* win; } MamRsmUserdata; typedef struct late_delivery_userdata { - ProfChatWin* win; + ProfWin* win; GDateTime* enddate; GDateTime* startdate; + gboolean is_reconnect; } LateDeliveryUserdata; static int _iq_handler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata); @@ -157,7 +160,7 @@ static int _command_exec_response_handler(xmpp_stanza_t* const stanza, void* con static int _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata); static int _register_change_password_result_id_handler(xmpp_stanza_t* const stanza, void* const userdata); -static void _iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate); +static void _iq_mam_request(ProfWin* win, GDateTime* startdate, GDateTime* enddate, gboolean is_reconnect); static void _iq_free_room_data(ProfRoomInfoData* roominfo); static void _iq_free_affiliation_set(ProfPrivilegeSet* affiliation_set); static void _iq_free_affiliation_list(ProfAffiliationList* affiliation_list); @@ -307,6 +310,12 @@ iq_id_handler_add(const char* const id, ProfIqCallback func, ProfIqFreeCallback } } +void +iq_disco_items_on_disconnect(void) +{ + received_disco_items = FALSE; +} + void iq_autoping_timer_cancel(void) { @@ -2551,7 +2560,7 @@ _disco_items_result_handler(xmpp_stanza_t* const stanza) while (late_delivery_windows) { LateDeliveryUserdata* del_data = late_delivery_windows->data; - _iq_mam_request(del_data->win, del_data->startdate, del_data->enddate); + _iq_mam_request(del_data->win, del_data->startdate, del_data->enddate, del_data->is_reconnect); free(del_data); late_delivery_windows = g_slist_delete_link(late_delivery_windows, late_delivery_windows); @@ -2610,17 +2619,24 @@ _iq_free_affiliation_list(ProfAffiliationList* affiliation_list) static int _mam_buffer_commit_handler(xmpp_stanza_t* const stanza, void* const userdata) { - ProfChatWin* chatwin = (ProfChatWin*)userdata; + ProfWin* win = (ProfWin*)userdata; // Remove the "Loading messages …" message - buffer_remove_entry(((ProfWin*)chatwin)->layout->buffer, 0); - chatwin_db_history(chatwin, NULL, NULL, TRUE); + buffer_remove_entry(win->layout->buffer, 0); + + if (win->type == WIN_CHAT) { + ProfChatWin* chatwin = (ProfChatWin*)win; + chatwin_db_history(chatwin, NULL, NULL, TRUE, TRUE); + } else if (win->type == WIN_MUC) { + ProfMucWin* mucwin = (ProfMucWin*)win; + mucwin_db_history(mucwin, NULL, NULL, TRUE, TRUE); + } return 0; } static const gchar* mam_timestamp_format_string = "%FT%T.%f%:z"; void -iq_mam_request_older(ProfChatWin* win) +iq_mam_request_older(ProfWin* win) { if (connection_supports(XMPP_FEATURE_MAM2) == FALSE) { log_warning("Server doesn't advertise %s feature.", XMPP_FEATURE_MAM2); @@ -2628,7 +2644,9 @@ iq_mam_request_older(ProfChatWin* win) return; } - ProfMessage* first_msg = log_database_get_limits_info(win->barejid, FALSE); + char* win_jid = win_get_tab_identifier(win); + + ProfMessage* first_msg = log_database_get_limits_info(win_jid, FALSE, NULL); char* firstid = NULL; char* enddate = NULL; @@ -2641,11 +2659,12 @@ iq_mam_request_older(ProfChatWin* win) } xmpp_ctx_t* const ctx = connection_get_ctx(); - xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, win->barejid, NULL, enddate, firstid, NULL); + xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, win_jid, NULL, enddate, firstid, NULL, win->type == WIN_MUC); iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_buffer_commit_handler, NULL, win); g_free(enddate); message_free(first_msg); + free(win_jid); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -2666,7 +2685,7 @@ _mam_userdata_free(MamRsmUserdata* data) } void -_iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate) +_iq_mam_request(ProfWin* win, GDateTime* startdate, GDateTime* enddate, gboolean is_reconnect) { if (connection_supports(XMPP_FEATURE_MAM2) == FALSE) { log_warning("Server doesn't advertise %s feature.", XMPP_FEATURE_MAM2); @@ -2676,7 +2695,8 @@ _iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate) return; } - char* firstid = ""; + // Empty firstid means that the rsm paging will start from the last/most recent page + char* firstid = is_reconnect ? NULL : ""; char* startdate_str = NULL; char* enddate_str = NULL; gboolean fetch_next = FALSE; @@ -2696,31 +2716,36 @@ _iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate) g_date_time_unref(enddate); } + char* win_jid = win_get_tab_identifier(win); xmpp_ctx_t* const ctx = connection_get_ctx(); - xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, win->barejid, startdate_str, enddate_str, firstid, NULL); + xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, win_jid, startdate_str, enddate_str, firstid, NULL, win->type == WIN_MUC); MamRsmUserdata* data = malloc(sizeof(MamRsmUserdata)); if (data) { data->start_datestr = startdate_str; data->end_datestr = enddate_str; - data->barejid = strdup(win->barejid); + data->barejid = strdup(win_jid); data->fetch_next = fetch_next; data->win = win; + data->is_reconnect = is_reconnect; iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_rsm_id_handler, (ProfIqFreeCallback)_mam_userdata_free, data); } + win_println(win, THEME_DEFAULT, "-", "(%s) (%s) (%s) (%d)", startdate_str, enddate_str, win_jid, fetch_next); iq_send_stanza(iq); xmpp_stanza_release(iq); + free(win_jid); return; } void -iq_mam_request(ProfChatWin* win, GDateTime* enddate) +iq_mam_request(ProfWin* win, GDateTime* enddate, gboolean is_reconnect) { - ProfMessage* last_msg = log_database_get_limits_info(win->barejid, TRUE); + char* win_jid = win_get_tab_identifier(win); + ProfMessage* last_msg = log_database_get_limits_info(win_jid, TRUE, NULL); GDateTime* startdate = g_date_time_add_seconds(last_msg->timestamp, 0); message_free(last_msg); @@ -2730,16 +2755,40 @@ iq_mam_request(ProfChatWin* win, GDateTime* enddate) cur_del_data->win = win; cur_del_data->enddate = enddate; cur_del_data->startdate = startdate; + cur_del_data->is_reconnect = is_reconnect; late_delivery_windows = g_slist_append(late_delivery_windows, cur_del_data); - log_debug("Save MAM request of %s for later", win->barejid); + log_debug("Save MAM request of %s for later", win_jid); + free(win_jid); return; } - _iq_mam_request(win, startdate, enddate); + free(win_jid); + _iq_mam_request(win, startdate, enddate, is_reconnect); return; } +void +_handle_mam_db_history(ProfWin* win, char* start_time, char* end_time, gboolean flip, gboolean limit_results) +{ + switch (win->type) { + case WIN_CHAT: + { + ProfChatWin* chatwin = (ProfChatWin*)win; + chatwin_db_history(chatwin, start_time, end_time, flip, limit_results); + return; + } + case WIN_MUC: + { + ProfMucWin* mucwin = (ProfMucWin*)win; + mucwin_db_history(mucwin, start_time, end_time, flip, limit_results); + return; + } + default: + return; + } +} + static int _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata) { @@ -2754,9 +2803,7 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata) if (fin) { gboolean is_complete = g_strcmp0(xmpp_stanza_get_attribute(fin, "complete"), "true") == 0; MamRsmUserdata* data = (MamRsmUserdata*)userdata; - ProfWin* window = (ProfWin*)data->win; - - buffer_remove_entry(window->layout->buffer, 0); + ProfWin* window = data->win; auto_char char* start_str = NULL; if (data->start_datestr) { @@ -2771,20 +2818,43 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata) end_str[strlen(end_str) - 3] = '\0'; } - if (is_complete || !data->fetch_next) { - chatwin_db_history(data->win, is_complete ? NULL : start_str, end_str, TRUE); - return 0; + if (data->is_reconnect) { + _handle_mam_db_history(data->win, start_str, end_str, FALSE, FALSE); + if (is_complete) { + char* old_end = end_str; + GDateTime* now = g_date_time_new_now_local(); + end_str = g_date_time_format(now, mam_timestamp_format_string); + end_str[strlen(end_str) - 3] = '\0'; + _handle_mam_db_history(data->win, old_end, end_str, FALSE, FALSE); + return 0; + } + } else { + buffer_remove_entry(window->layout->buffer, 0); + if (is_complete || !data->fetch_next) { + _handle_mam_db_history(data->win, is_complete ? NULL : start_str, end_str, TRUE, TRUE); + return 0; + } + + _handle_mam_db_history(data->win, start_str, end_str, TRUE, TRUE); } - chatwin_db_history(data->win, start_str, end_str, TRUE); xmpp_stanza_t* set = xmpp_stanza_get_child_by_name_and_ns(fin, STANZA_TYPE_SET, STANZA_NS_RSM); if (set) { - win_print_loading_history(window); - - char* firstid = NULL; - xmpp_stanza_t* first = xmpp_stanza_get_child_by_name(set, STANZA_NAME_FIRST); - firstid = xmpp_stanza_get_text(first); + char* firstid = NULL, *lastid = NULL; + if (!data->is_reconnect) { + win_print_loading_history(window); + xmpp_stanza_t* first = xmpp_stanza_get_child_by_name(set, STANZA_NAME_FIRST); + firstid = xmpp_stanza_get_text(first); + } else { + char* win_jid = win_get_tab_identifier(window); + ProfMessage* last_msg = log_database_get_limits_info(win_jid, TRUE, data->end_datestr); + GDateTime* startdate = g_date_time_add_seconds(last_msg->timestamp, 0); + _iq_mam_request(window, startdate, g_date_time_new_from_iso8601(data->end_datestr, NULL), TRUE); + message_free(last_msg); + free(win_jid); + return 0; + } // 4.3.2. send same stanza with set,max stanza xmpp_ctx_t* const ctx = connection_get_ctx(); @@ -2793,8 +2863,12 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata) free(data->end_datestr); data->end_datestr = NULL; } - xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, data->barejid, data->start_datestr, NULL, firstid, NULL); - free(firstid); + + xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, data->barejid, data->start_datestr, NULL, firstid, lastid, data->win->type == WIN_MUC); + + if (!data->is_reconnect) { + free(data->is_reconnect ? lastid : firstid); + } MamRsmUserdata* ndata = malloc(sizeof(*ndata)); *ndata = *data; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index a6372c4a6..9aaf42479 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -76,7 +76,7 @@ typedef struct p_message_handle_t static int _message_handler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata); static void _handle_error(xmpp_stanza_t* const stanza); -static void _handle_groupchat(xmpp_stanza_t* const stanza); +static void _handle_groupchat(xmpp_stanza_t* const stanza, gboolean is_mam, GDateTime* timestamp); static void _handle_muc_user(xmpp_stanza_t* const stanza); static void _handle_muc_private_message(xmpp_stanza_t* const stanza); static void _handle_conference(xmpp_stanza_t* const stanza); @@ -162,7 +162,7 @@ _message_handler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* con _handle_error(stanza); } else if (type && g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0) { // XEP-0045: Multi-User Chat - _handle_groupchat(stanza); + _handle_groupchat(stanza, FALSE, NULL); } else if (type && g_strcmp0(type, STANZA_TYPE_HEADLINE) == 0) { xmpp_stanza_t* event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); @@ -1019,7 +1019,7 @@ _room_config_handler(xmpp_stanza_t* const stanza, void* const userdata) } static void -_handle_groupchat(xmpp_stanza_t* const stanza) +_handle_groupchat(xmpp_stanza_t* const stanza, gboolean is_mam, GDateTime* timestamp) { xmpp_ctx_t* ctx = connection_get_ctx(); @@ -1093,6 +1093,7 @@ _handle_groupchat(xmpp_stanza_t* const stanza) ProfMessage* message = message_init(); jid_ref(from_jid); message->from_jid = from_jid; + message->is_mam = is_mam; message->type = PROF_MSG_TYPE_MUC; const char* id = xmpp_stanza_get_id(stanza); @@ -1156,8 +1157,11 @@ _handle_groupchat(xmpp_stanza_t* const stanza) message->timestamp = NULL; } - // we want to display the oldest delay - message->timestamp = stanza_get_oldest_delay(stanza); + message->timestamp = timestamp; + if (!timestamp) { + // we want to display the oldest delay + message->timestamp = stanza_get_oldest_delay(stanza); + } // now this has nothing to do with MUC history // it's just setting the time to the received time so upon displaying we can use this time @@ -1375,7 +1379,7 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c } // private message from chat room use full jid (room/nick) - if (muc_active(jid->barejid)) { + if (muc_active(jid->barejid) && !is_mam) { _handle_muc_private_message(stanza); return; } @@ -1557,9 +1561,14 @@ _handle_mam(xmpp_stanza_t* const stanza) GDateTime* timestamp = stanza_get_delay_from(forwarded, NULL); xmpp_stanza_t* message_stanza = xmpp_stanza_get_child_by_ns(forwarded, "jabber:client"); + const char* type = xmpp_stanza_get_type(message_stanza); - _handle_chat(message_stanza, TRUE, FALSE, result_id, timestamp); + if (type && g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0) { + _handle_groupchat(message_stanza, TRUE, timestamp); + return TRUE; + } + _handle_chat(message_stanza, TRUE, FALSE, result_id, timestamp); return TRUE; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index bbc15c13b..43440f1cf 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -34,6 +34,7 @@ */ #include "config.h" +#include "config/preferences.h" #ifdef HAVE_GIT_VERSION #include "gitversion.h" @@ -563,6 +564,15 @@ stanza_create_room_join_presence(xmpp_ctx_t* const ctx, xmpp_stanza_release(pass); } + // Disable muc history + if (prefs_get_boolean(PREF_MAM)) { + xmpp_stanza_t* history = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(history, STANZA_NAME_HISTORY); + xmpp_stanza_set_attribute(history, "maxchars", "0"); + xmpp_stanza_add_child(x, history); + xmpp_stanza_release(history); + } + xmpp_stanza_add_child(presence, x); xmpp_stanza_release(x); @@ -2771,7 +2781,7 @@ _text_stanza(xmpp_ctx_t* ctx, const char* name, const char* text) } xmpp_stanza_t* -stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const enddate, const char* const firstid, const char* const lastid) +stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const enddate, const char* const firstid, const char* const lastid, const gboolean is_muc) { char* id = connection_create_stanza_id(); xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); @@ -2797,14 +2807,19 @@ stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const s xmpp_stanza_add_child_ex(field_form_type, value_mam, 0); - // field 'with' - xmpp_stanza_t* field_with = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(field_with, STANZA_NAME_FIELD); - xmpp_stanza_set_attribute(field_with, STANZA_ATTR_VAR, "with"); + xmpp_stanza_t* field_with; + if (is_muc) { + xmpp_stanza_set_to(iq, jid); + } else { + // field 'with' + field_with = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(field_with, STANZA_NAME_FIELD); + xmpp_stanza_set_attribute(field_with, STANZA_ATTR_VAR, "with"); - xmpp_stanza_t* value_with = _text_stanza(ctx, STANZA_NAME_VALUE, jid); + xmpp_stanza_t* value_with = _text_stanza(ctx, STANZA_NAME_VALUE, jid); - xmpp_stanza_add_child_ex(field_with, value_with, 0); + xmpp_stanza_add_child_ex(field_with, value_with, 0); + } // 4.3.2 set/rsm xmpp_stanza_t* set = xmpp_stanza_new(ctx); @@ -2828,7 +2843,10 @@ stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const s xmpp_stanza_add_child_ex(iq, query, 0); xmpp_stanza_add_child_ex(query, x, 0); xmpp_stanza_add_child_ex(x, field_form_type, 0); - xmpp_stanza_add_child_ex(x, field_with, 0); + + if (!is_muc) { + xmpp_stanza_add_child_ex(x, field_with, 0); + } // field 'start' if (startdate) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 636fafb09..75b4edb41 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -56,6 +56,7 @@ #define STANZA_NAME_PRESENCE "presence" #define STANZA_NAME_PRIORITY "priority" #define STANZA_NAME_X "x" +#define STANZA_NAME_HISTORY "history" // XEP-0373: OpenPGP for XMPP #define STANZA_NAME_OPENPGP "openpgp" #define STANZA_NAME_PUPKEY "pubkey" @@ -420,7 +421,7 @@ xmpp_stanza_t* stanza_create_avatar_data_publish_iq(xmpp_ctx_t* ctx, const char* xmpp_stanza_t* stanza_create_avatar_metadata_publish_iq(xmpp_ctx_t* ctx, const char* img_data, gsize len, int height, int width); xmpp_stanza_t* stanza_disable_avatar_publish_iq(xmpp_ctx_t* ctx); xmpp_stanza_t* stanza_create_vcard_request_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const stanza_id); -xmpp_stanza_t* stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const enddate, const char* const firstid, const char* const lastid); +xmpp_stanza_t* stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const enddate, const char* const firstid, const char* const lastid, const gboolean is_muc); xmpp_stanza_t* stanza_change_password(xmpp_ctx_t* ctx, const char* const user, const char* const password); xmpp_stanza_t* stanza_register_new_account(xmpp_ctx_t* ctx, const char* const user, const char* const password); xmpp_stanza_t* stanza_request_voice(xmpp_ctx_t* ctx, const char* const room); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 2babe536a..0da7d8051 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -259,13 +259,14 @@ void iq_room_affiliation_set(const char* const room, const char* const jid, char void iq_room_kick_occupant(const char* const room, const char* const nick, const char* const reason); void iq_room_role_set(const char* const room, const char* const nick, char* role, const char* const reason); void iq_room_role_list(const char* const room, char* role); +void iq_disco_items_on_disconnect(void); void iq_autoping_timer_cancel(void); void iq_autoping_check(void); void iq_http_upload_request(HTTPUpload* upload); void iq_command_list(const char* const target); void iq_command_exec(const char* const target, const char* const command); -void iq_mam_request(ProfChatWin* win, GDateTime* enddate); -void iq_mam_request_older(ProfChatWin* win); +void iq_mam_request(ProfWin* win, GDateTime* enddate, gboolean is_reconnect); +void iq_mam_request_older(ProfWin* win); void iq_register_change_password(const char* const user, const char* const password); void iq_muc_register_nick(const char* const roomjid); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 608f5003c..4fec3527c 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -363,11 +363,11 @@ mucwin_roster(ProfMucWin* mucwin, GList* occupants, const char* const presence) { } void -mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message) +mucwin_history(ProfMucWin* mucwin, const ProfMessage* const message, gboolean flip) { } void -mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection) +mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList* mentions, GList* triggers, gboolean filter_reflection, gboolean flip) { } void diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index ffa7565d9..6153e4749 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -404,6 +404,9 @@ void iq_last_activity_request(gchar* jid) { } +iq_disco_items_on_disconnect(void) +{ +} void iq_autoping_timer_cancel(void) { @@ -435,7 +438,7 @@ iq_muc_register_nick(const char* const roomjid) } void -iq_mam_request(ProfChatWin* win, GDateTime* enddate) +iq_mam_request(ProfWin* win, GDateTime* enddate, gboolean is_reconnect) { }