2013-01-11 11:07:46 -05:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
|
|
/* NetworkManager Connection editor -- Connection editor for NetworkManager
|
|
|
|
*
|
|
|
|
* Dan Williams <dcbw@redhat.com>
|
|
|
|
*
|
|
|
|
* 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 <string.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <gmodule.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
|
|
|
|
#include <nm-connection.h>
|
|
|
|
#include <nm-setting-connection.h>
|
|
|
|
#include <nm-setting-vpn.h>
|
|
|
|
|
|
|
|
#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 {
|
2013-01-09 17:41:48 -05:00
|
|
|
VpnImportCallback callback;
|
2013-01-11 11:07:46 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-01-09 17:41:48 -05:00
|
|
|
if (!connection) {
|
2013-01-11 11:07:46 -05:00
|
|
|
GtkWidget *err_dialog;
|
|
|
|
char *bname = g_path_get_basename (filename);
|
|
|
|
|
2013-01-09 17:41:48 -05:00
|
|
|
err_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
|
|
|
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
2013-01-11 11:07:46 -05:00
|
|
|
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);
|
2013-01-09 17:41:48 -05:00
|
|
|
gtk_dialog_run (GTK_DIALOG (err_dialog));
|
2013-01-11 11:07:46 -05:00
|
|
|
}
|
|
|
|
g_clear_error (&error);
|
|
|
|
g_free (filename);
|
|
|
|
|
|
|
|
out:
|
|
|
|
gtk_widget_hide (dialog);
|
|
|
|
gtk_widget_destroy (dialog);
|
2013-01-09 17:41:48 -05:00
|
|
|
|
|
|
|
info->callback (connection, info->user_data);
|
2013-01-11 11:07:46 -05:00
|
|
|
g_free (info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy_import_chooser (GtkWidget *dialog, gpointer user_data)
|
|
|
|
{
|
2013-01-09 17:41:48 -05:00
|
|
|
ActionInfo *info = (ActionInfo *) user_data;
|
|
|
|
|
2013-01-11 11:07:46 -05:00
|
|
|
gtk_widget_destroy (dialog);
|
2013-01-09 17:41:48 -05:00
|
|
|
info->callback (NULL, info->user_data);
|
|
|
|
g_free (info);
|
2013-01-11 11:07:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-01-09 17:41:48 -05:00
|
|
|
vpn_import (GtkWindow *parent, VpnImportCallback callback, gpointer user_data)
|
2013-01-11 11:07:46 -05:00
|
|
|
{
|
|
|
|
GtkWidget *dialog;
|
|
|
|
ActionInfo *info;
|
|
|
|
const char *home_folder;
|
|
|
|
|
|
|
|
dialog = gtk_file_chooser_dialog_new (_("Select file to import"),
|
2013-01-09 17:41:48 -05:00
|
|
|
parent,
|
2013-01-11 11:07:46 -05:00
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
2013-07-19 15:56:26 +03:00
|
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
|
|
_("_Open"), GTK_RESPONSE_ACCEPT,
|
2013-01-11 11:07:46 -05:00
|
|
|
NULL);
|
2013-01-09 17:41:48 -05:00
|
|
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
2013-01-11 11:07:46 -05:00
|
|
|
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,
|
2013-07-19 15:56:26 +03:00
|
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
|
|
_("_Save"), GTK_RESPONSE_ACCEPT,
|
2013-01-11 11:07:46 -05:00
|
|
|
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;
|
|
|
|
}
|