Notify user when cancelling requests
authorMyhailo Danylenko <isbear@ukrpost.net>
Sun, 17 Feb 2013 05:59:50 +0200
changeset 60 db0a96df96b5
parent 59 50173578fddc
child 61 b0b6e8307e38
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)
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;
 }