From 8cfe8d10ba712bf879cd2d2dfc9766a02895f79e Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 11 Jan 2013 11:07:46 -0500 Subject: [PATCH] network: add VPN support to the connection editor Unfortunately, the VPN plugins provide their own .ui files for their editor pages, so we can't make them look competely GNOME-3-ish. But the code does try to fix them up a little bit by realigning the labels. vpn-helpers.[ch] is nearly identical to network-manager-applet's, but eventually this code will move into libnm-gtk. https://bugzilla.gnome.org/show_bug.cgi?id=691285 --- configure.ac | 10 +- panels/network/cc-network-panel.c | 1 + panels/network/connection-editor/Makefile.am | 10 +- .../connection-editor/ce-page-details.c | 4 +- .../network/connection-editor/ce-page-vpn.c | 188 ++++++++ .../network/connection-editor/ce-page-vpn.h | 74 ++++ .../connection-editor.gresource.xml | 1 + .../connection-editor/net-connection-editor.c | 6 +- .../network/connection-editor/vpn-helpers.c | 414 ++++++++++++++++++ .../network/connection-editor/vpn-helpers.h | 44 ++ panels/network/connection-editor/vpn-page.ui | 70 +++ panels/network/net-vpn.c | 38 +- panels/network/network-vpn.ui | 8 +- 13 files changed, 848 insertions(+), 20 deletions(-) create mode 100644 panels/network/connection-editor/ce-page-vpn.c create mode 100644 panels/network/connection-editor/ce-page-vpn.h create mode 100644 panels/network/connection-editor/vpn-helpers.c create mode 100644 panels/network/connection-editor/vpn-helpers.h create mode 100644 panels/network/connection-editor/vpn-page.ui diff --git a/configure.ac b/configure.ac index 9d9146abd..9b51cb027 100644 --- a/configure.ac +++ b/configure.ac @@ -99,7 +99,7 @@ CANBERRA_REQUIRED_VERSION=0.13 GDKPIXBUF_REQUIRED_VERSION=2.23.0 POLKIT_REQUIRED_VERSION=0.103 GSD_REQUIRED_VERSION=3.7.3 -NETWORK_MANAGER_REQUIRED_VERSION=0.9.6.4 +NETWORK_MANAGER_REQUIRED_VERSION=0.9.7.995 NETWORK_MANAGER_APPLET_REQUIRED_VERSION=0.9.7 LIBNOTIFY_REQUIRED_VERSION=0.7.3 GNOME_DESKTOP_REQUIRED_VERSION=3.7.5 @@ -179,20 +179,24 @@ AM_CONDITIONAL(HAVE_INTROSPECTION, false) GDESKTOP_PREFIX=`$PKG_CONFIG --variable prefix gsettings-desktop-schemas` AC_SUBST(GDESKTOP_PREFIX) -# Check for NetworkManager ~0.9 PKG_CHECK_MODULES(NETWORK_MANAGER, NetworkManager >= $NETWORK_MANAGER_REQUIRED_VERSION libnm-glib >= $NETWORK_MANAGER_REQUIRED_VERSION + libnm-glib-vpn >= $NETWORK_MANAGER_REQUIRED_VERSION libnm-util >= $NETWORK_MANAGER_REQUIRED_VERSION libnm-gtk >= $NETWORK_MANAGER_APPLET_REQUIRED_VERSION, [have_networkmanager=yes], have_networkmanager=no) if test "x$have_networkmanager" = xno ; then - AC_MSG_WARN(*** Network panel will not be built (NetworkManager ~0.9 or newer not found) ***) + AC_MSG_WARN(*** Network panel will not be built (NetworkManager $NETWORK_MANAGER_REQUIRED_VERSION or newer not found) ***) else AC_DEFINE(BUILD_NETWORK, 1, [Define to 1 to build the Network panel]) fi AM_CONDITIONAL(BUILD_NETWORK, [test x$have_networkmanager = xyes]) if test x${have_networkmanager} = xyes; then AC_DEFINE(HAVE_NETWORK_MANAGER, 1, [Define to 1 if NetworkManager is available]) + NM_VPN_CONFIG_DIR=`$PKG_CONFIG --variable configdir NetworkManager`/VPN + NM_VPN_MODULE_DIR=`$PKG_CONFIG --variable plugindir NetworkManager` + AC_SUBST(NM_VPN_CONFIG_DIR) + AC_SUBST(NM_VPN_MODULE_DIR) fi # Check for gnome-bluetooth diff --git a/panels/network/cc-network-panel.c b/panels/network/cc-network-panel.c index 0091e4d0f..6526404c4 100644 --- a/panels/network/cc-network-panel.c +++ b/panels/network/cc-network-panel.c @@ -1020,6 +1020,7 @@ panel_add_vpn_device (CcNetworkPanel *panel, NMConnection *connection) "id", id, "connection", connection, "client", panel->priv->client, + "remote-settings", panel->priv->remote_settings, NULL); g_signal_connect_object (net_vpn, "removed", G_CALLBACK (object_removed_cb), panel, 0); diff --git a/panels/network/connection-editor/Makefile.am b/panels/network/connection-editor/Makefile.am index db1c15031..de8bce484 100644 --- a/panels/network/connection-editor/Makefile.am +++ b/panels/network/connection-editor/Makefile.am @@ -25,12 +25,18 @@ libconnection_editor_la_SOURCES = \ ce-page-ethernet.h \ ce-page-ethernet.c \ ce-page-8021x-security.h \ - ce-page-8021x-security.c + ce-page-8021x-security.c \ + ce-page-vpn.h \ + ce-page-vpn.c \ + vpn-helpers.h \ + vpn-helpers.c libconnection_editor_la_CPPFLAGS = \ -I$(srcdir)/../wireless-security \ $(NETWORK_PANEL_CFLAGS) \ - $(NETWORK_MANAGER_CFLAGS) + $(NETWORK_MANAGER_CFLAGS) \ + -DNM_VPN_CONFIG_DIR=\""$(NM_VPN_CONFIG_DIR)"\" \ + -DNM_VPN_MODULE_DIR=\""$(NM_VPN_MODULE_DIR)"\" libconnection_editor_la_LIBADD = \ $(builddir)/../wireless-security/libwireless-security.la \ diff --git a/panels/network/connection-editor/ce-page-details.c b/panels/network/connection-editor/ce-page-details.c index 5bb05d843..6f62416f5 100644 --- a/panels/network/connection-editor/ce-page-details.c +++ b/panels/network/connection-editor/ce-page-details.c @@ -128,7 +128,7 @@ connect_details_page (CEPageDetails *page) else active_ap = NULL; - state = nm_device_get_state (page->device); + state = page->device ? nm_device_get_state (page->device) : NM_DEVICE_STATE_DISCONNECTED; device_is_active = FALSE; speed = 0; @@ -136,7 +136,7 @@ connect_details_page (CEPageDetails *page) device_is_active = TRUE; if (NM_IS_DEVICE_WIFI (page->device)) speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (page->device)) / 1000; - } else { + } else if (page->device) { NMActiveConnection *ac; const gchar *p1, *p2; diff --git a/panels/network/connection-editor/ce-page-vpn.c b/panels/network/connection-editor/ce-page-vpn.c new file mode 100644 index 000000000..1eed050ea --- /dev/null +++ b/panels/network/connection-editor/ce-page-vpn.c @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Red Hat, Inc + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include + +#include + +#include "ce-page-vpn.h" +#include "vpn-helpers.h" + +G_DEFINE_TYPE (CEPageVpn, ce_page_vpn, CE_TYPE_PAGE) + +/* Hack to make the plugin-provided editor widget fit in better with + * the control center by changing + * + * Foo: [__________] + * Bar baz: [__________] + * + * to + * + * Foo [__________] + * Bar baz [__________] + */ +static void +vpn_gnome3ify_editor (GtkWidget *widget) +{ + if (GTK_IS_CONTAINER (widget)) { + GList *children, *iter; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + for (iter = children; iter; iter = iter->next) + vpn_gnome3ify_editor (iter->data); + g_list_free (children); + } else if (GTK_IS_LABEL (widget)) { + const char *text; + gfloat xalign, yalign; + char *newtext; + int len; + + gtk_misc_get_alignment (GTK_MISC (widget), &xalign, &yalign); + if (xalign != 0.0) + return; + text = gtk_label_get_text (GTK_LABEL (widget)); + len = strlen (text); + if (len < 2 || text[len - 1] != ':') + return; + + newtext = g_strndup (text, len - 1); + gtk_label_set_text (GTK_LABEL (widget), newtext); + g_free (newtext); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, yalign); + } +} + +static void +load_vpn_plugin (CEPageVpn *page, NMConnection *connection) +{ + CEPage *parent = CE_PAGE (page); + GtkWidget *ui_widget, *failure; + + page->ui = nm_vpn_plugin_ui_interface_ui_factory (page->plugin, connection, NULL); + if (!page->ui) { + page->plugin = NULL; + return; + } + ui_widget = GTK_WIDGET (nm_vpn_plugin_ui_widget_interface_get_widget (page->ui)); + if (!ui_widget) { + g_clear_object (&page->ui); + page->plugin = NULL; + return; + } + vpn_gnome3ify_editor (ui_widget); + + failure = GTK_WIDGET (gtk_builder_get_object (parent->builder, "failure_label")); + gtk_widget_destroy (failure); + + gtk_box_pack_start (page->box, ui_widget, TRUE, TRUE, 0); + gtk_widget_show_all (ui_widget); + + g_signal_connect_swapped (page->ui, "changed", G_CALLBACK (ce_page_changed), page); +} + +static void +connect_vpn_page (CEPageVpn *page) +{ + const gchar *name; + + name = nm_setting_connection_get_id (page->setting_connection); + gtk_entry_set_text (page->name, name); + g_signal_connect_swapped (page->name, "changed", G_CALLBACK (ce_page_changed), page); +} + +static gboolean +validate (CEPage *page, + NMConnection *connection, + GError **error) +{ + CEPageVpn *self = CE_PAGE_VPN (page); + + g_object_set (self->setting_connection, + NM_SETTING_CONNECTION_ID, gtk_entry_get_text (self->name), + NULL); + if (!nm_setting_verify (NM_SETTING (self->setting_connection), NULL, error)) + return FALSE; + + if (!self->ui) + return TRUE; + + return nm_vpn_plugin_ui_widget_interface_update_connection (self->ui, connection, error); +} + +static void +ce_page_vpn_init (CEPageVpn *page) +{ +} + +static void +dispose (GObject *object) +{ + CEPageVpn *page = CE_PAGE_VPN (object); + + g_clear_object (&page->ui); + + G_OBJECT_CLASS (ce_page_vpn_parent_class)->dispose (object); +} + +static void +ce_page_vpn_class_init (CEPageVpnClass *class) +{ + CEPageClass *page_class = CE_PAGE_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = dispose; + + page_class->validate = validate; +} + +CEPage * +ce_page_vpn_new (NMConnection *connection, + NMClient *client, + NMRemoteSettings *settings) +{ + CEPageVpn *page; + const char *vpn_type; + + page = CE_PAGE_VPN (ce_page_new (CE_TYPE_PAGE_VPN, + connection, + client, + settings, + "/org/gnome/control-center/network/vpn-page.ui", + _("Identity"))); + + page->name = GTK_ENTRY (gtk_builder_get_object (CE_PAGE (page)->builder, "entry_name")); + page->box = GTK_BOX (gtk_builder_get_object (CE_PAGE (page)->builder, "page")); + + page->setting_connection = nm_connection_get_setting_connection (connection); + page->setting_vpn = nm_connection_get_setting_vpn (connection); + vpn_type = nm_setting_vpn_get_service_type (page->setting_vpn); + + page->plugin = vpn_get_plugin_by_service (vpn_type); + if (page->plugin) + load_vpn_plugin (page, connection); + + connect_vpn_page (page); + + return CE_PAGE (page); +} diff --git a/panels/network/connection-editor/ce-page-vpn.h b/panels/network/connection-editor/ce-page-vpn.h new file mode 100644 index 000000000..dfed60a36 --- /dev/null +++ b/panels/network/connection-editor/ce-page-vpn.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more vpn. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CE_PAGE_VPN_H +#define __CE_PAGE_VPN_H + +#include + +#include +#define NM_VPN_API_SUBJECT_TO_CHANGE +#include + +#include +#include "ce-page.h" + +G_BEGIN_DECLS + +#define CE_TYPE_PAGE_VPN (ce_page_vpn_get_type ()) +#define CE_PAGE_VPN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CE_TYPE_PAGE_VPN, CEPageVpn)) +#define CE_PAGE_VPN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CE_TYPE_PAGE_VPN, CEPageVpnClass)) +#define CE_IS_PAGE_VPN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CE_TYPE_PAGE_VPN)) +#define CE_IS_PAGE_VPN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CE_TYPE_PAGE_VPN)) +#define CE_PAGE_VPN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CE_TYPE_PAGE_VPN, CEPageVpnClass)) + +typedef struct _CEPageVpn CEPageVpn; +typedef struct _CEPageVpnClass CEPageVpnClass; + +struct _CEPageVpn +{ + CEPage parent; + + NMSettingConnection *setting_connection; + NMSettingVPN *setting_vpn; + + GtkEntry *name; + GtkBox *box; + + NMVpnPluginUiInterface *plugin; + NMVpnPluginUiWidgetInterface *ui; +}; + +struct _CEPageVpnClass +{ + CEPageClass parent_class; +}; + +GType ce_page_vpn_get_type (void); + +CEPage *ce_page_vpn_new (NMConnection *connection, + NMClient *client, + NMRemoteSettings *settings); + +G_END_DECLS + +#endif /* __CE_PAGE_VPN_H */ + diff --git a/panels/network/connection-editor/connection-editor.gresource.xml b/panels/network/connection-editor/connection-editor.gresource.xml index 74bf933e8..e9ad7d9ed 100644 --- a/panels/network/connection-editor/connection-editor.gresource.xml +++ b/panels/network/connection-editor/connection-editor.gresource.xml @@ -9,6 +9,7 @@ ip6-page.ui reset-page.ui security-page.ui + vpn-page.ui wifi-page.ui diff --git a/panels/network/connection-editor/net-connection-editor.c b/panels/network/connection-editor/net-connection-editor.c index e889e3ad8..812b8a14d 100644 --- a/panels/network/connection-editor/net-connection-editor.c +++ b/panels/network/connection-editor/net-connection-editor.c @@ -37,6 +37,7 @@ #include "ce-page-reset.h" #include "ce-page-ethernet.h" #include "ce-page-8021x-security.h" +#include "ce-page-vpn.h" #include "egg-list-box/egg-list-box.h" @@ -457,6 +458,8 @@ net_connection_editor_set_connection (NetConnectionEditor *editor, add_page (editor, ce_page_wifi_new (editor->connection, editor->client, editor->settings)); else if (strcmp (type, NM_SETTING_WIRED_SETTING_NAME) == 0) add_page (editor, ce_page_ethernet_new (editor->connection, editor->client, editor->settings)); + else if (strcmp (type, NM_SETTING_VPN_SETTING_NAME) == 0) + add_page (editor, ce_page_vpn_new (editor->connection, editor->client, editor->settings)); add_page (editor, ce_page_ip4_new (editor->connection, editor->client, editor->settings)); add_page (editor, ce_page_ip6_new (editor->connection, editor->client, editor->settings)); @@ -514,7 +517,8 @@ net_connection_editor_new (GtkWindow *parent_window, } if (ap) editor->ap = g_object_ref (ap); - editor->device = g_object_ref (device); + if (device) + editor->device = g_object_ref (device); editor->client = g_object_ref (client); editor->settings = g_object_ref (settings); diff --git a/panels/network/connection-editor/vpn-helpers.c b/panels/network/connection-editor/vpn-helpers.c new file mode 100644 index 000000000..5be39adc4 --- /dev/null +++ b/panels/network/connection-editor/vpn-helpers.c @@ -0,0 +1,414 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "vpn-helpers.h" + +#define NM_VPN_API_SUBJECT_TO_CHANGE +#include "nm-vpn-plugin-ui-interface.h" + +static GHashTable *plugins = NULL; + +NMVpnPluginUiInterface * +vpn_get_plugin_by_service (const char *service) +{ + g_return_val_if_fail (service != NULL, NULL); + + if (!plugins) { + vpn_get_plugins (NULL); + if (!plugins) + return NULL; + } + return g_hash_table_lookup (plugins, service); +} + +GHashTable * +vpn_get_plugins (GError **error) +{ + GDir *dir; + const char *f; + + if (error) + g_return_val_if_fail (*error == NULL, NULL); + + if (plugins) + return plugins; + + dir = g_dir_open (NM_VPN_CONFIG_DIR, 0, error); + if (!dir) + return NULL; + + plugins = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); + + while ((f = g_dir_read_name (dir))) { + char *path = NULL, *service = NULL; + char *so_path = NULL, *so_name = NULL; + GKeyFile *keyfile = NULL; + GModule *module; + NMVpnPluginUiFactory factory = NULL; + + if (!g_str_has_suffix (f, ".name")) + continue; + + path = g_strdup_printf ("%s/%s", NM_VPN_CONFIG_DIR, f); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, 0, NULL)) + goto next; + + service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL); + if (!service) + goto next; + + so_path = g_key_file_get_string (keyfile, "GNOME", "properties", NULL); + if (!so_path) + goto next; + + /* Remove any path and extension components, then reconstruct path + * to the SO in LIBDIR + */ + so_name = g_path_get_basename (so_path); + g_free (so_path); + so_path = g_build_filename (NM_VPN_MODULE_DIR, so_name, NULL); + g_free (so_name); + + module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!module) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot load the VPN plugin which provides the " + "service '%s'.", service); + goto next; + } + + if (g_module_symbol (module, "nm_vpn_plugin_ui_factory", (gpointer) &factory)) { + NMVpnPluginUiInterface *plugin; + GError *factory_error = NULL; + gboolean success = FALSE; + + plugin = factory (&factory_error); + if (plugin) { + char *plug_name = NULL, *plug_service = NULL; + + /* Validate plugin properties */ + g_object_get (G_OBJECT (plugin), + NM_VPN_PLUGIN_UI_INTERFACE_NAME, &plug_name, + NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, &plug_service, + NULL); + if (!plug_name || !strlen (plug_name)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot load VPN plugin in '%s': missing plugin name", + g_module_name (module)); + } else if (!plug_service || strcmp (plug_service, service)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot load VPN plugin in '%s': invalid service name", + g_module_name (module)); + } else { + /* Success! */ + g_object_set_data_full (G_OBJECT (plugin), "gmodule", module, + (GDestroyNotify) g_module_close); + g_hash_table_insert (plugins, g_strdup (service), plugin); + success = TRUE; + } + g_free (plug_name); + g_free (plug_service); + } else { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot load VPN plugin in '%s': %s", + g_module_name (module), g_module_error ()); + } + + if (!success) + g_module_close (module); + } else { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot locate nm_vpn_plugin_ui_factory() in '%s': %s", + g_module_name (module), g_module_error ()); + g_module_close (module); + } + + next: + g_free (so_path); + g_free (service); + g_key_file_free (keyfile); + g_free (path); + } + g_dir_close (dir); + + return plugins; +} + +typedef struct { + VpnImportSuccessCallback callback; + gpointer user_data; +} ActionInfo; + +static void +import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + char *filename = NULL; + ActionInfo *info = (ActionInfo *) user_data; + GHashTableIter iter; + gpointer key; + NMVpnPluginUiInterface *plugin; + NMConnection *connection = NULL; + GError *error = NULL; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_warning ("%s: didn't get a filename back from the chooser!", __func__); + goto out; + } + + g_hash_table_iter_init (&iter, plugins); + while (!connection && g_hash_table_iter_next (&iter, &key, (gpointer *)&plugin)) { + g_clear_error (&error); + connection = nm_vpn_plugin_ui_interface_import (plugin, filename, &error); + } + + if (connection) + info->callback (connection, info->user_data); + else { + GtkWidget *err_dialog; + char *bname = g_path_get_basename (filename); + + err_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot import VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The file '%s' could not be read or does not contain recognized VPN connection information\n\nError: %s."), + bname, error ? error->message : "unknown error"); + g_free (bname); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show_all (err_dialog); + gtk_window_present (GTK_WINDOW (err_dialog)); + } + + g_clear_error (&error); + g_free (filename); + +out: + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); + g_free (info); +} + +static void +destroy_import_chooser (GtkWidget *dialog, gpointer user_data) +{ + g_free (user_data); + gtk_widget_destroy (dialog); +} + +void +vpn_import (VpnImportSuccessCallback callback, gpointer user_data) +{ + GtkWidget *dialog; + ActionInfo *info; + const char *home_folder; + + dialog = gtk_file_chooser_dialog_new (_("Select file to import"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + info = g_malloc0 (sizeof (ActionInfo)); + info->callback = callback; + info->user_data = user_data; + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (destroy_import_chooser), info); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + NMConnection *connection = NM_CONNECTION (user_data); + char *filename = NULL; + GError *error = NULL; + NMVpnPluginUiInterface *plugin; + NMSettingConnection *s_con = NULL; + NMSettingVPN *s_vpn = NULL; + const char *service_type; + const char *id = NULL; + gboolean success = FALSE; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "no filename"); + goto done; + } + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + int replace_response; + GtkWidget *replace_dialog; + char *bname; + + bname = g_path_get_basename (filename); + replace_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("A file named \"%s\" already exists."), + bname); + gtk_dialog_add_buttons (GTK_DIALOG (replace_dialog), _("_Replace"), GTK_RESPONSE_OK, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (replace_dialog), + _("Do you want to replace %s with the VPN connection you are saving?"), bname); + g_free (bname); + replace_response = gtk_dialog_run (GTK_DIALOG (replace_dialog)); + gtk_widget_destroy (replace_dialog); + if (replace_response != GTK_RESPONSE_OK) + goto out; + } + + s_con = nm_connection_get_setting_connection (connection); + id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (!id) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "connection setting invalid"); + goto done; + } + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "VPN setting invalid"); + goto done; + } + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) + success = nm_vpn_plugin_ui_interface_export (plugin, filename, connection, &error); + +done: + if (!success) { + GtkWidget *err_dialog; + char *bname = filename ? g_path_get_basename (filename) : g_strdup ("(none)"); + + err_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot export VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The VPN connection '%s' could not be exported to %s.\n\nError: %s."), + id ? id : "(unknown)", bname, error ? error->message : "unknown error"); + g_free (bname); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show_all (err_dialog); + gtk_window_present (GTK_WINDOW (err_dialog)); + } + +out: + if (error) + g_error_free (error); + g_object_unref (connection); + + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); +} + +void +vpn_export (NMConnection *connection) +{ + GtkWidget *dialog; + NMVpnPluginUiInterface *plugin; + NMSettingVPN *s_vpn = NULL; + const char *service_type; + const char *home_folder; + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_warning ("%s: invalid VPN connection!", __func__); + return; + } + + dialog = gtk_file_chooser_dialog_new (_("Export VPN connection..."), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) { + char *suggested = NULL; + + suggested = nm_vpn_plugin_ui_interface_get_suggested_name (plugin, connection); + if (suggested) { + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested); + g_free (suggested); + } + } + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection)); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} + +gboolean +vpn_supports_ipv6 (NMConnection *connection) +{ + NMSettingVPN *s_vpn; + const char *service_type; + NMVpnPluginUiInterface *plugin; + guint32 capabilities; + + s_vpn = nm_connection_get_setting_vpn (connection); + g_return_val_if_fail (s_vpn != NULL, FALSE); + + service_type = nm_setting_vpn_get_service_type (s_vpn); + g_return_val_if_fail (service_type != NULL, FALSE); + + plugin = vpn_get_plugin_by_service (service_type); + g_return_val_if_fail (plugin != NULL, FALSE); + + capabilities = nm_vpn_plugin_ui_interface_get_capabilities (plugin); + return (capabilities & NM_VPN_PLUGIN_UI_CAPABILITY_IPV6) != 0; +} diff --git a/panels/network/connection-editor/vpn-helpers.h b/panels/network/connection-editor/vpn-helpers.h new file mode 100644 index 000000000..d14fc8fe9 --- /dev/null +++ b/panels/network/connection-editor/vpn-helpers.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#ifndef _VPN_HELPERS_H_ +#define _VPN_HELPERS_H_ + +#include +#include +#include + +#define NM_VPN_API_SUBJECT_TO_CHANGE +#include + +GHashTable *vpn_get_plugins (GError **error); + +NMVpnPluginUiInterface *vpn_get_plugin_by_service (const char *service); + +typedef void (*VpnImportSuccessCallback) (NMConnection *connection, gpointer user_data); +void vpn_import (VpnImportSuccessCallback callback, gpointer user_data); + +void vpn_export (NMConnection *connection); + +gboolean vpn_supports_ipv6 (NMConnection *connection); + +#endif /* _VPN_HELPERS_H_ */ diff --git a/panels/network/connection-editor/vpn-page.ui b/panels/network/connection-editor/vpn-page.ui new file mode 100644 index 000000000..050bc0b07 --- /dev/null +++ b/panels/network/connection-editor/vpn-page.ui @@ -0,0 +1,70 @@ + + + + + True + False + 50 + 50 + 12 + 12 + vertical + 10 + + + True + False + 6 + + + True + False + 1 + _Name + True + + + False + True + 0 + + + + + True + True + + True + + + True + True + 1 + + + + + False + True + 0 + + + + + True + False + 0 + (Error: unable to load VPN connection editor) + + + + + + False + True + 1 + + + + + diff --git a/panels/network/net-vpn.c b/panels/network/net-vpn.c index 7cfaa7a60..61ed8cc16 100644 --- a/panels/network/net-vpn.c +++ b/panels/network/net-vpn.c @@ -31,6 +31,8 @@ #include "nm-remote-connection.h" #include "nm-setting-vpn.h" +#include "connection-editor/net-connection-editor.h" + #define NET_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NET_TYPE_VPN, NetVpnPrivate)) struct _NetVpnPrivate @@ -381,22 +383,36 @@ edit_connection (GtkButton *button, NetVpn *vpn) net_object_edit (NET_OBJECT (vpn)); } +static void +editor_done (NetConnectionEditor *editor, + gboolean success, + NetVpn *vpn) +{ + g_object_unref (editor); + net_object_refresh (NET_OBJECT (vpn)); +} + static void vpn_proxy_edit (NetObject *object) { - const gchar *uuid; - gchar *cmdline; - GError *error = NULL; NetVpn *vpn = NET_VPN (object); + GtkWidget *button, *window; + NetConnectionEditor *editor; + NMClient *client; + NMRemoteSettings *settings; - uuid = nm_connection_get_uuid (vpn->priv->connection); - cmdline = g_strdup_printf ("nm-connection-editor --edit %s", uuid); - g_debug ("Launching '%s'\n", cmdline); - if (!g_spawn_command_line_async (cmdline, &error)) { - g_warning ("Failed to launch nm-connection-editor: %s", error->message); - g_error_free (error); - } - g_free (cmdline); + button = GTK_WIDGET (gtk_builder_get_object (vpn->priv->builder, + "button_options")); + window = gtk_widget_get_toplevel (button); + + client = net_object_get_client (object); + settings = net_object_get_remote_settings (object); + + editor = net_connection_editor_new (GTK_WINDOW (window), + vpn->priv->connection, + NULL, NULL, client, settings); + g_signal_connect (editor, "done", G_CALLBACK (editor_done), vpn); + net_connection_editor_run (editor); } /** diff --git a/panels/network/network-vpn.ui b/panels/network/network-vpn.ui index 8995a5c21..565461cc8 100644 --- a/panels/network/network-vpn.ui +++ b/panels/network/network-vpn.ui @@ -295,7 +295,6 @@ False - _Configure… False True True @@ -306,6 +305,13 @@ True True 1 + + + True + False + emblem-system-symbolic + + False