"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/collection/e-mapi-backend.c" (2 Dec 2022, 33306 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-backend.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * e-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 <e-mapi-connection.h>
   24 #include <e-mapi-folder.h>
   25 #include <e-mapi-utils.h>
   26 #include <e-source-mapi-folder.h>
   27 #include <camel-mapi-settings.h>
   28 
   29 #include "e-mapi-backend.h"
   30 
   31 struct _EMapiBackendPrivate {
   32     /* Folder ID -> ESource */
   33     GHashTable *folders;
   34 
   35     gboolean need_update_folders;
   36     gulong source_changed_handler_id;
   37 
   38     GMutex credentials_lock;
   39     ENamedParameters *credentials;
   40 };
   41 
   42 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EMapiBackend, e_mapi_backend, E_TYPE_COLLECTION_BACKEND, 0, G_ADD_PRIVATE_DYNAMIC (EMapiBackend))
   43 
   44 typedef gboolean (* EMapiBackendAuthenticatorFunc) (EBackend *backend,
   45                             CamelMapiSettings *settings,
   46                             EMapiConnection *conn,
   47                             gpointer user_data,
   48                             GCancellable *cancellable,
   49                             GError **error);
   50 
   51 static gboolean
   52 e_mapi_backend_authenticator_run (EBackend *backend,
   53                   CamelMapiSettings *settings,
   54                   const ENamedParameters *credentials,
   55                   EMapiBackendAuthenticatorFunc func,
   56                       gpointer user_data,
   57                   GCancellable *cancellable,
   58                   GError **error)
   59 {
   60     EMapiProfileData empd = { 0 };
   61     EMapiConnection *conn;
   62     CamelNetworkSettings *network_settings;
   63     GError *mapi_error = NULL;
   64     gboolean success;
   65 
   66     g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
   67     g_return_val_if_fail (CAMEL_IS_MAPI_SETTINGS (settings), FALSE);
   68     g_return_val_if_fail (func != NULL, FALSE);
   69 
   70     if (!credentials) {
   71         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
   72             _("Cannot connect, no credentials provided"));
   73         return FALSE;
   74     }
   75 
   76     g_object_ref (backend);
   77     g_object_ref (settings);
   78 
   79     network_settings = CAMEL_NETWORK_SETTINGS (settings);
   80 
   81     empd.server = camel_network_settings_get_host (network_settings);
   82     empd.username = camel_network_settings_get_user (network_settings);
   83     e_mapi_util_profiledata_from_settings (&empd, settings);
   84 
   85     conn = e_mapi_connection_new (
   86         NULL,
   87         camel_mapi_settings_get_profile (settings),
   88         credentials, cancellable, &mapi_error);
   89 
   90     if (mapi_error) {
   91         g_warn_if_fail (!conn);
   92 
   93         g_object_unref (backend);
   94         g_object_unref (settings);
   95 
   96         g_propagate_error (error, mapi_error);
   97 
   98         return FALSE;
   99     }
  100 
  101     g_warn_if_fail (conn != NULL);
  102 
  103     success = func (backend, settings, conn, user_data, cancellable, error);
  104 
  105     g_object_unref (conn);
  106     g_object_unref (backend);
  107     g_object_unref (settings);
  108 
  109     return success;
  110 }
  111 
  112 static CamelMapiSettings *
  113 mapi_backend_get_settings (EMapiBackend *backend)
  114 {
  115     ESource *source;
  116     ESourceCamel *extension;
  117     CamelSettings *settings;
  118     const gchar *extension_name;
  119 
  120     source = e_backend_get_source (E_BACKEND (backend));
  121     extension_name = e_source_camel_get_extension_name ("mapi");
  122     extension = e_source_get_extension (source, extension_name);
  123     settings = e_source_camel_get_settings (extension);
  124 
  125     return CAMEL_MAPI_SETTINGS (settings);
  126 }
  127 
  128 struct SyndFoldersData
  129 {
  130     EMapiBackend *backend;
  131     GSList *folders;
  132     gchar *profile;
  133 };
  134 
  135 static void
  136 sync_folders_data_free (gpointer data)
  137 {
  138     struct SyndFoldersData *sfd = data;
  139 
  140     if (!sfd)
  141         return;
  142 
  143     e_mapi_folder_free_list (sfd->folders);
  144     g_object_unref (sfd->backend);
  145     g_free (sfd->profile);
  146     g_slice_free (struct SyndFoldersData, sfd);
  147 }
  148 
  149 static void
  150 mapi_backend_update_enabled (ESource *data_source,
  151                  ESource *collection_source)
  152 {
  153     ESourceCollection *collection_extension = NULL;
  154     gboolean part_enabled = TRUE;
  155 
  156     g_return_if_fail (E_IS_SOURCE (data_source));
  157 
  158     if (!collection_source || !e_source_get_enabled (collection_source)) {
  159         e_source_set_enabled (data_source, FALSE);
  160         return;
  161     }
  162 
  163     if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION))
  164         collection_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION);
  165 
  166     if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_CALENDAR) ||
  167         e_source_has_extension (data_source, E_SOURCE_EXTENSION_TASK_LIST) ||
  168         e_source_has_extension (data_source, E_SOURCE_EXTENSION_MEMO_LIST)) {
  169         part_enabled = !collection_extension || e_source_collection_get_calendar_enabled (collection_extension);
  170     } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
  171         part_enabled = !collection_extension || e_source_collection_get_contacts_enabled (collection_extension);
  172     } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
  173            e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_IDENTITY) ||
  174            e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
  175         part_enabled = !collection_extension || e_source_collection_get_mail_enabled (collection_extension);
  176     }
  177 
  178     e_source_set_enabled (data_source, part_enabled);
  179 }
  180 
  181 static gboolean
  182 mapi_backend_sync_folders_idle_cb (gpointer user_data)
  183 {
  184     struct SyndFoldersData *sfd = user_data;
  185     GSList *iter;
  186     GList *configured, *all_sources, *citer;
  187     ESourceRegistryServer *server;
  188     EMapiBackend *backend;
  189     GSList *mapi_folders;
  190     gboolean has_gal = FALSE, is_online;
  191     gint color_seed;
  192 
  193     g_return_val_if_fail (sfd != NULL, FALSE);
  194     g_return_val_if_fail (sfd->backend != NULL, FALSE);
  195     g_return_val_if_fail (sfd->profile != NULL, FALSE);
  196 
  197     backend = sfd->backend;
  198     mapi_folders = sfd->folders;
  199     is_online = e_backend_get_online (E_BACKEND (backend));
  200 
  201     server = e_collection_backend_ref_server (E_COLLECTION_BACKEND (backend));
  202     all_sources = e_source_registry_server_list_sources (server, NULL);
  203     configured = e_mapi_utils_filter_sources_for_profile (all_sources, sfd->profile);
  204     g_list_free_full (all_sources, g_object_unref);
  205 
  206     color_seed = g_list_length (configured);
  207 
  208     for (iter = mapi_folders; iter; iter = iter->next) {
  209         EMapiFolder *folder = iter->data;
  210         ESource *source;
  211 
  212         if (e_mapi_folder_get_category (folder) != E_MAPI_FOLDER_CATEGORY_PERSONAL)
  213             continue;
  214 
  215         switch (e_mapi_folder_get_type (folder)) {
  216         case E_MAPI_FOLDER_TYPE_APPOINTMENT:
  217         case E_MAPI_FOLDER_TYPE_CONTACT:
  218         case E_MAPI_FOLDER_TYPE_MEMO:
  219         case E_MAPI_FOLDER_TYPE_JOURNAL:
  220         case E_MAPI_FOLDER_TYPE_TASK:
  221             break;
  222         default:
  223             continue;
  224         }
  225 
  226         source = e_mapi_utils_get_source_for_folder (configured, sfd->profile, e_mapi_folder_get_id (folder));
  227         if (source) {
  228             mapi_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend)));
  229 
  230             if (g_strcmp0 (e_source_get_display_name (source), e_mapi_folder_get_name (folder)) != 0)
  231                 e_source_set_display_name (source, e_mapi_folder_get_name (folder));
  232 
  233             configured = g_list_remove (configured, source);
  234             g_object_unref (source);
  235         } else {
  236             gchar *fid_str, *res_id;
  237             const gchar *parent_id;
  238 
  239             source = e_backend_get_source (E_BACKEND (backend));
  240 
  241             parent_id = e_source_get_uid (source);
  242             fid_str = e_mapi_util_mapi_id_to_string (e_mapi_folder_get_id (folder));
  243             res_id = g_strconcat (parent_id ? parent_id : "mapi", ".", fid_str, NULL);
  244             g_free (fid_str);
  245 
  246             source = e_collection_backend_new_child (E_COLLECTION_BACKEND (backend), res_id);
  247 
  248             if (e_mapi_folder_populate_esource (
  249                 source,
  250                 configured,
  251                 e_mapi_folder_get_type (folder),
  252                 sfd->profile,
  253                 TRUE,
  254                 E_MAPI_FOLDER_CATEGORY_PERSONAL,
  255                 NULL,
  256                 e_mapi_folder_get_name (folder),
  257                 e_mapi_folder_get_id (folder),
  258                 color_seed,
  259                 NULL,
  260                 NULL)) {
  261                 color_seed++;
  262                 mapi_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend)));
  263                 e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
  264                 e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
  265                 e_source_registry_server_add_source (server, source);
  266             }
  267 
  268             g_free (res_id);
  269             g_object_unref (source);
  270         }
  271     }
  272 
  273     /* those which left are either mail sources, GAL or removed from the server */
  274     for (citer = configured; citer && is_online; citer = citer->next) {
  275         ESource *source = citer->data;
  276         ESourceMapiFolder *folder_ext;
  277         const gchar *foreign_user_name;
  278 
  279         if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER))
  280             continue;
  281 
  282         if (!e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK) &&
  283             !e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
  284             !e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST) &&
  285             !e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
  286             continue;
  287 
  288         folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  289         if (e_source_mapi_folder_is_public (folder_ext))
  290             continue;
  291 
  292         foreign_user_name = e_source_mapi_folder_get_foreign_username (folder_ext);
  293         if (foreign_user_name && *foreign_user_name)
  294             continue;
  295 
  296         /* test GAL */
  297         if (!has_gal && e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
  298             ESourceAddressBook *book_ext;
  299 
  300             book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
  301             has_gal = g_strcmp0 ("mapigal", e_source_backend_get_backend_name (E_SOURCE_BACKEND (book_ext))) == 0;
  302             if (has_gal)
  303                 continue;
  304         }
  305 
  306         e_source_remove_sync (source, NULL, NULL);
  307     }
  308 
  309     all_sources = e_collection_backend_claim_all_resources (E_COLLECTION_BACKEND (backend));
  310     for (citer = all_sources; citer; citer = citer->next) {
  311         ESource *source = citer->data;
  312         ESourceMapiFolder *extension;
  313         const gchar *foreign_username;
  314         gboolean remove = FALSE;
  315 
  316         if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER))
  317             continue;
  318 
  319         /* foreign folders are just added */
  320         extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  321         foreign_username = e_source_mapi_folder_get_foreign_username (extension);
  322         if (e_source_mapi_folder_is_public (extension) || (foreign_username && *foreign_username)) {
  323             e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
  324             e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
  325             e_source_registry_server_add_source (server, source);
  326         } else if (!has_gal && e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
  327             ESourceAddressBook *book_ext;
  328 
  329             book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
  330             has_gal = g_strcmp0 ("mapigal", e_source_backend_get_backend_name (E_SOURCE_BACKEND (book_ext))) == 0;
  331             if (has_gal)
  332                 e_source_registry_server_add_source (server, source);
  333             else
  334                 remove = TRUE;
  335         } else {
  336             remove = TRUE;
  337         }
  338 
  339         if (remove) {
  340             /* in online mode remove means remove, while in offline mode
  341                there are used only discovered sources from the last run,
  342                thus re-add them all
  343             */
  344             if (is_online) {
  345                 e_source_remove_sync (source, NULL, NULL);
  346             } else {
  347                 e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
  348                 e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
  349                 e_source_registry_server_add_source (server, source);
  350             }
  351         }
  352     }
  353     g_list_free_full (all_sources, g_object_unref);
  354 
  355     /* add GAL, if not there already */
  356     if (!has_gal) {
  357         ESource *source;
  358 
  359         source = e_collection_backend_new_child (E_COLLECTION_BACKEND (backend), "mapigal");
  360 
  361         if (e_mapi_folder_populate_esource (
  362             source,
  363             configured,
  364             E_MAPI_FOLDER_TYPE_CONTACT,
  365             sfd->profile,
  366             FALSE,
  367             E_MAPI_FOLDER_CATEGORY_PERSONAL,
  368             NULL,
  369             _("Global Address List"),
  370             -1,
  371             0,
  372             NULL,
  373             NULL)) {
  374             ESourceAddressBook *book_ext;
  375             /* ESourceContancts *contacts_ext; */
  376 
  377             book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
  378             e_source_backend_set_backend_name (E_SOURCE_BACKEND (book_ext), "mapigal");
  379 
  380             /* exclude GAL from Birthday & Anniversaries calendar by default */
  381             /* but it is not accessible from outside (yet)
  382             contacts_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CONTACTS_BACKEND);
  383             e_source_contacts_set_include_me (contacts_ext, FALSE); */
  384 
  385             e_source_registry_server_add_source (server, source);
  386         }
  387 
  388         g_object_unref (source);
  389     }
  390 
  391     g_list_free_full (configured, g_object_unref);
  392     g_object_unref (server);
  393 
  394     e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
  395 
  396     return FALSE;
  397 }
  398 
  399 static void
  400 mapi_backend_queue_auth_session (EMapiBackend *backend)
  401 {
  402     CamelMapiSettings *mapi_settings;
  403 
  404     mapi_settings = mapi_backend_get_settings (backend);
  405 
  406     if (!e_backend_get_online (E_BACKEND (backend))) {
  407         struct SyndFoldersData *sfd;
  408 
  409         sfd = g_slice_new0 (struct SyndFoldersData);
  410         sfd->folders = NULL;
  411         sfd->backend = g_object_ref (backend);
  412         sfd->profile = camel_mapi_settings_dup_profile (mapi_settings);
  413 
  414         /* Needed, because the mapi_backend_sync_folders_idle_cb() calls 'thaw' */
  415         e_collection_backend_freeze_populate (E_COLLECTION_BACKEND (backend));
  416 
  417         mapi_backend_sync_folders_idle_cb (sfd);
  418 
  419         sync_folders_data_free (sfd);
  420 
  421         return;
  422     }
  423 
  424     backend->priv->need_update_folders = FALSE;
  425 
  426     /* kerberos doesn't use passwords, do it directly */
  427     if (camel_mapi_settings_get_kerberos (mapi_settings)) {
  428         e_backend_schedule_authenticate (E_BACKEND (backend), NULL);
  429         return;
  430     }
  431 
  432     /* For now at least, we don't need to know the
  433      * results, so no callback function is needed. */
  434     e_backend_credentials_required (
  435         E_BACKEND (backend), E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
  436         NULL, NULL, NULL);
  437 }
  438 
  439 static void
  440 mapi_backend_source_changed_cb (ESource *source,
  441                 EMapiBackend *backend)
  442 {
  443     /* does nothing currently */
  444     if (!e_collection_backend_get_part_enabled (E_COLLECTION_BACKEND (backend), E_COLLECTION_BACKEND_PART_ANY)) {
  445         backend->priv->need_update_folders = TRUE;
  446         return;
  447     }
  448 
  449     if (e_backend_get_online (E_BACKEND (backend)) &&
  450         backend->priv->need_update_folders)
  451         mapi_backend_queue_auth_session (backend);
  452 }
  453 
  454 static void
  455 mapi_backend_constructed (GObject *object)
  456 {
  457     EBackend *backend = E_BACKEND (object);
  458     ESource *source;
  459 
  460     /* Chain up to parent's constructed() method. */
  461     G_OBJECT_CLASS (e_mapi_backend_parent_class)->constructed (object);
  462 
  463     source = e_backend_get_source (backend);
  464 
  465     /* XXX Wondering if we ought to delay this until after folders
  466      *     are initially populated, just to remove the possibility
  467      *     of weird races with clients trying to create folders. */
  468     e_server_side_source_set_remote_creatable (
  469         E_SERVER_SIDE_SOURCE (source), TRUE);
  470 }
  471 
  472 static void
  473 mapi_backend_dispose (GObject *object)
  474 {
  475     EMapiBackend *mapi_backend = E_MAPI_BACKEND (object);
  476 
  477     g_hash_table_remove_all (mapi_backend->priv->folders);
  478 
  479     if (mapi_backend->priv->source_changed_handler_id) {
  480         g_signal_handler_disconnect (e_backend_get_source (E_BACKEND (object)), mapi_backend->priv->source_changed_handler_id);
  481         mapi_backend->priv->source_changed_handler_id = 0;
  482     }
  483 
  484     /* Chain up to parent's dispose() method. */
  485     G_OBJECT_CLASS (e_mapi_backend_parent_class)->dispose (object);
  486 }
  487 
  488 static void
  489 mapi_backend_finalize (GObject *object)
  490 {
  491     EMapiBackend *mapi_backend = E_MAPI_BACKEND (object);
  492 
  493     g_hash_table_destroy (mapi_backend->priv->folders);
  494 
  495     g_mutex_clear (&mapi_backend->priv->credentials_lock);
  496     e_named_parameters_free (mapi_backend->priv->credentials);
  497 
  498     /* Chain up to parent's finalize() method. */
  499     G_OBJECT_CLASS (e_mapi_backend_parent_class)->finalize (object);
  500 }
  501 
  502 static void
  503 mapi_backend_populate (ECollectionBackend *backend)
  504 {
  505     ESource *source;
  506     EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
  507 
  508     source = e_backend_get_source (E_BACKEND (backend));
  509 
  510     mapi_backend->priv->need_update_folders = TRUE;
  511 
  512     /* do not do anything, if account is disabled */
  513     if (!e_collection_backend_get_part_enabled (backend, E_COLLECTION_BACKEND_PART_ANY))
  514         return;
  515 
  516     if (!e_collection_backend_freeze_populate (backend)) {
  517         e_collection_backend_thaw_populate (backend);
  518         return;
  519     }
  520 
  521     if (!mapi_backend->priv->source_changed_handler_id)
  522         mapi_backend->priv->source_changed_handler_id = g_signal_connect (
  523             source, "changed",
  524             G_CALLBACK (mapi_backend_source_changed_cb), backend);
  525 
  526     /* We test authentication passwords by attempting to synchronize
  527      * the folder hierarchy.  Since we want to synchronize the folder
  528      * hierarchy immediately on startup, schedule an authentication
  529      * session first thing. */
  530     mapi_backend_queue_auth_session (mapi_backend);
  531 
  532     e_collection_backend_thaw_populate (backend);
  533 }
  534 
  535 static gchar *
  536 mapi_backend_dup_resource_id (ECollectionBackend *backend,
  537                   ESource *child_source)
  538 {
  539     ESourceMapiFolder *extension;
  540     const gchar *extension_name;
  541     gchar *fid_str, *res_id;
  542     const gchar *parent_id;
  543     ESource *source;
  544 
  545     extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
  546     extension = e_source_get_extension (child_source, extension_name);
  547     source = e_backend_get_source (E_BACKEND (backend));
  548 
  549     parent_id = e_source_get_uid (source);
  550     fid_str = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
  551     res_id = g_strconcat (parent_id ? parent_id : "mapi", ".", fid_str, NULL);
  552     g_free (fid_str);
  553 
  554     return res_id;
  555 }
  556 
  557 static void
  558 mapi_backend_child_added (ECollectionBackend *backend,
  559                           ESource *child_source)
  560 {
  561     ESource *collection_source;
  562     const gchar *extension_name;
  563     gboolean is_mail = FALSE;
  564 
  565     collection_source = e_backend_get_source (E_BACKEND (backend));
  566 
  567     extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
  568     is_mail |= e_source_has_extension (child_source, extension_name);
  569 
  570     extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
  571     is_mail |= e_source_has_extension (child_source, extension_name);
  572 
  573     extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
  574     is_mail |= e_source_has_extension (child_source, extension_name);
  575 
  576     /* Synchronize mail-related user with the collection identity. */
  577     extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
  578     if (is_mail && e_source_has_extension (child_source, extension_name)) {
  579         ESourceAuthentication *auth_child_extension;
  580         ESourceCollection *collection_extension;
  581 
  582         extension_name = E_SOURCE_EXTENSION_COLLECTION;
  583         collection_extension = e_source_get_extension (
  584             collection_source, extension_name);
  585 
  586         extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
  587         auth_child_extension = e_source_get_extension (
  588             child_source, extension_name);
  589 
  590         e_binding_bind_property (
  591             collection_extension, "identity",
  592             auth_child_extension, "user",
  593             G_BINDING_SYNC_CREATE);
  594     }
  595 
  596     /* We track MAPI folders in a hash table by folder ID. */
  597     extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
  598     if (e_source_has_extension (child_source, extension_name)) {
  599         ESourceMapiFolder *extension;
  600         gchar *folder_id;
  601 
  602         extension = e_source_get_extension (
  603             child_source, extension_name);
  604         folder_id = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
  605         if (folder_id != NULL) {
  606             EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
  607 
  608             g_hash_table_insert (
  609                 mapi_backend->priv->folders, folder_id,
  610                 g_object_ref (child_source));
  611         }
  612     }
  613 
  614     /* Chain up to parent's child_added() method. */
  615     E_COLLECTION_BACKEND_CLASS (e_mapi_backend_parent_class)->
  616         child_added (backend, child_source);
  617 }
  618 
  619 static void
  620 mapi_backend_child_removed (ECollectionBackend *backend,
  621                             ESource *child_source)
  622 {
  623     const gchar *extension_name;
  624 
  625     /* We track MAPI folders in a hash table by folder ID. */
  626     extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
  627     if (e_source_has_extension (child_source, extension_name)) {
  628         ESourceMapiFolder *extension;
  629         gchar *folder_id;
  630 
  631         extension = e_source_get_extension (child_source, extension_name);
  632         folder_id = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
  633         if (folder_id != NULL) {
  634             EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
  635 
  636             g_hash_table_remove (mapi_backend->priv->folders, folder_id);
  637         }
  638         g_free (folder_id);
  639     }
  640 
  641     /* Chain up to parent's child_removed() method. */
  642     E_COLLECTION_BACKEND_CLASS (e_mapi_backend_parent_class)->
  643         child_removed (backend, child_source);
  644 }
  645 
  646 static gboolean
  647 mapi_backend_create_resource_cb (EBackend *backend,
  648                  CamelMapiSettings *settings,
  649                  EMapiConnection *conn,
  650                  gpointer user_data,
  651                  GCancellable *cancellable,
  652                  GError **error)
  653 {
  654     ESourceBackend *backend_ext = NULL;
  655     const gchar *folder_type_str = NULL;
  656     ESource *source = user_data;
  657     ESourceMapiFolder *folder_ext;
  658     mapi_object_t obj_folder;
  659     const gchar *foreign_username;
  660     gboolean res = FALSE;
  661     guint64 fid;
  662 
  663     g_return_val_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER), FALSE);
  664 
  665     folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  666     foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
  667 
  668     fid = e_source_mapi_folder_get_id (folder_ext);
  669     g_return_val_if_fail (fid == 0, FALSE);
  670 
  671     if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
  672         backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
  673         folder_type_str = IPF_CONTACT;
  674     } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
  675         backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
  676         folder_type_str = IPF_APPOINTMENT;
  677     } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
  678         backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
  679         folder_type_str = IPF_TASK;
  680     } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
  681         backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
  682         folder_type_str = IPF_STICKYNOTE;
  683     }
  684 
  685     if (!backend_ext || g_strcmp0 (e_source_backend_get_backend_name (backend_ext), "mapi") != 0)
  686         return FALSE;
  687 
  688     fid = e_source_mapi_folder_get_parent_id (folder_ext);
  689 
  690     if (foreign_username && *foreign_username)
  691         res = e_mapi_connection_open_foreign_folder (conn, foreign_username, fid, &obj_folder, cancellable, error);
  692     else if (e_source_mapi_folder_is_public (folder_ext))
  693         res = e_mapi_connection_open_public_folder (conn, fid, &obj_folder, cancellable, error);
  694     else
  695         res = e_mapi_connection_open_personal_folder (conn, fid, &obj_folder, cancellable, error);
  696 
  697     if (res) {
  698         fid = 0;
  699         if (!e_mapi_connection_create_folder (conn, &obj_folder, e_source_get_display_name (source), folder_type_str, &fid, cancellable, error))
  700             fid = 0;
  701         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, error);
  702 
  703         if (fid)
  704             e_source_mapi_folder_set_id (folder_ext, fid);
  705         else
  706             res = FALSE;
  707     }
  708 
  709     return res;
  710 }
  711 
  712 static gboolean
  713 mapi_backend_create_resource_sync (ECollectionBackend *backend,
  714                                    ESource *source,
  715                                    GCancellable *cancellable,
  716                                    GError **error)
  717 {
  718     ESourceRegistryServer *server;
  719     ESource *parent_source;
  720     CamelMapiSettings *settings;
  721     ESourceMapiFolder *folder_ext;
  722     EMapiBackend *mapi_backend;
  723     ENamedParameters *credentials;
  724     const gchar *foreign_username;
  725     const gchar *cache_dir;
  726     const gchar *parent_uid;
  727 
  728     if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER)) {
  729         g_set_error (
  730             error, G_IO_ERROR,
  731             G_IO_ERROR_INVALID_ARGUMENT,
  732             _("Data source “%s” does not represent a MAPI folder"),
  733             e_source_get_display_name (source));
  734         return FALSE;
  735     }
  736 
  737     mapi_backend = E_MAPI_BACKEND (backend);
  738     settings = mapi_backend_get_settings (mapi_backend);
  739     g_return_val_if_fail (settings != NULL, FALSE);
  740 
  741     folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  742     foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
  743 
  744     g_mutex_lock (&mapi_backend->priv->credentials_lock);
  745     credentials = mapi_backend->priv->credentials ? e_named_parameters_new_clone (mapi_backend->priv->credentials) : NULL;
  746     g_mutex_unlock (&mapi_backend->priv->credentials_lock);
  747 
  748     if (!e_source_mapi_folder_is_public (folder_ext) &&
  749         !(foreign_username && *foreign_username) &&
  750         !e_mapi_backend_authenticator_run (
  751         E_BACKEND (backend), settings, credentials, mapi_backend_create_resource_cb, source, cancellable, error)) {
  752         e_named_parameters_free (credentials);
  753         return FALSE;
  754     }
  755 
  756     e_named_parameters_free (credentials);
  757 
  758     /* Configure the source as a collection member. */
  759     parent_source = e_backend_get_source (E_BACKEND (backend));
  760     parent_uid = e_source_get_uid (parent_source);
  761     e_source_set_parent (source, parent_uid);
  762 
  763     /* Changes should be written back to the cache directory. */
  764     cache_dir = e_collection_backend_get_cache_dir (backend);
  765     e_server_side_source_set_write_directory (
  766         E_SERVER_SIDE_SOURCE (source), cache_dir);
  767 
  768     /* Set permissions for clients. */
  769     e_server_side_source_set_writable (
  770         E_SERVER_SIDE_SOURCE (source), TRUE);
  771     e_server_side_source_set_remote_deletable (
  772         E_SERVER_SIDE_SOURCE (source), TRUE);
  773 
  774     server = e_collection_backend_ref_server (backend);
  775     e_source_registry_server_add_source (server, source);
  776     g_object_unref (server);
  777 
  778     return TRUE;
  779 }
  780 
  781 static gboolean
  782 mapi_backend_delete_resource_cb (EBackend *backend,
  783                  CamelMapiSettings *settings,
  784                  EMapiConnection *conn,
  785                  gpointer user_data,
  786                  GCancellable *cancellable,
  787                  GError **error)
  788 {
  789     ESource *source = user_data;
  790     ESourceMapiFolder *folder_ext;
  791     mapi_object_t *obj_store = NULL;
  792     const gchar *foreign_username;
  793     gboolean res = FALSE;
  794     guint64 fid;
  795 
  796     g_return_val_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER), FALSE);
  797 
  798     folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  799     g_return_val_if_fail (!e_source_mapi_folder_is_public (folder_ext), FALSE);
  800 
  801     foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
  802     g_return_val_if_fail (!foreign_username || !*foreign_username, FALSE);
  803 
  804     fid = e_source_mapi_folder_get_id (folder_ext);
  805     g_return_val_if_fail (fid != 0, FALSE);
  806 
  807     if (e_mapi_connection_peek_store (conn, FALSE, NULL, &obj_store, cancellable, error))
  808         res = e_mapi_connection_remove_folder (conn, obj_store, fid, cancellable, error);
  809 
  810     return res;
  811 }
  812 
  813 static gboolean
  814 mapi_backend_delete_resource_sync (ECollectionBackend *backend,
  815                                    ESource *source,
  816                                    GCancellable *cancellable,
  817                                    GError **error)
  818 {
  819     CamelMapiSettings *settings;
  820     ESourceMapiFolder *folder_ext;
  821     EMapiBackend *mapi_backend;
  822     const gchar *foreign_username;
  823     ENamedParameters *credentials;
  824 
  825     if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER)) {
  826         g_set_error (
  827             error, G_IO_ERROR,
  828             G_IO_ERROR_INVALID_ARGUMENT,
  829             _("Data source “%s” does not represent a MAPI folder"),
  830             e_source_get_display_name (source));
  831         return FALSE;
  832     }
  833 
  834     mapi_backend = E_MAPI_BACKEND (backend);
  835     settings = mapi_backend_get_settings (mapi_backend);
  836     g_return_val_if_fail (settings != NULL, FALSE);
  837 
  838     folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  839     foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
  840 
  841     g_mutex_lock (&mapi_backend->priv->credentials_lock);
  842     credentials = mapi_backend->priv->credentials ? e_named_parameters_new_clone (mapi_backend->priv->credentials) : NULL;
  843     g_mutex_unlock (&mapi_backend->priv->credentials_lock);
  844 
  845     if (!e_source_mapi_folder_is_public (folder_ext) &&
  846         !(foreign_username && *foreign_username) &&
  847         !e_mapi_backend_authenticator_run (
  848         E_BACKEND (backend), settings, credentials, mapi_backend_delete_resource_cb, source, cancellable, error)) {
  849         e_named_parameters_free (credentials);
  850         return FALSE;
  851     }
  852 
  853     e_named_parameters_free (credentials);
  854 
  855     return e_source_remove_sync (source, cancellable, error);
  856 }
  857 
  858 static ESourceAuthenticationResult
  859 mapi_backend_authenticate_sync (EBackend *backend,
  860                 const ENamedParameters *credentials,
  861                 gchar **out_certificate_pem,
  862                 GTlsCertificateFlags *out_certificate_errors,
  863                 GCancellable *cancellable,
  864                 GError **error)
  865 {
  866     EMapiBackend *mapi_backend;
  867     EMapiConnection *conn;
  868     CamelMapiSettings *settings;
  869     GSList *mapi_folders = NULL;
  870     gboolean in_sync_folders = FALSE;
  871     GError *mapi_error = NULL, *krb_error = NULL;
  872 
  873     g_return_val_if_fail (E_IS_MAPI_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
  874 
  875     e_collection_backend_freeze_populate (E_COLLECTION_BACKEND (backend));
  876 
  877     mapi_backend = E_MAPI_BACKEND (backend);
  878     settings = mapi_backend_get_settings (mapi_backend);
  879 
  880     if (camel_mapi_settings_get_kerberos (settings))
  881         e_mapi_util_trigger_krb_auth_from_settings (settings, &krb_error);
  882 
  883     conn = e_mapi_connection_new (NULL,
  884         camel_mapi_settings_get_profile (settings),
  885         credentials, cancellable, &mapi_error);
  886 
  887     if (!conn) {
  888         ESourceAuthenticationResult res = E_SOURCE_AUTHENTICATION_ERROR;
  889 
  890         mapi_backend->priv->need_update_folders = TRUE;
  891 
  892         if (g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_PASSWORD_CHANGE_REQUIRED) ||
  893             g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_PASSWORD_EXPIRED)) {
  894             res = E_SOURCE_AUTHENTICATION_REJECTED;
  895         } else if ((!mapi_error || mapi_error->domain == E_MAPI_ERROR) &&
  896                (!credentials || !e_named_parameters_count (credentials)) &&
  897                !camel_mapi_settings_get_kerberos (settings)) {
  898             res = E_SOURCE_AUTHENTICATION_REQUIRED;
  899         }
  900 
  901         if (res == E_SOURCE_AUTHENTICATION_ERROR) {
  902             if (krb_error) {
  903                 GError *new_error;
  904 
  905                 if (mapi_error) {
  906                     new_error = g_error_new (mapi_error->domain, mapi_error->code,
  907                         /* Translators: the first '%s' is replaced with a generic error message,
  908                            the second '%s' is replaced with additional error information. */
  909                         C_("gssapi_error", "%s (%s)"), mapi_error->message, krb_error->message);
  910                 } else {
  911                     new_error = g_error_copy (krb_error);
  912                 }
  913 
  914                 g_clear_error (&mapi_error);
  915                 mapi_error = new_error;
  916             }
  917 
  918             g_propagate_error (error, mapi_error);
  919         } else {
  920             g_clear_error (&mapi_error);
  921         }
  922 
  923         g_clear_error (&krb_error);
  924 
  925         e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
  926 
  927         return res;
  928     }
  929 
  930     if (e_mapi_connection_get_folders_list (conn, &mapi_folders, NULL, NULL, cancellable, &mapi_error)) {
  931         struct SyndFoldersData *sfd;
  932 
  933         g_mutex_lock (&mapi_backend->priv->credentials_lock);
  934         e_named_parameters_free (mapi_backend->priv->credentials);
  935         mapi_backend->priv->credentials = credentials ? e_named_parameters_new_clone (credentials) : NULL;
  936         g_mutex_unlock (&mapi_backend->priv->credentials_lock);
  937 
  938         sfd = g_slice_new0 (struct SyndFoldersData);
  939         sfd->folders = mapi_folders;
  940         sfd->backend = g_object_ref (mapi_backend);
  941         sfd->profile = camel_mapi_settings_dup_profile (settings);
  942 
  943         g_idle_add_full (
  944             G_PRIORITY_DEFAULT_IDLE,
  945             mapi_backend_sync_folders_idle_cb, sfd,
  946             sync_folders_data_free);
  947 
  948         e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials);
  949 
  950         in_sync_folders = TRUE;
  951     } else {
  952         ESource *source = e_backend_get_source (backend);
  953 
  954         mapi_backend->priv->need_update_folders = TRUE;
  955 
  956         g_message ("%s: Failed to get list of user's folders for '%s': %s",
  957             G_STRFUNC, e_source_get_display_name (source), mapi_error ? mapi_error->message : "Unknown error");
  958     }
  959 
  960     g_object_unref (conn);
  961     g_clear_error (&mapi_error);
  962     g_clear_error (&krb_error);
  963 
  964     if (!in_sync_folders)
  965         e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
  966 
  967     return E_SOURCE_AUTHENTICATION_ACCEPTED;
  968 }
  969 
  970 static void
  971 e_mapi_backend_class_init (EMapiBackendClass *class)
  972 {
  973     GObjectClass *object_class;
  974     EBackendClass *backend_class;
  975     ECollectionBackendClass *collection_backend_class;
  976 
  977     object_class = G_OBJECT_CLASS (class);
  978     object_class->constructed = mapi_backend_constructed;
  979     object_class->dispose = mapi_backend_dispose;
  980     object_class->finalize = mapi_backend_finalize;
  981 
  982     backend_class = E_BACKEND_CLASS (class);
  983     backend_class->authenticate_sync = mapi_backend_authenticate_sync;
  984 
  985     collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
  986     collection_backend_class->populate = mapi_backend_populate;
  987     collection_backend_class->dup_resource_id = mapi_backend_dup_resource_id;
  988     collection_backend_class->child_added = mapi_backend_child_added;
  989     collection_backend_class->child_removed = mapi_backend_child_removed;
  990     collection_backend_class->create_resource_sync = mapi_backend_create_resource_sync;
  991     collection_backend_class->delete_resource_sync = mapi_backend_delete_resource_sync;
  992 
  993     /* This generates an ESourceCamel subtype for CamelMapiSettings. */
  994     e_source_camel_generate_subtype ("mapi", CAMEL_TYPE_MAPI_SETTINGS);
  995 }
  996 
  997 static void
  998 e_mapi_backend_class_finalize (EMapiBackendClass *class)
  999 {
 1000 }
 1001 
 1002 static void
 1003 e_mapi_backend_init (EMapiBackend *backend)
 1004 {
 1005     backend->priv = e_mapi_backend_get_instance_private (backend);
 1006 
 1007     backend->priv->folders = g_hash_table_new_full (
 1008         g_str_hash,
 1009         g_str_equal,
 1010         g_free,
 1011         g_object_unref);
 1012 
 1013     g_mutex_init (&backend->priv->credentials_lock);
 1014     backend->priv->credentials = NULL;
 1015 }
 1016 
 1017 void
 1018 e_mapi_backend_type_register (GTypeModule *type_module)
 1019 {
 1020     /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
 1021      *     function, so we have to wrap it with a public function in
 1022      *     order to register types from a separate compilation unit. */
 1023     e_mapi_backend_register_type (type_module);
 1024 }