diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 5b61da6a..386e8939 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -113,6 +113,8 @@ static char* _status_autocomplete(ProfWin *window, const char *const input, gboo static char* _logging_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _color_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _avatar_autocomplete(ProfWin *window, const char *const input, gboolean previous); +static char* _correction_autocomplete(ProfWin *window, const char *const input, gboolean previous); +static char* _correct_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _script_autocomplete_func(const char *const prefix, gboolean previous, void *context); @@ -234,6 +236,7 @@ static Autocomplete status_ac; static Autocomplete status_state_ac; static Autocomplete logging_ac; static Autocomplete color_ac; +static Autocomplete correction_ac; void cmd_ac_init(void) @@ -928,6 +931,11 @@ cmd_ac_init(void) autocomplete_add(color_ac, "off"); autocomplete_add(color_ac, "redgreen"); autocomplete_add(color_ac, "blue"); + + correction_ac = autocomplete_new(); + autocomplete_add(correction_ac, "on"); + autocomplete_add(correction_ac, "off"); + autocomplete_add(correction_ac, "char"); } void @@ -1233,6 +1241,7 @@ cmd_ac_reset(ProfWin *window) autocomplete_reset(status_state_ac); autocomplete_reset(logging_ac); autocomplete_reset(color_ac); + autocomplete_reset(correction_ac); autocomplete_reset(script_ac); if (script_show_ac) { @@ -1380,6 +1389,7 @@ cmd_ac_uninit(void) autocomplete_free(status_state_ac); autocomplete_free(logging_ac); autocomplete_free(color_ac); + autocomplete_free(correction_ac); } static void @@ -1630,6 +1640,8 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ g_hash_table_insert(ac_funcs, "/logging", _logging_autocomplete); g_hash_table_insert(ac_funcs, "/color", _color_autocomplete); g_hash_table_insert(ac_funcs, "/avatar", _avatar_autocomplete); + g_hash_table_insert(ac_funcs, "/correction", _correction_autocomplete); + g_hash_table_insert(ac_funcs, "/correct", _correct_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3713,3 +3725,50 @@ _avatar_autocomplete(ProfWin *window, const char *const input, gboolean previous return NULL; } + +static char* +_correction_autocomplete(ProfWin *window, const char *const input, gboolean previous) +{ + char *result = NULL; + + result = autocomplete_param_with_ac(input, "/correction", correction_ac, TRUE, previous); + if (result) { + return result; + } + + return NULL; +} + +static char* +_correct_autocomplete(ProfWin *window, const char *const input, gboolean previous) +{ + char *last_message = NULL; + switch(window->type) { + case WIN_CHAT: + { + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + last_message = chatwin->last_message; + break; + } + case WIN_MUC: + { + ProfMucWin *mucwin = (ProfMucWin*)window; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + last_message = mucwin->last_message; + } + default: + break; + } + + if (last_message == NULL) { + return NULL; + } + + GString *result_str = g_string_new("/correct "); + g_string_append(result_str, last_message); + char *result = result_str->str; + g_string_free(result_str, FALSE); + + return result; +} diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index b583a06b..cc8f0a7a 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2361,6 +2361,41 @@ static struct cmd_t command_defs[] = { "on|off", ""}) CMD_NOEXAMPLES }, + + { "/correction", + parse_args, 1, 2, &cons_correction_setting, + CMD_NOSUBFUNCS + CMD_MAINFUNC(cmd_correction) + CMD_TAGS( + CMD_TAG_UI, + CMD_TAG_CHAT, + CMD_TAG_GROUPCHAT) + CMD_SYN( + "/correction |", + "/correction char ") + CMD_DESC( + "Settings regarding Last Message Correction (XEP-0308). Caution: We do not yet check the 'from' field. So it could happen that someone else is overwriting the actual message.") + CMD_ARGS( + { "on|off", "Enable/Disable support for last message correction."}, + { "char", "Set character that will prefix corrected messages. Default: +"}) + CMD_NOEXAMPLES + }, + + { "/correct", + parse_args, 1, -1, NULL, + CMD_NOSUBFUNCS + CMD_MAINFUNC(cmd_correct) + CMD_TAGS( + CMD_TAG_CHAT, + CMD_TAG_GROUPCHAT) + CMD_SYN( + "/correct ") + CMD_DESC( + "Correct and resend the last message (XEP-0308).") + CMD_ARGS( + { "message", "The corrected message."}) + CMD_NOEXAMPLES + }, }; static GHashTable *search_index; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 930004b3..40f5d8f4 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7529,7 +7529,7 @@ cmd_otr_start(ProfWin *window, const char *const command, gchar **args) if (!otr_is_secure(barejid)) { char *otr_query_message = otr_start_query(); - char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE, NULL); free(id); return TRUE; } @@ -7562,7 +7562,8 @@ cmd_otr_start(ProfWin *window, const char *const command, gchar **args) } char *otr_query_message = otr_start_query(); - char *id = message_send_chat_otr(chatwin->barejid, otr_query_message, FALSE); + char *id = message_send_chat_otr(chatwin->barejid, otr_query_message, FALSE, NULL); + free(id); return TRUE; } @@ -8651,3 +8652,77 @@ cmd_os(ProfWin *window, const char *const command, gchar **args) return TRUE; } + +gboolean +cmd_correction(ProfWin *window, const char *const command, gchar **args) +{ + // enable/disable + if (g_strcmp0(args[0], "on") == 0) { + _cmd_set_boolean_preference(args[0], command, "Last Message Correction", PREF_CORRECTION_ALLOW); + caps_add_feature(XMPP_FEATURE_LAST_MESSAGE_CORRECTION); + return TRUE; + } else if (g_strcmp0(args[0], "off") == 0) { + _cmd_set_boolean_preference(args[0], command, "Last Message Correction", PREF_CORRECTION_ALLOW); + caps_remove_feature(XMPP_FEATURE_LAST_MESSAGE_CORRECTION); + return TRUE; + } + + // char + if (g_strcmp0(args[0], "char") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else if (strlen(args[1]) != 1) { + cons_bad_cmd_usage(command); + } else { + prefs_set_correction_char(args[1][0]); + cons_show("LMC char set to %c.", args[1][0]); + } + } + + return TRUE; +} + +gboolean +cmd_correct(ProfWin *window, const char *const command, gchar **args) +{ + jabber_conn_status_t conn_status = connection_get_status(); + if (conn_status != JABBER_CONNECTED) { + cons_show("You are currently not connected."); + return TRUE; + } + + if (window->type == WIN_CHAT) { + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + + if (chatwin->last_msg_id == NULL || chatwin->last_message == NULL) { + win_println(window, THEME_DEFAULT, '!', "No last message to correct."); + return TRUE; + } + + // send message again, with replace flag + gchar *message = g_strjoinv(" ", args); + cl_ev_send_msg_correct(chatwin, message, FALSE, TRUE); + + free(message); + return TRUE; + } else if (window->type == WIN_MUC) { + ProfMucWin *mucwin = (ProfMucWin*)window; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + + if (mucwin->last_msg_id == NULL || mucwin->last_message == NULL) { + win_println(window, THEME_DEFAULT, '!', "No last message to correct."); + return TRUE; + } + + // send message again, with replace flag + gchar *message = g_strjoinv(" ", args); + cl_ev_send_muc_msg_corrected(mucwin, message, FALSE, TRUE); + + free(message); + return TRUE; + } + + win_println(window, THEME_DEFAULT, '!', "Command /correct only valid in regular chat windows."); + return TRUE; +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index a743951f..f283c910 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -54,7 +54,7 @@ typedef struct cmd_help_t { * cmd - The command string including leading '/' * parser - The function used to parse arguments * min_args - Minimum number of arguments - * max_args - Maximum number of arguments + * max_args - Maximum number of arguments, -1 for infinite * setting_func - Function to display current settings to the console * sub_funcs - Optional list of functions mapped to the first argument * func - Main function to call when no arguments, or sub_funcs not implemented @@ -227,4 +227,6 @@ gboolean cmd_paste(ProfWin *window, const char *const command, gchar **args); gboolean cmd_color(ProfWin *window, const char *const command, gchar **args); gboolean cmd_avatar(ProfWin *window, const char *const command, gchar **args); gboolean cmd_os(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_correction(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_correct(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/config/preferences.c b/src/config/preferences.c index 6cc2bb56..dea6a529 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1201,6 +1201,32 @@ prefs_set_roster_presence_indent(gint value) g_key_file_set_integer(prefs, PREF_GROUP_UI, "roster.presence.indent", value); } +char +prefs_get_correction_char(void) +{ + char result = '+'; + + char *resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "correction.char", NULL); + if (!resultstr) { + result = '+'; + } else { + result = resultstr[0]; + } + free(resultstr); + + return result; +} + +void +prefs_set_correction_char(char ch) +{ + char str[2]; + str[0] = ch; + str[1] = '\0'; + + g_key_file_set_string(prefs, PREF_GROUP_UI, "correction.char", str); +} + gboolean prefs_add_room_notify_trigger(const char * const text) { @@ -1760,6 +1786,7 @@ _get_group(preference_t pref) case PREF_RECEIPTS_REQUEST: case PREF_REVEAL_OS: case PREF_TLS_CERTPATH: + case PREF_CORRECTION_ALLOW: return PREF_GROUP_CONNECTION; case PREF_OTR_LOG: case PREF_OTR_POLICY: @@ -2010,6 +2037,8 @@ _get_key(preference_t pref) return "log"; case PREF_OMEMO_POLICY: return "policy"; + case PREF_CORRECTION_ALLOW: + return "correction.allow"; default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index e11eec46..4c903272 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -161,6 +161,7 @@ typedef enum { PREF_OMEMO_LOG, PREF_OMEMO_POLICY, PREF_OCCUPANTS_WRAP, + PREF_CORRECTION_ALLOW, } preference_t; typedef struct prof_alias_t { @@ -268,6 +269,9 @@ void prefs_set_roster_presence_indent(gint value); gint prefs_get_occupants_indent(void); void prefs_set_occupants_indent(gint value); +char prefs_get_correction_char(void); +void prefs_set_correction_char(char ch); + void prefs_add_login(const char *jid); void prefs_set_tray_timer(gint value); diff --git a/src/config/theme.c b/src/config/theme.c index f081bd21..f7842bab 100644 --- a/src/config/theme.c +++ b/src/config/theme.c @@ -483,6 +483,14 @@ _load_preferences(void) } } + if (g_key_file_has_key(theme, "ui", "correction.char", NULL)) { + gchar *ch = g_key_file_get_string(theme, "ui", "correction.char", NULL); + if (ch && strlen(ch) > 0) { + prefs_set_omemo_char(ch[0]); + g_free(ch); + } + } + // load window positions if (g_key_file_has_key(theme, "ui", "titlebar.position", NULL) && g_key_file_has_key(theme, "ui", "mainwin.position", NULL) && diff --git a/src/event/client_events.c b/src/event/client_events.c index 6716b7e2..e0ea85d7 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -115,45 +115,37 @@ cl_ev_presence_send(const resource_presence_t presence_type, const int idle_secs } void -cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url) +cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *const oob_url, gboolean correct_last_msg) { chat_state_active(chatwin->state); - gboolean request_receipt = FALSE; - if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) { - char *session_jid = chat_session_get_jid(chatwin->barejid); - if (session_jid) { - Jid *session_jidp = jid_create(session_jid); - if (session_jidp && session_jidp->resourcepart) { - if (caps_jid_has_feature(session_jid, XMPP_FEATURE_RECEIPTS)) { - request_receipt = TRUE; - } - } - jid_destroy(session_jidp); - free(session_jid); - } - } + gboolean request_receipt = prefs_get_boolean(PREF_RECEIPTS_REQUEST); char *plugin_msg = plugins_pre_chat_message_send(chatwin->barejid, msg); if (plugin_msg == NULL) { return; } + char *replace_id = NULL; + if (correct_last_msg) { + replace_id = chatwin->last_msg_id; + } + // OTR suported, PGP supported, OMEMO unsupported #ifdef HAVE_LIBOTR #ifdef HAVE_LIBGPGME #ifndef HAVE_OMEMO if (chatwin->pgp_send) { - char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id); free(id); } else { - gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id); if (!handled) { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } } @@ -169,9 +161,9 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBOTR #ifndef HAVE_LIBGPGME #ifndef HAVE_OMEMO - gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id); if (!handled) { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); free(id); @@ -189,14 +181,14 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifndef HAVE_OMEMO if (chatwin->pgp_send) { - char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id); free(id); } else { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } @@ -212,14 +204,14 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id); chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id); free(id); } else { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } @@ -235,16 +227,16 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id); chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id); free(id); } else { - gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id); if (!handled) { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } } @@ -261,19 +253,19 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id); chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id); free(id); } else if (chatwin->pgp_send) { - char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id); free(id); } else { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } @@ -289,21 +281,21 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id); chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id); free(id); } else if (chatwin->pgp_send) { - char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id); free(id); } else { - gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id); if (!handled) { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); } } @@ -319,9 +311,9 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBOTR #ifndef HAVE_LIBGPGME #ifndef HAVE_OMEMO - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, plugin_msg, NULL); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); free(id); plugins_post_chat_message_send(chatwin->barejid, plugin_msg); @@ -333,23 +325,34 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo } void -cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const oob_url) +cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url) +{ + cl_ev_send_msg_correct(chatwin, msg, oob_url, FALSE); +} + +void +cl_ev_send_muc_msg_corrected(ProfMucWin *mucwin, const char *const msg, const char *const oob_url, gboolean correct_last_msg) { char *plugin_msg = plugins_pre_room_message_send(mucwin->roomjid, msg); if (plugin_msg == NULL) { return; } + char *replace_id = NULL; + if (correct_last_msg) { + replace_id = mucwin->last_msg_id; + } + #ifdef HAVE_OMEMO if (mucwin->is_omemo) { - char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); + char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE, replace_id); groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_OMEMO); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, replace_id); free(id); } else { - char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url, replace_id); groupchat_log_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, replace_id); free(id); } @@ -359,9 +362,9 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const #endif #ifndef HAVE_OMEMO - char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url, replace_id); groupchat_log_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, replace_id); free(id); plugins_post_room_message_send(mucwin->roomjid, plugin_msg); @@ -370,6 +373,12 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const #endif } +void +cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const oob_url) +{ + cl_ev_send_muc_msg_corrected(mucwin, msg, oob_url, FALSE); +} + void cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg, const char *const oob_url) { diff --git a/src/event/client_events.h b/src/event/client_events.h index 82150f2e..87276331 100644 --- a/src/event/client_events.h +++ b/src/event/client_events.h @@ -45,7 +45,9 @@ void cl_ev_disconnect(void); void cl_ev_presence_send(const resource_presence_t presence_type, const int idle_secs); +void cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *const oob_url, gboolean correct_last_msg); void cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url); +void cl_ev_send_muc_msg_corrected(ProfMucWin *mucwin, const char *const msg, const char *const oob_url, gboolean correct_last_msg); void cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const oob_url); void cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg, const char *const oob_url); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 0c981db1..e44a4d71 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -671,7 +671,7 @@ out: } char * -omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc) +omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc, const char *const replace_id) { char *id = NULL; int res; @@ -809,11 +809,11 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ if (muc) { ProfMucWin *mucwin = (ProfMucWin *)win; assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE); + id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE, replace_id); } else { ProfChatWin *chatwin = (ProfChatWin *)win; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); - id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE); + id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE, replace_id); } out: diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index dfd23fd8..740654a2 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -92,5 +92,5 @@ void omemo_start_muc_sessions(const char *const roomjid); void omemo_start_device_session(const char *const jid, uint32_t device_id, GList *prekeys, uint32_t signed_prekey_id, const unsigned char *const signed_prekey, size_t signed_prekey_len, const unsigned char *const signature, size_t signature_len, const unsigned char *const identity_key, size_t identity_key_len); gboolean omemo_loaded(void); -char * omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc); +char * omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc, const char *const replace_id); char * omemo_on_message_recv(const char *const from, uint32_t sid, const unsigned char *const iv, size_t iv_len, GList *keys, const unsigned char *const payload, size_t payload_len, gboolean muc, gboolean *trusted); diff --git a/src/otr/otr.c b/src/otr/otr.c index 40684857..5ce34758 100644 --- a/src/otr/otr.c +++ b/src/otr/otr.c @@ -118,7 +118,7 @@ static void cb_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { - char *id = message_send_chat_otr(recipient, message, FALSE); + char *id = message_send_chat_otr(recipient, message, FALSE, NULL); free(id); } @@ -315,7 +315,7 @@ otr_on_message_recv(const char *const barejid, const char *const resource, const memmove(whitespace_base, whitespace_base+tag_length, tag_length); char *otr_query_message = otr_start_query(); cons_show("OTR Whitespace pattern detected. Attempting to start OTR session..."); - char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE, NULL); free(id); } } @@ -329,7 +329,7 @@ otr_on_message_recv(const char *const barejid, const char *const resource, const if (policy == PROF_OTRPOLICY_ALWAYS && *decrypted == FALSE && !whitespace_base) { char *otr_query_message = otr_start_query(); cons_show("Attempting to start OTR session..."); - char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE, NULL); free(id); } @@ -337,7 +337,7 @@ otr_on_message_recv(const char *const barejid, const char *const resource, const } gboolean -otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) +otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt, const char *const replace_id) { char *id = NULL; prof_otrpolicy_t policy = otr_get_policy(chatwin->barejid); @@ -346,9 +346,9 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean re if (otr_is_secure(chatwin->barejid)) { char *encrypted = otr_encrypt_message(chatwin->barejid, message); if (encrypted) { - id = message_send_chat_otr(chatwin->barejid, encrypted, request_receipt); + id = message_send_chat_otr(chatwin->barejid, encrypted, request_receipt, replace_id); chat_log_otr_msg_out(chatwin->barejid, message, NULL); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_OTR, request_receipt); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_OTR, request_receipt, replace_id); otr_free_message(encrypted); free(id); return TRUE; @@ -367,8 +367,8 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean re // tag and send for policy opportunistic if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) { char *otr_tagged_msg = otr_tag_message(message); - id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg, request_receipt); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_PLAIN, request_receipt); + id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg, request_receipt, replace_id); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, message, NULL); free(otr_tagged_msg); free(id); diff --git a/src/otr/otr.h b/src/otr/otr.h index 5b9a09d3..58b6decf 100644 --- a/src/otr/otr.h +++ b/src/otr/otr.h @@ -73,7 +73,7 @@ void otr_poll(void); void otr_on_connect(ProfAccount *account); char* otr_on_message_recv(const char *const barejid, const char *const resource, const char *const message, gboolean *decrypted); -gboolean otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); +gboolean otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt, const char *const replace_id); void otr_keygen(ProfAccount *account); diff --git a/src/tools/parser.c b/src/tools/parser.c index aa739330..fb21571c 100644 --- a/src/tools/parser.c +++ b/src/tools/parser.c @@ -48,7 +48,7 @@ * * inp - The line of input * min - The minimum allowed number of arguments - * max - The maximum allowed number of arguments + * max - The maximum allowed number of arguments, -1 for infinite * * Returns - An NULL terminated array of strings representing the arguments * of the command, or NULL if the validation fails. @@ -135,7 +135,7 @@ parse_args(const char *const inp, int min, int max, gboolean *result) int num = g_slist_length(tokens) - 1; // if num args not valid return NULL - if ((num < min) || (num > max)) { + if ((num < min) || ((max != -1) && (num > max))) { g_slist_free_full(tokens, free); g_free(copy); *result = FALSE; diff --git a/src/ui/buffer.c b/src/ui/buffer.c index 916aba1a..86f87563 100644 --- a/src/ui/buffer.c +++ b/src/ui/buffer.c @@ -3,6 +3,7 @@ * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 - 2020 Michael Vetter * * This file is part of Profanity. * @@ -81,7 +82,7 @@ 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 from, const char *const message, DeliveryReceipt *receipt, const char *const id) + int flags, theme_item_t theme_item, const char *const display_from, const char *const message, DeliveryReceipt *receipt, const char *const id) { ProfBuffEntry *e = malloc(sizeof(struct prof_buff_entry_t)); e->show_char = show_char; @@ -89,7 +90,7 @@ buffer_append(ProfBuff buffer, const char show_char, int pad_indent, GDateTime * e->flags = flags; e->theme_item = theme_item; e->time = g_date_time_ref(time); - e->from = from ? strdup(from) : NULL; + e->display_from = display_from ? strdup(display_from) : NULL; e->message = strdup(message); e->receipt = receipt; if (id) { @@ -163,7 +164,7 @@ static void _free_entry(ProfBuffEntry *entry) { free(entry->message); - free(entry->from); + free(entry->display_from); free(entry->id); free(entry->receipt); g_date_time_unref(entry->time); diff --git a/src/ui/buffer.h b/src/ui/buffer.h index 10151d17..813ac38e 100644 --- a/src/ui/buffer.h +++ b/src/ui/buffer.h @@ -3,6 +3,7 @@ * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 - 2020 Michael Vetter * * This file is part of Profanity. * @@ -51,7 +52,9 @@ typedef struct prof_buff_entry_t { GDateTime *time; int flags; theme_item_t theme_item; - char *from; + // from as it is displayed + // might be nick, jid.. + char *display_from; char *message; DeliveryReceipt *receipt; // message id, in case we have it @@ -63,7 +66,7 @@ typedef struct prof_buff_t *ProfBuff; ProfBuff buffer_create(); 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 from, const char *const message, DeliveryReceipt *receipt, const char *const id); + int flags, theme_item_t theme_item, const char *const display_from, const char *const message, DeliveryReceipt *receipt, const char *const id); void buffer_remove_entry_by_id(ProfBuff buffer, const char *const id); int buffer_size(ProfBuff buffer); ProfBuffEntry* buffer_get_entry(ProfBuff buffer, int entry); diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index 8faf4934..4b0fd889 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -3,6 +3,7 @@ * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 - 2020 Michael Vetter * * This file is part of Profanity. * @@ -56,6 +57,7 @@ #endif static void _chatwin_history(ProfChatWin *chatwin, const char *const contact); +static void _chatwin_set_last_message(ProfChatWin *chatwin, const char *const id, const char *const message); ProfChatWin* chatwin_new(const char *const barejid) @@ -308,7 +310,7 @@ chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_cr void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, - gboolean request_receipt) + gboolean request_receipt, const char *const replace_id) { assert(chatwin != NULL); @@ -324,9 +326,14 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, } if (request_receipt && id) { - win_print_with_receipt((ProfWin*)chatwin, enc_char, "me", message, id); + win_print_outgoing_with_receipt((ProfWin*)chatwin, enc_char, "me", message, id, replace_id); } else { - win_print_outgoing((ProfWin*)chatwin, enc_char, "%s", message); + win_print_outgoing((ProfWin*)chatwin, enc_char, id, replace_id, "%s", message); + } + + // save last id and message for LMC + if (id) { + _chatwin_set_last_message(chatwin, id, message); } } @@ -344,7 +351,7 @@ chatwin_outgoing_carbon(ProfChatWin *chatwin, ProfMessage *message) ProfWin *window = (ProfWin*)chatwin; - win_print_outgoing(window, enc_char, "%s", message->plain); + win_print_outgoing(window, enc_char, message->id, message->replace_id, "%s", message->plain); int num = wins_get_num(window); status_bar_active(num, WIN_CHAT, chatwin->barejid); } @@ -496,3 +503,13 @@ _chatwin_history(ProfChatWin *chatwin, const char *const contact) g_slist_free_full(history, free); } } + +static void +_chatwin_set_last_message(ProfChatWin *chatwin, const char *const id, const char *const message) +{ + free(chatwin->last_message); + chatwin->last_message = strdup(message); + + free(chatwin->last_msg_id); + chatwin->last_msg_id = strdup(id); +} diff --git a/src/ui/console.c b/src/ui/console.c index 1a716a7a..f7fa448d 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2021,6 +2021,19 @@ cons_os_setting(void) } } +void +cons_correction_setting(void) +{ + if (prefs_get_boolean(PREF_CORRECTION_ALLOW)) { + cons_show("Last Message Correction (XEP-0308) (/correction) : ON"); + } else { + cons_show("Last Message Correction (XEP-0308) (/correction) : OFF"); + } + + char cc = prefs_get_correction_char(); + cons_show("LMC indication char (/correction char) : %c", cc); +} + void cons_show_connection_prefs(void) { diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c index 0179c22b..28c3beea 100644 --- a/src/ui/mucwin.c +++ b/src/ui/mucwin.c @@ -50,6 +50,8 @@ #include "omemo/omemo.h" #endif +static void _mucwin_set_last_message(ProfMucWin *mucwin, const char *const id, const char *const message); + ProfMucWin* mucwin_new(const char *const barejid) { @@ -501,7 +503,7 @@ _mucwin_print_triggers(ProfWin *window, const char *const message, GList *trigge } void -mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) +mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode, const char *const replace_id) { assert(mucwin != NULL); @@ -519,7 +521,12 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *c ch = prefs_get_omemo_char(); } - win_println_me_message(window, ch, mynick, "%s", message); + win_print_outgoing_muc_msg(window, ch, mynick, id, replace_id, "%s", message); + + // save last id and message for LMC + if (id) { + _mucwin_set_last_message(mucwin, id, message); + } } void @@ -559,7 +566,7 @@ mucwin_incoming_msg(ProfMucWin *mucwin, ProfMessage *message, GSList *mentions, win_print_them(window, THEME_ROOMTRIGGER, ch, flags, message->jid->resourcepart); _mucwin_print_triggers(window, message->plain, triggers); } else { - win_println_them_message(window, ch, flags, message->jid->resourcepart, "%s", message->plain); + win_println_incoming_muc_msg(window, ch, flags, message->jid->resourcepart, message->id, message->replace_id, "%s", message->plain); } } @@ -951,3 +958,13 @@ mucwin_unset_message_char(ProfMucWin *mucwin) mucwin->message_char = NULL; } } + +static void +_mucwin_set_last_message(ProfMucWin *mucwin, const char *const id, const char *const message) +{ + free(mucwin->last_message); + mucwin->last_message = strdup(message); + + free(mucwin->last_msg_id); + mucwin->last_msg_id = strdup(id); +} diff --git a/src/ui/privwin.c b/src/ui/privwin.c index 4befc325..8fc1bcc7 100644 --- a/src/ui/privwin.c +++ b/src/ui/privwin.c @@ -95,7 +95,7 @@ privwin_outgoing_msg(ProfPrivateWin *privwin, const char *const message) { assert(privwin != NULL); - win_print_outgoing((ProfWin*)privwin, '-', "%s", message); + win_print_outgoing((ProfWin*)privwin, '-', NULL, NULL ,"%s", message); } void diff --git a/src/ui/ui.h b/src/ui/ui.h index 4783131e..77bdc1d5 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -124,8 +124,7 @@ ProfChatWin* chatwin_new(const char *const barejid); void chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_created); void chatwin_receipt_received(ProfChatWin *chatwin, const char *const id); void chatwin_recipient_gone(ProfChatWin *chatwin); -void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, - gboolean request_receipt); +void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, gboolean request_receipt, const char *const replace_id); void chatwin_outgoing_carbon(ProfChatWin *chatwin, ProfMessage *message); void chatwin_contact_online(ProfChatWin *chatwin, Resource *resource, GDateTime *last_activity); void chatwin_contact_offline(ProfChatWin *chatwin, char *resource, char *status); @@ -159,7 +158,7 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char 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 char *const nick, GDateTime *timestamp, const char *const message); -void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode); +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, ProfMessage *message, GSList *mentions, GList *triggers); void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject); void mucwin_requires_config(ProfMucWin *mucwin); @@ -319,6 +318,7 @@ void cons_statusbar_setting(void); void cons_winpos_setting(void); void cons_color_setting(void); void cons_os_setting(void); +void cons_correction_setting(void); void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity); void cons_show_contact_offline(PContact contact, char *resource, char *status); void cons_theme_properties(void); diff --git a/src/ui/win_types.h b/src/ui/win_types.h index 68ed719e..81944bc0 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -160,6 +160,9 @@ typedef struct prof_chat_win_t { char *enctext; char *incoming_char; char *outgoing_char; + // For LMC + char *last_message; + char *last_msg_id; } ProfChatWin; typedef struct prof_muc_win_t { @@ -175,6 +178,9 @@ typedef struct prof_muc_win_t { char *enctext; char *message_char; GDateTime *last_msg_timestamp; + // For LMC + char *last_message; + char *last_msg_id; } ProfMucWin; typedef struct prof_conf_win_t ProfConfWin; diff --git a/src/ui/window.c b/src/ui/window.c index c7bb359a..a1c71241 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -3,6 +3,7 @@ * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 - 2020 Michael Vetter * * This file is part of Profanity. * @@ -49,6 +50,7 @@ #include #endif +#include "log.h" #include "config/theme.h" #include "config/preferences.h" #include "ui/ui.h" @@ -62,9 +64,8 @@ #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 from, const char *const message, ...); -static void _win_print(ProfWin *window, const char show_char, int pad_indent, GDateTime *time, +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 from, const char *const message_id, 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); @@ -151,6 +152,8 @@ win_create_chat(const char *const barejid) new_win->enctext = NULL; new_win->incoming_char = NULL; new_win->outgoing_char = NULL; + new_win->last_message = NULL; + new_win->last_msg_id = NULL; new_win->memcheck = PROFCHATWIN_MEMCHECK; @@ -200,6 +203,8 @@ win_create_muc(const char *const roomjid) new_win->enctext = NULL; new_win->message_char = NULL; new_win->is_omemo = FALSE; + new_win->last_message = NULL; + new_win->last_msg_id = NULL; new_win->memcheck = PROFMUCWIN_MEMCHECK; @@ -487,6 +492,8 @@ win_free(ProfWin* window) free(chatwin->enctext); free(chatwin->incoming_char); free(chatwin->outgoing_char); + free(chatwin->last_message); + free(chatwin->last_msg_id); chat_state_free(chatwin->state); break; } @@ -1055,8 +1062,43 @@ win_show_status_string(ProfWin *window, const char *const from, win_appendln(window, presence_colour, ""); } +static void +_win_correct(ProfWin *window, const char *const message, const char *const id, const char *const replace_id) +{ + ProfBuffEntry *entry = buffer_get_entry_by_id(window->layout->buffer, replace_id); + if (!entry) { + log_debug("Replace ID %s could not be found in buffer. Message: %s", replace_id, message); + return; + } + + /*TODO: set date? + if (entry->date) { + if (entry->date->timestamp) { + g_date_time_unref(entry->date->timestamp); + } + free(entry->date); + } + + entry->date = buffer_date_new_now(); + */ + + entry->show_char = prefs_get_correction_char(); + + if (entry->message) { + free(entry->message); + } + entry->message = strdup(message); + + if (entry->id) { + free(entry->id); + } + entry->id = strdup(id); + + win_redraw(window); +} + void -win_print_incoming(ProfWin *window, const char *const from, ProfMessage *message) +win_print_incoming(ProfWin *window, const char *const display_name_from, ProfMessage *message) { char enc_char = '-'; int flags = NO_ME; @@ -1079,11 +1121,16 @@ win_print_incoming(ProfWin *window, const char *const from, ProfMessage *message } else if (message->enc == PROF_MSG_ENC_OMEMO) { enc_char = prefs_get_omemo_char(); } - _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, from, "%s", message->plain); + + if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) { + _win_correct(window, message->plain, message->id, message->replace_id); + } else { + _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->id, "%s", message->plain); + } break; } case WIN_PRIVATE: - _win_printf(window, '-', 0, message->timestamp, flags, THEME_TEXT_THEM, from, "%s", message->plain); + _win_printf(window, '-', 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->id, "%s", message->plain); break; default: assert(FALSE); @@ -1094,12 +1141,13 @@ win_print_incoming(ProfWin *window, const char *const from, ProfMessage *message void win_print_them(ProfWin *window, theme_item_t theme_item, char ch, int flags, const char *const them) { - _win_printf(window, ch, 0, NULL, flags | NO_ME | NO_EOL, theme_item, them, ""); + _win_printf(window, ch, 0, NULL, flags | NO_ME | NO_EOL, theme_item, them, NULL, ""); } void -win_println_them_message(ProfWin *window, char ch, int flags, const char *const them, const char *const message, ...) +win_println_incoming_muc_msg(ProfWin *window, char ch, int flags, const char *const them, const char *const id, const char *const replace_id, const char *const message, ...) { + //TODO: we always use current timestamp here. instead of the message->timestamp one if available. i think somewhere else we check whether it exists first. GDateTime *timestamp = g_date_time_new_now_local(); va_list arg; @@ -1107,9 +1155,14 @@ win_println_them_message(ProfWin *window, char ch, int flags, const char *const GString *fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL, NULL); + if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && replace_id) { + _win_correct(window, fmt_msg->str, id, replace_id); + } else { + _win_printf(window, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, id, "%s", fmt_msg->str); + } +// buffer_append(window->layout->buffer, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL, NULL); + // _win_print_internal(window, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL); - _win_print(window, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1118,7 +1171,7 @@ win_println_them_message(ProfWin *window, char ch, int flags, const char *const } void -win_println_me_message(ProfWin *window, char ch, const char *const me, const char *const message, ...) +win_print_outgoing_muc_msg(ProfWin *window, char ch, const char *const me, const char *const id, const char *const replace_id, const char *const message, ...) { GDateTime *timestamp = g_date_time_new_now_local(); @@ -1127,9 +1180,14 @@ win_println_me_message(ProfWin *window, char ch, const char *const me, const cha GString *fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, ch, 0, timestamp, 0, THEME_TEXT_ME, me, fmt_msg->str, NULL, NULL); + if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && replace_id) { + _win_correct(window, fmt_msg->str, id, replace_id); + } else { + _win_printf(window, ch, 0, timestamp, 0, THEME_TEXT_ME, me, id, "%s", fmt_msg->str); + } +// buffer_append(window->layout->buffer, ch, 0, timestamp, 0, THEME_TEXT_ME, me, fmt_msg->str, NULL, NULL); +// _win_print_internal(window, ch, 0, timestamp, 0, THEME_TEXT_ME, me, fmt_msg->str, NULL); - _win_print(window, ch, 0, timestamp, 0, THEME_TEXT_ME, me, fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1138,7 +1196,7 @@ win_println_me_message(ProfWin *window, char ch, const char *const me, const cha } void -win_print_outgoing(ProfWin *window, const char ch, const char *const message, ...) +win_print_outgoing(ProfWin *window, const char ch, const char *const id, const char *const replace_id, const char *const message, ...) { GDateTime *timestamp = g_date_time_new_now_local(); @@ -1147,9 +1205,12 @@ win_print_outgoing(ProfWin *window, const char ch, const char *const message, .. GString *fmt_msg = g_string_new(NULL); g_string_vprintf(fmt_msg, message, arg); - buffer_append(window->layout->buffer, ch, 0, timestamp, 0, THEME_TEXT_ME, "me", fmt_msg->str, NULL, NULL); + if (replace_id) { + _win_correct(window, fmt_msg->str, id, replace_id); + } else { + _win_printf(window, ch, 0, timestamp, 0, THEME_TEXT_THEM, "me", id, "%s", fmt_msg->str); + } - _win_print(window, ch, 0, timestamp, 0, THEME_TEXT_ME, "me", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1168,7 +1229,7 @@ win_print_history(ProfWin *window, GDateTime *timestamp, const char *const messa g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', 0, timestamp, 0, THEME_TEXT_HISTORY, "", fmt_msg->str, NULL, NULL); - _win_print(window, '-', 0, timestamp, 0, THEME_TEXT_HISTORY, "", fmt_msg->str, NULL); + _win_print_internal(window, '-', 0, timestamp, 0, THEME_TEXT_HISTORY, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1188,8 +1249,8 @@ win_print(ProfWin *window, theme_item_t theme_item, const char ch, const char *c g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, ch, 0, timestamp, NO_EOL, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, ch, 0, timestamp, NO_EOL, theme_item, "", fmt_msg->str, NULL); - _win_print(window, ch, 0, timestamp, NO_EOL, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1208,8 +1269,8 @@ win_println(ProfWin *window, theme_item_t theme_item, const char ch, const char g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, ch, 0, timestamp, 0, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, ch, 0, timestamp, 0, theme_item, "", fmt_msg->str, NULL); - _win_print(window, ch, 0, timestamp, 0, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1228,8 +1289,8 @@ win_println_indent(ProfWin *window, int pad, const char *const message, ...) g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', pad, timestamp, 0, THEME_DEFAULT, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, '-', pad, timestamp, 0, THEME_DEFAULT, "", fmt_msg->str, NULL); - _win_print(window, '-', pad, timestamp, 0, THEME_DEFAULT, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1248,8 +1309,8 @@ win_append(ProfWin *window, theme_item_t theme_item, const char *const message, g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', 0, timestamp, NO_DATE | NO_EOL, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, '-', 0, timestamp, NO_DATE | NO_EOL, theme_item, "", fmt_msg->str, NULL); - _win_print(window, '-', 0, timestamp, NO_DATE | NO_EOL, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1268,8 +1329,8 @@ win_appendln(ProfWin *window, theme_item_t theme_item, const char *const message g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', 0, timestamp, NO_DATE, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, '-', 0, timestamp, NO_DATE, theme_item, "", fmt_msg->str, NULL); - _win_print(window, '-', 0, timestamp, NO_DATE, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1288,8 +1349,8 @@ win_append_highlight(ProfWin *window, theme_item_t theme_item, const char *const g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, '-', 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL); - _win_print(window, '-', 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1308,8 +1369,8 @@ win_appendln_highlight(ProfWin *window, theme_item_t theme_item, const char *con g_string_vprintf(fmt_msg, message, arg); buffer_append(window->layout->buffer, '-', 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL, NULL); + _win_print_internal(window, '-', 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL); - _win_print(window, '-', 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1320,19 +1381,24 @@ win_appendln_highlight(ProfWin *window, theme_item_t theme_item, const char *con void win_print_http_upload(ProfWin *window, const char *const message, char *url) { - win_print_with_receipt(window, '!', NULL, message, url); + win_print_outgoing_with_receipt(window, '!', NULL, message, url, NULL); } void -win_print_with_receipt(ProfWin *window, const char show_char, const char *const from, const char *const message, char *id) +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) { GDateTime *time = g_date_time_new_now_local(); DeliveryReceipt *receipt = malloc(sizeof(struct delivery_receipt_t)); receipt->received = FALSE; - buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt, id); - _win_print(window, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt); + if (replace_id) { + _win_correct(window, message, id, replace_id); + } else { + buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt, id); + _win_print_internal(window, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt); + } + // TODO: cross-reference.. this should be replaced by a real event-based system inp_nonblocking(TRUE); g_date_time_unref(time); @@ -1373,7 +1439,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 from, const char *const message, ...) + int flags, theme_item_t theme_item, const char *const from, const char *const message_id, const char *const message, ...) { if (timestamp == NULL) { timestamp = g_date_time_new_now_local(); @@ -1386,9 +1452,10 @@ _win_printf(ProfWin *window, const char show_char, int pad_indent, GDateTime *ti 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, from, fmt_msg->str, NULL, NULL); + buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, from, fmt_msg->str, NULL, message_id); + + _win_print_internal(window, show_char, pad_indent, timestamp, flags, theme_item, from, fmt_msg->str, NULL); - _win_print(window, show_char, pad_indent, timestamp, flags, theme_item, from, fmt_msg->str, NULL); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1397,7 +1464,7 @@ _win_printf(ProfWin *window, const char show_char, int pad_indent, GDateTime *ti } static void -_win_print(ProfWin *window, const char show_char, int pad_indent, GDateTime *time, +_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) { // flags : 1st bit = 0/1 - me/not me @@ -1676,12 +1743,12 @@ win_redraw(ProfWin *window) for (i = 0; i < size; i++) { ProfBuffEntry *e = buffer_get_entry(window->layout->buffer, i); - if (e->from == NULL && e->message && e->message[0] == '-') { + if (e->display_from == NULL && e->message && e->message[0] == '-') { // just an indicator to print the separator not the actual message win_print_separator(window); } else { // regular thing to print - _win_print(window, e->show_char, e->pad_indent, e->time, e->flags, e->theme_item, e->from, e->message, e->receipt); + _win_print_internal(window, e->show_char, e->pad_indent, e->time, e->flags, e->theme_item, e->display_from, e->message, e->receipt); } } } diff --git a/src/ui/window.h b/src/ui/window.h index b1f9a4af..2bd11621 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -3,6 +3,7 @@ * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 - 2020 Michael Vetter * * This file is part of Profanity. * @@ -62,17 +63,16 @@ void win_show_status_string(ProfWin *window, const char *const from, const char *const default_show); void win_print_them(ProfWin *window, theme_item_t theme_item, char ch, int flags, const char *const them); -void win_println_them_message(ProfWin *window, char ch, int flags, const char *const them, const char *const message, ...); -void win_println_me_message(ProfWin *window, char ch, const char *const me, const char *const message, ...); +void win_println_incoming_muc_msg(ProfWin *window, char ch, int flags, const char *const them, const char *const id, const char *const replace_id, const char *const message, ...); +void win_print_outgoing_muc_msg(ProfWin *window, char ch, const char *const me, const char *const id, const char *const replace_id, const char *const message, ...); -void win_print_outgoing(ProfWin *window, const char ch, const char *const message, ...); +void win_print_outgoing(ProfWin *window, const char ch, const char *const id, const char *const replace_id, const char *const message, ...); void win_print_incoming(ProfWin *window, const char *const from, ProfMessage *message); void win_print_history(ProfWin *window, GDateTime *timestamp, const char *const message, ...); void win_print_http_upload(ProfWin *window, const char *const message, char *url); -void win_print_with_receipt(ProfWin *window, const char show_char, const char *const from, const char *const message, - char *id); +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_newline(ProfWin *window); void win_redraw(ProfWin *window); diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c index e1587ef8..a0fcecb0 100644 --- a/src/xmpp/capabilities.c +++ b/src/xmpp/capabilities.c @@ -104,12 +104,19 @@ caps_init(void) g_hash_table_add(prof_features, strdup(STANZA_NS_CHATSTATES)); g_hash_table_add(prof_features, strdup(STANZA_NS_PING)); g_hash_table_add(prof_features, strdup(STANZA_NS_STABLE_ID)); + if (prefs_get_boolean(PREF_RECEIPTS_SEND)) { g_hash_table_add(prof_features, strdup(STANZA_NS_RECEIPTS)); } + if (prefs_get_boolean(PREF_LASTACTIVITY)) { g_hash_table_add(prof_features, strdup(STANZA_NS_LASTACTIVITY)); } + + if (prefs_get_boolean(PREF_CORRECTION_ALLOW)) { + g_hash_table_add(prof_features, strdup(STANZA_NS_LAST_MESSAGE_CORRECTION)); + } + my_sha1 = NULL; } diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 1a97d9f1..0caef0f6 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -76,11 +76,11 @@ typedef struct p_message_handle_t { } ProfMessageHandler; static int _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata); -static void _private_chat_handler(xmpp_stanza_t *const stanza); static void _handle_error(xmpp_stanza_t *const stanza); static void _handle_groupchat(xmpp_stanza_t *const stanza); 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); static void _handle_captcha(xmpp_stanza_t *const stanza); static void _handle_receipt_received(xmpp_stanza_t *const stanza); @@ -188,6 +188,7 @@ message_init(void) message->jid = NULL; message->id = NULL; message->originid = NULL; + message->replace_id = NULL; message->body = NULL; message->encrypted = NULL; message->plain = NULL; @@ -215,6 +216,10 @@ message_free(ProfMessage *message) xmpp_free(ctx, message->originid); } + if (message->replace_id) { + xmpp_free(ctx, message->replace_id); + } + if (message->body) { xmpp_free(ctx, message->body); } @@ -254,8 +259,7 @@ message_pubsub_event_handler_add(const char *const node, ProfMessageCallback fun } char* -message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, - gboolean request_receipt) +message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, gboolean request_receipt, const char *const replace_id) { xmpp_ctx_t * const ctx = connection_get_ctx(); @@ -279,6 +283,10 @@ message_send_chat(const char *const barejid, const char *const msg, const char * stanza_attach_receipt_request(ctx, message); } + if (replace_id) { + stanza_attach_correction(ctx, message, replace_id); + } + _send_message_stanza(message); xmpp_stanza_release(message); @@ -286,7 +294,7 @@ message_send_chat(const char *const barejid, const char *const msg, const char * } char* -message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt) +message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id) { xmpp_ctx_t * const ctx = connection_get_ctx(); @@ -338,6 +346,10 @@ message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean stanza_attach_receipt_request(ctx, message); } + if (replace_id) { + stanza_attach_correction(ctx, message, replace_id); + } + _send_message_stanza(message); xmpp_stanza_release(message); @@ -345,7 +357,7 @@ message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean } char* -message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt) +message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id) { xmpp_ctx_t * const ctx = connection_get_ctx(); @@ -370,6 +382,10 @@ message_send_chat_otr(const char *const barejid, const char *const msg, gboolean stanza_attach_receipt_request(ctx, message); } + if (replace_id) { + stanza_attach_correction(ctx, message, replace_id); + } + _send_message_stanza(message); xmpp_stanza_release(message); @@ -381,7 +397,7 @@ char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, - gboolean request_receipt, gboolean muc) + gboolean request_receipt, gboolean muc, const char *const replace_id) { char *state = chat_session_get_state(jid); xmpp_ctx_t * const ctx = connection_get_ctx(); @@ -481,6 +497,10 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, stanza_attach_receipt_request(ctx, message); } + if (replace_id) { + stanza_attach_correction(ctx, message, replace_id); + } + _send_message_stanza(message); xmpp_stanza_release(message); @@ -508,7 +528,7 @@ message_send_private(const char *const fulljid, const char *const msg, const cha } char* -message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url) +message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url, const char *const replace_id) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id(); @@ -521,6 +541,10 @@ message_send_groupchat(const char *const roomjid, const char *const msg, const c stanza_attach_x_oob_url(ctx, message, oob_url); } + if (replace_id) { + stanza_attach_correction(ctx, message, replace_id); + } + _send_message_stanza(message); xmpp_stanza_release(message); @@ -807,13 +831,23 @@ _handle_groupchat(xmpp_stanza_t *const stanza) ProfMessage *message = message_init(); message->jid = jid; + if (id) { message->id = strdup(id); } + if (originid) { message->originid = strdup(originid); } + xmpp_stanza_t *replace_id_stanza = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_LAST_MESSAGE_CORRECTION); + if (replace_id_stanza) { + const char *replace_id = xmpp_stanza_get_id(replace_id_stanza); + if (replace_id) { + message->replace_id = strdup(replace_id); + } + } + message->body = xmpp_message_get_body(stanza); // check omemo encryption @@ -927,7 +961,7 @@ _receipt_request_handler(xmpp_stanza_t *const stanza) } static void -_private_chat_handler(xmpp_stanza_t *const stanza) +_handle_muc_private_message(xmpp_stanza_t *const stanza) { // standard chat message, use jid without resource ProfMessage *message = message_init(); @@ -936,6 +970,12 @@ _private_chat_handler(xmpp_stanza_t *const stanza) const gchar *from = xmpp_stanza_get_from(stanza); message->jid = jid_create(from); + // message stanza id + const char *id = xmpp_stanza_get_id(stanza); + if (id) { + message->id = strdup(id); + } + // check omemo encryption #ifdef HAVE_OMEMO message->plain = omemo_receive_message(stanza, &message->trusted); @@ -1015,6 +1055,21 @@ _handle_carbons(xmpp_stanza_t *const stanza) message->mucuser = TRUE; } + // id + const char *id = xmpp_stanza_get_id(message_stanza); + if (id) { + message->id = strdup(id); + } + + // replace id + xmpp_stanza_t *replace_id_stanza = xmpp_stanza_get_child_by_ns(message_stanza, STANZA_NS_LAST_MESSAGE_CORRECTION); + if (replace_id_stanza) { + const char *replace_id = xmpp_stanza_get_id(replace_id_stanza); + if (replace_id) { + message->replace_id = strdup(replace_id); + } + } + // check omemo encryption #ifdef HAVE_OMEMO message->plain = omemo_receive_message(message_stanza, &message->trusted); @@ -1104,7 +1159,7 @@ _handle_chat(xmpp_stanza_t *const stanza) // private message from chat room use full jid (room/nick) if (muc_active(jid->barejid)) { - _private_chat_handler(stanza); + _handle_muc_private_message(stanza); jid_destroy(jid); return; } @@ -1113,6 +1168,20 @@ _handle_chat(xmpp_stanza_t *const stanza) ProfMessage *message = message_init(); message->jid = jid; + // message stanza id + const char *id = xmpp_stanza_get_id(stanza); + if (id) { + message->id = strdup(id); + } + + xmpp_stanza_t *replace_id_stanza = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_LAST_MESSAGE_CORRECTION); + if (replace_id_stanza) { + const char *replace_id = xmpp_stanza_get_id(replace_id_stanza); + if (replace_id) { + message->replace_id = strdup(replace_id); + } + } + if (mucuser) { message->mucuser = TRUE; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index f398a268..7b0db5c7 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2606,3 +2606,15 @@ stanza_create_avatar_retrieve_data_request(xmpp_ctx_t *ctx, const char *stanza_i return iq; } + +xmpp_stanza_t* +stanza_attach_correction(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const replace_id) +{ + xmpp_stanza_t *replace_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(replace_stanza, "replace"); + xmpp_stanza_set_id(replace_stanza, replace_id); + xmpp_stanza_set_ns(replace_stanza, STANZA_NS_LAST_MESSAGE_CORRECTION); + xmpp_stanza_add_child(stanza, replace_stanza); + + return stanza; +} diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 7b5cbe35..ee5c6772 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -201,6 +201,7 @@ #define STANZA_NS_STABLE_ID "urn:xmpp:sid:0" #define STANZA_NS_USER_AVATAR_DATA "urn:xmpp:avatar:data" #define STANZA_NS_USER_AVATAR_METADATA "urn:xmpp:avatar:metadata" +#define STANZA_NS_LAST_MESSAGE_CORRECTION "urn:xmpp:message-correct:0" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" @@ -244,6 +245,7 @@ xmpp_stanza_t* stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza) xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url); xmpp_stanza_t* stanza_attach_origin_id(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const id); +xmpp_stanza_t* stanza_attach_correction(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const replace_id); xmpp_stanza_t* stanza_create_room_join_presence(xmpp_ctx_t *const ctx, const char *const full_room_jid, const char *const passwd); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index f5b4ce70..0563948a 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -68,6 +68,7 @@ #define XMPP_FEATURE_PUBSUB "http://jabber.org/protocol/pubsub" #define XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS "http://jabber.org/protocol/pubsub#publish-options" #define XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY "urn:xmpp:avatar:metadata+notify" +#define XMPP_FEATURE_LAST_MESSAGE_CORRECTION "urn:xmpp:message-correct:0" typedef enum { JABBER_CONNECTING, @@ -130,6 +131,8 @@ typedef struct prof_message_t { char *id; /* XEP-0359 */ char *originid; + /* XEP-0308 LMC */ + char *replace_id; /* The raw body from xmpp message, either plaintext or OTR encrypted text */ char *body; /* The encrypted message as for PGP */ @@ -168,13 +171,12 @@ char* connection_jid_for_feature(const char *const feature); const char* connection_get_profanity_identifier(void); -char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, - gboolean request_receipt); -char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt); -char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt); -char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc); +char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, gboolean request_receipt, const char *const replace_id); +char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id); +char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id); +char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc, const char *const replace_id); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); -char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); +char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url, const char *const replace_id); void message_send_groupchat_subject(const char *const roomjid, const char *const subject); void message_send_inactive(const char *const jid); void message_send_composing(const char *const jid); diff --git a/tests/unittests/otr/stub_otr.c b/tests/unittests/otr/stub_otr.c index 25c199a8..dffecfec 100644 --- a/tests/unittests/otr/stub_otr.c +++ b/tests/unittests/otr/stub_otr.c @@ -45,7 +45,7 @@ char* otr_on_message_recv(const char * const barejid, const char * const resourc { return NULL; } -gboolean otr_on_message_send(ProfChatWin *chatwin, const char * const message, gboolean request_receipt) +gboolean otr_on_message_send(ProfChatWin *chatwin, const char * const message, gboolean request_receipt, const char *const replace_id) { return FALSE; } diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 49a70892..ecfc55e0 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -160,8 +160,7 @@ void privwin_incoming_msg(ProfPrivateWin *privatewin, ProfMessage *message) {} void ui_disconnected(void) {} void chatwin_recipient_gone(ProfChatWin *chatwin) {} -void chatwin_outgoing_msg(ProfChatWin *chatwin, const char * const message, char *id, prof_enc_t enc_mode, - gboolean request_receipt) {} +void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, gboolean request_receipt, const char *const replace_id) {} void chatwin_outgoing_carbon(ProfChatWin *chatwin, ProfMessage *message) {} void privwin_outgoing_msg(ProfPrivateWin *privwin, const char * const message) {} @@ -191,7 +190,7 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {} void mucwin_history(ProfMucWin *mucwin, const char * const nick, GDateTime *timestamp, const char * const message) {} void mucwin_incoming_msg(ProfMucWin *mucwin, ProfMessage *message, GSList *mentions, GList *triggers) {} -void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) {} +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_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {} void mucwin_requires_config(ProfMucWin *mucwin) {} void ui_room_destroy(const char * const roomjid) {} @@ -452,6 +451,7 @@ void cons_winpos_setting(void) {} void cons_statusbar_setting(void) {} void cons_tray_setting(void) {} void cons_os_setting(void) {} +void cons_correction_setting(void) {} void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity) { diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index f5cfb7ef..e9ff27e5 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -97,28 +97,27 @@ const char* connection_get_profanity_identifier(void) { } // message functions -char* message_send_chat(const char * const barejid, const char * const msg, const char *const oob_url, - gboolean request_receipt) +char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, gboolean request_receipt, const char *const replace_id) { check_expected(barejid); check_expected(msg); return NULL; } -char* message_send_chat_otr(const char * const barejid, const char * const msg, gboolean request_receipt) +char* message_send_chat_otr(const char * const barejid, const char * const msg, gboolean request_receipt, const char *const replace_id) { check_expected(barejid); check_expected(msg); return NULL; } -char* message_send_chat_pgp(const char * const barejid, const char * const msg, gboolean request_receipt) +char* message_send_chat_pgp(const char * const barejid, const char * const msg, gboolean request_receipt, const char *const replace_id) { return NULL; } void message_send_private(const char * const fulljid, const char * const msg, const char *const oob_url) {} -char* message_send_groupchat(const char * const roomjid, const char * const msg, const char *const oob_url) +char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url, const char *const replace_id) { return NULL; } diff --git a/theme_template b/theme_template index 55de8521..3a512936 100644 --- a/theme_template +++ b/theme_template @@ -149,3 +149,4 @@ console.muc= console.chat= console.private= inputwin.position= +correction.char= diff --git a/themes/boothj5 b/themes/boothj5 index fee2a7d7..3955da6a 100644 --- a/themes/boothj5 +++ b/themes/boothj5 @@ -152,4 +152,5 @@ statusbar.tabs=10 statusbar.tablen=7 statusbar.show.name=true statusbar.show.number=true +correction.char=+