changeset 22847:790c606ff8a9

A patch from QuLogic to replace the libnm_glib calls with straight DBus calls. This removes a library dependence and should be more stable, we hope.
author Richard Laager <rlaager@wiktel.com>
date Sat, 10 May 2008 03:29:15 +0000
parents e8a66e79c49c
children 4f6040bb6f7a
files libpurple/network.c
diffstat 1 files changed, 108 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/network.c	Fri May 09 05:58:54 2008 +0000
+++ b/libpurple/network.c	Sat May 10 03:29:15 2008 +0000
@@ -60,10 +60,12 @@
 #endif
 
 #ifdef HAVE_LIBNM
-#include <libnm_glib.h>
+#include <dbus/dbus-glib.h>
+#include <NetworkManager.h>
 
-static libnm_glib_ctx *nm_context = NULL;
-static guint nm_callback_idx = 0;
+static DBusGConnection *nm_conn = NULL;
+static DBusGProxy *nm_proxy = NULL;
+static DBusGProxy *dbus_proxy = NULL;
 
 #elif defined _WIN32
 static int current_network_count;
@@ -80,7 +82,7 @@
 };
 
 #ifdef HAVE_LIBNM
-static void nm_callback_func(libnm_glib_ctx* ctx, gpointer user_data);
+static NMState nm_get_network_state(void);
 #endif
 
 const unsigned char *
@@ -593,17 +595,12 @@
 {
 #ifdef HAVE_LIBNM
 	/* Try NetworkManager first, maybe we'll get lucky */
-	int libnm_retval = -1;
-
-	if (nm_context)
+	if (nm_get_network_state() != NM_STATE_CONNECTED)
 	{
-		if ((libnm_retval = libnm_glib_get_network_state(nm_context)) == LIBNM_NO_NETWORK_CONNECTION)
-		{
-			purple_debug_warning("network", "NetworkManager not active or reports no connection (retval = %i)\n", libnm_retval);
-			return FALSE;
-		}
-		if (libnm_retval == LIBNM_ACTIVE_NETWORK_CONNECTION)	return TRUE;
-	}
+		purple_debug_warning("network", "NetworkManager not active or reports no connection\n");
+		return FALSE;
+	} else
+		return TRUE;
 #elif defined _WIN32
 	return (current_network_count > 0);
 #endif
@@ -612,40 +609,79 @@
 
 #ifdef HAVE_LIBNM
 static void
-nm_callback_func(libnm_glib_ctx* ctx, gpointer user_data)
+nm_update_state(NMState state)
 {
-	static libnm_glib_state prev = LIBNM_NO_DBUS;
-	libnm_glib_state current;
+	static NMState prev = NM_STATE_UNKNOWN;
 	PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
 
-	current = libnm_glib_get_network_state(ctx);
-	purple_debug_info("network","Entering nm_callback_func!\n");
-
 	purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
 
-	switch(current)
+	switch(state)
 	{
-	case LIBNM_ACTIVE_NETWORK_CONNECTION:
-		/* Call res_init in case DNS servers have changed */
-		res_init();
-		if (ui_ops != NULL && ui_ops->network_connected != NULL)
-			ui_ops->network_connected();
-		prev = current;
-		break;
-	case LIBNM_NO_NETWORK_CONNECTION:
-		if (prev != LIBNM_ACTIVE_NETWORK_CONNECTION)
+		case NM_STATE_CONNECTED:
+			/* Call res_init in case DNS servers have changed */
+			res_init();
+			if (ui_ops != NULL && ui_ops->network_connected != NULL)
+				ui_ops->network_connected();
+			prev = state;
 			break;
-		if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
-			ui_ops->network_disconnected();
-		prev = current;
-		break;
-	case LIBNM_NO_DBUS:
-	case LIBNM_NO_NETWORKMANAGER:
-	case LIBNM_INVALID_CONTEXT:
-	default:
-		break;
+		case NM_STATE_ASLEEP:
+		case NM_STATE_CONNECTING:
+		case NM_STATE_DISCONNECTED:
+			if (prev != NM_STATE_CONNECTED)
+				break;
+			if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
+				ui_ops->network_disconnected();
+			prev = state;
+			break;
+		case NM_STATE_UNKNOWN:
+		default:
+			break;
 	}
 }
+
+static void
+nm_state_change_cb(DBusGProxy *proxy, NMState state, gpointer user_data)
+{
+	purple_debug_info("network", "Got StateChange from NetworkManager: %d.\n", state);
+	nm_update_state(state);
+}
+
+static NMState
+nm_get_network_state(void)
+{
+	GError *err = NULL;
+	NMState state = NM_STATE_UNKNOWN;
+
+	if (!nm_proxy)
+		return NM_STATE_UNKNOWN;
+
+	if (!dbus_g_proxy_call(nm_proxy, "state", &err, G_TYPE_INVALID, G_TYPE_UINT, &state, G_TYPE_INVALID)) {
+		/* XXX: Print an error? */
+		return NM_STATE_UNKNOWN;
+	}
+
+	return state;
+}
+
+static void
+nm_dbus_name_owner_changed_cb(DBusGProxy *proxy, char *service, char *old_owner, char *new_owner, gpointer user_data)
+{
+	if (g_str_equal(service, NM_DBUS_SERVICE)) {
+		gboolean old_owner_good = old_owner && (old_owner[0] != '\0');
+		gboolean new_owner_good = new_owner && (new_owner[0] != '\0');
+
+		purple_debug_info("network", "Got NameOwnerChanged signal, service = '%s', old_owner = '%s', new_owner = '%s'\n", service, old_owner, new_owner);
+		if (!old_owner_good && new_owner_good) {	/* Equivalent to old ServiceCreated signal */
+			purple_debug_info("network", "NetworkManager has started.\n");
+			nm_update_state(nm_get_network_state());
+		} else if (old_owner_good && !new_owner_good) {	/* Equivalent to old ServiceDeleted signal */
+			purple_debug_info("network", "NetworkManager has gone away.\n");
+			nm_update_state(NM_STATE_UNKNOWN);
+		}
+	}
+}
+
 #endif
 
 void *
@@ -659,6 +695,9 @@
 void
 purple_network_init(void)
 {
+#ifdef HAVE_LIBNM
+	GError *error = NULL;
+#endif
 #ifdef _WIN32
 	GError *err = NULL;
 	gint cnt = wpurple_get_connected_network_count();
@@ -686,9 +725,26 @@
 		purple_upnp_discover(NULL, NULL);
 
 #ifdef HAVE_LIBNM
-	nm_context = libnm_glib_init();
-	if(nm_context)
-		nm_callback_idx = libnm_glib_register_callback(nm_context, nm_callback_func, NULL, g_main_context_default());
+	nm_conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (!nm_conn) {
+		purple_debug_warning("network", "Error connecting to DBus System service: %s.\n", error->message);
+	} else {
+		nm_proxy = dbus_g_proxy_new_for_name(nm_conn,
+		                                     NM_DBUS_SERVICE,
+		                                     NM_DBUS_PATH,
+		                                     NM_DBUS_INTERFACE);
+		dbus_g_proxy_add_signal(nm_proxy, NM_DBUS_SIGNAL_STATE_CHANGE, G_TYPE_UINT, G_TYPE_INVALID);
+		dbus_g_proxy_connect_signal(nm_proxy, NM_DBUS_SIGNAL_STATE_CHANGE,
+		                            G_CALLBACK(nm_state_change_cb), NULL, NULL);
+
+		dbus_proxy = dbus_g_proxy_new_for_name(nm_conn,
+		                                       DBUS_SERVICE_DBUS,
+		                                       DBUS_PATH_DBUS,
+		                                       DBUS_INTERFACE_DBUS);
+		dbus_g_proxy_add_signal(dbus_proxy, "NameOwnerChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+		dbus_g_proxy_connect_signal(dbus_proxy, "NameOwnerChanged",
+		                            G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL, NULL);
+	}
 #endif
 
 	purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
@@ -702,13 +758,16 @@
 purple_network_uninit(void)
 {
 #ifdef HAVE_LIBNM
-	/* FIXME: If anyone can think of a more clever way to shut down libnm without
-	 * using a global variable + this function, please do. */
-	if(nm_context && nm_callback_idx)
-		libnm_glib_unregister_callback(nm_context, nm_callback_idx);
-
-	if(nm_context)
-		libnm_glib_shutdown(nm_context);
+	if (nm_proxy) {
+		dbus_g_proxy_disconnect_signal(nm_proxy, NM_DBUS_SIGNAL_STATE_CHANGE, G_CALLBACK(nm_state_change_cb), NULL);
+		g_object_unref(G_OBJECT(nm_proxy));
+	}
+	if (dbus_proxy) {
+		dbus_g_proxy_disconnect_signal(dbus_proxy, "NameOwnerChanged", G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL);
+		g_object_unref(G_OBJECT(dbus_proxy));
+	}
+	if (nm_conn)
+		dbus_g_connection_unref(nm_conn);
 #endif
 
 	purple_signal_unregister(purple_network_get_handle(),