Blame src/connection-editor/nm-connection-editor.c

Packit Service 639700
// SPDX-License-Identifier: GPL-2.0+
Packit fabffb
/* NetworkManager Connection editor -- Connection editor for NetworkManager
Packit fabffb
 *
Packit fabffb
 * Rodrigo Moya <rodrigo@gnome-db.org>
Packit fabffb
 * Dan Williams <dcbw@redhat.com>
Packit fabffb
 * Tambet Ingo <tambet@gmail.com>
Packit fabffb
 *
Packit fabffb
 * Copyright 2007 - 2017 Red Hat, Inc.
Packit fabffb
 * Copyright 2007 - 2008 Novell, Inc.
Packit fabffb
 */
Packit fabffb
Packit fabffb
#include "nm-default.h"
Packit fabffb
Packit fabffb
#include <string.h>
Packit fabffb
#include <sys/types.h>
Packit fabffb
#include <unistd.h>
Packit fabffb
#include <errno.h>
Packit fabffb
#include <gdk/gdkx.h>
Packit fabffb
Packit fabffb
#if WITH_SELINUX
Packit fabffb
#include <selinux/selinux.h>
Packit fabffb
#endif
Packit fabffb
Packit fabffb
#include "nm-connection-editor.h"
Packit fabffb
#include "nma-cert-chooser.h"
Packit fabffb
Packit fabffb
#include "ce-page.h"
Packit fabffb
#include "page-general.h"
Packit fabffb
#include "page-ethernet.h"
Packit fabffb
#include "page-8021x-security.h"
Packit fabffb
#include "page-wifi.h"
Packit fabffb
#include "page-wifi-security.h"
Packit fabffb
#include "page-proxy.h"
Packit fabffb
#include "page-ip4.h"
Packit fabffb
#include "page-ip6.h"
Packit fabffb
#include "page-ip-tunnel.h"
Packit fabffb
#include "page-dsl.h"
Packit fabffb
#include "page-mobile.h"
Packit fabffb
#include "page-bluetooth.h"
Packit fabffb
#include "page-ppp.h"
Packit fabffb
#include "page-vpn.h"
Packit fabffb
#include "page-infiniband.h"
Packit fabffb
#include "page-bond.h"
Packit fabffb
#include "page-team.h"
Packit fabffb
#include "page-team-port.h"
Packit fabffb
#include "page-bridge.h"
Packit fabffb
#include "page-bridge-port.h"
Packit fabffb
#include "page-vlan.h"
Packit fabffb
#include "page-dcb.h"
Packit fabffb
#include "page-macsec.h"
Packit Service 639700
#include "page-wireguard.h"
Packit fabffb
#include "ce-polkit-button.h"
Packit fabffb
#include "vpn-helpers.h"
Packit fabffb
#include "eap-method.h"
Packit fabffb
Packit fabffb
extern gboolean nm_ce_keep_above;
Packit fabffb
Packit fabffb
G_DEFINE_TYPE (NMConnectionEditor, nm_connection_editor, G_TYPE_OBJECT)
Packit fabffb
Packit fabffb
enum {
Packit fabffb
	EDITOR_DONE,
Packit fabffb
	NEW_EDITOR,
Packit fabffb
	EDITOR_LAST_SIGNAL
Packit fabffb
};
Packit fabffb
Packit fabffb
static guint editor_signals[EDITOR_LAST_SIGNAL] = { 0 };
Packit fabffb
Packit fabffb
static GHashTable *active_editors;
Packit fabffb
Packit fabffb
static gboolean nm_connection_editor_set_connection (NMConnectionEditor *editor,
Packit fabffb
                                                     NMConnection *connection,
Packit fabffb
                                                     GError **error);
Packit fabffb
Packit fabffb
struct GetSecretsInfo {
Packit fabffb
	NMConnectionEditor *self;
Packit fabffb
	CEPage *page;
Packit fabffb
	char *setting_name;
Packit fabffb
	gboolean canceled;
Packit fabffb
};
Packit fabffb
Packit fabffb
#define SECRETS_TAG "secrets-setting-name"
Packit fabffb
#define ORDER_TAG "page-order"
Packit fabffb
Packit fabffb
static void
Packit fabffb
nm_connection_editor_update_title (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	const char *id;
Packit fabffb
Packit fabffb
	g_return_if_fail (editor != NULL);
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
	g_assert (s_con);
Packit fabffb
Packit fabffb
	id = nm_setting_connection_get_id (s_con);
Packit fabffb
	if (id && strlen (id)) {
Packit fabffb
		char *title = g_strdup_printf (_("Editing %s"), id);
Packit fabffb
		gtk_window_set_title (GTK_WINDOW (editor->window), title);
Packit fabffb
		g_free (title);
Packit fabffb
	} else
Packit fabffb
		gtk_window_set_title (GTK_WINDOW (editor->window), _("Editing un-named connection"));
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
ui_to_setting (NMConnectionEditor *editor, GError **error)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	GtkWidget *widget;
Packit fabffb
	const char *name;
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
	g_assert (s_con);
Packit fabffb
Packit fabffb
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
Packit fabffb
	name = gtk_entry_get_text (GTK_ENTRY (widget));
Packit fabffb
Packit fabffb
	g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, name, NULL);
Packit fabffb
	nm_connection_editor_update_title (editor);
Packit fabffb
Packit fabffb
	if (!name || !strlen (name)) {
Packit fabffb
		g_set_error_literal (error, NMA_ERROR, NMA_ERROR_GENERIC, _("Missing connection name"));
Packit fabffb
		return FALSE;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	return TRUE;
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
editor_is_initialized (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	return (g_slist_length (editor->initializing_pages) == 0);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
update_sensitivity (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	gboolean sensitive = FALSE;
Packit fabffb
	GtkWidget *widget;
Packit fabffb
	GSList *iter;
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
Packit fabffb
	/* Can't modify read-only connections; can't modify anything before the
Packit fabffb
	 * editor is initialized either.
Packit fabffb
	 */
Packit fabffb
	if (   editor_is_initialized (editor)
Packit fabffb
	    && editor->can_modify
Packit fabffb
	    && !nm_setting_connection_get_read_only (s_con)) {
Packit fabffb
		/* If the user cannot ever be authorized to change system connections,
Packit fabffb
		 * we desensitize the entire dialog.
Packit fabffb
		 */
Packit fabffb
		sensitive = ce_polkit_button_get_authorized (CE_POLKIT_BUTTON (editor->ok_button));
Packit fabffb
	}
Packit fabffb
Packit fabffb
	/* Cancel button is always sensitive */
Packit fabffb
	gtk_widget_set_sensitive (GTK_WIDGET (editor->cancel_button), TRUE);
Packit fabffb
Packit fabffb
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name_label"));
Packit fabffb
	gtk_widget_set_sensitive (widget, sensitive);
Packit fabffb
Packit fabffb
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
Packit fabffb
	gtk_widget_set_sensitive (widget, sensitive);
Packit fabffb
Packit fabffb
	for (iter = editor->pages; iter; iter = g_slist_next (iter)) {
Packit fabffb
		widget = ce_page_get_page (CE_PAGE (iter->data));
Packit fabffb
		gtk_widget_set_sensitive (widget, sensitive);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
#if WITH_SELINUX
Packit fabffb
/* This is what the files in ~/.cert would get. */
Packit fabffb
static const char certcon[] = "unconfined_u:object_r:home_cert_t:s0";
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
clear_name_if_present (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
Packit fabffb
{
Packit fabffb
	gchar **filename = data;
Packit fabffb
	gs_free char *existing = NULL;
Packit fabffb
Packit fabffb
	gtk_tree_model_get (model, iter, 2, &existing, -1);
Packit fabffb
	if (g_strcmp0 (existing, *filename) == 0) {
Packit fabffb
		*filename = NULL;
Packit fabffb
		return TRUE;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	return FALSE;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
update_relabel_list_filename (GtkListStore *store, char *filename)
Packit fabffb
{
Packit fabffb
	GtkTreeIter iter;
Packit fabffb
	gboolean writable;
Packit fabffb
	char *tcon;
Packit fabffb
	/* Any kind of VPN would do. If OpenVPN can't access the files
Packit fabffb
	 * no VPN likely can.  NetworkManager policy currently allows
Packit fabffb
	 * accessing home. It may make sense to tighten it some point. */
Packit fabffb
	static const char scon[] = "system_u:system_r:openvpn_t:s0";
Packit fabffb
Packit fabffb
	gtk_tree_model_foreach (GTK_TREE_MODEL (store), clear_name_if_present, &filename);
Packit fabffb
	if (filename == NULL)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	if (getfilecon (filename, &tcon) == -1) {
Packit fabffb
		/* Don't warn here, just ignore it. Perhaps the file
Packit fabffb
		 * is not on a SELinux-capable filesystem or something. */
Packit fabffb
		return;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	if (g_strcmp0 (certcon, tcon) == 0)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	writable = (access (filename, W_OK) == 0);
Packit fabffb
Packit fabffb
	if (selinux_check_access (scon, tcon, "file", "open", NULL) == -1) {
Packit fabffb
		gtk_list_store_append (store, &iter);
Packit fabffb
		gtk_list_store_set (store, &iter,
Packit fabffb
		                    0, writable,
Packit fabffb
		                    1, writable,
Packit fabffb
		                    2, filename,
Packit fabffb
		                    -1);
Packit fabffb
	}
Packit fabffb
Packit fabffb
	freecon (tcon);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
update_relabel_list (GtkWidget *widget, GtkListStore *store)
Packit fabffb
{
Packit fabffb
	gchar *filename = NULL;
Packit fabffb
	NMSetting8021xCKScheme scheme;
Packit fabffb
Packit fabffb
	if (!gtk_widget_is_sensitive (widget))
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	if (NMA_IS_CERT_CHOOSER (widget)) {
Packit fabffb
		filename = nma_cert_chooser_get_cert (NMA_CERT_CHOOSER (widget), &scheme);
Packit fabffb
		if (filename && scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
Packit fabffb
			update_relabel_list_filename (store, filename);
Packit fabffb
			g_free (filename);
Packit fabffb
		}
Packit fabffb
Packit fabffb
		filename = nma_cert_chooser_get_key (NMA_CERT_CHOOSER (widget), &scheme);
Packit fabffb
		if (filename && scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
Packit fabffb
			update_relabel_list_filename (store, filename);
Packit fabffb
			g_free (filename);
Packit fabffb
		}
Packit fabffb
	} else if (GTK_IS_CONTAINER (widget)) {
Packit fabffb
		gtk_container_foreach (GTK_CONTAINER (widget),
Packit fabffb
		                       (GtkCallback) update_relabel_list,
Packit fabffb
		                       store);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
recheck_relabel (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	gtk_list_store_clear (editor->relabel_list);
Packit fabffb
	update_relabel_list (editor->window, editor->relabel_list);
Packit fabffb
Packit fabffb
	if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (editor->relabel_list), NULL))
Packit fabffb
		gtk_widget_show (editor->relabel_info);
Packit fabffb
	else
Packit fabffb
		gtk_widget_hide (editor->relabel_info);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
relabel_toggled (GtkCellRendererToggle *cell_renderer, gchar *path, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = user_data;
Packit fabffb
	GtkTreeIter iter;
Packit fabffb
	gboolean relabel;
Packit fabffb
Packit fabffb
	if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (editor->relabel_list), &iter, path))
Packit fabffb
		g_return_if_reached ();
Packit fabffb
Packit fabffb
	gtk_tree_model_get (GTK_TREE_MODEL (editor->relabel_list), &iter, 0, &relabel, -1);
Packit fabffb
	gtk_list_store_set (editor->relabel_list, &iter, 0, !relabel, -1);
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
maybe_relabel (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
Packit fabffb
{
Packit fabffb
	gboolean relabel;
Packit fabffb
	gchar *filename;
Packit fabffb
Packit fabffb
	gtk_tree_model_get (model, iter, 0, &relabel, 2, &filename, -1);
Packit fabffb
	if (relabel) {
Packit fabffb
		if (setfilecon (filename, certcon) == -1)
Packit fabffb
			g_warning ("setfilecon: %s\n", g_strerror (errno));
Packit fabffb
	}
Packit fabffb
Packit fabffb
	g_free (filename);
Packit fabffb
	return FALSE;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
relabel_button_clicked_cb (GtkWidget *widget, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
Packit fabffb
	if (gtk_dialog_run (GTK_DIALOG (editor->relabel_dialog)) == GTK_RESPONSE_APPLY) {
Packit fabffb
		gtk_tree_model_foreach (GTK_TREE_MODEL (editor->relabel_list), maybe_relabel, NULL);
Packit fabffb
		recheck_relabel (editor);
Packit fabffb
	}
Packit fabffb
	gtk_widget_hide (editor->relabel_dialog);
Packit fabffb
}
Packit fabffb
#else /* !WITH_SELINUX */
Packit fabffb
static void
Packit fabffb
recheck_relabel (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
relabel_toggled (GtkCellRendererToggle *cell_renderer, gchar *path, gpointer user_data)
Packit fabffb
{
Packit fabffb
	g_return_if_reached ();
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
relabel_button_clicked_cb (GtkWidget *widget, gpointer user_data)
Packit fabffb
{
Packit fabffb
	g_return_if_reached ();
Packit fabffb
}
Packit fabffb
#endif /* WITH_SELINUX */
Packit fabffb
Packit fabffb
static void
Packit fabffb
connection_editor_validate (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	GSList *iter;
Packit fabffb
	gs_free char *validation_error = NULL;
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	if (!editor_is_initialized (editor)) {
Packit fabffb
		validation_error = g_strdup (_("Editor initializing…"));
Packit fabffb
		goto done_silent;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
	g_assert (s_con);
Packit fabffb
	if (nm_setting_connection_get_read_only (s_con)) {
Packit fabffb
		validation_error = g_strdup (_("Connection cannot be modified"));
Packit fabffb
		goto done;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	if (!ui_to_setting (editor, &error)) {
Packit fabffb
		validation_error = g_strdup (error->message);
Packit fabffb
		g_clear_error (&error);
Packit fabffb
		goto done;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	recheck_relabel (editor);
Packit fabffb
Packit fabffb
	for (iter = editor->pages; iter; iter = g_slist_next (iter)) {
Packit fabffb
		if (!ce_page_validate (CE_PAGE (iter->data), editor->connection, &error)) {
Packit fabffb
			if (!validation_error) {
Packit fabffb
				validation_error = g_strdup_printf (_("Invalid setting %s: %s"),
Packit fabffb
				                                    CE_PAGE (iter->data)->title,
Packit fabffb
				                                    error->message);
Packit fabffb
			}
Packit fabffb
			g_clear_error (&error);
Packit fabffb
		}
Packit fabffb
	}
Packit fabffb
Packit fabffb
done:
Packit fabffb
	if (g_strcmp0 (validation_error, editor->last_validation_error) != 0) {
Packit fabffb
		if (editor->last_validation_error && !validation_error)
Packit fabffb
			g_message ("Connection validates and can be saved");
Packit fabffb
		else if (validation_error)
Packit fabffb
			g_message ("Cannot save connection due to error: %s", validation_error);
Packit fabffb
		g_free (editor->last_validation_error);
Packit fabffb
		editor->last_validation_error = g_strdup (validation_error);
Packit fabffb
	}
Packit fabffb
Packit fabffb
done_silent:
Packit fabffb
	ce_polkit_button_set_validation_error (CE_POLKIT_BUTTON (editor->ok_button), validation_error);
Packit fabffb
	gtk_widget_set_sensitive (editor->export_button, !validation_error);
Packit fabffb
Packit fabffb
	update_sensitivity (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
ok_button_actionable_cb (GtkWidget *button,
Packit fabffb
                         gboolean actionable,
Packit fabffb
                         NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	connection_editor_validate (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
permissions_changed_cb (NMClient *client,
Packit fabffb
                        NMClientPermission permission,
Packit fabffb
                        NMClientPermissionResult result,
Packit fabffb
                        NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	if (permission != NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	if (result == NM_CLIENT_PERMISSION_RESULT_YES || result == NM_CLIENT_PERMISSION_RESULT_AUTH)
Packit fabffb
		editor->can_modify = TRUE;
Packit fabffb
	else
Packit fabffb
		editor->can_modify = FALSE;
Packit fabffb
Packit fabffb
	connection_editor_validate (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
destroy_inter_page_item (gpointer data)
Packit fabffb
{
Packit fabffb
	return;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
nm_connection_editor_init (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	GtkWidget *dialog;
Packit fabffb
	GError *error = NULL;
Packit fabffb
	const char *objects[] = { "nm-connection-editor", "relabel_dialog", "relabel_list", NULL };
Packit fabffb
Packit fabffb
	editor->builder = gtk_builder_new ();
Packit fabffb
Packit fabffb
	if (!gtk_builder_add_objects_from_resource (editor->builder,
Packit fabffb
	                                            "/org/gnome/nm_connection_editor/nm-connection-editor.ui",
Packit fabffb
	                                            (char **) objects,
Packit fabffb
	                                            &error)) {
Packit fabffb
		g_warning ("Couldn't load builder resource " "/org/gnome/nm_connection_editor/nm-connection-editor.ui: %s", error->message);
Packit fabffb
		g_error_free (error);
Packit fabffb
Packit fabffb
		dialog = gtk_message_dialog_new (NULL, 0,
Packit fabffb
		                                 GTK_MESSAGE_ERROR,
Packit fabffb
		                                 GTK_BUTTONS_OK,
Packit fabffb
		                                 "%s",
Packit fabffb
		                                 _("The connection editor could not find some required resources (the .ui file was not found)."));
Packit fabffb
		gtk_dialog_run (GTK_DIALOG (dialog));
Packit fabffb
		gtk_widget_destroy (dialog);
Packit fabffb
		gtk_main_quit ();
Packit fabffb
		return;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	editor->window = GTK_WIDGET (gtk_builder_get_object (editor->builder, "nm-connection-editor"));
Packit fabffb
	if (nm_ce_keep_above)
Packit fabffb
		gtk_window_set_keep_above (GTK_WINDOW (editor->window), TRUE);
Packit fabffb
Packit fabffb
	editor->cancel_button = GTK_WIDGET (gtk_builder_get_object (editor->builder, "cancel_button"));
Packit fabffb
	editor->export_button = GTK_WIDGET (gtk_builder_get_object (editor->builder, "export_button"));
Packit fabffb
	editor->relabel_info = GTK_WIDGET (gtk_builder_get_object (editor->builder, "relabel_info"));
Packit fabffb
	editor->relabel_dialog = GTK_WIDGET (gtk_builder_get_object (editor->builder, "relabel_dialog"));
Packit fabffb
	editor->relabel_button = GTK_WIDGET (gtk_builder_get_object (editor->builder, "relabel_button"));
Packit fabffb
	editor->relabel_list = GTK_LIST_STORE (gtk_builder_get_object (editor->builder, "relabel_list"));
Packit fabffb
	gtk_builder_add_callback_symbol (editor->builder, "relabel_toggled", G_CALLBACK (relabel_toggled));
Packit fabffb
Packit fabffb
	gtk_builder_connect_signals (editor->builder, editor);
Packit fabffb
Packit fabffb
	editor->inter_page_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) destroy_inter_page_item);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
get_secrets_info_free (GetSecretsInfo *info)
Packit fabffb
{
Packit fabffb
	g_free (info->setting_name);
Packit fabffb
	g_free (info);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
dispose (GObject *object)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (object);
Packit fabffb
Packit fabffb
	editor->disposed = TRUE;
Packit fabffb
Packit fabffb
	if (active_editors && editor->orig_connection)
Packit fabffb
		g_hash_table_remove (active_editors, editor->orig_connection);
Packit fabffb
Packit fabffb
	g_slist_free_full (editor->initializing_pages, g_object_unref);
Packit fabffb
	editor->initializing_pages = NULL;
Packit fabffb
Packit fabffb
	g_slist_free_full (editor->pages, g_object_unref);
Packit fabffb
	editor->pages = NULL;
Packit fabffb
Packit fabffb
	/* Mark any in-progress secrets call as canceled; it will clean up after itself. */
Packit fabffb
	if (editor->secrets_call)
Packit fabffb
		editor->secrets_call->canceled = TRUE;
Packit fabffb
Packit fabffb
	while (editor->pending_secrets_calls) {
Packit fabffb
		get_secrets_info_free ((GetSecretsInfo *) editor->pending_secrets_calls->data);
Packit fabffb
		editor->pending_secrets_calls = g_slist_delete_link (editor->pending_secrets_calls, editor->pending_secrets_calls);
Packit fabffb
	}
Packit fabffb
Packit fabffb
	nm_clear_g_source (&editor->validate_id);
Packit fabffb
Packit fabffb
	g_clear_object (&editor->connection);
Packit fabffb
	g_clear_object (&editor->orig_connection);
Packit fabffb
Packit fabffb
	if (editor->window) {
Packit fabffb
		gtk_widget_destroy (editor->window);
Packit fabffb
		editor->window = NULL;
Packit fabffb
	}
Packit fabffb
	g_clear_object (&editor->parent_window);
Packit fabffb
	g_clear_object (&editor->builder);
Packit fabffb
Packit fabffb
	nm_clear_g_signal_handler (editor->client, &editor->permission_id);
Packit fabffb
	g_clear_object (&editor->client);
Packit fabffb
Packit fabffb
	g_clear_pointer (&editor->last_validation_error, g_free);
Packit fabffb
Packit fabffb
	if (editor->inter_page_hash) {
Packit fabffb
		g_hash_table_destroy (editor->inter_page_hash);
Packit fabffb
		editor->inter_page_hash = NULL;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	g_slist_free_full (editor->unsupported_properties, g_free);
Packit fabffb
	editor->unsupported_properties = NULL;
Packit fabffb
Packit fabffb
	G_OBJECT_CLASS (nm_connection_editor_parent_class)->dispose (object);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
nm_connection_editor_class_init (NMConnectionEditorClass *klass)
Packit fabffb
{
Packit fabffb
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit fabffb
Packit fabffb
	/* virtual methods */
Packit fabffb
	object_class->dispose = dispose;
Packit fabffb
Packit fabffb
	/* Signals */
Packit fabffb
	editor_signals[EDITOR_DONE] =
Packit fabffb
		g_signal_new (NM_CONNECTION_EDITOR_DONE,
Packit fabffb
		              G_OBJECT_CLASS_TYPE (object_class),
Packit fabffb
		              G_SIGNAL_RUN_FIRST,
Packit fabffb
		              0, NULL, NULL, NULL,
Packit fabffb
		              G_TYPE_NONE, 1, GTK_TYPE_RESPONSE_TYPE);
Packit fabffb
Packit fabffb
	editor_signals[NEW_EDITOR] =
Packit fabffb
		g_signal_new (NM_CONNECTION_EDITOR_NEW_EDITOR,
Packit fabffb
		              G_OBJECT_CLASS_TYPE (object_class),
Packit fabffb
		              G_SIGNAL_RUN_FIRST,
Packit fabffb
		              0, NULL, NULL, NULL,
Packit fabffb
		              G_TYPE_NONE, 1, G_TYPE_POINTER);
Packit fabffb
}
Packit fabffb
Packit fabffb
NMConnectionEditor *
Packit fabffb
nm_connection_editor_new (GtkWindow *parent_window,
Packit fabffb
                          NMConnection *connection,
Packit fabffb
                          NMClient *client)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor;
Packit fabffb
	GtkWidget *hbox;
Packit fabffb
	gboolean is_new;
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
Packit fabffb
Packit fabffb
	is_new = !nm_client_get_connection_by_uuid (client, nm_connection_get_uuid (connection));
Packit fabffb
Packit fabffb
	editor = g_object_new (NM_TYPE_CONNECTION_EDITOR, NULL);
Packit fabffb
	editor->parent_window = parent_window ? g_object_ref (parent_window) : NULL;
Packit fabffb
	editor->client = g_object_ref (client);
Packit fabffb
	editor->is_new_connection = is_new;
Packit fabffb
Packit fabffb
	editor->can_modify = nm_client_get_permission_result (client, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM);
Packit fabffb
	editor->permission_id = g_signal_connect (editor->client,
Packit fabffb
	                                          "permission-changed",
Packit fabffb
	                                          G_CALLBACK (permissions_changed_cb),
Packit fabffb
	                                          editor);
Packit fabffb
Packit fabffb
	editor->ok_button = ce_polkit_button_new (_("_Save"),
Packit fabffb
	                                          _("Save any changes made to this connection."),
Packit fabffb
	                                          _("Authenticate to save this connection for all users of this machine."),
Packit fabffb
	                                          "emblem-ok-symbolic",
Packit fabffb
	                                          client,
Packit fabffb
	                                          NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM);
Packit fabffb
	gtk_button_set_use_underline (GTK_BUTTON (editor->ok_button), TRUE);
Packit fabffb
Packit fabffb
	g_signal_connect (editor->ok_button, "actionable",
Packit fabffb
	                  G_CALLBACK (ok_button_actionable_cb), editor);
Packit fabffb
	g_signal_connect (editor->ok_button, "authorized",
Packit fabffb
	                  G_CALLBACK (ok_button_actionable_cb), editor);
Packit fabffb
	hbox = GTK_WIDGET (gtk_builder_get_object (editor->builder, "action_area_hbox"));
Packit fabffb
	gtk_box_pack_end (GTK_BOX (hbox), editor->ok_button, TRUE, TRUE, 0);
Packit fabffb
	gtk_widget_show_all (editor->ok_button);
Packit fabffb
Packit fabffb
	if (!nm_connection_editor_set_connection (editor, connection, &error)) {
Packit fabffb
		nm_connection_editor_error (parent_window,
Packit fabffb
		                            is_new ? _("Could not create connection") : _("Could not edit connection"),
Packit fabffb
		                            "%s",
Packit fabffb
		                            error ? error->message : _("Unknown error creating connection editor dialog."));
Packit fabffb
		g_clear_error (&error);
Packit fabffb
		g_object_unref (editor);
Packit fabffb
		return NULL;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	if (!active_editors)
Packit fabffb
		active_editors = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
Packit fabffb
	g_hash_table_insert (active_editors, g_object_ref (connection), editor);
Packit fabffb
Packit fabffb
	return editor;
Packit fabffb
}
Packit fabffb
Packit fabffb
NMConnectionEditor *
Packit fabffb
nm_connection_editor_get (NMConnection *connection)
Packit fabffb
{
Packit fabffb
	return active_editors ? g_hash_table_lookup (active_editors, connection) : NULL;
Packit fabffb
}
Packit fabffb
Packit fabffb
/* Returns an editor for @slave's master, if any */
Packit fabffb
NMConnectionEditor *
Packit fabffb
nm_connection_editor_get_master (NMConnection *slave)
Packit fabffb
{
Packit fabffb
	GHashTableIter iter;
Packit fabffb
	gpointer connection, editor;
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	const char *master;
Packit fabffb
Packit fabffb
	if (!active_editors)
Packit fabffb
		return NULL;
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (slave);
Packit fabffb
	master = nm_setting_connection_get_master (s_con);
Packit fabffb
	if (!master)
Packit fabffb
		return NULL;
Packit fabffb
Packit fabffb
	g_hash_table_iter_init (&iter, active_editors);
Packit fabffb
	while (g_hash_table_iter_next (&iter, &connection, &editor)) {
Packit fabffb
		if (!g_strcmp0 (master, nm_connection_get_uuid (connection)))
Packit fabffb
			return editor;
Packit fabffb
		if (!g_strcmp0 (master, nm_connection_get_interface_name (connection)))
Packit fabffb
			return editor;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	return NULL;
Packit fabffb
}
Packit fabffb
Packit fabffb
NMConnection *
Packit fabffb
nm_connection_editor_get_connection (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), NULL);
Packit fabffb
Packit fabffb
	return editor->orig_connection;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
populate_connection_ui (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	GtkWidget *name;
Packit fabffb
Packit fabffb
	name = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
	gtk_entry_set_text (GTK_ENTRY (name), s_con ? nm_setting_connection_get_id (s_con) : NULL);
Packit fabffb
	gtk_widget_set_tooltip_text (name, nm_connection_get_uuid (editor->connection));
Packit fabffb
Packit fabffb
	g_signal_connect_swapped (name, "changed", G_CALLBACK (connection_editor_validate), editor);
Packit fabffb
Packit fabffb
	connection_editor_validate (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
page_changed (CEPage *page, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
	GSList *iter;
Packit fabffb
Packit fabffb
	/* Do page interdependent changes */
Packit fabffb
	for (iter = editor->pages; iter; iter = g_slist_next (iter))
Packit fabffb
		ce_page_inter_page_change (CE_PAGE (iter->data));
Packit fabffb
Packit fabffb
	if (editor_is_initialized (editor))
Packit fabffb
		nm_connection_editor_inter_page_clear_data (editor);
Packit fabffb
Packit fabffb
	connection_editor_validate (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
idle_validate (gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
Packit fabffb
	editor->validate_id = 0;
Packit fabffb
	connection_editor_validate (editor);
Packit fabffb
	return FALSE;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
recheck_initialization (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	GtkNotebook *notebook;
Packit fabffb
	GtkLabel *label;
Packit fabffb
Packit fabffb
	if (!editor_is_initialized (editor) || editor->init_run)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	editor->init_run = TRUE;
Packit fabffb
Packit fabffb
	populate_connection_ui (editor);
Packit fabffb
Packit fabffb
	/* Show the second page (the connection-type-specific data) first */
Packit fabffb
	notebook = GTK_NOTEBOOK (gtk_builder_get_object (editor->builder, "notebook"));
Packit fabffb
	gtk_notebook_set_current_page (notebook, 1);
Packit fabffb
Packit fabffb
	/* When everything is initialized, re-present the window to ensure it's on top */
Packit fabffb
	nm_connection_editor_present (editor);
Packit fabffb
Packit fabffb
	/* Validate the connection from an idle handler to ensure that stuff like
Packit fabffb
	 * GtkFileChoosers have had a chance to asynchronously find their files.
Packit fabffb
	 */
Packit fabffb
	if (editor->validate_id)
Packit fabffb
		g_source_remove (editor->validate_id);
Packit fabffb
	editor->validate_id = g_idle_add (idle_validate, editor);
Packit fabffb
Packit fabffb
	if (editor->unsupported_properties) {
Packit fabffb
		GString *str;
Packit fabffb
		GSList *iter;
Packit fabffb
		gs_free char *tooltip = NULL;
Packit fabffb
Packit fabffb
		str = g_string_new ("Unsupported properties: ");
Packit fabffb
Packit fabffb
		for (iter = editor->unsupported_properties; iter; iter = g_slist_next (iter)) {
Packit fabffb
			g_string_append (str, (char *) iter->data);
Packit fabffb
			if (iter->next)
Packit fabffb
				g_string_append (str, ", ");
Packit fabffb
		}
Packit fabffb
		tooltip = g_string_free (str, FALSE);
Packit fabffb
Packit fabffb
		label = GTK_LABEL (gtk_builder_get_object (editor->builder, "message_label"));
Packit fabffb
		gtk_label_set_text (label,
Packit fabffb
		                    _("Warning: the connection contains some properties not supported by the editor. "
Packit fabffb
		                      "They will be cleared upon save."));
Packit fabffb
		gtk_widget_set_tooltip_text (GTK_WIDGET (label), tooltip);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
page_initialized (CEPage *page, GError *error, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
	GtkWidget *widget, *parent;
Packit fabffb
	GtkNotebook *notebook;
Packit fabffb
	GtkWidget *label;
Packit fabffb
	GList *children, *iter;
Packit fabffb
	gpointer order, child_order;
Packit fabffb
	int i;
Packit fabffb
Packit fabffb
	if (error) {
Packit fabffb
		gtk_widget_hide (editor->window);
Packit fabffb
		nm_connection_editor_error (editor->parent_window,
Packit fabffb
		                            _("Error initializing editor"),
Packit fabffb
		                            "%s", error->message);
Packit fabffb
		g_signal_emit (editor, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_NONE);
Packit fabffb
		return;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	/* Add the page to the UI */
Packit fabffb
	notebook = GTK_NOTEBOOK (gtk_builder_get_object (editor->builder, "notebook"));
Packit fabffb
	label = gtk_label_new (ce_page_get_title (page));
Packit fabffb
	widget = ce_page_get_page (page);
Packit fabffb
	parent = gtk_widget_get_parent (widget);
Packit fabffb
	if (parent)
Packit fabffb
		gtk_container_remove (GTK_CONTAINER (parent), widget);
Packit fabffb
Packit fabffb
	order = g_object_get_data (G_OBJECT (page), ORDER_TAG);
Packit fabffb
	g_object_set_data (G_OBJECT (widget), ORDER_TAG, order);
Packit fabffb
Packit fabffb
	children = gtk_container_get_children (GTK_CONTAINER (notebook));
Packit fabffb
	for (iter = children, i = 0; iter; iter = iter->next, i++) {
Packit fabffb
		child_order = g_object_get_data (G_OBJECT (iter->data), ORDER_TAG);
Packit fabffb
		if (child_order > order)
Packit fabffb
			break;
Packit fabffb
	}
Packit fabffb
	g_list_free (children);
Packit fabffb
Packit fabffb
	gtk_notebook_insert_page (notebook, widget, label, i);
Packit fabffb
Packit fabffb
	if (CE_IS_PAGE_VPN (page) && ce_page_vpn_can_export (CE_PAGE_VPN (page)))
Packit fabffb
		gtk_widget_show (editor->export_button);
Packit fabffb
Packit fabffb
	/* Move the page from the initializing list to the main page list */
Packit fabffb
	editor->initializing_pages = g_slist_remove (editor->initializing_pages, page);
Packit fabffb
	editor->pages = g_slist_append (editor->pages, page);
Packit fabffb
Packit fabffb
	recheck_initialization (editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
page_new_editor (CEPage *page, NMConnectionEditor *new_editor, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
Packit fabffb
	g_signal_emit (self, editor_signals[NEW_EDITOR], 0, new_editor);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void request_secrets (GetSecretsInfo *info);
Packit fabffb
Packit fabffb
static void
Packit fabffb
get_secrets_cb (GObject *object,
Packit fabffb
                GAsyncResult *result,
Packit fabffb
                gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMRemoteConnection *connection = NM_REMOTE_CONNECTION (object);
Packit fabffb
	GetSecretsInfo *info = user_data;
Packit fabffb
	NMConnectionEditor *self;
Packit fabffb
	GVariant *secrets;
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	if (info->canceled) {
Packit fabffb
		get_secrets_info_free (info);
Packit fabffb
		return;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	secrets = nm_remote_connection_get_secrets_finish (connection, result, &error);
Packit fabffb
Packit fabffb
	self = info->self;
Packit fabffb
Packit fabffb
	/* Complete this secrets request; completion can actually dispose of the
Packit fabffb
	 * dialog if there was an error.
Packit fabffb
	 */
Packit fabffb
	self->secrets_call = NULL;
Packit fabffb
	ce_page_complete_init (info->page, info->setting_name, secrets, error);
Packit fabffb
	get_secrets_info_free (info);
Packit fabffb
Packit fabffb
	/* Kick off the next secrets request if there is one queued; if the dialog
Packit fabffb
	 * was disposed of by the completion above we don't need to do anything.
Packit fabffb
	 */
Packit fabffb
	if (!self->disposed && self->pending_secrets_calls) {
Packit fabffb
		self->secrets_call = g_slist_nth_data (self->pending_secrets_calls, 0);
Packit fabffb
		self->pending_secrets_calls = g_slist_remove (self->pending_secrets_calls, self->secrets_call);
Packit fabffb
Packit fabffb
		request_secrets (self->secrets_call);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
request_secrets (GetSecretsInfo *info)
Packit fabffb
{
Packit fabffb
	g_return_if_fail (info != NULL);
Packit fabffb
Packit fabffb
	nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (info->self->orig_connection),
Packit fabffb
	                                        info->setting_name, NULL, get_secrets_cb, info);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
get_secrets_for_page (NMConnectionEditor *self,
Packit fabffb
                      CEPage *page,
Packit fabffb
                      const char *setting_name)
Packit fabffb
{
Packit fabffb
	GetSecretsInfo *info;
Packit fabffb
Packit fabffb
	info = g_malloc0 (sizeof (GetSecretsInfo));
Packit fabffb
	info->self = self;
Packit fabffb
	info->page = page;
Packit fabffb
	info->setting_name = g_strdup (setting_name);
Packit fabffb
Packit fabffb
	/* PolicyKit doesn't queue up authorization requests internally.  Instead,
Packit fabffb
	 * if there's a pending authorization request, subsequent requests for that
Packit fabffb
	 * same authorization will return NotAuthorized+Challenge.  That's pretty
Packit fabffb
	 * inconvenient and it would be a lot nicer if PK just queued up subsequent
Packit fabffb
	 * authorization requests and executed them when the first one was finished.
Packit fabffb
	 * But it since it doesn't do that, we have to serialize the authorization
Packit fabffb
	 * requests ourselves to get the right authorization result.
Packit fabffb
	 */
Packit fabffb
	/* NOTE: PolicyKit-gnome 0.95 now serializes auth requests as of this commit:
Packit fabffb
	 * http://git.gnome.org/cgit/PolicyKit-gnome/commit/?id=f32cb7faa7197b9db55b569677732742c3c7fdc1
Packit fabffb
	 */
Packit fabffb
Packit fabffb
	/* If there's already an in-progress call, queue up the new one */
Packit fabffb
	if (self->secrets_call)
Packit fabffb
		self->pending_secrets_calls = g_slist_append (self->pending_secrets_calls, info);
Packit fabffb
	else {
Packit fabffb
		/* Request secrets for this page */
Packit fabffb
		self->secrets_call = info;
Packit fabffb
		request_secrets (info);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
add_page (NMConnectionEditor *editor,
Packit fabffb
          CEPageNewFunc func,
Packit fabffb
          NMConnection *connection,
Packit fabffb
          GError **error)
Packit fabffb
{
Packit fabffb
	CEPage *page;
Packit fabffb
	const char *secrets_setting_name = NULL;
Packit fabffb
Packit fabffb
	g_return_val_if_fail (editor != NULL, FALSE);
Packit fabffb
	g_return_val_if_fail (func != NULL, FALSE);
Packit fabffb
	g_return_val_if_fail (connection != NULL, FALSE);
Packit fabffb
Packit fabffb
	page = (*func) (editor, connection, GTK_WINDOW (editor->window), editor->client,
Packit fabffb
	                &secrets_setting_name, error);
Packit fabffb
	if (page) {
Packit fabffb
		g_object_set_data_full (G_OBJECT (page),
Packit fabffb
		                        SECRETS_TAG,
Packit fabffb
		                        g_strdup (secrets_setting_name),
Packit fabffb
		                        g_free);
Packit fabffb
		g_object_set_data (G_OBJECT (page),
Packit fabffb
		                   ORDER_TAG,
Packit fabffb
		                   GINT_TO_POINTER (g_slist_length (editor->initializing_pages)));
Packit fabffb
Packit fabffb
		editor->initializing_pages = g_slist_append (editor->initializing_pages, page);
Packit fabffb
		g_signal_connect (page, CE_PAGE_CHANGED, G_CALLBACK (page_changed), editor);
Packit fabffb
		g_signal_connect (page, CE_PAGE_INITIALIZED, G_CALLBACK (page_initialized), editor);
Packit fabffb
		g_signal_connect (page, CE_PAGE_NEW_EDITOR, G_CALLBACK (page_new_editor), editor);
Packit fabffb
	}
Packit fabffb
	return !!page;
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_add_unsupported_property (NMConnectionEditor *editor, const char *name)
Packit fabffb
{
Packit fabffb
	editor->unsupported_properties = g_slist_append (editor->unsupported_properties, g_strdup (name));
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_check_unsupported_properties (NMConnectionEditor *editor,
Packit fabffb
                                                   NMSetting *setting,
Packit fabffb
                                                   const char * const *known_props)
Packit fabffb
{
Packit fabffb
	gs_free GParamSpec **property_specs = NULL;
Packit fabffb
	GParamSpec *prop_spec;
Packit fabffb
	guint n_property_specs;
Packit fabffb
	guint i;
Packit fabffb
	char tmp[1024];
Packit fabffb
Packit fabffb
	if (!setting)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting),
Packit fabffb
	                                                 &n_property_specs);
Packit fabffb
	for (i = 0; i < n_property_specs; i++) {
Packit fabffb
		prop_spec = property_specs[i];
Packit fabffb
		if (   !g_strv_contains (known_props, prop_spec->name)
Packit fabffb
		    && !nm_streq0 (prop_spec->name, NM_SETTING_NAME)) {
Packit fabffb
			nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
Packit fabffb
Packit fabffb
			g_value_init (&value, prop_spec->value_type);
Packit fabffb
			g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
Packit fabffb
			if (!g_param_value_defaults (prop_spec, &value)) {
Packit fabffb
				nm_sprintf_buf (tmp, "%s.%s", nm_setting_get_name (setting), prop_spec->name);
Packit fabffb
				nm_connection_editor_add_unsupported_property (editor, tmp);
Packit fabffb
			}
Packit fabffb
		}
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
nm_connection_editor_set_connection (NMConnectionEditor *editor,
Packit fabffb
                                     NMConnection *orig_connection,
Packit fabffb
                                     GError **error)
Packit fabffb
{
Packit fabffb
	NMSettingConnection *s_con;
Packit fabffb
	const char *connection_type;
Packit fabffb
	const char *slave_type;
Packit fabffb
	gboolean success = FALSE;
Packit fabffb
	GSList *iter, *copy;
Packit fabffb
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), FALSE);
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION (orig_connection), FALSE);
Packit fabffb
Packit fabffb
	/* clean previous connection */
Packit fabffb
	if (editor->connection)
Packit fabffb
		g_object_unref (editor->connection);
Packit fabffb
Packit fabffb
	editor->connection = nm_simple_connection_new_clone (orig_connection);
Packit fabffb
Packit fabffb
	editor->orig_connection = g_object_ref (orig_connection);
Packit fabffb
	nm_connection_editor_update_title (editor);
Packit fabffb
Packit fabffb
	/* Handle CA cert ignore stuff */
Packit fabffb
	eap_method_ca_cert_ignore_load (editor->connection);
Packit fabffb
Packit fabffb
	s_con = nm_connection_get_setting_connection (editor->connection);
Packit fabffb
	g_assert (s_con);
Packit fabffb
Packit fabffb
	connection_type = nm_setting_connection_get_connection_type (s_con);
Packit fabffb
	if (!add_page (editor, ce_page_general_new, editor->connection, error))
Packit fabffb
		goto out;
Packit fabffb
	if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_ethernet_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_8021x_security_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_dcb_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_wifi_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_wifi_security_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_vpn_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_ip_tunnel_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_dsl_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_ppp_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_GSM_SETTING_NAME) || 
Packit fabffb
	           !strcmp (connection_type, NM_SETTING_CDMA_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_mobile_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_ppp_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
Packit fabffb
		NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (editor->connection);
Packit fabffb
		const char *type = nm_setting_bluetooth_get_connection_type (s_bt);
Packit fabffb
		g_assert (type);
Packit fabffb
Packit fabffb
		if (!add_page (editor, ce_page_bluetooth_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!g_strcmp0 (type, "dun")) {
Packit fabffb
			if (!add_page (editor, ce_page_mobile_new, editor->connection, error))
Packit fabffb
				goto out;
Packit fabffb
			if (!add_page (editor, ce_page_ppp_new, editor->connection, error))
Packit fabffb
				goto out;
Packit fabffb
		}
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_infiniband_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_BOND_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_bond_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_TEAM_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_team_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_bridge_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_VLAN_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_vlan_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!strcmp (connection_type, NM_SETTING_MACSEC_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_macsec_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
		if (!add_page (editor, ce_page_8021x_security_new, editor->connection, error))
Packit fabffb
			goto out;
Packit Service 639700
	} else if (!strcmp (connection_type, NM_SETTING_WIREGUARD_SETTING_NAME)) {
Packit Service 639700
		if (!add_page (editor, ce_page_wireguard_new, editor->connection, error))
Packit Service 639700
			goto out;
Packit fabffb
	} else {
Packit fabffb
		g_warning ("Unhandled setting type '%s'", connection_type);
Packit fabffb
	}
Packit fabffb
Packit fabffb
	slave_type = nm_setting_connection_get_slave_type (s_con);
Packit fabffb
	if (!g_strcmp0 (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_team_port_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	} else if (!g_strcmp0 (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
Packit fabffb
		if (!add_page (editor, ce_page_bridge_port_new, editor->connection, error))
Packit fabffb
			goto out;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	if (   nm_connection_get_setting_proxy (editor->connection)
Packit fabffb
	    && !add_page (editor, ce_page_proxy_new, editor->connection, error))
Packit fabffb
		goto out;
Packit fabffb
	if (   nm_connection_get_setting_ip4_config (editor->connection)
Packit fabffb
	    && !add_page (editor, ce_page_ip4_new, editor->connection, error))
Packit fabffb
		goto out;
Packit fabffb
	if (   nm_connection_get_setting_ip6_config (editor->connection)
Packit fabffb
	    && !add_page (editor, ce_page_ip6_new, editor->connection, error))
Packit fabffb
		goto out;
Packit fabffb
Packit fabffb
	/* After all pages are created, then kick off secrets requests that any
Packit fabffb
	 * the pages may need to make; if they don't need any secrets, then let
Packit fabffb
	 * them finish initialization.  The list might get modified during the loop
Packit fabffb
	 * which is why copy the list here.
Packit fabffb
	 */
Packit fabffb
	copy = g_slist_copy (editor->initializing_pages);
Packit fabffb
	for (iter = copy; iter; iter = g_slist_next (iter)) {
Packit fabffb
		CEPage *page = CE_PAGE (iter->data);
Packit fabffb
		const char *setting_name = g_object_get_data (G_OBJECT (page), SECRETS_TAG);
Packit fabffb
Packit fabffb
		if (!setting_name) {
Packit fabffb
			/* page doesn't need any secrets */
Packit fabffb
			ce_page_complete_init (page, NULL, NULL, NULL);
Packit fabffb
		} else if (!NM_IS_REMOTE_CONNECTION (editor->orig_connection)) {
Packit fabffb
			/* We want to get secrets using ->orig_connection, since that's the
Packit fabffb
			 * remote connection which can actually respond to secrets requests.
Packit fabffb
			 * ->connection is a plain NMConnection copy of ->orig_connection
Packit fabffb
			 * which is what gets changed when users modify anything.  But when
Packit fabffb
			 * creating or importing, ->orig_connection will be an NMConnection
Packit fabffb
			 * since the new connection hasn't been added to NetworkManager yet.
Packit fabffb
			 * So basically, skip requesting secrets if the connection can't
Packit fabffb
			 * handle a secrets request.
Packit fabffb
			 */
Packit fabffb
			ce_page_complete_init (page, setting_name, NULL, NULL);
Packit fabffb
		} else {
Packit fabffb
			/* Page wants secrets, get them */
Packit fabffb
			get_secrets_for_page (editor, page, setting_name);
Packit fabffb
		}
Packit fabffb
		g_object_set_data (G_OBJECT (page), SECRETS_TAG, NULL);
Packit fabffb
	}
Packit fabffb
	g_slist_free (copy);
Packit fabffb
Packit fabffb
	/* set the UI */
Packit fabffb
	recheck_initialization (editor);
Packit fabffb
	success = TRUE;
Packit fabffb
Packit fabffb
out:
Packit fabffb
	return success;
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_present (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	g_return_if_fail (NM_IS_CONNECTION_EDITOR (editor));
Packit fabffb
Packit fabffb
	gtk_window_present (GTK_WINDOW (editor->window));
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
cancel_button_clicked_cb (GtkWidget *widget, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
Packit fabffb
	/* If the dialog is busy waiting for authorization or something,
Packit fabffb
	 * don't destroy it until authorization returns.
Packit fabffb
	 */
Packit fabffb
	if (self->busy)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	g_signal_emit (self, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_CANCEL);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
editor_closed_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
Packit fabffb
{
Packit fabffb
	cancel_button_clicked_cb (widget, user_data);
Packit fabffb
}
Packit fabffb
Packit fabffb
static gboolean
Packit fabffb
key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
Packit fabffb
{
Packit fabffb
	if (event->keyval == GDK_KEY_Escape) {
Packit fabffb
		gtk_window_close (GTK_WINDOW (widget));
Packit fabffb
		return TRUE;
Packit fabffb
	}
Packit fabffb
Packit fabffb
	return FALSE;
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
added_connection_cb (GObject *client,
Packit fabffb
                     GAsyncResult *result,
Packit fabffb
                     gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = user_data;
Packit fabffb
	NMRemoteConnection *connection;
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	nm_connection_editor_set_busy (self, FALSE);
Packit fabffb
Packit fabffb
	connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
Packit fabffb
	if (error) {
Packit fabffb
		nm_connection_editor_error (self->parent_window, _("Connection add failed"),
Packit fabffb
		                            "%s", error->message);
Packit fabffb
		/* Leave the editor open */
Packit fabffb
		return;
Packit fabffb
	}
Packit fabffb
	g_clear_object (&connection);
Packit fabffb
	g_clear_error (&error);
Packit fabffb
Packit fabffb
	g_signal_emit (self, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_OK);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
update_complete (NMConnectionEditor *self, GError *error)
Packit fabffb
{
Packit fabffb
	nm_connection_editor_set_busy (self, FALSE);
Packit fabffb
	g_signal_emit (self, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_OK);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
updated_connection_cb (GObject *connection,
Packit fabffb
                       GAsyncResult *result,
Packit fabffb
                       gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
Packit fabffb
	                                            result, &error);
Packit fabffb
Packit fabffb
	/* Clear secrets so they don't lay around in memory; they'll get requested
Packit fabffb
	 * again anyway next time the connection is edited.
Packit fabffb
	 */
Packit fabffb
	nm_connection_clear_secrets (NM_CONNECTION (connection));
Packit fabffb
Packit fabffb
	update_complete (self, error);
Packit fabffb
	g_clear_error (&error);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
ok_button_clicked_save_connection (NMConnectionEditor *self)
Packit fabffb
{
Packit fabffb
	/* Copy the modified connection to the original connection */
Packit fabffb
	nm_connection_replace_settings_from_connection (self->orig_connection,
Packit fabffb
	                                                self->connection);
Packit fabffb
	nm_connection_editor_set_busy (self, TRUE);
Packit fabffb
Packit fabffb
	/* Save new CA cert ignore values to GSettings */
Packit fabffb
	eap_method_ca_cert_ignore_save (self->connection);
Packit fabffb
Packit fabffb
	if (self->is_new_connection) {
Packit fabffb
		nm_client_add_connection_async (self->client,
Packit fabffb
		                                self->orig_connection,
Packit fabffb
		                                TRUE,
Packit fabffb
		                                NULL,
Packit fabffb
		                                added_connection_cb,
Packit fabffb
		                                self);
Packit fabffb
	} else {
Packit fabffb
		nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (self->orig_connection),
Packit fabffb
		                                           TRUE, NULL, updated_connection_cb, self);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
ok_button_clicked_cb (GtkWidget *widget, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
	GSList *iter;
Packit fabffb
Packit fabffb
	/* If the dialog is busy waiting for authorization or something,
Packit fabffb
	 * don't destroy it until authorization returns.
Packit fabffb
	 */
Packit fabffb
	if (self->busy)
Packit fabffb
		return;
Packit fabffb
Packit fabffb
	/* Validate one last time to ensure all pages update the connection */
Packit fabffb
	connection_editor_validate (self);
Packit fabffb
Packit fabffb
	/* Perform page specific actions before the connection is saved */
Packit fabffb
	for (iter = self->pages; iter; iter = g_slist_next (iter))
Packit fabffb
		ce_page_last_update (CE_PAGE (iter->data), self->connection, NULL);
Packit fabffb
Packit fabffb
	ok_button_clicked_save_connection (self);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
vpn_export_get_secrets_cb (GObject *object,
Packit fabffb
                           GAsyncResult *result,
Packit fabffb
                           gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnection *tmp;
Packit fabffb
	GVariant *secrets;
Packit fabffb
	GError *error = NULL;
Packit fabffb
Packit fabffb
	secrets = nm_remote_connection_get_secrets_finish (NM_REMOTE_CONNECTION (object),
Packit fabffb
	                                                   result, &error);
Packit fabffb
Packit fabffb
	/* We don't really care about errors; if the user couldn't authenticate
Packit fabffb
	 * then just let them export everything except secrets.  Duplicate the
Packit fabffb
	 * connection so that we don't let secrets sit around in the original
Packit fabffb
	 * one.
Packit fabffb
	 */
Packit fabffb
	tmp = nm_simple_connection_new_clone (NM_CONNECTION (object));
Packit fabffb
	g_assert (tmp);
Packit fabffb
	if (secrets)
Packit fabffb
		nm_connection_update_secrets (tmp, NM_SETTING_VPN_SETTING_NAME, secrets, NULL);
Packit fabffb
	vpn_export (tmp);
Packit fabffb
	g_object_unref (tmp);
Packit fabffb
	if (secrets)
Packit fabffb
		g_variant_ref (secrets);
Packit fabffb
	g_clear_error (&error);
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
export_button_clicked_cb (GtkWidget *widget, gpointer user_data)
Packit fabffb
{
Packit fabffb
	NMConnectionEditor *self = NM_CONNECTION_EDITOR (user_data);
Packit fabffb
Packit fabffb
	if (NM_IS_REMOTE_CONNECTION (self->orig_connection)) {
Packit fabffb
		/* Grab secrets if we can */
Packit fabffb
		nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (self->orig_connection),
Packit fabffb
		                                        NM_SETTING_VPN_SETTING_NAME,
Packit fabffb
		                                        NULL,
Packit fabffb
		                                        vpn_export_get_secrets_cb,
Packit fabffb
		                                        self);
Packit fabffb
	} else
Packit fabffb
		vpn_export (self->connection);
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_run (NMConnectionEditor *self)
Packit fabffb
{
Packit fabffb
	g_return_if_fail (NM_IS_CONNECTION_EDITOR (self));
Packit fabffb
Packit fabffb
	g_signal_connect (G_OBJECT (self->window), "delete-event",
Packit fabffb
	                  G_CALLBACK (editor_closed_cb), self);
Packit fabffb
	g_signal_connect (G_OBJECT (self->window), "key-press-event",
Packit fabffb
	                  G_CALLBACK (key_press_cb), self);
Packit fabffb
Packit fabffb
	g_signal_connect (G_OBJECT (self->ok_button), "clicked",
Packit fabffb
	                  G_CALLBACK (ok_button_clicked_cb), self);
Packit fabffb
	g_signal_connect (G_OBJECT (self->cancel_button), "clicked",
Packit fabffb
	                  G_CALLBACK (cancel_button_clicked_cb), self);
Packit fabffb
	g_signal_connect (G_OBJECT (self->export_button), "clicked",
Packit fabffb
	                  G_CALLBACK (export_button_clicked_cb), self);
Packit fabffb
	g_signal_connect (G_OBJECT (self->relabel_button), "clicked",
Packit fabffb
	                  G_CALLBACK (relabel_button_clicked_cb), self);
Packit fabffb
Packit fabffb
	nm_connection_editor_present (self);
Packit fabffb
}
Packit fabffb
Packit fabffb
GtkWindow *
Packit fabffb
nm_connection_editor_get_window (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), NULL);
Packit fabffb
Packit fabffb
	return GTK_WINDOW (editor->window);
Packit fabffb
}
Packit fabffb
Packit fabffb
gboolean
Packit fabffb
nm_connection_editor_get_busy (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), FALSE);
Packit fabffb
Packit fabffb
	return editor->busy;
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_set_busy (NMConnectionEditor *editor, gboolean busy)
Packit fabffb
{
Packit fabffb
	g_return_if_fail (NM_IS_CONNECTION_EDITOR (editor));
Packit fabffb
Packit fabffb
	if (busy != editor->busy) {
Packit fabffb
		editor->busy = busy;
Packit fabffb
		gtk_widget_set_sensitive (editor->window, !busy);
Packit fabffb
	}
Packit fabffb
}
Packit fabffb
Packit fabffb
static void
Packit fabffb
nm_connection_editor_dialog (GtkWindow *parent, GtkMessageType type, const char *heading,
Packit fabffb
                             const char *message)
Packit fabffb
{
Packit fabffb
	GtkWidget *dialog;
Packit fabffb
Packit fabffb
	dialog = gtk_message_dialog_new (parent,
Packit fabffb
	                                 GTK_DIALOG_DESTROY_WITH_PARENT,
Packit fabffb
	                                 type,
Packit fabffb
	                                 GTK_BUTTONS_CLOSE,
Packit fabffb
	                                 "%s", heading);
Packit fabffb
Packit fabffb
	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", message);
Packit fabffb
Packit fabffb
	gtk_widget_show_all (dialog);
Packit fabffb
	gtk_window_present (GTK_WINDOW (dialog));
Packit fabffb
	gtk_dialog_run (GTK_DIALOG (dialog));
Packit fabffb
	gtk_widget_destroy (dialog);
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_error (GtkWindow *parent, const char *heading, const char *format, ...)
Packit fabffb
{
Packit fabffb
	va_list args;
Packit fabffb
	gs_free char *message = NULL;
Packit fabffb
Packit fabffb
	va_start (args, format);
Packit fabffb
	message = g_strdup_vprintf (format, args);
Packit fabffb
	va_end (args);
Packit fabffb
	nm_connection_editor_dialog (parent, GTK_MESSAGE_ERROR, heading, message);
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_warning (GtkWindow *parent, const char *heading, const char *format, ...)
Packit fabffb
{
Packit fabffb
	va_list args;
Packit fabffb
	gs_free char *message = NULL;
Packit fabffb
Packit fabffb
	va_start (args, format);
Packit fabffb
	message = g_strdup_vprintf (format, args);
Packit fabffb
	va_end (args);
Packit fabffb
	nm_connection_editor_dialog (parent, GTK_MESSAGE_WARNING, heading, message);
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_inter_page_set_value (NMConnectionEditor *editor, InterPageChangeType type, gpointer value)
Packit fabffb
{
Packit fabffb
	g_hash_table_insert (editor->inter_page_hash, GUINT_TO_POINTER (type), value);
Packit fabffb
}
Packit fabffb
Packit fabffb
gboolean
Packit fabffb
nm_connection_editor_inter_page_get_value (NMConnectionEditor *editor, InterPageChangeType type, gpointer *value)
Packit fabffb
{
Packit fabffb
	return g_hash_table_lookup_extended (editor->inter_page_hash, GUINT_TO_POINTER (type), NULL, value);
Packit fabffb
}
Packit fabffb
Packit fabffb
void
Packit fabffb
nm_connection_editor_inter_page_clear_data (NMConnectionEditor *editor)
Packit fabffb
{
Packit fabffb
	g_hash_table_remove_all (editor->inter_page_hash);
Packit fabffb
}
Packit fabffb