gnome-control-center/panels/network/connection-editor/vpn-helpers.c
Jonathan Kang ce93dbb3a0 network/connection-editor: always load all available VPN plugins
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.
2022-03-18 21:37:48 +00:00

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));
}