Previously, when a list of VPN plugins was loaded once, it will not load again the next time you try to add a VPN connnection. This causes an issue that if you keep gnome-control-center open and install a new VPN plugin, the new VPN plugin won't show up in the VPN plugin list. Fix that by always load all avilable VPN plugins when showing the "Add VPN" dialog.
358 lines
12 KiB
C
358 lines
12 KiB
C
/* -*- 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 <NetworkManager.h>
|
|
|
|
#include "vpn-helpers.h"
|
|
|
|
NMVpnEditorPlugin *
|
|
vpn_get_plugin_by_service (const char *service)
|
|
{
|
|
NMVpnPluginInfo *plugin_info;
|
|
|
|
g_return_val_if_fail (service != NULL, NULL);
|
|
|
|
plugin_info = nm_vpn_plugin_info_list_find_by_service (vpn_get_plugins (), service);
|
|
if (plugin_info)
|
|
return nm_vpn_plugin_info_get_editor_plugin (plugin_info);
|
|
return NULL;
|
|
}
|
|
|
|
static gint
|
|
_sort_vpn_plugins (NMVpnPluginInfo *aa, NMVpnPluginInfo *bb)
|
|
{
|
|
return strcmp (nm_vpn_plugin_info_get_name (aa), nm_vpn_plugin_info_get_name (bb));
|
|
}
|
|
|
|
GSList *
|
|
vpn_get_plugins (void)
|
|
{
|
|
static GSList *plugins = NULL;
|
|
GSList *p;
|
|
|
|
p = nm_vpn_plugin_info_list_load ();
|
|
plugins = NULL;
|
|
while (p) {
|
|
g_autoptr(NMVpnPluginInfo) plugin_info = NM_VPN_PLUGIN_INFO (p->data);
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
/* load the editor plugin, and preserve only those NMVpnPluginInfo that can
|
|
* successfully load the plugin. */
|
|
if (nm_vpn_plugin_info_load_editor_plugin (plugin_info, &error))
|
|
plugins = g_slist_prepend (plugins, g_steal_pointer (&plugin_info));
|
|
else {
|
|
if ( !nm_vpn_plugin_info_get_plugin (plugin_info)
|
|
&& nm_vpn_plugin_info_lookup_property (plugin_info, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties")) {
|
|
g_message ("vpn: (%s,%s) cannot load legacy-only plugin",
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
nm_vpn_plugin_info_get_filename (plugin_info));
|
|
} else if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
|
|
g_message ("vpn: (%s,%s) file \"%s\" not found. Did you install the client package?",
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
nm_vpn_plugin_info_get_filename (plugin_info),
|
|
nm_vpn_plugin_info_get_plugin (plugin_info));
|
|
} else {
|
|
g_warning ("vpn: (%s,%s) could not load plugin: %s",
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
nm_vpn_plugin_info_get_filename (plugin_info),
|
|
error->message);
|
|
}
|
|
}
|
|
p = g_slist_delete_link (p, p);
|
|
}
|
|
|
|
/* sort the list of plugins alphabetically. */
|
|
plugins = g_slist_sort (plugins, (GCompareFunc) _sort_vpn_plugins);
|
|
return plugins;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
GMainLoop *mainloop;
|
|
gint response;
|
|
} RunData;
|
|
|
|
static void
|
|
on_dialog_close_request_cb (GtkDialog *dialog,
|
|
gint response,
|
|
RunData *data)
|
|
{
|
|
data->response = GTK_RESPONSE_CLOSE;
|
|
g_main_loop_quit (data->mainloop);
|
|
}
|
|
|
|
static void
|
|
on_dialog_response_cb (GtkDialog *dialog,
|
|
gint response,
|
|
RunData *data)
|
|
{
|
|
data->response = response;
|
|
g_main_loop_quit (data->mainloop);
|
|
}
|
|
|
|
static int
|
|
run_dialog (GtkDialog *dialog)
|
|
{
|
|
g_autoptr(GMainLoop) mainloop = NULL;
|
|
RunData run_data;
|
|
gulong response_id;
|
|
gulong close_id;
|
|
|
|
mainloop = g_main_loop_new (NULL, FALSE);
|
|
run_data = (RunData) {
|
|
.response = GTK_RESPONSE_CLOSE,
|
|
.mainloop = mainloop,
|
|
};
|
|
|
|
close_id = g_signal_connect (dialog, "close-request", G_CALLBACK (on_dialog_close_request_cb), &run_data);
|
|
response_id = g_signal_connect_swapped (dialog, "response", G_CALLBACK (on_dialog_response_cb), &run_data);
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
g_main_loop_run (mainloop);
|
|
|
|
g_clear_signal_handler (&close_id, dialog);
|
|
g_clear_signal_handler (&response_id, dialog);
|
|
|
|
return run_data.response;
|
|
}
|
|
|
|
typedef struct {
|
|
VpnImportCallback callback;
|
|
gpointer user_data;
|
|
} ActionInfo;
|
|
|
|
static void
|
|
import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
|
|
{
|
|
g_autofree gchar *filename = NULL;
|
|
g_autoptr(GFile) file = NULL;
|
|
ActionInfo *info = (ActionInfo *) user_data;
|
|
NMConnection *connection = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
GSList *iter;
|
|
|
|
if (response != GTK_RESPONSE_ACCEPT)
|
|
goto out;
|
|
|
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
|
if (!file) {
|
|
g_warning ("%s: didn't get a filename back from the chooser!", __func__);
|
|
goto out;
|
|
}
|
|
|
|
filename = g_file_get_path (file);
|
|
for (iter = vpn_get_plugins (); !connection && iter; iter = iter->next) {
|
|
NMVpnEditorPlugin *plugin;
|
|
|
|
plugin = nm_vpn_plugin_info_get_editor_plugin (iter->data);
|
|
g_clear_error (&error);
|
|
connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
|
|
}
|
|
|
|
if (!connection) {
|
|
GtkWidget *err_dialog;
|
|
g_autofree gchar *bname = g_path_get_basename (filename);
|
|
|
|
err_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
|
|
GTK_DIALOG_MODAL | 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");
|
|
run_dialog (GTK_DIALOG (err_dialog));
|
|
}
|
|
|
|
out:
|
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
|
|
|
info->callback (connection, info->user_data);
|
|
g_free (info);
|
|
}
|
|
|
|
static gboolean
|
|
destroy_import_chooser (GtkWidget *dialog, gpointer user_data)
|
|
{
|
|
ActionInfo *info = (ActionInfo *) user_data;
|
|
|
|
info->callback (NULL, info->user_data);
|
|
g_free (info);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
vpn_import (GtkWindow *parent, VpnImportCallback callback, gpointer user_data)
|
|
{
|
|
g_autoptr(GFile) home_folder = NULL;
|
|
GtkWidget *dialog;
|
|
ActionInfo *info;
|
|
|
|
dialog = gtk_file_chooser_dialog_new (_("Select file to import"),
|
|
parent,
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_Open"), GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
|
home_folder = g_file_new_for_path (g_get_home_dir ());
|
|
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder, NULL);
|
|
|
|
info = g_malloc0 (sizeof (ActionInfo));
|
|
info->callback = callback;
|
|
info->user_data = user_data;
|
|
|
|
g_signal_connect (G_OBJECT (dialog), "close-request", G_CALLBACK (destroy_import_chooser), info);
|
|
g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info);
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
}
|
|
|
|
static void
|
|
export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
|
|
{
|
|
g_autoptr(NMConnection) connection = NM_CONNECTION (user_data);
|
|
g_autoptr(GFile) file = NULL;
|
|
char *filename = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
NMVpnEditorPlugin *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;
|
|
|
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
|
if (!file) {
|
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "no filename");
|
|
goto done;
|
|
}
|
|
|
|
filename = g_file_get_path (file);
|
|
if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
|
|
int replace_response;
|
|
GtkWidget *replace_dialog;
|
|
g_autofree gchar *bname = NULL;
|
|
|
|
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);
|
|
replace_response = run_dialog (GTK_DIALOG (replace_dialog));
|
|
gtk_window_destroy (GTK_WINDOW (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_editor_plugin_export (plugin, filename, connection, &error);
|
|
|
|
done:
|
|
if (!success) {
|
|
GtkWidget *err_dialog;
|
|
g_autofree gchar *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");
|
|
run_dialog (GTK_DIALOG (err_dialog));
|
|
}
|
|
|
|
out:
|
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
|
}
|
|
|
|
void
|
|
vpn_export (NMConnection *connection)
|
|
{
|
|
g_autoptr(GFile) home_folder = NULL;
|
|
GtkWidget *dialog;
|
|
NMVpnEditorPlugin *plugin;
|
|
NMSettingVpn *s_vpn = NULL;
|
|
const char *service_type;
|
|
|
|
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,
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_Save"), GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
home_folder = g_file_new_for_path (g_get_home_dir ());
|
|
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder, NULL);
|
|
|
|
plugin = vpn_get_plugin_by_service (service_type);
|
|
if (plugin) {
|
|
g_autofree gchar *suggested = NULL;
|
|
|
|
suggested = nm_vpn_editor_plugin_get_suggested_filename (plugin, connection);
|
|
if (suggested)
|
|
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested);
|
|
}
|
|
|
|
g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection));
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
}
|