protocols/ircv3/purpleircv3protocol.c

changeset 42652
225762d4e206
parent 42646
b82b5609c044
child 42697
12b41aac2510
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/protocols/ircv3/purpleircv3protocol.c	Mon Mar 25 21:43:28 2024 -0500
@@ -0,0 +1,279 @@
+/*
+ * Purple - Internet Messaging Library
+ * 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
+ * source distribution.
+ *
+ * This library 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 library 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 library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "purpleircv3protocol.h"
+
+#include "purpleircv3connection.h"
+#include "purpleircv3core.h"
+#include "purpleircv3protocolconversation.h"
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_ircv3_protocol_can_reach_cb(GObject *self, GAsyncResult *result,
+                                   gpointer data)
+{
+	GError *error = NULL;
+	GTask *task = data;
+	gboolean can_reach = FALSE;
+
+	/* task and result share a cancellable, so either can be used here. */
+	if(g_task_return_error_if_cancelled(task)) {
+		g_clear_object(&task);
+
+		return;
+	}
+
+	can_reach = g_network_monitor_can_reach_finish(G_NETWORK_MONITOR(self), result,
+	                                               &error);
+
+	if(error != NULL) {
+		g_task_return_error(task, error);
+	} else if(!can_reach) {
+		g_task_return_new_error(task, PURPLE_IRCV3_DOMAIN, 0,
+		                        _("Unknown network error."));
+	} else {
+		g_task_return_boolean(task, TRUE);
+	}
+
+	g_clear_object(&task);
+}
+
+/******************************************************************************
+ * PurpleProtocol Implementation
+ *****************************************************************************/
+static GList *
+purple_ircv3_protocol_get_user_splits(G_GNUC_UNUSED PurpleProtocol *protocol) {
+	PurpleAccountUserSplit *split = NULL;
+	GList *splits = NULL;
+
+	split = purple_account_user_split_new(_("Server"),
+	                                      PURPLE_IRCV3_DEFAULT_SERVER,
+	                                      '@');
+	splits = g_list_append(splits, split);
+
+	return splits;
+}
+
+static GList *
+purple_ircv3_protocol_get_account_options(G_GNUC_UNUSED PurpleProtocol *protocol)
+{
+	PurpleAccountOption *option;
+	GList *options = NULL;
+
+	option = purple_account_option_int_new(_("Port"), "port",
+	                                       PURPLE_IRCV3_DEFAULT_TLS_PORT);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_bool_new(_("Use TLS"), "use-tls", TRUE);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("Server password"),
+	                                          "server-password", "");
+	purple_account_option_string_set_masked(option, TRUE);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("Ident name"), "ident", "");
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("Real name"), "real-name", "");
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("SASL login name"),
+	                                          "sasl-login-name", "");
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("SASL mechanisms"),
+	                                          "sasl-mechanisms", "");
+	options = g_list_append(options, option);
+
+	option = purple_account_option_bool_new(_("Allow plaintext SASL auth over "
+	                                          "unencrypted connection"),
+	                                        "plain-sasl-in-clear", FALSE);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_int_new(_("Seconds between sending "
+	                                         "messages"),
+	                                       "rate-limit-interval", 2);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_int_new(_("Maximum messages to send at "
+	                                         "once"),
+	                                       "rate-limit-burst", 5);
+	options = g_list_append(options, option);
+
+	return options;
+}
+
+static PurpleConnection *
+purple_ircv3_protocol_create_connection(PurpleProtocol *protocol,
+                                        PurpleAccount *account,
+                                        const char *password,
+                                        GError **error)
+{
+	const char *username = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	/* Make sure the username (which includes the servername via usersplits),
+	 * does not contain any whitespace.
+	 */
+	username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
+	if(strpbrk(username, " \t\v\r\n") != NULL) {
+		g_set_error(error,
+		            PURPLE_CONNECTION_ERROR,
+		            PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+		            _("IRC nick and server may not contain whitespace"));
+
+		return NULL;
+	}
+
+	return g_object_new(
+		PURPLE_IRCV3_TYPE_CONNECTION,
+		"protocol", protocol,
+		"account", account,
+		"password", password,
+		NULL);
+}
+
+static GList *
+purple_ircv3_protocol_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
+                                   G_GNUC_UNUSED PurpleAccount *account)
+{
+	PurpleStatusType *type = NULL;
+	GList *types = NULL;
+
+	type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_with_attrs(
+		PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(G_TYPE_STRING),
+		NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE);
+	types = g_list_append(types, type);
+
+	return types;
+}
+
+static void
+purple_ircv3_protocol_can_connect_async(PurpleProtocol *protocol,
+                                        PurpleAccount *account,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer data)
+{
+	GNetworkMonitor *monitor = NULL;
+	GSocketConnectable *connectable = NULL;
+	GStrv parts = NULL;
+	GTask *task = NULL;
+	const char *username = NULL;
+	gint port = 0;
+
+	task = g_task_new(protocol, cancellable, callback, data);
+
+	monitor = g_network_monitor_get_default();
+
+	username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
+	parts = g_strsplit(username, "@", 2);
+	port = purple_account_get_int(account, "port",
+	                              PURPLE_IRCV3_DEFAULT_TLS_PORT);
+
+	connectable = g_network_address_new(parts[1], (guint16)port);
+	g_strfreev(parts);
+
+	g_network_monitor_can_reach_async(monitor, connectable, cancellable,
+	                                  purple_ircv3_protocol_can_reach_cb,
+	                                  task);
+	g_clear_object(&connectable);
+}
+
+static gboolean
+purple_ircv3_protocol_can_connect_finish(G_GNUC_UNUSED PurpleProtocol *protocol,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+	return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_DYNAMIC_TYPE_EXTENDED(
+	PurpleIRCv3Protocol,
+	purple_ircv3_protocol,
+	PURPLE_TYPE_PROTOCOL,
+	0,
+	G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CONVERSATION,
+	                              purple_ircv3_protocol_conversation_init))
+
+static void
+purple_ircv3_protocol_init(G_GNUC_UNUSED PurpleIRCv3Protocol *protocol) {
+}
+
+static void
+purple_ircv3_protocol_class_finalize(G_GNUC_UNUSED PurpleIRCv3ProtocolClass *klass) {
+}
+
+static void
+purple_ircv3_protocol_class_init(PurpleIRCv3ProtocolClass *klass) {
+	PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
+
+	protocol_class->get_user_splits = purple_ircv3_protocol_get_user_splits;
+	protocol_class->get_account_options =
+		purple_ircv3_protocol_get_account_options;
+	protocol_class->create_connection =
+		purple_ircv3_protocol_create_connection;
+	protocol_class->status_types = purple_ircv3_protocol_status_types;
+	protocol_class->can_connect_async =
+		purple_ircv3_protocol_can_connect_async;
+	protocol_class->can_connect_finish =
+		purple_ircv3_protocol_can_connect_finish;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+void
+purple_ircv3_protocol_register(GPluginNativePlugin *plugin) {
+	purple_ircv3_protocol_register_type(G_TYPE_MODULE(plugin));
+}
+
+PurpleProtocol *
+purple_ircv3_protocol_new(void) {
+	return g_object_new(
+		PURPLE_IRCV3_TYPE_PROTOCOL,
+		"id", "prpl-ircv3",
+		"name", "IRCv3",
+		"description", _("Version 3 of Internet Relay Chat (IRC)."),
+		"icon-name", "im-ircv3",
+		"icon-resource-path", "/im/pidgin/libpurple/ircv3/icons",
+		"options", OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL |
+		           OPT_PROTO_SLASH_COMMANDS_NATIVE,
+		NULL);
+}

mercurial