disco.c
changeset 60 db0a96df96b5
parent 59 50173578fddc
child 63 d644c08bbc27
--- 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;
 }