"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/configuration/e-mail-config-mapi-backend.c" (2 Dec 2022, 27632 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "e-mail-config-mapi-backend.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * e-mail-config-mapi-backend.c
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU Lesser General Public
    6  * License as published by the Free Software Foundation; either
    7  * version 2 of the License, or (at your option) version 3.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12  * Lesser General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU Lesser General Public
   15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   16  *
   17  */
   18 
   19 #include "evolution-mapi-config.h"
   20 
   21 #include <glib/gi18n-lib.h>
   22 
   23 #include <camel/camel.h>
   24 #include <libebackend/libebackend.h>
   25 #include <libedataserver/libedataserver.h>
   26 
   27 #include <mail/e-mail-config-auth-check.h>
   28 #include <mail/e-mail-config-receiving-page.h>
   29 #include <shell/e-shell.h>
   30 
   31 #include "camel-mapi-settings.h"
   32 #include "e-mapi-folder.h"
   33 #include "e-mapi-connection.h"
   34 #include "e-mapi-utils.h"
   35 #include "e-mapi-config-utils.h"
   36 
   37 #include "e-mail-config-mapi-backend.h"
   38 
   39 struct _EMailConfigMapiBackendPrivate {
   40     gint unused;
   41 };
   42 
   43 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
   44     EMailConfigMapiBackend,
   45     e_mail_config_mapi_backend,
   46     E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
   47     0,
   48     G_ADD_PRIVATE_DYNAMIC (EMailConfigMapiBackend))
   49 
   50 enum {
   51     COL_MAPI_FULL_NAME = 0,
   52     COL_MAPI_ACCOUNT,
   53     COL_MAPI_INDEX,
   54     COLS_MAX
   55 };
   56 
   57 static void
   58 tree_selection_changed (GtkTreeSelection *selection, GtkDialog *dialog)
   59 {
   60     gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, gtk_tree_selection_get_selected (selection, NULL, NULL));
   61 }
   62 
   63 static gboolean
   64 transform_security_method_to_boolean (GBinding *binding,
   65                                       const GValue *source_value,
   66                                       GValue *target_value,
   67                                       gpointer not_used)
   68 {
   69     CamelNetworkSecurityMethod security_method;
   70     gboolean use_ssl;
   71 
   72     security_method = g_value_get_enum (source_value);
   73     use_ssl = (security_method != CAMEL_NETWORK_SECURITY_METHOD_NONE);
   74     g_value_set_boolean (target_value, use_ssl);
   75 
   76     return TRUE;
   77 }
   78 
   79 static gboolean
   80 transform_boolean_to_security_method (GBinding *binding,
   81                                       const GValue *source_value,
   82                                       GValue *target_value,
   83                                       gpointer not_used)
   84 {
   85     CamelNetworkSecurityMethod security_method;
   86     gboolean use_ssl;
   87 
   88     use_ssl = g_value_get_boolean (source_value);
   89     if (use_ssl)
   90         security_method = CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT;
   91     else
   92         security_method = CAMEL_NETWORK_SECURITY_METHOD_NONE;
   93     g_value_set_enum (target_value, security_method);
   94 
   95     return TRUE;
   96 }
   97 
   98 struct ECreateProfileData
   99 {
  100     const gchar *username;
  101     struct PropertyRowSet_r *rowset;
  102     gint index;
  103     EFlag *flag;
  104 };
  105 
  106 static gboolean
  107 create_profile_callback_in_main (gpointer user_data)
  108 {
  109     struct ECreateProfileData *cpd = user_data;
  110     gint response;
  111     gint i, index = 0;
  112     GtkTreeIter iter;
  113     GtkListStore *store;
  114     GtkCellRenderer *renderer;
  115     GtkTreeSelection *selection;
  116     GtkWidget *dialog, *view;
  117     GtkBox *content_area;
  118 
  119     g_return_val_if_fail (cpd != NULL, FALSE);
  120 
  121     dialog = gtk_dialog_new_with_buttons (_("Select username"),
  122                           NULL, GTK_DIALOG_MODAL,
  123                           GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
  124                           GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
  125                           NULL);
  126 
  127     /*Tree View */
  128     view = gtk_tree_view_new ();
  129     renderer = gtk_cell_renderer_text_new ();
  130     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
  131                              -1, _("Full name"), renderer,
  132                              "text", COL_MAPI_FULL_NAME, NULL);
  133 
  134     renderer = gtk_cell_renderer_text_new ();
  135     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
  136                              -1, _("Username"), renderer,
  137                              "text", COL_MAPI_ACCOUNT, NULL);
  138 
  139     gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), TRUE);
  140     gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 1), TRUE);
  141 
  142     /* Model for TreeView */
  143     store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
  144     gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store));
  145 
  146     for (i = 0; i < cpd->rowset->cRows; i++) {
  147         const gchar *fullname = e_mapi_util_find_propertyrow_propval (&(cpd->rowset->aRow[i]), PidTagDisplayName);
  148         const gchar *account = e_mapi_util_find_propertyrow_propval (&(cpd->rowset->aRow[i]), PidTagAccount);
  149 
  150         if (fullname && account) {
  151             gtk_list_store_append (store, &iter);
  152             /* Preserve the index inside the store*/
  153             gtk_list_store_set (store, &iter,
  154                         COL_MAPI_FULL_NAME, fullname,
  155                         COL_MAPI_ACCOUNT, account,
  156                         COL_MAPI_INDEX, i, -1);
  157         }
  158     }
  159 
  160     /* Pack the TreeView into dialog's content area */
  161     content_area = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
  162 
  163     gtk_box_pack_start (content_area, gtk_label_new (_("There are more users with similar user name on a server.\nPlease select that you would like to use from the below list.")), TRUE, TRUE, 6);
  164     gtk_box_pack_start (content_area, view, TRUE, TRUE, 6);
  165 
  166     gtk_widget_show_all (GTK_WIDGET (content_area));
  167 
  168     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
  169     g_signal_connect (selection, "changed", G_CALLBACK (tree_selection_changed), dialog);
  170     tree_selection_changed (selection, GTK_DIALOG (dialog));
  171 
  172     response = gtk_dialog_run (GTK_DIALOG (dialog));
  173     if (response == GTK_RESPONSE_ACCEPT) {
  174            /* Get the index from the selected value */
  175         if (gtk_tree_selection_get_selected (selection, NULL, &iter))
  176             gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_MAPI_INDEX, &index, -1);
  177         else
  178             index = cpd->rowset->cRows + 1;
  179     } else /* If we return a value > available, we are canceling the login.*/
  180            index = cpd->rowset->cRows + 1;
  181 
  182     gtk_widget_destroy (dialog);
  183 
  184     cpd->index = index;
  185     e_flag_set (cpd->flag);
  186 
  187     return FALSE;
  188 }
  189 
  190 /* Callback for ProcessNetworkProfile. If we have more than one username,
  191  we need to let the user select. */
  192 static gint
  193 create_profile_callback_in_thread (struct PropertyRowSet_r *rowset,
  194                    gconstpointer data)
  195 {
  196     struct ECreateProfileData cpd;
  197     const gchar *username = (const gchar *) data;
  198     gint i;
  199 
  200     /* If we can find the exact username, then find & return its index. */
  201     for (i = 0; i < rowset->cRows; i++) {
  202         const gchar *account = e_mapi_util_find_propertyrow_propval (&(rowset->aRow[i]), PidTagAccount);
  203 
  204         if (account && g_strcmp0 (username, account) == 0)
  205             return i;
  206     }
  207 
  208     cpd.username = username;
  209     cpd.rowset = rowset;
  210     cpd.index = -1;
  211     cpd.flag = e_flag_new ();
  212 
  213     g_timeout_add (100, create_profile_callback_in_main, &cpd);
  214 
  215     e_flag_wait (cpd.flag);
  216     e_flag_free (cpd.flag);
  217 
  218     return cpd.index;
  219 }
  220 
  221 static gboolean
  222 validate_credentials_test (ESourceRegistry *registry,
  223                EMapiProfileData *empd,
  224                CamelMapiSettings *mapi_settings,
  225                GCancellable *cancellable,
  226                GError **perror)
  227 {
  228     gboolean status, success = FALSE;
  229     struct mapi_context *mapi_ctx = NULL;
  230 
  231     status = e_mapi_utils_create_mapi_context (&mapi_ctx, perror);
  232     status = status && e_mapi_create_profile (mapi_ctx, empd, create_profile_callback_in_thread, empd->username, NULL, perror);
  233     if (status && !g_cancellable_is_cancelled (cancellable)) {
  234         /* profile was created, try to connect to the server */
  235         EMapiConnection *conn;
  236         gchar *profname;
  237 
  238         status = FALSE;
  239         profname = e_mapi_util_profile_name (mapi_ctx, empd, FALSE);
  240 
  241         conn = e_mapi_connection_new (registry, profname, empd->credentials, cancellable, perror);
  242         if (conn) {
  243             status = e_mapi_connection_connected (conn);
  244             g_object_unref (conn);
  245         }
  246 
  247         g_free (profname);
  248     }
  249 
  250     if (status) {
  251         /* Things are successful */
  252         gchar *profname = NULL;
  253 
  254         profname = e_mapi_util_profile_name (mapi_ctx, empd, FALSE);
  255         camel_mapi_settings_set_profile (mapi_settings, profname);
  256         g_free (profname);
  257 
  258         success = TRUE;
  259     }
  260 
  261     e_mapi_utils_destroy_mapi_context (mapi_ctx);
  262 
  263     return success;
  264 }
  265 
  266 typedef struct _TryCredentialsData {
  267     gchar *username;
  268     gchar *domain;
  269     gchar *server;
  270     gboolean use_ssl;
  271     gboolean krb_sso;
  272     gchar *krb_realm;
  273     CamelMapiSettings *mapi_settings;
  274     EMailConfigServiceBackend *backend;
  275     gboolean success;
  276 } TryCredentialsData;
  277 
  278 static void
  279 try_credentials_data_free (gpointer ptr)
  280 {
  281     TryCredentialsData *data = ptr;
  282 
  283     if (data) {
  284         g_free (data->username);
  285         g_free (data->domain);
  286         g_free (data->server);
  287         g_free (data->krb_realm);
  288         g_object_unref (data->mapi_settings);
  289         g_object_unref (data->backend);
  290         g_slice_free (TryCredentialsData, data);
  291     }
  292 }
  293 
  294 static gboolean
  295 mail_config_mapi_try_credentials_sync (ECredentialsPrompter *prompter,
  296                        ESource *source,
  297                        const ENamedParameters *credentials,
  298                        gboolean *out_authenticated,
  299                        gpointer user_data,
  300                        GCancellable *cancellable,
  301                        GError **error)
  302 {
  303     TryCredentialsData *data = user_data;
  304     EMailConfigServicePage *page;
  305     ESourceRegistry *registry;
  306     EMapiProfileData empd = { 0 };
  307     GError *mapi_error = NULL;
  308 
  309     empd.username = data->username;
  310     empd.domain = data->domain;
  311     empd.server = data->server;
  312     empd.credentials = (ENamedParameters *) credentials;
  313     empd.use_ssl = data->use_ssl;
  314     empd.krb_sso = data->krb_sso;
  315     empd.krb_realm = data->krb_realm;
  316 
  317     page = e_mail_config_service_backend_get_page (data->backend);
  318     registry = e_mail_config_service_page_get_registry (page);
  319 
  320     data->success = validate_credentials_test (
  321         registry,
  322         &empd, 
  323         data->mapi_settings,
  324         cancellable,
  325         &mapi_error);
  326 
  327     if (mapi_error) {
  328         gboolean is_network_error = mapi_error && mapi_error->domain != E_MAPI_ERROR;
  329 
  330         g_warn_if_fail (!data->success);
  331         data->success = FALSE;
  332 
  333         if (is_network_error)
  334             g_propagate_error (error, mapi_error);
  335         else
  336             g_clear_error (&mapi_error);
  337 
  338         return is_network_error ? FALSE : TRUE;
  339     }
  340 
  341     g_warn_if_fail (data->success);
  342 
  343     *out_authenticated = data->success;
  344 
  345     return TRUE;
  346 }
  347 
  348 static void
  349 validate_credentials_idle (GObject *button,
  350                gpointer user_data,
  351                GCancellable *cancellable,
  352                GError **perror)
  353 {
  354     TryCredentialsData *data = user_data;
  355 
  356     g_return_if_fail (data != NULL);
  357 
  358     if (data->success)
  359         e_notice (NULL, GTK_MESSAGE_INFO, "%s", _("Authentication finished successfully."));
  360     else
  361         e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Authentication failed."));
  362 }
  363 
  364 static void
  365 validate_credentials_thread (GObject *button,
  366                  gpointer user_data,
  367                  GCancellable *cancellable,
  368                  GError **perror)
  369 {
  370     TryCredentialsData *data = user_data;
  371     EMailConfigServicePage *page;
  372     ESourceRegistry *registry;
  373 
  374     g_return_if_fail (data != NULL);
  375 
  376     page = e_mail_config_service_backend_get_page (data->backend);
  377     registry = e_mail_config_service_page_get_registry (page);
  378 
  379     if (data->krb_sso) {
  380         GError *krb_error = NULL, *local_error = NULL;
  381         EMapiProfileData empd = { 0 };
  382 
  383         empd.username = data->username;
  384         empd.domain = data->domain;
  385         empd.server = data->server;
  386         empd.use_ssl = data->use_ssl;
  387         empd.krb_sso = data->krb_sso;
  388         empd.krb_realm = data->krb_realm;
  389 
  390         e_mapi_util_trigger_krb_auth (&empd, &krb_error);
  391 
  392         data->success = validate_credentials_test (
  393             registry,
  394             &empd, 
  395             data->mapi_settings,
  396             cancellable,
  397             &local_error);
  398 
  399         if (!data->success) {
  400             if (krb_error && local_error) {
  401                 GError *new_error = g_error_new (local_error->domain, local_error->code,
  402                     /* Translators: the first '%s' is replaced with a generic error message,
  403                        the second '%s' is replaced with additional error information. */
  404                     C_("gssapi_error", "%s (%s)"), local_error->message, krb_error->message);
  405                 g_propagate_error (perror, new_error);
  406             } else if (krb_error) {
  407                 g_propagate_error (perror, krb_error);
  408                 krb_error = NULL;
  409             } else if (local_error) {
  410                 g_propagate_error (perror, local_error);
  411                 local_error = NULL;
  412             }
  413         }
  414 
  415         g_clear_error (&krb_error);
  416         g_clear_error (&local_error);
  417     } else {
  418         EShell *shell;
  419         ESource *source;
  420 
  421         shell = e_shell_get_default ();
  422         source = e_mail_config_service_backend_get_source (data->backend);
  423 
  424         e_credentials_prompter_loop_prompt_sync (e_shell_get_credentials_prompter (shell),
  425             source, E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE,
  426             mail_config_mapi_try_credentials_sync, data, cancellable, perror);
  427     }
  428 }
  429 
  430 static void
  431 validate_credentials_cb (GtkWidget *widget,
  432              EMailConfigServiceBackend *backend)
  433 {
  434     EMapiProfileData empd = { 0 };
  435     CamelSettings *settings;
  436     CamelMapiSettings *mapi_settings;
  437     CamelNetworkSettings *network_settings;
  438     const gchar *host;
  439     const gchar *user;
  440 
  441     if (!e_mapi_config_utils_is_online ()) {
  442         e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Cannot authenticate MAPI accounts in offline mode"));
  443         return;
  444     }
  445 
  446     settings = e_mail_config_service_backend_get_settings (backend);
  447     mapi_settings = CAMEL_MAPI_SETTINGS (settings);
  448     network_settings = CAMEL_NETWORK_SETTINGS (settings);
  449 
  450     host = camel_network_settings_get_host (network_settings);
  451     user = camel_network_settings_get_user (network_settings);
  452 
  453     /* Silently remove domain part from a username when user enters it as such.
  454        This change will be visible in the UI on new edit open. */
  455     if (user != NULL && strchr (user, '\\') != NULL) {
  456         gchar *at;
  457 
  458         at = strrchr (user, '\\') + 1;
  459         camel_network_settings_set_user (network_settings, at);
  460         user = camel_network_settings_get_user (network_settings);
  461     }
  462 
  463     empd.server = host;
  464     empd.username = user;
  465     e_mapi_util_profiledata_from_settings (&empd, mapi_settings);
  466 
  467     if (!empd.username || !*(empd.username)
  468         || !empd.server || !*(empd.server)
  469         || ((!empd.domain || !*(empd.domain))
  470         && !empd.krb_sso)) {
  471         e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Server, username and domain name cannot be empty. Please fill them with correct values."));
  472         return;
  473     } else if (empd.krb_sso && (!empd.krb_realm || !*(empd.krb_realm))) {
  474         e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Realm name cannot be empty when kerberos is selected. Please fill them with correct values."));
  475         return;
  476     }
  477 
  478     if (COMPLETE_PROFILEDATA (&empd)) {
  479         TryCredentialsData *data = g_slice_new0 (TryCredentialsData);
  480 
  481         data->username = g_strdup (empd.username);
  482         data->domain = g_strdup (empd.domain);
  483         data->server = g_strdup (empd.server);
  484         data->use_ssl = empd.use_ssl;
  485         data->krb_sso = empd.krb_sso;
  486         data->krb_realm = g_strdup (empd.krb_realm);
  487         data->mapi_settings = g_object_ref (mapi_settings);
  488         data->backend = g_object_ref (backend);
  489         data->success = FALSE;
  490 
  491         e_mapi_config_utils_run_in_thread_with_feedback_modal (e_mapi_config_utils_get_widget_toplevel_window (widget),
  492             G_OBJECT (widget),
  493             _("Connecting to the server, please wait…"),
  494             validate_credentials_thread,
  495             validate_credentials_idle,
  496             data,
  497             try_credentials_data_free);
  498     } else {
  499         e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Authentication failed."));
  500     }
  501 
  502     g_warn_if_fail (empd.credentials == NULL);
  503 }
  504 
  505 static ESource *
  506 mail_config_mapi_backend_new_collection (EMailConfigServiceBackend *backend)
  507 {
  508     EMailConfigServiceBackendClass *class;
  509     ESourceBackend *extension;
  510     ESource *source;
  511     const gchar *extension_name;
  512 
  513     /* This backend serves double duty.  One instance holds the
  514      * mail account source, another holds the mail transport source.
  515      * We can differentiate by examining the EMailConfigServicePage
  516      * the backend is associated with.  We return a new collection
  517      * for both the Receiving Page and Sending Page.  Although the
  518      * Sending Page instance ultimately gets discarded, it's still
  519      * needed to avoid creating a [Mapi Backend] extension in the
  520      * mail transport source. */
  521 
  522     class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
  523 
  524     source = e_source_new (NULL, NULL, NULL);
  525     extension_name = E_SOURCE_EXTENSION_COLLECTION;
  526     extension = e_source_get_extension (source, extension_name);
  527     e_source_backend_set_backend_name (extension, class->backend_name);
  528 
  529     return source;
  530 }
  531 
  532 static GHashTable *
  533 get_kerberos_realms (void)
  534 {
  535     GFile *file;
  536     GHashTable *realms = NULL;
  537 
  538     file = g_file_new_for_path ("/etc/krb5.conf");
  539     
  540     if (file) {
  541         GFileInputStream *input_stream = g_file_read (file, NULL, NULL);
  542 
  543         if (input_stream) {
  544             GDataInputStream *data = g_data_input_stream_new (G_INPUT_STREAM (input_stream));
  545 
  546             if (data) {
  547                 gchar *line;
  548                 gboolean in_domain_realm = FALSE;
  549 
  550                 while (line = g_data_input_stream_read_line_utf8 (data, NULL, NULL, NULL), line) {
  551                     g_strstrip (line);
  552 
  553                     if (line [0] == '[') {
  554                         if (in_domain_realm) {
  555                             g_free (line);
  556                             break;
  557                         }
  558 
  559                         if (g_str_equal (line, "[domain_realm]"))
  560                             in_domain_realm = TRUE;
  561                     } else if (in_domain_realm) {
  562                         gchar **split = g_strsplit (line, "=", 2);
  563 
  564                         if (split && split[0] && split[1] && !split[2]) {
  565                             g_strstrip (split[0]);
  566                             g_strstrip (split[1]);
  567 
  568                             if (split[0][0] && split[1][0]) {
  569                                 if (!realms)
  570                                     realms = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  571 
  572                                 g_hash_table_insert (realms, g_strdup (split[0]), g_strdup (split[1]));
  573                             }
  574                         }
  575 
  576                         g_strfreev (split);
  577                     }
  578 
  579                     g_free (line);
  580                 }
  581 
  582                 g_object_unref (data);
  583             }
  584 
  585             g_object_unref (input_stream);
  586         }
  587 
  588         g_object_unref (file);
  589     }
  590 
  591     return realms;
  592 }
  593 
  594 static const gchar *
  595 find_in_realms (GHashTable *realms, const gchar *domain)
  596 {
  597     GHashTableIter iter;
  598     gpointer key, value;
  599 
  600     g_return_val_if_fail (realms != NULL, NULL);
  601     g_return_val_if_fail (domain != NULL, NULL);
  602 
  603     if (!*domain)
  604         return NULL;
  605 
  606     g_hash_table_iter_init (&iter, realms);
  607     while (g_hash_table_iter_next (&iter, &key, &value)) {
  608         if (g_ascii_strcasecmp (domain, key) == 0)
  609             return value;
  610     }
  611 
  612     return NULL;
  613 }
  614 
  615 static void
  616 kerberos_toggled_cb (GtkWidget *check_button,
  617              GParamSpec *param,
  618              CamelMapiSettings *settings)
  619 {
  620     gchar *host;
  621 
  622     if (!camel_mapi_settings_get_kerberos (settings))
  623         return;
  624 
  625     host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings));
  626 
  627     if (host && *host) {
  628         /* guess realm from /etc/krb5.conf, if available;
  629            it's just trying to be nice to the user, no big deal if this fails */
  630         GHashTable *realms;
  631 
  632         realms = get_kerberos_realms ();
  633         if (realms) {
  634             const gchar *dot;
  635 
  636             dot = host;
  637             while (dot) {
  638                 const gchar *realm;
  639 
  640                 realm = find_in_realms (realms, dot);
  641                 if (realm && *realm) {
  642                     camel_mapi_settings_set_realm (settings, realm);
  643                     break;
  644                 }
  645 
  646                 dot = *dot ? strchr (dot + 1, '.') : NULL;
  647             }
  648 
  649             g_hash_table_destroy (realms);
  650         }
  651     }
  652 
  653     g_free (host);
  654 }
  655 
  656 static void
  657 mail_config_mapi_backend_insert_widgets (EMailConfigServiceBackend *backend,
  658                      GtkBox *parent)
  659 {
  660     EMailConfigServicePage *page;
  661     ESource *source;
  662     ESourceExtension *extension;
  663     CamelSettings *settings;
  664     GtkWidget *hgrid = NULL;
  665     GtkWidget *label, *entry;
  666     GtkWidget *auth_button;
  667     GtkWidget *secure_conn;
  668     GtkWidget *krb_sso;
  669     GtkGrid *content_grid;
  670     gchar *markup;
  671     gint irow;
  672 
  673     page = e_mail_config_service_backend_get_page (backend);
  674 
  675     /* This backend serves double duty.  One instance holds the
  676      * mail account source, another holds the mail transport source.
  677      * We can differentiate by examining the EMailConfigServicePage
  678      * the backend is associated with.  This method only applies to
  679      * the Receiving Page. */
  680     if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
  681         return;
  682 
  683     /* This needs to come _after_ the page type check so we don't
  684      * introduce a backend extension in the mail transport source. */
  685     settings = e_mail_config_service_backend_get_settings (backend);
  686 
  687     content_grid = GTK_GRID (gtk_grid_new ());
  688     gtk_widget_set_margin_left (GTK_WIDGET (content_grid), 12);
  689     gtk_grid_set_row_spacing (content_grid, 6);
  690     gtk_grid_set_column_spacing (content_grid, 6);
  691     gtk_box_pack_start (GTK_BOX (parent), GTK_WIDGET (content_grid), FALSE, FALSE, 0);
  692 
  693     irow = 0;
  694 
  695     markup = g_markup_printf_escaped ("<b>%s</b>", _("Configuration"));
  696     label = gtk_label_new (markup);
  697     gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  698     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  699     g_free (markup);
  700 
  701     gtk_grid_attach (content_grid, label, 0, irow, 2, 1);
  702     irow++;
  703 
  704     label = gtk_label_new_with_mnemonic (_("_Server:"));
  705     gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  706 
  707     entry = gtk_entry_new ();
  708     gtk_widget_set_hexpand (entry, TRUE);
  709     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  710 
  711     e_binding_bind_object_text_property (
  712         settings, "host",
  713         entry, "text",
  714         G_BINDING_BIDIRECTIONAL |
  715         G_BINDING_SYNC_CREATE);
  716 
  717     gtk_grid_attach (content_grid, label, 0, irow, 1, 1);
  718     gtk_grid_attach (content_grid, entry, 1, irow, 1, 1);
  719     irow++;
  720 
  721     label = gtk_label_new_with_mnemonic (_("User_name:"));
  722     gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  723 
  724     entry = gtk_entry_new ();
  725     gtk_widget_set_hexpand (entry, TRUE);
  726     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  727 
  728     e_binding_bind_object_text_property (
  729         settings, "user",
  730         entry, "text",
  731         G_BINDING_BIDIRECTIONAL |
  732         G_BINDING_SYNC_CREATE);
  733 
  734     gtk_grid_attach (content_grid, label, 0, irow, 1, 1);
  735     gtk_grid_attach (content_grid, entry, 1, irow, 1, 1);
  736     irow++;
  737 
  738     /* Domain name & Authenticate Button */
  739     hgrid = g_object_new (GTK_TYPE_GRID,
  740         "column-homogeneous", FALSE,
  741         "column-spacing", 6,
  742         "orientation", GTK_ORIENTATION_HORIZONTAL,
  743         NULL);
  744     gtk_widget_set_hexpand (hgrid, TRUE);
  745 
  746     label = gtk_label_new_with_mnemonic (_("_Domain name:"));
  747     gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  748 
  749     entry = gtk_entry_new ();
  750     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  751     gtk_widget_set_hexpand (entry, TRUE);
  752     gtk_container_add (GTK_CONTAINER (hgrid), entry);
  753     e_binding_bind_object_text_property (
  754         settings, "domain",
  755         entry, "text",
  756         G_BINDING_BIDIRECTIONAL |
  757         G_BINDING_SYNC_CREATE);
  758 
  759     auth_button = gtk_button_new_with_mnemonic (_("_Authenticate"));
  760     gtk_container_add (GTK_CONTAINER (hgrid), auth_button);
  761     g_signal_connect (auth_button, "clicked",  G_CALLBACK (validate_credentials_cb), backend);
  762 
  763     gtk_grid_attach (content_grid, label, 0, irow, 1, 1);
  764     gtk_grid_attach (content_grid, hgrid, 1, irow, 1, 1);
  765     irow++;
  766 
  767     secure_conn = gtk_check_button_new_with_mnemonic (_("_Use secure connection"));
  768     gtk_widget_set_hexpand (secure_conn, TRUE);
  769     
  770     gtk_grid_attach (content_grid, secure_conn, 1, irow, 1, 1);
  771     irow++;
  772 
  773     e_binding_bind_property_full (
  774         settings, "security-method",
  775         secure_conn, "active",
  776         G_BINDING_BIDIRECTIONAL |
  777         G_BINDING_SYNC_CREATE,
  778         transform_security_method_to_boolean,
  779         transform_boolean_to_security_method,
  780         NULL, NULL);
  781 
  782     krb_sso = gtk_check_button_new_with_mnemonic (_("_Kerberos authentication"));
  783     gtk_widget_set_hexpand (secure_conn, TRUE);
  784 
  785     e_binding_bind_property (
  786         settings, "kerberos",
  787         krb_sso, "active",
  788         G_BINDING_BIDIRECTIONAL |
  789         G_BINDING_SYNC_CREATE);
  790 
  791     gtk_grid_attach (content_grid, krb_sso, 1, irow, 1, 1);
  792     irow++;
  793 
  794     label = gtk_label_new_with_mnemonic (_("_Realm name:"));
  795     gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  796 
  797     e_binding_bind_property (
  798         settings, "kerberos",
  799         label, "sensitive",
  800         G_BINDING_SYNC_CREATE);
  801 
  802     g_signal_connect_object (settings, "notify::kerberos",
  803         G_CALLBACK (kerberos_toggled_cb), settings,
  804         G_CONNECT_AFTER);
  805 
  806     entry = gtk_entry_new ();
  807     gtk_widget_set_hexpand (entry, TRUE);
  808     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  809 
  810     e_binding_bind_object_text_property (
  811         settings, "realm",
  812         entry, "text",
  813         G_BINDING_BIDIRECTIONAL |
  814         G_BINDING_SYNC_CREATE);
  815 
  816     e_binding_bind_property (
  817         settings, "kerberos",
  818         entry, "sensitive",
  819         G_BINDING_SYNC_CREATE);
  820 
  821     gtk_grid_attach (content_grid, label, 0, irow, 1, 1);
  822     gtk_grid_attach (content_grid, entry, 1, irow, 1, 1);
  823 
  824     source = e_mail_config_service_backend_get_collection (backend);
  825     extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
  826 
  827     /* The collection identity is the user name. */
  828     e_binding_bind_property (
  829         settings, "user",
  830         extension, "identity",
  831         G_BINDING_BIDIRECTIONAL |
  832         G_BINDING_SYNC_CREATE);
  833 
  834     gtk_widget_show_all (GTK_WIDGET (content_grid));
  835 }
  836 
  837 static void
  838 mail_config_mapi_backend_setup_defaults (EMailConfigServiceBackend *backend)
  839 {
  840     CamelSettings *settings;
  841     EMailConfigServicePage *page;
  842     const gchar *email_address;
  843     gchar **parts = NULL;
  844 
  845     page = e_mail_config_service_backend_get_page (backend);
  846 
  847     /* This backend serves double duty.  One instance holds the
  848      * mail account source, another holds the mail transport source.
  849      * We can differentiate by examining the EMailConfigServicePage
  850      * the backend is associated with.  This method only applies to
  851      * the Receiving Page. */
  852     if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
  853         return;
  854 
  855     /* This needs to come _after_ the page type check so we don't
  856      * introduce a backend extension in the mail transport source. */
  857     settings = e_mail_config_service_backend_get_settings (backend);
  858 
  859     email_address = e_mail_config_service_page_get_email_address (page);
  860     if (email_address != NULL)
  861         parts = g_strsplit (email_address, "@", 2);
  862 
  863     if (parts != NULL && g_strv_length (parts) >= 2) {
  864         CamelNetworkSettings *network_settings;
  865         gchar *host;
  866 
  867         g_strstrip (parts[0]);  /* user name */
  868         g_strstrip (parts[1]);  /* domain name */
  869 
  870         host = g_strdup_printf ("exchange.%s", parts[1]);
  871 
  872         network_settings = CAMEL_NETWORK_SETTINGS (settings);
  873         camel_network_settings_set_host (network_settings, host);
  874         camel_network_settings_set_user (network_settings, parts[0]);
  875 
  876         g_free (host);
  877     }
  878 
  879     g_strfreev (parts);
  880 }
  881 
  882 static gboolean
  883 mail_config_mapi_backend_check_complete (EMailConfigServiceBackend *backend)
  884 {
  885     EMailConfigServicePage *page;
  886     CamelSettings *settings;
  887     CamelMapiSettings *mapi_settings;
  888     const gchar *profile;
  889 
  890     page = e_mail_config_service_backend_get_page (backend);
  891 
  892     /* This backend serves double duty.  One instance holds the
  893      * mail account source, another holds the mail transport source.
  894      * We can differentiate by examining the EMailConfigServicePage
  895      * the backend is associated with.  This method only applies to
  896      * the Receiving Page. */
  897     if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
  898         return TRUE;
  899 
  900     /* This needs to come _after_ the page type check so we don't
  901      * introduce a backend extension in the mail transport source. */
  902     settings = e_mail_config_service_backend_get_settings (backend);
  903     mapi_settings = CAMEL_MAPI_SETTINGS (settings);
  904 
  905     /* We assume that if the profile is set, then the setting is valid. */
  906     profile = camel_mapi_settings_get_profile (mapi_settings);
  907 
  908     /* Profile not set. Do not proceed with account creation.*/
  909     return (profile != NULL && *profile != '\0');
  910 }
  911 
  912 static void
  913 e_mail_config_mapi_backend_class_init (EMailConfigMapiBackendClass *class)
  914 {
  915     EMailConfigServiceBackendClass *backend_class;
  916 
  917     backend_class = E_MAIL_CONFIG_SERVICE_BACKEND_CLASS (class);
  918     backend_class->backend_name = "mapi";
  919     backend_class->new_collection = mail_config_mapi_backend_new_collection;
  920     backend_class->insert_widgets = mail_config_mapi_backend_insert_widgets;
  921     backend_class->setup_defaults = mail_config_mapi_backend_setup_defaults;
  922     backend_class->check_complete = mail_config_mapi_backend_check_complete;
  923 }
  924 
  925 static void
  926 e_mail_config_mapi_backend_class_finalize (EMailConfigMapiBackendClass *class)
  927 {
  928 }
  929 
  930 static void
  931 e_mail_config_mapi_backend_init (EMailConfigMapiBackend *backend)
  932 {
  933     backend->priv = e_mail_config_mapi_backend_get_instance_private (backend);
  934 }
  935 
  936 void
  937 e_mail_config_mapi_backend_type_register (GTypeModule *type_module)
  938 {
  939     /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
  940      *     function, so we have to wrap it with a public function in
  941      *     order to register types from a separate compilation unit. */
  942     e_mail_config_mapi_backend_register_type (type_module);
  943 }