132032f488157cc8c7be371e92fbc8c95f96ebf7
[NetworkManager.git] / libnm-glib / nm-remote-connection.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301 USA.
17  *
18  * Copyright 2007 - 2008 Novell, Inc.
19  * Copyright 2007 - 2011 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <string.h>
25
26 #include "NetworkManager.h"
27 #include "nm-utils.h"
28 #include "nm-setting-connection.h"
29 #include "nm-remote-connection.h"
30 #include "nm-remote-connection-private.h"
31 #include "nm-object-private.h"
32 #include "nm-dbus-glib-types.h"
33 #include "nm-dbus-helpers-private.h"
34
35 #define NM_REMOTE_CONNECTION_BUS "bus"
36 #define NM_REMOTE_CONNECTION_DBUS_CONNECTION "dbus-connection"
37 #define NM_REMOTE_CONNECTION_DBUS_PATH "dbus-path"
38
39 static void nm_remote_connection_initable_iface_init (GInitableIface *iface);
40 static void nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface);
41
42 G_DEFINE_TYPE_WITH_CODE (NMRemoteConnection, nm_remote_connection, NM_TYPE_CONNECTION,
43                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_connection_initable_iface_init);
44                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_connection_async_initable_iface_init);
45                          )
46
47 enum {
48         PROP_0,
49         PROP_BUS,
50         PROP_DBUS_CONNECTION,
51         PROP_DBUS_PATH,
52         PROP_UNSAVED,
53
54         LAST_PROP
55 };
56
57 enum {
58         UPDATED,
59         REMOVED,
60         VISIBLE,
61
62         LAST_SIGNAL
63 };
64 static guint signals[LAST_SIGNAL] = { 0 };
65
66 typedef struct RemoteCall RemoteCall;
67 typedef void (*RemoteCallFetchResultCb) (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error);
68
69
70 struct RemoteCall {
71         NMRemoteConnection *self;
72         DBusGProxyCall *call;
73         RemoteCallFetchResultCb fetch_result_cb;
74         GFunc callback;
75         gpointer user_data;
76 };
77
78 typedef struct {
79         DBusGConnection *bus;
80         DBusGProxy *proxy;
81         DBusGProxy *props_proxy;
82         gboolean proxy_is_destroyed;
83         GSList *calls;
84
85         gboolean inited;
86         gboolean unsaved;
87
88         gboolean visible;
89 } NMRemoteConnectionPrivate;
90
91 #define NM_REMOTE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionPrivate))
92
93 /**
94  * nm_remote_connection_error_quark:
95  *
96  * Registers an error quark for #NMRemoteConnection if necessary.
97  *
98  * Returns: the error quark used for #NMRemoteConnection errors.
99  **/
100 GQuark
101 nm_remote_connection_error_quark (void)
102 {
103         static GQuark quark = 0;
104
105         if (G_UNLIKELY (quark == 0))
106                 quark = g_quark_from_static_string ("nm-remote-connection-error-quark");
107         return quark;
108 }
109
110 /****************************************************************/
111
112 static void
113 _nm_remote_connection_ensure_inited (NMRemoteConnection *self)
114 {
115         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
116         GError *error = NULL;
117
118         if (!priv->inited) {
119                 if (!g_initable_init (G_INITABLE (self), NULL, &error)) {
120                         /* Don't warn when the call times out because the settings service can't
121                          * be activated or whatever.
122                          */
123                         if (!g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) {
124                                 g_warning ("%s: (NMRemoteConnection) error initializing: %s\n",
125                                            __func__, error->message);
126                         }
127                         g_error_free (error);
128                 }
129                 priv->inited = TRUE;
130         }
131 }
132
133 /****************************************************************/
134
135 static void
136 remote_call_dbus_cb (DBusGProxy *proxy, DBusGProxyCall *proxy_call, gpointer user_data)
137 {
138         RemoteCall *call = user_data;
139         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (call->self);
140         GError *error = NULL;
141
142         g_assert ( (!proxy && !proxy_call &&  priv->proxy_is_destroyed) ||
143                    ( proxy &&  proxy_call && !priv->proxy_is_destroyed && proxy == priv->proxy) );
144
145         if (priv->proxy_is_destroyed) {
146                 error = g_error_new_literal (NM_REMOTE_CONNECTION_ERROR,
147                                              NM_REMOTE_CONNECTION_ERROR_DISCONNECTED,
148                                              _("Disconnected by D-Bus"));
149         }
150         call->fetch_result_cb (call, proxy_call, error);
151         g_clear_error (&error);
152
153         priv->calls = g_slist_remove (priv->calls, call);
154         g_object_unref (call->self);
155         g_free (call);
156 }
157
158 static gboolean
159 remote_call_cleanup_cb (void *user_data)
160 {
161         remote_call_dbus_cb (NULL, NULL, user_data);
162         return G_SOURCE_REMOVE;
163 }
164
165 static RemoteCall *
166 remote_call_new (NMRemoteConnection *self,
167                  RemoteCallFetchResultCb fetch_result_cb,
168                  GFunc callback,
169                  gpointer user_data)
170 {
171         RemoteCall *call;
172         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
173
174         g_assert (fetch_result_cb);
175
176         if (priv->proxy_is_destroyed && !callback)
177                 return NULL;
178
179         call = g_malloc0 (sizeof (RemoteCall));
180         call->self = g_object_ref (self);
181         call->fetch_result_cb = fetch_result_cb;
182         call->user_data = user_data;
183         call->callback = callback;
184
185         if (priv->proxy_is_destroyed) {
186                 g_idle_add (remote_call_cleanup_cb, call);
187                 return NULL;
188         }
189         priv->calls = g_slist_prepend (priv->calls, call);
190         return call;
191 }
192
193 static void
194 proxy_set_destroyed (NMRemoteConnection *self)
195 {
196         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
197
198         if (priv->proxy_is_destroyed) {
199                 g_assert (!priv->calls);
200                 return;
201         }
202
203         priv->proxy_is_destroyed = TRUE;
204
205         priv->calls = g_slist_reverse (priv->calls);
206         while (priv->calls)
207                 remote_call_dbus_cb (NULL, NULL, priv->calls->data);
208 }
209
210 static void
211 proxy_destroy_cb (DBusGProxy* proxy, gpointer user_data) {
212         proxy_set_destroyed (user_data);
213 }
214
215 /****************************************************************/
216
217 static void
218 result_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error)
219 {
220         NMRemoteConnectionResultFunc func = (NMRemoteConnectionResultFunc) call->callback;
221         GError *local_error = NULL;
222
223         if (!error) {
224                 dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy,
225                                        proxy_call, &local_error, G_TYPE_INVALID);
226                 error = local_error;
227         }
228         if (func)
229                 (*func) (call->self, error, call->user_data);
230         g_clear_error (&local_error);
231 }
232
233 /**
234  * nm_remote_connection_commit_changes:
235  * @connection: the #NMRemoteConnection
236  * @callback: (scope async) (allow-none): a function to be called when the
237  * commit completes
238  * @user_data: (closure): caller-specific data to be passed to @callback
239  *
240  * Send any local changes to the settings and properties of this connection to
241  * NetworkManager, which will immediately save them to disk.
242  **/
243 void
244 nm_remote_connection_commit_changes (NMRemoteConnection *self,
245                                      NMRemoteConnectionResultFunc callback,
246                                      gpointer user_data)
247 {
248         NMRemoteConnectionPrivate *priv;
249         RemoteCall *call;
250         GHashTable *settings;
251
252         g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
253
254         priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
255
256         call = remote_call_new (self, result_cb, (GFunc) callback, user_data);
257         if (!call)
258                 return;
259
260         settings = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ALL);
261         call->call = dbus_g_proxy_begin_call (priv->proxy, "Update",
262                                               remote_call_dbus_cb, call, NULL,
263                                               DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings,
264                                               G_TYPE_INVALID);
265         g_assert (call->call);
266         g_hash_table_destroy (settings);
267 }
268
269 /**
270  * nm_remote_connection_commit_changes_unsaved:
271  * @connection: the #NMRemoteConnection
272  * @callback: (scope async) (allow-none): a function to be called when the
273  * commit completes
274  * @user_data: (closure): caller-specific data to be passed to @callback
275  *
276  * Send any local changes to the settings and properties of this connection to
277  * NetworkManager.  The changes are not saved to disk until either
278  * nm_remote_connection_save() or nm_remote_connection_commit_changes() is
279  * called.
280  *
281  * Since: 0.9.10
282  **/
283 void
284 nm_remote_connection_commit_changes_unsaved (NMRemoteConnection *connection,
285                                              NMRemoteConnectionResultFunc callback,
286                                              gpointer user_data)
287 {
288         NMRemoteConnectionPrivate *priv;
289         GHashTable *settings = NULL;
290         RemoteCall *call;
291
292         g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
293
294         priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
295
296         call = remote_call_new (connection, result_cb, (GFunc) callback, user_data);
297         if (!call)
298                 return;
299
300         settings = nm_connection_to_hash (NM_CONNECTION (connection), NM_SETTING_HASH_FLAG_ALL);
301         call->call = dbus_g_proxy_begin_call (priv->proxy, "UpdateUnsaved",
302                                               remote_call_dbus_cb, call, NULL,
303                                               DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings,
304                                               G_TYPE_INVALID);
305         g_assert (call->call);
306         g_hash_table_destroy (settings);
307 }
308
309 /**
310  * nm_remote_connection_save:
311  * @connection: the #NMRemoteConnection
312  * @callback: (scope async) (allow-none): a function to be called when the
313  * save completes
314  * @user_data: (closure): caller-specific data to be passed to @callback
315  *
316  * Saves the connection to disk if the connection has changes that have not yet
317  * been written to disk, or if the connection has never been saved.
318  *
319  * Since: 0.9.10
320  **/
321 void
322 nm_remote_connection_save (NMRemoteConnection *connection,
323                            NMRemoteConnectionResultFunc callback,
324                            gpointer user_data)
325 {
326         NMRemoteConnectionPrivate *priv;
327         RemoteCall *call;
328
329         g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
330
331         priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
332
333         call = remote_call_new (connection, result_cb, (GFunc) callback, user_data);
334         if (!call)
335                 return;
336
337         call->call = dbus_g_proxy_begin_call (priv->proxy, "Save", remote_call_dbus_cb, call, NULL, G_TYPE_INVALID);
338         g_assert (call->call);
339 }
340
341 /**
342  * nm_remote_connection_delete:
343  * @connection: the #NMRemoteConnection
344  * @callback: (scope async) (allow-none): a function to be called when the delete completes
345  * @user_data: (closure): caller-specific data to be passed to @callback
346  *
347  * Delete the connection.
348  **/
349 void
350 nm_remote_connection_delete (NMRemoteConnection *self,
351                              NMRemoteConnectionResultFunc callback,
352                              gpointer user_data)
353 {
354         NMRemoteConnectionPrivate *priv;
355         RemoteCall *call;
356
357         g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
358
359         priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
360
361         call = remote_call_new (self, result_cb, (GFunc) callback, user_data);
362         if (!call)
363                 return;
364
365         call->call = dbus_g_proxy_begin_call (priv->proxy, "Delete",
366                                               remote_call_dbus_cb, call, NULL,
367                                               G_TYPE_INVALID);
368         g_assert (call->call);
369 }
370
371 static void
372 get_secrets_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error)
373 {
374         NMRemoteConnectionGetSecretsFunc func = (NMRemoteConnectionGetSecretsFunc) call->callback;
375         GHashTable *secrets = NULL;
376         GError *local_error = NULL;
377
378         if (!error) {
379                 dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy,
380                                        proxy_call, &local_error,
381                                        DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &secrets,
382                                        G_TYPE_INVALID);
383                 error = local_error;
384         }
385         if (func)
386                 (*func) (call->self, error ? NULL : secrets, error, call->user_data);
387         g_clear_error (&local_error);
388         if (secrets)
389                 g_hash_table_destroy (secrets);
390 }
391
392
393 /**
394  * nm_remote_connection_get_secrets:
395  * @connection: the #NMRemoteConnection
396  * @setting_name: the #NMSetting object name to get secrets for
397  * @callback: (scope async): a function to be called when the update completes;
398  * must not be %NULL
399  * @user_data: (closure): caller-specific data to be passed to @callback
400  *
401  * Request the connection's secrets.
402  **/
403 void
404 nm_remote_connection_get_secrets (NMRemoteConnection *self,
405                                   const char *setting_name,
406                                   NMRemoteConnectionGetSecretsFunc callback,
407                                   gpointer user_data)
408 {
409         NMRemoteConnectionPrivate *priv;
410         RemoteCall *call;
411
412         g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
413         g_return_if_fail (callback != NULL);
414
415         priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
416
417         call = remote_call_new (self, get_secrets_cb, (GFunc) callback, user_data);
418         if (!call)
419                 return;
420
421         call->call = dbus_g_proxy_begin_call (priv->proxy, "GetSecrets",
422                                               remote_call_dbus_cb, call, NULL,
423                                               G_TYPE_STRING, setting_name,
424                                               G_TYPE_INVALID);
425         g_assert (call->call);
426 }
427
428 /**
429  * nm_remote_connection_get_unsaved:
430  * @connection: the #NMRemoteConnection
431  *
432  * Returns: %TRUE if the remote connection contains changes that have not
433  * been saved to disk, %FALSE if the connection is the same as its on-disk
434  * representation.
435  *
436  * Since: 0.9.10
437  **/
438 gboolean
439 nm_remote_connection_get_unsaved (NMRemoteConnection *connection)
440 {
441         g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
442
443         _nm_remote_connection_ensure_inited (connection);
444         return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->unsaved;
445 }
446
447 /****************************************************************/
448
449 static void
450 replace_settings (NMRemoteConnection *self, GHashTable *new_settings)
451 {
452         GError *error = NULL;
453
454         if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, &error))
455                 g_signal_emit (self, signals[UPDATED], 0, new_settings);
456         else {
457                 g_warning ("%s: error updating connection %s settings: %s",
458                            __func__,
459                            nm_connection_get_path (NM_CONNECTION (self)),
460                            error->message);
461                 g_clear_error (&error);
462
463                 g_signal_emit (self, signals[REMOVED], 0);
464         }
465 }
466
467 static void
468 updated_get_settings_cb (DBusGProxy *proxy,
469                          DBusGProxyCall *call,
470                          gpointer user_data)
471 {
472         NMRemoteConnection *self = user_data;
473         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
474         GHashTable *new_settings;
475         GError *error = NULL;
476
477         dbus_g_proxy_end_call (proxy, call, &error,
478                                DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &new_settings,
479                                G_TYPE_INVALID);
480         if (error) {
481                 GHashTable *hash;
482
483                 g_error_free (error);
484
485                 /* Connection is no longer visible to this user.  Let the settings
486                  * service handle this via 'visible'.  The settings service will emit
487                  * the "removed" signal for us since it handles the lifetime of this
488                  * object.
489                  */
490                 hash = g_hash_table_new (g_str_hash, g_str_equal);
491                 nm_connection_replace_settings (NM_CONNECTION (self), hash, NULL);
492                 g_hash_table_destroy (hash);
493
494                 priv->visible = FALSE;
495                 g_signal_emit (self, signals[VISIBLE], 0, FALSE);
496         } else {
497                 gs_unref_object NMConnection *self_alive = NULL;
498
499                 self_alive = g_object_ref (self);
500                 replace_settings (self, new_settings);
501                 g_hash_table_destroy (new_settings);
502
503                 /* Settings service will handle announcing the connection to clients */
504                 if (priv->visible == FALSE) {
505                         priv->visible = TRUE;
506                         g_signal_emit (self, signals[VISIBLE], 0, TRUE);
507                 }
508         }
509 }
510
511 static void
512 updated_cb (DBusGProxy *proxy, gpointer user_data)
513 {
514         NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
515         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
516
517         /* The connection got updated; request the replacement settings */
518         if (!priv->proxy_is_destroyed) {
519                 dbus_g_proxy_begin_call (priv->proxy, "GetSettings",
520                                          updated_get_settings_cb, self, NULL,
521                                          G_TYPE_INVALID);
522         }
523 }
524
525 static void
526 removed_cb (DBusGProxy *proxy, gpointer user_data)
527 {
528         g_signal_emit (G_OBJECT (user_data), signals[REMOVED], 0);
529 }
530
531 static void
532 properties_changed_cb (DBusGProxy *proxy,
533                        GHashTable *properties,
534                        gpointer user_data)
535 {
536         NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
537         GHashTableIter iter;
538         const char *key;
539         GValue *value;
540
541         g_hash_table_iter_init (&iter, properties);
542         while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
543                 if (!strcmp (key, "Unsaved")) {
544                         NM_REMOTE_CONNECTION_GET_PRIVATE (self)->unsaved = g_value_get_boolean (value);
545                         g_object_notify (G_OBJECT (self), NM_REMOTE_CONNECTION_UNSAVED);
546                 }
547         }
548 }
549
550 /****************************************************************/
551
552 /**
553  * nm_remote_connection_new:
554  * @bus: a valid and connected D-Bus connection
555  * @path: the D-Bus path of the connection as exported by the settings service
556  *
557  * Creates a new object representing the remote connection.
558  *
559  * Returns: the new remote connection object on success, or %NULL on failure
560  **/
561 NMRemoteConnection *
562 nm_remote_connection_new (DBusGConnection *bus,
563                           const char *path)
564 {
565         g_return_val_if_fail (bus != NULL, NULL);
566         g_return_val_if_fail (path != NULL, NULL);
567
568         return (NMRemoteConnection *) g_object_new (NM_TYPE_REMOTE_CONNECTION,
569                                                     NM_REMOTE_CONNECTION_BUS, bus,
570                                                     NM_CONNECTION_PATH, path,
571                                                     NULL);
572 }
573
574 static void
575 constructed (GObject *object)
576 {
577         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
578
579         G_OBJECT_CLASS (nm_remote_connection_parent_class)->constructed (object);
580
581         g_assert (priv->bus);
582         g_assert (nm_connection_get_path (NM_CONNECTION (object)));
583
584         priv->proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
585                                                          nm_connection_get_path (NM_CONNECTION (object)),
586                                                          NM_DBUS_IFACE_SETTINGS_CONNECTION);
587         g_assert (priv->proxy);
588         dbus_g_proxy_set_default_timeout (priv->proxy, G_MAXINT);
589
590         dbus_g_proxy_add_signal (priv->proxy, "Updated", G_TYPE_INVALID);
591         dbus_g_proxy_connect_signal (priv->proxy, "Updated", G_CALLBACK (updated_cb), object, NULL);
592
593         dbus_g_proxy_add_signal (priv->proxy, "Removed", G_TYPE_INVALID);
594         dbus_g_proxy_connect_signal (priv->proxy, "Removed", G_CALLBACK (removed_cb), object, NULL);
595
596         g_signal_connect (priv->proxy, "destroy", G_CALLBACK (proxy_destroy_cb), object);
597
598         /* Monitor properties */
599         dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
600                                            G_TYPE_NONE,
601                                            DBUS_TYPE_G_MAP_OF_VARIANT,
602                                            G_TYPE_INVALID);
603         dbus_g_proxy_add_signal (priv->proxy, "PropertiesChanged",
604                                  DBUS_TYPE_G_MAP_OF_VARIANT,
605                                  G_TYPE_INVALID);
606         dbus_g_proxy_connect_signal (priv->proxy, "PropertiesChanged",
607                                      G_CALLBACK (properties_changed_cb),
608                                      object,
609                                      NULL);
610
611         priv->props_proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
612                                                                nm_connection_get_path (NM_CONNECTION (object)),
613                                                                DBUS_INTERFACE_PROPERTIES);
614         g_assert (priv->props_proxy);
615 }
616
617 static gboolean
618 init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
619 {
620         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
621         GHashTable *hash;
622         gs_unref_object NMConnection *self_alive = NULL;
623
624         if (!dbus_g_proxy_call (priv->proxy, "GetSettings", error,
625                                 G_TYPE_INVALID,
626                                 DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &hash,
627                                 G_TYPE_INVALID))
628                 return FALSE;
629         priv->visible = TRUE;
630         self_alive = g_object_ref (initable);
631         replace_settings (NM_REMOTE_CONNECTION (initable), hash);
632         g_hash_table_destroy (hash);
633
634         /* Get properties */
635         hash = NULL;
636         if (!dbus_g_proxy_call (priv->props_proxy, "GetAll", error,
637                                 G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION,
638                                 G_TYPE_INVALID,
639                                 DBUS_TYPE_G_MAP_OF_VARIANT, &hash,
640                                 G_TYPE_INVALID))
641                 return FALSE;
642         properties_changed_cb (priv->props_proxy, hash, NM_REMOTE_CONNECTION (initable));
643         g_hash_table_destroy (hash);
644
645         return TRUE;
646 }
647
648 typedef struct {
649         NMRemoteConnection *connection;
650         GSimpleAsyncResult *result;
651 } NMRemoteConnectionInitData;
652
653 static void
654 init_async_complete (NMRemoteConnectionInitData *init_data, GError *error)
655 {
656         if (error)
657                 g_simple_async_result_take_error (init_data->result, error);
658         else {
659                 g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
660                 NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection)->inited = TRUE;
661         }
662
663         g_simple_async_result_complete (init_data->result);
664         g_object_unref (init_data->result);
665         g_slice_free (NMRemoteConnectionInitData, init_data);
666 }
667
668 static void
669 init_async_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
670 {
671         NMRemoteConnectionInitData *init_data = user_data;
672         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
673         GHashTable *props;
674         GError *error = NULL;
675
676         if (dbus_g_proxy_end_call (proxy, call, &error,
677                                    DBUS_TYPE_G_MAP_OF_VARIANT, &props,
678                                    G_TYPE_INVALID)) {
679                 properties_changed_cb (priv->props_proxy, props, init_data->connection);
680                 g_hash_table_destroy (props);
681         }
682         init_async_complete (init_data, error);
683 }
684
685 static void
686 init_get_settings_cb (DBusGProxy *proxy,
687                       DBusGProxyCall *call,
688                       gpointer user_data)
689 {
690         NMRemoteConnectionInitData *init_data = user_data;
691         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
692         GHashTable *settings;
693         GError *error = NULL;
694         gs_unref_object NMConnection *self_alive = NULL;
695
696         dbus_g_proxy_end_call (proxy, call, &error,
697                                DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings,
698                                G_TYPE_INVALID);
699         if (error) {
700                 init_async_complete (init_data, error);
701                 return;
702         }
703
704         priv->visible = TRUE;
705         self_alive = g_object_ref (init_data->connection);
706         replace_settings (init_data->connection, settings);
707         g_hash_table_destroy (settings);
708
709         /* Grab properties */
710         dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
711                                  init_async_got_properties, init_data, NULL,
712                                  G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION,
713                                  G_TYPE_INVALID);
714 }
715
716 static void
717 init_async (GAsyncInitable *initable, int io_priority,
718             GCancellable *cancellable, GAsyncReadyCallback callback,
719             gpointer user_data)
720 {
721         NMRemoteConnectionInitData *init_data;
722         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
723
724
725         init_data = g_slice_new0 (NMRemoteConnectionInitData);
726         init_data->connection = NM_REMOTE_CONNECTION (initable);
727         init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
728                                                        user_data, init_async);
729
730         dbus_g_proxy_begin_call (priv->proxy, "GetSettings",
731                                  init_get_settings_cb, init_data, NULL,
732                                  G_TYPE_INVALID);
733 }
734
735 static gboolean
736 init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
737 {
738         GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
739
740         if (g_simple_async_result_propagate_error (simple, error))
741                 return FALSE;
742         else
743                 return TRUE;
744 }
745
746 static void
747 nm_remote_connection_init (NMRemoteConnection *self)
748 {
749 }
750
751 static GObject *
752 constructor (GType type, guint n_construct_properties,
753              GObjectConstructParam *construct_properties)
754 {
755         static GParamSpec *nm_connection_path = NULL;
756         static GParamSpec *nm_remote_connection_dbus_path = NULL;
757         int i, path_index = -1, dbus_path_index = -1;
758
759         if (!nm_connection_path) {
760                 nm_connection_path =
761                         g_object_class_find_property (g_type_class_peek (NM_TYPE_CONNECTION),
762                                                       NM_CONNECTION_PATH);
763                 nm_remote_connection_dbus_path =
764                         g_object_class_find_property (g_type_class_peek (NM_TYPE_REMOTE_CONNECTION),
765                                                       NM_REMOTE_CONNECTION_DBUS_PATH);
766         }
767
768         /* Find the two properties */
769         for (i = 0; i < n_construct_properties; i++) {
770                 if (construct_properties[i].pspec == nm_connection_path)
771                         path_index = i;
772                 else if (construct_properties[i].pspec == nm_remote_connection_dbus_path)
773                         dbus_path_index = i;
774         }
775         g_assert (path_index != -1 && dbus_path_index != -1);
776
777         /* If NMRemoteConnection:dbus-path is set, and NMConnection:path
778          * is not, then copy the value of the former to the latter.
779          */
780         if (g_value_get_string (construct_properties[dbus_path_index].value) &&
781             !g_value_get_string (construct_properties[path_index].value))
782                 construct_properties[path_index].value = construct_properties[dbus_path_index].value;
783
784         return G_OBJECT_CLASS (nm_remote_connection_parent_class)->
785                 constructor (type, n_construct_properties, construct_properties);
786 }
787
788 static void
789 get_property (GObject *object, guint prop_id,
790               GValue *value, GParamSpec *pspec)
791 {
792         _nm_remote_connection_ensure_inited (NM_REMOTE_CONNECTION (object));
793
794         switch (prop_id) {
795         case PROP_UNSAVED:
796                 g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->unsaved);
797                 break;
798         default:
799                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
800                 break;
801         }
802 }
803
804 static void
805 set_property (GObject *object, guint prop_id,
806               const GValue *value, GParamSpec *pspec)
807 {
808         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
809
810         switch (prop_id) {
811         case PROP_BUS:
812         case PROP_DBUS_CONNECTION:
813                 /* Construct only */
814                 /* priv->bus is set from either of two properties so that it (a) remains
815                  * backwards compatible with the previous "bus" property, and that (b)
816                  * it can be created just like an NMObject using the "dbus-connection",
817                  * even though it's not a subclass of NMObject.  So don't overwrite the
818                  * a valid value that the other property set with NULL, if one of the
819                  * properties isn't specified at construction time.
820                  */
821                 if (!priv->bus)
822                         priv->bus = g_value_dup_boxed (value);
823                 break;
824         case PROP_DBUS_PATH:
825                 /* Don't need to do anything; see constructor(). */
826                 break;
827         default:
828                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
829                 break;
830         }
831 }
832
833 static void
834 dispose (GObject *object)
835 {
836         NMRemoteConnection *self = NM_REMOTE_CONNECTION (object);
837         NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
838
839         proxy_set_destroyed (self);
840
841         if (priv->proxy) {
842                 g_signal_handlers_disconnect_by_func (priv->proxy, proxy_destroy_cb, object);
843                 g_clear_object (&priv->proxy);
844         }
845         g_clear_object (&priv->props_proxy);
846
847         if (priv->bus) {
848                 dbus_g_connection_unref (priv->bus);
849                 priv->bus = NULL;
850         }
851
852         G_OBJECT_CLASS (nm_remote_connection_parent_class)->dispose (object);
853 }
854
855 static void
856 nm_remote_connection_class_init (NMRemoteConnectionClass *remote_class)
857 {
858         GObjectClass *object_class = G_OBJECT_CLASS (remote_class);
859
860         g_type_class_add_private (object_class, sizeof (NMRemoteConnectionPrivate));
861
862         /* virtual methods */
863         object_class->constructor = constructor;
864         object_class->get_property = get_property;
865         object_class->set_property = set_property;
866         object_class->dispose = dispose;
867         object_class->constructed = constructed;
868
869         /* Properties */
870         /**
871          * NMRemoteConnection:bus:
872          *
873          * The #DBusGConnection that the #NMRemoteConnection is connected to.
874          */
875         g_object_class_install_property
876                 (object_class, PROP_BUS,
877                  g_param_spec_boxed (NM_REMOTE_CONNECTION_BUS, "", "",
878                                      DBUS_TYPE_G_CONNECTION,
879                                      G_PARAM_WRITABLE |
880                                      G_PARAM_CONSTRUCT_ONLY |
881                                      G_PARAM_STATIC_STRINGS));
882
883         /* These are needed so _nm_object_create() can create NMRemoteConnections */
884         g_object_class_install_property
885                 (object_class, PROP_DBUS_CONNECTION,
886                  g_param_spec_boxed (NM_REMOTE_CONNECTION_DBUS_CONNECTION, "", "",
887                                      DBUS_TYPE_G_CONNECTION,
888                                      G_PARAM_WRITABLE |
889                                      G_PARAM_CONSTRUCT_ONLY |
890                                      G_PARAM_STATIC_STRINGS));
891         g_object_class_install_property
892                 (object_class, PROP_DBUS_PATH,
893                  g_param_spec_string (NM_REMOTE_CONNECTION_DBUS_PATH, "", "",
894                                       NULL,
895                                       G_PARAM_WRITABLE |
896                                       G_PARAM_CONSTRUCT_ONLY |
897                                       G_PARAM_STATIC_STRINGS));
898
899         /**
900          * NMRemoteConnection:unsaved:
901          *
902          * %TRUE if the remote connection contains changes that have not been saved
903          * to disk, %FALSE if the connection is the same as its on-disk representation.
904          *
905          * Since: 0.9.10
906          **/
907         g_object_class_install_property
908                 (object_class, PROP_UNSAVED,
909                  g_param_spec_boolean (NM_REMOTE_CONNECTION_UNSAVED, "", "",
910                                        FALSE,
911                                        G_PARAM_READABLE |
912                                        G_PARAM_STATIC_STRINGS));
913
914         /* Signals */
915         /**
916          * NMRemoteConnection::updated:
917          * @connection: a #NMConnection
918          *
919          * This signal is emitted when a connection changes, and it is
920          * still visible to the user.
921          */
922         signals[UPDATED] =
923                 g_signal_new (NM_REMOTE_CONNECTION_UPDATED,
924                               G_TYPE_FROM_CLASS (remote_class),
925                               G_SIGNAL_RUN_FIRST,
926                               G_STRUCT_OFFSET (NMRemoteConnectionClass, updated),
927                               NULL, NULL,
928                               g_cclosure_marshal_VOID__VOID,
929                               G_TYPE_NONE, 0);
930
931         /**
932          * NMRemoteConnection::removed:
933          * @connection: a #NMConnection
934          *
935          * This signal is emitted when a connection is either deleted or becomes
936          * invisible to the current user.
937          */
938         signals[REMOVED] =
939                 g_signal_new (NM_REMOTE_CONNECTION_REMOVED,
940                               G_TYPE_FROM_CLASS (remote_class),
941                               G_SIGNAL_RUN_FIRST,
942                               G_STRUCT_OFFSET (NMRemoteConnectionClass, removed),
943                               NULL, NULL,
944                               g_cclosure_marshal_VOID__VOID,
945                               G_TYPE_NONE, 0);
946
947         /* Private signal */
948         signals[VISIBLE] =
949                 g_signal_new ("visible",
950                               G_TYPE_FROM_CLASS (remote_class),
951                               G_SIGNAL_RUN_FIRST,
952                               0, NULL, NULL,
953                               g_cclosure_marshal_VOID__BOOLEAN,
954                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
955 }
956
957 static void
958 nm_remote_connection_initable_iface_init (GInitableIface *iface)
959 {
960         iface->init = init_sync;
961 }
962
963 static void
964 nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface)
965 {
966         iface->init_async = init_async;
967         iface->init_finish = init_finish;
968 }