pidgin/pidgin

Parents c67c45d70006
Children 973533570200
Add async function to PurpleProtocol for testing whether or not connections are possible

This is intended to be used by the connection manager to attempt to
intelligently connect accounts.

Testing Done:
Ran the unit tests and forced them to fail as well.

Reviewed at https://reviews.imfreedom.org/r/2024/
--- a/libpurple/purpleprotocol.c Fri Nov 04 02:32:53 2022 -0500
+++ b/libpurple/purpleprotocol.c Fri Nov 04 04:00:14 2022 -0500
@@ -567,6 +567,41 @@
}
}
+void
+purple_protocol_can_connect_async(PurpleProtocol *protocol,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleProtocolClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+ if(klass != NULL && klass->can_connect_async != NULL) {
+ klass->can_connect_async(protocol, account, cancellable, callback,
+ data);
+ }
+}
+
+gboolean
+purple_protocol_can_connect_finish(PurpleProtocol *protocol,
+ GAsyncResult *result,
+ GError **error)
+{
+ PurpleProtocolClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE);
+
+ klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+ if(klass != NULL && klass->can_connect_finish != NULL) {
+ return klass->can_connect_finish(protocol, result, error);
+ }
+
+ return FALSE;
+}
PurpleConnection *
purple_protocol_create_connection(PurpleProtocol *protocol,
--- a/libpurple/purpleprotocol.h Fri Nov 04 02:32:53 2022 -0500
+++ b/libpurple/purpleprotocol.h Fri Nov 04 04:00:14 2022 -0500
@@ -145,6 +145,9 @@
void (*close)(PurpleProtocol *protocol, PurpleConnection *connection);
+ void (*can_connect_async)(PurpleProtocol *protocol, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+ gboolean (*can_connect_finish)(PurpleProtocol *protocol, GAsyncResult *result, GError **error);
+
PurpleConnection *(*create_connection)(PurpleProtocol *protocol, PurpleAccount *account, const char *password, GError **error);
GList *(*status_types)(PurpleProtocol *protocol, PurpleAccount *account);
@@ -291,6 +294,39 @@
void purple_protocol_close(PurpleProtocol *protocol, PurpleConnection *connection);
/**
+ * purple_protocol_can_connect_async:
+ * @protocol: The instance.
+ * @account: The [class@Purple.Account] instance.
+ * @cancellable: (nullable): The [class@Gio.Cancellable] instance.
+ * @callback: (scope async): The [callback@Gio.AsyncReadyCallback] to call.
+ * @data: (nullable): User data to pass to @callback.
+ *
+ * Asks @protocol if it can determine if @account can be connected.
+ *
+ * Most protocol plugins will call [method@Gio.NetworkMonitor.can_reach_async]
+ * to determine if a connection is possible.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_can_connect_async(PurpleProtocol *protocol, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_protocol_can_connect_finish:
+ * @protocol: The instance.
+ * @result: The [iface@Gio.AsyncResult] of the operation.
+ * @error: Return address for a #GError, or %NULL.
+ *
+ * This should be called from the callback of
+ * [method@Purple.Protocol.can_connect_async] to get the result of the call.
+ *
+ * Returns: %TRUE on success, otherwise %FALSE with @error optionally set.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_protocol_can_connect_finish(PurpleProtocol *protocol, GAsyncResult *result, GError **error);
+
+
+/**
* purple_protocol_create_connection:
* @protocol: The instance.
* @account: The [class@Purple.Account] for the connection.
--- a/libpurple/tests/meson.build Fri Nov 04 02:32:53 2022 -0500
+++ b/libpurple/tests/meson.build Fri Nov 04 04:00:14 2022 -0500
@@ -16,6 +16,7 @@
'notification',
'notification_manager',
'person',
+ 'protocol',
'protocol_action',
'protocol_xfer',
'purplepath',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_protocol.c Fri Nov 04 04:00:14 2022 -0500
@@ -0,0 +1,245 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+#include "test_ui.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+/* Since we're using GTask to test asynchronous functions, we need to use a
+ * main loop.
+ */
+static GMainLoop *loop = NULL;
+
+#define TEST_PROTOCOL_DOMAIN (g_quark_from_static_string("test-protocol"))
+
+/******************************************************************************
+ * TestPurpleProtocol
+ *****************************************************************************/
+#define TEST_PURPLE_TYPE_PROTOCOL (test_purple_protocol_get_type())
+G_DECLARE_FINAL_TYPE(TestPurpleProtocol,
+ test_purple_protocol,
+ TEST_PURPLE, PROTOCOL,
+ PurpleProtocol)
+
+struct _TestPurpleProtocol {
+ PurpleProtocol parent;
+
+ gboolean result;
+ GError *error;
+
+ gboolean can_connect_async;
+ gboolean can_connect_finish;
+};
+
+G_DEFINE_TYPE(TestPurpleProtocol, test_purple_protocol, PURPLE_TYPE_PROTOCOL)
+
+static void
+test_purple_protocol_can_connect_async(PurpleProtocol *protocol,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol);
+ GTask *task = NULL;
+
+ task = g_task_new(protocol, cancellable, callback, data);
+ if(test_protocol->error != NULL) {
+ g_task_return_error(task, test_protocol->error);
+ } else {
+ g_task_return_boolean(task, test_protocol->result);
+ }
+ g_clear_object(&task);
+
+ test_protocol->can_connect_async = TRUE;
+}
+
+static gboolean
+test_purple_protocol_can_connect_finish(PurpleProtocol *protocol,
+ GAsyncResult *result,
+ GError **error)
+{
+ TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol);
+
+ test_protocol->can_connect_finish = TRUE;
+
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static void
+test_purple_protocol_finalize(GObject *obj) {
+ TestPurpleProtocol *protocol = TEST_PURPLE_PROTOCOL(obj);
+
+ g_clear_error(&protocol->error);
+
+ G_OBJECT_CLASS(test_purple_protocol_parent_class)->finalize(obj);
+}
+
+static void
+test_purple_protocol_init(G_GNUC_UNUSED TestPurpleProtocol *provider) {
+}
+
+static void
+test_purple_protocol_class_init(TestPurpleProtocolClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
+
+ obj_class->finalize = test_purple_protocol_finalize;
+
+ protocol_class->can_connect_async = test_purple_protocol_can_connect_async;
+ protocol_class->can_connect_finish = test_purple_protocol_can_connect_finish;
+}
+
+static TestPurpleProtocol *
+test_purple_protocol_new(gboolean result, GError *error) {
+ TestPurpleProtocol *test_protocol = NULL;
+
+ test_protocol = g_object_new(
+ TEST_PURPLE_TYPE_PROTOCOL,
+ "id", "test-provider",
+ "name", "Test Provider",
+ NULL);
+
+ test_protocol->result = result;
+ test_protocol->error = error;
+
+ return test_protocol;
+}
+
+/******************************************************************************
+ * TestPurpleProtocol->can_connect Tests
+ *****************************************************************************/
+static gboolean
+test_purple_protocol_timeout_cb(gpointer data) {
+ g_main_loop_quit((GMainLoop *)data);
+
+ g_warning("timed out waiting for the callback function to be called");
+
+ return FALSE;
+}
+
+static void
+test_purple_protocol_can_connect_cb(GObject *obj, GAsyncResult *res,
+ gpointer data)
+{
+ PurpleProtocol *protocol = PURPLE_PROTOCOL(obj);
+ TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol);
+ GError *error = NULL;
+ gboolean result = FALSE;
+
+ result = purple_protocol_can_connect_finish(protocol, res, &error);
+
+ g_main_loop_quit(loop);
+
+ if(test_protocol->error != NULL) {
+ g_assert_error(error, TEST_PROTOCOL_DOMAIN, 0);
+ } else {
+ g_assert_no_error(error);
+ }
+
+ g_assert_true(result == test_protocol->result);
+
+ /* This is the account that we need to unref. */
+ g_object_unref(data);
+}
+
+static gboolean
+test_purple_protocol_can_connect_idle(gpointer data) {
+ PurpleProtocol *p = PURPLE_PROTOCOL(data);
+ PurpleAccount *account = NULL;
+
+ account = purple_account_new("test", "test");
+
+ purple_protocol_can_connect_async(p, account, NULL,
+ test_purple_protocol_can_connect_cb,
+ account);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+test_purple_protocol_can_connect(TestPurpleProtocol *test_protocol) {
+ g_idle_add(test_purple_protocol_can_connect_idle, test_protocol);
+ g_timeout_add_seconds(100, test_purple_protocol_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ g_assert_true(test_protocol->can_connect_async);
+ g_assert_true(test_protocol->can_connect_finish);
+
+ g_object_unref(test_protocol);
+}
+
+static void
+test_purple_protocol_can_connect_error(void) {
+ TestPurpleProtocol *protocol = NULL;
+ GError *error = NULL;
+
+ error = g_error_new(TEST_PROTOCOL_DOMAIN, 0, "no network");
+ protocol = test_purple_protocol_new(FALSE, error);
+
+ test_purple_protocol_can_connect(protocol);
+}
+
+static void
+test_purple_protocol_can_connect_false(void) {
+ TestPurpleProtocol *test_protocol = test_purple_protocol_new(FALSE, NULL);
+
+ test_purple_protocol_can_connect(test_protocol);
+}
+
+static void
+test_purple_protocol_can_connect_true(void) {
+ TestPurpleProtocol *test_protocol = test_purple_protocol_new(TRUE, NULL);
+
+ test_purple_protocol_can_connect(test_protocol);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ gint ret = 0;
+
+ g_test_init(&argc, &argv, NULL);
+
+ test_ui_purple_init();
+
+ loop = g_main_loop_new(NULL, FALSE);
+
+ g_test_add_func("/protocol/can-connect/error",
+ test_purple_protocol_can_connect_error);
+ g_test_add_func("/protocol/can-connect/false",
+ test_purple_protocol_can_connect_false);
+ g_test_add_func("/protocol/can-connect/true",
+ test_purple_protocol_can_connect_true);
+
+ ret = g_test_run();
+
+ g_main_loop_unref(loop);
+
+ return ret;
+}