libnm-glib: don't fail creating connection in NMVPNPlugin
[NetworkManager.git] / libnm-glib / nm-vpn-plugin.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 - 2008 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-vpn-plugin.h"
25
26 #include <signal.h>
27
28 #include "nm-vpn-enum-types.h"
29 #include "nm-utils.h"
30 #include "nm-connection.h"
31 #include "nm-dbus-glib-types.h"
32 #include "nm-setting-private.h"
33
34 static gboolean impl_vpn_plugin_connect    (NMVPNPlugin *plugin,
35                                             GHashTable *connection,
36                                             GError **error);
37
38 static gboolean impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin,
39                                                      GHashTable *connection,
40                                                      GHashTable *details,
41                                                      GError **error);
42
43 static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin,
44                                               GHashTable *connection,
45                                               char **service_name,
46                                               GError **err);
47
48 static gboolean impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin,
49                                              GHashTable *connection,
50                                              GError **err);
51
52 static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
53                                             GError **err);
54
55 static gboolean impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
56                                             GHashTable *config,
57                                             GError **err);
58
59 static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
60                                                 GHashTable *config,
61                                                 GError **err);
62
63 static gboolean impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
64                                                 GHashTable *config,
65                                                 GError **err);
66
67 static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
68                                              char *reason,
69                                              GError **err);
70
71 #include "nm-vpn-plugin-glue.h"
72
73 #define NM_VPN_PLUGIN_QUIT_TIMER    180
74
75 G_DEFINE_ABSTRACT_TYPE (NMVPNPlugin, nm_vpn_plugin, G_TYPE_OBJECT)
76
77 typedef struct {
78         NMVPNServiceState state;
79
80         /* DBUS-y stuff */
81         DBusGConnection *connection;
82         char *dbus_service_name;
83
84         /* Temporary stuff */
85         guint connect_timer;
86         guint quit_timer;
87         guint fail_stop_id;
88         gboolean interactive;
89
90         gboolean got_config;
91         gboolean has_ip4, got_ip4;
92         gboolean has_ip6, got_ip6;
93
94         /* Config stuff copied from config to ip4config */
95         GValue banner, tundev, gateway, mtu;
96 } NMVPNPluginPrivate;
97
98 #define NM_VPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN, NMVPNPluginPrivate))
99
100 enum {
101         STATE_CHANGED,
102         CONFIG,
103         IP4_CONFIG,
104         IP6_CONFIG,
105         LOGIN_BANNER,
106         FAILURE,
107         QUIT,
108         SECRETS_REQUIRED,
109
110         LAST_SIGNAL
111 };
112
113 static guint signals[LAST_SIGNAL] = { 0 };
114
115 enum {
116         PROP_0,
117         PROP_DBUS_SERVICE_NAME,
118         PROP_STATE,
119
120         LAST_PROP
121 };
122
123 static GSList *active_plugins = NULL;
124
125
126 GQuark
127 nm_vpn_plugin_error_quark (void)
128 {
129         static GQuark quark = 0;
130
131         if (!quark)
132                 quark = g_quark_from_static_string ("nm_vpn_plugin_error");
133
134         return quark;
135 }
136
137
138 static void
139 nm_vpn_plugin_set_connection (NMVPNPlugin *plugin,
140                               DBusGConnection *connection)
141 {
142         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
143
144         if (priv->connection)
145                 dbus_g_connection_unref (priv->connection);
146
147         priv->connection = connection;
148 }
149
150 DBusGConnection *
151 nm_vpn_plugin_get_connection (NMVPNPlugin *plugin)
152 {
153         DBusGConnection *connection;
154
155         g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NULL);
156
157         connection = NM_VPN_PLUGIN_GET_PRIVATE (plugin)->connection;
158
159         if (connection)
160                 dbus_g_connection_ref (connection);
161
162         return connection;
163 }
164
165 NMVPNServiceState
166 nm_vpn_plugin_get_state (NMVPNPlugin *plugin)
167 {
168         g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN);
169
170         return NM_VPN_PLUGIN_GET_PRIVATE (plugin)->state;
171 }
172
173 void
174 nm_vpn_plugin_set_state (NMVPNPlugin *plugin,
175                          NMVPNServiceState state)
176 {
177         NMVPNPluginPrivate *priv;
178
179         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
180
181         priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
182         if (priv->state != state) {
183                 priv->state = state;
184                 g_signal_emit (plugin, signals[STATE_CHANGED], 0, state);
185         }
186 }
187
188 void
189 nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin,
190                                 const char *banner)
191 {
192         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
193         g_return_if_fail (banner != NULL);
194
195         g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner);
196 }
197
198 void
199 nm_vpn_plugin_failure (NMVPNPlugin *plugin,
200                        NMVPNPluginFailure reason)
201 {
202         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
203
204         g_signal_emit (plugin, signals[FAILURE], 0, reason);
205 }
206
207 gboolean
208 nm_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err)
209 {
210         gboolean ret = FALSE;
211         NMVPNServiceState state;
212
213         g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
214
215         state = nm_vpn_plugin_get_state (plugin);
216         switch (state) {
217         case NM_VPN_SERVICE_STATE_STOPPING:
218                 g_set_error (err,
219                              NM_VPN_PLUGIN_ERROR,
220                              NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
221                              "%s",
222                              "Could not process the request because the VPN connection is already being stopped.");
223                 break;
224         case NM_VPN_SERVICE_STATE_STOPPED:
225                 g_set_error (err,
226                              NM_VPN_PLUGIN_ERROR,
227                              NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED,
228                              "%s",
229                              "Could not process the request because no VPN connection was active.");
230                 break;
231         case NM_VPN_SERVICE_STATE_STARTING:
232         case NM_VPN_SERVICE_STATE_STARTED:
233                 nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING);
234                 ret = NM_VPN_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err);
235                 nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
236                 break;
237         case NM_VPN_SERVICE_STATE_INIT:
238                 ret = TRUE;
239                 break;
240
241         default:
242                 g_warning ("Unhandled VPN service state %d", state);
243                 g_assert_not_reached ();
244                 break;
245         }
246
247         return ret;
248 }
249
250 static void
251 nm_vpn_plugin_emit_quit (NMVPNPlugin *plugin)
252 {
253         g_signal_emit (plugin, signals[QUIT], 0);
254 }
255
256 static gboolean
257 connect_timer_expired (gpointer data)
258 {
259         NMVPNPlugin *plugin = NM_VPN_PLUGIN (data);
260         GError *err = NULL;
261
262         NM_VPN_PLUGIN_GET_PRIVATE (plugin)->connect_timer = 0;
263         g_message ("Connect timer expired, disconnecting.");
264         nm_vpn_plugin_disconnect (plugin, &err);
265         if (err) {
266                 g_warning ("Disconnect failed: %s", err->message);
267                 g_error_free (err);
268         }
269
270         return G_SOURCE_REMOVE;
271 }
272
273 static gboolean
274 quit_timer_expired (gpointer data)
275 {
276         NMVPNPlugin *self = NM_VPN_PLUGIN (data);
277
278         NM_VPN_PLUGIN_GET_PRIVATE (self)->quit_timer = 0;
279         nm_vpn_plugin_emit_quit (self);
280         return G_SOURCE_REMOVE;
281 }
282
283 static void
284 schedule_quit_timer (NMVPNPlugin *self)
285 {
286         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (self);
287
288         nm_clear_g_source (&priv->quit_timer);
289         priv->quit_timer = g_timeout_add_seconds (NM_VPN_PLUGIN_QUIT_TIMER,
290                                                   quit_timer_expired,
291                                                   self);
292 }
293
294 static gboolean
295 fail_stop (gpointer data)
296 {
297         NMVPNPlugin *self = NM_VPN_PLUGIN (data);
298
299         NM_VPN_PLUGIN_GET_PRIVATE (self)->fail_stop_id = 0;
300         nm_vpn_plugin_set_state (self, NM_VPN_SERVICE_STATE_STOPPED);
301         return G_SOURCE_REMOVE;
302 }
303
304 static void
305 schedule_fail_stop (NMVPNPlugin *plugin, guint timeout_secs)
306 {
307         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
308
309         nm_clear_g_source (&priv->fail_stop_id);
310         if (timeout_secs)
311                 priv->fail_stop_id = g_timeout_add_seconds (timeout_secs, fail_stop, plugin);
312         else
313                 priv->fail_stop_id = g_idle_add (fail_stop, plugin);
314 }
315
316 static void
317 _g_value_set (GValue *dst, GValue *src)
318 {
319         if (src) {
320                 GType type = G_VALUE_TYPE (src);
321
322                 if (G_IS_VALUE (dst))
323                         g_value_unset (dst);
324                 g_value_init (dst, type);
325                 g_value_copy (src, dst);
326         } else if (G_IS_VALUE (dst))
327                 g_value_unset (dst);
328 }
329
330 void
331 nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
332                           GHashTable *config)
333 {
334         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
335         GValue *val;
336
337         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
338         g_return_if_fail (config != NULL);
339
340         priv->got_config = TRUE;
341
342         val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4);
343         if (val && g_value_get_boolean (val))
344                 priv->has_ip4 = TRUE;
345         val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
346         if (val && g_value_get_boolean (val))
347                 priv->has_ip6 = TRUE;
348
349         /* Record the items that need to also be inserted into the
350          * ip4config, for compatibility with older daemons.
351          */
352         _g_value_set (&priv->banner, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER));
353         _g_value_set (&priv->tundev, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV));
354         _g_value_set (&priv->gateway, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY));
355         _g_value_set (&priv->mtu, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU));
356
357         g_signal_emit (plugin, signals[CONFIG], 0, config);
358 }
359
360 void
361 nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
362                               GHashTable *ip4_config)
363 {
364         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
365         GHashTable *combined_config;
366         GHashTableIter iter;
367         gpointer key, value;
368
369         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
370         g_return_if_fail (ip4_config != NULL);
371
372         priv->got_ip4 = TRUE;
373
374         /* Old plugins won't send the "config" signal and thus can't send
375          * NM_VPN_PLUGIN_CONFIG_HAS_IP4 either.  But since they don't support IPv6,
376          * we can safely assume that, if we don't receive a "config" signal but do
377          * receive an "ip4-config" signal, the old plugin supports IPv4.
378          */
379         if (!priv->got_config)
380                 priv->has_ip4 = TRUE;
381
382         /* Older NetworkManager daemons expect all config info to be in
383          * the ip4 config, so they won't even notice the "config" signal
384          * being emitted. So just copy all of that data into the ip4
385          * config too.
386          */
387         combined_config = g_hash_table_new (g_str_hash, g_str_equal);
388         g_hash_table_iter_init (&iter, ip4_config);
389         while (g_hash_table_iter_next (&iter, &key, &value))
390                 g_hash_table_insert (combined_config, key, value);
391
392         if (G_VALUE_TYPE (&priv->banner) != G_TYPE_INVALID)
393                 g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
394         if (G_VALUE_TYPE (&priv->tundev) != G_TYPE_INVALID)
395                 g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
396         if (G_VALUE_TYPE (&priv->gateway) != G_TYPE_INVALID)
397                 g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
398         if (G_VALUE_TYPE (&priv->mtu) != G_TYPE_INVALID)
399                 g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
400
401         g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
402         g_hash_table_destroy (combined_config);
403
404         if (   priv->has_ip4 == priv->got_ip4
405             && priv->has_ip6 == priv->got_ip6)
406                 nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
407 }
408
409 void
410 nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
411                               GHashTable *ip6_config)
412 {
413         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
414
415         g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
416         g_return_if_fail (ip6_config != NULL);
417
418         priv->got_ip6 = TRUE;
419         g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
420
421         if (   priv->has_ip4 == priv->got_ip4
422             && priv->has_ip6 == priv->got_ip6)
423                 nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
424 }
425
426 static void
427 connect_timer_start (NMVPNPlugin *plugin)
428 {
429         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
430
431         nm_clear_g_source (&priv->connect_timer);
432         priv->connect_timer = g_timeout_add_seconds (60, connect_timer_expired, plugin);
433 }
434
435 static gboolean
436 _connect_generic (NMVPNPlugin *plugin,
437                   GHashTable *properties,
438                   GHashTable *details,
439                   GError **error)
440 {
441         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
442         NMVPNPluginClass *vpn_class = NM_VPN_PLUGIN_GET_CLASS (plugin);
443         NMConnection *connection;
444         gboolean success = FALSE;
445         GError *local = NULL;
446         guint fail_stop_timeout = 0;
447
448         if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
449             priv->state != NM_VPN_SERVICE_STATE_INIT) {
450                 g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE,
451                              "Could not start connection: wrong plugin state %d",
452                              priv->state);
453                 return FALSE;
454         }
455
456         connection = _nm_connection_new_from_hash (properties);
457
458         priv->interactive = FALSE;
459         if (details && !vpn_class->connect_interactive) {
460                 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
461                                      "Plugin does not implement ConnectInteractive()");
462                 return FALSE;
463         }
464
465         nm_clear_g_source (&priv->fail_stop_id);
466
467         if (details) {
468                 priv->interactive = TRUE;
469                 success = vpn_class->connect_interactive (plugin, connection, details, &local);
470                 if (g_error_matches (local, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
471                         /* Give NetworkManager a bit of time to fall back to Connect() */
472                         fail_stop_timeout = 5;
473                 }
474                 g_propagate_error (error, local);
475         } else
476                 success = vpn_class->connect (plugin, connection, error);
477
478         if (success) {
479                 nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
480
481                 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
482                 connect_timer_start (plugin);
483         } else {
484                 /* Stop the plugin from an idle handler so that the Connect
485                  * method return gets sent before the STOP StateChanged signal.
486                  */
487                 schedule_fail_stop (plugin, fail_stop_timeout);
488         }
489
490         g_object_unref (connection);
491         return success;
492 }
493
494 static gboolean
495 impl_vpn_plugin_connect (NMVPNPlugin *plugin,
496                          GHashTable *connection,
497                          GError **error)
498 {
499         return _connect_generic (plugin, connection, NULL, error);
500 }
501
502 static gboolean
503 impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin,
504                                      GHashTable *connection,
505                                      GHashTable *details,
506                                      GError **error)
507 {
508         return _connect_generic (plugin, connection, details, error);
509 }
510
511 /***************************************************************/
512
513 static gboolean
514 impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin,
515                               GHashTable *properties,
516                               char **setting_name,
517                               GError **err)
518 {
519         gboolean ret = FALSE;
520         NMConnection *connection;
521         char *sn = NULL;
522         GError *ns_err = NULL;
523         gboolean needed = FALSE;
524
525         g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
526         g_return_val_if_fail (properties != NULL, FALSE);
527
528         connection = _nm_connection_new_from_hash (properties);
529
530         if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets) {
531                 *setting_name = "";
532                 ret = TRUE;
533                 goto out;
534         }
535
536         needed = NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &sn, &ns_err);
537         if (ns_err) {
538                 *err = g_error_copy (ns_err);
539                 g_error_free (ns_err);
540                 goto out;
541         }
542
543         ret = TRUE;
544         if (needed) {
545                 /* Push back the quit timer so the VPN plugin doesn't quit in the
546                  * middle of asking the user for secrets.
547                  */
548                 schedule_quit_timer (plugin);
549
550                 g_assert (sn);
551                 *setting_name = g_strdup (sn);
552         } else {
553                 /* No secrets required */
554                 *setting_name = g_strdup ("");
555         }
556
557 out:
558         return ret;
559 }
560
561 static gboolean
562 impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin,
563                              GHashTable *properties,
564                              GError **error)
565 {
566         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
567         NMConnection *connection;
568         gboolean success;
569
570         if (priv->state != NM_VPN_SERVICE_STATE_STARTING) {
571                 g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE,
572                              "Could not accept new secrets: wrong plugin state %d",
573                              priv->state);
574                 return FALSE;
575         }
576
577         connection = _nm_connection_new_from_hash (properties);
578
579         if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets) {
580                 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
581                                      "Could not accept new secrets: plugin cannot process interactive secrets");
582                 g_object_unref (connection);
583                 return FALSE;
584         }
585
586         success = NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, error);
587         if (success) {
588                 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
589                 connect_timer_start (plugin);
590         } else {
591                 /* Stop the plugin from and idle handler so that the NewSecrets
592                  * method return gets sent before the STOP StateChanged signal.
593                  */
594                 schedule_fail_stop (plugin, 0);
595         }
596
597         g_object_unref (connection);
598         return success;
599 }
600
601 /**
602  * nm_vpn_plugin_secrets_required:
603  * @plugin: the #NMVPNPlugin
604  * @message: an information message about why secrets are required, if any
605  * @hints: VPN specific secret names for required new secrets
606  *
607  * Called by VPN plugin implementations to signal to NetworkManager that secrets
608  * are required during the connection process.  This signal may be used to
609  * request new secrets when the secrets originally provided by NetworkManager
610  * are insufficient, or the VPN process indicates that it needs additional
611  * information to complete the request.
612  *
613  * Since: 0.9.10
614  */
615 void
616 nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin,
617                                 const char *message,
618                                 const char **hints)
619 {
620         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
621
622         /* Plugin must be able to accept the new secrets if it calls this method */
623         g_return_if_fail (NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets);
624
625         /* Plugin cannot call this method if NetworkManager didn't originally call
626          * ConnectInteractive().
627          */
628         g_return_if_fail (priv->interactive == TRUE);
629
630         /* Cancel the connect timer since secrets might take a while.  It'll
631          * get restarted when the secrets come back via NewSecrets().
632          */
633         nm_clear_g_source (&priv->connect_timer);
634
635         g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints);
636 }
637
638 /***************************************************************/
639
640 static gboolean
641 impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
642                             GError **err)
643 {
644         return nm_vpn_plugin_disconnect (plugin, err);
645 }
646
647 static gboolean
648 impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
649                             GHashTable *config,
650                             GError **err)
651 {
652         nm_vpn_plugin_set_config (plugin, config);
653
654         return TRUE;
655 }
656
657 static gboolean
658 impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
659                                 GHashTable *config,
660                                 GError **err)
661 {
662         nm_vpn_plugin_set_ip4_config (plugin, config);
663
664         return TRUE;
665 }
666
667 static gboolean
668 impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
669                                 GHashTable *config,
670                                 GError **err)
671 {
672         nm_vpn_plugin_set_ip6_config (plugin, config);
673
674         return TRUE;
675 }
676
677 static gboolean
678 impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
679                              char *reason,
680                              GError **err)
681 {
682         nm_vpn_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG);
683
684         return TRUE;
685 }
686
687 /*********************************************************************/
688
689 static void
690 sigterm_handler (int signum)
691 {
692         g_slist_foreach (active_plugins, (GFunc) nm_vpn_plugin_emit_quit, NULL);
693 }
694
695 static void
696 setup_unix_signal_handler (void)
697 {
698         struct sigaction action;
699         sigset_t block_mask;
700
701         action.sa_handler = sigterm_handler;
702         sigemptyset (&block_mask);
703         action.sa_mask = block_mask;
704         action.sa_flags = 0;
705         sigaction (SIGINT, &action, NULL);
706         sigaction (SIGTERM, &action, NULL);
707 }
708
709 /*********************************************************************/
710
711 static void
712 one_plugin_destroyed (gpointer data,
713                       GObject *object)
714 {
715         active_plugins = g_slist_remove (active_plugins, object);
716 }
717
718 static void
719 nm_vpn_plugin_init (NMVPNPlugin *plugin)
720 {
721         active_plugins = g_slist_append (active_plugins, plugin);
722         g_object_weak_ref (G_OBJECT (plugin),
723                            one_plugin_destroyed,
724                            NULL);
725 }
726
727 static GObject *
728 constructor (GType type,
729              guint n_construct_params,
730              GObjectConstructParam *construct_params)
731 {
732         GObject *object;
733         NMVPNPlugin *plugin;
734         NMVPNPluginPrivate *priv;
735         DBusGConnection *connection;
736         DBusGProxy *proxy;
737         guint request_name_result;
738         GError *err = NULL;
739
740         object = G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->constructor (type,
741                                                                            n_construct_params,
742                                                                            construct_params);
743         if (!object)
744                 return NULL;
745
746         priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
747         if (!priv->dbus_service_name)
748                 goto err;
749
750         connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
751         if (!connection)
752                 goto err;
753
754         proxy = dbus_g_proxy_new_for_name (connection,
755                                            DBUS_SERVICE_DBUS,
756                                            DBUS_PATH_DBUS,
757                                            DBUS_INTERFACE_DBUS);
758
759         if (!dbus_g_proxy_call (proxy, "RequestName", &err,
760                                 G_TYPE_STRING, priv->dbus_service_name,
761                                 G_TYPE_UINT, 0,
762                                 G_TYPE_INVALID,
763                                 G_TYPE_UINT, &request_name_result,
764                                 G_TYPE_INVALID)) {
765                 g_object_unref (proxy);
766                 goto err;
767         }
768
769         g_object_unref (proxy);
770
771         dbus_g_connection_register_g_object (connection,
772                                              NM_VPN_DBUS_PLUGIN_PATH,
773                                              object);
774
775         plugin = NM_VPN_PLUGIN (object);
776
777         nm_vpn_plugin_set_connection (plugin, connection);
778         nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT);
779
780         return object;
781
782  err:
783         if (err) {
784                 g_warning ("Failed to initialize VPN plugin: %s", err->message);
785                 g_error_free (err);
786         }
787
788         if (object)
789                 g_object_unref (object);
790
791         return NULL;
792 }
793
794 static void
795 set_property (GObject *object, guint prop_id,
796               const GValue *value, GParamSpec *pspec)
797 {
798         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
799
800         switch (prop_id) {
801         case PROP_DBUS_SERVICE_NAME:
802                 /* Construct-only */
803                 priv->dbus_service_name = g_strdup (g_value_get_string (value));
804                 break;
805         case PROP_STATE:
806                 nm_vpn_plugin_set_state (NM_VPN_PLUGIN (object),
807                                          (NMVPNServiceState) g_value_get_uint (value));
808                 break;
809         default:
810                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
811                 break;
812         }
813 }
814
815 static void
816 get_property (GObject *object, guint prop_id,
817               GValue *value, GParamSpec *pspec)
818 {
819         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
820
821         switch (prop_id) {
822         case PROP_DBUS_SERVICE_NAME:
823                 g_value_set_string (value, priv->dbus_service_name);
824                 break;
825         case PROP_STATE:
826                 g_value_set_uint (value, nm_vpn_plugin_get_state (NM_VPN_PLUGIN (object)));
827                 break;
828         default:
829                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
830                 break;
831         }
832 }
833
834 static void
835 dispose (GObject *object)
836 {
837         NMVPNPlugin *plugin = NM_VPN_PLUGIN (object);
838         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
839         NMVPNServiceState state;
840         GError *err = NULL;
841
842         nm_clear_g_source (&priv->fail_stop_id);
843         nm_clear_g_source (&priv->quit_timer);
844         nm_clear_g_source (&priv->connect_timer);
845
846         state = nm_vpn_plugin_get_state (plugin);
847
848         if (state == NM_VPN_SERVICE_STATE_STARTED ||
849             state == NM_VPN_SERVICE_STATE_STARTING)
850                 nm_vpn_plugin_disconnect (plugin, &err);
851
852         if (err) {
853                 g_warning ("Error disconnecting VPN connection: %s", err->message);
854                 g_error_free (err);
855         }
856
857         G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->dispose (object);
858 }
859
860 static void
861 finalize (GObject *object)
862 {
863         NMVPNPlugin *plugin = NM_VPN_PLUGIN (object);
864         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
865
866         nm_vpn_plugin_set_connection (plugin, NULL);
867         g_free (priv->dbus_service_name);
868
869         if (G_IS_VALUE (&priv->banner))
870                 g_value_unset (&priv->banner);
871         if (G_IS_VALUE (&priv->tundev))
872                 g_value_unset (&priv->tundev);
873         if (G_IS_VALUE (&priv->gateway))
874                 g_value_unset (&priv->gateway);
875         if (G_IS_VALUE (&priv->mtu))
876                 g_value_unset (&priv->mtu);
877
878         G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->finalize (object);
879 }
880
881 static void
882 state_changed (NMVPNPlugin *plugin, NMVPNServiceState state)
883 {
884         NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
885
886         switch (state) {
887         case NM_VPN_SERVICE_STATE_STARTING:
888                 nm_clear_g_source (&priv->quit_timer);
889                 nm_clear_g_source (&priv->fail_stop_id);
890                 break;
891         case NM_VPN_SERVICE_STATE_STOPPED:
892                 schedule_quit_timer (plugin);
893                 break;
894         default:
895                 /* Clean up all timers we might have set up. */
896                 nm_clear_g_source (&priv->connect_timer);
897                 nm_clear_g_source (&priv->quit_timer);
898                 nm_clear_g_source (&priv->fail_stop_id);
899                 break;
900         }
901 }
902
903 static void
904 nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class)
905 {
906         GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
907
908         g_type_class_add_private (object_class, sizeof (NMVPNPluginPrivate));
909
910         dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (plugin_class),
911                                          &dbus_glib_nm_vpn_plugin_object_info);
912
913         /* virtual methods */
914         object_class->constructor  = constructor;
915         object_class->set_property = set_property;
916         object_class->get_property = get_property;
917         object_class->dispose      = dispose;
918         object_class->finalize     = finalize;
919
920         plugin_class->state_changed = state_changed;
921
922         /* properties */
923
924         /**
925          * NMVPNPlugin:service-name:
926          *
927          * The D-Bus service name of this plugin.
928          */
929         g_object_class_install_property
930                 (object_class, PROP_DBUS_SERVICE_NAME,
931                  g_param_spec_string (NM_VPN_PLUGIN_DBUS_SERVICE_NAME, "", "",
932                                       NULL,
933                                       G_PARAM_READWRITE |
934                                       G_PARAM_CONSTRUCT_ONLY |
935                                       G_PARAM_STATIC_STRINGS));
936
937         /**
938          * NMVPNPlugin:state:
939          *
940          * The state of the plugin.
941          */
942         g_object_class_install_property
943                 (object_class, PROP_STATE,
944                  g_param_spec_uint (NM_VPN_PLUGIN_STATE, "", "",
945                                     NM_VPN_SERVICE_STATE_UNKNOWN,
946                                     NM_VPN_SERVICE_STATE_STOPPED,
947                                     NM_VPN_SERVICE_STATE_INIT,
948                                     G_PARAM_READWRITE |
949                                     G_PARAM_STATIC_STRINGS));
950
951         /* signals */
952         signals[STATE_CHANGED] =
953                 g_signal_new ("state-changed",
954                               G_OBJECT_CLASS_TYPE (object_class),
955                               G_SIGNAL_RUN_FIRST,
956                               G_STRUCT_OFFSET (NMVPNPluginClass, state_changed),
957                               NULL, NULL,
958                               g_cclosure_marshal_VOID__UINT,
959                               G_TYPE_NONE, 1,
960                               G_TYPE_UINT);
961
962         signals[SECRETS_REQUIRED] =
963                 g_signal_new ("secrets-required",
964                               G_OBJECT_CLASS_TYPE (object_class),
965                               G_SIGNAL_RUN_FIRST,
966                               0, NULL, NULL,
967                               NULL,
968                               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV);
969
970         signals[CONFIG] =
971                 g_signal_new ("config",
972                               G_OBJECT_CLASS_TYPE (object_class),
973                               G_SIGNAL_RUN_FIRST,
974                               G_STRUCT_OFFSET (NMVPNPluginClass, config),
975                               NULL, NULL,
976                               g_cclosure_marshal_VOID__BOXED,
977                               G_TYPE_NONE, 1,
978                               DBUS_TYPE_G_MAP_OF_VARIANT);
979
980         signals[IP4_CONFIG] =
981                 g_signal_new ("ip4-config",
982                               G_OBJECT_CLASS_TYPE (object_class),
983                               G_SIGNAL_RUN_FIRST,
984                               G_STRUCT_OFFSET (NMVPNPluginClass, ip4_config),
985                               NULL, NULL,
986                               g_cclosure_marshal_VOID__BOXED,
987                               G_TYPE_NONE, 1,
988                               DBUS_TYPE_G_MAP_OF_VARIANT);
989
990         signals[IP6_CONFIG] =
991                 g_signal_new ("ip6-config",
992                               G_OBJECT_CLASS_TYPE (object_class),
993                               G_SIGNAL_RUN_FIRST,
994                               G_STRUCT_OFFSET (NMVPNPluginClass, ip6_config),
995                               NULL, NULL,
996                               g_cclosure_marshal_VOID__BOXED,
997                               G_TYPE_NONE, 1,
998                               DBUS_TYPE_G_MAP_OF_VARIANT);
999
1000         signals[LOGIN_BANNER] =
1001                 g_signal_new ("login-banner",
1002                               G_OBJECT_CLASS_TYPE (object_class),
1003                               G_SIGNAL_RUN_FIRST,
1004                               G_STRUCT_OFFSET (NMVPNPluginClass, login_banner),
1005                               NULL, NULL,
1006                               g_cclosure_marshal_VOID__STRING,
1007                               G_TYPE_NONE, 1,
1008                               G_TYPE_STRING);
1009
1010         signals[FAILURE] =
1011                 g_signal_new ("failure",
1012                               G_OBJECT_CLASS_TYPE (object_class),
1013                               G_SIGNAL_RUN_FIRST,
1014                               G_STRUCT_OFFSET (NMVPNPluginClass, failure),
1015                               NULL, NULL,
1016                               g_cclosure_marshal_VOID__UINT,
1017                               G_TYPE_NONE, 1,
1018                               G_TYPE_UINT);
1019
1020         signals[QUIT] =
1021                 g_signal_new ("quit",
1022                               G_OBJECT_CLASS_TYPE (object_class),
1023                               G_SIGNAL_RUN_FIRST,
1024                               G_STRUCT_OFFSET (NMVPNPluginClass, quit),
1025                               NULL, NULL,
1026                               g_cclosure_marshal_VOID__VOID,
1027                               G_TYPE_NONE, 0,
1028                               G_TYPE_NONE);
1029
1030         dbus_g_error_domain_register (NM_VPN_PLUGIN_ERROR,
1031                                       NM_DBUS_VPN_ERROR_PREFIX,
1032                                       NM_TYPE_VPN_PLUGIN_ERROR);
1033
1034         setup_unix_signal_handler ();
1035 }