diff options
author | Will Thompson <will.thompson@collabora.co.uk> | 2011-09-28 11:33:09 +0100 |
---|---|---|
committer | Will Thompson <will.thompson@collabora.co.uk> | 2011-09-28 11:33:48 +0100 |
commit | 25799ee8f5b02a8893d7bc5c8007e58b9d444bdd (patch) | |
tree | 16a4d53542b46f4229170bafa04426923a1190ec | |
parent | 66b28358c295e994c5a3711b6d1e04edb53d423f (diff) | |
parent | 38d76aa0c45d66828c1935b0b7573e7739af45b1 (diff) |
Merge branch 'fd.o-38978-connectivity'
Fixes: <http://bugs.freedesktop.org/show_bug.cgi?id=38978>
Reviewed-by: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
27 files changed, 1467 insertions, 198 deletions
diff --git a/configure.ac b/configure.ac index 55dad03d..aa8aa98e 100644 --- a/configure.ac +++ b/configure.ac @@ -310,6 +310,59 @@ if test x"$HAVE_MCE" = xyes; then AC_DEFINE([HAVE_MCE], [1], [Define if mce is available]) fi +# ----------------------------------------------------------- +# Connectivity integration +# ----------------------------------------------------------- +AC_ARG_WITH(connectivity, + AS_HELP_STRING([--with-connectivity=@<:@nm/connman/auto/no@:>@], + [build with connectivity support]), , + with_connectivity=auto) + +if test "x$with_connectivity" = "xno"; then + have_nm=no + have_connman=no + +elif test "x$with_connectivity" = "xconnman"; then + have_nm=no + + PKG_CHECK_MODULES(CONNMAN, + [ + dbus-glib-1 + ], have_connman="yes", have_connman="no") + + if test "x$have_connman" = "xyes"; then + AC_DEFINE(HAVE_CONNMAN, 1, [Define if you have connman dependencies]) + fi + +else + have_connman=no + + PKG_CHECK_MODULES(NETWORK_MANAGER, + [ + libnm-glib >= 0.7.0 + ], have_nm="yes", have_nm="no") + + if test "x$have_nm" = "xyes"; then + AC_DEFINE(HAVE_NM, 1, [Define if you have libnm-glib]) + fi +fi + +if test "x$with_connectivity" = "xconnman" -a "x$have_connman" != "xyes"; then + AC_MSG_ERROR([Couldn't find connman dependencies: + +$CONNMAN_PKG_ERRORS]) +fi + +if test "x$with_connectivity" = "xnm" -a "x$have_nm" != "xyes"; then + AC_MSG_ERROR([Couldn't find Network Manager dependencies: + +$NETWORK_MANAGER_PKG_ERRORS]) +fi + +AM_CONDITIONAL(HAVE_NM, test "x$have_nm" = "xyes") +AM_CONDITIONAL(HAVE_CONNMAN, test "x$have_connman" = "xyes") + + dnl *************************************************************************** dnl Check for marshal and enum generators dnl *************************************************************************** @@ -361,6 +414,8 @@ Configure summary: Features: Plugin API documentation.....: ${enable_gtk_doc} Gnome Keyring................: ${keyring_enabled} + Network Manager integration..: ${have_nm} + ConnMan integration..........: ${have_connman} Aegis........................: ${aegis_enabled} libaccounts-glib backend.....: ${libaccounts_sso_enabled} Hidden accounts-glib accounts: ${with_accounts_glib_hidden_service_type} diff --git a/src/Makefile.am b/src/Makefile.am index 7b13de59..61de2bde 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,6 +2,8 @@ INCLUDES = \ $(TELEPATHY_CFLAGS) \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ + $(NETWORK_MANAGER_CFLAGS) \ + $(CONNMAN_CFLAGS) \ -DG_LOG_DOMAIN=\"mcd\" \ -DMCD_PLUGIN_LOADER_DIR=\"@libdir@/mission-control-plugins.@MCP_ABI_VERSION@\" \ -DMC_DISABLE_DEPRECATED -I$(top_srcdir) @@ -97,7 +99,9 @@ libmcd_convenience_la_LIBADD = \ $(top_builddir)/mission-control-plugins/libmission-control-plugins.la \ $(TELEPATHY_LIBS) \ $(DBUS_LIBS) \ - $(GLIB_LIBS) + $(GLIB_LIBS) \ + $(NETWORK_MANAGER_LIBS) \ + $(CONNMAN_LIBS) if ENABLE_GNOME_KEYRING libmcd_convenience_la_LIBADD += $(GNOME_KEYRING_LIBS) @@ -168,7 +172,11 @@ libmcd_convenience_la_SOURCES = \ channel-utils.h \ client-registry.c \ client-registry.h \ + connectivity-monitor.c \ + connectivity-monitor.h \ gtypes.c \ + kludge-transport.c \ + kludge-transport.h \ mcd-dbusprop.c \ mcd-dbusprop.h \ mcd-debug.c \ diff --git a/src/connectivity-monitor.c b/src/connectivity-monitor.c new file mode 100644 index 00000000..3235f776 --- /dev/null +++ b/src/connectivity-monitor.c @@ -0,0 +1,438 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* + * Copyright © 2009–2011 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Jonny Lamb <jonny.lamb@collabora.co.uk> + * Will Thompson <will.thompson@collabora.co.uk> + */ + +#include "config.h" +#include "connectivity-monitor.h" + + +#ifdef HAVE_NM +# ifdef HAVE_CONNMAN +# error tried to build both Network Manager and ConnMan support simultaneously! +/* I mean, we could support both one day, but not for now. */ +# endif +/* Moving swiftly on… */ +#include <nm-client.h> +#endif + +#ifdef HAVE_CONNMAN +#include <dbus/dbus-glib.h> +#endif + +#include <telepathy-glib/util.h> + +#include "mcd-debug.h" + +struct _McdConnectivityMonitorPrivate { +#ifdef HAVE_NM + NMClient *nm_client; + gulong state_change_signal_id; +#endif + +#ifdef HAVE_CONNMAN + DBusGProxy *proxy; +#endif + + gboolean connected; + gboolean use_conn; +}; + +enum { + STATE_CHANGE, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_USE_CONN, +}; + +static guint signals[LAST_SIGNAL]; +static McdConnectivityMonitor *connectivity_monitor_singleton = NULL; + +G_DEFINE_TYPE (McdConnectivityMonitor, mcd_connectivity_monitor, G_TYPE_OBJECT); + +static void +connectivity_monitor_change_state (McdConnectivityMonitor *connectivity_monitor, + gboolean new_state) +{ + McdConnectivityMonitorPrivate *priv; + + priv = connectivity_monitor->priv; + + if (priv->connected == new_state) + return; + + priv->connected = new_state; + + g_signal_emit (connectivity_monitor, signals[STATE_CHANGE], 0, + priv->connected); +} + +#ifdef HAVE_NM + +#if !defined(NM_CHECK_VERSION) +#define NM_CHECK_VERSION(x,y,z) 0 +#endif + +static void +connectivity_monitor_nm_state_change_cb (NMClient *client, + const GParamSpec *pspec, + McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv; + gboolean new_nm_connected; + NMState state; + + priv = connectivity_monitor->priv; + + if (!priv->use_conn) + return; + + state = nm_client_get_state (priv->nm_client); + new_nm_connected = !(state == NM_STATE_CONNECTING +#if NM_CHECK_VERSION(0,8,992) + || state == NM_STATE_DISCONNECTING +#endif + || state == NM_STATE_ASLEEP + || state == NM_STATE_DISCONNECTED); + + DEBUG ("New NetworkManager network state %d (connected: %s)", state, + new_nm_connected ? "true" : "false"); + + connectivity_monitor_change_state (connectivity_monitor, new_nm_connected); +} +#endif + +#ifdef HAVE_CONNMAN +static void +connectivity_monitor_connman_state_changed_cb (DBusGProxy *proxy, + const gchar *new_state, + McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv; + gboolean new_connected; + + priv = connectivity_monitor->priv; + + if (!priv->use_conn) + return; + + new_connected = !tp_strdiff (new_state, "online"); + + DEBUG ("New ConnMan network state %s", new_state); + + connectivity_monitor_change_state (connectivity_monitor, new_connected); +} + +static void +connectivity_monitor_connman_check_state_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) +{ + McdConnectivityMonitor *connectivity_monitor = (McdConnectivityMonitor *) user_data; + GError *error = NULL; + gchar *state; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, + G_TYPE_STRING, &state, G_TYPE_INVALID)) + { + connectivity_monitor_connman_state_changed_cb (proxy, state, + connectivity_monitor); + g_free (state); + } + else + { + DEBUG ("Failed to call GetState: %s", error->message); + connectivity_monitor_connman_state_changed_cb (proxy, "offline", + connectivity_monitor); + } +} + +static void +connectivity_monitor_connman_check_state (McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv; + + priv = connectivity_monitor->priv; + + dbus_g_proxy_begin_call (priv->proxy, "GetState", + connectivity_monitor_connman_check_state_cb, connectivity_monitor, NULL, + G_TYPE_INVALID); +} +#endif + +static void +mcd_connectivity_monitor_init (McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv; +#ifdef HAVE_CONNMAN + DBusGConnection *connection; + GError *error = NULL; +#endif + + priv = G_TYPE_INSTANCE_GET_PRIVATE (connectivity_monitor, + MCD_TYPE_CONNECTIVITY_MONITOR, McdConnectivityMonitorPrivate); + + connectivity_monitor->priv = priv; + + priv->use_conn = TRUE; + +#ifdef HAVE_NM + priv->nm_client = nm_client_new (); + if (priv->nm_client != NULL) + { + priv->state_change_signal_id = g_signal_connect (priv->nm_client, + "notify::" NM_CLIENT_STATE, + G_CALLBACK (connectivity_monitor_nm_state_change_cb), connectivity_monitor); + + connectivity_monitor_nm_state_change_cb (priv->nm_client, NULL, connectivity_monitor); + } + else + { + DEBUG ("Failed to get NetworkManager proxy"); + } +#endif + +#ifdef HAVE_CONNMAN + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection != NULL) + { + priv->proxy = dbus_g_proxy_new_for_name (connection, + "net.connman", "/", + "net.connman.Manager"); + + dbus_g_object_register_marshaller ( + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->proxy, "StateChanged", + G_TYPE_STRING, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->proxy, "StateChanged", + G_CALLBACK (connectivity_monitor_connman_state_changed_cb), + connectivity_monitor, NULL); + + connectivity_monitor_connman_check_state (connectivity_monitor); + } + else + { + DEBUG ("Failed to get system bus connection: %s", error->message); + g_error_free (error); + } +#endif + +#if !defined(HAVE_NM) && !defined(HAVE_CONNMAN) + priv->connected = TRUE; +#endif +} + +static void +connectivity_monitor_finalize (GObject *object) +{ +#ifdef HAVE_NM + McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object); + McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; + + if (priv->nm_client != NULL) + { + g_signal_handler_disconnect (priv->nm_client, + priv->state_change_signal_id); + priv->state_change_signal_id = 0; + g_object_unref (priv->nm_client); + priv->nm_client = NULL; + } +#endif + +#ifdef HAVE_CONNMAN + McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object); + McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; + + if (priv->proxy != NULL) + { + dbus_g_proxy_disconnect_signal (priv->proxy, "StateChanged", + G_CALLBACK (connectivity_monitor_connman_state_changed_cb), connectivity_monitor); + + g_object_unref (priv->proxy); + priv->proxy = NULL; + } +#endif + + G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->finalize (object); +} + +static void +connectivity_monitor_dispose (GObject *object) +{ + G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->dispose (object); +} + +static GObject * +connectivity_monitor_constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *retval; + + if (!connectivity_monitor_singleton) + { + retval = G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->constructor + (type, n_construct_params, construct_params); + + connectivity_monitor_singleton = MCD_CONNECTIVITY_MONITOR (retval); + g_object_add_weak_pointer (retval, (gpointer) &connectivity_monitor_singleton); + } + else + { + retval = g_object_ref (connectivity_monitor_singleton); + } + + return retval; +} + +static void +connectivity_monitor_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object); + + switch (param_id) + { + case PROP_USE_CONN: + g_value_set_boolean (value, mcd_connectivity_monitor_get_use_conn ( + connectivity_monitor)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +connectivity_monitor_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object); + + switch (param_id) + { + case PROP_USE_CONN: + mcd_connectivity_monitor_set_use_conn (connectivity_monitor, + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +mcd_connectivity_monitor_class_init (McdConnectivityMonitorClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->finalize = connectivity_monitor_finalize; + oclass->dispose = connectivity_monitor_dispose; + oclass->constructor = connectivity_monitor_constructor; + oclass->get_property = connectivity_monitor_get_property; + oclass->set_property = connectivity_monitor_set_property; + + signals[STATE_CHANGE] = + g_signal_new ("state-change", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN, NULL); + + g_object_class_install_property (oclass, + PROP_USE_CONN, + g_param_spec_boolean ("use-conn", + "Use connectivity managers", + "Set presence according to connectivity managers", + TRUE, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_type_class_add_private (oclass, sizeof (McdConnectivityMonitorPrivate)); +} + +/* public methods */ + +McdConnectivityMonitor * +mcd_connectivity_monitor_new (void) +{ + return g_object_new (MCD_TYPE_CONNECTIVITY_MONITOR, NULL); +} + +gboolean +mcd_connectivity_monitor_is_online (McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; + + return priv->connected; +} + +gboolean +mcd_connectivity_monitor_get_use_conn (McdConnectivityMonitor *connectivity_monitor) +{ + McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; + + return priv->use_conn; +} + +void +mcd_connectivity_monitor_set_use_conn (McdConnectivityMonitor *connectivity_monitor, + gboolean use_conn) +{ + McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; + + if (use_conn == priv->use_conn) + return; + + DEBUG ("use_conn GSetting key changed; new value = %s", + use_conn ? "true" : "false"); + + priv->use_conn = use_conn; + +#if defined(HAVE_NM) || defined(HAVE_CONNMAN) + if (use_conn) + { +#if defined(HAVE_NM) + connectivity_monitor_nm_state_change_cb (priv->nm_client, NULL, connectivity_monitor); +#elif defined(HAVE_CONNMAN) + connectivity_monitor_connman_check_state (connectivity_monitor); +#endif + } + else +#endif + { + connectivity_monitor_change_state (connectivity_monitor, TRUE); + } + + g_object_notify (G_OBJECT (connectivity_monitor), "use-conn"); +} diff --git a/src/connectivity-monitor.h b/src/connectivity-monitor.h new file mode 100644 index 00000000..ce4b59e7 --- /dev/null +++ b/src/connectivity-monitor.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* + * Copyright © 2009–2011 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Jonny Lamb <jonny.lamb@collabora.co.uk> + * Will Thompson <will.thompson@collabora.co.uk> + */ + +#ifndef MCD_CONNECTIVITY_MONITOR_H +#define MCD_CONNECTIVITY_MONITOR_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MCD_TYPE_CONNECTIVITY_MONITOR (mcd_connectivity_monitor_get_type ()) +#define MCD_CONNECTIVITY_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_CONNECTIVITY_MONITOR, \ + McdConnectivityMonitor)) +#define MCD_CONNECTIVITY_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MCD_TYPE_CONNECTIVITY_MONITOR, \ + McdConnectivityMonitorClass)) +#define MCD_IS_CONNECTIVITY_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_CONNECTIVITY_MONITOR)) +#define MCD_IS_CONNECTIVITY_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_CONNECTIVITY_MONITOR)) +#define MCD_CONNECTIVITY_MONITOR_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_CONNECTIVITY_MONITOR, \ + McdConnectivityMonitorClass)) + +typedef struct _McdConnectivityMonitor McdConnectivityMonitor; +typedef struct _McdConnectivityMonitorClass McdConnectivityMonitorClass; +typedef struct _McdConnectivityMonitorPrivate McdConnectivityMonitorPrivate; + +struct _McdConnectivityMonitor { + GObject parent; + McdConnectivityMonitorPrivate *priv; +}; + +struct _McdConnectivityMonitorClass { + GObjectClass parent_class; +}; + +GType mcd_connectivity_monitor_get_type (void); + +/* public methods */ + +McdConnectivityMonitor *mcd_connectivity_monitor_new (void); + +gboolean mcd_connectivity_monitor_is_online (McdConnectivityMonitor *connectivity); + +gboolean mcd_connectivity_monitor_get_use_conn (McdConnectivityMonitor *connectivity); +void mcd_connectivity_monitor_set_use_conn (McdConnectivityMonitor *connectivity, + gboolean use_conn); + +G_END_DECLS + +#endif /* MCD_CONNECTIVITY_MONITOR_H */ + diff --git a/src/kludge-transport.c b/src/kludge-transport.c new file mode 100644 index 00000000..54717bb4 --- /dev/null +++ b/src/kludge-transport.c @@ -0,0 +1,263 @@ +/* + * kludge-transport.c - the shortest path to NM integration + * Copyright ©2011 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "kludge-transport.h" + +#include <telepathy-glib/util.h> + +#include "mcd-debug.h" + +#include "connectivity-monitor.h" + +struct _McdKludgeTransportPrivate { + /* Rawr! I'm a mythical creature. */ + McdConnectivityMonitor *minotaur; + + /* Pointers representing network connections, exposed to the application as + * opaque McdTransport pointers. + * + * In this generate example of an McdTransportPlugin, we only have one + * transport, representing "the internet". So in fact this list always + * contains a single pointer, namely the McdKludgeTransport instance + * itself. + */ + GList *transports; + + /* Hold a set of McdAccounts which would like to go online. */ + GHashTable *pending_accounts; +}; + +static void transport_iface_init ( + gpointer g_iface, + gpointer iface_data); +static void monitor_state_changed_cb ( + McdConnectivityMonitor *monitor, + gboolean connected, + gpointer user_data); + +G_DEFINE_TYPE_WITH_CODE (McdKludgeTransport, mcd_kludge_transport, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (MCD_TYPE_TRANSPORT_PLUGIN, transport_iface_init); + ) + +static void +mcd_kludge_transport_init (McdKludgeTransport *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + MCD_TYPE_KLUDGE_TRANSPORT, McdKludgeTransportPrivate); +} + +static void +mcd_kludge_transport_constructed (GObject *object) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (object); + McdKludgeTransportPrivate *priv = self->priv; + GObjectClass *parent_class = mcd_kludge_transport_parent_class; + + if (parent_class->constructed != NULL) + parent_class->constructed (object); + + priv->minotaur = mcd_connectivity_monitor_new (); + tp_g_signal_connect_object (priv->minotaur, "state-change", + (GCallback) monitor_state_changed_cb, self, G_CONNECT_AFTER); + + /* We just use ourself as the McdTransport pointer... */ + priv->transports = g_list_prepend (NULL, self); + + priv->pending_accounts = g_hash_table_new_full (NULL, NULL, + g_object_unref, NULL); +} + +static void +mcd_kludge_transport_dispose (GObject *object) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (object); + McdKludgeTransportPrivate *priv = self->priv; + GObjectClass *parent_class = mcd_kludge_transport_parent_class; + + tp_clear_object (&priv->minotaur); + g_list_free (priv->transports); + priv->transports = NULL; + + g_hash_table_unref (priv->pending_accounts); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + +static void +mcd_kludge_transport_class_init (McdKludgeTransportClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = mcd_kludge_transport_constructed; + object_class->dispose = mcd_kludge_transport_dispose; + + g_type_class_add_private (klass, sizeof (McdKludgeTransportPrivate)); +} + +static const gchar * +mcd_kludge_transport_get_name ( + McdTransportPlugin *plugin) +{ + g_return_val_if_fail (MCD_IS_KLUDGE_TRANSPORT (plugin), NULL); + + return "McdKludgeTransport"; +} + +static const GList * +mcd_kludge_transport_get_transports ( + McdTransportPlugin *plugin) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (plugin); + + g_return_val_if_fail (MCD_IS_KLUDGE_TRANSPORT (plugin), NULL); + + return self->priv->transports; +} + +static const gchar * +mcd_kludge_transport_get_transport_name ( + McdTransportPlugin *plugin, + McdTransport *transport) +{ + g_return_val_if_fail (MCD_IS_KLUDGE_TRANSPORT (plugin), NULL); + g_return_val_if_fail (plugin == (McdTransportPlugin *) transport, NULL); + + return "i love the internet"; +} + +static McdTransportStatus +mcd_kludge_transport_get_transport_status ( + McdTransportPlugin *plugin, + McdTransport *transport) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (plugin); + gboolean online; + + g_return_val_if_fail (MCD_IS_KLUDGE_TRANSPORT (plugin), + MCD_TRANSPORT_STATUS_DISCONNECTED); + g_return_val_if_fail (plugin == (McdTransportPlugin *) transport, + MCD_TRANSPORT_STATUS_DISCONNECTED); + + online = mcd_connectivity_monitor_is_online (self->priv->minotaur); + DEBUG ("we are allegedly %s", online ? "online" : "offline"); + + if (online) + return MCD_TRANSPORT_STATUS_CONNECTED; + else + return MCD_TRANSPORT_STATUS_DISCONNECTED; +} + +static void +transport_iface_init ( + gpointer g_iface, + gpointer iface_data) +{ + McdTransportPluginIface *klass = g_iface; + + klass->get_name = mcd_kludge_transport_get_name; + klass->get_transports = mcd_kludge_transport_get_transports; + klass->get_transport_name = mcd_kludge_transport_get_transport_name; + klass->get_transport_status = mcd_kludge_transport_get_transport_status; +} + +static void +monitor_state_changed_cb ( + McdConnectivityMonitor *monitor, + gboolean connected, + gpointer user_data) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (user_data); + McdTransportStatus new_status = + connected ? MCD_TRANSPORT_STATUS_CONNECTED + : MCD_TRANSPORT_STATUS_DISCONNECTED; + GHashTableIter iter; + gpointer key; + + g_signal_emit_by_name (self, "status-changed", self, new_status); + + g_hash_table_iter_init (&iter, self->priv->pending_accounts); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + McdAccount *account = MCD_ACCOUNT (key); + + /* If we've gone online, allow the account to actually try to connect; + * if we've fallen offline, say as much. (I don't actually think this + * code will be reached if !connected, but.) + */ + DEBUG ("telling %s to %s", mcd_account_get_unique_name (account), + connected ? "proceed" : "give up"); + mcd_account_connection_bind_transport (account, (McdTransport *) self); + mcd_account_connection_proceed_with_reason (account, connected, + connected ? TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED + : TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); + g_hash_table_iter_remove (&iter); + } +} + +/* + * mcd_kludge_transport_account_connection_cb: + * @account: an account which would like to go online + * @parameters: the connection parameters to be used + * @user_data: the McdKludgeTransport. + * + * Called when an account would like to sign in. + */ +static void +mcd_kludge_transport_account_connection_cb ( + McdAccount *account, + GHashTable *parameters, + gpointer user_data) +{ + McdKludgeTransport *self = MCD_KLUDGE_TRANSPORT (user_data); + McdKludgeTransportPrivate *priv = self->priv; + + if (mcd_connectivity_monitor_is_online (priv->minotaur)) + { + mcd_account_connection_bind_transport (account, (McdTransport *) self); + mcd_account_connection_proceed (account, TRUE); + } + else if (g_hash_table_lookup (priv->pending_accounts, account) == NULL) + { + DEBUG ("%s wants to connect, but we're offline; queuing it up", + mcd_account_get_unique_name (account)); + g_object_ref (account); + g_hash_table_insert (priv->pending_accounts, account, account); + } + /* ... else we're already waiting, I guess */ +} + +static McdTransportPlugin * +mcd_kludge_transport_new (void) +{ + return g_object_new (MCD_TYPE_KLUDGE_TRANSPORT, NULL); +} + +void +mcd_kludge_transport_install ( + McdPlugin *plugin) +{ + McdTransportPlugin *self = mcd_kludge_transport_new (); + + mcd_plugin_register_transport (plugin, self); + mcd_plugin_register_account_connection (plugin, + mcd_kludge_transport_account_connection_cb, + MCD_ACCOUNT_CONNECTION_PRIORITY_TRANSPORT, self); +} diff --git a/src/kludge-transport.h b/src/kludge-transport.h new file mode 100644 index 00000000..b52d3a7a --- /dev/null +++ b/src/kludge-transport.h @@ -0,0 +1,62 @@ +/* + * kludge-transport.h - header for the shortest path to NM integration + * Copyright ©2011 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MCD_KLUDGE_TRANSPORT_H +#define MCD_KLUDGE_TRANSPORT_H + +#include <glib-object.h> +#include "mcd-plugin.h" +#include "mcd-transport.h" + +typedef struct _McdKludgeTransport McdKludgeTransport; +typedef struct _McdKludgeTransportClass McdKludgeTransportClass; +typedef struct _McdKludgeTransportPrivate McdKludgeTransportPrivate; + +struct _McdKludgeTransportClass { + GObjectClass parent_class; +}; + +struct _McdKludgeTransport { + GObject parent; + + McdKludgeTransportPrivate *priv; +}; + +GType mcd_kludge_transport_get_type (void); + +void mcd_kludge_transport_install ( + McdPlugin *plugin); + +/* TYPE MACROS */ +#define MCD_TYPE_KLUDGE_TRANSPORT \ + (mcd_kludge_transport_get_type ()) +#define MCD_KLUDGE_TRANSPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MCD_TYPE_KLUDGE_TRANSPORT, McdKludgeTransport)) +#define MCD_KLUDGE_TRANSPORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MCD_TYPE_KLUDGE_TRANSPORT,\ + McdKludgeTransportClass)) +#define MCD_IS_KLUDGE_TRANSPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MCD_TYPE_KLUDGE_TRANSPORT)) +#define MCD_IS_KLUDGE_TRANSPORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MCD_TYPE_KLUDGE_TRANSPORT)) +#define MCD_KLUDGE_TRANSPORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), MCD_TYPE_KLUDGE_TRANSPORT, \ + McdKludgeTransportClass)) + +#endif /* MCD_KLUDGE_TRANSPORT_H */ diff --git a/src/mcd-account-priv.h b/src/mcd-account-priv.h index 0e744f09..9097ed5c 100644 --- a/src/mcd-account-priv.h +++ b/src/mcd-account-priv.h @@ -108,17 +108,6 @@ void _mcd_account_online_request (McdAccount *account, gpointer userdata); void _mcd_account_connect_with_auto_presence (McdAccount *account, gboolean user_initiated); -G_GNUC_INTERNAL -void _mcd_account_online_request_completed (McdAccount *account, - GError *error); - -typedef struct { - McdOnlineRequestCb callback; - gpointer user_data; -} McdOnlineRequestData; - -G_GNUC_INTERNAL -GList *_mcd_account_get_online_requests (McdAccount *account); G_GNUC_INTERNAL McdStorage *_mcd_account_get_storage (McdAccount *account); @@ -192,11 +181,6 @@ G_GNUC_INTERNAL gboolean _mcd_account_set_enabled (McdAccount *account, gboolean write_out, GError **error); -G_GNUC_INTERNAL void _mcd_account_get_requested_presence (McdAccount *account, - TpConnectionPresenceType *presence, - const gchar **status, - const gchar **message); - G_GNUC_INTERNAL gboolean _mcd_account_presence_type_is_settable ( TpConnectionPresenceType type); diff --git a/src/mcd-account.c b/src/mcd-account.c index 7935a36e..d43a69a3 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -118,6 +118,11 @@ G_DEFINE_TYPE_WITH_CODE (McdAccount, mcd_account, G_TYPE_OBJECT, properties_iface_init); ) +typedef struct { + McdOnlineRequestCb callback; + gpointer user_data; +} McdOnlineRequestData; + struct _McdAccountPrivate { gchar *unique_name; @@ -233,27 +238,8 @@ _mcd_account_maybe_autoconnect (McdAccount *account) g_return_if_fail (MCD_IS_ACCOUNT (account)); priv = account->priv; - if (!priv->enabled) - { - DEBUG ("%s not Enabled", priv->unique_name); - return; - } - - if (!mcd_account_is_valid (account)) + if (!mcd_account_would_like_to_connect (account)) { - DEBUG ("%s not Valid", priv->unique_name); - return; - } - - if (priv->conn_status != TP_CONNECTION_STATUS_DISCONNECTED) - { - DEBUG ("%s already connecting/connected", priv->unique_name); - return; - } - - if (!priv->connect_automatically) - { - DEBUG ("%s does not ConnectAutomatically", priv->unique_name); return; } @@ -919,6 +905,25 @@ mcd_account_request_presence_int (McdAccount *account, } } +/* + * mcd_account_rerequest_presence: + * + * Re-requests the account's current RequestedPresence, possibly triggering a + * new connection attempt. + */ +static void +mcd_account_rerequest_presence (McdAccount *account, + gboolean user_initiated) +{ + McdAccountPrivate *priv = account->priv; + + mcd_account_request_presence_int (account, + priv->req_presence_type, + priv->req_presence_status, + priv->req_presence_message, + user_initiated); +} + void _mcd_account_connect (McdAccount *account, GHashTable *params) { @@ -1205,11 +1210,7 @@ _mcd_account_set_enabled (McdAccount *account, if (enabled) { - mcd_account_request_presence_int (account, - priv->req_presence_type, - priv->req_presence_status, - priv->req_presence_message, - TRUE); + mcd_account_rerequest_presence (account, TRUE); _mcd_account_maybe_autoconnect (account); } } @@ -3357,24 +3358,6 @@ mcd_account_get_requested_presence (McdAccount *account, *message = priv->req_presence_message; } -void -_mcd_account_get_requested_presence (McdAccount *account, - TpConnectionPresenceType *presence, - const gchar **status, - const gchar **message) -{ - McdAccountPrivate *priv = account->priv; - - if (presence != NULL) - *presence = priv->req_presence_type; - - if (status != NULL) - *status = priv->req_presence_status; - - if (message != NULL) - *message = priv->req_presence_message; -} - /* TODO: remove when the relative members will become public */ void mcd_account_get_current_presence (McdAccount *account, @@ -3401,6 +3384,52 @@ mcd_account_get_connect_automatically (McdAccount *account) return priv->connect_automatically; } +/* + * mcd_account_would_like_to_connect: + * @account: an account + * + * Returns: %TRUE if @account is not currently in the process of trying to + * connect, but would like to be, in a perfect world. + */ +gboolean +mcd_account_would_like_to_connect (McdAccount *account) +{ + McdAccountPrivate *priv; + + g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE); + priv = account->priv; + + if (!priv->enabled) + { + DEBUG ("%s not Enabled", priv->unique_name); + return FALSE; + } + + if (!mcd_account_is_valid (account)) + { + DEBUG ("%s not Valid", priv->unique_name); + return FALSE; + } + + if (priv->conn_status != TP_CONNECTION_STATUS_DISCONNECTED) + { + DEBUG ("%s already connecting/connected", priv->unique_name); + return FALSE; + } + + if (!priv->connect_automatically && + !_presence_type_is_online (priv->req_presence_type)) + { + DEBUG ("%s does not ConnectAutomatically, and its RequestedPresence " + "(%u, '%s', '%s') doesn't indicate the user wants to be online", + priv->unique_name, priv->req_presence_type, + priv->req_presence_status, priv->req_presence_message); + return FALSE; + } + + return TRUE; +} + /* TODO: remove when the relative members will become public */ void mcd_account_get_automatic_presence (McdAccount *account, @@ -3657,7 +3686,7 @@ mcd_account_get_alias (McdAccount *account) MC_ACCOUNTS_KEY_ALIAS); } -void +static void _mcd_account_online_request_completed (McdAccount *account, GError *error) { McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account); @@ -3678,14 +3707,6 @@ _mcd_account_online_request_completed (McdAccount *account, GError *error) priv->online_requests = NULL; } -GList * -_mcd_account_get_online_requests (McdAccount *account) -{ - g_return_val_if_fail (MCD_IS_ACCOUNT (account), NULL); - - return account->priv->online_requests; -} - static inline void process_online_requests (McdAccount *account, TpConnectionStatus status, @@ -3968,11 +3989,7 @@ check_validity_check_parameters_cb (McdAccount *account, /* Newly valid - try setting requested presence again. * This counts as user-initiated, because the user caused the * account to become valid somehow. */ - mcd_account_request_presence_int (account, - priv->req_presence_type, - priv->req_presence_status, - priv->req_presence_message, - TRUE); + mcd_account_rerequest_presence (account, TRUE); } } @@ -4002,8 +4019,12 @@ mcd_account_check_validity (McdAccount *account, /* * _mcd_account_connect_with_auto_presence: * @account: the #McdAccount. + * @user_initiated: %TRUE if the connection attempt is in response to a user + * request (like a request for a channel) + * + * Request the account to go back online with the current RequestedPresence, if + * it is not Offline, or with the configured AutomaticPresence otherwise. * - * Request the account to go online with the configured AutomaticPresence. * This is appropriate in these situations: * - going online automatically because we've gained connectivity * - going online automatically in order to request a channel @@ -4014,11 +4035,14 @@ _mcd_account_connect_with_auto_presence (McdAccount *account, { McdAccountPrivate *priv = account->priv; - mcd_account_request_presence_int (account, - priv->auto_presence_type, - priv->auto_presence_status, - priv->auto_presence_message, - user_initiated); + if (_presence_type_is_online (priv->req_presence_type)) + mcd_account_rerequest_presence (account, user_initiated); + else + mcd_account_request_presence_int (account, + priv->auto_presence_type, + priv->auto_presence_status, + priv->auto_presence_message, + user_initiated); } /* diff --git a/src/mcd-account.h b/src/mcd-account.h index 49b019c4..2480440e 100644 --- a/src/mcd-account.h +++ b/src/mcd-account.h @@ -119,6 +119,7 @@ void mcd_account_get_requested_presence (McdAccount *account, const gchar **message); gboolean mcd_account_get_connect_automatically (McdAccount *account); +gboolean mcd_account_would_like_to_connect (McdAccount *account); void mcd_account_get_automatic_presence (McdAccount *account, TpConnectionPresenceType *presence, const gchar **status, diff --git a/src/mcd-connection.c b/src/mcd-connection.c index 7ddc9d4c..4b276333 100644 --- a/src/mcd-connection.c +++ b/src/mcd-connection.c @@ -409,7 +409,7 @@ presence_get_statuses_cb (TpProxy *proxy, const GValue *v_statuses, } /* Now the presence info is ready. We can set the presence */ - _mcd_account_get_requested_presence (priv->account, &presence, + mcd_account_get_requested_presence (priv->account, &presence, &status, &message); if (priv->connected) { diff --git a/src/mcd-master.c b/src/mcd-master.c index 29e9fbe8..5ea3384e 100644 --- a/src/mcd-master.c +++ b/src/mcd-master.c @@ -65,6 +65,7 @@ #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/telepathy-glib.h> +#include "kludge-transport.h" #include "mcd-master.h" #include "mcd-master-priv.h" #include "mcd-proxy.h" @@ -118,6 +119,10 @@ typedef struct { gpointer userdata; } McdAccountConnectionData; +/* Used to poison 'default_master' when the object it points to is disposed. + * The default_master should basically be alive for the duration of the MC run. + */ +#define POISONED_MASTER ((McdMaster *) 0xdeadbeef) static McdMaster *default_master = NULL; @@ -140,13 +145,7 @@ mcd_master_transport_connected (McdMaster *master, McdTransportPlugin *plugin, McdAccount *account = MCD_ACCOUNT (v); GHashTable *conditions; - /* get all enabled accounts, which have the "ConnectAutomatically" - * flag set and that are not connected */ - if (!mcd_account_is_valid (account) || - !mcd_account_is_enabled (account) || - !mcd_account_get_connect_automatically (account) || - mcd_account_get_connection_status (account) == - TP_CONNECTION_STATUS_CONNECTED) + if (!mcd_account_would_like_to_connect (account)) continue; DEBUG ("account %s would like to connect", @@ -425,6 +424,11 @@ _mcd_master_dispose (GObject * object) priv->dispatcher = NULL; g_object_unref (priv->proxy); + if (default_master == (McdMaster *) object) + { + default_master = POISONED_MASTER; + } + G_OBJECT_CLASS (mcd_master_parent_class)->dispose (object); } @@ -468,6 +472,8 @@ mcd_master_constructor (GType type, guint n_params, mcd_master_load_mcd_plugins (master); #endif + mcd_kludge_transport_install ((McdPlugin *) master); + /* we assume that at this point all transport plugins have been registered. * We get the active transports and check whether some accounts should be * automatically connected */ @@ -549,6 +555,9 @@ mcd_master_get_default (void) { if (!default_master) default_master = MCD_MASTER (g_object_new (MCD_TYPE_MASTER, NULL)); + + g_return_val_if_fail (default_master != POISONED_MASTER, NULL); + return default_master; } diff --git a/src/mcd-transport.c b/src/mcd-transport.c index e52bd401..433fce17 100644 --- a/src/mcd-transport.c +++ b/src/mcd-transport.c @@ -115,6 +115,7 @@ mcd_transport_plugin_get_name (McdTransportPlugin *plugin) McdTransportPluginIface *iface; iface = MCD_TRANSPORT_PLUGIN_GET_IFACE (plugin); + g_return_val_if_fail (iface->get_name != NULL, NULL); return iface->get_name (plugin); } @@ -133,6 +134,7 @@ mcd_transport_plugin_get_transports (McdTransportPlugin *plugin) McdTransportPluginIface *iface; iface = MCD_TRANSPORT_PLUGIN_GET_IFACE (plugin); + g_return_val_if_fail (iface->get_transports != NULL, NULL); return iface->get_transports (plugin); } @@ -155,7 +157,11 @@ mcd_transport_plugin_check_conditions (McdTransportPlugin *plugin, McdTransportPluginIface *iface; iface = MCD_TRANSPORT_PLUGIN_GET_IFACE (plugin); - return iface->check_conditions (plugin, transport, conditions); + + if (iface->check_conditions == NULL) + return TRUE; + else + return iface->check_conditions (plugin, transport, conditions); } /** @@ -173,6 +179,7 @@ mcd_transport_get_name (McdTransportPlugin *plugin, McdTransport *transport) McdTransportPluginIface *iface; iface = MCD_TRANSPORT_PLUGIN_GET_IFACE (plugin); + g_return_val_if_fail (iface->get_transport_name != NULL, NULL); return iface->get_transport_name (plugin, transport); } @@ -191,6 +198,8 @@ mcd_transport_get_status (McdTransportPlugin *plugin, McdTransport *transport) McdTransportPluginIface *iface; iface = MCD_TRANSPORT_PLUGIN_GET_IFACE (plugin); + g_return_val_if_fail (iface->get_transport_status != NULL, + MCD_TRANSPORT_STATUS_DISCONNECTED); return iface->get_transport_status (plugin, transport); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 7f6c3996..65e5ebc4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,7 +17,7 @@ endif SUBDIRS = . twisted TEST_EXECUTABLES = test-value-is-same -NON_TEST_EXECUTABLES = account-store +NON_TEST_EXECUTABLES = account-store tease-the-minotaur if ENABLE_GNOME_KEYRING NON_TEST_EXECUTABLES += keyring-command @@ -32,6 +32,9 @@ TESTS = $(TEST_EXECUTABLES) test_value_is_same_SOURCES = value-is-same.c test_value_is_same_LDADD = $(top_builddir)/src/libmcd-convenience.la +tease_the_minotaur_SOURCES = tease-the-minotaur.c +tease_the_minotaur_LDADD = $(top_builddir)/src/libmcd-convenience.la + account_store_LDADD = $(GLIB_LIBS) account_store_SOURCES = \ account-store.c \ diff --git a/tests/tease-the-minotaur.c b/tests/tease-the-minotaur.c new file mode 100644 index 00000000..1db9d0a2 --- /dev/null +++ b/tests/tease-the-minotaur.c @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* + * tease-the-minotaur: a simple interactive test app for McdConnectivityMonitor + * + * Copyright © 2009–2011 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Will Thompson <will.thompson@collabora.co.uk> + */ + +#include "connectivity-monitor.h" + +static void +state_change_cb ( + McdConnectivityMonitor *minotaur, + gboolean connected, + gpointer user_data) +{ + g_print (connected ? "connected\n" : "disconnected\n"); +} + +int +main ( + int argc, + char *argv[]) +{ + McdConnectivityMonitor *minotaur; + GMainLoop *im_feeling_loopy; + + g_type_init (); + + minotaur = mcd_connectivity_monitor_new (); + g_signal_connect (minotaur, "state-change", (GCallback) state_change_cb, NULL); + state_change_cb (minotaur, mcd_connectivity_monitor_is_online (minotaur), NULL); + + im_feeling_loopy = g_main_loop_new (NULL, FALSE); + g_main_loop_run (im_feeling_loopy); + return 0; +} diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 392dbe05..5832e53e 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -79,6 +79,7 @@ endif # account-storage/*.py need their own instances. TWISTED_SPECIAL_BUILD_TESTS = \ account-manager/auto-away.py \ + account-manager/connectivity.py \ account-manager/hidden.py \ account-storage/default-keyring-storage.py \ account-storage/diverted-storage.py @@ -103,9 +104,20 @@ else HAVE_MCE_PYBOOL = False endif +if HAVE_NM +HAVE_CONNECTIVITY_PYBOOL = True +else +if HAVE_CONNMAN +HAVE_CONNECTIVITY_PYBOOL = True +else +HAVE_CONNECTIVITY_PYBOOL = False +endif +endif + config.py: Makefile $(AM_V_GEN) { \ echo "HAVE_MCE = $(HAVE_MCE_PYBOOL)"; \ + echo "HAVE_CONNECTIVITY = $(HAVE_CONNECTIVITY_PYBOOL)"; \ } > $@ BUILT_SOURCES = config.py @@ -184,6 +196,7 @@ endif WITH_SESSION_BUS = \ sh $(srcdir)/tools/with-session-bus.sh \ + --also-for-system \ --config-file=tools/tmp-session-bus.conf -- COMBINED_TESTS_ENVIRONMENT = \ @@ -283,6 +296,7 @@ EXTRA_DIST = \ constants.py \ fakeclient.py \ fakecm.py \ + fakeconnectivity.py \ mctest.py \ servicetest.py diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index 9f5803a1..38227f5f 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -27,8 +27,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async, assertEquals -from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ - make_mc +from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs cm_name_ref = dbus.service.BusName( @@ -79,9 +78,9 @@ def test(q, bus, unused): 'password': r'\\ionstorm\\', } - mc = make_mc(bus) + mc = MC(q, bus) - request_conn, prop_changed, _ = q.expect_many( + request_conn, prop_changed = q.expect_many( EventPattern('dbus-method-call', method='RequestConnection', args=['fakeprotocol', expected_params], destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', @@ -90,8 +89,6 @@ def test(q, bus, unused): handled=False), EventPattern('dbus-signal', signal='AccountPropertyChanged', predicate=(lambda e: 'ConnectionStatus' in e.args[0])), - EventPattern('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.AM and e.args[2]), ) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', diff --git a/tests/twisted/account-manager/avatar-persist.py b/tests/twisted/account-manager/avatar-persist.py index b4cd6570..06ad65d8 100644 --- a/tests/twisted/account-manager/avatar-persist.py +++ b/tests/twisted/account-manager/avatar-persist.py @@ -28,8 +28,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async -from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ - make_mc +from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs cm_name_ref = dbus.service.BusName( @@ -77,18 +76,14 @@ def test(q, bus, unused): 'password': 'ionstorm', } - mc = make_mc(bus) + mc = MC(q, bus) - e, _ = q.expect_many( - EventPattern('dbus-method-call', method='RequestConnection', + e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', expected_params], destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', path=cs.tp_path_prefix + '/ConnectionManager/fakecm', interface=cs.tp_name_prefix + '.ConnectionManager', - handled=False), - EventPattern('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.AM and e.args[2]), - ) + handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', 'myself', has_avatars=True, avatars_persist=True) diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index 87895042..a93912eb 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -28,8 +28,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async -from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ - make_mc +from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs cm_name_ref = dbus.service.BusName( @@ -77,18 +76,14 @@ def test(q, bus, unused): 'password': 'ionstorm', } - mc = make_mc(bus) + mc = MC(q, bus) - e, _ = q.expect_many( - EventPattern('dbus-method-call', method='RequestConnection', + e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', expected_params], destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', path=cs.tp_path_prefix + '/ConnectionManager/fakecm', interface=cs.tp_name_prefix + '.ConnectionManager', - handled=False), - EventPattern('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.AM and e.args[2]), - ) + handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', 'myself', has_avatars=True, avatars_persist=False) diff --git a/tests/twisted/account-manager/connectivity.py b/tests/twisted/account-manager/connectivity.py new file mode 100644 index 00000000..0f1c3407 --- /dev/null +++ b/tests/twisted/account-manager/connectivity.py @@ -0,0 +1,138 @@ +# vim: set fileencoding=utf-8 : +# Copyright © 2011 Collabora Ltd. +# +# 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.1 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus.service + +from servicetest import ( + EventPattern, call_async, sync_dbus, assertEquals, +) +from mctest import ( + exec_test, create_fakecm_account, expect_fakecm_connection, + SimulatedConnection, +) +import constants as cs + +import config + +if not config.HAVE_CONNECTIVITY: + print "NOTE: built without ConnMan or NM support" + raise SystemExit(77) + +def sync_connectivity_state(mc): + # We cannot simply use sync_dbus here, because nm-glib reports property + # changes in an idle (presumably to batch them all together). This is fine + # and all that, but means we have to find a way to make sure MC has flushed + # its idle queue to avoid this test being racy. (This isn't just + # theoretical: this test failed about once per five runs when it used sync_dbus.) + # + # The test-specific version of MC implements the 'BillyIdle' method, which + # returns from a low-priority idle. + mc.BillyIdle(dbus_interface='org.freedesktop.Telepathy.MissionControl5.RegressionTests') + +def test(q, bus, mc): + params = dbus.Dictionary( + {"account": "yum yum network manager", + "password": "boo boo connman (although your API *is* simpler)", + }, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + # While we're not connected to the internet, RequestConnection should not + # be called. + request_connection_event = [ + EventPattern('dbus-method-call', method='RequestConnection'), + ] + q.forbid_events(request_connection_event) + + account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'hlaghalgh')) + + # Turn the account on, re-request an online presence, and even tell it to + # connect automatically, to check that none of these make it sign in. + call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'Enabled', True) + q.expect('dbus-return', method='Set') + requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'gtfo') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', + requested_presence) + q.expect('dbus-return', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'ConnectAutomatically', + True) + q.expect('dbus-return', method='Set') + # (but actually let's turn ConnectAutomatically off: we want to check that + # MC continues to try to apply RequestedPresence if the network connection + # goes away and comes back again, regardless of this setting) + call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'ConnectAutomatically', + False) + q.expect('dbus-return', method='Set') + + sync_dbus(bus, q, mc) + q.unforbid_events(request_connection_event) + + # Okay, I'm satisfied. Turn the network on. + mc.connectivity.go_online() + + expect_fakecm_connection(q, bus, mc, account, params, has_presence=True, + expect_before_connect=[ + EventPattern('dbus-method-call', method='SetPresence', + args=list(requested_presence[1:])), + ]) + + # If we turn the network off, the connection should be banished. + mc.connectivity.go_offline() + q.expect('dbus-method-call', method='Disconnect') + + # When we turn the network back on, MC should try to sign us back on. + mc.connectivity.go_online() + e = q.expect('dbus-method-call', method='RequestConnection') + + # In the process, our RequestedPresence should not have been trampled on. + # (Historically, MC would replace it with the AutomaticPresence, but that + # behaviour was unexpected: if the user explicitly set a status or message, + # why should the network connection cutting out and coming back up cause + # that to be lost?) + assertEquals(requested_presence, + account.Properties.Get(cs.ACCOUNT, 'RequestedPresence')) + + # But if we get disconnected before RequestConnection returns, MC should + # clean up the new connection when it does, rather than trying to sign it + # in. + connect_event = [ EventPattern('dbus-method-call', method='Connect'), ] + q.forbid_events(connect_event) + + mc.connectivity.go_offline() + # Make sure that MC has noticed that the network connection has gone away. + sync_connectivity_state(mc) + + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', + account.object_path.split('/')[-1], 'myself') + q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') + + q.expect('dbus-method-call', method='Disconnect') + + # So now the user gives up and sets their RequestedPresence to offline. + # Because this account does not ConnectAutomatically, if the network + # connection comes back up the account should not be brought back online. + q.forbid_events(request_connection_event) + account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + mc.connectivity.go_online() + # Make sure MC has noticed that the network connection has come back. + sync_connectivity_state(mc) + +if __name__ == '__main__': + exec_test(test, initially_online=False) diff --git a/tests/twisted/account-manager/create-with-properties.py b/tests/twisted/account-manager/create-with-properties.py index 235853e9..d5e9685e 100644 --- a/tests/twisted/account-manager/create-with-properties.py +++ b/tests/twisted/account-manager/create-with-properties.py @@ -22,22 +22,15 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async -from mctest import exec_test, create_fakecm_account, get_account_manager +from mctest import exec_test, create_fakecm_account, AccountManager import constants as cs def test(q, bus, mc): - # Get the AccountManager interface - account_manager = get_account_manager(bus) - account_manager_iface = dbus.Interface(account_manager, cs.AM) - - # Introspect AccountManager for debugging purpose - account_manager_introspected = account_manager.Introspect( - dbus_interface=cs.INTROSPECTABLE_IFACE) - #print account_manager_introspected + account_manager = AccountManager(bus) # Check AccountManager has D-Bus property interface - properties = account_manager.GetAll(cs.AM, - dbus_interface=cs.PROPERTIES_IFACE) + call_async(q, account_manager.Properties, 'GetAll', cs.AM) + properties, = q.expect('dbus-return', method='GetAll').value assert properties is not None assert properties.get('ValidAccounts') == [], \ properties.get('ValidAccounts') @@ -65,8 +58,6 @@ def test(q, bus, mc): cm_name_ref = dbus.service.BusName(cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - account_manager = bus.get_object(cs.AM, cs.AM_PATH) - am_iface = dbus.Interface(account_manager, cs.AM) creation_properties = dbus.Dictionary({ cs.ACCOUNT + '.Enabled': True, @@ -87,7 +78,7 @@ def test(q, bus, mc): dbus.Dictionary({ 'has-quad-damage': ':y' }, signature='ss'), }, signature='sv') - call_async(q, am_iface, 'CreateAccount', + call_async(q, account_manager, 'CreateAccount', 'fakecm', 'fakeprotocol', 'fakeaccount', @@ -149,7 +140,7 @@ def test(q, bus, mc): creation_properties2 = creation_properties.copy() creation_properties2[cs.ACCOUNT + '.NonExistent'] = 'foo' - call_async(q, am_iface, 'CreateAccount', + call_async(q, account_manager, 'CreateAccount', 'fakecm', 'fakeprotocol', 'fakeaccount', @@ -159,7 +150,7 @@ def test(q, bus, mc): params2 = params.copy() params2['fake_param'] = 'foo' - call_async(q, am_iface, 'CreateAccount', + call_async(q, account_manager, 'CreateAccount', 'fakecm', 'fakeprotocol', 'fakeaccount', diff --git a/tests/twisted/account-manager/make-valid.py b/tests/twisted/account-manager/make-valid.py index 9853b293..ee3ee98f 100644 --- a/tests/twisted/account-manager/make-valid.py +++ b/tests/twisted/account-manager/make-valid.py @@ -27,8 +27,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async, sync_dbus -from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ - make_mc +from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs cm_name_ref = dbus.service.BusName( @@ -94,14 +93,7 @@ def test(q, bus, unused): q.forbid_events(events) # Wait for MC to load - mc = make_mc(bus) - - q.expect_many( - EventPattern('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.AM), - EventPattern('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.CD), - ) + mc = MC(q, bus) # Trying to make a channel on account 1 doesn't work, because it's # not valid diff --git a/tests/twisted/crash-recovery/crash-recovery.py b/tests/twisted/crash-recovery/crash-recovery.py index 42963772..e189b850 100644 --- a/tests/twisted/crash-recovery/crash-recovery.py +++ b/tests/twisted/crash-recovery/crash-recovery.py @@ -28,7 +28,7 @@ import dbus.service from servicetest import EventPattern, call_async from mctest import exec_test, SimulatedConnection, SimulatedClient, \ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ - expect_client_setup, make_mc + expect_client_setup, MC import constants as cs def preseed(): @@ -95,20 +95,8 @@ def test(q, bus, unused): # Service-activate MC. # We're told about the other channel as an observer... - mc = make_mc(bus) - _, _, _, e = q.expect_many( - EventPattern('dbus-signal', - path='/org/freedesktop/DBus', - interface='org.freedesktop.DBus', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.AM and e.args[2]), - EventPattern('dbus-signal', - path='/org/freedesktop/DBus', - interface='org.freedesktop.DBus', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.CD and e.args[2]), - EventPattern('dbus-signal', - path='/org/freedesktop/DBus', - interface='org.freedesktop.DBus', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == cs.MC and e.args[2]), + mc = MC(q, bus, wait_for_names=False) + e, = mc.wait_for_names( EventPattern('dbus-method-call', path=client.object_path, interface=cs.OBSERVER, method='ObserveChannels', diff --git a/tests/twisted/dispatcher/create-at-startup.py b/tests/twisted/dispatcher/create-at-startup.py index 6cacfc23..c4e24765 100644 --- a/tests/twisted/dispatcher/create-at-startup.py +++ b/tests/twisted/dispatcher/create-at-startup.py @@ -29,7 +29,7 @@ from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async from mctest import exec_test, SimulatedConnection, SimulatedClient, \ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ - expect_client_setup, make_mc + expect_client_setup, MC import constants as cs def preseed(): @@ -72,9 +72,16 @@ def test(q, bus, unused): cm_name_ref = dbus.service.BusName( cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - # service-activate MC and immediately make a request - mc = make_mc(bus) + # service-activate MC; it will try to introspect the running client. + mc = MC(q, bus, wait_for_names=False) + get_interfaces, = mc.wait_for_names( + EventPattern('dbus-method-call', path=client.object_path, + interface=cs.PROPERTIES_IFACE, method='Get', + args=[cs.CLIENT, 'Interfaces'], + handled=False)) + # The client doesn't reply just yet; meanwhile, immediately make a channel + # request account = bus.get_object(cs.MC, cs.tp_path_prefix + '/Account/fakecm/fakeprotocol/jc_2edenton_40unatco_2eint') @@ -90,12 +97,6 @@ def test(q, bus, unused): account.object_path, request, user_action_time, client.bus_name, dbus_interface=cs.CD) - get_interfaces = q.expect('dbus-method-call', path=client.object_path, - interface=cs.PROPERTIES_IFACE, method='Get', - args=[cs.CLIENT, 'Interfaces'], - handled=False) - # don't reply just yet - ret = q.expect('dbus-return', method='CreateChannel') request_path = ret.value[0] diff --git a/tests/twisted/fakeconnectivity.py b/tests/twisted/fakeconnectivity.py new file mode 100644 index 00000000..c6641ca6 --- /dev/null +++ b/tests/twisted/fakeconnectivity.py @@ -0,0 +1,103 @@ +import dbus +from dbus.service import Object, method, signal + +import sys + +class FakeConnectivity(object): + NM_BUS_NAME = 'org.freedesktop.NetworkManager' + NM_PATH = '/org/freedesktop/NetworkManager' + NM_INTERFACE = NM_BUS_NAME + + NM_STATE_UNKNOWN = 0 + NM_STATE_ASLEEP = 10 + NM_STATE_DISCONNECTED = 20 + NM_STATE_DISCONNECTING = 30 + NM_STATE_CONNECTING = 40 + NM_STATE_CONNECTED_LOCAL = 50 + NM_STATE_CONNECTED_SITE = 60 + NM_STATE_CONNECTED_GLOBAL = 70 + + CONNMAN_BUS_NAME = 'net.connman' + CONNMAN_PATH = '/' + CONNMAN_INTERFACE = 'net.connman.Manager' + + CONNMAN_OFFLINE = "offline" + CONNMAN_ONLINE = "online" + + def __init__(self, q, bus, initially_online): + self.q = q + self.bus = bus + + self.nm_name_ref = dbus.service.BusName(self.NM_BUS_NAME, bus) + self.connman_name_ref = dbus.service.BusName(self.CONNMAN_BUS_NAME, bus) + + q.add_dbus_method_impl(self.NM_GetPermissions, + path=self.NM_PATH, interface=self.NM_INTERFACE, + method='GetPermissions') + q.add_dbus_method_impl(self.NM_Get, + path=self.NM_PATH, interface=dbus.PROPERTIES_IFACE, method='Get', + predicate=lambda e: e.args[0] == self.NM_INTERFACE) + + q.add_dbus_method_impl(self.ConnMan_GetState, + path=self.CONNMAN_PATH, interface=self.CONNMAN_INTERFACE, + method='GetState') + + self.change_state(initially_online) + + def NM_GetPermissions(self, e): + permissions = { + self.NM_INTERFACE + '.network-control': 'yes', + self.NM_INTERFACE + '.enable-disable-wwan': 'yes', + self.NM_INTERFACE + '.settings.modify.own': 'yes', + self.NM_INTERFACE + '.wifi.share.protected': 'yes', + self.NM_INTERFACE + '.wifi.share.open': 'yes', + self.NM_INTERFACE + '.enable-disable-network': 'yes', + self.NM_INTERFACE + '.enable-disable-wimax': 'yes', + self.NM_INTERFACE + '.sleep-wake': 'no', + self.NM_INTERFACE + '.enable-disable-wifi': 'yes', + self.NM_INTERFACE + '.settings.modify.system': 'auth', + self.NM_INTERFACE + '.settings.modify.hostname': 'auth', + } + self.q.dbus_return(e.message, permissions, signature='a{ss}') + + def NM_Get(self, e): + props = { + 'NetworkingEnabled': True, + 'WirelessEnabled': True, + 'WirelessHardwareEnabled': True, + 'WwanEnabled': False, + 'WwanHardwareEnabled': True, + 'WimaxEnabled': True, + 'WimaxHardwareEnabled': True, + 'ActiveConnections': dbus.Array([], signature='o'), + 'Version': '0.9.0', + 'State': dbus.UInt32(self.nm_state), + } + + self.q.dbus_return(e.message, props[e.args[1]], signature='v') + + def ConnMan_GetState(self, e): + self.q.dbus_return(e.message, self.connman_state, signature='s') + + def change_state(self, online): + if online: + self.nm_state = self.NM_STATE_CONNECTED_GLOBAL + self.connman_state = self.CONNMAN_ONLINE + else: + self.nm_state = self.NM_STATE_DISCONNECTED + self.connman_state = self.CONNMAN_OFFLINE + + self.q.dbus_emit(self.NM_PATH, self.NM_INTERFACE, + 'PropertiesChanged', { "State": dbus.UInt32(self.nm_state) }, + signature='a{sv}') + self.q.dbus_emit(self.NM_PATH, self.NM_INTERFACE, + 'StateChanged', self.nm_state, + signature='u') + self.q.dbus_emit(self.CONNMAN_PATH, self.CONNMAN_INTERFACE, + 'StateChanged', self.connman_state, signature='s') + + def go_online(self): + self.change_state(True) + + def go_offline(self): + self.change_state(False) diff --git a/tests/twisted/mc-debug-server.c b/tests/twisted/mc-debug-server.c index 3bcbc4d0..c1a485fe 100644 --- a/tests/twisted/mc-debug-server.c +++ b/tests/twisted/mc-debug-server.c @@ -42,6 +42,7 @@ #include "mcd-service.h" +TpDBusDaemon *bus_daemon = NULL; static McdService *mcd = NULL; static gboolean @@ -68,6 +69,19 @@ delayed_abort (gpointer data G_GNUC_UNUSED) return FALSE; } +static gboolean +billy_idle (gpointer user_data) +{ + DBusMessage *reply = user_data; + DBusConnection *connection = dbus_g_connection_get_connection ( + ((TpProxy *) bus_daemon)->dbus_connection); + + if (!dbus_connection_send (connection, reply, NULL)) + g_error ("Out of memory"); + + return FALSE; +} + #define MCD_SYSTEM_MEMORY_CONSERVED (1 << 1) #define MCD_SYSTEM_IDLE (1 << 5) @@ -156,6 +170,24 @@ dbus_filter_function (DBusConnection *connection, return DBUS_HANDLER_RESULT_HANDLED; } + else if (dbus_message_is_method_call (message, + "org.freedesktop.Telepathy.MissionControl5.RegressionTests", + "BillyIdle")) + { + /* Used to drive a souped-up version of sync_dbus(), where we need to + * ensure that all idles have fired, on top of the D-Bus queue being + * drained. + */ + DBusMessage *reply = dbus_message_new_method_return (message); + + if (reply == NULL) + g_error ("Out of memory"); + + g_idle_add_full (G_PRIORITY_LOW, billy_idle, reply, + (GDestroyNotify) dbus_message_unref); + + return DBUS_HANDLER_RESULT_HANDLED; + } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -163,7 +195,6 @@ dbus_filter_function (DBusConnection *connection, int main (int argc, char **argv) { - TpDBusDaemon *bus_daemon = NULL; GError *error = NULL; DBusConnection *connection = NULL; int ret = 1; diff --git a/tests/twisted/mctest.py b/tests/twisted/mctest.py index c9639479..cd1a8530 100644 --- a/tests/twisted/mctest.py +++ b/tests/twisted/mctest.py @@ -33,14 +33,7 @@ from twisted.internet import reactor import dbus import dbus.service -def make_mc(bus): - mc = bus.get_object( - cs.tp_name_prefix + '.MissionControl5', - cs.tp_path_prefix + '/MissionControl5', - follow_name_owner_changes=True) - assert mc is not None - - return mc +from fakeconnectivity import FakeConnectivity def install_colourer(): def red(s): @@ -66,19 +59,52 @@ def install_colourer(): sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout -def wait_for_name(queue, bus, name): - if not bus.name_has_owner(name): - queue.expect('dbus-signal', signal='NameOwnerChanged', - predicate=lambda e: e.args[0] == name and e.args[2]) +class MC(dbus.proxies.ProxyObject): + def __init__(self, queue, bus, wait_for_names=True, initially_online=True): + """ + Arguments: + + queue: an event queue + bus: a D-Bus connection + wait_for_names: if True, the constructor will wait for MC to have + been service-activated before returning. if False, + the caller may later call wait_for_names(). + initially_online: whether the fake implementations of Network Manager + and ConnMan should claim to be online or offline. + """ + dbus.proxies.ProxyObject.__init__(self, + conn=bus, + bus_name=cs.MC, + object_path=cs.MC_PATH, + follow_name_owner_changes=True) + + self.connectivity = FakeConnectivity(queue, bus, initially_online) + self.q = queue + self.bus = bus + + if wait_for_names: + self.wait_for_names() + + def wait_for_names(self, *also_expect): + """ + Waits for MC to have claimed all its bus names, along with the + (optional) EventPatterns passed as arguments. + """ + + patterns = [ + servicetest.EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e, name=name: e.args[0] == name and e.args[2] != '') + for name in [cs.AM, cs.CD, cs.MC] + if not self.bus.name_has_owner(name)] -def wait_for_mc(queue, bus): - mc = make_mc(bus) - wait_for_name(queue, bus, cs.AM) - wait_for_name(queue, bus, cs.CD) - return mc + patterns.extend(also_expect) + + events = self.q.expect_many(*patterns) + + return events[3:] def exec_test_deferred (fun, params, protocol=None, timeout=None, - preload_mc=True): + preload_mc=True, initially_online=True): colourer = None if sys.stdout.isatty(): @@ -95,7 +121,7 @@ def exec_test_deferred (fun, params, protocol=None, timeout=None, if preload_mc: try: - mc = wait_for_mc(queue, bus) + mc = MC(queue, bus, initially_online=initially_online) except Exception, e: import traceback traceback.print_exc() @@ -158,9 +184,10 @@ def exec_test_deferred (fun, params, protocol=None, timeout=None, if colourer: sys.stdout = colourer.fh -def exec_test(fun, params=None, protocol=None, timeout=None, preload_mc=True): +def exec_test(fun, params=None, protocol=None, timeout=None, + preload_mc=True, initially_online=True): reactor.callWhenRunning (exec_test_deferred, fun, params, protocol, timeout, - preload_mc) + preload_mc, initially_online) reactor.run() class SimulatedConnection(object): @@ -893,13 +920,15 @@ def get_fakecm_account(bus, mc, account_path): return account -def enable_fakecm_account(q, bus, mc, account, expected_params, - has_requests=True, has_presence=False, has_aliasing=False, - has_avatars=False, avatars_persist=True, - extra_interfaces=[], - requested_presence=(2, 'available', ''), - expect_before_connect=[], expect_after_connect=[], - has_hidden=False): +def enable_fakecm_account(q, bus, mc, account, expected_params, **kwargs): + # I'm too lazy to manually pass all the other kwargs to + # expect_fakecm_connection + try: + requested_presence = kwargs['requested_presence'] + del kwargs['requested_presence'] + except KeyError: + requested_presence = (2, 'available', '') + # Enable the account account.Properties.Set(cs.ACCOUNT, 'Enabled', True) @@ -911,6 +940,14 @@ def enable_fakecm_account(q, bus, mc, account, expected_params, account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence) + return expect_fakecm_connection(q, bus, mc, account, expected_params, **kwargs) + +def expect_fakecm_connection(q, bus, mc, account, expected_params, + has_requests=True, has_presence=False, has_aliasing=False, + has_avatars=False, avatars_persist=True, + extra_interfaces=[], + expect_before_connect=[], expect_after_connect=[], + has_hidden=False): e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', expected_params], destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', @@ -1084,13 +1121,14 @@ def tell_mc_to_die(q, bus): def resuscitate_mc(q, bus, mc): """Having killed MC with tell_mc_to_die(), this function revives it.""" - bus.get_object(cs.MC, "/") + # We kick the daemon asynchronously because nm-glib makes blocking calls + # back to us during initialization... + bus.call_async(dbus.BUS_DAEMON_NAME, dbus.BUS_DAEMON_PATH, + dbus.BUS_DAEMON_IFACE, 'StartServiceByName', 'su', (cs.MC, 0), + reply_handler=None, error_handler=None) # Wait until it's up - q.expect('dbus-signal', signal='NameOwnerChanged', - predicate=(lambda e: - e.args[0] == 'org.freedesktop.Telepathy.AccountManager' and - e.args[2] != '')) + mc.wait_for_names() return connect_to_mc(q, bus, mc) diff --git a/tests/twisted/tools/exec-with-log.sh.in b/tests/twisted/tools/exec-with-log.sh.in index 0b40a24b..84b13ea2 100644 --- a/tests/twisted/tools/exec-with-log.sh.in +++ b/tests/twisted/tools/exec-with-log.sh.in @@ -50,6 +50,9 @@ elif test -n "$MISSIONCONTROL_TEST_REFDBG"; then fi fi +# The bus-daemon that is activating us doesn't know it's also the system bus +export DBUS_SYSTEM_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" + if test "z$MC_EXECUTABLE" = z; then MC_EXECUTABLE=@abs_top_builddir@/tests/twisted/mc-debug-server fi |