# HG changeset patch
# Node ID b27e2fc7828e235081f604a46573ceaffd9d0cb5
# Parent  fd62436d0604fef77afe56fdec32762cc4c19a12
diff --git a/protocols/ircv3/purpleircv3connection.c b/protocols/ircv3/purpleircv3connection.c
--- a/protocols/ircv3/purpleircv3connection.c
+++ b/protocols/ircv3/purpleircv3connection.c
@@ -497,7 +497,9 @@
 	g_signal_connect_object(client, "message::" IBIS_MSG_WALLOPS,
 	                        G_CALLBACK(purple_ircv3_message_handler_wallops),
 	                        connection, G_CONNECT_DEFAULT);
-
+	g_signal_connect_object(client, "message::" IBIS_MSG_KICK,
+	                        G_CALLBACK(purple_ircv3_message_handler_kick),
+	                        connection, G_CONNECT_DEFAULT);
 
 	g_signal_connect_object(client, "message",
 	                        G_CALLBACK(purple_ircv3_connection_unknown_message_cb),
@@ -975,6 +977,9 @@
 		purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), nick);
 
 		purple_contact_manager_add(manager, contact);
+
+		/* We don't return a reference and the manager add its own. */
+		g_object_unref(G_OBJECT(contact));
 	}
 
 	purple_contact_info_set_sid(PURPLE_CONTACT_INFO(contact), source);
@@ -985,6 +990,43 @@
 	return contact;
 }
 
+PurpleContact *
+purple_ircv3_connection_find_or_create_contact_from_nick(PurpleIRCv3Connection *connection,
+                                                         const char *nick)
+{
+	PurpleAccount *account = NULL;
+	PurpleContact *contact = NULL;
+	PurpleContactManager *manager = NULL;
+
+	g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), NULL);
+	g_return_val_if_fail(!purple_strempty(nick), NULL);
+
+	account = purple_connection_get_account(PURPLE_CONNECTION(connection));
+	manager = purple_contact_manager_get_default();
+
+	contact = purple_contact_manager_find_with_id(manager, account, nick);
+	if(!PURPLE_IS_CONTACT(contact)) {
+		contact = purple_contact_manager_find_with_username(manager, account,
+		                                                    nick);
+	}
+
+	if(!PURPLE_IS_CONTACT(contact)) {
+		PurpleAccount *account = NULL;
+
+		account = purple_connection_get_account(PURPLE_CONNECTION(connection));
+
+		contact = purple_contact_new(account, nick);
+		purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), nick);
+		purple_contact_info_set_display_name(PURPLE_CONTACT_INFO(contact),
+		                                     nick);
+
+		purple_contact_manager_add(manager, contact);
+		g_object_unref(G_OBJECT(contact));
+	}
+
+	return contact;
+}
+
 PurpleConversation *
 purple_ircv3_connection_get_status_conversation(PurpleIRCv3Connection *connection)
 {
diff --git a/protocols/ircv3/purpleircv3connection.h b/protocols/ircv3/purpleircv3connection.h
--- a/protocols/ircv3/purpleircv3connection.h
+++ b/protocols/ircv3/purpleircv3connection.h
@@ -130,7 +130,7 @@
  * Note that the ownership of the contact remains with the default
  * [class@Purple.ContactManager].
  *
- * Returns: (transfer none) (nullable): The contact.
+ * Returns: (transfer none): The contact.
  *
  * Since: 3.0
  */
@@ -138,6 +138,20 @@
 PurpleContact *purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection, IbisMessage *message);
 
 /**
+ * purple_ircv3_connection_find_or_create_contact_from_nick:
+ * @connection: The instance.
+ * @nick: The nick to find or create a contact for.
+ *
+ * Finds an existing contact or creates one for @nick.
+ *
+ * Returns: (transfer none): The contact.
+ *
+ * Since: 3.0
+ */
+PURPLE_IRCV3_AVAILABLE_IN_ALL
+PurpleContact *purple_ircv3_connection_find_or_create_contact_from_nick(PurpleIRCv3Connection *connection, const char *nick);
+
+/**
  * purple_ircv3_connection_get_status_conversation: (skip)
  * @connection: The instance.
  *
diff --git a/protocols/ircv3/purpleircv3messagehandlers.c b/protocols/ircv3/purpleircv3messagehandlers.c
--- a/protocols/ircv3/purpleircv3messagehandlers.c
+++ b/protocols/ircv3/purpleircv3messagehandlers.c
@@ -711,3 +711,80 @@
 
 	return TRUE;
 }
+
+gboolean
+purple_ircv3_message_handler_kick(G_GNUC_UNUSED IbisClient *client,
+                                  G_GNUC_UNUSED const char *command,
+                                  IbisMessage *ibis_message,
+                                  gpointer data)
+{
+	PurpleIRCv3Connection *v3_connection = data;
+	PurpleAccount *account = NULL;
+	PurpleContact *kicker = NULL;
+	PurpleContact *kickee = NULL;
+	PurpleContactInfo *me = NULL;
+	PurpleConversation *conversation = NULL;
+	GStrv params = NULL;
+	char *reason = NULL;
+	guint n_params = 0;
+
+	params = ibis_message_get_params(ibis_message);
+	n_params = g_strv_length(params);
+
+	if(n_params < 2) {
+		g_message("received KICK with %u params, need at least 2", n_params);
+		return FALSE;
+	}
+
+	account = purple_connection_get_account(PURPLE_CONNECTION(v3_connection));
+	me = purple_account_get_contact_info(account);
+
+	kicker = purple_ircv3_connection_find_or_create_contact(v3_connection,
+	                                                        ibis_message);
+
+	conversation = purple_ircv3_connection_find_or_create_conversation(v3_connection,
+	                                                                   params[0]);
+
+	kickee = purple_ircv3_connection_find_or_create_contact_from_nick(v3_connection,
+	                                                                  params[1]);
+
+	if(n_params > 2) {
+		reason = g_strjoinv(" ", params + 2);
+	} else {
+		reason = g_strdup(_("no reason"));
+	}
+
+	if(PURPLE_CONTACT_INFO(kickee) == me) {
+		GError *error = NULL;
+
+		error = g_error_new(PURPLE_IRCV3_DOMAIN, 0,
+		                    _("You were kicked from %s by %s: %s"),
+		                    params[0],
+		                    purple_contact_info_get_name_for_display(PURPLE_CONTACT_INFO(kicker)),
+		                    reason);
+		purple_conversation_set_error(conversation, error);
+		g_clear_error(&error);
+
+		purple_conversation_set_online(conversation, FALSE);
+	} else {
+		PurpleConversationMembers *members = NULL;
+		char *contents = NULL;
+
+		contents = g_strdup_printf(_("%s kicked %s from %s: %s"),
+		                           purple_contact_info_get_name_for_display(PURPLE_CONTACT_INFO(kicker)),
+		                           purple_contact_info_get_name_for_display(PURPLE_CONTACT_INFO(kickee)),
+		                           params[0],
+		                           reason);
+
+		members = purple_conversation_get_members(conversation);
+		purple_conversation_members_remove_member(members,
+		                                          PURPLE_CONTACT_INFO(kickee),
+		                                          TRUE,
+		                                          contents);
+		g_free(contents);
+	}
+
+	g_free(reason);
+
+	return TRUE;
+}
diff --git a/protocols/ircv3/purpleircv3messagehandlers.h b/protocols/ircv3/purpleircv3messagehandlers.h
--- a/protocols/ircv3/purpleircv3messagehandlers.h
+++ b/protocols/ircv3/purpleircv3messagehandlers.h
@@ -48,6 +48,7 @@
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_nick(IbisClient *client, const char *command, IbisMessage *message, gpointer data);
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_error(IbisClient *client, const char *command, IbisMessage *message, gpointer data);
 G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_wallops(IbisClient *client, const char *command, IbisMessage *message, gpointer data);
+G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_kick(IbisClient *client, const char *command, IbisMessage *message, gpointer data);
 
 G_END_DECLS
 
