gossip r2820 - in trunk: . data/glade libgossip src



Author: mr
Date: Sat Jul  5 12:58:07 2008
New Revision: 2820
URL: http://svn.gnome.org/viewvc/gossip?rev=2820&view=rev

Log:
Fixes 347026, 350333, 541651, reworked Jabber chatroom backend


Added:
   trunk/libgossip/gossip-async.c
      - copied, changed from r2819, /trunk/libgossip/gossip-jabber-private.h
Modified:
   trunk/ChangeLog
   trunk/data/glade/chat.glade
   trunk/libgossip/Makefile.am
   trunk/libgossip/gossip-async.h
   trunk/libgossip/gossip-chatroom-invite.c
   trunk/libgossip/gossip-chatroom-invite.h
   trunk/libgossip/gossip-chatroom-manager.c
   trunk/libgossip/gossip-chatroom-manager.h
   trunk/libgossip/gossip-chatroom-provider.c
   trunk/libgossip/gossip-chatroom-provider.h
   trunk/libgossip/gossip-chatroom.c
   trunk/libgossip/gossip-chatroom.h
   trunk/libgossip/gossip-contact-manager.c
   trunk/libgossip/gossip-contact-manager.h
   trunk/libgossip/gossip-contact.c
   trunk/libgossip/gossip-contact.h
   trunk/libgossip/gossip-jabber-chatrooms.c
   trunk/libgossip/gossip-jabber-disco.c
   trunk/libgossip/gossip-jabber-ft.c
   trunk/libgossip/gossip-jabber-private.h
   trunk/libgossip/gossip-jabber-register.c
   trunk/libgossip/gossip-jabber-utils.c
   trunk/libgossip/gossip-jabber-utils.h
   trunk/libgossip/gossip-jabber-vcard.c
   trunk/libgossip/gossip-jabber.c
   trunk/libgossip/gossip-jabber.h
   trunk/libgossip/gossip-jid.c
   trunk/libgossip/gossip-jid.h
   trunk/libgossip/gossip-log.c
   trunk/libgossip/gossip-private.h
   trunk/libgossip/gossip-session.c
   trunk/libgossip/gossip-session.h
   trunk/src/gossip-app.c
   trunk/src/gossip-chat-view.c
   trunk/src/gossip-chat-window.c
   trunk/src/gossip-chatrooms-window.c
   trunk/src/gossip-contact-info-dialog.c
   trunk/src/gossip-contact-list.c
   trunk/src/gossip-dbus.c
   trunk/src/gossip-edit-contact-dialog.c
   trunk/src/gossip-group-chat.c
   trunk/src/gossip-new-chatroom-dialog.c
   trunk/src/gossip-theme.c

Modified: trunk/data/glade/chat.glade
==============================================================================
--- trunk/data/glade/chat.glade	(original)
+++ trunk/data/glade/chat.glade	Sat Jul  5 12:58:07 2008
@@ -339,7 +339,6 @@
 
 		  <child>
 		    <widget class="GtkMenuItem" id="menu_room_contact">
-		      <property name="visible">False</property>
 		      <property name="label" translatable="yes">_Contact</property>
 		      <property name="use_underline">True</property>
 		    </widget>
@@ -697,7 +696,7 @@
 		  <property name="editable">True</property>
 		  <property name="visibility">True</property>
 		  <property name="max_length">0</property>
-		  <property name="text" translatable="yes">You have been invited to join a chat conference.</property>
+		  <property name="text" translatable="yes"></property>
 		  <property name="has_frame">True</property>
 		  <property name="invisible_char">*</property>
 		  <property name="activates_default">True</property>

Modified: trunk/libgossip/Makefile.am
==============================================================================
--- trunk/libgossip/Makefile.am	(original)
+++ trunk/libgossip/Makefile.am	Sat Jul  5 12:58:07 2008
@@ -45,6 +45,7 @@
 	gossip-account.h           			\
 	gossip-account-manager.c   			\
 	gossip-account-manager.h   			\
+	gossip-async.c             			\
 	gossip-async.h             			\
 	gossip-avatar.c           			\
 	gossip-avatar.h           			\

Copied: trunk/libgossip/gossip-async.c (from r2819, /trunk/libgossip/gossip-jabber-private.h)
==============================================================================
--- /trunk/libgossip/gossip-jabber-private.h	(original)
+++ trunk/libgossip/gossip-async.c	Sat Jul  5 12:58:07 2008
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2005 Imendio AB
+ * Copyright (C) 2008 Imendio AB
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -18,25 +18,34 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __GOSSIP_JABBER_PRIVATE_H__
-#define __GOSSIP_JABBER_PRIVATE_H__
+#include "gossip-async.h"
 
-#include <loudmouth/loudmouth.h>
-
-#include "gossip-jabber.h"
-#include "gossip-jabber-ft.h"
-
-G_BEGIN_DECLS
-
-LmConnection *   gossip_jabber_new_connection (GossipJabber  *jabber,
-					       GossipAccount *account);
-gboolean         gossip_jabber_set_connection (LmConnection  *connection,
-					       GossipJabber  *jabber,
-					       GossipAccount *account);
-LmConnection *   gossip_jabber_get_connection (GossipJabber  *jabber);
-GossipJabberFTs *gossip_jabber_get_fts        (GossipJabber  *jabber);
-
-G_END_DECLS
-
-#endif /* __GOSSIP_JABBER_PRIVATE_H__ */
+GossipCallbackData *
+gossip_callback_data_new (gpointer callback,
+                          gpointer user_data,
+                          gpointer data1,
+                          gpointer data2,
+                          gpointer data3)
+{
+	GossipCallbackData *data;
+
+	data = g_slice_new0 (GossipCallbackData);
+
+	data->callback = callback;
+	data->user_data = user_data;
+        data->data1 = data1;
+        data->data2 = data2;
+        data->data3 = data3;
+	
+	return data;
+}
+
+void
+gossip_callback_data_free (GossipCallbackData *data)
+{
+        if (!data) {
+                return;
+        }
 
+        g_slice_free (GossipCallbackData, data);
+}

Modified: trunk/libgossip/gossip-async.h
==============================================================================
--- trunk/libgossip/gossip-async.h	(original)
+++ trunk/libgossip/gossip-async.h	Sat Jul  5 12:58:07 2008
@@ -57,6 +57,13 @@
 				       GossipVersionInfo *info,
 				       gpointer           user_data);
 
+GossipCallbackData *gossip_callback_data_new  (gpointer            callback,
+					       gpointer            user_data,
+					       gpointer            data1,
+					       gpointer            data2,
+					       gpointer            data3);
+void                gossip_callback_data_free (GossipCallbackData *data);
+
 G_END_DECLS 
 
 #endif /* __GOSSIP_ASYNC_H__ */

Modified: trunk/libgossip/gossip-chatroom-invite.c
==============================================================================
--- trunk/libgossip/gossip-chatroom-invite.c	(original)
+++ trunk/libgossip/gossip-chatroom-invite.c	Sat Jul  5 12:58:07 2008
@@ -27,7 +27,7 @@
 struct _GossipChatroomInvite {
 	guint          ref_count;
 
-	GossipContact *invitor;
+	GossipContact *inviter;
 	gchar         *id;
 	gchar         *reason;
 };
@@ -48,20 +48,20 @@
 }
 
 GossipChatroomInvite *
-gossip_chatroom_invite_new (GossipContact *invitor,
+gossip_chatroom_invite_new (GossipContact *inviter,
 			    const gchar   *id,
 			    const gchar   *reason)
 {
 	GossipChatroomInvite *invite;
 
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (invitor), NULL);
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (inviter), NULL);
 	g_return_val_if_fail (id != NULL, NULL);
 
 	invite = g_new0 (GossipChatroomInvite, 1);
 
 	invite->ref_count = 1;
 
-	invite->invitor = g_object_ref (invitor);
+	invite->inviter = g_object_ref (inviter);
 	invite->id = g_strdup (id);
 
 	if (reason) {
@@ -94,8 +94,8 @@
 		return;
 	}
 
-	if (invite->invitor) {
-		g_object_unref (invite->invitor);
+	if (invite->inviter) {
+		g_object_unref (invite->inviter);
 	}
 
 	g_free (invite->id);
@@ -103,11 +103,11 @@
 }
 
 GossipContact *
-gossip_chatroom_invite_get_invitor (GossipChatroomInvite *invite)
+gossip_chatroom_invite_get_inviter (GossipChatroomInvite *invite)
 {
 	g_return_val_if_fail (invite != NULL, NULL);
 
-	return invite->invitor;
+	return invite->inviter;
 }
 
 const gchar *

Modified: trunk/libgossip/gossip-chatroom-invite.h
==============================================================================
--- trunk/libgossip/gossip-chatroom-invite.h	(original)
+++ trunk/libgossip/gossip-chatroom-invite.h	Sat Jul  5 12:58:07 2008
@@ -37,7 +37,7 @@
 							  const gchar          *reason);
 GossipChatroomInvite *gossip_chatroom_invite_ref         (GossipChatroomInvite *invite);
 void                  gossip_chatroom_invite_unref       (GossipChatroomInvite *invite);
-GossipContact *       gossip_chatroom_invite_get_invitor (GossipChatroomInvite *invite);
+GossipContact *       gossip_chatroom_invite_get_inviter (GossipChatroomInvite *invite);
 const gchar *         gossip_chatroom_invite_get_id      (GossipChatroomInvite *invite);
 const gchar *         gossip_chatroom_invite_get_reason  (GossipChatroomInvite *invite);
 

Modified: trunk/libgossip/gossip-chatroom-manager.c
==============================================================================
--- trunk/libgossip/gossip-chatroom-manager.c	(original)
+++ trunk/libgossip/gossip-chatroom-manager.c	Sat Jul  5 12:58:07 2008
@@ -47,31 +47,31 @@
 
 struct _GossipChatroomManagerPriv {
 	GossipAccountManager *account_manager;
+	GossipContactManager *contact_manager;
 
 	GList                *chatrooms;
 
 	gchar                *chatrooms_file_name;
-
 	gchar                *default_name;
 };
 
-static void     chatroom_manager_finalize              (GObject                  *object);
-static void     chatroom_manager_chatroom_enabled_cb   (GossipChatroom           *chatroom,
-							GParamSpec               *arg1,
-							GossipChatroomManager    *manager);
-static void     chatroom_manager_chatroom_favourite_cb (GossipChatroom           *chatroom,
-							GParamSpec               *arg1,
-							GossipChatroomManager    *manager);
-static gboolean chatroom_manager_get_all               (GossipChatroomManager    *manager);
-static gboolean chatroom_manager_file_parse            (GossipChatroomManager    *manager,
-							const gchar              *filename);
-static gboolean chatroom_manager_file_save             (GossipChatroomManager    *manager);
+static void     chatroom_manager_finalize             (GObject                  *object);
+static void     chatroom_manager_chatroom_enabled_cb  (GossipChatroom           *chatroom,
+						       GParamSpec               *arg1,
+						       GossipChatroomManager    *manager);
+static void     chatroom_manager_chatroom_favorite_cb (GossipChatroom           *chatroom,
+						       GParamSpec               *arg1,
+						       GossipChatroomManager    *manager);
+static gboolean chatroom_manager_get_all              (GossipChatroomManager    *manager);
+static gboolean chatroom_manager_file_parse           (GossipChatroomManager    *manager,
+						       const gchar              *filename);
+static gboolean chatroom_manager_file_save            (GossipChatroomManager    *manager);
 
 enum {
 	CHATROOM_ADDED,
 	CHATROOM_REMOVED,
 	CHATROOM_ENABLED,
-	CHATROOM_FAVOURITE_UPDATE,
+	CHATROOM_FAVORITE_UPDATE,
 	NEW_DEFAULT,
 	LAST_SIGNAL
 };
@@ -114,8 +114,8 @@
 			      libgossip_marshal_VOID__OBJECT,
 			      G_TYPE_NONE,
 			      1, GOSSIP_TYPE_CHATROOM);
-	signals[CHATROOM_FAVOURITE_UPDATE] =
-		g_signal_new ("chatroom-favourite-update",
+	signals[CHATROOM_FAVORITE_UPDATE] =
+		g_signal_new ("chatroom-favorite-update",
 			      G_TYPE_FROM_CLASS (klass),
 			      G_SIGNAL_RUN_LAST,
 			      0,
@@ -149,21 +149,26 @@
 
 	priv = GET_PRIV (object);
 
+	g_free (priv->chatrooms_file_name);
+	g_free (priv->default_name);
+
 	g_list_foreach (priv->chatrooms, (GFunc)g_object_unref, NULL);
 	g_list_free (priv->chatrooms);
 
+	if (priv->contact_manager) {
+		g_object_unref (priv->contact_manager);
+	}
+
 	if (priv->account_manager) {
 		g_object_unref (priv->account_manager);
 	}
 
-	g_free (priv->chatrooms_file_name);
-	g_free (priv->default_name);
-
 	(G_OBJECT_CLASS (gossip_chatroom_manager_parent_class)->finalize) (object);
 }
 
 GossipChatroomManager *
 gossip_chatroom_manager_new (GossipAccountManager *account_manager,
+			     GossipContactManager *contact_manager,
 			     const gchar          *filename)
 {
 
@@ -171,6 +176,7 @@
 	GossipChatroomManagerPriv *priv;
 
 	g_return_val_if_fail (GOSSIP_IS_ACCOUNT_MANAGER (account_manager), NULL);
+	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (contact_manager), NULL);
 
 	manager = g_object_new (GOSSIP_TYPE_CHATROOM_MANAGER, NULL);
 
@@ -180,6 +186,10 @@
 		priv->account_manager = g_object_ref (account_manager);
 	}
 
+	if (contact_manager) {
+		priv->contact_manager = g_object_ref (contact_manager);
+	}
+
 	if (filename) {
 		priv->chatrooms_file_name = g_strdup (filename);
 	}
@@ -195,39 +205,48 @@
 			     GossipChatroom        *chatroom)
 {
 	GossipChatroomManagerPriv *priv;
+	GossipContact             *own_contact;
+	const gchar               *name;
 
 	g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), FALSE);
 	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), FALSE);
 
 	priv = GET_PRIV (manager);
 
-	/* don't add more than once */
-	if (!gossip_chatroom_manager_find (manager, gossip_chatroom_get_id (chatroom))) {
-		const gchar *name;
-
-		name = gossip_chatroom_get_name (chatroom);
-
-		gossip_debug (DEBUG_DOMAIN, "Adding %s chatroom with name:'%s'",
-			      gossip_chatroom_get_auto_connect (chatroom) ? "connecting on startup " : "",
-			      name);
-
-		g_signal_connect (chatroom, "notify::enabled",
-				  G_CALLBACK (chatroom_manager_chatroom_enabled_cb),
-				  manager);
-
-		g_signal_connect (chatroom, "notify::favourite",
-				  G_CALLBACK (chatroom_manager_chatroom_favourite_cb),
-				  manager);
-
-		priv->chatrooms = g_list_append (priv->chatrooms,
-						 g_object_ref (chatroom));
-
-		g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
-
-		return TRUE;
+	/* Don't add more than once */
+	if (gossip_chatroom_manager_find (manager, gossip_chatroom_get_id (chatroom))) {
+		return FALSE;
 	}
-
-	return FALSE;
+       
+	name = gossip_chatroom_get_name (chatroom);
+	
+	gossip_debug (DEBUG_DOMAIN, "Adding %s chatroom with name:'%s'",
+		      gossip_chatroom_get_auto_connect (chatroom) ? "connecting on startup " : "",
+		      name);
+
+	/* Make sure we have an 'own_contact' */
+	own_contact = gossip_contact_manager_find_or_create (priv->contact_manager,
+							     gossip_chatroom_get_account (chatroom),
+							     GOSSIP_CONTACT_TYPE_CHATROOM,
+							     gossip_chatroom_get_own_contact_id_str (chatroom),
+							     NULL);
+	
+	gossip_chatroom_set_own_contact (chatroom, own_contact);
+	
+	g_signal_connect (chatroom, "notify::enabled",
+			  G_CALLBACK (chatroom_manager_chatroom_enabled_cb),
+			  manager);
+	
+	g_signal_connect (chatroom, "notify::favorite",
+			  G_CALLBACK (chatroom_manager_chatroom_favorite_cb),
+			  manager);
+	
+	priv->chatrooms = g_list_append (priv->chatrooms,
+					 g_object_ref (chatroom));
+	
+	g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
+	
+	return TRUE;
 }
 
 void
@@ -251,7 +270,7 @@
 					      manager);
 
 	g_signal_handlers_disconnect_by_func (chatroom,
-					      chatroom_manager_chatroom_favourite_cb,
+					      chatroom_manager_chatroom_favorite_cb,
 					      manager);
 
 	link = g_list_find (priv->chatrooms, chatroom);
@@ -291,11 +310,11 @@
 }
 
 static void
-chatroom_manager_chatroom_favourite_cb (GossipChatroom        *chatroom,
+chatroom_manager_chatroom_favorite_cb (GossipChatroom        *chatroom,
 					GParamSpec            *arg1,
 					GossipChatroomManager *manager)
 {
-	g_signal_emit (manager, signals[CHATROOM_FAVOURITE_UPDATE], 0, chatroom);
+	g_signal_emit (manager, signals[CHATROOM_FAVORITE_UPDATE], 0, chatroom);
 }
 
 GList *
@@ -426,8 +445,6 @@
 			this_account = gossip_chatroom_get_account (chatroom);
 			if (this_account) {
 				same_account = gossip_account_equal (account, this_account);
-			} else {
-				same_account = FALSE;
 			}
 		}
 
@@ -441,6 +458,46 @@
 	return found;
 }
 
+GossipChatroom *
+gossip_chatroom_manager_find_or_create (GossipChatroomManager *manager,
+					GossipAccount         *account,
+					const gchar           *server,
+					const gchar           *room,
+					gboolean              *created)
+{
+	GossipChatroom *chatroom;
+	GList          *found;
+
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), NULL);
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+	g_return_val_if_fail (server != NULL, NULL);
+	g_return_val_if_fail (room != NULL, NULL);
+
+	found = gossip_chatroom_manager_find_extended (manager, 
+						       account, 
+						       server,
+						       room);
+
+	if (g_list_length (found) > 1) {
+		g_warning ("Expected ONE chatroom to be found, actually found %d, using first", 
+			   g_list_length (found));
+	}
+
+	if (created) {
+		*created = found == NULL;
+	}
+
+	if (found) {
+		chatroom = found->data;
+		g_list_free (found);
+	} else {
+		chatroom = gossip_chatroom_new (account, server, room);
+		gossip_chatroom_manager_add (manager, chatroom);
+	}
+
+	return chatroom;
+}
+
 void
 gossip_chatroom_manager_set_default (GossipChatroomManager *manager,
 				     GossipChatroom        *chatroom)
@@ -556,7 +613,7 @@
 	gchar                     *str;
 	gchar                     *name, *nick, *server;
 	gchar                     *room, *password, *account_name;
-	gboolean                   auto_connect, favourite;
+	gboolean                   auto_connect;
 
 	priv = GET_PRIV (manager);
 
@@ -567,7 +624,6 @@
 	room = NULL;
 	password = NULL;
 	auto_connect = TRUE;
-	favourite = FALSE;
 	account_name = NULL;
 
 	child = node->children;
@@ -608,13 +664,6 @@
 				auto_connect = FALSE;
 			}
 		}
-		else if (strcmp (tag, "favourite") == 0) {
-			if (strcmp (str, "yes") == 0) {
-				favourite = TRUE;
-			} else {
-				favourite = FALSE;
-			}
-		}
 		else if (strcmp (tag, "account") == 0) {
 			account_name = g_strdup (str);
 		}
@@ -625,13 +674,15 @@
 	}
 
 	if (name && server && room) {
-		chatroom = g_object_new (GOSSIP_TYPE_CHATROOM,
-					 "name", name,
-					 "server", server,
-					 "room", room,
-					 "auto_connect", auto_connect,
-					 "favourite", favourite,
-					 NULL);
+		GossipAccount *account = NULL;
+		
+		account = gossip_account_manager_find (priv->account_manager,
+						       account_name);
+		
+		chatroom = gossip_chatroom_new (account, server, room);
+		gossip_chatroom_set_name (chatroom, name);
+		gossip_chatroom_set_auto_connect (chatroom, auto_connect);
+		gossip_chatroom_set_favorite (chatroom, TRUE);
 
 		if (nick) {
 			gossip_chatroom_set_nick (chatroom, nick);
@@ -641,19 +692,6 @@
 			gossip_chatroom_set_password (chatroom, password);
 		}
 
-		if (account_name) {
-			GossipAccount *account = NULL;
-
-			if (priv->account_manager) {
-				account = gossip_account_manager_find (priv->account_manager,
-								       account_name);
-			}
-
-			if (account) {
-				gossip_chatroom_set_account (chatroom, account);
-			}
-		}
-
 		gossip_chatroom_manager_add (manager, chatroom);
 
 		g_object_unref (chatroom);
@@ -779,6 +817,11 @@
 
 		chatroom = l->data;
 
+		/* We only save favorites */
+		if (!gossip_chatroom_get_favorite (chatroom)) {
+			continue;
+		}
+
 		node = xmlNewChild (root, NULL, "chatroom", NULL);
 		xmlNewChild (node, NULL, "type", "normal"); /* We should remove this */
 		xmlNewTextChild (node, NULL, "name", gossip_chatroom_get_name (chatroom));
@@ -789,12 +832,9 @@
 
 		xmlNewTextChild (node, NULL, "password", gossip_chatroom_get_password (chatroom));
 		xmlNewChild (node, NULL, "auto_connect", gossip_chatroom_get_auto_connect (chatroom) ? "yes" : "no");
-		xmlNewChild (node, NULL, "favourite", gossip_chatroom_get_favourite (chatroom) ? "yes" : "no");
 
 		account = gossip_chatroom_get_account (chatroom);
-		if (account) {
-			xmlNewTextChild (node, NULL, "account", gossip_account_get_name (account));
-		}
+		xmlNewTextChild (node, NULL, "account", gossip_account_get_name (account));
 	}
 
 	/* Make sure the XML is indented properly */

Modified: trunk/libgossip/gossip-chatroom-manager.h
==============================================================================
--- trunk/libgossip/gossip-chatroom-manager.h	(original)
+++ trunk/libgossip/gossip-chatroom-manager.h	Sat Jul  5 12:58:07 2008
@@ -60,6 +60,11 @@
 							 GossipAccount         *account,
 							 const gchar           *server,
 							 const gchar           *room);
+GossipChatroom *gossip_chatroom_manager_find_or_create  (GossipChatroomManager *manager,
+							 GossipAccount         *account,
+							 const gchar           *server,
+							 const gchar           *room,
+							 gboolean              *created);
 gboolean        gossip_chatroom_manager_store           (GossipChatroomManager *manager);
 GList *         gossip_chatroom_manager_get_chatrooms   (GossipChatroomManager *manager,
 							 GossipAccount         *account);
@@ -68,7 +73,6 @@
 GossipChatroom *gossip_chatroom_manager_get_default     (GossipChatroomManager *manager);
 void            gossip_chatroom_manager_set_default     (GossipChatroomManager *manager,
 							 GossipChatroom        *chatroom);
-
 G_END_DECLS
 
 #endif /* __GOSSIP_CHATROOM_MANAGER_H__ */

Modified: trunk/libgossip/gossip-chatroom-provider.c
==============================================================================
--- trunk/libgossip/gossip-chatroom-provider.c	(original)
+++ trunk/libgossip/gossip-chatroom-provider.c	Sat Jul  5 12:58:07 2008
@@ -30,8 +30,8 @@
 static void chatroom_provider_base_init (gpointer g_class);
 
 enum {
-	CHATROOM_JOINED,
 	CHATROOM_KICKED,
+	CHATROOM_NICK_CHANGED,
 	CHATROOM_NEW_MESSAGE,
 	CHATROOM_NEW_EVENT,
 	CHATROOM_SUBJECT_CHANGED,
@@ -76,8 +76,8 @@
 	static gboolean initialized = FALSE;
 
 	if (!initialized) {
-		signals[CHATROOM_JOINED] =
-			g_signal_new ("chatroom-joined",
+		signals[CHATROOM_KICKED] =
+			g_signal_new ("chatroom-kicked",
 				      G_TYPE_FROM_CLASS (g_class),
 				      G_SIGNAL_RUN_LAST,
 				      0,
@@ -85,17 +85,15 @@
 				      libgossip_marshal_VOID__INT,
 				      G_TYPE_NONE,
 				      1, G_TYPE_INT);
-
-		signals[CHATROOM_KICKED] =
-			g_signal_new ("chatroom-kicked",
+		signals[CHATROOM_NICK_CHANGED] =
+			g_signal_new ("chatroom-nick-changed",
 				      G_TYPE_FROM_CLASS (g_class),
 				      G_SIGNAL_RUN_LAST,
 				      0,
 				      NULL, NULL,
-				      libgossip_marshal_VOID__INT,
+				      libgossip_marshal_VOID__INT_OBJECT_STRING,
 				      G_TYPE_NONE,
-				      1, G_TYPE_INT);
-
+				      3, G_TYPE_INT, GOSSIP_TYPE_CONTACT, G_TYPE_STRING);
 		signals[CHATROOM_NEW_MESSAGE] =
 			g_signal_new ("chatroom-new-message",
 				      G_TYPE_FROM_CLASS (g_class),
@@ -249,21 +247,6 @@
 	}
 }
 
-GSList *
-gossip_chatroom_provider_get_contacts (GossipChatroomProvider *provider,
-				       GossipChatroomId        id)
-{
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM_PROVIDER (provider), NULL);
-	g_return_val_if_fail (id > 0, NULL);
-
-	if (GOSSIP_CHATROOM_PROVIDER_GET_IFACE (provider)->get_contacts) {
-		return GOSSIP_CHATROOM_PROVIDER_GET_IFACE (provider)->get_contacts (provider,
-										    id);
-	}
-
-	return NULL;
-}
-
 GossipChatroom *
 gossip_chatroom_provider_find_by_id (GossipChatroomProvider *provider,
 				     GossipChatroomId        id)

Modified: trunk/libgossip/gossip-chatroom-provider.h
==============================================================================
--- trunk/libgossip/gossip-chatroom-provider.h	(original)
+++ trunk/libgossip/gossip-chatroom-provider.h	Sat Jul  5 12:58:07 2008
@@ -72,8 +72,6 @@
 					     GossipChatroomId        id,
 					     GossipContact          *contact,
 					     const gchar            *reason);
-	GSList *         (*get_contacts)    (GossipChatroomProvider *provider,
-					     GossipChatroomId        id);
 	GossipChatroom * (*find_by_id)      (GossipChatroomProvider *provider,
 					     GossipChatroomId        id);
 	GossipChatroom * (*find)            (GossipChatroomProvider *provider,
@@ -122,6 +120,9 @@
 							  const gchar            *reason);
 GSList *     gossip_chatroom_provider_get_contacts       (GossipChatroomProvider *provider,
 							  GossipChatroomId        id);
+GossipContact *
+             gossip_chatroom_provider_get_own_contacts   (GossipChatroomProvider *provider,
+							  GossipChatroomId        id);
 GossipChatroom *
 	     gossip_chatroom_provider_find_by_id         (GossipChatroomProvider *provider,
 							  GossipChatroomId        id);

Modified: trunk/libgossip/gossip-chatroom.c
==============================================================================
--- trunk/libgossip/gossip-chatroom.c	(original)
+++ trunk/libgossip/gossip-chatroom.c	Sat Jul  5 12:58:07 2008
@@ -26,6 +26,8 @@
 #include <glib/gi18n.h>
 
 #include "gossip-chatroom.h"
+#include "gossip-utils.h"
+#include "gossip-jid.h"
 
 #include "libgossip-marshal.h"
 
@@ -47,7 +49,7 @@
 	gchar                 *room;
 	gchar                 *password;
 	gboolean               auto_connect;
-	gboolean               favourite;
+	gboolean               favorite;
 
 	GossipChatroomFeature  features;
 	GossipChatroomStatus   status;
@@ -57,6 +59,8 @@
 	GossipChatroomError    last_error;
 
 	GHashTable            *contacts;
+	GossipContact         *own_contact;
+	gchar                 *own_contact_id_str;
 };
 
 static void gossip_chatroom_class_init (GossipChatroomClass *klass);
@@ -70,6 +74,7 @@
 					guint                param_id,
 					const GValue        *value,
 					GParamSpec          *pspec);
+static void chatroom_contact_info_free (gpointer             data);
 
 enum {
 	PROP_0,
@@ -84,11 +89,12 @@
 	PROP_ROOM,
 	PROP_PASSWORD,
 	PROP_AUTO_CONNECT,
-	PROP_FAVOURITE,
+	PROP_FAVORITE,
 	PROP_FEATURES,
 	PROP_STATUS,
 	PROP_OCCUPANTS,
 	PROP_LAST_ERROR,
+	PROP_OWN_CONTACT
 };
 
 enum {
@@ -171,6 +177,13 @@
 	object_class->set_property = chatroom_set_property;
 
 	g_object_class_install_property (object_class,
+					 PROP_ACCOUNT,
+					 g_param_spec_object ("account",
+							      "Chatroom Account",
+							      "The account associated with an chatroom",
+							      GOSSIP_TYPE_ACCOUNT,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
 					 PROP_ID,
 					 g_param_spec_int ("id",
 							   "Chatroom Id",
@@ -253,10 +266,10 @@
 							       G_PARAM_READWRITE));
 
 	g_object_class_install_property (object_class,
-					 PROP_FAVOURITE,
-					 g_param_spec_boolean ("favourite",
-							       "Chatroom Favourite",
-							       "Used to connect favourites quickly",
+					 PROP_FAVORITE,
+					 g_param_spec_boolean ("favorite",
+							       "Chatroom Favorite",
+							       "Used to connect favorites quickly",
 							       FALSE,
 							       G_PARAM_READWRITE));
 
@@ -300,11 +313,11 @@
 							    G_PARAM_READWRITE));
 
 	g_object_class_install_property (object_class,
-					 PROP_ACCOUNT,
-					 g_param_spec_object ("account",
-							      "Chatroom Account",
-							      "The account associated with an chatroom",
-							      GOSSIP_TYPE_ACCOUNT,
+					 PROP_OWN_CONTACT,
+					 g_param_spec_object ("own-contact",
+							      "Chatroom Own Contact",
+							      "The contact you are in this chatroom",
+							      GOSSIP_TYPE_CONTACT,
 							      G_PARAM_READWRITE));
 
 	signals[CONTACT_JOINED] =
@@ -346,17 +359,17 @@
 
 	priv = GET_PRIV (chatroom);
 
-	priv->id           = id++;
+	priv->id = id++;
 
 	priv->auto_connect = FALSE;
-	priv->favourite    = FALSE;
+	priv->favorite = FALSE;
 
-	priv->status       = GOSSIP_CHATROOM_STATUS_INACTIVE;
+	priv->status = GOSSIP_CHATROOM_STATUS_INACTIVE;
 
-	priv->contacts     = g_hash_table_new_full (gossip_contact_hash,
-						    gossip_contact_equal,
-						    (GDestroyNotify) g_object_unref,
-						    g_free);
+	priv->contacts = g_hash_table_new_full (gossip_contact_hash,
+						gossip_contact_equal,
+						(GDestroyNotify) g_object_unref,
+						chatroom_contact_info_free);
 }
 
 static void
@@ -366,6 +379,10 @@
 
 	priv = GET_PRIV (object);
 
+	if (priv->account) {
+		g_object_unref (priv->account);
+	}
+
 	g_free (priv->id_str);
 
 	g_free (priv->name);
@@ -374,12 +391,12 @@
 	g_free (priv->room);
 	g_free (priv->password);
 
-	if (priv->account) {
-		g_object_unref (priv->account);
-	}
-
 	g_hash_table_destroy (priv->contacts);
 
+	if (priv->own_contact) {
+		g_object_unref (priv->own_contact);
+	}
+
 	(G_OBJECT_CLASS (gossip_chatroom_parent_class)->finalize) (object);
 }
 
@@ -427,8 +444,8 @@
 	case PROP_AUTO_CONNECT:
 		g_value_set_boolean (value, priv->auto_connect);
 		break;
-	case PROP_FAVOURITE:
-		g_value_set_boolean (value, priv->favourite);
+	case PROP_FAVORITE:
+		g_value_set_boolean (value, priv->favorite);
 		break;
 	case PROP_FEATURES:
 		g_value_set_int (value, priv->features);
@@ -442,6 +459,9 @@
 	case PROP_LAST_ERROR:
 		g_value_set_enum (value, priv->last_error);
 		break;
+	case PROP_OWN_CONTACT:
+		g_value_set_object (value, priv->own_contact);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -495,9 +515,9 @@
 		gossip_chatroom_set_auto_connect (GOSSIP_CHATROOM (object),
 						 g_value_get_boolean (value));
 		break;
-	case PROP_FAVOURITE:
-		gossip_chatroom_set_favourite (GOSSIP_CHATROOM (object),
-					       g_value_get_boolean (value));
+	case PROP_FAVORITE:
+		gossip_chatroom_set_favorite (GOSSIP_CHATROOM (object),
+					      g_value_get_boolean (value));
 		break;
 	case PROP_FEATURES:
 		gossip_chatroom_set_features (GOSSIP_CHATROOM (object),
@@ -515,12 +535,100 @@
 		gossip_chatroom_set_last_error (GOSSIP_CHATROOM (object),
 						g_value_get_enum (value));
 		break;
+	case PROP_OWN_CONTACT:
+		gossip_chatroom_set_own_contact (GOSSIP_CHATROOM (object),
+						 g_value_get_object (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
 	};
 }
 
+static void
+update_id_str (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	priv = GET_PRIV (chatroom);
+
+	if (priv->room && priv->server) {
+		g_free (priv->id_str);
+		priv->id_str = g_strconcat (priv->room, "@", priv->server, NULL);
+	}
+}
+
+static void
+update_own_contact_id_str (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	priv = GET_PRIV (chatroom);
+
+	if (priv->id_str && priv->nick) {
+		g_free (priv->own_contact_id_str);
+		priv->own_contact_id_str = g_strconcat (priv->id_str, "/", priv->nick, NULL);
+	}
+}
+
+static void
+update_own_contact (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	priv = GET_PRIV (chatroom);
+
+	/* We can't do anything without an account, own contact or
+	 * id_str (room and server) to generate the own
+	 * contact.
+	 */
+	if (!priv->account || 
+	    !priv->own_contact ||
+	    !priv->id_str ||
+	    !priv->own_contact_id_str) {
+		return;
+	}
+
+	/* First try the nick that we have saved in the Chatroom */
+	if (G_STR_EMPTY (priv->nick)) {
+		const gchar *account_id;
+		gchar       *part_name;
+
+		/* Second try to use the name part of the account ID */
+		account_id = gossip_account_get_id (priv->account);
+		part_name = gossip_jid_string_get_part_name (account_id);
+
+		/* Update nick if we don't have one */
+		gossip_chatroom_set_nick (chatroom, part_name);
+		g_free (part_name);
+	}
+
+	/* Update id and name */
+	gossip_contact_set_id (priv->own_contact, priv->own_contact_id_str);
+	gossip_contact_set_name (priv->own_contact, priv->nick);	
+}
+
+GossipChatroom *
+gossip_chatroom_new (GossipAccount *account,
+		     const gchar   *server,
+		     const gchar   *room)
+{
+	GossipChatroom *chatroom;
+
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+	g_return_val_if_fail (server != NULL, NULL);
+	g_return_val_if_fail (room != NULL, NULL);
+
+	chatroom = g_object_new (GOSSIP_TYPE_CHATROOM, 
+				 "account", account,
+				 "server", server,
+				 "room", room,
+				 "name", room, /* Use the room as the name */
+				 NULL);
+
+	return chatroom;
+}
+
 GossipAccount *
 gossip_chatroom_get_account (GossipChatroom *chatroom)
 {
@@ -653,7 +761,7 @@
 }
 
 gboolean
-gossip_chatroom_get_favourite (GossipChatroom *chatroom)
+gossip_chatroom_get_favorite (GossipChatroom *chatroom)
 {
 	GossipChatroomPriv *priv;
 
@@ -661,7 +769,7 @@
 
 	priv = GET_PRIV (chatroom);
 
-	return priv->favourite;
+	return priv->favorite;
 }
 
 GossipChatroomFeature
@@ -727,6 +835,46 @@
 	return g_hash_table_lookup (priv->contacts, contact);
 }
 
+GList *
+gossip_chatroom_get_contacts (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), NULL);
+
+	priv = GET_PRIV (chatroom);
+
+	return g_hash_table_get_keys (priv->contacts);
+}
+
+GossipContact *
+gossip_chatroom_get_own_contact (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), NULL);
+
+	priv = GET_PRIV (chatroom);
+
+	return priv->own_contact;
+}
+
+const gchar *
+gossip_chatroom_get_own_contact_id_str (GossipChatroom *chatroom)
+{
+	GossipChatroomPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), NULL);
+
+	priv = GET_PRIV (chatroom);
+
+	if (!priv->own_contact_id_str) {
+		priv->own_contact_id_str = g_strdup ("");
+	}
+
+	return priv->own_contact_id_str;
+}
+
 void
 gossip_chatroom_set_account (GossipChatroom *chatroom,
 			     GossipAccount  *account)
@@ -743,6 +891,9 @@
 
 	priv->account = g_object_ref (account);
 
+	/* Update dependencies, like own-contact */
+	update_own_contact (chatroom);
+
 	g_object_notify (G_OBJECT (chatroom), "account");
 }
 
@@ -811,6 +962,10 @@
 	g_free (priv->nick);
 	priv->nick = g_strdup (nick);
 
+	/* Update dependencies, like own-contact */
+	update_own_contact_id_str (chatroom);
+	update_own_contact (chatroom);
+
 	g_object_notify (G_OBJECT (chatroom), "nick");
 }
 
@@ -828,10 +983,10 @@
 	g_free (priv->server);
 	priv->server = g_strdup (server);
 
-	if (priv->room && priv->server) {
-		g_free (priv->id_str);
-		priv->id_str = g_strdup_printf ("%s %s", priv->room, priv->server);
-	}
+	/* Update dependencies, like own-contact */
+	update_id_str (chatroom);
+	update_own_contact_id_str (chatroom);
+	update_own_contact (chatroom);
 
 	g_object_notify (G_OBJECT (chatroom), "server");
 }
@@ -850,10 +1005,10 @@
 	g_free (priv->room);
 	priv->room = g_strdup (room);
 
-	if (priv->room && priv->server) {
-		g_free (priv->id_str);
-		priv->id_str = g_strdup_printf ("%s %s", priv->room, priv->server);
-	}
+	/* Update dependencies, like own-contact */
+	update_id_str (chatroom);
+	update_own_contact_id_str (chatroom);
+	update_own_contact (chatroom);
 
 	g_object_notify (G_OBJECT (chatroom), "room");
 }
@@ -890,17 +1045,17 @@
 }
 
 void
-gossip_chatroom_set_favourite (GossipChatroom *chatroom,
-			       gboolean        favourite)
+gossip_chatroom_set_favorite (GossipChatroom *chatroom,
+			      gboolean        favorite)
 {
 	GossipChatroomPriv *priv;
 
 	g_return_if_fail (GOSSIP_IS_CHATROOM (chatroom));
 
 	priv = GET_PRIV (chatroom);
-	priv->favourite = favourite;
+	priv->favorite = favorite;
 
-	g_object_notify (G_OBJECT (chatroom), "favourite");
+	g_object_notify (G_OBJECT (chatroom), "favorite");
 }
 
 static void
@@ -977,6 +1132,12 @@
 	g_object_notify (G_OBJECT (chatroom), "last-error");
 }
 
+static void
+chatroom_contact_info_free (gpointer data)
+{
+	g_free (data);
+}
+
 void
 gossip_chatroom_set_contact_info (GossipChatroom            *chatroom,
 				  GossipContact             *contact,
@@ -1002,36 +1163,67 @@
 	g_signal_emit (chatroom, signals[CONTACT_INFO_CHANGED], 0, contact);
 }
 
+void
+gossip_chatroom_set_own_contact (GossipChatroom *chatroom,
+				 GossipContact  *own_contact)
+{
+	GossipChatroomPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_CHATROOM (chatroom));
+	g_return_if_fail (GOSSIP_IS_CONTACT (own_contact));
+
+	priv = GET_PRIV (chatroom);
+
+	if (priv->own_contact) {
+		g_object_unref (priv->own_contact);
+	}
+
+	priv->own_contact = g_object_ref (own_contact);
+
+	g_object_notify (G_OBJECT (chatroom), "own-contact");
+}
+
 guint
 gossip_chatroom_hash (gconstpointer key)
 {
-	GossipChatroomPriv *priv;
+	const gchar *id_str;
+	guint        hash = 0;
 
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM (key), 0);
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (key), +1);
 
-	priv = GET_PRIV (key);
+	id_str = gossip_chatroom_get_id_str (GOSSIP_CHATROOM (key));
+	g_return_val_if_fail (!G_STR_EMPTY (id_str), +1);
 
-	return g_int_hash (&priv->id);
+	hash += gossip_account_hash (gossip_chatroom_get_account (GOSSIP_CHATROOM (key)));
+	hash += g_str_hash (id_str);
+
+	return hash;
 }
 
 gboolean
-gossip_chatroom_equal (gconstpointer a,
-		       gconstpointer b)
+gossip_chatroom_equal (gconstpointer v1,
+		       gconstpointer v2)
 {
-	GossipChatroomPriv *priv1;
-	GossipChatroomPriv *priv2;
+	GossipAccount *account_a;
+	GossipAccount *account_b;
+	const gchar   *id_a;
+	const gchar   *id_b;
+	gboolean       equal;
 
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM (a), FALSE);
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM (b), FALSE);
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (v1), FALSE);
+	g_return_val_if_fail (GOSSIP_IS_CHATROOM (v2), FALSE);
 
-	priv1 = GET_PRIV (a);
-	priv2 = GET_PRIV (b);
+	account_a = gossip_chatroom_get_account (GOSSIP_CHATROOM (v1));
+	account_b = gossip_chatroom_get_account (GOSSIP_CHATROOM (v2));
 
-	if (!priv1 || !priv2) {
-		return FALSE;
-	}
+	id_a = gossip_chatroom_get_id_str (GOSSIP_CHATROOM (v1));
+	id_b = gossip_chatroom_get_id_str (GOSSIP_CHATROOM (v2));
+
+	equal  = TRUE;
+	equal &= gossip_account_equal (account_a, account_b);
+	equal &= g_str_equal (id_a, id_b);
 
-	return (priv1->id == priv2->id);
+	return equal;
 }
 
 gboolean
@@ -1109,31 +1301,38 @@
 gossip_chatroom_error_to_string (GossipChatroomError error)
 {
 	switch (error) {
+	case GOSSIP_CHATROOM_ERROR_NONE: 
+		break;
 	case GOSSIP_CHATROOM_ERROR_PASSWORD_INVALID_OR_MISSING:
 		return _("The chat room you tried to join requires a password. "
-			 "You either failed to supply a password or the password you tried was incorrect.");
-	case GOSSIP_CHATROOM_ERROR_USER_BANNED:
-		return _("You have been banned from this chatroom.");
+			 "You either failed to supply a password or the password you tried was incorrect");	case GOSSIP_CHATROOM_ERROR_USER_BANNED:
+		return _("You have been banned from this chatroom");
 	case GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND:
-		return _("The conference room you tried to join could not be found.");
+		return _("The conference room you tried to join could not be found");
 	case GOSSIP_CHATROOM_ERROR_ROOM_CREATION_RESTRICTED:
-		return _("Chatroom creation is restricted on this server.");
+		return _("Chatroom creation is restricted on this server");
 	case GOSSIP_CHATROOM_ERROR_USE_RESERVED_ROOM_NICK:
-		return _("Chatroom reserved nick names must be used on this server.");
+		return _("Chatroom reserved nick names must be used on this server");
 	case GOSSIP_CHATROOM_ERROR_NOT_ON_MEMBERS_LIST:
-		return _("You are not on the chatroom's members list.");
+		return _("You are not on the chatroom's members list");
 	case GOSSIP_CHATROOM_ERROR_NICK_IN_USE:
-		return _("The nickname you have chosen is already in use.");
+		return _("The nickname you have chosen is already in use");
 	case GOSSIP_CHATROOM_ERROR_MAXIMUM_USERS_REACHED:
-		return _("The maximum number of users for this chatroom has been reached.");
+		return _("The maximum number of users for this chatroom has been reached");
+	case GOSSIP_CHATROOM_ERROR_UNAUTHORIZED_REQUEST:
+		return _("Unauthorized request, you do not have privileges to do that");
+	case GOSSIP_CHATROOM_ERROR_FORBIDDEN:
+		return _("That action is forbidden");
+	case GOSSIP_CHATROOM_ERROR_ALREADY_OPEN: 
+		break;
 	case GOSSIP_CHATROOM_ERROR_TIMED_OUT:
-		return _("The remote conference server did not respond in a sensible time.");
-	case GOSSIP_CHATROOM_ERROR_UNKNOWN:
-		return _("An unknown error occurred, check your details are correct.");
+		return _("The remote conference server did not respond in a sensible time");
 	case GOSSIP_CHATROOM_ERROR_CANCELED:
-		return _("Joining the chatroom was canceled.");
-	default:
-		break;
+		return _("Joining the chatroom was canceled");
+	case GOSSIP_CHATROOM_ERROR_BAD_REQUEST:
+		return _("A bad request was sent to the server");
+	case GOSSIP_CHATROOM_ERROR_UNKNOWN:
+		return _("An unknown error occurred, check your details are correct");
 	}
 
 	return "";

Modified: trunk/libgossip/gossip-chatroom.h
==============================================================================
--- trunk/libgossip/gossip-chatroom.h	(original)
+++ trunk/libgossip/gossip-chatroom.h	Sat Jul  5 12:58:07 2008
@@ -83,11 +83,14 @@
 	GOSSIP_CHATROOM_ERROR_NOT_ON_MEMBERS_LIST,
 	GOSSIP_CHATROOM_ERROR_NICK_IN_USE,
 	GOSSIP_CHATROOM_ERROR_MAXIMUM_USERS_REACHED,
+	GOSSIP_CHATROOM_ERROR_UNAUTHORIZED_REQUEST,
+	GOSSIP_CHATROOM_ERROR_FORBIDDEN,
 
 	/* Internal errors */
 	GOSSIP_CHATROOM_ERROR_ALREADY_OPEN,
 	GOSSIP_CHATROOM_ERROR_TIMED_OUT,
 	GOSSIP_CHATROOM_ERROR_CANCELED,
+	GOSSIP_CHATROOM_ERROR_BAD_REQUEST,
 	GOSSIP_CHATROOM_ERROR_UNKNOWN
 } GossipChatroomError;
 
@@ -143,20 +146,24 @@
 
 /* Chatroom */
 GType          gossip_chatroom_get_type                (void) G_GNUC_CONST;
+GossipChatroom *
+               gossip_chatroom_new                     (GossipAccount             *account,
+							const gchar               *server,
+							const gchar               *room);
 GossipAccount * 
-               gossip_chatroom_get_account             (GossipChatroom           *chatroom);
+               gossip_chatroom_get_account             (GossipChatroom            *chatroom);
 GossipChatroomId 
-               gossip_chatroom_get_id                  (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_id_str              (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_name                (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_description         (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_subject             (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_nick                (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_server              (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_room                (GossipChatroom           *chatroom);
-const gchar *  gossip_chatroom_get_password            (GossipChatroom           *chatroom);
-gboolean       gossip_chatroom_get_auto_connect        (GossipChatroom           *chatroom);
-gboolean       gossip_chatroom_get_favourite           (GossipChatroom           *chatroom);
+               gossip_chatroom_get_id                  (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_id_str              (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_name                (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_description         (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_subject             (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_nick                (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_server              (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_room                (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_password            (GossipChatroom            *chatroom);
+gboolean       gossip_chatroom_get_auto_connect        (GossipChatroom            *chatroom);
+gboolean       gossip_chatroom_get_favorite            (GossipChatroom            *chatroom);
 
 GossipChatroomFeature
                gossip_chatroom_get_features            (GossipChatroom            *chatroom);
@@ -168,7 +175,10 @@
 GossipChatroomContactInfo *
                gossip_chatroom_get_contact_info        (GossipChatroom            *chatroom,
 							GossipContact             *contact);
+GList *        gossip_chatroom_get_contacts            (GossipChatroom            *chatroom);
 
+GossipContact *gossip_chatroom_get_own_contact         (GossipChatroom            *chatroom);
+const gchar *  gossip_chatroom_get_own_contact_id_str  (GossipChatroom            *chatroom);
 void           gossip_chatroom_set_account             (GossipChatroom            *chatroom,
 							GossipAccount             *account);
 void           gossip_chatroom_set_name                (GossipChatroom            *chatroom,
@@ -187,8 +197,8 @@
 							const gchar               *password);
 void           gossip_chatroom_set_auto_connect        (GossipChatroom            *chatroom,
 							gboolean                   auto_connect);
-void           gossip_chatroom_set_favourite           (GossipChatroom            *chatroom,
-							gboolean                   favourite);
+void           gossip_chatroom_set_favorite            (GossipChatroom            *chatroom,
+							gboolean                   favorite);
 void           gossip_chatroom_set_features            (GossipChatroom            *chatroom,
 							GossipChatroomFeature      features);
 void           gossip_chatroom_set_status              (GossipChatroom            *chatroom,
@@ -200,6 +210,8 @@
 void           gossip_chatroom_set_contact_info        (GossipChatroom            *chatroom,
 							GossipContact             *contact,
 							GossipChatroomContactInfo *info);
+void           gossip_chatroom_set_own_contact         (GossipChatroom            *chatroom,
+							GossipContact             *contact);
 
 /* Utils */
 guint          gossip_chatroom_hash                    (gconstpointer              key);

Modified: trunk/libgossip/gossip-contact-manager.c
==============================================================================
--- trunk/libgossip/gossip-contact-manager.c	(original)
+++ trunk/libgossip/gossip-contact-manager.c	Sat Jul  5 12:58:07 2008
@@ -29,7 +29,7 @@
 
 #include "gossip-session.h"
 #include "gossip-debug.h"
-#include "gossip-jabber.h"
+#include "gossip-jabber-utils.h"
 #include "gossip-contact-manager.h"
 #include "gossip-account-manager.h"
 #include "gossip-private.h"
@@ -49,7 +49,7 @@
 struct _GossipContactManagerPriv {
 	GossipSession *session;
 
-	GList         *contacts;
+	GHashTable    *contacts;
 	gchar         *contacts_file_name;
 
 	guint          store_timeout_id;
@@ -89,8 +89,7 @@
 
 	priv = GET_PRIV (object);
 
-	g_list_foreach (priv->contacts, (GFunc) g_object_unref, NULL);
-	g_list_free (priv->contacts);
+	g_hash_table_unref (priv->contacts);
 
 	g_free (priv->contacts_file_name);
 
@@ -129,6 +128,11 @@
 			  G_CALLBACK (contact_manager_contact_added_cb),
 			  manager);
 
+	priv->contacts = g_hash_table_new_full (gossip_contact_hash,
+						gossip_contact_equal,
+						g_object_unref,
+						NULL);
+
 	if (filename) {
 		priv->contacts_file_name = g_strdup (filename);
 	}
@@ -146,8 +150,6 @@
 	GossipContactManagerPriv *priv;
 	GossipAccount            *account;
 	GossipContact            *own_contact;
-	const gchar              *contact_id;
-	gboolean                  found;
 	
 	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager), FALSE);
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), FALSE);
@@ -155,27 +157,28 @@
 	priv = GET_PRIV (manager);
 
 	account = gossip_contact_get_account (contact);
-	own_contact = gossip_session_get_own_contact (priv->session, account);
-	contact_id = gossip_contact_get_id (contact);
+	own_contact = gossip_contact_manager_get_own_contact (manager, account);
 
 	/* Don't add if it is a self contact, since we do this ourselves */
 	if (gossip_contact_equal (own_contact, contact)) {
-		return TRUE;
+		return FALSE;
 	}
 
 	/* Don't add more than once */
-	found = gossip_contact_manager_find (manager, account, contact_id) != NULL;
-
-	if (!found) {
-		gossip_debug (DEBUG_DOMAIN, 
-			      "Adding contact from account:'%s' with id:'%s'",
-			      gossip_account_get_name (account),
-			      contact_id);
-
-		priv->contacts = g_list_append (priv->contacts, g_object_ref (contact));
+	if (g_hash_table_lookup (priv->contacts, contact)) {
+		return FALSE;
 	}
 
-	return !found;
+	gossip_debug (DEBUG_DOMAIN, 
+		      "Adding contact with account:'%s' with id:'%s'",
+		      gossip_account_get_name (account),
+		      gossip_contact_get_id (contact));
+
+	g_hash_table_insert (priv->contacts, 
+			     g_object_ref (contact),
+			     GINT_TO_POINTER (1));
+
+	return TRUE;
 }
 
 void
@@ -183,23 +186,20 @@
 			       GossipContact        *contact)
 {
 	GossipContactManagerPriv *priv;
-	GList                    *l;
+	GossipAccount            *account;
 
 	g_return_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager));
 	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
 
 	priv = GET_PRIV (manager);
 
-	gossip_debug (DEBUG_DOMAIN,
-		      "Removing contact with name:'%s'",
-		      gossip_contact_get_name (contact));
+	account = gossip_contact_get_account (contact);
+	gossip_debug (DEBUG_DOMAIN, 
+		      "Removing contact with account:'%s' with id:'%s'",
+		      gossip_account_get_name (account),
+		      gossip_contact_get_id (contact));
 
-	l = g_list_find (priv->contacts, contact);
-	if (l) {
-		priv->contacts = g_list_remove_link (priv->contacts, l);
-		g_object_unref (contact);
-		g_list_free1 (l);
-	}
+	g_hash_table_remove (priv->contacts, contact);
 }
 
 GossipContact *
@@ -208,34 +208,224 @@
 			     const gchar          *contact_id)
 {
 	GossipContactManagerPriv *priv;
+	GossipContact            *found = NULL;
+	GList                    *contacts;
 	GList                    *l;
 
 	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager), NULL);
-	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
 	g_return_val_if_fail (contact_id != NULL, NULL);
 
 	priv = GET_PRIV (manager);
 
-	for (l = priv->contacts; l; l = l->next) {
-		GossipAccount *this_account;
+	contacts = g_hash_table_get_keys (priv->contacts);
+
+	for (l = contacts; l && !found; l = l->next) {
 		GossipContact *this_contact;
 		const gchar   *this_contact_id;
+		gboolean       equal;
 
 		this_contact = l->data;
-		this_account = gossip_contact_get_account (this_contact);
 		this_contact_id = gossip_contact_get_id (this_contact);
 
 		if (!this_contact_id) {
 			continue;
 		}
 
-		if (gossip_account_equal (account, this_account) &&
-		    strcmp (this_contact_id, contact_id) == 0) {
-			return this_contact;
+		equal = TRUE;
+		
+		if (account) {
+			GossipAccount *this_account;
+
+			this_account = gossip_contact_get_account (this_contact);
+			equal &= gossip_account_equal (account, this_account);
+		}
+
+		equal &= g_str_equal (contact_id, this_contact_id);
+		
+		if (equal) {
+			found = this_contact;
 		}
 	}
 
-	return NULL;
+	g_list_free (contacts);
+
+	return found;
+}
+
+GossipContact *
+gossip_contact_manager_find_extended (GossipContactManager *manager,
+				      GossipAccount        *account,
+				      GossipContactType     contact_type,
+				      const gchar          *contact_id)
+{
+	GossipContactManagerPriv *priv;
+	GossipContact            *found = NULL;
+	GList                    *contacts;
+	GList                    *l;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager), NULL);
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+	g_return_val_if_fail (contact_id != NULL, NULL);
+
+	priv = GET_PRIV (manager);
+
+	contacts = g_hash_table_get_keys (priv->contacts);
+
+	for (l = contacts; l && !found; l = l->next) {
+		GossipContact     *this_contact;
+		GossipContactType  this_contact_type;
+		const gchar       *this_contact_id;
+		gboolean           equal;
+		
+		this_contact = l->data;
+		this_contact_id = gossip_contact_get_id (this_contact);
+
+		if (!this_contact_id) {
+			continue;
+		}
+
+		equal = TRUE;
+		
+		if (account) {
+			GossipAccount *this_account;
+
+			this_account = gossip_contact_get_account (this_contact);
+			equal &= gossip_account_equal (account, this_account);
+		}
+
+		this_contact_type = gossip_contact_get_type (this_contact);
+		
+		equal &= contact_type == this_contact_type;
+		equal &= g_str_equal (contact_id, this_contact_id);
+		
+		if (equal) {
+			found = this_contact;
+		}
+	}
+
+	g_list_free (contacts);
+
+	return found;
+}
+
+GossipContact *
+gossip_contact_manager_find_or_create (GossipContactManager *manager,
+				       GossipAccount        *account,
+				       GossipContactType     contact_type,
+				       const gchar          *contact_id,
+				       gboolean             *created)
+{
+	GossipContact *contact;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager), NULL);
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+	g_return_val_if_fail (contact_id != NULL, NULL);
+
+	/* Special case chatrooms, since we want to ONLY search for
+	 * the contact id, account and type together for chatrooms,
+	 * for other contacts, just account and contact id are fine
+	 * because there is a chance that a contact is temporary until
+	 * added to your contact list.
+	 */
+	if (contact_type == GOSSIP_CONTACT_TYPE_CHATROOM) { 
+		contact = gossip_contact_manager_find_extended (manager, 
+								account, 
+								contact_type,
+								contact_id);
+	} else {
+		contact = gossip_contact_manager_find (manager, 
+						       account, 
+						       contact_id);
+	}
+
+	if (created) {
+		*created = contact == NULL;
+	}
+
+	if (contact) {
+		gossip_debug (DEBUG_DOMAIN,
+			      "Get contact:'%s', (%s)",
+			      gossip_contact_get_id (contact), 
+			      gossip_contact_type_to_string (contact_type));
+
+		return contact;
+	} else {
+		gchar *name;
+
+		gossip_debug (DEBUG_DOMAIN,
+			      "New contact:'%s' (%s)",
+			      contact_id,
+			      gossip_contact_type_to_string (contact_type));
+
+		name = gossip_jabber_get_name_to_use (contact_id, NULL, NULL, NULL);
+
+		/* Create the contact using a default name as the ID */
+		contact = gossip_contact_new (contact_type, account);
+		gossip_contact_set_id (contact, contact_id);
+		gossip_contact_set_name (contact, name);
+		g_free (name);
+
+		gossip_contact_manager_add (manager, contact);
+		gossip_contact_manager_store (manager);
+	}
+
+	return contact;
+}
+
+GossipContact *
+gossip_contact_manager_get_own_contact (GossipContactManager *manager,
+					GossipAccount        *account)
+{
+	GossipContact     *contact;
+	GossipContactType  contact_type;
+	const gchar       *contact_id;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT_MANAGER (manager), NULL);
+	g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), NULL);
+
+	contact_type = GOSSIP_CONTACT_TYPE_USER;
+	contact_id = gossip_account_get_id (account);
+
+	contact = gossip_contact_manager_find_extended (manager, 
+							account, 
+							contact_type,
+							contact_id);
+
+	if (contact) {
+		gossip_debug (DEBUG_DOMAIN,
+			      "Get own contact:'%s', (%s)",
+			      gossip_contact_get_id (contact), 
+			      gossip_contact_type_to_string (contact_type));
+
+		return contact;
+	} else {
+		GossipContactManagerPriv *priv;
+		gchar                    *name;
+
+		gossip_debug (DEBUG_DOMAIN,
+			      "New own contact:'%s' (%s)",
+			      contact_id,
+			      gossip_contact_type_to_string (contact_type));
+		
+		name = gossip_jabber_get_name_to_use (contact_id, NULL, NULL, NULL);
+
+		/* Create the contact using a default name as the ID */
+		contact = gossip_contact_new (contact_type, account);
+		gossip_contact_set_id (contact, contact_id);
+		gossip_contact_set_name (contact, name);
+		g_free (name);
+
+		/* Don't use _manager_add() - recursive loop */
+		priv = GET_PRIV (manager);
+		
+		g_hash_table_insert (priv->contacts, 
+				     contact,
+				     GINT_TO_POINTER (1));
+
+		gossip_contact_manager_store (manager);
+		
+		return contact;
+	}
 }
 
 static gboolean
@@ -339,16 +529,15 @@
 	name = gossip_xml_node_get_child_content (node, "name");
 
 	if (id && name) {
-		GossipContactManagerPriv *priv;
-		GossipJabber             *jabber;
-		GossipContact            *contact;
-
-		priv = GET_PRIV (manager);
-
-		jabber = gossip_session_get_protocol (priv->session, account); 
-		contact = gossip_jabber_new_contact (jabber, id, name);
+		GossipContact *contact;
 
-		gossip_contact_manager_add (manager, contact);
+		contact = gossip_contact_manager_find_or_create (manager, 
+								 account,
+								 GOSSIP_CONTACT_TYPE_TEMPORARY,
+								 id,
+								 NULL);
+		
+		gossip_contact_set_name (contact, name);
 	}
 
 	xmlFree (name);
@@ -361,8 +550,7 @@
 			    xmlNodePtr            node)
 {
 	GossipContactManagerPriv *priv;
-	GossipJabber             *jabber;
-	GossipContact            *contact;
+	GossipContact            *own_contact;
 	const gchar              *id;
 	const gchar              *name;
 	gchar                    *new_name;
@@ -374,11 +562,10 @@
 
 	priv = GET_PRIV (manager);
 
-	jabber = gossip_session_get_protocol (priv->session, account); 
-	contact = gossip_jabber_get_own_contact (jabber);
+	own_contact = gossip_contact_manager_get_own_contact (manager, account);
 
-	id = gossip_contact_get_id (contact);
-	name = gossip_contact_get_name (contact);
+	id = gossip_contact_get_id (own_contact);
+	name = gossip_contact_get_name (own_contact);
 	
 	/* We only set the name here if it is the contact ID or NULL
 	 * because this is only needed for offline purposes, we must
@@ -387,7 +574,7 @@
 	 * correctly. 
 	 */ 
 	if (G_STR_EMPTY (name) || (!G_STR_EMPTY (id) && strcmp (id, name) == 0)) {
-		gossip_contact_set_name (contact, new_name);
+		gossip_contact_set_name (own_contact, new_name);
 	}
 
 	xmlFree (new_name);
@@ -483,7 +670,7 @@
 
 	gossip_debug (DEBUG_DOMAIN,
 		      "Parsed %d contacts",
-		      g_list_length (priv->contacts));
+		      g_hash_table_size (priv->contacts));
 
 	xmlFreeDoc(doc);
 	xmlFreeParserCtxt (ctxt);
@@ -506,6 +693,7 @@
 	xmlDocPtr                 doc;
 	xmlNodePtr                root;
 	GList                    *accounts;
+	GList                    *contacts;
 	GList                    *l;
 	GHashTable               *nodes;
 	gboolean                  create_file = FALSE;
@@ -571,8 +759,6 @@
 	account_manager = gossip_session_get_account_manager (priv->session);
 	accounts = gossip_account_manager_get_accounts (account_manager);
 
-	gossip_debug (DEBUG_DOMAIN, "Checking account nodes exist");
-
 	for (l = accounts; l; l = l->next) {
 		xmlNodePtr node;
 		gboolean   exists = FALSE;
@@ -605,16 +791,9 @@
 		}
 
 		if (exists) {
-			gossip_debug (DEBUG_DOMAIN, 
-				      "Using existing xml node for account:'%s'", 
-				      account_name);
 			continue;
 		}
 
-		gossip_debug (DEBUG_DOMAIN, 
-			      "Creating xml node for account:'%s'", 
-			      account_name);
-
 		/* Add node for this account */
 		node = xmlNewChild (root, NULL, "account", NULL);
 		xmlNewProp (node, "name", account_name);
@@ -625,26 +804,20 @@
 	/* This is a self contact */
 	for (l = accounts; l; l = l->next) {
 		xmlNodePtr     node, child, p;
-		GossipJabber  *jabber;
-		GossipContact *contact;
+		GossipContact *own_contact;
 		const gchar   *name; 
 
 		account = l->data;
 		
-		jabber = gossip_session_get_protocol (priv->session, account);
-		contact = gossip_jabber_get_own_contact (jabber);
-		
-		name = gossip_contact_get_name (contact);
+		own_contact = gossip_contact_manager_get_own_contact (manager, account);
+	
+		name = gossip_contact_get_name (own_contact);
 
 		node = g_hash_table_lookup (nodes, account);
 		
 		/* Set self details */
 		p = gossip_xml_node_get_child (node, "self");
 		if (!p) {
-			gossip_debug (DEBUG_DOMAIN, 
-				      "Creating xml node for self contact for "
-				      "account:'%s'", 
-				      gossip_account_get_name (account));
 			child = xmlNewChild (node, NULL, "self", NULL);
 		} else {
 			child = p;
@@ -655,26 +828,36 @@
 			child = xmlNewChild (child, NULL, "name", NULL);
 		} else {
 			child = p;
-		}
-		
-		gossip_debug (DEBUG_DOMAIN, 
-			      "Updating xml node for self contact for "
-			      "account:'%s' with name:'%s'", 
-			      gossip_account_get_name (account),
-			      name);
-		
+		}	
+	
 		xmlNodeSetContent (child, name);
 	}
 
-	for (l = priv->contacts; l; l = l->next) {
-		xmlNodePtr     node, child, p;
-		GossipContact *contact;
-		const gchar   *id;
-		const gchar   *name; 
+	contacts = g_hash_table_get_keys (priv->contacts);
+
+	for (l = contacts; l; l = l->next) {
+		xmlNodePtr         node, child, p;
+		GossipContact     *contact;
+		GossipContactType  type;
+		const gchar       *id;
+		const gchar       *name; 
 
 		contact = l->data;
-		account = gossip_contact_get_account (contact);
 
+		type = gossip_contact_get_type (contact);
+
+		if (type == GOSSIP_CONTACT_TYPE_CHATROOM ||
+		    type == GOSSIP_CONTACT_TYPE_USER) {
+			/* Ignore the user contact since that is
+			 * ourselves and we already added that above.
+			 * Plus don't add chatroom contacts, they seem
+			 * pointless, the nick is in the id itself so
+			 * there is no need to store it offline.
+			 */
+			continue;
+		}
+
+		account = gossip_contact_get_account (contact);
 		node = g_hash_table_lookup (nodes, account);
 
 		if (!node) {
@@ -693,11 +876,6 @@
 		/* This is a normal contact */
 		p = gossip_xml_node_find_child_prop_value (node, "id", id); 
 		if (!p) {
-			gossip_debug (DEBUG_DOMAIN, 
-				      "Creating xml node for contact:'%s' for "
-				      "account:'%s'", 
-				      id,
-				      gossip_account_get_name (account));
 			child = xmlNewChild (node, NULL, "contact", NULL);
 			xmlNewProp (child, "id", id);
 		} else {
@@ -706,22 +884,16 @@
 		
 		p = gossip_xml_node_get_child (child, "name");
 		if (!p) {
-			gossip_debug (DEBUG_DOMAIN, "Adding name node...");
 			child = xmlNewChild (child, NULL, "name", NULL);
 		} else {
 			child = p;
 		}
-		
-		gossip_debug (DEBUG_DOMAIN, 
-			      "Updating xml node for contact:'%s' for "
-			      "account:'%s' to:'%s'", 
-			      id, 
-			      gossip_account_get_name (account),
-			      name);
-		
+			
 		xmlNodeSetContent (child, name);
 	}
 
+	g_list_free (contacts);
+
 	/* Save own contacts */
 	g_hash_table_destroy (nodes);
 

Modified: trunk/libgossip/gossip-contact-manager.h
==============================================================================
--- trunk/libgossip/gossip-contact-manager.h	(original)
+++ trunk/libgossip/gossip-contact-manager.h	Sat Jul  5 12:58:07 2008
@@ -46,15 +46,28 @@
 	GObjectClass parent_class;
 };
 
-GType                 gossip_contact_manager_get_type (void) G_GNUC_CONST;
-gboolean              gossip_contact_manager_add      (GossipContactManager *manager,
+GType          gossip_contact_manager_get_type        (void) G_GNUC_CONST;
+
+gboolean       gossip_contact_manager_add             (GossipContactManager *manager,
 						       GossipContact        *contact);
-void                  gossip_contact_manager_remove   (GossipContactManager *manager,
+void           gossip_contact_manager_remove          (GossipContactManager *manager,
 						       GossipContact        *contact);
-GossipContact *       gossip_contact_manager_find     (GossipContactManager *manager,
+GossipContact *gossip_contact_manager_find            (GossipContactManager *manager,
+						       GossipAccount        *account,
+						       const gchar          *contact_id);
+GossipContact *gossip_contact_manager_find_extended   (GossipContactManager *manager,
 						       GossipAccount        *account,
+						       GossipContactType     type,
 						       const gchar          *contact_id);
-gboolean              gossip_contact_manager_store    (GossipContactManager *manager);
+GossipContact *gossip_contact_manager_find_or_create  (GossipContactManager *manager,
+						       GossipAccount        *account,
+						       GossipContactType     type,
+						       const gchar          *contact_id,
+						       gboolean             *created);
+GossipContact *gossip_contact_manager_get_own_contact (GossipContactManager *manager,
+						       GossipAccount        *account);
+gboolean       gossip_contact_manager_store           (GossipContactManager *manager);
+
 
 G_END_DECLS
 

Modified: trunk/libgossip/gossip-contact.c
==============================================================================
--- trunk/libgossip/gossip-contact.c	(original)
+++ trunk/libgossip/gossip-contact.c	Sat Jul  5 12:58:07 2008
@@ -25,6 +25,7 @@
 #include <glib/gi18n.h>
 
 #include "gossip-contact.h"
+#include "gossip-jid.h"
 #include "gossip-utils.h"
 
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT, GossipContactPriv))
@@ -61,8 +62,6 @@
 				   guint               param_id,
 				   const GValue       *value,
 				   GParamSpec         *pspec);
-static void contact_set_presences (GossipContact      *contact,
-				   GList              *presences);
 
 enum {
 	PROP_0,
@@ -253,12 +252,13 @@
 		g_value_set_int (value, priv->type);
 		break;
 	case PROP_NAME:
-		g_value_set_string (value,
-				    gossip_contact_get_name (GOSSIP_CONTACT (object)));
+		/* We call the function here because it returns
+		 * something else if priv->name == NULL.
+		 */
+		g_value_set_string (value, gossip_contact_get_name (GOSSIP_CONTACT (object)));
 		break;
 	case PROP_ID:
-		g_value_set_string (value,
-				    gossip_contact_get_id (GOSSIP_CONTACT (object)));
+		g_value_set_string (value, priv->id);
 		break;
 	case PROP_PRESENCES:
 		g_value_set_pointer (value, priv->presences);
@@ -308,8 +308,8 @@
 				       g_value_get_string (value));
 		break;
 	case PROP_PRESENCES:
-		contact_set_presences (GOSSIP_CONTACT (object),
-				       g_value_get_pointer (value));
+		gossip_contact_set_presence_list (GOSSIP_CONTACT (object),
+						  g_value_get_pointer (value));
 		break;
 	case PROP_GROUPS:
 		gossip_contact_set_groups (GOSSIP_CONTACT (object),
@@ -364,32 +364,24 @@
 GossipContact *
 gossip_contact_copy (GossipContact *contact)
 {
-	GossipContact *new_contact;
-
-	new_contact = g_object_new (GOSSIP_TYPE_CONTACT, NULL);
-	gossip_contact_copy_values (contact, new_contact);
-
-	return new_contact;
-}
+	GossipContact     *new_contact;
+	GossipContactPriv *priv;
+	
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
-void
-gossip_contact_copy_values (GossipContact *contact_from,
-			    GossipContact *contact_to)
-{
-	GossipContactPriv *from_priv;
+	priv = GET_PRIV (contact);
 
-	g_return_if_fail (GOSSIP_IS_CONTACT (contact_from));
-	g_return_if_fail (GOSSIP_IS_CONTACT (contact_to));
+	new_contact = gossip_contact_new_full (gossip_contact_get_type (contact),
+					       priv->account,
+					       priv->id,
+					       priv->name);
+
+	gossip_contact_set_subscription (new_contact, priv->subscription);
+	gossip_contact_set_avatar (new_contact, priv->avatar);
+	gossip_contact_set_groups (new_contact, priv->groups);
+	gossip_contact_set_presence_list (new_contact, priv->presences);
 
-	from_priv = GET_PRIV (contact_from);
-
-	gossip_contact_set_name (contact_to, from_priv->name);
-	gossip_contact_set_id (contact_to, from_priv->id);
-	gossip_contact_set_subscription (contact_to, from_priv->subscription);
-	gossip_contact_set_avatar (contact_to, from_priv->avatar);
-	gossip_contact_set_account (contact_to, from_priv->account);
-	gossip_contact_set_groups (contact_to, from_priv->groups);
-	contact_set_presences (contact_to, from_priv->presences);
+	return new_contact;
 }
 
 GossipContactType
@@ -542,6 +534,31 @@
 	return priv->presences;
 }
 
+void
+gossip_contact_set_presence_list (GossipContact *contact,
+				  GList         *presences)
+{
+	GossipContactPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+	priv = GET_PRIV (contact);
+
+	if (priv->presences) {
+		g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->presences);
+	}
+
+	if (presences) {
+		priv->presences = g_list_copy (presences);
+		g_list_foreach (priv->presences, (GFunc) g_object_ref, NULL);
+	} else {
+		priv->presences = NULL;
+	}
+
+	g_object_notify (G_OBJECT (contact), "presences");
+}
+
 GList *
 gossip_contact_get_groups (GossipContact *contact)
 {
@@ -614,25 +631,6 @@
 	g_object_notify (G_OBJECT (contact), "name");
 }
 
-static void
-contact_set_presences (GossipContact *contact,
-		       GList         *presences)
-{
-	GossipContactPriv *priv;
-
-	priv = GET_PRIV (contact);
-
-	if (priv->presences) {
-		g_list_foreach (priv->presences, (GFunc) g_object_unref, NULL);
-		g_list_free (priv->presences);
-	}
-
-	priv->presences = g_list_copy (presences);
-	g_list_foreach (priv->presences, (GFunc) g_object_ref, NULL);
-
-	g_object_notify (G_OBJECT (contact), "presences");
-}
-
 void
 gossip_contact_set_avatar (GossipContact *contact,
 			   GossipAvatar  *avatar)
@@ -832,78 +830,66 @@
 	g_object_notify (G_OBJECT (contact), "subscription");
 }
 
-gint
-gossip_contact_name_compare (gconstpointer a,
-			     gconstpointer b)
+guint
+gossip_contact_hash (gconstpointer key)
 {
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (a), 0);
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (b), 0);
+	GossipContactPriv *priv;
+	guint              hash = 0;
 
-	return strcmp (gossip_contact_get_name (GOSSIP_CONTACT (a)),
-		       gossip_contact_get_name (GOSSIP_CONTACT (b)));
-}
+	g_return_val_if_fail (GOSSIP_IS_CONTACT (key), +1);
 
-gint
-gossip_contact_name_case_compare (gconstpointer a,
-				  gconstpointer b)
-{
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (a), 0);
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (b), 0);
+	priv = GET_PRIV (key);
 
-	return gossip_contact_name_case_n_compare (a, b, -1);
-}
+	hash += gossip_account_hash (gossip_contact_get_account (GOSSIP_CONTACT (key)));
 
-gint
-gossip_contact_name_case_n_compare (gconstpointer a,
-				    gconstpointer b,
-				    gsize         n)
-{
-	const gchar *name_a, *name_b;
+	/* Use simple JID hash for all contacts except chatroom contacts */
+	if (priv->type == GOSSIP_CONTACT_TYPE_CHATROOM) {	
+		if (priv->presences && priv->presences->data) {
+			GossipPresence *presence;
+			const gchar    *resource;
 
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (a), 0);
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (b), 0);
+			presence = priv->presences->data;
+			resource = gossip_presence_get_resource (presence);
 
-	name_a = gossip_contact_get_name (GOSSIP_CONTACT (a));
-	name_b = gossip_contact_get_name (GOSSIP_CONTACT (b));
+			if (resource) {
+				hash += g_str_hash (resource);
+			}
+		}
+	}
 
-	return gossip_strncasecmp (name_a, name_b, -1);
+	return hash;
 }
 
 gboolean
 gossip_contact_equal (gconstpointer v1,
 		      gconstpointer v2)
 {
-	GossipAccount *account_a;
-	GossipAccount *account_b;
-	const gchar   *id_a;
-	const gchar   *id_b;
+	GossipAccount     *account_a;
+	GossipAccount     *account_b;
+	GossipContactType  type_a;
+	GossipContactType  type_b;
+	const gchar       *id_a;
+	const gchar       *id_b;
+	gboolean           equal;
 
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (v1), FALSE);
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (v2), FALSE);
 
+	type_a = gossip_contact_get_type (GOSSIP_CONTACT (v1));
+	type_b = gossip_contact_get_type (GOSSIP_CONTACT (v2));
+
 	account_a = gossip_contact_get_account (GOSSIP_CONTACT (v1));
 	account_b = gossip_contact_get_account (GOSSIP_CONTACT (v2));
 
 	id_a = gossip_contact_get_id (GOSSIP_CONTACT (v1));
 	id_b = gossip_contact_get_id (GOSSIP_CONTACT (v2));
 
-	return gossip_account_equal (account_a, account_b) && g_str_equal (id_a, id_b);
-}
-
-guint
-gossip_contact_hash (gconstpointer key)
-{
-	GossipContactPriv *priv;
-	guint              hash = 0;
-
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (key), +1);
+	equal  = TRUE;
+	equal &= type_a == type_b; 
+	equal &= gossip_account_equal (account_a, account_b);
+	equal &= g_str_equal (id_a, id_b);
 
-	priv = GET_PRIV (GOSSIP_CONTACT (key));
-
-	hash += gossip_account_hash (gossip_contact_get_account (GOSSIP_CONTACT (key)));
-	hash += g_str_hash (gossip_contact_get_id (GOSSIP_CONTACT (key)));
-
-	return hash;
+	return equal;
 }
 
 gboolean

Modified: trunk/libgossip/gossip-contact.h
==============================================================================
--- trunk/libgossip/gossip-contact.h	(original)
+++ trunk/libgossip/gossip-contact.h	Sat Jul  5 12:58:07 2008
@@ -72,8 +72,7 @@
 							     const gchar        *id,
 							     const gchar        *name);
 GossipContact *    gossip_contact_copy                      (GossipContact      *contact);
-void               gossip_contact_copy_values               (GossipContact      *contact_from,
-							     GossipContact      *contact_to);
+
 GossipContactType  gossip_contact_get_type                  (GossipContact      *contact);
 const gchar *      gossip_contact_get_id                    (GossipContact      *contact);
 const gchar *      gossip_contact_get_name                  (GossipContact      *contact);
@@ -81,16 +80,9 @@
 GdkPixbuf *        gossip_contact_get_avatar_pixbuf         (GossipContact      *contact);
 GossipAccount *    gossip_contact_get_account               (GossipContact      *contact);
 GossipVCard *      gossip_contact_get_vcard                 (GossipContact      *contact);
-void               gossip_contact_add_presence              (GossipContact      *contact,
-							     GossipPresence     *presence);
-void               gossip_contact_remove_presence           (GossipContact      *contact,
-							     GossipPresence     *presence);
-GossipPresence *   gossip_contact_get_presence_for_resource (GossipContact      *contact,
-							     const gchar        *resource);
-GossipPresence *   gossip_contact_get_active_presence       (GossipContact      *contact);
-GList *            gossip_contact_get_presence_list         (GossipContact      *contact);
 GList *            gossip_contact_get_groups                (GossipContact      *contact);
 GossipSubscription gossip_contact_get_subscription          (GossipContact      *contact);
+
 void               gossip_contact_set_type                  (GossipContact      *contact,
 							     GossipContactType   type);
 void               gossip_contact_set_id                    (GossipContact      *contact,
@@ -107,13 +99,20 @@
 							     GList              *categories);
 void               gossip_contact_set_subscription          (GossipContact      *contact,
 							     GossipSubscription  subscription);
-gint               gossip_contact_name_compare              (gconstpointer       a,
-							     gconstpointer       b);
-gint               gossip_contact_name_case_compare         (gconstpointer       a,
-							     gconstpointer       b);
-gint               gossip_contact_name_case_n_compare       (gconstpointer       a,
-							     gconstpointer       b,
-							     gsize               n);
+
+/* Presence */
+void               gossip_contact_add_presence              (GossipContact      *contact,
+							     GossipPresence     *presence);
+void               gossip_contact_remove_presence           (GossipContact      *contact,
+							     GossipPresence     *presence);
+GossipPresence *   gossip_contact_get_presence_for_resource (GossipContact      *contact,
+							     const gchar        *resource);
+GossipPresence *   gossip_contact_get_active_presence       (GossipContact      *contact);
+GList *            gossip_contact_get_presence_list         (GossipContact      *contact);
+void               gossip_contact_set_presence_list         (GossipContact      *contact,
+							     GList              *presences);
+
+/* Utility functions */
 gboolean           gossip_contact_equal                     (gconstpointer       v1,
 							     gconstpointer       v2);
 guint              gossip_contact_hash                      (gconstpointer       key);

Modified: trunk/libgossip/gossip-jabber-chatrooms.c
==============================================================================
--- trunk/libgossip/gossip-jabber-chatrooms.c	(original)
+++ trunk/libgossip/gossip-jabber-chatrooms.c	Sat Jul  5 12:58:07 2008
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 2004-2006 Imendio AB
+ * Copyright (C) 2004-2008 Imendio AB
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -24,13 +24,14 @@
 #include <string.h>
 
 
-#include <libgossip/gossip-debug.h>
-#include <libgossip/gossip-chatroom.h>
-#include <libgossip/gossip-chatroom-invite.h>
-#include <libgossip/gossip-ft.h>
-#include <libgossip/gossip-ft-provider.h>
-#include <libgossip/gossip-message.h>
-#include <libgossip/gossip-utils.h>
+#include "gossip-debug.h"
+#include "gossip-chatroom.h"
+#include "gossip-chatroom-invite.h"
+#include "gossip-chatroom-manager.h"
+#include "gossip-ft.h"
+#include "gossip-ft-provider.h"
+#include "gossip-message.h"
+#include "gossip-utils.h"
 
 #include "gossip-jabber-chatrooms.h"
 #include "gossip-jabber-disco.h"
@@ -48,83 +49,32 @@
 #define JOIN_TIMEOUT 20000
 
 struct _GossipJabberChatrooms {
-	GossipJabber          *jabber;
-	GossipPresence        *presence;
-	LmConnection          *connection;
+	GossipJabber   *jabber;
+	GossipPresence *presence;
+	LmConnection   *connection;
 
-	GHashTable            *room_id_hash;
-	GHashTable            *room_jid_hash;
+	GHashTable     *chatrooms_by_id;
+	GHashTable     *chatrooms_by_pointer;
+	GHashTable     *chatrooms_by_jid;
+	GHashTable     *join_timeouts;
+	GHashTable     *join_callbacks;
 };
 
-typedef struct {
-	gint                   ref_count;
-
-	GossipChatroom        *chatroom;
-
-	GossipJID             *jid;
-	GossipContact         *own_contact;
-
-	GSList                *contacts;
-
-	guint                  timeout_id;
-
-	GossipJabberChatrooms *chatrooms;
-	GossipChatroomJoinCb   callback;
-	gpointer               user_data;
+static void            logged_out_cb                  (GossipJabber          *jabber,
+						       GossipAccount         *account,
+						       gint                   reason,
+						       GossipJabberChatrooms *chatrooms);
+static void            join_timeout_destroy_notify_cb (gpointer               data);
+static LmHandlerResult message_handler                (LmMessageHandler      *handler,
+						       LmConnection          *conn,
+						       LmMessage             *message,
+						       GossipJabberChatrooms *chatrooms);
+static LmHandlerResult presence_handler               (LmMessageHandler      *handler,
+						       LmConnection          *conn,
+						       LmMessage             *message,
+						       GossipJabberChatrooms *chatrooms);
 
-	LmConnection          *connection;
-	LmMessageHandler      *join_handler;
-} JabberChatroom;
 
-static void             jabber_chatrooms_logged_out_cb        (GossipJabber           *jabber,
-							       GossipAccount          *account,
-							       gint                    reason,
-							       GossipJabberChatrooms  *chatrooms);
-static JabberChatroom * jabber_chatrooms_chatroom_new         (GossipJabberChatrooms  *chatrooms,
-							       GossipChatroom         *chatroom);
-static JabberChatroom * jabber_chatrooms_chatroom_ref         (JabberChatroom         *room);
-static void             jabber_chatrooms_chatroom_unref       (JabberChatroom         *room);
-static GossipChatroomId jabber_chatrooms_chatroom_get_id      (JabberChatroom         *room);
-static GossipContact *  jabber_chatrooms_get_contact          (JabberChatroom         *room,
-							       GossipJID              *jid,
-							       gboolean               *new_contact);
-static LmHandlerResult  jabber_chatrooms_message_handler      (LmMessageHandler       *handler,
-							       LmConnection           *conn,
-							       LmMessage              *message,
-							       GossipJabberChatrooms  *chatrooms);
-static LmHandlerResult  jabber_chatrooms_presence_handler     (LmMessageHandler       *handler,
-							       LmConnection           *conn,
-							       LmMessage              *message,
-							       GossipJabberChatrooms  *chatrooms);
-static LmHandlerResult  jabber_chatrooms_join_cb              (LmMessageHandler       *handler,
-							       LmConnection           *connection,
-							       LmMessage              *message,
-							       JabberChatroom         *room);
-static void             jabber_chatrooms_close                (GossipJabberChatrooms  *chatrooms,
-							       GossipChatroomId        id);
-static GossipChatroomError
-                        jabber_chatrooms_error_from_code      (gint                    code);
-static GArray *         jabber_chatrooms_get_status           (LmMessage              *m);
-static gboolean         jabber_chatrooms_has_status           (GArray                 *status, 
-							       gint                    code);
-static void             jabber_chatrooms_get_rooms_foreach    (gpointer                key,
-							       JabberChatroom         *room,
-							       GList                 **list);
-static void             jabber_chatrooms_browse_rooms_cb      (GossipJabberDisco      *disco,
-							       GossipJabberDiscoItem  *item,
-							       gboolean                last_item,
-							       gboolean                timeout,
-							       GError                 *error,
-							       GossipCallbackData     *data);
-static void             jabber_chatrooms_set_presence_foreach (gpointer                key,
-							       JabberChatroom         *room,
-							       GossipJabberChatrooms  *chatrooms);
-
-static LmMessageNode *  jabber_chatrooms_find_muc_user_node   (LmMessageNode          *parent_node);
-static GossipChatroomRole 
-                        jabber_chatrooms_get_role             (LmMessageNode          *muc_node);
-static GossipChatroomAffiliation 
-                        jabber_chatrooms_get_affiliation      (LmMessageNode       *muc_node);
 
 GossipJabberChatrooms *
 gossip_jabber_chatrooms_init (GossipJabber *jabber)
@@ -134,8 +84,8 @@
 	LmMessageHandler      *handler;
 
 	g_return_val_if_fail (GOSSIP_IS_JABBER (jabber), NULL);
-
-	connection = gossip_jabber_get_connection (jabber);
+	
+	connection = _gossip_jabber_get_connection (jabber);
 	g_return_val_if_fail (connection != NULL, NULL);
 
 	chatrooms = g_new0 (GossipJabberChatrooms, 1);
@@ -144,18 +94,40 @@
 	chatrooms->connection = lm_connection_ref (connection);
 	chatrooms->presence = NULL;
 
-	chatrooms->room_id_hash = g_hash_table_new (NULL, NULL);
-	chatrooms->room_jid_hash = g_hash_table_new_full (gossip_jid_hash,
-							  gossip_jid_equal,
-							  (GDestroyNotify) gossip_jid_unref,
-							  NULL);
-
 	g_signal_connect (chatrooms->jabber, "disconnected",
-			  G_CALLBACK (jabber_chatrooms_logged_out_cb),
+			  G_CALLBACK (logged_out_cb),
 			  chatrooms);
 
-	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_chatrooms_message_handler,
-					  chatrooms, NULL);
+	chatrooms->chatrooms_by_id = 
+		g_hash_table_new_full (g_direct_hash,
+				       g_direct_equal,
+				       NULL,
+				       (GDestroyNotify) g_object_unref);
+	chatrooms->chatrooms_by_pointer = 
+		g_hash_table_new_full (gossip_chatroom_hash,
+				       gossip_chatroom_equal,
+				       (GDestroyNotify) g_object_unref,
+				       NULL);
+	chatrooms->chatrooms_by_jid = 
+		g_hash_table_new_full (gossip_jid_hash_without_resource,
+				       gossip_jid_equal_without_resource, 
+				       (GDestroyNotify) g_object_unref,
+				       (GDestroyNotify) g_object_unref);
+	chatrooms->join_timeouts = 
+		g_hash_table_new_full (gossip_chatroom_hash,
+				       gossip_chatroom_equal,
+				       (GDestroyNotify) g_object_unref,
+				       join_timeout_destroy_notify_cb);
+	chatrooms->join_callbacks = 
+		g_hash_table_new_full (gossip_chatroom_hash,
+				       gossip_chatroom_equal,
+				       (GDestroyNotify) g_object_unref,
+				       (GDestroyNotify) gossip_callback_data_free);
+
+	/* Set up message and presence handlers */
+	handler = lm_message_handler_new ((LmHandleMessageFunction) message_handler,
+					  chatrooms, 
+					  NULL);
 
 	lm_connection_register_message_handler (chatrooms->connection,
 						handler,
@@ -163,8 +135,9 @@
 						LM_HANDLER_PRIORITY_NORMAL);
 	lm_message_handler_unref (handler);
 
-	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_chatrooms_presence_handler,
-					  chatrooms, NULL);
+	handler = lm_message_handler_new ((LmHandleMessageFunction) presence_handler,
+					  chatrooms, 
+					  NULL);
 
 	lm_connection_register_message_handler (chatrooms->connection,
 						handler,
@@ -186,13 +159,25 @@
 		return;
 	}
 
+	g_hash_table_unref (chatrooms->chatrooms_by_id);
+	chatrooms->chatrooms_by_id = NULL;
+
+	g_hash_table_unref (chatrooms->chatrooms_by_pointer);
+	chatrooms->chatrooms_by_pointer = NULL;
+
+	g_hash_table_unref (chatrooms->chatrooms_by_jid);
+	chatrooms->chatrooms_by_jid = NULL;
+
+	g_hash_table_unref (chatrooms->join_timeouts);
+	chatrooms->join_timeouts = NULL;
+
+	g_hash_table_unref (chatrooms->join_callbacks);
+	chatrooms->join_timeouts = NULL;
+
 	g_signal_handlers_disconnect_by_func (chatrooms->jabber,
-					      jabber_chatrooms_logged_out_cb,
+					      logged_out_cb,
 					      chatrooms);
 
-	g_hash_table_destroy (chatrooms->room_id_hash);
-	g_hash_table_destroy (chatrooms->room_jid_hash);
-
 	if (chatrooms->presence) {
 		g_object_unref (chatrooms->presence);
 	}
@@ -204,137 +189,42 @@
 }
 
 static void
-jabber_chatrooms_logged_out_cb (GossipJabber          *jabber,
-				GossipAccount         *account,
-				gint                   reason,
-				GossipJabberChatrooms *chatrooms)
-{
-	GList *rooms;
-	GList *l;
-
-	/* Clean up chat rooms */
-	rooms = gossip_jabber_chatrooms_get_rooms (chatrooms);
-
-	for (l = rooms; l; l = l->next) {
-		GossipChatroomId id;
-
-		id = GPOINTER_TO_INT (l->data);
-		gossip_jabber_chatrooms_leave (chatrooms, id);
-	}
-
-	g_list_free (rooms);
-}
-
-static JabberChatroom *
-jabber_chatrooms_chatroom_new (GossipJabberChatrooms *chatrooms,
-			       GossipChatroom        *chatroom)
+logged_out_foreach (gpointer key,
+		    gpointer value,
+		    gpointer user_data)
 {
-	GossipJabber   *jabber;
-	GossipContact  *own_contact;
-	JabberChatroom *room;
-	gchar          *jid_str;
-
-	jabber = chatrooms->jabber;
-
-	room = g_new0 (JabberChatroom, 1);
-
-	room->ref_count = 1;
-	room->chatroom = g_object_ref (chatroom);
-
-	jid_str = g_strdup_printf ("%s/%s",
-				   gossip_chatroom_get_id_str (chatroom),
-				   gossip_chatroom_get_nick (chatroom));
-	room->jid = gossip_jid_new (jid_str);
-	g_free (jid_str);
-
-	room->contacts = NULL;
-
-	/* What we do here is copy the contact instead of reference
-	 * it, plus we change the name according to how the user wants
-	 * their nickname in the chat room.
-	 */
-	own_contact = gossip_jabber_get_own_contact (jabber);
-	room->own_contact = gossip_contact_copy (own_contact);
-	gossip_contact_set_name (room->own_contact,
-				 gossip_chatroom_get_nick (chatroom));
+	GossipJabberChatrooms *chatrooms;
+	GossipChatroomId       id;
 
-	return room;
-}
+	id = GPOINTER_TO_INT (key);
+	chatrooms = (GossipJabberChatrooms *) user_data;
 
-static JabberChatroom *
-jabber_chatrooms_chatroom_ref (JabberChatroom *room)
-{
-	if (!room) {
-		return NULL;
-	}
-
-	room->ref_count++;
-	return room;
+	gossip_jabber_chatrooms_leave (chatrooms, id);
 }
 
 static void
-jabber_chatrooms_chatroom_unref (JabberChatroom *room)
+logged_out_cb (GossipJabber          *jabber,
+	       GossipAccount         *account,
+	       gint                   reason,
+	       GossipJabberChatrooms *chatrooms)
 {
-	if (!room) {
-		return;
-	}
-
-	room->ref_count--;
-
-	if (room->ref_count > 0) {
-		return;
-	}
-
-	g_object_unref (room->chatroom);
-
-	gossip_jid_unref (room->jid);
-
-	g_slist_foreach (room->contacts, (GFunc)g_object_unref, NULL);
-	g_slist_free (room->contacts);
-
-	g_object_unref (room->own_contact);
-
-	if (room->timeout_id) {
-		g_source_remove (room->timeout_id);
-		room->timeout_id = 0;
-	}
-
-	if (room->join_handler) {
-		lm_connection_unregister_message_handler (room->connection,
-							  room->join_handler,
-							  LM_MESSAGE_TYPE_PRESENCE);
-		room->join_handler = NULL;
-	}
-
-	if (room->connection) {
-		lm_connection_unref (room->connection);
-		room->connection = NULL;
-	}
-
-	g_free (room);
-}
-
-static GossipChatroomId
-jabber_chatrooms_chatroom_get_id (JabberChatroom *room)
-{
-	g_return_val_if_fail (room != NULL, 0);
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM (room->chatroom), 0);
-
-	return gossip_chatroom_get_id (room->chatroom);
+	g_hash_table_foreach (chatrooms->chatrooms_by_id,
+			      logged_out_foreach,
+			      chatrooms);
 }
 
 static LmHandlerResult
-jabber_chatrooms_message_handler (LmMessageHandler      *handler,
-				  LmConnection          *conn,
-				  LmMessage             *m,
-				  GossipJabberChatrooms *chatrooms)
+message_handler (LmMessageHandler      *handler,
+		 LmConnection          *conn,
+		 LmMessage             *m,
+		 GossipJabberChatrooms *chatrooms)
 {
+	LmMessageNode    *node;
 	GossipJID        *jid;
-	GossipMessage    *message;
-	GossipContact    *contact;
+	GossipChatroom   *chatroom;
 	GossipChatroomId  id;
-	JabberChatroom   *room;
-	LmMessageNode    *node;
+	GossipContact    *contact;
+	GossipMessage    *message;
 	const gchar      *from;
 
 	if (lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_GROUPCHAT) {
@@ -344,38 +234,50 @@
 	from = lm_message_node_get_attribute (m->node, "from");
 	jid = gossip_jid_new (from);
 
-	room = g_hash_table_lookup (chatrooms->room_jid_hash, jid);
-	if (!room) {
-		gossip_jid_unref (jid);
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_jid, jid);
+
+	if (!chatroom) {
+		g_object_unref (jid);
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;	
 	}
 
-	id = jabber_chatrooms_chatroom_get_id (room);
+	id = gossip_chatroom_get_id (chatroom);
+
+	contact = gossip_jabber_get_contact_from_jid (chatrooms->jabber, 
+						      from, 
+						      FALSE,
+						      FALSE,
+						      FALSE);
 
 	node = lm_message_node_get_child (m->node, "body");
 	if (node) {
 		if (gossip_jid_get_resource (jid) == NULL) {
 			g_signal_emit_by_name (chatrooms->jabber,
 					       "chatroom-new-event",
-					       id, node->value);
+					       id, 
+					       node->value);
 		} else {
 			GossipTime timestamp;
 
 			timestamp = gossip_jabber_get_message_timestamp (m);
 
+			/* NOTE: We don't use the chatroom contact
+			 * here, we use the actual REAL contact
+			 * instead, this is to keep things sane in
+			 * the UI code.
+			 */
 			message = gossip_message_new (GOSSIP_MESSAGE_TYPE_CHAT_ROOM,
-						      room->own_contact);
+						      gossip_jabber_get_own_contact (chatrooms->jabber));
 
 			timestamp = gossip_jabber_get_message_timestamp (m);
 			gossip_message_set_timestamp (message, timestamp);
-
-			contact = jabber_chatrooms_get_contact (room, jid, NULL);
 			gossip_message_set_sender (message, contact);
 			gossip_message_set_body (message, node->value);
 
 			g_signal_emit_by_name (chatrooms->jabber,
 					       "chatroom-new-message",
-					       id, message);
+					       id, 
+					       message);
 
 			g_object_unref (message);
 		}
@@ -383,319 +285,218 @@
 
 	node = lm_message_node_get_child (m->node, "subject");
 	if (node) {
-		contact = jabber_chatrooms_get_contact (room, jid, NULL);
-
-		g_signal_emit_by_name (chatrooms->jabber,
-				       "chatroom-subject-changed",
-				       id, contact, node->value);
+		/* We don't handle subject change twice! Above, you
+		 * may notice that we emit the "chatroom-new-event"
+		 * signal. This is used to emit the subject for the
+		 * chatroom when there is NO contact. The subject
+		 * here has the same content EXCEPT that it doesn't
+		 * include the name of the person that set the
+		 * subject, just what the subject _IS_. We only use
+		 * this when we know the contact.
+		 */
+		if (contact) {
+			g_signal_emit_by_name (chatrooms->jabber,
+					       "chatroom-subject-changed",
+					       id, 
+					       contact, 
+					       node->value);
+		}
 	}
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 
 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 
 }
 
-static GossipContact *
-jabber_chatrooms_get_contact (JabberChatroom *room,
-			      GossipJID      *jid,
-			      gboolean       *new_contact)
+static GossipChatroomError 
+error_from_code (const gchar *xmlns, 
+		 gint         code)
 {
-	GossipContact *contact;
-	GSList        *l;
-	const gchar   *id;
-	const gchar   *name;
-
-	id = gossip_jid_get_full (jid);
-
-	for (l = room->contacts; l; l = l->next) {
-		contact = GOSSIP_CONTACT (l->data);
-
-		if (g_ascii_strcasecmp (gossip_contact_get_id (contact), id) == 0) {
-			if (new_contact) {
-				*new_contact = FALSE;
-			}
-
-			return contact;
+	if (G_STR_EMPTY (xmlns)) {
+		/* 503 is really service not available,
+		 * 403 is really subject change not authorized
+		 * 405 is really kick/ban/etc not authorized
+		 */
+		switch (code) {
+		case 400: return GOSSIP_CHATROOM_ERROR_BAD_REQUEST;
+		case 403: return GOSSIP_CHATROOM_ERROR_UNAUTHORIZED_REQUEST; 
+		case 405: return GOSSIP_CHATROOM_ERROR_UNAUTHORIZED_REQUEST;
+		case 409: return GOSSIP_CHATROOM_ERROR_NICK_IN_USE;
+		case 503: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
+
+		/* Legacy Errors */
+		case 502: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
+		case 504: return GOSSIP_CHATROOM_ERROR_TIMED_OUT;
+		default:
+			break;
 		}
 	}
 
-	if (new_contact) {
-		*new_contact = TRUE;
-	}
-
-	name = gossip_jid_get_resource (jid);
-	if (!name) {
-		name = id;
-	}
-
-	contact = g_object_new (GOSSIP_TYPE_CONTACT,
-				"account", gossip_contact_get_account (room->own_contact),
-				"id", id,
-				"name", name,
-				NULL);
-
-	room->contacts = g_slist_prepend (room->contacts, contact);
-
-	return contact;
-}
-
-static GossipChatroomError 
-jabber_chatrooms_error_from_code (gint code)
-{
-	switch (code) {
-	case 401: return GOSSIP_CHATROOM_ERROR_PASSWORD_INVALID_OR_MISSING;
-	case 403: return GOSSIP_CHATROOM_ERROR_USER_BANNED;
-	case 404: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
-	case 405: return GOSSIP_CHATROOM_ERROR_ROOM_CREATION_RESTRICTED;
-	case 406: return GOSSIP_CHATROOM_ERROR_USE_RESERVED_ROOM_NICK;
-	case 407: return GOSSIP_CHATROOM_ERROR_NOT_ON_MEMBERS_LIST;
-	case 409: return GOSSIP_CHATROOM_ERROR_NICK_IN_USE;
-	case 503: return GOSSIP_CHATROOM_ERROR_MAXIMUM_USERS_REACHED;
-
-	/* Legacy Errors */
-	case 502: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
-	case 504: return GOSSIP_CHATROOM_ERROR_TIMED_OUT;
-
-	default:
-		break;
+	if (strcmp (xmlns, XMPP_MUC_XMLNS) == 0) {
+		switch (code) {
+			/* 503 could mean room is full */
+		case 401: return GOSSIP_CHATROOM_ERROR_PASSWORD_INVALID_OR_MISSING;
+		case 403: return GOSSIP_CHATROOM_ERROR_USER_BANNED;
+		case 404: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
+		case 405: return GOSSIP_CHATROOM_ERROR_ROOM_CREATION_RESTRICTED;
+		case 406: return GOSSIP_CHATROOM_ERROR_USE_RESERVED_ROOM_NICK;
+		case 407: return GOSSIP_CHATROOM_ERROR_NOT_ON_MEMBERS_LIST;
+		case 409: return GOSSIP_CHATROOM_ERROR_NICK_IN_USE;
+		case 503: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
+		default:
+			break;
+		}
+	} else if (strcmp (xmlns, XMPP_MUC_USER_XMLNS) == 0) {
+		switch (code) {
+		case 401: return GOSSIP_CHATROOM_ERROR_PASSWORD_INVALID_OR_MISSING;
+		case 403: return GOSSIP_CHATROOM_ERROR_FORBIDDEN;
+		case 404: return GOSSIP_CHATROOM_ERROR_ROOM_NOT_FOUND;
+		case 405: return GOSSIP_CHATROOM_ERROR_ROOM_CREATION_RESTRICTED;
+		case 406: return GOSSIP_CHATROOM_ERROR_USE_RESERVED_ROOM_NICK;
+		case 407: return GOSSIP_CHATROOM_ERROR_NOT_ON_MEMBERS_LIST;
+		case 409: return GOSSIP_CHATROOM_ERROR_NICK_IN_USE;
+		default:
+			break;
+		}
+	} else if (strcmp (xmlns, XMPP_MUC_OWNER_XMLNS) == 0 ||
+		   strcmp (xmlns, XMPP_MUC_ADMIN_XMLNS) == 0) {
+		switch (code) {
+		case 403: return GOSSIP_CHATROOM_ERROR_FORBIDDEN;
+		default:
+			break;
+		}
 	}
 	
 	return GOSSIP_CHATROOM_ERROR_UNKNOWN;
 }
 
-static GArray *
-jabber_chatrooms_get_status (LmMessage *m)
+static LmMessageNode *
+find_muc_user_node (LmMessageNode *parent_node)
 {
-	LmMessageNode *node;
-	GArray        *status = NULL;
+	LmMessageNode *child;
 
-	if (!m) {
-		return NULL;
-	}
+	/* Should have a function in Loudmouth to find a child with xmlns */
+	child = parent_node->children;
 
-	node = lm_message_node_get_child (m->node, "x");
-	if (!node) {
+	if (!child) {
 		return NULL;
 	}
 
-	node = node->children;
-
-	while (node) {
-		if (!node) {
-			break;
-		}
-
-		if (node->name && strcmp (node->name, "status") == 0) {
-			const gchar *code;
-
-			code = lm_message_node_get_attribute (node, "code");
-			if (code) {
-				gint value;
+	while (child) {
+		if (strcmp (child->name, "x") == 0) {
+			const gchar *xmlns;
 
-				if (!status) {
-					status = g_array_new (FALSE, FALSE, sizeof (gint));
-				}
+			xmlns = lm_message_node_get_attribute (child, "xmlns");
 
-				value = atoi (code);
-				g_array_append_val (status, value);
+			if (xmlns && strcmp (xmlns, XMPP_MUC_USER_XMLNS) == 0) {
+				return child;
 			}
 		}
 
-		node = node->next;
+		child = child->next;
 	}
 
-	return status;
+	return NULL;
 }
 
-static gboolean 
-jabber_chatrooms_has_status (GArray *status, 
-			     gint    code)
+static GossipChatroomRole
+get_role (LmMessageNode *muc_node)
 {
-	gint i = 0;
+	LmMessageNode *item_node;
+	const gchar   *role;
 
-	if (!status) {
-		return FALSE;
-	}
-	
-	for (i = 0; i < status->len; i++) {
-		if (g_array_index (status, gint, i) == code) {
-			return TRUE;
-		}
+	if (!muc_node) {
+		return GOSSIP_CHATROOM_ROLE_NONE;
 	}
-	
-	return FALSE;
-}
-
-static LmHandlerResult
-jabber_chatrooms_presence_handler (LmMessageHandler      *handler,
-				   LmConnection          *conn,
-				   LmMessage             *m,
-				   GossipJabberChatrooms *chatrooms)
-{
-	const gchar               *from;
-	GossipJID                 *jid;
-	JabberChatroom            *room;
-	GossipContact             *contact;
-	GossipPresence            *presence;
-	GossipPresenceState        p_state;
-	GossipChatroomId           id;
-	LmMessageSubType           type;
-	LmMessageNode             *node;
-	gboolean                   new_contact;
-	gboolean                   was_offline;
-	LmMessageNode             *muc_user_node;
-	GossipChatroomContactInfo  muc_contact_info;
 
-	from = lm_message_node_get_attribute (m->node, "from");
-	jid = gossip_jid_new (from);
-
-	room = g_hash_table_lookup (chatrooms->room_jid_hash, jid);
-	if (!room) {
-		gossip_jid_unref (jid);
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	item_node = lm_message_node_get_child (muc_node, "item");
+	if (!item_node) {
+		return GOSSIP_CHATROOM_ROLE_NONE;
 	}
 
-	id = jabber_chatrooms_chatroom_get_id (room);
-
-	gossip_debug (DEBUG_DOMAIN, "Presence from: %s", from);
-
-	type = lm_message_get_sub_type (m);
-	switch (type) {
-	case LM_MESSAGE_SUB_TYPE_AVAILABLE:
-		/* get details */
-		contact = jabber_chatrooms_get_contact (room, jid,
-							&new_contact);
-
-		presence = gossip_presence_new ();
-		node = lm_message_node_get_child (m->node, "show");
-		if (node) {
-			p_state = gossip_jabber_presence_state_from_str (node->value);
-			gossip_presence_set_state (presence, p_state);
-		}
-		node = lm_message_node_get_child (m->node, "status");
-		if (node) {
-			gossip_presence_set_status (presence, node->value);
-		}
-
-		/* Should signal joined if contact was found but offline */
-		was_offline = !gossip_contact_is_online (contact);
-		gossip_contact_add_presence (contact, presence);
-		g_object_unref (presence);
-
-		muc_user_node = jabber_chatrooms_find_muc_user_node (m->node);
-		muc_contact_info.role = jabber_chatrooms_get_role (muc_user_node);
-		muc_contact_info.affiliation = jabber_chatrooms_get_affiliation (muc_user_node);
-
-		/* Is contact new or updated */
-		if (new_contact || was_offline) {
-			gossip_debug (DEBUG_DOMAIN, "ID[%d] Presence for new joining contact:'%s'",
-				      id, gossip_jid_get_full (jid));
-			gossip_chatroom_contact_joined (room->chatroom,
-							contact,
-							&muc_contact_info);
-		} else {
-			gossip_chatroom_set_contact_info (room->chatroom,
-							  contact,
-							  &muc_contact_info);
-		}
-		break;
-
-	case LM_MESSAGE_SUB_TYPE_UNAVAILABLE:
-		contact = jabber_chatrooms_get_contact (room, jid, NULL);
-		if (gossip_jid_equals (jid, room->jid)) {
-			gossip_debug (DEBUG_DOMAIN, "ID[%d] We have been kicked!", id);
-			gossip_chatroom_set_status (room->chatroom, GOSSIP_CHATROOM_STATUS_INACTIVE);
-			g_signal_emit_by_name (chatrooms->jabber, "chatroom-kicked", id);
-			jabber_chatrooms_close (chatrooms, id);
-		} else {
-			gossip_debug (DEBUG_DOMAIN, "ID[%d] Contact left:'%s'",
-				      id, gossip_jid_get_full (jid));
-			gossip_chatroom_contact_left (room->chatroom, contact);
-			room->contacts = g_slist_remove (room->contacts, contact);
-			g_object_unref (contact);
-		}
-		break;
-
-	case LM_MESSAGE_SUB_TYPE_ERROR:
-		node = lm_message_node_get_child (m->node, "error");
-		if (node) {
-			GossipChatroomError  error;
-			const gchar         *str;
-			gint                 code;
-			
-			str = lm_message_node_get_attribute (node, "code");
-			code = str ? atoi (str) : 0;
-			
-			error = jabber_chatrooms_error_from_code (code);
-			gossip_debug (DEBUG_DOMAIN, "ID[%d] %s", 
-				      id, gossip_chatroom_error_to_string (error));
-			
-			g_signal_emit_by_name (chatrooms->jabber,
-					       "chatroom-error",
-					       id, error);
-		}
-		break;
-
-	default:
-		gossip_debug (DEBUG_DOMAIN, "Presence not handled for:'%s'",
-			      gossip_jid_get_full (jid));
-		gossip_jid_unref (jid);
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	role = lm_message_node_get_attribute (item_node, "role");
+	if (!role) {
+		return GOSSIP_CHATROOM_ROLE_NONE;
 	}
 
-	return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	if (strcmp (role, "moderator") == 0) {
+		return GOSSIP_CHATROOM_ROLE_MODERATOR;
+	}
+	else if (strcmp (role, "participant") == 0) {
+		return GOSSIP_CHATROOM_ROLE_PARTICIPANT;
+	}
+	else if (strcmp (role, "visitor") == 0) {
+		return GOSSIP_CHATROOM_ROLE_VISITOR;
+	} else {
+		return GOSSIP_CHATROOM_ROLE_NONE;
+	}
 }
 
-static gboolean
-jabber_chatrooms_join_timeout_cb (JabberChatroom *room)
+static GossipChatroomAffiliation
+get_affiliation (LmMessageNode *muc_node)
 {
-	GossipChatroomId       id;
-	GossipChatroomError    error;
-	GossipJabberChatrooms *chatrooms;
-
-	room->timeout_id = 0;
+	LmMessageNode *item_node;
+	const gchar   *affiliation;
 
-	if (room->join_handler) {
-		lm_message_handler_unref (room->join_handler);
-		room->join_handler = NULL;
+	if (!muc_node) {
+		return GOSSIP_CHATROOM_AFFILIATION_NONE;
 	}
 
-	id = jabber_chatrooms_chatroom_get_id (room);
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Join timed out (internally)", id);
-
-	/* Set chatroom status and error */
-	error = GOSSIP_CHATROOM_ERROR_TIMED_OUT;
-
-	gossip_chatroom_set_last_error (room->chatroom, error);
-	gossip_chatroom_set_status (room->chatroom, GOSSIP_CHATROOM_STATUS_ERROR);
+	item_node = lm_message_node_get_child (muc_node, "item");
+	if (!item_node) {
+		return GOSSIP_CHATROOM_AFFILIATION_NONE;
+	}
 
-	/* Call callback */
-	chatrooms = room->chatrooms;
+	affiliation = lm_message_node_get_attribute (item_node, "affiliation");
+	if (!affiliation) {
+		return GOSSIP_CHATROOM_AFFILIATION_NONE;
+	}
 
-	if (room->callback != NULL) {
-		gossip_debug (DEBUG_DOMAIN, "ID[%d] Calling back... (timed out)", id);
-		(room->callback) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
-				  id,
-				  error,
-				  room->user_data);
+	if (strcmp (affiliation, "owner") == 0) {
+		return GOSSIP_CHATROOM_AFFILIATION_OWNER;
+	}
+	else if (strcmp (affiliation, "admin") == 0) {
+		return GOSSIP_CHATROOM_AFFILIATION_ADMIN;
+	}
+	else if (strcmp (affiliation, "member") == 0) {
+		return GOSSIP_CHATROOM_AFFILIATION_MEMBER;
+	}
+	else if (strcmp (affiliation, "outcast") == 0) {
+		return GOSSIP_CHATROOM_AFFILIATION_OUTCAST;
+	} else {
+		return GOSSIP_CHATROOM_AFFILIATION_NONE;
 	}
+}
 
-	/* Clean up */
-	g_hash_table_remove (chatrooms->room_id_hash, GINT_TO_POINTER (id));
-	g_hash_table_remove (chatrooms->room_jid_hash, room->jid);
+static void
+leave_chatroom (GossipJabberChatrooms *chatrooms,
+		GossipChatroom        *chatroom)
+{
+	GossipJID        *jid;
+	GossipChatroomId  id;
 
-	/* Clean up callback data */
-	room->callback = NULL;
-	room->user_data = NULL;
+	id = gossip_chatroom_get_id (chatroom);
 
-	jabber_chatrooms_chatroom_unref (room);
+	gossip_debug (DEBUG_DOMAIN, 
+		      "ID[%d] Leaving room", 
+		      id);
 
-	return FALSE;
+	gossip_chatroom_set_last_error (chatroom, GOSSIP_CHATROOM_ERROR_NONE);
+	gossip_chatroom_set_status (chatroom, GOSSIP_CHATROOM_STATUS_INACTIVE);
+
+	jid = gossip_jid_new (gossip_chatroom_get_id_str (chatroom));
+	
+	g_hash_table_remove (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+	g_hash_table_remove (chatrooms->chatrooms_by_pointer, chatroom);
+	g_hash_table_remove (chatrooms->chatrooms_by_jid, jid);
+	
+	g_object_unref (jid);
 }
 
 static void
-jabber_chatrooms_create_instant_room (JabberChatroom *room)
+create_instant_room (LmConnection   *connection,
+		     GossipChatroom *chatroom)
 {
 	LmMessage     *m;
 	LmMessageNode *node;
@@ -707,7 +508,7 @@
 					  LM_MESSAGE_TYPE_IQ,
 					  LM_MESSAGE_SUB_TYPE_SET);
 
-	account = gossip_contact_get_account (room->own_contact);
+	account = gossip_chatroom_get_account (chatroom);
 	from = g_strconcat (gossip_account_get_id (account),
 			    "/",
 			    gossip_account_get_resource (account),
@@ -715,7 +516,7 @@
 	lm_message_node_set_attribute (m->node, "from", from);
 	g_free (from);
 
-	to = gossip_chatroom_get_id_str (room->chatroom);
+	to = gossip_chatroom_get_id_str (chatroom);
 	lm_message_node_set_attribute (m->node, "to", to);
 
 	node = lm_message_node_add_child (m->node, "query", NULL);
@@ -727,12 +528,13 @@
 					"type", "submit",
 					NULL);
 
-	lm_connection_send (room->connection, m,  NULL);
+	lm_connection_send (connection, m,  NULL);
 	lm_message_unref (m);
 }
 
 static void
-jabber_chatrooms_create_reserved_room (JabberChatroom *room)
+create_reserved_room (LmConnection   *connection,
+		      GossipChatroom *chatroom)
 {
 	LmMessage     *m;
 	LmMessageNode *node;
@@ -747,7 +549,7 @@
 					  LM_MESSAGE_TYPE_IQ,
 					  LM_MESSAGE_SUB_TYPE_SET);
 
-	account = gossip_contact_get_account (room->own_contact);
+	account = gossip_chatroom_get_account (chatroom);
 	from = g_strconcat (gossip_account_get_id (account),
 			    "/",
 			    gossip_account_get_resource (account),
@@ -755,7 +557,7 @@
 	lm_message_node_set_attribute (m->node, "from", from);
 	g_free (from);
 
-	to = gossip_chatroom_get_id_str (room->chatroom);
+	to = gossip_chatroom_get_id_str (chatroom);
 	lm_message_node_set_attribute (m->node, "to", to);
 
 	node = lm_message_node_add_child (m->node, "query", NULL);
@@ -768,8 +570,8 @@
 					NULL);
 
 	/* FIXME: This is a shortcut for now, we should use their forms */
-	name = gossip_chatroom_get_name (room->chatroom);
-	password = gossip_chatroom_get_password (room->chatroom);
+	name = gossip_chatroom_get_name (chatroom);
+	password = gossip_chatroom_get_password (chatroom);
 
         child = lm_message_node_add_child (node, "field", NULL);
         lm_message_node_set_attributes (child, "var", "muc#roomconfig_roomname", NULL);
@@ -784,12 +586,13 @@
 	lm_message_node_add_child (child, "value", G_STR_EMPTY (password) ? "" : password);
 	
 	/* Finally send */
-	lm_connection_send (room->connection, m,  NULL);
+	lm_connection_send (connection, m,  NULL);
 	lm_message_unref (m);
 }
 
 static void
-jabber_chatrooms_request_reserved_room (JabberChatroom *room)
+request_reserved_room (LmConnection   *connection,
+		       GossipChatroom *chatroom)
 {
 	LmMessage     *m;
 	LmMessageNode *node;
@@ -801,7 +604,7 @@
 					  LM_MESSAGE_TYPE_IQ,
 					  LM_MESSAGE_SUB_TYPE_GET);
 
-	account = gossip_contact_get_account (room->own_contact);
+	account = gossip_chatroom_get_account (chatroom);
 	from = g_strconcat (gossip_account_get_id (account),
 			    "/",
 			    gossip_account_get_resource (account),
@@ -809,173 +612,410 @@
 	lm_message_node_set_attribute (m->node, "from", from);
 	g_free (from);
 
-	to = gossip_chatroom_get_id_str (room->chatroom);
+	to = gossip_chatroom_get_id_str (chatroom);
 	lm_message_node_set_attribute (m->node, "to", to);
 
 	node = lm_message_node_add_child (m->node, "query", NULL);
         lm_message_node_set_attributes (node, "xmlns", XMPP_MUC_OWNER_XMLNS, NULL);
 
-	lm_connection_send (room->connection, m,  NULL);
+	lm_connection_send (connection, m,  NULL);
 	lm_message_unref (m);
 }
 
-static LmHandlerResult
-jabber_chatrooms_join_cb (LmMessageHandler *handler,
-			  LmConnection     *connection,
-			  LmMessage        *m,
-			  JabberChatroom   *room)
-{
-	GossipJabberChatrooms *chatrooms;
-	GossipChatroomError    error;
-	GossipChatroomStatus   status;
-	GossipChatroomId       id;
-	GossipChatroomId       id_found;
-	LmMessageSubType       type;
-	LmMessageNode         *node = NULL;
-	const gchar           *from;
-	GossipJID             *jid;
-	JabberChatroom        *room_found;
-	GArray                *status_codes;
-	gboolean               room_match = FALSE;
+static gboolean
+join_finish (GossipJabberChatrooms *chatrooms,
+	     GossipChatroom        *chatroom,
+	     LmMessage             *m)
+{
+	GossipChatroomError   error;
+	GossipChatroomStatus  status;
+	GossipChatroomId      id;
+	GossipCallbackData   *data;
+	LmMessageSubType      type;
+	LmMessageNode        *node = NULL;
 
-	if (!room || !room->join_handler) {
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-	}
+	/* Get room id */
+	id = gossip_chatroom_get_id (chatroom);
 
-	chatrooms = room->chatrooms;
+	/* Clean up the join timeout */
+	g_hash_table_remove (chatrooms->join_timeouts, chatroom);
 
-	/* Get room id */
-	id = jabber_chatrooms_chatroom_get_id (room);
+	/* Check status code */
+	if (gossip_jabber_get_message_has_status (m, 201)) {
+		/* Room was created for us */
+		if (0) {
+			request_reserved_room (chatrooms->connection, chatroom);
+			create_instant_room (chatrooms->connection, chatroom); 
+		} else {
+			create_reserved_room (chatrooms->connection, chatroom);
+		}
+	}
 
-	from = lm_message_node_get_attribute (m->node, "from");
-	jid = gossip_jid_new (from);
+	/* Check for error */
+	type = lm_message_get_sub_type (m);
+	if (type == LM_MESSAGE_SUB_TYPE_ERROR) {
+		node = lm_message_node_get_child (m->node, "error");
+	}
+
+	if (node) {
+		const gchar *xmlns = NULL;
+		const gchar *code_str;
+		gint         code;
 
-	room_found = g_hash_table_lookup (chatrooms->room_jid_hash, jid);
-	gossip_jid_unref (jid);
+		code_str = lm_message_node_get_attribute (node, "code");
+		code = code_str ? atoi (code_str) : 0;
 
-	if (room_found) {
-		id_found = jabber_chatrooms_chatroom_get_id (room_found);
-		if (id == id_found) {
-			room_match = TRUE;
+		node = lm_message_node_get_child (m->node, "x");
+		if (node) {
+			xmlns = lm_message_node_get_attribute (node, "xmlns");
 		}
+
+		error = error_from_code (xmlns, code);
+		gossip_debug (DEBUG_DOMAIN, 
+			      "ID[%d] %s", 
+			      id, 
+			      gossip_chatroom_error_to_string (error));
+
+		/* Set room state */
+		status = GOSSIP_CHATROOM_STATUS_ERROR;
+	} else {
+		error = GOSSIP_CHATROOM_ERROR_NONE;
+		status = GOSSIP_CHATROOM_STATUS_ACTIVE;
 	}
 
-	if (!room_match) {
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	gossip_chatroom_set_last_error (chatroom, error);
+	gossip_chatroom_set_status (chatroom, status);
+
+	data = g_hash_table_lookup (chatrooms->join_callbacks, chatroom);
+
+	if (data && data->callback) {
+		GossipChatroomJoinCb func;
+
+		func = (GossipChatroomJoinCb) data->callback;
+
+		gossip_debug (DEBUG_DOMAIN,
+			      "ID[%d] Calling back...", 
+			      id);
+		(func) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
+			id, 
+			error, 
+			data->user_data);
 	}
 
-	/* Clean up the join timeout */
-	if (room->timeout_id) {
-		g_source_remove (room->timeout_id);
-		room->timeout_id = 0;
+	/* Clean up the user data */
+	g_hash_table_remove (chatrooms->join_callbacks, chatroom);
+
+	/* If we have an error, clean up */
+	if (error != GOSSIP_CHATROOM_ERROR_NONE) {
+		GossipJID        *jid;
+		GossipChatroomId  id;
+
+		/* Clean up */
+		id = gossip_chatroom_get_id (chatroom);
+		jid = gossip_jid_new (gossip_chatroom_get_id_str (chatroom));
+
+		g_hash_table_remove (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+		g_hash_table_remove (chatrooms->chatrooms_by_pointer, chatroom);
+		g_hash_table_remove (chatrooms->chatrooms_by_jid, jid);
+
+		g_object_unref (jid);
+
+		return FALSE;
 	}
 
-	/* Clean up handler */
-	if (room->join_handler) {
-		lm_message_handler_unref (room->join_handler);
-		room->join_handler = NULL;
+	return TRUE;
+}
+
+static gchar *
+get_new_id_for_new_nick (GossipContact *contact,
+			 const gchar   *new_nick)
+{
+	GossipJID   *jid;
+	const gchar *id_str;
+	const gchar *id_str_without_resource;
+	gchar       *id_str_with_new_nick;
+
+	id_str = gossip_contact_get_id (contact);
+	jid = gossip_jid_new (id_str);
+	id_str_without_resource = gossip_jid_get_without_resource (jid);
+	id_str_with_new_nick = g_strconcat (id_str_without_resource, "/", new_nick, NULL);
+	g_object_unref (jid);
+
+	return id_str_with_new_nick;
+}
+
+static LmHandlerResult
+presence_handler (LmMessageHandler      *handler,
+		  LmConnection          *connection,
+		  LmMessage             *m,
+		  GossipJabberChatrooms *chatrooms)
+{
+	const gchar               *from;
+	GossipJID                 *jid;
+	GossipContact             *own_contact;
+	GossipContact             *contact;
+	GossipPresence            *presence;
+	GossipChatroom            *chatroom;
+	GossipChatroomId           id;
+	LmMessageSubType           type;
+	LmMessageNode             *node;
+	gboolean                   was_offline;
+	LmMessageNode             *muc_user_node;
+	GossipChatroomContactInfo  muc_contact_info;
+	gchar                     *new_nick;
+
+	from = lm_message_node_get_attribute (m->node, "from");
+	jid = gossip_jid_new (from);
+
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_jid, jid);
+
+	if (!chatroom) {
+		g_object_unref (jid);
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;	
+	}
+
+	gossip_debug (DEBUG_DOMAIN,
+		      "Presence from:'%s'", 
+		      from);
+
+	/* If this JID matches a room we are joining, first call the
+	 * callback and clean up to show we are now joined and then
+	 * continue to handle the first presence message we were sent.
+	 */
+	if (g_hash_table_lookup (chatrooms->join_timeouts, chatroom)) {
+		/* If this returns FALSE, it means there was an
+		 * error, and we have handled it, so don't handle it
+		 * again here or any further messages from the room 
+		 */
+		if (!join_finish (chatrooms, chatroom, m)) {
+			g_object_unref (jid);
+			return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;	
+		}
 	}
 
-	/* Check status code */
-	status_codes = jabber_chatrooms_get_status (m);
-	if (status_codes) {
-		if (jabber_chatrooms_has_status (status_codes, 201)) {
-			/* Room was created for us */
-			if (0) {
-				jabber_chatrooms_request_reserved_room (room);
-				jabber_chatrooms_create_instant_room (room); 
-			} else {
-				jabber_chatrooms_create_reserved_room (room);
+	id = gossip_chatroom_get_id (chatroom);
+
+	type = lm_message_get_sub_type (m);
+	switch (type) {
+	case LM_MESSAGE_SUB_TYPE_AVAILABLE:
+		/* Get details */
+		contact = gossip_jabber_get_contact_from_jid (chatrooms->jabber, 
+							      from, 
+							      FALSE,
+							      FALSE,
+							      FALSE);
+
+		presence = gossip_presence_new ();
+
+		node = lm_message_node_get_child (m->node, "show");
+		if (node) {
+			GossipPresenceState state;
+
+			state = gossip_jabber_presence_state_from_str (node->value);
+			gossip_presence_set_state (presence, state);
+		}
+
+		node = lm_message_node_get_child (m->node, "status");
+		if (node) {
+			gossip_presence_set_status (presence, node->value);
+		}
+
+		/* Should signal joined if contact was found but offline */
+		was_offline = !gossip_contact_is_online (contact);
+		gossip_contact_add_presence (contact, presence);
+		g_object_unref (presence);
+
+		muc_user_node = find_muc_user_node (m->node);
+		muc_contact_info.role = get_role (muc_user_node);
+		muc_contact_info.affiliation = get_affiliation (muc_user_node);
+
+		/* Is contact new or updated */
+		if (was_offline) {
+			gossip_debug (DEBUG_DOMAIN,
+				      "ID[%d] Presence for new joining contact:'%s'",
+				      id,
+				      gossip_jid_get_full (jid));
+			gossip_chatroom_contact_joined (chatroom,
+							contact,
+							&muc_contact_info);
+		} else {
+			gossip_chatroom_set_contact_info (chatroom,
+							  contact,
+							  &muc_contact_info);
+		}
+		break;
+
+	case LM_MESSAGE_SUB_TYPE_UNAVAILABLE:
+		contact = gossip_jabber_get_contact_from_jid (chatrooms->jabber, 
+							      from, 
+							      FALSE,
+							      FALSE,
+							      FALSE);
+
+		new_nick = NULL;
+
+		if (gossip_jabber_get_message_is_muc_new_nick (m, &new_nick)) {
+			gchar *old_nick;
+			gchar *new_id;
+
+			old_nick = g_strdup (gossip_contact_get_name (contact));
+			new_id = get_new_id_for_new_nick (contact, new_nick);
+			gossip_contact_set_id (contact, new_id);
+			gossip_contact_set_name (contact, new_nick);
+			g_free (new_id);
+			g_free (new_nick);
+
+			gossip_debug (DEBUG_DOMAIN,
+				      "[%d] Nick changed for contact:'%s', old nick:'%s', new nick:'%s'", 
+				      id, 
+				      gossip_contact_get_id (contact),
+				      old_nick,
+				      gossip_contact_get_name (contact));
+			
+			g_signal_emit_by_name (chatrooms->jabber, 
+					       "chatroom-nick-changed", 
+					       id,
+					       contact,
+					       old_nick);
+			
+			g_free (old_nick);
+		} else {
+			own_contact = gossip_chatroom_get_own_contact (chatroom);
+
+			if (gossip_contact_equal (contact, own_contact)) {
+				gossip_debug (DEBUG_DOMAIN, 
+					      "ID[%d] We have been kicked!", 
+					      id);
+
+				gossip_chatroom_set_status (chatroom, 
+							    GOSSIP_CHATROOM_STATUS_INACTIVE);
+
+				g_signal_emit_by_name (chatrooms->jabber, 
+						       "chatroom-kicked", 
+						       id);
+
+				leave_chatroom (chatrooms, chatroom);
+			} else {
+				gossip_debug (DEBUG_DOMAIN,
+					      "ID[%d] Contact left:'%s'",
+					      id,
+					      gossip_contact_get_id (contact));
+
+				gossip_chatroom_contact_left (chatroom, contact);
+			}
+		}
+		break;
+
+	case LM_MESSAGE_SUB_TYPE_ERROR:
+		node = lm_message_node_get_child (m->node, "error");
+		if (node) {
+			GossipChatroomError  error;
+			const gchar         *xmlns = NULL;
+			const gchar         *code_str;
+			gint                 code;
+			
+			code_str = lm_message_node_get_attribute (node, "code");
+			code = code_str ? atoi (code_str) : 0;
+
+			node = lm_message_node_get_child (m->node, "x");
+			if (node) {
+				xmlns = lm_message_node_get_attribute (node, "xmlns");
 			}
+			
+			error = error_from_code (xmlns, code);
+			gossip_debug (DEBUG_DOMAIN,
+				      "ID[%d] %s", 
+				      id, 
+				      gossip_chatroom_error_to_string (error));
+			
+			g_signal_emit_by_name (chatrooms->jabber,
+					       "chatroom-error",
+					       id, 
+					       error);
 		}
+		break;
 
-		g_array_free (status_codes, TRUE);
+	default:
+		gossip_debug (DEBUG_DOMAIN, 
+			      "Presence not handled for:'%s'",
+			      gossip_jid_get_full (jid));
+		break;
 	}
 
-	/* Check for error */
-	type = lm_message_get_sub_type (m);
-	if (type == LM_MESSAGE_SUB_TYPE_ERROR) {
-		node = lm_message_node_get_child (m->node, "error");
-	}
+	g_object_unref (jid);
 
-	if (node) {
-		const gchar *str;
-		gint         code;
+	return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+}
 
-		str = lm_message_node_get_attribute (node, "code");
-		code = str ? atoi (str) : 0;
+static void
+join_timeout_destroy_notify_cb (gpointer data)
+{
+	guint timeout_id;
 
-		error = jabber_chatrooms_error_from_code (code);
-		gossip_debug (DEBUG_DOMAIN, "ID[%d] %s", 
-			      id, gossip_chatroom_error_to_string (error));
+	timeout_id = GPOINTER_TO_UINT (data);
+	g_source_remove (timeout_id);
+}
 
-		/* Set room state */
-		status = GOSSIP_CHATROOM_STATUS_ERROR;
-	} else {
-		error = GOSSIP_CHATROOM_ERROR_NONE;
-		status = GOSSIP_CHATROOM_STATUS_ACTIVE;
-	}
+static gboolean
+join_timeout_cb (GossipCallbackData *timeout_data)
+{
+	GossipJID             *jid;
+	GossipChatroomId       id;
+	GossipChatroom        *chatroom;
+	GossipJabberChatrooms *chatrooms;
+	GossipChatroomError    error;
+	GossipCallbackData    *data;
 
-	gossip_chatroom_set_last_error (room->chatroom, error);
-	gossip_chatroom_set_status (room->chatroom, status);
+	chatrooms = timeout_data->data1;
+	chatroom = timeout_data->data2;
 
-	if (room->callback != NULL) {
-		gossip_debug (DEBUG_DOMAIN, "ID[%d] Calling back...", id);
-		(room->callback) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
-				  id, 
-				  error, 
-				  room->user_data);
-	}
-
-	/* Clean up callback data */
-	room->callback = NULL;
-	room->user_data = NULL;
-
-	/* Articulate own contact presence so we appear in group chat */
-	if (error == GOSSIP_CHATROOM_ERROR_NONE) {
-		gossip_contact_add_presence (room->own_contact,
-					     chatrooms->presence);
-
-		g_signal_emit_by_name (chatrooms->jabber,
-				       "chatroom-joined",
-				       id);
-	} else {
-		/* Clean up */
-		g_hash_table_remove (chatrooms->room_id_hash,
-				     GINT_TO_POINTER (id));
-		g_hash_table_remove (chatrooms->room_jid_hash,
-				     room->jid);
-	}
+	g_hash_table_remove (chatrooms->join_timeouts, chatroom);
 
-	return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-}
+	id = gossip_chatroom_get_id (chatroom);
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Join timed out (internally)", 
+		      id);
 
-static void
-jabber_chatrooms_close (GossipJabberChatrooms *chatrooms,
-			GossipChatroomId       id)
-{
-	JabberChatroom *room;
+	/* Set chatroom status and error */
+	error = GOSSIP_CHATROOM_ERROR_TIMED_OUT;
 
-	g_return_if_fail (chatrooms != NULL);
+	gossip_chatroom_set_last_error (chatroom, error);
+	gossip_chatroom_set_status (chatroom, GOSSIP_CHATROOM_STATUS_ERROR);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		return;
+	/* Call callback */
+	data = g_hash_table_lookup (chatrooms->join_callbacks, chatroom);
+
+	if (data && data->callback) {
+		GossipChatroomJoinCb   func;
+		GossipJabberChatrooms *chatrooms;
+
+		func = (GossipChatroomJoinCb) data->callback;
+		chatrooms = (GossipJabberChatrooms *) data->data1;
+
+		gossip_debug (DEBUG_DOMAIN,
+			      "ID[%d] Calling back... (timed out)", 
+			      id);
+		(func) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
+			id, 
+			error, 
+			data->user_data);
 	}
 
-	gossip_chatroom_set_last_error (room->chatroom, GOSSIP_CHATROOM_ERROR_NONE);
-	gossip_chatroom_set_status (room->chatroom, GOSSIP_CHATROOM_STATUS_INACTIVE);
+	/* Clean up the user data */
+	g_hash_table_remove (chatrooms->join_callbacks, chatroom);
 
-	g_hash_table_remove (chatrooms->room_id_hash,
-			     GINT_TO_POINTER (id));
-	g_hash_table_remove (chatrooms->room_jid_hash,
-			     room->jid);
+	/* Clean up */
+	jid = gossip_jid_new (gossip_chatroom_get_id_str (chatroom));
+	
+	g_hash_table_remove (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+	g_hash_table_remove (chatrooms->chatrooms_by_pointer, chatroom);
+	g_hash_table_remove (chatrooms->chatrooms_by_jid, jid);
+	
+	g_object_unref (jid);
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Leaving room, ref count is %d",
-		      id, room->ref_count - 1);
+	gossip_callback_data_free (timeout_data);
 
-	jabber_chatrooms_chatroom_unref (room);
+	return FALSE;
 }
 
 GossipChatroomId
@@ -984,28 +1024,26 @@
 			      GossipChatroomJoinCb   callback,
 			      gpointer               user_data)
 {
-	GossipChatroomId   id;
-	JabberChatroom    *room, *existing_room;
-	LmMessage         *m;
-	LmMessageNode     *node;
-	const gchar       *show = NULL;
-	gchar             *id_str;
-	const gchar       *password;
+	LmMessage            *m;
+	LmMessageNode        *node;
+	GossipContact        *own_contact;
+	GossipChatroomId      id;
+	gchar                *id_str;
+	const gchar          *jid_str;
+	const gchar          *show = NULL;
+	const gchar          *password;
+	guint                 timeout_id;
 
 	g_return_val_if_fail (chatrooms != NULL, 0);
 	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), 0);
 	g_return_val_if_fail (callback != NULL, 0);
 
-	room = jabber_chatrooms_chatroom_new (chatrooms, chatroom);
-	existing_room = g_hash_table_lookup (chatrooms->room_jid_hash, room->jid);
-
-	if (existing_room) {
-		jabber_chatrooms_chatroom_unref (room);
-
+	if (g_hash_table_lookup (chatrooms->chatrooms_by_pointer, chatroom)) {
 		/* Duplicate room already exists. */
-		id = jabber_chatrooms_chatroom_get_id (existing_room);
+		id = gossip_chatroom_get_id (chatroom);
 
-		gossip_debug (DEBUG_DOMAIN, "ID[%d] Join chatroom:'%s', room already exists.",
+		gossip_debug (DEBUG_DOMAIN, 
+			      "ID[%d] Join chatroom:'%s', room already exists.",
 			      id,
 			      gossip_chatroom_get_room (chatroom));
 
@@ -1017,31 +1055,49 @@
 		return id;
 	}
 
+	jid_str = gossip_chatroom_get_id_str (chatroom);
+
 	/* Get real chatroom. */
-	id = jabber_chatrooms_chatroom_get_id (room);
+	id = gossip_chatroom_get_id (chatroom);
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Join chatroom:'%s' on server:'%s'",
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Join chatroom:'%s' on server:'%s'",
 		      id,
 		      gossip_chatroom_get_room (chatroom),
 		      gossip_chatroom_get_server (chatroom));
 
-
 	/* Add timeout for server response. */
-	room->timeout_id = g_timeout_add (JOIN_TIMEOUT,
-					  (GSourceFunc) jabber_chatrooms_join_timeout_cb,
-					  room);
-
-	room->chatrooms = chatrooms;
-
-	/* Set callback data. */
-	room->callback = callback;
-	room->user_data = user_data;
+	timeout_id = g_timeout_add (JOIN_TIMEOUT,
+				    (GSourceFunc) join_timeout_cb,
+				    gossip_callback_data_new (NULL, NULL, chatrooms, chatroom, NULL));
+
+	g_hash_table_insert (chatrooms->join_timeouts, 
+			     g_object_ref (chatroom),
+			     GUINT_TO_POINTER (timeout_id));
+	g_hash_table_insert (chatrooms->join_callbacks, 
+			     g_object_ref (chatroom),
+			     gossip_callback_data_new (callback, user_data, chatrooms, NULL, NULL));
+	g_hash_table_insert (chatrooms->chatrooms_by_id, 
+			     GINT_TO_POINTER (id),
+			     g_object_ref (chatroom));
+	g_hash_table_insert (chatrooms->chatrooms_by_pointer, 
+			     g_object_ref (chatroom), 
+			     GINT_TO_POINTER (1));
+	g_hash_table_insert (chatrooms->chatrooms_by_jid,
+			     gossip_jid_new (jid_str),
+			     g_object_ref (chatroom));
 
-	gossip_chatroom_set_last_error (room->chatroom, GOSSIP_CHATROOM_ERROR_NONE);
+	gossip_chatroom_set_last_error (chatroom, GOSSIP_CHATROOM_ERROR_NONE);
 	gossip_chatroom_set_status (chatroom, GOSSIP_CHATROOM_STATUS_JOINING);
 
+	/* The other hash table inserts MUST occur before this, since
+	 * we check to see if the contact is a chatroom contact and
+	 * that does a hash tabe lookup.
+	 */
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+
 	/* Compose message. */
-	m = lm_message_new_with_sub_type (gossip_jid_get_full (room->jid),
+	m = lm_message_new_with_sub_type (gossip_contact_get_id (own_contact),
 					  LM_MESSAGE_TYPE_PRESENCE,
 					  LM_MESSAGE_SUB_TYPE_AVAILABLE);
 
@@ -1054,15 +1110,6 @@
 		lm_message_node_add_child (node, "password", password);
 	}
 
-	g_hash_table_insert (chatrooms->room_id_hash,
-			     GINT_TO_POINTER (id),
-			     jabber_chatrooms_chatroom_ref (room));
-	g_hash_table_insert (chatrooms->room_jid_hash,
-			     room->jid,
-			     jabber_chatrooms_chatroom_ref (room));
-
-	jabber_chatrooms_chatroom_unref (room);
-
 	show = gossip_jabber_presence_state_to_str (chatrooms->presence);
 
 	if (show) {
@@ -1073,20 +1120,6 @@
 	lm_message_node_set_attribute (m->node, "id", id_str);
 	g_free (id_str);
 
-	/* Send message. */
-	room->connection = lm_connection_ref (chatrooms->connection);
-	room->join_handler = lm_message_handler_new ((LmHandleMessageFunction)
-						     jabber_chatrooms_join_cb,
-						     room, NULL);
-
-	lm_connection_register_message_handler (chatrooms->connection,
-						room->join_handler,
-						LM_MESSAGE_TYPE_PRESENCE,
-						LM_HANDLER_PRIORITY_FIRST);
-
-	/* Some servers don't honor the id so we don't get a reply and
-	 * are waiting forever. 
-	 */
 	lm_connection_send (chatrooms->connection, m,  NULL);
 	lm_message_unref (m);
 
@@ -1097,47 +1130,52 @@
 gossip_jabber_chatrooms_cancel (GossipJabberChatrooms *chatrooms,
 				GossipChatroomId       id)
 {
-	JabberChatroom *room;
+	GossipChatroom     *chatroom;
+	GossipJID          *jid;
+	GossipCallbackData *data;
 
 	g_return_if_fail (chatrooms != NULL);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+	if (!chatroom) {
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Cancel joining room", id);
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Cancel joining room", 
+		      id);
+	
+	g_hash_table_remove (chatrooms->join_timeouts, chatroom);
 
-	if (room->timeout_id) {
-		g_source_remove (room->timeout_id);
-		room->timeout_id = 0;
-	}
+	gossip_chatroom_set_last_error (chatroom, GOSSIP_CHATROOM_ERROR_NONE);
+	gossip_chatroom_set_status (chatroom, GOSSIP_CHATROOM_STATUS_INACTIVE);
 
-	if (room->join_handler) {
-		lm_message_handler_unref (room->join_handler);
-		room->join_handler = NULL;
-	}
+	data = g_hash_table_lookup (chatrooms->join_callbacks, chatroom);
+
+	if (data && data->callback) {
+		GossipChatroomJoinCb func;
 
-	gossip_chatroom_set_last_error (room->chatroom, GOSSIP_CHATROOM_ERROR_NONE);
-	gossip_chatroom_set_status (room->chatroom, GOSSIP_CHATROOM_STATUS_INACTIVE);
+		func = (GossipChatroomJoinCb) data->callback;
 
-	if (room->callback != NULL) {
-		gossip_debug (DEBUG_DOMAIN, "ID[%d] Calling back...", id);
-		(room->callback) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
-				  id, 
-				  GOSSIP_CHATROOM_ERROR_CANCELED, 
-				  room->user_data);
+		gossip_debug (DEBUG_DOMAIN,
+			      "ID[%d] Calling back... (cancelled)", 
+			      id);
+		(func) (GOSSIP_CHATROOM_PROVIDER (chatrooms->jabber),
+			id, 
+			GOSSIP_CHATROOM_ERROR_CANCELED, 
+			data->user_data);
 	}
 
-	/* Clean up callback data */
-	room->callback = NULL;
-	room->user_data = NULL;
+	/* Clean up the user data */
+	g_hash_table_remove (chatrooms->join_callbacks, chatroom);
 
-	g_hash_table_remove (chatrooms->room_id_hash,
-			     GINT_TO_POINTER (id));
-	g_hash_table_remove (chatrooms->room_jid_hash,
-			     room->jid);
+	jid = gossip_jid_new (gossip_chatroom_get_id_str (chatroom));
+	
+	g_hash_table_remove (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+	g_hash_table_remove (chatrooms->chatrooms_by_pointer, chatroom);
+	g_hash_table_remove (chatrooms->chatrooms_by_jid, jid);
+	
+	g_object_unref (jid);
 }
 
 void
@@ -1146,21 +1184,27 @@
 			      const gchar           *message)
 {
 	LmMessage      *m;
-	JabberChatroom *room;
+	GossipChatroom *chatroom;
+	const gchar    *jid_str;
 
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (message != NULL);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		g_warning ("ProtocolChatrooms: Unknown chatroom id: %d", id);
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, 
+					GINT_TO_POINTER (id));
+
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not send message, unknown chatroom id:%d", id);
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Send message", id);
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Send message", 
+		      id);
 
-	m = lm_message_new_with_sub_type (gossip_jid_get_without_resource (room->jid),
+	jid_str = gossip_chatroom_get_id_str (chatroom);
+	m = lm_message_new_with_sub_type (jid_str, 
 					  LM_MESSAGE_TYPE_MESSAGE,
 					  LM_MESSAGE_SUB_TYPE_GROUPCHAT);
 	lm_message_node_add_child (m->node, "body", message);
@@ -1174,26 +1218,29 @@
 					GossipChatroomId       id,
 					const gchar           *new_subject)
 {
-	JabberChatroom *room;
-	const gchar    *without_resource;
 	LmMessage      *m;
+	GossipChatroom *chatroom;
+	const gchar    *jid_str;
 
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (new_subject != NULL);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		g_warning ("ProtocolChatrooms: Unknown chatroom id: %d", id);
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, 
+					GINT_TO_POINTER (id));
+
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not change subject, unknown chatroom id:%d", id);
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Change subject to:'%s'",
-		      id, new_subject);
-
-	without_resource = gossip_jid_get_without_resource (room->jid);
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Change subject to:'%s'",
+		      id,
+		      new_subject);
 
-	m = lm_message_new_with_sub_type (without_resource,
+	jid_str = gossip_chatroom_get_id_str (chatroom);
+	m = lm_message_new_with_sub_type (jid_str,
 					  LM_MESSAGE_TYPE_MESSAGE,
 					  LM_MESSAGE_SUB_TYPE_GROUPCHAT);
 
@@ -1209,25 +1256,40 @@
 				     const gchar           *new_nick)
 {
 	LmMessage      *m;
-	JabberChatroom *room;
+	GossipChatroom *chatroom;
+	GossipContact  *own_contact;
+	gchar          *new_id;
 
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (new_nick != NULL);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		g_warning ("ProtocolChatrooms: Unknown chatroom id: %d", id);
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not change nick, unknown chatroom id:%d", id);
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Change chatroom nick to:'%s'",
-		      id, new_nick);
-
-	gossip_jid_set_resource (room->jid, new_nick);
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+	
+	if (!own_contact) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not get own contact, unknown chatroom id:%d", id);
+		return;
+	}
 
-	m = lm_message_new (gossip_jid_get_full (room->jid),
-			    LM_MESSAGE_TYPE_PRESENCE);
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Change chatroom nick to:'%s'",
+		      id, 
+		      new_nick);
+	
+	/* NOTE: Don't change the nick until we get confirmation from
+	 * the server that it has changed.
+	 */
+	new_id = get_new_id_for_new_nick (own_contact, new_nick);
+	m = lm_message_new (new_id, LM_MESSAGE_TYPE_PRESENCE);
+	g_free (new_id);
 
 	lm_connection_send (chatrooms->connection, m, NULL);
 	lm_message_unref (m);
@@ -1238,24 +1300,40 @@
 			       GossipChatroomId       id)
 {
 	LmMessage      *m;
-	JabberChatroom *room;
+	GossipChatroom *chatroom;
+	GossipContact  *own_contact;
 
 	g_return_if_fail (chatrooms != NULL);
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not leave chatroom, unknown chatroom id:%d", id);
+		return;
+	}
+
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+	
+	if (!own_contact) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not get own contact, unknown chatroom id:%d", id);
 		return;
 	}
 
-	m = lm_message_new_with_sub_type (gossip_jid_get_full (room->jid),
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Leaving chatroom:'%s'",
+		      id,
+		      gossip_chatroom_get_id_str (chatroom));
+
+	m = lm_message_new_with_sub_type (gossip_contact_get_id (own_contact),
 					  LM_MESSAGE_TYPE_PRESENCE,
 					  LM_MESSAGE_SUB_TYPE_UNAVAILABLE);
 
 	lm_connection_send (chatrooms->connection, m, NULL);
 	lm_message_unref (m);
 
-	jabber_chatrooms_close (chatrooms, id);
+	leave_chatroom (chatrooms, chatroom);
 }
 
 void
@@ -1266,8 +1344,8 @@
 {
 	LmMessage      *m;
 	LmMessageNode  *node;
-	JabberChatroom *room;
 	GossipAccount  *account;
+	GossipChatroom *chatroom;
 	GossipJID      *jid;
 	gchar          *from;
 	const gchar    *contact_id;
@@ -1277,9 +1355,13 @@
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
+
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not kick:'%s', unknown chatroom id:%d", 
+			   gossip_contact_get_id (contact), 
+			   id);
 		return;
 	}
 
@@ -1287,7 +1369,7 @@
 					  LM_MESSAGE_TYPE_IQ,
 					  LM_MESSAGE_SUB_TYPE_SET);
 
-	account = gossip_contact_get_account (room->own_contact);
+	account = gossip_chatroom_get_account (chatroom);
 	from = g_strconcat (gossip_account_get_id (account),
 			    "/",
 			    gossip_account_get_resource (account),
@@ -1295,7 +1377,7 @@
 	lm_message_node_set_attribute (m->node, "from", from);
 	g_free (from);
 
-	to = gossip_chatroom_get_id_str (room->chatroom);
+	to = gossip_chatroom_get_id_str (chatroom);
 	lm_message_node_set_attribute (m->node, "to", to);
 
 	node = lm_message_node_add_child (m->node, "query", NULL);
@@ -1310,71 +1392,29 @@
 					"nick", nick, 
 					"role", "none",
 					NULL);
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 	
 	lm_connection_send (chatrooms->connection, m, NULL);
 	lm_message_unref (m);
 }
 
-GSList *
-gossip_jabber_chatrooms_get_contacts (GossipJabberChatrooms *chatrooms, 
-				      GossipChatroomId       id)
-{
-	JabberChatroom *room;
-
-	g_return_val_if_fail (chatrooms != NULL, NULL);
-
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		return NULL;
-	}
-
-	return room->contacts;
-}
-
 GossipChatroom *
 gossip_jabber_chatrooms_find_by_id (GossipJabberChatrooms *chatrooms,
 				    GossipChatroomId       id)
 {
-	JabberChatroom *room;
-
-	g_return_val_if_fail (chatrooms != NULL, NULL);
-
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
-	if (!room) {
-		return NULL;
-	}
-
-	return room->chatroom;
+	return g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
 }
 
 GossipChatroom *
 gossip_jabber_chatrooms_find (GossipJabberChatrooms *chatrooms,
 			      GossipChatroom        *chatroom)
 {
-	JabberChatroom *room;
-	GossipJID      *jid;
-	gchar          *jid_str;
-
-	g_return_val_if_fail (chatrooms != NULL, NULL);
-	g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), NULL);
-
-	jid_str = g_strdup_printf ("%s/%s",
-				   gossip_chatroom_get_id_str (chatroom),
-				   gossip_chatroom_get_nick (chatroom));
-	jid = gossip_jid_new (jid_str);
-	g_free (jid_str);
-
-	room = g_hash_table_lookup (chatrooms->room_jid_hash, jid);
-	gossip_jid_unref (jid);
-
-	if (!room) {
-		return NULL;
+	/* FIXME: What is the point of this now? */
+	if (g_hash_table_lookup (chatrooms->chatrooms_by_pointer, chatroom)) {
+		return chatroom;
 	}
-
-	return room->chatroom;
+	
+	return NULL;
 }
 
 void
@@ -1386,28 +1426,40 @@
 	LmMessage      *m;
 	LmMessageNode  *parent;
 	LmMessageNode  *node;
-	JabberChatroom *room;
+	GossipChatroom *chatroom;
+	GossipContact  *own_contact;
 
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (GOSSIP_IS_CONTACT (contact));
 
-	room = g_hash_table_lookup (chatrooms->room_id_hash,
-				    GINT_TO_POINTER (id));
+	chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_id, GINT_TO_POINTER (id));
 
-	if (!room) {
-		g_warning ("ProtocolChatrooms: Unknown chatroom id: %d", id);
+	if (!chatroom) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not invite:'%s', unknown chatroom id:%d", 
+			   gossip_contact_get_id (contact),
+			   id);
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "ID[%d] Invitation to contact:'%s' from:'%s'",
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+	
+	if (!own_contact) {
+		/* FIXME: Should error this up? */
+		g_warning ("Could not get own contact, unknown chatroom id:%d", id);
+		return;
+	}
+
+	gossip_debug (DEBUG_DOMAIN,
+		      "ID[%d] Invitation to contact:'%s' from:'%s'",
 		      id,
 		      gossip_contact_get_id (contact),
-		      gossip_contact_get_id (room->own_contact));
+		      gossip_contact_get_id (own_contact));
 
-	m = lm_message_new (gossip_jid_get_without_resource (room->jid),
+	m = lm_message_new (gossip_chatroom_get_id_str (chatroom),
 			    LM_MESSAGE_TYPE_MESSAGE);
 	lm_message_node_set_attributes (m->node,
-					"from", gossip_contact_get_id (room->own_contact),
+					"from", gossip_contact_get_id (own_contact),
 					NULL);
 
 	parent = lm_message_node_add_child (m->node, "x", NULL);
@@ -1428,19 +1480,20 @@
 				       GossipChatroomInvite  *invite,
 				       const gchar           *nickname)
 {
-	GossipChatroom *chatroom;
-	GossipContact  *contact;
-	GossipAccount  *account;
-	gchar          *room = NULL;
-	const gchar    *id;
-	const gchar    *server;
+	GossipSession         *session;
+	GossipChatroom        *chatroom;
+	GossipChatroomManager *chatroom_manager;
+	GossipContact         *contact;
+	gchar                 *room = NULL;
+	const gchar           *id;
+	const gchar           *server;
 
 	g_return_if_fail (chatrooms != NULL);
 	g_return_if_fail (invite != NULL);
 	g_return_if_fail (callback != NULL);
 
 	id = gossip_chatroom_invite_get_id (invite);
-	contact = gossip_chatroom_invite_get_invitor (invite);
+	contact = gossip_chatroom_invite_get_inviter (invite);
 
 	server = strstr (id, "@");
 
@@ -1452,15 +1505,15 @@
 		server++;
 	}
 
-	account = gossip_contact_get_account (contact);
+	session = _gossip_jabber_get_session (chatrooms->jabber);
+	chatroom_manager = gossip_session_get_chatroom_manager (session);
+	chatroom = gossip_chatroom_manager_find_or_create (chatroom_manager, 
+							   gossip_contact_get_account (contact), 
+							   server, 
+							   room,
+							   NULL);
 
-	chatroom = g_object_new (GOSSIP_TYPE_CHATROOM,
-				 "account", account,
-				 "server", server,
-				 "name", room,
-				 "room", room,
-				 "nick", nickname,
-				 NULL);
+	gossip_chatroom_set_nick (chatroom, nickname);
 
 	gossip_jabber_chatrooms_join (chatrooms,
 				      chatroom,
@@ -1486,11 +1539,13 @@
 	g_return_if_fail (invite != NULL);
 
 	own_contact = gossip_jabber_get_own_contact (chatrooms->jabber);
-	contact = gossip_chatroom_invite_get_invitor (invite);
+	contact = gossip_chatroom_invite_get_inviter (invite);
 	id = gossip_chatroom_invite_get_id (invite);
 
-	gossip_debug (DEBUG_DOMAIN, "Invitation decline to:'%s' into room:'%s'",
-		      gossip_contact_get_id (contact), id);
+	gossip_debug (DEBUG_DOMAIN,
+		      "Invitation decline to:'%s' into room:'%s'",
+		      gossip_contact_get_id (contact), 
+		      id);
 
 	m = lm_message_new (id, LM_MESSAGE_TYPE_MESSAGE);
 	lm_message_node_set_attributes (m->node,
@@ -1510,11 +1565,14 @@
 }
 
 static void
-jabber_chatrooms_get_rooms_foreach (gpointer               key,
-				    JabberChatroom        *room,
-				    GList                **list)
+get_rooms_foreach (gpointer key,
+		   gpointer value,
+		   gpointer user_data)
 {
-	*list = g_list_append (*list, key);
+	GList **list;
+
+	list = (GList **) user_data;
+	*list = g_list_prepend (*list, key);
 }
 
 GList *
@@ -1524,10 +1582,12 @@
 
 	g_return_val_if_fail (chatrooms != NULL, NULL);
 
-	g_hash_table_foreach (chatrooms->room_id_hash,
-			      (GHFunc) jabber_chatrooms_get_rooms_foreach,
+	g_hash_table_foreach (chatrooms->chatrooms_by_id,
+			      get_rooms_foreach,
 			      &list);
 
+	list = g_list_reverse (list);
+
 	return list;
 }
 
@@ -1542,7 +1602,6 @@
 	GossipJID             *jid = NULL;
 	GossipJabberChatrooms *chatrooms;
 	GossipChatroom        *chatroom = NULL;
-	JabberChatroom        *room = NULL;
 	GList                 *list;
 	gchar                 *server;
 
@@ -1556,14 +1615,12 @@
 
 	if (item) {
 		jid = gossip_jabber_disco_item_get_jid (item);
-		room = g_hash_table_lookup (chatrooms->room_jid_hash, jid);
+		chatroom = g_hash_table_lookup (chatrooms->chatrooms_by_jid, jid);
 	}
 
-	if (room) {
-		chatroom = room->chatroom;
-	} 
-
-	if (!room && !timeout && !error) {
+	if (!chatroom && !timeout && !error) {
+		GossipSession         *session;
+		GossipChatroomManager *chatroom_manager;
 		GossipAccount         *account;
 		const gchar           *server;
 		gchar                 *room;
@@ -1577,11 +1634,13 @@
 		room = gossip_jid_get_part_name (jid);
 
 		/* Create new chatroom */
-		chatroom = g_object_new (GOSSIP_TYPE_CHATROOM,
-					 "account", account,
-					 "server", server,
-					 "room", room,
-					 NULL);
+		session = _gossip_jabber_get_session (chatrooms->jabber);
+		chatroom_manager = gossip_session_get_chatroom_manager (session);
+		chatroom = gossip_chatroom_manager_find_or_create (chatroom_manager, 
+								   account, 
+								   server, 
+								   room,
+								   NULL);
 
 		g_free (room);
 	}
@@ -1696,7 +1755,7 @@
 
 		g_free (server);
 
-		g_free (data);
+		gossip_callback_data_free (data);
 	}
 }
 
@@ -1709,13 +1768,12 @@
 	GossipJabberDisco  *disco;
 	GossipCallbackData *data;
 	
-	data = g_new0 (GossipCallbackData, 1);
+	data = gossip_callback_data_new (callback, 
+					 user_data, 
+					 chatrooms, 
+					 g_strdup (server), 
+					 NULL);
 	
-	data->callback = callback;
-	data->user_data = user_data;
-	data->data1 = chatrooms;
-	data->data2 = g_strdup (server);
-
 	disco = gossip_jabber_disco_request (chatrooms->jabber,
 					     server,
 					     (GossipJabberDiscoItemFunc) 
@@ -1724,15 +1782,20 @@
 }
 
 static void
-jabber_chatrooms_set_presence_foreach (gpointer               key,
-				       JabberChatroom        *room,
-				       GossipJabberChatrooms *chatrooms)
+jabber_chatrooms_set_presence_foreach (gpointer key,
+				       gpointer value,
+				       gpointer user_data)
 {
-	LmConnection   *connection;
-	LmMessage      *m;
-	const gchar    *show;
-	const gchar    *status;
-	GossipPresence *presence;
+	LmConnection          *connection;
+	LmMessage             *m;
+	GossipJabberChatrooms *chatrooms;
+	GossipChatroom        *chatroom;
+	GossipPresence        *presence;
+	const gchar           *show;
+	const gchar           *status;
+
+	chatroom = GOSSIP_CHATROOM (key);
+	chatrooms = (GossipJabberChatrooms *) user_data;
 
 	connection = chatrooms->connection;
 	presence = chatrooms->presence;
@@ -1740,7 +1803,7 @@
 	show = gossip_jabber_presence_state_to_str (presence);
 	status = gossip_presence_get_status (presence);
 
-	m = lm_message_new_with_sub_type (gossip_jid_get_full (room->jid),
+	m = lm_message_new_with_sub_type (gossip_chatroom_get_id_str (chatroom),
 					  LM_MESSAGE_TYPE_PRESENCE,
 					  LM_MESSAGE_SUB_TYPE_AVAILABLE);
 
@@ -1756,104 +1819,6 @@
 	lm_message_unref (m);
 }
 
-static LmMessageNode *
-jabber_chatrooms_find_muc_user_node (LmMessageNode *parent_node)
-{
-	LmMessageNode *child;
-
-	/* Should have a function in Loudmouth to find a child with xmlns */
-	child = parent_node->children;
-
-	if (!child) {
-		return NULL;
-	}
-
-	while (child) {
-		if (strcmp (child->name, "x") == 0) {
-			const gchar *xmlns;
-
-			xmlns = lm_message_node_get_attribute (child, "xmlns");
-
-			if (xmlns && strcmp (xmlns, XMPP_MUC_USER_XMLNS) == 0) {
-				return child;
-			}
-		}
-
-		child = child->next;
-	}
-
-	return NULL;
-}
-
-static GossipChatroomRole
-jabber_chatrooms_get_role (LmMessageNode *muc_node)
-{
-	LmMessageNode *item_node;
-	const gchar   *role;
-
-	if (!muc_node) {
-		return GOSSIP_CHATROOM_ROLE_NONE;
-	}
-
-	item_node = lm_message_node_get_child (muc_node, "item");
-	if (!item_node) {
-		return GOSSIP_CHATROOM_ROLE_NONE;
-	}
-
-	role = lm_message_node_get_attribute (item_node, "role");
-	if (!role) {
-		return GOSSIP_CHATROOM_ROLE_NONE;
-	}
-
-	if (strcmp (role, "moderator") == 0) {
-		return GOSSIP_CHATROOM_ROLE_MODERATOR;
-	}
-	else if (strcmp (role, "participant") == 0) {
-		return GOSSIP_CHATROOM_ROLE_PARTICIPANT;
-	}
-	else if (strcmp (role, "visitor") == 0) {
-		return GOSSIP_CHATROOM_ROLE_VISITOR;
-	} else {
-		return GOSSIP_CHATROOM_ROLE_NONE;
-	}
-}
-
-static GossipChatroomAffiliation
-jabber_chatrooms_get_affiliation (LmMessageNode *muc_node)
-{
-	LmMessageNode *item_node;
-	const gchar   *affiliation;
-
-	if (!muc_node) {
-		return GOSSIP_CHATROOM_AFFILIATION_NONE;
-	}
-
-	item_node = lm_message_node_get_child (muc_node, "item");
-	if (!item_node) {
-		return GOSSIP_CHATROOM_AFFILIATION_NONE;
-	}
-
-	affiliation = lm_message_node_get_attribute (item_node, "affiliation");
-	if (!affiliation) {
-		return GOSSIP_CHATROOM_AFFILIATION_NONE;
-	}
-
-	if (strcmp (affiliation, "owner") == 0) {
-		return GOSSIP_CHATROOM_AFFILIATION_OWNER;
-	}
-	else if (strcmp (affiliation, "admin") == 0) {
-		return GOSSIP_CHATROOM_AFFILIATION_ADMIN;
-	}
-	else if (strcmp (affiliation, "member") == 0) {
-		return GOSSIP_CHATROOM_AFFILIATION_MEMBER;
-	}
-	else if (strcmp (affiliation, "outcast") == 0) {
-		return GOSSIP_CHATROOM_AFFILIATION_OUTCAST;
-	} else {
-		return GOSSIP_CHATROOM_AFFILIATION_NONE;
-	}
-}
-
 void
 gossip_jabber_chatrooms_set_presence (GossipJabberChatrooms  *chatrooms,
 				      GossipPresence         *presence)
@@ -1866,8 +1831,8 @@
 
 	chatrooms->presence = g_object_ref (presence);
 
-	g_hash_table_foreach (chatrooms->room_id_hash,
-			      (GHFunc) jabber_chatrooms_set_presence_foreach,
+	g_hash_table_foreach (chatrooms->chatrooms_by_pointer,
+			      jabber_chatrooms_set_presence_foreach,
 			      chatrooms);
 }
 
@@ -1878,13 +1843,17 @@
 	GossipJID *jid;
 	gboolean   ret_val = FALSE;
 
+	if (!chatrooms->chatrooms_by_jid) {
+		return FALSE;
+	}
+
 	jid = gossip_jid_new (jid_str);
 
-	if (g_hash_table_lookup (chatrooms->room_jid_hash, jid)) {
+	if (g_hash_table_lookup (chatrooms->chatrooms_by_jid, jid)) {
 		ret_val = TRUE;
 	}
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 
 	return ret_val;
 }

Modified: trunk/libgossip/gossip-jabber-disco.c
==============================================================================
--- trunk/libgossip/gossip-jabber-disco.c	(original)
+++ trunk/libgossip/gossip-jabber-disco.c	Sat Jul  5 12:58:07 2008
@@ -157,7 +157,7 @@
 
 	disco->jabber = g_object_ref (jabber);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	handler = lm_message_handler_new (jabber_disco_message_handler, disco, NULL);
 	disco->message_handler = handler;
@@ -180,9 +180,9 @@
 
 	inited = TRUE;
 
-	discos = g_hash_table_new_full (gossip_jid_hash,
-					gossip_jid_equal,
-					(GDestroyNotify) gossip_jid_unref,
+	discos = g_hash_table_new_full (gossip_jid_hash_without_resource,
+					gossip_jid_equal_without_resource,
+					(GDestroyNotify) g_object_unref,
 					(GDestroyNotify) jabber_disco_free);
 }
 
@@ -190,7 +190,7 @@
 jabber_disco_destroy_items_foreach (GossipJabberDiscoItem *item,
 				    gpointer               user_data)
 {
-	gossip_jid_unref (item->jid);
+	g_object_unref (item->jid);
 
 	g_free (item->node);
 	g_free (item->name);
@@ -366,7 +366,7 @@
 
 	if (lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_RESULT &&
 	    lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_ERROR) {
-		gossip_jid_unref (from_jid);
+		g_object_unref (from_jid);
 
 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 
@@ -439,7 +439,7 @@
 		      "disco items to: %s",
 		      gossip_jid_get_full (disco->to));
 
-	connection = gossip_jabber_get_connection (disco->jabber);
+	connection = _gossip_jabber_get_connection (disco->jabber);
 
 	lm_message_node_add_child (m->node, "query", NULL);
 	node = lm_message_node_get_child (m->node, "query");
@@ -557,7 +557,7 @@
 	GList        *l;
 	GossipJID    *jid;
 
-	connection = gossip_jabber_get_connection (disco->jabber);
+	connection = _gossip_jabber_get_connection (disco->jabber);
 	jid = gossip_jid_new ("users.jabber.org");
 
 	disco->items_total = disco->items_remaining = g_list_length (disco->items);
@@ -606,7 +606,7 @@
 		lm_message_unref (m);
 	}
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 }
 
 static void
@@ -812,7 +812,7 @@
 	}
 
 	disco = jabber_disco_new (jabber);
-	g_hash_table_insert (discos, gossip_jid_ref (jid), disco);
+	g_hash_table_insert (discos, g_object_ref (jid), disco);
 
 	disco->to = jid;
 
@@ -848,7 +848,7 @@
 
 	disco->destroying = TRUE;
 
-	connection = gossip_jabber_get_connection (disco->jabber);
+	connection = _gossip_jabber_get_connection (disco->jabber);
 
 	handler = disco->message_handler;
 	if (handler) {
@@ -897,7 +897,7 @@
 	disco = g_hash_table_lookup (discos, jid);
 
 	if (disco) {
-		gossip_jid_unref (jid);
+		g_object_unref (jid);
 		return disco;
 	}
 
@@ -907,7 +907,7 @@
 	disco->jabber = g_object_ref (jabber);
 
 	/* Set up handler */
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	handler = lm_message_handler_new (jabber_disco_message_handler, disco, NULL);
 	disco->message_handler = handler;
@@ -917,7 +917,7 @@
 						LM_HANDLER_PRIORITY_NORMAL);
 
 	/* Add disco and configure members */
-	g_hash_table_insert (discos, gossip_jid_ref (jid), disco);
+	g_hash_table_insert (discos, g_object_ref (jid), disco);
 
 	disco->to = jid;
 
@@ -932,7 +932,7 @@
 	/* Add item */
 	item = g_slice_new0 (GossipJabberDiscoItem);
 
-	item->jid = gossip_jid_ref (jid);
+	item->jid = g_object_ref (jid);
 
 	disco->items = g_list_append (disco->items, item);
 
@@ -991,7 +991,7 @@
 		}
 
 		if (have_category && can_register) {
-			services = g_list_append (services, gossip_jid_ref (item->jid));
+			services = g_list_append (services, g_object_ref (item->jid));
 		}
 	}
 
@@ -1063,7 +1063,7 @@
 		}
 
 		if (have_category && have_type && can_register) {
-			services = g_list_append (services, gossip_jid_ref (item->jid));
+			services = g_list_append (services, g_object_ref (item->jid));
 		}
 	}
 
@@ -1261,7 +1261,7 @@
 
 	g_return_if_fail (GOSSIP_IS_JABBER (jabber));
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_disco_info_handler,
 					  jabber, NULL);
 	lm_connection_register_message_handler (connection,

Modified: trunk/libgossip/gossip-jabber-ft.c
==============================================================================
--- trunk/libgossip/gossip-jabber-ft.c	(original)
+++ trunk/libgossip/gossip-jabber-ft.c	Sat Jul  5 12:58:07 2008
@@ -122,7 +122,7 @@
 
 	gossip_debug (DEBUG_DOMAIN, "Initializing GossipJabberFT");
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	fts = g_new0 (GossipJabberFTs, 1);
 
@@ -213,7 +213,7 @@
 	GHashTable      *jids;
 	gpointer         id_ptr;
 	
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	g_return_val_if_fail (fts, 0);
 
 	jids = g_hash_table_lookup (fts->jid_sids, jid);
@@ -239,7 +239,7 @@
 	GossipFT        *ft;
 	const gchar     *id_str;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	id_str = g_hash_table_lookup (fts->str_ids, GUINT_TO_POINTER (id));
 	ft = g_hash_table_lookup (fts->ft_ids, id_str);
 
@@ -254,7 +254,7 @@
 	GossipJabberFTs *fts;
 	GossipFT        *ft;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	ft = jabber_ft_get_ft_from_id (jabber, id);
  
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Transfer initiated", id);
@@ -269,7 +269,7 @@
 	GossipJabberFTs *fts;
 	GossipFT        *ft;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	ft = jabber_ft_get_ft_from_id (jabber, id);
  
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Transfer complete", id);
@@ -285,7 +285,7 @@
 	GossipJabberFTs *fts;
 	GossipFT        *ft;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	ft = jabber_ft_get_ft_from_id (jabber, id);
 
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Progress: %f %%", id, progress * 100);
@@ -302,7 +302,7 @@
 	GossipFT        *ft;
 	GossipFTError    error_code;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	ft = jabber_ft_get_ft_from_id (jabber, id);
 
 	switch (error->code) {
@@ -387,7 +387,7 @@
 	}
 
 	iq_id = lm_message_node_get_attribute (m->node, "id");
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	lm_bs_session_streamhost_activate (fts->bs_session, iq_id, attr);
 
 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
@@ -438,7 +438,7 @@
 	}
 	
 	iq_id = lm_message_node_get_attribute (m->node, "id");
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	lm_bs_session_set_iq_id (fts->bs_session, id, iq_id);
 
 	/* Get all children named "streamhost" */
@@ -484,7 +484,7 @@
 	guint            id;
 	gchar           *jid_str;
 	
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	g_return_if_fail (fts != NULL);
 
 	file_id = gossip_ft_get_id (ft);
@@ -549,7 +549,7 @@
 	LmMessageNode   *node;
 	const gchar     *attr;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	g_return_val_if_fail (fts != NULL, LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS);
 
 	node = lm_message_node_get_child (m->node, "si");
@@ -691,14 +691,14 @@
 	const gchar     *file_mime;
 	const gchar     *sid;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	g_return_if_fail (fts != NULL);
 
 	from_str = lm_message_node_get_attribute (m->node, "from");
 	id_str = lm_message_node_get_attribute (m->node, "id");
 	from = gossip_jabber_get_contact_from_jid (jabber, 
 						   from_str, 
-						   NULL, 
+						   FALSE,
 						   FALSE,
 						   TRUE);
 
@@ -755,7 +755,7 @@
 	GossipContact   *from;
 	LmMessageNode   *node;
 
-	fts = gossip_jabber_get_fts (jabber);
+	fts = _gossip_jabber_get_fts (jabber);
 	g_return_if_fail (fts != NULL);
 
 	id_str = lm_message_node_get_attribute (m->node, "id");
@@ -771,7 +771,7 @@
 	from_str = lm_message_node_get_attribute (m->node, "from");
 	from = gossip_jabber_get_contact_from_jid (jabber, 
 						   from_str, 
-						   NULL, 
+						   FALSE,
 						   FALSE, 
 						   TRUE);
 
@@ -965,7 +965,7 @@
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 	g_return_val_if_fail (file != NULL, NULL);
 
-	connection = gossip_jabber_get_connection (fts->jabber);
+	connection = _gossip_jabber_get_connection (fts->jabber);
 	own_contact = gossip_jabber_get_own_contact (fts->jabber);
 
 	to = jabber_ft_get_contact_last_jid (contact);
@@ -1096,7 +1096,7 @@
 
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Accepting file transfer", id);
 
-	connection = gossip_jabber_get_connection (fts->jabber);
+	connection = _gossip_jabber_get_connection (fts->jabber);
 	own_contact = gossip_jabber_get_own_contact (fts->jabber);
 	to_str = g_hash_table_lookup (fts->remote_ids, GUINT_TO_POINTER (id));
 	id_str = g_hash_table_lookup (fts->str_ids, GUINT_TO_POINTER (id));
@@ -1177,7 +1177,7 @@
 
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Declining file transfer", id);
 
-	connection = gossip_jabber_get_connection (fts->jabber);
+	connection = _gossip_jabber_get_connection (fts->jabber);
 	own_contact = gossip_jabber_get_own_contact (fts->jabber);
 
 	to_str = g_hash_table_lookup (fts->remote_ids, GUINT_TO_POINTER (id));
@@ -1265,7 +1265,7 @@
 
 	g_return_if_fail (to != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (to,
 					  LM_MESSAGE_TYPE_IQ,
@@ -1294,7 +1294,7 @@
 	g_return_if_fail (to != NULL);
 	g_return_if_fail (id != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (to,
 					  LM_MESSAGE_TYPE_IQ,
@@ -1323,7 +1323,7 @@
 	g_return_if_fail (error_code != NULL);
 	g_return_if_fail (error_type != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (to,
 					  LM_MESSAGE_TYPE_IQ,
@@ -1366,7 +1366,7 @@
 	g_return_if_fail (data != NULL);
 	g_return_if_fail (seq != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new (to, LM_MESSAGE_TYPE_MESSAGE);
 
@@ -1416,7 +1416,7 @@
 	g_return_if_fail (to != NULL);
 	g_return_if_fail (sid != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (to,
 					  LM_MESSAGE_TYPE_IQ,
@@ -1445,7 +1445,7 @@
 	g_return_if_fail (to != NULL);
 	g_return_if_fail (id != NULL);
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (to,
 					  LM_MESSAGE_TYPE_IQ,

Modified: trunk/libgossip/gossip-jabber-private.h
==============================================================================
--- trunk/libgossip/gossip-jabber-private.h	(original)
+++ trunk/libgossip/gossip-jabber-private.h	Sat Jul  5 12:58:07 2008
@@ -23,18 +23,20 @@
 
 #include <loudmouth/loudmouth.h>
 
+#include "gossip-session.h"
 #include "gossip-jabber.h"
 #include "gossip-jabber-ft.h"
 
 G_BEGIN_DECLS
 
-LmConnection *   gossip_jabber_new_connection (GossipJabber  *jabber,
-					       GossipAccount *account);
-gboolean         gossip_jabber_set_connection (LmConnection  *connection,
-					       GossipJabber  *jabber,
-					       GossipAccount *account);
-LmConnection *   gossip_jabber_get_connection (GossipJabber  *jabber);
-GossipJabberFTs *gossip_jabber_get_fts        (GossipJabber  *jabber);
+LmConnection *   _gossip_jabber_new_connection (GossipJabber  *jabber,
+						GossipAccount *account);
+gboolean         _gossip_jabber_set_connection (LmConnection  *connection,
+						GossipJabber  *jabber,
+						GossipAccount *account);
+LmConnection *   _gossip_jabber_get_connection (GossipJabber  *jabber);
+GossipSession *  _gossip_jabber_get_session    (GossipJabber  *jabber);
+GossipJabberFTs *_gossip_jabber_get_fts        (GossipJabber  *jabber);
 
 G_END_DECLS
 

Modified: trunk/libgossip/gossip-jabber-register.c
==============================================================================
--- trunk/libgossip/gossip-jabber-register.c	(original)
+++ trunk/libgossip/gossip-jabber-register.c	Sat Jul  5 12:58:07 2008
@@ -90,7 +90,7 @@
 	
 	rd = g_new0 (RegisterData, 1);
 
-	rd->connection = gossip_jabber_new_connection (jabber, account);
+	rd->connection = _gossip_jabber_new_connection (jabber, account);
 	if (!rd->connection) {
 		g_free (rd);
 		return NULL;

Modified: trunk/libgossip/gossip-jabber-utils.c
==============================================================================
--- trunk/libgossip/gossip-jabber-utils.c	(original)
+++ trunk/libgossip/gossip-jabber-utils.c	Sat Jul  5 12:58:07 2008
@@ -23,6 +23,7 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
 
 #include <glib/gi18n.h>
 
@@ -33,6 +34,7 @@
 #include "gossip-jabber.h"
 #include "gossip-jabber-utils.h"
 #include "gossip-jid.h"
+#include "gossip-utils.h"
 
 #define DEBUG_DOMAIN "JabberUtils"
 
@@ -162,7 +164,7 @@
 	contact_id = lm_message_node_get_attribute (node, "from");
 	contact = gossip_jabber_get_contact_from_jid (jabber,
 						      contact_id,
-						      NULL,
+						      FALSE,
 						      FALSE,
 						      TRUE);
 
@@ -236,31 +238,160 @@
 	return FALSE;
 }
 
+static GArray *
+get_status (LmMessage *m)
+{
+	LmMessageNode *node;
+	GArray        *status = NULL;
+
+	if (!m) {
+		return NULL;
+	}
+
+	node = lm_message_node_get_child (m->node, "x");
+	if (!node) {
+		return NULL;
+	}
+
+	node = node->children;
+
+	while (node) {
+		if (!node) {
+			break;
+		}
+
+		if (node->name && strcmp (node->name, "status") == 0) {
+			const gchar *code;
+
+			code = lm_message_node_get_attribute (node, "code");
+			if (code) {
+				gint value;
+
+				if (!status) {
+					status = g_array_new (FALSE, FALSE, sizeof (gint));
+				}
+
+				value = atoi (code);
+				g_array_append_val (status, value);
+			}
+		}
+
+		node = node->next;
+	}
+
+	return status;
+}
+
+gboolean 
+gossip_jabber_get_message_has_status (LmMessage *m,
+				      gint       code)
+{
+	GArray   *codes;
+	gboolean  found;
+	gint      i;
+
+	g_return_val_if_fail (m != NULL, FALSE);
+	g_return_val_if_fail (code > 0, FALSE);
+
+	codes = get_status (m);
+	if (!codes) {
+		return FALSE;
+	}
+
+	for (i = 0, found = FALSE; i < codes->len && !found; i++) {
+		if (g_array_index (codes, gint, i) == code) {
+			found = TRUE;
+		}
+	}
+
+	g_array_free (codes, TRUE);
+	
+	return found;
+}
+
+gboolean
+gossip_jabber_get_message_is_muc_new_nick (LmMessage  *m, 
+					   gchar     **new_nick)
+{
+	LmMessageNode *node;
+	const gchar   *str;
+
+	g_return_val_if_fail (m != NULL, FALSE);
+
+	if (new_nick) {
+		*new_nick = NULL;
+	}
+
+	/* 303 = nick changed */
+	if (!gossip_jabber_get_message_has_status (m, 303)) {
+		return FALSE;
+	}
+	
+	if (!new_nick) {
+		return TRUE;
+	}
+
+	node = lm_message_get_node (m);
+
+	node = lm_message_node_find_child (node, "x");
+	if (!node) {
+		return FALSE;
+	}
+
+	str = lm_message_node_get_attribute (node, "xmlns");
+	if (!str || strcmp (str, "http://jabber.org/protocol/muc#user";) != 0) {
+		return FALSE;
+	}
+
+	node = lm_message_node_find_child (node, "item");
+	if (!node) {
+		return FALSE;
+	}
+
+	/* Get old nick */
+	str = lm_message_node_get_attribute (node, "nick");
+
+	*new_nick = g_strdup (str);
+
+	return TRUE;
+}
+
 gchar *
 gossip_jabber_get_name_to_use (const gchar *jid_str,
 			       const gchar *nickname,
-			       const gchar *full_name)
+			       const gchar *full_name,
+			       const gchar *current_name)
 {
-	if (nickname && strlen (nickname) > 0) {
+	if (!G_STR_EMPTY (current_name)) {
+		gchar     *part_name;
+		gboolean   use_current_name;
+
+		part_name = gossip_jid_string_get_part_name (jid_str);
+		
+		use_current_name = 
+			g_ascii_strcasecmp (jid_str, current_name) != 0 && 
+			g_ascii_strcasecmp (part_name, current_name) != 0;
+
+		g_free (part_name); 
+
+		if (use_current_name) {
+			return g_strdup (current_name);
+		}
+	}
+
+	if (!G_STR_EMPTY (nickname)) {
 		return g_strdup (nickname);
 	}
 
-	if (full_name && strlen (full_name) > 0) {
+	if (!G_STR_EMPTY (full_name)) {
 		return g_strdup (full_name);
 	}
 
-	if (jid_str && strlen (jid_str) > 0) {
-		GossipJID *jid;
-		gchar     *part_name;
-
-		jid = gossip_jid_new (jid_str);
-		part_name = gossip_jid_get_part_name (jid);
-		gossip_jid_unref (jid);
-
-		return part_name;
+	if (!G_STR_EMPTY (jid_str)) {
+		return gossip_jid_string_get_part_name (jid_str);
 	}
 
-	return "";
+	return g_strdup ("");
 }
 
 GError *

Modified: trunk/libgossip/gossip-jabber-utils.h
==============================================================================
--- trunk/libgossip/gossip-jabber-utils.h	(original)
+++ trunk/libgossip/gossip-jabber-utils.h	Sat Jul  5 12:58:07 2008
@@ -51,33 +51,39 @@
 } GossipJabberAsyncData;
 
 /* Data utils */
-GossipJabberAsyncData *gossip_jabber_async_data_new           (GossipJabber          *jabber,
-							       GossipErrorCallback    callback,
-							       gpointer               user_data);
-void                   gossip_jabber_async_data_free          (GossipJabberAsyncData *ad);
+GossipJabberAsyncData *gossip_jabber_async_data_new              (GossipJabber           *jabber,
+								  GossipErrorCallback     callback,
+								  gpointer                user_data);
+void                   gossip_jabber_async_data_free             (GossipJabberAsyncData  *ad);
 
 /* Presence utils */
-const gchar *          gossip_jabber_presence_state_to_str    (GossipPresence        *presence);
-GossipPresenceState    gossip_jabber_presence_state_from_str  (const gchar           *str);
+const gchar *          gossip_jabber_presence_state_to_str       (GossipPresence         *presence);
+GossipPresenceState    gossip_jabber_presence_state_from_str     (const gchar            *str);
+
 
 /* Message utils */
-GossipTime             gossip_jabber_get_message_timestamp    (LmMessage             *m);
-GossipChatroomInvite * gossip_jabber_get_message_conference   (GossipJabber          *jabber,
-							       LmMessage             *m);
-gboolean               gossip_jabber_get_message_is_event     (LmMessage             *m);
-gboolean               gossip_jabber_get_message_is_composing (LmMessage             *m);
+GossipTime             gossip_jabber_get_message_timestamp       (LmMessage              *m);
+GossipChatroomInvite * gossip_jabber_get_message_conference      (GossipJabber           *jabber,
+								  LmMessage              *m);
+gboolean               gossip_jabber_get_message_is_event        (LmMessage              *m);
+gboolean               gossip_jabber_get_message_is_composing    (LmMessage              *m);
+gboolean               gossip_jabber_get_message_has_status      (LmMessage              *m,
+								  gint                    code);
+gboolean               gossip_jabber_get_message_is_muc_new_nick (LmMessage              *m,
+								  gchar                 **new_nick);
 
 /* Contact utils */
-gchar *                gossip_jabber_get_name_to_use          (const gchar           *jid_str,
-							       const gchar           *nickname,
-							       const gchar           *full_name);
+gchar *                gossip_jabber_get_name_to_use             (const gchar            *jid_str,
+								  const gchar            *nickname,
+								  const gchar            *full_name,
+								  const gchar            *current_name);
 
 /* Error utils */
-GError *               gossip_jabber_error_create             (GossipJabberError      code,
-							       const gchar           *reason);
-void                   gossip_jabber_error                    (GossipJabber          *jabber,
-							       GossipJabberError      code);
-const gchar *          gossip_jabber_error_to_string          (GossipJabberError      error);
+GError *               gossip_jabber_error_create                (GossipJabberError       code,
+								  const gchar            *reason);
+void                   gossip_jabber_error                       (GossipJabber           *jabber,
+								  GossipJabberError       code);
+const gchar *          gossip_jabber_error_to_string             (GossipJabberError       error);
 
 G_END_DECLS
 

Modified: trunk/libgossip/gossip-jabber-vcard.c
==============================================================================
--- trunk/libgossip/gossip-jabber-vcard.c	(original)
+++ trunk/libgossip/gossip-jabber-vcard.c	Sat Jul  5 12:58:07 2008
@@ -232,7 +232,7 @@
 	LmMessageHandler   *handler;
 	GossipCallbackData *data;
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	gossip_debug (DEBUG_DOMAIN, "Requesting VCard, JID:'%s'", jid_str);
 
@@ -315,7 +315,7 @@
 
 	gossip_debug (DEBUG_DOMAIN, "Setting...");
 
-	connection = gossip_jabber_get_connection (jabber);
+	connection = _gossip_jabber_get_connection (jabber);
 
 	m = lm_message_new_with_sub_type (NULL,
 					  LM_MESSAGE_TYPE_IQ,

Modified: trunk/libgossip/gossip-jabber.c
==============================================================================
--- trunk/libgossip/gossip-jabber.c	(original)
+++ trunk/libgossip/gossip-jabber.c	Sat Jul  5 12:58:07 2008
@@ -25,22 +25,21 @@
 
 #include <glib/gi18n.h>
 
-#include <libgossip/gossip-account.h>
-#include <libgossip/gossip-async.h>
-#include <libgossip/gossip-avatar.h>
-#include <libgossip/gossip-contact.h>
-#include <libgossip/gossip-contact-manager.h>
-#include <libgossip/gossip-conf.h>
-#include <libgossip/gossip-chatroom.h>
-#include <libgossip/gossip-debug.h>
-#include <libgossip/gossip-chatroom-provider.h>
-#include <libgossip/gossip-ft.h>
-#include <libgossip/gossip-ft-provider.h>
-#include <libgossip/gossip-message.h>
-#include <libgossip/gossip-session.h>
-#include <libgossip/gossip-utils.h>
-#include <libgossip/gossip-vcard.h>
-#include <libgossip/gossip-version-info.h>
+#include "gossip-jabber.h"
+#include "gossip-account.h"
+#include "gossip-avatar.h"
+#include "gossip-contact.h"
+#include "gossip-contact-manager.h"
+#include "gossip-conf.h"
+#include "gossip-chatroom.h"
+#include "gossip-debug.h"
+#include "gossip-chatroom-provider.h"
+#include "gossip-ft.h"
+#include "gossip-ft-provider.h"
+#include "gossip-utils.h"
+#include "gossip-vcard.h"
+#include "gossip-version-info.h"
+#include "gossip-session.h"
 
 #include "gossip-jid.h"
 #include "gossip-jabber-chatrooms.h"
@@ -53,7 +52,6 @@
 #include "gossip-jabber-utils.h"
 #include "libgossip-marshal.h"
 
-#include "gossip-jabber.h"
 #include "gossip-jabber-private.h"
 #include "gossip-sha.h"
 
@@ -82,16 +80,17 @@
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_JABBER, GossipJabberPriv))
 
 struct _GossipJabberPriv {
+	GossipSession         *session;
+
 	LmConnection          *connection;
 	LmSSLStatus            ssl_status;
 	gboolean               ssl_disconnection;
 
-	GossipContact         *contact;
 	GossipAccount         *account;
 	GossipPresence        *presence;
 	GossipVCard           *vcard;
 
-	GHashTable            *contacts;
+	GHashTable            *contact_list;
 
 	/* Cancel registration attempt */
 	gboolean               register_cancel;
@@ -138,8 +137,8 @@
 static void             jabber_finalize                     (GObject                    *object);
 static gboolean         jabber_login_timeout_cb             (GossipJabber               *jabber);
 static gboolean         jabber_logout_contact_foreach       (gpointer                    key,
-							     GossipContact              *contact,
-							     GossipJabber               *jabber);
+							     gpointer                    value,
+							     gpointer                    user_data);
 static void             jabber_connected_cb                 (LmConnection               *connection,
 							     gboolean                    result,
 						 	     GossipJabber               *jabber);
@@ -165,13 +164,13 @@
 static void             jabber_contact_vcard_cb             (GossipResult                result,
 							     GossipVCard                *vcard,
 							     JabberData                 *data);
-static void             jabber_group_rename_foreach_cb      (const gchar                *jid,
-							     GossipContact              *contact,
-							     RenameGroupData            *rg);
+static void             jabber_group_rename_foreach_cb      (gpointer                    key,
+							     gpointer                    value,
+							     gpointer                    user_data);
 static GossipPresence * jabber_get_presence                 (LmMessage                  *message);
-static void             jabber_get_groups_foreach_cb        (const gchar                *jid,
-							     GossipContact              *contact,
-							     GList                     **list);
+static void             jabber_get_groups_foreach_cb        (gpointer                    key,
+							     gpointer                    value,
+							     gpointer                    user_data);
 static LmHandlerResult  jabber_message_handler              (LmMessageHandler           *handler,
 							     LmConnection               *conn,
 							     LmMessage                  *message,
@@ -220,8 +219,6 @@
 							     GossipChatroomId            id,
 							     GossipContact              *contact,
 							     const gchar                *reason);
-static GSList *         jabber_chatroom_get_contacts        (GossipChatroomProvider     *provider,
-							     GossipChatroomId            id);
 static GossipChatroom * jabber_chatroom_find_by_id          (GossipChatroomProvider     *provider,
 							     GossipChatroomId            id);
 static GossipChatroom * jabber_chatroom_find                (GossipChatroomProvider     *provider,
@@ -259,7 +256,7 @@
 static JabberData *     jabber_data_new                     (GossipJabber               *jabber,
 							     GossipContact              *contact,
 							     gpointer                    user_data);
-static void             jabber_data_free                    (JabberData                 *data);
+static void             jabber_data_free                    (gpointer                    data);
 
 static const gchar *server_conversions[] = {
 	"gmail.com", "talk.google.com",
@@ -423,28 +420,28 @@
 
 	priv = GET_PRIV (jabber);
 
-	priv->contacts =
-		g_hash_table_new_full (g_str_hash,
-				       g_str_equal,
-				       (GDestroyNotify) g_free,
-				       (GDestroyNotify) g_object_unref);
+	priv->contact_list = 
+		g_hash_table_new_full (gossip_contact_hash,
+				       gossip_contact_equal,
+				       g_object_unref,
+				       NULL);
 
 	priv->composing_ids =
 		g_hash_table_new_full (gossip_contact_hash,
 				       gossip_contact_equal,
-				       (GDestroyNotify) g_object_unref,
-				       (GDestroyNotify) g_free);
+				       g_object_unref,
+				       g_free);
 
 	priv->composing_timeouts =
 		g_hash_table_new_full (gossip_contact_hash,
 				       gossip_contact_equal,
-				       (GDestroyNotify) g_object_unref,
-				       (GDestroyNotify) jabber_data_free);
+				       g_object_unref,
+				       jabber_data_free);
 
 	priv->composing_requests =
 		g_hash_table_new_full (gossip_contact_hash,
 				       gossip_contact_equal,
-				       (GDestroyNotify) g_object_unref,
+				       g_object_unref,
 				       NULL);
 }
 
@@ -461,10 +458,6 @@
 		g_object_unref (priv->account);
 	}
 
-	if (priv->contact) {
-		g_object_unref (priv->contact);
-	}
-
 	if (priv->vcard) {
 		g_object_unref (priv->vcard);
 	}
@@ -473,7 +466,11 @@
 		g_object_unref (priv->presence);
 	}
 
-	g_hash_table_destroy (priv->contacts);
+	if (priv->session) {
+		g_object_unref (priv->session);
+	}
+
+	g_hash_table_unref (priv->contact_list);
 
 	/* finalize extended modules */
 	gossip_jabber_chatrooms_finalize (priv->chatrooms);
@@ -500,6 +497,22 @@
 	(G_OBJECT_CLASS (gossip_jabber_parent_class)->finalize) (object);
 }
 
+GossipJabber *
+gossip_jabber_new (gpointer session)
+{
+	GossipJabber     *jabber;
+	GossipJabberPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
+
+	jabber = g_object_new (GOSSIP_TYPE_JABBER, NULL);
+	
+	priv = GET_PRIV (jabber);
+	priv->session = g_object_ref (session);
+
+	return jabber;
+}
+
 GossipAccount *
 gossip_jabber_new_account (void)
 {
@@ -527,40 +540,17 @@
 				"use_ssl", gossip_jabber_is_ssl_supported (),
 				NULL);
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 
 	return account;
 }
 
-GossipContact *
-gossip_jabber_new_contact (GossipJabber *jabber,
-			   const gchar  *id,
-			   const gchar  *name)
-{
-	GossipContact *contact;
-	gboolean       new_contact;
-
-	g_return_val_if_fail (GOSSIP_IS_JABBER (jabber), NULL);
-
-	contact = gossip_jabber_get_contact_from_jid (jabber,
-						      id,
-						      &new_contact,
-						      FALSE,
-						      FALSE);
-	if (new_contact && !G_STR_EMPTY (name)) {
-		gossip_contact_set_name (contact, name);
-	}
-
-	return contact;
-}
-
 void
 gossip_jabber_setup (GossipJabber  *jabber,
 		     GossipAccount *account)
 {
 	GossipJabberPriv *priv;
 	LmMessageHandler *handler;
-	const gchar      *server;
 
 	g_return_if_fail (GOSSIP_IS_JABBER (jabber));
 	g_return_if_fail (GOSSIP_IS_ACCOUNT (account));
@@ -568,22 +558,20 @@
 	priv = GET_PRIV (jabber);
 	
 	priv->account = g_object_ref (account);
-	priv->contact = gossip_contact_new (GOSSIP_CONTACT_TYPE_USER,
-					    priv->account);
 
-	/* Store the password for the next connection */
+	/* Update the connection details */
+	priv->connection = _gossip_jabber_new_connection (jabber, account);
 
-	server = gossip_account_get_server (priv->account);
-
-	priv->connection = gossip_jabber_new_connection (jabber, account);
-
-	/* setup the connection to send keep alive messages every 30 seconds */
+	/* Setup the connection to send keep alive messages every 30
+	 * seconds.
+	 */
 	lm_connection_set_keep_alive_rate (priv->connection, 30);
 
 	lm_connection_set_disconnect_function (priv->connection,
 					       (LmDisconnectFunction) jabber_disconnected_cb,
 					       jabber, NULL);
 
+	/* Set up handlers for messages and presence */
 	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_message_handler,
 					  jabber, NULL);
 	lm_connection_register_message_handler (priv->connection,
@@ -608,7 +596,7 @@
 						LM_HANDLER_PRIORITY_NORMAL);
 	lm_message_handler_unref (handler);
 
-	/* initiate extended modules */
+	/* Initiate extended modules */
 	priv->chatrooms = gossip_jabber_chatrooms_init (jabber);
 	priv->fts = gossip_jabber_ft_init (jabber);
 	gossip_jabber_disco_init (jabber);
@@ -624,6 +612,7 @@
 gossip_jabber_login (GossipJabber *jabber)
 {
 	GossipJabberPriv *priv;
+	GossipContact    *own_contact;
 	const gchar      *id;
 	const gchar      *password;
 	GError           *error = NULL;
@@ -635,14 +624,20 @@
 
 	gossip_debug (DEBUG_DOMAIN, "Refreshing connection details");
 
-	gossip_jabber_set_connection (priv->connection, 
-				      jabber,
-				      priv->account);
+	own_contact = gossip_jabber_get_contact_from_jid (jabber,
+							  gossip_account_get_id (priv->account),
+							  TRUE,
+							  FALSE,
+							  FALSE);
+
+	_gossip_jabber_set_connection (priv->connection, 
+				       jabber,
+				       priv->account);
 
 	/* Update connection details and own contact information */
 	id = gossip_account_get_id (priv->account);
-	gossip_contact_set_id (priv->contact, id);
-
+	gossip_contact_set_id (own_contact, id);
+	
 	/* Check the saved password */
 	password = gossip_account_get_password (priv->account);
 	if (G_STR_EMPTY (password)) {
@@ -772,33 +767,21 @@
 }
 
 static gboolean
-jabber_logout_contact_foreach (gpointer       key,
-			       GossipContact *contact,
-			       GossipJabber  *jabber)
+jabber_logout_contact_foreach (gpointer key,
+			       gpointer value,
+			       gpointer user_data)
 {
-	GList          *presences;
-	GList          *l;
-	GossipPresence *presence;
+	GossipContact *contact;
+	GossipJabber  *jabber;
 
-	/* Set each contact to be offline, since they effectively are
-	 * now we don't know.
-	 */
-	presences = g_list_copy (gossip_contact_get_presence_list (contact));
+	contact = GOSSIP_CONTACT (key);
+	jabber = GOSSIP_JABBER (user_data);
 
 	/* Copy the list since it will be modified during traversal
 	 * otherwise.
 	 */
-	for (l = presences; l; l = l->next) {
-		presence = l->data;
-
-		if (!presence) {
-			continue;
-		}
-
-		gossip_contact_remove_presence (contact, presence);
-	}
 
-	g_list_free (presences);
+	gossip_contact_set_presence_list (contact, NULL);
 
 	g_signal_emit_by_name (jabber, "contact-removed", contact);
 
@@ -992,6 +975,7 @@
 		GossipJabber *jabber)
 {
 	GossipJabberPriv *priv;
+	GossipContact    *own_contact;
 	LmMessage        *m;
 	LmMessageNode    *node;
 
@@ -1025,15 +1009,22 @@
 
 	g_signal_emit_by_name (jabber, "connected", priv->account);
 
+	own_contact = gossip_jabber_get_contact_from_jid (jabber,
+							  gossip_account_get_id (priv->account),
+							  TRUE,
+							  FALSE,
+							  FALSE);
+
 	if (priv->vcard) {
 		gchar *name;
 
 		name = gossip_jabber_get_name_to_use
-			(gossip_contact_get_id (priv->contact),
+			(gossip_contact_get_id (own_contact),
 			 gossip_vcard_get_nickname (priv->vcard),
-			 gossip_vcard_get_name (priv->vcard));
+			 gossip_vcard_get_name (priv->vcard), 
+			 gossip_contact_get_name (own_contact));
 
-		gossip_contact_set_name (priv->contact, name);
+		gossip_contact_set_name (own_contact, name);
 		g_free (name);
 
 		/* Set the vcard waiting to be sent to our jabber server once
@@ -1049,7 +1040,7 @@
 		/* Request our vcard so we know what our nick name is to use
 		 * in chats windows, etc.
 		 */
-		jabber_contact_vcard (jabber, priv->contact);
+		jabber_contact_vcard (jabber, own_contact);
 	}
 }
 
@@ -1069,9 +1060,9 @@
 	}
 
 	/* Signal removal of each contact */
-	if (priv->contacts) {
-		g_hash_table_foreach_remove (priv->contacts,
-					     (GHRFunc) jabber_logout_contact_foreach,
+	if (priv->contact_list) {
+		g_hash_table_foreach_remove (priv->contact_list,
+					     jabber_logout_contact_foreach,
 					     jabber);
 	}
 
@@ -1227,7 +1218,7 @@
         lm_message_node_add_child (node, "username", gossip_jid_get_part_name (jid));
         lm_message_node_add_child (node, "password", new_password);
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 
 	ad = gossip_jabber_async_data_new (jabber, callback, user_data);
 	ad->message_handler = lm_message_handler_new ((LmHandleMessageFunction)
@@ -1371,7 +1362,7 @@
 	}
 
 	server = g_strdup (str);
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 
 	return server;
 }
@@ -1395,7 +1386,6 @@
 	const gchar      *recipient_id;
 	const gchar      *resource;
 	gchar            *jid_str;
-	gboolean          new_contact;
 
 	g_return_if_fail (GOSSIP_IS_JABBER (jabber));
 
@@ -1406,17 +1396,11 @@
 	recipient_id = gossip_contact_get_id (recipient);
 	resource = gossip_message_get_explicit_resource (message);
 
-	/* Getting contact from JID, this will add them to our
-	 * contacts hash table if they don't exist too.
-	 */
 	recipient = gossip_jabber_get_contact_from_jid (jabber,
 							recipient_id,
-							&new_contact,
+							FALSE,
 							FALSE,
 							TRUE);
-	if (new_contact) {
-		g_object_unref (recipient);
-	}
 
 	if (resource && g_utf8_strlen (resource, -1) > 0) {
 		jid_str = g_strdup_printf ("%s/%s", recipient_id, resource);
@@ -1638,26 +1622,6 @@
 					error);
 }
 
-GossipContact *
-gossip_jabber_find_contact (GossipJabber *jabber, const gchar *id)
-{
-	GossipJabberPriv *priv;
-	GossipJID        *jid;
-	GossipContact    *contact;
-
-	g_return_val_if_fail (GOSSIP_IS_JABBER (jabber), NULL);
-
-	priv = GET_PRIV (jabber);
-
-	jid = gossip_jid_new (id);
-	contact = g_hash_table_lookup (priv->contacts,
-				       gossip_jid_get_without_resource (jid));
-
-	gossip_jid_unref (jid);
-
-	return contact;
-}
-
 void
 gossip_jabber_add_contact (GossipJabber *jabber,
 			   const gchar  *id,
@@ -1682,7 +1646,12 @@
 	priv = GET_PRIV (jabber);
 
 	jid = gossip_jid_new (id);
-	contact = g_hash_table_lookup (priv->contacts, gossip_jid_get_without_resource (jid));
+
+	contact = gossip_jabber_get_contact_from_jid (jabber, 
+						      gossip_jid_get_without_resource (jid),
+						      FALSE,
+						      FALSE,
+						      FALSE);
 
 	if (contact) {
 		subscription = gossip_contact_get_subscription (contact);
@@ -1695,7 +1664,6 @@
 	 * and it makes sense to use our provided name/group, etc
 	 */
 	add_to_roster = TRUE;
-/* 	add_to_roster = gossip_contact_get_type (contact) == GOSSIP_CONTACT_TYPE_TEMPORARY; */
 
 	if (add_to_roster) {
 		gossip_debug (DEBUG_DOMAIN, "Adding contact:'%s' to roster...", id);
@@ -1748,7 +1716,7 @@
 			      "subscription is either TO or BOTH");
 	}
 
-	gossip_jid_unref (jid);
+	g_object_unref (jid);
 }
 
 void
@@ -1829,8 +1797,6 @@
 	gossip_debug (DEBUG_DOMAIN,
 		      "Contact:'%s' being removed with current ref count:%d",
 		      gossip_contact_get_id (contact), G_OBJECT (contact)->ref_count);
-
-	g_hash_table_remove (priv->contacts, contact);
 }
 
 void
@@ -1919,7 +1885,8 @@
 		name = gossip_jabber_get_name_to_use
 			(gossip_contact_get_id (data->contact),
 			 gossip_vcard_get_nickname (vcard),
-			 gossip_vcard_get_name (vcard));
+			 gossip_vcard_get_name (vcard),
+			 gossip_contact_get_name (data->contact));
 
 		gossip_contact_set_name (data->contact, name);
 		g_free (name);
@@ -1951,44 +1918,23 @@
 				 NULL);
 }
 
-void
-gossip_jabber_rename_group (GossipJabber *jabber,
-			    const gchar  *group,
-			    const gchar  *new_name)
-{
-	GossipJabberPriv *priv;
-
-	RenameGroupData  *rg;
-
-	priv = GET_PRIV (jabber);
-
-	rg = g_new0 (RenameGroupData, 1);
-
-	rg->jabber = jabber;
-	rg->group = g_strdup (group);
-	rg->new_name = g_strdup (new_name);
-
-	g_hash_table_foreach (priv->contacts,
-			      (GHFunc)jabber_group_rename_foreach_cb,
-			      rg);
-
-	g_free (rg->group);
-	g_free (rg->new_name);
-	g_free (rg);
-}
-
 static void
-jabber_group_rename_foreach_cb (const gchar     *jid,
-				GossipContact   *contact,
-				RenameGroupData *rg)
+jabber_group_rename_foreach_cb (gpointer key,
+				gpointer value,
+				gpointer user_data)
 {
 	GossipJabberPriv *priv;
+	GossipContact    *contact;
+	RenameGroupData  *rg;
 	LmMessage        *m;
 	LmMessageNode    *node;
 	gchar            *escaped;
 	GList            *l;
 	gboolean          found = FALSE;
 
+	contact = GOSSIP_CONTACT (key);
+	rg = (RenameGroupData *) user_data;
+
 	priv = GET_PRIV (rg->jabber);
 
 	for (l = gossip_contact_get_groups (contact); l && !found; l = l->next) {
@@ -2042,6 +1988,32 @@
 	lm_message_unref (m);
 }
 
+void
+gossip_jabber_rename_group (GossipJabber *jabber,
+			    const gchar  *group,
+			    const gchar  *new_name)
+{
+	GossipJabberPriv *priv;
+
+	RenameGroupData  *rg;
+
+	priv = GET_PRIV (jabber);
+
+	rg = g_new0 (RenameGroupData, 1);
+
+	rg->jabber = jabber;
+	rg->group = g_strdup (group);
+	rg->new_name = g_strdup (new_name);
+
+	g_hash_table_foreach (priv->contact_list,
+			      (GHFunc) jabber_group_rename_foreach_cb,
+			      rg);
+
+	g_free (rg->group);
+	g_free (rg->new_name);
+	g_free (rg);
+}
+
 static GossipPresence *
 jabber_get_presence (LmMessage *m)
 {
@@ -2099,29 +2071,17 @@
 	return gossip_presence_get_resource (presence);
 }
 
-GList *
-gossip_jabber_get_groups (GossipJabber *jabber)
-{
-	GossipJabberPriv *priv;
-	GList            *list = NULL;
-
-	priv = GET_PRIV (jabber);
-
-	g_hash_table_foreach (priv->contacts,
-			      (GHFunc)jabber_get_groups_foreach_cb,
-			      &list);
-
-	list = g_list_sort (list, (GCompareFunc)strcmp);
-
-	return list;
-}
-
 static void
-jabber_get_groups_foreach_cb (const gchar    *jid,
-			      GossipContact  *contact,
-			      GList         **list)
+jabber_get_groups_foreach_cb (gpointer key,
+			      gpointer value,
+			      gpointer user_data)
 {
-	GList *l;
+	GossipContact  *contact;
+	GList         **list;
+	GList          *l;
+	
+	contact = GOSSIP_CONTACT (key);
+	list = (GList **) user_data;
 
 	if (!gossip_contact_get_groups (contact)) {
 		return;
@@ -2142,6 +2102,23 @@
 	}
 }
 
+GList *
+gossip_jabber_get_groups (GossipJabber *jabber)
+{
+	GossipJabberPriv *priv;
+	GList            *list = NULL;
+
+	priv = GET_PRIV (jabber);
+
+	g_hash_table_foreach (priv->contact_list,
+			      (GHFunc) jabber_get_groups_foreach_cb,
+			      &list);
+
+	list = g_list_sort (list, (GCompareFunc)strcmp);
+
+	return list;
+}
+
 gboolean
 gossip_jabber_get_vcard (GossipJabber         *jabber,
 			 GossipContact        *contact,
@@ -2157,7 +2134,15 @@
 	if (contact) {
 		jid_str = gossip_contact_get_id (contact);
 	} else {
-		jid_str = gossip_contact_get_id (priv->contact);
+		GossipContact *own_contact;
+
+		own_contact = gossip_jabber_get_contact_from_jid (jabber,
+								  gossip_account_get_id (priv->account),
+								  TRUE,
+								  FALSE,
+								  FALSE);
+
+		jid_str = gossip_contact_get_id (own_contact);
 	}
 
 	return gossip_jabber_vcard_get (jabber,
@@ -2211,6 +2196,7 @@
 	GossipMessage        *message;
 	const gchar          *from_str;
 	GossipContact        *from;
+	GossipContact        *own_contact;
 	const gchar          *thread = NULL;
 	const gchar          *subject = NULL;
 	const gchar          *body = NULL;
@@ -2231,9 +2217,10 @@
 	}
 
 	from_str = lm_message_node_get_attribute (m->node, "from");
+
 	from = gossip_jabber_get_contact_from_jid (jabber,
 						   from_str,
-						   NULL,
+						   FALSE,
 						   FALSE,
 						   TRUE);
 
@@ -2301,12 +2288,12 @@
 
 	invite = gossip_jabber_get_message_conference (jabber, m);
 	if (invite) {
-		GossipContact *invitor;
+		GossipContact *inviter;
 
-		invitor = gossip_chatroom_invite_get_invitor (invite);
+		inviter = gossip_chatroom_invite_get_inviter (invite);
 
 		gossip_debug (DEBUG_DOMAIN, "Chat room invitiation from:'%s' for room:'%s', reason:'%s'",
-			      gossip_contact_get_id (invitor),
+			      gossip_contact_get_id (inviter),
 			      gossip_chatroom_invite_get_id (invite),
 			      gossip_chatroom_invite_get_reason (invite));
 
@@ -2314,7 +2301,7 @@
 		 * not the chatroom that sent the request on their
 		 * behalf.
 		 */
-		from = invitor;
+		from = inviter;
 
 		/* Make sure we have some sort of body for
 		 * invitations, since it is not necessary but should
@@ -2342,8 +2329,13 @@
 		thread = node->value;
 	}
 
-	message = gossip_message_new (GOSSIP_MESSAGE_TYPE_NORMAL,
-				      priv->contact);
+	own_contact = gossip_jabber_get_contact_from_jid (jabber,
+							  gossip_account_get_id (priv->account),
+							  TRUE,
+							  FALSE,
+							  FALSE);
+
+	message = gossip_message_new (GOSSIP_MESSAGE_TYPE_NORMAL, own_contact);
 
 	/* To make the sender right in private chat messages sent from
 	 * groupchats, we take the name from the resource, which carries the
@@ -2362,7 +2354,7 @@
 
 		gossip_contact_set_name (from, resource);
 
-		gossip_jid_unref (jid);
+		g_object_unref (jid);
 	}
 
 	gossip_message_set_sender (message, from);
@@ -2405,22 +2397,21 @@
 	GossipContact    *contact;
 	const gchar      *from;
 	const gchar      *type;
-	gboolean          new_item = FALSE;
 
 	priv = GET_PRIV (jabber);
 
 	from = lm_message_node_get_attribute (m->node, "from");
-	gossip_debug (DEBUG_DOMAIN, "New presence from:'%s'",
-		      lm_message_node_get_attribute (m->node, "from"));
 
-	if (gossip_jabber_chatrooms_get_jid_is_chatroom (priv->chatrooms,
-							 from)) {
+	if (gossip_jabber_chatrooms_get_jid_is_chatroom (priv->chatrooms, from)) {
 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 	}
 
+	gossip_debug (DEBUG_DOMAIN, "New presence from:'%s'",
+		      lm_message_node_get_attribute (m->node, "from"));
+
 	contact = gossip_jabber_get_contact_from_jid (jabber, 
 						      from, 
-						      &new_item, 
+						      FALSE,
 						      FALSE, 
 						      TRUE);
 
@@ -2484,7 +2475,7 @@
 			g_object_unref (presence);
 		}
 
-		gossip_jid_unref (jid);
+		g_object_unref (jid);
 	}
 
 	return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
@@ -2722,10 +2713,16 @@
 
 		contact = gossip_jabber_get_contact_from_jid (jabber,
 							      jid_str,
-							      &added_item,
+							      FALSE,
 							      TRUE,
 							      FALSE);
 
+		if (!g_hash_table_lookup (priv->contact_list, contact)) {
+			g_hash_table_insert (priv->contact_list, 
+					     g_object_ref (contact),
+					     GINT_TO_POINTER (1));
+		}
+
 		type = gossip_contact_get_type (contact);
 
 		/* Subscription */
@@ -2735,7 +2732,7 @@
 
 			if (strcmp (subscription, "remove") == 0) {
 				g_signal_emit_by_name (jabber, "contact-removed", contact);
-				g_hash_table_remove (priv->contacts, gossip_contact_get_id (contact));
+				g_hash_table_remove (priv->contact_list, contact);
 				continue;
 			} else if (strcmp (subscription, "both") == 0) {
 				subscription_type = GOSSIP_SUBSCRIPTION_BOTH;
@@ -2848,7 +2845,6 @@
 	iface->change_nick     = jabber_chatroom_change_nick;
 	iface->leave           = jabber_chatroom_leave;
 	iface->kick            = jabber_chatroom_kick;
-	iface->get_contacts    = jabber_chatroom_get_contacts;
 	iface->find_by_id      = jabber_chatroom_find_by_id;
 	iface->find            = jabber_chatroom_find;
 	iface->invite          = jabber_chatroom_invite;
@@ -2974,21 +2970,6 @@
 	gossip_jabber_chatrooms_kick (priv->chatrooms, id, contact, reason);
 }
 
-static GSList *
-jabber_chatroom_get_contacts (GossipChatroomProvider *provider,
-			      GossipChatroomId        id)
-{
-	GossipJabber     *jabber;
-	GossipJabberPriv *priv;
-
-	g_return_val_if_fail (GOSSIP_IS_JABBER (provider), NULL);
-
-	jabber = GOSSIP_JABBER (provider);
-	priv = GET_PRIV (jabber);
-
-	return gossip_jabber_chatrooms_get_contacts (priv->chatrooms, id);
-}
-
 static GossipChatroom *
 jabber_chatroom_find_by_id (GossipChatroomProvider *provider,
 			    GossipChatroomId        id)
@@ -3214,21 +3195,25 @@
 }
 
 static void
-jabber_data_free (JabberData *data)
+jabber_data_free (gpointer data)
 {
+	JabberData *jd;
+
 	if (!data) {
 		return;
 	}
 
-	if (data->jabber) {
-		g_object_unref (data->jabber);
+	jd = (JabberData *) data;
+
+	if (jd->jabber) {
+		g_object_unref (jd->jabber);
 	}
 
-	if (data->contact) {
-		g_object_unref (data->contact);
+	if (jd->contact) {
+		g_object_unref (jd->contact);
 	}
 
-	g_slice_free (JabberData, data);
+	g_slice_free (JabberData, jd);
 }
 
 /*
@@ -3257,81 +3242,97 @@
 gossip_jabber_get_own_contact (GossipJabber *jabber)
 {
 	GossipJabberPriv *priv;
+	GossipContact    *own_contact;
 
 	g_return_val_if_fail (GOSSIP_IS_JABBER (jabber), NULL);
 
 	priv = GET_PRIV (jabber);
 
-	return priv->contact;
+	own_contact = gossip_jabber_get_contact_from_jid (jabber,
+							  gossip_account_get_id (priv->account),
+							  TRUE,
+							  FALSE,
+							  FALSE);
+
+	return own_contact;
 }
 
 GossipContact *
 gossip_jabber_get_contact_from_jid (GossipJabber *jabber,
 				    const gchar  *jid_str,
-				    gboolean     *new_item,
+				    gboolean      own_contact,
 				    gboolean      set_permanent,
 				    gboolean      get_vcard)
 {
-	GossipJabberPriv  *priv;
-	GossipContact     *contact;
-	GossipContactType  type;
-	GossipJID         *jid;
-	gboolean           tmp_new_item = FALSE;
+	GossipJabberPriv     *priv;
+	GossipContact        *contact;
+	GossipContactManager *contact_manager;
+	GossipContactType     type;
+	GossipJID            *jid;
+	gboolean              is_chatroom;
+	gboolean              created;
 
 	priv = GET_PRIV (jabber);
 
+	contact_manager = gossip_session_get_contact_manager (priv->session);
+
 	jid = gossip_jid_new (jid_str);
+	is_chatroom = gossip_jabber_chatrooms_get_jid_is_chatroom (priv->chatrooms, jid_str);
+
+	if (is_chatroom) {
+		const gchar *resource;
+
+		resource = gossip_jid_get_resource (jid);
+
+		/* If there is no resource, this is the chatroom JID
+		 * itself, it isn't a contact. So we don't set the
+		 * name. Should we return NULL here as the contact?
+		 */
+		if (!G_STR_EMPTY (resource)) {
+			type = GOSSIP_CONTACT_TYPE_CHATROOM;
 
-	contact = g_hash_table_lookup (priv->contacts,
-				       gossip_jid_get_without_resource (jid));
+			contact = gossip_contact_manager_find_or_create (contact_manager,
+									 priv->account,
+									 type,
+									 gossip_jid_get_full (jid),
+									 &created);
+			gossip_contact_set_name (contact, resource);
 
-	if (!contact) {
-		if (set_permanent) {
+			if (!created && set_permanent) {
+				gossip_contact_set_type (contact, type);
+			}
+		} else {
+			contact = NULL;
+		}			
+	} else {
+		if (own_contact) {
+			type = GOSSIP_CONTACT_TYPE_USER;
+		} else if (set_permanent) {
 			type = GOSSIP_CONTACT_TYPE_CONTACTLIST;
 		} else {
 			type = GOSSIP_CONTACT_TYPE_TEMPORARY;
 		}
 
-		gossip_debug (DEBUG_DOMAIN,
-			      "New contact:'%s' (%s)",
-			      gossip_jid_get_full (jid),
-			      gossip_contact_type_to_string (type));
-
-		contact = gossip_contact_new (type, priv->account);
-		gossip_contact_set_id (contact, gossip_jid_get_full (jid));
-		gossip_contact_set_name (contact, gossip_jid_get_without_resource (jid));
-
-		tmp_new_item = TRUE;
-
-		g_hash_table_insert (priv->contacts,
-				     g_strdup (gossip_jid_get_without_resource (jid)),
-				     contact);
+		contact = gossip_contact_manager_find_or_create (contact_manager,
+								 priv->account,
+								 type,
+								 gossip_jid_get_without_resource (jid),
+								 &created);
 
+		if (!created && set_permanent) {
+			gossip_contact_set_type (contact, type);
+		}
+		
+		/* Don't get vcards for chatroom contacts */
 		if (get_vcard) {
 			/* Request contacts VCard details so we can get the
 			 * real name for them for chat windows, etc
 			 */
 			jabber_contact_vcard (jabber, contact);
 		}
-	} else {
-		if (set_permanent) {
-			gossip_contact_set_type (contact, GOSSIP_CONTACT_TYPE_CONTACTLIST);
-		}
-
-		type = gossip_contact_get_type (contact);
-
-		gossip_debug (DEBUG_DOMAIN,
-			      "Get contact:'%s', type:%d-->'%s'",
-			      gossip_jid_get_full (jid), 
-			      type, 
-			      gossip_contact_type_to_string (type));
 	}
 
-	gossip_jid_unref (jid);
-
-	if (new_item) {
-		*new_item = tmp_new_item;
-	}
+	g_object_unref (jid);
 
 	return contact;
 }
@@ -3449,9 +3450,9 @@
  */
 
 gboolean
-gossip_jabber_set_connection (LmConnection  *connection, 
-			      GossipJabber  *jabber,
-			      GossipAccount *account)
+_gossip_jabber_set_connection (LmConnection  *connection, 
+			       GossipJabber  *jabber,
+			       GossipAccount *account)
 {
 	GossipJID    *jid;
 	const gchar  *id;
@@ -3472,7 +3473,7 @@
 		gossip_debug (DEBUG_DOMAIN, "- ID:'%s'", id);
 		jid = gossip_jid_new (id);
 		lm_connection_set_jid (connection, gossip_jid_get_without_resource (jid));
-		gossip_jid_unref (jid);
+		g_object_unref (jid);
 	}
 
 
@@ -3569,7 +3570,7 @@
 }
 
 LmConnection *
-gossip_jabber_new_connection (GossipJabber  *jabber,
+_gossip_jabber_new_connection (GossipJabber  *jabber,
 			      GossipAccount *account)
 {
 	LmConnection *connection;
@@ -3579,13 +3580,13 @@
 
 	server = gossip_account_get_server (account);
 	connection = lm_connection_new (server);
- 	gossip_jabber_set_connection (connection, jabber, account);
+ 	_gossip_jabber_set_connection (connection, jabber, account);
 
 	return connection;
 }
 
 LmConnection *
-gossip_jabber_get_connection (GossipJabber *jabber)
+_gossip_jabber_get_connection (GossipJabber *jabber)
 {
 	GossipJabberPriv *priv;
 
@@ -3596,8 +3597,18 @@
 	return priv->connection;
 }
 
+GossipSession *
+_gossip_jabber_get_session (GossipJabber *jabber)
+{
+	GossipJabberPriv *priv;
+
+	priv = GET_PRIV (jabber);
+
+	return priv->session;
+}
+
 GossipJabberFTs *
-gossip_jabber_get_fts (GossipJabber *jabber)
+_gossip_jabber_get_fts (GossipJabber *jabber)
 {
 	GossipJabberPriv *priv;
 
@@ -3607,4 +3618,3 @@
 
 	return priv->fts;
 }
-

Modified: trunk/libgossip/gossip-jabber.h
==============================================================================
--- trunk/libgossip/gossip-jabber.h	(original)
+++ trunk/libgossip/gossip-jabber.h	Sat Jul  5 12:58:07 2008
@@ -22,6 +22,7 @@
 #define __GOSSIP_JABBER_H__
 
 #include <glib-object.h>
+
 #include "gossip-async.h"
 #include "gossip-message.h"
 
@@ -57,6 +58,8 @@
 
 GQuark         gossip_jabber_error_quark               (void) G_GNUC_CONST;
 
+GossipJabber * gossip_jabber_new                       (gpointer session);
+
 void           gossip_jabber_setup                     (GossipJabber        *jabber,
 							GossipAccount       *account);
 void           gossip_jabber_login                     (GossipJabber        *jabber);
@@ -66,7 +69,7 @@
 GossipContact *gossip_jabber_get_own_contact           (GossipJabber        *jabber);
 GossipContact *gossip_jabber_get_contact_from_jid      (GossipJabber        *jabber,
 							const gchar         *jid,
-							gboolean            *new_item,
+							gboolean             own_contact,
 							gboolean             set_permanent,
 							gboolean             get_vcard);
 void           gossip_jabber_send_presence             (GossipJabber        *jabber,
@@ -81,9 +84,6 @@
 gchar *        gossip_jabber_get_default_server        (const gchar    *username);
 guint          gossip_jabber_get_default_port          (gboolean        use_ssl);
 gboolean       gossip_jabber_is_ssl_supported          (void);
-GossipContact *gossip_jabber_new_contact               (GossipJabber        *jabber,
-							const gchar         *id,
-							const gchar         *name);
 gboolean       gossip_jabber_is_connected              (GossipJabber        *jabber);
 gboolean       gossip_jabber_is_connecting             (GossipJabber        *jabber);
 void           gossip_jabber_send_message              (GossipJabber        *jabber,
@@ -101,8 +101,6 @@
 							GossipCallback       callback,
 							gpointer             user_data,
 							GError             **error);
-GossipContact * gossip_jabber_find_contact             (GossipJabber        *jabber,
-							const gchar         *id);
 void            gossip_jabber_add_contact              (GossipJabber        *jabber,
 							const gchar         *id,
 							const gchar         *name,

Modified: trunk/libgossip/gossip-jid.c
==============================================================================
--- trunk/libgossip/gossip-jid.c	(original)
+++ trunk/libgossip/gossip-jid.c	Sat Jul  5 12:58:07 2008
@@ -29,27 +29,114 @@
 
 #include "gossip-jid.h"
 
-struct GossipJID {
-	gchar *full;
-	gchar *no_resource;
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_JID, GossipJIDPriv))
+
+typedef struct _GossipJIDPriv GossipJIDPriv;
+
+struct _GossipJIDPriv {
+	gchar       *full;
+	gchar       *no_resource;
 	const gchar *resource;
+};
 
-	guint  ref_count;
+static void         gossip_jid_class_init (GossipJIDClass *class);
+static void         gossip_jid_init       (GossipJID      *jid);
+static void         gossip_jid_finalize   (GObject        *object);
+static void         jid_get_property      (GObject        *object,
+					   guint           param_id,
+					   GValue         *value,
+					   GParamSpec     *pspec);
+static const gchar *jid_locate_resource   (const gchar    *str);
+
+enum {
+	PROP_0,
+	PROP_FULL,
+	PROP_WITHOUT_RESOURCE,
+	PROP_RESOURCE
 };
 
-void         jid_free            (GossipJID   *jid);
-const gchar *jid_locate_resource (const gchar *str);
+G_DEFINE_TYPE (GossipJID, gossip_jid, G_TYPE_OBJECT);
 
-void
-jid_free (GossipJID *jid)
+static void
+gossip_jid_class_init (GossipJIDClass *class)
 {
-	g_free (jid->full);
-	g_free (jid->no_resource);
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize     = gossip_jid_finalize;
+	object_class->get_property = jid_get_property;
 
-	g_slice_free (GossipJID, jid);
+	g_object_class_install_property (object_class,
+					 PROP_FULL,
+					 g_param_spec_string ("full",
+							      "Full JID",
+							      "Full JID",
+							      NULL,
+							      G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_WITHOUT_RESOURCE,
+					 g_param_spec_string ("without-resource",
+							      "JID without the resource",
+							      "JID without the resource",
+							      NULL,
+							      G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_RESOURCE,
+					 g_param_spec_string ("resource",
+							      "Resource",
+							      "Resource",
+							      NULL,
+							      G_PARAM_READABLE));
+
+	g_type_class_add_private (object_class, sizeof (GossipJIDPriv));
 }
 
-const gchar *
+static void
+gossip_jid_init (GossipJID *jid)
+{
+}
+
+static void
+gossip_jid_finalize (GObject *object)
+{
+	GossipJIDPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	g_free (priv->full);
+	g_free (priv->no_resource);
+
+	(G_OBJECT_CLASS (gossip_jid_parent_class)->finalize) (object);
+}
+
+static void
+jid_get_property (GObject    *object,
+		  guint       param_id,
+		  GValue     *value,
+		  GParamSpec *pspec)
+{
+	GossipJIDPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_FULL:
+		g_value_set_string (value, priv->full);
+		break;
+	case PROP_WITHOUT_RESOURCE:
+		g_value_set_string (value, priv->no_resource);
+		break;
+	case PROP_RESOURCE:
+		g_value_set_string (value, priv->resource);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static const gchar *
 jid_locate_resource (const gchar *str)
 {
 	gchar *ch;
@@ -83,91 +170,116 @@
 }
 
 GossipJID *
-gossip_jid_new (const gchar *str_jid)
+gossip_jid_new (const gchar *id)
 {
-	GossipJID *jid;
+	GossipJID     *jid;
+	GossipJIDPriv *priv;
 
-	g_return_val_if_fail (str_jid != NULL, NULL);
+	g_return_val_if_fail (id != NULL, NULL);
 
-	jid = g_slice_new0 (GossipJID);
+	jid = g_object_new (GOSSIP_TYPE_JID, NULL);
+	
+	priv = GET_PRIV (jid);
 
-	jid->ref_count = 1;
-	jid->full = jid_casefold_node (str_jid);
+	priv->full = jid_casefold_node (id);
+	priv->resource = jid_locate_resource (priv->full);
 
-	jid->resource = jid_locate_resource (jid->full);
-	if (jid->resource) {
-		jid->no_resource = g_strndup (jid->full, jid->resource - 1 - jid->full);
+	if (priv->resource) {
+		priv->no_resource = g_strndup (priv->full, 
+					       priv->resource - 1 - priv->full);
 	} else {
-		jid->no_resource = g_strdup (jid->full);
+		priv->no_resource = g_strdup (priv->full);
 	}
 
 	return jid;
 }
 
 void
-gossip_jid_set_without_resource (GossipJID *jid, const gchar *str)
+gossip_jid_set_without_resource (GossipJID   *jid, 
+				 const gchar *str)
 {
-	gchar *resource = NULL;
+	GossipJIDPriv *priv;
+	gchar         *resource = NULL;
+
+	g_return_if_fail (GOSSIP_IS_JID (jid));
 
-	g_return_if_fail (jid != NULL);
+	priv = GET_PRIV (jid);
 
-	if (jid->resource) {
-		resource = g_strdup (jid->resource);
+	if (priv->resource) {
+		resource = g_strdup (priv->resource);
 	}
 
-	g_free (jid->full);
-	g_free (jid->no_resource);
+	g_free (priv->full);
+	g_free (priv->no_resource);
 
-	jid->no_resource = jid_casefold_node (str);
+	priv->no_resource = jid_casefold_node (str);
 
 	if (resource) {
-		jid->full = g_strdup_printf ("%s/%s",
-					     jid->no_resource, resource);
+		priv->full = g_strdup_printf ("%s/%s",
+					      priv->no_resource, 
+					      resource);
 		g_free (resource);
-		jid->resource = jid_locate_resource (jid->full);
+		priv->resource = jid_locate_resource (priv->full);
 	} else {
-		jid->full = g_strdup (jid->no_resource);
+		priv->full = g_strdup (priv->no_resource);
 	}
 }
 
 void
-gossip_jid_set_resource (GossipJID *jid, const gchar *resource)
+gossip_jid_set_resource (GossipJID   *jid, 
+			 const gchar *resource)
 {
-	g_return_if_fail (jid != NULL);
+	GossipJIDPriv *priv;
 
-	g_free (jid->full);
+	g_return_if_fail (GOSSIP_IS_JID (jid));
 
-	jid->full = g_strdup_printf ("%s/%s", jid->no_resource, resource);
-	jid->resource = jid_locate_resource (jid->full);
+	priv = GET_PRIV (jid);
+
+	g_free (priv->full);
+
+	priv->full = g_strdup_printf ("%s/%s", priv->no_resource, resource);
+	priv->resource = jid_locate_resource (priv->full);
 }
 
 const gchar *
 gossip_jid_get_full (GossipJID *jid)
 {
-	g_return_val_if_fail (jid != NULL, "");
+	GossipJIDPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), "");
 
-	return jid->full;
+	priv = GET_PRIV (jid);
+
+	return priv->full;
 }
 
 const gchar *
 gossip_jid_get_without_resource (GossipJID *jid)
 {
-	g_return_val_if_fail (jid != NULL, "");
+	GossipJIDPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), "");
+
+	priv = GET_PRIV (jid);
 
-	if (jid->no_resource) {
-		return jid->no_resource;
+	if (priv->no_resource) {
+		return priv->no_resource;
 	}
 
-	return jid->full;
+	return priv->full;
 }
 
 const gchar *
 gossip_jid_get_resource (GossipJID *jid)
 {
-	g_return_val_if_fail (jid != NULL, NULL);
+	GossipJIDPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), NULL);
+
+	priv = GET_PRIV (jid);
 
-	if (jid->resource) {
-		return jid->resource;
+	if (priv->resource) {
+		return priv->resource;
 	}
 
 	return NULL;
@@ -176,15 +288,19 @@
 gboolean
 gossip_jid_is_service (GossipJID *jid)
 {
-	gchar *ch;
+	GossipJIDPriv *priv;
+	gchar         *ch;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), FALSE);
 
-	/* this basically checks to see if there is an '@'
-	   sign in the jid, if not, we assume it is a component
-	   or service (for example msn.jabber.org.uk) */
+	/* This basically checks to see if there is an '@' sign in the
+	 * jid, if not, we assume it is a component or service (for
+	 * example msn.jabber.org.uk).
+	 */
 
-	g_return_val_if_fail (jid != NULL, FALSE);
+	priv = GET_PRIV (jid);
 
-	ch = strchr (jid->full, '@');
+	ch = strchr (priv->full, '@');
 	if (!ch) {
 		return TRUE;
 	} else {
@@ -195,13 +311,16 @@
 gchar *
 gossip_jid_get_part_name (GossipJID *jid)
 {
-	gchar *ch;
+	GossipJIDPriv *priv;
+	gchar         *ch;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), g_strdup (""));
 
-	g_return_val_if_fail (jid != NULL, g_strdup (""));
+	priv = GET_PRIV (jid);
 
-	for (ch = jid->full; *ch; ++ch) {
+	for (ch = priv->full; *ch; ++ch) {
 		if (*ch == '@') {
-			return g_strndup (jid->full, ch - jid->full);
+			return g_strndup (priv->full, ch - priv->full);
 		}
 	}
 
@@ -213,7 +332,7 @@
 {
 	const gchar *ch;
 
-	g_return_val_if_fail (jid != NULL, "");
+	g_return_val_if_fail (GOSSIP_IS_JID (jid), g_strdup (""));
 
 	for (ch = gossip_jid_get_without_resource (jid); *ch; ++ch) {
 		if (*ch == '@') {
@@ -224,39 +343,25 @@
 	return "";
 }
 
-GossipJID *
-gossip_jid_ref (GossipJID *jid)
-{
-	g_return_val_if_fail (jid != NULL, NULL);
-
-	jid->ref_count++;
-
-	return jid;
-}
-
-void
-gossip_jid_unref (GossipJID *jid)
-{
-	g_return_if_fail (jid != NULL);
-
-	jid->ref_count--;
-	if (jid->ref_count <= 0) {
-		jid_free (jid);
-	}
-}
-
 gboolean
 gossip_jid_equals (GossipJID *jid_a,
 		   GossipJID *jid_b)
 {
-	g_return_val_if_fail (jid_a != NULL, FALSE);
-	g_return_val_if_fail (jid_b != NULL, FALSE);
+	GossipJIDPriv *priv_a;
+	GossipJIDPriv *priv_b;
+
+	g_return_val_if_fail (GOSSIP_IS_JID (jid_a), FALSE);
+	g_return_val_if_fail (GOSSIP_IS_JID (jid_b), FALSE);
 
 	/* NOTE: This is not strictly correct, since the node and resource are
 	 * UTF8, and the domain have other rules. The node is also already
 	 * casefolded.
 	 */
-	if (g_ascii_strcasecmp (jid_a->full, jid_b->full) == 0) {
+
+	priv_a = GET_PRIV (jid_a);
+	priv_b = GET_PRIV (jid_b);
+
+	if (g_ascii_strcasecmp (priv_a->full, priv_b->full) == 0) {
 		return TRUE;
 	}
 
@@ -269,8 +374,8 @@
 {
 	const gchar *a, *b;
 
-	g_return_val_if_fail (jid_a != NULL, FALSE);
-	g_return_val_if_fail (jid_b != NULL, FALSE);
+	g_return_val_if_fail (GOSSIP_IS_JID (jid_a), FALSE);
+	g_return_val_if_fail (GOSSIP_IS_JID (jid_b), FALSE);
 
 	a = gossip_jid_get_without_resource (jid_a);
 	b = gossip_jid_get_without_resource (jid_b);
@@ -413,8 +518,8 @@
 	g_return_val_if_fail (v1 != NULL, FALSE);
 	g_return_val_if_fail (v2 != NULL, FALSE);
 
-	a = gossip_jid_get_without_resource ((GossipJID *) v1);
-	b = gossip_jid_get_without_resource ((GossipJID *) v2);
+	a = gossip_jid_get_full (GOSSIP_JID (v1));
+	b = gossip_jid_get_full (GOSSIP_JID (v2));
 
 	/* NOTE: This is not strictly correct, since the node and resource are
 	 * UTF8, and the domain have other rules. The node is also already
@@ -426,7 +531,45 @@
 guint
 gossip_jid_hash (gconstpointer key)
 {
-	GossipJID *jid = (GossipJID *) key;
+	GossipJID *jid;
+	gchar     *lower;
+	guint      ret_val;
+
+	/* NOTE: This is not strictly correct, since the node and resource are
+	 * UTF8, and the domain have other rules. The node is also already
+	 * casefolded.
+	 */
+	jid = GOSSIP_JID (key);
+	lower = g_ascii_strdown (gossip_jid_get_full (jid), -1);
+	ret_val = g_str_hash (lower);
+	g_free (lower);
+
+	return ret_val;
+}
+
+gboolean
+gossip_jid_equal_without_resource (gconstpointer v1,
+				   gconstpointer v2)
+{
+	const gchar *a, *b;
+
+	g_return_val_if_fail (v1 != NULL, FALSE);
+	g_return_val_if_fail (v2 != NULL, FALSE);
+
+	a = gossip_jid_get_without_resource (GOSSIP_JID (v1));
+	b = gossip_jid_get_without_resource (GOSSIP_JID (v2));
+
+	/* NOTE: This is not strictly correct, since the node and resource are
+	 * UTF8, and the domain have other rules. The node is also already
+	 * casefolded.
+	 */
+	return g_ascii_strcasecmp (a, b) == 0;
+}
+
+guint
+gossip_jid_hash_without_resource (gconstpointer key)
+{
+	GossipJID *jid;
 	gchar     *lower;
 	guint      ret_val;
 
@@ -434,6 +577,7 @@
 	 * UTF8, and the domain have other rules. The node is also already
 	 * casefolded.
 	 */
+	jid = GOSSIP_JID (key);
 	lower = g_ascii_strdown (gossip_jid_get_without_resource (jid), -1);
 	ret_val = g_str_hash (lower);
 	g_free (lower);

Modified: trunk/libgossip/gossip-jid.h
==============================================================================
--- trunk/libgossip/gossip-jid.h	(original)
+++ trunk/libgossip/gossip-jid.h	Sat Jul  5 12:58:07 2008
@@ -21,16 +21,31 @@
 #ifndef __GOSSIP_JID_H__
 #define __GOSSIP_JID_H__
 
-#include <glib.h>
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
-typedef struct GossipJID GossipJID;
+#define GOSSIP_TYPE_JID         (gossip_jid_get_type ())
+#define GOSSIP_JID(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_JID, GossipJID))
+#define GOSSIP_JID_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_JID, GossipJIDClass))
+#define GOSSIP_IS_JID(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_JID))
+#define GOSSIP_IS_JID_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_JID))
+#define GOSSIP_JID_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_JID, GossipJIDClass))
+
+typedef struct _GossipJID      GossipJID;
+typedef struct _GossipJIDClass GossipJIDClass;
+
+struct _GossipJID {
+	GObject parent;
+};
+
+struct _GossipJIDClass {
+	GObjectClass parent_class;
+};
 
-GossipJID *  gossip_jid_new                      (const gchar   *str_jid);
-GossipJID *  gossip_jid_ref                      (GossipJID     *jid);
-void         gossip_jid_unref                    (GossipJID     *jid);
+GType        gossip_jid_get_type                 (void) G_GNUC_CONST;
 
+GossipJID *  gossip_jid_new                      (const gchar   *str_jid);
 void         gossip_jid_set_without_resource     (GossipJID     *jid,
 						  const gchar   *str);
 void         gossip_jid_set_resource             (GossipJID     *jid,
@@ -63,6 +78,10 @@
 						  gconstpointer  v2);
 guint        gossip_jid_hash                     (gconstpointer  key);
 
+gboolean     gossip_jid_equal_without_resource   (gconstpointer  v1,
+						  gconstpointer  v2);
+guint        gossip_jid_hash_without_resource    (gconstpointer  key);
+
 const gchar *gossip_jid_get_example_string       (void);
 
 G_END_DECLS

Modified: trunk/libgossip/gossip-log.c
==============================================================================
--- trunk/libgossip/gossip-log.c	(original)
+++ trunk/libgossip/gossip-log.c	Sat Jul  5 12:58:07 2008
@@ -925,7 +925,6 @@
 	GossipLogManagerPriv  *priv;
 	GossipChatroomManager *chatroom_manager;
 	GossipChatroom        *chatroom;
-	GList                 *found;
 	gchar                 *server;
 	gchar                 *room;
 
@@ -943,22 +942,11 @@
 	server++;
 
 	chatroom_manager = gossip_session_get_chatroom_manager (priv->session);
-	found = gossip_chatroom_manager_find_extended (chatroom_manager, account, server, room);
-
-	if (!found) {
-		chatroom = g_object_new (GOSSIP_TYPE_CHATROOM,
-					 "account", account,
-					 "name", room,
-					 "server", server,
-					 "room", room,
-					 NULL);
-	} else {
-		/* FIXME: What do we do if there are more than
-		 * 1 chatrooms found.
-		 */
-		chatroom = found->data;
-	}
-
+	chatroom = gossip_chatroom_manager_find_or_create (chatroom_manager, 
+							   account, 
+							   server, 
+							   room,
+							   NULL);
 	g_free (room);
 
 	return chatroom;
@@ -2442,10 +2430,6 @@
 							       account, 
 							       contact_id);
 			if (!contact) {
-				/* FIXME: What do we do here, do we
-				 * create a new contact explicitly for
-				 * this log entry?
-				 */
 				g_warning ("No contact for '%s' (escaping contact id to '%s'), ignoring", 
 					   filename, contact_id);
 				g_free (contact_id);
@@ -2630,160 +2614,3 @@
 
 	return hit->url;
 }
-
-#if 0
-/* Function to convert links into new file format */
-GList *
-log_links_convert_old_to_new (GossipLogManager *manager)
-{
-	GossipLogManagerPriv *priv;
-	GossipContactManager *contact_manager;
-	GList                *files;
-	GList                *l;
-	const gchar          *filename;
-	gchar                *text_casefold = NULL;
-	gchar                *contents;
-	gchar                *contents_casefold;
-	GList                *hits = NULL;
-
-	g_return_val_if_fail (GOSSIP_IS_LOG_MANAGER (manager), NULL);
-
-	priv = GET_PRIV (manager);
-	
-	if (!G_STR_EMPTY (text)) {
-		text_casefold = g_utf8_casefold (text, -1);
-	}
-
-	if (log_get_all_log_files (&files)) {
-		gossip_debug (DEBUG_DOMAIN, "Found %d log files in total", g_list_length (files));
-	} else {
-		gossip_debug (DEBUG_DOMAIN, "Failed to retrieve all log files");
-	}
-
-	/* Do this here instead of for each file */
-	contact_manager = gossip_session_get_contact_manager (priv->session);
-
-	for (l = files; l; l = l->next) {
-		GMappedFile *file;
-		GArray      *start, *end;
-		gsize        length;
-		gint         num_matches;
-		gint         i;
-
-		filename = l->data;
-
-		/* FIXME: Handle chatrooms */
-		if (strstr (filename, LOG_DIR_CHATROOMS)) {
-			gossip_debug (DEBUG_DOMAIN, "Ignoring chatroom filename:'%s'", filename);
-			continue;
-		}
-
-		file = g_mapped_file_new (filename, FALSE, NULL);
-		if (!file) {
-			continue;
-		}
-
-		length = g_mapped_file_get_length (file);
-		contents = g_mapped_file_get_contents (file);
-
-		contents_casefold = g_utf8_casefold (contents, length);
-
-		g_mapped_file_free (file);
-
-		/* Use Regex to find links */
-		start = g_array_new (FALSE, FALSE, sizeof (gint));
-		end = g_array_new (FALSE, FALSE, sizeof (gint));
-		
-		num_matches = gossip_regex_match (GOSSIP_REGEX_BROWSER,
-						  contents_casefold, 
-						  start, end);
-		
-		for (i = 0; i < num_matches; i++) {
-			gchar    *link;
-			gint      s = 0;
-			gint      e = 0;
-			gboolean  add_link = TRUE;
-
-			s = g_array_index (start, gint, i);
-			e = g_array_index (end, gint, i);
-
-			link = gossip_substring (contents_casefold, s, e);
-			if (G_STR_EMPTY (link) ||
-			    (!G_STR_EMPTY (text_casefold) &&
-			     !strstr (contents_casefold, text_casefold))) {
-				add_link = FALSE;
-			}
-
-			if (add_link) {
-				GossipLogSearchHit *hit;
-				GossipAccount      *account;
-				GossipContact      *contact;
-				gchar              *contact_id;
-				gint                len;
-
-				/* FIX a nasty issue where we get the
-				 * </message> in part in the URL from
-				 * the log file format.
-				 */
-				len = strlen (link);
-				if (link[len - 1] == '<') {
-					link[len - 1] = '\0';
-				}
-				
-				account = log_get_account_from_filename (manager, filename);
-				if (!account) {
-					/* We must have other directories in
-					 * here which are not account
-					 * directories, so we just ignore them.
-					 */
-					g_free (link);
-					continue;
-				}
-				
-				contact_id = log_get_contact_id_from_filename (filename);
-				contact = gossip_contact_manager_find (contact_manager, 
-								       account, 
-								       contact_id);
-				g_free (contact_id);
-				
-				if (!contact) {
-					g_free (link);
-					continue;
-				}
-				
-				/* FIXME: Add function call here to remember link 
-				 *
-				 */
-				
-				hit = g_new0 (GossipLogSearchHit, 1);
-				
-				hit->date = log_get_date_from_filename (filename);
-				hit->filename = g_strdup (filename);
-				hit->account = g_object_ref (account);
-				hit->contact = g_object_ref (contact);
-				hit->link = link;
-
-				gossip_debug (DEBUG_DOMAIN, 
-					      "Found link:'%s' in file:'%s' on date:'%s'...",
-					      link, hit->filename, hit->date);
-				continue;
-			}
-			
-			g_free (link);
-		}
-
-		g_array_free (start, TRUE);
-		g_array_free (end, TRUE);
-
-		g_free (contents_casefold);
-	}
-
-	g_list_foreach (files, (GFunc) g_free, NULL);
-	g_list_free (files);
-
-	g_free (text_casefold);
-
-	return hits;
-}
-
-#endif

Modified: trunk/libgossip/gossip-private.h
==============================================================================
--- trunk/libgossip/gossip-private.h	(original)
+++ trunk/libgossip/gossip-private.h	Sat Jul  5 12:58:07 2008
@@ -31,7 +31,8 @@
 #include "gossip-chatroom-manager.h"
 #include "gossip-account-manager.h"
 
-GossipChatroomManager *gossip_chatroom_manager_new (GossipAccountManager  *manager,
+GossipChatroomManager *gossip_chatroom_manager_new (GossipAccountManager  *account_manager,
+						    GossipContactManager  *contact_manager,
 						    const gchar           *filename);
 GossipContactManager * gossip_contact_manager_new  (GossipSession         *session,
 						    const gchar           *filename);

Modified: trunk/libgossip/gossip-session.c
==============================================================================
--- trunk/libgossip/gossip-session.c	(original)
+++ trunk/libgossip/gossip-session.c	Sat Jul  5 12:58:07 2008
@@ -655,34 +655,10 @@
 static GossipJabber *
 session_get_protocol (GossipSession *session, GossipContact *contact)
 {
-	GossipSessionPriv *priv;
-	GList             *l;
-	const gchar       *id;
-
 	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
-	priv = GET_PRIV (session);
-
-	id = gossip_contact_get_id (contact);
-
-	for (l = priv->protocols; l; l = l->next) {
-		GossipJabber  *jabber;
-		GossipContact *this_contact;
-
-		jabber = l->data;
-
-		this_contact = gossip_jabber_find_contact (jabber, id);
-		if (!this_contact) {
-			continue;
-		}
-
-		if (gossip_contact_equal (this_contact, contact)) {
-			return jabber;
-		}
-	}
-
-	return NULL;
+	return gossip_session_get_protocol (session, gossip_contact_get_account (contact));
 }
 
 GossipSession *
@@ -727,6 +703,7 @@
 
 	/* Set up chatroom manager */
 	priv->chatroom_manager = gossip_chatroom_manager_new (priv->account_manager, 
+							      priv->contact_manager,
 							      chatrooms_file);
 
 	/* Set up log manager */
@@ -926,7 +903,7 @@
 
 	priv = GET_PRIV (session);
 
-	jabber = g_object_new (GOSSIP_TYPE_JABBER, NULL);
+	jabber = gossip_jabber_new (session);
 
 	account = gossip_jabber_new_account ();
 	priv->protocols = g_list_append (priv->protocols,
@@ -963,7 +940,7 @@
 		return TRUE;
 	}
 
-	jabber = g_object_new (GOSSIP_TYPE_JABBER, NULL);
+	jabber = gossip_jabber_new (session);
 
 	priv->protocols = g_list_append (priv->protocols,
 					 g_object_ref (jabber));
@@ -1011,40 +988,6 @@
 }
 
 static void
-session_find_account_foreach_cb (GossipAccount *account,
-				 GossipJabber  *jabber,
-				 FindAccount   *fa)
-{
-	const gchar *id;
-
-	id = gossip_contact_get_id (fa->contact);
-	if (gossip_jabber_find_contact (jabber, id)) {
-		fa->account = g_object_ref (account);
-	}
-}
-
-GossipAccount *
-gossip_session_find_account (GossipSession *session, GossipContact *contact)
-{
-	GossipSessionPriv *priv;
-	FindAccount        fa;
-
-	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
-	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
-
-	priv = GET_PRIV (session);
-
-	fa.contact = contact;
-	fa.account = NULL;
-
-	g_hash_table_foreach (priv->accounts,
-			      (GHFunc) session_find_account_foreach_cb,
-			      &fa);
-
-	return fa.account;
-}
-
-static void
 session_find_account_for_own_contact_foreach_cb (GossipAccount *account,
 						 GossipJabber  *jabber,
 						 FindAccount   *fa)
@@ -1470,33 +1413,6 @@
 	return GOSSIP_FT_PROVIDER (jabber);
 }
 
-GossipContact *
-gossip_session_find_contact (GossipSession *session,
-			     const gchar   *id)
-{
-	GossipSessionPriv *priv;
-	GList             *l;
-
-	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
-	g_return_val_if_fail (id != NULL, NULL);
-
-	priv = GET_PRIV (session);
-
-	for (l = priv->protocols; l; l = l->next) {
-		GossipJabber  *jabber;
-		GossipContact *contact;
-
-		jabber = l->data;
-
-		contact = gossip_jabber_find_contact (jabber, id);
-		if (contact) {
-			return contact;
-		}
-	}
-
-	return NULL;
-}
-
 void
 gossip_session_add_contact (GossipSession *session,
 			    GossipAccount *account,
@@ -1954,18 +1870,17 @@
 	jabber = session_get_protocol (session, contact);
 
 	if (!jabber) {
-		/* Temporary contact. Use account */
-		GossipAccount           *account;
-		GossipSessionPriv        *priv = GET_PRIV (session);
+		GossipSessionPriv *priv;
+		GossipAccount     *account;
 
-		account = gossip_session_find_account (session, contact);
+		priv = GET_PRIV (session);
+
+		/* Temporary contact. Use account */
+		account = gossip_contact_get_account (contact);
 		g_return_val_if_fail (GOSSIP_IS_ACCOUNT (account), FALSE);
 
 		jabber = g_hash_table_lookup (priv->accounts, account);
- 		g_object_unref (account);
-
 		g_return_val_if_fail (jabber, FALSE);
-
 	}
 
 	return gossip_jabber_get_version (jabber, contact,
@@ -1993,7 +1908,7 @@
 
 		chatroom = l->data;
 
-		if (!gossip_chatroom_get_favourite (chatroom)) {
+		if (!gossip_chatroom_get_favorite (chatroom)) {
 			continue;
 		}
 

Modified: trunk/libgossip/gossip-session.h
==============================================================================
--- trunk/libgossip/gossip-session.h	(original)
+++ trunk/libgossip/gossip-session.h	Sat Jul  5 12:58:07 2008
@@ -97,8 +97,6 @@
 							GossipAccount          *account);
 gboolean        gossip_session_remove_account          (GossipSession          *session,
 							GossipAccount          *account);
-GossipAccount * gossip_session_find_account            (GossipSession          *session,
-							GossipContact          *contact);
 GossipAccount * gossip_session_find_account_for_own_contact 
                                                        (GossipSession          *session,
 							GossipContact          *own_contact);
@@ -133,8 +131,6 @@
 							gsize                  *max_size,
 							gchar                 **format);
 /* Contact management */
-GossipContact * gossip_session_find_contact            (GossipSession          *session,
-							const gchar            *str);
 void            gossip_session_add_contact             (GossipSession          *session,
 							GossipAccount          *account,
 							const gchar            *id,

Modified: trunk/src/gossip-app.c
==============================================================================
--- trunk/src/gossip-app.c	(original)
+++ trunk/src/gossip-app.c	Sat Jul  5 12:58:07 2008
@@ -1201,7 +1201,7 @@
 		account = gossip_chatroom_get_account (chatroom);
 		menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item");
 
-		visible = gossip_chatroom_get_favourite (chatroom);
+		visible = gossip_chatroom_get_favorite (chatroom);
 		visible &= gossip_session_is_connected (priv->session, account);
 
 		if (visible) {
@@ -1237,7 +1237,7 @@
 	account = gossip_chatroom_get_account (chatroom);
 	menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item");
 
-	visible = gossip_chatroom_get_favourite (chatroom);
+	visible = gossip_chatroom_get_favorite (chatroom);
 	visible &= gossip_session_is_connected (priv->session, account);
 
 	if (menu_item) {
@@ -1314,7 +1314,7 @@
 	account = gossip_chatroom_get_account (chatroom);
 	menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item");
 
-	visible = gossip_chatroom_get_favourite (chatroom);
+	visible = gossip_chatroom_get_favorite (chatroom);
 	visible &= gossip_session_is_connected (priv->session, account);
 
 	g_signal_handlers_disconnect_by_func (chatroom, 
@@ -1363,7 +1363,7 @@
 
 	gtk_widget_set_sensitive (priv->room_join_favorites, found);
 
-	g_signal_connect (priv->chatroom_manager, "chatroom-favourite-update",
+	g_signal_connect (priv->chatroom_manager, "chatroom-favorite-update",
 			  G_CALLBACK (app_favorite_chatroom_menu_update_cb),
 			  NULL);
 	g_signal_connect (priv->chatroom_manager, "chatroom-added",

Modified: trunk/src/gossip-chat-view.c
==============================================================================
--- trunk/src/gossip-chat-view.c	(original)
+++ trunk/src/gossip-chat-view.c	Sat Jul  5 12:58:07 2008
@@ -712,7 +712,7 @@
 	gtk_widget_set_sensitive (button, FALSE);
 	gtk_widget_set_sensitive (other_button, FALSE);
 
-	contact = gossip_chatroom_invite_get_invitor (invite);
+	contact = gossip_chatroom_invite_get_inviter (invite);
 	reason = gossip_chatroom_invite_get_reason (invite);
 
 	session = gossip_app_get_session ();
@@ -744,7 +744,7 @@
 	gtk_widget_set_sensitive (button, FALSE);
 	gtk_widget_set_sensitive (other_button, FALSE);
 
-	contact = gossip_chatroom_invite_get_invitor (invite);
+	contact = gossip_chatroom_invite_get_inviter (invite);
 
 	session = gossip_app_get_session ();
 	account = gossip_contact_get_account (contact);
@@ -896,18 +896,17 @@
 				GossipMessage  *message)
 {
 	GossipChatViewPriv   *priv;
-	GossipContact        *sender;
+	GossipContact        *inviter;
 	GossipChatroomInvite *invite;
-	const gchar          *body;
 	GtkTextChildAnchor   *anchor;
 	GtkTextIter           iter;
 	GtkWidget            *button_accept;
 	GtkWidget            *button_decline;
-	const gchar          *id;
+	const gchar          *id_str;
 	const gchar          *reason;
 	gboolean              bottom;
 	const gchar          *tag;
-	GString              *s;
+	gchar                *str;
 
 	g_return_if_fail (GOSSIP_IS_CHAT_VIEW (view));
 
@@ -917,50 +916,41 @@
 
 	bottom = chat_view_is_scrolled_down (view);
 
-	sender = gossip_message_get_sender (message);
 	invite = gossip_message_get_invite (message);
-	body = gossip_message_get_body (message);
+	inviter = gossip_chatroom_invite_get_inviter (invite);
+	id_str = gossip_chatroom_invite_get_id (invite);
 
-	gossip_theme_append_timestamp (priv->theme, priv->theme_context,
+	gossip_theme_append_timestamp (priv->theme, 
+				       priv->theme_context,
 				       view, message, TRUE, TRUE);
+	
+	str = g_strdup_printf (_("You have been invited to join a chat "
+				 "conference with '%s' in the room '%s'"),
+			       gossip_contact_get_name (inviter),
+			       id_str);
+
+	gossip_theme_append_text (priv->theme, 
+				  priv->theme_context, 
+				  view, 
+				  str, 
+				  tag, 
+				  NULL);
+	g_free (str);
 
 	reason = gossip_chatroom_invite_get_reason (invite);
 
-	s = g_string_new ("");
-	if (!G_STR_EMPTY (body)) {
-		s = g_string_append (s, body);
-	}
-
-	/* Make sure the reason is not the body (or in the body) */
-	if (!G_STR_EMPTY (reason) && !strstr (body, reason)) {
-		if (s->len > 0) {
-			s = g_string_append_c (s, '\n');
-		}
-
-		s = g_string_append (s, reason);
-	}
-
-	if (s->len < 1) {
-		s = g_string_append
-			(s, _("You have been invited to join a chat conference."));
-	}
-
-	/* Don't include the invite in the chat window if it is part of the
-	 * actual request - some chat clients send this and it looks weird
-	 * repeated.
-	 */
-	id = gossip_chatroom_invite_get_id (invite);
+	if (!G_STR_EMPTY (reason)) {
+		str = g_strconcat ("\n", _("Reason: "), reason, NULL);
 
-	if (!strstr (s->str, id)) {
-		g_string_append_printf (s, "\n(%s)\n", id);
+		gossip_theme_append_text (priv->theme, 
+					  priv->theme_context, 
+					  view, 
+					  str, 
+					  tag, 
+					  NULL);
+		g_free (str);
 	}
 
-	s = g_string_prepend_c (s, '\n');
-
-	gossip_theme_append_text (priv->theme, priv->theme_context, 
-				  view, s->str, tag, NULL);
-	g_string_free (s, TRUE);
-
 	gtk_text_buffer_get_end_iter (priv->buffer, &iter);
 
 	anchor = gtk_text_buffer_create_child_anchor (priv->buffer, &iter);

Modified: trunk/src/gossip-chat-window.c
==============================================================================
--- trunk/src/gossip-chat-window.c	(original)
+++ trunk/src/gossip-chat-window.c	Sat Jul  5 12:58:07 2008
@@ -854,7 +854,7 @@
 		gtk_widget_hide (priv->menu_conv_info);
 		gtk_widget_hide (priv->menu_conv_separator);
 
-		/* Can we add this room to our favourites and are we
+		/* Can we add this room to our favorites and are we
 		 * connected to the room?
 		 */
 		manager = gossip_app_get_chatroom_manager ();
@@ -1209,7 +1209,7 @@
 
 	group_chat = GOSSIP_GROUP_CHAT (priv->current_chat);
 	chatroom = gossip_group_chat_get_chatroom (group_chat);
-	gossip_chatroom_set_favourite (chatroom, TRUE);
+	gossip_chatroom_set_favorite (chatroom, TRUE);
 
 	manager = gossip_app_get_chatroom_manager ();
 	gossip_chatroom_manager_add (manager, chatroom);
@@ -1817,11 +1817,12 @@
 				GossipChatWindow *window)
 {
 	if (info == DND_DRAG_TYPE_CONTACT_ID) {
-		GossipChatManager *manager;
-		GossipContact     *contact;
-		GossipChat        *chat;
-		GossipChatWindow  *old_window;
-		const gchar       *id = NULL;
+		GossipChatManager    *manager;
+		GossipContactManager *contact_manager;
+		GossipContact        *contact;
+		GossipChat           *chat;
+		GossipChatWindow     *old_window;
+		const gchar          *id = NULL;
 
 		if (selection) {
 			id = (const gchar*) selection->data;
@@ -1829,7 +1830,9 @@
 
 		gossip_debug (DEBUG_DOMAIN, "DND contact from roster with id:'%s'", id);
 		
-		contact = gossip_session_find_contact (gossip_app_get_session (), id);
+		contact_manager = gossip_session_get_contact_manager (gossip_app_get_session ());
+		contact = gossip_contact_manager_find (contact_manager, NULL, id);
+
 		if (!contact) {
 			gossip_debug (DEBUG_DOMAIN, "DND contact from roster not found");
 			return;

Modified: trunk/src/gossip-chatrooms-window.c
==============================================================================
--- trunk/src/gossip-chatrooms-window.c	(original)
+++ trunk/src/gossip-chatrooms-window.c	Sat Jul  5 12:58:07 2008
@@ -273,26 +273,14 @@
 static void
 chatrooms_window_model_action_selected (GossipChatroomsWindow *window)
 {
-	GossipSession          *session;
-	GossipAccount          *account;
-	GossipAccountChooser   *account_chooser;
-	GossipChatroomProvider *provider;
-	GossipChatroom         *chatroom;
-	GossipChatroomStatus    status;
-	GtkTreeView            *view;
-	GtkTreeModel           *model;
+	GossipSession        *session;
+	GossipChatroom       *chatroom;
+	GossipChatroomStatus  status;
+	GtkTreeView          *view;
+	GtkTreeModel         *model;
 
 	session = gossip_app_get_session ();
 
-	account_chooser = GOSSIP_ACCOUNT_CHOOSER (window->account_chooser);
-	account = gossip_account_chooser_get_account (account_chooser);
-
-	provider = gossip_session_get_chatroom_provider (session, account);
-
-	if (account) {
-		g_object_unref (account);
-	}
-
 	view = GTK_TREE_VIEW (window->treeview);
 	model = gtk_tree_view_get_model (view);
 
@@ -385,6 +373,11 @@
 	const gchar      *password_protected = NULL;
 	const gchar      *auto_connect = NULL;
 
+	/* We only show favorites here, not EVERY chatroom */
+	if (!gossip_chatroom_get_favorite (chatroom)) {
+		return;
+	}
+
 	view = GTK_TREE_VIEW (window->treeview);
 	selection = gtk_tree_view_get_selection (view);
 	model = gtk_tree_view_get_model (view);
@@ -557,7 +550,7 @@
 
 	/* Remove from config */
 	manager = gossip_app_get_chatroom_manager ();
-	gossip_chatroom_manager_remove (manager, chatroom);
+	gossip_chatroom_set_favorite (chatroom, FALSE);
 	gossip_chatroom_manager_store (manager);
 	
 	g_object_unref (chatroom);

Modified: trunk/src/gossip-contact-info-dialog.c
==============================================================================
--- trunk/src/gossip-contact-info-dialog.c	(original)
+++ trunk/src/gossip-contact-info-dialog.c	Sat Jul  5 12:58:07 2008
@@ -538,7 +538,7 @@
 	}
 
 	session = gossip_app_get_session ();
-	account = gossip_session_find_account (session, contact);
+	account = gossip_contact_get_account (contact);
 
 	dialog = g_new0 (GossipContactInfoDialog, 1);
 

Modified: trunk/src/gossip-contact-list.c
==============================================================================
--- trunk/src/gossip-contact-list.c	(original)
+++ trunk/src/gossip-contact-list.c	Sat Jul  5 12:58:07 2008
@@ -1947,6 +1947,8 @@
 
 		g_strfreev (uri_strv);
 	} else if (info == DND_DRAG_TYPE_CONTACT_ID) {
+		GossipContactManager *contact_manager;
+
 		priv = GET_PRIV (widget);
 	
 		id = (const gchar*) selection->data;
@@ -1955,7 +1957,10 @@
 			      context->action == GDK_ACTION_COPY ? "copy" : "",
 			      id);
 
-		contact = gossip_session_find_contact (priv->session, id);
+
+		contact_manager = gossip_session_get_contact_manager (priv->session);
+		contact = gossip_contact_manager_find (contact_manager, NULL, id);
+
 		if (!contact) {
 			gossip_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
 			goto out;
@@ -1977,6 +1982,7 @@
 			}
 
 			gossip_contact_set_groups (contact, NULL);
+			drag_success = TRUE;
 		} else {
 			GList    *l, *new_groups;
 			gchar    *name;
@@ -2146,19 +2152,22 @@
 	GtkTreePath           *path;
 	GtkTreeIter            iter;
 
-	priv = GET_PRIV (widget);
-
-	GTK_WIDGET_CLASS (gossip_contact_list_parent_class)->drag_begin (widget,
-									 context);
-
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
 	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
 		return;
 	}
 
+
 	path = gtk_tree_model_get_path (model, &iter);
+	if (!path) {
+		return;
+	}
+
+	priv = GET_PRIV (widget);
 	priv->drag_row = gtk_tree_row_reference_new (model, path);
 	gtk_tree_path_free (path);
+
+	GTK_WIDGET_CLASS (gossip_contact_list_parent_class)->drag_begin (widget, context);
 }
 
 static void

Modified: trunk/src/gossip-dbus.c
==============================================================================
--- trunk/src/gossip-dbus.c	(original)
+++ trunk/src/gossip-dbus.c	Sat Jul  5 12:58:07 2008
@@ -233,7 +233,11 @@
 		      id ? id : "SELF");
 
 	if (!G_STR_EMPTY (id)) {
-		contact = gossip_session_find_contact (saved_session, id);
+		GossipContactManager *contact_manager;
+
+		contact_manager = gossip_session_get_contact_manager (saved_session);
+		contact = gossip_contact_manager_find (contact_manager, NULL, id);
+
 		if (!contact) {
 			gossip_debug (DEBUG_DOMAIN, "Contact:'%s' not recognised", id);
 
@@ -297,7 +301,11 @@
 	*name = NULL;
 
 	if (!G_STR_EMPTY (id)) {
-		contact = gossip_session_find_contact (saved_session, id);
+		GossipContactManager *contact_manager;
+
+		contact_manager = gossip_session_get_contact_manager (saved_session);
+		contact = gossip_contact_manager_find (contact_manager, NULL, id);
+
 		if (!contact) {
 			gossip_debug (DEBUG_DOMAIN, "Contact:'%s' not recognised", id);
 
@@ -369,12 +377,15 @@
 			  const gchar  *contact_id,
 			  GError      **error)
 {
-	GossipChatManager *manager;
-	GossipContact     *contact;
+	GossipChatManager    *manager;
+	GossipContactManager *contact_manager;
+	GossipContact        *contact;
 
 	gossip_debug (DEBUG_DOMAIN, "Sending message to contact:'%s'", contact_id);
 
-	contact = gossip_session_find_contact (saved_session, contact_id);
+	contact_manager = gossip_session_get_contact_manager (saved_session);
+	contact = gossip_contact_manager_find (contact_manager, NULL, contact_id);
+
 	if (!contact) {
 		g_set_error (error, gossip_dbus_error_quark (), 0,
 			     "Contact:'%s' not found", contact_id);

Modified: trunk/src/gossip-edit-contact-dialog.c
==============================================================================
--- trunk/src/gossip-edit-contact-dialog.c	(original)
+++ trunk/src/gossip-edit-contact-dialog.c	Sat Jul  5 12:58:07 2008
@@ -612,7 +612,7 @@
 	message = _("I would like to add you to my contact list.");
 
 	session = gossip_app_get_session ();
-	account = gossip_session_find_account (session, dialog->contact);
+	account = gossip_contact_get_account (dialog->contact);
 
 	gossip_session_add_contact (session,
 				    account,

Modified: trunk/src/gossip-group-chat.c
==============================================================================
--- trunk/src/gossip-group-chat.c	(original)
+++ trunk/src/gossip-group-chat.c	Sat Jul  5 12:58:07 2008
@@ -58,8 +58,6 @@
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_GROUP_CHAT, GossipGroupChatPriv))
 
 struct _GossipGroupChatPriv {
-	GossipContact          *own_contact;
-
 	GossipChatroomProvider *chatroom_provider;
 	GossipChatroom         *chatroom;
 	GossipChatroomStatus    last_status;
@@ -151,6 +149,11 @@
 static void            group_chat_kicked_cb                   (GossipChatroomProvider       *provider,
 							       gint                          id,
 							       GossipGroupChat              *chat);
+static void            group_chat_nick_changed_cb             (GossipChatroomProvider       *provider,
+							       gint                          id,
+							       GossipContact                *contact,
+							       const gchar                  *old_nick,
+							       GossipGroupChat              *chat);
 static void            group_chat_new_message_cb              (GossipChatroomProvider       *provider,
 							       gint                          id,
 							       GossipMessage                *message,
@@ -228,7 +231,7 @@
 							       GossipGroupChat              *chat);
 static gboolean        group_chat_cl_button_press_event_cb    (GtkTreeView                  *view,
 							       GdkEventButton               *event,
-							       GossipGroupChat              *chat);  
+							       GossipGroupChat              *chat);
 static gint            group_chat_cl_sort_func                (GtkTreeModel                 *model,
 							       GtkTreeIter                  *iter_a,
 							       GtkTreeIter                  *iter_b,
@@ -262,13 +265,13 @@
 static GtkWidget *     group_chat_cl_menu_create              (GossipGroupChat              *chat,
 							       GossipContact                *contact);
 static void            group_chat_cl_menu_destroy             (GtkWidget                    *menu,
-	                                                       GossipGroupChat              *chat);
+							       GossipGroupChat              *chat);
 static void            group_chat_cl_menu_info_activate_cb    (GtkMenuItem                  *menuitem,
-	                                                       GossipGroupChat              *chat);
+							       GossipGroupChat              *chat);
 static void            group_chat_cl_menu_chat_activate_cb    (GtkMenuItem                  *menuitem,
-	                                                       GossipGroupChat              *chat);
+							       GossipGroupChat              *chat);
 static void            group_chat_cl_menu_kick_activate_cb    (GtkMenuItem                  *menuitem,
-	                                                       GossipGroupChat              *chat);
+							       GossipGroupChat              *chat);
 static void            group_chat_set_scrolling_for_events    (GossipGroupChat              *chat,
 							       gboolean                      disable);
 static gboolean        group_chat_scroll_down_when_idle_func  (GossipGroupChat              *chat);
@@ -354,9 +357,12 @@
 static void
 group_chat_finalize (GObject *object)
 {
-	GossipGroupChat     *chat;
-	GossipGroupChatPriv *priv;
-	GossipChatroomId     id;
+	GossipGroupChat      *chat;
+	GossipGroupChatPriv  *priv;
+	GossipChatroomId      id;
+	GossipChatroomStatus  status;
+	GList                *l;
+	GList                *contacts;
 
 	gossip_debug (DEBUG_DOMAIN, "Finalized:%p", object);
 
@@ -372,6 +378,18 @@
 	id = gossip_chatroom_get_id (priv->chatroom);
 	g_hash_table_steal (group_chats, GINT_TO_POINTER (id));
 
+	contacts = gossip_chatroom_get_contacts (priv->chatroom);
+	for (l = contacts; l; l = l->next) {
+		g_signal_handlers_disconnect_by_func (l->data,
+						      group_chat_contact_updated_cb,
+						      chat);
+		g_signal_handlers_disconnect_by_func (l->data,
+						      group_chat_contact_presence_updated_cb,
+						      chat);
+	}
+
+	g_list_free (contacts);
+
 	g_signal_handlers_disconnect_by_func (priv->chatroom, 
 					      group_chat_chatroom_name_cb,
 					      chat);
@@ -382,6 +400,9 @@
 					      group_chat_kicked_cb, 
 					      chat);
 	g_signal_handlers_disconnect_by_func (priv->chatroom_provider,
+					      group_chat_nick_changed_cb, 
+					      chat);
+	g_signal_handlers_disconnect_by_func (priv->chatroom_provider,
 					      group_chat_new_message_cb, 
 					      chat);
 	g_signal_handlers_disconnect_by_func (priv->chatroom_provider,
@@ -407,21 +428,17 @@
 	 * because when we update the status, we get called back and
 	 * the widget is destroyed. 
 	 */
-	if (priv->chatroom) {
-		GossipChatroomStatus status;
-
-		status = gossip_chatroom_get_status (priv->chatroom);
-		if (status == GOSSIP_CHATROOM_STATUS_ACTIVE) {
-			gossip_chatroom_provider_leave (priv->chatroom_provider,
-							gossip_chatroom_get_id (priv->chatroom));
-		} else if (status == GOSSIP_CHATROOM_STATUS_JOINING) {
-			gossip_chatroom_provider_cancel (priv->chatroom_provider,
-							 gossip_chatroom_get_id (priv->chatroom));
-		}
-
-		g_object_unref (priv->chatroom);
+	status = gossip_chatroom_get_status (priv->chatroom);
+	if (status == GOSSIP_CHATROOM_STATUS_ACTIVE) {
+		gossip_chatroom_provider_leave (priv->chatroom_provider,
+						gossip_chatroom_get_id (priv->chatroom));
+	} else if (status == GOSSIP_CHATROOM_STATUS_JOINING) {
+		gossip_chatroom_provider_cancel (priv->chatroom_provider,
+						 gossip_chatroom_get_id (priv->chatroom));
 	}
 	
+	g_object_unref (priv->chatroom);
+	
 	g_object_unref (priv->chatroom_provider);
 
 	g_free (priv->subject);
@@ -431,10 +448,6 @@
 			chat);
 	g_list_free (priv->private_chats);
 
-	if (priv->own_contact) {
-		g_object_unref (priv->own_contact);
-	}
-
 	if (priv->scroll_idle_id) {
 		g_source_remove (priv->scroll_idle_id);
 	}
@@ -695,10 +708,13 @@
 {
 	GossipGroupChatPriv *priv;
 	GossipAccount       *this_account;
+	GossipContact       *own_contact;
 
 	priv = GET_PRIV (chat);
 
-	this_account = gossip_contact_get_account (priv->own_contact);
+	own_contact = gossip_chatroom_get_own_contact (priv->chatroom);
+	this_account = gossip_contact_get_account (own_contact);
+
 	if (!gossip_account_equal (this_account, account)) {
 		return;
 	}
@@ -886,16 +902,19 @@
 			       GossipGroupChat  *chat)
 {
 	if (info == DND_DRAG_TYPE_CONTACT_ID) {
-		GossipGroupChatPriv *priv;
-		GossipContact       *contact;
-		const gchar         *id;
-		gchar               *str;
+		GossipGroupChatPriv  *priv;
+		GossipContactManager *contact_manager;
+		GossipContact        *contact;
+		const gchar          *id;
+		gchar                *str;
 
 		priv = GET_PRIV (chat);
 		
 		id = (const gchar*) selection->data;
 		
-		contact = gossip_session_find_contact (gossip_app_get_session (), id);
+		contact_manager = gossip_session_get_contact_manager (gossip_app_get_session ());
+		contact = gossip_contact_manager_find (contact_manager, NULL, id);
+
 		if (!contact) {
 			gossip_debug (DEBUG_DOMAIN, 
 				      "Drag data received, but no contact found by the id:'%s'", 
@@ -1179,6 +1198,8 @@
 {
 	GossipGroupChatPriv *priv;
 	GossipChatView      *chatview;
+	GList               *contacts;
+	GList               *l;
 
 	priv = GET_PRIV (chat);
 
@@ -1190,6 +1211,17 @@
 
 	chatview = GOSSIP_CHAT (chat)->view;
 
+	contacts = gossip_chatroom_get_contacts (priv->chatroom);
+	for (l = contacts; l; l = l->next) {
+		g_signal_handlers_disconnect_by_func (l->data,
+						      group_chat_contact_updated_cb,
+						      chat);
+		g_signal_handlers_disconnect_by_func (l->data,
+						      group_chat_contact_presence_updated_cb,
+						      chat);
+	}
+	g_list_free (contacts);
+
 	gtk_widget_set_sensitive (priv->hbox_subject, FALSE);
 	gtk_widget_set_sensitive (priv->scrolled_window_contacts, FALSE);
 	gtk_widget_set_sensitive (priv->scrolled_window_input, FALSE);
@@ -1202,6 +1234,43 @@
 }
 
 static void
+group_chat_nick_changed_cb (GossipChatroomProvider *provider,
+			    gint                    id,
+			    GossipContact          *contact,
+			    const gchar            *old_nick,
+			    GossipGroupChat        *chat)
+{
+	GossipGroupChatPriv *priv;
+	GossipChatView      *chatview;
+	gchar               *str;
+
+	priv = GET_PRIV (chat);
+
+	if (id != gossip_chatroom_get_id (priv->chatroom)) {
+		return;
+	}
+
+	gossip_debug (DEBUG_DOMAIN,
+		      "[%d] Nick changed for contact:'%s', old nick:'%s', new nick:'%s'", 
+		      id, 
+		      gossip_contact_get_id (contact),
+		      old_nick,
+		      gossip_contact_get_name (contact));
+
+	chatview = GOSSIP_CHAT (chat)->view;
+
+	str = g_strdup_printf (_("%s is now known as %s"),
+			       old_nick,
+			       gossip_contact_get_name (contact));
+	
+	group_chat_set_scrolling_for_events (GOSSIP_GROUP_CHAT (chat), TRUE);
+	gossip_chat_view_append_event (chatview, str);
+	group_chat_set_scrolling_for_events (GOSSIP_GROUP_CHAT (chat), FALSE);
+
+	g_free (str);
+}
+
+static void
 group_chat_new_message_cb (GossipChatroomProvider *provider,
 			   gint                    id,
 			   GossipMessage          *message,
@@ -1210,6 +1279,7 @@
 	GossipGroupChatPriv  *priv;
 	GossipChatroomInvite *invite;
 	GossipContact        *sender;
+	GossipContact        *own_contact;
 	GossipLogManager     *log_manager;
 	GossipTime            timestamp;
 	gboolean              is_incoming;
@@ -1225,7 +1295,9 @@
 	is_backlog = timestamp < priv->time_joined;
 
 	sender = gossip_message_get_sender (message);
-	is_incoming = !gossip_contact_equal (sender, priv->own_contact);
+	own_contact = gossip_chatroom_get_own_contact (priv->chatroom);
+
+	is_incoming = !gossip_contact_equal (sender, own_contact);
 
 	gossip_debug (DEBUG_DOMAIN, 
 		      "[%d] New message with timestamp:%d, message %s backlog, %s incoming", 
@@ -1246,13 +1318,13 @@
 			gossip_chat_view_append_message_from_other (
 				GOSSIP_CHAT (chat)->view,
 				message,
-				priv->own_contact,
+				own_contact,
 				NULL);
 		} else {
 			gossip_chat_view_append_message_from_self (
 				GOSSIP_CHAT (chat)->view,
 				message,
-				priv->own_contact,
+				own_contact,
 				NULL);
 		}
 	}
@@ -1260,7 +1332,7 @@
 	/* Play sound? */
 	if (!is_backlog && 
 	    gossip_chat_should_play_sound (GOSSIP_CHAT (chat)) &&
-	    gossip_chat_should_highlight_nick (message, priv->own_contact)) {
+	    gossip_chat_should_highlight_nick (message, own_contact)) {
 		gossip_sound_play (GOSSIP_SOUND_CHAT);
 	}
 
@@ -1316,18 +1388,31 @@
 		return;
 	}
 
-	gossip_debug (DEBUG_DOMAIN, "[%d] Subject changed by:'%s' to:'%s'",
-		      id, gossip_contact_get_id (who), new_subject);
+	if (who) {
+		gossip_debug (DEBUG_DOMAIN, 
+			      "[%d] Subject changed by:'%s' to:'%s'",
+			      id, 
+			      gossip_contact_get_id (who),
+			      new_subject);
+
+		event = g_strdup_printf (_("%s has set the subject: %s"),
+					 gossip_contact_get_name (who),
+					 new_subject);
+	} else {
+		gossip_debug (DEBUG_DOMAIN, 
+			      "[%d] Subject set as:'%s'",
+			      id, 
+			      new_subject);
+
+		event = g_strdup_printf (_("Subject is set as: %s"),
+					 new_subject);
+	}
 
 	g_free (priv->subject);
 	priv->subject = g_strdup (new_subject);
 	
 	gtk_label_set_text (GTK_LABEL (priv->label_subject), new_subject);
 
-	event = g_strdup_printf (_("%s has set the subject: %s"),
-				 gossip_contact_get_name (who),
-				 new_subject);
-
 	group_chat_set_scrolling_for_events (chat, TRUE);
 	gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, event);
 	group_chat_set_scrolling_for_events (chat, FALSE);
@@ -1498,9 +1583,11 @@
 				GtkTreeViewColumn *col,
 				GossipGroupChat   *chat)
 {
-	GtkTreeModel  *model;
-	GtkTreeIter    iter;
-	GossipContact *contact;
+	GossipGroupChatPriv *priv;
+	GossipContact       *own_contact;
+	GossipContact       *contact;
+	GtkTreeModel        *model;
+	GtkTreeIter          iter;
 
 	if (gtk_tree_path_get_depth (path) == 1) {
 		/* Do nothing for role groups */
@@ -1515,7 +1602,12 @@
 			    COL_CONTACT, &contact,
 			    -1);
 
-	group_chat_private_chat_new (chat, contact);
+	priv = GET_PRIV (chat);
+	own_contact = gossip_chatroom_get_own_contact (priv->chatroom);
+
+	if (!gossip_contact_equal (own_contact, contact)) {
+		group_chat_private_chat_new (chat, contact);
+	}
 
 	g_object_unref (contact);
 }
@@ -1886,11 +1978,13 @@
 			      GossipGroupChat *chat)
 {
 	GossipGroupChatPriv *priv;
+	GossipContact       *own_contact;
 
 	priv = GET_PRIV (chat);
 
-	gossip_debug (DEBUG_DOMAIN, "Contact joined:'%s'",
-		      gossip_contact_get_id (contact));
+	gossip_debug (DEBUG_DOMAIN, "Contact joined:'%s' (refs:%d)",
+		      gossip_contact_get_id (contact),
+		      G_OBJECT (contact)->ref_count);
 
 	group_chat_contact_add (chat, contact);
 
@@ -1901,10 +1995,12 @@
 			  G_CALLBACK (group_chat_contact_updated_cb),
 			  chat);
 
-	g_signal_emit_by_name (chat, "contact_added", contact);
+	g_signal_emit_by_name (chat, "contact-added", contact);
 
 	/* Add event to chatroom */
-	if (!gossip_contact_equal (priv->own_contact, contact)) {
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+
+	if (!gossip_contact_equal (own_contact, contact)) {
 		gchar *str;
 
 		str = g_strdup_printf (_("%s has joined the room"),
@@ -1924,11 +2020,13 @@
 			    GossipGroupChat *chat)
 {
 	GossipGroupChatPriv *priv;
+	GossipContact       *own_contact;
 
 	priv = GET_PRIV (chat);
 
-	gossip_debug (DEBUG_DOMAIN, "Contact left:'%s'",
-		      gossip_contact_get_id (contact));
+	gossip_debug (DEBUG_DOMAIN, "Contact left:'%s' (refs:%d)",
+		      gossip_contact_get_id (contact),
+		      G_OBJECT (contact)->ref_count);
 
 	g_signal_handlers_disconnect_by_func (contact,
 					      group_chat_contact_updated_cb,
@@ -1942,8 +2040,11 @@
 	g_signal_emit_by_name (chat, "contact_removed", contact);
 
 	/* Add event to chatroom */
-	if (!gossip_contact_equal (priv->own_contact, contact)) {
+	own_contact = gossip_chatroom_get_own_contact (chatroom);
+
+	if (!gossip_contact_equal (own_contact, contact)) {
 		gchar *str;
+
 		str = g_strdup_printf (_("%s has left the room"),
 				       gossip_contact_get_name (contact));
 
@@ -1956,9 +2057,9 @@
 }
 
 static void
-group_chat_contact_info_changed_cb (GossipChatroom            *chatroom,
-				    GossipContact             *contact,
-				    GossipGroupChat           *chat)
+group_chat_contact_info_changed_cb (GossipChatroom  *chatroom,
+				    GossipContact   *contact,
+				    GossipGroupChat *chat)
 {
 	group_chat_contact_remove (chat, contact);
 	group_chat_contact_add (chat, contact);
@@ -2177,14 +2278,13 @@
 		handled_command = TRUE;
 	}
 	else if (g_ascii_strncasecmp (msg, "/kick ", 6) == 0 && strlen (msg) > 6) {
-		GSList        *contacts, *l;
+		GList         *contacts, *l;
 		GossipContact *contact = NULL;
 		const gchar   *nick;
 
 		nick = msg + 6;
 
-		contacts = gossip_chatroom_provider_get_contacts (priv->chatroom_provider,
-								  gossip_chatroom_get_id (priv->chatroom));
+		contacts = gossip_chatroom_get_contacts (priv->chatroom);
 
 		for (l = contacts; l && !contact; l = l->next) {
 			const gchar *name;
@@ -2202,6 +2302,8 @@
 			g_free (nick_caseless);
 			g_free (name_caseless);
 		}
+
+		g_list_free (contacts);
 		
 		if (contact) {
 			gossip_group_chat_contact_kick (chat, contact);
@@ -2386,7 +2488,7 @@
 	group_chat = GOSSIP_GROUP_CHAT (chat);
 	priv = GET_PRIV (group_chat);
 
-	return priv->own_contact;
+	return gossip_chatroom_get_own_contact (priv->chatroom);
 }
 
 static GossipChatroom *
@@ -2525,38 +2627,12 @@
 	}
 }
 
-/* Copied from the jabber backend for now since we don't have an abstraction for
- * "contacts" for group chats. Casefolds the node part (the part before @).
- */
-static gchar *
-jid_casefold_node (const gchar *str)
-{
-	gchar       *tmp;
-	gchar       *ret;
-	const gchar *at;
-
-	at = strchr (str, '@');
-	if (!at) {
-		return g_strdup (str);
-	}
-
-	tmp = g_utf8_casefold (str, at - str);
-	ret = g_strconcat (tmp, at, NULL);
-	g_free (tmp);
-
-	return ret;
-}
-
 GossipGroupChat *
 gossip_group_chat_new (GossipChatroomProvider *provider,
 		       GossipChatroom         *chatroom)
 {
 	GossipGroupChat     *chat;
 	GossipGroupChatPriv *priv;
-	gchar               *casefolded_id;
-	gchar               *own_contact_id;
-	GossipContact       *own_contact;
-	GossipChatroom      *chatroom_found;
 	GossipChatroomId     id;
 
 	g_return_val_if_fail (GOSSIP_IS_CHATROOM_PROVIDER (provider), NULL);
@@ -2577,37 +2653,11 @@
 		return chat;
 	}
 
-	chatroom_found = gossip_chatroom_provider_find (provider, chatroom);
-	if (chatroom_found) {
-		/* Check this group chat is not shown under another id */
-		id = gossip_chatroom_get_id (chatroom_found);
-		chat = g_hash_table_lookup (group_chats, GINT_TO_POINTER (id));
-		if (chat) {
-			gossip_chat_present (GOSSIP_CHAT (chat));
-			return chat;
-		}
-	}
-
-	/* FIXME: Jabberism --- Get important details like own contact, etc */
-	casefolded_id = jid_casefold_node (gossip_chatroom_get_id_str (chatroom));
-	own_contact_id = g_strdup_printf ("%s/%s",
-					  casefolded_id,
-					  gossip_chatroom_get_nick (chatroom));
-	g_free (casefolded_id);
-
-	own_contact = gossip_contact_new_full (GOSSIP_CONTACT_TYPE_TEMPORARY,
-					       gossip_chatroom_get_account (chatroom),
-					       own_contact_id,
-					       gossip_chatroom_get_nick (chatroom));
-	g_free (own_contact_id);
-
 	/* Create new group chat object */
 	chat = g_object_new (GOSSIP_TYPE_GROUP_CHAT, NULL);
 
 	priv = GET_PRIV (chat);
 
-	priv->own_contact = own_contact;
-
 	priv->chatroom = g_object_ref (chatroom);
 	priv->chatroom_provider = g_object_ref (provider);
 	priv->last_status = gossip_chatroom_get_status (chatroom);
@@ -2625,6 +2675,9 @@
 	g_signal_connect (provider, "chatroom-kicked",
 			  G_CALLBACK (group_chat_kicked_cb),
 			  chat);
+	g_signal_connect (provider, "chatroom-nick-changed",
+			  G_CALLBACK (group_chat_nick_changed_cb),
+			  chat);
 	g_signal_connect (provider, "chatroom-new-message",
 			  G_CALLBACK (group_chat_new_message_cb),
 			  chat);
@@ -2711,6 +2764,7 @@
 {
 	GossipGroupChatPriv *priv;
 	GossipContact       *contact;
+	GossipContact       *own_contact;
 	GtkWidget           *menu = NULL;
 
 	g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (group_chat), NULL);
@@ -2726,7 +2780,8 @@
 		return NULL;
 	}
 	
-	if (gossip_contact_equal (contact, priv->own_contact)) {
+	own_contact = gossip_chatroom_get_own_contact (priv->chatroom);
+	if (gossip_contact_equal (contact, own_contact)) {
 		g_object_unref (contact);
 		return NULL;
 	}

Modified: trunk/src/gossip-new-chatroom-dialog.c
==============================================================================
--- trunk/src/gossip-new-chatroom-dialog.c	(original)
+++ trunk/src/gossip-new-chatroom-dialog.c	Sat Jul  5 12:58:07 2008
@@ -736,13 +736,16 @@
 	
 	/* New or existing? */
 	if (new_chatroom) {
-		chatroom = g_object_new (GOSSIP_TYPE_CHATROOM,
-					 "account", account,
-					 "name", room,
-					 "server", server,
-					 "nick", nick,
-					 "room", room,
-					 NULL);
+		GossipChatroomManager *chatroom_manager;
+
+		chatroom_manager = gossip_session_get_chatroom_manager (session);
+		chatroom = gossip_chatroom_manager_find_or_create (chatroom_manager, 
+								   account, 
+								   server, 
+								   room,
+								   NULL);
+
+		gossip_chatroom_set_nick (chatroom, nick);
 
 		if (!G_STR_EMPTY (password)) {
 			gossip_chatroom_set_password (chatroom, password);

Modified: trunk/src/gossip-theme.c
==============================================================================
--- trunk/src/gossip-theme.c	(original)
+++ trunk/src/gossip-theme.c	Sat Jul  5 12:58:07 2008
@@ -461,15 +461,15 @@
 			if (!link_tag) {
 				gtk_text_buffer_insert (buffer, &iter,
 							tmp, -1);
-			} 
-
-			gtk_text_buffer_insert_with_tags_by_name (buffer,
-								  &iter,
-								  tmp,
-								  -1,
-								  link_tag,
-								  "link",
-								  NULL);
+			} else {
+				gtk_text_buffer_insert_with_tags_by_name (buffer,
+									  &iter,
+									  tmp,
+									  -1,
+									  link_tag,
+									  "link",
+									  NULL);
+			}
 
 			g_free (tmp);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]