diff --git a/libpurple/protocols/ircv3/purpleircv3connection.h b/libpurple/protocols/ircv3/purpleircv3connection.h
--- a/libpurple/protocols/ircv3/purpleircv3connection.h
+++ b/libpurple/protocols/ircv3/purpleircv3connection.h
@@ -104,6 +104,20 @@
  */
 gboolean purple_ircv3_connection_get_registered(PurpleIRCv3Connection *connection);
 
+/**
+ * purple_ircv3_connection_add_status_message:
+ * @connection: The instance.
+ * @source: The source/author of the message.
+ * @command: The command name.
+ * @n_params: The number of parameters.
+ * @params: The parameters themselves.
+ *
+ * Adds a message to the status conversation/window for @connection.
+ *
+ * Since: 3.0.0
+ */
+void purple_ircv3_connection_add_status_message(PurpleIRCv3Connection *connection, const char *source, const char *command, guint n_params, GStrv params);
+
 G_END_DECLS
 
 #endif /* PURPLE_IRCV3_CONNECTION_H */
# HG changeset patch
# Node ID 1e1e2748da2d
# Parent  015257326ad4
diff --git a/libpurple/protocols/ircv3/purpleircv3connection.c b/libpurple/protocols/ircv3/purpleircv3connection.c
--- a/libpurple/protocols/ircv3/purpleircv3connection.c
+++ b/libpurple/protocols/ircv3/purpleircv3connection.c
@@ -51,6 +51,8 @@
 	PurpleIRCv3Parser *parser;
 
 	PurpleIRCv3Capabilities *capabilities;
+
+	PurpleConversation *status_conversation;
 } PurpleIRCv3ConnectionPrivate;
 
 G_DEFINE_DYNAMIC_TYPE_EXTENDED(PurpleIRCv3Connection,
@@ -402,6 +404,7 @@
 
 	g_clear_object(&priv->capabilities);
 	g_clear_object(&priv->parser);
+	g_clear_object(&priv->status_conversation);
 
 	G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->dispose(obj);
 }
@@ -423,13 +426,19 @@
 	PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
 	PurpleAccount *account = NULL;
+	PurpleContactInfo *info = NULL;
 	char **userparts = NULL;
 	const char *username = NULL;
+	gchar *title = NULL;
 
 	G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->constructed(obj);
 
 	priv = purple_ircv3_connection_get_instance_private(connection);
 	account = purple_connection_get_account(PURPLE_CONNECTION(connection));
+	info = PURPLE_CONTACT_INFO(account);
+
+	title = g_strdup_printf("status for %s",
+	                       purple_contact_info_get_username(info));
 
 	/* Split the username into nick and server and store the values. */
 	username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
@@ -441,6 +450,23 @@
 	/* Free the userparts vector. */
 	g_strfreev(userparts);
 
+	/* Create our status conversation. */
+	priv->status_conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"name", priv->server_name,
+		"title", title,
+		NULL);
+
+	g_clear_pointer(&title, g_free);
+
+	/* TODO later: add an account action that'll register and unregister this
+	 * with the conversation manager.
+	 */
+	purple_conversation_manager_register(purple_conversation_manager_get_default(),
+	                                     priv->status_conversation);
+
+
 	/* Finally create our objects. */
 	priv->cancellable = g_cancellable_new();
 
@@ -618,3 +644,42 @@
 
 	return priv->registered;
 }
+
+void
+purple_ircv3_connection_add_status_message(PurpleIRCv3Connection *connection,
+                                           const char *source,
+                                           const char *command,
+                                           guint n_params,
+                                           GStrv params)
+{
+	PurpleIRCv3ConnectionPrivate *priv = NULL;
+	PurpleMessage *message = NULL;
+	GString *str = NULL;
+
+	g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
+	g_return_if_fail(command != NULL);
+
+	priv = purple_ircv3_connection_get_instance_private(connection);
+
+	str = g_string_new(command);
+
+	if(n_params > 0) {
+		char *joined = g_strjoinv(" ", params);
+
+		g_string_append_printf(str, " %s", joined);
+
+		g_free(joined);
+	}
+
+	message = g_object_new(
+		PURPLE_TYPE_MESSAGE,
+		"author", source,
+		"contents", str->str,
+		NULL);
+
+	g_string_free(str, TRUE);
+
+	purple_conversation_write_message(priv->status_conversation, message);
+
+	g_clear_object(&message);
+}
diff --git a/libpurple/protocols/ircv3/purpleircv3messagehandlers.h b/libpurple/protocols/ircv3/purpleircv3messagehandlers.h
--- a/libpurple/protocols/ircv3/purpleircv3messagehandlers.h
+++ b/libpurple/protocols/ircv3/purpleircv3messagehandlers.h
@@ -56,6 +56,8 @@
                                               gpointer data);
 
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_fallback(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
+G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_status(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
+G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_status_ignore_param0(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_ping(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_privmsg(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
 
diff --git a/libpurple/protocols/ircv3/purpleircv3messagehandlers.c b/libpurple/protocols/ircv3/purpleircv3messagehandlers.c
--- a/libpurple/protocols/ircv3/purpleircv3messagehandlers.c
+++ b/libpurple/protocols/ircv3/purpleircv3messagehandlers.c
@@ -16,6 +16,8 @@
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
+#include <glib/gi18n-lib.h>
+
 #include "purpleircv3messagehandlers.h"
 
 #include "purpleircv3connection.h"
@@ -28,20 +30,59 @@
 purple_ircv3_message_handler_fallback(G_GNUC_UNUSED GHashTable *tags,
                                       const char *source,
                                       const char *command,
-                                      G_GNUC_UNUSED guint n_params,
+                                      guint n_params,
                                       GStrv params,
                                       G_GNUC_UNUSED GError **error,
-                                      G_GNUC_UNUSED gpointer data)
+                                      gpointer data)
 {
-	gchar *joined = g_strjoinv(" ", params);
+	PurpleIRCv3Connection *connection = data;
+	gchar *new_command = NULL;
+
+	new_command = g_strdup_printf(_("unknown command '%s'"), command);
+	purple_ircv3_connection_add_status_message(connection, source, new_command,
+	                                           n_params, params);
+
+	g_clear_pointer(&new_command, g_free);
+
+	return TRUE;
+}
 
-	g_message("-- unhandled message --");
-	g_message("source: %s", source);
-	g_message("command: %s", command);
-	g_message("params: %s", joined);
-	g_message("-- end of unhandled message --");
+/******************************************************************************
+ * Status Messages
+ *****************************************************************************/
+gboolean
+purple_ircv3_message_handler_status(G_GNUC_UNUSED GHashTable *tags,
+                                    const char *source,
+                                    const char *command,
+                                    guint n_params,
+                                    GStrv params,
+                                    G_GNUC_UNUSED GError **error,
+                                    gpointer data)
+{
+	purple_ircv3_connection_add_status_message(data, source, command, n_params,
+	                                           params);
+
+	return TRUE;
+}
 
-	g_free(joined);
+gboolean
+purple_ircv3_message_handler_status_ignore_param0(G_GNUC_UNUSED GHashTable *tags,
+                                                  const char *source,
+                                                  const char *command,
+                                                  guint n_params,
+                                                  GStrv params,
+                                                  GError **error,
+                                                  gpointer data)
+{
+	if(n_params <= 1) {
+		g_set_error(error, PURPLE_IRCV3_DOMAIN, 0,
+		            "expected n_params > 1, got %u", n_params);
+
+		return FALSE;
+	}
+
+	purple_ircv3_connection_add_status_message(data, source, command,
+	                                           n_params - 1, params + 1);
 
 	return TRUE;
 }
diff --git a/libpurple/protocols/ircv3/purpleircv3parser.h b/libpurple/protocols/ircv3/purpleircv3parser.h
--- a/libpurple/protocols/ircv3/purpleircv3parser.h
+++ b/libpurple/protocols/ircv3/purpleircv3parser.h
@@ -43,7 +43,7 @@
  *
  * Since: 3.0.0
  */
-G_GNUC_INTERNAL PurpleIRCv3Parser *purple_ircv3_parser_new(void);
+PurpleIRCv3Parser *purple_ircv3_parser_new(void);
 
 /**
  * purple_ircv3_parser_set_fallback_handler: (skip):
@@ -55,7 +55,7 @@
  *
  * Since: 3.0.0
  */
-G_GNUC_INTERNAL void purple_ircv3_parser_set_fallback_handler(PurpleIRCv3Parser *parser, PurpleIRCv3MessageHandler handler);
+void purple_ircv3_parser_set_fallback_handler(PurpleIRCv3Parser *parser, PurpleIRCv3MessageHandler handler);
 
 /**
  * purple_ircv3_parser_parse:
@@ -70,7 +70,32 @@
  *
  * Since: 3.0.0
  */
-G_GNUC_INTERNAL gboolean purple_ircv3_parser_parse(PurpleIRCv3Parser *parser, const gchar *buffer, GError **error, gpointer data);
+gboolean purple_ircv3_parser_parse(PurpleIRCv3Parser *parser, const gchar *buffer, GError **error, gpointer data);
+
+/**
+ * purple_ircv3_parser_add_handler:
+ * @parser: The instance.
+ * @command: The command string.
+ * @handler: (scope forever): The handler to call.
+ *
+ * Calls @handler every time @parser finds the command named @command.
+ *
+ * Since: 3.0.0
+ */
+void purple_ircv3_parser_add_handler(PurpleIRCv3Parser *parser, const gchar *command, PurpleIRCv3MessageHandler handler);
+
+/**
+ * purple_ircv3_parser_add_handlers:
+ * @parser: The instance.
+ * @handler: (scope forever): The handler to call when the command is received.
+ * @...: A %NULL terminated list of string command names.
+ *
+ * Like [method@Parser.add_handler] but allows you to add multiple commands at
+ * once that share a handler.
+ *
+ * Since: 3.0.0
+ */
+void purple_ircv3_parser_add_handlers(PurpleIRCv3Parser *parser, PurpleIRCv3MessageHandler handler, ...) G_GNUC_NULL_TERMINATED;
 
 /**
  * purple_ircv3_parser_add_default_handlers:
@@ -80,7 +105,7 @@
  *
  * Since: 3.0.0
  */
-G_GNUC_INTERNAL void purple_ircv3_parser_add_default_handlers(PurpleIRCv3Parser *parser);
+void purple_ircv3_parser_add_default_handlers(PurpleIRCv3Parser *parser);
 
 G_END_DECLS
 
diff --git a/libpurple/protocols/ircv3/purpleircv3parser.c b/libpurple/protocols/ircv3/purpleircv3parser.c
--- a/libpurple/protocols/ircv3/purpleircv3parser.c
+++ b/libpurple/protocols/ircv3/purpleircv3parser.c
@@ -212,18 +212,6 @@
 	return result;
 }
 
-static void
-purple_ircv3_parser_add_handler(PurpleIRCv3Parser *parser,
-                                const gchar *command,
-                                PurpleIRCv3MessageHandler handler)
-{
-	g_return_if_fail(PURPLE_IRCV3_IS_PARSER(parser));
-	g_return_if_fail(command != NULL);
-	g_return_if_fail(handler != NULL);
-
-	g_hash_table_insert(parser->handlers, g_strdup(command), handler);
-}
-
 /******************************************************************************
  * Handlers
  *****************************************************************************/
@@ -396,6 +384,38 @@
 }
 
 void
+purple_ircv3_parser_add_handler(PurpleIRCv3Parser *parser,
+                                const gchar *command,
+                                PurpleIRCv3MessageHandler handler)
+{
+	g_return_if_fail(PURPLE_IRCV3_IS_PARSER(parser));
+	g_return_if_fail(command != NULL);
+	g_return_if_fail(handler != NULL);
+
+	g_hash_table_insert(parser->handlers, g_strdup(command), handler);
+}
+
+void
+purple_ircv3_parser_add_handlers(PurpleIRCv3Parser *parser,
+                                 PurpleIRCv3MessageHandler handler,
+                                 ...)
+{
+	va_list vargs;
+	const char *command = NULL;
+
+	g_return_if_fail(PURPLE_IRCV3_IS_PARSER(parser));
+	g_return_if_fail(handler != NULL);
+
+	va_start(vargs, handler);
+
+	while((command = va_arg(vargs, const char *)) != NULL) {
+		purple_ircv3_parser_add_handler(parser, command, handler);
+	}
+
+	va_end(vargs);
+}
+
+void
 purple_ircv3_parser_add_default_handlers(PurpleIRCv3Parser *parser) {
 	g_return_if_fail(PURPLE_IRCV3_IS_PARSER(parser));
 
@@ -412,6 +432,21 @@
 	purple_ircv3_parser_add_handler(parser, "PRIVMSG",
 	                                purple_ircv3_message_handler_privmsg);
 
+	/* Post Registration Greetings */
+	purple_ircv3_parser_add_handlers(parser,
+	                                 purple_ircv3_message_handler_status_ignore_param0,
+	                                 "001", "002", "003", "004", NULL);
+
+	/* Luser's */
+	purple_ircv3_parser_add_handlers(parser,
+	                                 purple_ircv3_message_handler_status_ignore_param0,
+	                                 "251", "252", "253", "254", "255", NULL);
+
+	/* MOTD */
+	purple_ircv3_parser_add_handlers(parser,
+	                                 purple_ircv3_message_handler_status_ignore_param0,
+	                                 "372", "375", "376", NULL);
+
 	/* SASL stuff. */
 	purple_ircv3_parser_add_handler(parser, "900",
 	                                purple_ircv3_sasl_logged_in);
diff --git a/libpurple/purpleconversation.c b/libpurple/purpleconversation.c
--- a/libpurple/purpleconversation.c
+++ b/libpurple/purpleconversation.c
@@ -395,6 +395,8 @@
 purple_conversation_class_init(PurpleConversationClass *klass) {
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
+	klass->write_message = _purple_conversation_write_common;
+
 	obj_class->constructed = purple_conversation_constructed;
 	obj_class->dispose = purple_conversation_dispose;
 	obj_class->finalize = purple_conversation_finalize;
