summary refs log tree commit diff
path: root/libnm/nm-remote-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm/nm-remote-connection.c')
-rw-r--r--libnm/nm-remote-connection.c857
1 files changed, 857 insertions, 0 deletions
diff --git a/libnm/nm-remote-connection.c b/libnm/nm-remote-connection.c
new file mode 100644
index 00000000..3afe7f8e
--- /dev/null
+++ b/libnm/nm-remote-connection.c
@@ -0,0 +1,857 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-dbus-interface.h>
+#include <nm-utils.h>
+#include <nm-setting-connection.h>
+#include "nm-remote-connection.h"
+#include "nm-remote-connection-private.h"
+#include "nm-object-private.h"
+#include "nm-glib-compat.h"
+#include "nm-dbus-helpers.h"
+
+#include "nmdbus-settings-connection.h"
+
+static void nm_remote_connection_connection_iface_init (NMConnectionInterface *iface);
+static void nm_remote_connection_initable_iface_init (GInitableIface *iface);
+static void nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface);
+static GInitableIface *nm_remote_connection_parent_initable_iface;
+static GAsyncInitableIface *nm_remote_connection_parent_async_initable_iface;
+
+G_DEFINE_TYPE_WITH_CODE (NMRemoteConnection, nm_remote_connection, NM_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_remote_connection_connection_iface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_connection_initable_iface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_connection_async_initable_iface_init);
+                         )
+
+enum {
+	PROP_0,
+	PROP_UNSAVED,
+	PROP_VISIBLE,
+
+	LAST_PROP
+};
+
+typedef struct {
+	NMDBusSettingsConnection *proxy;
+
+	gboolean unsaved;
+
+	gboolean visible;
+} NMRemoteConnectionPrivate;
+
+#define NM_REMOTE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionPrivate))
+
+/****************************************************************/
+
+/**
+ * nm_remote_connection_commit_changes:
+ * @connection: the #NMRemoteConnection
+ * @save_to_disk: whether to persist the changes to disk
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: location for a #GError, or %NULL
+ *
+ * Send any local changes to the settings and properties of @connection to
+ * NetworkManager. If @save_to_disk is %TRUE, the updated connection will be saved to
+ * disk; if %FALSE, then only the in-memory representation will be changed.
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_commit_changes (NMRemoteConnection *connection,
+                                     gboolean save_to_disk,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+	NMRemoteConnectionPrivate *priv;
+	GVariant *settings;
+	gboolean ret;
+
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	settings = nm_connection_to_dbus (NM_CONNECTION (connection), NM_CONNECTION_SERIALIZE_ALL);
+	if (save_to_disk) {
+		ret = nmdbus_settings_connection_call_update_sync (priv->proxy,
+		                                                   settings,
+		                                                   cancellable, error);
+	} else {
+		ret = nmdbus_settings_connection_call_update_unsaved_sync (priv->proxy,
+		                                                           settings,
+		                                                           cancellable, error);
+	}
+	if (error && *error)
+		g_dbus_error_strip_remote_error (*error);
+	return ret;
+}
+
+static void
+update_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	gboolean (*finish_func) (NMDBusSettingsConnection *, GAsyncResult *, GError **);
+	GError *error = NULL;
+
+	finish_func = g_object_get_data (G_OBJECT (simple), "finish_func");
+	if (finish_func (NMDBUS_SETTINGS_CONNECTION (proxy), result, &error))
+		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+/**
+ * nm_remote_connection_commit_changes_async:
+ * @connection: the #NMRemoteConnection
+ * @save_to_disk: whether to save the changes to persistent storage
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to be called when the commit operation completes
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Asynchronously sends any local changes to the settings and properties of
+ * @connection to NetworkManager. If @save is %TRUE, the updated connection will
+ * be saved to disk; if %FALSE, then only the in-memory representation will be
+ * changed.
+ **/
+void
+nm_remote_connection_commit_changes_async (NMRemoteConnection *connection,
+                                           gboolean save_to_disk,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+	NMRemoteConnectionPrivate *priv;
+	GSimpleAsyncResult *simple;
+	GVariant *settings;
+
+	g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
+	                                    nm_remote_connection_commit_changes_async);
+
+	settings = nm_connection_to_dbus (NM_CONNECTION (connection), NM_CONNECTION_SERIALIZE_ALL);
+	if (save_to_disk) {
+		g_object_set_data (G_OBJECT (simple), "finish_func",
+		                   nmdbus_settings_connection_call_update_finish);
+		nmdbus_settings_connection_call_update (priv->proxy,
+		                                        settings,
+		                                        cancellable,
+		                                        update_cb, simple);
+	} else {
+		g_object_set_data (G_OBJECT (simple), "finish_func",
+		                   nmdbus_settings_connection_call_update_unsaved_finish);
+		nmdbus_settings_connection_call_update_unsaved (priv->proxy,
+		                                                settings,
+		                                                cancellable,
+		                                                update_cb, simple);
+	}
+}
+
+/**
+ * nm_remote_connection_commit_changes_finish:
+ * @connection: the #NMRemoteConnection
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_remote_connection_commit_changes_async().
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_commit_changes_finish (NMRemoteConnection *connection,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_commit_changes_async), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+	else
+		return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+/**
+ * nm_remote_connection_save:
+ * @connection: the #NMRemoteConnection
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: location for a #GError, or %NULL
+ *
+ * Saves the connection to disk if the connection has changes that have not yet
+ * been written to disk, or if the connection has never been saved.
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_save (NMRemoteConnection *connection,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	NMRemoteConnectionPrivate *priv;
+	gboolean ret;
+
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	ret = nmdbus_settings_connection_call_save_sync (priv->proxy, cancellable, error);
+	if (error && *error)
+		g_dbus_error_strip_remote_error (*error);
+	return ret;
+}
+
+static void
+save_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	GError *error = NULL;
+
+	if (nmdbus_settings_connection_call_save_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
+	                                                 result, &error))
+		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+/**
+ * nm_remote_connection_save_async:
+ * @connection: the #NMRemoteConnection
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to be called when the save operation completes
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Saves the connection to disk if the connection has changes that have not yet
+ * been written to disk, or if the connection has never been saved.
+ **/
+void
+nm_remote_connection_save_async (NMRemoteConnection *connection,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	NMRemoteConnectionPrivate *priv;
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
+	                                    nm_remote_connection_save_async);
+	nmdbus_settings_connection_call_save (priv->proxy, cancellable, save_cb, simple);
+}
+
+/**
+ * nm_remote_connection_save_finish:
+ * @connection: the #NMRemoteConnection
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_remote_connection_save_async().
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_save_finish (NMRemoteConnection *connection,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_save_async), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+	else
+		return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+/**
+ * nm_remote_connection_delete:
+ * @connection: the #NMRemoteConnection
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: location for a #GError, or %NULL
+ *
+ * Deletes the connection.
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_delete (NMRemoteConnection *connection,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+	NMRemoteConnectionPrivate *priv;
+	gboolean ret;
+
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	ret = nmdbus_settings_connection_call_delete_sync (priv->proxy, cancellable, error);
+	if (error && *error)
+		g_dbus_error_strip_remote_error (*error);
+	return ret;
+}
+
+static void
+delete_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	GError *error = NULL;
+
+	if (nmdbus_settings_connection_call_delete_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
+	                                                   result, &error))
+		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+/**
+ * nm_remote_connection_delete_async:
+ * @connection: the #NMRemoteConnection
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to be called when the delete operation completes
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Asynchronously deletes the connection.
+ **/
+void
+nm_remote_connection_delete_async (NMRemoteConnection *connection,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	NMRemoteConnectionPrivate *priv;
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
+	                                    nm_remote_connection_delete_async);
+	nmdbus_settings_connection_call_delete (priv->proxy, cancellable, delete_cb, simple);
+}
+
+/**
+ * nm_remote_connection_delete_finish:
+ * @connection: the #NMRemoteConnection
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_remote_connection_delete_async().
+ *
+ * Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
+ **/
+gboolean
+nm_remote_connection_delete_finish (NMRemoteConnection *connection,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_delete_async), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+	else
+		return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+/**
+ * nm_remote_connection_get_secrets:
+ * @connection: the #NMRemoteConnection
+ * @setting_name: the #NMSetting object name to get secrets for
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: location for a #GError, or %NULL
+ *
+ * Request the connection's secrets. Note that this is a blocking D-Bus call,
+ * not a simple property accessor.
+ *
+ * Returns: a #GVariant of type %NM_VARIANT_TYPE_CONNECTION containing
+ * @connection's secrets, or %NULL on error.
+ **/
+GVariant *
+nm_remote_connection_get_secrets (NMRemoteConnection *connection,
+                                  const char *setting_name,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	NMRemoteConnectionPrivate *priv;
+	GVariant *secrets;
+
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), NULL);
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	if (nmdbus_settings_connection_call_get_secrets_sync (priv->proxy,
+	                                                      setting_name,
+	                                                      &secrets,
+	                                                      cancellable, error))
+		return secrets;
+	else {
+		if (error && *error)
+			g_dbus_error_strip_remote_error (*error);
+		return NULL;
+	}
+}
+
+static void
+get_secrets_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	GVariant *secrets = NULL;
+	GError *error = NULL;
+
+	if (nmdbus_settings_connection_call_get_secrets_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
+	                                                        &secrets, result, &error))
+		g_simple_async_result_set_op_res_gpointer (simple, secrets, (GDestroyNotify) g_variant_unref);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+/**
+ * nm_remote_connection_get_secrets_async:
+ * @connection: the #NMRemoteConnection
+ * @setting_name: the #NMSetting object name to get secrets for
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to be called when the secret request completes
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Asynchronously requests the connection's secrets.
+ **/
+void
+nm_remote_connection_get_secrets_async (NMRemoteConnection *connection,
+                                        const char *setting_name,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data)
+{
+	NMRemoteConnectionPrivate *priv;
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+	priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+	simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
+	                                    nm_remote_connection_get_secrets_async);
+
+	nmdbus_settings_connection_call_get_secrets (priv->proxy,
+	                                             setting_name,
+	                                             cancellable,
+	                                             get_secrets_cb, simple);
+}
+
+/**
+ * nm_remote_connection_get_secrets_finish:
+ * @connection: the #NMRemoteConnection
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_remote_connection_get_secrets_async().
+ *
+ * Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION
+ *   containing @connection's secrets, or %NULL on error.
+ **/
+GVariant *
+nm_remote_connection_get_secrets_finish (NMRemoteConnection *connection,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_get_secrets_async), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+	else
+		return g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+/**
+ * nm_remote_connection_get_unsaved:
+ * @connection: the #NMRemoteConnection
+ *
+ * Returns: %TRUE if the remote connection contains changes that have not
+ * been saved to disk, %FALSE if the connection is the same as its on-disk
+ * representation.
+ **/
+gboolean
+nm_remote_connection_get_unsaved (NMRemoteConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+	return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->unsaved;
+}
+
+/**
+ * nm_remote_connection_get_visible:
+ * @connection: the #NMRemoteConnection
+ *
+ * Checks if the connection is visible to the current user.  If the
+ * connection is not visible then it is essentially useless; it will
+ * not contain any settings, and operations such as
+ * nm_remote_connection_save() and nm_remote_connection_delete() will
+ * always fail. (#NMRemoteSettings will not normally return
+ * non-visible connections to callers, but it is possible for a
+ * connection's visibility to change after you already have a
+ * reference to it.)
+ *
+ * Returns: %TRUE if the remote connection is visible to the current
+ * user, %FALSE if not.
+ **/
+gboolean
+nm_remote_connection_get_visible (NMRemoteConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+	return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->visible;
+}
+
+/****************************************************************/
+
+static void
+replace_settings (NMRemoteConnection *self, GVariant *new_settings)
+{
+	GError *error = NULL;
+
+	if (!nm_connection_replace_settings (NM_CONNECTION (self), new_settings, &error)) {
+		g_warning ("%s: error updating connection %s settings: (%d) %s",
+		           __func__,
+		           nm_connection_get_path (NM_CONNECTION (self)),
+		           error ? error->code : -1,
+		           (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+	}
+}
+
+static void
+updated_get_settings_cb (GObject *proxy,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+	NMRemoteConnection *self = user_data;
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+	GVariant *new_settings;
+	gboolean visible;
+
+	if (!nmdbus_settings_connection_call_get_settings_finish (priv->proxy, &new_settings,
+	                                                          result, NULL)) {
+		/* Connection is no longer visible to this user. */
+		nm_connection_clear_settings (NM_CONNECTION (self));
+
+		visible = FALSE;
+	} else {
+		replace_settings (self, new_settings);
+		g_variant_unref (new_settings);
+
+		visible = TRUE;
+	}
+
+	if (visible != priv->visible) {
+		priv->visible = visible;
+		g_object_notify (G_OBJECT (self), NM_REMOTE_CONNECTION_VISIBLE);
+	}
+}
+
+static void
+updated_cb (NMDBusSettingsConnection *proxy, gpointer user_data)
+{
+	NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+	/* The connection got updated; request the replacement settings */
+	nmdbus_settings_connection_call_get_settings (priv->proxy,
+	                                              NULL,
+	                                              updated_get_settings_cb, self);
+}
+
+/****************************************************************/
+
+static void
+init_dbus (NMObject *object)
+{
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
+	const NMPropertiesInfo property_info[] = {
+		{ NM_REMOTE_CONNECTION_UNSAVED, &priv->unsaved },
+		{ NULL },
+	};
+
+	NM_OBJECT_CLASS (nm_remote_connection_parent_class)->init_dbus (object);
+
+	priv->proxy = NMDBUS_SETTINGS_CONNECTION (_nm_object_get_proxy (object, NM_DBUS_INTERFACE_SETTINGS_CONNECTION));
+	g_assert (priv->proxy);
+
+	_nm_object_register_properties (object,
+	                                NM_DBUS_INTERFACE_SETTINGS_CONNECTION,
+	                                property_info);
+
+	g_signal_connect (priv->proxy, "updated",
+	                  G_CALLBACK (updated_cb), object);
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+	NMRemoteConnection *self = NM_REMOTE_CONNECTION (initable);
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
+	GVariant *settings;
+
+	if (!nm_remote_connection_parent_initable_iface->init (initable, cancellable, error))
+		return FALSE;
+
+	if (!nmdbus_settings_connection_call_get_settings_sync (priv->proxy,
+	                                                        &settings,
+	                                                        cancellable, error)) {
+		if (error && *error)
+			g_dbus_error_strip_remote_error (*error);
+		return FALSE;
+	}
+
+	priv->visible = TRUE;
+	replace_settings (self, settings);
+	g_variant_unref (settings);
+
+	return TRUE;
+}
+
+typedef struct {
+	NMRemoteConnection *connection;
+	GCancellable *cancellable;
+	GSimpleAsyncResult *result;
+} NMRemoteConnectionInitData;
+
+static void
+init_async_complete (NMRemoteConnectionInitData *init_data, GError *error)
+{
+	if (error)
+		g_simple_async_result_take_error (init_data->result, error);
+	else
+		g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
+
+	g_simple_async_result_complete (init_data->result);
+	g_object_unref (init_data->result);
+	g_clear_object (&init_data->cancellable);
+	g_slice_free (NMRemoteConnectionInitData, init_data);
+}
+
+static void
+init_get_settings_cb (GObject *proxy,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+	NMRemoteConnectionInitData *init_data = user_data;
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
+	GVariant *settings;
+	GError *error = NULL;
+
+	if (!nmdbus_settings_connection_call_get_settings_finish (priv->proxy, &settings,
+	                                                          result, &error)) {
+		g_dbus_error_strip_remote_error (error);
+		init_async_complete (init_data, error);
+		return;
+	}
+
+	priv->visible = TRUE;
+	replace_settings (init_data->connection, settings);
+	g_variant_unref (settings);
+
+	init_async_complete (init_data, NULL);
+}
+
+static void
+init_async_parent_inited (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+	NMRemoteConnectionInitData *init_data = user_data;
+	NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
+	GError *error = NULL;
+
+	if (!nm_remote_connection_parent_async_initable_iface->init_finish (G_ASYNC_INITABLE (source), result, &error)) {
+		init_async_complete (init_data, error);
+		return;
+	}
+
+	nmdbus_settings_connection_call_get_settings (priv->proxy,
+	                                              init_data->cancellable,
+	                                              init_get_settings_cb, init_data);
+}
+
+static void
+init_async (GAsyncInitable *initable, int io_priority,
+            GCancellable *cancellable, GAsyncReadyCallback callback,
+            gpointer user_data)
+{
+	NMRemoteConnectionInitData *init_data;
+
+	init_data = g_slice_new0 (NMRemoteConnectionInitData);
+	init_data->connection = NM_REMOTE_CONNECTION (initable);
+	init_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
+	                                               user_data, init_async);
+
+	nm_remote_connection_parent_async_initable_iface->
+		init_async (initable, io_priority, cancellable, init_async_parent_inited, init_data);
+}
+
+static gboolean
+init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
+{
+	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+	else
+		return TRUE;
+}
+
+static void
+nm_remote_connection_init (NMRemoteConnection *self)
+{
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case PROP_UNSAVED:
+		g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->unsaved);
+		break;
+	case PROP_VISIBLE:
+		g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->visible);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+constructed (GObject *object)
+{
+	nm_connection_set_path (NM_CONNECTION (object),
+	                        nm_object_get_path (NM_OBJECT (object)));
+}
+
+static void
+nm_remote_connection_class_init (NMRemoteConnectionClass *remote_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (remote_class);
+	NMObjectClass *nm_object_class = NM_OBJECT_CLASS (remote_class);
+
+	g_type_class_add_private (object_class, sizeof (NMRemoteConnectionPrivate));
+
+	_nm_object_class_add_interface (nm_object_class, NM_DBUS_INTERFACE_SETTINGS_CONNECTION);
+	_nm_dbus_register_proxy_type (NM_DBUS_INTERFACE_SETTINGS_CONNECTION,
+	                              NMDBUS_TYPE_SETTINGS_CONNECTION_PROXY);
+
+	/* virtual methods */
+	object_class->constructed = constructed;
+	object_class->get_property = get_property;
+
+	nm_object_class->init_dbus = init_dbus;
+
+	/* Properties */
+	/**
+	 * NMRemoteConnection:unsaved:
+	 *
+	 * %TRUE if the remote connection contains changes that have not been saved
+	 * to disk, %FALSE if the connection is the same as its on-disk representation.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_UNSAVED,
+		 g_param_spec_boolean (NM_REMOTE_CONNECTION_UNSAVED, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMRemoteConnection:visible:
+	 *
+	 * %TRUE if the remote connection is visible to the current user, %FALSE if
+	 * not.  If the connection is not visible then it is essentially useless; it
+	 * will not contain any settings, and operations such as
+	 * nm_remote_connection_save() and nm_remote_connection_delete() will always
+	 * fail. (#NMRemoteSettings will not normally return non-visible connections
+	 * to callers, but it is possible for a connection's visibility to change
+	 * after you already have a reference to it.)
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_VISIBLE,
+		 g_param_spec_boolean (NM_REMOTE_CONNECTION_VISIBLE, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+nm_remote_connection_connection_iface_init (NMConnectionInterface *iface)
+{
+}
+
+static void
+nm_remote_connection_initable_iface_init (GInitableIface *iface)
+{
+	nm_remote_connection_parent_initable_iface = g_type_interface_peek_parent (iface);
+
+	iface->init = init_sync;
+}
+
+static void
+nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+	nm_remote_connection_parent_async_initable_iface = g_type_interface_peek_parent (iface);
+
+	iface->init_async = init_async;
+	iface->init_finish = init_finish;
+}