1 /*
2  * e-mail-config-assistant.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <glib/gi18n-lib.h>
21 
22 #include <libebackend/libebackend.h>
23 
24 #include <e-util/e-util.h>
25 #include <shell/e-shell.h>
26 #include <shell/e-shell-window.h>
27 #include <shell/e-shell-view.h>
28 #include <shell/e-shell-sidebar.h>
29 
30 #include "e-mail-config-confirm-page.h"
31 #include "e-mail-config-identity-page.h"
32 #include "e-mail-config-lookup-page.h"
33 #include "e-mail-config-provider-page.h"
34 #include "e-mail-config-receiving-page.h"
35 #include "e-mail-config-sending-page.h"
36 #include "e-mail-config-summary-page.h"
37 #include "e-mail-config-welcome-page.h"
38 
39 #include "em-folder-tree.h"
40 
41 #include "e-mail-config-assistant.h"
42 
43 #define E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE(obj) \
44 	(G_TYPE_INSTANCE_GET_PRIVATE \
45 	((obj), E_TYPE_MAIL_CONFIG_ASSISTANT, EMailConfigAssistantPrivate))
46 
47 /* GtkAssistant's back button label. */
48 #define BACK_BUTTON_LABEL N_("Go _Back")
49 
50 typedef struct _ConfigLookupContext ConfigLookupContext;
51 
52 struct _EMailConfigAssistantPrivate {
53 	EMailSession *session;
54 	ESource *identity_source;
55 	GPtrArray *account_sources;
56 	GPtrArray *transport_sources;
57 	EMailConfigServicePage *receiving_page;
58 	EMailConfigServicePage *sending_page;
59 	EMailConfigSummaryPage *summary_page;
60 	EMailConfigPage *identity_page;
61 	EMailConfigPage *lookup_page;
62 	GHashTable *visited_pages;
63 	gboolean auto_configured;
64 
65 	/* GtkAssistant owns this. */
66 	GtkButton *back_button;  /* not referenced */
67 };
68 
69 struct _ConfigLookupContext {
70 	GtkAssistant *assistant;
71 	GCancellable *cancellable;
72 	GtkWidget *skip_button;  /* not referenced */
73 	EConfigLookup *config_lookup;
74 	gchar *email_address;
75 };
76 
77 enum {
78 	PROP_0,
79 	PROP_ACCOUNT_BACKEND,
80 	PROP_ACCOUNT_SOURCE,
81 	PROP_IDENTITY_SOURCE,
82 	PROP_SESSION,
83 	PROP_TRANSPORT_BACKEND,
84 	PROP_TRANSPORT_SOURCE
85 };
86 
87 enum {
88 	NEW_SOURCE,
89 	LAST_SIGNAL
90 };
91 
92 static gulong signals[LAST_SIGNAL];
93 
94 /* XXX We implement EAlertSink but don't implement a custom submit_alert()
95  *     method.  So any alert results in a pop-up message dialog, which is a
96  *     fashion faux pas these days.  But it's only used when submitting the
97  *     the newly-configured account fails, so should rarely be seen. */
98 
G_DEFINE_TYPE_WITH_CODE(EMailConfigAssistant,e_mail_config_assistant,GTK_TYPE_ASSISTANT,G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK,NULL)G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL))99 G_DEFINE_TYPE_WITH_CODE (
100 	EMailConfigAssistant,
101 	e_mail_config_assistant,
102 	GTK_TYPE_ASSISTANT,
103 	G_IMPLEMENT_INTERFACE (
104 		E_TYPE_ALERT_SINK, NULL)
105 	G_IMPLEMENT_INTERFACE (
106 		E_TYPE_EXTENSIBLE, NULL))
107 
108 static void
109 config_lookup_skip_button_clicked_cb (GtkButton *button,
110 				      GCancellable *cancellable)
111 {
112 	g_cancellable_cancel (cancellable);
113 }
114 
115 static ConfigLookupContext *
config_lookup_context_new(GtkAssistant * assistant,ESourceRegistry * registry,const gchar * email_address)116 config_lookup_context_new (GtkAssistant *assistant,
117 			   ESourceRegistry *registry,
118 			   const gchar *email_address)
119 {
120 	ConfigLookupContext *context;
121 	const gchar *text;
122 
123 	context = g_slice_new0 (ConfigLookupContext);
124 	context->assistant = g_object_ref (assistant);
125 	context->cancellable = g_cancellable_new ();
126 	context->config_lookup = e_config_lookup_new (registry);
127 	context->email_address = g_strdup (email_address);
128 
129 	/* GtkAssistant sinks the floating button reference. */
130 	text = _("_Skip Lookup");
131 	context->skip_button = gtk_button_new_with_mnemonic (text);
132 	gtk_assistant_add_action_widget (
133 		context->assistant, context->skip_button);
134 	gtk_widget_show (context->skip_button);
135 
136 	g_signal_connect_object (
137 		context->skip_button, "clicked",
138 		G_CALLBACK (config_lookup_skip_button_clicked_cb),
139 		context->cancellable, 0);
140 
141 	return context;
142 }
143 
144 static void
config_lookup_context_free(ConfigLookupContext * context)145 config_lookup_context_free (ConfigLookupContext *context)
146 {
147 	gtk_assistant_remove_action_widget (
148 		context->assistant, context->skip_button);
149 
150 	g_object_unref (context->assistant);
151 	g_object_unref (context->cancellable);
152 	g_object_unref (context->config_lookup);
153 	g_free (context->email_address);
154 
155 	g_slice_free (ConfigLookupContext, context);
156 }
157 
158 static gint
mail_config_assistant_provider_compare(gconstpointer data1,gconstpointer data2)159 mail_config_assistant_provider_compare (gconstpointer data1,
160                                         gconstpointer data2)
161 {
162 	const CamelProvider *provider1 = data1;
163 	const CamelProvider *provider2 = data2;
164 
165 	/* The "none" provider comes first. */
166 	if (g_strcmp0 (provider1->protocol, "none") == 0)
167 		return -1;
168 	if (g_strcmp0 (provider2->protocol, "none") == 0)
169 		return 1;
170 
171 	/* Then sort remote providers before local providers. */
172 	if (provider1->flags & CAMEL_PROVIDER_IS_REMOTE) {
173 		if (provider2->flags & CAMEL_PROVIDER_IS_REMOTE)
174 			return 0;
175 		else
176 			return -1;
177 	} else {
178 		if (provider2->flags & CAMEL_PROVIDER_IS_REMOTE)
179 			return 1;
180 		else
181 			return 0;
182 	}
183 }
184 
185 static GList *
mail_config_assistant_list_providers(void)186 mail_config_assistant_list_providers (void)
187 {
188 	GList *list, *link;
189 	GQueue trash = G_QUEUE_INIT;
190 
191 	list = camel_provider_list (TRUE);
192 	list = g_list_sort (list, mail_config_assistant_provider_compare);
193 
194 	/* Keep only providers with a "mail" or "news" domain. */
195 
196 	for (link = list; link != NULL; link = g_list_next (link)) {
197 		CamelProvider *provider = link->data;
198 		gboolean mail_or_news_domain;
199 
200 		mail_or_news_domain =
201 			(g_strcmp0 (provider->domain, "mail") == 0) ||
202 			(g_strcmp0 (provider->domain, "news") == 0);
203 
204 		if (mail_or_news_domain)
205 			continue;
206 
207 		g_queue_push_tail (&trash, link);
208 	}
209 
210 	while ((link = g_queue_pop_head (&trash)) != NULL)
211 		list = g_list_delete_link (list, link);
212 
213 	return list;
214 }
215 
216 static void
mail_config_assistant_notify_account_backend(EMailConfigServicePage * page,GParamSpec * pspec,EMailConfigAssistant * assistant)217 mail_config_assistant_notify_account_backend (EMailConfigServicePage *page,
218                                               GParamSpec *pspec,
219                                               EMailConfigAssistant *assistant)
220 {
221 	EMailConfigServiceBackend *backend;
222 	EMailConfigServicePage *sending_page;
223 	EMailConfigServicePageClass *page_class;
224 	CamelProvider *provider;
225 
226 	backend = e_mail_config_service_page_get_active_backend (page);
227 
228 	/* The Receiving Page combo box may not have an active item. */
229 	if (backend == NULL)
230 		goto notify;
231 
232 	/* The Sending Page may not have been created yet. */
233 	if (assistant->priv->sending_page == NULL)
234 		goto notify;
235 
236 	provider = e_mail_config_service_backend_get_provider (backend);
237 
238 	/* XXX This should never fail, but the Camel macro below does
239 	 *     not check for NULL so better to malfunction than crash. */
240 	g_return_if_fail (provider != NULL);
241 
242 	sending_page = assistant->priv->sending_page;
243 	page_class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (sending_page);
244 
245 	/* The Sending Page is invisible when the CamelProvider for the
246 	 * receiving type defines both a storage and transport service.
247 	 * This is common in CamelProviders for groupware products like
248 	 * Microsoft Exchange and Novell GroupWise. */
249 	if (CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT (provider) &&
250 	    g_strcmp0 (provider->protocol, "none") != 0) {
251 		backend = e_mail_config_service_page_lookup_backend (
252 			sending_page, provider->protocol);
253 		gtk_widget_hide (GTK_WIDGET (sending_page));
254 	} else {
255 		backend = e_mail_config_service_page_lookup_backend (
256 			sending_page, page_class->default_backend_name);
257 		gtk_widget_show (GTK_WIDGET (sending_page));
258 	}
259 
260 	e_mail_config_service_page_set_active_backend (sending_page, backend);
261 
262 notify:
263 	g_object_freeze_notify (G_OBJECT (assistant));
264 
265 	g_object_notify (G_OBJECT (assistant), "account-backend");
266 	g_object_notify (G_OBJECT (assistant), "account-source");
267 
268 	g_object_thaw_notify (G_OBJECT (assistant));
269 }
270 
271 static void
mail_config_assistant_notify_transport_backend(EMailConfigServicePage * page,GParamSpec * pspec,EMailConfigAssistant * assistant)272 mail_config_assistant_notify_transport_backend (EMailConfigServicePage *page,
273                                                 GParamSpec *pspec,
274                                                 EMailConfigAssistant *assistant)
275 {
276 	g_object_freeze_notify (G_OBJECT (assistant));
277 
278 	g_object_notify (G_OBJECT (assistant), "transport-backend");
279 	g_object_notify (G_OBJECT (assistant), "transport-source");
280 
281 	g_object_thaw_notify (G_OBJECT (assistant));
282 }
283 
284 static void
mail_config_assistant_page_changed(EMailConfigPage * page,EMailConfigAssistant * assistant)285 mail_config_assistant_page_changed (EMailConfigPage *page,
286                                     EMailConfigAssistant *assistant)
287 {
288 	gtk_assistant_set_page_complete (
289 		GTK_ASSISTANT (assistant), GTK_WIDGET (page),
290 		e_mail_config_page_check_complete (page));
291 }
292 
293 static void
mail_config_assistant_config_lookup_run_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)294 mail_config_assistant_config_lookup_run_cb (GObject *source_object,
295 					    GAsyncResult *result,
296 					    gpointer user_data)
297 {
298 	EMailConfigAssistantPrivate *priv;
299 	ConfigLookupContext *context;
300 	gint n_pages, ii, complete = 0;
301 	gboolean any_configured = FALSE;
302 	gboolean is_complete;
303 
304 	context = (ConfigLookupContext *) user_data;
305 
306 	priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (context->assistant);
307 
308 	e_config_lookup_run_finish (E_CONFIG_LOOKUP (source_object), result);
309 
310 	is_complete = FALSE;
311 
312 	if (e_mail_config_service_page_auto_configure (priv->receiving_page, context->config_lookup, &is_complete)) {
313 		any_configured = TRUE;
314 		/* Add the page to the visited pages hash table to
315 		 * prevent calling e_mail_config_page_setup_defaults(). */
316 		g_hash_table_add (priv->visited_pages, priv->receiving_page);
317 
318 		if (is_complete)
319 			complete++;
320 	}
321 
322 	is_complete = FALSE;
323 
324 	if (e_mail_config_service_page_auto_configure (priv->sending_page, context->config_lookup, &is_complete)) {
325 		any_configured = TRUE;
326 		/* Add the page to the visited pages hash table to
327 		 * prevent calling e_mail_config_page_setup_defaults(). */
328 		g_hash_table_add (priv->visited_pages, priv->sending_page);
329 
330 		if (is_complete)
331 			complete++;
332 	}
333 
334 	if (!any_configured || complete != 2) {
335 		if (any_configured) {
336 			/* Set the initial display name to the email address
337 			 * given so the user can just click past the Summary page. */
338 			e_source_set_display_name (priv->identity_source, context->email_address);
339 		}
340 
341 		gtk_assistant_next_page (context->assistant);
342 		goto exit;
343 	}
344 
345 	/* Autoconfiguration worked!  Feed the results to the
346 	 * service pages and then skip to the Summary page. */
347 
348 	/* For the summary page... */
349 	priv->auto_configured = TRUE;
350 
351 	/* Also set the initial display name to the email address
352 	 * given so the user can just click past the Summary page. */
353 	e_source_set_display_name (priv->identity_source, context->email_address);
354 
355 	/* Go to the next page (Receiving Email) before skipping to the
356 	 * Summary Page to get it into GtkAssistant visited page history.
357 	 * We want the back button to return to Receiving Email. */
358 	gtk_assistant_next_page (context->assistant);
359 
360 	/* XXX Can't find a better way to learn the page number of
361 	 *     the summary page.  Oh my god this API is horrible. */
362 	n_pages = gtk_assistant_get_n_pages (context->assistant);
363 	for (ii = 0; ii < n_pages; ii++) {
364 		GtkWidget *page;
365 
366 		page = gtk_assistant_get_nth_page (context->assistant, ii);
367 		if (E_IS_MAIL_CONFIG_SUMMARY_PAGE (page))
368 			break;
369 	}
370 
371 	g_warn_if_fail (ii < n_pages);
372 	gtk_assistant_set_current_page (context->assistant, ii);
373 
374 exit:
375 	/* Set the page invisible so we never revisit it. */
376 	gtk_widget_set_visible (GTK_WIDGET (priv->lookup_page), FALSE);
377 
378 	config_lookup_context_free (context);
379 }
380 
381 static ESource *
mail_config_assistant_get_source_cb(EConfigLookup * config_lookup,EConfigLookupSourceKind kind,gpointer user_data)382 mail_config_assistant_get_source_cb (EConfigLookup *config_lookup,
383 				     EConfigLookupSourceKind kind,
384 				     gpointer user_data)
385 {
386 	EMailConfigAssistant *assistant = user_data;
387 	EMailConfigServiceBackend *backend;
388 	ESource *source = NULL;
389 
390 	g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
391 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
392 
393 	switch (kind) {
394 	case E_CONFIG_LOOKUP_SOURCE_UNKNOWN:
395 		break;
396 	case E_CONFIG_LOOKUP_SOURCE_COLLECTION:
397 		backend = e_mail_config_assistant_get_account_backend (assistant);
398 		source = e_mail_config_service_backend_get_collection (backend);
399 		break;
400 	case E_CONFIG_LOOKUP_SOURCE_MAIL_ACCOUNT:
401 		source = e_mail_config_assistant_get_account_source (assistant);
402 		break;
403 	case E_CONFIG_LOOKUP_SOURCE_MAIL_IDENTITY:
404 		source = e_mail_config_assistant_get_identity_source (assistant);
405 		break;
406 	case E_CONFIG_LOOKUP_SOURCE_MAIL_TRANSPORT:
407 		source = e_mail_config_assistant_get_transport_source (assistant);
408 		break;
409 	}
410 
411 	return source;
412 }
413 
414 static gboolean
mail_config_assistant_provider_page_visible(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer unused)415 mail_config_assistant_provider_page_visible (GBinding *binding,
416                                              const GValue *source_value,
417                                              GValue *target_value,
418                                              gpointer unused)
419 {
420 	EMailConfigServiceBackend *active_backend;
421 	EMailConfigServiceBackend *page_backend;
422 	EMailConfigProviderPage *page;
423 	GObject *target_object;
424 	gboolean visible;
425 
426 	target_object = g_binding_get_target (binding);
427 	page = E_MAIL_CONFIG_PROVIDER_PAGE (target_object);
428 	page_backend = e_mail_config_provider_page_get_backend (page);
429 
430 	active_backend = g_value_get_object (source_value);
431 	visible = (page_backend == active_backend);
432 	g_value_set_boolean (target_value, visible);
433 
434 	return TRUE;
435 }
436 
437 static void
mail_config_assistant_select_account_node(const gchar * account_uid)438 mail_config_assistant_select_account_node (const gchar *account_uid)
439 {
440 	EShell *shell;
441 	EShellWindow *shell_window;
442 	EShellView *shell_view;
443 	EShellSidebar *shell_sidebar;
444 	EMFolderTree *folder_tree = NULL;
445 	GtkWindow *active_window;
446 	const gchar *active_view;
447 
448 	g_return_if_fail (account_uid != NULL);
449 
450 	shell = e_shell_get_default ();
451 	active_window = e_shell_get_active_window (shell);
452 
453 	if (!E_IS_SHELL_WINDOW (active_window))
454 		return;
455 
456 	shell_window = E_SHELL_WINDOW (active_window);
457 	active_view = e_shell_window_get_active_view (shell_window);
458 
459 	if (g_strcmp0 (active_view, "mail") != 0)
460 		return;
461 
462 	shell_view = e_shell_window_get_shell_view (shell_window, "mail");
463 
464 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
465 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
466 
467 	em_folder_tree_select_store_when_added (folder_tree, account_uid);
468 
469 	g_object_unref (folder_tree);
470 
471 }
472 
473 static void
mail_config_assistant_close_cb(GObject * object,GAsyncResult * result,gpointer user_data)474 mail_config_assistant_close_cb (GObject *object,
475                                 GAsyncResult *result,
476                                 gpointer user_data)
477 {
478 	EMailConfigAssistant *assistant;
479 	GdkWindow *gdk_window;
480 	GError *error = NULL;
481 
482 	assistant = E_MAIL_CONFIG_ASSISTANT (object);
483 
484 	/* Set the cursor back to normal. */
485 	gdk_window = gtk_widget_get_window (GTK_WIDGET (assistant));
486 	gdk_window_set_cursor (gdk_window, NULL);
487 
488 	/* Allow user interaction with window content. */
489 	gtk_widget_set_sensitive (GTK_WIDGET (assistant), TRUE);
490 
491 	e_mail_config_assistant_commit_finish (assistant, result, &error);
492 
493 	/* Ignore cancellations. */
494 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
495 		g_error_free (error);
496 
497 	} else if (error != NULL) {
498 		e_alert_submit (
499 			E_ALERT_SINK (assistant),
500 			"system:simple-error",
501 			error->message, NULL);
502 		g_error_free (error);
503 
504 	} else {
505 		ESource *source;
506 
507 		source = e_mail_config_assistant_get_account_source (assistant);
508 		if (source != NULL) {
509 			const gchar *uid;
510 
511 			uid = e_source_get_uid (source);
512 			mail_config_assistant_select_account_node (uid);
513 		}
514 
515 		gtk_widget_destroy (GTK_WIDGET (assistant));
516 	}
517 }
518 
519 static void
mail_config_assistant_find_back_button_cb(GtkWidget * widget,gpointer user_data)520 mail_config_assistant_find_back_button_cb (GtkWidget *widget,
521                                            gpointer user_data)
522 {
523 	EMailConfigAssistant *assistant;
524 
525 	assistant = E_MAIL_CONFIG_ASSISTANT (user_data);
526 
527 	if (GTK_IS_BUTTON (widget)) {
528 		GtkButton *button;
529 		const gchar *gtk_label;
530 		const gchar *our_label;
531 
532 		button = GTK_BUTTON (widget);
533 
534 		/* XXX The gtkassistant.ui file assigns the back button
535 		 *     an ID of "back", but I don't think we have access
536 		 *     to it from here.  I guess just compare by label,
537 		 *     and hope our translation matches GTK's.  Yuck. */
538 
539 		gtk_label = gtk_button_get_label (button);
540 		our_label = gettext (BACK_BUTTON_LABEL);
541 
542 		if (g_strcmp0 (gtk_label, our_label) == 0)
543 			assistant->priv->back_button = button;
544 
545 	} else if (GTK_IS_CONTAINER (widget)) {
546 		gtk_container_forall (
547 			GTK_CONTAINER (widget),
548 			mail_config_assistant_find_back_button_cb,
549 			assistant);
550 	}
551 }
552 
553 static void
mail_config_assistant_set_session(EMailConfigAssistant * assistant,EMailSession * session)554 mail_config_assistant_set_session (EMailConfigAssistant *assistant,
555                                    EMailSession *session)
556 {
557 	g_return_if_fail (E_IS_MAIL_SESSION (session));
558 	g_return_if_fail (assistant->priv->session == NULL);
559 
560 	assistant->priv->session = g_object_ref (session);
561 }
562 
563 static void
mail_config_assistant_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)564 mail_config_assistant_set_property (GObject *object,
565                                     guint property_id,
566                                     const GValue *value,
567                                     GParamSpec *pspec)
568 {
569 	switch (property_id) {
570 		case PROP_SESSION:
571 			mail_config_assistant_set_session (
572 				E_MAIL_CONFIG_ASSISTANT (object),
573 				g_value_get_object (value));
574 			return;
575 	}
576 
577 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
578 }
579 
580 static void
mail_config_assistant_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)581 mail_config_assistant_get_property (GObject *object,
582                                     guint property_id,
583                                     GValue *value,
584                                     GParamSpec *pspec)
585 {
586 	switch (property_id) {
587 		case PROP_ACCOUNT_BACKEND:
588 			g_value_set_object (
589 				value,
590 				e_mail_config_assistant_get_account_backend (
591 				E_MAIL_CONFIG_ASSISTANT (object)));
592 			return;
593 
594 		case PROP_ACCOUNT_SOURCE:
595 			g_value_set_object (
596 				value,
597 				e_mail_config_assistant_get_account_source (
598 				E_MAIL_CONFIG_ASSISTANT (object)));
599 			return;
600 
601 		case PROP_IDENTITY_SOURCE:
602 			g_value_set_object (
603 				value,
604 				e_mail_config_assistant_get_identity_source (
605 				E_MAIL_CONFIG_ASSISTANT (object)));
606 			return;
607 
608 		case PROP_SESSION:
609 			g_value_set_object (
610 				value,
611 				e_mail_config_assistant_get_session (
612 				E_MAIL_CONFIG_ASSISTANT (object)));
613 			return;
614 
615 		case PROP_TRANSPORT_BACKEND:
616 			g_value_set_object (
617 				value,
618 				e_mail_config_assistant_get_transport_backend (
619 				E_MAIL_CONFIG_ASSISTANT (object)));
620 			return;
621 
622 		case PROP_TRANSPORT_SOURCE:
623 			g_value_set_object (
624 				value,
625 				e_mail_config_assistant_get_transport_source (
626 				E_MAIL_CONFIG_ASSISTANT (object)));
627 			return;
628 	}
629 
630 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
631 }
632 
633 static void
mail_config_assistant_dispose(GObject * object)634 mail_config_assistant_dispose (GObject *object)
635 {
636 	EMailConfigAssistantPrivate *priv;
637 
638 	priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (object);
639 	g_clear_object (&priv->session);
640 	g_clear_object (&priv->identity_source);
641 	g_clear_object (&priv->receiving_page);
642 	g_clear_object (&priv->sending_page);
643 	g_clear_object (&priv->summary_page);
644 	g_clear_object (&priv->lookup_page);
645 	g_clear_object (&priv->identity_page);
646 
647 	g_ptr_array_set_size (priv->account_sources, 0);
648 	g_ptr_array_set_size (priv->transport_sources, 0);
649 
650 	/* Chain up to parent's dispose() method. */
651 	G_OBJECT_CLASS (e_mail_config_assistant_parent_class)->
652 		dispose (object);
653 }
654 
655 static void
mail_config_assistant_finalize(GObject * object)656 mail_config_assistant_finalize (GObject *object)
657 {
658 	EMailConfigAssistantPrivate *priv;
659 
660 	priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (object);
661 
662 	g_ptr_array_free (priv->account_sources, TRUE);
663 	g_ptr_array_free (priv->transport_sources, TRUE);
664 
665 	g_hash_table_destroy (priv->visited_pages);
666 
667 	/* Chain up to parent's finalize() method. */
668 	G_OBJECT_CLASS (e_mail_config_assistant_parent_class)->
669 		finalize (object);
670 }
671 
672 static void
mail_config_assistant_prefill_user(ESource * on_source)673 mail_config_assistant_prefill_user (ESource *on_source)
674 {
675 	if (e_source_has_extension (on_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
676 		ESourceAuthentication *auth_extension;
677 
678 		auth_extension = e_source_get_extension (on_source, E_SOURCE_EXTENSION_AUTHENTICATION);
679 
680 		if (!e_source_authentication_get_user (auth_extension))
681 			e_source_authentication_set_user (auth_extension, g_get_user_name ());
682 	}
683 }
684 
685 static void
mail_config_assistant_constructed(GObject * object)686 mail_config_assistant_constructed (GObject *object)
687 {
688 	EMailConfigAssistant *assistant;
689 	ESource *identity_source;
690 	ESourceRegistry *registry;
691 	ESourceExtension *extension;
692 	ESourceMailComposition *mail_composition_extension;
693 	ESourceMailIdentity *mail_identity_extension;
694 	ESourceMailSubmission *mail_submission_extension;
695 	EMailSession *session;
696 	EMailConfigPage *page;
697 	GtkWidget *autodiscover_check;
698 	GList *list, *link;
699 	const gchar *extension_name;
700 	const gchar *title;
701 	GtkRequisition requisition;
702 	GSList *children = NULL;
703 	gint ii, npages;
704 
705 	assistant = E_MAIL_CONFIG_ASSISTANT (object);
706 
707 	/* Chain up to parent's constructed() method. */
708 	G_OBJECT_CLASS (e_mail_config_assistant_parent_class)->constructed (object);
709 
710 	title = _("Evolution Account Assistant");
711 	gtk_window_set_title (GTK_WINDOW (assistant), title);
712 	gtk_window_set_position (GTK_WINDOW (assistant), GTK_WIN_POS_CENTER);
713 	gtk_window_set_default_size (GTK_WINDOW (assistant), 640, 480);
714 
715 	session = e_mail_config_assistant_get_session (assistant);
716 	registry = e_mail_session_get_registry (session);
717 
718 	/* XXX Locate the GtkAssistant's internal "Go Back" button so
719 	 *     we can temporarily rename it for autoconfigure results.
720 	 *     Walking the container like this is an extremely naughty
721 	 *     and brittle hack, but GtkAssistant does not provide API
722 	 *     to access it directly. */
723 	gtk_container_forall (
724 		GTK_CONTAINER (assistant),
725 		mail_config_assistant_find_back_button_cb,
726 		assistant);
727 
728 	/* Configure a new identity source. */
729 
730 	identity_source = e_source_new (NULL, NULL, NULL);
731 	assistant->priv->identity_source = identity_source;
732 	session = e_mail_config_assistant_get_session (assistant);
733 
734 	extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
735 	extension = e_source_get_extension (identity_source, extension_name);
736 	mail_composition_extension = E_SOURCE_MAIL_COMPOSITION (extension);
737 
738 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
739 	extension = e_source_get_extension (identity_source, extension_name);
740 	mail_identity_extension = E_SOURCE_MAIL_IDENTITY (extension);
741 
742 	extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
743 	extension = e_source_get_extension (identity_source, extension_name);
744 	mail_submission_extension = E_SOURCE_MAIL_SUBMISSION (extension);
745 
746 	e_source_mail_identity_set_name (mail_identity_extension, g_get_real_name ());
747 
748 	e_source_mail_composition_set_drafts_folder (
749 		mail_composition_extension,
750 		e_mail_session_get_local_folder_uri (
751 		session, E_MAIL_LOCAL_FOLDER_DRAFTS));
752 
753 	e_source_mail_composition_set_templates_folder (
754 		mail_composition_extension,
755 		e_mail_session_get_local_folder_uri (
756 		session, E_MAIL_LOCAL_FOLDER_TEMPLATES));
757 
758 	e_source_mail_submission_set_sent_folder (
759 		mail_submission_extension,
760 		e_mail_session_get_local_folder_uri (
761 		session, E_MAIL_LOCAL_FOLDER_SENT));
762 
763 	gtk_widget_get_preferred_size (GTK_WIDGET (assistant), &requisition, NULL);
764 	requisition.width += 2 * 12;
765 	requisition.height += 2 * 12;
766 
767 	/*** Welcome Page ***/
768 
769 	page = e_mail_config_welcome_page_new ();
770 	e_mail_config_assistant_add_page (assistant, page);
771 
772 	/*** Identity Page ***/
773 
774 	page = e_mail_config_identity_page_new (registry, identity_source);
775 	e_mail_config_identity_page_set_show_account_info (
776 		E_MAIL_CONFIG_IDENTITY_PAGE (page), FALSE);
777 	e_mail_config_identity_page_set_show_signatures (
778 		E_MAIL_CONFIG_IDENTITY_PAGE (page), FALSE);
779 	e_mail_config_identity_page_set_show_autodiscover_check (
780 		E_MAIL_CONFIG_IDENTITY_PAGE (page), TRUE);
781 	autodiscover_check = e_mail_config_identity_page_get_autodiscover_check (
782 		E_MAIL_CONFIG_IDENTITY_PAGE (page));
783 	e_mail_config_assistant_add_page (assistant, page);
784 	assistant->priv->identity_page = g_object_ref (page);
785 
786 	/*** Lookup Page ***/
787 
788 	page = e_mail_config_lookup_page_new ();
789 	e_mail_config_assistant_add_page (assistant, page);
790 	assistant->priv->lookup_page = g_object_ref (page);
791 
792 	e_binding_bind_property (
793 		autodiscover_check, "active",
794 		page, "visible",
795 		G_BINDING_SYNC_CREATE);
796 
797 	/*** Receiving Page ***/
798 
799 	page = e_mail_config_receiving_page_new (registry);
800 	e_mail_config_assistant_add_page (assistant, page);
801 	assistant->priv->receiving_page = E_MAIL_CONFIG_SERVICE_PAGE (g_object_ref (page));
802 
803 	e_binding_bind_object_text_property (
804 		mail_identity_extension, "address",
805 		page, "email-address",
806 		G_BINDING_SYNC_CREATE);
807 
808 	e_signal_connect_notify (
809 		page, "notify::active-backend",
810 		G_CALLBACK (mail_config_assistant_notify_account_backend),
811 		assistant);
812 
813 	/*** Receiving Options (multiple) ***/
814 
815 	/* Populate the Receiving Email page while at the same time
816 	 * adding a Receiving Options page for each account type. */
817 
818 	list = mail_config_assistant_list_providers ();
819 
820 	for (link = list; link != NULL; link = g_list_next (link)) {
821 		EMailConfigServiceBackend *backend;
822 		CamelProvider *provider = link->data;
823 		ESourceBackend *backend_extension;
824 		ESource *scratch_source;
825 		const gchar *backend_name;
826 
827 		if (provider->object_types[CAMEL_PROVIDER_STORE] == 0)
828 			continue;
829 
830 		/* ESource uses "backend_name" and CamelProvider
831 		 * uses "protocol", but the terms are synonymous. */
832 		backend_name = provider->protocol;
833 
834 		scratch_source = e_source_new (NULL, NULL, NULL);
835 		backend_extension = e_source_get_extension (
836 			scratch_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
837 		e_source_backend_set_backend_name (
838 			backend_extension, backend_name);
839 
840 		/* Keep display names synchronized. */
841 		e_binding_bind_property (
842 			identity_source, "display-name",
843 			scratch_source, "display-name",
844 			G_BINDING_BIDIRECTIONAL |
845 			G_BINDING_SYNC_CREATE);
846 
847 		/* We always pass NULL for the collection argument.
848 		 * The backend generates its own scratch collection
849 		 * source if implements the new_collection() method. */
850 		backend = e_mail_config_service_page_add_scratch_source (
851 			assistant->priv->receiving_page, scratch_source, NULL);
852 
853 		mail_config_assistant_prefill_user (scratch_source);
854 
855 		g_object_unref (scratch_source);
856 
857 		page = e_mail_config_provider_page_new (backend);
858 
859 		/* Note: We exclude this page if it has no options,
860 		 *       but we don't know that until we create it. */
861 		if (e_mail_config_provider_page_is_empty (
862 				E_MAIL_CONFIG_PROVIDER_PAGE (page))) {
863 			g_object_unref (g_object_ref_sink (page));
864 			continue;
865 		} else {
866 			e_mail_config_assistant_add_page (assistant, page);
867 		}
868 
869 		/* Each Receiving Options page is only visible when its
870 		 * service backend is active on the Receiving Email page. */
871 		e_binding_bind_property_full (
872 			assistant->priv->receiving_page, "active-backend",
873 			page, "visible",
874 			G_BINDING_SYNC_CREATE,
875 			mail_config_assistant_provider_page_visible,
876 			NULL,
877 			NULL, (GDestroyNotify) NULL);
878 	}
879 
880 	g_list_free (list);
881 
882 	/*** Sending Page ***/
883 
884 	page = e_mail_config_sending_page_new (registry);
885 	e_mail_config_assistant_add_page (assistant, page);
886 	assistant->priv->sending_page = E_MAIL_CONFIG_SERVICE_PAGE (g_object_ref (page));
887 
888 	e_binding_bind_object_text_property (
889 		mail_identity_extension, "address",
890 		page, "email-address",
891 		G_BINDING_SYNC_CREATE);
892 
893 	e_signal_connect_notify (
894 		page, "notify::active-backend",
895 		G_CALLBACK (mail_config_assistant_notify_transport_backend),
896 		assistant);
897 
898 	list = mail_config_assistant_list_providers ();
899 
900 	for (link = list; link != NULL; link = g_list_next (link)) {
901 		CamelProvider *provider = link->data;
902 		ESourceBackend *backend_extension;
903 		ESource *scratch_source;
904 		const gchar *backend_name;
905 
906 		if (provider->object_types[CAMEL_PROVIDER_TRANSPORT] == 0)
907 			continue;
908 
909 		/* ESource uses "backend_name" and CamelProvider
910 		 * uses "protocol", but the terms are synonymous. */
911 		backend_name = provider->protocol;
912 
913 		scratch_source = e_source_new (NULL, NULL, NULL);
914 		backend_extension = e_source_get_extension (
915 			scratch_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT);
916 		e_source_backend_set_backend_name (
917 			backend_extension, backend_name);
918 
919 		/* Keep display names synchronized. */
920 		e_binding_bind_property (
921 			identity_source, "display-name",
922 			scratch_source, "display-name",
923 			G_BINDING_BIDIRECTIONAL |
924 			G_BINDING_SYNC_CREATE);
925 
926 		/* We always pass NULL for the collection argument.
927 		 * The backend generates its own scratch collection
928 		 * source if implements the new_collection() method. */
929 		e_mail_config_service_page_add_scratch_source (
930 			assistant->priv->sending_page, scratch_source, NULL);
931 
932 		mail_config_assistant_prefill_user (scratch_source);
933 
934 		g_object_unref (scratch_source);
935 	}
936 
937 	g_list_free (list);
938 
939 	/*** Summary Page ***/
940 
941 	page = e_mail_config_summary_page_new ();
942 	e_mail_config_assistant_add_page (assistant, page);
943 	assistant->priv->summary_page = E_MAIL_CONFIG_SUMMARY_PAGE (g_object_ref (page));
944 
945 	e_binding_bind_property (
946 		assistant, "account-backend",
947 		page, "account-backend",
948 		G_BINDING_SYNC_CREATE);
949 
950 	e_binding_bind_property (
951 		assistant, "identity-source",
952 		page, "identity-source",
953 		G_BINDING_SYNC_CREATE);
954 
955 	e_binding_bind_property (
956 		assistant, "transport-backend",
957 		page, "transport-backend",
958 		G_BINDING_SYNC_CREATE);
959 
960 	/*** Confirm Page ***/
961 
962 	page = e_mail_config_confirm_page_new ();
963 	e_mail_config_assistant_add_page (assistant, page);
964 
965 	e_extensible_load_extensions (E_EXTENSIBLE (assistant));
966 
967 	npages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant));
968 	for (ii = 0; ii < npages; ii++) {
969 		children = g_slist_prepend (children, gtk_assistant_get_nth_page (GTK_ASSISTANT (assistant), ii));
970 	}
971 
972 	e_util_resize_window_for_screen (GTK_WINDOW (assistant), requisition.width, requisition.height, children);
973 
974 	g_slist_free (children);
975 }
976 
977 static void
mail_config_assistant_remove(GtkContainer * container,GtkWidget * widget)978 mail_config_assistant_remove (GtkContainer *container,
979                               GtkWidget *widget)
980 {
981 	if (E_IS_MAIL_CONFIG_PAGE (widget))
982 		g_signal_handlers_disconnect_by_func (
983 			widget, mail_config_assistant_page_changed,
984 			E_MAIL_CONFIG_ASSISTANT (container));
985 
986 	/* Chain up to parent's remove() method. */
987 	GTK_CONTAINER_CLASS (e_mail_config_assistant_parent_class)->
988 		remove (container, widget);
989 }
990 
991 static void
mail_config_assistant_prepare(GtkAssistant * assistant,GtkWidget * page)992 mail_config_assistant_prepare (GtkAssistant *assistant,
993                                GtkWidget *page)
994 {
995 	EMailConfigAssistantPrivate *priv;
996 	gboolean first_visit = FALSE;
997 
998 	priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (assistant);
999 
1000 	/* Only setup defaults the first time a page is visited. */
1001 	if (!g_hash_table_contains (priv->visited_pages, page)) {
1002 		if (E_IS_MAIL_CONFIG_PAGE (page))
1003 			e_mail_config_page_setup_defaults (
1004 				E_MAIL_CONFIG_PAGE (page));
1005 		g_hash_table_add (priv->visited_pages, page);
1006 		first_visit = TRUE;
1007 	}
1008 
1009 	/* Are we viewing autoconfiguration results?  If so, temporarily
1010 	 * rename the back button to clarify that account details can be
1011 	 * revised.  Otherwise reset the button to its original label. */
1012 	if (priv->back_button != NULL) {
1013 		gboolean auto_configure_results;
1014 		const gchar *label;
1015 
1016 		auto_configure_results =
1017 			E_IS_MAIL_CONFIG_SUMMARY_PAGE (page) &&
1018 			priv->auto_configured && first_visit;
1019 
1020 		if (auto_configure_results)
1021 			label = _("_Revise Details");
1022 		else
1023 			label = gettext (BACK_BUTTON_LABEL);
1024 
1025 		gtk_button_set_label (priv->back_button, label);
1026 	}
1027 
1028 	if (E_IS_MAIL_CONFIG_LOOKUP_PAGE (page)) {
1029 		ConfigLookupContext *context;
1030 		ESource *source;
1031 		ESourceRegistry *registry;
1032 		ESourceMailIdentity *extension;
1033 		ENamedParameters *params;
1034 		const gchar *email_address;
1035 		const gchar *extension_name;
1036 
1037 		registry = e_mail_session_get_registry (priv->session);
1038 
1039 		source = priv->identity_source;
1040 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1041 		extension = e_source_get_extension (source, extension_name);
1042 		email_address = e_source_mail_identity_get_address (extension);
1043 
1044 		context = config_lookup_context_new (assistant, registry, email_address);
1045 
1046 		g_signal_connect (context->config_lookup, "get-source",
1047 			G_CALLBACK (mail_config_assistant_get_source_cb), assistant);
1048 
1049 		params = e_named_parameters_new ();
1050 		e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS, email_address);
1051 
1052 		e_config_lookup_run (context->config_lookup,
1053 			params,
1054 			context->cancellable,
1055 			mail_config_assistant_config_lookup_run_cb,
1056 			context);
1057 
1058 		e_named_parameters_free (params);
1059 	}
1060 
1061 	if (!first_visit && E_IS_MAIL_CONFIG_IDENTITY_PAGE (page)) {
1062 		ESource *source;
1063 		ESourceMailIdentity *extension;
1064 		const gchar *email_address;
1065 		const gchar *extension_name;
1066 
1067 		source = priv->identity_source;
1068 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1069 		extension = e_source_get_extension (source, extension_name);
1070 		email_address = e_source_mail_identity_get_address (extension);
1071 
1072 		/* Set the value to an empty string when going back to the identity page,
1073 		   thus when moving away from it the source's display name is updated
1074 		   with the new address, in case it changed. Do not modify the display
1075 		   name when the user changed it. */
1076 		if (g_strcmp0 (e_mail_config_summary_page_get_account_name (priv->summary_page), email_address) == 0)
1077 			e_source_set_display_name (source, "");
1078 	}
1079 
1080 	if (E_IS_MAIL_CONFIG_RECEIVING_PAGE (page)) {
1081 		ESource *source;
1082 		ESourceMailIdentity *extension;
1083 		const gchar *email_address;
1084 		const gchar *extension_name;
1085 
1086 		/* Use the email address from the Identity Page as
1087 		 * the initial display name, so in case we have to
1088 		 * query a remote mail server, the password prompt
1089 		 * will have a more meaningful description. */
1090 
1091 		source = priv->identity_source;
1092 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1093 		extension = e_source_get_extension (source, extension_name);
1094 		email_address = e_source_mail_identity_get_address (extension);
1095 
1096 		if (first_visit || g_strcmp0 (e_source_get_display_name (source), "") == 0)
1097 			e_source_set_display_name (source, email_address);
1098 	}
1099 
1100 	if (first_visit && (
1101 	    E_IS_MAIL_CONFIG_LOOKUP_PAGE (page) ||
1102 	    E_IS_MAIL_CONFIG_RECEIVING_PAGE (page)))
1103 		e_mail_config_identity_page_set_show_autodiscover_check (
1104 			E_MAIL_CONFIG_IDENTITY_PAGE (priv->identity_page), FALSE);
1105 }
1106 
1107 static void
mail_config_assistant_close(GtkAssistant * assistant)1108 mail_config_assistant_close (GtkAssistant *assistant)
1109 {
1110 	GdkCursor *gdk_cursor;
1111 	GdkWindow *gdk_window;
1112 
1113 	/* Do not chain up.  GtkAssistant does not implement this method. */
1114 
1115 	/* Make the cursor appear busy. */
1116 	gdk_cursor = gdk_cursor_new (GDK_WATCH);
1117 	gdk_window = gtk_widget_get_window (GTK_WIDGET (assistant));
1118 	gdk_window_set_cursor (gdk_window, gdk_cursor);
1119 	g_object_unref (gdk_cursor);
1120 
1121 	/* Prevent user interaction with window content. */
1122 	gtk_widget_set_sensitive (GTK_WIDGET (assistant), FALSE);
1123 
1124 	/* XXX This operation is not cancellable. */
1125 	e_mail_config_assistant_commit (
1126 		E_MAIL_CONFIG_ASSISTANT (assistant),
1127 		NULL, mail_config_assistant_close_cb, NULL);
1128 }
1129 
1130 static void
mail_config_assistant_cancel(GtkAssistant * assistant)1131 mail_config_assistant_cancel (GtkAssistant *assistant)
1132 {
1133 	/* Do not chain up.  GtkAssistant does not implement this method. */
1134 
1135 	gtk_widget_destroy (GTK_WIDGET (assistant));
1136 }
1137 
1138 static void
e_mail_config_assistant_class_init(EMailConfigAssistantClass * class)1139 e_mail_config_assistant_class_init (EMailConfigAssistantClass *class)
1140 {
1141 	GObjectClass *object_class;
1142 	GtkContainerClass *container_class;
1143 	GtkAssistantClass *assistant_class;
1144 
1145 	g_type_class_add_private (class, sizeof (EMailConfigAssistantPrivate));
1146 
1147 	object_class = G_OBJECT_CLASS (class);
1148 	object_class->set_property = mail_config_assistant_set_property;
1149 	object_class->get_property = mail_config_assistant_get_property;
1150 	object_class->dispose = mail_config_assistant_dispose;
1151 	object_class->finalize = mail_config_assistant_finalize;
1152 	object_class->constructed = mail_config_assistant_constructed;
1153 
1154 	container_class = GTK_CONTAINER_CLASS (class);
1155 	container_class->remove = mail_config_assistant_remove;
1156 
1157 	assistant_class = GTK_ASSISTANT_CLASS (class);
1158 	assistant_class->prepare = mail_config_assistant_prepare;
1159 	assistant_class->close = mail_config_assistant_close;
1160 	assistant_class->cancel = mail_config_assistant_cancel;
1161 
1162 	g_object_class_install_property (
1163 		object_class,
1164 		PROP_ACCOUNT_BACKEND,
1165 		g_param_spec_object (
1166 			"account-backend",
1167 			"Account Backend",
1168 			"Active mail account service backend",
1169 			E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
1170 			G_PARAM_READABLE |
1171 			G_PARAM_STATIC_STRINGS));
1172 
1173 	g_object_class_install_property (
1174 		object_class,
1175 		PROP_ACCOUNT_SOURCE,
1176 		g_param_spec_object (
1177 			"account-source",
1178 			"Account Source",
1179 			"Mail account source being edited",
1180 			E_TYPE_SOURCE,
1181 			G_PARAM_READABLE |
1182 			G_PARAM_STATIC_STRINGS));
1183 
1184 	g_object_class_install_property (
1185 		object_class,
1186 		PROP_IDENTITY_SOURCE,
1187 		g_param_spec_object (
1188 			"identity-source",
1189 			"Identity Source",
1190 			"Mail identity source being edited",
1191 			E_TYPE_SOURCE,
1192 			G_PARAM_READABLE |
1193 			G_PARAM_STATIC_STRINGS));
1194 
1195 	g_object_class_install_property (
1196 		object_class,
1197 		PROP_SESSION,
1198 		g_param_spec_object (
1199 			"session",
1200 			"Session",
1201 			"Mail session",
1202 			E_TYPE_MAIL_SESSION,
1203 			G_PARAM_READWRITE |
1204 			G_PARAM_CONSTRUCT_ONLY |
1205 			G_PARAM_STATIC_STRINGS));
1206 
1207 	g_object_class_install_property (
1208 		object_class,
1209 		PROP_TRANSPORT_BACKEND,
1210 		g_param_spec_object (
1211 			"transport-backend",
1212 			"Transport Backend",
1213 			"Active mail transport service backend",
1214 			E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
1215 			G_PARAM_READABLE |
1216 			G_PARAM_STATIC_STRINGS));
1217 
1218 	g_object_class_install_property (
1219 		object_class,
1220 		PROP_TRANSPORT_SOURCE,
1221 		g_param_spec_object (
1222 			"transport-source",
1223 			"Transport Source",
1224 			"Mail transport source being edited",
1225 			E_TYPE_SOURCE,
1226 			G_PARAM_READABLE |
1227 			G_PARAM_STATIC_STRINGS));
1228 
1229 	/**
1230 	 * EMailConfigAssistant::new-source:
1231 	 * @uid: an #ESource UID which had been created
1232 	 *
1233 	 * Emitted to notify about the assistant finishing an account #ESource.
1234 	 *
1235 	 * Since: 3.28
1236 	 **/
1237 	signals[NEW_SOURCE] = g_signal_new (
1238 		"new-source",
1239 		G_TYPE_FROM_CLASS (class),
1240 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1241 		G_STRUCT_OFFSET (EMailConfigAssistantClass, new_source),
1242 		NULL, NULL,
1243 		NULL,
1244 		G_TYPE_NONE, 1, G_TYPE_STRING);
1245 }
1246 
1247 static void
e_mail_config_assistant_init(EMailConfigAssistant * assistant)1248 e_mail_config_assistant_init (EMailConfigAssistant *assistant)
1249 {
1250 	GObject *action_area;
1251 	GtkBuilder *builder;
1252 
1253 	builder = gtk_builder_new ();
1254 	action_area = gtk_buildable_get_internal_child (
1255 		GTK_BUILDABLE (assistant), builder, "action_area");
1256 	if (action_area)
1257 		gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
1258 	g_object_unref (builder);
1259 
1260 	assistant->priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (assistant);
1261 
1262 	assistant->priv->account_sources =
1263 		g_ptr_array_new_with_free_func (
1264 		(GDestroyNotify) g_object_unref);
1265 
1266 	assistant->priv->transport_sources =
1267 		g_ptr_array_new_with_free_func (
1268 		(GDestroyNotify) g_object_unref);
1269 
1270 	assistant->priv->visited_pages = g_hash_table_new (NULL, NULL);
1271 }
1272 
1273 GtkWidget *
e_mail_config_assistant_new(EMailSession * session)1274 e_mail_config_assistant_new (EMailSession *session)
1275 {
1276 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1277 
1278 	return g_object_new (
1279 		E_TYPE_MAIL_CONFIG_ASSISTANT,
1280 		"session", session, NULL);
1281 }
1282 
1283 EMailSession *
e_mail_config_assistant_get_session(EMailConfigAssistant * assistant)1284 e_mail_config_assistant_get_session (EMailConfigAssistant *assistant)
1285 {
1286 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1287 
1288 	return assistant->priv->session;
1289 }
1290 
1291 EMailConfigServiceBackend *
e_mail_config_assistant_get_account_backend(EMailConfigAssistant * assistant)1292 e_mail_config_assistant_get_account_backend (EMailConfigAssistant *assistant)
1293 {
1294 	EMailConfigServicePage *page;
1295 
1296 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1297 
1298 	page = assistant->priv->receiving_page;
1299 
1300 	return e_mail_config_service_page_get_active_backend (page);
1301 }
1302 
1303 ESource *
e_mail_config_assistant_get_account_source(EMailConfigAssistant * assistant)1304 e_mail_config_assistant_get_account_source (EMailConfigAssistant *assistant)
1305 {
1306 	EMailConfigServiceBackend *backend;
1307 	ESource *source = NULL;
1308 
1309 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1310 
1311 	backend = e_mail_config_assistant_get_account_backend (assistant);
1312 
1313 	if (backend != NULL)
1314 		source = e_mail_config_service_backend_get_source (backend);
1315 
1316 	return source;
1317 }
1318 
1319 ESource *
e_mail_config_assistant_get_identity_source(EMailConfigAssistant * assistant)1320 e_mail_config_assistant_get_identity_source (EMailConfigAssistant *assistant)
1321 {
1322 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1323 
1324 	return assistant->priv->identity_source;
1325 }
1326 
1327 EMailConfigServiceBackend *
e_mail_config_assistant_get_transport_backend(EMailConfigAssistant * assistant)1328 e_mail_config_assistant_get_transport_backend (EMailConfigAssistant *assistant)
1329 {
1330 	EMailConfigServicePage *page;
1331 
1332 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1333 
1334 	page = assistant->priv->sending_page;
1335 
1336 	return e_mail_config_service_page_get_active_backend (page);
1337 }
1338 
1339 ESource *
e_mail_config_assistant_get_transport_source(EMailConfigAssistant * assistant)1340 e_mail_config_assistant_get_transport_source (EMailConfigAssistant *assistant)
1341 {
1342 	EMailConfigServiceBackend *backend;
1343 	ESource *source = NULL;
1344 
1345 	g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL);
1346 
1347 	backend = e_mail_config_assistant_get_transport_backend (assistant);
1348 
1349 	if (backend != NULL)
1350 		source = e_mail_config_service_backend_get_source (backend);
1351 
1352 	return source;
1353 }
1354 
1355 void
e_mail_config_assistant_add_page(EMailConfigAssistant * assistant,EMailConfigPage * page)1356 e_mail_config_assistant_add_page (EMailConfigAssistant *assistant,
1357                                   EMailConfigPage *page)
1358 {
1359 	EMailConfigPageInterface *page_interface;
1360 	GtkAssistantPageType page_type;
1361 	GtkWidget *page_widget;
1362 	gint n_pages, position;
1363 	const gchar *page_title;
1364 	gboolean complete;
1365 
1366 	g_return_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant));
1367 	g_return_if_fail (E_IS_MAIL_CONFIG_PAGE (page));
1368 
1369 	page_widget = GTK_WIDGET (page);
1370 	page_interface = E_MAIL_CONFIG_PAGE_GET_INTERFACE (page);
1371 	page_type = page_interface->page_type;
1372 	page_title = page_interface->title;
1373 
1374 	/* Determine the position to insert the page. */
1375 	n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant));
1376 	for (position = 0; position < n_pages; position++) {
1377 		GtkWidget *nth_page;
1378 
1379 		nth_page = gtk_assistant_get_nth_page (
1380 			GTK_ASSISTANT (assistant), position);
1381 		if (e_mail_config_page_compare (page_widget, nth_page) < 0)
1382 			break;
1383 	}
1384 
1385 	gtk_widget_show (page_widget);
1386 
1387 	/* Some pages can be clicked through unchanged. */
1388 	complete = e_mail_config_page_check_complete (page);
1389 
1390 	gtk_assistant_insert_page (
1391 		GTK_ASSISTANT (assistant), page_widget, position);
1392 	gtk_assistant_set_page_type (
1393 		GTK_ASSISTANT (assistant), page_widget, page_type);
1394 	gtk_assistant_set_page_title (
1395 		GTK_ASSISTANT (assistant), page_widget, page_title);
1396 	gtk_assistant_set_page_complete (
1397 		GTK_ASSISTANT (assistant), page_widget, complete);
1398 
1399 	/* XXX GtkAssistant has no equivalent to GtkNotebook's
1400 	 *     "page-added" and "page-removed" signals.  Fortunately
1401 	 *     removing a page does trigger GtkContainer::remove, so
1402 	 *     we can override that method and disconnect our signal
1403 	 *     handler before chaining up.  But I don't see any way
1404 	 *     for a subclass to intercept GtkAssistant pages being
1405 	 *     added, so we have to connect our signal handler here.
1406 	 *     Not really an issue, I'm just being pedantic. */
1407 
1408 	g_signal_connect (
1409 		page, "changed",
1410 		G_CALLBACK (mail_config_assistant_page_changed),
1411 		assistant);
1412 }
1413 
1414 /********************* e_mail_config_assistant_commit() **********************/
1415 
1416 static void
mail_config_assistant_commit_cb(GObject * object,GAsyncResult * result,gpointer user_data)1417 mail_config_assistant_commit_cb (GObject *object,
1418                                  GAsyncResult *result,
1419                                  gpointer user_data)
1420 {
1421 	GSimpleAsyncResult *simple;
1422 	GError *error = NULL;
1423 
1424 	simple = G_SIMPLE_ASYNC_RESULT (user_data);
1425 
1426 	e_source_registry_create_sources_finish (
1427 		E_SOURCE_REGISTRY (object), result, &error);
1428 
1429 	if (error != NULL)
1430 		g_simple_async_result_take_error (simple, error);
1431 
1432 	g_simple_async_result_complete (simple);
1433 
1434 	g_object_unref (simple);
1435 }
1436 
1437 void
e_mail_config_assistant_commit(EMailConfigAssistant * assistant,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1438 e_mail_config_assistant_commit (EMailConfigAssistant *assistant,
1439                                 GCancellable *cancellable,
1440                                 GAsyncReadyCallback callback,
1441                                 gpointer user_data)
1442 {
1443 	EMailConfigServiceBackend *backend;
1444 	GSimpleAsyncResult *simple;
1445 	ESourceRegistry *registry;
1446 	EMailSession *session;
1447 	ESource *source;
1448 	GQueue *queue;
1449 	gint n_pages, ii;
1450 
1451 	g_return_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant));
1452 
1453 	session = e_mail_config_assistant_get_session (assistant);
1454 	registry = e_mail_session_get_registry (session);
1455 
1456 	queue = g_queue_new ();
1457 
1458 	/* Queue the collection data source if one is defined. */
1459 	backend = e_mail_config_assistant_get_account_backend (assistant);
1460 	source = e_mail_config_service_backend_get_collection (backend);
1461 	if (source != NULL)
1462 		g_queue_push_tail (queue, g_object_ref (source));
1463 
1464 	/* Queue the mail-related data sources for the account. */
1465 	source = e_mail_config_assistant_get_account_source (assistant);
1466 	if (source != NULL)
1467 		g_queue_push_tail (queue, g_object_ref (source));
1468 	source = e_mail_config_assistant_get_identity_source (assistant);
1469 	if (source != NULL)
1470 		g_queue_push_tail (queue, g_object_ref (source));
1471 	source = e_mail_config_assistant_get_transport_source (assistant);
1472 	if (source != NULL)
1473 		g_queue_push_tail (queue, g_object_ref (source));
1474 
1475 	n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant));
1476 
1477 	/* Tell all EMailConfigPages to commit their UI state to their
1478 	 * scratch ESources and push any additional data sources on to
1479 	 * the given source queue, such as calendars or address books
1480 	 * to be bundled with the mail account. */
1481 	for (ii = 0; ii < n_pages; ii++) {
1482 		GtkWidget *widget;
1483 
1484 		widget = gtk_assistant_get_nth_page (
1485 			GTK_ASSISTANT (assistant), ii);
1486 
1487 		if (E_IS_MAIL_CONFIG_PAGE (widget)) {
1488 			EMailConfigPage *page;
1489 			page = E_MAIL_CONFIG_PAGE (widget);
1490 			e_mail_config_page_commit_changes (page, queue);
1491 		}
1492 	}
1493 
1494 	simple = g_simple_async_result_new (
1495 		G_OBJECT (assistant), callback, user_data,
1496 		e_mail_config_assistant_commit);
1497 
1498 	e_source_registry_create_sources (
1499 		registry, g_queue_peek_head_link (queue),
1500 		cancellable, mail_config_assistant_commit_cb, simple);
1501 
1502 	g_queue_free_full (queue, (GDestroyNotify) g_object_unref);
1503 }
1504 
1505 gboolean
e_mail_config_assistant_commit_finish(EMailConfigAssistant * assistant,GAsyncResult * result,GError ** error)1506 e_mail_config_assistant_commit_finish (EMailConfigAssistant *assistant,
1507                                        GAsyncResult *result,
1508                                        GError **error)
1509 {
1510 	GSimpleAsyncResult *simple;
1511 	gboolean success;
1512 
1513 	g_return_val_if_fail (
1514 		g_simple_async_result_is_valid (
1515 		result, G_OBJECT (assistant),
1516 		e_mail_config_assistant_commit), FALSE);
1517 
1518 	simple = G_SIMPLE_ASYNC_RESULT (result);
1519 
1520 	/* Assume success unless a GError is set. */
1521 	success = !g_simple_async_result_propagate_error (simple, error);
1522 
1523 	if (success) {
1524 		ESource *source;
1525 
1526 		source = e_mail_config_assistant_get_account_source (assistant);
1527 		if (source)
1528 			g_signal_emit (assistant, signals[NEW_SOURCE], 0, e_source_get_uid (source));
1529 	}
1530 
1531 	return success;
1532 }
1533 
1534