From a978bb12bf0235be9e57e0e08b6328acc88add8b Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 18 Aug 2016 22:51:06 +0100 Subject: [PATCH] Send receipt request only when receiver supports feature issue #829 --- src/command/cmd_funcs.c | 4 +- src/event/client_events.c | 27 ++++++++--- src/otr/otr.c | 16 +++---- src/otr/otr.h | 2 +- src/ui/chatwin.c | 5 ++- src/ui/ui.h | 3 +- src/xmpp/capabilities.c | 18 ++++++++ src/xmpp/chat_session.c | 35 +++++++++++++++ src/xmpp/chat_session.h | 2 + src/xmpp/jid.c | 18 ++++---- src/xmpp/message.c | 59 ++++++------------------- src/xmpp/xmpp.h | 8 ++-- tests/functionaltests/functionaltests.c | 1 + tests/functionaltests/test_receipts.c | 39 +++++++++++++++- tests/functionaltests/test_receipts.h | 1 + tests/unittests/otr/stub_otr.c | 2 +- tests/unittests/ui/stub_ui.c | 3 +- tests/unittests/xmpp/stub_xmpp.c | 11 +++-- 18 files changed, 172 insertions(+), 82 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 16f25abd..192b4611 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -6616,7 +6616,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); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); free(id); return TRUE; } @@ -6649,7 +6649,7 @@ 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); + char *id = message_send_chat_otr(chatwin->barejid, otr_query_message, FALSE); free(id); return TRUE; } diff --git a/src/event/client_events.c b/src/event/client_events.c index dd9c4432..c8111970 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -38,6 +38,7 @@ #include #include "log.h" +#include "config/preferences.h" #include "plugins/plugins.h" #include "ui/window_list.h" #include "ui/ui.h" @@ -111,22 +112,38 @@ void cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url) { 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); + } + } + char *plugin_msg = plugins_pre_chat_message_send(chatwin->barejid, msg); // OTR suported, PGP supported #ifdef HAVE_LIBOTR #ifdef HAVE_LIBGPGME if (chatwin->pgp_send) { - char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg); + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt); free(id); } else { - gboolean handled = otr_on_message_send(chatwin, plugin_msg); + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); if (!handled) { - char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url); + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); chat_log_msg_out(chatwin->barejid, plugin_msg); - chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); free(id); } } diff --git a/src/otr/otr.c b/src/otr/otr.c index 8aa3bac1..158e6d27 100644 --- a/src/otr/otr.c +++ b/src/otr/otr.c @@ -117,7 +117,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); + char *id = message_send_chat_otr(recipient, message, FALSE); free(id); } @@ -314,7 +314,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); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); free(id); } } @@ -328,7 +328,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); + char *id = message_send_chat_otr(barejid, otr_query_message, FALSE); free(id); } @@ -336,7 +336,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) +otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) { char *id = NULL; prof_otrpolicy_t policy = otr_get_policy(chatwin->barejid); @@ -345,9 +345,9 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message) if (otr_is_secure(chatwin->barejid)) { char *encrypted = otr_encrypt_message(chatwin->barejid, message); if (encrypted) { - id = message_send_chat_otr(chatwin->barejid, encrypted); + id = message_send_chat_otr(chatwin->barejid, encrypted, request_receipt); chat_log_otr_msg_out(chatwin->barejid, message); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OTR); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OTR, request_receipt); otr_free_message(encrypted); free(id); return TRUE; @@ -366,8 +366,8 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message) // 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); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_PLAIN); + id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg, request_receipt); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_PLAIN, request_receipt); chat_log_msg_out(chatwin->barejid, message); free(otr_tagged_msg); free(id); diff --git a/src/otr/otr.h b/src/otr/otr.h index 0853f4fc..a50503af 100644 --- a/src/otr/otr.h +++ b/src/otr/otr.h @@ -72,7 +72,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 otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); void otr_keygen(ProfAccount *account); diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index 32d8b79c..9098d4cc 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -293,7 +293,8 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha } void -chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode) +chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, + gboolean request_receipt) { assert(chatwin != NULL); @@ -304,7 +305,7 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, enc_char = prefs_get_pgp_char(); } - if (prefs_get_boolean(PREF_RECEIPTS_REQUEST) && id) { + if (request_receipt && id) { win_print_with_receipt((ProfWin*)chatwin, enc_char, 0, NULL, 0, THEME_TEXT_ME, "me", message, id); } else { win_print((ProfWin*)chatwin, enc_char, 0, NULL, 0, THEME_TEXT_ME, "me", message); diff --git a/src/ui/ui.h b/src/ui/ui.h index 2683a32b..9db4fe33 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -133,7 +133,8 @@ void chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, cons GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode); 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); +void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, + gboolean request_receipt); void chatwin_outgoing_carbon(ProfChatWin *chatwin, const char *const message, prof_enc_t enc_mode); void chatwin_contact_online(ProfChatWin *chatwin, Resource *resource, GDateTime *last_activity); void chatwin_contact_offline(ProfChatWin *chatwin, char *resource, char *status); diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c index 6ca3a29e..ca09e703 100644 --- a/src/xmpp/capabilities.c +++ b/src/xmpp/capabilities.c @@ -307,6 +307,24 @@ caps_lookup(const char *const jid) return NULL; } +gboolean +caps_jid_has_feature(const char *const jid, const char *const feature) +{ + char *ver = g_hash_table_lookup(jid_to_ver, jid); + EntityCapabilities *caps = ver ? _caps_by_ver(ver) : _caps_by_jid(jid); + + if (caps == NULL) { + return FALSE; + } + + GSList *found = g_slist_find_custom(caps->features, feature, (GCompareFunc)g_strcmp0); + gboolean result = found != NULL; + + caps_destroy(caps); + + return result; +} + char* caps_get_my_sha1(xmpp_ctx_t *const ctx) { diff --git a/src/xmpp/chat_session.c b/src/xmpp/chat_session.c index 61d7018c..d08ba340 100644 --- a/src/xmpp/chat_session.c +++ b/src/xmpp/chat_session.c @@ -41,6 +41,7 @@ #include "log.h" #include "config/preferences.h" #include "xmpp/xmpp.h" +#include "xmpp/stanza.h" #include "xmpp/chat_session.h" static GHashTable *sessions; @@ -100,6 +101,40 @@ chat_session_get(const char *const barejid) return g_hash_table_lookup(sessions, barejid); } +char* +chat_session_get_jid(const char *const barejid) +{ + ChatSession *session = chat_session_get(barejid); + char *jid = NULL; + if (session) { + Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource); + jid = strdup(jidp->fulljid); + jid_destroy(jidp); + } else { + jid = strdup(barejid); + } + + return jid; +} + +char* +chat_session_get_state(const char *const barejid) +{ + ChatSession *session = chat_session_get(barejid); + char *state = NULL; + if (session) { + if (prefs_get_boolean(PREF_STATES) && session->send_states) { + state = STANZA_NAME_ACTIVE; + } + } else { + if (prefs_get_boolean(PREF_STATES)) { + state = STANZA_NAME_ACTIVE; + } + } + + return state; +} + void chat_session_recipient_gone(const char *const barejid, const char *const resource) { diff --git a/src/xmpp/chat_session.h b/src/xmpp/chat_session.h index 705799a1..46edb494 100644 --- a/src/xmpp/chat_session.h +++ b/src/xmpp/chat_session.h @@ -56,6 +56,8 @@ void chat_session_recipient_typing(const char *const barejid, const char *const void chat_session_recipient_paused(const char *const barejid, const char *const resource); void chat_session_recipient_gone(const char *const barejid, const char *const resource); void chat_session_recipient_inactive(const char *const barejid, const char *const resource); +char* chat_session_get_jid(const char *const barejid); +char* chat_session_get_state(const char *const barejid); void chat_session_remove(const char *const barejid); diff --git a/src/xmpp/jid.c b/src/xmpp/jid.c index fb4361d7..7289ca4c 100644 --- a/src/xmpp/jid.c +++ b/src/xmpp/jid.c @@ -119,15 +119,17 @@ jid_create_from_bare_and_resource(const char *const room, const char *const nick void jid_destroy(Jid *jid) { - if (jid) { - g_free(jid->str); - g_free(jid->localpart); - g_free(jid->domainpart); - g_free(jid->resourcepart); - g_free(jid->barejid); - g_free(jid->fulljid); - free(jid); + if (jid == NULL) { + return; } + + g_free(jid->str); + g_free(jid->localpart); + g_free(jid->domainpart); + g_free(jid->resourcepart); + g_free(jid->barejid); + g_free(jid->fulljid); + free(jid); } gboolean diff --git a/src/xmpp/message.c b/src/xmpp/message.c index fc314e30..60c63178 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -131,47 +131,14 @@ message_handlers_init(void) xmpp_handler_add(conn, _message_handler, NULL, STANZA_NAME_MESSAGE, NULL, ctx); } -static char* -_session_jid(const char *const barejid) -{ - ChatSession *session = chat_session_get(barejid); - char *jid = NULL; - if (session) { - Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource); - jid = strdup(jidp->fulljid); - jid_destroy(jidp); - } else { - jid = strdup(barejid); - } - - return jid; -} - -static char* -_session_state(const char *const barejid) -{ - ChatSession *session = chat_session_get(barejid); - char *state = NULL; - if (session) { - if (prefs_get_boolean(PREF_STATES) && session->send_states) { - state = STANZA_NAME_ACTIVE; - } - } else { - if (prefs_get_boolean(PREF_STATES)) { - state = STANZA_NAME_ACTIVE; - } - } - - return state; -} - char* -message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url) +message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, + gboolean request_receipt) { xmpp_ctx_t * const ctx = connection_get_ctx(); - char *state = _session_state(barejid); - char *jid = _session_jid(barejid); + char *state = chat_session_get_state(barejid); + char *jid = chat_session_get_jid(barejid); char *id = create_unique_id("msg"); xmpp_stanza_t *message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg); @@ -185,7 +152,7 @@ message_send_chat(const char *const barejid, const char *const msg, const char * stanza_attach_x_oob_url(ctx, message, oob_url); } - if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) { + if (request_receipt) { stanza_attach_receipt_request(ctx, message); } @@ -196,12 +163,12 @@ 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) +message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt) { xmpp_ctx_t * const ctx = connection_get_ctx(); - char *state = _session_state(barejid); - char *jid = _session_jid(barejid); + char *state = chat_session_get_state(barejid); + char *jid = chat_session_get_jid(barejid); char *id = create_unique_id("msg"); xmpp_stanza_t *message = NULL; @@ -240,7 +207,7 @@ message_send_chat_pgp(const char *const barejid, const char *const msg) stanza_attach_state(ctx, message, state); } - if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) { + if (request_receipt) { stanza_attach_receipt_request(ctx, message); } @@ -251,12 +218,12 @@ message_send_chat_pgp(const char *const barejid, const char *const msg) } char* -message_send_chat_otr(const char *const barejid, const char *const msg) +message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt) { xmpp_ctx_t * const ctx = connection_get_ctx(); - char *state = _session_state(barejid); - char *jid = _session_jid(barejid); + char *state = chat_session_get_state(barejid); + char *jid = chat_session_get_jid(barejid); char *id = create_unique_id("msg"); xmpp_stanza_t *message = stanza_create_message(ctx, id, barejid, STANZA_TYPE_CHAT, msg); @@ -270,7 +237,7 @@ message_send_chat_otr(const char *const barejid, const char *const msg) stanza_attach_hints_no_copy(ctx, message); stanza_attach_hints_no_store(ctx, message); - if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) { + if (request_receipt) { stanza_attach_receipt_request(ctx, message); } diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 80245b2d..9c0690a2 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -130,9 +130,10 @@ gboolean connection_send_stanza(const char *const stanza); GList* connection_get_available_resources(void); gboolean connection_supports(const char *const feature); -char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url); -char* message_send_chat_otr(const char *const barejid, const char *const msg); -char* message_send_chat_pgp(const char *const barejid, const char *const msg); +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); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); void message_send_groupchat_subject(const char *const roomjid, const char *const subject); @@ -183,6 +184,7 @@ void caps_destroy(EntityCapabilities *caps); void caps_reset_ver(void); void caps_add_feature(char *feature); void caps_remove_feature(char *feature); +gboolean caps_jid_has_feature(const char *const jid, const char *const feature); gboolean bookmark_add(const char *jid, const char *nick, const char *password, const char *autojoin_str); gboolean bookmark_update(const char *jid, const char *nick, const char *password, const char *autojoin_str); diff --git a/tests/functionaltests/functionaltests.c b/tests/functionaltests/functionaltests.c index 878561dc..d6989377 100644 --- a/tests/functionaltests/functionaltests.c +++ b/tests/functionaltests/functionaltests.c @@ -74,6 +74,7 @@ int main(int argc, char* argv[]) { PROF_FUNC_TEST(send_receipt_request), PROF_FUNC_TEST(send_receipt_on_request), + PROF_FUNC_TEST(does_not_send_receipt_request_to_barejid), PROF_FUNC_TEST(sends_new_item), PROF_FUNC_TEST(sends_new_item_nick), PROF_FUNC_TEST(sends_remove_item), diff --git a/tests/functionaltests/test_receipts.c b/tests/functionaltests/test_receipts.c index 6a347f7e..59333a87 100644 --- a/tests/functionaltests/test_receipts.c +++ b/tests/functionaltests/test_receipts.c @@ -12,7 +12,7 @@ #include "proftest.h" void -send_receipt_request(void **state) +does_not_send_receipt_request_to_barejid(void **state) { prof_input("/receipts request on"); @@ -23,6 +23,43 @@ send_receipt_request(void **state) assert_true(stbbr_received( "" "Hi there" + "" + )); +} + +void +send_receipt_request(void **state) +{ + prof_input("/receipts request on"); + + prof_connect(); + + stbbr_for_id("prof_caps_4", + "" + "" + "" + "" + "" + "" + ); + + stbbr_send( + "" + "15" + "My status" + "" + "" + ); + + prof_output_exact("Buddy1 is online, \"My status\""); + + prof_input("/msg Buddy1"); + prof_input("/resource set laptop"); + prof_input("Hi there, where is my receipt?"); + + assert_true(stbbr_received( + "" + "Hi there, where is my receipt?" "" "" )); diff --git a/tests/functionaltests/test_receipts.h b/tests/functionaltests/test_receipts.h index 5bfa5d1f..65c6fba6 100644 --- a/tests/functionaltests/test_receipts.h +++ b/tests/functionaltests/test_receipts.h @@ -1,3 +1,4 @@ +void does_not_send_receipt_request_to_barejid(void **state); void send_receipt_request(void **state); void send_receipt_on_request(void **state); diff --git a/tests/unittests/otr/stub_otr.c b/tests/unittests/otr/stub_otr.c index 098484d5..5070a66c 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 otr_on_message_send(ProfChatWin *chatwin, const char * const message, gboolean request_receipt) { return FALSE; } diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 2f9c3bdb..928000a3 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -164,7 +164,8 @@ void privwin_incoming_msg(ProfPrivateWin *privatewin, const char * const 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) {} +void chatwin_outgoing_msg(ProfChatWin *chatwin, const char * const message, char *id, prof_enc_t enc_mode, + gboolean request_receipt) {} void chatwin_outgoing_carbon(ProfChatWin *chatwin, const char * const message, prof_enc_t enc_mode) {} void privwin_outgoing_msg(ProfPrivateWin *privwin, const char * const message) {} diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index e77afdcd..c474e82a 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -91,21 +91,22 @@ connection_supports(const char *const feature) } // message functions -char* message_send_chat(const char * const barejid, const char * const msg, const char *const oob_url) +char* message_send_chat(const char * const barejid, const char * const msg, const char *const oob_url, + gboolean request_receipt) { check_expected(barejid); check_expected(msg); return NULL; } -char* message_send_chat_otr(const char * const barejid, const char * const msg) +char* message_send_chat_otr(const char * const barejid, const char * const msg, gboolean request_receipt) { check_expected(barejid); check_expected(msg); return NULL; } -char* message_send_chat_pgp(const char * const barejid, const char * const msg) +char* message_send_chat_pgp(const char * const barejid, const char * const msg, gboolean request_receipt) { return NULL; } @@ -213,6 +214,10 @@ EntityCapabilities* caps_lookup(const char * const jid) void caps_close(void) {} void caps_destroy(EntityCapabilities *caps) {} void caps_reset_ver(void) {} +gboolean caps_jid_has_feature(const char *const jid, const char *const feature) +{ + return FALSE; +} gboolean bookmark_add(const char *jid, const char *nick, const char *password, const char *autojoin_str) {