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