From 4b45f9cf581ea7f801a69de40c591d2b9c76a4b1 Mon Sep 17 00:00:00 2001 From: "jabber.developer2" Date: Mon, 16 Feb 2026 11:10:09 +0300 Subject: [PATCH] fix(xmpp): handle disco#items error responses (XEP-0030 Section 7) Refactor disco#items request handling to use id_handler pattern (consistent with disco#info): - Use dynamic stanza ID via connection_create_stanza_id() - Register _disco_items_response_id_handler via iq_id_handler_add() - Handle type='error' responses with cons_show_error() - Split onconnect handler to use namespace-based routing separately Previously, disco#items used a fixed stanza ID with namespace-based routing, which could not intercept error responses (error stanzas don't contain the query child element with the disco#items namespace). Closes #91 --- src/xmpp/iq.c | 99 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 34795692..012d2472 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -129,6 +129,7 @@ static void _ping_get_handler(xmpp_stanza_t* const stanza); static int _version_result_id_handler(xmpp_stanza_t* const stanza, void* const userdata); static int _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata); static int _disco_info_response_id_handler_onconnect(xmpp_stanza_t* const stanza, void* const userdata); +static int _disco_items_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata); static int _http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const upload_ctx); static int _last_activity_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata); static int _room_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata); @@ -203,7 +204,11 @@ _iq_handler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const us _disco_items_get_handler(stanza); } if (discoitems && (g_strcmp0(type, STANZA_TYPE_RESULT) == 0)) { - _disco_items_result_handler(stanza); + // Only handle onconnect here, user requests use id_handler + const char* id = xmpp_stanza_get_id(stanza); + if (g_strcmp0(id, "discoitemsreq_onconnect") == 0) { + _disco_items_result_handler(stanza); + } } xmpp_stanza_t* lastactivity = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_LASTACTIVITY); @@ -637,7 +642,11 @@ void iq_disco_items_request(const char* jid) { xmpp_ctx_t* const ctx = connection_get_ctx(); - xmpp_stanza_t* iq = stanza_create_disco_items_iq(ctx, "discoitemsreq", jid, NULL); + auto_char char* id = connection_create_stanza_id(); + xmpp_stanza_t* iq = stanza_create_disco_items_iq(ctx, id, jid, NULL); + + iq_id_handler_add(id, _disco_items_response_id_handler, NULL, NULL); + iq_send_stanza(iq); xmpp_stanza_release(iq); } @@ -2514,13 +2523,12 @@ _http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const userda static void _disco_items_result_handler(xmpp_stanza_t* const stanza) { - log_debug("Received disco#items response"); + // This handler is only for onconnect disco#items requests + log_debug("Received disco#items response (onconnect)"); const char* id = xmpp_stanza_get_id(stanza); - const char* from = xmpp_stanza_get_from(stanza); GSList* items = NULL; - if ((g_strcmp0(id, "discoitemsreq") != 0) && (g_strcmp0(id, "discoitemsreq_onconnect") != 0)) { - log_warning("_disco_items_result_handler: Received unexpected disco id: %s", id); + if (g_strcmp0(id, "discoitemsreq_onconnect") != 0) { return; } @@ -2555,23 +2563,80 @@ _disco_items_result_handler(xmpp_stanza_t* const stanza) child = xmpp_stanza_get_next(child); } - if (g_strcmp0(id, "discoitemsreq") == 0) { - cons_show_disco_items(items, from); - } else if (g_strcmp0(id, "discoitemsreq_onconnect") == 0) { - connection_set_disco_items(items); + connection_set_disco_items(items); - while (late_delivery_windows) { - LateDeliveryUserdata* del_data = late_delivery_windows->data; - _iq_mam_request(del_data->win, del_data->startdate, del_data->enddate); - free(del_data); - late_delivery_windows = g_slist_delete_link(late_delivery_windows, - late_delivery_windows); - } + while (late_delivery_windows) { + LateDeliveryUserdata* del_data = late_delivery_windows->data; + _iq_mam_request(del_data->win, del_data->startdate, del_data->enddate); + free(del_data); + late_delivery_windows = g_slist_delete_link(late_delivery_windows, + late_delivery_windows); } g_slist_free_full(items, (GDestroyNotify)_item_destroy); } +static int +_disco_items_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata) +{ + const char* from = xmpp_stanza_get_from(stanza); + const char* type = xmpp_stanza_get_type(stanza); + + if (from) { + log_debug("Received disco#items response from: %s", from); + } else { + log_debug("Received disco#items response"); + } + + // Handle error responses + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + auto_char char* error_message = stanza_get_error_message(stanza); + if (from) { + cons_show_error("Service discovery failed for %s: %s", from, error_message); + } else { + cons_show_error("Service discovery failed: %s", error_message); + } + return 0; + } + + // Handle result responses + xmpp_stanza_t* query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + if (query == NULL) { + cons_show_disco_items(NULL, from); + return 0; + } + + GSList* items = NULL; + xmpp_stanza_t* child = xmpp_stanza_get_children(query); + + while (child) { + const char* stanza_name = xmpp_stanza_get_name(child); + if (stanza_name && (g_strcmp0(stanza_name, STANZA_NAME_ITEM) == 0)) { + const char* item_jid = xmpp_stanza_get_attribute(child, STANZA_ATTR_JID); + if (item_jid) { + DiscoItem* item = malloc(sizeof(struct disco_item_t)); + if (item) { + item->jid = strdup(item_jid); + const char* item_name = xmpp_stanza_get_attribute(child, STANZA_ATTR_NAME); + if (item_name) { + item->name = strdup(item_name); + } else { + item->name = NULL; + } + items = g_slist_append(items, item); + } + } + } + + child = xmpp_stanza_get_next(child); + } + + cons_show_disco_items(items, from); + g_slist_free_full(items, (GDestroyNotify)_item_destroy); + + return 0; +} + void iq_feature_retrieval_complete_handler(void) {