diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index e760a0b7..710e71b2 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -75,6 +75,7 @@ static struct _jabber_conn_t { } jabber_conn; static GHashTable *available_resources; +static GSList *disco_items; // for auto reconnect static struct { @@ -113,6 +114,18 @@ void _connection_free_saved_account(void); void _connection_free_saved_details(void); void _connection_free_session_data(void); +static void +_info_destroy(DiscoInfo *info) +{ + if (info) { + free(info->item); + if (info->features) { + g_hash_table_remove_all(info->features); + } + free(info); + } +} + void jabber_init(void) { @@ -125,6 +138,7 @@ jabber_init(void) presence_sub_requests_init(); caps_init(); available_resources = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)resource_destroy); + disco_items = NULL; xmpp_initialize(); } @@ -323,6 +337,18 @@ jabber_set_connection_status(jabber_conn_status_t status) jabber_conn.conn_status = status; } +GSList* +jabber_get_disco_items(void) +{ + return (disco_items); +} + +void +jabber_set_disco_items(GSList *_disco_items) +{ + disco_items = _disco_items; +} + xmpp_conn_t* connection_get_conn(void) { @@ -420,6 +446,8 @@ _connection_free_saved_details(void) void _connection_free_session_data(void) { + g_slist_free_full(disco_items, (GDestroyNotify)_info_destroy); + disco_items = NULL; g_hash_table_remove_all(available_resources); chat_sessions_clear(); presence_clear_sub_requests(); @@ -651,6 +679,14 @@ _connection_handler(xmpp_conn_t *const conn, const xmpp_conn_event_t status, con roster_request(); bookmark_request(); + // items discovery + DiscoInfo *info = malloc(sizeof(struct disco_info_t)); + info->item = strdup(jabber_conn.domain); + info->features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); + disco_items = g_slist_append(disco_items, info); + iq_disco_info_request_onconnect(info->item); + iq_disco_items_request_onconnect(jabber_conn.domain); + if (prefs_get_boolean(PREF_CARBONS)){ iq_enable_carbons(); } diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 1242c6cf..a2566945 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -87,6 +87,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 _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); static int _destroy_room_result_id_handler(xmpp_stanza_t *const stanza, void *const userdata); @@ -307,6 +308,21 @@ iq_disco_info_request(gchar *jid) xmpp_stanza_release(iq); } +void +iq_disco_info_request_onconnect(gchar *jid) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = create_unique_id("disco_info_onconnect"); + xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, jid, NULL); + + id_handler_add(id, _disco_info_response_id_handler_onconnect, NULL); + + free(id); + + send_iq_stanza(iq); + xmpp_stanza_release(iq); +} + void iq_last_activity_request(gchar *jid) { @@ -428,6 +444,15 @@ iq_disco_items_request(gchar *jid) xmpp_stanza_release(iq); } +void +iq_disco_items_request_onconnect(gchar *jid) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, "discoitemsreq_onconnect", jid); + send_iq_stanza(iq); + xmpp_stanza_release(iq); +} + void iq_send_software_version(const char *const fulljid) { @@ -1817,6 +1842,68 @@ _disco_info_response_id_handler(xmpp_stanza_t *const stanza, void *const userdat return 0; } +static int +_disco_info_response_id_handler_onconnect(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + const char *type = xmpp_stanza_get_type(stanza); + + if (from) { + log_info("Received disco#info response from: %s", from); + } else { + log_info("Received disco#info response"); + } + + // handle error responses + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + char *error_message = stanza_get_error_message(stanza); + if (from) { + log_error("Service discovery failed for %s: %s", from, error_message); + } else { + log_error("Service discovery failed: %s", error_message); + } + free(error_message); + return 0; + } + + xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + + if (query) { + xmpp_stanza_t *child = xmpp_stanza_get_children(query); + + GSList *disco_items = jabber_get_disco_items(); + DiscoInfo *disco_info; + if (disco_items && (g_slist_length(disco_items) > 0)) { + while (disco_items) { + disco_info = disco_items->data; + if (g_strcmp0(disco_info->item, from) == 0) { + break; + } + disco_items = g_slist_next(disco_items); + if (!disco_items) { + log_error("No matching disco item found for %s", from); + return 1; + } + } + } else { + return 1; + } + + while (child) { + const char *stanza_name = xmpp_stanza_get_name(child); + if (g_strcmp0(stanza_name, STANZA_NAME_FEATURE) == 0) { + const char *var = xmpp_stanza_get_attribute(child, STANZA_ATTR_VAR); + if (var) { + g_hash_table_add(disco_info->features, strdup(var)); + } + } + child = xmpp_stanza_get_next(child); + } + } + + return 0; +} + static void _disco_items_result_handler(xmpp_stanza_t *const stanza) { @@ -1825,7 +1912,7 @@ _disco_items_result_handler(xmpp_stanza_t *const stanza) const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); GSList *items = NULL; - if ((g_strcmp0(id, "confreq") == 0) || (g_strcmp0(id, "discoitemsreq") == 0)) { + if ((g_strcmp0(id, "confreq") == 0) || (g_strcmp0(id, "discoitemsreq") == 0) || (g_strcmp0(id, "discoitemsreq_onconnect") == 0)) { log_debug("Response to query: %s", id); xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); @@ -1857,6 +1944,19 @@ _disco_items_result_handler(xmpp_stanza_t *const stanza) cons_show_room_list(items, from); } else if (g_strcmp0(id, "discoitemsreq") == 0) { cons_show_disco_items(items, from); + } else if (g_strcmp0(id, "discoitemsreq_onconnect") == 0) { + GSList *res_items = items; + if (res_items && (g_slist_length(res_items) > 0)) { + while (res_items) { + DiscoItem *item = res_items->data; + DiscoInfo *info = malloc(sizeof(struct disco_info_t)); + info->item = strdup(item->jid); + info->features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); + jabber_set_disco_items(g_slist_append(jabber_get_disco_items(), info)); + iq_disco_info_request_onconnect(info->item); + res_items = g_slist_next(res_items); + } + } } g_slist_free_full(items, (GDestroyNotify)_item_destroy); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 9dcc1798..28cb755b 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -95,6 +95,11 @@ typedef struct disco_identity_t { char *category; } DiscoIdentity; +typedef struct disco_info_t { + char *item; + GHashTable *features; +} DiscoInfo; + typedef enum { FIELD_HIDDEN, FIELD_TEXT_SINGLE, @@ -150,6 +155,8 @@ const char* jabber_get_fulljid(void); const char* jabber_get_domain(void); jabber_conn_status_t jabber_get_connection_status(void); void jabber_set_connection_status(jabber_conn_status_t status); +GSList* jabber_get_disco_items(void); +void jabber_set_disco_items(GSList *disco_items); char* jabber_get_presence_message(void); char* jabber_get_account_name(void); GList* jabber_get_available_resources(void); @@ -194,7 +201,9 @@ void iq_disable_carbons(void); void iq_send_software_version(const char *const fulljid); void iq_room_list_request(gchar *conferencejid); void iq_disco_info_request(gchar *jid); +void iq_disco_info_request_onconnect(gchar *jid); void iq_disco_items_request(gchar *jid); +void iq_disco_items_request_onconnect(gchar *jid); void iq_last_activity_request(gchar *jid); void iq_set_autoping(int seconds); void iq_confirm_instant_room(const char *const room_jid);