disco.c
changeset 16 e903da874e63
parent 15 2aa6a333b0af
child 17 2780d5e74400
equal deleted inserted replaced
15:2aa6a333b0af 16:e903da874e63
    24 #include <string.h>
    24 #include <string.h>
    25 
    25 
    26 #include "commands.h"
    26 #include "commands.h"
    27 #include "logprint.h"
    27 #include "logprint.h"
    28 #include "utils.h"
    28 #include "utils.h"
       
    29 #include "hooks.h"
    29 #include "xmpp.h"
    30 #include "xmpp.h"
    30 #include "compl.h"
    31 #include "compl.h"
    31 #include "xmpp_defines.h"
    32 #include "xmpp_defines.h"
    32 #include "screen.h"
    33 #include "screen.h"
    33 #include "hbuf.h"
    34 #include "hbuf.h"
    34 
    35 
    35 static guint             disco_cid                 = 0;
    36 #include "disco.h"
    36 static LmMessageHandler *disco_info_reply_handler  = NULL;
    37 
    37 static LmMessageHandler *disco_items_reply_handler = NULL;
    38 //
    38 
    39 // private types
    39 static LmHandlerResult disco_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata)
    40 //
    40 {
    41 
    41 	gboolean info_request = handler == disco_info_reply_handler ? TRUE : FALSE;
    42 // lm message handler userdata
       
    43 typedef struct {
       
    44 	disco_info_handler_t handler;
       
    45 	gpointer             data;
       
    46 	GDestroyNotify       notify;
       
    47 } disco_info_reply_handler_t;
       
    48 
       
    49 // lm message handler userdata
       
    50 typedef struct {
       
    51 	disco_items_handler_t handler;
       
    52 	gpointer              data;
       
    53 	GDestroyNotify        notify;
       
    54 } disco_items_reply_handler_t;
       
    55 
       
    56 // user request disco handler userdata (common for info and items)
       
    57 typedef struct {
       
    58 	gchar *jid;
       
    59 	gchar *node;
       
    60 } disco_handler_t;
       
    61 
       
    62 //
       
    63 // globals
       
    64 //
       
    65 
       
    66 static guint   disco_cid      = 0;
       
    67 static GSList *reply_handlers = NULL;
       
    68 
       
    69 //
       
    70 // destroyers
       
    71 //
       
    72 
       
    73 static void disco_info_reply_handler_destroy_notify (gpointer data)
       
    74 {
       
    75 	disco_info_reply_handler_t *cb = data;
       
    76 	if (cb -> notify)
       
    77 		cb -> notify (cb -> data);
       
    78 	g_free (cb);
       
    79 	return;
       
    80 }
       
    81 
       
    82 static void disco_items_reply_handler_destroy_notify (gpointer data)
       
    83 {
       
    84 	disco_items_reply_handler_t *cb = data;
       
    85 	if (cb -> notify)
       
    86 		cb -> notify (cb -> data);
       
    87 	g_free (cb);
       
    88 	return;
       
    89 }
       
    90 
       
    91 static void disco_handler_destroy_notify (gpointer data)
       
    92 {
       
    93 	disco_handler_t *cb = data;
       
    94 	g_free (cb -> jid);
       
    95 	g_free (cb -> node);
       
    96 	g_free (cb);
       
    97 	return;
       
    98 }
       
    99 
       
   100 //
       
   101 // lm reply handlers
       
   102 //
       
   103 
       
   104 static LmHandlerResult disco_info_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata)
       
   105 {
       
   106 	disco_info_reply_handler_t *cb = udata;
       
   107 
       
   108 	reply_handlers = g_slist_remove (reply_handlers, handler);
    42 
   109 
    43 	switch (lm_message_get_sub_type (message)) {
   110 	switch (lm_message_get_sub_type (message)) {
    44 	case LM_MESSAGE_SUB_TYPE_RESULT:
   111 	case LM_MESSAGE_SUB_TYPE_RESULT:
    45 
   112 
    46 		{
   113 		{
    47 			LmMessageNode *node  = lm_message_get_node (message);
   114 			LmMessageNode *node       = lm_message_get_node (message);
    48 			const gchar   *from  = lm_message_node_get_attribute (node, "from");
   115 			const gchar   *from       = lm_message_node_get_attribute (node, "from");
    49 			GString       *info;
   116 			GSList        *identities = NULL;
       
   117 			GSList        *features   = NULL;
    50 
   118 
    51 			node = lm_message_node_get_child (node, "query");
   119 			node = lm_message_node_get_child (node, "query");
    52 
   120 
    53 			// check xmlns
   121 			// check xmlns
    54 			if (!node || strcmp (lm_message_node_get_attribute (node, "xmlns"), info_request ? NS_DISCO_INFO : NS_DISCO_ITEMS))
   122 			if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_INFO))
    55 				break;
   123 				break;
    56 
   124 
    57 			{ // header for user message
   125 			// parse request results
    58 				const gchar *rnode = lm_message_node_get_attribute (node, "node");
   126 			if (node->children)
    59 
   127 				for (node = node->children; node; node = node->next)
    60 				// create user message string
   128 					if (!strcasecmp (node->name, "identity")) {
    61 				info = g_string_new (NULL);
   129 						disco_identity_t *identity = g_new (disco_identity_t, 1);
    62 				g_string_printf (info, "Service discovery %s results for %s", info_request ? "info" : "items", from);
   130 
    63 				if (rnode)
   131 						identity -> category = lm_message_node_get_attribute (node, "category");
    64 					g_string_append_printf (info, " (%s):", rnode);
   132 						identity -> type     = lm_message_node_get_attribute (node, "type");
    65 				else
   133 						identity -> name     = lm_message_node_get_attribute (node, "name");
    66 					g_string_append (info, ":");
   134 						identity -> reserved = NULL;
       
   135 
       
   136 						identities = g_slist_append (identities, identity);
       
   137 					} else if (!strcasecmp (node->name, "feature"))
       
   138 						features = g_slist_insert_sorted (features, (gpointer) lm_message_node_get_attribute (node, "var"), (GCompareFunc) g_strcmp0);
       
   139 			
       
   140 			// call handler
       
   141 			cb -> handler (identities, features, cb -> data);
       
   142 
       
   143 			{ // free resources
       
   144 				GSList *iel;
       
   145 
       
   146 				for (iel = identities; iel; iel = iel -> next)
       
   147 					g_free (iel -> data);
       
   148 
       
   149 				g_slist_free (identities);
       
   150 				g_slist_free (features);
    67 			}
   151 			}
    68 
       
    69 			if (node->children) {
       
    70 				// parse request results
       
    71 				if (info_request) { // info
       
    72 					GString *identities = g_string_new (NULL);
       
    73 					GString *features   = g_string_new (NULL);
       
    74 
       
    75 					for (node = node->children; node; node = node->next) {
       
    76 						if (!strcasecmp (node->name, "identity")) {
       
    77 							const gchar *category = lm_message_node_get_attribute (node, "category");
       
    78 							const gchar *type     = lm_message_node_get_attribute (node, "type");
       
    79 							const gchar *name     = lm_message_node_get_attribute (node, "name");
       
    80 
       
    81 							g_string_append_printf (identities, "\n    [%s (%s)] %s", category ? category : "none", type ? type : "none", name ? name : "");
       
    82 
       
    83 						} else if (!strcasecmp (node->name, "feature")) {
       
    84 							const gchar *var = lm_message_node_get_attribute (node, "var");
       
    85 
       
    86 							g_string_append_printf (features, "\n    [%s]", var ? var : "none");
       
    87 						}
       
    88 					}
       
    89 
       
    90 					if (identities->len)
       
    91 						g_string_append_printf (info, "\n  Identities:%s", identities->str);
       
    92 					if (features->len)
       
    93 						g_string_append_printf (info, "\n  Features:%s", features->str);
       
    94 
       
    95 					g_string_free (identities, TRUE);
       
    96 					g_string_free (features, TRUE);
       
    97 
       
    98 				} else { // items
       
    99 					for (node = node->children; node; node = node->next) {
       
   100 						const gchar *name  = lm_message_node_get_attribute (node, "name");
       
   101 						const gchar *jid   = lm_message_node_get_attribute (node, "jid");
       
   102 						const gchar *inode = lm_message_node_get_attribute (node, "node");
       
   103 	
       
   104 						if (inode)
       
   105 							g_string_append_printf (info, "\n  [%s (%s)] %s", jid ? jid : "none", inode, name ? name : "");
       
   106 						else
       
   107 							g_string_append_printf (info, "\n  [%s] %s", jid ? jid : "none", name ? name : "");
       
   108 					}
       
   109 				}
       
   110 			} else
       
   111 				g_string_append (info, "\n  Empty result.");
       
   112 			
       
   113 			{ // print to buddy's buffer
       
   114 				gchar *jid = jidtodisp (from);
       
   115 
       
   116 				// XXX check for message size? conference server lists may be huge...
       
   117 				scr_WriteIncomingMessage (jid, info->str, 0, HBB_PREFIX_INFO, 0); // NO conversion from utf-8
       
   118 
       
   119 				g_free (jid);
       
   120 			}
       
   121 
       
   122 			g_string_free (info, TRUE);
       
   123 		}
   152 		}
   124 
   153 
   125 		break;
   154 		break;
   126 
   155 
   127 	case LM_MESSAGE_SUB_TYPE_ERROR:
   156 	case LM_MESSAGE_SUB_TYPE_ERROR:
   137 			if (node->children)
   166 			if (node->children)
   138 				reason = node->children->name;
   167 				reason = node->children->name;
   139 			else
   168 			else
   140 				reason = "undefined";
   169 				reason = "undefined";
   141 
   170 
   142 			{ // print to buddy's buffer
   171 			// XXX: we need to inform user, but do we really need to print this on every possible error?
   143 				gchar *jid  = jidtodisp (from);
   172 			scr_LogPrint (LPRINT_LOGNORM, "disco: Service info discovery for %s failed: %s - %s", from, type, reason);
   144 				gchar *mesg = g_strdup_printf ("Service %s discovery for %s failed: %s - %s", info_request ? "info" : "items", from, type, reason);
   173 
   145 
   174 			cb -> handler (NULL, NULL, cb -> data);
   146 				scr_WriteIncomingMessage (jid, mesg, 0, HBB_PREFIX_INFO, 0);
       
   147 
       
   148 				g_free (mesg);
       
   149 				g_free (jid);
       
   150 			}
       
   151 		}
   175 		}
   152 
   176 
   153 		break;
   177 		break;
   154 
   178 
   155 	default:
   179 	default:
   156 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
   180 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   181 	}
       
   182 
       
   183 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   184 }
       
   185 
       
   186 static LmHandlerResult disco_items_reply_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata)
       
   187 {
       
   188 	disco_items_reply_handler_t *cb = udata;
       
   189 
       
   190 	reply_handlers = g_slist_remove (reply_handlers, handler);
       
   191 
       
   192 	switch (lm_message_get_sub_type (message)) {
       
   193 	case LM_MESSAGE_SUB_TYPE_RESULT:
       
   194 
       
   195 		{
       
   196 			LmMessageNode *node  = lm_message_get_node (message);
       
   197 			const gchar   *from  = lm_message_node_get_attribute (node, "from");
       
   198 			GSList        *items = NULL;
       
   199 
       
   200 			node = lm_message_node_get_child (node, "query");
       
   201 
       
   202 			// check xmlns
       
   203 			if (!node || g_strcmp0 (lm_message_node_get_attribute (node, "xmlns"), NS_DISCO_ITEMS))
       
   204 				break;
       
   205 
       
   206 			// parse request results
       
   207 			if (node->children)
       
   208 				for (node = node->children; node; node = node->next)
       
   209 					if (!strcasecmp (node->name, "item")) {
       
   210 						disco_item_t *item = g_new (disco_item_t, 1);
       
   211 
       
   212 						item -> name = lm_message_node_get_attribute (node, "name");
       
   213 						item -> jid  = lm_message_node_get_attribute (node, "jid");
       
   214 						item -> node = lm_message_node_get_attribute (node, "node");
       
   215 
       
   216 						items = g_slist_append (items, item);
       
   217 					}
       
   218 			
       
   219 			// call handler
       
   220 			cb -> handler (items, cb -> data);
       
   221 
       
   222 			{ // free resources
       
   223 				GSList *iel;
       
   224 
       
   225 				for (iel = items; iel; iel = iel -> next)
       
   226 					g_free (iel -> data);
       
   227 
       
   228 				g_slist_free (items);
       
   229 			}
       
   230 		}
       
   231 
   157 		break;
   232 		break;
       
   233 
       
   234 	case LM_MESSAGE_SUB_TYPE_ERROR:
       
   235 
       
   236 		{
       
   237 			LmMessageNode *node   = lm_message_get_node (message);
       
   238 			const gchar   *from   = lm_message_node_get_attribute (node, "from");
       
   239 			const gchar   *type;
       
   240 			const gchar   *reason;
       
   241 
       
   242 			node = lm_message_node_get_child (node, "error");
       
   243 			type = lm_message_node_get_attribute (node, "type");
       
   244 			if (node->children)
       
   245 				reason = node->children->name;
       
   246 			else
       
   247 				reason = "undefined";
       
   248 
       
   249 			// XXX: we need to inform user, but do we really need to print this on every possible error?
       
   250 			scr_LogPrint (LPRINT_LOGNORM, "disco: Service items discovery for %s failed: %s - %s", from, type, reason);
       
   251 
       
   252 			cb -> handler (NULL, cb -> data);
       
   253 		}
       
   254 
       
   255 		break;
       
   256 
       
   257 	default:
       
   258 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
   158 	}
   259 	}
   159 
   260 
   160 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
   261 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
   161 }
   262 }
       
   263 
       
   264 //
       
   265 // disco requests sending
       
   266 //
       
   267 
       
   268 static void disco_features_cb (gconstpointer data, gpointer userdata)
       
   269 {
       
   270 	const gchar  *feature  = data;
       
   271 	GSList      **features = userdata;
       
   272 	*features = g_slist_insert_sorted (*features, (gpointer) feature, (GCompareFunc) g_strcmp0);
       
   273 	return;
       
   274 }
       
   275 
       
   276 void disco_info_request (const gchar *jid, const gchar *dnode, disco_info_handler_t handler, gpointer userdata, GDestroyNotify notify)
       
   277 {
       
   278 	if (!handler) {
       
   279 		if (notify)
       
   280 			notify (userdata);
       
   281 		return;
       
   282 	}
       
   283 
       
   284 	if (0 && !dnode) { // FIXME: no way to get identity(ies) from caps
       
   285 		gchar       *bjid     = jidtodisp (jid);
       
   286 		GSList      *buddy    = roster_find (bjid, jidsearch, ROSTER_TYPE_USER | ROSTER_TYPE_ROOM | ROSTER_TYPE_AGENT);
       
   287 		const gchar *resource = strchr (jid, JID_RESOURCE_SEPARATOR);
       
   288 
       
   289 		g_free (bjid);
       
   290 
       
   291 		if (buddy) {
       
   292 			const gchar *hash = buddy_resource_getcaps (BUDDATA(buddy), resource); // ?? will it all work?
       
   293 			if (hash) { // cached result
       
   294 				GSList *identities = NULL;
       
   295 				GSList *features   = NULL;
       
   296 				caps_foreach_feature (hash, disco_features_cb, &features);
       
   297 				handler (identities, features, userdata);
       
   298 				if (notify)
       
   299 					notify (userdata);
       
   300 				return;
       
   301 			}
       
   302 		}
       
   303 	}
       
   304 
       
   305 	{ // send request
       
   306 		LmMessage     *request;
       
   307 		LmMessageNode *node;
       
   308 		GError        *error   = NULL;
       
   309 
       
   310 		request = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
       
   311 		node    = lm_message_get_node (request);
       
   312 		node    = lm_message_node_add_child (node, "query", NULL);
       
   313 		lm_message_node_set_attribute (node, "xmlns", NS_DISCO_INFO);
       
   314 		if (dnode)
       
   315 			lm_message_node_set_attribute (node, "node", dnode);
       
   316 
       
   317 		{
       
   318 			disco_info_reply_handler_t *cb       = g_new (disco_info_reply_handler_t, 1);
       
   319 			LmMessageHandler           *lhandler = lm_message_handler_new (disco_info_reply_handler, cb, disco_info_reply_handler_destroy_notify);
       
   320 
       
   321 			cb -> handler = handler;
       
   322 			cb -> data    = userdata;
       
   323 			cb -> notify  = notify;
       
   324 
       
   325 			reply_handlers = g_slist_append (reply_handlers, lhandler);
       
   326 
       
   327 			lm_connection_send_with_reply (lconnection, request, lhandler, &error);
       
   328 
       
   329 			if (error) {
       
   330 				scr_LogPrint (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message);
       
   331 				g_error_free (error);
       
   332 			}
       
   333 
       
   334 			lm_message_handler_unref (lhandler);
       
   335 		}
       
   336 
       
   337 		lm_message_unref (request);
       
   338 	}
       
   339 
       
   340 	return;
       
   341 }
       
   342 
       
   343 void disco_items_request (const gchar *jid, const gchar *dnode, disco_items_handler_t handler, gpointer userdata, GDestroyNotify notify)
       
   344 {
       
   345 	if (!handler) {
       
   346 		if (notify)
       
   347 			notify (userdata);
       
   348 		return;
       
   349 	}
       
   350 
       
   351 	{ // send request
       
   352 		LmMessage     *request;
       
   353 		LmMessageNode *node;
       
   354 		GError        *error   = NULL;
       
   355 
       
   356 		request = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
       
   357 		node    = lm_message_get_node (request);
       
   358 		node    = lm_message_node_add_child (node, "query", NULL);
       
   359 		lm_message_node_set_attribute (node, "xmlns", NS_DISCO_ITEMS);
       
   360 		if (dnode)
       
   361 			lm_message_node_set_attribute (node, "node", dnode);
       
   362 
       
   363 		{
       
   364 			disco_items_reply_handler_t *cb       = g_new (disco_items_reply_handler_t, 1);
       
   365 			LmMessageHandler            *lhandler = lm_message_handler_new (disco_items_reply_handler, cb, disco_items_reply_handler_destroy_notify);
       
   366 
       
   367 			cb -> handler = handler;
       
   368 			cb -> data    = userdata;
       
   369 			cb -> notify  = notify;
       
   370 
       
   371 			reply_handlers = g_slist_append (reply_handlers, lhandler);
       
   372 
       
   373 			lm_connection_send_with_reply (lconnection, request, lhandler, &error);
       
   374 
       
   375 			if (error) {
       
   376 				scr_LogPrint (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message);
       
   377 				g_error_free (error);
       
   378 			}
       
   379 
       
   380 			lm_message_handler_unref (lhandler);
       
   381 		}
       
   382 
       
   383 		lm_message_unref (request);
       
   384 	}
       
   385 
       
   386 	return;
       
   387 }
       
   388 
       
   389 //
       
   390 // user requests handlers (print results)
       
   391 //
       
   392 
       
   393 static void disco_info_handler (GSList *identities, GSList *features, gpointer udata)
       
   394 {
       
   395 	disco_handler_t *cb   = udata;
       
   396 	GString         *info = g_string_new ("Service discovery info results for ");
       
   397 
       
   398 	g_string_append (info, cb -> jid);
       
   399 	if (cb -> node)
       
   400 		g_string_append_printf (info, " (%s):", cb -> node);
       
   401 	else
       
   402 		g_string_append_c (info, ':');
       
   403 
       
   404 	{
       
   405 		GString *tmp = g_string_new (NULL);
       
   406 		GSList  *el;
       
   407 
       
   408 		// compose identities part
       
   409 		for (el = identities; el; el = el -> next) {
       
   410 			disco_identity_t *identity = el -> data;
       
   411 			g_string_append_printf (tmp, "\n    [%s (%s)] %s",
       
   412 			                        identity -> category ? identity -> category : "none",
       
   413 			                        identity -> type ? identity -> type : "none",
       
   414 									identity -> name ? identity -> name : "");
       
   415 		}
       
   416 
       
   417 		if (tmp -> len) {
       
   418 			g_string_append_printf (info, "\n  Identities:%s", tmp -> str);
       
   419 			g_string_truncate (info, 0);
       
   420 		}
       
   421 
       
   422 		// compose features part
       
   423 		for (el = features; el; el = el -> next) {
       
   424 			gchar *feature = el -> data;
       
   425 			g_string_append_printf (tmp, "\n    [%s]", feature ? feature : "none");
       
   426 		}
       
   427 
       
   428 		if (tmp -> len)
       
   429 			g_string_append_printf (info, "\n  Features:%s", tmp -> str);
       
   430 
       
   431 		g_string_free (tmp, TRUE);
       
   432 	}
       
   433 
       
   434 	{ // print to buddy's buffer
       
   435 		gchar *bjid = jidtodisp (cb -> jid);
       
   436 
       
   437 		scr_WriteIncomingMessage (bjid, info -> str, 0, HBB_PREFIX_INFO, 0); // NO conversion from utf-8
       
   438 
       
   439 		g_free (bjid);
       
   440 	}
       
   441 
       
   442 	g_string_free (info, TRUE);
       
   443 
       
   444 	return;
       
   445 }
       
   446 
       
   447 static void disco_items_handler (GSList *items, gpointer udata)
       
   448 {
       
   449 	disco_handler_t *cb   = udata;
       
   450 	GString         *info = g_string_new ("Service discovery items results for ");
       
   451 
       
   452 	g_string_append (info, cb -> jid);
       
   453 	if (cb -> node)
       
   454 		g_string_append_printf (info, " (%s):", cb -> node);
       
   455 	else
       
   456 		g_string_append_c (info, ':');
       
   457 
       
   458 	{
       
   459 		GSList  *el;
       
   460 
       
   461 		// add items info
       
   462 		for (el = items; el; el = el -> next) {
       
   463 			disco_item_t *item = el -> data;
       
   464 			if (item -> node)
       
   465 				g_string_append_printf (info, "\n  [%s (%s)] %s",
       
   466 				                        item -> jid ? item -> jid : "none", item -> node,
       
   467 										item -> name ? item -> name : "");
       
   468 			else
       
   469 				g_string_append_printf (info, "\n  [%s] %s",
       
   470 				                        item -> jid ? item -> jid : "none",
       
   471 										item -> name ? item -> name : "");
       
   472 		}
       
   473 	}
       
   474 
       
   475 	{ // print to buddy's buffer
       
   476 		gchar *bjid = jidtodisp (cb -> jid);
       
   477 
       
   478 		scr_WriteIncomingMessage (bjid, info -> str, 0, HBB_PREFIX_INFO, 0); // NO conversion from utf-8
       
   479 
       
   480 		g_free (bjid);
       
   481 	}
       
   482 
       
   483 	g_string_free (info, TRUE);
       
   484 
       
   485 	return;
       
   486 }
       
   487 
       
   488 //
       
   489 // command
       
   490 //
   162 
   491 
   163 static void do_disco (char *arg)
   492 static void do_disco (char *arg)
   164 {
   493 {
   165 	char **args = split_arg (arg, 3, 0);
   494 	char **args = split_arg (arg, 3, 0);
   166 	int    info = -1;
   495 	int    info = -1;
   171 		info = 0;
   500 		info = 0;
   172 	else
   501 	else
   173 		scr_LogPrint (LPRINT_NORMAL, "Unknown subcomand.");
   502 		scr_LogPrint (LPRINT_NORMAL, "Unknown subcomand.");
   174 
   503 
   175 	if (info != -1) {
   504 	if (info != -1) {
   176 		LmMessageHandler *handler;
   505 		char *to    = NULL;
   177 		LmMessage        *request;
   506 		char *dnode = NULL;
   178 		char             *to      = NULL;
       
   179 		char             *dnode   = NULL;
       
   180 		
   507 		
   181 		if (args[0] && args[1]) {
   508 		if (args[0] && args[1]) {
   182 			char *p = args[1];
   509 			char *p = args[1];
   183 
   510 
   184 			if (*p == '.') {
   511 			if (*p == '.') {
   195 			if (args[2])
   522 			if (args[2])
   196 				dnode = to_utf8 (args[2]);
   523 				dnode = to_utf8 (args[2]);
   197 		}
   524 		}
   198 			// XXX send to all resources/current resource?
   525 			// XXX send to all resources/current resource?
   199 
   526 
   200 		{ // create message
       
   201 			LmMessageNode *node;
       
   202 
       
   203 			request = lm_message_new_with_sub_type (to ? to : CURRENT_JID, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
       
   204 			node    = lm_message_get_node (request);
       
   205 			node    = lm_message_node_add_child (node, "query", NULL);
       
   206 			lm_message_node_set_attribute (node, "xmlns", info ? NS_DISCO_INFO : NS_DISCO_ITEMS);
       
   207 			if (dnode)
       
   208 				lm_message_node_set_attribute (node, "node", dnode);
       
   209 		}
       
   210 
       
   211 		{
   527 		{
   212 			GError *error = NULL;
   528 			disco_handler_t *cb = g_new (disco_handler_t, 1);
   213 
   529 
   214 			lm_connection_send_with_reply (lconnection, request, info ? disco_info_reply_handler : disco_items_reply_handler, &error);
   530 			cb -> jid  = to ? to : g_strdup (CURRENT_JID);
   215 
   531 			cb -> node = dnode;
   216 			if (error) {
   532 
   217 				scr_LogPrint (LPRINT_DEBUG, "disco: Error sending disco request: %s.", error -> message);
   533 			if (info)
   218 				g_error_free (error);
   534 				disco_info_request (cb -> jid, cb -> node, disco_info_handler, cb, disco_handler_destroy_notify);
   219 			}
   535 			else
   220 		}
   536 				disco_items_request (cb -> jid, cb -> node, disco_items_handler, cb, disco_handler_destroy_notify);
   221 
   537 		}
   222 		lm_message_unref (request);
       
   223 		if (dnode)
       
   224 			g_free (dnode);
       
   225 		if (to)
       
   226 			g_free (to);
       
   227 	}
   538 	}
   228 
   539 
   229 	free_arg_lst (args);
   540 	free_arg_lst (args);
       
   541 	return;
       
   542 }
       
   543 
       
   544 //
       
   545 // module mechanics
       
   546 //
       
   547 
       
   548 static void disco_unregister_handlers (void)
       
   549 {
       
   550 	GSList *hel;
       
   551 
       
   552 	for (hel = reply_handlers; hel; hel = hel -> next) {
       
   553 		LmMessageHandler *handler = hel -> data;
       
   554 		lm_message_handler_invalidate (handler);
       
   555 	}
       
   556 
       
   557 	g_slist_free (reply_handlers);
       
   558 	reply_handlers = NULL;
       
   559 
       
   560 	return;
       
   561 }
       
   562 
       
   563 static void disco_hh (guint32 htype, hk_arg_t *args, gpointer ignore)
       
   564 {
       
   565 	hk_arg_t *arg;
       
   566 
       
   567 	for (arg = args; arg->name; ++arg)
       
   568 		if (!strcmp (arg->name, "hook")) {
       
   569 			if (!strcmp (arg->value, "hook-pre-disconnect"))
       
   570 				disco_unregister_handlers ();
       
   571 			return;
       
   572 		}
       
   573 
       
   574 	return;
   230 }
   575 }
   231 
   576 
   232 const gchar *g_module_check_init(GModule *module)
   577 const gchar *g_module_check_init(GModule *module)
   233 {
   578 {
   234 	// create handlers
       
   235 	disco_info_reply_handler  = lm_message_handler_new (disco_handler, NULL, NULL);
       
   236 	disco_items_reply_handler = lm_message_handler_new (disco_handler, NULL, NULL);
       
   237 
       
   238 	// completion
   579 	// completion
   239 	disco_cid = compl_new_category ();
   580 	disco_cid = compl_new_category ();
   240 	if (disco_cid) {
   581 	if (disco_cid) {
   241 		compl_add_category_word (disco_cid, "info");
   582 		compl_add_category_word (disco_cid, "info");
   242 		compl_add_category_word (disco_cid, "items");
   583 		compl_add_category_word (disco_cid, "items");
   243 	}
   584 	}
   244 
   585 
       
   586 	// hook handler
       
   587 	hk_add_handler (disco_hh, HOOK_INTERNAL, NULL);
       
   588 
   245 	// command
   589 	// command
   246 	cmd_add ("disco", "", disco_cid, COMPL_JID, do_disco, NULL);
   590 	cmd_add ("disco", "", disco_cid, COMPL_JID, do_disco, NULL);
   247 
   591 
   248 	return NULL;
   592 	return NULL;
   249 }
   593 }
   255 
   599 
   256 	// completion
   600 	// completion
   257 	if (disco_cid)
   601 	if (disco_cid)
   258 		compl_del_category (disco_cid);
   602 		compl_del_category (disco_cid);
   259 	
   603 	
       
   604 	// hook handler
       
   605 	hk_del_handler (disco_hh, NULL);
       
   606 
   260 	// unregister handlers
   607 	// unregister handlers
   261 	if (disco_info_reply_handler) {
   608 	disco_unregister_handlers ();
   262 		lm_message_handler_invalidate (disco_info_reply_handler);
   609 
   263 		lm_message_handler_unref (disco_info_reply_handler);
   610 	return;
   264 	}
       
   265 
       
   266 	if (disco_items_reply_handler) {
       
   267 		lm_message_handler_invalidate (disco_items_reply_handler);
       
   268 		lm_message_handler_unref (disco_items_reply_handler);
       
   269 	}
       
   270 }
   611 }
   271 
   612 
   272 /* vim: se ts=4 sw=4: */
   613 /* vim: se ts=4 sw=4: */