"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/configuration/e-mapi-subscribe-foreign-folder.c" (2 Dec 2022, 27023 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-mapi-subscribe-foreign-folder.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.44.2_vs_3.45.1.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Authors:
   18  *    Milan Crha <mcrha@redhat.com>
   19  *
   20  * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
   21  *
   22  */
   23 
   24 #include "evolution-mapi-config.h"
   25 
   26 #include <glib/gi18n-lib.h>
   27 #include <gtk/gtk.h>
   28 
   29 #include <libemail-engine/libemail-engine.h>
   30 
   31 #include "camel/camel-mapi-store.h"
   32 #include "camel/camel-mapi-store-summary.h"
   33 
   34 #include "e-mapi-config-utils.h"
   35 #include "e-mapi-search-gal-user.h"
   36 #include "e-mapi-subscribe-foreign-folder.h"
   37 #include "e-mapi-utils.h"
   38 
   39 #ifndef PidTagMailboxOwnerName
   40 #define PidTagMailboxOwnerName PR_USER_NAME_UNICODE
   41 #endif
   42 
   43 #define STR_ACCOUNTS_COMBO      "e-mapi-accounts-combo"
   44 #define STR_USER_NAME_SELECTOR_ENTRY    "e-mapi-name-selector-entry"
   45 #define STR_FOLDER_NAME_COMBO       "e-mapi-folder-name-combo"
   46 #define STR_SUBFOLDERS_CHECK        "e-mapi-subfolders-check"
   47 #define STR_MAPI_CAMEL_SESSION      "e-mapi-camel-session"
   48 #define STR_MAPI_DIRECT_USER_NAME   "e-mapi-direct-user-name"
   49 
   50 enum {
   51     COLUMN_UID = 0,
   52     COLUMN_DISPLAY_NAME,
   53     COLUMN_STORE
   54 };
   55 
   56 static gboolean
   57 add_foreign_folder_to_camel (CamelMapiStore *mapi_store,
   58                  const gchar *foreign_username,
   59                  mapi_id_t folder_id,
   60                  mapi_id_t parent_fid,
   61                  gboolean include_subfolders,
   62                  const gchar *display_username,
   63                  const gchar *display_foldername,
   64                  GError **perror)
   65 {
   66     gboolean res = TRUE;
   67     gchar *parent_path = NULL;
   68     CamelStoreInfo *parent_si = NULL;
   69     GPtrArray *array;
   70     guint ii;
   71 
   72     g_return_val_if_fail (mapi_store != NULL, FALSE);
   73     g_return_val_if_fail (mapi_store->summary != NULL, FALSE);
   74     g_return_val_if_fail (foreign_username != NULL, FALSE);
   75     g_return_val_if_fail (folder_id != 0, FALSE);
   76     g_return_val_if_fail (folder_id != parent_fid, FALSE);
   77     g_return_val_if_fail (display_username != NULL, FALSE);
   78     g_return_val_if_fail (display_foldername != NULL, FALSE);
   79 
   80     array = camel_store_summary_array (mapi_store->summary);
   81 
   82     for (ii = 0; res && ii < array->len; ii++) {
   83         CamelStoreInfo *si;
   84         CamelMapiStoreInfo *msi;
   85 
   86         si = g_ptr_array_index (array, ii);
   87 
   88         msi = (CamelMapiStoreInfo *) si;
   89 
   90         /* folder_id is unique even between users, thus can just check for it */
   91         if (msi->folder_id == folder_id) {
   92             res = FALSE;
   93             g_propagate_error (perror,
   94                 g_error_new (E_MAPI_ERROR, MAPI_E_INVALID_PARAMETER,
   95                 _("Cannot add folder, folder already exists as “%s”"), camel_store_info_get_path (si)));
   96         } else if (parent_fid != 0 && msi->folder_id == parent_fid) {
   97             if (g_strcmp0 (foreign_username, msi->foreign_username) == 0) {
   98                 g_free (parent_path);
   99                 if (parent_si)
  100                     camel_store_info_unref (parent_si);
  101                 parent_si = camel_store_info_ref (si);
  102                 parent_path = g_strdup (camel_store_info_get_path (parent_si));
  103             } else {
  104                 g_debug ("%s: parent folder '%s' with other user '%s' than expected '%s', skipping chain",
  105                     G_STRFUNC, camel_store_info_get_path (si), msi->foreign_username, foreign_username);
  106             }
  107         }
  108     }
  109 
  110     camel_store_summary_array_free (mapi_store->summary, array);
  111 
  112     if (res) {
  113         gchar *path;
  114 
  115         if (!parent_path) {
  116             gchar *mailbox;
  117 
  118             /* Translators: The '%s' is replaced with user name, to whom the foreign mailbox belongs.
  119                Example result: "Mailbox — John Smith"
  120             */
  121             mailbox = g_strdup_printf (C_("ForeignFolder", "Mailbox — %s"), display_username);
  122             parent_path = g_strdup_printf ("%s/%s", DISPLAY_NAME_FOREIGN_FOLDERS, mailbox);
  123 
  124             g_free (mailbox);
  125         }
  126 
  127         path = g_strconcat (parent_path, "/", display_foldername, NULL);
  128 
  129         /* make sure the path is unique */
  130         camel_mapi_store_ensure_unique_path (mapi_store, &path);
  131 
  132         if (camel_mapi_store_summary_add_from_full (mapi_store->summary, path, folder_id, parent_fid,
  133             CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_FOLDER_NOCHILDREN | CAMEL_FOLDER_SUBSCRIBED,
  134             CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN | CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL |
  135             (include_subfolders ? CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS : 0),
  136             foreign_username)) {
  137             if (parent_si) {
  138                 CamelMapiStoreInfo *msi = (CamelMapiStoreInfo *) parent_si;
  139 
  140                 msi->camel_folder_flags = msi->camel_folder_flags & (~CAMEL_FOLDER_NOCHILDREN);
  141             }
  142 
  143             camel_store_summary_touch (mapi_store->summary);
  144             camel_store_summary_save (mapi_store->summary);
  145 
  146             camel_mapi_store_announce_subscribed_folder (mapi_store, path);
  147         } else {
  148             res = FALSE;
  149             g_propagate_error (perror,
  150                 g_error_new (E_MAPI_ERROR, MAPI_E_INVALID_PARAMETER,
  151                 _("Cannot add folder, failed to add to store’s summary")));
  152         }
  153 
  154         g_free (path);
  155     }
  156 
  157     if (parent_si)
  158         camel_store_info_unref (parent_si);
  159     g_free (parent_path);
  160 
  161     return res;
  162 }
  163 
  164 static void
  165 enable_ok_button_by_data (GObject *dialog)
  166 {
  167     GtkEntry *entry;
  168     GtkComboBoxText *combo;
  169     const gchar *entry_text;
  170     gchar *combo_text;
  171 
  172     g_return_if_fail (dialog != NULL);
  173 
  174     entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
  175     g_return_if_fail (entry != NULL);
  176 
  177     combo = g_object_get_data (dialog, STR_FOLDER_NAME_COMBO);
  178     g_return_if_fail (combo != NULL);
  179 
  180     entry_text = gtk_entry_get_text (entry);
  181     combo_text = gtk_combo_box_text_get_active_text (combo);
  182 
  183     gtk_dialog_set_response_sensitive (
  184         GTK_DIALOG (dialog), GTK_RESPONSE_OK,
  185         entry_text && *entry_text && *entry_text != ' ' && *entry_text != ',' &&
  186         combo_text && *combo_text);
  187 
  188     g_free (combo_text);
  189 }
  190 
  191 static void
  192 name_entry_changed_cb (GObject *dialog)
  193 {
  194     GtkEntry *entry;
  195 
  196     g_return_if_fail (dialog != NULL);
  197 
  198     entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
  199     g_return_if_fail (entry != NULL);
  200 
  201     g_object_set_data (G_OBJECT (entry), STR_MAPI_DIRECT_USER_NAME, NULL);
  202 
  203     enable_ok_button_by_data (dialog);
  204 }
  205 
  206 static void
  207 folder_name_combo_changed_cb (GObject *dialog,
  208                   GtkComboBox *combo)
  209 {
  210     enable_ok_button_by_data (dialog);
  211 }
  212 
  213 struct EMapiCheckForeignFolderData
  214 {
  215     GtkWidget *dialog;
  216     gboolean include_subfolders;
  217     gchar *username;
  218     gchar *direct_username;
  219     gchar *user_displayname;
  220     gchar *orig_foldername;
  221     gchar *use_foldername;
  222     gchar *folder_displayname;
  223     gchar *folder_container_class;
  224     mapi_id_t folder_id;
  225     mapi_id_t parent_folder_id;
  226 };
  227 
  228 static void
  229 e_mapi_check_foreign_folder_data_free (gpointer ptr)
  230 {
  231     struct EMapiCheckForeignFolderData *cffd = ptr;
  232 
  233     if (!cffd)
  234         return;
  235 
  236     g_free (cffd->username);
  237     g_free (cffd->direct_username);
  238     g_free (cffd->user_displayname);
  239     g_free (cffd->orig_foldername);
  240     g_free (cffd->use_foldername);
  241     g_free (cffd->folder_displayname);
  242     g_free (cffd->folder_container_class);
  243 
  244     /* folder_id tells whether successfully finished,
  245        then the dialog can be destroyed */
  246     if (cffd->folder_id && cffd->dialog)
  247         gtk_widget_destroy (cffd->dialog);
  248 
  249     g_slice_free (struct EMapiCheckForeignFolderData, cffd);
  250 }
  251 
  252 static gboolean
  253 check_foreign_username_resolved_cb (EMapiConnection *conn,
  254                     TALLOC_CTX *mem_ctx,
  255                     /* const */ struct mapi_SPropValue_array *properties,
  256                     gpointer user_data,
  257                     GCancellable *cancellable,
  258                     GError **perror)
  259 {
  260     struct EMapiCheckForeignFolderData *cffd = user_data;
  261 
  262     g_return_val_if_fail (properties != NULL, FALSE);
  263     g_return_val_if_fail (cffd != NULL, FALSE);
  264     g_return_val_if_fail (cffd->user_displayname == NULL, FALSE);
  265 
  266     cffd->user_displayname = g_strdup (e_mapi_util_find_array_propval (properties, PidTagDisplayName));
  267 
  268     return TRUE;
  269 }
  270 
  271 static gboolean
  272 foreign_folder_add_props_cb (EMapiConnection *conn,
  273                  TALLOC_CTX *mem_ctx,
  274                  struct SPropTagArray *props,
  275                  gpointer data,
  276                  GCancellable *cancellable,
  277                  GError **perror)
  278 {
  279     g_return_val_if_fail (mem_ctx != NULL, FALSE);
  280     g_return_val_if_fail (props != NULL, FALSE);
  281 
  282     SPropTagArray_add (mem_ctx, props, PidTagDisplayName);
  283     SPropTagArray_add (mem_ctx, props, PidTagContainerClass);
  284     SPropTagArray_add (mem_ctx, props, PidTagParentFolderId);
  285 
  286     return TRUE;
  287 }
  288 
  289 static gboolean
  290 foreign_folder_get_props_cb (EMapiConnection *conn,
  291                  TALLOC_CTX *mem_ctx,
  292                  /* const */ struct mapi_SPropValue_array *properties,
  293                  gpointer user_data,
  294                  GCancellable *cancellable,
  295                  GError **perror)
  296 {
  297     struct EMapiCheckForeignFolderData *cffd = user_data;
  298     const mapi_id_t *pid;
  299 
  300     g_return_val_if_fail (properties != NULL, FALSE);
  301     g_return_val_if_fail (cffd != NULL, FALSE);
  302     g_return_val_if_fail (cffd->folder_displayname == NULL, FALSE);
  303     g_return_val_if_fail (cffd->folder_container_class == NULL, FALSE);
  304 
  305     pid = e_mapi_util_find_array_propval (properties, PidTagParentFolderId);
  306 
  307     cffd->folder_displayname = g_strdup (e_mapi_util_find_array_propval (properties, PidTagDisplayName));
  308     cffd->folder_container_class = g_strdup (e_mapi_util_find_array_propval (properties, PidTagContainerClass));
  309     cffd->parent_folder_id = pid ? *pid : 0;
  310 
  311     if (!cffd->folder_container_class) {
  312         /* Default to mail folder */
  313         cffd->folder_container_class = g_strdup (IPF_NOTE);
  314     }
  315 
  316     return TRUE;
  317 }
  318 
  319 static void
  320 check_foreign_folder_thread (GObject *with_object,
  321                  gpointer user_data,
  322                  GCancellable *cancellable,
  323                  GError **perror)
  324 {
  325     struct EMapiCheckForeignFolderData *cffd = user_data;
  326     GError *local_error = NULL;
  327     EMapiConnection *conn;
  328     mapi_object_t obj_folder;
  329     mapi_id_t fid = 0;
  330 
  331     g_return_if_fail (with_object != NULL);
  332     g_return_if_fail (CAMEL_IS_MAPI_STORE (with_object));
  333     g_return_if_fail (user_data != NULL);
  334     g_return_if_fail (cffd->username != NULL);
  335 
  336     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
  337         return;
  338 
  339     conn = camel_mapi_store_ref_connection (CAMEL_MAPI_STORE (with_object), cancellable, perror);
  340     if (!conn || !e_mapi_connection_connected (conn)) {
  341         if (conn)
  342             g_object_unref (conn);
  343         make_mapi_error (perror, "EMapiConnection", MAPI_E_NOT_INITIALIZED);
  344         return;
  345     }
  346 
  347     if (cffd->direct_username && *cffd->direct_username) {
  348         g_return_if_fail (cffd->user_displayname == NULL);
  349 
  350         cffd->user_displayname = cffd->username;
  351         cffd->username = g_strdup (cffd->direct_username);
  352     } else {
  353         if (!e_mapi_connection_resolve_username (conn, cffd->username,
  354             NULL, NULL,
  355             check_foreign_username_resolved_cb, cffd,
  356             cancellable, perror)) {
  357             g_object_unref (conn);
  358             make_mapi_error (perror, "e_mapi_connection_resolve_username", MAPI_E_CALL_FAILED);
  359             return;
  360         }
  361     }
  362 
  363     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  364         g_object_unref (conn);
  365         return;
  366     }
  367 
  368     if (!e_mapi_connection_test_foreign_folder (conn, cffd->username,
  369         cffd->use_foldername ? cffd->use_foldername : cffd->orig_foldername,
  370         &fid, cancellable, &local_error)) {
  371         if (g_error_matches (local_error, E_MAPI_ERROR, MAPI_E_NOT_FOUND)) {
  372             g_clear_error (&local_error);
  373             local_error = g_error_new (E_MAPI_ERROR, MAPI_E_NOT_FOUND,
  374                 _("Folder “%s” not found. Either it does not exist or you do not have permission to access it."),
  375                 cffd->orig_foldername);
  376         }
  377 
  378         g_object_unref (conn);
  379         g_propagate_error (perror, local_error);
  380         return;
  381     }
  382 
  383     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  384         g_object_unref (conn);
  385         return;
  386     }
  387 
  388     if (!e_mapi_connection_open_foreign_folder (conn, cffd->username, fid, &obj_folder, cancellable, perror)) {
  389         g_object_unref (conn);
  390         make_mapi_error (perror, "e_mapi_connection_open_foreign_folder", MAPI_E_CALL_FAILED);
  391         return;
  392     }
  393 
  394     if (!e_mapi_connection_get_folder_properties (conn, &obj_folder,
  395         foreign_folder_add_props_cb, NULL,
  396         foreign_folder_get_props_cb, cffd,
  397         cancellable, perror)) {
  398         make_mapi_error (perror, "e_mapi_connection_get_folder_properties", MAPI_E_CALL_FAILED);
  399 
  400         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, perror);
  401         g_object_unref (conn);
  402         return;
  403     }
  404 
  405     e_mapi_connection_close_folder (conn, &obj_folder, cancellable, perror);
  406     g_object_unref (conn);
  407 
  408     if (!cffd->folder_container_class) {
  409         g_propagate_error (perror, g_error_new_literal (E_MAPI_ERROR, MAPI_E_CALL_FAILED, _("Cannot add folder, cannot determine folder’s type")));
  410         return;
  411     }
  412 
  413     cffd->folder_id = fid;
  414 }
  415 
  416 static void
  417 check_foreign_folder_idle (GObject *with_object,
  418                gpointer user_data,
  419                GCancellable *cancellable,
  420                GError **perror)
  421 {
  422     struct EMapiCheckForeignFolderData *cffd = user_data;
  423     gchar *folder_name;
  424     const gchar *base_username, *base_foldername;
  425     CamelSettings *settings;
  426     CamelMapiSettings *mapi_settings;
  427     CamelMapiStore *mapi_store;
  428     ESourceRegistry *registry = NULL;
  429     CamelSession *session;
  430     EMapiFolderType folder_type;
  431     gchar *profile;
  432 
  433     g_return_if_fail (with_object != NULL);
  434     g_return_if_fail (CAMEL_IS_MAPI_STORE (with_object));
  435     g_return_if_fail (user_data != NULL);
  436     g_return_if_fail (cffd->username != NULL);
  437     g_return_if_fail (cffd->folder_container_class != NULL);
  438 
  439     if (!cffd->folder_id)
  440         return;
  441 
  442     base_username = cffd->user_displayname ? cffd->user_displayname : cffd->username;
  443     base_foldername = cffd->folder_displayname ? cffd->folder_displayname : cffd->orig_foldername;
  444 
  445     /* Translators: This is used to name foreign folder.
  446        The first '%s' is replaced with user name to whom the folder belongs,
  447        the second '%s' is replaced with folder name.
  448        Example result: "John Smith — Calendar"
  449     */
  450     folder_name = g_strdup_printf (C_("ForeignFolder", "%s  %s"), base_username, base_foldername);
  451 
  452     mapi_store = CAMEL_MAPI_STORE (with_object);
  453 
  454     settings = camel_service_ref_settings (CAMEL_SERVICE (mapi_store));
  455 
  456     mapi_settings = CAMEL_MAPI_SETTINGS (settings);
  457     profile = camel_mapi_settings_dup_profile (mapi_settings);
  458 
  459     g_object_unref (settings);
  460 
  461     session = camel_service_ref_session (CAMEL_SERVICE (mapi_store));
  462     if (E_IS_MAIL_SESSION (session))
  463         registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
  464 
  465     folder_type = e_mapi_folder_type_from_string (cffd->folder_container_class);
  466     if ((folder_type == E_MAPI_FOLDER_TYPE_MAIL &&
  467          !add_foreign_folder_to_camel (mapi_store,
  468         cffd->username,
  469         cffd->folder_id,
  470         cffd->parent_folder_id,
  471         cffd->include_subfolders,
  472         base_username,
  473         base_foldername,
  474         perror)) ||
  475         (folder_type != E_MAPI_FOLDER_TYPE_MAIL && !e_mapi_folder_add_as_esource (registry, folder_type, profile,
  476         TRUE /* camel_offline_settings_get_stay_synchronized (CAMEL_OFFLINE_SETTINGS (mapi_settings)) */,
  477         E_MAPI_FOLDER_CATEGORY_FOREIGN,
  478         cffd->username,
  479         folder_name,
  480         cffd->folder_id,
  481         0,
  482         cancellable,
  483         perror))) {
  484         /* to not destroy the dialog on error */
  485         cffd->folder_id = 0;
  486     }
  487 
  488     g_object_unref (session);
  489 
  490     g_free (folder_name);
  491     g_free (profile);
  492 }
  493 
  494 static gpointer
  495 ref_selected_store (GObject *dialog)
  496 {
  497     GtkComboBox *combo_box;
  498     CamelStore *store = NULL;
  499     GtkTreeIter iter;
  500 
  501     combo_box = g_object_get_data (dialog, STR_ACCOUNTS_COMBO);
  502     g_return_val_if_fail (combo_box != NULL, NULL);
  503 
  504     if (gtk_combo_box_get_active_iter (combo_box, &iter)) {
  505         gtk_tree_model_get (gtk_combo_box_get_model (combo_box), &iter,
  506             COLUMN_STORE, &store, -1);
  507     }
  508 
  509     return store;
  510 }
  511 
  512 static void
  513 subscribe_foreign_response_cb (GObject *dialog,
  514                    gint response_id)
  515 {
  516     struct EMapiCheckForeignFolderData *cffd;
  517     ENameSelectorEntry *entry;
  518     GtkComboBoxText *combo_text;
  519     GtkToggleButton *subfolders_check;
  520     EDestinationStore *dest_store;
  521     CamelStore *cstore;
  522     gchar *description;
  523     const gchar *username;
  524     gchar *orig_foldername, *use_foldername = NULL;
  525 
  526     if (response_id != GTK_RESPONSE_OK) {
  527         gtk_widget_destroy (GTK_WIDGET (dialog));
  528         return;
  529     }
  530 
  531     g_return_if_fail (dialog != NULL);
  532 
  533     entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
  534     combo_text = g_object_get_data (dialog, STR_FOLDER_NAME_COMBO);
  535     subfolders_check = g_object_get_data (dialog, STR_SUBFOLDERS_CHECK);
  536 
  537     g_return_if_fail (entry != NULL);
  538 
  539     cstore = ref_selected_store (dialog);
  540     g_return_if_fail (cstore != NULL);
  541 
  542     username = NULL;
  543     dest_store = e_name_selector_entry_peek_destination_store (entry);
  544     if (dest_store && e_destination_store_get_destination_count (dest_store) > 0) {
  545         EDestination *dest;
  546         GList *dests = e_destination_store_list_destinations (dest_store);
  547 
  548         g_return_if_fail (dests != NULL);
  549 
  550         /* pick the first, there is no option to limit to only one destination */
  551         dest = dests->data;
  552         if (dest) {
  553             username = e_destination_get_email (dest);
  554             if (!username || !*username)
  555                 username = e_destination_get_name (dest);
  556         }
  557 
  558         g_list_free (dests);
  559     }
  560 
  561     if (!username || !*username)
  562         username = gtk_entry_get_text (GTK_ENTRY (entry));
  563 
  564     orig_foldername = gtk_combo_box_text_get_active_text (combo_text);
  565     if (!orig_foldername)
  566         orig_foldername = g_strdup ("");
  567 
  568     /* convert well-known names to their non-localized form */
  569     if (g_strcmp0 (orig_foldername, _("Inbox")) == 0) {
  570         use_foldername = g_strdup ("Inbox");
  571     } else if (g_strcmp0 (orig_foldername, _("Contacts")) == 0) {
  572         use_foldername = g_strdup ("Contacts");
  573     } else if (g_strcmp0 (orig_foldername, _("Calendar")) == 0) {
  574         use_foldername = g_strdup ("Calendar");
  575     } else if (g_strcmp0 (orig_foldername, _("Memos")) == 0) {
  576         use_foldername = g_strdup ("Notes");
  577     } else if (g_strcmp0 (orig_foldername, _("Tasks")) == 0) {
  578         use_foldername = g_strdup ("Tasks");
  579     }
  580 
  581     cffd = g_slice_new0 (struct EMapiCheckForeignFolderData);
  582     cffd->dialog = GTK_WIDGET (dialog);
  583     cffd->username = g_strdup (username ? username : "");
  584     cffd->direct_username = g_strdup (g_object_get_data (G_OBJECT (entry), STR_MAPI_DIRECT_USER_NAME));
  585     cffd->orig_foldername = orig_foldername;
  586     cffd->use_foldername = use_foldername;
  587     cffd->folder_id = 0;
  588     cffd->parent_folder_id = 0;
  589     cffd->include_subfolders = gtk_toggle_button_get_active (subfolders_check);
  590 
  591     description = g_strdup_printf (_("Testing availability of folder “%s” of user “%s”, please wait…"), cffd->orig_foldername, cffd->username);
  592 
  593     e_mapi_config_utils_run_in_thread_with_feedback (
  594         GTK_WINDOW (dialog),
  595         G_OBJECT (cstore),
  596         description,
  597         check_foreign_folder_thread,
  598         check_foreign_folder_idle,
  599         cffd,
  600         e_mapi_check_foreign_folder_data_free);
  601 
  602     g_free (description);
  603     g_object_unref (cstore);
  604 }
  605 
  606 static void
  607 pick_gal_user_clicked_cb (GtkButton *button,
  608               GObject *dialog)
  609 {
  610     GtkEntry *entry;
  611     CamelMapiStore *mapi_store;
  612     EMapiConnection *conn;
  613     gchar *text, *display_name = NULL, *dn = NULL;
  614     EMapiGalUserType searched_type = E_MAPI_GAL_USER_NONE;
  615 
  616     g_return_if_fail (dialog != NULL);
  617 
  618     entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
  619 
  620     g_return_if_fail (entry != NULL);
  621 
  622     mapi_store = ref_selected_store (dialog);
  623     g_return_if_fail (mapi_store != NULL);
  624 
  625     text = g_strstrip (g_strdup (gtk_entry_get_text (entry)));
  626 
  627     conn = camel_mapi_store_ref_connection (mapi_store, NULL, NULL);
  628     if (!conn) {
  629         e_notice (dialog, GTK_MESSAGE_ERROR, "%s", _("Cannot search for user when the account is offline"));
  630     } else if (e_mapi_search_gal_user_modal (GTK_WINDOW (dialog),
  631                       conn,
  632                       text,
  633                       &searched_type,
  634                       &display_name,
  635                       NULL,
  636                       &dn,
  637                       NULL)) {
  638         if (searched_type == E_MAPI_GAL_USER_REGULAR &&
  639             display_name && dn && *dn && strchr (dn, '=')) {
  640             gtk_entry_set_text (entry, display_name);
  641             g_object_set_data_full (G_OBJECT (entry), STR_MAPI_DIRECT_USER_NAME, g_strdup (strrchr (dn, '=') + 1), g_free);
  642         }
  643     }
  644 
  645     g_object_unref (mapi_store);
  646     g_clear_object (&conn);
  647     g_free (text);
  648     g_free (display_name);
  649     g_free (dn);
  650 }
  651 
  652 static gint
  653 sort_accounts_by_display_name_cb (gconstpointer ptr1,
  654                   gconstpointer ptr2)
  655 {
  656     CamelService *service1 = (CamelService *) ptr1;
  657     CamelService *service2 = (CamelService *) ptr2;
  658 
  659     return g_utf8_collate (camel_service_get_display_name (service1), camel_service_get_display_name (service2));
  660 }
  661 
  662 static GtkWidget *
  663 create_accounts_combo (CamelSession *session,
  664                EClientCache *client_cache,
  665                CamelStore *store)
  666 {
  667     GtkListStore *list_store;
  668     GtkTreeIter iter;
  669     GtkComboBox *combo_box;
  670     ESourceRegistry *registry;
  671     GList *services, *link, *accounts = NULL;
  672     GtkCellRenderer *renderer;
  673 
  674     list_store = gtk_list_store_new (3,
  675         G_TYPE_STRING,      /* COLUMN_UID - UID of the CamelMAPIStore */
  676         G_TYPE_STRING,      /* COLUMN_DISPLAY_NAME */
  677         CAMEL_TYPE_MAPI_STORE); /* COLUMN_STORE */
  678 
  679     registry = e_client_cache_ref_registry (client_cache);
  680     services = camel_session_list_services (session);
  681 
  682     for (link = services; link; link = g_list_next (link)) {
  683         CamelService *service = link->data;
  684 
  685         if (CAMEL_IS_MAPI_STORE (service)) {
  686             ESource *source;
  687 
  688             source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
  689             if (source && e_source_registry_check_enabled (registry, source)) {
  690                 accounts = g_list_prepend (accounts, service);
  691             }
  692 
  693             g_clear_object (&source);
  694         }
  695     }
  696 
  697     accounts = g_list_sort (accounts, sort_accounts_by_display_name_cb);
  698 
  699     for (link = accounts; link; link = g_list_next (link)) {
  700         CamelService *service = link->data;
  701 
  702         gtk_list_store_append (list_store, &iter);
  703         gtk_list_store_set (list_store, &iter,
  704             COLUMN_UID, camel_service_get_uid (service),
  705             COLUMN_DISPLAY_NAME, camel_service_get_display_name (service),
  706             COLUMN_STORE, service,
  707             -1);
  708     }
  709 
  710     g_list_free_full (services, g_object_unref);
  711     g_list_free (accounts);
  712     g_clear_object (&registry);
  713 
  714     combo_box = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store)));
  715     g_object_unref (list_store);
  716 
  717     renderer = gtk_cell_renderer_text_new ();
  718     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
  719     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, "text", COLUMN_DISPLAY_NAME, NULL);
  720 
  721     gtk_combo_box_set_id_column (combo_box, COLUMN_UID);
  722     if (store)
  723         gtk_combo_box_set_active_id (combo_box, camel_service_get_uid (CAMEL_SERVICE (store)));
  724     else if (accounts)
  725         gtk_combo_box_set_active (combo_box, 0);
  726 
  727     return GTK_WIDGET (combo_box);
  728 }
  729 
  730 /* Opens dialog to subscribe to folders of other
  731    users in the given store */
  732 void
  733 e_mapi_subscribe_foreign_folder (GtkWindow *parent,
  734                  CamelSession *session,
  735                  CamelStore *store,
  736                                  EClientCache *client_cache)
  737 {
  738     ENameSelector *name_selector;
  739     ENameSelectorModel *name_selector_model;
  740     ENameSelectorDialog *name_selector_dialog;
  741     GObject *dialog;
  742     GtkWidget *content;
  743     GtkWidget *label, *widget, *entry, *check, *accounts_combo;
  744     GtkGrid *grid;
  745     GtkComboBoxText *combo_text;
  746     gint row;
  747 
  748     g_return_if_fail (session != NULL);
  749     if (store)
  750         g_return_if_fail (CAMEL_IS_MAPI_STORE (store));
  751 
  752     dialog = G_OBJECT (gtk_dialog_new_with_buttons (
  753         _("Subscribe to folder of other MAPI user…"),
  754         parent,
  755         GTK_DIALOG_DESTROY_WITH_PARENT,
  756         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
  757         GTK_STOCK_OK, GTK_RESPONSE_OK,
  758         NULL));
  759 
  760     g_signal_connect (dialog, "response", G_CALLBACK (subscribe_foreign_response_cb), NULL);
  761 
  762     content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  763 
  764     grid = GTK_GRID (gtk_grid_new ());
  765     gtk_grid_set_row_homogeneous (grid, FALSE);
  766     gtk_grid_set_row_spacing (grid, 6);
  767     gtk_grid_set_column_homogeneous (grid, FALSE);
  768     gtk_grid_set_column_spacing (grid, 6);
  769     gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
  770     gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
  771 
  772     row = 0;
  773 
  774     label = gtk_label_new (_("Account:"));
  775     g_object_set (G_OBJECT (label),
  776         "hexpand", FALSE,
  777         "vexpand", FALSE,
  778         "xalign", 0.0,
  779         "halign", GTK_ALIGN_START,
  780         NULL);
  781 
  782     widget = create_accounts_combo (session, client_cache, store);
  783     g_object_set (G_OBJECT (widget),
  784         "hexpand", TRUE,
  785         "vexpand", FALSE,
  786         "halign", GTK_ALIGN_START,
  787         NULL);
  788     accounts_combo = widget;
  789 
  790     gtk_grid_attach (grid, label, 0, row, 1, 1);
  791     gtk_grid_attach (grid, widget, 1, row, 2, 1);
  792 
  793     row++;
  794 
  795     name_selector = e_name_selector_new (client_cache);
  796     name_selector_model = e_name_selector_peek_model (name_selector);
  797     e_name_selector_model_add_section (name_selector_model, "User", _("User"), NULL);
  798     name_selector_dialog = e_name_selector_peek_dialog (name_selector);
  799     g_signal_connect (name_selector_dialog, "response", G_CALLBACK (gtk_widget_hide), name_selector);
  800     e_name_selector_load_books (name_selector);
  801 
  802     g_object_set_data_full (dialog, "e-mapi-name-selector", name_selector, g_object_unref);
  803 
  804     label = gtk_label_new_with_mnemonic (_("_User:"));
  805     g_object_set (G_OBJECT (label),
  806         "hexpand", FALSE,
  807         "vexpand", FALSE,
  808         "xalign", 0.0,
  809         NULL);
  810 
  811     entry = GTK_WIDGET (e_name_selector_peek_section_entry (name_selector, "User"));
  812     g_object_set (G_OBJECT (entry),
  813         "hexpand", TRUE,
  814         "vexpand", FALSE,
  815         NULL);
  816 
  817     widget = gtk_button_new_with_mnemonic (_("C_hoose…"));
  818     g_object_set (G_OBJECT (entry),
  819         "hexpand", TRUE,
  820         "vexpand", FALSE,
  821         NULL);
  822 
  823     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  824     g_signal_connect (widget, "clicked", G_CALLBACK (pick_gal_user_clicked_cb), dialog);
  825 
  826     gtk_grid_attach (grid, label, 0, row, 1, 1);
  827     gtk_grid_attach (grid, entry, 1, row, 1, 1);
  828     gtk_grid_attach (grid, widget, 2, row, 1, 1);
  829 
  830     row++;
  831 
  832     label = gtk_label_new_with_mnemonic (_("_Folder name:"));
  833     g_object_set (G_OBJECT (label),
  834         "hexpand", FALSE,
  835         "vexpand", FALSE,
  836         "xalign", 0.0,
  837         NULL);
  838 
  839     widget = GTK_WIDGET (g_object_new (gtk_combo_box_text_get_type (),
  840         "has-entry", TRUE,
  841         "entry-text-column", 0,
  842         "hexpand", TRUE,
  843         "vexpand", FALSE,
  844         NULL));
  845 
  846     combo_text = GTK_COMBO_BOX_TEXT (widget);
  847     gtk_combo_box_text_append_text (combo_text, _("Inbox"));
  848     gtk_combo_box_text_append_text (combo_text, _("Contacts"));
  849     gtk_combo_box_text_append_text (combo_text, _("Calendar"));
  850     gtk_combo_box_text_append_text (combo_text, _("Memos"));
  851     gtk_combo_box_text_append_text (combo_text, _("Tasks"));
  852     gtk_combo_box_set_active (GTK_COMBO_BOX (combo_text), 0);
  853 
  854     gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
  855     gtk_grid_attach (grid, label, 0, row, 1, 1);
  856     gtk_grid_attach (grid, widget, 1, row, 2, 1);
  857 
  858     row++;
  859 
  860     check = gtk_check_button_new_with_mnemonic (_("Include _subfolders"));
  861     gtk_grid_attach (grid, check, 1, row, 2, 1);
  862 
  863     /* remember widgets for later use */
  864     g_object_set_data (dialog, STR_ACCOUNTS_COMBO, accounts_combo);
  865     g_object_set_data (dialog, STR_USER_NAME_SELECTOR_ENTRY, entry);
  866     g_object_set_data (dialog, STR_FOLDER_NAME_COMBO, widget);
  867     g_object_set_data (dialog, STR_SUBFOLDERS_CHECK, check);
  868 
  869     g_object_set_data_full (dialog, STR_MAPI_CAMEL_SESSION, g_object_ref (session), g_object_unref);
  870 
  871     g_signal_connect_swapped (entry, "changed", G_CALLBACK (name_entry_changed_cb), dialog);
  872     g_signal_connect_swapped (combo_text, "changed", G_CALLBACK (folder_name_combo_changed_cb), dialog);
  873     g_signal_connect_swapped (accounts_combo, "changed", G_CALLBACK (name_entry_changed_cb), dialog);
  874 
  875     name_entry_changed_cb (dialog);
  876 
  877     gtk_widget_show_all (content);
  878     gtk_widget_show (GTK_WIDGET (dialog));
  879 }