diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml
--- a/doc/reference/libpurple/libpurple-docs.xml
+++ b/doc/reference/libpurple/libpurple-docs.xml
@@ -47,7 +47,6 @@
       <xi:include href="xml/contact.xml" />
       <xi:include href="xml/connection.xml" />
       <xi:include href="xml/conversation.xml" />
-      <xi:include href="xml/conversationtypes.xml" />
       <xi:include href="xml/conversations.xml" />
       <xi:include href="xml/countingnode.xml" />
       <xi:include href="xml/debug.xml" />
@@ -72,6 +71,7 @@
       <xi:include href="xml/purpleaccountusersplit.xml" />
       <xi:include href="xml/purpleattentiontype.xml" />
       <xi:include href="xml/purplebuddypresence.xml" />
+      <xi:include href="xml/purplechatconversation.xml" />
       <xi:include href="xml/purplechatuser.xml" />
       <xi:include href="xml/purplecredentialmanager.xml" />
       <xi:include href="xml/purplecredentialprovider.xml" />
diff --git a/libpurple/conversations.h b/libpurple/conversations.h
--- a/libpurple/conversations.h
+++ b/libpurple/conversations.h
@@ -33,7 +33,7 @@
  * @see_also: <link linkend="chapter-signals-conversation">Conversation signals</link>
  */
 
-#include "conversationtypes.h"
+#include "purplechatconversation.h"
 #include "server.h"
 
 G_BEGIN_DECLS
diff --git a/libpurple/conversationtypes.h b/libpurple/purplechatconversation.h
rename from libpurple/conversationtypes.h
rename to libpurple/purplechatconversation.h
--- a/libpurple/conversationtypes.h
+++ b/libpurple/purplechatconversation.h
@@ -1,4 +1,6 @@
-/* purple
+/*
+ * purple
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -15,28 +17,23 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
 # error "only <purple.h> may be included directly"
 #endif
 
-#ifndef PURPLE_CONVERSATION_TYPES_H
-#define PURPLE_CONVERSATION_TYPES_H
+#ifndef PURPLE_CHAT_CONVERSATION_H
+#define PURPLE_CHAT_CONVERSATION_H
 
 /**
- * SECTION:conversationtypes
- * @section_id: libpurple-conversationtypes
- * @short_description: <filename>conversationtypes.h</filename>
+ * SECTION:purplechatconversation
+ * @section_id: libpurple-purplechatconversation
+ * @short_description: <filename>purplechatconversation.h</filename>
  * @title: Chat Conversation Objects
  */
 
-/**************************************************************************/
-/* Data Structures                                                        */
-/**************************************************************************/
-
 #define PURPLE_TYPE_CHAT_CONVERSATION            (purple_chat_conversation_get_type())
 #define PURPLE_CHAT_CONVERSATION(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversation))
 #define PURPLE_CHAT_CONVERSATION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversationClass))
@@ -44,23 +41,21 @@
 #define PURPLE_IS_CHAT_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CHAT_CONVERSATION))
 #define PURPLE_CHAT_CONVERSATION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversationClass))
 
-typedef struct _PurpleChatConversation       PurpleChatConversation;
-typedef struct _PurpleChatConversationClass  PurpleChatConversationClass;
+typedef struct _PurpleChatConversation           PurpleChatConversation;
+typedef struct _PurpleChatConversationClass      PurpleChatConversationClass;
 
 #include "purplechatuser.h"
 #include "purpleimconversation.h"
 #include "conversation.h"
 
-/**************************************************************************/
-/* PurpleChatConversation                                                 */
-/**************************************************************************/
+G_BEGIN_DECLS
+
 /**
  * PurpleChatConversation:
  *
  * Structure representing a chat conversation instance.
  */
-struct _PurpleChatConversation
-{
+struct _PurpleChatConversation {
 	PurpleConversation parent_object;
 };
 
@@ -73,18 +68,9 @@
 	PurpleConversationClass parent_class;
 
 	/*< private >*/
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	gpointer reserved[4];
 };
 
-G_BEGIN_DECLS
-
-/**************************************************************************/
-/* Chat Conversation API                                                  */
-/**************************************************************************/
-
 /**
  * purple_chat_conversation_get_type:
  *
@@ -94,16 +80,15 @@
 
 /**
  * purple_chat_conversation_new:
- * @account: The account opening the conversation window on the purple
- *                user's end.
- * @name:    The name of the conversation.
+ * @account: The account opening the conversation window on the purple user's
+ *           end.
+ * @name: The name of the conversation.
  *
  * Creates a new chat conversation.
  *
  * Returns: The new conversation.
  */
-PurpleChatConversation *purple_chat_conversation_new(PurpleAccount *account,
-		const char *name);
+PurpleChatConversation *purple_chat_conversation_new(PurpleAccount *account, const gchar *name);
 
 /**
  * purple_chat_conversation_get_users:
@@ -126,8 +111,7 @@
  *
  * Returns: The count of users in the chat room.
  */
-guint
-purple_chat_conversation_get_users_count(PurpleChatConversation *chat);
+guint purple_chat_conversation_get_users_count(PurpleChatConversation *chat);
 
 /**
  * purple_chat_conversation_ignore:
@@ -136,7 +120,7 @@
  *
  * Ignores a user in a chat room.
  */
-void purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name);
+void purple_chat_conversation_ignore(PurpleChatConversation *chat, const gchar *name);
 
 /**
  * purple_chat_conversation_unignore:
@@ -145,11 +129,11 @@
  *
  * Unignores a user in a chat room.
  */
-void purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name);
+void purple_chat_conversation_unignore(PurpleChatConversation *chat, const gchar *name);
 
 /**
  * purple_chat_conversation_set_ignored:
- * @chat:    The chat.
+ * @chat: The chat.
  * @ignored: (element-type utf8): The list of ignored users.
  *
  * Sets the list of ignored users in the chat room.
@@ -181,10 +165,9 @@
  * formatting.
  *
  * Returns: The ignored user if found, complete with prefixes, or %NULL
- *         if not found.
+ *          if not found.
  */
-const char *purple_chat_conversation_get_ignored_user(PurpleChatConversation *chat,
-											const char *user);
+const gchar *purple_chat_conversation_get_ignored_user(PurpleChatConversation *chat, const gchar *user);
 
 /**
  * purple_chat_conversation_is_ignored_user:
@@ -195,19 +178,17 @@
  *
  * Returns: %TRUE if the user is in the ignore list; %FALSE otherwise.
  */
-gboolean purple_chat_conversation_is_ignored_user(PurpleChatConversation *chat,
-										const char *user);
+gboolean purple_chat_conversation_is_ignored_user(PurpleChatConversation *chat, const gchar *user);
 
 /**
  * purple_chat_conversation_set_topic:
- * @chat:  The chat.
- * @who:   The user that set the topic.
+ * @chat: The chat.
+ * @who: The user that set the topic.
  * @topic: The topic.
  *
  * Sets the chat room's topic.
  */
-void purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who,
-							  const char *topic);
+void purple_chat_conversation_set_topic(PurpleChatConversation *chat, const gchar *who, const gchar *topic);
 
 /**
  * purple_chat_conversation_get_topic:
@@ -217,7 +198,7 @@
  *
  * Returns: The chat's topic.
  */
-const char *purple_chat_conversation_get_topic(PurpleChatConversation *chat);
+const gchar *purple_chat_conversation_get_topic(PurpleChatConversation *chat);
 
 /**
  * purple_chat_conversation_get_topic_who:
@@ -227,40 +208,38 @@
  *
  * Returns: Who set the topic.
  */
-const char *purple_chat_conversation_get_topic_who(PurpleChatConversation *chat);
+const gchar *purple_chat_conversation_get_topic_who(PurpleChatConversation *chat);
 
 /**
  * purple_chat_conversation_set_id:
  * @chat: The chat.
- * @id:   The ID.
+ * @id: The ID.
  *
  * Sets the chat room's ID.
  */
-void purple_chat_conversation_set_id(PurpleChatConversation *chat, int id);
+void purple_chat_conversation_set_id(PurpleChatConversation *chat, gint id);
 
 /**
  * purple_chat_conversation_get_id:
  * @chat: The chat.
  *
- * Returns the chat room's ID.
+ * Gets the chat room's ID.
  *
  * Returns: The ID.
  */
-int purple_chat_conversation_get_id(PurpleChatConversation *chat);
+gint purple_chat_conversation_get_id(PurpleChatConversation *chat);
 
 /**
  * purple_chat_conversation_add_user:
- * @chat:        The chat.
- * @user:        The user to add.
- * @extra_msg:   An extra message to display with the join message.
- * @flags:       The users flags
+ * @chat: The chat.
+ * @user: The user to add.
+ * @extra_msg: An extra message to display with the join message.
+ * @flags: The users flags
  * @new_arrival: Decides whether or not to show a join notice.
  *
  * Adds a user to a chat.
  */
-void purple_chat_conversation_add_user(PurpleChatConversation *chat, const char *user,
-							 const char *extra_msg, PurpleChatUserFlags flags,
-							 gboolean new_arrival);
+void purple_chat_conversation_add_user(PurpleChatConversation *chat, const gchar *user, const gchar *extra_msg, PurpleChatUserFlags flags, gboolean new_arrival);
 
 /**
  * purple_chat_conversation_add_users:
@@ -282,55 +261,50 @@
  * The data is copied from @users, @extra_msgs, and @flags, so it is up to
  * the caller to free this list after calling this function.
  */
-void purple_chat_conversation_add_users(PurpleChatConversation *chat,
-		GList *users, GList *extra_msgs, GList *flags, gboolean new_arrivals);
+void purple_chat_conversation_add_users(PurpleChatConversation *chat, GList *users, GList *extra_msgs, GList *flags, gboolean new_arrivals);
 
 /**
  * purple_chat_conversation_rename_user:
- * @chat:     The chat.
+ * @chat: The chat.
  * @old_user: The old username.
  * @new_user: The new username.
  *
  * Renames a user in a chat.
  */
-void purple_chat_conversation_rename_user(PurpleChatConversation *chat,
-		const char *old_user, const char *new_user);
+void purple_chat_conversation_rename_user(PurpleChatConversation *chat, const gchar *old_user, const gchar *new_user);
 
 /**
  * purple_chat_conversation_remove_user:
- * @chat:   The chat.
- * @user:   The user that is being removed.
+ * @chat: The chat.
+ * @user: The user that is being removed.
  * @reason: The optional reason given for the removal. Can be %NULL.
  *
  * Removes a user from a chat, optionally with a reason.
  *
  * It is up to the developer to free this list after calling this function.
  */
-void purple_chat_conversation_remove_user(PurpleChatConversation *chat,
-		const char *user, const char *reason);
+void purple_chat_conversation_remove_user(PurpleChatConversation *chat, const gchar *user, const gchar *reason);
 
 /**
  * purple_chat_conversation_remove_users:
- * @chat:   The chat.
+ * @chat: The chat.
  * @users: (element-type utf8): The users that are being removed.
  * @reason: The optional reason given for the removal. Can be %NULL.
  *
  * Removes a list of users from a chat, optionally with a single reason.
  */
-void purple_chat_conversation_remove_users(PurpleChatConversation *chat,
-		GList *users, const char *reason);
+void purple_chat_conversation_remove_users(PurpleChatConversation *chat, GList *users, const gchar *reason);
 
 /**
  * purple_chat_conversation_has_user:
- * @chat:   The chat.
- * @user:   The user to look for.
+ * @chat: The chat.
+ * @user: The user to look for.
  *
  * Checks if a user is in a chat
  *
- * Returns: TRUE if the user is in the chat, FALSE if not
+ * Returns: %TRUE if the user is in the chat, %FALSE if not
  */
-gboolean purple_chat_conversation_has_user(PurpleChatConversation *chat,
-		const char *user);
+gboolean purple_chat_conversation_has_user(PurpleChatConversation *chat, const gchar *user);
 
 /**
  * purple_chat_conversation_clear_users:
@@ -347,8 +321,7 @@
  *
  * Sets your nickname (used for hilighting) for a chat.
  */
-void purple_chat_conversation_set_nick(PurpleChatConversation *chat,
-		const char *nick);
+void purple_chat_conversation_set_nick(PurpleChatConversation *chat, const gchar *nick);
 
 /**
  * purple_chat_conversation_get_nick:
@@ -356,9 +329,9 @@
  *
  * Gets your nickname (used for hilighting) for a chat.
  *
- * Returns:  The nick.
+ * Returns: The nick.
  */
-const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat);
+const gchar *purple_chat_conversation_get_nick(PurpleChatConversation *chat);
 
 /**
  * purple_chat_conversation_leave:
@@ -376,39 +349,35 @@
  *
  * Find a chat user in a chat
  *
- * Returns: (transfer none):
- *         The @PurpleChatUser with the name refered by @name.
+ * Returns: (transfer none): The #PurpleChatUser with the name referred by
+ *          @name.
  */
-PurpleChatUser *purple_chat_conversation_find_user(PurpleChatConversation *chat,
-		const char *name);
+PurpleChatUser *purple_chat_conversation_find_user(PurpleChatConversation *chat, const gchar *name);
 
 /**
  * purple_chat_conversation_invite_user:
- * @chat:     The chat.
- * @user:     The user to invite to the chat.
- * @message:  The message to send with the invitation.
- * @confirm:  Prompt before sending the invitation. The user is always
- *            prompted if either \a user or \a message is %NULL.
+ * @chat: The chat.
+ * @user: The user to invite to the chat.
+ * @message: The message to send with the invitation.
+ * @confirm: Prompt before sending the invitation. The user is always prompted
+ *           if either @user or @message is %NULL.
  *
  * Invite a user to a chat.
  * The user will be prompted to enter the user's name or a message if one is
  * not given.
  */
-void purple_chat_conversation_invite_user(PurpleChatConversation *chat,
-		const char *user, const char *message, gboolean confirm);
+void purple_chat_conversation_invite_user(PurpleChatConversation *chat, const gchar *user, const gchar *message, gboolean confirm);
 
 /**
  * purple_chat_conversation_has_left:
  * @chat: The chat.
  *
- * Returns true if we're no longer in this chat,
- * and just left the window open.
+ * Gets whether we're no longer in this chat, and just left the window open.
  *
- * Returns: %TRUE if we left the chat already, %FALSE if
- * we're still there.
+ * Returns: %TRUE if we left the chat already, %FALSE if we're still there.
  */
 gboolean purple_chat_conversation_has_left(PurpleChatConversation *chat);
 
 G_END_DECLS
 
-#endif /* PURPLE_CONVERSATION_TYPES_H */
+#endif /* PURPLE_CHAT_CONVERSATION_H */
diff --git a/libpurple/conversationtypes.c b/libpurple/purplechatconversation.c
rename from libpurple/conversationtypes.c
rename to libpurple/purplechatconversation.c
--- a/libpurple/conversationtypes.c
+++ b/libpurple/purplechatconversation.c
@@ -23,20 +23,12 @@
 #include <glib/gi18n-lib.h>
 
 #include "internal.h"
-#include "conversationtypes.h"
 #include "debug.h"
 #include "enums.h"
+#include "purplechatconversation.h"
 #include "purpleprivate.h"
 
-/**************************************************************************/
-/* PurpleChatConversation                                                 */
-/**************************************************************************/
-
-/*
- * Data specific to Chats.
- */
-typedef struct
-{
+typedef struct {
 	GList *ignored;     /* Ignored users.                            */
 	char  *who;         /* The person who set the topic.             */
 	char  *topic;       /* The topic.                                */
@@ -48,66 +40,363 @@
 
 /* Chat Property enums */
 enum {
-	CHAT_PROP_0,
-	CHAT_PROP_TOPIC_WHO,
-	CHAT_PROP_TOPIC,
-	CHAT_PROP_ID,
-	CHAT_PROP_NICK,
-	CHAT_PROP_LEFT,
-	CHAT_PROP_LAST
+	PROP_0,
+	PROP_TOPIC_WHO,
+	PROP_TOPIC,
+	PROP_CHAT_ID,
+	PROP_NICK,
+	PROP_LEFT,
+	N_PROPERTIES
 };
-
-static GParamSpec *chat_properties[CHAT_PROP_LAST];
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
 
 G_DEFINE_TYPE_WITH_PRIVATE(PurpleChatConversation, purple_chat_conversation,
-		PURPLE_TYPE_CONVERSATION);
+                           PURPLE_TYPE_CONVERSATION);
 
 /**************************************************************************
- * Chat Conversation API
+ * Helpers
  **************************************************************************/
 static guint
-_purple_conversation_user_hash(gconstpointer data)
-{
-	const gchar *name = data;
+purple_conversation_user_hash(gconstpointer data) {
 	gchar *collated;
 	guint hash;
 
-	collated = g_utf8_collate_key(name, -1);
-	hash     = g_str_hash(collated);
+	collated = g_utf8_collate_key((const gchar *)data, -1);
+	hash = g_str_hash(collated);
 	g_free(collated);
+
 	return hash;
 }
 
 static gboolean
-_purple_conversation_user_equal(gconstpointer a, gconstpointer b)
-{
+purple_conversation_user_equal(gconstpointer a, gconstpointer b) {
 	return !g_utf8_collate(a, b);
 }
 
+static void
+purple_chat_conversation_clear_users_helper(gpointer data, gpointer user_data)
+{
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(user_data);
+	const gchar *name = (const gchar *)data;
+	gpointer handle = purple_conversations_get_handle();
+
+	purple_signal_emit(handle, "chat-user-leaving", chat, name, NULL);
+	purple_signal_emit(handle, "chat-user-left", chat, name, NULL);
+}
+
+/******************************************************************************
+ * PurpleConversation Implementation
+ *****************************************************************************/
+static void
+chat_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg) {
+	PurpleChatConversation *chat_conv = PURPLE_CHAT_CONVERSATION(conv);
+	PurpleChatConversationPrivate *priv = NULL;
+	PurpleMessageFlags flags;
+	const gchar *author = NULL;
+
+	g_return_if_fail(msg != NULL);
+
+	priv = purple_chat_conversation_get_instance_private(chat_conv);
+
+	/* Don't display this if the person who wrote it is ignored. */
+	author = purple_message_get_author(msg);
+	if(purple_chat_conversation_is_ignored_user(chat_conv, author)) {
+		return;
+	}
+
+	flags = purple_message_get_flags(msg);
+	if(flags & PURPLE_MESSAGE_RECV) {
+		if(purple_utf8_has_word(purple_message_get_contents(msg), priv->nick)) {
+			flags |= PURPLE_MESSAGE_NICK;
+			purple_message_set_flags(msg, flags);
+		}
+	}
+
+	_purple_conversation_write_common(conv, msg);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_chat_conversation_set_property(GObject *obj, guint param_id,
+                                      const GValue *value, GParamSpec *pspec)
+{
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+
+	switch(param_id) {
+		case PROP_CHAT_ID:
+			purple_chat_conversation_set_id(chat, g_value_get_int(value));
+			break;
+		case PROP_NICK:
+			purple_chat_conversation_set_nick(chat, g_value_get_string(value));
+			break;
+		case PROP_LEFT:
+			if(g_value_get_boolean(value)) {
+				purple_chat_conversation_leave(chat);
+			}
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_chat_conversation_get_property(GObject *obj, guint param_id,
+                                      GValue *value, GParamSpec *pspec)
+{
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+
+	switch(param_id) {
+		case PROP_TOPIC_WHO:
+			g_value_set_string(value, purple_chat_conversation_get_topic_who(chat));
+			break;
+		case PROP_TOPIC:
+			g_value_set_string(value, purple_chat_conversation_get_topic(chat));
+			break;
+		case PROP_CHAT_ID:
+			g_value_set_int(value, purple_chat_conversation_get_id(chat));
+			break;
+		case PROP_NICK:
+			g_value_set_string(value, purple_chat_conversation_get_nick(chat));
+			break;
+		case PROP_LEFT:
+			g_value_set_boolean(value, purple_chat_conversation_has_left(chat));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_chat_conversation_init(PurpleChatConversation *chat) {
+	PurpleChatConversationPrivate *priv = NULL;
+
+	priv = purple_chat_conversation_get_instance_private(chat);
+
+	priv->users = g_hash_table_new_full(purple_conversation_user_hash,
+	                                    purple_conversation_user_equal,
+	                                    g_free, g_object_unref);
+}
+
+static void
+purple_chat_conversation_constructed(GObject *obj) {
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+	PurpleAccount *account = NULL;
+	PurpleConnection *connection = NULL;
+	const gchar *display_name = NULL;
+
+	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->constructed(obj);
+
+	g_object_get(obj, "account", &account, NULL);
+
+	connection = purple_account_get_connection(account);
+	display_name = purple_connection_get_display_name(connection);
+	if(display_name != NULL) {
+		purple_chat_conversation_set_nick(chat, display_name);
+	} else {
+		const gchar *username = purple_account_get_username(account);
+
+		purple_chat_conversation_set_nick(chat, username);
+	}
+
+	if(purple_prefs_get_bool("/purple/logging/log_chats")) {
+		purple_conversation_set_logging(PURPLE_CONVERSATION(chat), TRUE);
+	}
+
+	g_object_unref(account);
+}
+
+static void
+purple_chat_conversation_dispose(GObject *obj) {
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+	PurpleChatConversationPrivate *priv = NULL;
+
+	priv = purple_chat_conversation_get_instance_private(chat);
+
+	g_hash_table_remove_all(priv->users);
+
+	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->dispose(obj);
+}
+
+static void
+purple_chat_conversation_finalize(GObject *obj) {
+	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+	PurpleConversation *conv = PURPLE_CONVERSATION(chat);
+	PurpleConnection *gc = purple_conversation_get_connection(conv);
+	PurpleChatConversationPrivate *priv = NULL;
+
+	priv = purple_chat_conversation_get_instance_private(chat);
+
+	if(PURPLE_IS_CONNECTION(gc)) {
+		/* Still connected */
+		gint chat_id = purple_chat_conversation_get_id(chat);
+
+		/*
+		 * Close the window when the user tells us to, and let the protocol
+		 * deal with the internals on its own time. Don't do this if the
+		 * protocol already knows it left the chat.
+		 */
+		if(!purple_chat_conversation_has_left(chat)) {
+			purple_serv_chat_leave(gc, chat_id);
+		}
+
+		/*
+		 * If they didn't call purple_serv_got_chat_left by now, it's too late.
+		 * So we better do it for them before we destroy the thing.
+		 */
+		if(!purple_chat_conversation_has_left(chat)) {
+			purple_serv_got_chat_left(gc, chat_id);
+		}
+	}
+
+	g_clear_pointer(&priv->users, g_hash_table_destroy);
+
+	g_list_free_full(priv->ignored, g_free);
+	priv->ignored = NULL;
+
+	g_clear_pointer(&priv->who, g_free);
+	g_clear_pointer(&priv->topic, g_free);
+	g_clear_pointer(&priv->nick, g_free);
+
+	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->finalize(obj);
+}
+
+static void
+purple_chat_conversation_class_init(PurpleChatConversationClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
+
+	obj_class->constructed = purple_chat_conversation_constructed;
+	obj_class->dispose = purple_chat_conversation_dispose;
+	obj_class->finalize = purple_chat_conversation_finalize;
+	obj_class->get_property = purple_chat_conversation_get_property;
+	obj_class->set_property = purple_chat_conversation_set_property;
+
+	conv_class->write_message = chat_conversation_write_message;
+
+	/**
+	 * PurpleChatConversation::topic-who:
+	 *
+	 * The username who changed the topic last.
+	 */
+	properties[PROP_TOPIC_WHO] = g_param_spec_string(
+		"topic-who", "who set topic",
+		"Who set the topic of the chat.",
+		NULL,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleChatConversation::topic:
+	 *
+	 * The text of the topic.
+	 */
+	properties[PROP_TOPIC] = g_param_spec_string(
+		"topic", "topic",
+		"The topic of the chat.",
+		NULL,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleChatConversation::chat-id:
+	 *
+	 * The identifier of the chat.
+	 */
+	properties[PROP_CHAT_ID] = g_param_spec_int(
+		"chat-id", "chat id",
+		"The identifier of the chat.",
+		G_MININT, G_MAXINT, 0,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleChatConversation::nick:
+	 *
+	 * The nickname of the user in the chat.
+	 */
+	properties[PROP_NICK] = g_param_spec_string(
+		"nick", "nick",
+		"The nickname of the user in a chat.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleChatConversation::left:
+	 *
+	 * Whether the user has left the chat or not.
+	 */
+	properties[PROP_LEFT] = g_param_spec_boolean(
+		"left", "left",
+		"Whether the user has left the chat.",
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleChatConversation *
+purple_chat_conversation_new(PurpleAccount *account, const gchar *name) {
+	PurpleChatConversation *chat = NULL;
+	PurpleConnection *connection = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	/* Check if this conversation already exists. */
+	chat = purple_conversations_find_chat_with_account(name, account);
+	if(PURPLE_IS_CHAT_CONVERSATION(chat)) {
+		if(!purple_chat_conversation_has_left(chat)) {
+			purple_debug_warning("chat-conversation", "A chat named %s "
+			                     "already exists.", name);
+			return NULL;
+		}
+	}
+
+	connection = purple_account_get_connection(account);
+	if(!PURPLE_IS_CONNECTION(connection)) {
+		purple_debug_warning("chat-conversation", "Refusing to create chat "
+		                     "for disconnected account %s",
+		                     purple_account_get_username(account));
+		return NULL;
+	}
+
+	return PURPLE_CHAT_CONVERSATION(g_object_new(
+		PURPLE_TYPE_CHAT_CONVERSATION,
+		"account", account,
+		"name", name,
+		"title", name,
+		NULL));
+}
+
 GList *
-purple_chat_conversation_get_users(PurpleChatConversation *chat)
-{
+purple_chat_conversation_get_users(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return g_hash_table_get_values(priv->users);
 }
 
 guint
-purple_chat_conversation_get_users_count(PurpleChatConversation *chat)
-{
+purple_chat_conversation_get_users_count(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), 0);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return g_hash_table_size(priv->users);
 }
 
 void
-purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name)
+purple_chat_conversation_ignore(PurpleChatConversation *chat,
+                                const gchar *name)
 {
 	PurpleChatConversationPrivate *priv = NULL;
 
@@ -117,15 +406,16 @@
 	priv = purple_chat_conversation_get_instance_private(chat);
 
 	/* Make sure the user isn't already ignored. */
-	if (purple_chat_conversation_is_ignored_user(chat, name))
+	if(purple_chat_conversation_is_ignored_user(chat, name)) {
 		return;
+	}
 
-	purple_chat_conversation_set_ignored(chat,
-		g_list_append(priv->ignored, g_strdup(name)));
+	priv->ignored = g_list_prepend(priv->ignored, g_strdup(name));
 }
 
 void
-purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name)
+purple_chat_conversation_unignore(PurpleChatConversation *chat,
+                                  const gchar *name)
 {
 	PurpleChatConversationPrivate *priv = NULL;
 	GList *item;
@@ -136,32 +426,34 @@
 	priv = purple_chat_conversation_get_instance_private(chat);
 
 	/* Make sure the user is actually ignored. */
-	if (!purple_chat_conversation_is_ignored_user(chat, name))
+	if(!purple_chat_conversation_is_ignored_user(chat, name)) {
 		return;
+	}
 
-	item = g_list_find(purple_chat_conversation_get_ignored(chat),
+	item = g_list_find(priv->ignored,
 					   purple_chat_conversation_get_ignored_user(chat, name));
 	g_free(item->data);
 
-	purple_chat_conversation_set_ignored(chat,
-		g_list_delete_link(priv->ignored, item));
+	priv->ignored = g_list_delete_link(priv->ignored, item);
 }
 
 GList *
-purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored)
+purple_chat_conversation_set_ignored(PurpleChatConversation *chat,
+                                     GList *ignored)
 {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	priv->ignored = ignored;
+
 	return ignored;
 }
 
 GList *
-purple_chat_conversation_get_ignored(PurpleChatConversation *chat)
-{
+purple_chat_conversation_get_ignored(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
@@ -170,30 +462,34 @@
 	return priv->ignored;
 }
 
-const char *
-purple_chat_conversation_get_ignored_user(PurpleChatConversation *chat, const char *user)
+const gchar *
+purple_chat_conversation_get_ignored_user(PurpleChatConversation *chat,
+                                          const gchar *user)
 {
 	GList *ignored;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 	g_return_val_if_fail(user != NULL, NULL);
 
-	for (ignored = purple_chat_conversation_get_ignored(chat);
-		 ignored != NULL;
-		 ignored = ignored->next) {
-
-		const char *ign = (const char *)ignored->data;
+	ignored = purple_chat_conversation_get_ignored(chat);
+	for(; ignored != NULL; ignored = ignored->next) {
+		const gchar *ign = (const gchar *)ignored->data;
 
-		if (!purple_utf8_strcasecmp(user, ign) ||
-			((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
+		if(!purple_utf8_strcasecmp(user, ign) ||
+			((*ign == '+' || *ign == '%') &&
+			  !purple_utf8_strcasecmp(user, ign + 1)))
+		{
 			return ign;
+		}
 
-		if (*ign == '@') {
+		if(*ign == '@') {
 			ign++;
 
-			if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
-				(*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
+			if((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
+			   (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
+			{
 				return ign;
+			}
 		}
 	}
 
@@ -201,7 +497,8 @@
 }
 
 gboolean
-purple_chat_conversation_is_ignored_user(PurpleChatConversation *chat, const char *user)
+purple_chat_conversation_is_ignored_user(PurpleChatConversation *chat,
+                                         const gchar *user)
 {
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
 	g_return_val_if_fail(user != NULL, FALSE);
@@ -210,7 +507,8 @@
 }
 
 void
-purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who, const char *topic)
+purple_chat_conversation_set_topic(PurpleChatConversation *chat,
+                                   const gchar *who, const gchar *topic)
 {
 	PurpleChatConversationPrivate *priv = NULL;
 	GObject *obj;
@@ -219,50 +517,49 @@
 
 	priv = purple_chat_conversation_get_instance_private(chat);
 
-	g_free(priv->who);
-	g_free(priv->topic);
+	g_clear_pointer(&priv->who, g_free);
+	g_clear_pointer(&priv->topic, g_free);
 
-	priv->who   = g_strdup(who);
+	priv->who = g_strdup(who);
 	priv->topic = g_strdup(topic);
 
 	obj = G_OBJECT(chat);
 	g_object_freeze_notify(obj);
-	g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC_WHO]);
-	g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC]);
+	g_object_notify_by_pspec(obj, properties[PROP_TOPIC_WHO]);
+	g_object_notify_by_pspec(obj, properties[PROP_TOPIC]);
 	g_object_thaw_notify(obj);
 
 	purple_conversation_update(PURPLE_CONVERSATION(chat),
-							 PURPLE_CONVERSATION_UPDATE_TOPIC);
+	                           PURPLE_CONVERSATION_UPDATE_TOPIC);
 
 	purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
-					 chat, priv->who, priv->topic);
+	                   chat, priv->who, priv->topic);
 }
 
-const char *
-purple_chat_conversation_get_topic(PurpleChatConversation *chat)
-{
+const gchar *
+purple_chat_conversation_get_topic(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return priv->topic;
 }
 
-const char *
-purple_chat_conversation_get_topic_who(PurpleChatConversation *chat)
-{
+const gchar *
+purple_chat_conversation_get_topic_who(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return priv->who;
 }
 
 void
-purple_chat_conversation_set_id(PurpleChatConversation *chat, int id)
-{
+purple_chat_conversation_set_id(PurpleChatConversation *chat, gint id) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
@@ -270,59 +567,32 @@
 	priv = purple_chat_conversation_get_instance_private(chat);
 	priv->id = id;
 
-	g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_ID]);
+	g_object_notify_by_pspec(G_OBJECT(chat), properties[PROP_CHAT_ID]);
 }
 
-int
-purple_chat_conversation_get_id(PurpleChatConversation *chat)
-{
+gint
+purple_chat_conversation_get_id(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), -1);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return priv->id;
 }
 
-static void
-chat_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
+void
+purple_chat_conversation_add_user(PurpleChatConversation *chat,
+                                  const gchar *user, const gchar *extra_msg,
+                                  PurpleChatUserFlags flags,
+                                  gboolean new_arrival)
 {
-	PurpleChatConversationPrivate *priv = NULL;
-	PurpleMessageFlags flags;
-
-	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv));
-	g_return_if_fail(msg != NULL);
-
-	priv = purple_chat_conversation_get_instance_private(PURPLE_CHAT_CONVERSATION(conv));
-
-	/* Don't display this if the person who wrote it is ignored. */
-	if (purple_message_get_author(msg) && purple_chat_conversation_is_ignored_user(
-		PURPLE_CHAT_CONVERSATION(conv), purple_message_get_author(msg)))
-	{
-		return;
-	}
-
-	flags = purple_message_get_flags(msg);
-	if (flags & PURPLE_MESSAGE_RECV) {
-		if (purple_utf8_has_word(purple_message_get_contents(msg), priv->nick)) {
-			flags |= PURPLE_MESSAGE_NICK;
-			purple_message_set_flags(msg, flags);
-		}
-	}
-
-	_purple_conversation_write_common(conv, msg);
-}
-
-void
-purple_chat_conversation_add_user(PurpleChatConversation *chat, const char *user,
-						const char *extra_msg, PurpleChatUserFlags flags,
-						gboolean new_arrival)
-{
-	GList *users = g_list_append(NULL, (char *)user);
-	GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
+	GList *users = g_list_append(NULL, (gchar *)user);
+	GList *extra_msgs = g_list_append(NULL, (gchar *)extra_msg);
 	GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
 
-	purple_chat_conversation_add_users(chat, users, extra_msgs, flags2, new_arrival);
+	purple_chat_conversation_add_users(chat, users, extra_msgs, flags2,
+	                                   new_arrival);
 
 	g_list_free(users);
 	g_list_free(extra_msgs);
@@ -330,8 +600,9 @@
 }
 
 void
-purple_chat_conversation_add_users(PurpleChatConversation *chat, GList *users, GList *extra_msgs,
-						 GList *flags, gboolean new_arrivals)
+purple_chat_conversation_add_users(PurpleChatConversation *chat, GList *users,
+                                   GList *extra_msgs, GList *flags,
+                                   gboolean new_arrivals)
 {
 	PurpleConversation *conv;
 	PurpleConversationUiOps *ops;
@@ -340,51 +611,53 @@
 	PurpleAccount *account;
 	PurpleConnection *gc;
 	PurpleProtocol *protocol;
-	GList *ul, *fl;
 	GList *cbuddies = NULL;
+	gpointer handle;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
 	g_return_if_fail(users != NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
 	conv = PURPLE_CONVERSATION(chat);
-	ops  = purple_conversation_get_ui_ops(conv);
+	ops = purple_conversation_get_ui_ops(conv);
 
 	account = purple_conversation_get_account(conv);
 	gc = purple_conversation_get_connection(conv);
 	g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+
 	protocol = purple_connection_get_protocol(gc);
 	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
 
-	ul = users;
-	fl = flags;
-	while ((ul != NULL) && (fl != NULL)) {
-		const char *user = (const char *)ul->data;
-		const char *alias = user;
+	handle = purple_conversations_get_handle();
+
+	while(users != NULL && flags != NULL) {
+		const gchar *user = (const gchar *)users->data;
+		const gchar *alias = user;
 		gboolean quiet;
-		PurpleChatUserFlags flag = GPOINTER_TO_INT(fl->data);
-		const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
+		PurpleChatUserFlags flag = GPOINTER_TO_INT(flags->data);
+		const gchar *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
 
 		if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
-			if (purple_strequal(priv->nick, purple_normalize(account, user))) {
-				const char *alias2 = purple_account_get_private_alias(account);
-				if (alias2 != NULL)
+			if(purple_strequal(priv->nick, purple_normalize(account, user))) {
+				const gchar *alias2 = purple_account_get_private_alias(account);
+				if(alias2 != NULL) {
 					alias = alias2;
-				else
-				{
-					const char *display_name = purple_connection_get_display_name(gc);
-					if (display_name != NULL)
+				} else {
+					const gchar *display_name = purple_connection_get_display_name(gc);
+					if(display_name != NULL) {
 						alias = display_name;
+					}
 				}
 			} else {
 				PurpleBuddy *buddy;
-				if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
+				if((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL) {
 					alias = purple_buddy_get_contact_alias(buddy);
+				}
 			}
 		}
 
-		quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
-						 "chat-user-joining", chat, user, flag)) ||
+		quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(handle,
+		                        "chat-user-joining", chat, user, flag)) ||
 				purple_chat_conversation_is_ignored_user(chat, user);
 
 		chatuser = purple_chat_user_new(chat, user, alias, flag);
@@ -395,44 +668,48 @@
 
 		cbuddies = g_list_prepend(cbuddies, chatuser);
 
-		if (!quiet && new_arrivals) {
-			char *alias_esc = g_markup_escape_text(alias, -1);
-			char *tmp;
+		if(!quiet && new_arrivals) {
+			gchar *alias_esc = g_markup_escape_text(alias, -1);
+			gchar *tmp;
 
-			if (extra_msg == NULL)
+			if(extra_msg == NULL) {
 				tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
-			else {
-				char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
+			} else {
+				gchar *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
 				tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
 				                      alias_esc, extra_msg_esc);
 				g_free(extra_msg_esc);
 			}
 			g_free(alias_esc);
 
-			purple_conversation_write_system_message(
-				conv, tmp, PURPLE_MESSAGE_NO_LINKIFY);
+			purple_conversation_write_system_message(conv, tmp,
+			                                         PURPLE_MESSAGE_NO_LINKIFY);
 			g_free(tmp);
 		}
 
-		purple_signal_emit(purple_conversations_get_handle(),
-						 "chat-user-joined", chat, user, flag, new_arrivals);
-		ul = ul->next;
-		fl = fl->next;
-		if (extra_msgs != NULL)
+		purple_signal_emit(handle, "chat-user-joined", chat, user, flag,
+		                   new_arrivals);
+
+		users = users->next;
+		flags = flags->next;
+		if(extra_msgs != NULL) {
 			extra_msgs = extra_msgs->next;
+		}
 	}
 
 	cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_chat_user_compare);
 
-	if (ops != NULL && ops->chat_add_users != NULL)
+	if(ops != NULL && ops->chat_add_users != NULL) {
 		ops->chat_add_users(chat, cbuddies, new_arrivals);
+	}
 
 	g_list_free(cbuddies);
 }
 
 void
-purple_chat_conversation_rename_user(PurpleChatConversation *chat, const char *old_user,
-						   const char *new_user)
+purple_chat_conversation_rename_user(PurpleChatConversation *chat,
+                                     const gchar *old_user,
+                                     const gchar *new_user)
 {
 	PurpleConversation *conv;
 	PurpleConversationUiOps *ops;
@@ -442,8 +719,8 @@
 	PurpleChatUser *cb;
 	PurpleChatUserFlags flags;
 	PurpleChatConversationPrivate *priv;
-	const char *new_alias = new_user;
-	char tmp[BUF_LONG];
+	const gchar *new_alias = new_user;
+	gchar tmp[BUF_LONG];
 	gboolean is_me = FALSE;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
@@ -451,8 +728,8 @@
 	g_return_if_fail(new_user != NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
-	conv    = PURPLE_CONVERSATION(chat);
-	ops     = purple_conversation_get_ui_ops(conv);
+	conv = PURPLE_CONVERSATION(chat);
+	ops = purple_conversation_get_ui_ops(conv);
 	account = purple_conversation_get_account(conv);
 
 	gc = purple_conversation_get_connection(conv);
@@ -460,27 +737,28 @@
 	protocol = purple_connection_get_protocol(gc);
 	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
 
-	if (purple_strequal(priv->nick, purple_normalize(account, old_user))) {
-		const char *alias;
+	if(purple_strequal(priv->nick, purple_normalize(account, old_user))) {
+		const gchar *alias;
 
 		/* Note this for later. */
 		is_me = TRUE;
 
 		if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
 			alias = purple_account_get_private_alias(account);
-			if (alias != NULL)
+			if(alias != NULL) {
 				new_alias = alias;
-			else
-			{
-				const char *display_name = purple_connection_get_display_name(gc);
-				if (display_name != NULL)
+			} else {
+				const gchar *display_name = purple_connection_get_display_name(gc);
+				if(display_name != NULL) {
 					new_alias = display_name;
+				}
 			}
 		}
-	} else if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
+	} else if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
 		PurpleBuddy *buddy;
-		if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
+		if((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL) {
 			new_alias = purple_buddy_get_contact_alias(buddy);
+		}
 	}
 
 	flags = purple_chat_user_get_flags(purple_chat_conversation_find_user(chat, old_user));
@@ -489,64 +767,70 @@
 	g_hash_table_replace(priv->users,
 		g_strdup(purple_chat_user_get_name(cb)), cb);
 
-	if (ops != NULL && ops->chat_rename_user != NULL)
+	if(ops != NULL && ops->chat_rename_user != NULL) {
 		ops->chat_rename_user(chat, old_user, new_user, new_alias);
+	}
 
 	cb = purple_chat_conversation_find_user(chat, old_user);
-
-	if (cb)
+	if(cb) {
 		g_hash_table_remove(priv->users, purple_chat_user_get_name(cb));
+	}
 
-	if (purple_chat_conversation_is_ignored_user(chat, old_user)) {
+	if(purple_chat_conversation_is_ignored_user(chat, old_user)) {
 		purple_chat_conversation_unignore(chat, old_user);
 		purple_chat_conversation_ignore(chat, new_user);
-	}
-	else if (purple_chat_conversation_is_ignored_user(chat, new_user))
+	} else if(purple_chat_conversation_is_ignored_user(chat, new_user)) {
 		purple_chat_conversation_unignore(chat, new_user);
+	}
 
-	if (is_me)
+	if(is_me) {
 		purple_chat_conversation_set_nick(chat, new_user);
+	}
 
-	if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
-	    !purple_chat_conversation_is_ignored_user(chat, new_user)) {
-
-		if (is_me) {
-			char *escaped = g_markup_escape_text(new_user, -1);
-			g_snprintf(tmp, sizeof(tmp),
-					_("You are now known as %s"), escaped);
+	if(purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
+	   !purple_chat_conversation_is_ignored_user(chat, new_user))
+	{
+		if(is_me) {
+			gchar *escaped = g_markup_escape_text(new_user, -1);
+			g_snprintf(tmp, sizeof(tmp), _("You are now known as %s"),
+			           escaped);
 			g_free(escaped);
 		} else {
-			const char *old_alias = old_user;
-			const char *new_alias = new_user;
-			char *escaped;
-			char *escaped2;
+			const gchar *old_alias = old_user;
+			const gchar *new_alias = new_user;
+			gchar *escaped;
+			gchar *escaped2;
 
-			if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
+			if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
 				PurpleBuddy *buddy;
 
-				if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), old_user)) != NULL)
+				if((buddy = purple_blist_find_buddy(account, old_user)) != NULL) {
 					old_alias = purple_buddy_get_contact_alias(buddy);
-				if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
+				}
+
+				if((buddy = purple_blist_find_buddy(account, new_user)) != NULL) {
 					new_alias = purple_buddy_get_contact_alias(buddy);
+				}
 			}
 
 			escaped = g_markup_escape_text(old_alias, -1);
 			escaped2 = g_markup_escape_text(new_alias, -1);
-			g_snprintf(tmp, sizeof(tmp),
-					_("%s is now known as %s"), escaped, escaped2);
+			g_snprintf(tmp, sizeof(tmp), _("%s is now known as %s"), escaped,
+			           escaped2);
 			g_free(escaped);
 			g_free(escaped2);
 		}
 
-		purple_conversation_write_system_message(conv,
-			tmp, PURPLE_MESSAGE_NO_LINKIFY);
+		purple_conversation_write_system_message(conv, tmp,
+		                                         PURPLE_MESSAGE_NO_LINKIFY);
 	}
 }
 
 void
-purple_chat_conversation_remove_user(PurpleChatConversation *chat, const char *user, const char *reason)
+purple_chat_conversation_remove_user(PurpleChatConversation *chat,
+                                     const gchar *user, const gchar *reason)
 {
-	GList *users = g_list_append(NULL, (char *)user);
+	GList *users = g_list_append(NULL, (gchar *)user);
 
 	purple_chat_conversation_remove_users(chat, users, reason);
 
@@ -554,7 +838,8 @@
 }
 
 void
-purple_chat_conversation_remove_users(PurpleChatConversation *chat, GList *users, const char *reason)
+purple_chat_conversation_remove_users(PurpleChatConversation *chat,
+                                      GList *users, const gchar *reason)
 {
 	PurpleConversation *conv;
 	PurpleConnection *gc;
@@ -564,6 +849,7 @@
 	PurpleChatConversationPrivate *priv;
 	GList *l;
 	gboolean quiet;
+	gpointer handle;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
 	g_return_if_fail(users != NULL);
@@ -573,43 +859,44 @@
 
 	gc = purple_conversation_get_connection(conv);
 	g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+
 	protocol = purple_connection_get_protocol(gc);
 	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
 
-	ops  = purple_conversation_get_ui_ops(conv);
+	ops = purple_conversation_get_ui_ops(conv);
+	handle = purple_conversations_get_handle();
 
-	for (l = users; l != NULL; l = l->next) {
-		const char *user = (const char *)l->data;
-		quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
-					"chat-user-leaving", chat, user, reason)) |
+	for(l = users; l != NULL; l = l->next) {
+		const gchar *user = (const gchar *)l->data;
+		quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(handle,
+		                        "chat-user-leaving", chat, user, reason)) |
 				purple_chat_conversation_is_ignored_user(chat, user);
 
 		cb = purple_chat_conversation_find_user(chat, user);
 
-		if (cb) {
-			g_hash_table_remove(priv->users,
-				purple_chat_user_get_name(cb));
+		if(cb) {
+			g_hash_table_remove(priv->users, purple_chat_user_get_name(cb));
 		}
 
 		/* NOTE: Don't remove them from ignored in case they re-enter. */
+		if(!quiet) {
+			const gchar *alias = user;
+			gchar *alias_esc;
+			gchar *tmp;
 
-		if (!quiet) {
-			const char *alias = user;
-			char *alias_esc;
-			char *tmp;
-
-			if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
+			if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
 				PurpleBuddy *buddy;
 
-				if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
+				if((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL) {
 					alias = purple_buddy_get_contact_alias(buddy);
+				}
 			}
 
 			alias_esc = g_markup_escape_text(alias, -1);
 
-			if (reason == NULL || !*reason)
+			if(reason == NULL || !*reason) {
 				tmp = g_strdup_printf(_("%s left the room."), alias_esc);
-			else {
+			} else {
 				char *reason_esc = g_markup_escape_text(reason, -1);
 				tmp = g_strdup_printf(_("%s left the room (%s)."),
 				                      alias_esc, reason_esc);
@@ -617,94 +904,92 @@
 			}
 			g_free(alias_esc);
 
-			purple_conversation_write_system_message(conv,
-				tmp, PURPLE_MESSAGE_NO_LINKIFY);
+			purple_conversation_write_system_message(conv, tmp,
+			                                         PURPLE_MESSAGE_NO_LINKIFY);
 			g_free(tmp);
 		}
 
-		purple_signal_emit(purple_conversations_get_handle(), "chat-user-left",
-						 conv, user, reason);
+		purple_signal_emit(handle, "chat-user-left", conv, user, reason);
 	}
 
-	if (ops != NULL && ops->chat_remove_users != NULL)
+	if(ops != NULL && ops->chat_remove_users != NULL) {
 		ops->chat_remove_users(chat, users);
+	}
 }
 
 void
-purple_chat_conversation_clear_users(PurpleChatConversation *chat)
-{
+purple_chat_conversation_clear_users(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
-	PurpleConversationUiOps *ops;
-	GHashTableIter it;
-	gchar *name;
+	PurpleConversationUiOps *ops = NULL;
+	GList *names = NULL;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
 
 	priv = purple_chat_conversation_get_instance_private(chat);
 	ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(chat));
-
-	if (ops != NULL && ops->chat_remove_users != NULL) {
-		GList *names = NULL;
+	names = g_hash_table_get_keys(priv->users);
 
-		g_hash_table_iter_init(&it, priv->users);
-		while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL))
-			names = g_list_prepend(names, name);
-
+	if(ops != NULL && ops->chat_remove_users != NULL) {
 		ops->chat_remove_users(chat, names);
-		g_list_free(names);
 	}
 
-	g_hash_table_iter_init(&it, priv->users);
-	while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL)) {
-		purple_signal_emit(purple_conversations_get_handle(),
-						 "chat-user-leaving", chat, name, NULL);
-		purple_signal_emit(purple_conversations_get_handle(),
-						 "chat-user-left", chat, name, NULL);
-	}
+	g_list_foreach(names, purple_chat_conversation_clear_users_helper, chat);
 
+	g_list_free(names);
 	g_hash_table_remove_all(priv->users);
 }
 
-void purple_chat_conversation_set_nick(PurpleChatConversation *chat, const char *nick) {
+void
+purple_chat_conversation_set_nick(PurpleChatConversation *chat,
+                                  const gchar *nick)
+{
 	PurpleChatConversationPrivate *priv = NULL;
+	PurpleAccount *account = NULL;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
+	account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
+
 	g_free(priv->nick);
-	priv->nick = g_strdup(purple_normalize(
-			purple_conversation_get_account(PURPLE_CONVERSATION(chat)), nick));
+	priv->nick = g_strdup(purple_normalize(account, nick));
 
-	g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_NICK]);
+	g_object_notify_by_pspec(G_OBJECT(chat), properties[PROP_NICK]);
 }
 
-const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat) {
+const gchar *
+purple_chat_conversation_get_nick(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return priv->nick;
 }
 
 static void
-invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
-{
-	PurpleConversation *conv;
+invite_user_to_chat(gpointer data, PurpleRequestFields *fields) {
+	PurpleChatConversation *chat;
 	PurpleChatConversationPrivate *priv;
-	const char *user, *message;
+	PurpleConnection *pc;
+	const gchar *user, *message;
 
-	conv = data;
-	priv = purple_chat_conversation_get_instance_private(
-			PURPLE_CHAT_CONVERSATION(conv));
+	chat = PURPLE_CHAT_CONVERSATION(data);
+	priv = purple_chat_conversation_get_instance_private(chat);
+
 	user = purple_request_fields_get_string(fields, "screenname");
 	message = purple_request_fields_get_string(fields, "message");
 
-	purple_serv_chat_invite(purple_conversation_get_connection(conv), priv->id, message, user);
+	pc = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
+	purple_serv_chat_invite(pc, priv->id, message, user);
 }
 
-void purple_chat_conversation_invite_user(PurpleChatConversation *chat, const char *user,
-		const char *message, gboolean confirm)
+void
+purple_chat_conversation_invite_user(PurpleChatConversation *chat,
+                                     const gchar *user, const gchar *message,
+                                     gboolean confirm)
 {
 	PurpleAccount *account;
 	PurpleRequestFields *fields;
@@ -713,14 +998,16 @@
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
 
-	if (!user || !*user || !message || !*message)
+	if(user == NULL || *user == '\0' || message == NULL || *message == '\0') {
 		confirm = TRUE;
+	}
 
 	account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
 
-	if (!confirm) {
+	if(!confirm) {
 		purple_serv_chat_invite(purple_account_get_connection(account),
-				purple_chat_conversation_get_id(chat), message, user);
+		                        purple_chat_conversation_get_id(chat), message,
+		                        user);
 		return;
 	}
 
@@ -728,26 +1015,29 @@
 	group = purple_request_field_group_new(_("Invite to chat"));
 	purple_request_fields_add_group(fields, group);
 
-	field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
+	field = purple_request_field_string_new("screenname", _("Buddy"), user,
+	                                        FALSE);
 	purple_request_field_group_add_field(group, field);
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_set_type_hint(field, "screenname");
 
-	field = purple_request_field_string_new("message", _("Message"), message, FALSE);
+	field = purple_request_field_string_new("message", _("Message"), message,
+	                                        FALSE);
 	purple_request_field_group_add_field(group, field);
 
 	purple_request_fields(chat, _("Invite to chat"), NULL,
-			_("Please enter the name of the user you wish to invite, "
-				"along with an optional invite message."),
-			fields,
-			_("Invite"), G_CALLBACK(invite_user_to_chat),
-			_("Cancel"), NULL,
-			purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat)),
-			chat);
+	                      _("Please enter the name of the user you wish to "
+	                        "invite, along with an optional invite message."),
+	                      fields,
+	                      _("Invite"), G_CALLBACK(invite_user_to_chat),
+	                      _("Cancel"), NULL,
+	                      purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat)),
+	                      chat);
 }
 
 gboolean
-purple_chat_conversation_has_user(PurpleChatConversation *chat, const char *user)
+purple_chat_conversation_has_user(PurpleChatConversation *chat,
+                                  const gchar *user)
 {
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
 	g_return_val_if_fail(user != NULL, FALSE);
@@ -756,8 +1046,7 @@
 }
 
 void
-purple_chat_conversation_leave(PurpleChatConversation *chat)
-{
+purple_chat_conversation_leave(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
@@ -765,59 +1054,28 @@
 	priv = purple_chat_conversation_get_instance_private(chat);
 	priv->left = TRUE;
 
-	if (!g_object_get_data(G_OBJECT(chat), "is-finalizing"))
-		g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
+	if(!g_object_get_data(G_OBJECT(chat), "is-finalizing")) {
+		g_object_notify_by_pspec(G_OBJECT(chat), properties[PROP_LEFT]);
+	}
 
-	purple_conversation_update(PURPLE_CONVERSATION(chat), PURPLE_CONVERSATION_UPDATE_CHATLEFT);
+	purple_conversation_update(PURPLE_CONVERSATION(chat),
+	                           PURPLE_CONVERSATION_UPDATE_CHATLEFT);
 }
 
 gboolean
-purple_chat_conversation_has_left(PurpleChatConversation *chat)
-{
+purple_chat_conversation_has_left(PurpleChatConversation *chat) {
 	PurpleChatConversationPrivate *priv = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), TRUE);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return priv->left;
 }
 
-static void
-chat_conversation_cleanup_for_rejoin(PurpleChatConversation *chat)
-{
-	const char *disp;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	PurpleConversation *conv = PURPLE_CONVERSATION(chat);
-	PurpleChatConversationPrivate *priv =
-			purple_chat_conversation_get_instance_private(chat);
-
-	account = purple_conversation_get_account(conv);
-
-	purple_conversation_close_logs(conv);
-	purple_conversation_set_logging(conv, TRUE);
-
-	gc = purple_account_get_connection(account);
-
-	if ((disp = purple_connection_get_display_name(gc)) != NULL)
-		purple_chat_conversation_set_nick(chat, disp);
-	else
-	{
-		purple_chat_conversation_set_nick(chat,
-								purple_account_get_username(account));
-	}
-
-	purple_chat_conversation_clear_users(chat);
-	purple_chat_conversation_set_topic(chat, NULL, NULL);
-	priv->left = FALSE;
-
-	g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
-
-	purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_CHATLEFT);
-}
-
 PurpleChatUser *
-purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
+purple_chat_conversation_find_user(PurpleChatConversation *chat,
+                                   const gchar *name)
 {
 	PurpleChatConversationPrivate *priv = NULL;
 
@@ -825,248 +1083,6 @@
 	g_return_val_if_fail(name != NULL, NULL);
 
 	priv = purple_chat_conversation_get_instance_private(chat);
+
 	return g_hash_table_lookup(priv->users, name);
 }
-
-/**************************************************************************
- * GObject code for chats
- **************************************************************************/
-
-/* Set method for GObject properties */
-static void
-purple_chat_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
-		GParamSpec *pspec)
-{
-	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
-
-	switch (param_id) {
-		case CHAT_PROP_ID:
-			purple_chat_conversation_set_id(chat, g_value_get_int(value));
-			break;
-		case CHAT_PROP_NICK:
-			purple_chat_conversation_set_nick(chat, g_value_get_string(value));
-			break;
-		case CHAT_PROP_LEFT:
-			{
-				gboolean left = g_value_get_boolean(value);
-				if (left)
-					purple_chat_conversation_leave(chat);
-			}
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* Get method for GObject properties */
-static void
-purple_chat_conversation_get_property(GObject *obj, guint param_id, GValue *value,
-		GParamSpec *pspec)
-{
-	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
-
-	switch (param_id) {
-		case CHAT_PROP_TOPIC_WHO:
-			g_value_set_string(value, purple_chat_conversation_get_topic_who(chat));
-			break;
-		case CHAT_PROP_TOPIC:
-			g_value_set_string(value, purple_chat_conversation_get_topic(chat));
-			break;
-		case CHAT_PROP_ID:
-			g_value_set_int(value, purple_chat_conversation_get_id(chat));
-			break;
-		case CHAT_PROP_NICK:
-			g_value_set_string(value, purple_chat_conversation_get_nick(chat));
-			break;
-		case CHAT_PROP_LEFT:
-			g_value_set_boolean(value, purple_chat_conversation_has_left(chat));
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* GObject initialization function */
-static void purple_chat_conversation_init(PurpleChatConversation *chat)
-{
-	PurpleChatConversationPrivate *priv =
-			purple_chat_conversation_get_instance_private(chat);
-
-	priv->users = g_hash_table_new_full(_purple_conversation_user_hash,
-		_purple_conversation_user_equal, g_free, g_object_unref);
-}
-
-/* Called when done constructing */
-static void
-purple_chat_conversation_constructed(GObject *object)
-{
-	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
-	PurpleAccount *account;
-	const char *disp;
-
-	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->
-			constructed(object);
-
-	g_object_get(object, "account", &account, NULL);
-
-	if ((disp = purple_connection_get_display_name(purple_account_get_connection(account))))
-		purple_chat_conversation_set_nick(chat, disp);
-	else
-		purple_chat_conversation_set_nick(chat,
-								purple_account_get_username(account));
-
-	if (purple_prefs_get_bool("/purple/logging/log_chats"))
-		purple_conversation_set_logging(PURPLE_CONVERSATION(chat), TRUE);
-
-	g_object_unref(account);
-}
-
-/* GObject dispose function */
-static void
-purple_chat_conversation_dispose(GObject *object)
-{
-	PurpleChatConversationPrivate *priv =
-			purple_chat_conversation_get_instance_private
-					(PURPLE_CHAT_CONVERSATION(object));
-
-	g_hash_table_remove_all(priv->users);
-
-	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->dispose(object);
-}
-
-/* GObject finalize function */
-static void
-purple_chat_conversation_finalize(GObject *object)
-{
-	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
-	PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
-	PurpleChatConversationPrivate *priv =
-			purple_chat_conversation_get_instance_private(chat);
-
-	if (gc != NULL)
-	{
-		/* Still connected */
-		int chat_id = purple_chat_conversation_get_id(chat);
-
-		/*
-		 * Close the window when the user tells us to, and let the protocol
-		 * deal with the internals on it's own time. Don't do this if the
-		 * protocol already knows it left the chat.
-		 */
-		if (!purple_chat_conversation_has_left(chat))
-			purple_serv_chat_leave(gc, chat_id);
-
-		/*
-		 * If they didn't call purple_serv_got_chat_left by now, it's too late.
-		 * So we better do it for them before we destroy the thing.
-		 */
-		if (!purple_chat_conversation_has_left(chat))
-			purple_serv_got_chat_left(gc, chat_id);
-	}
-
-	g_hash_table_destroy(priv->users);
-	priv->users = NULL;
-
-	g_list_free_full(priv->ignored, g_free);
-	priv->ignored = NULL;
-
-	g_free(priv->who);
-	g_free(priv->topic);
-	g_free(priv->nick);
-
-	priv->who = NULL;
-	priv->topic = NULL;
-	priv->nick = NULL;
-
-	G_OBJECT_CLASS(purple_chat_conversation_parent_class)->finalize(object);
-}
-
-/* Class initializer function */
-static void purple_chat_conversation_class_init(PurpleChatConversationClass *klass)
-{
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-	PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
-
-	obj_class->dispose = purple_chat_conversation_dispose;
-	obj_class->finalize = purple_chat_conversation_finalize;
-	obj_class->constructed = purple_chat_conversation_constructed;
-
-	/* Setup properties */
-	obj_class->get_property = purple_chat_conversation_get_property;
-	obj_class->set_property = purple_chat_conversation_set_property;
-
-	conv_class->write_message = chat_conversation_write_message;
-
-	chat_properties[CHAT_PROP_TOPIC_WHO] = g_param_spec_string("topic-who",
-				"Who set topic",
-				"Who set the chat topic.", NULL,
-				G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-	chat_properties[CHAT_PROP_TOPIC] = g_param_spec_string("topic",
-				"Topic",
-				"Topic of the chat.", NULL,
-				G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-	chat_properties[CHAT_PROP_ID] = g_param_spec_int("chat-id",
-				"Chat ID",
-				"The ID of the chat.", G_MININT, G_MAXINT, 0,
-				G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	chat_properties[CHAT_PROP_NICK] = g_param_spec_string("nick",
-				"Nickname",
-				"The nickname of the user in a chat.", NULL,
-				G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	chat_properties[CHAT_PROP_LEFT] = g_param_spec_boolean("left",
-				"Left the chat",
-				"Whether the user has left the chat.", FALSE,
-				G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties(obj_class, CHAT_PROP_LAST,
-				chat_properties);
-}
-
-PurpleChatConversation *
-purple_chat_conversation_new(PurpleAccount *account, const char *name)
-{
-	PurpleChatConversation *chat;
-	PurpleConnection *gc;
-
-	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
-	g_return_val_if_fail(name != NULL, NULL);
-
-	/* Check if this conversation already exists. */
-	if ((chat = purple_conversations_find_chat_with_account(name, account)) != NULL)
-	{
-		if (!purple_chat_conversation_has_left(chat)) {
-			purple_debug_warning("conversationtypes", "Trying to create "
-					"multiple chats (%s) with the same name is deprecated and "
-					"will be removed in libpurple 3.0.0", name);
-		} else {
-			/*
-			 * This hack is necessary because some protocols (MSN) have unnamed chats
-			 * that all use the same name.  A PurpleConversation for one of those
-			 * is only ever re-used if the user has left, so calls to
-			 * purple_conversation_new need to fall-through to creating a new
-			 * chat.
-			 * TODO 3.0.0: Remove this workaround and mandate unique names.
-			 */
-
-			chat_conversation_cleanup_for_rejoin(chat);
-			return chat;
-		}
-	}
-
-	gc = purple_account_get_connection(account);
-	g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
-
-	chat = g_object_new(PURPLE_TYPE_CHAT_CONVERSATION,
-			"account", account,
-			"name",    name,
-			"title",   name,
-			NULL);
-
-	return chat;
-}
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -12,7 +12,6 @@
 	'connection.c',
 	'contact.c',
 	'conversation.c',
-	'conversationtypes.c',
 	'conversations.c',
 	'core.c',
 	'countingnode.c',
@@ -49,6 +48,7 @@
 	'purpleattachment.c',
 	'purpleattentiontype.c',
 	'purplebuddypresence.c',
+	'purplechatconversation.c',
 	'purplechatuser.c',
 	'purplecredentialmanager.c',
 	'purplecredentialprovider.c',
@@ -110,7 +110,6 @@
 	'connection.h',
 	'contact.h',
 	'conversation.h',
-	'conversationtypes.h',
 	'conversations.h',
 	'core.h',
 	'countingnode.h',
@@ -140,6 +139,7 @@
 	'purpleaccountusersplit.h',
 	'purpleattentiontype.h',
 	'purplebuddypresence.h',
+	'purplechatconversation.h',
 	'purplechatuser.h',
 	'purplecredentialmanager.h',
 	'purplecredentialprovider.h',
diff --git a/libpurple/purplechatuser.h b/libpurple/purplechatuser.h
--- a/libpurple/purplechatuser.h
+++ b/libpurple/purplechatuser.h
@@ -82,7 +82,7 @@
 G_DECLARE_FINAL_TYPE(PurpleChatUser, purple_chat_user, PURPLE, CHAT_USER,
                      GObject)
 
-#include <libpurple/conversationtypes.h>
+#include <libpurple/purplechatconversation.h>
 
 G_BEGIN_DECLS
 
diff --git a/libpurple/purpleprotocolim.h b/libpurple/purpleprotocolim.h
--- a/libpurple/purpleprotocolim.h
+++ b/libpurple/purpleprotocolim.h
@@ -41,7 +41,6 @@
 #include <glib-object.h>
 
 #include <libpurple/connection.h>
-#include <libpurple/conversationtypes.h>
 
 #define PURPLE_TYPE_PROTOCOL_IM (purple_protocol_im_get_type())
 G_DECLARE_INTERFACE(PurpleProtocolIM, purple_protocol_im, PURPLE, PROTOCOL_IM,
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,13 +31,13 @@
 libpurple/buddyicon.c
 libpurple/buddylist.c
 libpurple/chat.c
+libpurple/chatconversation.c
 libpurple/circularbuffer.c
 libpurple/cmds.c
 libpurple/connection.c
 libpurple/contact.c
 libpurple/conversation.c
 libpurple/conversations.c
-libpurple/conversationtypes.c
 libpurple/core.c
 libpurple/countingnode.c
 libpurple/debug.c
