diff options
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/presence.c | 560 |
1 files changed, 311 insertions, 249 deletions
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c index 31fc6cde..6b13f4a7 100644 --- a/src/xmpp/presence.c +++ b/src/xmpp/presence.c @@ -78,7 +78,6 @@ static void _available_handler(xmpp_stanza_t *const stanza); void _send_caps_request(char *node, char *caps_key, char *id, char *from); static void _send_room_presence(xmpp_stanza_t *presence); - static void _send_presence_stanza(xmpp_stanza_t *const stanza); void @@ -87,52 +86,6 @@ presence_sub_requests_init(void) sub_requests_ac = autocomplete_new(); } -static int -_presence_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) -{ - log_debug("Presence stanza handler fired"); - - char *text; - size_t text_size; - xmpp_stanza_to_text(stanza, &text, &text_size); - gboolean cont = plugins_on_presence_stanza_receive(text); - xmpp_free(connection_get_ctx(), text); - if (!cont) { - return 1; - } - - const char *type = xmpp_stanza_get_type(stanza); - - if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { - _presence_error_handler(stanza); - } - - if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { - _unavailable_handler(stanza); - } - - if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBE) == 0) { - _subscribe_handler(stanza); - } - - if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBED) == 0) { - _subscribed_handler(stanza); - } - - if (g_strcmp0(type, STANZA_TYPE_UNSUBSCRIBED) == 0) { - _unsubscribed_handler(stanza); - } - - xmpp_stanza_t *mucuser = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); - if (mucuser) { - _muc_user_handler(stanza); - } - - _available_handler(stanza); - - return 1; -} - void presence_handlers_init(void) { @@ -146,13 +99,10 @@ presence_subscription(const char *const jid, const jabber_subscr_t action) { assert(jid != NULL); - xmpp_ctx_t * const ctx = connection_get_ctx(); - const char *type = NULL; - Jid *jidp = jid_create(jid); - autocomplete_remove(sub_requests_ac, jidp->barejid); + const char *type = NULL; switch (action) { case PRESENCE_SUBSCRIBE: @@ -168,20 +118,27 @@ presence_subscription(const char *const jid, const jabber_subscr_t action) type = STANZA_TYPE_UNSUBSCRIBED; break; default: - log_warning("Attempt to send unknown subscription action: %s", jid); break; } + if (!type) { + log_error("Attempt to send unknown subscription action: %s", jid); + return; + } + xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *presence = xmpp_presence_new(ctx); + char *id = create_unique_id("sub"); xmpp_stanza_set_id(presence, id); + free(id); + xmpp_stanza_set_type(presence, type); xmpp_stanza_set_to(presence, jidp->barejid); + jid_destroy(jidp); + _send_presence_stanza(presence); - xmpp_stanza_release(presence); - jid_destroy(jidp); - free(id); + xmpp_stanza_release(presence); } GSList* @@ -212,20 +169,17 @@ gboolean presence_sub_request_exists(const char *const bare_jid) { gboolean result = FALSE; - GSList *requests_p = autocomplete_create_list(sub_requests_ac); - GSList *requests = requests_p; - while (requests) { - if (strcmp(requests->data, bare_jid) == 0) { + GSList *requests = autocomplete_create_list(sub_requests_ac); + GSList *curr = requests; + while (curr) { + if (strcmp(curr->data, bare_jid) == 0) { result = TRUE; break; } - requests = g_slist_next(requests); - } - - if (requests_p) { - g_slist_free_full(requests_p, free); + curr = g_slist_next(curr); } + g_slist_free_full(requests, free); return result; } @@ -250,16 +204,19 @@ presence_send(const resource_presence_t presence_type, const char *const msg, co log_debug("Updating presence: %s", string_from_resource_presence(presence_type)); } - xmpp_ctx_t * const ctx = connection_get_ctx(); - const int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); - const char *show = stanza_get_presence_string_from_type(presence_type); - connection_set_presence_msg(msg); + + const int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); connection_set_priority(pri); + xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *presence = xmpp_presence_new(ctx); + char *id = create_unique_id("presence"); xmpp_stanza_set_id(presence, id); + free(id); + + const char *show = stanza_get_presence_string_from_type(presence_type); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, msg); @@ -268,21 +225,28 @@ presence_send(const resource_presence_t presence_type, const char *const msg, co xmpp_stanza_t *x = xmpp_stanza_new(ctx); xmpp_stanza_set_name(x, STANZA_NAME_X); xmpp_stanza_set_ns(x, STANZA_NS_SIGNED); + xmpp_stanza_t *signed_text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(signed_text, signed_status); + xmpp_stanza_add_child(x, signed_text); xmpp_stanza_release(signed_text); + xmpp_stanza_add_child(presence, x); xmpp_stanza_release(x); } stanza_attach_priority(ctx, presence, pri); + if (idle > 0) { stanza_attach_last_activity(ctx, presence, idle); } + stanza_attach_caps(ctx, presence); + _send_presence_stanza(presence); _send_room_presence(presence); + xmpp_stanza_release(presence); // set last presence for account @@ -290,10 +254,10 @@ presence_send(const resource_presence_t presence_type, const char *const msg, co if (last == NULL) { last = STANZA_TEXT_ONLINE; } + char *account = session_get_account_name(); accounts_set_last_presence(account, last); accounts_set_last_status(account, msg); - free(id); } static void @@ -301,23 +265,21 @@ _send_room_presence(xmpp_stanza_t *presence) { GList *rooms = muc_rooms(); GList *curr = rooms; - while (curr) { const char *room = curr->data; const char *nick = muc_nick(room); if (nick) { char *full_room_jid = create_fulljid(room, nick); - xmpp_stanza_set_to(presence, full_room_jid); log_debug("Sending presence to room: %s", full_room_jid); - _send_presence_stanza(presence); free(full_room_jid); + + _send_presence_stanza(presence); } curr = g_list_next(curr); } - g_list_free(rooms); } @@ -325,16 +287,14 @@ void presence_join_room(const char *const room, const char *const nick, const char *const passwd) { Jid *jid = jid_create_from_bare_and_resource(room, nick); - log_debug("Sending room join presence to: %s", jid->fulljid); - xmpp_ctx_t *ctx = connection_get_ctx(); - resource_presence_t presence_type = - accounts_get_last_presence(session_get_account_name()); + + resource_presence_t presence_type = accounts_get_last_presence(session_get_account_name()); const char *show = stanza_get_presence_string_from_type(presence_type); char *status = connection_get_presence_msg(); - int pri = accounts_get_priority_for_presence_type(session_get_account_name(), - presence_type); + int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); + xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *presence = stanza_create_room_join_presence(ctx, jid->fulljid, passwd); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, status); @@ -342,8 +302,8 @@ presence_join_room(const char *const room, const char *const nick, const char *c stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); - xmpp_stanza_release(presence); + xmpp_stanza_release(presence); jid_destroy(jid); } @@ -354,25 +314,22 @@ presence_change_room_nick(const char *const room, const char *const nick) assert(nick != NULL); log_debug("Sending room nickname change to: %s, nick: %s", room, nick); - xmpp_ctx_t *ctx = connection_get_ctx(); - resource_presence_t presence_type = - accounts_get_last_presence(session_get_account_name()); + resource_presence_t presence_type = accounts_get_last_presence(session_get_account_name()); const char *show = stanza_get_presence_string_from_type(presence_type); char *status = connection_get_presence_msg(); - int pri = accounts_get_priority_for_presence_type(session_get_account_name(), - presence_type); - + int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); char *full_room_jid = create_fulljid(room, nick); - xmpp_stanza_t *presence = - stanza_create_room_newnick_presence(ctx, full_room_jid); + + xmpp_ctx_t *ctx = connection_get_ctx(); + xmpp_stanza_t *presence = stanza_create_room_newnick_presence(ctx, full_room_jid); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, status); stanza_attach_priority(ctx, presence, pri); stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); - xmpp_stanza_release(presence); + xmpp_stanza_release(presence); free(full_room_jid); } @@ -381,37 +338,83 @@ presence_leave_chat_room(const char *const room_jid) { assert(room_jid != NULL); + char *nick = muc_nick(room_jid); + if (!nick) { + log_error("Could not get nickname for room: %s", room_jid); + return; + } + log_debug("Sending room leave presence to: %s", room_jid); + xmpp_ctx_t *ctx = connection_get_ctx(); - char *nick = muc_nick(room_jid); + xmpp_stanza_t *presence = stanza_create_room_leave_presence(ctx, room_jid, nick); + + _send_presence_stanza(presence); + + xmpp_stanza_release(presence); +} + +static int +_presence_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) +{ + log_debug("Presence stanza handler fired"); + + char *text = NULL; + size_t text_size; + xmpp_stanza_to_text(stanza, &text, &text_size); + + gboolean cont = plugins_on_presence_stanza_receive(text); + xmpp_free(connection_get_ctx(), text); + if (!cont) { + return 1; + } + + const char *type = xmpp_stanza_get_type(stanza); + + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + _presence_error_handler(stanza); + } + + if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { + _unavailable_handler(stanza); + } + + if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBE) == 0) { + _subscribe_handler(stanza); + } + + if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBED) == 0) { + _subscribed_handler(stanza); + } + + if (g_strcmp0(type, STANZA_TYPE_UNSUBSCRIBED) == 0) { + _unsubscribed_handler(stanza); + } - if (nick) { - xmpp_stanza_t *presence = stanza_create_room_leave_presence(ctx, room_jid, nick); - _send_presence_stanza(presence); - xmpp_stanza_release(presence); + xmpp_stanza_t *mucuser = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (mucuser) { + _muc_user_handler(stanza); } + + _available_handler(stanza); + + return 1; } static void _presence_error_handler(xmpp_stanza_t *const stanza) { - const char *id = xmpp_stanza_get_id(stanza); - const char *from = xmpp_stanza_get_from(stanza); - xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR); - xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X); const char *xmlns = NULL; + xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X); if (x) { xmlns = xmpp_stanza_get_ns(x); } - const char *type = NULL; - if (error_stanza) { - type = xmpp_stanza_get_type(error_stanza); - } + + const char *from = xmpp_stanza_get_from(stanza); + xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR); // handle MUC join errors if (g_strcmp0(xmlns, STANZA_NS_MUC) == 0) { - Jid *fulljid = jid_create(from); - const char *error_cond = NULL; xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_ns(error_stanza, STANZA_NS_STANZAS); if (reason_st) { @@ -421,19 +424,19 @@ _presence_error_handler(xmpp_stanza_t *const stanza) error_cond = "unknown"; } + Jid *fulljid = jid_create(from); log_info("Error joining room: %s, reason: %s", fulljid->barejid, error_cond); if (muc_active(fulljid->barejid)) { muc_leave(fulljid->barejid); } cons_show_error("Error joining room %s, reason: %s", fulljid->barejid, error_cond); jid_destroy(fulljid); + return; } - // stanza_get_error never returns NULL - char *err_msg = stanza_get_error_message(stanza); - GString *log_msg = g_string_new("presence stanza error received"); + const char *id = xmpp_stanza_get_id(stanza); if (id) { g_string_append(log_msg, " id="); g_string_append(log_msg, id); @@ -442,10 +445,18 @@ _presence_error_handler(xmpp_stanza_t *const stanza) g_string_append(log_msg, " from="); g_string_append(log_msg, from); } + + const char *type = NULL; + if (error_stanza) { + type = xmpp_stanza_get_type(error_stanza); + } if (type) { g_string_append(log_msg, " type="); g_string_append(log_msg, type); } + + // stanza_get_error never returns NULL + char *err_msg = stanza_get_error_message(stanza); g_string_append(log_msg, " error="); g_string_append(log_msg, err_msg); @@ -462,14 +473,17 @@ _presence_error_handler(xmpp_stanza_t *const stanza) free(err_msg); } - static void _unsubscribed_handler(xmpp_stanza_t *const stanza) { const char *from = xmpp_stanza_get_from(stanza); - Jid *from_jid = jid_create(from); + if (!from) { + log_warning("Unsubscribed presence handler received with no from attribute"); + return; + } log_debug("Unsubscribed presence handler fired for %s", from); + Jid *from_jid = jid_create(from); sv_ev_subscription(from_jid->barejid, PRESENCE_UNSUBSCRIBED); autocomplete_remove(sub_requests_ac, from_jid->barejid); @@ -480,9 +494,13 @@ static void _subscribed_handler(xmpp_stanza_t *const stanza) { const char *from = xmpp_stanza_get_from(stanza); - Jid *from_jid = jid_create(from); + if (!from) { + log_warning("Subscribed presence handler received with no from attribute"); + return; + } log_debug("Subscribed presence handler fired for %s", from); + Jid *from_jid = jid_create(from); sv_ev_subscription(from_jid->barejid, PRESENCE_SUBSCRIBED); autocomplete_remove(sub_requests_ac, from_jid->barejid); @@ -493,6 +511,9 @@ static void _subscribe_handler(xmpp_stanza_t *const stanza) { const char *from = xmpp_stanza_get_from(stanza); + if (!from) { + log_warning("Subscribe presence handler received with no from attribute", from); + } log_debug("Subscribe presence handler fired for %s", from); Jid *from_jid = jid_create(from); @@ -514,6 +535,9 @@ _unavailable_handler(xmpp_stanza_t *const stanza) xmpp_conn_t *conn = connection_get_conn(); const char *jid = xmpp_conn_get_jid(conn); const char *from = xmpp_stanza_get_from(stanza); + if (!from) { + log_warning("Unavailable presence received with no from attribute"); + } log_debug("Unavailable presence handler fired for %s", from); Jid *my_jid = jid_create(jid); @@ -524,9 +548,8 @@ _unavailable_handler(xmpp_stanza_t *const stanza) return; } - char *status_str = stanza_get_status(stanza, NULL); - if (strcmp(my_jid->barejid, from_jid->barejid) !=0) { + char *status_str = stanza_get_status(stanza, NULL); if (from_jid->resourcepart) { sv_ev_contact_offline(from_jid->barejid, from_jid->resourcepart, status_str); @@ -534,13 +557,13 @@ _unavailable_handler(xmpp_stanza_t *const stanza) } else { sv_ev_contact_offline(from_jid->barejid, "__prof_default", status_str); } + free(status_str); } else { if (from_jid->resourcepart) { connection_remove_available_resource(from_jid->resourcepart); } } - free(status_str); jid_destroy(my_jid); jid_destroy(from_jid); } @@ -661,174 +684,213 @@ _available_handler(xmpp_stanza_t *const stanza) void _send_caps_request(char *node, char *caps_key, char *id, char *from) { - xmpp_ctx_t *ctx = connection_get_ctx(); - - if (node) { - log_debug("Node string: %s.", node); - if (!caps_cache_contains(caps_key)) { - log_debug("Capabilities not cached for '%s', sending discovery IQ.", from); - xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, from, node); - iq_send_stanza(iq); - xmpp_stanza_release(iq); - } else { - log_debug("Capabilities already cached, for %s", caps_key); - } - } else { + if (!node) { log_debug("No node string, not sending discovery IQ."); + return; + } + + log_debug("Node string: %s.", node); + if (caps_cache_contains(caps_key)) { + log_debug("Capabilities already cached, for %s", caps_key); + return; } + + log_debug("Capabilities not cached for '%s', sending discovery IQ.", from); + xmpp_ctx_t *ctx = connection_get_ctx(); + xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, from, node); + + iq_send_stanza(iq); + + xmpp_stanza_release(iq); } static void -_muc_user_handler(xmpp_stanza_t *const stanza) +_muc_user_self_handler(xmpp_stanza_t *stanza) { - inp_nonblocking(TRUE); + const char *from = xmpp_stanza_get_from(stanza); + Jid *from_jid = jid_create(from); + + log_debug("Room self presence received from %s", from_jid->fulljid); + + char *room = from_jid->barejid; const char *type = xmpp_stanza_get_type(stanza); - const char *from = xmpp_stanza_get_from(stanza); + if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { - // handler still fires if error - if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { - return; + // handle nickname change + const char *new_nick = stanza_get_new_nick(stanza); + if (new_nick) { + muc_nick_change_start(room, new_nick); + + // handle left room + } else { + GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); + + // room destroyed + if (stanza_room_destroyed(stanza)) { + const char *new_jid = stanza_get_muc_destroy_alternative_room(stanza); + char *password = stanza_get_muc_destroy_alternative_password(stanza); + char *reason = stanza_get_muc_destroy_reason(stanza); + sv_ev_room_destroyed(room, new_jid, password, reason); + free(password); + free(reason); + + // kicked from room + } else if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) { + const char *actor = stanza_get_actor(stanza); + char *reason = stanza_get_reason(stanza); + sv_ev_room_kicked(room, actor, reason); + free(reason); + + // banned from room + } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) { + const char *actor = stanza_get_actor(stanza); + char *reason = stanza_get_reason(stanza); + sv_ev_room_banned(room, actor, reason); + free(reason); + + // normal exit + } else { + sv_ev_leave_room(room); + } + + g_slist_free_full(status_codes, free); + } + } else { + gboolean config_required = stanza_muc_requires_config(stanza); + const char *actor = stanza_get_actor(stanza); + char *reason = stanza_get_reason(stanza); + char *nick = from_jid->resourcepart; + char *show_str = stanza_get_show(stanza, "online"); + char *status_str = stanza_get_status(stanza, NULL); + const char *jid = NULL; + const char *role = NULL; + const char *affiliation = NULL; + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM); + if (item) { + jid = xmpp_stanza_get_attribute(item, "jid"); + role = xmpp_stanza_get_attribute(item, "role"); + affiliation = xmpp_stanza_get_attribute(item, "affiliation"); + } + } + sv_ev_muc_self_online(room, nick, config_required, role, affiliation, actor, reason, jid, show_str, status_str); + free(show_str); + free(status_str); + free(reason); } +} - // invalid from attribute +static void +_muc_user_occupant_handler(xmpp_stanza_t *stanza) +{ + const char *from = xmpp_stanza_get_from(stanza); Jid *from_jid = jid_create(from); - if (from_jid == NULL || from_jid->resourcepart == NULL) { - jid_destroy(from_jid); - return; - } + + log_debug("Room presence received from %s", from_jid->fulljid); char *room = from_jid->barejid; char *nick = from_jid->resourcepart; - - char *show_str = stanza_get_show(stanza, "online"); char *status_str = stanza_get_status(stanza, NULL); - const char *jid = NULL; - const char *role = NULL; - const char *affiliation = NULL; - - xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); - if (x) { - xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM); - if (item) { - jid = xmpp_stanza_get_attribute(item, "jid"); - role = xmpp_stanza_get_attribute(item, "role"); - affiliation = xmpp_stanza_get_attribute(item, "affiliation"); - } - } - - // handle self presence - if (stanza_is_muc_self_presence(stanza, connection_get_fulljid())) { - log_debug("Room self presence received from %s", from_jid->fulljid); + const char *type = xmpp_stanza_get_type(stanza); + if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { - // self unavailable - if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { + // handle nickname change + const char *new_nick = stanza_get_new_nick(stanza); + if (new_nick) { + muc_occupant_nick_change_start(room, new_nick, nick); - // handle nickname change - const char *new_nick = stanza_get_new_nick(stanza); - if (new_nick) { - muc_nick_change_start(room, new_nick); + // handle left room + } else { + GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); + + // kicked from room + if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) { + const char *actor = stanza_get_actor(stanza); + char *reason = stanza_get_reason(stanza); + sv_ev_room_occupent_kicked(room, nick, actor, reason); + free(reason); + + // banned from room + } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) { + const char *actor = stanza_get_actor(stanza); + char *reason = stanza_get_reason(stanza); + sv_ev_room_occupent_banned(room, nick, actor, reason); + free(reason); + + // normal exit } else { - GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); - - // room destroyed - if (stanza_room_destroyed(stanza)) { - const char *new_jid = stanza_get_muc_destroy_alternative_room(stanza); - char *password = stanza_get_muc_destroy_alternative_password(stanza); - char *reason = stanza_get_muc_destroy_reason(stanza); - sv_ev_room_destroyed(room, new_jid, password, reason); - free(password); - free(reason); - - // kicked from room - } else if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) { - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_room_kicked(room, actor, reason); - free(reason); - - // banned from room - } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) { - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_room_banned(room, actor, reason); - free(reason); - - // normal exit - } else { - sv_ev_leave_room(room); - } - - g_slist_free_full(status_codes, free); + sv_ev_room_occupant_offline(room, nick, "offline", status_str); } - // self online - } else { - gboolean config_required = stanza_muc_requires_config(stanza); - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_muc_self_online(room, nick, config_required, role, affiliation, actor, reason, jid, show_str, status_str); - free(reason); + g_slist_free_full(status_codes, free); } - // handle presence from room members + // room occupant online } else { - log_debug("Room presence received from %s", from_jid->fulljid); - - if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { + // send disco info for capabilities, if not cached + XMPPCaps *caps = stanza_parse_caps(stanza); + if (caps) { + log_info("Presence contains capabilities."); + _handle_caps(from, caps); + } + stanza_free_caps(caps); + + const char *actor = stanza_get_actor(stanza); + char *show_str = stanza_get_show(stanza, "online"); + char *reason = stanza_get_reason(stanza); + const char *jid = NULL; + const char *role = NULL; + const char *affiliation = NULL; + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM); + if (item) { + jid = xmpp_stanza_get_attribute(item, "jid"); + role = xmpp_stanza_get_attribute(item, "role"); + affiliation = xmpp_stanza_get_attribute(item, "affiliation"); + } + } + sv_ev_muc_occupant_online(room, nick, jid, role, affiliation, actor, reason, show_str, status_str); + free(show_str); + free(reason); + } - // handle nickname change - const char *new_nick = stanza_get_new_nick(stanza); - if (new_nick) { - muc_occupant_nick_change_start(room, new_nick, nick); + free(status_str); +} - // handle left room - } else { - GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); - - // kicked from room - if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) { - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_room_occupent_kicked(room, nick, actor, reason); - free(reason); - - // banned from room - } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) { - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_room_occupent_banned(room, nick, actor, reason); - free(reason); - - // normal exit - } else { - sv_ev_room_occupant_offline(room, nick, "offline", status_str); - } - - g_slist_free_full(status_codes, free); - } +static void +_muc_user_handler(xmpp_stanza_t *const stanza) +{ + inp_nonblocking(TRUE); - // room occupant online - } else { - // send disco info for capabilities, if not cached - XMPPCaps *caps = stanza_parse_caps(stanza); - if (caps) { - log_info("Presence contains capabilities."); - _handle_caps(from, caps); - } - stanza_free_caps(caps); + const char *type = xmpp_stanza_get_type(stanza); + // handler still fires if error + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + return; + } - const char *actor = stanza_get_actor(stanza); - char *reason = stanza_get_reason(stanza); - sv_ev_muc_occupant_online(room, nick, jid, role, affiliation, actor, reason, show_str, status_str); - free(reason); - } + const char *from = xmpp_stanza_get_from(stanza); + if (!from) { + log_warning("MUC User stanza received with no from attribute"); + return; } - free(show_str); - free(status_str); + Jid *from_jid = jid_create(from); + if (from_jid == NULL || from_jid->resourcepart == NULL) { + log_warning("MUC User stanza received with invalid from attribute: %s", from); + jid_destroy(from_jid); + return; + } jid_destroy(from_jid); + + if (stanza_is_muc_self_presence(stanza, connection_get_fulljid())) { + _muc_user_self_handler(stanza); + } else { + _muc_user_occupant_handler(stanza); + } } static void |