# HG changeset patch # User Myhailo Danylenko # Date 1361073590 -7200 # Node ID db0a96df96b5c3507bafae7b807487b459c03972 # Parent 50173578fddcef87dd4c8aabb7342842e2c0a8e0 Notify user when cancelling requests Call handler with empty results, if destroying disco request object not by user request (eg. on reconnect or module unloading) diff -r 50173578fddc -r db0a96df96b5 disco.c --- a/disco.c Thu Oct 18 22:47:45 2012 +0300 +++ b/disco.c Sun Feb 17 05:59:50 2013 +0200 @@ -62,19 +62,20 @@ // private types // -// lm message handler userdata -typedef struct { - disco_info_handler_t handler; - gpointer data; - GDestroyNotify notify; -} disco_info_reply_handler_t; +#define DISCO_INFO_REQUEST ( 0 ) +#define DISCO_ITEMS_REQUEST ( 1 ) // lm message handler userdata typedef struct { - disco_items_handler_t handler; - gpointer data; - GDestroyNotify notify; -} disco_items_reply_handler_t; + LmMessageHandler * reply_handler; + gpointer data; + GDestroyNotify notify; + guint type; + union { + disco_info_handler_t info; + disco_items_handler_t items; + } handler; +} disco_request_t; // user request disco handler userdata (common for info and items) typedef struct { @@ -87,41 +88,39 @@ // #ifdef MCABBER_API_HAVE_CMD_ID -static gpointer disco_cmid = NULL; +static gpointer disco_cmid = NULL; #endif - -static guint disco_cid = 0; -static guint disco_hid = 0; -static GSList *reply_handlers = NULL; +static guint disco_cid = 0; +static guint disco_hid = 0; +static GSList * disco_requests = NULL; // // destroyers // -static void disco_info_reply_handler_destroy_notify (gpointer data) +static void disco_request_free ( disco_request_t * cb ) { - disco_info_reply_handler_t *cb = data; - if (cb -> notify) - cb -> notify (cb -> data); - g_slice_free (disco_info_reply_handler_t, cb); + disco_requests = g_slist_remove ( disco_requests, cb ); + + if ( cb -> reply_handler ) { + lm_message_handler_invalidate ( cb -> reply_handler ); +#ifdef HAVE_LM_CONNECTION_UNREGISTER_REPLY_HANDLER + if ( lconnection ) + lm_connection_unregister_reply_handler ( lconnection, cb -> reply_handler ); +#endif + } + if ( cb -> notify ) + cb -> notify ( cb -> data ); + g_slice_free ( disco_request_t, cb ); return; } -static void disco_items_reply_handler_destroy_notify (gpointer data) +static void disco_handler_destroy_notify ( gpointer data ) { - disco_items_reply_handler_t *cb = data; - if (cb -> notify) - cb -> notify (cb -> data); - g_slice_free (disco_items_reply_handler_t, cb); - return; -} - -static void disco_handler_destroy_notify (gpointer data) -{ - disco_handler_t *cb = data; - g_free (cb -> jid); - g_free (cb -> node); - g_slice_free (disco_handler_t, cb); + disco_handler_t * cb = data; + g_free ( cb -> jid ); + g_free ( cb -> node ); + g_slice_free ( disco_handler_t, cb ); return; } @@ -129,52 +128,88 @@ // lm reply handlers // -static LmHandlerResult disco_info_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata) +static LmHandlerResult disco_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata) { - disco_info_reply_handler_t *cb = udata; - - reply_handlers = g_slist_remove (reply_handlers, handler); + disco_request_t * cb = udata; + LmHandlerResult result = LM_HANDLER_RESULT_REMOVE_MESSAGE; switch (lm_message_get_sub_type (message)) { case LM_MESSAGE_SUB_TYPE_RESULT: { - LmMessageNode *node = lm_message_get_node (message); - GSList *identities = NULL; - GSList *features = NULL; + LmMessageNode *node = lm_message_get_node (message); node = lm_message_node_get_child (node, "query"); - // check xmlns - if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_INFO)) - break; + if ( cb -> type == DISCO_INFO_REQUEST ) { + + GSList * identities = NULL; + GSList * features = NULL; + + // check xmlns + if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_INFO)) + break; + + // parse request results + if (node->children) + for (node = node->children; node; node = node->next) + if (!strcasecmp (node->name, "identity")) { + disco_identity_t *identity = g_slice_new (disco_identity_t); - // parse request results - if (node->children) - for (node = node->children; node; node = node->next) - if (!strcasecmp (node->name, "identity")) { - disco_identity_t *identity = g_slice_new (disco_identity_t); + identity -> category = lm_message_node_get_attribute (node, "category"); + identity -> type = lm_message_node_get_attribute (node, "type"); + identity -> name = lm_message_node_get_attribute (node, "name"); + identity -> reserved = NULL; + + identities = g_slist_append (identities, identity); + } else if (!strcasecmp (node->name, "feature")) + features = g_slist_insert_sorted (features, (gpointer) lm_message_node_get_attribute (node, "var"), (GCompareFunc) g_strcmp0); - identity -> category = lm_message_node_get_attribute (node, "category"); - identity -> type = lm_message_node_get_attribute (node, "type"); - identity -> name = lm_message_node_get_attribute (node, "name"); - identity -> reserved = NULL; + // call handler + cb -> handler.info (identities, features, cb -> data); + + { // free resources + GSList *iel; + + for (iel = identities; iel; iel = iel -> next) + g_slice_free (disco_identity_t, iel -> data); - identities = g_slist_append (identities, identity); - } else if (!strcasecmp (node->name, "feature")) - features = g_slist_insert_sorted (features, (gpointer) lm_message_node_get_attribute (node, "var"), (GCompareFunc) g_strcmp0); + g_slist_free (identities); + g_slist_free (features); + } + + } else { // items request + + GSList * items = NULL; - // call handler - cb -> handler (identities, features, cb -> data); + // check xmlns + if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_ITEMS)) + break; + + // parse request results + if (node->children) + for (node = node->children; node; node = node->next) + if (!strcasecmp (node->name, "item")) { + disco_item_t *item = g_slice_new (disco_item_t); - { // free resources - GSList *iel; + item -> name = lm_message_node_get_attribute (node, "name"); + item -> jid = lm_message_node_get_attribute (node, "jid"); + item -> node = lm_message_node_get_attribute (node, "node"); + + items = g_slist_append (items, item); + } - for (iel = identities; iel; iel = iel -> next) - g_slice_free (disco_identity_t, iel -> data); + // call handler + cb -> handler.items (items, cb -> data); + + { // free resources + GSList *iel; - g_slist_free (identities); - g_slist_free (features); + for (iel = items; iel; iel = iel -> next) + g_slice_free (disco_item_t, iel -> data); + + g_slist_free (items); + } } } @@ -196,95 +231,30 @@ reason = "undefined"; // XXX: we need to inform user, but do we really need to print this on every possible error? - scr_log_print (LPRINT_LOGNORM, "disco: Service info discovery for %s failed: %s - %s", from, type, reason); + if ( cb -> type == DISCO_INFO_REQUEST ) { + + scr_log_print (LPRINT_LOGNORM, "disco: Service info discovery for %s failed: %s - %s", from, type, reason); + cb -> handler.info (NULL, NULL, cb -> data); - cb -> handler (NULL, NULL, cb -> data); + } else { + + scr_log_print (LPRINT_LOGNORM, "disco: Service items discovery for %s failed: %s - %s", from, type, reason); + cb -> handler.items (NULL, cb -> data); + } } break; default: - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + + result = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + + break; } - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmHandlerResult disco_items_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata) -{ - disco_items_reply_handler_t *cb = udata; - - reply_handlers = g_slist_remove (reply_handlers, handler); - - switch (lm_message_get_sub_type (message)) { - case LM_MESSAGE_SUB_TYPE_RESULT: - - { - LmMessageNode *node = lm_message_get_node (message); - GSList *items = NULL; - - node = lm_message_node_get_child (node, "query"); - - // check xmlns - if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_ITEMS)) - break; - - // parse request results - if (node->children) - for (node = node->children; node; node = node->next) - if (!strcasecmp (node->name, "item")) { - disco_item_t *item = g_slice_new (disco_item_t); - - item -> name = lm_message_node_get_attribute (node, "name"); - item -> jid = lm_message_node_get_attribute (node, "jid"); - item -> node = lm_message_node_get_attribute (node, "node"); - - items = g_slist_append (items, item); - } - - // call handler - cb -> handler (items, cb -> data); - - { // free resources - GSList *iel; - - for (iel = items; iel; iel = iel -> next) - g_slice_free (disco_item_t, iel -> data); - - g_slist_free (items); - } - } - - break; - - case LM_MESSAGE_SUB_TYPE_ERROR: - - { - LmMessageNode *node = lm_message_get_node (message); - const gchar *from = lm_message_node_get_attribute (node, "from"); - const gchar *type; - const gchar *reason; - - node = lm_message_node_get_child (node, "error"); - type = lm_message_node_get_attribute (node, "type"); - if (node->children) - reason = node->children->name; - else - reason = "undefined"; - - // XXX: we need to inform user, but do we really need to print this on every possible error? - scr_log_print (LPRINT_LOGNORM, "disco: Service items discovery for %s failed: %s - %s", from, type, reason); - - cb -> handler (NULL, cb -> data); - } - - break; - - default: - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; + disco_request_free ( cb ); + + return result; } // @@ -304,8 +274,10 @@ gpointer disco_info_request (const gchar *jid, const gchar *dnode, disco_info_handler_t handler, gpointer userdata, GDestroyNotify notify) { if (!handler || !xmpp_is_online ()) { - if (notify) - notify (userdata); + if ( handler ) + handler ( NULL, NULL, userdata ); // XXX + if ( notify ) + notify ( userdata ); return NULL; } @@ -333,10 +305,11 @@ #endif { // send request - LmMessage *request; - LmMessageNode *node; - LmMessageHandler *lhandler; - GError *error = NULL; + LmMessage * request; + LmMessageNode * node; + LmMessageHandler * lhandler; + GError * error = NULL; + disco_request_t * cb = g_slice_new ( disco_request_t ); request = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); node = lm_message_get_node (request); @@ -345,47 +318,49 @@ if (dnode) lm_message_node_set_attribute (node, "node", dnode); - { - disco_info_reply_handler_t *cb = g_slice_new (disco_info_reply_handler_t); + lhandler = lm_message_handler_new (disco_reply_handler, cb, NULL); - lhandler = lm_message_handler_new (disco_info_reply_handler, cb, disco_info_reply_handler_destroy_notify); - - cb -> handler = handler; - cb -> data = userdata; - cb -> notify = notify; + cb -> reply_handler = lhandler; + cb -> type = DISCO_INFO_REQUEST; + cb -> handler.info = handler; + cb -> data = userdata; + cb -> notify = notify; - reply_handlers = g_slist_append (reply_handlers, lhandler); + disco_requests = g_slist_append ( disco_requests, cb ); - lm_connection_send_with_reply (lconnection, request, lhandler, &error); + lm_connection_send_with_reply (lconnection, request, lhandler, &error); - if (error) { - // XXX destroy handler and return NULL? - scr_log_print (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message); - g_error_free (error); - } - - lm_message_handler_unref (lhandler); + if (error) { + scr_log_print (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message); + g_error_free (error); + handler ( NULL, NULL, userdata ); // XXX + disco_request_free ( cb ); + cb = NULL; } + lm_message_handler_unref (lhandler); lm_message_unref (request); - return lhandler; + return cb; } } gpointer disco_items_request (const gchar *jid, const gchar *dnode, disco_items_handler_t handler, gpointer userdata, GDestroyNotify notify) { if (!handler || !xmpp_is_online ()) { - if (notify) - notify (userdata); + if ( handler ) + handler ( NULL, userdata ); // XXX + if ( notify ) + notify ( userdata ); return NULL; } { // send request - LmMessage *request; - LmMessageNode *node; - GError *error = NULL; - LmMessageHandler *lhandler; + LmMessage * request; + LmMessageNode * node; + LmMessageHandler * lhandler; + GError * error = NULL; + disco_request_t * cb = g_slice_new (disco_request_t); request = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); node = lm_message_get_node (request); @@ -394,49 +369,38 @@ if (dnode) lm_message_node_set_attribute (node, "node", dnode); - { - disco_items_reply_handler_t *cb = g_slice_new (disco_items_reply_handler_t); + lhandler = lm_message_handler_new (disco_reply_handler, cb, NULL); - lhandler = lm_message_handler_new (disco_items_reply_handler, cb, disco_items_reply_handler_destroy_notify); - - cb -> handler = handler; - cb -> data = userdata; - cb -> notify = notify; + cb -> reply_handler = lhandler; + cb -> type = DISCO_ITEMS_REQUEST; + cb -> handler.items = handler; + cb -> data = userdata; + cb -> notify = notify; - reply_handlers = g_slist_append (reply_handlers, lhandler); + disco_requests = g_slist_append ( disco_requests, cb ); - lm_connection_send_with_reply (lconnection, request, lhandler, &error); + lm_connection_send_with_reply (lconnection, request, lhandler, &error); - if (error) { - // XXX destroy handler and return NULL? - scr_log_print (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message); - g_error_free (error); - } - - lm_message_handler_unref (lhandler); + if (error) { + scr_log_print (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message); + g_error_free (error); + handler ( NULL, userdata ); + disco_request_free ( cb ); + cb = NULL; } + lm_message_handler_unref (lhandler); lm_message_unref (request); - return lhandler; + return cb; } } -void disco_cancel_request (gpointer id) +void disco_cancel_request ( gpointer id ) { - GSList *hel; - - for (hel = reply_handlers; hel; hel = hel -> next) { - if (hel -> data == id) { - LmMessageHandler *handler = id; - reply_handlers = g_slist_remove (reply_handlers, handler); - lm_message_handler_invalidate (handler); -#ifdef HAVE_LM_CONNECTION_UNREGISTER_REPLY_HANDLER - if (lconnection) - lm_connection_unregister_reply_handler (lconnection, handler); -#endif - return; - } + if ( g_slist_find ( disco_requests, id ) ) { + disco_request_t * cb = id; + disco_request_free ( cb ); } return; @@ -620,28 +584,27 @@ // module mechanics // -static void disco_unregister_handlers (void) +static void disco_cancel_requests (void) { - GSList *hel; + GSList * rel = disco_requests; - for (hel = reply_handlers; hel; hel = hel -> next) { - LmMessageHandler *handler = hel -> data; - lm_message_handler_invalidate (handler); -#ifdef HAVE_LM_CONNECTION_UNREGISTER_REPLY_HANDLER - if (lconnection) - lm_connection_unregister_reply_handler (lconnection, handler); -#endif + while ( rel ) { + disco_request_t * cb = rel -> data; + rel = rel -> next; + if ( cb -> type == DISCO_INFO_REQUEST ) + cb -> handler.info ( NULL, NULL, cb -> data ); + else + cb -> handler.items ( NULL, cb -> data ); + disco_request_free ( cb ); } - g_slist_free (reply_handlers); - reply_handlers = NULL; - return; } static guint disco_hh (const gchar *htype, hk_arg_t *args, gpointer ignore) { - disco_unregister_handlers (); + disco_cancel_requests (); + return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } @@ -689,7 +652,7 @@ hk_del_handler (HOOK_PRE_DISCONNECT, disco_hid); // unregister handlers - disco_unregister_handlers (); + disco_cancel_requests (); return; }