If an user opens an connection editor and then closes it, repeat this multiple times causes lots of unused hidden editor windows. Fix that by destroying the connection editor instead of hiding them when closing.
824 lines
28 KiB
C
824 lines
28 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2012 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 <glib-object.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <NetworkManager.h>
|
|
|
|
#include "net-connection-editor.h"
|
|
#include "net-connection-editor-resources.h"
|
|
#include "ce-page.h"
|
|
#include "ce-page-details.h"
|
|
#include "ce-page-wifi.h"
|
|
#include "ce-page-ip4.h"
|
|
#include "ce-page-ip6.h"
|
|
#include "ce-page-security.h"
|
|
#include "ce-page-ethernet.h"
|
|
#include "ce-page-8021x-security.h"
|
|
#include "ce-page-vpn.h"
|
|
#include "vpn-helpers.h"
|
|
#include "eap-method.h"
|
|
|
|
enum {
|
|
DONE,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
struct _NetConnectionEditor
|
|
{
|
|
GtkDialog parent;
|
|
|
|
GtkBox *add_connection_box;
|
|
AdwBin *add_connection_frame;
|
|
GtkButton *apply_button;
|
|
GtkButton *cancel_button;
|
|
GtkNotebook *notebook;
|
|
GtkStack *toplevel_stack;
|
|
|
|
NMClient *client;
|
|
NMDevice *device;
|
|
|
|
NMConnection *connection;
|
|
NMConnection *orig_connection;
|
|
gboolean is_new_connection;
|
|
gboolean is_changed;
|
|
NMAccessPoint *ap;
|
|
|
|
GSList *initializing_pages;
|
|
|
|
NMClientPermissionResult can_modify;
|
|
|
|
gboolean title_set;
|
|
};
|
|
|
|
G_DEFINE_TYPE (NetConnectionEditor, net_connection_editor, GTK_TYPE_DIALOG)
|
|
|
|
static void page_changed (NetConnectionEditor *self);
|
|
|
|
static void
|
|
cancel_editing (NetConnectionEditor *self)
|
|
{
|
|
gtk_window_destroy (GTK_WINDOW (self));
|
|
g_signal_emit (self, signals[DONE], 0, FALSE);
|
|
}
|
|
|
|
static void
|
|
close_request_cb (NetConnectionEditor *self)
|
|
{
|
|
cancel_editing (self);
|
|
}
|
|
|
|
static void
|
|
cancel_clicked_cb (NetConnectionEditor *self)
|
|
{
|
|
cancel_editing (self);
|
|
}
|
|
|
|
static void
|
|
update_connection (NetConnectionEditor *self)
|
|
{
|
|
g_autoptr(GVariant) settings = NULL;
|
|
|
|
settings = nm_connection_to_dbus (self->connection, NM_CONNECTION_SERIALIZE_ALL);
|
|
nm_connection_replace_settings (self->orig_connection, settings, NULL);
|
|
}
|
|
|
|
static void
|
|
update_complete (NetConnectionEditor *self,
|
|
gboolean success)
|
|
{
|
|
gtk_widget_hide (GTK_WIDGET (self));
|
|
g_signal_emit (self, signals[DONE], 0, success);
|
|
}
|
|
|
|
static void
|
|
updated_connection_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
NetConnectionEditor *self;
|
|
g_autoptr(GError) error = NULL;
|
|
gboolean success = TRUE;
|
|
|
|
if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (source_object),
|
|
res, &error)) {
|
|
g_warning ("Failed to commit changes: %s", error->message);
|
|
success = FALSE;
|
|
//return; FIXME return if cancelled
|
|
}
|
|
|
|
nm_connection_clear_secrets (NM_CONNECTION (source_object));
|
|
|
|
self = user_data;
|
|
update_complete (self, success);
|
|
}
|
|
|
|
static void
|
|
added_connection_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
NetConnectionEditor *self;
|
|
g_autoptr(GError) error = NULL;
|
|
gboolean success = TRUE;
|
|
|
|
if (!nm_client_add_connection_finish (NM_CLIENT (source_object), res, &error)) {
|
|
g_warning ("Failed to add connection: %s", error->message);
|
|
success = FALSE;
|
|
/* Leave the editor open */
|
|
// return; FIXME return if cancelled
|
|
}
|
|
|
|
self = user_data;
|
|
update_complete (self, success);
|
|
}
|
|
|
|
static void
|
|
apply_clicked_cb (NetConnectionEditor *self)
|
|
{
|
|
update_connection (self);
|
|
|
|
eap_method_ca_cert_ignore_save (self->connection);
|
|
|
|
if (self->is_new_connection) {
|
|
nm_client_add_connection_async (self->client,
|
|
self->orig_connection,
|
|
TRUE,
|
|
NULL,
|
|
added_connection_cb,
|
|
self);
|
|
} else {
|
|
nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (self->orig_connection),
|
|
TRUE,
|
|
NULL,
|
|
updated_connection_cb, self);
|
|
}
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_init (NetConnectionEditor *self)
|
|
{
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_finalize (GObject *object)
|
|
{
|
|
NetConnectionEditor *self = NET_CONNECTION_EDITOR (object);
|
|
|
|
g_clear_object (&self->connection);
|
|
g_clear_object (&self->orig_connection);
|
|
g_clear_object (&self->device);
|
|
g_clear_object (&self->client);
|
|
g_clear_object (&self->ap);
|
|
|
|
G_OBJECT_CLASS (net_connection_editor_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_class_init (NetConnectionEditorClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
|
|
g_resources_register (net_connection_editor_get_resource ());
|
|
|
|
object_class->finalize = net_connection_editor_finalize;
|
|
|
|
signals[DONE] = g_signal_new ("done",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
0,
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/connection-editor.ui");
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, add_connection_box);
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, add_connection_frame);
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, apply_button);
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, cancel_button);
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, notebook);
|
|
gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, toplevel_stack);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, cancel_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, close_request_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, apply_clicked_cb);
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_error_dialog (NetConnectionEditor *self,
|
|
const char *primary_text,
|
|
const char *secondary_text)
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWindow *parent;
|
|
|
|
if (gtk_widget_is_visible (GTK_WIDGET (self)))
|
|
parent = GTK_WINDOW (self);
|
|
else
|
|
parent = gtk_window_get_transient_for (GTK_WINDOW (self));
|
|
|
|
dialog = gtk_message_dialog_new (parent,
|
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"%s", primary_text);
|
|
|
|
if (secondary_text) {
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
"%s", secondary_text);
|
|
}
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_do_fallback (NetConnectionEditor *self, const gchar *type)
|
|
{
|
|
g_autofree gchar *cmdline = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (self->is_new_connection) {
|
|
cmdline = g_strdup_printf ("nm-connection-editor --type='%s' --create", type);
|
|
} else {
|
|
cmdline = g_strdup_printf ("nm-connection-editor --edit='%s'",
|
|
nm_connection_get_uuid (self->connection));
|
|
}
|
|
|
|
g_spawn_command_line_async (cmdline, &error);
|
|
|
|
if (error)
|
|
net_connection_editor_error_dialog (self,
|
|
_("Unable to open connection editor"),
|
|
error->message);
|
|
|
|
g_signal_emit (self, signals[DONE], 0, FALSE);
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_update_title (NetConnectionEditor *self)
|
|
{
|
|
g_autofree gchar *id = NULL;
|
|
|
|
if (self->title_set)
|
|
return;
|
|
|
|
if (self->is_new_connection) {
|
|
if (self->device) {
|
|
id = g_strdup (_("New Profile"));
|
|
} else {
|
|
/* Leave it set to "Add New Connection" */
|
|
return;
|
|
}
|
|
} else {
|
|
NMSettingWireless *sw;
|
|
sw = nm_connection_get_setting_wireless (self->connection);
|
|
if (sw) {
|
|
GBytes *ssid;
|
|
ssid = nm_setting_wireless_get_ssid (sw);
|
|
id = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
|
|
} else {
|
|
id = g_strdup (nm_connection_get_id (self->connection));
|
|
}
|
|
}
|
|
gtk_window_set_title (GTK_WINDOW (self), id);
|
|
}
|
|
|
|
static gboolean
|
|
editor_is_initialized (NetConnectionEditor *self)
|
|
{
|
|
return self->initializing_pages == NULL;
|
|
}
|
|
|
|
static void
|
|
update_sensitivity (NetConnectionEditor *self)
|
|
{
|
|
NMSettingConnection *sc;
|
|
gboolean sensitive;
|
|
gint i;
|
|
|
|
if (!editor_is_initialized (self))
|
|
return;
|
|
|
|
sc = nm_connection_get_setting_connection (self->connection);
|
|
|
|
if (nm_setting_connection_get_read_only (sc)) {
|
|
sensitive = FALSE;
|
|
} else {
|
|
sensitive = self->can_modify;
|
|
}
|
|
|
|
for (i = 0; i < gtk_notebook_get_n_pages (self->notebook); i++) {
|
|
GtkWidget *page = gtk_notebook_get_nth_page (self->notebook, i);
|
|
gtk_widget_set_sensitive (page, sensitive);
|
|
}
|
|
}
|
|
|
|
static void
|
|
validate (NetConnectionEditor *self)
|
|
{
|
|
gboolean valid = FALSE;
|
|
gint i;
|
|
|
|
if (!editor_is_initialized (self))
|
|
goto done;
|
|
|
|
valid = TRUE;
|
|
for (i = 0; i < gtk_notebook_get_n_pages (self->notebook); i++) {
|
|
CEPage *page = CE_PAGE (gtk_notebook_get_nth_page (self->notebook, i));
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (!ce_page_validate (page, self->connection, &error)) {
|
|
valid = FALSE;
|
|
if (error) {
|
|
g_debug ("Invalid setting %s: %s", ce_page_get_title (page), error->message);
|
|
} else {
|
|
g_debug ("Invalid setting %s", ce_page_get_title (page));
|
|
}
|
|
}
|
|
}
|
|
|
|
update_sensitivity (self);
|
|
done:
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->apply_button), valid && self->is_changed);
|
|
}
|
|
|
|
static void
|
|
page_changed (NetConnectionEditor *self)
|
|
{
|
|
if (editor_is_initialized (self))
|
|
self->is_changed = TRUE;
|
|
validate (self);
|
|
}
|
|
|
|
static gboolean
|
|
idle_validate (gpointer user_data)
|
|
{
|
|
validate (NET_CONNECTION_EDITOR (user_data));
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
recheck_initialization (NetConnectionEditor *self)
|
|
{
|
|
if (!editor_is_initialized (self))
|
|
return;
|
|
|
|
gtk_stack_set_visible_child (self->toplevel_stack, GTK_WIDGET (self->notebook));
|
|
gtk_notebook_set_current_page (self->notebook, 0);
|
|
|
|
g_idle_add (idle_validate, self);
|
|
}
|
|
|
|
static void
|
|
page_initialized (NetConnectionEditor *self, GError *error, CEPage *page)
|
|
{
|
|
GtkWidget *label;
|
|
gint position;
|
|
gint i;
|
|
|
|
position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (page), "position"));
|
|
g_object_set_data (G_OBJECT (page), "position", GINT_TO_POINTER (position));
|
|
for (i = 0; i < gtk_notebook_get_n_pages (self->notebook); i++) {
|
|
GtkWidget *page = gtk_notebook_get_nth_page (self->notebook, i);
|
|
gint pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (page), "position"));
|
|
if (pos > position)
|
|
break;
|
|
}
|
|
|
|
label = gtk_label_new (ce_page_get_title (page));
|
|
|
|
gtk_notebook_insert_page (self->notebook, GTK_WIDGET (page), label, i);
|
|
|
|
self->initializing_pages = g_slist_remove (self->initializing_pages, page);
|
|
|
|
recheck_initialization (self);
|
|
}
|
|
|
|
typedef struct {
|
|
NetConnectionEditor *editor;
|
|
CEPage *page;
|
|
const gchar *setting_name;
|
|
} GetSecretsInfo;
|
|
|
|
static void
|
|
get_secrets_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnection *connection;
|
|
g_autofree GetSecretsInfo *info = user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) variant = NULL;
|
|
|
|
connection = NM_REMOTE_CONNECTION (source_object);
|
|
variant = nm_remote_connection_get_secrets_finish (connection, res, &error);
|
|
|
|
if (!variant && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
ce_page_complete_init (info->page, info->editor->connection, info->setting_name, variant, g_steal_pointer (&error));
|
|
}
|
|
|
|
static void
|
|
get_secrets_for_page (NetConnectionEditor *self,
|
|
CEPage *page,
|
|
const gchar *setting_name)
|
|
{
|
|
GetSecretsInfo *info;
|
|
|
|
info = g_new0 (GetSecretsInfo, 1);
|
|
info->editor = self;
|
|
info->page = page;
|
|
info->setting_name = setting_name;
|
|
|
|
nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (self->orig_connection),
|
|
setting_name,
|
|
NULL, //FIXME
|
|
get_secrets_cb,
|
|
info);
|
|
}
|
|
|
|
static void
|
|
add_page (NetConnectionEditor *self, CEPage *page)
|
|
{
|
|
gint position;
|
|
|
|
position = g_slist_length (self->initializing_pages);
|
|
g_object_set_data (G_OBJECT (page), "position", GINT_TO_POINTER (position));
|
|
|
|
self->initializing_pages = g_slist_append (self->initializing_pages, page);
|
|
|
|
g_signal_connect_object (page, "changed", G_CALLBACK (page_changed), self, G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (page, "initialized", G_CALLBACK (page_initialized), self, G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_set_connection (NetConnectionEditor *self,
|
|
NMConnection *connection)
|
|
{
|
|
GSList *pages, *l;
|
|
NMSettingConnection *sc;
|
|
const gchar *type;
|
|
gboolean is_wired;
|
|
gboolean is_wifi;
|
|
gboolean is_vpn;
|
|
|
|
self->is_new_connection = !nm_client_get_connection_by_uuid (self->client,
|
|
nm_connection_get_uuid (connection));
|
|
|
|
if (self->is_new_connection) {
|
|
gtk_button_set_label (self->apply_button, _("_Add"));
|
|
self->is_changed = TRUE;
|
|
}
|
|
|
|
self->connection = nm_simple_connection_new_clone (connection);
|
|
self->orig_connection = g_object_ref (connection);
|
|
|
|
net_connection_editor_update_title (self);
|
|
|
|
eap_method_ca_cert_ignore_load (self->connection);
|
|
|
|
sc = nm_connection_get_setting_connection (connection);
|
|
type = nm_setting_connection_get_connection_type (sc);
|
|
|
|
is_wired = g_str_equal (type, NM_SETTING_WIRED_SETTING_NAME);
|
|
is_wifi = g_str_equal (type, NM_SETTING_WIRELESS_SETTING_NAME);
|
|
is_vpn = g_str_equal (type, NM_SETTING_VPN_SETTING_NAME);
|
|
|
|
if (!self->is_new_connection)
|
|
add_page (self, CE_PAGE (ce_page_details_new (self->connection, self->device, self->ap, self)));
|
|
|
|
if (is_wifi)
|
|
add_page (self, CE_PAGE (ce_page_wifi_new (self->connection, self->client)));
|
|
else if (is_wired)
|
|
add_page (self, CE_PAGE (ce_page_ethernet_new (self->connection, self->client)));
|
|
else if (is_vpn)
|
|
add_page (self, CE_PAGE (ce_page_vpn_new (self->connection)));
|
|
else {
|
|
/* Unsupported type */
|
|
net_connection_editor_do_fallback (self, type);
|
|
return;
|
|
}
|
|
|
|
add_page (self, CE_PAGE (ce_page_ip4_new (self->connection, self->client)));
|
|
add_page (self, CE_PAGE (ce_page_ip6_new (self->connection, self->client)));
|
|
|
|
if (is_wifi)
|
|
add_page (self, CE_PAGE (ce_page_security_new (self->connection)));
|
|
else if (is_wired)
|
|
add_page (self, CE_PAGE (ce_page_8021x_security_new (self->connection)));
|
|
|
|
pages = g_slist_copy (self->initializing_pages);
|
|
for (l = pages; l; l = l->next) {
|
|
CEPage *page = l->data;
|
|
const gchar *security_setting;
|
|
|
|
security_setting = ce_page_get_security_setting (page);
|
|
if (!security_setting || self->is_new_connection) {
|
|
ce_page_complete_init (page, NULL, NULL, NULL, NULL);
|
|
} else {
|
|
get_secrets_for_page (self, page, security_setting);
|
|
}
|
|
}
|
|
g_slist_free (pages);
|
|
}
|
|
|
|
static NMConnection *
|
|
complete_vpn_connection (NetConnectionEditor *self, NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
NMSetting *s_type;
|
|
|
|
if (!connection)
|
|
connection = nm_simple_connection_new ();
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (!s_con) {
|
|
s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
|
|
nm_connection_add_setting (connection, NM_SETTING (s_con));
|
|
}
|
|
|
|
if (!nm_setting_connection_get_uuid (s_con)) {
|
|
g_autofree gchar *uuid = nm_utils_uuid_generate ();
|
|
g_object_set (s_con,
|
|
NM_SETTING_CONNECTION_UUID, uuid,
|
|
NULL);
|
|
}
|
|
|
|
if (!nm_setting_connection_get_id (s_con)) {
|
|
const GPtrArray *connections;
|
|
g_autofree gchar *id = NULL;
|
|
|
|
connections = nm_client_get_connections (self->client);
|
|
id = ce_page_get_next_available_name (connections, NAME_FORMAT_TYPE, _("VPN"));
|
|
g_object_set (s_con,
|
|
NM_SETTING_CONNECTION_ID, id,
|
|
NULL);
|
|
}
|
|
|
|
s_type = nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
|
|
if (!s_type) {
|
|
s_type = g_object_new (NM_TYPE_SETTING_VPN, NULL);
|
|
nm_connection_add_setting (connection, s_type);
|
|
}
|
|
|
|
if (!nm_setting_connection_get_connection_type (s_con)) {
|
|
g_object_set (s_con,
|
|
NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (s_type),
|
|
NULL);
|
|
}
|
|
|
|
return connection;
|
|
}
|
|
|
|
static void
|
|
finish_add_connection (NetConnectionEditor *self, NMConnection *connection)
|
|
{
|
|
adw_bin_set_child (self->add_connection_frame, NULL);
|
|
gtk_stack_set_visible_child (self->toplevel_stack, GTK_WIDGET (self->notebook));
|
|
gtk_widget_show (GTK_WIDGET (self->apply_button));
|
|
|
|
if (connection)
|
|
net_connection_editor_set_connection (self, connection);
|
|
}
|
|
|
|
static void
|
|
vpn_import_complete (NMConnection *connection, gpointer user_data)
|
|
{
|
|
NetConnectionEditor *self = user_data;
|
|
|
|
if (!connection) {
|
|
/* The import code shows its own error dialogs. */
|
|
g_signal_emit (self, signals[DONE], 0, FALSE);
|
|
return;
|
|
}
|
|
|
|
complete_vpn_connection (self, connection);
|
|
finish_add_connection (self, connection);
|
|
}
|
|
|
|
static void
|
|
vpn_type_activated (NetConnectionEditor *self, GtkWidget *row)
|
|
{
|
|
const char *service_name = g_object_get_data (G_OBJECT (row), "service_name");
|
|
NMConnection *connection;
|
|
NMSettingVpn *s_vpn;
|
|
NMSettingConnection *s_con;
|
|
|
|
if (!strcmp (service_name, "import")) {
|
|
vpn_import (GTK_WINDOW (self), vpn_import_complete, self);
|
|
return;
|
|
}
|
|
|
|
connection = complete_vpn_connection (self, NULL);
|
|
s_vpn = nm_connection_get_setting_vpn (connection);
|
|
g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_name, NULL);
|
|
|
|
/* Mark the connection as private to this user, and non-autoconnect */
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
g_object_set (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, NULL);
|
|
nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
|
|
|
|
finish_add_connection (self, connection);
|
|
}
|
|
|
|
static void
|
|
select_vpn_type (NetConnectionEditor *self, GtkListBox *list)
|
|
{
|
|
GSList *vpn_plugins, *iter;
|
|
GtkWidget *row, *row_box;
|
|
GtkWidget *name_label, *desc_label;
|
|
GtkWidget *child;
|
|
|
|
/* Get the available VPN types */
|
|
vpn_plugins = vpn_get_plugins ();
|
|
|
|
/* Remove the previous menu contents */
|
|
while ((child = gtk_widget_get_first_child (GTK_WIDGET (list))) != NULL)
|
|
gtk_list_box_remove (list, child);
|
|
|
|
/* Add the VPN types */
|
|
for (iter = vpn_plugins; iter; iter = iter->next) {
|
|
NMVpnEditorPlugin *plugin = nm_vpn_plugin_info_get_editor_plugin (iter->data);
|
|
g_autofree gchar *name = NULL;
|
|
g_autofree gchar *desc = NULL;
|
|
g_autofree gchar *desc_markup = NULL;
|
|
g_autofree gchar *service_name = NULL;
|
|
|
|
g_object_get (plugin,
|
|
NM_VPN_EDITOR_PLUGIN_NAME, &name,
|
|
NM_VPN_EDITOR_PLUGIN_DESCRIPTION, &desc,
|
|
NM_VPN_EDITOR_PLUGIN_SERVICE, &service_name,
|
|
NULL);
|
|
desc_markup = g_markup_printf_escaped ("<span size='smaller'>%s</span>", desc);
|
|
|
|
row = gtk_list_box_row_new ();
|
|
|
|
row_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_widget_set_margin_start (row_box, 12);
|
|
gtk_widget_set_margin_end (row_box, 12);
|
|
gtk_widget_set_margin_top (row_box, 12);
|
|
gtk_widget_set_margin_bottom (row_box, 12);
|
|
|
|
name_label = gtk_label_new (name);
|
|
gtk_widget_set_halign (name_label, GTK_ALIGN_START);
|
|
gtk_box_append (GTK_BOX (row_box), name_label);
|
|
|
|
desc_label = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (desc_label), desc_markup);
|
|
gtk_label_set_wrap (GTK_LABEL (desc_label), TRUE);
|
|
gtk_widget_set_halign (desc_label, GTK_ALIGN_START);
|
|
gtk_widget_add_css_class (desc_label, "dim-label");
|
|
gtk_box_append (GTK_BOX (row_box), desc_label);
|
|
|
|
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
|
|
g_object_set_data_full (G_OBJECT (row), "service_name", g_steal_pointer (&service_name), g_free);
|
|
gtk_list_box_append (list, row);
|
|
}
|
|
|
|
/* Import */
|
|
row = gtk_list_box_row_new ();
|
|
|
|
row_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_widget_set_margin_start (row_box, 12);
|
|
gtk_widget_set_margin_end (row_box, 12);
|
|
gtk_widget_set_margin_top (row_box, 12);
|
|
gtk_widget_set_margin_bottom (row_box, 12);
|
|
|
|
name_label = gtk_label_new (_("Import from file…"));
|
|
gtk_widget_set_halign (name_label, GTK_ALIGN_START);
|
|
gtk_box_append (GTK_BOX (row_box), name_label);
|
|
|
|
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
|
|
g_object_set_data (G_OBJECT (row), "service_name", "import");
|
|
gtk_list_box_append (list, row);
|
|
|
|
g_signal_connect_object (list, "row-activated",
|
|
G_CALLBACK (vpn_type_activated), self, G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
static void
|
|
net_connection_editor_add_connection (NetConnectionEditor *self)
|
|
{
|
|
GtkListBox *list;
|
|
|
|
list = GTK_LIST_BOX (gtk_list_box_new ());
|
|
gtk_list_box_set_selection_mode (list, GTK_SELECTION_NONE);
|
|
|
|
select_vpn_type (self, list);
|
|
|
|
adw_bin_set_child (self->add_connection_frame, GTK_WIDGET (list));
|
|
|
|
gtk_stack_set_visible_child (self->toplevel_stack, GTK_WIDGET (self->add_connection_box));
|
|
gtk_widget_hide (GTK_WIDGET (self->apply_button));
|
|
gtk_window_set_title (GTK_WINDOW (self), _("Add VPN"));
|
|
}
|
|
|
|
static void
|
|
permission_changed (NetConnectionEditor *self,
|
|
NMClientPermission permission,
|
|
NMClientPermissionResult result)
|
|
{
|
|
if (permission != NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM)
|
|
return;
|
|
|
|
if (result == NM_CLIENT_PERMISSION_RESULT_YES || result == NM_CLIENT_PERMISSION_RESULT_AUTH)
|
|
self->can_modify = TRUE;
|
|
else
|
|
self->can_modify = FALSE;
|
|
|
|
validate (self);
|
|
}
|
|
|
|
NetConnectionEditor *
|
|
net_connection_editor_new (NMConnection *connection,
|
|
NMDevice *device,
|
|
NMAccessPoint *ap,
|
|
NMClient *client)
|
|
{
|
|
NetConnectionEditor *self;
|
|
|
|
self = g_object_new (net_connection_editor_get_type (),
|
|
/* This doesn't seem to work for a template, so it is also hardcoded. */
|
|
"use-header-bar", 1,
|
|
NULL);
|
|
|
|
if (ap)
|
|
self->ap = g_object_ref (ap);
|
|
if (device)
|
|
self->device = g_object_ref (device);
|
|
self->client = g_object_ref (client);
|
|
|
|
self->can_modify = nm_client_get_permission_result (client, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM);
|
|
g_signal_connect_object (self->client, "permission-changed",
|
|
G_CALLBACK (permission_changed), self, G_CONNECT_SWAPPED);
|
|
|
|
if (connection)
|
|
net_connection_editor_set_connection (self, connection);
|
|
else
|
|
net_connection_editor_add_connection (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
forgotten_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnection *connection = NM_REMOTE_CONNECTION (source_object);
|
|
NetConnectionEditor *self = user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (!nm_remote_connection_delete_finish (connection, res, &error)) {
|
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
g_warning ("Failed to delete connection %s: %s",
|
|
nm_connection_get_id (NM_CONNECTION (connection)),
|
|
error->message);
|
|
return;
|
|
}
|
|
|
|
cancel_editing (self);
|
|
}
|
|
|
|
void
|
|
net_connection_editor_forget (NetConnectionEditor *self)
|
|
{
|
|
nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (self->orig_connection),
|
|
NULL, forgotten_cb, self);
|
|
}
|
|
|
|
void
|
|
net_connection_editor_set_title (NetConnectionEditor *self,
|
|
const gchar *title)
|
|
{
|
|
gtk_window_set_title (GTK_WINDOW (self), title);
|
|
self->title_set = TRUE;
|
|
}
|