Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 36e9f62a6e6fdb1217b3b819320ac5a94025c448)
+++ protocols/jabber/io.c	(revision 038d17f834219505cbbdae469b2b150117467dd0)
@@ -360,5 +360,5 @@
 		xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind" );
 		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_packet( gc, reply );
+		jabber_cache_add( gc, reply );
 		
 		if( !jabber_write_packet( gc, reply ) )
@@ -373,5 +373,5 @@
 		xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-session" );
 		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_packet( gc, reply );
+		jabber_cache_add( gc, reply );
 		
 		if( !jabber_write_packet( gc, reply ) )
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 36e9f62a6e6fdb1217b3b819320ac5a94025c448)
+++ protocols/jabber/iq.c	(revision 038d17f834219505cbbdae469b2b150117467dd0)
@@ -41,5 +41,5 @@
 	
 	if( ( s = xt_find_attr( node, "id" ) ) )
-		orig = jabber_packet_from_cache( gc, s );
+		orig = jabber_cache_get( gc, s );
 	
 	if( strcmp( type, "result" ) == 0 && xmlns && strcmp( xmlns, "jabber:iq:auth" ) == 0 )
@@ -85,5 +85,5 @@
 		
 		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_packet( gc, reply );
+		jabber_cache_add( gc, reply );
 		st = jabber_write_packet( gc, reply );
 		
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 36e9f62a6e6fdb1217b3b819320ac5a94025c448)
+++ protocols/jabber/jabber.c	(revision 038d17f834219505cbbdae469b2b150117467dd0)
@@ -79,5 +79,5 @@
 	jd->server ++;
 	
-	jd->node_cache = xt_new_node( "cache", NULL, NULL );
+	jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
 	
 	/* Figure out the hostname to connect to. */
@@ -125,5 +125,6 @@
 		g_free( jd->txq );
 	
-	xt_free_node( jd->node_cache );
+	g_hash_table_destroy( jd->node_cache );
+	
 	xt_free( jd->xt );
 	
@@ -164,4 +165,16 @@
 	
 	return l;
+}
+
+static void jabber_get_info( struct gaim_connection *gc, char *who )
+{
+	struct xt_node *node;
+	
+	node = xt_new_node( "query", NULL, NULL );
+	xt_add_attr( node, "xmlns", "http://jabber.org/protocol/disco#info" );
+	node = jabber_make_packet( "iq", "get", who, node );
+	jabber_cache_add( gc, node );
+	
+	jabber_write_packet( gc, node );
 }
 
@@ -194,48 +207,10 @@
 static void jabber_keepalive( struct gaim_connection *gc )
 {
-	struct jabber_data *jd = gc->proto_data;
-	struct xt_node *c, *tmp;
-	
 	/* Just any whitespace character is enough as a keepalive for XMPP sessions. */
 	jabber_write( gc, "\n", 1 );
 	
-	/* Let's abuse this keepalive for garbage collection of the node cache too.
-	   It runs every minute, so let's mark every node with a special flag the
-	   first time we see it, and clean it up the second time (clean up all
-	   packets with the flag set).
-	   
-	   node->flags is normally only used by xmltree itself for parsing/handling,
-	   so it should be safe to use the variable for gc. */
-	
-	/* This horrible loop is explained in xmltree.c. Makes me wonder if maybe I
-	   didn't choose the perfect data structure... */
-	for( c = jd->node_cache->children; c; c =  c->next )
-		if( !( c->flags & XT_SEEN ) )
-			break;
-	
-	/* Now c points at the first unflagged node (or at NULL). Clean up
-	   everything until that point. */
-	while( jd->node_cache->children != c )
-	{
-		/*
-		printf( "Cleaning up:\n" );
-		xt_print( jd->node_cache->children );
-		*/
-		
-		tmp = jd->node_cache->children->next;
-		xt_free_node( jd->node_cache->children );
-		jd->node_cache->children = tmp;
-	}
-	
-	/* Now flag the ones that were still unflagged. */
-	for( c = jd->node_cache->children; c; c = c->next )
-	{
-		/*
-		printf( "Flagged:\n" );
-		xt_print( c );
-		*/
-		
-		c->flags |= XT_SEEN;
-	}
+	/* This runs the garbage collection every minute, which means every packet
+	   is in the cache for about a minute (which should be enough AFAIK). */
+	jabber_cache_clean( gc );
 }
 
@@ -253,5 +228,5 @@
 	ret->set_away = jabber_set_away;
 //	ret->set_info = jabber_set_info;
-//	ret->get_info = jabber_get_info;
+	ret->get_info = jabber_get_info;
 	ret->add_buddy = jabber_add_buddy;
 	ret->remove_buddy = jabber_remove_buddy;
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 36e9f62a6e6fdb1217b3b819320ac5a94025c448)
+++ protocols/jabber/jabber.h	(revision 038d17f834219505cbbdae469b2b150117467dd0)
@@ -60,5 +60,5 @@
 	char *away_message;
 	
-	struct xt_node *node_cache;
+	GHashTable *node_cache;
 };
 
@@ -67,4 +67,12 @@
 	char code[5];
 	char *full_name;
+};
+
+typedef xt_status (*jabber_cache_event) ( struct gaim_connection *gc, struct xt_node *packet );
+
+struct jabber_cache_entry
+{
+	struct xt_node *node;
+	jabber_cache_event func;
 };
 
@@ -88,6 +96,8 @@
 char *set_eval_tls( set_t *set, char *value );
 struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children );
-void jabber_cache_packet( struct gaim_connection *gc, struct xt_node *node );
-struct xt_node *jabber_packet_from_cache( struct gaim_connection *gc, char *id );
+void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node );
+struct xt_node *jabber_cache_get( struct gaim_connection *gc, char *id );
+void jabber_cache_entry_free( gpointer entry );
+void jabber_cache_clean( struct gaim_connection *gc );
 const struct jabber_away_state *jabber_away_state_by_code( char *code );
 const struct jabber_away_state *jabber_away_state_by_name( char *name );
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 36e9f62a6e6fdb1217b3b819320ac5a94025c448)
+++ protocols/jabber/jabber_util.c	(revision 038d17f834219505cbbdae469b2b150117467dd0)
@@ -82,28 +82,54 @@
    them when you receive the response. Use this BEFORE sending the packet so
    it'll get an id= tag, and do NOT free() the packet after writing it! */
-void jabber_cache_packet( struct gaim_connection *gc, struct xt_node *node )
+void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node )
 {
 	struct jabber_data *jd = gc->proto_data;
 	char *id = g_strdup_printf( "BeeX%04x", next_id++ );
-	
-	/* FIXME: Maybe start using g_error() here if nodes still have a parent, for example? */
+	struct jabber_cache_entry *entry = g_new0( struct jabber_cache_entry, 1 );
 	
 	xt_add_attr( node, "id", id );
-	xt_add_child( jd->node_cache, node );
 	g_free( id );
-}
-
-/* Emptying this cache is a BIG TODO! */
-struct xt_node *jabber_packet_from_cache( struct gaim_connection *gc, char *id )
+	
+	entry->node = node;
+	g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry );
+}
+
+struct xt_node *jabber_cache_get( struct gaim_connection *gc, char *id )
 {
 	struct jabber_data *jd = gc->proto_data;
-	struct xt_node *node;
-	char *s;
-	
-	for( node = jd->node_cache->children; node; node = node->next )
-		if( ( s = xt_find_attr( node, "id" ) ) && strcmp( id, s ) == 0 )
-			break;
-	
-	return node;
+	struct jabber_cache_entry *entry = g_hash_table_lookup( jd->node_cache, id );
+	
+	return entry ? entry->node : NULL;
+}
+
+void jabber_cache_entry_free( gpointer data )
+{
+	struct jabber_cache_entry *entry = data;
+	
+	xt_free_node( entry->node );
+	g_free( entry );
+}
+
+gboolean jabber_cache_clean_entry( gpointer key, gpointer entry, gpointer nullpointer );
+
+void jabber_cache_clean( struct gaim_connection *gc )
+{
+	struct jabber_data *jd = gc->proto_data;
+	
+	g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, NULL );
+}
+
+gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer nullpointer )
+{
+	struct jabber_cache_entry *entry = entry_;
+	struct xt_node *node = entry->node;
+	
+	if( node->flags & XT_SEEN )
+		return TRUE;
+	else
+	{
+		node->flags |= XT_SEEN;
+		return FALSE;
+	}
 }
 
