pidgin/pidgin

b82b5609c044
Parents 3844b333df53
Children b6c3acaddc83
Use PurpleProtocol.can_connect_async in the login process

Also implement it for all the new protocol plugins.

Testing Done:
Attempted connections for the new bonjour, ircv3, and new xmpp accounts and verified them made it past the checks.
Attempted and ircv3 account with a bad DNS name and verified it failed in the `can_reach_async` functions.
Also ran with the turtles.

Reviewed at https://reviews.imfreedom.org/r/3005/
--- a/libpurple/protocols/ircv3/purpleircv3protocol.c Tue Mar 19 00:38:22 2024 -0500
+++ b/libpurple/protocols/ircv3/purpleircv3protocol.c Tue Mar 19 00:53:11 2024 -0500
@@ -29,6 +29,39 @@
#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 *
@@ -148,6 +181,46 @@
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
*****************************************************************************/
@@ -177,6 +250,10 @@
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;
}
/******************************************************************************
--- a/libpurple/purpleaccount.c Tue Mar 19 00:38:22 2024 -0500
+++ b/libpurple/purpleaccount.c Tue Mar 19 00:53:11 2024 -0500
@@ -429,6 +429,54 @@
}
}
+static void
+purple_account_can_connect_cb(GObject *source, GAsyncResult *result,
+ gpointer data)
+{
+ PurpleAccount *account = data;
+ PurpleProtocol *protocol = PURPLE_PROTOCOL(source);
+ PurpleProtocolOptions options;
+ GError *error = NULL;
+ gboolean can_connect = FALSE;
+ gboolean require_password = TRUE;
+
+ can_connect = purple_protocol_can_connect_finish(protocol, result, &error);
+ if(!can_connect || error != NULL) {
+ PurpleConnectionErrorInfo *info = NULL;
+ const char *error_message = _("unknown error");
+
+ if(error != NULL) {
+ error_message = error->message;
+ }
+
+ info = purple_connection_error_info_new(PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ error_message);
+ purple_account_set_error(account, info);
+
+ g_clear_error(&error);
+
+ return;
+ }
+
+ options = purple_protocol_get_options(protocol);
+ if(options & OPT_PROTO_PASSWORD_OPTIONAL) {
+ require_password = purple_account_get_require_password(account);
+ } else if(options & OPT_PROTO_NO_PASSWORD) {
+ require_password = FALSE;
+ }
+
+ if(require_password) {
+ PurpleCredentialManager *manager = NULL;
+
+ manager = purple_credential_manager_get_default();
+ purple_credential_manager_read_password_async(manager, account, NULL,
+ purple_account_connect_got_password_cb,
+ account);
+ } else {
+ g_timeout_add_seconds(0, no_password_cb, account);
+ }
+}
+
/******************************************************************************
* XmlNode Helpers
*****************************************************************************/
@@ -1065,11 +1113,8 @@
void
purple_account_connect(PurpleAccount *account)
{
- PurpleCredentialManager *manager = NULL;
PurpleProtocol *protocol = NULL;
- PurpleProtocolOptions options;
const char *username = NULL;
- gboolean require_password = TRUE;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
@@ -1095,23 +1140,8 @@
return;
}
- purple_debug_info("account", "Connecting to account %s.\n", username);
-
- options = purple_protocol_get_options(protocol);
- if(options & OPT_PROTO_PASSWORD_OPTIONAL) {
- require_password = purple_account_get_require_password(account);
- } else if(options & OPT_PROTO_NO_PASSWORD) {
- require_password = FALSE;
- }
-
- if(require_password) {
- manager = purple_credential_manager_get_default();
- purple_credential_manager_read_password_async(manager, account, NULL,
- purple_account_connect_got_password_cb,
- account);
- } else {
- g_timeout_add_seconds(0, no_password_cb, account);
- }
+ purple_protocol_can_connect_async(protocol, account, NULL,
+ purple_account_can_connect_cb, account);
}
void
--- a/libpurple/purpleprotocol.c Tue Mar 19 00:38:22 2024 -0500
+++ b/libpurple/purpleprotocol.c Tue Mar 19 00:53:11 2024 -0500
@@ -561,6 +561,13 @@
if(klass != NULL && klass->can_connect_async != NULL) {
klass->can_connect_async(protocol, account, cancellable, callback,
data);
+ } else {
+ GTask *task = g_task_new(protocol, cancellable, callback, data);
+
+ g_task_return_boolean(task, TRUE);
+ g_task_set_source_tag(task, purple_protocol_can_connect_async);
+
+ g_clear_object(&task);
}
}
@@ -569,13 +576,19 @@
GAsyncResult *result,
GError **error)
{
- PurpleProtocolClass *klass = NULL;
+ gpointer tag = 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);
+ tag = g_task_get_source_tag(G_TASK(result));
+ if(tag == purple_protocol_can_connect_async) {
+ return g_task_propagate_boolean(G_TASK(result), error);
+ } else {
+ PurpleProtocolClass *klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+
+ if(klass != NULL && klass->can_connect_finish != NULL) {
+ return klass->can_connect_finish(protocol, result, error);
+ }
}
return FALSE;
--- a/libpurple/purpleprotocol.h Tue Mar 19 00:38:22 2024 -0500
+++ b/libpurple/purpleprotocol.h Tue Mar 19 00:53:11 2024 -0500
@@ -317,6 +317,9 @@
* Most protocol plugins will call [method@Gio.NetworkMonitor.can_reach_async]
* to determine if a connection is possible.
*
+ * If [vfunc@Protocol.can_connect_async] is not implemented, this function will
+ * create a [type@Gio.Task] that returns %TRUE.
+ *
* Since: 3.0
*/
PURPLE_AVAILABLE_IN_3_0
--- a/protocols/bonjour/purplebonjourprotocol.c Tue Mar 19 00:38:22 2024 -0500
+++ b/protocols/bonjour/purplebonjourprotocol.c Tue Mar 19 00:53:11 2024 -0500
@@ -22,6 +22,7 @@
#include "purplebonjourconnection.h"
#include "purplebonjourconstants.h"
+#include "purplebonjourcore.h"
struct _PurpleBonjourProtocol {
PurpleProtocol parent;
@@ -60,6 +61,41 @@
return types;
}
+static void
+purple_bonjour_protocol_can_connect_async(PurpleProtocol *protocol,
+ G_GNUC_UNUSED PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GTask *task = NULL;
+ GNetworkMonitor *monitor = NULL;
+ GNetworkConnectivity connectivity = 0;
+
+ task = g_task_new(protocol, cancellable, callback, data);
+
+ /* Bonjour runs on LANs so we just need local connectivity. */
+ monitor = g_network_monitor_get_default();
+ connectivity = g_network_monitor_get_connectivity(monitor);
+
+ if(connectivity >= G_NETWORK_CONNECTIVITY_LOCAL) {
+ g_task_return_boolean(task, TRUE);
+ } else {
+ g_task_return_new_error(task, PURPLE_BONJOUR_DOMAIN, 0,
+ _("Network connection not detected."));
+ }
+
+ g_clear_object(&task);
+}
+
+static gboolean
+purple_bonjour_protocol_can_connect_finish(G_GNUC_UNUSED PurpleProtocol *protocol,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
static PurpleConnection *
purple_bonjour_protocol_create_connection(PurpleProtocol *protocol,
PurpleAccount *account,
@@ -99,7 +135,8 @@
protocol_class->get_account_options = purple_bonjour_protocol_get_account_options;
protocol_class->status_types = purple_bonjour_protocol_status_types;
-
+ protocol_class->can_connect_async = purple_bonjour_protocol_can_connect_async;
+ protocol_class->can_connect_finish = purple_bonjour_protocol_can_connect_finish;
protocol_class->create_connection = purple_bonjour_protocol_create_connection;
}
--- a/protocols/xmpp/purplexmppprotocol.c Tue Mar 19 00:38:22 2024 -0500
+++ b/protocols/xmpp/purplexmppprotocol.c Tue Mar 19 00:53:11 2024 -0500
@@ -22,6 +22,7 @@
#include "purplexmppconnection.h"
#include "purplexmppconstants.h"
+#include "purplexmppcore.h"
struct _PurpleXmppProtocol {
PurpleProtocol parent;
@@ -110,6 +111,43 @@
return types;
}
+static void
+purple_xmpp_protocol_can_connect_async(PurpleProtocol *protocol,
+ G_GNUC_UNUSED PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GTask *task = NULL;
+ GNetworkMonitor *monitor = NULL;
+ GNetworkConnectivity connectivity = 0;
+
+ task = g_task_new(protocol, cancellable, callback, data);
+
+ /* Since we could be connecting to localhost, we only need to verify that
+ * our connectivity is at least G_NETWORK_CONNECTIVITY_LOCAL.
+ */
+ monitor = g_network_monitor_get_default();
+ connectivity = g_network_monitor_get_connectivity(monitor);
+
+ if(connectivity >= G_NETWORK_CONNECTIVITY_LOCAL) {
+ g_task_return_boolean(task, TRUE);
+ } else {
+ g_task_return_new_error(task, PURPLE_XMPP_DOMAIN, 0,
+ _("Network connection not detected."));
+ }
+
+ g_clear_object(&task);
+}
+
+static gboolean
+purple_xmpp_protocol_can_connect_finish(G_GNUC_UNUSED PurpleProtocol *protocol,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
static PurpleConnection *
purple_xmpp_protocol_create_connection(PurpleProtocol *protocol,
PurpleAccount *account,
@@ -149,7 +187,8 @@
protocol_class->get_user_splits = purple_xmpp_protocol_get_user_splits;
protocol_class->get_account_options = purple_xmpp_protocol_get_account_options;
protocol_class->status_types = purple_xmpp_protocol_status_types;
-
+ protocol_class->can_connect_async = purple_xmpp_protocol_can_connect_async;
+ protocol_class->can_connect_finish = purple_xmpp_protocol_can_connect_finish;
protocol_class->create_connection = purple_xmpp_protocol_create_connection;
}