Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 861c199fb60fecf5dab96e0ed9d4b0cf0c57822f)
+++ protocols/jabber/jabber.c	(revision 6a1128d1333cf79f1ef9fb1f55b1b8fec67caf2a)
@@ -80,4 +80,5 @@
 	
 	jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
+	jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
 	
 	/* Figure out the hostname to connect to. */
@@ -169,8 +170,21 @@
 static void jabber_get_info( struct gaim_connection *gc, char *who )
 {
+	struct jabber_buddy *bud;
 	struct xt_node *node;
 	
+	bud = jabber_buddy_by_jid( gc, who );
+	while( bud )
+	{
+		serv_got_crap( gc, "Buddy %s/%s (%d) information:\nAway state: %s\nAway message: %s",
+		                   bud->handle, bud->resource, bud->priority,
+		                   bud->away_state ? bud->away_state->full_name : "(none)",
+		                   bud->away_message ? : "(none)" );
+		bud = bud->next;
+	}
+	
+//	node = xt_new_node( "vCard", NULL, NULL );
+//	xt_add_attr( node, "xmlns", "vcard-temp" );
 	node = xt_new_node( "query", NULL, NULL );
-	xt_add_attr( node, "xmlns", "http://jabber.org/protocol/disco#info" );
+	xt_add_attr( node, "xmlns", "jabber:iq:version" );
 	node = jabber_make_packet( "iq", "get", who, node );
 	// jabber_cache_add( gc, node,  );
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 861c199fb60fecf5dab96e0ed9d4b0cf0c57822f)
+++ protocols/jabber/jabber.h	(revision 6a1128d1333cf79f1ef9fb1f55b1b8fec67caf2a)
@@ -61,4 +61,5 @@
 	
 	GHashTable *node_cache;
+	GHashTable *buddies;
 };
 
@@ -75,4 +76,19 @@
 	struct xt_node *node;
 	jabber_cache_event func;
+};
+
+struct jabber_buddy
+{
+	char *handle;
+	char *resource;
+	
+	int priority;
+	struct jabber_away_state *away_state;
+	char *away_message;
+	
+	time_t last_act;
+	int flags;
+	
+	struct jabber_buddy *next;
 };
 
@@ -104,4 +120,7 @@
 const struct jabber_away_state *jabber_away_state_by_name( char *name );
 void jabber_buddy_ask( struct gaim_connection *gc, char *handle );
+struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid );
+struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid );
+int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid );
 
 extern const struct jabber_away_state jabber_away_state_list[];
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 861c199fb60fecf5dab96e0ed9d4b0cf0c57822f)
+++ protocols/jabber/jabber_util.c	(revision 6a1128d1333cf79f1ef9fb1f55b1b8fec67caf2a)
@@ -202,2 +202,134 @@
 	g_free( buf );
 }
+
+/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
+   FULL jid or if we already have this buddy/resource. */
+struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid )
+{
+	struct jabber_data *jd = gc->proto_data;
+	struct jabber_buddy *bud, *new, *bi;
+	char *s;
+	
+	if( !( s = strchr( full_jid, '/' ) ) )
+		return NULL;
+	
+	new = g_new0( struct jabber_buddy, 1 );
+	
+	*s = 0;
+	if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
+	{
+		new->handle = bud->handle;
+		
+		/* We already have another resource for this buddy, add the
+		   new one to the list. */
+		for( bi = bud; bi; bi = bi->next )
+		{
+			/* Check for dupes. Resource seem to be case sensitive. */
+			if( strcmp( bi->resource, s + 1 ) == 0 )
+			{
+				*s = '/';
+				g_free( new );
+				return NULL;
+			}
+			/* Append the new item to the list. */
+			else if( bi->next == NULL )
+			{
+				bi->next = new;
+				break;
+			}
+		}
+	}
+	else
+	{
+		new->handle = g_strdup( full_jid );
+		g_hash_table_insert( jd->buddies, new->handle, new );
+	}
+	
+	*s = '/';
+	new->resource = g_strdup( s + 1 );
+	
+	return new;
+}
+
+struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid )
+{
+	struct jabber_data *jd = gc->proto_data;
+	struct jabber_buddy *bud;
+	char *s;
+	
+	if( ( s = strchr( jid, '/' ) ) )
+	{
+		*s = 0;
+		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
+			for( ; bud; bud = bud->next )
+				if( strcmp( bud->resource, s + 1 ) == 0 )
+					break;
+	}
+	else
+	{
+		/* TODO: Add selection. */
+		return g_hash_table_lookup( jd->buddies, jid );
+	}
+	
+	*s = '/';
+	return bud;
+}
+
+int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid )
+{
+	struct jabber_data *jd = gc->proto_data;
+	struct jabber_buddy *bud, *prev, *bi;
+	char *s;
+	
+	if( !( s = strchr( full_jid, '/' ) ) )
+		return 0;
+	
+	*s = 0;
+	if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
+	{
+		/* If there's only one item in the list (and if the resource
+		   matches), removing it is simple. (And the hash reference
+		   should be removed too!) */
+		if( bud->next == NULL && strcmp( bud->resource, s + 1 ) == 0 )
+		{
+			g_hash_table_remove( jd->buddies, bud->handle );
+			g_free( bud->handle );
+			g_free( bud->resource );
+			g_free( bud->away_message );
+			g_free( bud );
+		}
+		else
+		{
+			for( bi = bud, prev = NULL; bi; bi = (prev=bi)->next )
+				if( strcmp( bi->resource, s + 1 ) == 0 )
+					break;
+			
+			if( bi )
+			{
+				if( prev )
+					prev->next = bi->next;
+				else
+					/* The hash table should point at the second
+					   item, because we're removing the first. */
+					g_hash_table_replace( jd->buddies, bi->handle, bi->next );
+				
+				g_free( bi->resource );
+				g_free( bi->away_message );
+				g_free( bi );
+			}
+			else
+			{
+				*s = '/';
+				return 0;
+			}
+		}
+		
+		*s = '/';
+		return 1;
+	}
+	else
+	{
+		*s = '/';
+		return 0;
+	}
+}
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision 861c199fb60fecf5dab96e0ed9d4b0cf0c57822f)
+++ protocols/jabber/presence.c	(revision 6a1128d1333cf79f1ef9fb1f55b1b8fec67caf2a)
@@ -29,24 +29,60 @@
 	char *from = xt_find_attr( node, "from" );
 	char *type = xt_find_attr( node, "type" );	/* NULL should mean the person is online. */
-	char *s;
+	struct xt_node *c;
 	
 	if( !from )
 		return XT_HANDLED;
 	
-	s = strchr( from, '/' );
-	if( s )
-		*s = 0;
-	
-	/* Will implement better parsing of away states/msgs when we
-	   finally do those API changes. Which will probably be after
-	   merging this module into the main tree. */
 	if( type == NULL )
-		serv_got_update( gc, from, 1, 0, 0, 0, 0, 0 );
+	{
+		struct jabber_buddy *bud;
+		
+		if( !( bud = jabber_buddy_by_jid( gc, from ) ) )
+		{
+			bud = jabber_buddy_add( gc, from );
+		}
+		
+		g_free( bud->away_message );
+		if( ( c = xt_find_node( node->children, "status" ) ) && c->text_len > 0 )
+			bud->away_message = g_strdup( c->text );
+		else
+			bud->away_message = NULL;
+		
+		if( ( c = xt_find_node( node->children, "show" ) ) && c->text_len > 0 )
+			bud->away_state = (void*) jabber_away_state_by_code( c->text );
+		else
+			bud->away_state = NULL;
+		
+		if( ( c = xt_find_node( node->children, "priority" ) ) && c->text_len > 0 )
+			bud->priority = atoi( c->text );
+		else
+			bud->priority = 0;
+		
+		serv_got_update( gc, bud->handle, 1, 0, 0, 0, 0, 0 );
+	}
 	else if( strcmp( type, "unavailable" ) == 0 )
-		serv_got_update( gc, from, 0, 0, 0, 0, 0, 0 );
+	{
+		char *s;
+		
+		jabber_buddy_remove( gc, from );
+		
+		if( ( s = strchr( from, '/' ) ) )
+			*s = 0;
+		
+		/* Only count this as offline if there's no other resource
+		   available anymore. */
+		if( jabber_buddy_by_jid( gc, from ) == NULL )
+			serv_got_update( gc, from, 0, 0, 0, 0, 0, 0 );
+		
+		*s = '/';
+	}
 	else if( strcmp( type, "subscribe" ) == 0 )
+	{
 		jabber_buddy_ask( gc, from );
+	}
 	else if( strcmp( type, "subscribed" ) == 0 )
+	{
 		serv_got_crap( gc, "%s just accepted your authorization request", from );
+	}
 	else if( strcmp( type, "unsubscribe" ) == 0 || strcmp( type, "unsubscribed" ) == 0 )
 	{
@@ -69,7 +105,4 @@
 		xt_print( node );
 	}
-	
-	if( s )
-		*s = '/';
 	
 	return XT_HANDLED;
