pidgin/pidgin

Parents 0cf9a6438a8f
Children e128168d9ea5
Create PurpleIRCv3Capabilities for managing capabilities for each connection

This implements everything in capabilities version 302 except for handling the
NEW and DEL commands from the server. I'm not sure how we can test that yet,
so we're ignoring it for now.

After the server has told us about all of the capabilities it supports, we
explicitly request cap-notify which we implictly request when we send
CAP LS 302. This is mostly to test our code and might be removed in the future.

Testing Done:
Connected to my local ergo and used ngrep to verify that everything was being sent correctly.

Reviewed at https://reviews.imfreedom.org/r/2100/
--- a/libpurple/protocols/ircv3/meson.build Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/meson.build Tue Nov 29 00:06:45 2022 -0600
@@ -1,4 +1,5 @@
IRCV3_SOURCES = [
+ 'purpleircv3capabilities.c',
'purpleircv3connection.c',
'purpleircv3core.c',
'purpleircv3messagehandlers.c',
@@ -8,6 +9,7 @@
]
IRCV3_HEADERS = [
+ 'purpleircv3capabilities.h',
'purpleircv3connection.h',
'purpleircv3core.h',
'purpleircv3messagehandlers.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/ircv3/purpleircv3capabilities.c Tue Nov 29 00:06:45 2022 -0600
@@ -0,0 +1,452 @@
+/*
+ * 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 "purpleircv3capabilities.h"
+
+#include "purpleircv3connection.h"
+#include "purpleircv3core.h"
+
+enum {
+ PROP_0,
+ PROP_CONNECTION,
+ N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
+enum {
+ SIG_READY,
+ SIG_ACK,
+ SIG_NAK,
+ N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
+struct _PurpleIRCv3Capabilities {
+ GObject parent;
+
+ PurpleIRCv3Connection *connection;
+
+ GHashTable *caps;
+ GPtrArray *requests;
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+purple_ircv3_capabilities_set_connection(PurpleIRCv3Capabilities *capabilities,
+ PurpleIRCv3Connection *connection)
+{
+ g_return_if_fail(PURPLE_IRCV3_IS_CAPABILITIES(capabilities));
+
+ if(g_set_object(&capabilities->connection, connection)) {
+ g_object_notify_by_pspec(G_OBJECT(capabilities),
+ properties[PROP_CONNECTION]);
+ }
+}
+
+/******************************************************************************
+ * PurpleIRCv3Capabilities Implementation
+ *****************************************************************************/
+static void
+purple_ircv3_capabilities_default_ready_cb(PurpleIRCv3Capabilities *capabilities)
+{
+ gboolean found = FALSE;
+
+ purple_ircv3_capabilities_lookup(capabilities, "cap-notify", &found);
+ if(found) {
+ purple_ircv3_capabilities_request(capabilities, "cap-notify");
+ }
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_DYNAMIC_TYPE(PurpleIRCv3Capabilities, purple_ircv3_capabilities,
+ G_TYPE_OBJECT)
+
+static void
+purple_ircv3_capabilities_get_property(GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ PurpleIRCv3Capabilities *capabilities = PURPLE_IRCV3_CAPABILITIES(obj);
+
+ switch(param_id) {
+ case PROP_CONNECTION:
+ g_value_set_object(value,
+ purple_ircv3_capabilities_get_connection(capabilities));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_ircv3_capabilities_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleIRCv3Capabilities *capabilities = PURPLE_IRCV3_CAPABILITIES(obj);
+
+ switch(param_id) {
+ case PROP_CONNECTION:
+ purple_ircv3_capabilities_set_connection(capabilities,
+ g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_ircv3_capabilities_dispose(GObject *obj) {
+ PurpleIRCv3Capabilities *capabilities = PURPLE_IRCV3_CAPABILITIES(obj);
+
+ g_clear_object(&capabilities->connection);
+
+ G_OBJECT_CLASS(purple_ircv3_capabilities_parent_class)->dispose(obj);
+}
+
+static void
+purple_ircv3_capabilities_finalize(GObject *obj) {
+ PurpleIRCv3Capabilities *capabilities = PURPLE_IRCV3_CAPABILITIES(obj);
+
+ g_hash_table_destroy(capabilities->caps);
+ g_ptr_array_free(capabilities->requests, TRUE);
+
+ G_OBJECT_CLASS(purple_ircv3_capabilities_parent_class)->finalize(obj);
+}
+
+static void
+purple_ircv3_capabilities_init(PurpleIRCv3Capabilities *capabilities) {
+ capabilities->caps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);
+ capabilities->requests = g_ptr_array_new_full(0, g_free);
+}
+
+static void
+purple_ircv3_capabilities_class_finalize(G_GNUC_UNUSED PurpleIRCv3CapabilitiesClass *klass) {
+}
+
+static void
+purple_ircv3_capabilities_class_init(PurpleIRCv3CapabilitiesClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->dispose = purple_ircv3_capabilities_dispose;
+ obj_class->finalize = purple_ircv3_capabilities_finalize;
+ obj_class->get_property = purple_ircv3_capabilities_get_property;
+ obj_class->set_property = purple_ircv3_capabilities_set_property;
+
+ /**
+ * PurpleIRCv3Capabilities:connection:
+ *
+ * The PurpleIRCv3Connection object that this capabilities was created
+ * with.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_CONNECTION] = g_param_spec_object(
+ "connection", "connection",
+ "The connection this capabilities was created for.",
+ PURPLE_IRCV3_TYPE_CONNECTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+ /**
+ * PurpleIRCv3Capabilities::ready:
+ * @capabilities: The instance.
+ *
+ * Emitted when @capabilities has finished receiving the list of
+ * capabilities from the server at startup.
+ *
+ * For dynamically added capabilities see the `added` and `removed`
+ * signals.
+ *
+ * Since: 3.0.0
+ */
+ signals[SIG_READY] = g_signal_new_class_handler(
+ "ready",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK(purple_ircv3_capabilities_default_ready_cb),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * PurpleIRCv3Capabilities::ack:
+ * @capabilities: The instance.
+ * @capability: The capability string.
+ *
+ * Emitted when the server has acknowledged a `CAP REQ` call from
+ * purple_ircv3_capabilities_request.
+ *
+ * The value of @capability will be the same as the one that was requested.
+ *
+ * Since: 3.0.0
+ */
+ signals[SIG_ACK] = g_signal_new_class_handler(
+ "ack",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ /**
+ * PurpleIRCv3Capabilities::nak:
+ * @capabilities: The instance.
+ * @capability: The capability string.
+ *
+ * Emitted when the server has nacked a `CAP REQ` call from
+ * purple_ircv3_capabilities_request.
+ *
+ * The value of @capability will be the same as the one that was requested.
+ *
+ * Since: 3.0.0
+ */
+ signals[SIG_NAK] = g_signal_new_class_handler(
+ "nak",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+}
+
+/******************************************************************************
+ * Command handlers
+ *****************************************************************************/
+static gboolean
+purple_ircv3_capabilities_handle_list(guint n_params,
+ GStrv params,
+ G_GNUC_UNUSED GError **error,
+ gpointer data)
+{
+ PurpleIRCv3Capabilities *capabilities = data;
+ gboolean done = TRUE;
+ gchar **parts = NULL;
+
+ /* Check if we have more messages coming. */
+ if(n_params > 1 && purple_strequal(params[0], "*")) {
+ parts = g_strsplit(params[1], " ", -1);
+ done = FALSE;
+ } else {
+ parts = g_strsplit(params[0], " ", -1);
+ }
+
+ /* Add each capability to our hash table, splitting the keys and values. */
+ for(int i = 0; parts[i] != NULL; i++) {
+ char *equals = g_strstr_len(parts[i], -1, "=");
+
+ if(equals != NULL) {
+ char *key = g_strndup(parts[i], equals - parts[i]);
+ char *value = g_strdup(equals + 1);
+
+ g_hash_table_insert(capabilities->caps, key, value);
+ } else {
+ g_hash_table_insert(capabilities->caps, g_strdup(parts[i]), NULL);
+ }
+ }
+
+ g_strfreev(parts);
+
+ if(done) {
+ g_signal_emit(capabilities, signals[SIG_READY], 0, signals[SIG_READY]);
+
+ /* If no capabilities were requested after we emitted the ready signal
+ * we're done with capability negotiation.
+ */
+ if(capabilities->requests->len == 0) {
+ purple_ircv3_connection_writef(capabilities->connection,
+ "CAP END");
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+purple_ircv3_capabilities_handle_ack_nak(PurpleIRCv3Capabilities *capabilities,
+ GStrv params,
+ guint sig,
+ const char *method,
+ GError **error)
+{
+ char *caps = g_strjoinv(" ", params);
+ guint index = 0;
+ gboolean found = FALSE;
+ gboolean ret = TRUE;
+
+ g_signal_emit(capabilities, sig, 0, caps);
+
+ found = g_ptr_array_find_with_equal_func(capabilities->requests, caps,
+ g_str_equal, &index);
+ if(found) {
+ g_ptr_array_remove_index(capabilities->requests, index);
+ } else {
+ g_set_error(error, PURPLE_IRCV3_DOMAIN, 0,
+ "received CAP %s for unknown capability %s", method, caps);
+ ret = FALSE;
+ }
+ g_free(caps);
+
+ if(capabilities->requests->len == 0) {
+ purple_ircv3_connection_writef(capabilities->connection, "CAP END");
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * Internal API
+ *****************************************************************************/
+void
+purple_ircv3_capabilities_register(GPluginNativePlugin *plugin) {
+ purple_ircv3_capabilities_register_type(G_TYPE_MODULE(plugin));
+}
+
+PurpleIRCv3Capabilities *
+purple_ircv3_capabilities_new(PurpleIRCv3Connection *connection) {
+ return g_object_new(
+ PURPLE_IRCV3_TYPE_CAPABILITIES,
+ "connection", connection,
+ NULL);
+}
+
+gboolean
+purple_ircv3_capabilities_message_handler(G_GNUC_UNUSED GHashTable *tags,
+ G_GNUC_UNUSED const char *source,
+ G_GNUC_UNUSED const char *command,
+ guint n_params,
+ GStrv params,
+ GError **error,
+ gpointer data)
+{
+ PurpleIRCv3Connection *connection = data;
+ PurpleIRCv3Capabilities *capabilities = NULL;
+ const char *subcommand = NULL;
+ guint n_subparams = 0;
+ GStrv subparams = NULL;
+
+ if(n_params < 2) {
+ return FALSE;
+ }
+
+ capabilities = purple_ircv3_connection_get_capabilities(connection);
+
+ /* Initialize some variables to make it easier to call our sub command
+ * handlers.
+ *
+ * params[0] is the nick or * if it hasn't been negotiated yet, we don't
+ * have a need for this, so we ignore it.
+ *
+ * params[1] is the CAP subcommand sent from the server. We use it here
+ * purely for dispatching to our subcommand handlers.
+ *
+ * params[2] and higher are the parameters to the subcommand. To make the
+ * code a bit easier all around, we subtract 2 from n_params to remove
+ * references to the nick and subcommand name. Like wise, we add 2 to the
+ * params GStrv which will now point to the second item in the array again
+ * ignoring the nick and subcommand.
+ */
+ subcommand = params[1];
+ n_subparams = n_params - 2;
+ subparams = params + 2;
+
+ /* Dispatch the subcommand. */
+ if(purple_strequal(subcommand, "LS") ||
+ purple_strequal(subcommand, "LIST"))
+ {
+ return purple_ircv3_capabilities_handle_list(n_subparams, subparams,
+ error, capabilities);
+ } else if(purple_strequal(subcommand, "ACK")) {
+ return purple_ircv3_capabilities_handle_ack_nak(capabilities,
+ subparams,
+ signals[SIG_ACK],
+ "ACK",
+ error);
+ } else if(purple_strequal(subcommand, "NAK")) {
+ return purple_ircv3_capabilities_handle_ack_nak(capabilities,
+ subparams,
+ signals[SIG_NAK],
+ "NAK",
+ error);
+ }
+
+ g_set_error(error, PURPLE_IRCV3_DOMAIN, 0,
+ "No handler for CAP subcommand %s", subcommand);
+
+ return FALSE;
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleIRCv3Connection *
+purple_ircv3_capabilities_get_connection(PurpleIRCv3Capabilities *capabilities)
+{
+ g_return_val_if_fail(PURPLE_IRCV3_IS_CAPABILITIES(capabilities), NULL);
+
+ return capabilities->connection;
+}
+
+void
+purple_ircv3_capabilities_request(PurpleIRCv3Capabilities *capabilities,
+ const char *capability)
+{
+ g_return_if_fail(PURPLE_IRCV3_IS_CAPABILITIES(capabilities));
+ g_return_if_fail(capability != NULL);
+
+ g_ptr_array_add(capabilities->requests, g_strdup(capability));
+
+ purple_ircv3_connection_writef(capabilities->connection, "CAP REQ :%s",
+ capability);
+}
+
+const char *
+purple_ircv3_capabilities_lookup(PurpleIRCv3Capabilities *capabilities,
+ const char *name, gboolean *found)
+{
+ gpointer value = NULL;
+ gboolean real_found = FALSE;
+
+ g_return_val_if_fail(PURPLE_IRCV3_IS_CAPABILITIES(capabilities), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ real_found = g_hash_table_lookup_extended(capabilities->caps, name, NULL,
+ &value);
+
+ if(found != NULL) {
+ *found = real_found;
+ }
+
+ return value;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/ircv3/purpleircv3capabilities.h Tue Nov 29 00:06:45 2022 -0600
@@ -0,0 +1,87 @@
+/*
+ * 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/>.
+ */
+
+#ifndef PURPLE_IRCV3_CAPABILITIES_H
+#define PURPLE_IRCV3_CAPABILITIES_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_IRCV3_TYPE_CAPABILITIES (purple_ircv3_capabilities_get_type())
+G_DECLARE_FINAL_TYPE(PurpleIRCv3Capabilities, purple_ircv3_capabilities,
+ PURPLE_IRCV3, CAPABILITIES, GObject)
+
+#include "purpleircv3connection.h"
+
+G_GNUC_INTERNAL void purple_ircv3_capabilities_register(GPluginNativePlugin *plugin);
+
+G_GNUC_INTERNAL PurpleIRCv3Capabilities *purple_ircv3_capabilities_new(PurpleIRCv3Connection *connection);
+
+G_GNUC_INTERNAL gboolean purple_ircv3_capabilities_message_handler(GHashTable *tags, const char *source, const char *command, guint n_params, GStrv params, GError **error, gpointer data);
+
+/**
+ * purple_ircv3_capabilities_get_connection:
+ * @capabilities: The instance.
+ *
+ * Gets the PurpleIRCv3Connection object that @capabilities was created with.
+ *
+ * Returns: (transfer none): The connection instance.
+ *
+ * Since: 3.0.0
+ */
+PurpleIRCv3Connection *purple_ircv3_capabilities_get_connection(PurpleIRCv3Capabilities *capabilities);
+
+/**
+ * purple_ircv3_capabilities_request:
+ * @capabilities: The instance.
+ * @capability: The capabilities to request.
+ *
+ * This method will send `CAP REQ @capability` to the server. Listen to the
+ * `::ack` and `::nak` signals which will contain the contents of @capability
+ * that was passed in here.
+ *
+ * Since: 3.0.0
+ */
+void purple_ircv3_capabilities_request(PurpleIRCv3Capabilities *capabilities, const char *capability);
+
+/**
+ * purple_ircv3_capabilities_lookup:
+ * @capabilities: The instance.
+ * @name: The name of the capability to look for.
+ * @found: (out) (nullable): A return address for a boolean on whether the
+ * capability was advertised or not.
+ *
+ * Gets the value that the @name capability provided if it was advertised. To
+ * determine if the capability was advertised use the @found parameter.
+ *
+ * Returns: The value of the capability named @name.
+ *
+ * Since: 3.0.0
+ */
+const char *purple_ircv3_capabilities_lookup(PurpleIRCv3Capabilities *capabilities, const char *name, gboolean *found);
+
+G_END_DECLS
+
+#endif /* PURPLE_IRCV3_CAPABILITIES_H */
--- a/libpurple/protocols/ircv3/purpleircv3connection.c Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3connection.c Tue Nov 29 00:06:45 2022 -0600
@@ -44,7 +44,7 @@
PurpleIRCv3Parser *parser;
- char *capabilities;
+ PurpleIRCv3Capabilities *capabilities;
};
G_DEFINE_DYNAMIC_TYPE(PurpleIRCv3Connection, purple_ircv3_connection,
@@ -104,6 +104,7 @@
GError *error = NULL;
gchar *line = NULL;
gsize length;
+ gboolean parsed = FALSE;
line = g_data_input_stream_read_line_finish(istream, result, &length,
&error);
@@ -126,7 +127,13 @@
return;
}
- purple_ircv3_parser_parse(connection->parser, line, &error, connection);
+ parsed = purple_ircv3_parser_parse(connection->parser, line, &error,
+ connection);
+ if(!parsed) {
+ g_warning("failed to parse '%s': %s", line,
+ error != NULL ? error->message : "unknown error");
+ }
+ g_clear_error(&error);
g_free(line);
@@ -314,7 +321,7 @@
purple_ircv3_connection_get_cancellable(connection));
break;
case PROP_CAPABILITIES:
- g_value_set_string(value,
+ g_value_set_object(value,
purple_ircv3_connection_get_capabilities(connection));
break;
default:
@@ -324,19 +331,6 @@
}
static void
-purple_ircv3_connection_set_property(GObject *obj, guint param_id,
- const GValue *value, GParamSpec *pspec)
-{
- // PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
-
- switch(param_id) {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
- break;
- }
-}
-
-static void
purple_ircv3_connection_dispose(GObject *obj) {
PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
@@ -346,6 +340,7 @@
g_clear_object(&connection->output);
g_clear_object(&connection->connection);
+ g_clear_object(&connection->capabilities);
g_clear_object(&connection->parser);
G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->dispose(obj);
@@ -357,8 +352,6 @@
g_clear_pointer(&connection->server_name, g_free);
- g_clear_pointer(&connection->capabilities, g_free);
-
G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->finalize(obj);
}
@@ -381,8 +374,9 @@
connection->server_name = g_strdup(userparts[1]);
g_strfreev(userparts);
- /* Finally create our cancellable. */
+ /* Finally create our objects. */
connection->cancellable = g_cancellable_new();
+ connection->capabilities = purple_ircv3_capabilities_new(connection);
}
static void
@@ -399,7 +393,6 @@
PurpleConnectionClass *connection_class = PURPLE_CONNECTION_CLASS(klass);
obj_class->get_property = purple_ircv3_connection_get_property;
- obj_class->set_property = purple_ircv3_connection_set_property;
obj_class->constructed = purple_ircv3_connection_constructed;
obj_class->dispose = purple_ircv3_connection_dispose;
obj_class->finalize = purple_ircv3_connection_finalize;
@@ -430,10 +423,10 @@
*
* Since: 3.0.0
*/
- properties[PROP_CAPABILITIES] = g_param_spec_string(
+ properties[PROP_CAPABILITIES] = g_param_spec_object(
"capabilities", "capabilities",
"The capabilities that the server supports",
- NULL,
+ PURPLE_IRCV3_TYPE_CAPABILITIES,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
@@ -488,32 +481,9 @@
g_bytes_unref(bytes);
}
-const char *
+PurpleIRCv3Capabilities *
purple_ircv3_connection_get_capabilities(PurpleIRCv3Connection *connection) {
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), NULL);
return connection->capabilities;
}
-
-void
-purple_ircv3_connection_append_capabilities(PurpleIRCv3Connection *connection,
- const char *capabilities)
-{
- g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
- g_return_if_fail(capabilities != NULL);
-
- if(connection->capabilities == NULL) {
- connection->capabilities = g_strdup(capabilities);
- } else {
- char *tmp = connection->capabilities;
-
- connection->capabilities = g_strdup_printf("%s %s",
- connection->capabilities,
- capabilities);
-
- g_free(tmp);
- }
-
- g_object_notify_by_pspec(G_OBJECT(connection),
- properties[PROP_CAPABILITIES]);
-}
--- a/libpurple/protocols/ircv3/purpleircv3connection.h Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3connection.h Tue Nov 29 00:06:45 2022 -0600
@@ -33,6 +33,8 @@
G_DECLARE_FINAL_TYPE(PurpleIRCv3Connection, purple_ircv3_connection,
PURPLE_IRCV3, CONNECTION, PurpleConnection)
+#include "purpleircv3capabilities.h"
+
G_GNUC_INTERNAL void purple_ircv3_connection_register(GPluginNativePlugin *plugin);
G_GNUC_INTERNAL PurpleAccount *purple_ircv3_connection_get_account(PurpleIRCv3Connection *connection);
@@ -49,9 +51,7 @@
*
* Returns: The list of capabilities that the server supports.
*/
-const char *purple_ircv3_connection_get_capabilities(PurpleIRCv3Connection *connection);
-
-G_GNUC_INTERNAL void purple_ircv3_connection_append_capabilities(PurpleIRCv3Connection *connection, const char *capabilities);
+PurpleIRCv3Capabilities *purple_ircv3_connection_get_capabilities(PurpleIRCv3Connection *connection);
G_END_DECLS
--- a/libpurple/protocols/ircv3/purpleircv3core.c Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3core.c Tue Nov 29 00:06:45 2022 -0600
@@ -26,6 +26,7 @@
#include "purpleircv3core.h"
+#include "purpleircv3capabilities.h"
#include "purpleircv3connection.h"
#include "purpleircv3protocol.h"
@@ -71,8 +72,9 @@
return FALSE;
}
+ purple_ircv3_capabilities_register(GPLUGIN_NATIVE_PLUGIN(plugin));
+ purple_ircv3_connection_register(GPLUGIN_NATIVE_PLUGIN(plugin));
purple_ircv3_protocol_register(GPLUGIN_NATIVE_PLUGIN(plugin));
- purple_ircv3_connection_register(GPLUGIN_NATIVE_PLUGIN(plugin));
manager = purple_protocol_manager_get_default();
--- a/libpurple/protocols/ircv3/purpleircv3messagehandlers.c Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3messagehandlers.c Tue Nov 29 00:06:45 2022 -0600
@@ -47,76 +47,6 @@
}
/******************************************************************************
- * Capabilities
- *****************************************************************************/
-static gboolean
-purple_ircv3_message_handler_cap_list(guint n_params, GStrv params,
- GError **error, gpointer data)
-{
- PurpleIRCv3Connection *connection = data;
-
- /* Check if we have more messages coming. */
- if(n_params > 1 && purple_strequal(params[0], "*")) {
- purple_ircv3_connection_append_capabilities(connection, params[1]);
- } else {
- purple_ircv3_connection_append_capabilities(connection, params[0]);
-
- g_message("**** capabilities-list: %s",
- purple_ircv3_connection_get_capabilities(connection));
-
- purple_ircv3_connection_writef(connection, "CAP END");
- }
-
- return TRUE;
-}
-
-gboolean
-purple_ircv3_message_handler_cap(GHashTable *tags, const char *source,
- const char *command, guint n_params,
- GStrv params, GError **error, gpointer data)
-{
- const char *subcommand = NULL;
- guint n_subparams = 0;
- GStrv subparams = NULL;
-
- if(n_params < 2) {
- return FALSE;
- }
-
- /* Initialize some variables to make it easier to call our sub command
- * handlers.
- *
- * params[0] is the nick or * if it hasn't been negotiated yet, we don't
- * have a need for this, so we ignore it.
- *
- * params[1] is the CAP subcommand sent from the server. We use it here
- * purely for dispatching to our subcommand handlers.
- *
- * params[2] and higher are the parameters to the subcommand. To make the
- * code a bit easier all around, we subtract 2 from n_params to remove
- * references to the nick and subcommand name. Like wise, we add 2 to the
- * params GStrv which will now point to the second item in the array again
- * ignoring the nick and subcommand.
- */
- subcommand = params[1];
- n_subparams = n_params - 2;
- subparams = params + 2;
-
- /* Dispatch the subcommand. */
- if(purple_strequal(subcommand, "LS") ||
- purple_strequal(subcommand, "LIST"))
- {
- return purple_ircv3_message_handler_cap_list(n_subparams, subparams,
- error, data);
- }
-
- g_set_error(error, PURPLE_IRCV3_DOMAIN, 0,
- "No handler for CAP subcommand %s", subcommand);
-
- return FALSE;
-}
-
-/******************************************************************************
* General Commands
*****************************************************************************/
gboolean
--- a/libpurple/protocols/ircv3/purpleircv3messagehandlers.h Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3messagehandlers.h Tue Nov 29 00:06:45 2022 -0600
@@ -51,7 +51,6 @@
GError **error,
gpointer data);
-G_GNUC_INTERNAL gboolean purple_ircv3_message_handler_cap(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_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_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);
--- a/libpurple/protocols/ircv3/purpleircv3parser.c Tue Nov 29 00:05:16 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3parser.c Tue Nov 29 00:06:45 2022 -0600
@@ -18,6 +18,7 @@
#include "purpleircv3parser.h"
+#include "purpleircv3capabilities.h"
#include "purpleircv3core.h"
#include "purpleircv3messagehandlers.h"
@@ -398,7 +399,7 @@
purple_ircv3_message_handler_fallback);
purple_ircv3_parser_add_handler(parser, "CAP",
- purple_ircv3_message_handler_cap);
+ purple_ircv3_capabilities_message_handler);
purple_ircv3_parser_add_handler(parser, "NOTICE",
purple_ircv3_message_handler_privmsg);
purple_ircv3_parser_add_handler(parser, "PING",