Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/Makefile	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
+objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
 
 CFLAGS += -Wall
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
+++ protocols/jabber/conference.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -0,0 +1,193 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Jabber module - Conference rooms                                         *
+*                                                                           *
+*  Copyright 2007 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This program is free software; you can redistribute it and/or modify     *
+*  it under the terms of the GNU General Public License as published by     *
+*  the Free Software Foundation; either version 2 of the License, or        *
+*  (at your option) any later version.                                      *
+*                                                                           *
+*  This program is distributed in the hope that it will be useful,          *
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
+*  GNU General Public License for more details.                             *
+*                                                                           *
+*  You should have received a copy of the GNU General Public License along  *
+*  with this program; if not, write to the Free Software Foundation, Inc.,  *
+*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
+*                                                                           *
+\***************************************************************************/
+
+#include "jabber.h"
+
+struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password )
+{
+	struct jabber_chat *jc;
+	struct xt_node *node;
+	struct groupchat *c;
+	char *roomjid;
+	
+	roomjid = g_strdup_printf( "%s/%s", room, nick );
+	node = xt_new_node( "x", NULL, NULL );
+	xt_add_attr( node, "xmlns", XMLNS_MUC );
+	node = jabber_make_packet( "presence", NULL, roomjid, node );
+	
+	if( !jabber_write_packet( ic, node ) )
+	{
+		g_free( roomjid );
+		xt_free_node( node );
+		return NULL;
+	}
+	xt_free_node( node );
+	
+	jc = g_new0( struct jabber_chat, 1 );
+	jc->name = jabber_normalize( room );
+	
+	if( ( jc->me = jabber_buddy_add( ic, roomjid ) ) == NULL )
+	{
+		g_free( roomjid );
+		g_free( jc->name );
+		g_free( jc );
+		return NULL;
+	}
+	g_free( roomjid );
+	
+	c = imcb_chat_new( ic, room );
+	c->data = jc;
+	
+	return c;
+}
+
+int jabber_chat_leave( struct groupchat *c, const char *reason )
+{
+	struct im_connection *ic = c->ic;
+	struct jabber_chat *jc = c->data;
+	struct xt_node *node;
+	
+	node = xt_new_node( "x", NULL, NULL );
+	xt_add_attr( node, "xmlns", XMLNS_MUC );
+	node = jabber_make_packet( "presence", "unavailable", jc->me->full_jid, node );
+	
+	if( !jabber_write_packet( ic, node ) )
+	{
+		xt_free_node( node );
+		return 0;
+	}
+	xt_free_node( node );
+	
+	/* Remove all participants from jc->buddies and clean up our data. */
+	jabber_buddy_remove_bare( ic, jc->name );
+	g_free( jc->name );
+	g_free( jc );
+	
+	/* And the generic stuff. */
+	imcb_chat_free( c );
+	
+	return 1;
+}
+
+/* Not really the same syntax as the normal pkt_ functions, but this isn't
+   called by the xmltree parser exactly and this way I can add some extra
+   parameters so we won't have to repeat too many things done by the caller
+   already. */
+void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
+{
+	struct groupchat *chat;
+	struct xt_node *c;
+	char *type = xt_find_attr( node, "type" );
+	struct jabber_chat *jc;
+	char *s;
+	
+	if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL )
+	{
+		/* How could this happen?? We could do kill( self, 11 )
+		   now or just wait for the OS to do it. :-) */
+		return;
+	}
+	
+	jc = chat->data;
+	
+	if( type == NULL && !( bud->flags & JBFLAG_IS_CHATROOM ) )
+	{
+		bud->flags |= JBFLAG_IS_CHATROOM;
+		/* If this one wasn't set yet, this buddy just joined the chat.
+		   Slightly hackish way of finding out eh? ;-) */
+		
+		/* This is pretty messy... */
+		for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+			if( ( s = xt_find_attr( c, "xmlns" ) ) &&
+			    ( strcmp( s, XMLNS_MUC_USER ) == 0 ) )
+			{
+				c = xt_find_node( c->children, "item" );
+				if( ( s = xt_find_attr( c, "jid" ) ) )
+				{
+					/* Yay, found what we need. :-) */
+					bud->orig_jid = g_strdup( s );
+					break;
+				}
+			}
+		
+		/* Won't handle this for now. */
+		if( bud->orig_jid == NULL )
+			return;
+		
+		s = strchr( bud->orig_jid, '/' );
+		if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */
+		imcb_chat_add_buddy( chat, bud->orig_jid );
+		if( s ) *s = '/';
+	}
+	else if( type ) /* This only gets called if type=="unavailable" */
+	{
+		/* Won't handle this for now. */
+		if( bud->orig_jid == NULL )
+			return;
+		s = strchr( bud->orig_jid, '/' );
+		if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */
+		imcb_chat_remove_buddy( chat, bud->orig_jid, NULL );
+		if( s ) *s = '/';
+		
+		if( bud == jc->me )
+		{
+			g_free( jc->name );
+			g_free( jc );
+			imcb_chat_free( chat );
+		}
+	}
+}
+
+void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
+{
+	struct xt_node *body = xt_find_node( node->children, "body" );
+	struct groupchat *chat;
+	char *s;
+	
+	if( bud == NULL )
+	{
+		s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */
+		if( strchr( s, '/' ) == NULL )
+			/* This is fine, the groupchat itself isn't in jd->buddies. */
+			imcb_log( ic, "System message from groupchat %s: %s", s, body? body->text : "NULL" );
+		else
+			/* This, however, isn't fine! */
+			imcb_log( ic, "Groupchat message from unknown participant %s: %s", s, body ? body->text : "NULL" );
+		
+		return;
+	}
+	else if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL )
+	{
+		/* How could this happen?? We could do kill( self, 11 )
+		   now or just wait for the OS to do it. :-) */
+		return;
+	}
+	
+	if( body && body->text_len > 0 )
+	{
+		s = strchr( bud->orig_jid, '/' );
+		if( s ) *s = 0;
+		imcb_chat_msg( chat, bud->orig_jid, body->text, 0, 0 );
+		if( s ) *s = '/';
+	}
+}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/jabber.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -324,4 +324,22 @@
 }
 
+static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password )
+{
+	if( strchr( room, '@' ) == NULL )
+		imcb_error( ic, "Invalid room name: %s", room );
+	else if( jabber_chat_by_name( ic, room ) )
+		imcb_error( ic, "Already present in chat `%s'", room );
+	else
+		return jabber_chat_join( ic, room, nick, password );
+	
+	return NULL;
+}
+
+static void jabber_chat_leave_( struct groupchat *c )
+{
+	if( c )
+		jabber_chat_leave( c, NULL );
+}
+
 static void jabber_keepalive( struct im_connection *ic )
 {
@@ -396,6 +414,6 @@
 //	ret->chat_msg = jabber_chat_msg;
 //	ret->chat_invite = jabber_chat_invite;
-//	ret->chat_leave = jabber_chat_leave;
-//	ret->chat_open = jabber_chat_open;
+	ret->chat_leave = jabber_chat_leave_;
+	ret->chat_join = jabber_chat_join_;
 	ret->keepalive = jabber_keepalive;
 	ret->send_typing = jabber_send_typing;
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/jabber.h	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -50,4 +50,6 @@
 	JBFLAG_DOES_XEP85 = 2,		/* Set this when the resource seems to support
 	                                   XEP85 (typing notification shite). */
+	JBFLAG_IS_CHATROOM = 4,		/* It's convenient to use this JID thingy for
+	                                   groupchat state info too. */
 } jabber_buddy_flags_t;
 
@@ -101,4 +103,7 @@
 	char *resource;
 	
+	/* Groupchat-only */
+	char *orig_jid;
+	
 	int priority;
 	struct jabber_away_state *away_state;
@@ -109,4 +114,11 @@
 	
 	struct jabber_buddy *next;
+};
+
+struct jabber_chat
+{
+	int flags;
+	char *name;
+	struct jabber_buddy *me;
 };
 
@@ -134,4 +146,6 @@
 #define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */
 #define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */
+#define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */
+#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */
 
 /* iq.c */
@@ -164,10 +178,11 @@
 const struct jabber_away_state *jabber_away_state_by_name( char *name );
 void jabber_buddy_ask( struct im_connection *ic, char *handle );
-char *jabber_normalize( char *orig );
+char *jabber_normalize( const char *orig );
 
 typedef enum
 {
 	GET_BUDDY_CREAT = 1,	/* Try to create it, if necessary. */
-	GET_BUDDY_EXACT = 2,	/* Get an exact message (only makes sense with bare JIDs). */
+	GET_BUDDY_EXACT = 2,	/* Get an exact match (only makes sense with bare JIDs). */
+	GET_BUDDY_FIRST = 4,	/* No selection, simply get the first resource for this JID. */
 } get_buddy_flags_t;
 
@@ -176,4 +191,5 @@
 int jabber_buddy_remove( struct im_connection *ic, char *full_jid );
 int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid );
+struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name );
 
 extern const struct jabber_away_state jabber_away_state_list[];
@@ -193,3 +209,9 @@
 gboolean sasl_supported( struct im_connection *ic );
 
+/* conference.c */
+struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password );
+int jabber_chat_leave( struct groupchat *c, const char *reason );
+void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
+void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
+
 #endif
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/jabber_util.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -48,5 +48,5 @@
 		   call p_s_u() now to send the new prio setting, it would
 		   send the old setting because the set->value gets changed
-		   when the eval returns a non-NULL value.
+		   after the (this) eval returns a non-NULL value.
 		   
 		   So now I can choose between implementing post-set
@@ -129,5 +129,5 @@
 /* Cache a node/packet for later use. Mainly useful for IQ packets if you need
    them when you receive the response. Use this BEFORE sending the packet so
-   it'll get a new id= tag, and do NOT free() the packet after writing it! */
+   it'll get a new id= tag, and do NOT free() the packet after sending it! */
 void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )
 {
@@ -252,5 +252,5 @@
 
 /* Returns a new string. Don't leak it! */
-char *jabber_normalize( char *orig )
+char *jabber_normalize( const char *orig )
 {
 	int len, i;
@@ -353,4 +353,6 @@
 	if( ( s = strchr( jid, '/' ) ) )
 	{
+		int none_found = 0;
+		
 		*s = 0;
 		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
@@ -370,6 +372,14 @@
 			}
 		}
-		
-		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) )
+		else
+		{
+			/* This hack is there to make sure that O_CREAT will
+			   work if there's already another resouce present
+			   for this JID, even if it's an unknown buddy. This
+			   is done to handle conferences properly. */
+			none_found = 1;
+		}
+		
+		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
 		{
 			*s = '/';
@@ -531,2 +541,19 @@
 	}
 }
+
+struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name )
+{
+	char *normalized = jabber_normalize( name );
+	struct groupchat *ret;
+	struct jabber_chat *jc;
+	
+	for( ret = ic->groupchats; ret; ret = ret->next )
+	{
+		jc = ret->data;
+		if( strcmp( normalized, jc->name ) == 0 )
+			break;
+	}
+	g_free( normalized );
+	
+	return ret;
+}
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/message.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -30,5 +30,11 @@
 	char *type = xt_find_attr( node, "type" );
 	struct xt_node *body = xt_find_node( node->children, "body" ), *c;
+	struct jabber_buddy *bud = NULL;
 	char *s;
+	
+	if( !from )
+		return XT_HANDLED; /* Consider this packet corrupted. */
+	
+	bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT );
 	
 	if( type && strcmp( type, "error" ) == 0 )
@@ -36,16 +42,15 @@
 		/* Handle type=error packet. */
 	}
-	else if( type && strcmp( type, "groupchat" ) == 0 )
+	else if( type && from && strcmp( type, "groupchat" ) == 0 )
 	{
-		/* TODO! */
+		jabber_chat_pkt_message( ic, bud, node );
 	}
 	else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
 	{
-		struct jabber_buddy *bud = NULL;
 		GString *fullmsg = g_string_new( "" );
 		
 		if( ( s = strchr( from, '/' ) ) )
 		{
-			if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) )
+			if( bud )
 				bud->last_act = time( NULL );
 			else
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/presence.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -31,8 +31,17 @@
 	struct xt_node *c;
 	struct jabber_buddy *bud;
+	int is_chat = 0;
 	char *s;
 	
 	if( !from )
 		return XT_HANDLED;
+	
+	if( ( s = strchr( from, '/' ) ) )
+	{
+		*s = 0;
+		if( jabber_chat_by_name( ic, from ) )
+			is_chat = 1;
+		*s = '/';
+	}
 	
 	if( type == NULL )
@@ -72,5 +81,7 @@
 			bud->priority = 0;
 		
-		if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
+		if( is_chat )
+			jabber_chat_pkt_presence( ic, bud, node );
+		else if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
 			imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
 			                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
@@ -79,5 +90,5 @@
 	else if( strcmp( type, "unavailable" ) == 0 )
 	{
-		if( jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) == NULL )
+		if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) == NULL )
 		{
 			if( set_getbool( &ic->irc->set, "debug" ) )
@@ -86,7 +97,17 @@
 		}
 		
+		/* Handle this before we delete the JID. */
+		if( is_chat )
+		{
+			jabber_chat_pkt_presence( ic, bud, node );
+		}
+		
 		jabber_buddy_remove( ic, from );
 		
-		if( ( s = strchr( from, '/' ) ) )
+		if( is_chat )
+		{
+			/* Nothing else to do for now? */
+		}
+		else if( ( s = strchr( from, '/' ) ) )
 		{
 			*s = 0;
@@ -96,4 +117,6 @@
 			if( jabber_buddy_by_jid( ic, from, 0 ) == NULL )
 				imcb_buddy_status( ic, from, 0, NULL, NULL );
+			/* FIXME: If this resource was not away and another resource is,
+			   we should definitely send an update here. */
 			
 			*s = '/';
Index: protocols/jabber/xmltree.c
===================================================================
--- protocols/jabber/xmltree.c	(revision df1fb67d1dbf52d138f63e0d917dda2412d4fc0b)
+++ protocols/jabber/xmltree.c	(revision e35d1a121d5fb2da3698fbe4a365fe38d0097665)
@@ -188,4 +188,6 @@
 			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
 			{
+				xt_print( node );
+				
 				st = xt->handlers[i].func( node, xt->data );
 				
