summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will.thompson@collabora.co.uk>2011-09-28 11:33:09 +0100
committerWill Thompson <will.thompson@collabora.co.uk>2011-09-28 11:33:48 +0100
commit25799ee8f5b02a8893d7bc5c8007e58b9d444bdd (patch)
tree16a4d53542b46f4229170bafa04426923a1190ec
parent66b28358c295e994c5a3711b6d1e04edb53d423f (diff)
parent38d76aa0c45d66828c1935b0b7573e7739af45b1 (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>
-rw-r--r--configure.ac55
-rw-r--r--src/Makefile.am10
-rw-r--r--src/connectivity-monitor.c438
-rw-r--r--src/connectivity-monitor.h74
-rw-r--r--src/kludge-transport.c263
-rw-r--r--src/kludge-transport.h62
-rw-r--r--src/mcd-account-priv.h16
-rw-r--r--src/mcd-account.c150
-rw-r--r--src/mcd-account.h1
-rw-r--r--src/mcd-connection.c2
-rw-r--r--src/mcd-master.c23
-rw-r--r--src/mcd-transport.c11
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/tease-the-minotaur.c53
-rw-r--r--tests/twisted/Makefile.am14
-rw-r--r--tests/twisted/account-manager/auto-connect.py9
-rw-r--r--tests/twisted/account-manager/avatar-persist.py13
-rw-r--r--tests/twisted/account-manager/avatar-refresh.py13
-rw-r--r--tests/twisted/account-manager/connectivity.py138
-rw-r--r--tests/twisted/account-manager/create-with-properties.py23
-rw-r--r--tests/twisted/account-manager/make-valid.py12
-rw-r--r--tests/twisted/crash-recovery/crash-recovery.py18
-rw-r--r--tests/twisted/dispatcher/create-at-startup.py19
-rw-r--r--tests/twisted/fakeconnectivity.py103
-rw-r--r--tests/twisted/mc-debug-server.c33
-rw-r--r--tests/twisted/mctest.py104
-rw-r--r--tests/twisted/tools/exec-with-log.sh.in3
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