nm-connection-editor.c 31.4 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
Rodrigo Moya's avatar
Rodrigo Moya committed
2 3 4
/* NetworkManager Connection editor -- Connection editor for NetworkManager
 *
 * Rodrigo Moya <rodrigo@gnome-db.org>
5 6
 * Dan Williams <dcbw@redhat.com>
 * Tambet Ingo <tambet@gmail.com>
Rodrigo Moya's avatar
Rodrigo Moya committed
7 8 9 10 11 12 13 14 15 16 17
 *
 * 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.
 *
18 19 20
 * 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.
Rodrigo Moya's avatar
Rodrigo Moya committed
21
 *
22
 * (C) Copyright 2007 - 2011 Red Hat, Inc.
23
 * (C) Copyright 2007 - 2008 Novell, Inc.
Rodrigo Moya's avatar
Rodrigo Moya committed
24 25
 */

26 27
#include "config.h"

28
#include <string.h>
29 30
#include <sys/types.h>
#include <unistd.h>
31

32
#include <gtk/gtk.h>
33
#include <gdk/gdkx.h>
34 35
#include <glib/gi18n.h>

36 37
#include <nm-setting-connection.h>
#include <nm-setting-ip4-config.h>
Dan Winship's avatar
Dan Winship committed
38
#include <nm-setting-ip6-config.h>
39
#include <nm-setting-wired.h>
40
#include <nm-setting-8021x.h>
41
#include <nm-setting-wireless.h>
42
#include <nm-setting-wireless-security.h>
43
#include <nm-setting-vpn.h>
Tambet Ingo's avatar
Tambet Ingo committed
44
#include <nm-setting-pppoe.h>
45
#include <nm-setting-ppp.h>
Tambet Ingo's avatar
Tambet Ingo committed
46 47
#include <nm-setting-gsm.h>
#include <nm-setting-cdma.h>
48
#include <nm-setting-wimax.h>
49
#include <nm-setting-infiniband.h>
50
#include <nm-utils.h>
51

Dan Williams's avatar
Dan Williams committed
52 53
#include <nm-remote-connection.h>

Rodrigo Moya's avatar
Rodrigo Moya committed
54
#include "nm-connection-editor.h"
55
#include "nma-marshal.h"
56

57
#include "ce-page.h"
58 59 60 61
#include "page-ethernet.h"
#include "page-8021x-security.h"
#include "page-wifi.h"
#include "page-wifi-security.h"
62
#include "page-ip4.h"
Dan Winship's avatar
Dan Winship committed
63
#include "page-ip6.h"
Tambet Ingo's avatar
Tambet Ingo committed
64
#include "page-dsl.h"
Tambet Ingo's avatar
Tambet Ingo committed
65
#include "page-mobile.h"
66
#include "page-ppp.h"
67
#include "page-vpn.h"
68
#include "page-wimax.h"
69
#include "page-infiniband.h"
70
#include "ce-polkit-button.h"
71
#include "vpn-helpers.h"
Rodrigo Moya's avatar
Rodrigo Moya committed
72 73 74

G_DEFINE_TYPE (NMConnectionEditor, nm_connection_editor, G_TYPE_OBJECT)

75 76 77 78 79 80 81
enum {
	EDITOR_DONE,
	EDITOR_LAST_SIGNAL
};

static guint editor_signals[EDITOR_LAST_SIGNAL] = { 0 };

82 83 84
static gboolean nm_connection_editor_set_connection (NMConnectionEditor *editor,
                                                     NMConnection *connection,
                                                     GError **error);
Rodrigo Moya's avatar
Rodrigo Moya committed
85

86
struct GetSecretsInfo {
87 88 89 90
	NMConnectionEditor *self;
	CEPage *page;
	char *setting_name;
	gboolean canceled;
91
};
92

93 94 95 96
static void
nm_connection_editor_update_title (NMConnectionEditor *editor)
{
	NMSettingConnection *s_con;
97
	const char *id;
98 99 100

	g_return_if_fail (editor != NULL);

101
	s_con = nm_connection_get_setting_connection (editor->connection);
102 103
	g_assert (s_con);

104 105 106
	id = nm_setting_connection_get_id (s_con);
	if (id && strlen (id)) {
		char *title = g_strdup_printf (_("Editing %s"), id);
107
		gtk_window_set_title (GTK_WINDOW (editor->window), title);
108 109
		g_free (title);
	} else
110
		gtk_window_set_title (GTK_WINDOW (editor->window), _("Editing un-named connection"));
111 112
}

113 114
static gboolean
ui_to_setting (NMConnectionEditor *editor)
115
{
116
	NMSettingConnection *s_con;
117 118
	GtkWidget *widget;
	const char *name;
119
	gboolean autoconnect = FALSE, everyone = FALSE;
120

121
	s_con = nm_connection_get_setting_connection (editor->connection);
122
	g_assert (s_con);
123

124
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
125 126
	name = gtk_entry_get_text (GTK_ENTRY (widget));

127 128 129
	g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, name, NULL);
	nm_connection_editor_update_title (editor);

130
	if (!name || !strlen (name))
131 132
		return FALSE;

133
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_autoconnect"));
134 135 136
	autoconnect = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
	g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_AUTOCONNECT, autoconnect, NULL);

137 138 139 140 141 142
	/* Handle visibility */
	everyone = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->all_checkbutton));
	g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_PERMISSIONS, NULL, NULL);
	if (everyone == FALSE) {
		/* Only visible to this user */
		nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
Dan Williams's avatar
Dan Williams committed
143
	}
144 145 146 147

	return TRUE;
}

148 149 150 151 152 153
static gboolean
editor_is_initialized (NMConnectionEditor *editor)
{
	return (g_slist_length (editor->initializing_pages) == 0);
}

154
static void
155
update_sensitivity (NMConnectionEditor *editor)
156
{
157 158 159
	NMSettingConnection *s_con;
	gboolean actionable = FALSE, authorized = FALSE, sensitive = FALSE;
	GtkWidget *widget;
160 161
	GSList *iter;

162
	s_con = nm_connection_get_setting_connection (editor->connection);
163

164 165 166
	/* Can't modify read-only connections; can't modify anything before the
	 * editor is initialized either.
	 */
167 168
	if (   !nm_setting_connection_get_read_only (s_con)
	    && editor_is_initialized (editor)) {
Dan Williams's avatar
Dan Williams committed
169
		if (editor->can_modify) {
170
			actionable = ce_polkit_button_get_actionable (CE_POLKIT_BUTTON (editor->ok_button));
171
			authorized = ce_polkit_button_get_authorized (CE_POLKIT_BUTTON (editor->ok_button));
172
		}
173

Dan Williams's avatar
Dan Williams committed
174 175 176 177
		/* If the user cannot ever be authorized to change system connections,
		 * we desensitize the entire dialog.
		 */
		sensitive = authorized;
178
	}
179

Dan Williams's avatar
Dan Williams committed
180
	gtk_widget_set_sensitive (GTK_WIDGET (editor->all_checkbutton), actionable && authorized);
181 182 183 184

	/* Cancel button is always sensitive */
	gtk_widget_set_sensitive (GTK_WIDGET (editor->cancel_button), TRUE);

185
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name_label"));
186 187
	gtk_widget_set_sensitive (widget, sensitive);

188
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
189 190
	gtk_widget_set_sensitive (widget, sensitive);

191
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_autoconnect"));
192 193
	gtk_widget_set_sensitive (widget, sensitive);

194
	widget = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
195 196 197 198 199 200 201 202 203
	gtk_widget_set_sensitive (widget, sensitive);

	for (iter = editor->pages; iter; iter = g_slist_next (iter)) {
		widget = ce_page_get_page (CE_PAGE (iter->data));
		gtk_widget_set_sensitive (widget, sensitive);
	}
}

static void
204
connection_editor_validate (NMConnectionEditor *editor)
205
{
206
	NMSettingConnection *s_con;
207
	gboolean valid = FALSE, printed = FALSE;
208 209
	GSList *iter;

210
	if (!editor_is_initialized (editor))
211
		goto done;
212

213
	s_con = nm_connection_get_setting_connection (editor->connection);
214 215 216 217 218 219 220
	g_assert (s_con);
	if (nm_setting_connection_get_read_only (s_con))
		goto done;

	if (!ui_to_setting (editor))
		goto done;

221
	valid = TRUE;
222 223
	for (iter = editor->pages; iter; iter = g_slist_next (iter)) {
		GError *error = NULL;
224

225
		if (!ce_page_validate (CE_PAGE (iter->data), editor->connection, &error)) {
226
			valid = FALSE;
227

228 229 230 231 232 233 234 235 236
			/* FIXME: use the error to indicate which UI widgets are invalid */
			if (!printed) {
				printed = TRUE;
				if (error) {
					g_warning ("Invalid setting %s: %s", CE_PAGE (iter->data)->title, error->message);
					g_error_free (error);
				} else
					g_warning ("Invalid setting %s", CE_PAGE (iter->data)->title);
			}
237
		}
238
	}
239 240 241

done:
	ce_polkit_button_set_master_sensitive (CE_POLKIT_BUTTON (editor->ok_button), valid);
242
	gtk_widget_set_sensitive (editor->export_button, valid);
243 244 245 246 247 248 249 250 251
	update_sensitivity (editor);
}

static void
ok_button_actionable_cb (GtkWidget *button,
                         gboolean actionable,
                         NMConnectionEditor *editor)
{
	connection_editor_validate (editor);
252 253 254
}

static void
Dan Williams's avatar
Dan Williams committed
255 256 257 258
permissions_changed_cb (NMClient *client,
	                    NMClientPermission permission,
	                    NMClientPermissionResult result,                       
                        NMConnectionEditor *editor)
259
{
260
	if (permission != NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM)
Dan Williams's avatar
Dan Williams committed
261 262 263 264 265 266 267
		return;

	if (result == NM_CLIENT_PERMISSION_RESULT_YES || result == NM_CLIENT_PERMISSION_RESULT_AUTH)
		editor->can_modify = TRUE;
	else
		editor->can_modify = FALSE;

268 269 270 271
	connection_editor_validate (editor);
}

static void
Dan Williams's avatar
Dan Williams committed
272
all_checkbutton_toggled_cb (GtkWidget *widget, NMConnectionEditor *editor)
273 274
{
	connection_editor_validate (editor);
275 276
}

Rodrigo Moya's avatar
Rodrigo Moya committed
277 278 279
static void
nm_connection_editor_init (NMConnectionEditor *editor)
{
280
	GtkWidget *dialog;
281
	GError *error = NULL;
282
	const char *objects[] = { "nm-connection-editor", NULL };
283

284
	editor->builder = gtk_builder_new ();
285

286 287 288 289 290
	if (!gtk_builder_add_objects_from_file (editor->builder,
	                                        UIDIR "/nm-connection-editor.ui",
	                                        (char **) objects,
	                                        &error)) {
		g_warning ("Couldn't load builder file " UIDIR "/nm-connection-editor.ui: %s", error->message);
291 292
		g_error_free (error);

293 294 295 296
		dialog = gtk_message_dialog_new (NULL, 0,
		                                 GTK_MESSAGE_ERROR,
		                                 GTK_BUTTONS_OK,
		                                 "%s",
297
		                                 _("The connection editor could not find some required resources (the .ui file was not found)."));
298 299 300 301 302
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		gtk_main_quit ();
		return;
	}
Rodrigo Moya's avatar
Rodrigo Moya committed
303

304 305
	editor->window = GTK_WIDGET (gtk_builder_get_object (editor->builder, "nm-connection-editor"));
	editor->cancel_button = GTK_WIDGET (gtk_builder_get_object (editor->builder, "cancel_button"));
306
	editor->export_button = GTK_WIDGET (gtk_builder_get_object (editor->builder, "export_button"));
Dan Williams's avatar
Dan Williams committed
307
	editor->all_checkbutton = GTK_WIDGET (gtk_builder_get_object (editor->builder, "system_checkbutton"));
Rodrigo Moya's avatar
Rodrigo Moya committed
308 309
}

310 311 312 313 314 315 316
static void
get_secrets_info_free (GetSecretsInfo *info)
{
	g_free (info->setting_name);
	g_free (info);
}

Rodrigo Moya's avatar
Rodrigo Moya committed
317
static void
318
dispose (GObject *object)
Rodrigo Moya's avatar
Rodrigo Moya committed
319 320
{
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (object);
321
	GSList *iter;
Rodrigo Moya's avatar
Rodrigo Moya committed
322

323 324
	if (editor->disposed)
		goto out;
325 326 327 328 329 330
	editor->disposed = TRUE;

	g_slist_foreach (editor->initializing_pages, (GFunc) g_object_unref, NULL);
	g_slist_free (editor->initializing_pages);
	editor->initializing_pages = NULL;

331 332 333 334
	g_slist_foreach (editor->pages, (GFunc) g_object_unref, NULL);
	g_slist_free (editor->pages);
	editor->pages = NULL;

335 336 337 338 339 340 341
	/* Mark any in-progress secrets call as canceled; it will clean up after itself. */
	if (editor->secrets_call)
		editor->secrets_call->canceled = TRUE;

	/* Kill any pending secrets calls */
	for (iter = editor->pending_secrets_calls; iter; iter = g_slist_next (iter)) {
		get_secrets_info_free ((GetSecretsInfo *) iter->data);
342
	}
343 344
	g_slist_free (editor->pending_secrets_calls);
	editor->pending_secrets_calls = NULL;
345

346
	if (editor->connection) {
347 348 349
		g_object_unref (editor->connection);
		editor->connection = NULL;
	}
350 351 352 353
	if (editor->orig_connection) {
		g_object_unref (editor->orig_connection);
		editor->orig_connection = NULL;
	}
354
	if (editor->window) {
355
		gtk_widget_destroy (editor->window);
356 357
		editor->window = NULL;
	}
358 359 360
	if (editor->builder) {
		g_object_unref (editor->builder);
		editor->builder = NULL;
361
	}
362

363 364 365 366
	g_signal_handler_disconnect (editor->client, editor->permission_id);
	g_object_unref (editor->client);

out:
367
	G_OBJECT_CLASS (nm_connection_editor_parent_class)->dispose (object);
Rodrigo Moya's avatar
Rodrigo Moya committed
368 369 370 371 372 373 374 375
}

static void
nm_connection_editor_class_init (NMConnectionEditorClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	/* virtual methods */
376
	object_class->dispose = dispose;
377 378 379 380

	/* Signals */
	editor_signals[EDITOR_DONE] =
		g_signal_new ("done",
381 382 383 384
		              G_OBJECT_CLASS_TYPE (object_class),
		              G_SIGNAL_RUN_FIRST,
		              G_STRUCT_OFFSET (NMConnectionEditorClass, done),
		              NULL, NULL,
385
		              _nma_marshal_VOID__INT_POINTER,
386
		              G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
Rodrigo Moya's avatar
Rodrigo Moya committed
387 388 389
}

NMConnectionEditor *
390
nm_connection_editor_new (NMConnection *connection,
Dan Williams's avatar
Dan Williams committed
391
                          NMClient *client,
392
                          GError **error)
Rodrigo Moya's avatar
Rodrigo Moya committed
393 394
{
	NMConnectionEditor *editor;
395
	GtkWidget *hbox;
Rodrigo Moya's avatar
Rodrigo Moya committed
396

397
	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
398

Rodrigo Moya's avatar
Rodrigo Moya committed
399
	editor = g_object_new (NM_TYPE_CONNECTION_EDITOR, NULL);
400
	if (!editor) {
Dan Winship's avatar
Dan Winship committed
401
		g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "%s", _("Error creating connection editor dialog."));
402
		return NULL;
403
	}
404

405 406
	editor->client = g_object_ref (client);

407
	editor->can_modify = nm_client_get_permission_result (client, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM);
408 409 410 411
	editor->permission_id = g_signal_connect (editor->client,
	                                          "permission-changed",
	                                          G_CALLBACK (permissions_changed_cb),
	                                          editor);
412

413
	editor->ok_button = ce_polkit_button_new (_("_Save"),
414
	                                          _("Save any changes made to this connection."),
415
	                                          _("_Save..."),
416 417
	                                          _("Authenticate to save this connection for all users of this machine."),
	                                          GTK_STOCK_APPLY,
Dan Williams's avatar
Dan Williams committed
418
	                                          client,
419
	                                          NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM);
420
	gtk_button_set_use_underline (GTK_BUTTON (editor->ok_button), TRUE);
421 422 423 424 425

	g_signal_connect (editor->ok_button, "actionable",
	                  G_CALLBACK (ok_button_actionable_cb), editor);
	g_signal_connect (editor->ok_button, "authorized",
	                  G_CALLBACK (ok_button_actionable_cb), editor);
426
	hbox = GTK_WIDGET (gtk_builder_get_object (editor->builder, "action_area_hbox"));
427 428 429
	gtk_box_pack_end (GTK_BOX (hbox), editor->ok_button, TRUE, TRUE, 0);
	gtk_widget_show_all (editor->ok_button);

430 431 432 433
	if (!nm_connection_editor_set_connection (editor, connection, error)) {
		g_object_unref (editor);
		return NULL;
	}
Rodrigo Moya's avatar
Rodrigo Moya committed
434 435 436 437

	return editor;
}

438
NMConnection *
Rodrigo Moya's avatar
Rodrigo Moya committed
439 440 441 442
nm_connection_editor_get_connection (NMConnectionEditor *editor)
{
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), NULL);

443 444 445
	return editor->orig_connection;
}

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
static void
update_secret_flags (NMSetting *setting,
                     const char *key,
                     const GValue *value,
                     GParamFlags flags,
                     gpointer user_data)
{
	gboolean everyone = !!GPOINTER_TO_UINT (user_data);
	NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;

	if (!(flags & NM_SETTING_PARAM_SECRET))
		return;

	/* VPN connections never get changed */
	if (NM_IS_SETTING_VPN (setting))
		return;

	/* 802.1x passwords don't get changed either */
	if (NM_IS_SETTING_802_1X (setting)) {
		if (   g_strcmp0 (key, NM_SETTING_802_1X_PASSWORD) == 0
		    || g_strcmp0 (key, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD) == 0
		    || g_strcmp0 (key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD) == 0)
			return;
	}

	nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
	if (everyone)
		secret_flags &= ~NM_SETTING_SECRET_FLAG_AGENT_OWNED;
	else
		secret_flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;

	nm_setting_set_secret_flags (setting, key, secret_flags, NULL);
}

480 481 482 483
gboolean
nm_connection_editor_update_connection (NMConnectionEditor *editor, GError **error)
{
	GHashTable *settings;
484
	gboolean everyone = FALSE;
485 486 487 488 489 490

	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), FALSE);

	if (!nm_connection_verify (editor->connection, error))
		return FALSE;

491 492 493 494 495 496 497
	/* Update secret flags at the end after all other settings have updated,
	 * otherwise the secret flags we set here might be overwritten during
	 * setting validation.
	 */
	everyone = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->all_checkbutton));
	nm_connection_for_each_setting_value (editor->connection, update_secret_flags, GUINT_TO_POINTER (everyone));

498
	/* Copy the modified connection to the original connection */
499
	settings = nm_connection_to_hash (editor->connection, NM_SETTING_HASH_FLAG_ALL);
500 501
	nm_connection_replace_settings (editor->orig_connection, settings, NULL);
	g_hash_table_destroy (settings);
502

503
	return TRUE;
504 505
}

Rodrigo Moya's avatar
Rodrigo Moya committed
506
static void
507
populate_connection_ui (NMConnectionEditor *editor)
Rodrigo Moya's avatar
Rodrigo Moya committed
508
{
509 510 511
	NMSettingConnection *s_con;
	GtkWidget *name;
	GtkWidget *autoconnect;
512
	gboolean system_connection = TRUE;
Rodrigo Moya's avatar
Rodrigo Moya committed
513

514 515
	name = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_name"));
	autoconnect = GTK_WIDGET (gtk_builder_get_object (editor->builder, "connection_autoconnect"));
516

517
	s_con = nm_connection_get_setting_connection (editor->connection);
518
	if (s_con) {
519 520 521 522 523
		const char *id = nm_setting_connection_get_id (s_con);

		gtk_entry_set_text (GTK_ENTRY (name), id);
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (autoconnect),
		                              nm_setting_connection_get_autoconnect (s_con));
524 525 526

		if (nm_setting_connection_get_num_permissions (s_con))
			system_connection = FALSE;
Rodrigo Moya's avatar
Rodrigo Moya committed
527
	} else {
528
		gtk_entry_set_text (GTK_ENTRY (name), NULL);
Rodrigo Moya's avatar
Rodrigo Moya committed
529
	}
530

531 532
	g_signal_connect_swapped (name, "changed", G_CALLBACK (connection_editor_validate), editor);
	g_signal_connect_swapped (autoconnect, "toggled", G_CALLBACK (connection_editor_validate), editor);
533

Dan Williams's avatar
Dan Williams committed
534
	g_signal_connect (editor->all_checkbutton, "toggled", G_CALLBACK (all_checkbutton_toggled_cb), editor);
535

536
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (editor->all_checkbutton), system_connection);
537

538
	connection_editor_validate (editor);
Rodrigo Moya's avatar
Rodrigo Moya committed
539 540
}

541 542 543 544 545
static void
page_changed (CEPage *page, gpointer user_data)
{
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);

546
	connection_editor_validate (editor);
547 548
}

549 550 551 552 553 554 555 556 557 558
static gboolean
idle_validate (gpointer user_data)
{
	connection_editor_validate (NM_CONNECTION_EDITOR (user_data));
	return FALSE;
}

static void
recheck_initialization (NMConnectionEditor *editor)
{
559
	if (!editor_is_initialized (editor) || editor->init_run)
560
		return;
561

562 563
	editor->init_run = TRUE;

564 565
	populate_connection_ui (editor);

566 567 568
	/* When everything is initialized, re-present the window to ensure it's on top */
	nm_connection_editor_present (editor);

569 570 571 572 573 574
	/* Validate the connection from an idle handler to ensure that stuff like
	 * GtkFileChoosers have had a chance to asynchronously find their files.
	 */
	g_idle_add (idle_validate, editor);
}

575
static void
576
page_initialized (CEPage *page, GError *error, gpointer user_data)
577 578
{
	NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
579
	GtkWidget *widget, *parent;
580
	GtkNotebook *notebook;
581
	GtkWidget *label;
582 583 584 585 586 587 588

	if (error) {
		gtk_widget_hide (editor->window);
		g_signal_emit (editor, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_NONE, error);
		return;
	}

589
	/* Add the page to the UI */
590
	notebook = GTK_NOTEBOOK (gtk_builder_get_object (editor->builder, "notebook"));
591 592
	label = gtk_label_new (ce_page_get_title (page));
	widget = ce_page_get_page (page);
593 594 595
	parent = gtk_widget_get_parent (widget);
	if (parent)
		gtk_container_remove (GTK_CONTAINER (parent), widget);
596
	gtk_notebook_append_page (notebook, widget, label);
597

598 599 600
	if (CE_IS_PAGE_VPN (page) && ce_page_vpn_can_export (CE_PAGE_VPN (page)))
		gtk_widget_show (editor->export_button);

601 602
	/* Move the page from the initializing list to the main page list */
	editor->initializing_pages = g_slist_remove (editor->initializing_pages, page);
603 604
	editor->pages = g_slist_append (editor->pages, page);

605 606 607
	recheck_initialization (editor);
}

608 609
static void request_secrets (GetSecretsInfo *info);

610
static void
Dan Williams's avatar
Dan Williams committed
611
get_secrets_cb (NMRemoteConnection *connection,
612 613 614 615 616
                GHashTable *secrets,
                GError *error,
                gpointer user_data)
{
	GetSecretsInfo *info = user_data;
617
	NMConnectionEditor *self;
618

619 620 621
	if (info->canceled) {
		get_secrets_info_free (info);
		return;
622
	}
623

624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
	self = info->self;

	/* Complete this secrets request; completion can actually dispose of the
	 * dialog if there was an error.
	 */
	self->secrets_call = NULL;
	ce_page_complete_init (info->page, info->setting_name, secrets, error);
	get_secrets_info_free (info);

	/* Kick off the next secrets request if there is one queued; if the dialog
	 * was disposed of by the completion above we don't need to do anything.
	 */
	if (!self->disposed && self->pending_secrets_calls) {
		self->secrets_call = g_slist_nth_data (self->pending_secrets_calls, 0);
		self->pending_secrets_calls = g_slist_remove (self->pending_secrets_calls, self->secrets_call);

		request_secrets (self->secrets_call);
	}
}

static void
request_secrets (GetSecretsInfo *info)
{
	g_return_if_fail (info != NULL);

Dan Williams's avatar
Dan Williams committed
649 650 651 652
	nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (info->self->orig_connection),
	                                  info->setting_name,
	                                  get_secrets_cb,
	                                  info);
653 654 655 656 657 658 659 660 661 662 663 664 665 666
}

static void
get_secrets_for_page (NMConnectionEditor *self,
                      CEPage *page,
                      const char *setting_name)
{
	GetSecretsInfo *info;

	info = g_malloc0 (sizeof (GetSecretsInfo));
	info->self = self;
	info->page = page;
	info->setting_name = g_strdup (setting_name);

667 668 669 670 671 672 673 674
	/* PolicyKit doesn't queue up authorization requests internally.  Instead,
	 * if there's a pending authorization request, subsequent requests for that
	 * same authorization will return NotAuthorized+Challenge.  That's pretty
	 * inconvenient and it would be a lot nicer if PK just queued up subsequent
	 * authorization requests and executed them when the first one was finished.
	 * But it since it doesn't do that, we have to serialize the authorization
	 * requests ourselves to get the right authorization result.
	 */
675 676 677
	/* NOTE: PolicyKit-gnome 0.95 now serializes auth requests as of this commit:
	 * http://git.gnome.org/cgit/PolicyKit-gnome/commit/?id=f32cb7faa7197b9db55b569677732742c3c7fdc1
	 */
678

679 680 681 682 683 684 685
	/* If there's already an in-progress call, queue up the new one */
	if (self->secrets_call)
		self->pending_secrets_calls = g_slist_append (self->pending_secrets_calls, info);
	else {
		/* Request secrets for this page */
		self->secrets_call = info;
		request_secrets (info);
686 687 688
	}
}

689 690
#define SECRETS_TAG "secrets-setting-name"

691 692 693 694 695
static gboolean
add_page (NMConnectionEditor *editor,
          CEPageNewFunc func,
          NMConnection *connection,
          GError **error)
696
{
697
	CEPage *page;
698
	const char *secrets_setting_name = NULL;
699

700 701 702 703
	g_return_val_if_fail (editor != NULL, FALSE);
	g_return_val_if_fail (func != NULL, FALSE);
	g_return_val_if_fail (connection != NULL, FALSE);

704
	page = (*func) (connection, GTK_WINDOW (editor->window), editor->client, &secrets_setting_name, error);
705 706 707 708 709 710 711 712 713 714 715
	if (page) {
		g_object_set_data_full (G_OBJECT (page),
		                        SECRETS_TAG,
		                        g_strdup (secrets_setting_name),
		                        g_free);

		editor->initializing_pages = g_slist_append (editor->initializing_pages, page);
		g_signal_connect (page, "changed", G_CALLBACK (page_changed), editor);
		g_signal_connect (page, "initialized", G_CALLBACK (page_initialized), editor);
	}
	return !!page;
716 717
}

718 719
static gboolean
nm_connection_editor_set_connection (NMConnectionEditor *editor,
720
                                     NMConnection *orig_connection,
721
                                     GError **error)
Rodrigo Moya's avatar
Rodrigo Moya committed
722
{
723
	NMSettingConnection *s_con;
724
	const char *connection_type;
725
	gboolean success = FALSE;
726
	GSList *iter, *copy;
727

728
	g_return_val_if_fail (NM_IS_CONNECTION_EDITOR (editor), FALSE);
729
	g_return_val_if_fail (NM_IS_CONNECTION (orig_connection), FALSE);
Rodrigo Moya's avatar
Rodrigo Moya committed
730 731

	/* clean previous connection */
732 733
	if (editor->connection)
		g_object_unref (editor->connection);
Rodrigo Moya's avatar
Rodrigo Moya committed
734

735
	editor->connection = nm_connection_duplicate (orig_connection);
736

737
	editor->orig_connection = g_object_ref (orig_connection);
738 739
	nm_connection_editor_update_title (editor);

740
	s_con = nm_connection_get_setting_connection (editor->connection);
741 742
	g_assert (s_con);

743 744
	connection_type = nm_setting_connection_get_connection_type (s_con);
	if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) {
745
		if (!add_page (editor, ce_page_ethernet_new, editor->connection, error))
746
			goto out;
747
		if (!add_page (editor, ce_page_8021x_security_new, editor->connection, error))
748
			goto out;
749
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
750
			goto out;
751
		if (!add_page (editor, ce_page_ip6_new, editor->connection, error))
Dan Winship's avatar
Dan Winship committed
752
			goto out;
753
	} else if (!strcmp (connection_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
754
		if (!add_page (editor, ce_page_wifi_new, editor->connection, error))
755
			goto out;
756
		if (!add_page (editor, ce_page_wifi_security_new, editor->connection, error))
757
			goto out;
758
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
759
			goto out;
760
		if (!add_page (editor, ce_page_ip6_new, editor->connection, error))
Dan Winship's avatar
Dan Winship committed
761
			goto out;
762
	} else if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) {
763
		if (!add_page (editor, ce_page_vpn_new, editor->connection, error))
764
			goto out;
765
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
766
			goto out;
767 768 769
		if (   vpn_supports_ipv6 (editor->connection)
			&& !add_page (editor, ce_page_ip6_new, editor->connection, error))
			goto out;
770
	} else if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
771
		if (!add_page (editor, ce_page_dsl_new, editor->connection, error))
772
			goto out;
773
		if (!add_page (editor, ce_page_ethernet_new, editor->connection, error))
774
			goto out;
775
		if (!add_page (editor, ce_page_ppp_new, editor->connection, error))
776
			goto out;
777
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
778
			goto out;
779
	} else if (!strcmp (connection_type, NM_SETTING_GSM_SETTING_NAME) || 
780
	           !strcmp (connection_type, NM_SETTING_CDMA_SETTING_NAME)) {
781
		if (!add_page (editor, ce_page_mobile_new, editor->connection, error))
782
			goto out;
783
		if (!add_page (editor, ce_page_ppp_new, editor->connection, error))
784
			goto out;
785
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
786
			goto out;
787 788 789 790 791 792 793
	} else if (!strcmp (connection_type, NM_SETTING_WIMAX_SETTING_NAME)) {
		if (!add_page (editor, ce_page_wimax_new, editor->connection, error))
			goto out;
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
			goto out;
		if (!add_page (editor, ce_page_ip6_new, editor->connection, error))
			goto out;
794 795 796 797 798 799 800
	} else if (!strcmp (connection_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
		if (!add_page (editor, ce_page_infiniband_new, editor->connection, error))
			goto out;
		if (!add_page (editor, ce_page_ip4_new, editor->connection, error))
			goto out;
		if (!add_page (editor, ce_page_ip6_new, editor->connection, error))
			goto out;
801
	} else {
802
		g_warning ("Unhandled setting type '%s'", connection_type);
803
	}
Rodrigo Moya's avatar
Rodrigo Moya committed
804

805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820