# HG changeset patch
# Node ID 56225e67c543
# Parent  56a4fd2844ae
diff --git a/libpurple/protocols/ircv3/meson.build b/libpurple/protocols/ircv3/meson.build
--- a/libpurple/protocols/ircv3/meson.build
+++ b/libpurple/protocols/ircv3/meson.build
@@ -2,6 +2,7 @@
 	'purpleircv3capabilities.c',
 	'purpleircv3connection.c',
 	'purpleircv3core.c',
+	'purpleircv3ctcp.c',
 	'purpleircv3formatting.c',
 	'purpleircv3message.c',
 	'purpleircv3messagehandlers.c',
@@ -17,6 +18,7 @@
 	'purpleircv3connection.h',
 	'purpleircv3constants.h',
 	'purpleircv3core.h',
+	'purpleircv3ctcp.h',
 	'purpleircv3formatting.h',
 	'purpleircv3message.h',
 	'purpleircv3messagehandlers.h',
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
@@ -75,6 +75,32 @@
 G_GNUC_INTERNAL GCancellable *purple_ircv3_connection_get_cancellable(PurpleIRCv3Connection *connection);
 
 /**
+ * purple_ircv3_connection_emit_ctcp_request:
+ * @connection: The instance.
+ * @command: The CTCP command.
+ * @parameters: The CTCP parameters.
+ *
+ * Emits the [signal@Connection:ctcp-request] signal with the given @command
+ * and @parameters.
+ *
+ * Since: 3.0.0
+ */
+G_GNUC_INTERNAL void purple_ircv3_connection_emit_ctcp_request(PurpleIRCv3Connection *connection, const char *command, const char *parameters);
+
+/**
+ * purple_ircv3_connection_emit_ctcp_response:
+ * @connection: The instance.
+ * @command: The CTCP command.
+ * @parameters: The CTCP parameters.
+ *
+ * Emits the [signal@Connection:ctcp-response] signal with the given @command
+ * and @parameters.
+ *
+ * Since: 3.0.0
+ */
+G_GNUC_INTERNAL void purple_ircv3_connection_emit_ctcp_response(PurpleIRCv3Connection *connection, const char *command, const char *parameters);
+
+/**
  * purple_ircv3_connection_writef:
  * @connection: The instance.
  * @format: The format string.
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
@@ -36,6 +36,8 @@
 
 enum {
 	SIG_REGISTRATION_COMPLETE,
+	SIG_CTCP_REQUEST,
+	SIG_CTCP_RESPONSE,
 	N_SIGNALS,
 };
 static guint signals[N_SIGNALS] = {0, };
@@ -606,6 +608,52 @@
 		NULL,
 		G_TYPE_NONE,
 		0);
+
+	/**
+	 * PurpleIRCv3Connection::ctcp-request:
+	 * @connection: The instance.
+	 * @command: The CTCP command.
+	 * @params: The CTCP parameters.
+	 *
+	 * This signal is emitted after a CTCP request has been received.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_CTCP_REQUEST] = g_signal_new_class_handler(
+		"ctcp-request",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		2,
+		G_TYPE_STRING,
+		G_TYPE_STRING);
+
+	/**
+	 * PurpleIRCv3Connection::ctcp-response:
+	 * @connection: The instance.
+	 * @command: The CTCP command.
+	 * @params: The CTCP parameters.
+	 *
+	 * This signal is emitted after a CTCP response has been received.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_CTCP_RESPONSE] = g_signal_new_class_handler(
+		"ctcp-response",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		2,
+		G_TYPE_STRING,
+		G_TYPE_STRING);
 }
 
 /******************************************************************************
@@ -639,6 +687,28 @@
 	return priv->cancellable;
 }
 
+void
+purple_ircv3_connection_emit_ctcp_request(PurpleIRCv3Connection *connection,
+                                      const char *command,
+                                      const char *parameters)
+{
+	g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
+
+	g_signal_emit(connection, signals[SIG_CTCP_REQUEST], 0, command,
+	              parameters);
+}
+
+void
+purple_ircv3_connection_emit_ctcp_response(PurpleIRCv3Connection *connection,
+                                           const char *command,
+                                           const char *parameters)
+{
+	g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
+
+	g_signal_emit(connection, signals[SIG_CTCP_RESPONSE], 0, command,
+	              parameters);
+}
+
 /******************************************************************************
  * Public API
  *****************************************************************************/
diff --git a/libpurple/protocols/ircv3/purpleircv3constants.h b/libpurple/protocols/ircv3/purpleircv3constants.h
--- a/libpurple/protocols/ircv3/purpleircv3constants.h
+++ b/libpurple/protocols/ircv3/purpleircv3constants.h
@@ -24,6 +24,15 @@
 #define PURPLE_IRCV3_CONSTANTS_H
 
 /**
+ * PURPLE_IRCV3_CTCP_DELIMITER:
+ *
+ * The delimiter used for CTCP messages.
+ *
+ * Since: 3.0.0
+ */
+#define PURPLE_IRCV3_CTCP_DELIMITER (0x1) PURPLE_IRCV3_AVAILABLE_MACRO_IN_3_0
+
+/**
  * PURPLE_IRCV3_ERR_NICKLOCKED:
  *
  * A constant for the IRC %NICKLOCKED error.
diff --git a/libpurple/protocols/ircv3/purpleircv3ctcp.h b/libpurple/protocols/ircv3/purpleircv3ctcp.h
new file mode 100644
--- /dev/null
+++ b/libpurple/protocols/ircv3/purpleircv3ctcp.h
@@ -0,0 +1,49 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+#if !defined(PURPLE_IRCV3_GLOBAL_HEADER_INSIDE) && \
+    !defined(PURPLE_IRCV3_COMPILATION)
+# error "only <libpurple/protocols/ircv3.h> may be included directly"
+#endif
+
+#ifndef PURPLE_IRCV3_CTCP_H
+#define PURPLE_IRCV3_CTCP_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <purple.h>
+
+#include "purpleircv3connection.h"
+
+G_BEGIN_DECLS
+
+/**
+ * purple_ircv3_ctcp_handle: (skip)
+ * @connection: The connection instance.
+ * @message: The message instance.
+ *
+ * Check if @message is a CTCP message and handles it accordingly.
+ *
+ * Since: 3.0.0
+ */
+G_GNUC_INTERNAL
+void purple_ircv3_ctcp_handle(PurpleIRCv3Connection *connection, PurpleMessage *message);
+
+G_END_DECLS
+
+#endif /* PURPLE_IRCV3_CTCP_H */
diff --git a/libpurple/protocols/ircv3/purpleircv3ctcp.c b/libpurple/protocols/ircv3/purpleircv3ctcp.c
new file mode 100644
--- /dev/null
+++ b/libpurple/protocols/ircv3/purpleircv3ctcp.c
@@ -0,0 +1,96 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "purpleircv3ctcp.h"
+
+#include "purpleircv3constants.h"
+
+/******************************************************************************
+ * Internal API
+ *****************************************************************************/
+void
+purple_ircv3_ctcp_handle(PurpleIRCv3Connection *connection,
+                         PurpleMessage *message)
+{
+    const char *contents = NULL;
+
+    g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
+    g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+    contents = purple_message_get_contents(message);
+    if(contents != NULL && contents[0] == PURPLE_IRCV3_CTCP_DELIMITER) {
+        PurpleMessageFlags flags;
+        GString *str = NULL;
+        char *command = NULL;
+        char *params = NULL;
+        char *ptr = NULL;
+
+        str = g_string_new(NULL);
+
+        /* Move past the delimiter. */
+        contents += 1;
+
+        /* Find the delimiter for the command. */
+        ptr = g_strstr_len(contents, -1, " ");
+
+        /* If we don't have a space, then we have no parameters. */
+        if(ptr == NULL) {
+            size_t len = strlen(contents);
+
+            command = g_strdup(contents);
+            if(command[len -1] == PURPLE_IRCV3_CTCP_DELIMITER) {
+                command[len -1] = '\0';
+            }
+        } else {
+            size_t len = 0;
+
+            command = g_strndup(contents, ptr - contents);
+
+            params = g_strdup(ptr + 1);
+            len = strlen(params);
+            if(params[len - 1] == PURPLE_IRCV3_CTCP_DELIMITER) {
+                params[len - 1] = '\0';
+            }
+        }
+
+        flags = purple_message_get_flags(message);
+        if(flags & PURPLE_MESSAGE_NOTIFY) {
+            g_string_append_printf(str, _("CTCP %s response: %s"), command,
+                                   params);
+
+            purple_ircv3_connection_emit_ctcp_response(connection, command,
+                                                       params);
+        } else {
+            g_string_append_printf(str, _("requested CTCP %s"), command);
+            if(!purple_strempty(params)) {
+                g_string_append_printf(str, ": %s", params);
+            }
+
+            purple_ircv3_connection_emit_ctcp_request(connection, command,
+                                                      params);
+        }
+
+        purple_message_set_contents(message, str->str);
+        g_string_free(str, TRUE);
+
+        g_clear_pointer(&command, g_free);
+        g_clear_pointer(&params, g_free);
+    }
+}
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
@@ -23,6 +23,7 @@
 #include "purpleircv3connection.h"
 #include "purpleircv3constants.h"
 #include "purpleircv3core.h"
+#include "purpleircv3ctcp.h"
 #include "purpleircv3formatting.h"
 #include "purpleircv3source.h"
 
@@ -227,6 +228,9 @@
 
 	g_date_time_unref(dt);
 
+	/* Check if this is a CTCP message. */
+	purple_ircv3_ctcp_handle(connection, message);
+
 	purple_conversation_write_message(conversation, message);
 
 	g_clear_pointer(&nick, g_free);
