--- 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 @@
+purple_protocol_can_connect_async(PurpleProtocol *protocol, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + 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, +purple_protocol_can_connect_finish(PurpleProtocol *protocol, + 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); 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. +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. +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 @@
--- /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/>. +/****************************************************************************** + *****************************************************************************/ +/* Since we're using GTask to test asynchronous functions, we need to use a +static GMainLoop *loop = NULL; +#define TEST_PROTOCOL_DOMAIN (g_quark_from_static_string("test-protocol")) +/****************************************************************************** + *****************************************************************************/ +#define TEST_PURPLE_TYPE_PROTOCOL (test_purple_protocol_get_type()) +G_DECLARE_FINAL_TYPE(TestPurpleProtocol, +struct _TestPurpleProtocol { + gboolean can_connect_async; + gboolean can_connect_finish; +G_DEFINE_TYPE(TestPurpleProtocol, test_purple_protocol, PURPLE_TYPE_PROTOCOL) +test_purple_protocol_can_connect_async(PurpleProtocol *protocol, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol); + task = g_task_new(protocol, cancellable, callback, data); + if(test_protocol->error != NULL) { + g_task_return_error(task, test_protocol->error); + g_task_return_boolean(task, test_protocol->result); + test_protocol->can_connect_async = TRUE; +test_purple_protocol_can_connect_finish(PurpleProtocol *protocol, + TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol); + test_protocol->can_connect_finish = TRUE; + return g_task_propagate_boolean(G_TASK(result), error); +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); +test_purple_protocol_init(G_GNUC_UNUSED TestPurpleProtocol *provider) { +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, + "name", "Test Provider", + test_protocol->result = result; + test_protocol->error = error; +/****************************************************************************** + * TestPurpleProtocol->can_connect Tests + *****************************************************************************/ +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"); +test_purple_protocol_can_connect_cb(GObject *obj, GAsyncResult *res, + PurpleProtocol *protocol = PURPLE_PROTOCOL(obj); + TestPurpleProtocol *test_protocol = TEST_PURPLE_PROTOCOL(protocol); + 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); + g_assert_no_error(error); + g_assert_true(result == test_protocol->result); + /* This is the account that we need to unref. */ +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, + return G_SOURCE_REMOVE; +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_assert_true(test_protocol->can_connect_async); + g_assert_true(test_protocol->can_connect_finish); + g_object_unref(test_protocol); +test_purple_protocol_can_connect_error(void) { + TestPurpleProtocol *protocol = NULL; + error = g_error_new(TEST_PROTOCOL_DOMAIN, 0, "no network"); + protocol = test_purple_protocol_new(FALSE, error); + test_purple_protocol_can_connect(protocol); +test_purple_protocol_can_connect_false(void) { + TestPurpleProtocol *test_protocol = test_purple_protocol_new(FALSE, NULL); + test_purple_protocol_can_connect(test_protocol); +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 argc, gchar *argv[]) { + g_test_init(&argc, &argv, NULL); + 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); + g_main_loop_unref(loop);