[evolution] Bug 760329 - Try to poke autodiscovery when creating new account



commit 1bd5a348be9644c40b325422b0c57464281436e3
Author: Milan Crha <mcrha redhat com>
Date:   Thu Nov 9 16:44:24 2017 +0100

    Bug 760329 - Try to poke autodiscovery when creating new account

 .../evolution-util/evolution-util-docs.sgml.in     |    6 +
 po/POTFILES.in                                     |    5 +
 src/e-util/CMakeLists.txt                          |    4 +
 src/e-util/e-accounts-window.c                     |  135 +-
 src/e-util/e-accounts-window.h                     |    2 +
 src/e-util/e-collection-account-wizard.c           | 2333 ++++++++++++++++++++
 src/e-util/e-collection-account-wizard.h           |   95 +
 src/e-util/e-config-lookup-result-simple.c         |   59 +-
 src/e-util/e-config-lookup-result-simple.h         |    6 +-
 src/e-util/e-config-lookup-result.c                |   71 +-
 src/e-util/e-config-lookup-result.h                |    8 +
 src/e-util/e-config-lookup-worker.c                |  105 +
 src/e-util/e-config-lookup-worker.h                |  107 +
 src/e-util/e-config-lookup.c                       |  611 +++++-
 src/e-util/e-config-lookup.h                       |   61 +-
 src/e-util/e-simple-async-result.c                 |    4 +-
 src/e-util/e-source-config.c                       |    2 +-
 src/e-util/e-util-enums.h                          |    8 +
 src/e-util/e-util.h                                |    2 +
 src/mail/e-mail-autoconfig.c                       |   86 +-
 src/mail/e-mail-autoconfig.h                       |    4 +
 src/mail/e-mail-config-assistant.c                 |   37 +-
 src/mail/e-mail-config-assistant.h                 |    4 +
 src/mail/e-mail-config-service-backend.c           |    8 +-
 src/mail/test-mail-autoconfig.c                    |    2 +-
 src/modules/accounts-window/CMakeLists.txt         |    2 +
 src/modules/accounts-window/accounts-window.c      |    2 +
 .../accounts-window/e-accounts-window-editors.c    |   54 +-
 .../accounts-window/e-collection-wizard-page.c     |  277 +++
 .../accounts-window/e-collection-wizard-page.h     |   29 +
 src/modules/config-lookup/CMakeLists.txt           |    2 +
 src/modules/config-lookup/config-lookup.c          |    2 +
 src/modules/config-lookup/e-gnome-config-lookup.c  |   65 +-
 src/modules/config-lookup/e-srv-config-lookup.c    |  108 +-
 src/modules/config-lookup/e-webdav-config-lookup.c |  435 ++++
 src/modules/config-lookup/e-webdav-config-lookup.h |   29 +
 src/modules/mail/e-mail-shell-backend.c            |   24 +-
 src/modules/mail/e-mail-shell-backend.h            |    4 +-
 38 files changed, 4568 insertions(+), 230 deletions(-)
---
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in 
b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index ae6e31b..73c9363 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -260,9 +260,11 @@
     <xi:include href="xml/e-calendar.xml"/>
     <xi:include href="xml/e-charset.xml"/>
     <xi:include href="xml/e-charset-combo-box.xml"/>
+    <xi:include href="xml/e-collection-account-wizard.xml"/>
     <xi:include href="xml/e-config-lookup.xml"/>
     <xi:include href="xml/e-config-lookup-result.xml"/>
     <xi:include href="xml/e-config-lookup-result-simple.xml"/>
+    <xi:include href="xml/e-config-lookup-worker.xml"/>
     <xi:include href="xml/e-conflict-search-selector.xml"/>
     <xi:include href="xml/e-contact-store.xml"/>
     <xi:include href="xml/e-data-capture.xml"/>
@@ -322,6 +324,10 @@
     <title>Index</title>
     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3.28" role="3.28">
+    <title>Index of new symbols in 3.28</title>
+    <xi:include href="xml/api-index-3.28.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3.26" role="3.26">
     <title>Index of new symbols in 3.26</title>
     <xi:include href="xml/api-index-3.26.xml"><xi:fallback /></xi:include>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1fc9950..750961e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -195,8 +195,10 @@ src/e-util/e-cell-text.c
 src/e-util/e-charset.c
 src/e-util/e-charset-combo-box.c
 src/e-util/e-client-cache.c
+src/e-util/e-collection-account-wizard.c
 src/e-util/e-color-chooser-widget.c
 src/e-util/e-color-combo.c
+src/e-util/e-config-lookup.c
 src/e-util/e-dateedit.c
 src/e-util/e-datetime-format.c
 src/e-util/e-dialog-utils.c
@@ -384,6 +386,7 @@ src/mail/message-list.etspec
 src/mail/searchtypes.xml.in
 src/mail/vfoldertypes.xml.in
 src/modules/accounts-window/e-accounts-window-editors.c
+src/modules/accounts-window/e-collection-wizard-page.c
 src/modules/accounts-window/e-webdav-browser-page.c
 src/modules/addressbook/autocompletion-config.c
 src/modules/addressbook/eab-composer-util.c
@@ -439,7 +442,9 @@ src/modules/calendar/e-task-shell-view.c
 src/modules/calendar/e-task-shell-view-private.c
 src/modules/composer-to-meeting/e-composer-to-meeting.c
 src/modules/composer-to-meeting/e-meeting-to-composer.c
+src/modules/config-lookup/e-gnome-config-lookup.c
 src/modules/config-lookup/e-srv-config-lookup.c
+src/modules/config-lookup/e-webdav-config-lookup.c
 src/modules/itip-formatter/e-mail-formatter-itip.c
 src/modules/itip-formatter/itip-view.c
 src/modules/itip-formatter/org-gnome-itip-formatter.error.xml
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index effd4a6..d58ee56 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -96,12 +96,14 @@ set(SOURCES
        e-client-cache.c
        e-client-combo-box.c
        e-client-selector.c
+       e-collection-account-wizard.c
        e-color-chooser-widget.c
        e-color-combo.c
        e-config.c
        e-config-lookup.c
        e-config-lookup-result.c
        e-config-lookup-result-simple.c
+       e-config-lookup-worker.c
        e-conflict-search-selector.c
        e-contact-store.c
        e-content-editor.c
@@ -365,12 +367,14 @@ set(HEADERS
        e-client-cache.h
        e-client-combo-box.h
        e-client-selector.h
+       e-collection-account-wizard.h
        e-color-chooser-widget.h
        e-color-combo.h
        e-config.h
        e-config-lookup.h
        e-config-lookup-result.h
        e-config-lookup-result-simple.h
+       e-config-lookup-worker.h
        e-conflict-search-selector.h
        e-contact-store.h
        e-content-editor.h
diff --git a/src/e-util/e-accounts-window.c b/src/e-util/e-accounts-window.c
index 9047df4..57388ca 100644
--- a/src/e-util/e-accounts-window.c
+++ b/src/e-util/e-accounts-window.c
@@ -61,6 +61,7 @@ struct _EAccountsWindowPrivate {
        GtkWidget *delete_button;       /* not referenced */
 
        GHashTable *references; /* gchar *UID ~> GtkTreeRowReference * */
+       gchar *select_source_uid; /* Which source to select, or NULL */
 
        gulong source_enabled_handler_id;
        gulong source_disabled_handler_id;
@@ -177,10 +178,10 @@ accounts_window_emit_delete_source (EAccountsWindow *accounts_window)
 }
 
 static gboolean
-accounts_window_find_source_iter (EAccountsWindow *accounts_window,
-                                 ESource *source,
-                                 GtkTreeIter *out_iter,
-                                 GtkTreeModel **out_model)
+accounts_window_find_source_uid_iter (EAccountsWindow *accounts_window,
+                                     const gchar *uid,
+                                     GtkTreeIter *out_iter,
+                                     GtkTreeModel **out_model)
 {
        GtkTreeRowReference *reference;
        GtkTreePath *path;
@@ -188,13 +189,13 @@ accounts_window_find_source_iter (EAccountsWindow *accounts_window,
        gboolean valid;
 
        g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
-       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
        g_return_val_if_fail (out_iter != NULL, FALSE);
 
-       reference = g_hash_table_lookup (accounts_window->priv->references, e_source_get_uid (source));
+       reference = g_hash_table_lookup (accounts_window->priv->references, uid);
        if (!reference ||
            !gtk_tree_row_reference_valid (reference)) {
-               g_hash_table_remove (accounts_window->priv->references, e_source_get_uid (source));
+               g_hash_table_remove (accounts_window->priv->references, uid);
 
                return FALSE;
        }
@@ -215,6 +216,19 @@ accounts_window_find_source_iter (EAccountsWindow *accounts_window,
 }
 
 static gboolean
+accounts_window_find_source_iter (EAccountsWindow *accounts_window,
+                                 ESource *source,
+                                 GtkTreeIter *out_iter,
+                                 GtkTreeModel **out_model)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+       g_return_val_if_fail (out_iter != NULL, FALSE);
+
+       return accounts_window_find_source_uid_iter (accounts_window, e_source_get_uid (source), out_iter, 
out_model);
+}
+
+static gboolean
 accounts_window_find_child_with_sort_hint (EAccountsWindow *accounts_window,
                                           GtkTreeStore *tree_store,
                                           GtkTreeIter *parent,
@@ -346,7 +360,7 @@ accounts_window_fill_row_with_source (EAccountsWindow *accounts_window,
                        use_type = g_strconcat ("UOA:", backend_name, NULL);
                        icon_name = "credentials-preferences";
                        enabled_visible = FALSE;
-               } else {
+               } else if (g_strcmp0 (backend_name, "none") != 0) {
                        use_type = backend_name;
                        backend_name = NULL;
                }
@@ -502,7 +516,7 @@ accounts_window_fill_children (EAccountsWindow *accounts_window,
                gboolean *subroot_set;
                GtkTreeIter iter, *subroot;
 
-               if (accounts_window_get_sort_hint_for_source (source) == -1)
+               if (accounts_window_get_sort_hint_for_source (source) == UNKNOWN_SORT_HINT)
                        continue;
 
                if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
@@ -551,7 +565,9 @@ accounts_window_fill_children (EAccountsWindow *accounts_window,
                                subroot_display_name, subroot_icon_name, subroot_sort_hint);
                }
 
-               gtk_tree_store_append (tree_store, &iter, subroot);
+               if (!lookup_subroot ||
+                   !accounts_window_find_source_iter (accounts_window, source, &iter, NULL))
+                       gtk_tree_store_append (tree_store, &iter, subroot);
 
                accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, 
mail_account_slaves, !is_managed_collection);
        }
@@ -772,6 +788,7 @@ accounts_window_source_added_cb (ESourceRegistry *registry,
        GtkTreeIter iter, root;
        GSList *children_and_siblings = NULL;
        GList *sources, *llink;
+       gboolean restart = FALSE;
 
        g_return_if_fail (E_IS_SOURCE (source));
        g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
@@ -780,15 +797,19 @@ accounts_window_source_added_cb (ESourceRegistry *registry,
            accounts_window_find_source_iter (accounts_window, source, &iter, NULL))
                return;
 
+       g_object_ref (source);
+
        tree_store = GTK_TREE_STORE (gtk_tree_model_sort_get_model (
                GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (
                GTK_TREE_VIEW (accounts_window->priv->tree_view)))));
 
        sources = e_source_registry_list_sources (accounts_window->priv->registry, NULL);
-       for (llink = sources; llink; llink = g_list_next (llink)) {
+       for (llink = sources; llink; llink = restart ? sources : g_list_next (llink)) {
                ESource *other_source = llink->data;
                const gchar *parent_uid;
 
+               restart = FALSE;
+
                if (!E_IS_SOURCE (other_source) ||
                    e_source_has_extension (other_source, E_SOURCE_EXTENSION_PROXY) ||
                    e_source_has_extension (other_source, E_SOURCE_EXTENSION_MAIL_SIGNATURE))
@@ -799,6 +820,16 @@ accounts_window_source_added_cb (ESourceRegistry *registry,
                    g_strcmp0 (parent_uid, e_source_get_parent (source)) == 0 ||
                    g_strcmp0 (parent_uid, e_source_get_uid (source)) == 0)) {
                        children_and_siblings = g_slist_prepend (children_and_siblings, g_object_ref 
(other_source));
+               } else if (e_source_has_extension (other_source, E_SOURCE_EXTENSION_COLLECTION) && 
other_source != source &&
+                          !e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION) &&
+                          g_strcmp0 (e_source_get_uid (other_source), e_source_get_parent (source)) == 0) {
+                       /* Use the collection source when there's any such found */
+                       g_object_unref (source);
+                       source = g_object_ref (other_source);
+
+                       g_slist_free_full (children_and_siblings, g_object_unref);
+                       children_and_siblings = NULL;
+                       restart = TRUE;
                }
        }
 
@@ -807,12 +838,14 @@ accounts_window_source_added_cb (ESourceRegistry *registry,
        if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
                gboolean is_managed_collection;
 
-               gtk_tree_store_append (tree_store, &iter, NULL);
-
                is_managed_collection = e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
                        e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
 
-               accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, NULL, TRUE);
+               if (!accounts_window_find_source_iter (accounts_window, source, &iter, NULL)) {
+                       gtk_tree_store_append (tree_store, &iter, NULL);
+                       accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, 
NULL, TRUE);
+               }
+
                accounts_window_fill_children (accounts_window, tree_store, &iter, is_managed_collection, 
TRUE, children_and_siblings);
        } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) && (
                   !e_source_get_parent (source) || g_strcmp0 (e_source_get_parent (source), "") == 0)) {
@@ -898,6 +931,10 @@ accounts_window_source_added_cb (ESourceRegistry *registry,
        }
 
        g_slist_free_full (children_and_siblings, g_object_unref);
+       g_object_unref (source);
+
+       if (accounts_window->priv->select_source_uid)
+               e_accounts_window_select_source (accounts_window, accounts_window->priv->select_source_uid);
 }
 
 static void
@@ -1406,7 +1443,7 @@ accounts_window_show_add_popup (EAccountsWindow *accounts_window,
                const gchar *text;
                const gchar *icon_name;
        } items[] = {
-               /* { "collection",      N_("Collection _Account"),      "evolution" }, */
+               { "collection", N_("Collection _Account"),      "evolution" },
                { "mail",       N_("_Mail Account"),            "evolution-mail" },
                { "book",       N_("Address _Book"),            "x-office-address-book" },
                { "calendar",   N_("_Calendar"),                "x-office-calendar" },
@@ -1585,6 +1622,7 @@ accounts_window_finalize (GObject *object)
        EAccountsWindow *accounts_window = E_ACCOUNTS_WINDOW (object);
 
        g_hash_table_destroy (accounts_window->priv->references);
+       g_clear_pointer (&accounts_window->priv->select_source_uid, g_free);
 
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_accounts_window_parent_class)->finalize (object);
@@ -1604,7 +1642,7 @@ accounts_window_constructed (GObject *object)
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_accounts_window_parent_class)->constructed (object);
 
-       gtk_window_set_default_size (GTK_WINDOW (accounts_window), 480, 360);
+       gtk_window_set_default_size (GTK_WINDOW (accounts_window), 480, 410);
        gtk_window_set_title (GTK_WINDOW (accounts_window), _("Evolution Accounts"));
        gtk_container_set_border_width (GTK_CONTAINER (accounts_window), 12);
 
@@ -1701,6 +1739,8 @@ accounts_window_constructed (GObject *object)
                GTK_ACCEL_VISIBLE);
        gtk_window_add_accel_group (GTK_WINDOW (accounts_window), accel_group);
 
+       registry = e_accounts_window_get_registry (accounts_window);
+
        gtk_widget_show_all (GTK_WIDGET (grid));
 
        /* First load extensions, thus the fill-tree-view can call them. */
@@ -1708,8 +1748,6 @@ accounts_window_constructed (GObject *object)
 
        accounts_window_fill_tree_view (accounts_window);
 
-       registry = e_accounts_window_get_registry (accounts_window);
-
        accounts_window->priv->source_enabled_handler_id =
                g_signal_connect (registry, "source-enabled",
                        G_CALLBACK (accounts_window_source_enabled_cb), accounts_window);
@@ -2023,6 +2061,67 @@ e_accounts_window_ref_selected_source (EAccountsWindow *accounts_window)
 }
 
 /**
+ * e_accounts_window_select_source:
+ * @accounts_window: an #EAccountsWindow
+ * @uid: (nullable): an #ESource UID to select
+ *
+ * Selects an #ESource with the given @uid. If no such is available in time
+ * of this call, then it is remembered and selected once it appears.
+ * The function doesn't change selection, when @uid is %NULL, but it
+ * unsets remembered UID from any previous call.
+ *
+ * Since: 3.28
+ **/
+void
+e_accounts_window_select_source (EAccountsWindow *accounts_window,
+                                const gchar *uid)
+{
+       GtkTreeModel *model;
+       GtkTreeIter child_iter;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (!uid || !*uid) {
+               g_clear_pointer (&accounts_window->priv->select_source_uid, g_free);
+               return;
+       }
+
+       if (accounts_window_find_source_uid_iter (accounts_window, uid, &child_iter, &model)) {
+               GtkTreeModel *sort_model;
+               GtkTreeView *tree_view;
+               GtkTreeIter iter;
+
+               g_clear_pointer (&accounts_window->priv->select_source_uid, g_free);
+
+               tree_view = GTK_TREE_VIEW (accounts_window->priv->tree_view);
+               sort_model = gtk_tree_view_get_model (tree_view);
+
+               if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &iter, 
&child_iter)) {
+                       GtkTreeSelection *selection;
+                       GtkTreePath *path;
+
+                       path = gtk_tree_model_get_path (sort_model, &iter);
+                       if (path) {
+                               gtk_tree_view_expand_to_path (tree_view, path);
+                               gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
+                       }
+
+                       gtk_tree_path_free (path);
+
+                       selection = gtk_tree_view_get_selection (tree_view);
+                       gtk_tree_selection_select_iter (selection, &iter);
+               }
+
+               return;
+       }
+
+       if (g_strcmp0 (accounts_window->priv->select_source_uid, uid) != 0) {
+               g_clear_pointer (&accounts_window->priv->select_source_uid, g_free);
+               accounts_window->priv->select_source_uid = g_strdup (uid);
+       }
+}
+
+/**
  * e_accounts_window_insert_to_add_popup:
  * @accounts_window: an #EAccountsWindow
  * @popup_menu: a #GtkMenuShell
diff --git a/src/e-util/e-accounts-window.h b/src/e-util/e-accounts-window.h
index 1eacc80..d09b035 100644
--- a/src/e-util/e-accounts-window.h
+++ b/src/e-util/e-accounts-window.h
@@ -98,6 +98,8 @@ ESourceRegistry *
 void           e_accounts_window_show_with_parent      (EAccountsWindow *accounts_window,
                                                         GtkWindow *parent);
 ESource *      e_accounts_window_ref_selected_source   (EAccountsWindow *accounts_window);
+void           e_accounts_window_select_source         (EAccountsWindow *accounts_window,
+                                                        const gchar *uid);
 void           e_accounts_window_insert_to_add_popup   (EAccountsWindow *accounts_window,
                                                         GtkMenuShell *popup_menu,
                                                         const gchar *kind,
diff --git a/src/e-util/e-collection-account-wizard.c b/src/e-util/e-collection-account-wizard.c
new file mode 100644
index 0000000..e70d68d
--- /dev/null
+++ b/src/e-util/e-collection-account-wizard.c
@@ -0,0 +1,2333 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-collection-account-wizard
+ * @include: e-util/e-util.h
+ * @short_description: Collection account wizard
+ *
+ * #ECollectionAccountWizard is a configuration wizard which guides
+ * user through steps to created collection accounts. Such accounts
+ * provide multiple sources at once, being it address books, calendars,
+ * mail and others.
+ **/
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <libedataserver/libedataserver.h>
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-config-lookup.h"
+#include "e-dialog-widgets.h"
+#include "e-misc-utils.h"
+#include "e-spinner.h"
+#include "e-simple-async-result.h"
+
+#include "e-collection-account-wizard.h"
+
+/* There is no mail identity in the EConfigLookupResultKind enum, thus define a fake one */
+#define FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY E_CONFIG_LOOKUP_RESULT_UNKNOWN
+
+struct _ECollectionAccountWizardPrivate {
+       ESourceRegistry *registry;
+       EConfigLookup *config_lookup;
+       GHashTable *store_passwords; /* gchar *source-uid ~> gchar *password */
+       GHashTable *workers; /* EConfigLookupWorker * ~> WorkerData * */
+       guint running_workers;
+       ESimpleAsyncResult *running_result;
+       gboolean changed;
+
+       ESource *sources[E_CONFIG_LOOKUP_RESULT_LAST_KIND + 1];
+
+       /* Lookup page */
+       GtkWidget *email_entry;
+       GtkWidget *advanced_expander;
+       GtkWidget *servers_entry;
+       GtkWidget *results_label;
+
+       /* Parts page */
+       GtkTreeView *parts_tree_view;
+
+       /* Finish page */
+       GtkWidget *display_name_entry;
+       GtkWidget *finish_running_box;
+       GtkWidget *finish_spinner;
+       GtkWidget *finish_label;
+       GtkWidget *finish_cancel_button;
+       GCancellable *finish_cancellable;
+};
+
+enum {
+       PROP_0,
+       PROP_REGISTRY,
+       PROP_CAN_RUN
+};
+
+enum {
+       DONE,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (ECollectionAccountWizard, e_collection_account_wizard, GTK_TYPE_NOTEBOOK)
+
+enum {
+       PART_COLUMN_BOOL_ENABLED,               /* G_TYPE_BOOLEAN */
+       PART_COLUMN_BOOL_ENABLED_VISIBLE,       /* G_TYPE_BOOLEAN */
+       PART_COLUMN_BOOL_RADIO,                 /* G_TYPE_BOOLEAN */
+       PART_COLUMN_BOOL_SENSITIVE,             /* G_TYPE_BOOLEAN */
+       PART_COLUMN_BOOL_IS_COLLECTION_GROUP,   /* G_TYPE_BOOLEAN */
+       PART_COLUMN_BOOL_ICON_VISIBLE,          /* G_TYPE_BOOLEAN */
+       PART_COLUMN_STRING_ICON_NAME,           /* G_TYPE_STRING */
+       PART_COLUMN_STRING_DESCRIPTION,         /* G_TYPE_STRING */
+       PART_COLUMN_STRING_PROTOCOL,            /* G_TYPE_STRING */
+       PART_COLUMN_OBJECT_RESULT,              /* E_TYPE_CONFIG_LOOKUP_RESULT */
+       PART_N_COLUMNS
+};
+
+typedef struct _WorkerData {
+       GtkWidget *enabled_check;
+       GtkWidget *running_box;
+       GtkWidget *spinner;
+       GtkWidget *running_label;
+       GtkWidget *cancel_button;
+       GCancellable *cancellable;
+       gulong status_id;
+       ENamedParameters *restart_params;
+       gchar *certificate_error;
+       gboolean remember_password;
+} WorkerData;
+
+static void
+worker_data_free (gpointer ptr)
+{
+       WorkerData *wd = ptr;
+
+       if (wd) {
+               if (wd->cancellable) {
+                       g_cancellable_cancel (wd->cancellable);
+
+                       if (wd->status_id) {
+                               g_signal_handler_disconnect (wd->cancellable, wd->status_id);
+                               wd->status_id = 0;
+                       }
+
+                       g_clear_object (&wd->cancellable);
+               }
+
+               g_clear_pointer (&wd->certificate_error, g_free);
+               g_clear_pointer (&wd->restart_params, e_named_parameters_free);
+
+               g_free (wd);
+       }
+}
+
+static void
+collection_account_wizard_update_status_cb (CamelOperation *op,
+                                           const gchar *what,
+                                           gint pc,
+                                           gpointer user_data)
+{
+       GtkLabel *label = user_data;
+
+       g_return_if_fail (GTK_IS_LABEL (label));
+
+       if (what)
+               gtk_label_set_label (label, what);
+}
+
+static void
+collection_account_wizard_notify_can_run (GObject *wizard)
+{
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       g_object_notify (wizard, "can-run");
+}
+
+static void
+collection_account_wizard_mark_changed (ECollectionAccountWizard *wizard)
+{
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       wizard->priv->changed = TRUE;
+}
+
+static void
+collection_account_wizard_worker_cancel_clicked_cb (GtkWidget *button,
+                                                   gpointer user_data)
+{
+       WorkerData *wd = user_data;
+
+       g_return_if_fail (wd != NULL);
+
+       if (wd->cancellable)
+               g_cancellable_cancel (wd->cancellable);
+}
+
+static void
+collection_account_wizard_worker_started_cb (EConfigLookup *config_lookup,
+                                            EConfigLookupWorker *worker,
+                                            GCancellable *cancellable,
+                                            gpointer user_data)
+{
+       ECollectionAccountWizard *wizard = user_data;
+       WorkerData *wd;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       wd = g_hash_table_lookup (wizard->priv->workers, worker);
+       g_return_if_fail (wd != NULL);
+
+       if (wizard->priv->changed) {
+               wizard->priv->changed = FALSE;
+               e_config_lookup_clear_results (wizard->priv->config_lookup);
+       }
+
+       wizard->priv->running_workers++;
+
+       g_warn_if_fail (wd->cancellable == NULL);
+       wd->cancellable = g_object_ref (cancellable);
+
+       wd->status_id = 0;
+       if (CAMEL_IS_OPERATION (wd->cancellable)) {
+               wd->status_id = g_signal_connect (wd->cancellable, "status",
+                       G_CALLBACK (collection_account_wizard_update_status_cb), wd->running_label);
+       }
+
+       gtk_label_set_label (GTK_LABEL (wd->running_label), _("Looking up details, please wait…"));
+       e_spinner_start (E_SPINNER (wd->spinner));
+       gtk_widget_show (wd->spinner);
+       gtk_widget_show (wd->cancel_button);
+       gtk_widget_show (wd->running_box);
+
+       if (wizard->priv->running_workers == 1) {
+               GHashTableIter iter;
+               gpointer value;
+
+               g_hash_table_iter_init (&iter, wizard->priv->workers);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       WorkerData *wd2 = value;
+
+                       gtk_widget_set_sensitive (wd2->enabled_check, FALSE);
+               }
+
+               g_object_notify (G_OBJECT (wizard), "can-run");
+
+               gtk_label_set_text (GTK_LABEL (wizard->priv->results_label), "");
+       }
+}
+
+static void
+collection_account_wizard_worker_finished_cb (EConfigLookup *config_lookup,
+                                             EConfigLookupWorker *worker,
+                                             const ENamedParameters *restart_params,
+                                             const GError *error,
+                                             gpointer user_data)
+{
+       ECollectionAccountWizard *wizard = user_data;
+       WorkerData *wd;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       wd = g_hash_table_lookup (wizard->priv->workers, worker);
+       g_return_if_fail (wd != NULL);
+
+       wizard->priv->running_workers--;
+
+       if (wd->status_id) {
+               g_signal_handler_disconnect (wd->cancellable, wd->status_id);
+               wd->status_id = 0;
+       }
+       g_clear_object (&wd->cancellable);
+       g_clear_pointer (&wd->certificate_error, g_free);
+
+       e_spinner_stop (E_SPINNER (wd->spinner));
+       gtk_widget_hide (wd->spinner);
+       gtk_widget_hide (wd->cancel_button);
+
+       if (g_error_matches (error, E_CONFIG_LOOKUP_WORKER_ERROR, 
E_CONFIG_LOOKUP_WORKER_ERROR_REQUIRES_PASSWORD)) {
+               gchar *markup, *link;
+
+               link = g_markup_printf_escaped ("<a href=\"evo:enter-password\">%s</a>", _("Enter password"));
+
+               /* Translators: The %s is replaced with a clickable text "Enter password", thus it'll be 
"Requires password to continue. Enter password." at the end. */
+               markup = g_strdup_printf (_("Requires password to continue. %s."), link);
+
+               gtk_label_set_markup (GTK_LABEL (wd->running_label), markup);
+
+               g_free (markup);
+               g_free (link);
+       } else if (g_error_matches (error, E_CONFIG_LOOKUP_WORKER_ERROR, 
E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE) &&
+                  restart_params && e_named_parameters_exists (restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM) &&
+                  e_named_parameters_exists (restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST)) {
+               gchar *markup, *link;
+
+               wd->certificate_error = g_strdup (error->message);
+
+               link = g_markup_printf_escaped ("<a href=\"evo:view-certificate\">%s</a>", _("View 
certificate"));
+
+               /* Translators: The first %s is replaced with actual error message about site certificate 
invalidity,
+                  like "The signing certificate authority is not known.", the second %s is replaced with a 
link "View certificate". */
+               markup = g_strdup_printf (C_("collection-account-wizard", "%s %s."), error->message, link);
+
+               gtk_label_set_markup (GTK_LABEL (wd->running_label), markup);
+
+               g_free (markup);
+               g_free (link);
+       } else if (error) {
+               gtk_label_set_text (GTK_LABEL (wd->running_label), error->message);
+       } else {
+               gtk_widget_hide (wd->running_box);
+       }
+
+       e_named_parameters_free (wd->restart_params);
+       wd->restart_params = restart_params ? e_named_parameters_new_clone (restart_params) : NULL;
+
+       if (!wizard->priv->running_workers) {
+               GHashTableIter iter;
+               gpointer value;
+               gint n_results;
+               gchar *str;
+
+               g_hash_table_iter_init (&iter, wizard->priv->workers);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       WorkerData *wd2 = value;
+
+                       gtk_widget_set_sensitive (wd2->enabled_check, TRUE);
+               }
+
+               if (wizard->priv->running_result) {
+                       e_simple_async_result_complete_idle (wizard->priv->running_result);
+                       g_clear_object (&wizard->priv->running_result);
+               }
+
+               g_object_notify (G_OBJECT (wizard), "can-run");
+
+               n_results = e_config_lookup_count_results (wizard->priv->config_lookup);
+
+               if (!n_results) {
+                       gtk_label_set_text (GTK_LABEL (wizard->priv->results_label), _("Found no candidates. 
It can also mean that the server doesn't provide any information about its configuration using the selected 
lookup methods. Enter the account manually instead or change above settings."));
+               } else {
+                       str = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Found one candidate", "Found %d 
candidates", n_results), n_results);
+                       gtk_label_set_text (GTK_LABEL (wizard->priv->results_label), str);
+                       g_free (str);
+               }
+       }
+}
+
+typedef struct _PasswordPromptData {
+       ECollectionAccountWizard *wizard;
+       EConfigLookupWorker *worker;
+       GtkWidget *popover;
+       GtkWidget *password_entry;
+       GtkWidget *remember_check;
+} PasswordPromptData;
+
+static PasswordPromptData *
+password_prompt_data_new (ECollectionAccountWizard *wizard,
+                         EConfigLookupWorker *worker,
+                         GtkWidget *popover,
+                         GtkWidget *password_entry,
+                         GtkWidget *remember_check)
+{
+       PasswordPromptData *ppd;
+
+       ppd = g_new0 (PasswordPromptData, 1);
+       ppd->wizard = wizard;
+       ppd->worker = worker;
+       ppd->popover = popover;
+       ppd->password_entry = password_entry;
+       ppd->remember_check = remember_check;
+
+       return ppd;
+}
+
+static void
+password_prompt_data_free (gpointer data,
+                          GClosure *closure)
+{
+       PasswordPromptData *ppd = data;
+
+       if (ppd) {
+               /* Nothing to free inside the structure */
+               g_free (ppd);
+       }
+}
+
+static void
+collection_account_wizard_try_again_clicked_cb (GtkButton *button,
+                                               gpointer user_data)
+{
+       PasswordPromptData *ppd = user_data;
+       ENamedParameters *params;
+       WorkerData *wd;
+
+       g_return_if_fail (ppd != NULL);
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (ppd->wizard));
+       g_return_if_fail (GTK_IS_ENTRY (ppd->password_entry));
+
+       wd = g_hash_table_lookup (ppd->wizard->priv->workers, ppd->worker);
+       g_return_if_fail (wd != NULL);
+
+       params = e_named_parameters_new_clone (wd->restart_params);
+       g_return_if_fail (params != NULL);
+
+       wd->remember_password = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ppd->remember_check));
+
+       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS, gtk_entry_get_text (GTK_ENTRY 
(ppd->wizard->priv->email_entry)));
+       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_SERVERS, gtk_entry_get_text (GTK_ENTRY 
(ppd->wizard->priv->servers_entry)));
+       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_PASSWORD, gtk_entry_get_text (GTK_ENTRY 
(ppd->password_entry)));
+       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_REMEMBER_PASSWORD, wd->remember_password ? "1" 
: NULL);
+
+       e_config_lookup_run_worker (ppd->wizard->priv->config_lookup, ppd->worker, params, NULL);
+
+       e_named_parameters_free (params);
+
+       gtk_widget_hide (ppd->popover);
+}
+
+static void
+collection_account_wizard_show_password_prompt (ECollectionAccountWizard *wizard,
+                                               EConfigLookupWorker *worker,
+                                               WorkerData *wd)
+{
+       GtkWidget *widget, *label, *entry, *check, *button;
+       GtkGrid *grid;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
+       g_return_if_fail (wd != NULL);
+
+       widget = gtk_grid_new ();
+       grid = GTK_GRID (widget);
+       gtk_grid_set_column_spacing (grid, 6);
+       gtk_grid_set_row_spacing (grid, 6);
+
+       widget = gtk_label_new_with_mnemonic (_("_Password:"));
+       gtk_widget_set_halign (widget, GTK_ALIGN_END);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+       label = widget;
+
+       widget = gtk_entry_new ();
+       gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       entry = widget;
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Remember password"));
+       gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+       check = widget;
+
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), wd->remember_password);
+
+       widget = gtk_button_new_with_mnemonic (_("_Try Again"));
+       gtk_widget_set_halign (widget, GTK_ALIGN_END);
+       gtk_widget_set_can_default (widget, TRUE);
+       gtk_grid_attach (grid, widget, 0, 2, 2, 1);
+       button = widget;
+
+       gtk_widget_show_all (GTK_WIDGET (grid));
+
+       widget = gtk_popover_new (wd->running_label);
+       gtk_popover_set_position (GTK_POPOVER (widget), GTK_POS_BOTTOM);
+       gtk_popover_set_default_widget (GTK_POPOVER (widget), button);
+       gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+       gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+       gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
+
+       g_signal_connect_data (button, "clicked",
+               G_CALLBACK (collection_account_wizard_try_again_clicked_cb),
+               password_prompt_data_new (wizard, worker, widget, entry, check),
+               password_prompt_data_free, 0);
+
+       g_signal_connect (widget, "closed",
+               G_CALLBACK (gtk_widget_destroy), NULL);
+
+       gtk_widget_show (widget);
+
+       gtk_widget_grab_focus (entry);
+}
+
+static void
+collection_account_wizard_view_certificate (ECollectionAccountWizard *wizard,
+                                           EConfigLookupWorker *worker,
+                                           WorkerData *wd)
+{
+       ETrustPromptResponse response;
+       GtkWidget *toplevel;
+       GtkWindow *parent = NULL;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
+       g_return_if_fail (wd != NULL);
+       g_return_if_fail (wd->restart_params != NULL);
+       g_return_if_fail (e_named_parameters_exists (wd->restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM));
+       g_return_if_fail (e_named_parameters_exists (wd->restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST));
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (wizard));
+       if (GTK_IS_WINDOW (toplevel))
+               parent = GTK_WINDOW (toplevel);
+
+       response = e_trust_prompt_run_modal (parent, NULL, NULL,
+               e_named_parameters_get (wd->restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST),
+               e_named_parameters_get (wd->restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM),
+               0, wd->certificate_error);
+
+       if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+               ENamedParameters *params;
+
+               params = e_named_parameters_new_clone (wd->restart_params);
+               g_return_if_fail (params != NULL);
+
+               e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS, gtk_entry_get_text 
(GTK_ENTRY (wizard->priv->email_entry)));
+               e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_SERVERS, gtk_entry_get_text (GTK_ENTRY 
(wizard->priv->servers_entry)));
+               e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST, 
e_config_lookup_encode_certificate_trust (response));
+
+               e_config_lookup_run_worker (wizard->priv->config_lookup, worker, params, NULL);
+
+               e_named_parameters_free (params);
+       }
+}
+
+static gboolean
+collection_account_wizard_activate_link_cb (GtkWidget *label,
+                                           const gchar *uri,
+                                           gpointer user_data)
+{
+       ECollectionAccountWizard *wizard = user_data;
+       EConfigLookupWorker *worker = NULL;
+       WorkerData *wd;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), TRUE);
+
+       g_hash_table_iter_init (&iter, wizard->priv->workers);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               worker = key;
+               wd = value;
+
+               if (worker && wd && wd->running_label == label)
+                       break;
+
+               worker = NULL;
+               wd = NULL;
+       }
+
+       if (worker && wd) {
+               if (g_strcmp0 (uri, "evo:enter-password") == 0)
+                       collection_account_wizard_show_password_prompt (wizard, worker, wd);
+               else if (g_strcmp0 (uri, "evo:view-certificate") == 0)
+                       collection_account_wizard_view_certificate (wizard, worker, wd);
+               else
+                       g_warning ("%s: Do not know what to do with '%s'", G_STRFUNC, uri);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+collection_account_wizard_fill_results (ECollectionAccountWizard *wizard)
+{
+       struct _results_info {
+               EConfigLookupResultKind kind;
+               const gchar *display_name;
+               const gchar *icon_name;
+               GSList *results; /* EConfigLookupResult * */
+       } results_info[] = {
+               { E_CONFIG_LOOKUP_RESULT_COLLECTION,    N_("Collection"),       "evolution",            NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  N_("Mail Receive"),     "evolution-mail",       NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_MAIL_SEND,     N_("Mail Send"),        "mail-send",            NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK,  N_("Address Book"),     "x-office-address-book",NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_CALENDAR,      N_("Calendar"),         "x-office-calendar",    NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_MEMO_LIST,     N_("Memo List"),        "evolution-memos",      NULL 
},
+               { E_CONFIG_LOOKUP_RESULT_TASK_LIST,     N_("Task List"),        "evolution-tasks",      NULL }
+       };
+
+       GtkTreeStore *tree_store;
+       GtkTreeIter iter, parent;
+       gint ii;
+       gboolean found_any = FALSE;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), FALSE);
+
+       tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (wizard->priv->parts_tree_view));
+       gtk_tree_store_clear (tree_store);
+
+       for (ii = 0; ii < G_N_ELEMENTS (results_info); ii++) {
+               results_info[ii].results = e_config_lookup_dup_results (wizard->priv->config_lookup, 
results_info[ii].kind, NULL);
+
+               if (results_info[ii].results) {
+                       found_any = TRUE;
+
+                       results_info[ii].results = g_slist_sort (results_info[ii].results, 
e_config_lookup_result_compare);
+               }
+       }
+
+       if (!found_any)
+               return FALSE;
+
+       for (ii = 0; ii < G_N_ELEMENTS (results_info); ii++) {
+               GSList *results = results_info[ii].results, *link, *known_results = NULL, *klink;
+               GtkTreePath *path;
+
+               /* Skip empty groups */
+               if (!results)
+                       continue;
+
+               gtk_tree_store_append (tree_store, &parent, NULL);
+               gtk_tree_store_set (tree_store, &parent,
+                       PART_COLUMN_BOOL_ENABLED, TRUE,
+                       PART_COLUMN_BOOL_ENABLED_VISIBLE, TRUE,
+                       PART_COLUMN_BOOL_RADIO, FALSE,
+                       PART_COLUMN_BOOL_SENSITIVE, results != NULL,
+                       PART_COLUMN_BOOL_IS_COLLECTION_GROUP, results_info[ii].kind == 
E_CONFIG_LOOKUP_RESULT_COLLECTION,
+                       PART_COLUMN_BOOL_ICON_VISIBLE, results_info[ii].icon_name != NULL,
+                       PART_COLUMN_STRING_ICON_NAME, results_info[ii].icon_name,
+                       PART_COLUMN_STRING_DESCRIPTION, _(results_info[ii].display_name),
+                       -1);
+
+               for (link = results; link; link = g_slist_next (link)) {
+                       EConfigLookupResult *result = link->data;
+                       const gchar *display_name, *description;
+                       gchar *markup;
+
+                       if (!result)
+                               continue;
+
+                       for (klink = known_results; klink; klink = g_slist_next (klink)) {
+                               if (e_config_lookup_result_equal (result, klink->data))
+                                       break;
+                       }
+
+                       /* Found one such processed already. */
+                       if (klink)
+                               continue;
+
+                       /* Just borrow it, no need to reference it. */
+                       known_results = g_slist_prepend (known_results, result);
+
+                       display_name = e_config_lookup_result_get_display_name (result);
+                       description = e_config_lookup_result_get_description (result);
+
+                       if (description && *description)
+                               markup = g_markup_printf_escaped ("%s\n<small>%s</small>", display_name, 
description);
+                       else
+                               markup = g_markup_printf_escaped ("%s", display_name);
+
+                       gtk_tree_store_append (tree_store, &iter, &parent);
+                       gtk_tree_store_set (tree_store, &iter,
+                               PART_COLUMN_BOOL_ENABLED, link == results,
+                               PART_COLUMN_BOOL_ENABLED_VISIBLE, g_slist_next (results) != NULL,
+                               PART_COLUMN_BOOL_RADIO, results_info[ii].kind != 
E_CONFIG_LOOKUP_RESULT_COLLECTION,
+                               PART_COLUMN_BOOL_SENSITIVE, TRUE,
+                               PART_COLUMN_BOOL_ICON_VISIBLE, NULL,
+                               PART_COLUMN_STRING_ICON_NAME, NULL,
+                               PART_COLUMN_STRING_DESCRIPTION, markup,
+                               PART_COLUMN_STRING_PROTOCOL, e_config_lookup_result_get_protocol (result),
+                               PART_COLUMN_OBJECT_RESULT, result,
+                               -1);
+
+                       g_free (markup);
+               }
+
+               g_slist_free (known_results);
+
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), &parent);
+               if (path) {
+                       gtk_tree_view_expand_to_path (wizard->priv->parts_tree_view, path);
+                       gtk_tree_path_free (path);
+               }
+       }
+
+       for (ii = 0; ii < G_N_ELEMENTS (results_info); ii++) {
+               g_slist_free_full (results_info[ii].results, g_object_unref);
+               results_info[ii].results = NULL;
+       }
+
+       return TRUE;
+}
+
+static void
+collection_account_wizard_part_enabled_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                                                  const gchar *path_string,
+                                                  gpointer user_data)
+{
+       ECollectionAccountWizard *wizard = user_data;
+       GtkTreeStore *tree_store;
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter, parent, child;
+       EConfigLookupResult *src_result = NULL, *cur_result = NULL;
+       gboolean set_enabled, is_radio = FALSE;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       model = gtk_tree_view_get_model (wizard->priv->parts_tree_view);
+       tree_store = GTK_TREE_STORE (model);
+
+       path = gtk_tree_path_new_from_string (path_string);
+       if (!gtk_tree_model_get_iter (model, &child, path)) {
+               g_warn_if_reached ();
+               gtk_tree_path_free (path);
+
+               return;
+       }
+
+       gtk_tree_path_free (path);
+
+       set_enabled = !gtk_cell_renderer_toggle_get_active (cell_renderer);
+
+       gtk_tree_model_get (model, &child,
+               PART_COLUMN_BOOL_RADIO, &is_radio,
+               PART_COLUMN_OBJECT_RESULT, &src_result,
+               -1);
+
+       /* Reflect the change for other radio-s in this level */
+       if (is_radio) {
+               GtkTreeIter sibling = child;
+
+               iter = child;
+
+               /* Move to the first sibling */
+               if (gtk_tree_model_iter_parent (model, &parent, &child) &&
+                   gtk_tree_model_iter_nth_child (model, &iter, &parent, 0)) {
+                       sibling = iter;
+               } else {
+                       while (gtk_tree_model_iter_previous (model, &iter))
+                               sibling = iter;
+               }
+
+               do {
+                       is_radio = FALSE;
+
+                       gtk_tree_model_get (model, &sibling,
+                               PART_COLUMN_BOOL_RADIO, &is_radio,
+                               PART_COLUMN_OBJECT_RESULT, &cur_result,
+                               -1);
+
+                       if (is_radio) {
+                               gtk_tree_store_set (tree_store, &sibling,
+                                       PART_COLUMN_BOOL_ENABLED, cur_result == src_result,
+                                       -1);
+                       }
+
+                       g_clear_object (&cur_result);
+               } while (gtk_tree_model_iter_next (model, &sibling));
+       } else {
+               gtk_tree_store_set (tree_store, &child,
+                       PART_COLUMN_BOOL_ENABLED, set_enabled,
+                       -1);
+       }
+
+       /* De/sensitize children of the group nodes */
+       if (!gtk_tree_model_iter_parent (model, &parent, &child) &&
+           gtk_tree_model_iter_nth_child (model, &iter, &child, 0)) {
+               do {
+                       gtk_tree_store_set (tree_store, &iter,
+                               PART_COLUMN_BOOL_SENSITIVE, set_enabled,
+                               -1);
+               } while (gtk_tree_model_iter_next (model, &iter));
+       }
+
+       g_clear_object (&src_result);
+
+       if (!is_radio)
+               g_object_notify (G_OBJECT (wizard), "can-run");
+}
+
+static ESource *
+collection_account_wizard_create_child_source (ECollectionAccountWizard *wizard,
+                                              const gchar *add_extension_name)
+{
+       ESource *source;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), NULL);
+       g_return_val_if_fail (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION] != NULL, NULL);
+
+       source = e_source_new (NULL, NULL, NULL);
+
+       e_source_set_parent (source, e_source_get_uid 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION]));
+
+       if (add_extension_name)
+               e_source_get_extension (source, add_extension_name);
+
+       return source;
+}
+
+static ESource *
+collection_account_wizard_get_source (ECollectionAccountWizard *wizard,
+                                     EConfigLookupResultKind kind)
+{
+       ESource *source = NULL;
+       const gchar *extension_name = NULL;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), NULL);
+
+       switch (kind) {
+       case E_CONFIG_LOOKUP_RESULT_COLLECTION:
+               source = wizard->priv->sources[kind];
+               g_warn_if_fail (source != NULL);
+               break;
+       case E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE:
+               extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+               break;
+       case FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY: /* E_CONFIG_LOOKUP_RESULT_UNKNOWN */
+               extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+               break;
+       case E_CONFIG_LOOKUP_RESULT_MAIL_SEND:
+               extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+               break;
+       case E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK:
+               extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+               break;
+       case E_CONFIG_LOOKUP_RESULT_CALENDAR:
+               extension_name = E_SOURCE_EXTENSION_CALENDAR;
+               break;
+       case E_CONFIG_LOOKUP_RESULT_MEMO_LIST:
+               extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+               break;
+       case E_CONFIG_LOOKUP_RESULT_TASK_LIST:
+               extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+               break;
+       }
+
+       g_return_val_if_fail (kind >= 0 && kind <= E_CONFIG_LOOKUP_RESULT_LAST_KIND, NULL);
+
+       source = wizard->priv->sources[kind];
+
+       if (!source && kind != E_CONFIG_LOOKUP_RESULT_COLLECTION) {
+               source = collection_account_wizard_create_child_source (wizard, extension_name);
+               wizard->priv->sources[kind] = source;
+       }
+
+       return source;
+}
+
+static ESource *
+collection_account_wizard_get_source_cb (ECollectionAccountWizard *wizard,
+                                        EConfigLookupSourceKind kind)
+{
+       ESource *source = NULL;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), NULL);
+
+       switch (kind) {
+       case E_CONFIG_LOOKUP_SOURCE_UNKNOWN:
+               break;
+       case E_CONFIG_LOOKUP_SOURCE_COLLECTION:
+               source = collection_account_wizard_get_source (wizard, E_CONFIG_LOOKUP_RESULT_COLLECTION);
+               break;
+       case E_CONFIG_LOOKUP_SOURCE_MAIL_ACCOUNT:
+               source = collection_account_wizard_get_source (wizard, E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE);
+               break;
+       case E_CONFIG_LOOKUP_SOURCE_MAIL_IDENTITY:
+               source = collection_account_wizard_get_source (wizard, 
FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY);
+               break;
+       case E_CONFIG_LOOKUP_SOURCE_MAIL_TRANSPORT:
+               source = collection_account_wizard_get_source (wizard, E_CONFIG_LOOKUP_RESULT_MAIL_SEND);
+               break;
+       }
+
+       return source;
+}
+
+static gboolean
+collection_account_wizard_host_is_google_server (const gchar *host)
+{
+       if (!host || !*host)
+               return FALSE;
+
+       return camel_strstrcase (host, "gmail.com") ||
+              camel_strstrcase (host, "googlemail.com") ||
+              camel_strstrcase (host, "google.com") ||
+              camel_strstrcase (host, "googleusercontent.com");
+}
+
+static void
+collection_account_wizard_write_changes_thread (ESimpleAsyncResult *result,
+                                               gpointer source_object,
+                                               GCancellable *cancellable)
+{
+       ECollectionAccountWizard *wizard = source_object;
+       ESourceCollection *collection_extension;
+       ESource *source;
+       gint ii;
+       const gchar *text;
+       GList *sources = NULL;
+       gboolean google_supported, any_is_google = FALSE;
+       GError *local_error = NULL;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       /* Deal with LDAP addressbook first */
+       source = wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK];
+       if (source &&
+           e_source_has_extension (source, E_SOURCE_EXTENSION_LDAP_BACKEND) &&
+           e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+               ESourceAuthentication *auth_extension;
+               ESourceLDAP *ldap_extension;
+               const gchar *root_dn;
+
+               auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+               ldap_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_LDAP_BACKEND);
+               root_dn = e_source_ldap_get_root_dn (ldap_extension);
+
+               if (!root_dn || !*root_dn) {
+                       gchar **root_dse = NULL;
+
+                       camel_operation_push_message (cancellable, "%s", _("Looking up LDAP server's search 
base…"));
+
+                       if (e_util_query_ldap_root_dse_sync (
+                               e_source_authentication_get_host (auth_extension),
+                               e_source_authentication_get_port (auth_extension),
+                               &root_dse, cancellable, NULL)) {
+                               if (root_dse && root_dse[0])
+                                       e_source_ldap_set_root_dn (ldap_extension, root_dse[0]);
+
+                               g_strfreev (root_dse);
+                       }
+
+                       camel_operation_pop_message (cancellable);
+               }
+       }
+
+       if (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE]) {
+               ESourceMailAccount *mail_account_extension;
+               ESourceMailIdentity *mail_identity_extension;
+               ESourceMailTransport *mail_transport_extension;
+               ESourceMailSubmission *mail_submission_extension;
+
+               mail_account_extension = e_source_get_extension 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE], E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+               e_source_mail_account_set_identity_uid (mail_account_extension, e_source_get_uid 
(wizard->priv->sources[FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY]));
+
+               text = e_source_backend_get_backend_name (E_SOURCE_BACKEND (mail_account_extension));
+               if (!text || !*text)
+                       e_source_backend_set_backend_name (E_SOURCE_BACKEND (mail_account_extension), "none");
+
+               mail_identity_extension = e_source_get_extension 
(wizard->priv->sources[FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY], E_SOURCE_EXTENSION_MAIL_IDENTITY);
+               text = e_source_mail_identity_get_name (mail_identity_extension);
+               if (!text || !*text)
+                       e_source_mail_identity_set_name (mail_identity_extension, g_get_real_name ());
+
+               mail_submission_extension = e_source_get_extension 
(wizard->priv->sources[FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY], E_SOURCE_EXTENSION_MAIL_SUBMISSION);
+               e_source_mail_submission_set_transport_uid (mail_submission_extension, e_source_get_uid 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_SEND]));
+
+               mail_transport_extension = e_source_get_extension 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_SEND], E_SOURCE_EXTENSION_MAIL_TRANSPORT);
+
+               text = e_source_backend_get_backend_name (E_SOURCE_BACKEND (mail_transport_extension));
+               if (!text || !*text)
+                       e_source_backend_set_backend_name (E_SOURCE_BACKEND (mail_transport_extension), 
"none");
+       }
+
+       if (!e_source_has_extension (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION], 
E_SOURCE_EXTENSION_AUTHENTICATION)) {
+               /* Make sure the collection source has the Authentication extension,
+                  thus the credentials can be reused. It's fine when the extension
+                  doesn't have set values. */
+               e_source_get_extension (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION], 
E_SOURCE_EXTENSION_AUTHENTICATION);
+       }
+
+       collection_extension = e_source_get_extension 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION], E_SOURCE_EXTENSION_COLLECTION);
+
+       /* Collections with empty backend-name are skipped by ESourceRegistry */
+       text = e_source_backend_get_backend_name (E_SOURCE_BACKEND (collection_extension));
+       if (!text || !*text)
+               e_source_backend_set_backend_name (E_SOURCE_BACKEND (collection_extension), "none");
+
+       google_supported = e_source_credentials_google_is_supported ();
+
+       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+               source = wizard->priv->sources[ii];
+
+               if (!source)
+                       continue;
+
+               /* This is not great, to special-case the Google server, but there's nothing
+                  better at the moment and it's the only OAuth2 right now anyway. */
+               if (google_supported && ii != E_CONFIG_LOOKUP_RESULT_COLLECTION &&
+                   e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+                       ESourceAuthentication *authentication_extension;
+
+                       authentication_extension = e_source_get_extension (source, 
E_SOURCE_EXTENSION_AUTHENTICATION);
+                       if (collection_account_wizard_host_is_google_server (e_source_authentication_get_host 
(authentication_extension))) {
+                               any_is_google = TRUE;
+                               e_source_authentication_set_method (authentication_extension, "Google");
+                       }
+               }
+
+               sources = g_list_prepend (sources, source);
+       }
+
+       source = wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION];
+       /* It is always true, but have the variables local in this place only */
+       if (source) {
+               ESourceAuthentication *authentication_extension;
+               ESourceCollection *collection_extension;
+               const gchar *host;
+
+               authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+               collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+
+               host = e_source_collection_get_calendar_url (collection_extension);
+               if (collection_account_wizard_host_is_google_server (host)) {
+                       any_is_google = TRUE;
+                       if (strstr (host, "calendar.google.com")) {
+                               e_source_backend_set_backend_name (E_SOURCE_BACKEND (collection_extension), 
"webdav");
+
+                               if (google_supported)
+                                       e_source_collection_set_calendar_url (collection_extension, 
"https://apidata.googleusercontent.com/caldav/v2/";);
+                               else
+                                       e_source_collection_set_calendar_url (collection_extension, 
"https://www.google.com/calendar/dav/";);
+                       }
+               }
+
+               if (any_is_google && google_supported) {
+                       e_source_authentication_set_method (authentication_extension, "Google");
+                       e_source_backend_set_backend_name (E_SOURCE_BACKEND (collection_extension), "google");
+               }
+       }
+
+       /* First store passwords, thus the evolution-source-registry has them ready if needed. */
+       if (g_hash_table_size (wizard->priv->store_passwords) > 0) {
+               GHashTableIter iter;
+               gpointer key, value;
+
+               g_hash_table_iter_init (&iter, wizard->priv->store_passwords);
+               while (g_hash_table_iter_next (&iter, &key, &value)) {
+                       const gchar *uid = key, *password = value;
+
+                       if (uid && *uid && password && *password) {
+                               source = NULL;
+
+                               for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+                                       source = wizard->priv->sources[ii];
+
+                                       if (source && g_strcmp0 (uid, e_source_get_uid (source)) == 0)
+                                               break;
+
+                                       source = NULL;
+                               }
+
+                               if (source && !e_source_store_password_sync (source, password, TRUE, 
cancellable, &local_error)) {
+                                       e_simple_async_result_set_user_data (result, local_error, 
(GDestroyNotify) g_error_free);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (!e_simple_async_result_get_user_data (result) && /* No error from password save */
+           !e_source_registry_create_sources_sync (wizard->priv->registry, sources, cancellable, 
&local_error) && local_error) {
+               e_simple_async_result_set_user_data (result, local_error, (GDestroyNotify) g_error_free);
+       }
+
+       g_list_free (sources);
+}
+
+static void
+collection_account_wizard_write_changes_done (GObject *source_object,
+                                             GAsyncResult *result,
+                                             gpointer user_data)
+{
+       ECollectionAccountWizard *wizard;
+       const GError *error;
+       gboolean is_cancelled = FALSE;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (source_object));
+
+       wizard = E_COLLECTION_ACCOUNT_WIZARD (source_object);
+
+       g_clear_object (&wizard->priv->finish_cancellable);
+       g_hash_table_remove_all (wizard->priv->store_passwords);
+
+       error = e_simple_async_result_get_user_data (E_SIMPLE_ASYNC_RESULT (result));
+       if (error) {
+               is_cancelled = g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+
+               gtk_label_set_text (GTK_LABEL (wizard->priv->finish_label), error->message);
+       }
+
+       e_spinner_stop (E_SPINNER (wizard->priv->finish_spinner));
+
+       gtk_widget_set_visible (wizard->priv->finish_running_box, error && !is_cancelled);
+       gtk_widget_set_visible (wizard->priv->finish_spinner, FALSE);
+       gtk_widget_set_visible (wizard->priv->finish_label, !is_cancelled);
+       gtk_widget_set_visible (wizard->priv->finish_cancel_button, FALSE);
+
+       g_object_notify (source_object, "can-run");
+
+       if (!error) {
+               ESource *source = wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION];
+
+               g_warn_if_fail (source != NULL);
+
+               g_signal_emit (wizard, signals[DONE], 0, e_source_get_uid (source));
+       }
+}
+
+static void
+collection_account_wizard_save_sources (ECollectionAccountWizard *wizard)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       ESimpleAsyncResult *simple_result;
+       ESource *source;
+       const gchar *display_name;
+       gint ii;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       g_hash_table_remove_all (wizard->priv->store_passwords);
+
+       model = gtk_tree_view_get_model (wizard->priv->parts_tree_view);
+       if (gtk_tree_model_get_iter_first (model, &iter)) {
+               do {
+                       gboolean enabled = FALSE;
+
+                       gtk_tree_model_get (model, &iter,
+                               PART_COLUMN_BOOL_ENABLED, &enabled,
+                               -1);
+
+                       if (enabled) {
+                               GtkTreeIter child;
+
+                               if (gtk_tree_model_iter_nth_child (model, &child, &iter, 0)) {
+                                       do {
+                                               enabled = FALSE;
+
+                                               gtk_tree_model_get (model, &child,
+                                                       PART_COLUMN_BOOL_ENABLED, &enabled,
+                                                       -1);
+
+                                               if (enabled) {
+                                                       EConfigLookupResult *lookup_result = NULL;
+
+                                                       gtk_tree_model_get (model, &child,
+                                                               PART_COLUMN_OBJECT_RESULT, &lookup_result,
+                                                               -1);
+
+                                                       if (lookup_result) {
+                                                               source = collection_account_wizard_get_source 
(wizard, e_config_lookup_result_get_kind (lookup_result));
+                                                               if (source) {
+                                                                       g_warn_if_fail 
(e_config_lookup_result_configure_source (lookup_result, wizard->priv->config_lookup, source));
+
+                                                                       if 
(e_config_lookup_result_get_password (lookup_result)) {
+                                                                               g_hash_table_insert 
(wizard->priv->store_passwords, e_source_dup_uid (source),
+                                                                                       g_strdup 
(e_config_lookup_result_get_password (lookup_result)));
+                                                                       }
+                                                               }
+
+                                                               g_clear_object (&lookup_result);
+                                                       }
+                                               }
+                                       } while (gtk_tree_model_iter_next (model, &child));
+                               }
+                       }
+               } while (gtk_tree_model_iter_next (model, &iter));
+       }
+
+       display_name = gtk_entry_get_text (GTK_ENTRY (wizard->priv->display_name_entry));
+
+       if (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE] ||
+           wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_SEND]) {
+               ESourceMailIdentity *identity_extension;
+
+               /* Ensure all three exist */
+               collection_account_wizard_get_source (wizard, E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE);
+               collection_account_wizard_get_source (wizard, E_CONFIG_LOOKUP_RESULT_MAIL_SEND);
+
+               source = collection_account_wizard_get_source (wizard, 
FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY);
+               identity_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
+               e_source_mail_identity_set_address (identity_extension, gtk_entry_get_text (GTK_ENTRY 
(wizard->priv->email_entry)));
+       } else {
+               g_clear_object (&wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE]);
+               g_clear_object (&wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_MAIL_SEND]);
+               g_clear_object (&wizard->priv->sources[FAKE_E_CONFIG_LOOKUP_RESULT_MAIL_IDENTITY]);
+       }
+
+       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+               source = wizard->priv->sources[ii];
+
+               if (source) {
+                       if (ii == E_CONFIG_LOOKUP_RESULT_COLLECTION) {
+                               ESourceAuthentication *authentication_extension;
+                               ESourceCollection *collection_extension;
+                               const gchar *user;
+
+                               authentication_extension = e_source_get_extension (source, 
E_SOURCE_EXTENSION_AUTHENTICATION);
+                               collection_extension = e_source_get_extension (source, 
E_SOURCE_EXTENSION_COLLECTION);
+                               user = gtk_entry_get_text (GTK_ENTRY (wizard->priv->email_entry));
+
+                               if (!e_source_authentication_get_user (authentication_extension))
+                                       e_source_authentication_set_user (authentication_extension, user);
+
+                               if (!e_source_collection_get_identity (collection_extension))
+                                       e_source_collection_set_identity (collection_extension, user);
+                       } else {
+                               e_source_set_parent (source, e_source_get_uid 
(wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION]));
+                       }
+
+                       e_source_set_display_name (source, display_name);
+               }
+       }
+
+       g_warn_if_fail (wizard->priv->finish_cancellable == NULL);
+
+       gtk_label_set_text (GTK_LABEL (wizard->priv->finish_label), _("Saving account settings, please 
wait…"));
+       gtk_widget_show (wizard->priv->finish_spinner);
+       gtk_widget_show (wizard->priv->finish_label);
+       gtk_widget_show (wizard->priv->finish_cancel_button);
+       gtk_widget_show (wizard->priv->finish_running_box);
+
+       e_spinner_start (E_SPINNER (wizard->priv->finish_spinner));
+
+       wizard->priv->finish_cancellable = camel_operation_new ();
+
+       g_signal_connect (wizard->priv->finish_cancellable, "status",
+               G_CALLBACK (collection_account_wizard_update_status_cb), wizard->priv->finish_label);
+
+       simple_result = e_simple_async_result_new (G_OBJECT (wizard),
+               collection_account_wizard_write_changes_done, NULL,
+               collection_account_wizard_write_changes_done);
+
+       e_simple_async_result_run_in_thread (simple_result, G_PRIORITY_HIGH_IDLE,
+               collection_account_wizard_write_changes_thread, wizard->priv->finish_cancellable);
+
+       g_object_unref (simple_result);
+
+       g_object_notify (G_OBJECT (wizard), "can-run");
+}
+
+static void
+collection_account_wizard_finish_cancel_clicked_cb (GtkButton *button,
+                                                   gpointer user_data)
+{
+       ECollectionAccountWizard *wizard = user_data;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       if (wizard->priv->finish_cancellable)
+               g_cancellable_cancel (wizard->priv->finish_cancellable);
+}
+
+static void
+collection_account_wizard_set_registry (ECollectionAccountWizard *wizard,
+                                       ESourceRegistry *registry)
+{
+       g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+       g_return_if_fail (wizard->priv->registry == NULL);
+
+       wizard->priv->registry = g_object_ref (registry);
+}
+
+static void
+collection_account_wizard_set_property (GObject *object,
+                                       guint property_id,
+                                       const GValue *value,
+                                       GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_REGISTRY:
+                       collection_account_wizard_set_registry (
+                               E_COLLECTION_ACCOUNT_WIZARD (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+collection_account_wizard_get_property (GObject *object,
+                                       guint property_id,
+                                       GValue *value,
+                                       GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CAN_RUN:
+                       g_value_set_boolean (
+                               value,
+                               e_collection_account_wizard_get_can_run (
+                               E_COLLECTION_ACCOUNT_WIZARD (object)));
+                       return;
+
+               case PROP_REGISTRY:
+                       g_value_set_object (
+                               value,
+                               e_collection_account_wizard_get_registry (
+                               E_COLLECTION_ACCOUNT_WIZARD (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+collection_account_wizard_constructed (GObject *object)
+{
+       ECollectionAccountWizard *wizard = E_COLLECTION_ACCOUNT_WIZARD (object);
+       GtkBox *hbox, *vbox;
+       GtkGrid *grid;
+       GtkWidget *label, *widget, *expander, *scrolled_window;
+       GtkTreeStore *tree_store;
+       GSList *workers, *link;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *cell_renderer;
+       gchar *markup;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_collection_account_wizard_parent_class)->constructed (object);
+
+       g_object_set (object,
+               "show-border", FALSE,
+               "show-tabs", FALSE,
+               NULL);
+
+       wizard->priv->config_lookup = e_config_lookup_new (wizard->priv->registry);
+
+       g_signal_connect_swapped (wizard->priv->config_lookup, "get-source",
+               G_CALLBACK (collection_account_wizard_get_source_cb), wizard);
+
+       g_signal_connect (wizard->priv->config_lookup, "worker-started",
+               G_CALLBACK (collection_account_wizard_worker_started_cb), wizard);
+
+       g_signal_connect (wizard->priv->config_lookup, "worker-finished",
+               G_CALLBACK (collection_account_wizard_worker_finished_cb), wizard);
+
+       /* Lookup page */
+
+       vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 6));
+       g_object_set (G_OBJECT (vbox),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               NULL);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       g_object_set (G_OBJECT (grid),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "border-width", 12,
+               "row-spacing", 6,
+               "column-spacing", 6,
+               NULL);
+
+       widget = gtk_frame_new (_("User details"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_CENTER,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+
+       gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
+
+       label = gtk_label_new_with_mnemonic (_("_Email Address or User name:"));
+       g_object_set (G_OBJECT (label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               NULL);
+
+       widget = gtk_entry_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               NULL);
+       wizard->priv->email_entry = widget;
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+
+       gtk_grid_attach (grid, label, 0, 0, 1, 1);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+       g_signal_connect_swapped (wizard->priv->email_entry, "changed",
+               G_CALLBACK (collection_account_wizard_notify_can_run), wizard);
+
+       g_signal_connect_swapped (wizard->priv->email_entry, "changed",
+               G_CALLBACK (collection_account_wizard_mark_changed), wizard);
+
+       expander = gtk_expander_new_with_mnemonic (_("_Advanced Options"));
+       gtk_widget_show (expander);
+       wizard->priv->advanced_expander = expander;
+       gtk_grid_attach (grid, expander, 0, 1, 2, 1);
+
+       label = gtk_label_new_with_mnemonic (_("_Server:"));
+       g_object_set (G_OBJECT (label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", FALSE,
+               NULL);
+
+       widget = gtk_entry_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", FALSE,
+               NULL);
+       wizard->priv->servers_entry = widget;
+       gtk_widget_set_tooltip_text (widget, _("Semicolon (';') separated list of servers to look up 
information for, in addition to the domain of the e-mail address."));
+
+       g_signal_connect_swapped (wizard->priv->servers_entry, "changed",
+               G_CALLBACK (collection_account_wizard_mark_changed), wizard);
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+
+       gtk_grid_attach (grid, label, 0, 2, 1, 1);
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+
+       e_binding_bind_property (expander, "expanded", label, "visible", G_BINDING_DEFAULT | 
G_BINDING_SYNC_CREATE);
+       e_binding_bind_property (expander, "expanded", widget, "visible", G_BINDING_DEFAULT | 
G_BINDING_SYNC_CREATE);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               NULL);
+
+       gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
+
+       label = gtk_label_new ("");
+       g_object_set (G_OBJECT (label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "margin-start", 12,
+               "margin-top", 12,
+               "visible", TRUE,
+               "max-width-chars", 120,
+               "wrap", TRUE,
+               NULL);
+
+       gtk_box_pack_start (vbox, label, FALSE, FALSE, 0);
+
+       wizard->priv->results_label = label;
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (wizard), GTK_WIDGET (vbox), NULL);
+
+       vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 4));
+       g_object_set (G_OBJECT (vbox),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (vbox));
+
+       workers = e_config_lookup_dup_registered_workers (wizard->priv->config_lookup);
+
+       for (link = workers; link; link = g_slist_next (link)) {
+               EConfigLookupWorker *worker = link->data;
+               WorkerData *wd;
+
+               if (!worker)
+                       continue;
+
+               wd = g_new0 (WorkerData, 1);
+
+               widget = gtk_check_button_new_with_label (e_config_lookup_worker_get_display_name (worker));
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", TRUE,
+                       "halign", GTK_ALIGN_FILL,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "margin-start", 12,
+                       "visible", TRUE,
+                       "active", TRUE,
+                       NULL);
+               wd->enabled_check = widget;
+
+               g_signal_connect_swapped (wd->enabled_check, "toggled",
+                       G_CALLBACK (collection_account_wizard_notify_can_run), wizard);
+
+               g_signal_connect_swapped (wd->enabled_check, "toggled",
+                       G_CALLBACK (collection_account_wizard_mark_changed), wizard);
+
+               gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
+
+               widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_START,
+                       "margin-start", 12,
+                       "visible", TRUE,
+                       NULL);
+               gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
+
+               e_binding_bind_property (wd->enabled_check, "active", widget, "visible", G_BINDING_DEFAULT | 
G_BINDING_SYNC_CREATE);
+
+               hbox = GTK_BOX (widget);
+
+               /* spacer */
+               widget = gtk_label_new ("");
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_START,
+                       "margin-start", 12,
+                       "visible", TRUE,
+                       NULL);
+               gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+               widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_START,
+                       "margin-start", 12,
+                       "visible", FALSE,
+                       NULL);
+               wd->running_box = widget;
+
+               gtk_box_pack_start (hbox, widget, TRUE, TRUE, 0);
+
+               hbox = GTK_BOX (widget);
+
+               widget = e_spinner_new ();
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "visible", TRUE,
+                       NULL);
+               wd->spinner = widget;
+
+               gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+               label = gtk_label_new (NULL);
+               g_object_set (G_OBJECT (label),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "visible", TRUE,
+                       "ellipsize", PANGO_ELLIPSIZE_END,
+                       NULL);
+               wd->running_label = label;
+
+               gtk_box_pack_start (hbox, label, FALSE, FALSE, 0);
+
+               g_signal_connect (wd->running_label, "activate-link",
+                       G_CALLBACK (collection_account_wizard_activate_link_cb), wizard);
+
+               e_binding_bind_property (wd->enabled_check, "sensitive", wd->running_label, "sensitive", 
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+               widget = e_dialog_button_new_with_icon ("process-stop", _("_Cancel"));
+               g_object_set (G_OBJECT (widget),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_START,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "visible", TRUE,
+                       NULL);
+               wd->cancel_button = widget;
+
+               g_signal_connect (wd->cancel_button, "clicked",
+                       G_CALLBACK (collection_account_wizard_worker_cancel_clicked_cb), wd);
+
+               gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+               g_hash_table_insert (wizard->priv->workers, g_object_ref (worker), wd);
+       }
+
+       g_slist_free_full (workers, g_object_unref);
+
+       /* Parts page */
+
+       vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 6));
+       g_object_set (G_OBJECT (vbox),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "margin-bottom", 12,
+               NULL);
+
+       label = gtk_label_new (_("Select which parts should be configured:"));
+       g_object_set (G_OBJECT (label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               NULL);
+
+       gtk_box_pack_start (vbox, label, FALSE, FALSE, 0);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "margin-bottom", TRUE,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               NULL);
+
+       scrolled_window = widget;
+
+       gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
+
+       tree_store = gtk_tree_store_new (PART_N_COLUMNS,
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_ENABLED */
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_ENABLED_VISIBLE */
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_RADIO */
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_SENSITIVE */
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_IS_COLLECTION_GROUP */
+               G_TYPE_BOOLEAN, /* PART_COLUMN_BOOL_ICON_VISIBLE */
+               G_TYPE_STRING,  /* PART_COLUMN_STRING_ICON_NAME */
+               G_TYPE_STRING,  /* PART_COLUMN_STRING_DESCRIPTION */
+               G_TYPE_STRING,  /* PART_COLUMN_STRING_PROTOCOL */
+               E_TYPE_CONFIG_LOOKUP_RESULT); /* PART_COLUMN_OBJECT_RESULT */
+
+       widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               NULL);
+       g_object_unref (tree_store);
+       wizard->priv->parts_tree_view = GTK_TREE_VIEW (widget);
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (wizard), GTK_WIDGET (vbox), NULL);
+
+       /* Column: Description */
+
+       column = gtk_tree_view_column_new ();
+       gtk_tree_view_column_set_expand (column, TRUE);
+       gtk_tree_view_column_set_title (column, _("Description"));
+
+       cell_renderer = gtk_cell_renderer_toggle_new ();
+       gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+       g_signal_connect (cell_renderer, "toggled",
+               G_CALLBACK (collection_account_wizard_part_enabled_toggled_cb), wizard);
+
+       gtk_tree_view_column_set_attributes (column, cell_renderer,
+               "sensitive", PART_COLUMN_BOOL_SENSITIVE,
+               "active", PART_COLUMN_BOOL_ENABLED,
+               "visible", PART_COLUMN_BOOL_ENABLED_VISIBLE,
+               "radio", PART_COLUMN_BOOL_RADIO,
+               NULL);
+
+       cell_renderer = gtk_cell_renderer_pixbuf_new ();
+       g_object_set (cell_renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+       gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+       gtk_tree_view_column_set_attributes (column, cell_renderer,
+               "sensitive", PART_COLUMN_BOOL_SENSITIVE,
+               "icon-name", PART_COLUMN_STRING_ICON_NAME,
+               "visible", PART_COLUMN_BOOL_ICON_VISIBLE,
+               NULL);
+
+       cell_renderer = gtk_cell_renderer_text_new ();
+       g_object_set (cell_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+       gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+       gtk_tree_view_column_set_attributes (column, cell_renderer,
+               "sensitive", PART_COLUMN_BOOL_SENSITIVE,
+               "markup", PART_COLUMN_STRING_DESCRIPTION,
+               NULL);
+
+       gtk_tree_view_append_column (wizard->priv->parts_tree_view, column);
+       gtk_tree_view_set_expander_column (wizard->priv->parts_tree_view, column);
+
+       /* Column: Type */
+
+       column = gtk_tree_view_column_new ();
+       gtk_tree_view_column_set_expand (column, FALSE);
+       gtk_tree_view_column_set_title (column, _("Type"));
+
+       cell_renderer = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+
+       gtk_tree_view_column_set_attributes (column, cell_renderer,
+               "sensitive", PART_COLUMN_BOOL_SENSITIVE,
+               "text", PART_COLUMN_STRING_PROTOCOL,
+               NULL);
+
+       gtk_tree_view_append_column (wizard->priv->parts_tree_view, column);
+
+       /* Finish page */
+
+       grid = GTK_GRID (gtk_grid_new ());
+       g_object_set (G_OBJECT (grid),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "column-spacing", 4,
+               NULL);
+       markup = g_markup_printf_escaped ("<b>%s</b>", _("Account Information"));
+       widget = gtk_label_new (markup);
+       gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+       gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+       gtk_widget_set_margin_bottom (widget, 12);
+       gtk_grid_attach (grid, widget, 0, 0, 2, 1);
+       gtk_widget_show (widget);
+       g_free (markup);
+
+       label = gtk_label_new_with_mnemonic (_("_Name:"));
+       gtk_widget_set_margin_left (label, 12);
+       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+       gtk_grid_attach (grid, label, 0, 1, 1, 1);
+       gtk_widget_show (label);
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+       gtk_widget_show (widget);
+
+       wizard->priv->display_name_entry = widget;
+
+       g_signal_connect_swapped (wizard->priv->display_name_entry, "changed",
+               G_CALLBACK (collection_account_wizard_notify_can_run), wizard);
+
+       widget = gtk_label_new ("The above name will be used to identify this account.\nUse for example, 
“Work” or “Personal”.");
+       gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+       gtk_widget_show (widget);
+
+       vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 6));
+       g_object_set (G_OBJECT (vbox),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+
+       gtk_box_pack_end (vbox, GTK_WIDGET (grid), FALSE, FALSE, 0);
+
+       hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4));
+       g_object_set (G_OBJECT (hbox),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "margin-start", 12,
+               "visible", FALSE,
+               NULL);
+       wizard->priv->finish_running_box = GTK_WIDGET (hbox);
+
+       widget = e_spinner_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               NULL);
+       wizard->priv->finish_spinner = widget;
+
+       gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+       label = gtk_label_new (NULL);
+       g_object_set (G_OBJECT (label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               "max-width-chars", 120,
+               "wrap", TRUE,
+               NULL);
+       wizard->priv->finish_label = label;
+
+       gtk_box_pack_start (hbox, label, FALSE, FALSE, 0);
+
+       widget = e_dialog_button_new_with_icon ("process-stop", _("_Cancel"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "visible", TRUE,
+               NULL);
+       wizard->priv->finish_cancel_button = widget;
+
+       g_signal_connect (wizard->priv->finish_cancel_button, "clicked",
+               G_CALLBACK (collection_account_wizard_finish_cancel_clicked_cb), wizard);
+
+       gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (wizard), GTK_WIDGET (vbox), NULL);
+
+       gtk_notebook_set_current_page (GTK_NOTEBOOK (wizard), 0);
+}
+
+static void
+collection_account_wizard_dispose (GObject *object)
+{
+       ECollectionAccountWizard *wizard = E_COLLECTION_ACCOUNT_WIZARD (object);
+       gint ii;
+
+       g_clear_object (&wizard->priv->registry);
+       g_clear_object (&wizard->priv->config_lookup);
+       g_clear_object (&wizard->priv->finish_cancellable);
+
+       if (wizard->priv->workers) {
+               g_hash_table_destroy (wizard->priv->workers);
+               wizard->priv->workers = NULL;
+       }
+
+       if (wizard->priv->store_passwords) {
+               g_hash_table_destroy (wizard->priv->store_passwords);
+               wizard->priv->store_passwords = NULL;
+       }
+
+       g_warn_if_fail (wizard->priv->running_result == NULL);
+
+       if (wizard->priv->running_result) {
+               e_simple_async_result_complete_idle (wizard->priv->running_result);
+               g_clear_object (&wizard->priv->running_result);
+       }
+
+       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+               g_clear_object (&wizard->priv->sources[ii]);
+       }
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_collection_account_wizard_parent_class)->dispose (object);
+}
+
+static void
+e_collection_account_wizard_class_init (ECollectionAccountWizardClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECollectionAccountWizardPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = collection_account_wizard_set_property;
+       object_class->get_property = collection_account_wizard_get_property;
+       object_class->constructed = collection_account_wizard_constructed;
+       object_class->dispose = collection_account_wizard_dispose;
+
+       /**
+        * ECollectionAccountWizard:registry:
+        *
+        * The #ESourceRegistry manages #ESource instances.
+        *
+        * Since: 3.28
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_REGISTRY,
+               g_param_spec_object (
+                       "registry",
+                       "Registry",
+                       "Data source registry",
+                       E_TYPE_SOURCE_REGISTRY,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * ECollectionAccountWizard:can-run:
+        *
+        * Whether can call e_collection_account_wizard_run().
+        * See e_collection_account_wizard_get_can_run() for more information.
+        *
+        * Since: 3.28
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_RUN,
+               g_param_spec_boolean (
+                       "can-run",
+                       "Can Run",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * ECollectionAccountWizard::done:
+        * @uid: an #ESource UID which had been created
+        *
+        * Emitted to notify about the wizard being done.
+        *
+        * Since: 3.28
+        **/
+       signals[DONE] = g_signal_new (
+               "done",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (ECollectionAccountWizardClass, done),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+static void
+e_collection_account_wizard_init (ECollectionAccountWizard *wizard)
+{
+       gint ii;
+
+       wizard->priv = G_TYPE_INSTANCE_GET_PRIVATE (wizard, E_TYPE_COLLECTION_ACCOUNT_WIZARD, 
ECollectionAccountWizardPrivate);
+       wizard->priv->workers = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, 
worker_data_free);
+       wizard->priv->store_passwords = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+       wizard->priv->running_workers = 0;
+
+       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+               wizard->priv->sources[ii] = NULL;
+       }
+}
+
+/**
+ * e_collection_account_wizard_new:
+ * @registry: an #ESourceRegistry
+ *
+ * Creates a new #ECollectionAccountWizard instance.
+ *
+ * Returns: (transfer full): a new #ECollectionAccountWizard
+ *
+ * Since: 3.28
+ **/
+GtkWidget *
+e_collection_account_wizard_new (ESourceRegistry *registry)
+{
+       g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+       return g_object_new (E_TYPE_COLLECTION_ACCOUNT_WIZARD,
+               "registry", registry,
+               NULL);
+}
+
+/**
+ * e_collection_account_wizard_get_registry:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Returns the #ESourceRegistry passed to e_collection_account_wizard_new().
+ *
+ * Returns: (transfer none): an #ESourceRegistry
+ *
+ * Since: 3.28
+ **/
+ESourceRegistry *
+e_collection_account_wizard_get_registry (ECollectionAccountWizard *wizard)
+{
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), NULL);
+
+       return wizard->priv->registry;
+}
+
+/**
+ * e_collection_account_wizard_get_can_run:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Returns whether e_collection_account_wizard_run() can be called, that is,
+ * whether at least one worker is enabled to run and the @wizard is not
+ * running.
+ *
+ * Returns: whether e_collection_account_wizard_run() can be called.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_collection_account_wizard_get_can_run (ECollectionAccountWizard *wizard)
+{
+       GHashTableIter iter;
+       gpointer value;
+       const gchar *email;
+       gint current_page;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), FALSE);
+
+       if (wizard->priv->running_workers ||
+           wizard->priv->running_result ||
+           wizard->priv->finish_cancellable)
+               return FALSE;
+
+       email = gtk_entry_get_text (GTK_ENTRY (wizard->priv->email_entry));
+       if (!email || !*email)
+               return FALSE;
+
+       current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (wizard));
+
+       if (current_page == 1) { /* Parts page */
+               GtkTreeModel *model;
+               GtkTreeIter iter;
+
+               model = gtk_tree_view_get_model (wizard->priv->parts_tree_view);
+               if (gtk_tree_model_get_iter_first (model, &iter)) {
+                       do {
+                               gboolean enabled = FALSE, is_collection_group = FALSE;
+
+                               gtk_tree_model_get (model, &iter,
+                                       PART_COLUMN_BOOL_ENABLED, &enabled,
+                                       PART_COLUMN_BOOL_IS_COLLECTION_GROUP, &is_collection_group,
+                                       -1);
+
+                               if (enabled && is_collection_group) {
+                                       /* Collection is not with radio, verify at least one child is 
selected */
+                                       GtkTreeIter child;
+
+                                       if (gtk_tree_model_iter_nth_child (model, &child, &iter, 0)) {
+                                               do {
+                                                       enabled = FALSE;
+
+                                                       gtk_tree_model_get (model, &child,
+                                                               PART_COLUMN_BOOL_ENABLED, &enabled,
+                                                               -1);
+
+                                                       if (enabled)
+                                                               return TRUE;
+                                               } while (gtk_tree_model_iter_next (model, &child));
+                                       }
+                               } else if (enabled) {
+                                       return TRUE;
+                               }
+                       } while (gtk_tree_model_iter_next (model, &iter));
+               }
+
+               return FALSE;
+       } else if (current_page == 2) { /* Finish page */
+               gchar *display_name;
+               gboolean can_run;
+
+               display_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (wizard->priv->display_name_entry)));
+               if (!display_name)
+                       return FALSE;
+
+               g_strstrip (display_name);
+
+               can_run = display_name && *display_name;
+
+               g_free (display_name);
+
+               return can_run;
+       }
+
+       /* Look up page */
+
+       g_hash_table_iter_init (&iter, wizard->priv->workers);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               WorkerData *wd = value;
+
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wd->enabled_check)))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+/**
+ * e_collection_account_wizard_reset:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Resets content of the @wizard to the initial state. This might be called
+ * whenever the widget is going to be shown.
+ *
+ * Since: 3.28
+ **/
+void
+e_collection_account_wizard_reset (ECollectionAccountWizard *wizard)
+{
+       GtkTreeModel *model;
+       GHashTableIter iter;
+       gpointer value;
+       gint ii;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       e_collection_account_wizard_abort (wizard);
+
+       g_hash_table_iter_init (&iter, wizard->priv->workers);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               WorkerData *wd = value;
+
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wd->enabled_check), TRUE);
+               gtk_widget_hide (wd->running_box);
+               e_named_parameters_free (wd->restart_params);
+               wd->restart_params = NULL;
+       }
+
+       gtk_entry_set_text (GTK_ENTRY (wizard->priv->email_entry), "");
+       gtk_entry_set_text (GTK_ENTRY (wizard->priv->servers_entry), "");
+       gtk_label_set_text (GTK_LABEL (wizard->priv->results_label), "");
+       gtk_entry_set_text (GTK_ENTRY (wizard->priv->display_name_entry), "");
+       gtk_expander_set_expanded (GTK_EXPANDER (wizard->priv->advanced_expander), FALSE);
+       e_config_lookup_clear_results (wizard->priv->config_lookup);
+
+       model = gtk_tree_view_get_model (wizard->priv->parts_tree_view);
+       gtk_tree_store_clear (GTK_TREE_STORE (model));
+
+       wizard->priv->changed = FALSE;
+
+       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+               g_clear_object (&wizard->priv->sources[ii]);
+       }
+
+       gtk_notebook_set_current_page (GTK_NOTEBOOK (wizard), 0);
+
+       g_object_notify (G_OBJECT (wizard), "can-run");
+}
+
+/**
+ * e_collection_account_wizard_next:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Instructs the @wizard to advance to the next step. It does nothing
+ * when there is an ongoing lookup or when the current page cannot
+ * be advanced.
+ *
+ * This can influence e_collection_account_wizard_is_finish_page().
+ *
+ * Returns: %TRUE, when the step had been changed, %FALSE otherwise.
+ *   Note that when this is called on a finish page, then the %TRUE
+ *   means that the @wizard finished all its settings and should be
+ *   closed now.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_collection_account_wizard_next (ECollectionAccountWizard *wizard)
+{
+       gboolean changed = FALSE;
+       const gchar *text;
+       gint ii;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), FALSE);
+
+       switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (wizard))) {
+       case 0: /* Lookup page */
+               if (wizard->priv->changed ||
+                   !e_config_lookup_count_results (wizard->priv->config_lookup)) {
+                       for (ii = 0; ii <= E_CONFIG_LOOKUP_RESULT_LAST_KIND; ii++) {
+                               g_clear_object (&wizard->priv->sources[ii]);
+                       }
+
+                       wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION] = e_source_new (NULL, NULL, 
NULL);
+                       e_source_get_extension (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION], 
E_SOURCE_EXTENSION_COLLECTION);
+
+                       e_collection_account_wizard_run (wizard, NULL, NULL);
+                       changed = TRUE;
+               } else if (collection_account_wizard_fill_results (wizard)) {
+                       gtk_notebook_set_current_page (GTK_NOTEBOOK (wizard), 1);
+                       changed = TRUE;
+               }
+               break;
+       case 1: /* Parts page */
+               g_warn_if_fail (wizard->priv->sources[E_CONFIG_LOOKUP_RESULT_COLLECTION] != NULL);
+
+               text = gtk_entry_get_text (GTK_ENTRY (wizard->priv->display_name_entry));
+               if (!text || !*text) {
+                       gtk_entry_set_text (GTK_ENTRY (wizard->priv->display_name_entry),
+                               gtk_entry_get_text (GTK_ENTRY (wizard->priv->email_entry)));
+               }
+
+               gtk_notebook_set_current_page (GTK_NOTEBOOK (wizard), 2);
+               changed = TRUE;
+               break;
+       case 2: /* Finish page */
+               /* It's an asynchronous operation, just fail or run it */
+               collection_account_wizard_save_sources (wizard);
+               changed = FALSE;
+               break;
+       }
+
+       return changed;
+}
+
+/**
+ * e_collection_account_wizard_prev:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Instructs the @wizard to go back to the previous step.
+ *
+ * This can influence e_collection_account_wizard_is_finish_page().
+ *
+ * Returns: %TRUE, when the step had been changed, %FALSE otherwise.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_collection_account_wizard_prev (ECollectionAccountWizard *wizard)
+{
+       gint current_page;
+
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), FALSE);
+
+       current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (wizard));
+
+       if (current_page < 1)
+               return FALSE;
+
+       gtk_notebook_set_current_page (GTK_NOTEBOOK (wizard), current_page - 1);
+
+       return TRUE;
+}
+
+/**
+ * e_collection_account_wizard_is_finish_page:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Returns: whether the @wizard is at the last page.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_collection_account_wizard_is_finish_page (ECollectionAccountWizard *wizard)
+{
+       g_return_val_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard), FALSE);
+
+       return gtk_notebook_get_current_page (GTK_NOTEBOOK (wizard)) == gtk_notebook_get_n_pages 
(GTK_NOTEBOOK (wizard)) - 1;
+}
+
+/**
+ * e_collection_account_wizard_run:
+ * @wizard: an #ECollectionAccountWizard
+ * @callback: a callback to call, when the run is finished
+ * @user_data: user data for the @callback
+ *
+ * Runs lookup for all enabled lookup workers. Finish the call
+ * with e_collection_account_wizard_run_finish() from the @callback.
+ *
+ * This function can be called only if e_collection_account_wizard_get_can_run()
+ * returns %TRUE.
+ *
+ * Since: 3.28
+ **/
+void
+e_collection_account_wizard_run (ECollectionAccountWizard *wizard,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       gboolean any_worker = FALSE;
+
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+       g_return_if_fail (e_collection_account_wizard_get_can_run (wizard));
+
+       wizard->priv->running_result = e_simple_async_result_new (G_OBJECT (wizard), callback, user_data, 
e_collection_account_wizard_run);
+
+       g_hash_table_iter_init (&iter, wizard->priv->workers);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               EConfigLookupWorker *worker = key;
+               WorkerData *wd = value;
+
+               if (worker && wd &&
+                   gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wd->enabled_check))) {
+                       ENamedParameters *params;
+
+                       params = e_named_parameters_new_clone (wd->restart_params);
+                       if (!params)
+                               params = e_named_parameters_new ();
+                       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS, 
gtk_entry_get_text (GTK_ENTRY (wizard->priv->email_entry)));
+                       e_named_parameters_set (params, E_CONFIG_LOOKUP_PARAM_SERVERS, gtk_entry_get_text 
(GTK_ENTRY (wizard->priv->servers_entry)));
+
+                       any_worker = TRUE;
+                       e_config_lookup_run_worker (wizard->priv->config_lookup, worker, params, NULL);
+
+                       e_named_parameters_free (params);
+               }
+       }
+
+       if (!any_worker) {
+               e_simple_async_result_complete_idle (wizard->priv->running_result);
+               g_clear_object (&wizard->priv->running_result);
+       }
+}
+
+/**
+ * e_collection_account_wizard_run_finish:
+ * @wizard: an #ECollectionAccountWizard
+ * @result: result of the operation
+ *
+ * Finishes the wizard run issued by e_collection_account_wizard_run().
+ * It doesn't return anything, because everything is handled within
+ * the @wizard, thus it is provided mainly for consistency with asynchronous API.
+ *
+ * Since: 3.28
+ **/
+void
+e_collection_account_wizard_run_finish (ECollectionAccountWizard *wizard,
+                                       GAsyncResult *result)
+{
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+       g_return_if_fail (G_IS_ASYNC_RESULT (result));
+       g_return_if_fail (g_async_result_is_tagged (result, e_collection_account_wizard_run));
+}
+
+/**
+ * e_collection_account_wizard_abort:
+ * @wizard: an #ECollectionAccountWizard
+ *
+ * Aborts any ongoing operation the @wizard may run. If there is nothing
+ * running, then does nothing.
+ *
+ * Since: 3.28
+ **/
+void
+e_collection_account_wizard_abort (ECollectionAccountWizard *wizard)
+{
+       g_return_if_fail (E_IS_COLLECTION_ACCOUNT_WIZARD (wizard));
+
+       e_config_lookup_cancel_all (wizard->priv->config_lookup);
+
+       if (wizard->priv->finish_cancellable)
+               g_cancellable_cancel (wizard->priv->finish_cancellable);
+}
diff --git a/src/e-util/e-collection-account-wizard.h b/src/e-util/e-collection-account-wizard.h
new file mode 100644
index 0000000..68e3e29
--- /dev/null
+++ b/src/e-util/e-collection-account-wizard.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_COLLECTION_ACCOUNT_WIZARD_H
+#define E_COLLECTION_ACCOUNT_WIZARD_H
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COLLECTION_ACCOUNT_WIZARD \
+       (e_collection_account_wizard_get_type ())
+#define E_COLLECTION_ACCOUNT_WIZARD(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COLLECTION_ACCOUNT_WIZARD, ECollectionAccountWizard))
+#define E_COLLECTION_ACCOUNT_WIZARD_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COLLECTION_ACCOUNT_WIZARD, ECollectionAccountWizardClass))
+#define E_IS_COLLECTION_ACCOUNT_WIZARD(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COLLECTION_ACCOUNT_WIZARD))
+#define E_IS_COLLECTION_ACCOUNT_WIZARD_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COLLECTION_ACCOUNT_WIZARD))
+#define E_COLLECTION_ACCOUNT_WIZARD_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COLLECTION_ACCOUNT_WIZARD, ECollectionAccountWizardClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECollectionAccountWizard ECollectionAccountWizard;
+typedef struct _ECollectionAccountWizardClass ECollectionAccountWizardClass;
+typedef struct _ECollectionAccountWizardPrivate ECollectionAccountWizardPrivate;
+
+/**
+ * ECollectionAccountWizard:
+ *
+ * Contains only private data that should be read and manipulated using
+ * the functions below.
+ *
+ * Since: 3.28
+ **/
+struct _ECollectionAccountWizard {
+       /*< private >*/
+       GtkNotebook parent;
+       ECollectionAccountWizardPrivate *priv;
+};
+
+struct _ECollectionAccountWizardClass {
+       /*< private >*/
+       GtkNotebookClass parent_class;
+
+       /* Signals */
+       void            (* done)                (ECollectionAccountWizard *wizard,
+                                                const gchar *uid);
+};
+
+GType          e_collection_account_wizard_get_type            (void) G_GNUC_CONST;
+GtkWidget *    e_collection_account_wizard_new                 (ESourceRegistry *registry);
+ESourceRegistry *
+               e_collection_account_wizard_get_registry        (ECollectionAccountWizard *wizard);
+gboolean       e_collection_account_wizard_get_can_run         (ECollectionAccountWizard *wizard);
+void           e_collection_account_wizard_reset               (ECollectionAccountWizard *wizard);
+gboolean       e_collection_account_wizard_next                (ECollectionAccountWizard *wizard);
+gboolean       e_collection_account_wizard_prev                (ECollectionAccountWizard *wizard);
+gboolean       e_collection_account_wizard_is_finish_page      (ECollectionAccountWizard *wizard);
+void           e_collection_account_wizard_run                 (ECollectionAccountWizard *wizard,
+                                                                GAsyncReadyCallback callback,
+                                                                gpointer user_data);
+void           e_collection_account_wizard_run_finish          (ECollectionAccountWizard *wizard,
+                                                                GAsyncResult *result);
+void           e_collection_account_wizard_abort               (ECollectionAccountWizard *wizard);
+
+G_END_DECLS
+
+#endif /* E_COLLECTION_ACCOUNT_WIZARD_H */
diff --git a/src/e-util/e-config-lookup-result-simple.c b/src/e-util/e-config-lookup-result-simple.c
index af60006..5cd16ba 100644
--- a/src/e-util/e-config-lookup-result-simple.c
+++ b/src/e-util/e-config-lookup-result-simple.c
@@ -35,6 +35,7 @@
 #include <libedataserver/libedataserver.h>
 
 #include "e-util-enumtypes.h"
+#include "e-config-lookup.h"
 #include "e-config-lookup-result.h"
 
 #include "e-config-lookup-result-simple.h"
@@ -46,6 +47,7 @@ struct _EConfigLookupResultSimplePrivate {
        gchar *protocol;
        gchar *display_name;
        gchar *description;
+       gchar *password;
        GSList *values; /* ValueData * */
 };
 
@@ -56,7 +58,8 @@ enum {
        PROP_IS_COMPLETE,
        PROP_PROTOCOL,
        PROP_DISPLAY_NAME,
-       PROP_DESCRIPTION
+       PROP_DESCRIPTION,
+       PROP_PASSWORD
 };
 
 static void e_config_lookup_result_simple_result_init (EConfigLookupResultInterface *iface);
@@ -148,14 +151,24 @@ config_lookup_result_simple_get_description (EConfigLookupResult *lookup_result)
        return E_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result)->priv->description;
 }
 
+static const gchar *
+config_lookup_result_simple_get_password (EConfigLookupResult *lookup_result)
+{
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result), NULL);
+
+       return E_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result)->priv->password;
+}
+
 static gboolean
 config_lookup_result_simple_configure_source (EConfigLookupResult *lookup_result,
+                                             EConfigLookup *config_lookup,
                                              ESource *source)
 {
        EConfigLookupResultSimple *result_simple;
        GSList *link;
 
        g_return_val_if_fail (E_IS_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result), FALSE);
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
        g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
 
        result_simple = E_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result);
@@ -192,18 +205,20 @@ config_lookup_result_simple_configure_source (EConfigLookupResult *lookup_result
 
 static gboolean
 config_lookup_result_simple_configure_source_wrapper (EConfigLookupResult *lookup_result,
+                                                     EConfigLookup *config_lookup,
                                                      ESource *source)
 {
        EConfigLookupResultSimpleClass *klass;
 
        g_return_val_if_fail (E_IS_CONFIG_LOOKUP_RESULT_SIMPLE (lookup_result), FALSE);
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
        g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
 
        klass = E_CONFIG_LOOKUP_RESULT_SIMPLE_GET_CLASS (lookup_result);
        g_return_val_if_fail (klass != NULL, FALSE);
        g_return_val_if_fail (klass->configure_source != NULL, FALSE);
 
-       return klass->configure_source (lookup_result, source);
+       return klass->configure_source (lookup_result, config_lookup, source);
 }
 
 static void
@@ -287,6 +302,12 @@ config_lookup_result_simple_set_property (GObject *object,
                                result_simple, g_value_get_string (value),
                                &result_simple->priv->description);
                        return;
+
+               case PROP_PASSWORD:
+                       config_lookup_result_simple_set_string (
+                               result_simple, g_value_get_string (value),
+                               &result_simple->priv->password);
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -340,6 +361,13 @@ config_lookup_result_simple_get_property (GObject *object,
                                config_lookup_result_simple_get_description (
                                E_CONFIG_LOOKUP_RESULT (object)));
                        return;
+
+               case PROP_PASSWORD:
+                       g_value_set_string (
+                               value,
+                               config_lookup_result_simple_get_password (
+                               E_CONFIG_LOOKUP_RESULT (object)));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -353,6 +381,7 @@ config_lookup_result_simple_finalize (GObject *object)
        g_free (result_simple->priv->protocol);
        g_free (result_simple->priv->display_name);
        g_free (result_simple->priv->description);
+       e_util_safe_free_string (result_simple->priv->password);
        g_slist_free_full (result_simple->priv->values, value_data_free);
 
        /* Chain up to parent's method. */
@@ -488,6 +517,26 @@ e_config_lookup_result_simple_class_init (EConfigLookupResultSimpleClass *klass)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EConfigLookupResultSimple:password:
+        *
+        * The password to store for the #EConfigLookupResult.
+        * Can be %NULL, to not store any.
+        *
+        * Since: 3.28
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_PASSWORD,
+               g_param_spec_string (
+                       "password",
+                       "Password",
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -499,6 +548,7 @@ e_config_lookup_result_simple_result_init (EConfigLookupResultInterface *iface)
        iface->get_protocol = config_lookup_result_simple_get_protocol;
        iface->get_display_name = config_lookup_result_simple_get_display_name;
        iface->get_description = config_lookup_result_simple_get_description;
+       iface->get_password = config_lookup_result_simple_get_password;
        iface->configure_source = config_lookup_result_simple_configure_source_wrapper;
 }
 
@@ -516,6 +566,7 @@ e_config_lookup_result_simple_init (EConfigLookupResultSimple *result_simple)
  * @protocol: (nullable): protocol name of the result, or %NULL
  * @display_name: display name of the result
  * @description: description of the result
+ * @password: (nullable): password to store with the result
  *
  * Creates a new #EConfigLookupResultSimple instance with prefilled values.
  *
@@ -529,7 +580,8 @@ e_config_lookup_result_simple_new (EConfigLookupResultKind kind,
                                   gboolean is_complete,
                                   const gchar *protocol,
                                   const gchar *display_name,
-                                  const gchar *description)
+                                  const gchar *description,
+                                  const gchar *password)
 {
        g_return_val_if_fail (kind != E_CONFIG_LOOKUP_RESULT_UNKNOWN, NULL);
        g_return_val_if_fail (display_name != NULL, NULL);
@@ -542,6 +594,7 @@ e_config_lookup_result_simple_new (EConfigLookupResultKind kind,
                "protocol", protocol,
                "display-name", display_name,
                "description", description,
+               "password", password,
                NULL);
 }
 
diff --git a/src/e-util/e-config-lookup-result-simple.h b/src/e-util/e-config-lookup-result-simple.h
index 1942204..3c20e93 100644
--- a/src/e-util/e-config-lookup-result-simple.h
+++ b/src/e-util/e-config-lookup-result-simple.h
@@ -46,6 +46,8 @@
 
 G_BEGIN_DECLS
 
+struct _EConfigLookup;
+
 typedef struct _EConfigLookupResultSimple EConfigLookupResultSimple;
 typedef struct _EConfigLookupResultSimpleClass EConfigLookupResultSimpleClass;
 typedef struct _EConfigLookupResultSimplePrivate EConfigLookupResultSimplePrivate;
@@ -69,6 +71,7 @@ struct _EConfigLookupResultSimpleClass {
        GObjectClass parent_class;
 
        gboolean        (* configure_source)            (EConfigLookupResult *lookup_result,
+                                                        struct _EConfigLookup *config_lookup,
                                                         ESource *source);
 };
 
@@ -79,7 +82,8 @@ EConfigLookupResult *
                                                         gboolean is_complete,
                                                         const gchar *protocol,
                                                         const gchar *display_name,
-                                                        const gchar *description);
+                                                        const gchar *description,
+                                                        const gchar *password);
 void           e_config_lookup_result_simple_add_value (EConfigLookupResult *lookup_result,
                                                         const gchar *extension_name,
                                                         const gchar *property_name,
diff --git a/src/e-util/e-config-lookup-result.c b/src/e-util/e-config-lookup-result.c
index 04e2a7b..4ee4825 100644
--- a/src/e-util/e-config-lookup-result.c
+++ b/src/e-util/e-config-lookup-result.c
@@ -32,6 +32,7 @@
 #include <libedataserver/libedataserver.h>
 
 #include "e-util-enums.h"
+#include "e-config-lookup.h"
 
 #include "e-config-lookup-result.h"
 
@@ -46,6 +47,7 @@ e_config_lookup_result_default_init (EConfigLookupResultInterface *iface)
        iface->get_protocol = NULL;
        iface->get_display_name = NULL;
        iface->get_description = NULL;
+       iface->get_password = NULL;
        iface->configure_source = NULL;
 }
 
@@ -184,11 +186,35 @@ e_config_lookup_result_get_description (EConfigLookupResult *lookup_result)
 }
 
 /**
+ * e_config_lookup_result_get_password:
+ * @lookup_result: an #EConfigLookupResult
+ *
+ * Returns: password to store with this @lookup_result, or %NULL for none
+ *
+ * Since: 3.28
+ **/
+const gchar *
+e_config_lookup_result_get_password (EConfigLookupResult *lookup_result)
+{
+       EConfigLookupResultInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP_RESULT (lookup_result), NULL);
+
+       iface = E_CONFIG_LOOKUP_RESULT_GET_INTERFACE (lookup_result);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_password != NULL, NULL);
+
+       return iface->get_password (lookup_result);
+}
+
+/**
  * e_config_lookup_result_configure_source:
  * @lookup_result: an #EConfigLookupResult
+ * @config_lookup: an #EConfigLookup
  * @source: an #ESource to configure
  *
- * Configures the @source with the looked up configuration.
+ * Configures the @source with the looked up configuration. The @config_lookup
+ * can be used to get other than the provided @source.
  *
  * Returns: %TRUE when made any changes to the @source, %FALSE otherwise
  *
@@ -196,17 +222,19 @@ e_config_lookup_result_get_description (EConfigLookupResult *lookup_result)
  **/
 gboolean
 e_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
+                                        EConfigLookup *config_lookup,
                                         ESource *source)
 {
        EConfigLookupResultInterface *iface;
 
        g_return_val_if_fail (E_IS_CONFIG_LOOKUP_RESULT (lookup_result), FALSE);
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
 
        iface = E_CONFIG_LOOKUP_RESULT_GET_INTERFACE (lookup_result);
        g_return_val_if_fail (iface != NULL, FALSE);
        g_return_val_if_fail (iface->configure_source != NULL, FALSE);
 
-       return iface->configure_source (lookup_result, source);
+       return iface->configure_source (lookup_result, config_lookup, source);
 }
 
 /**
@@ -219,7 +247,9 @@ e_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
  * and value greater than 0, when @lookup_result_a is after @lookup_result_b.
  *
  * The comparison is done on kind, is-complete, priority and display name values,
- * in this order.
+ * in this order. Due to this it doesn't mean that the two results are equal when
+ * the function returns 0, use e_config_lookup_result_equal() to check complete
+ * equality instead.
  *
  * Returns: strcmp()-like value, what the position between @lookup_result_a and
  *    @lookup_result_b is.
@@ -261,3 +291,38 @@ e_config_lookup_result_compare (gconstpointer lookup_result_a,
 
        return res;
 }
+
+/**
+ * e_config_lookup_result_equal:
+ * @lookup_result_a: the first #EConfigLookupResult
+ * @lookup_result_b: the second #EConfigLookupResult
+ *
+ * Returns: whether the two results are the same.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_config_lookup_result_equal (gconstpointer lookup_result_a,
+                             gconstpointer lookup_result_b)
+{
+       EConfigLookupResult *lra = (EConfigLookupResult *) lookup_result_a;
+       EConfigLookupResult *lrb = (EConfigLookupResult *) lookup_result_b;
+
+       if (!lra || !lrb || lra == lrb)
+               return lra == lrb;
+
+       return e_config_lookup_result_get_kind (lra) ==
+               e_config_lookup_result_get_kind (lrb) &&
+               e_config_lookup_result_get_priority (lra) ==
+               e_config_lookup_result_get_priority (lrb) &&
+               (e_config_lookup_result_get_is_complete (lra) ? 1 : 0) ==
+               (e_config_lookup_result_get_is_complete (lrb) ? 1 : 0) &&
+               g_strcmp0 (e_config_lookup_result_get_protocol (lra),
+                          e_config_lookup_result_get_protocol (lrb)) == 0 &&
+               g_strcmp0 (e_config_lookup_result_get_display_name (lra),
+                          e_config_lookup_result_get_display_name (lrb)) == 0 &&
+               g_strcmp0 (e_config_lookup_result_get_description (lra),
+                          e_config_lookup_result_get_description (lrb)) == 0 &&
+               g_strcmp0 (e_config_lookup_result_get_password (lra),
+                          e_config_lookup_result_get_password (lrb)) == 0;
+}
diff --git a/src/e-util/e-config-lookup-result.h b/src/e-util/e-config-lookup-result.h
index e7e7b6f..3d99829 100644
--- a/src/e-util/e-config-lookup-result.h
+++ b/src/e-util/e-config-lookup-result.h
@@ -51,6 +51,8 @@
 
 G_BEGIN_DECLS
 
+struct _EConfigLookup;
+
 typedef struct _EConfigLookupResult EConfigLookupResult;
 typedef struct _EConfigLookupResultInterface EConfigLookupResultInterface;
 
@@ -64,7 +66,9 @@ struct _EConfigLookupResultInterface {
        const gchar *   (* get_protocol)                (EConfigLookupResult *lookup_result);
        const gchar *   (* get_display_name)            (EConfigLookupResult *lookup_result);
        const gchar *   (* get_description)             (EConfigLookupResult *lookup_result);
+       const gchar *   (* get_password)                (EConfigLookupResult *lookup_result);
        gboolean        (* configure_source)            (EConfigLookupResult *lookup_result,
+                                                        struct _EConfigLookup *config_lookup,
                                                         ESource *source);
 };
 
@@ -76,10 +80,14 @@ gboolean    e_config_lookup_result_get_is_complete  (EConfigLookupResult *lookup_res
 const gchar *  e_config_lookup_result_get_protocol     (EConfigLookupResult *lookup_result);
 const gchar *  e_config_lookup_result_get_display_name (EConfigLookupResult *lookup_result);
 const gchar *  e_config_lookup_result_get_description  (EConfigLookupResult *lookup_result);
+const gchar *  e_config_lookup_result_get_password     (EConfigLookupResult *lookup_result);
 gboolean       e_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
+                                                        struct _EConfigLookup *config_lookup,
                                                         ESource *source);
 gint           e_config_lookup_result_compare          (gconstpointer lookup_result_a,
                                                         gconstpointer lookup_result_b);
+gboolean       e_config_lookup_result_equal            (gconstpointer lookup_result_a,
+                                                        gconstpointer lookup_result_b);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-config-lookup-worker.c b/src/e-util/e-config-lookup-worker.c
new file mode 100644
index 0000000..d84a5f7
--- /dev/null
+++ b/src/e-util/e-config-lookup-worker.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-config-lookup-worker
+ * @include: e-util/e-util.h
+ * @short_description: Configuration lookup worker interface
+ *
+ * #EConfigLookupWorker is an interface which runs the configuration look up.
+ **/
+
+#include "evolution-config.h"
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-config-lookup.h"
+#include "e-util-enums.h"
+
+#include "e-config-lookup-worker.h"
+
+G_DEFINE_QUARK (e-config-lookup-worker-error-quark, e_config_lookup_worker_error)
+
+G_DEFINE_INTERFACE (EConfigLookupWorker, e_config_lookup_worker, G_TYPE_OBJECT)
+
+static void
+e_config_lookup_worker_default_init (EConfigLookupWorkerInterface *iface)
+{
+       iface->get_display_name = NULL;
+       iface->run = NULL;
+}
+
+/**
+ * e_config_lookup_worker_get_display_name:
+ * @lookup_worker: an #EConfigLookupWorker
+ *
+ * Returns: human readable display name of this @lookup_worker
+ *
+ * Since: 3.28
+ **/
+const gchar *
+e_config_lookup_worker_get_display_name (EConfigLookupWorker *lookup_worker)
+{
+       EConfigLookupWorkerInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP_WORKER (lookup_worker), NULL);
+
+       iface = E_CONFIG_LOOKUP_WORKER_GET_INTERFACE (lookup_worker);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_display_name != NULL, NULL);
+
+       return iface->get_display_name (lookup_worker);
+}
+
+/**
+ * e_config_lookup_worker_run:
+ * @lookup_worker: an #EConfigLookupWorker
+ * @config_lookup: an #EConfigLookup
+ * @params: an #ENamedParameters with additional parameters
+ * @out_restart_params: (out): optional #ENamedParameters, used to pass when restart is requested
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Runs actual configuration look up. This method is called
+ * from a dedicated thread.
+ *
+ * When @out_restart_params is not %NULL at the end of the call,
+ * then it's saved for later re-run of the look up, in case
+ * the @error indicates it can be restarted, by using appropriate
+ * #EConfigLookupWorkerError.
+ *
+ * Since: 3.28
+ **/
+void
+e_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
+                           EConfigLookup *config_lookup,
+                           const ENamedParameters *params,
+                           ENamedParameters **out_restart_params,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       EConfigLookupWorkerInterface *iface;
+
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (lookup_worker));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+
+       iface = E_CONFIG_LOOKUP_WORKER_GET_INTERFACE (lookup_worker);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->run != NULL);
+
+       iface->run (lookup_worker, config_lookup, params, out_restart_params, cancellable, error);
+}
diff --git a/src/e-util/e-config-lookup-worker.h b/src/e-util/e-config-lookup-worker.h
new file mode 100644
index 0000000..3cc2bab
--- /dev/null
+++ b/src/e-util/e-config-lookup-worker.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_CONFIG_LOOKUP_WORKER_H
+#define E_CONFIG_LOOKUP_WORKER_H
+
+#include <libedataserver/libedataserver.h>
+
+#include <e-util/e-util-enums.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CONFIG_LOOKUP_WORKER \
+       (e_config_lookup_worker_get_type ())
+#define E_CONFIG_LOOKUP_WORKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CONFIG_LOOKUP_WORKER, EConfigLookupWorker))
+#define E_CONFIG_LOOKUP_WORKER_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CONFIG_LOOKUP_WORKER, EConfigLookupWorkerInterface))
+#define E_IS_CONFIG_LOOKUP_WORKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CONFIG_LOOKUP_WORKER))
+#define E_IS_CONFIG_LOOKUP_WORKER_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_CONFIG_LOOKUP_WORKER))
+#define E_CONFIG_LOOKUP_WORKER_GET_INTERFACE(obj) \
+       (G_TYPE_INSTANCE_GET_INTERFACE \
+       ((obj), E_TYPE_CONFIG_LOOKUP_WORKER, EConfigLookupWorkerInterface))
+
+/**
+ * E_CONFIG_LOOKUP_WORKER_ERROR:
+ *
+ * An #EConfigLookupWorker error domain, which can be used
+ * in e_config_lookup_worker_run().
+ *
+ * Since: 3.28
+ **/
+#define E_CONFIG_LOOKUP_WORKER_ERROR (e_config_lookup_worker_error_quark ())
+
+G_BEGIN_DECLS
+
+/**
+ * EConfigLookupWorkerError:
+ * @E_CONFIG_LOOKUP_WORKER_ERROR_REQUIRES_PASSWORD: a password is required to continue
+ * @E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE: there is an issue with server certificate; the error
+ *    message contains description of the issue, the out_restart_params contains 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM
+ *    with the offending certificate and when the user sets a trust on the certificate it is passed
+ *    back to the caller in the E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST parameters. Decode the trust
+ *    value with e_config_lookup_decode_certificate_trust()
+ *
+ * A set of possible errors in #E_CONFIG_LOOKUP_WORKER_ERROR domain.
+ *
+ * Since: 3.28
+ **/
+typedef enum {
+       E_CONFIG_LOOKUP_WORKER_ERROR_REQUIRES_PASSWORD,
+       E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE
+} EConfigLookupWorkerError;
+
+struct _EConfigLookup;
+
+typedef struct _EConfigLookupWorker EConfigLookupWorker;
+typedef struct _EConfigLookupWorkerInterface EConfigLookupWorkerInterface;
+
+struct _EConfigLookupWorkerInterface {
+       GTypeInterface parent_interface;
+
+       const gchar *   (* get_display_name)    (EConfigLookupWorker *lookup_worker);
+       void            (* run)                 (EConfigLookupWorker *lookup_worker,
+                                                struct _EConfigLookup *config_lookup,
+                                                const ENamedParameters *params,
+                                                ENamedParameters **out_restart_params,
+                                                GCancellable *cancellable,
+                                                GError **error);
+};
+
+GQuark         e_config_lookup_worker_error_quark      (void) G_GNUC_CONST;
+GType          e_config_lookup_worker_get_type         (void);
+const gchar *  e_config_lookup_worker_get_display_name (EConfigLookupWorker *lookup_worker);
+void           e_config_lookup_worker_run              (EConfigLookupWorker *lookup_worker,
+                                                        struct _EConfigLookup *config_lookup,
+                                                        const ENamedParameters *params,
+                                                        ENamedParameters **out_restart_params,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+
+G_END_DECLS
+
+#endif /* E_CONFIG_LOOKUP_WORKER_H */
diff --git a/src/e-util/e-config-lookup.c b/src/e-util/e-config-lookup.c
index fb9ccdc..d0eff38 100644
--- a/src/e-util/e-config-lookup.c
+++ b/src/e-util/e-config-lookup.c
@@ -28,10 +28,12 @@
 
 #include "evolution-config.h"
 
+#include <glib/gi18n-lib.h>
+#include <camel/camel.h>
 #include <libedataserver/libedataserver.h>
 
-#include "e-activity.h"
 #include "e-config-lookup-result.h"
+#include "e-config-lookup-worker.h"
 #include "e-simple-async-result.h"
 #include "e-util-enumtypes.h"
 
@@ -41,22 +43,27 @@ struct _EConfigLookupPrivate {
        ESourceRegistry *registry;
 
        GMutex property_lock;
+       GSList *workers; /* EConfigLookupWorker * */
        GSList *results; /* EConfigLookupResult * */
 
        ESimpleAsyncResult *run_result;
        GCancellable *run_cancellable;
+       GSList *worker_cancellables; /* CamelOperation * */
 
        GThreadPool *pool;
 };
 
 enum {
        PROP_0,
-       PROP_REGISTRY
+       PROP_REGISTRY,
+       PROP_BUSY
 };
 
 enum {
-       RUN,
        GET_SOURCE,
+       WORKER_STARTED,
+       WORKER_FINISHED,
+       RESULT_ADDED,
        LAST_SIGNAL
 };
 
@@ -65,12 +72,85 @@ static guint signals[LAST_SIGNAL];
 G_DEFINE_TYPE_WITH_CODE (EConfigLookup, e_config_lookup, G_TYPE_OBJECT,
        G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
 
+enum {
+       EMIT_BUSY               = 1 << 0,
+       EMIT_WORKER_STARTED     = 1 << 1,
+       EMIT_WORKER_FINISHED    = 1 << 2
+};
+
+typedef struct _EmitData {
+       EConfigLookup *config_lookup;
+       EConfigLookupWorker *worker;
+       guint32 flags;
+       GCancellable *cancellable;
+       ENamedParameters *params;
+       GError *error;
+} EmitData;
+
+static void
+emit_data_free (gpointer ptr)
+{
+       EmitData *ed = ptr;
+
+       if (ed) {
+               e_named_parameters_free (ed->params);
+               g_clear_object (&ed->config_lookup);
+               g_clear_object (&ed->worker);
+               g_clear_object (&ed->cancellable);
+               g_clear_error (&ed->error);
+               g_free (ed);
+       }
+}
+
+static gboolean
+config_lookup_emit_idle_cb (gpointer user_data)
+{
+       EmitData *ed = user_data;
+
+       g_return_val_if_fail (ed != NULL, FALSE);
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (ed->config_lookup), FALSE);
+
+       if ((ed->flags & EMIT_WORKER_STARTED) != 0)
+               g_signal_emit (ed->config_lookup, signals[WORKER_STARTED], 0, ed->worker, ed->cancellable);
+
+       if ((ed->flags & EMIT_WORKER_FINISHED) != 0)
+               g_signal_emit (ed->config_lookup, signals[WORKER_FINISHED], 0, ed->worker, ed->params, 
ed->error);
+
+       if ((ed->flags & EMIT_BUSY) != 0)
+               g_object_notify (G_OBJECT (ed->config_lookup), "busy");
+
+       return FALSE;
+}
+
+static void
+config_lookup_schedule_emit_idle (EConfigLookup *config_lookup,
+                                 guint32 emit_flags,
+                                 EConfigLookupWorker *worker,
+                                 GCancellable *cancellable,
+                                 const ENamedParameters *params,
+                                 const GError *error)
+{
+       EmitData *ed;
+
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+       if (worker)
+               g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
+
+       ed = g_new0 (EmitData, 1);
+       ed->config_lookup = g_object_ref (config_lookup);
+       ed->flags = emit_flags;
+       ed->worker = worker ? g_object_ref (worker) : NULL;
+       ed->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+       ed->params = params ? e_named_parameters_new_clone (params) : NULL;
+       ed->error = error ? g_error_copy (error) : NULL;
+
+       g_idle_add_full (G_PRIORITY_HIGH_IDLE, config_lookup_emit_idle_cb, ed, emit_data_free);
+}
+
 typedef struct _ThreadData {
        ENamedParameters *params;
-       EActivity *activity;
-       EConfigLookupThreadFunc thread_func;
-       gpointer user_data;
-       GDestroyNotify user_data_free;
+       EConfigLookupWorker *worker;
+       GCancellable *cancellable;
 } ThreadData;
 
 static void
@@ -79,20 +159,52 @@ config_lookup_thread (gpointer data,
 {
        ThreadData *td = data;
        EConfigLookup *config_lookup = user_data;
+       ESimpleAsyncResult *run_result = NULL;
+       guint32 emit_flags;
+       ENamedParameters *restart_params = NULL;
+       GError *error = NULL;
 
        g_return_if_fail (td != NULL);
        g_return_if_fail (td->params != NULL);
-       g_return_if_fail (E_IS_ACTIVITY (td->activity));
-       g_return_if_fail (td->thread_func != NULL);
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (td->worker));
+       g_return_if_fail (G_IS_CANCELLABLE (td->cancellable));
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
 
-       td->thread_func (config_lookup, td->params, td->user_data, e_activity_get_cancellable (td->activity));
+       e_config_lookup_worker_run (td->worker, config_lookup, td->params, &restart_params, td->cancellable, 
&error);
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       emit_flags = EMIT_WORKER_FINISHED;
+
+       if (g_slist_find (config_lookup->priv->worker_cancellables, td->cancellable)) {
+               config_lookup->priv->worker_cancellables = g_slist_remove 
(config_lookup->priv->worker_cancellables, td->cancellable);
+               g_object_unref (td->cancellable);
+
+               if (!config_lookup->priv->worker_cancellables)
+                       emit_flags |= EMIT_BUSY;
+       }
+
+       config_lookup_schedule_emit_idle (config_lookup, emit_flags, td->worker, NULL, restart_params, error);
 
-       if (td->user_data_free)
-               td->user_data_free (td->user_data);
+       if ((emit_flags & EMIT_BUSY) != 0) {
+               run_result = config_lookup->priv->run_result;
+               config_lookup->priv->run_result = NULL;
 
+               g_clear_object (&config_lookup->priv->run_cancellable);
+       }
+
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+
+       if (run_result) {
+               e_simple_async_result_complete_idle (run_result);
+               g_object_unref (run_result);
+       }
+
+       e_named_parameters_free (restart_params);
        e_named_parameters_free (td->params);
-       g_object_unref (td->activity);
+       g_clear_object (&td->worker);
+       g_clear_object (&td->cancellable);
+       g_clear_error (&error);
        g_free (td);
 }
 
@@ -130,6 +242,13 @@ config_lookup_get_property (GObject *object,
                            GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_BUSY:
+                       g_value_set_boolean (
+                               value,
+                               e_config_lookup_get_busy (
+                               E_CONFIG_LOOKUP (object)));
+                       return;
+
                case PROP_REGISTRY:
                        g_value_set_object (
                                value,
@@ -154,16 +273,26 @@ static void
 config_lookup_dispose (GObject *object)
 {
        EConfigLookup *config_lookup = E_CONFIG_LOOKUP (object);
+       gboolean had_running_workers;
+
+       e_config_lookup_cancel_all (config_lookup);
 
        g_mutex_lock (&config_lookup->priv->property_lock);
 
-       if (config_lookup->priv->run_cancellable) {
-               g_cancellable_cancel (config_lookup->priv->run_cancellable);
-               g_clear_object (&config_lookup->priv->run_cancellable);
-       }
+       g_clear_object (&config_lookup->priv->run_cancellable);
+
+       g_slist_free_full (config_lookup->priv->workers, g_object_unref);
+       config_lookup->priv->workers = NULL;
+
+       had_running_workers = config_lookup->priv->worker_cancellables != NULL;
+       g_slist_free_full (config_lookup->priv->worker_cancellables, g_object_unref);
+       config_lookup->priv->worker_cancellables = NULL;
 
        g_mutex_unlock (&config_lookup->priv->property_lock);
 
+       if (had_running_workers)
+               g_object_notify (object, "busy");
+
        g_clear_object (&config_lookup->priv->registry);
 
        /* Chain up to parent's method. */
@@ -217,32 +346,22 @@ e_config_lookup_class_init (EConfigLookupClass *klass)
                        G_PARAM_STATIC_STRINGS));
 
        /**
-        * EConfigLookup::run:
-        * @params: an #ENamedParameters with additional parameters
-        * @activity: an #EActivity
+        * EConfigLookup:busy:
         *
-        * Emitted to run config lookup by each extension. The extensions
-        * run their code asynchronously and claim the result by
-        * e_config_lookup_add_result(). The extension also references the @activity
-        * for the whole run, because it's used to know when all extensions
-        * are finished with searching. Extensions can use e_config_lookup_create_thread(),
-        * which does necessary things for it.
+        * Whether the EConfigLookup has any running workers.
         *
-        * The signal is emitted from the main/GUI thread, but the @activity can be
-        * unreffed in another thread too.
-        *
-        * Since: 3.26
+        * Since: 3.28
         **/
-       signals[RUN] = g_signal_new (
-               "run",
-               G_TYPE_FROM_CLASS (klass),
-               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-               G_STRUCT_OFFSET (EConfigLookupClass, run),
-               NULL, NULL,
-               NULL,
-               G_TYPE_NONE, 2,
-               E_TYPE_NAMED_PARAMETERS,
-               E_TYPE_ACTIVITY);
+       g_object_class_install_property (
+               object_class,
+               PROP_BUSY,
+               g_param_spec_boolean (
+                       "busy",
+                       "Busy",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
 
        /**
         * EConfigLookup::get-source:
@@ -261,6 +380,74 @@ e_config_lookup_class_init (EConfigLookupClass *klass)
                NULL,
                G_TYPE_POINTER, 1,
                E_TYPE_CONFIG_LOOKUP_SOURCE_KIND);
+
+       /**
+        * EConfigLookup::worker-started:
+        * @worker: an #EConfigLookupWorker
+        * @cancellable: associated #GCancellable for this worker run
+        *
+        * Emitted when the @worker is about to start running.
+        * Corresponding @EConfigLookup::worker-finished is emitted when
+        * the run is finished.
+        *
+        * Note that this signal is always emitted in the main thread.
+        *
+        * Since: 3.28
+        **/
+       signals[WORKER_STARTED] = g_signal_new (
+               "worker-started",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EConfigLookupClass, worker_started),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 2,
+               E_TYPE_CONFIG_LOOKUP_WORKER,
+               G_TYPE_CANCELLABLE);
+
+       /**
+        * EConfigLookup::worker-finished:
+        * @worker: an #EConfigLookupWorker
+        * @restart_params: an optional #ENamedParameters to use when the @worker might be restarted
+        * @error: an optional #GError with an overall result of the run
+        *
+        * Emitted when the @worker finished its running.
+        *
+        * Note that this signal is always emitted in the main thread.
+        *
+        * Since: 3.28
+        **/
+       signals[WORKER_FINISHED] = g_signal_new (
+               "worker-finished",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EConfigLookupClass, worker_finished),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 3,
+               E_TYPE_CONFIG_LOOKUP_WORKER,
+               E_TYPE_NAMED_PARAMETERS,
+               G_TYPE_ERROR);
+
+       /**
+        * EConfigLookup::result-added:
+        * @result: an #EConfigLookupResult
+        *
+        * Emitted when a new @result is added to the config lookup.
+        *
+        * Note that this signal can be emitted in a worker's dedicated thread.
+        *
+        * Since: 3.28
+        **/
+       signals[RESULT_ADDED] = g_signal_new (
+               "result-added",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EConfigLookupClass, result_added),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               E_TYPE_CONFIG_LOOKUP_RESULT);
 }
 
 static void
@@ -336,28 +523,151 @@ e_config_lookup_get_source (EConfigLookup *config_lookup,
        return source;
 }
 
-static void
-config_lookup_activity_gone (gpointer user_data,
-                            GObject *object)
+/**
+ * e_config_lookup_get_busy:
+ * @config_lookup: an #EConfigLookup
+ *
+ * Returns whether there's any running worker. They can be cancelled
+ * with e_config_lookup_cancel_all().
+ *
+ * Returns: whether there's any running worker
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_config_lookup_get_busy (EConfigLookup *config_lookup)
 {
-       EConfigLookup *config_lookup = user_data;
-       ESimpleAsyncResult *run_result;
+       gboolean busy;
+
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+       busy = config_lookup->priv->worker_cancellables != NULL;
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+
+       return busy;
+}
+
+/**
+ * e_config_lookup_cancel_all:
+ * @config_lookup: an #EConfigLookup
+ *
+ * Cancels all pending workers.
+ *
+ * Since: 3.28
+ **/
+void
+e_config_lookup_cancel_all (EConfigLookup *config_lookup)
+{
+       GSList *cancellables;
+       GCancellable *run_cancellable;
 
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
 
        g_mutex_lock (&config_lookup->priv->property_lock);
+       cancellables = g_slist_copy_deep (config_lookup->priv->worker_cancellables, (GCopyFunc) g_object_ref, 
NULL);
+       run_cancellable = config_lookup->priv->run_cancellable ? g_object_ref 
(config_lookup->priv->run_cancellable) : NULL;
+       g_mutex_unlock (&config_lookup->priv->property_lock);
 
-       run_result = config_lookup->priv->run_result;
-       config_lookup->priv->run_result = NULL;
+       g_slist_foreach (cancellables, (GFunc) g_cancellable_cancel, NULL);
+       g_slist_free_full (cancellables, g_object_unref);
+
+       if (run_cancellable) {
+               g_cancellable_cancel (run_cancellable);
+               g_object_unref (run_cancellable);
+       }
+}
+
+/**
+ * e_config_lookup_register_worker:
+ * @config_lookup: an #EConfigLookup
+ * @worker: an #EConfigLookupWorker
+ *
+ * Registers a @worker as a worker, which can be run as part of e_config_lookup_run().
+ * The function adds its own reference to @worker.
+ *
+ * Since: 3.28
+ **/
+void
+e_config_lookup_register_worker (EConfigLookup *config_lookup,
+                                EConfigLookupWorker *worker)
+{
+       GSList *existing_worker;
+
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       existing_worker = g_slist_find (config_lookup->priv->workers, worker);
+
+       g_warn_if_fail (existing_worker == NULL);
+
+       if (!existing_worker)
+               config_lookup->priv->workers = g_slist_prepend (config_lookup->priv->workers, g_object_ref 
(worker));
 
        g_mutex_unlock (&config_lookup->priv->property_lock);
+}
 
-       if (run_result) {
-               e_simple_async_result_complete_idle (run_result);
-               g_object_unref (run_result);
+/**
+ * e_config_lookup_unregister_worker:
+ * @config_lookup: an #EConfigLookup
+ * @worker: an #EConfigLookupWorker
+ *
+ * Removes a @worker previously registered with e_config_lookup_register_worker().
+ *
+ * Since: 3.28
+ **/
+void
+e_config_lookup_unregister_worker (EConfigLookup *config_lookup,
+                                  EConfigLookupWorker *worker)
+{
+       GSList *existing_worker;
+
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       existing_worker = g_slist_find (config_lookup->priv->workers, worker);
+
+       g_warn_if_fail (existing_worker != NULL);
+
+       if (existing_worker) {
+               config_lookup->priv->workers = g_slist_remove (config_lookup->priv->workers, worker);
+               g_object_unref (worker);
        }
 
-       g_object_unref (config_lookup);
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+}
+
+/**
+ * e_config_lookup_dup_registered_workers:
+ * @config_lookup: an #EConfigLookup
+ *
+ * Returns a list of all registered #EConfigLookupWorker objects.
+ *
+ * The returned #GSList should be freed with
+ * g_slist_free_full (workers, g_object_unref);
+ * when no longer needed.
+ *
+ * Returns: (transfer full) (element-type EConfigLookupWorker): a #GSList with all
+ *    workers registered with e_config_lookup_register_worker().
+ *
+ * Since: 3.28
+ **/
+GSList *
+e_config_lookup_dup_registered_workers (EConfigLookup *config_lookup)
+{
+       GSList *workers;
+
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+       workers = g_slist_copy_deep (config_lookup->priv->workers, (GCopyFunc) g_object_ref, NULL);
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+
+       return workers;
 }
 
 /**
@@ -368,9 +678,11 @@ config_lookup_activity_gone (gpointer user_data,
  * @callback: a callback to call, when the run is finished
  * @user_data: user data for the @callback
  *
- * Runs configuration lookup asynchronously, by emitting the #EConfigLookup::run signal.
- * Once the run is done, the @callback is called, and the call can be finished with
- * e_config_lookup_run_finish(). The @callback is always called from the main thread.
+ * Runs configuration lookup asynchronously. Once the run is done, the @callback is called,
+ * and the call can be finished with e_config_lookup_run_finish(). The @callback is always
+ * called from the main thread.
+ *
+ * Workers can be run individually using e_config_lookup_run_worker().
  *
  * Note that there cannot be run two lookups at the same time, thus if it
  * happens, then the @callback is called immediately with a %NULL result.
@@ -384,18 +696,18 @@ e_config_lookup_run (EConfigLookup *config_lookup,
                     GAsyncReadyCallback callback,
                     gpointer user_data)
 {
-       EActivity *activity;
+       GSList *workers, *link;
 
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
        g_return_if_fail (params != NULL);
-       g_return_if_fail (callback != NULL);
 
        g_mutex_lock (&config_lookup->priv->property_lock);
 
        if (config_lookup->priv->run_result) {
                g_mutex_unlock (&config_lookup->priv->property_lock);
 
-               callback (G_OBJECT (config_lookup), NULL, user_data);
+               if (callback)
+                       callback (G_OBJECT (config_lookup), NULL, user_data);
                return;
        }
 
@@ -410,16 +722,35 @@ e_config_lookup_run (EConfigLookup *config_lookup,
        config_lookup->priv->run_result = e_simple_async_result_new (G_OBJECT (config_lookup), callback, 
user_data, e_config_lookup_run);
        config_lookup->priv->run_cancellable = cancellable;
 
-       activity = e_activity_new ();
-       e_activity_set_cancellable (activity, cancellable);
-
-       g_object_weak_ref (G_OBJECT (activity), config_lookup_activity_gone, g_object_ref (config_lookup));
+       workers = g_slist_copy_deep (config_lookup->priv->workers, (GCopyFunc) g_object_ref, NULL);
 
        g_mutex_unlock (&config_lookup->priv->property_lock);
 
-       g_signal_emit (config_lookup, signals[RUN], 0, params, activity);
+       if (workers) {
+               for (link = workers; link; link = g_slist_next (link)) {
+                       EConfigLookupWorker *worker = link->data;
+
+                       e_config_lookup_run_worker (config_lookup, worker, params, cancellable);
+               }
+
+               g_slist_free_full (workers, g_object_unref);
+       } else {
+               ESimpleAsyncResult *run_result;
+
+               g_mutex_lock (&config_lookup->priv->property_lock);
+
+               run_result = config_lookup->priv->run_result;
+               config_lookup->priv->run_result = NULL;
 
-       g_object_unref (activity);
+               g_clear_object (&config_lookup->priv->run_cancellable);
+
+               g_mutex_unlock (&config_lookup->priv->property_lock);
+
+               if (run_result) {
+                       e_simple_async_result_complete_idle (run_result);
+                       g_object_unref (run_result);
+               }
+       }
 }
 
 /**
@@ -443,45 +774,56 @@ e_config_lookup_run_finish (EConfigLookup *config_lookup,
 }
 
 /**
- * e_config_lookup_create_thread:
+ * e_config_lookup_run_worker:
  * @config_lookup: an #EConfigLookup
+ * @worker: an #EConfigLookupWorker to run in a dedicated thread
  * @params: an #ENamedParameters with lookup parameters
- * @activity: an #EActivity
- * @thread_func: function to call in a new thread
- * @user_data: (nullable): optional user data for @thread_func, or %NULL
- * @user_data_free: (nullable): optional free function for @user_data, or %NULL
+ * @cancellable: an optional #GCancellable, or %NULL
  *
- * Creates a new thread and calls @thread_func in it. It also references @activity
- * and unreferences it once the @thread_func is done.
+ * Creates a new thread and runs @worker in it. When the @cancellable is %NULL,
+ * then there's creates a new #CamelOperation, which either proxies currently
+ * running lookup or the newly created cancellable is completely independent.
  *
- * This function might be usually called by extensions in a signal handler
- * for the #EConfigLookup::run signal.
+ * This function can be called while there's an ongoing configuration lookup, but
+ * also when the @worker is restarted.
  *
- * Since: 3.26
+ * Since: 3.28
  **/
 void
-e_config_lookup_create_thread (EConfigLookup *config_lookup,
-                              const ENamedParameters *params,
-                              EActivity *activity,
-                              EConfigLookupThreadFunc thread_func,
-                              gpointer user_data,
-                              GDestroyNotify user_data_free)
+e_config_lookup_run_worker (EConfigLookup *config_lookup,
+                           EConfigLookupWorker *worker,
+                           const ENamedParameters *params,
+                           GCancellable *cancellable)
 {
        ThreadData *td;
 
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
        g_return_if_fail (params != NULL);
-       g_return_if_fail (E_IS_ACTIVITY (activity));
-       g_return_if_fail (thread_func != NULL);
 
        td = g_new0 (ThreadData, 1);
        td->params = e_named_parameters_new_clone (params);
-       td->activity = g_object_ref (activity);
-       td->thread_func = thread_func;
-       td->user_data = user_data;
-       td->user_data_free = user_data_free;
+       td->worker = g_object_ref (worker);
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       if (cancellable)
+               td->cancellable = camel_operation_new_proxy (cancellable);
+       else if (config_lookup->priv->run_cancellable)
+               td->cancellable = camel_operation_new_proxy (config_lookup->priv->run_cancellable);
+       else
+               td->cancellable = camel_operation_new ();
+
+       camel_operation_push_message (td->cancellable, "%s", _("Running…"));
+       config_lookup->priv->worker_cancellables = g_slist_prepend (config_lookup->priv->worker_cancellables, 
g_object_ref (td->cancellable));
+
+       config_lookup_schedule_emit_idle (config_lookup, EMIT_WORKER_STARTED |
+               (!config_lookup->priv->worker_cancellables->next ? EMIT_BUSY : 0),
+               worker, td->cancellable, NULL, NULL);
 
        g_thread_pool_push (config_lookup->priv->pool, td, NULL);
+
+       g_mutex_unlock (&config_lookup->priv->property_lock);
 }
 
 /**
@@ -493,7 +835,7 @@ e_config_lookup_create_thread (EConfigLookup *config_lookup,
  * The @config_lookup assumes ownership of the @result and frees it
  * when no longer needed.
  *
- * The list of results can be obtained with e_config_lookup_get_results().
+ * The list of results can be obtained with e_config_lookup_dup_results().
  *
  * Since: 3.26
  **/
@@ -509,10 +851,36 @@ e_config_lookup_add_result (EConfigLookup *config_lookup,
        config_lookup->priv->results = g_slist_prepend (config_lookup->priv->results, result);
 
        g_mutex_unlock (&config_lookup->priv->property_lock);
+
+       g_signal_emit (config_lookup, signals[RESULT_ADDED], 0, result);
 }
 
 /**
- * e_config_lookup_get_results:
+ * e_config_lookup_count_results:
+ * @config_lookup: an #EConfigLookup
+ *
+ * Returns: how many results had been added already.
+ *
+ * Since: 3.28
+ **/
+gint
+e_config_lookup_count_results (EConfigLookup *config_lookup)
+{
+       gint n_results;
+
+       g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), -1);
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       n_results = g_slist_length (config_lookup->priv->results);
+
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+
+       return n_results;
+}
+
+/**
+ * e_config_lookup_dup_results:
  * @config_lookup: an #EConfigLookup
  * @kind: an #EConfigLookupResultKind to filter the results with
  * @protocol: (nullable): optional protocol to filter the results with, or %NULL
@@ -526,13 +894,13 @@ e_config_lookup_add_result (EConfigLookup *config_lookup,
  * g_slist_free_full (results, g_object_unref);
  * when no longer needed.
  *
- * Returns: (element-type EConfigLookupResult) (transfer full): a #GSList
+ * Returns: (transfer full) (element-type EConfigLookupResult): a #GSList
  *    with results satisfying the @kind and @protocol filtering conditions.
  *
- * Since: 3.26
+ * Since: 3.28
  **/
 GSList *
-e_config_lookup_get_results (EConfigLookup *config_lookup,
+e_config_lookup_dup_results (EConfigLookup *config_lookup,
                             EConfigLookupResultKind kind,
                             const gchar *protocol)
 {
@@ -563,3 +931,66 @@ e_config_lookup_get_results (EConfigLookup *config_lookup,
 
        return results;
 }
+
+/**
+ * e_config_lookup_clear_results:
+ * @config_lookup: an #EConfigLookup
+ *
+ * Frees all gathered results. This might be usually called before
+ * starting new custom lookup. The e_config_lookup_run() frees
+ * all results automatically.
+ *
+ * Since: 3.28
+ **/
+void
+e_config_lookup_clear_results (EConfigLookup *config_lookup)
+{
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+
+       g_mutex_lock (&config_lookup->priv->property_lock);
+
+       g_slist_free_full (config_lookup->priv->results, g_object_unref);
+       config_lookup->priv->results = NULL;
+
+       g_mutex_unlock (&config_lookup->priv->property_lock);
+}
+
+/**
+ * e_config_lookup_encode_certificate_trust:
+ * @response: an #ETrustPromptResponse to encode
+ *
+ * Encodes @response to a string. This can be decoded back to enum
+ * with e_config_lookup_decode_certificate_trust().
+ *
+ * Returns: string representation of @response.
+ *
+ * Since: 3.28
+ **/
+const gchar *
+e_config_lookup_encode_certificate_trust (ETrustPromptResponse response)
+{
+       return e_enum_to_string (E_TYPE_TRUST_PROMPT_RESPONSE, response);
+}
+
+/**
+ * e_config_lookup_decode_certificate_trust:
+ * @value: a text value to decode
+ *
+ * Decodes text @value to #ETrustPromptResponse, previously encoded
+ * with e_config_lookup_encode_certificate_trust().
+ *
+ * Returns: an #ETrustPromptResponse corresponding to @value.
+ *
+ * Since: 3.28
+ **/
+ETrustPromptResponse
+e_config_lookup_decode_certificate_trust (const gchar *value)
+{
+       gint decoded;
+
+       if (!value ||
+           !e_enum_from_string (E_TYPE_TRUST_PROMPT_RESPONSE, value, &decoded))
+               decoded = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+
+       return decoded;
+}
diff --git a/src/e-util/e-config-lookup.h b/src/e-util/e-config-lookup.h
index 92e27e3..3ed56c5 100644
--- a/src/e-util/e-config-lookup.h
+++ b/src/e-util/e-config-lookup.h
@@ -24,9 +24,9 @@
 
 #include <libedataserver/libedataserver.h>
 
-#include <e-util/e-activity.h>
 #include <e-util/e-util-enums.h>
 #include <e-util/e-config-lookup-result.h>
+#include <e-util/e-config-lookup-worker.h>
 
 /* Standard GObject macros */
 #define E_TYPE_CONFIG_LOOKUP \
@@ -49,8 +49,12 @@
 
 #define E_CONFIG_LOOKUP_PARAM_USER             "user"
 #define E_CONFIG_LOOKUP_PARAM_PASSWORD         "password"
+#define E_CONFIG_LOOKUP_PARAM_REMEMBER_PASSWORD        "remember-password" /* Is part of params, if can 
remember, otherwise not in params */
 #define E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS    "email-address"
 #define E_CONFIG_LOOKUP_PARAM_SERVERS          "servers"
+#define E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM  "certificate-pem"
+#define E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST "certificate-host"
+#define E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST        "certificate-trust"
 
 G_BEGIN_DECLS
 
@@ -59,22 +63,6 @@ typedef struct _EConfigLookupClass EConfigLookupClass;
 typedef struct _EConfigLookupPrivate EConfigLookupPrivate;
 
 /**
- * EConfigLookupThreadFunc:
- * @config_lookup: an #EConfigLookup
- * @params: an #ENamedParameters with additional parameters
- * @user_data: user data passed to e_config_lookup_create_thread()
- * @cancellable: a #GCancellable
- *
- * A function executed in a dedicated thread.
- *
- * Since: 3.26
- **/
-typedef void (* EConfigLookupThreadFunc) (EConfigLookup *config_lookup,
-                                         const ENamedParameters *params,
-                                         gpointer user_data,
-                                         GCancellable *cancellable);
-
-/**
  * EConfigLookup:
  *
  * Contains only private data that should be read and manipulated using
@@ -93,11 +81,17 @@ struct _EConfigLookupClass {
        GObjectClass parent_class;
 
        /* Signals */
-       void            (* run)         (EConfigLookup *config_lookup,
-                                        const ENamedParameters *params,
-                                        EActivity *activity);
-       ESource *       (* get_source)  (EConfigLookup *config_lookup,
-                                        EConfigLookupSourceKind kind);
+       ESource *       (* get_source)          (EConfigLookup *config_lookup,
+                                                EConfigLookupSourceKind kind);
+       void            (* worker_started)      (EConfigLookup *config_lookup,
+                                                EConfigLookupWorker *worker,
+                                                GCancellable *cancellable);
+       void            (* worker_finished)     (EConfigLookup *config_lookup,
+                                                EConfigLookupWorker *worker,
+                                                const ENamedParameters *restart_params,
+                                                const GError *error);
+       void            (* result_added)        (EConfigLookup *config_lookup,
+                                                EConfigLookupResult *result);
 };
 
 GType          e_config_lookup_get_type                (void) G_GNUC_CONST;
@@ -106,6 +100,13 @@ ESourceRegistry *
                e_config_lookup_get_registry            (EConfigLookup *config_lookup);
 ESource *      e_config_lookup_get_source              (EConfigLookup *config_lookup,
                                                         EConfigLookupSourceKind kind);
+gboolean       e_config_lookup_get_busy                (EConfigLookup *config_lookup);
+void           e_config_lookup_cancel_all              (EConfigLookup *config_lookup);
+void           e_config_lookup_register_worker         (EConfigLookup *config_lookup,
+                                                        EConfigLookupWorker *worker);
+void           e_config_lookup_unregister_worker       (EConfigLookup *config_lookup,
+                                                        EConfigLookupWorker *worker);
+GSList *       e_config_lookup_dup_registered_workers  (EConfigLookup *config_lookup);
 void           e_config_lookup_run                     (EConfigLookup *config_lookup,
                                                         const ENamedParameters *params,
                                                         GCancellable *cancellable,
@@ -113,17 +114,21 @@ void              e_config_lookup_run                     (EConfigLookup *config_lookup,
                                                         gpointer user_data);
 void           e_config_lookup_run_finish              (EConfigLookup *config_lookup,
                                                         GAsyncResult *result);
-void           e_config_lookup_create_thread           (EConfigLookup *config_lookup,
+void           e_config_lookup_run_worker              (EConfigLookup *config_lookup,
+                                                        EConfigLookupWorker *worker,
                                                         const ENamedParameters *params,
-                                                        EActivity *activity,
-                                                        EConfigLookupThreadFunc thread_func,
-                                                        gpointer user_data,
-                                                        GDestroyNotify user_data_free);
+                                                        GCancellable *cancellable);
 void           e_config_lookup_add_result              (EConfigLookup *config_lookup,
                                                         EConfigLookupResult *result);
-GSList *       e_config_lookup_get_results             (EConfigLookup *config_lookup,
+gint           e_config_lookup_count_results           (EConfigLookup *config_lookup);
+GSList *       e_config_lookup_dup_results             (EConfigLookup *config_lookup,
                                                         EConfigLookupResultKind kind,
                                                         const gchar *protocol);
+void           e_config_lookup_clear_results           (EConfigLookup *config_lookup);
+
+const gchar *  e_config_lookup_encode_certificate_trust(ETrustPromptResponse response);
+ETrustPromptResponse
+               e_config_lookup_decode_certificate_trust(const gchar *value);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-simple-async-result.c b/src/e-util/e-simple-async-result.c
index 5959a69..89423f2 100644
--- a/src/e-util/e-simple-async-result.c
+++ b/src/e-util/e-simple-async-result.c
@@ -124,7 +124,6 @@ e_simple_async_result_new (GObject *source_object,
 {
        ESimpleAsyncResult *result;
 
-       g_return_val_if_fail (callback != NULL, NULL);
        if (source_object)
                g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
 
@@ -294,7 +293,8 @@ e_simple_async_result_complete (ESimpleAsyncResult *result)
 
        g_object_ref (result);
 
-       result->priv->callback (result->priv->source_object, G_ASYNC_RESULT (result), 
result->priv->callback_user_data);
+       if (result->priv->callback)
+               result->priv->callback (result->priv->source_object, G_ASYNC_RESULT (result), 
result->priv->callback_user_data);
 
        g_object_unref (result);
 }
diff --git a/src/e-util/e-source-config.c b/src/e-util/e-source-config.c
index 066cf10..e6e788d 100644
--- a/src/e-util/e-source-config.c
+++ b/src/e-util/e-source-config.c
@@ -1473,7 +1473,7 @@ e_source_config_add_user_entry (ESourceConfig *config,
 
        widget = gtk_entry_new ();
        e_source_config_insert_widget (
-               config, scratch_source, _("User"), widget);
+               config, scratch_source, _("User:"), widget);
        gtk_widget_show (widget);
 
        e_binding_bind_object_text_property (
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index 5b6bef2..a951f6b 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -619,6 +619,14 @@ typedef enum {
        E_CONFIG_LOOKUP_RESULT_TASK_LIST
 } EConfigLookupResultKind;
 
+/**
+ * @E_CONFIG_LOOKUP_RESULT_LAST_KIND:
+ * The last known %EConfigLookupResultKind.
+ *
+ * Since: 3.28
+ **/
+#define E_CONFIG_LOOKUP_RESULT_LAST_KIND E_CONFIG_LOOKUP_RESULT_TASK_LIST
+
 G_END_DECLS
 
 #endif /* E_UTIL_ENUMS_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index 51e049b..c207820 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -81,12 +81,14 @@
 #include <e-util/e-client-cache.h>
 #include <e-util/e-client-combo-box.h>
 #include <e-util/e-client-selector.h>
+#include <e-util/e-collection-account-wizard.h>
 #include <e-util/e-color-chooser-widget.h>
 #include <e-util/e-color-combo.h>
 #include <e-util/e-config.h>
 #include <e-util/e-config-lookup.h>
 #include <e-util/e-config-lookup-result.h>
 #include <e-util/e-config-lookup-result-simple.h>
+#include <e-util/e-config-lookup-worker.h>
 #include <e-util/e-conflict-search-selector.h>
 #include <e-util/e-contact-store.h>
 #include <e-util/e-content-editor.h>
diff --git a/src/mail/e-mail-autoconfig.c b/src/mail/e-mail-autoconfig.c
index 4640a44..33994ff 100644
--- a/src/mail/e-mail-autoconfig.c
+++ b/src/mail/e-mail-autoconfig.c
@@ -98,6 +98,7 @@ struct _EMailAutoconfigPrivate {
        gchar *email_address;
        gchar *email_local_part;
        gchar *email_domain_part;
+       gchar *use_domain;
        EMailAutoconfigResult imap_result;
        EMailAutoconfigResult pop3_result;
        EMailAutoconfigResult smtp_result;
@@ -111,7 +112,8 @@ struct _ParserClosure {
 enum {
        PROP_0,
        PROP_EMAIL_ADDRESS,
-       PROP_REGISTRY
+       PROP_REGISTRY,
+       PROP_USE_DOMAIN
 };
 
 /* Forward Declarations */
@@ -241,7 +243,7 @@ mail_autoconfig_parse_text (GMarkupParseContext *context,
                        }
 
                        variable = "%EMAILDOMAIN%";
-                       substitute = priv->email_domain_part;
+                       substitute = priv->use_domain;
 
                        if (strncmp (cp, variable, strlen (variable)) == 0) {
                                g_string_append (string, substitute);
@@ -455,7 +457,8 @@ exit:
 static gboolean
 mail_autoconfig_set_details (EMailAutoconfigResult *result,
                              ESource *source,
-                             const gchar *extension_name)
+                             const gchar *extension_name,
+                            const gchar *default_backend_name)
 {
        ESourceCamel *camel_ext;
        ESourceBackend *backend_ext;
@@ -472,6 +475,14 @@ mail_autoconfig_set_details (EMailAutoconfigResult *result,
 
        backend_ext = e_source_get_extension (source, extension_name);
        backend_name = e_source_backend_get_backend_name (backend_ext);
+       if (!backend_name || !*backend_name) {
+               e_source_backend_set_backend_name (backend_ext, default_backend_name);
+               backend_name = default_backend_name;
+       }
+
+       if (!backend_name)
+               return FALSE;
+
        extension_name = e_source_camel_get_extension_name (backend_name);
        camel_ext = e_source_get_extension (source, extension_name);
 
@@ -521,6 +532,7 @@ G_DEFINE_TYPE (EMailConfigLookupResult, e_mail_config_lookup_result, E_TYPE_CONF
 
 static gboolean
 mail_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
+                                           EConfigLookup *config_lookup,
                                            ESource *source)
 {
        EMailConfigLookupResult *mail_result;
@@ -530,7 +542,8 @@ mail_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
        mail_result = E_MAIL_CONFIG_LOOKUP_RESULT (lookup_result);
 
        /* No chain up to parent method, not needed here, because not used */
-       return mail_autoconfig_set_details (&mail_result->result, source, mail_result->extension_name);
+       return mail_autoconfig_set_details (&mail_result->result, source, mail_result->extension_name,
+               e_config_lookup_result_get_protocol (lookup_result));
 }
 
 static void
@@ -589,6 +602,7 @@ e_mail_config_lookup_result_new (EConfigLookupResultKind kind,
                "protocol", protocol,
                "display-name", display_name,
                "description", description,
+               "password", NULL,
                NULL);
 
        mail_result->result.set = result->set;
@@ -665,6 +679,16 @@ mail_autoconfig_set_email_address (EMailAutoconfig *autoconfig,
 }
 
 static void
+mail_autoconfig_set_use_domain (EMailAutoconfig *autoconfig,
+                               const gchar *use_domain)
+{
+       if (g_strcmp0 (autoconfig->priv->use_domain, use_domain) != 0) {
+               g_free (autoconfig->priv->use_domain);
+               autoconfig->priv->use_domain = g_strdup (use_domain);
+       }
+}
+
+static void
 mail_autoconfig_set_registry (EMailAutoconfig *autoconfig,
                               ESourceRegistry *registry)
 {
@@ -692,6 +716,12 @@ mail_autoconfig_set_property (GObject *object,
                                E_MAIL_AUTOCONFIG (object),
                                g_value_get_object (value));
                        return;
+
+               case PROP_USE_DOMAIN:
+                       mail_autoconfig_set_use_domain (
+                               E_MAIL_AUTOCONFIG (object),
+                               g_value_get_string (value));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -717,6 +747,13 @@ mail_autoconfig_get_property (GObject *object,
                                e_mail_autoconfig_get_registry (
                                E_MAIL_AUTOCONFIG (object)));
                        return;
+
+               case PROP_USE_DOMAIN:
+                       g_value_set_string (
+                               value,
+                               e_mail_autoconfig_get_use_domain (
+                               E_MAIL_AUTOCONFIG (object)));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -745,6 +782,7 @@ mail_autoconfig_finalize (GObject *object)
        g_free (priv->email_address);
        g_free (priv->email_local_part);
        g_free (priv->email_domain_part);
+       g_free (priv->use_domain);
 
        g_free (priv->imap_result.user);
        g_free (priv->imap_result.host);
@@ -799,6 +837,9 @@ mail_autoconfig_initable_init (GInitable *initable,
                g_strndup (email_address, cp - email_address);
        autoconfig->priv->email_domain_part = g_strdup (domain);
 
+       if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
+               domain = autoconfig->priv->use_domain;
+
        /* First try the email address domain verbatim. */
        success = mail_autoconfig_lookup (
                autoconfig, domain, cancellable, &local_error);
@@ -892,6 +933,18 @@ e_mail_autoconfig_class_init (EMailAutoconfigClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_USE_DOMAIN,
+               g_param_spec_string (
+                       "use-domain",
+                       "Use Domain",
+                       "A domain to use, instead of the one from email-address",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -909,6 +962,7 @@ e_mail_autoconfig_init (EMailAutoconfig *autoconfig)
 EMailAutoconfig *
 e_mail_autoconfig_new_sync (ESourceRegistry *registry,
                             const gchar *email_address,
+                           const gchar *use_domain,
                             GCancellable *cancellable,
                             GError **error)
 {
@@ -920,12 +974,14 @@ e_mail_autoconfig_new_sync (ESourceRegistry *registry,
                cancellable, error,
                "registry", registry,
                "email-address", email_address,
+               "use-domain", use_domain,
                NULL);
 }
 
 void
 e_mail_autoconfig_new (ESourceRegistry *registry,
                        const gchar *email_address,
+                      const gchar *use_domain,
                        gint io_priority,
                        GCancellable *cancellable,
                        GAsyncReadyCallback callback,
@@ -940,6 +996,7 @@ e_mail_autoconfig_new (ESourceRegistry *registry,
                callback, user_data,
                "registry", registry,
                "email-address", email_address,
+               "use-domain", use_domain,
                NULL);
 }
 
@@ -991,6 +1048,14 @@ e_mail_autoconfig_get_email_address (EMailAutoconfig *autoconfig)
        return autoconfig->priv->email_address;
 }
 
+const gchar *
+e_mail_autoconfig_get_use_domain (EMailAutoconfig *autoconfig)
+{
+       g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
+
+       return autoconfig->priv->use_domain;
+}
+
 gboolean
 e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
                                     ESource *imap_source)
@@ -1000,7 +1065,7 @@ e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
 
        return mail_autoconfig_set_details (
                &autoconfig->priv->imap_result,
-               imap_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+               imap_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "imapx");
 }
 
 gboolean
@@ -1012,7 +1077,7 @@ e_mail_autoconfig_set_pop3_details (EMailAutoconfig *autoconfig,
 
        return mail_autoconfig_set_details (
                &autoconfig->priv->pop3_result,
-               pop3_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+               pop3_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "pop3");
 }
 
 gboolean
@@ -1024,7 +1089,7 @@ e_mail_autoconfig_set_smtp_details (EMailAutoconfig *autoconfig,
 
        return mail_autoconfig_set_details (
                &autoconfig->priv->smtp_result,
-               smtp_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT);
+               smtp_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT, "smtp");
 }
 
 void
@@ -1043,7 +1108,10 @@ e_mail_autoconfig_dump_results (EMailAutoconfig *autoconfig)
                autoconfig->priv->smtp_result.set;
 
        if (have_results) {
-               g_print ("Results for <%s>\n", email_address);
+               if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
+                       g_print ("Results for <%s> and domain '%s'\n", email_address, 
autoconfig->priv->use_domain);
+               else
+                       g_print ("Results for <%s>\n", email_address);
 
                if (autoconfig->priv->imap_result.set) {
                        g_print (
@@ -1069,6 +1137,8 @@ e_mail_autoconfig_dump_results (EMailAutoconfig *autoconfig)
                                autoconfig->priv->smtp_result.port);
                }
 
+       } else if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain) {
+               g_print ("No results for <%s> and domain '%s'\n", email_address, 
autoconfig->priv->use_domain);
        } else {
                g_print ("No results for <%s>\n", email_address);
        }
diff --git a/src/mail/e-mail-autoconfig.h b/src/mail/e-mail-autoconfig.h
index 2ef717c..bdcc51c 100644
--- a/src/mail/e-mail-autoconfig.h
+++ b/src/mail/e-mail-autoconfig.h
@@ -59,10 +59,12 @@ GType               e_mail_autoconfig_get_type      (void) G_GNUC_CONST;
 EMailAutoconfig *
                e_mail_autoconfig_new_sync      (ESourceRegistry *registry,
                                                 const gchar *email_address,
+                                                const gchar *use_domain,
                                                 GCancellable *cancellable,
                                                 GError **error);
 void           e_mail_autoconfig_new           (ESourceRegistry *registry,
                                                 const gchar *email_address,
+                                                const gchar *use_domain,
                                                 gint io_priority,
                                                 GCancellable *cancellable,
                                                 GAsyncReadyCallback callback,
@@ -74,6 +76,8 @@ ESourceRegistry *
                e_mail_autoconfig_get_registry  (EMailAutoconfig *autoconfig);
 const gchar *  e_mail_autoconfig_get_email_address
                                                (EMailAutoconfig *autoconfig);
+const gchar *  e_mail_autoconfig_get_use_domain
+                                               (EMailAutoconfig *autoconfig);
 gboolean       e_mail_autoconfig_set_imap_details
                                                (EMailAutoconfig *autoconfig,
                                                 ESource *imap_source);
diff --git a/src/mail/e-mail-config-assistant.c b/src/mail/e-mail-config-assistant.c
index bbe52ff..8aba8af 100644
--- a/src/mail/e-mail-config-assistant.c
+++ b/src/mail/e-mail-config-assistant.c
@@ -84,6 +84,13 @@ enum {
        PROP_TRANSPORT_SOURCE
 };
 
+enum {
+       NEW_SOURCE,
+       LAST_SIGNAL
+};
+
+static gulong signals[LAST_SIGNAL];
+
 /* XXX We implement EAlertSink but don't implement a custom submit_alert()
  *     method.  So any alert results in a pop-up message dialog, which is a
  *     fashion faux pas these days.  But it's only used when submitting the
@@ -1208,6 +1215,23 @@ e_mail_config_assistant_class_init (EMailConfigAssistantClass *class)
                        E_TYPE_SOURCE,
                        G_PARAM_READABLE |
                        G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EMailConfigAssistant::new-source:
+        * @uid: an #ESource UID which had been created
+        *
+        * Emitted to notify about the assistant finishing an account #ESource.
+        *
+        * Since: 3.28
+        **/
+       signals[NEW_SOURCE] = g_signal_new (
+               "new-source",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMailConfigAssistantClass, new_source),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1, G_TYPE_STRING);
 }
 
 static void
@@ -1474,6 +1498,7 @@ e_mail_config_assistant_commit_finish (EMailConfigAssistant *assistant,
                                        GError **error)
 {
        GSimpleAsyncResult *simple;
+       gboolean success;
 
        g_return_val_if_fail (
                g_simple_async_result_is_valid (
@@ -1483,6 +1508,16 @@ e_mail_config_assistant_commit_finish (EMailConfigAssistant *assistant,
        simple = G_SIMPLE_ASYNC_RESULT (result);
 
        /* Assume success unless a GError is set. */
-       return !g_simple_async_result_propagate_error (simple, error);
+       success = !g_simple_async_result_propagate_error (simple, error);
+
+       if (success) {
+               ESource *source;
+
+               source = e_mail_config_assistant_get_account_source (assistant);
+               if (source)
+                       g_signal_emit (assistant, signals[NEW_SOURCE], 0, e_source_get_uid (source));
+       }
+
+       return success;
 }
 
diff --git a/src/mail/e-mail-config-assistant.h b/src/mail/e-mail-config-assistant.h
index 4aa5f14..2b65fb1 100644
--- a/src/mail/e-mail-config-assistant.h
+++ b/src/mail/e-mail-config-assistant.h
@@ -56,6 +56,10 @@ struct _EMailConfigAssistant {
 
 struct _EMailConfigAssistantClass {
        GtkAssistantClass parent_class;
+
+       /* Signals */
+       void            (* new_source)  (EMailConfigAssistant *assistant,
+                                        const gchar *uid);
 };
 
 GType          e_mail_config_assistant_get_type
diff --git a/src/mail/e-mail-config-service-backend.c b/src/mail/e-mail-config-service-backend.c
index 6854c38..29411c9 100644
--- a/src/mail/e-mail-config-service-backend.c
+++ b/src/mail/e-mail-config-service-backend.c
@@ -527,7 +527,7 @@ e_mail_config_service_backend_commit_changes (EMailConfigServiceBackend *backend
  * @config_lookup: an #EConfigLookup
  * @kind: an #EConfigLookupResultKind
  * @protocol: (nullable): optional protocol name, or %NULL
- * @source: (nullable): optioanl #ESource to configure, or %NULL
+ * @source: (nullable): optional #ESource to configure, or %NULL
  * @out_priority: (out) (nullable): priority of the chosen lookup result
  * @out_is_complete: (out) (nullable): whether the config is complete
  *
@@ -535,7 +535,7 @@ e_mail_config_service_backend_commit_changes (EMailConfigServiceBackend *backend
  * configures the @source with it. The @out_priority is set to the priority
  * of that lookup result.
  *
- * If no @protocol is given, then the backend name of the @backend it used.
+ * If no @protocol is given, then the backend name of the @backend is used.
  * If no @source is given, then gets it with e_mail_config_service_backend_get_source().
  *
  * Returns: whether applied any changes
@@ -567,13 +567,13 @@ e_mail_config_service_backend_auto_configure_for_kind (EMailConfigServiceBackend
        if (!protocol)
                protocol = klass->backend_name;
 
-       results = e_config_lookup_get_results (config_lookup, kind, protocol);
+       results = e_config_lookup_dup_results (config_lookup, kind, protocol);
        results = g_slist_sort (results, e_config_lookup_result_compare);
 
        if (results && results->data) {
                EConfigLookupResult *lookup_result = results->data;
 
-               changed = e_config_lookup_result_configure_source (lookup_result, source);
+               changed = e_config_lookup_result_configure_source (lookup_result, config_lookup, source);
 
                if (changed) {
                        if (out_priority)
diff --git a/src/mail/test-mail-autoconfig.c b/src/mail/test-mail-autoconfig.c
index 6e7ad4e..8b58aa0 100644
--- a/src/mail/test-mail-autoconfig.c
+++ b/src/mail/test-mail-autoconfig.c
@@ -39,7 +39,7 @@ main (gint argc,
 
        if (registry != NULL) {
                autoconfig = e_mail_autoconfig_new_sync (
-                       registry, argv[1], NULL, &error);
+                       registry, argv[1], NULL, NULL, &error);
                g_object_unref (registry);
        } else {
                autoconfig = NULL;
diff --git a/src/modules/accounts-window/CMakeLists.txt b/src/modules/accounts-window/CMakeLists.txt
index 1c51b47..3f80e7d 100644
--- a/src/modules/accounts-window/CMakeLists.txt
+++ b/src/modules/accounts-window/CMakeLists.txt
@@ -6,6 +6,8 @@ set(sources
        accounts-window.c
        e-accounts-window-editors.c
        e-accounts-window-editors.h
+       e-collection-wizard-page.c
+       e-collection-wizard-page.h
        e-webdav-browser-page.c
        e-webdav-browser-page.h
 )
diff --git a/src/modules/accounts-window/accounts-window.c b/src/modules/accounts-window/accounts-window.c
index 400fbc2..be8c92d 100644
--- a/src/modules/accounts-window/accounts-window.c
+++ b/src/modules/accounts-window/accounts-window.c
@@ -21,6 +21,7 @@
 #include <glib-object.h>
 
 #include "e-accounts-window-editors.h"
+#include "e-collection-wizard-page.h"
 #include "e-webdav-browser-page.h"
 
 /* Module Entry Points */
@@ -31,6 +32,7 @@ G_MODULE_EXPORT void
 e_module_load (GTypeModule *type_module)
 {
        e_accounts_window_editors_type_register (type_module);
+       e_collection_wizard_page_type_register (type_module);
        e_webdav_browser_page_type_register (type_module);
 }
 
diff --git a/src/modules/accounts-window/e-accounts-window-editors.c 
b/src/modules/accounts-window/e-accounts-window-editors.c
index 967251a..c0b28e6 100644
--- a/src/modules/accounts-window/e-accounts-window-editors.c
+++ b/src/modules/accounts-window/e-accounts-window-editors.c
@@ -395,6 +395,46 @@ accounts_window_editors_get_editing_flags_cb (EAccountsWindow *accounts_window,
        return FALSE;
 }
 
+static void
+accounts_window_editors_commit_changes_cb (ESourceConfig *config,
+                                          ESource *scratch_source,
+                                          gpointer user_data)
+{
+       EAccountsWindow *accounts_window;
+       GWeakRef *weakref = user_data;
+
+       g_return_if_fail (E_IS_SOURCE (scratch_source));
+       g_return_if_fail (weakref != NULL);
+
+       accounts_window = g_weak_ref_get (weakref);
+       if (!accounts_window)
+               return;
+
+       e_accounts_window_select_source (accounts_window, e_source_get_uid (scratch_source));
+
+       g_object_unref (accounts_window);
+}
+
+static void
+accounts_window_editors_new_mail_source_cb (GtkWidget *assistant,
+                                           const gchar *uid,
+                                           gpointer user_data)
+{
+       EAccountsWindow *accounts_window;
+       GWeakRef *weakref = user_data;
+
+       g_return_if_fail (uid != NULL);
+       g_return_if_fail (weakref != NULL);
+
+       accounts_window = g_weak_ref_get (weakref);
+       if (!accounts_window)
+               return;
+
+       e_accounts_window_select_source (accounts_window, uid);
+
+       g_object_unref (accounts_window);
+}
+
 static gboolean
 accounts_window_editors_add_source_cb (EAccountsWindow *accounts_window,
                                       const gchar *kind,
@@ -417,9 +457,17 @@ accounts_window_editors_add_source_cb (EAccountsWindow *accounts_window,
                EShell *shell = e_shell_get_default ();
 
                if (shell) {
+                       GtkWidget *assistant = NULL;
+
                        shell_backend = e_shell_get_backend_by_name (shell, "mail");
 
-                       g_signal_emit_by_name (shell_backend, "new-account", GTK_WINDOW (accounts_window));
+                       g_signal_emit_by_name (shell_backend, "new-account", GTK_WINDOW (accounts_window), 
&assistant);
+
+                       if (assistant) {
+                               g_signal_connect_data (assistant, "new-source",
+                                       G_CALLBACK (accounts_window_editors_new_mail_source_cb),
+                                       e_weak_ref_new (accounts_window), (GClosureNotify) e_weak_ref_free, 
0);
+                       }
                }
 
                return TRUE;
@@ -444,6 +492,10 @@ accounts_window_editors_add_source_cb (EAccountsWindow *accounts_window,
        if (config) {
                GtkWidget *dialog;
 
+               g_signal_connect_data (config, "commit-changes",
+                       G_CALLBACK (accounts_window_editors_commit_changes_cb),
+                       e_weak_ref_new (accounts_window), (GClosureNotify) e_weak_ref_free, 0);
+
                dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
 
                gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (accounts_window));
diff --git a/src/modules/accounts-window/e-collection-wizard-page.c 
b/src/modules/accounts-window/e-collection-wizard-page.c
new file mode 100644
index 0000000..fab0ffe
--- /dev/null
+++ b/src/modules/accounts-window/e-collection-wizard-page.c
@@ -0,0 +1,277 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-util/e-util.h"
+#include "shell/e-shell.h"
+
+#include "e-collection-wizard-page.h"
+
+/* Standard GObject macros */
+#define E_TYPE_COLLECTION_WIZARD_PAGE \
+       (e_collection_wizard_page_get_type ())
+#define E_COLLECTION_WIZARD_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COLLECTION_WIZARD_PAGE, ECollectionWizardPage))
+#define E_COLLECTION_WIZARD_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COLLECTION_WIZARD_PAGE, ECollectionWizardPageClass))
+#define E_IS_COLLECTION_WIZARD_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COLLECTION_WIZARD_PAGE))
+#define E_IS_COLLECTION_WIZARD_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COLLECTION_WIZARD_PAGE))
+#define E_COLLECTION_WIZARD_PAGE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COLLECTION_WIZARD_PAGE, ECollectionWizardPageClass))
+
+typedef struct _ECollectionWizardPage ECollectionWizardPage;
+typedef struct _ECollectionWizardPageClass ECollectionWizardPageClass;
+
+struct _ECollectionWizardPage {
+       EExtension parent;
+
+       ECollectionAccountWizard *collection_wizard;
+       gint page_index;
+
+       GtkButton *prev_button; /* not referenced */
+       GtkButton *next_button; /* not referenced */
+};
+
+struct _ECollectionWizardPageClass {
+       EExtensionClass parent_class;
+};
+
+GType e_collection_wizard_page_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_DYNAMIC_TYPE (ECollectionWizardPage, e_collection_wizard_page, E_TYPE_EXTENSION)
+
+static void
+collection_wizard_page_update_button_captions (ECollectionWizardPage *page)
+{
+       g_return_if_fail (E_IS_COLLECTION_WIZARD_PAGE (page));
+
+       if (gtk_notebook_get_current_page (GTK_NOTEBOOK (page->collection_wizard)))
+               gtk_button_set_label (page->prev_button, _("_Previous"));
+       else
+               gtk_button_set_label (page->prev_button, _("_Back"));
+
+       if (e_collection_account_wizard_is_finish_page (page->collection_wizard))
+               gtk_button_set_label (page->next_button, _("_Finish"));
+       else
+               gtk_button_set_label (page->next_button, _("_Next"));
+}
+
+static gboolean
+collection_wizard_page_add_source_cb (EAccountsWindow *accounts_window,
+                                     const gchar *kind,
+                                     gpointer user_data)
+{
+       ECollectionWizardPage *page = user_data;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_COLLECTION_WIZARD_PAGE (page), FALSE);
+
+       if (g_strcmp0 (kind, "collection") != 0)
+               return FALSE;
+
+       e_collection_account_wizard_reset (page->collection_wizard);
+       collection_wizard_page_update_button_captions (page);
+
+       e_accounts_window_activate_page (accounts_window, page->page_index);
+
+       return TRUE;
+}
+
+static void
+collection_wizard_page_wizard_done (ECollectionWizardPage *page,
+                                   const gchar *uid)
+{
+       EAccountsWindow *accounts_window;
+
+       g_return_if_fail (E_IS_COLLECTION_WIZARD_PAGE (page));
+
+       accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       e_collection_account_wizard_abort (page->collection_wizard);
+
+       e_accounts_window_select_source (accounts_window, uid);
+       e_accounts_window_activate_page (accounts_window, -1);
+}
+
+static void
+collection_wizard_back_button_clicked_cb (GtkButton *button,
+                                         gpointer user_data)
+{
+       ECollectionWizardPage *page = user_data;
+       EAccountsWindow *accounts_window;
+
+       g_return_if_fail (E_IS_COLLECTION_WIZARD_PAGE (page));
+
+       accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (!e_collection_account_wizard_prev (page->collection_wizard)) {
+               e_collection_account_wizard_abort (page->collection_wizard);
+               e_accounts_window_activate_page (accounts_window, -1);
+       } else {
+               collection_wizard_page_update_button_captions (page);
+       }
+}
+
+static void
+collection_wizard_next_button_clicked_cb (GtkButton *button,
+                                         gpointer user_data)
+{
+       ECollectionWizardPage *page = user_data;
+       EAccountsWindow *accounts_window;
+       gboolean is_finish_page;
+
+       g_return_if_fail (E_IS_COLLECTION_WIZARD_PAGE (page));
+
+       accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       is_finish_page = e_collection_account_wizard_is_finish_page (page->collection_wizard);
+
+       if (e_collection_account_wizard_next (page->collection_wizard)) {
+               if (is_finish_page) {
+                       collection_wizard_page_wizard_done (page, NULL);
+               } else {
+                       collection_wizard_page_update_button_captions (page);
+               }
+       }
+}
+
+static void
+collection_wizard_page_constructed (GObject *object)
+{
+       EAccountsWindow *accounts_window;
+       ECollectionWizardPage *page;
+       GtkWidget *widget;
+       GtkWidget *vbox, *hbox;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_collection_wizard_page_parent_class)->constructed (object);
+
+       page = E_COLLECTION_WIZARD_PAGE (object);
+       accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+
+       g_signal_connect (accounts_window, "add-source",
+               G_CALLBACK (collection_wizard_page_add_source_cb), object);
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_widget_show (vbox);
+
+       widget = e_collection_account_wizard_new (e_accounts_window_get_registry (accounts_window));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
+       page->collection_wizard = E_COLLECTION_ACCOUNT_WIZARD (widget);
+
+       g_signal_connect_swapped (page->collection_wizard, "done",
+               G_CALLBACK (collection_wizard_page_wizard_done), page);
+
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       g_object_set (G_OBJECT (hbox),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+       widget = e_dialog_button_new_with_icon ("go-previous", _("_Back"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+       page->prev_button = GTK_BUTTON (widget);
+
+       g_signal_connect (widget, "clicked",
+               G_CALLBACK (collection_wizard_back_button_clicked_cb), page);
+
+       widget = e_dialog_button_new_with_icon ("go-next", _("_Next"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "visible", TRUE,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+       page->next_button = GTK_BUTTON (widget);
+
+       e_binding_bind_property (
+               page->collection_wizard, "can-run",
+               widget, "sensitive",
+               G_BINDING_DEFAULT);
+
+       g_signal_connect (widget, "clicked",
+               G_CALLBACK (collection_wizard_next_button_clicked_cb), page);
+
+       page->page_index = e_accounts_window_add_page (accounts_window, vbox);
+}
+
+static void
+e_collection_wizard_page_class_init (ECollectionWizardPageClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = collection_wizard_page_constructed;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_ACCOUNTS_WINDOW;
+}
+
+static void
+e_collection_wizard_page_class_finalize (ECollectionWizardPageClass *class)
+{
+}
+
+static void
+e_collection_wizard_page_init (ECollectionWizardPage *extension)
+{
+}
+
+void
+e_collection_wizard_page_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_collection_wizard_page_register_type (type_module);
+}
diff --git a/src/modules/accounts-window/e-collection-wizard-page.h 
b/src/modules/accounts-window/e-collection-wizard-page.h
new file mode 100644
index 0000000..4d1df7c
--- /dev/null
+++ b/src/modules/accounts-window/e-collection-wizard-page.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_COLLECTION_WIZARD_PAGE_H
+#define E_COLLECTION_WIZARD_PAGE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_collection_wizard_page_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_COLLECTION_WIZARD_PAGE_H */
diff --git a/src/modules/config-lookup/CMakeLists.txt b/src/modules/config-lookup/CMakeLists.txt
index cbf3abb..fce8536 100644
--- a/src/modules/config-lookup/CMakeLists.txt
+++ b/src/modules/config-lookup/CMakeLists.txt
@@ -8,6 +8,8 @@ set(sources
        e-gnome-config-lookup.h
        e-srv-config-lookup.c
        e-srv-config-lookup.h
+       e-webdav-config-lookup.c
+       e-webdav-config-lookup.h
 )
 set(extra_defines)
 set(extra_cflags)
diff --git a/src/modules/config-lookup/config-lookup.c b/src/modules/config-lookup/config-lookup.c
index 3d3895c..dfd7035 100644
--- a/src/modules/config-lookup/config-lookup.c
+++ b/src/modules/config-lookup/config-lookup.c
@@ -22,6 +22,7 @@
 
 #include "e-gnome-config-lookup.h"
 #include "e-srv-config-lookup.h"
+#include "e-webdav-config-lookup.h"
 
 /* Module Entry Points */
 void e_module_load (GTypeModule *type_module);
@@ -32,6 +33,7 @@ e_module_load (GTypeModule *type_module)
 {
        e_gnome_config_lookup_type_register (type_module);
        e_srv_config_lookup_type_register (type_module);
+       e_webdav_config_lookup_type_register (type_module);
 }
 
 G_MODULE_EXPORT void
diff --git a/src/modules/config-lookup/e-gnome-config-lookup.c 
b/src/modules/config-lookup/e-gnome-config-lookup.c
index 3567773..ad3f8ea 100644
--- a/src/modules/config-lookup/e-gnome-config-lookup.c
+++ b/src/modules/config-lookup/e-gnome-config-lookup.c
@@ -17,6 +17,8 @@
 
 #include "evolution-config.h"
 
+#include <glib/gi18n-lib.h>
+
 #include "mail/e-mail-autoconfig.h"
 #include "e-util/e-util.h"
 
@@ -54,18 +56,31 @@ struct _EGnomeConfigLookupClass {
 
 GType e_gnome_config_lookup_get_type (void) G_GNUC_CONST;
 
-G_DEFINE_DYNAMIC_TYPE (EGnomeConfigLookup, e_gnome_config_lookup, E_TYPE_EXTENSION)
+static void gnome_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (EGnomeConfigLookup, e_gnome_config_lookup, E_TYPE_EXTENSION, 0,
+       G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_CONFIG_LOOKUP_WORKER, gnome_config_lookup_worker_iface_init))
+
+static const gchar *
+gnome_config_lookup_worker_get_display_name (EConfigLookupWorker *lookup_worker)
+{
+       return _("Look up e-mail domain on the GNOME server");
+}
 
 static void
-gnome_config_lookup_thread (EConfigLookup *config_lookup,
-                           const ENamedParameters *params,
-                           gpointer user_data,
-                           GCancellable *cancellable)
+gnome_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
+                               EConfigLookup *config_lookup,
+                               const ENamedParameters *params,
+                               ENamedParameters **out_restart_params,
+                               GCancellable *cancellable,
+                               GError **error)
 {
        EMailAutoconfig *mail_autoconfig;
        ESourceRegistry *registry;
        const gchar *email_address;
+       const gchar *servers;
 
+       g_return_if_fail (E_IS_GNOME_CONFIG_LOOKUP (lookup_worker));
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
        g_return_if_fail (params != NULL);
 
@@ -75,25 +90,29 @@ gnome_config_lookup_thread (EConfigLookup *config_lookup,
        if (!email_address || !*email_address)
                return;
 
-       mail_autoconfig = e_mail_autoconfig_new_sync (registry, email_address, cancellable, NULL);
+       mail_autoconfig = e_mail_autoconfig_new_sync (registry, email_address, NULL, cancellable, NULL);
        if (mail_autoconfig)
                e_mail_autoconfig_copy_results_to_config_lookup (mail_autoconfig, config_lookup);
 
        g_clear_object (&mail_autoconfig);
-}
 
-static void
-gnome_config_lookup_run_cb (EConfigLookup *config_lookup,
-                           const ENamedParameters *params,
-                           EActivity *activity,
-                           gpointer user_data)
-{
-       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
-       g_return_if_fail (E_IS_GNOME_CONFIG_LOOKUP (user_data));
-       g_return_if_fail (E_IS_ACTIVITY (activity));
+       servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS);
+       if (servers && *servers) {
+               gchar **servers_strv;
+               gint ii;
+
+               servers_strv = g_strsplit (servers, ";", 0);
+
+               for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); 
ii++) {
+                       mail_autoconfig = e_mail_autoconfig_new_sync (registry, email_address, 
servers_strv[ii], cancellable, NULL);
+                       if (mail_autoconfig)
+                               e_mail_autoconfig_copy_results_to_config_lookup (mail_autoconfig, 
config_lookup);
+
+                       g_clear_object (&mail_autoconfig);
+               }
 
-       e_config_lookup_create_thread (config_lookup, params, activity,
-               gnome_config_lookup_thread, NULL, NULL);
+               g_strfreev (servers_strv);
+       }
 }
 
 static void
@@ -106,8 +125,7 @@ gnome_config_lookup_constructed (GObject *object)
 
        config_lookup = E_CONFIG_LOOKUP (e_extension_get_extensible (E_EXTENSION (object)));
 
-       g_signal_connect (config_lookup, "run",
-               G_CALLBACK (gnome_config_lookup_run_cb), object);
+       e_config_lookup_register_worker (config_lookup, E_CONFIG_LOOKUP_WORKER (object));
 }
 
 static void
@@ -129,6 +147,13 @@ e_gnome_config_lookup_class_finalize (EGnomeConfigLookupClass *class)
 }
 
 static void
+gnome_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface)
+{
+       iface->get_display_name = gnome_config_lookup_worker_get_display_name;
+       iface->run = gnome_config_lookup_worker_run;
+}
+
+static void
 e_gnome_config_lookup_init (EGnomeConfigLookup *extension)
 {
 }
diff --git a/src/modules/config-lookup/e-srv-config-lookup.c b/src/modules/config-lookup/e-srv-config-lookup.c
index 7e81d4d..d2d0168 100644
--- a/src/modules/config-lookup/e-srv-config-lookup.c
+++ b/src/modules/config-lookup/e-srv-config-lookup.c
@@ -60,7 +60,10 @@ struct _ESrvConfigLookupClass {
 
 GType e_srv_config_lookup_get_type (void) G_GNUC_CONST;
 
-G_DEFINE_DYNAMIC_TYPE (ESrvConfigLookup, e_srv_config_lookup, E_TYPE_EXTENSION)
+static void srv_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (ESrvConfigLookup, e_srv_config_lookup, E_TYPE_EXTENSION, 0,
+       G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_CONFIG_LOOKUP_WORKER, srv_config_lookup_worker_iface_init))
 
 static void
 srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
@@ -73,19 +76,20 @@ srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
                EConfigLookupResultKind kind;
                const gchar *evo_protocol;
                const gchar *display_name;
+               const gchar *lookup_info;
                gint priority_base;
        } known_services[] = {
-               { "imaps",      E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "imapx",   _("IMAP server"),    
E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP },
-               { "imap",       E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "imapx",   _("IMAP server"),    
E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP + PRIORITY_OFFSET / 2 },
-               { "pop3s",      E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "pop",     _("POP3 server"),    
E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3 },
-               { "pop3",       E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "pop",     _("POP3 server"),    
E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3 + PRIORITY_OFFSET / 2 },
-               { "submission", E_CONFIG_LOOKUP_RESULT_MAIL_SEND,     "smtp",    _("SMTP server"),    
E_CONFIG_LOOKUP_RESULT_PRIORITY_SMTP },
-               { "caldavs",    E_CONFIG_LOOKUP_RESULT_COLLECTION,    "caldav",  _("CalDAV server"),  
PRIORITY_DEFAULT },
-               { "caldav",     E_CONFIG_LOOKUP_RESULT_COLLECTION,    "caldav",  _("CalDAV server"),  
PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 },
-               { "carddavs",   E_CONFIG_LOOKUP_RESULT_COLLECTION,    "carddav", _("CardDAV server"), 
PRIORITY_DEFAULT },
-               { "carddav",    E_CONFIG_LOOKUP_RESULT_COLLECTION,    "carddav", _("CardDAV server"), 
PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 },
-               { "ldaps",      E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK,  "ldap",    _("LDAP server"),    
PRIORITY_DEFAULT },
-               { "ldap",       E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK,  "ldap",    _("LDAP server"),    
PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 }
+               { "imaps",      E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "imapx",   N_("IMAP server"),    
N_("Looking up IMAP server…"),    E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP },
+               { "imap",       E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "imapx",   N_("IMAP server"),    
N_("Looking up IMAP server…"),    E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP + PRIORITY_OFFSET / 2 },
+               { "pop3s",      E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "pop",     N_("POP3 server"),    
N_("Looking up POP3 server…"),    E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3 },
+               { "pop3",       E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE,  "pop",     N_("POP3 server"),    
N_("Looking up POP3 server…"),    E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3 + PRIORITY_OFFSET / 2 },
+               { "submission", E_CONFIG_LOOKUP_RESULT_MAIL_SEND,     "smtp",    N_("SMTP server"),    
N_("Looking up SMTP server…"),    E_CONFIG_LOOKUP_RESULT_PRIORITY_SMTP },
+               { "caldavs",    E_CONFIG_LOOKUP_RESULT_COLLECTION,    "caldav",  N_("CalDAV server"),  
N_("Looking up CalDAV server…"),  PRIORITY_DEFAULT },
+               { "caldav",     E_CONFIG_LOOKUP_RESULT_COLLECTION,    "caldav",  N_("CalDAV server"),  
N_("Looking up CalDAV server…"),  PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 },
+               { "carddavs",   E_CONFIG_LOOKUP_RESULT_COLLECTION,    "carddav", N_("CardDAV server"), 
N_("Looking up CardDAV server…"), PRIORITY_DEFAULT },
+               { "carddav",    E_CONFIG_LOOKUP_RESULT_COLLECTION,    "carddav", N_("CardDAV server"), 
N_("Looking up CardDAV server…"), PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 },
+               { "ldaps",      E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK,  "ldap",    N_("LDAP server"),    
N_("Looking up LDAP server…"),    PRIORITY_DEFAULT },
+               { "ldap",       E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK,  "ldap",    N_("LDAP server"),    
N_("Looking up LDAP server…"),    PRIORITY_DEFAULT + PRIORITY_OFFSET / 2 }
        };
 
        GResolver *resolver;
@@ -103,7 +107,9 @@ srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
                GList *targets;
                GError *local_error = NULL;
 
+               camel_operation_push_message (cancellable, "%s", _(known_services[ii].lookup_info));
                targets = g_resolver_lookup_service (resolver, known_services[ii].gio_protocol, "tcp", 
domain, cancellable, &local_error);
+               camel_operation_pop_message (cancellable);
 
                if (local_error) {
                        if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
@@ -136,8 +142,9 @@ srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
                                        known_services[ii].priority_base - PRIORITY_OFFSET,
                                        FALSE,
                                        known_services[ii].evo_protocol,
-                                       known_services[ii].display_name,
-                                       description);
+                                       _(known_services[ii].display_name),
+                                       description,
+                                       NULL);
 
                                g_free (description);
 
@@ -179,23 +186,33 @@ srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
                                                "backend-name", "webdav");
 
                                        e_config_lookup_result_simple_add_string (lookup_result, 
E_SOURCE_EXTENSION_COLLECTION,
+                                               "identity", email_address);
+
+                                       e_config_lookup_result_simple_add_string (lookup_result, 
E_SOURCE_EXTENSION_COLLECTION,
                                                is_calendar ? "calendar-url" : "contacts-url", url);
 
                                        g_free (url);
                                } else if (known_services[ii].kind == E_CONFIG_LOOKUP_RESULT_ADDRESS_BOOK) {
                                        ESourceLDAPSecurity security;
 
+                                       e_config_lookup_result_simple_add_string (lookup_result, 
E_SOURCE_EXTENSION_ADDRESS_BOOK,
+                                               "backend-name", "ldap");
+
                                        e_config_lookup_result_simple_add_string (lookup_result, NULL, 
"parent", "ldap-stub");
                                        e_config_lookup_result_simple_add_string (lookup_result, 
E_SOURCE_EXTENSION_AUTHENTICATION, "host", hostname);
                                        e_config_lookup_result_simple_add_uint (lookup_result, 
E_SOURCE_EXTENSION_AUTHENTICATION, "port", g_srv_target_get_port (target));
 
+                                       e_config_lookup_result_simple_add_enum (lookup_result,
+                                               E_SOURCE_EXTENSION_LDAP_BACKEND, "scope",
+                                               E_TYPE_SOURCE_LDAP_SCOPE, E_SOURCE_LDAP_SCOPE_SUBTREE);
+
                                        if (g_str_equal (known_services[ii].gio_protocol, "ldaps"))
                                                security = E_SOURCE_LDAP_SECURITY_LDAPS;
                                        else
                                                security = E_SOURCE_LDAP_SECURITY_NONE;
 
                                        e_config_lookup_result_simple_add_enum (lookup_result,
-                                               E_SOURCE_EXTENSION_LDAP_BACKEND, "security-method",
+                                               E_SOURCE_EXTENSION_LDAP_BACKEND, "security",
                                                E_TYPE_SOURCE_LDAP_SECURITY, security);
                                } else {
                                        g_warn_if_reached ();
@@ -211,15 +228,25 @@ srv_config_lookup_domain_sync (EConfigLookup *config_lookup,
        g_object_unref (resolver);
 }
 
+static const gchar *
+srv_config_lookup_worker_get_display_name (EConfigLookupWorker *worker)
+{
+       return _("Look up in SRV records");
+}
+
 static void
-srv_config_lookup_thread (EConfigLookup *config_lookup,
-                         const ENamedParameters *params,
-                         gpointer user_data,
-                         GCancellable *cancellable)
+srv_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
+                             EConfigLookup *config_lookup,
+                             const ENamedParameters *params,
+                             ENamedParameters **out_restart_params,
+                             GCancellable *cancellable,
+                             GError **error)
 {
        const gchar *email_address;
-       gchar *domain;
+       const gchar *domain;
+       const gchar *servers;
 
+       g_return_if_fail (E_IS_SRV_CONFIG_LOOKUP (lookup_worker));
        g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
        g_return_if_fail (params != NULL);
 
@@ -229,28 +256,25 @@ srv_config_lookup_thread (EConfigLookup *config_lookup,
                return;
 
        domain = strchr (email_address, '@');
-       if (!domain)
-               return;
+       if (domain && *domain)
+               srv_config_lookup_domain_sync (config_lookup, email_address, domain + 1, cancellable);
 
-       domain = g_strdup (domain + 1);
+       servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS);
+       if (servers && *servers) {
+               gchar **servers_strv;
+               gint ii;
 
-       srv_config_lookup_domain_sync (config_lookup, email_address, domain, cancellable);
+               servers_strv = g_strsplit (servers, ";", 0);
 
-       g_free (domain);
-}
+               for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); 
ii++) {
+                       domain = servers_strv[ii];
 
-static void
-srv_config_lookup_run_cb (EConfigLookup *config_lookup,
-                         const ENamedParameters *params,
-                         EActivity *activity,
-                         gpointer user_data)
-{
-       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
-       g_return_if_fail (E_IS_SRV_CONFIG_LOOKUP (user_data));
-       g_return_if_fail (E_IS_ACTIVITY (activity));
+                       if (domain && *domain)
+                               srv_config_lookup_domain_sync (config_lookup, email_address, domain, 
cancellable);
+               }
 
-       e_config_lookup_create_thread (config_lookup, params, activity,
-               srv_config_lookup_thread, NULL, NULL);
+               g_strfreev (servers_strv);
+       }
 }
 
 static void
@@ -263,8 +287,7 @@ srv_config_lookup_constructed (GObject *object)
 
        config_lookup = E_CONFIG_LOOKUP (e_extension_get_extensible (E_EXTENSION (object)));
 
-       g_signal_connect (config_lookup, "run",
-               G_CALLBACK (srv_config_lookup_run_cb), object);
+       e_config_lookup_register_worker (config_lookup, E_CONFIG_LOOKUP_WORKER (object));
 }
 
 static void
@@ -286,6 +309,13 @@ e_srv_config_lookup_class_finalize (ESrvConfigLookupClass *class)
 }
 
 static void
+srv_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface)
+{
+       iface->get_display_name = srv_config_lookup_worker_get_display_name;
+       iface->run = srv_config_lookup_worker_run;
+}
+
+static void
 e_srv_config_lookup_init (ESrvConfigLookup *extension)
 {
 }
diff --git a/src/modules/config-lookup/e-webdav-config-lookup.c 
b/src/modules/config-lookup/e-webdav-config-lookup.c
new file mode 100644
index 0000000..4c17798
--- /dev/null
+++ b/src/modules/config-lookup/e-webdav-config-lookup.c
@@ -0,0 +1,435 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <libedataserver/libedataserver.h>
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-util/e-util.h"
+
+#include "e-webdav-config-lookup.h"
+
+/* Standard GObject macros */
+#define E_TYPE_WEBDAV_CONFIG_LOOKUP \
+       (e_webdav_config_lookup_get_type ())
+#define E_WEBDAV_CONFIG_LOOKUP(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBDAV_CONFIG_LOOKUP, EWebDAVConfigLookup))
+#define E_WEBDAV_CONFIG_LOOKUP_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEBDAV_CONFIG_LOOKUP, EWebDAVConfigLookupClass))
+#define E_IS_WEBDAV_CONFIG_LOOKUP(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEBDAV_CONFIG_LOOKUP))
+#define E_IS_WEBDAV_CONFIG_LOOKUP_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEBDAV_CONFIG_LOOKUP))
+#define E_WEBDAV_CONFIG_LOOKUP_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEBDAV_CONFIG_LOOKUP, EWebDAVConfigLookupClass))
+
+typedef struct _EWebDAVConfigLookup EWebDAVConfigLookup;
+typedef struct _EWebDAVConfigLookupClass EWebDAVConfigLookupClass;
+
+struct _EWebDAVConfigLookup {
+       EExtension parent;
+};
+
+struct _EWebDAVConfigLookupClass {
+       EExtensionClass parent_class;
+};
+
+GType e_webdav_config_lookup_get_type (void) G_GNUC_CONST;
+
+static void webdav_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (EWebDAVConfigLookup, e_webdav_config_lookup, E_TYPE_EXTENSION, 0,
+       G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_CONFIG_LOOKUP_WORKER, webdav_config_lookup_worker_iface_init))
+
+static void
+webdav_config_lookup_to_result (EConfigLookup *config_lookup,
+                               const gchar *url,
+                               const ENamedParameters *params,
+                               const gchar *user,
+                               const gchar *certificate_trust,
+                               const GSList *discovered_sources)
+{
+       EConfigLookupResult *lookup_result;
+       GSList *link;
+       gboolean has_calendar = FALSE, has_contacts = FALSE;
+       GString *description;
+
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+
+       for (link = (GSList *) discovered_sources; link && (!has_calendar || !has_contacts); link = 
g_slist_next (link)) {
+               EWebDAVDiscoveredSource *discovered = link->data;
+
+               if (!discovered)
+                       continue;
+
+               has_calendar = has_calendar ||
+                       (discovered->supports & E_WEBDAV_DISCOVER_SUPPORTS_EVENTS) != 0 ||
+                       (discovered->supports & E_WEBDAV_DISCOVER_SUPPORTS_MEMOS) != 0 ||
+                       (discovered->supports & E_WEBDAV_DISCOVER_SUPPORTS_TASKS) != 0;
+
+               has_contacts = has_contacts ||
+                       (discovered->supports & E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS) != 0;
+       }
+
+       if (!has_calendar && !has_contacts)
+               return;
+
+       description = g_string_new ("");
+
+       if (has_calendar) {
+               if (description->len)
+                       g_string_append_c (description, '\n');
+
+               g_string_append_printf (description, _("CalDAV: %s"), url);
+       }
+
+       if (has_contacts) {
+               if (description->len)
+                       g_string_append_c (description, '\n');
+
+               g_string_append_printf (description, _("CardDAV: %s"), url);
+       }
+
+       lookup_result = e_config_lookup_result_simple_new (E_CONFIG_LOOKUP_RESULT_COLLECTION,
+               E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3,
+               TRUE, "webdav",
+               has_calendar && has_contacts ? _("CalDAV and CardDAV server") :
+               has_calendar ? _("CalDAV server") : _("CardDAV server"),
+               description->str,
+               params && e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_PASSWORD) &&
+               e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_REMEMBER_PASSWORD) ?
+               e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_PASSWORD) : NULL);
+
+       g_string_free (description, TRUE);
+
+       e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_COLLECTION,
+               "backend-name", "webdav");
+
+       e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_COLLECTION,
+               "identity", user);
+
+       e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_AUTHENTICATION,
+               "user", user);
+
+       if (has_calendar) {
+               e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_COLLECTION,
+                       "calendar-url", url);
+       }
+
+       if (has_contacts) {
+               e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_COLLECTION,
+                       "contacts-url", url);
+       }
+
+       if (certificate_trust) {
+               e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_WEBDAV_BACKEND,
+                       "ssl-trust", certificate_trust);
+       }
+
+       e_config_lookup_add_result (config_lookup, lookup_result);
+}
+
+static gboolean
+webdav_config_lookup_propagate_error (GError **error,
+                                     GError *local_error,
+                                     const gchar *certificate_pem,
+                                     GTlsCertificateFlags certificate_errors)
+{
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+               local_error->domain = E_CONFIG_LOOKUP_WORKER_ERROR;
+               local_error->code = E_CONFIG_LOOKUP_WORKER_ERROR_REQUIRES_PASSWORD;
+
+               g_propagate_error (error, local_error);
+
+               return TRUE;
+       }
+
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) &&
+           certificate_pem && *certificate_pem && certificate_errors) {
+               gchar *description = e_trust_prompt_describe_certificate_errors (certificate_errors);
+
+               if (description) {
+                       g_set_error_literal (error, E_CONFIG_LOOKUP_WORKER_ERROR,
+                               E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE, description);
+
+                       g_clear_error (&local_error);
+                       g_free (description);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static void
+webdav_config_lookup_set_host_from_url (ESourceAuthentication *authentication_extension,
+                                       const gchar *url)
+{
+       SoupURI *suri = NULL;
+       const gchar *host = NULL;
+
+       g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (authentication_extension));
+
+       if (url) {
+               suri = soup_uri_new (url);
+               if (suri)
+                       host = soup_uri_get_host (suri);
+       }
+
+       e_source_authentication_set_host (authentication_extension, host);
+
+       if (suri)
+               soup_uri_free (suri);
+}
+
+static const gchar *
+webdav_config_lookup_worker_get_display_name (EConfigLookupWorker *lookup_worker)
+{
+       return _("Look up for a CalDAV/CardDAV server");
+}
+
+static void
+webdav_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
+                                EConfigLookup *config_lookup,
+                                const ENamedParameters *params,
+                                ENamedParameters **out_restart_params,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+       ESource *dummy_source;
+       ESourceAuthentication *authentication_extension;
+       ESourceWebdav *webdav_extension;
+       ENamedParameters *credentials = NULL;
+       GSList *discovered_sources = NULL;
+       gchar *certificate_pem = NULL;
+       GTlsCertificateFlags certificate_errors = 0;
+       ETrustPromptResponse trust_response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+       GTlsCertificate *certificate = NULL;
+       gchar *email_address, *at_pos;
+       const gchar *servers;
+       GError *local_error = NULL;
+
+       g_return_if_fail (E_IS_WEBDAV_CONFIG_LOOKUP (lookup_worker));
+       g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
+       g_return_if_fail (params != NULL);
+       g_return_if_fail (out_restart_params != NULL);
+
+       email_address = g_strdup (e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS));
+
+       if (!email_address || !*email_address) {
+               g_free (email_address);
+               return;
+       }
+
+       if (e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_PASSWORD)) {
+               credentials = e_named_parameters_new ();
+
+               e_named_parameters_set (credentials, E_SOURCE_CREDENTIAL_PASSWORD,
+                       e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_PASSWORD));
+       }
+
+       *out_restart_params = e_named_parameters_new_clone (params);
+
+       dummy_source = e_source_new (NULL, NULL, NULL);
+       e_source_set_display_name (dummy_source, "Dummy Source");
+
+       webdav_extension = e_source_get_extension (dummy_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+       e_source_webdav_set_display_name (webdav_extension, "Dummy Source");
+
+       if (e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM) &&
+           e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST)) {
+               const gchar *certificate_pem;
+
+               certificate_pem = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM);
+               certificate = g_tls_certificate_new_from_pem (certificate_pem, -1, NULL);
+
+               if (certificate) {
+                       trust_response = e_config_lookup_decode_certificate_trust (
+                               e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST));
+               }
+       }
+
+       at_pos = strchr (email_address, '@');
+       if (at_pos)
+               *at_pos = '\0';
+
+       authentication_extension = e_source_get_extension (dummy_source, E_SOURCE_EXTENSION_AUTHENTICATION);
+       if (e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_USER))
+               e_source_authentication_set_user (authentication_extension, e_named_parameters_get (params, 
E_CONFIG_LOOKUP_PARAM_USER));
+       else
+               e_source_authentication_set_user (authentication_extension, email_address);
+
+       /* Do not try to guess from an email domain, it is almost always wrong. */
+       #if 0
+       if (at_pos && at_pos[1]) {
+               const gchar *host = at_pos + 1;
+               gchar *url;
+
+               /* Intentionally use the secure HTTP; users can override it with the Servers value */
+               url = g_strconcat ("https://";, host, NULL);
+
+               webdav_config_lookup_set_host_from_url (authentication_extension, host);
+
+               if (certificate && trust_response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
+                       e_source_webdav_update_ssl_trust (webdav_extension, host, certificate, 
trust_response);
+
+               if (e_webdav_discover_sources_sync (dummy_source, url,
+                       E_WEBDAV_DISCOVER_SUPPORTS_NONE, credentials, &certificate_pem, &certificate_errors,
+                       &discovered_sources, NULL, cancellable, &local_error)) {
+                       webdav_config_lookup_to_result (config_lookup, url, params,
+                               e_source_authentication_get_user (authentication_extension),
+                               e_source_webdav_get_ssl_trust (webdav_extension), discovered_sources);
+                       e_webdav_discover_free_discovered_sources (discovered_sources);
+                       discovered_sources = NULL;
+               } else if (webdav_config_lookup_propagate_error (error, local_error, certificate_pem, 
certificate_errors)) {
+                       if (certificate_pem) {
+                               e_named_parameters_set (*out_restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM, certificate_pem);
+                               e_named_parameters_set (*out_restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST,
+                                       e_source_authentication_get_host (authentication_extension));
+                       }
+
+                       e_named_parameters_free (credentials);
+                       g_clear_object (&dummy_source);
+                       g_clear_object (&certificate);
+                       g_free (certificate_pem);
+                       g_free (email_address);
+                       g_free (url);
+                       return;
+               } else {
+                       g_clear_error (&local_error);
+               }
+
+               g_free (url);
+       }
+
+       g_clear_pointer (&certificate_pem, g_free);
+       #endif
+
+       servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS);
+       if (servers && *servers) {
+               gchar **servers_strv;
+               gint ii;
+
+               servers_strv = g_strsplit (servers, ";", 0);
+
+               for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); 
ii++) {
+                       gchar *url;
+
+                       if (strstr (servers_strv[ii], "://")) {
+                               url = g_strdup (servers_strv[ii]);
+                       } else {
+                               /* Intentionally use secure HTTP; users can override it, if needed */
+                               url = g_strconcat ("https://";, servers_strv[ii], NULL);
+                       }
+
+                       webdav_config_lookup_set_host_from_url (authentication_extension, url);
+
+                       if (certificate && trust_response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
+                               e_source_webdav_update_ssl_trust (webdav_extension, 
e_source_authentication_get_host (authentication_extension), certificate, trust_response);
+
+                       if (e_webdav_discover_sources_sync (dummy_source, url,
+                               E_WEBDAV_DISCOVER_SUPPORTS_NONE, credentials, &certificate_pem, 
&certificate_errors,
+                               &discovered_sources, NULL, cancellable, &local_error)) {
+                               webdav_config_lookup_to_result (config_lookup, url, params,
+                                       e_source_authentication_get_user (authentication_extension),
+                                       e_source_webdav_get_ssl_trust (webdav_extension), discovered_sources);
+                               e_webdav_discover_free_discovered_sources (discovered_sources);
+                               discovered_sources = NULL;
+                       } else if (webdav_config_lookup_propagate_error (error, local_error, certificate_pem, 
certificate_errors)) {
+                               if (certificate_pem) {
+                                       e_named_parameters_set (*out_restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM, certificate_pem);
+                                       e_named_parameters_set (*out_restart_params, 
E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST,
+                                               e_source_authentication_get_host (authentication_extension));
+                               }
+                               g_free (url);
+                               break;
+                       } else {
+                               g_clear_error (&local_error);
+                       }
+
+                       g_clear_pointer (&certificate_pem, g_free);
+                       g_free (url);
+               }
+
+               g_clear_pointer (&certificate_pem, g_free);
+               g_strfreev (servers_strv);
+       }
+
+       e_named_parameters_free (credentials);
+       g_clear_object (&dummy_source);
+       g_clear_object (&certificate);
+       g_free (email_address);
+}
+
+static void
+webdav_config_lookup_constructed (GObject *object)
+{
+       EConfigLookup *config_lookup;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_webdav_config_lookup_parent_class)->constructed (object);
+
+       config_lookup = E_CONFIG_LOOKUP (e_extension_get_extensible (E_EXTENSION (object)));
+
+       e_config_lookup_register_worker (config_lookup, E_CONFIG_LOOKUP_WORKER (object));
+}
+
+static void
+e_webdav_config_lookup_class_init (EWebDAVConfigLookupClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = webdav_config_lookup_constructed;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_CONFIG_LOOKUP;
+}
+
+static void
+e_webdav_config_lookup_class_finalize (EWebDAVConfigLookupClass *class)
+{
+}
+
+static void
+webdav_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface)
+{
+       iface->get_display_name = webdav_config_lookup_worker_get_display_name;
+       iface->run = webdav_config_lookup_worker_run;
+}
+
+static void
+e_webdav_config_lookup_init (EWebDAVConfigLookup *extension)
+{
+}
+
+void
+e_webdav_config_lookup_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_webdav_config_lookup_register_type (type_module);
+}
diff --git a/src/modules/config-lookup/e-webdav-config-lookup.h 
b/src/modules/config-lookup/e-webdav-config-lookup.h
new file mode 100644
index 0000000..f6b6338
--- /dev/null
+++ b/src/modules/config-lookup/e-webdav-config-lookup.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_WEBDAV_CONFIG_LOOKUP_H
+#define E_WEBDAV_CONFIG_LOOKUP_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_webdav_config_lookup_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_CONFIG_LOOKUP_H */
diff --git a/src/modules/mail/e-mail-shell-backend.c b/src/modules/mail/e-mail-shell-backend.c
index 78bb8dc..0187868 100644
--- a/src/modules/mail/e-mail-shell-backend.c
+++ b/src/modules/mail/e-mail-shell-backend.c
@@ -914,7 +914,7 @@ mail_shell_backend_start (EShellBackend *shell_backend)
        }
 }
 
-static void
+static GtkWidget *
 mail_shell_backend_new_account_default (EMailShellBackend *mail_shell_backend,
                                        GtkWindow *parent)
 {
@@ -922,13 +922,13 @@ mail_shell_backend_new_account_default (EMailShellBackend *mail_shell_backend,
        EMailBackend *backend;
        EMailSession *session;
 
-       g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
+       g_return_val_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend), NULL);
 
        assistant = mail_shell_backend->priv->assistant;
 
        if (assistant != NULL) {
                gtk_window_present (GTK_WINDOW (assistant));
-               return;
+               return assistant;
        }
 
        backend = E_MAIL_BACKEND (mail_shell_backend);
@@ -945,6 +945,8 @@ mail_shell_backend_new_account_default (EMailShellBackend *mail_shell_backend,
        g_object_add_weak_pointer (
                G_OBJECT (mail_shell_backend->priv->assistant),
                &mail_shell_backend->priv->assistant);
+
+       return assistant;
 }
 
 static void
@@ -1109,6 +1111,8 @@ e_mail_shell_backend_class_init (EMailShellBackendClass *class)
         *
         * Opens wizard to create a new mail account.
         *
+        * Returns: The new mail account assistant widget
+        *
         * Since: 3.26
         **/
        signals[NEW_ACCOUNT] = g_signal_new (
@@ -1118,7 +1122,7 @@ e_mail_shell_backend_class_init (EMailShellBackendClass *class)
                G_STRUCT_OFFSET (EMailShellBackendClass, new_account),
                NULL, NULL,
                NULL,
-               G_TYPE_NONE, 1,
+               GTK_TYPE_WIDGET, 1,
                GTK_TYPE_WINDOW);
 
        /**
@@ -1163,14 +1167,18 @@ e_mail_shell_backend_type_register (GTypeModule *type_module)
        e_mail_shell_backend_register_type (type_module);
 }
 
-void
+GtkWidget *
 e_mail_shell_backend_new_account (EMailShellBackend *mail_shell_backend,
                                  GtkWindow *parent)
 {
-       g_return_if_fail (mail_shell_backend != NULL);
-       g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
+       GtkWidget *assistant = NULL;
+
+       g_return_val_if_fail (mail_shell_backend != NULL, NULL);
+       g_return_val_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend), NULL);
+
+       g_signal_emit (mail_shell_backend, signals[NEW_ACCOUNT], 0, parent, &assistant);
 
-       g_signal_emit (mail_shell_backend, signals[NEW_ACCOUNT], 0, parent);
+       return assistant;
 }
 
 void
diff --git a/src/modules/mail/e-mail-shell-backend.h b/src/modules/mail/e-mail-shell-backend.h
index 8941919..79e0694 100644
--- a/src/modules/mail/e-mail-shell-backend.h
+++ b/src/modules/mail/e-mail-shell-backend.h
@@ -58,7 +58,7 @@ struct _EMailShellBackendClass {
        EMailBackendClass parent_class;
 
        /* Signals */
-       void            (* new_account)         (EMailShellBackend *mail_shell_backend,
+       GtkWidget *     (* new_account)         (EMailShellBackend *mail_shell_backend,
                                                 GtkWindow *parent);
        void            (* edit_account)        (EMailShellBackend *mail_shell_backend,
                                                 GtkWindow *parent,
@@ -69,7 +69,7 @@ GType         e_mail_shell_backend_get_type   (void);
 void           e_mail_shell_backend_type_register
                                                (GTypeModule *type_module);
 
-void           e_mail_shell_backend_new_account
+GtkWidget *    e_mail_shell_backend_new_account
                                                (EMailShellBackend *mail_shell_backend,
                                                 GtkWindow *parent);
 void           e_mail_shell_backend_edit_account


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]