From 84506cbaebdf14bb95a04245c8651fc402706c73 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 20 Jan 2020 14:28:13 +0100 Subject: [PATCH] Use OMEMO for offline MUC members (#1242) --- src/command/cmd_funcs.c | 18 ++++++---- src/event/server_events.c | 7 ++++ src/omemo/omemo.c | 32 ++++++------------ src/xmpp/bookmark.c | 3 ++ src/xmpp/iq.c | 41 ++++++++++++++++++----- src/xmpp/muc.c | 57 ++++++++++++++++++++++++++++++++ src/xmpp/muc.h | 5 +++ src/xmpp/xmpp.h | 2 +- tests/unittests/xmpp/stub_xmpp.c | 2 +- 9 files changed, 129 insertions(+), 38 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 151ce008..fbb5e581 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -3586,6 +3586,9 @@ cmd_join(ProfWin *window, const char *const command, gchar **args) if (!muc_active(room)) { presence_join_room(room, nick, passwd); muc_join(room, nick, passwd, FALSE); + iq_room_affiliation_list(room, "member", false); + iq_room_affiliation_list(room, "admin", false); + iq_room_affiliation_list(room, "owner", false); } else if (muc_roster_complete(room)) { ui_switch_to_room(room); } @@ -4131,14 +4134,14 @@ cmd_affiliation(ProfWin *window, const char *const command, gchar **args) if (g_strcmp0(cmd, "list") == 0) { if (!affiliation) { - iq_room_affiliation_list(mucwin->roomjid, "owner"); - iq_room_affiliation_list(mucwin->roomjid, "admin"); - iq_room_affiliation_list(mucwin->roomjid, "member"); - iq_room_affiliation_list(mucwin->roomjid, "outcast"); + iq_room_affiliation_list(mucwin->roomjid, "owner", true); + iq_room_affiliation_list(mucwin->roomjid, "admin", true); + iq_room_affiliation_list(mucwin->roomjid, "member", true); + iq_room_affiliation_list(mucwin->roomjid, "outcast", true); } else if (g_strcmp0(affiliation, "none") == 0) { win_println(window, THEME_DEFAULT, '!', "Cannot list users with no affiliation."); } else { - iq_room_affiliation_list(mucwin->roomjid, affiliation); + iq_room_affiliation_list(mucwin->roomjid, affiliation, true); } return TRUE; } @@ -8124,12 +8127,13 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) ProfMucWin *mucwin = (ProfMucWin*)window; assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS) { + if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS + && muc_member_type(mucwin->roomjid) == MUC_MEMBER_TYPE_MEMBERS_ONLY) { accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, TRUE); omemo_start_muc_sessions(mucwin->roomjid); mucwin->is_omemo = TRUE; } else { - win_println(window, THEME_DEFAULT, '!', "MUC must be non-anonymous (i.e. be configured to present real jid to anyone) in order to support OMEMO."); + win_println(window, THEME_DEFAULT, '!', "MUC must be non-anonymous (i.e. be configured to present real jid to anyone) and members-only in order to support OMEMO."); } } else { win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); diff --git a/src/event/server_events.c b/src/event/server_events.c index f2a3abf5..d42629e5 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -1199,6 +1199,10 @@ sv_ev_muc_occupant_online(const char *const room, const char *const nick, const gboolean updated = muc_roster_add(room, nick, jid, role, affiliation, show, status); + if (jid != NULL && affiliation != NULL) { + muc_members_update(room, jid, affiliation); + } + // not yet finished joining room if (!muc_roster_complete(room)) { return; @@ -1424,6 +1428,9 @@ sv_ev_bookmark_autojoin(Bookmark *bookmark) if (!muc_active(bookmark->barejid)) { presence_join_room(bookmark->barejid, nick, bookmark->password); muc_join(bookmark->barejid, nick, bookmark->password, TRUE); + iq_room_affiliation_list(bookmark->barejid, "member", false); + iq_room_affiliation_list(bookmark->barejid, "admin", false); + iq_room_affiliation_list(bookmark->barejid, "owner", false); } free(nick); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1f22559c..55e07a01 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -421,19 +421,14 @@ omemo_start_session(const char *const barejid) void omemo_start_muc_sessions(const char *const roomjid) { - GList *roster = muc_roster(roomjid); + GList *members = muc_members(roomjid); GList *iter; - for (iter = roster; iter != NULL; iter = iter->next) { - Occupant *occupant = (Occupant *)iter->data; - if (occupant->jid != NULL) { - Jid *jid = jid_create(occupant->jid); - omemo_start_session(jid->barejid); - jid_destroy(jid); - } else { - log_error("OMEMO: cannot get real jid for %s in %s", occupant->nick, roomjid); - } + for (iter = members; iter != NULL; iter = iter->next) { + Jid *jid = jid_create(iter->data); + omemo_start_session(jid->barejid); + jid_destroy(jid); } - g_list_free(roster); + g_list_free(members); } gboolean @@ -712,19 +707,14 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ if (muc) { ProfMucWin *mucwin = (ProfMucWin *)win; assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - GList *roster = muc_roster(mucwin->roomjid); + GList *members = muc_members(mucwin->roomjid); GList *iter; - for (iter = roster; iter != NULL; iter = iter->next) { - Occupant *occupant = (Occupant *)iter->data; - Jid *jid = jid_create(occupant->jid); - if (!jid->barejid) { - log_warning("OMEMO: missing barejid for MUC %s occupant %s", mucwin->roomjid, occupant->nick); - } else { - recipients = g_list_append(recipients, strdup(jid->barejid)); - } + for (iter = members; iter != NULL; iter = iter->next) { + Jid *jid = jid_create(iter->data); + recipients = g_list_append(recipients, strdup(jid->barejid)); jid_destroy(jid); } - g_list_free(roster); + g_list_free(members); } else { ProfChatWin *chatwin = (ProfChatWin *)win; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); diff --git a/src/xmpp/bookmark.c b/src/xmpp/bookmark.c index 19bb2062..3fc3a2d4 100644 --- a/src/xmpp/bookmark.c +++ b/src/xmpp/bookmark.c @@ -185,6 +185,9 @@ bookmark_join(const char *jid) } presence_join_room(bookmark->barejid, nick, bookmark->password); muc_join(bookmark->barejid, nick, bookmark->password, FALSE); + iq_room_affiliation_list(bookmark->barejid, "member", false); + iq_room_affiliation_list(bookmark->barejid, "admin", false); + iq_room_affiliation_list(bookmark->barejid, "owner", false); account_free(account); } else if (muc_roster_complete(bookmark->barejid)) { ui_room_join(bookmark->barejid, TRUE); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 1ae9d02c..7589c547 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -93,6 +93,11 @@ typedef struct privilege_set_t { char *privilege; } ProfPrivilegeSet; +typedef struct affiliation_list_t { + char *affiliation; + bool show_ui_message; +} ProfAffiliationList; + typedef struct command_config_data_t { char *sessionid; char *command; @@ -135,6 +140,7 @@ static int _command_exec_response_handler(xmpp_stanza_t *const stanza, void *con static void _iq_free_room_data(ProfRoomInfoData *roominfo); static void _iq_free_affiliation_set(ProfPrivilegeSet *affiliation_set); +static void _iq_free_affiliation_list(ProfAffiliationList *affiliation_list); static void _iq_id_handler_free(ProfIqHandler *handler); // scheduled @@ -637,13 +643,18 @@ iq_room_config_cancel(ProfConfWin *confwin) } void -iq_room_affiliation_list(const char *const room, char *affiliation) +iq_room_affiliation_list(const char *const room, char *affiliation, bool show_ui_message) { xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_room_affiliation_list_iq(ctx, room, affiliation); const char *id = xmpp_stanza_get_id(iq); - iq_id_handler_add(id, _room_affiliation_list_result_id_handler, free, strdup(affiliation)); + + ProfAffiliationList *affiliation_list = malloc(sizeof(ProfAffiliationList)); + affiliation_list->affiliation = strdup(affiliation); + affiliation_list->show_ui_message = show_ui_message; + + iq_id_handler_add(id, _room_affiliation_list_result_id_handler, (ProfIqFreeCallback)_iq_free_affiliation_list, affiliation_list); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -1851,7 +1862,7 @@ _room_affiliation_list_result_id_handler(xmpp_stanza_t *const stanza, void *cons const char *id = xmpp_stanza_get_id(stanza); const char *type = xmpp_stanza_get_type(stanza); const char *from = xmpp_stanza_get_from(stanza); - char *affiliation = (char *)userdata; + ProfAffiliationList *affiliation_list = (ProfAffiliationList *)userdata; if (id) { log_debug("IQ affiliation list result handler fired, id: %s.", id); @@ -1862,10 +1873,10 @@ _room_affiliation_list_result_id_handler(xmpp_stanza_t *const stanza, void *cons // handle error responses if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { char *error_message = stanza_get_error_message(stanza); - log_debug("Error retrieving %s list for room %s: %s", affiliation, from, error_message); + log_debug("Error retrieving %s list for room %s: %s", affiliation_list->affiliation, from, error_message); ProfMucWin *mucwin = wins_get_muc(from); - if (mucwin) { - mucwin_affiliation_list_error(mucwin, affiliation, error_message); + if (mucwin && affiliation_list->show_ui_message) { + mucwin_affiliation_list_error(mucwin, affiliation_list->affiliation, error_message); } free(error_message); return 0; @@ -1880,6 +1891,11 @@ _room_affiliation_list_result_id_handler(xmpp_stanza_t *const stanza, void *cons if (g_strcmp0(name, "item") == 0) { const char *jid = xmpp_stanza_get_attribute(child, STANZA_ATTR_JID); if (jid) { + if (g_strcmp0(affiliation_list->affiliation, "member") == 0 + || g_strcmp0(affiliation_list->affiliation, "owner") == 0 + || g_strcmp0(affiliation_list->affiliation, "admin") == 0) { + muc_members_add(from, jid); + } jids = g_slist_insert_sorted(jids, (gpointer)jid, (GCompareFunc)g_strcmp0); } } @@ -1889,8 +1905,8 @@ _room_affiliation_list_result_id_handler(xmpp_stanza_t *const stanza, void *cons muc_jid_autocomplete_add_all(from, jids); ProfMucWin *mucwin = wins_get_muc(from); - if (mucwin) { - mucwin_handle_affiliation_list(mucwin, affiliation, jids); + if (mucwin && affiliation_list->show_ui_message) { + mucwin_handle_affiliation_list(mucwin, affiliation_list->affiliation, jids); } g_slist_free(jids); @@ -2429,3 +2445,12 @@ _iq_free_affiliation_set(ProfPrivilegeSet *affiliation_set) free(affiliation_set); } } + +static void +_iq_free_affiliation_list(ProfAffiliationList *affiliation_list) +{ + if (affiliation_list) { + free(affiliation_list->affiliation); + free(affiliation_list); + } +} diff --git a/src/xmpp/muc.c b/src/xmpp/muc.c index f8b2582e..98c1e479 100644 --- a/src/xmpp/muc.c +++ b/src/xmpp/muc.c @@ -47,6 +47,10 @@ #include "xmpp/muc.h" #include "xmpp/contact.h" +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + typedef struct _muc_room_t { char *room; // e.g. test@conference.server char *nick; // e.g. Some User @@ -60,6 +64,7 @@ typedef struct _muc_room_t { gboolean autojoin; gboolean pending_nick_change; GHashTable *roster; + GHashTable *members; Autocomplete nick_ac; Autocomplete jid_ac; GHashTable *nick_changes; @@ -220,6 +225,7 @@ muc_join(const char *const room, const char *const nick, const char *const passw new_room->pending_broadcasts = NULL; new_room->pending_config = FALSE; new_room->roster = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_occupant_free); + new_room->members = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); new_room->nick_ac = autocomplete_new(); new_room->jid_ac = autocomplete_new(); new_room->nick_changes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); @@ -858,6 +864,54 @@ muc_anonymity_type(const char *const room) } } +/* + * Return the list of jid affiliated as member in the room + */ +GList* +muc_members(const char *const room) +{ + ChatRoom *chat_room = g_hash_table_lookup(rooms, room); + if (chat_room) { + return g_hash_table_get_keys(chat_room->members); + } else { + return NULL; + } +} + +void +muc_members_add(const char *const room, const char *const jid) +{ + ChatRoom *chat_room = g_hash_table_lookup(rooms, room); + if (chat_room) { + if (g_hash_table_insert(chat_room->members, strdup(jid), NULL)) { +#ifdef HAVE_OMEMO + Jid *our_jid = jid_create(connection_get_fulljid()); + if (strcmp(jid, our_jid->barejid) != 0) { + omemo_start_session(jid); + } +#endif + } + } +} + +void +muc_members_remove(const char *const room, const char *const jid) +{ + ChatRoom *chat_room = g_hash_table_lookup(rooms, room); + if (chat_room) { + g_hash_table_remove(chat_room->members, jid); + } +} + +void +muc_members_update(const char *const room, const char *const jid, const char *const affiliation) +{ + if (strcmp(affiliation, "outcast") == 0 || strcmp(affiliation, "none") == 0) { + muc_members_remove(room, jid); + } else if (strcmp(affiliation, "member") == 0 || strcmp(affiliation, "admin") == 0 || strcmp(affiliation, "owner") == 0) { + muc_members_add(room, jid); + } +} static void _free_room(ChatRoom *room) @@ -871,6 +925,9 @@ _free_room(ChatRoom *room) if (room->roster) { g_hash_table_destroy(room->roster); } + if (room->members) { + g_hash_table_destroy(room->members); + } autocomplete_free(room->nick_ac); autocomplete_free(room->jid_ac); if (room->nick_changes) { diff --git a/src/xmpp/muc.h b/src/xmpp/muc.h index 35e90aaa..1f9e088b 100644 --- a/src/xmpp/muc.h +++ b/src/xmpp/muc.h @@ -159,4 +159,9 @@ char* muc_affiliation_str(const char *const room); muc_member_type_t muc_member_type(const char *const room); muc_anonymity_type_t muc_anonymity_type(const char *const room); +GList* muc_members(const char *const room); +void muc_members_add(const char *const room, const char *const jid); +void muc_members_remove(const char *const room, const char *const jid); +void muc_members_update(const char *const room, const char *const jid, const char *const affiliation); + #endif diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 6210b384..0667dead 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -212,7 +212,7 @@ void iq_submit_room_config(ProfConfWin *confwin); void iq_room_config_cancel(ProfConfWin *confwin); void iq_send_ping(const char *const target); void iq_room_info_request(const char *const room, gboolean display_result); -void iq_room_affiliation_list(const char *const room, char *affiliation); +void iq_room_affiliation_list(const char *const room, char *affiliation, bool show); void iq_room_affiliation_set(const char *const room, const char *const jid, char *affiliation, const char *const reason); void iq_room_kick_occupant(const char *const room, const char *const nick, const char *const reason); diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index a9b45321..7c054f3f 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -206,7 +206,7 @@ void iq_send_caps_request_for_jid(const char * const to, const char * const id, void iq_send_caps_request_legacy(const char * const to, const char * const id, const char * const node, const char * const ver) {} void iq_room_info_request(const char * const room, gboolean display) {} -void iq_room_affiliation_list(const char * const room, char *affiliation) {} +void iq_room_affiliation_list(const char * const room, char *affiliation, bool show) {} void iq_room_affiliation_set(const char * const room, const char * const jid, char *affiliation, const char * const reason) {} void iq_room_kick_occupant(const char * const room, const char * const nick, const char * const reason) {}