"Fossies" - the Fresh Open Source Software Archive

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

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
    4  * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
    5  *
    6  * This program is free software; you can redistribute it and/or
    7  * modify it under the terms of the GNU Lesser General Public
    8  * License as published by the Free Software Foundation; either
    9  * version 2 of the License, or (at your option) version 3.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14  * Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   18  *
   19  *
   20  * Authors:
   21  *    Suman Manjunath <msuman@novell.com>
   22  */
   23 
   24 #include "evolution-mapi-config.h"
   25 
   26 #include <glib/gi18n-lib.h>
   27 #include <gio/gio.h>
   28 
   29 #include <libedata-cal/libedata-cal.h>
   30 #include <libedataserver/libedataserver.h>
   31 
   32 #include "e-mapi-connection.h"
   33 #include "e-mapi-cal-utils.h"
   34 #include "e-mapi-utils.h"
   35 #include "e-source-mapi-folder.h"
   36 
   37 #include "e-cal-backend-mapi.h"
   38 
   39 #define d(x)
   40 
   41 #ifdef G_OS_WIN32
   42 /* Undef the similar macro from pthread.h, it doesn't check if
   43  * gmtime() returns NULL.
   44  */
   45 #undef gmtime_r
   46 
   47 /* The gmtime() in Microsoft's C library is MT-safe */
   48 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
   49 #endif
   50 
   51 #define EC_ERROR(_code) e_client_error_create (_code, NULL)
   52 #define EC_ERROR_EX(_code, _msg) e_client_error_create (_code, _msg)
   53 #define ECC_ERROR(_code) e_cal_client_error_create (_code, NULL)
   54 
   55 /* Current data version */
   56 #define EMA_DATA_VERSION    1
   57 #define EMA_DATA_VERSION_KEY    "ema-data-version"
   58 
   59 struct _ECalBackendMAPIPrivate {
   60     GRecMutex conn_lock;
   61     EMapiConnection *conn;
   62 };
   63 
   64 G_DEFINE_TYPE_WITH_PRIVATE (ECalBackendMAPI, e_cal_backend_mapi, E_TYPE_CAL_META_BACKEND)
   65 
   66 static gchar *  ecb_mapi_dup_component_revision_cb  (ECalCache *cal_cache,
   67                              ICalComponent *icomp);
   68 
   69 static void
   70 ecb_mapi_error_to_client_error (GError **perror,
   71                 const GError *mapi_error,
   72                 GQuark domain,
   73                 gint code,
   74                 const gchar *context)
   75 {
   76     gchar *err_msg = NULL;
   77 
   78     if (!perror)
   79         return;
   80 
   81     if (g_error_matches (mapi_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
   82         g_propagate_error (perror, g_error_copy (mapi_error));
   83         return;
   84     }
   85 
   86     if (domain == E_CLIENT_ERROR && code == E_CLIENT_ERROR_OTHER_ERROR &&
   87         mapi_error && mapi_error->domain == E_MAPI_ERROR) {
   88         /* Change error to more accurate only with OtherError */
   89         switch (mapi_error->code) {
   90         case MAPI_E_PASSWORD_CHANGE_REQUIRED:
   91         case MAPI_E_PASSWORD_EXPIRED:
   92             code = E_CLIENT_ERROR_AUTHENTICATION_REQUIRED;
   93             break;
   94         case ecRpcFailed:
   95             code = E_CLIENT_ERROR_REPOSITORY_OFFLINE;
   96             break;
   97         default:
   98             break;
   99         }
  100     }
  101 
  102     if (context)
  103         err_msg = g_strconcat (context, mapi_error ? ": " : NULL, mapi_error ? mapi_error->message : NULL, NULL);
  104 
  105     g_set_error_literal (perror, domain, code, err_msg ? err_msg : mapi_error ? mapi_error->message : _("Unknown error"));
  106 
  107     g_free (err_msg);
  108 }
  109 
  110 static void
  111 ecb_mapi_lock_connection (ECalBackendMAPI *cbmapi)
  112 {
  113     g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
  114 
  115     g_rec_mutex_lock (&cbmapi->priv->conn_lock);
  116 }
  117 
  118 static void
  119 ecb_mapi_unlock_connection (ECalBackendMAPI *cbmapi)
  120 {
  121     g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
  122 
  123     g_rec_mutex_unlock (&cbmapi->priv->conn_lock);
  124 }
  125 
  126 static CamelMapiSettings *
  127 ecb_mapi_get_collection_settings (ECalBackendMAPI *cbmapi)
  128 {
  129     ESource *source;
  130     ESource *collection;
  131     ESourceCamel *extension;
  132     ESourceRegistry *registry;
  133     CamelSettings *settings;
  134     const gchar *extension_name;
  135 
  136     source = e_backend_get_source (E_BACKEND (cbmapi));
  137     registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi));
  138 
  139     extension_name = e_source_camel_get_extension_name ("mapi");
  140     e_source_camel_generate_subtype ("mapi", CAMEL_TYPE_MAPI_SETTINGS);
  141 
  142     /* The collection settings live in our parent data source. */
  143     collection = e_source_registry_find_extension (registry, source, extension_name);
  144     g_return_val_if_fail (collection != NULL, NULL);
  145 
  146     extension = e_source_get_extension (collection, extension_name);
  147     settings = e_source_camel_get_settings (extension);
  148 
  149     g_object_unref (collection);
  150 
  151     return CAMEL_MAPI_SETTINGS (settings);
  152 }
  153 
  154 static gboolean
  155 ecb_mapi_open_folder (ECalBackendMAPI *cbmapi,
  156               mapi_object_t *out_obj_folder,
  157               GCancellable *cancellable,
  158               GError **error)
  159 {
  160     ESource *source;
  161     ESourceMapiFolder *ext_mapi_folder;
  162     mapi_id_t fid;
  163     gchar *foreign_username;
  164     gboolean success;
  165 
  166     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
  167     g_return_val_if_fail (cbmapi->priv->conn != NULL, FALSE);
  168     g_return_val_if_fail (out_obj_folder != NULL, FALSE);
  169 
  170     source = e_backend_get_source (E_BACKEND (cbmapi));
  171     ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  172 
  173     fid = e_source_mapi_folder_get_id (ext_mapi_folder);
  174     foreign_username = e_source_mapi_folder_dup_foreign_username (ext_mapi_folder);
  175 
  176     if (foreign_username && *foreign_username)
  177         success = e_mapi_connection_open_foreign_folder (cbmapi->priv->conn, foreign_username, fid, out_obj_folder, cancellable, error);
  178     else if (e_source_mapi_folder_is_public (ext_mapi_folder))
  179         success = e_mapi_connection_open_public_folder (cbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
  180     else
  181         success = e_mapi_connection_open_personal_folder (cbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
  182 
  183     g_free (foreign_username);
  184 
  185     return success;
  186 }
  187 
  188 static void
  189 ecb_mapi_maybe_disconnect (ECalBackendMAPI *cbmapi,
  190                const GError *mapi_error)
  191 {
  192     g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
  193 
  194     /* no error or already disconnected */
  195     if (!mapi_error || !cbmapi->priv->conn)
  196         return;
  197 
  198     if (g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed) ||
  199         g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_CALL_FAILED)) {
  200         e_mapi_connection_disconnect (cbmapi->priv->conn,
  201             !g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed),
  202             NULL, NULL);
  203         g_clear_object (&cbmapi->priv->conn);
  204     }
  205 }
  206 
  207 static void
  208 ecb_mapi_get_comp_mid (ICalComponent *icomp,
  209                mapi_id_t *mid)
  210 {
  211     gchar *x_mid;
  212 
  213     g_return_if_fail (icomp != NULL);
  214     g_return_if_fail (mid != NULL);
  215 
  216     x_mid = e_cal_util_component_dup_x_property (icomp, "X-EVOLUTION-MAPI-MID");
  217     if (x_mid) {
  218         e_mapi_util_mapi_id_from_string (x_mid, mid);
  219         g_free (x_mid);
  220     } else {
  221         e_mapi_util_mapi_id_from_string (i_cal_component_get_uid (icomp), mid);
  222     }
  223 }
  224 
  225 static gboolean
  226 ecb_mapi_capture_req_props (EMapiConnection *conn,
  227                 TALLOC_CTX *mem_ctx,
  228                 /* const */ EMapiObject *object,
  229                 guint32 obj_index,
  230                 guint32 obj_total,
  231                 gpointer user_data,
  232                 GCancellable *cancellable,
  233                 GError **perror)
  234 {
  235     struct cal_cbdata *cbdata = user_data;
  236     const uint32_t *ui32;
  237 
  238     g_return_val_if_fail (object != NULL, FALSE);
  239     g_return_val_if_fail (cbdata != NULL, FALSE);
  240 
  241     ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagOwnerAppointmentId);
  242     if (ui32)
  243         cbdata->appt_id = *ui32;
  244     ui32 = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentSequence);
  245     if (ui32)
  246         cbdata->appt_seq = *ui32;
  247 
  248     cbdata->cleanglobalid = e_mapi_util_copy_sbinary_short (e_mapi_util_find_array_propval (&object->properties, PidLidCleanGlobalObjectId));
  249     cbdata->globalid = e_mapi_util_copy_sbinary_short (e_mapi_util_find_array_propval (&object->properties, PidLidGlobalObjectId));
  250 
  251     cbdata->username = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingName));
  252     cbdata->useridtype = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingAddressType));
  253     cbdata->userid = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingEmailAddress));
  254 
  255     cbdata->ownername = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderName));
  256     cbdata->owneridtype = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderAddressType));
  257     cbdata->ownerid = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderEmailAddress));
  258 
  259     return TRUE;
  260 }
  261 
  262 static gboolean
  263 ecb_mapi_list_for_one_mid_cb (EMapiConnection *conn,
  264                   TALLOC_CTX *mem_ctx,
  265                   const ListObjectsData *object_data,
  266                   guint32 obj_index,
  267                   guint32 obj_total,
  268                   gpointer user_data,
  269                   GCancellable *cancellable,
  270                   GError **perror)
  271 {
  272     mapi_id_t *pmid = user_data;
  273 
  274     g_return_val_if_fail (pmid != NULL, FALSE);
  275     g_return_val_if_fail (object_data != NULL, FALSE);
  276 
  277     *pmid = object_data->mid;
  278 
  279     return TRUE;
  280 }
  281 
  282 static gboolean
  283 ecb_mapi_build_global_id_restriction (EMapiConnection *conn,
  284                       TALLOC_CTX *mem_ctx,
  285                       struct mapi_SRestriction **restrictions,
  286                       gpointer user_data,
  287                       GCancellable *cancellable,
  288                       GError **perror)
  289 {
  290     ECalComponent *comp = user_data;
  291     struct SBinary_short sb;
  292     struct SPropValue sprop;
  293     struct mapi_SRestriction *restriction;
  294     gchar *propval;
  295 
  296     g_return_val_if_fail (restrictions != NULL, FALSE);
  297     g_return_val_if_fail (comp != NULL, FALSE);
  298 
  299     restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
  300     g_return_val_if_fail (restriction != NULL, FALSE);
  301 
  302     restriction->rt = RES_PROPERTY;
  303     restriction->res.resProperty.relop = RELOP_EQ;
  304     restriction->res.resProperty.ulPropTag = PidLidGlobalObjectId;
  305 
  306     propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-GLOBALID");
  307     if (propval && *propval) {
  308         gsize len = 0;
  309 
  310         sb.lpb = g_base64_decode (propval, &len);
  311         sb.cb = len;
  312     } else {
  313         ICalTime *dtstamp;
  314         struct FILETIME creation_time = { 0 };
  315         const gchar *uid;
  316 
  317         uid = e_cal_component_get_uid (comp);
  318 
  319         dtstamp = e_cal_component_get_dtstamp (comp);
  320         if (!dtstamp)
  321             dtstamp = i_cal_time_new_null_time ();
  322 
  323         e_mapi_util_time_t_to_filetime (i_cal_time_as_timet (dtstamp), &creation_time);
  324         e_mapi_cal_util_generate_globalobjectid (FALSE, uid, NULL, (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &sb);
  325 
  326         g_clear_object (&dtstamp);
  327     }
  328     g_free (propval);
  329 
  330     set_SPropValue_proptag (&sprop, PidLidGlobalObjectId, &sb);
  331     cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
  332 
  333     *restrictions = restriction;
  334 
  335     return TRUE;
  336 }
  337 
  338 static gboolean
  339 ecb_mapi_build_global_id_or_mid_restriction_from_uid (EMapiConnection *conn,
  340                               TALLOC_CTX *mem_ctx,
  341                               struct mapi_SRestriction **restrictions,
  342                               gpointer user_data,
  343                               GCancellable *cancellable,
  344                               GError **perror)
  345 {
  346     const gchar *uid = user_data;
  347     struct SPropValue sprop;
  348     struct mapi_SRestriction *restriction;
  349     mapi_id_t mid = 0;
  350 
  351     g_return_val_if_fail (restrictions != NULL, FALSE);
  352     g_return_val_if_fail (uid != NULL, FALSE);
  353 
  354     restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
  355     g_return_val_if_fail (restriction != NULL, FALSE);
  356 
  357     restriction->rt = RES_PROPERTY;
  358     restriction->res.resProperty.relop = RELOP_EQ;
  359 
  360     if (e_mapi_util_mapi_id_from_string (uid, &mid) && mid) {
  361         restriction->res.resProperty.ulPropTag = PidTagMid;
  362 
  363         set_SPropValue_proptag (&sprop, PidTagMid, &mid);
  364         cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
  365     } else {
  366         struct SBinary_short sb;
  367         gsize len = 0;
  368 
  369         sb.lpb = g_base64_decode (uid, &len);
  370         sb.cb = len;
  371 
  372         restriction->res.resProperty.ulPropTag = PidLidGlobalObjectId;
  373 
  374         set_SPropValue_proptag (&sprop, PidLidGlobalObjectId, &sb);
  375         cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
  376     }
  377 
  378     *restrictions = restriction;
  379 
  380     return TRUE;
  381 }
  382 
  383 /* should call free_server_data() before done with cbdata */
  384 static void
  385 ecb_mapi_get_server_data (ECalBackendMAPI *cbmapi,
  386               ECalComponent *comp,
  387               struct cal_cbdata *cbdata,
  388               GCancellable *cancellable)
  389 {
  390     EMapiConnection *conn;
  391     ICalComponent *icomp;
  392     mapi_id_t mid;
  393     mapi_object_t obj_folder;
  394     GError *mapi_error = NULL;
  395 
  396     icomp = e_cal_component_get_icalcomponent (comp);
  397     ecb_mapi_get_comp_mid (icomp, &mid);
  398 
  399     conn = cbmapi->priv->conn;
  400     if (!conn)
  401         goto cleanup;
  402 
  403     if (!ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error))
  404         goto cleanup;
  405 
  406     if (!e_mapi_connection_transfer_object (conn, &obj_folder, mid, ecb_mapi_capture_req_props, cbdata, cancellable, &mapi_error)) {
  407         if (!g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_NOT_FOUND)) {
  408             g_clear_error (&mapi_error);
  409             e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
  410             goto cleanup;
  411         }
  412 
  413         /* try to find by global-id, if not found by MID */
  414         g_clear_error (&mapi_error);
  415     }
  416 
  417     if (e_mapi_connection_list_objects (conn, &obj_folder,
  418                         ecb_mapi_build_global_id_restriction, comp,
  419                         ecb_mapi_list_for_one_mid_cb, &mid,
  420                         cancellable, &mapi_error)) {
  421         e_mapi_connection_transfer_object (conn, &obj_folder, mid, ecb_mapi_capture_req_props, cbdata, cancellable, &mapi_error);
  422     }
  423 
  424     e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
  425 
  426  cleanup:
  427     ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
  428     g_clear_error (&mapi_error);
  429 }
  430 
  431 /* frees data members allocated in get_server_data(), not the cbdata itself */
  432 static void
  433 ecb_mapi_free_server_data (struct cal_cbdata *cbdata)
  434 {
  435     if (!cbdata)
  436         return;
  437 
  438     #define do_free(_func, _val) _func (_val); _val = NULL
  439 
  440     do_free (e_mapi_util_free_sbinary_short, cbdata->cleanglobalid);
  441     do_free (e_mapi_util_free_sbinary_short, cbdata->globalid);
  442     do_free (g_free, cbdata->username);
  443     do_free (g_free, cbdata->useridtype);
  444     do_free (g_free, cbdata->userid);
  445     do_free (g_free, cbdata->ownername);
  446     do_free (g_free, cbdata->owneridtype);
  447     do_free (g_free, cbdata->ownerid);
  448 
  449     #undef do_free
  450 }
  451 
  452 #define free_and_dupe_str(_des, _new) G_STMT_START {    \
  453     g_free (_des);                  \
  454     _des = g_strdup (_new);             \
  455     } G_STMT_END
  456 
  457 static ESource *
  458 ecb_mapi_find_identity_source (ECalBackendMAPI *cbmapi)
  459 {
  460     ESourceRegistry *registry;
  461     GList *all_sources, *my_sources, *iter;
  462     CamelMapiSettings *settings;
  463     ESource *res = NULL;
  464 
  465     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), NULL);
  466 
  467     settings = ecb_mapi_get_collection_settings (cbmapi);
  468     g_return_val_if_fail (settings != NULL, NULL);
  469 
  470     registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi));
  471     all_sources = e_source_registry_list_sources (registry, NULL);
  472     my_sources = e_mapi_utils_filter_sources_for_profile (all_sources,
  473         camel_mapi_settings_get_profile (settings));
  474     g_list_free_full (all_sources, g_object_unref);
  475 
  476     for (iter = my_sources; iter; iter = iter->next) {
  477         ESource *source = iter->data;
  478 
  479         if (!source)
  480             continue;
  481 
  482         if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY)) {
  483             res = g_object_ref (source);
  484             break;
  485         }
  486     }
  487 
  488     g_list_free_full (my_sources, g_object_unref);
  489 
  490     return res;
  491 }
  492 
  493 static const gchar *
  494 ecb_mapi_get_owner_name (ECalBackendMAPI *cbmapi)
  495 {
  496     ESource *identity_source;
  497     ESourceMailIdentity *identity_ext;
  498     const gchar *res = NULL;
  499 
  500     identity_source = ecb_mapi_find_identity_source (cbmapi);
  501     if (!identity_source)
  502         return NULL;
  503 
  504     identity_ext = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
  505     if (identity_ext)
  506         res = e_source_mail_identity_get_name (identity_ext);
  507 
  508     g_object_unref (identity_source);
  509 
  510     return res;
  511 }
  512 
  513 static const gchar *
  514 ecb_mapi_get_owner_email (ECalBackendMAPI *cbmapi)
  515 {
  516     ESource *identity_source;
  517     ESourceMailIdentity *identity_ext;
  518     const gchar *res = NULL;
  519 
  520     identity_source = ecb_mapi_find_identity_source (cbmapi);
  521     if (!identity_source)
  522         return NULL;
  523 
  524     identity_ext = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
  525     if (identity_ext)
  526         res = e_source_mail_identity_get_address (identity_ext);
  527 
  528     g_object_unref (identity_source);
  529 
  530     return res;
  531 }
  532 
  533 static gboolean
  534 ecb_mapi_modifier_is_organizer (ECalBackendMAPI *cbmapi,
  535                 ECalComponent *comp)
  536 {
  537     ECalComponentOrganizer *org;
  538     const gchar *ownerid, *orgid;
  539     gboolean res;
  540 
  541     if (!e_cal_component_has_organizer (comp))
  542         return TRUE;
  543 
  544     org = e_cal_component_get_organizer (comp);
  545     if (!org)
  546         return TRUE;
  547 
  548     orgid = e_cal_component_organizer_get_value (org);
  549 
  550     if (orgid && !g_ascii_strncasecmp (orgid, "mailto:", 7))
  551         orgid = orgid + 7;
  552 
  553     ownerid = ecb_mapi_get_owner_email (cbmapi);
  554 
  555     res = g_ascii_strcasecmp (orgid, ownerid) == 0;
  556 
  557     e_cal_component_organizer_free (org);
  558 
  559     return res;
  560 }
  561 
  562 static OlResponseStatus
  563 ecb_mapi_find_my_response (ECalBackendMAPI *cbmapi,
  564                ECalComponent *comp)
  565 {
  566     ICalComponent *icomp = e_cal_component_get_icalcomponent (comp);
  567     ICalProperty *attendee;
  568     gchar *att = NULL;
  569     OlResponseStatus val = olResponseTentative;
  570 
  571     att = g_strdup_printf ("mailto:%s", ecb_mapi_get_owner_email (cbmapi));
  572 
  573     for (attendee = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
  574          attendee;
  575          g_object_unref (attendee), attendee = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
  576         const gchar *value = i_cal_property_get_attendee (attendee);
  577         if (!g_ascii_strcasecmp (value, att)) {
  578             ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
  579             ICalParameter *param;
  580 
  581             param = i_cal_property_get_first_parameter (attendee, I_CAL_PARTSTAT_PARAMETER);
  582             if (param) {
  583                 partstat = i_cal_parameter_get_partstat (param);
  584                 g_object_unref (param);
  585             }
  586 
  587             switch (partstat) {
  588             case I_CAL_PARTSTAT_ACCEPTED:
  589                 val = olResponseAccepted;
  590                 break;
  591             case I_CAL_PARTSTAT_TENTATIVE:
  592                 val = olResponseTentative;
  593                 break;
  594             case I_CAL_PARTSTAT_DECLINED:
  595                 val = olResponseDeclined;
  596                 break;
  597             default:
  598                 val = olResponseTentative;
  599                 break;
  600             }
  601 
  602             g_object_unref (attendee);
  603             break;
  604         }
  605     }
  606 
  607     g_free (att);
  608 
  609     return val;
  610 }
  611 
  612 static void
  613 ecb_mapi_server_notification_cb (EMapiConnection *conn,
  614                  guint event_mask,
  615                  gpointer event_data,
  616                  gpointer user_data)
  617 {
  618     ECalBackendMAPI *cbmapi = user_data;
  619     mapi_id_t update_folder1 = 0, update_folder2 = 0;
  620 
  621     g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
  622 
  623     switch (event_mask) {
  624     case fnevNewMail:
  625     case fnevNewMail | fnevMbit: {
  626         struct NewMailNotification *newmail = event_data;
  627 
  628         if (newmail)
  629             update_folder1 = newmail->FID;
  630         } break;
  631     case fnevObjectCreated:
  632     case fnevMbit | fnevObjectCreated: {
  633         struct MessageCreatedNotification *msgcreated = event_data;
  634 
  635         if (msgcreated)
  636             update_folder1 = msgcreated->FID;
  637         } break;
  638     case fnevObjectModified:
  639     case fnevMbit | fnevObjectModified: {
  640         struct MessageModifiedNotification *msgmodified = event_data;
  641 
  642         if (msgmodified)
  643             update_folder1 = msgmodified->FID;
  644         } break;
  645     case fnevObjectDeleted:
  646     case fnevMbit | fnevObjectDeleted: {
  647         struct MessageDeletedNotification *msgdeleted = event_data;
  648 
  649         if (msgdeleted)
  650             update_folder1 = msgdeleted->FID;
  651         } break;
  652     case fnevObjectMoved:
  653     case fnevMbit | fnevObjectMoved: {
  654         struct MessageMoveCopyNotification *msgmoved = event_data;
  655 
  656         if (msgmoved) {
  657             update_folder1 = msgmoved->OldFID;
  658             update_folder2 = msgmoved->FID;
  659         }
  660         } break;
  661     case fnevObjectCopied:
  662     case fnevMbit | fnevObjectCopied: {
  663         struct MessageMoveCopyNotification *msgcopied = event_data;
  664 
  665         if (msgcopied) {
  666             update_folder1 = msgcopied->OldFID;
  667             update_folder2 = msgcopied->FID;
  668         }
  669         } break;
  670     default:
  671         break;
  672     }
  673 
  674     if (update_folder1 || update_folder2) {
  675         ESource *source;
  676         ESourceMapiFolder *ext_mapi_folder;
  677 
  678         source = e_backend_get_source (E_BACKEND (cbmapi));
  679         ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  680 
  681         if (update_folder1 == e_source_mapi_folder_get_id (ext_mapi_folder) ||
  682             update_folder2 == e_source_mapi_folder_get_id (ext_mapi_folder)) {
  683             e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbmapi));
  684         }
  685     }
  686 }
  687 
  688 static gboolean
  689 ecb_mapi_connect_sync (ECalMetaBackend *meta_backend,
  690                const ENamedParameters *credentials,
  691                ESourceAuthenticationResult *out_auth_result,
  692                gchar **out_certificate_pem,
  693                GTlsCertificateFlags *out_certificate_errors,
  694                GCancellable *cancellable,
  695                GError **error)
  696 {
  697     ECalBackendMAPI *cbmapi;
  698     EMapiConnection *old_conn;
  699     CamelMapiSettings *settings;
  700     ESource *source;
  701     ESourceMapiFolder *ext_mapi_folder;
  702     GError *mapi_error = NULL;
  703 
  704     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
  705     g_return_val_if_fail (out_auth_result != NULL, FALSE);
  706 
  707     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
  708 
  709     ecb_mapi_lock_connection (cbmapi);
  710 
  711     if (cbmapi->priv->conn &&
  712         e_mapi_connection_connected (cbmapi->priv->conn)) {
  713         ecb_mapi_unlock_connection (cbmapi);
  714         return TRUE;
  715     }
  716 
  717     settings = ecb_mapi_get_collection_settings (cbmapi);
  718     source = e_backend_get_source (E_BACKEND (cbmapi));
  719     ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
  720 
  721     old_conn = cbmapi->priv->conn;
  722 
  723     cbmapi->priv->conn = e_mapi_connection_new (
  724         e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi)),
  725         camel_mapi_settings_get_profile (settings),
  726         credentials, cancellable, &mapi_error);
  727 
  728     if (!cbmapi->priv->conn) {
  729         cbmapi->priv->conn = e_mapi_connection_find (camel_mapi_settings_get_profile (settings));
  730         if (cbmapi->priv->conn && !e_mapi_connection_connected (cbmapi->priv->conn))
  731             e_mapi_connection_reconnect (cbmapi->priv->conn, credentials, cancellable, &mapi_error);
  732     }
  733 
  734     if (old_conn)
  735         g_signal_handlers_disconnect_by_func (old_conn, G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
  736 
  737     g_clear_object (&old_conn);
  738 
  739     if (!cbmapi->priv->conn || mapi_error) {
  740         gboolean is_network_error = mapi_error && mapi_error->domain != E_MAPI_ERROR;
  741 
  742         g_clear_object (&cbmapi->priv->conn);
  743         ecb_mapi_unlock_connection (cbmapi);
  744 
  745         if (is_network_error)
  746             ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, NULL);
  747 
  748         g_clear_error (&mapi_error);
  749 
  750         if (is_network_error) {
  751             *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
  752         } else if ((!credentials || !e_named_parameters_count (credentials)) && !camel_mapi_settings_get_kerberos (settings)) {
  753             *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
  754         } else {
  755             *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
  756         }
  757 
  758         return FALSE;
  759     }
  760 
  761     if (e_source_mapi_folder_get_server_notification (ext_mapi_folder)) {
  762         mapi_object_t obj_folder;
  763         GError *mapi_error = NULL;
  764 
  765         g_signal_connect (cbmapi->priv->conn, "server-notification", G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
  766 
  767         if (ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error)) {
  768             e_mapi_connection_enable_notifications (cbmapi->priv->conn, &obj_folder,
  769                 fnevObjectCreated | fnevObjectModified | fnevObjectDeleted | fnevObjectMoved | fnevObjectCopied,
  770                 cancellable, &mapi_error);
  771 
  772             e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
  773         }
  774 
  775         if (mapi_error) {
  776             ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
  777             g_clear_error (&mapi_error);
  778         }
  779     }
  780 
  781     ecb_mapi_unlock_connection (cbmapi);
  782 
  783     *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
  784 
  785     return TRUE;
  786 }
  787 
  788 static gboolean
  789 ecb_mapi_disconnect_sync (ECalMetaBackend *meta_backend,
  790               GCancellable *cancellable,
  791               GError **error)
  792 {
  793     ECalBackendMAPI *cbmapi;
  794     gboolean success = TRUE;
  795 
  796     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
  797 
  798     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
  799 
  800     ecb_mapi_lock_connection (cbmapi);
  801 
  802     if (cbmapi->priv->conn) {
  803         g_signal_handlers_disconnect_by_func (cbmapi->priv->conn, G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
  804 
  805         success = e_mapi_connection_disconnect (cbmapi->priv->conn, FALSE, cancellable, error);
  806         g_clear_object (&cbmapi->priv->conn);
  807     }
  808 
  809     ecb_mapi_unlock_connection (cbmapi);
  810 
  811     return success;
  812 }
  813 
  814 typedef struct _LoadMultipleData {
  815     ECalMetaBackend *meta_backend;
  816     ICalComponentKind kind;
  817     GSList **out_components; /* ICalComponent * */
  818 } LoadMultipleData;
  819 
  820 static gboolean
  821 transfer_calendar_objects_cb (EMapiConnection *conn,
  822                   TALLOC_CTX *mem_ctx,
  823                   /* const */ EMapiObject *object,
  824                   guint32 obj_index,
  825                   guint32 obj_total,
  826                   gpointer user_data,
  827                   GCancellable *cancellable,
  828                   GError **perror)
  829 {
  830     LoadMultipleData *lmd = user_data;
  831     ECalComponent *comp;
  832     const mapi_id_t *pmid;
  833     gchar *use_uid;
  834     GSList *instances = NULL;
  835 
  836     g_return_val_if_fail (conn != NULL, FALSE);
  837     g_return_val_if_fail (object != NULL, FALSE);
  838     g_return_val_if_fail (lmd != NULL, FALSE);
  839 
  840     pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
  841     if (pmid)
  842         use_uid = e_mapi_util_mapi_id_to_string (*pmid);
  843     else
  844         use_uid = e_util_generate_uid ();
  845 
  846     comp = e_mapi_cal_util_object_to_comp (conn, object,
  847         lmd->kind, FALSE, use_uid, &instances);
  848 
  849     g_free (use_uid);
  850 
  851     if (comp)
  852         instances = g_slist_prepend (instances, comp);
  853 
  854     if (instances) {
  855         ICalComponent *icomp;
  856 
  857         icomp = e_cal_meta_backend_merge_instances (lmd->meta_backend, instances, FALSE);
  858         if (icomp)
  859             *lmd->out_components = g_slist_prepend (*lmd->out_components, icomp);
  860     }
  861 
  862     g_slist_free_full (instances, g_object_unref);
  863 
  864     return TRUE;
  865 }
  866 
  867 static gboolean
  868 ecb_mapi_load_multiple_sync (ECalBackendMAPI *cbmapi,
  869                  const GSList *uids, /* gchar * */
  870                  GSList **out_components, /* ICalComponent * */
  871                  GCancellable *cancellable,
  872                  GError **error)
  873 {
  874     LoadMultipleData lmd;
  875     GSList *mids = NULL, *link;
  876     mapi_object_t obj_folder;
  877     gboolean success;
  878     GError *mapi_error = NULL;
  879 
  880     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
  881     g_return_val_if_fail (uids != NULL, FALSE);
  882     g_return_val_if_fail (out_components != NULL, FALSE);
  883 
  884     for (link = (GSList *) uids; link; link = g_slist_next (link)) {
  885         mapi_id_t *pmid, mid;
  886 
  887         if (e_mapi_util_mapi_id_from_string  (link->data, &mid)) {
  888             pmid = g_new0 (mapi_id_t, 1);
  889             *pmid = mid;
  890 
  891             mids = g_slist_prepend (mids, pmid);
  892         }
  893     }
  894 
  895     ecb_mapi_lock_connection (cbmapi);
  896 
  897     lmd.meta_backend = E_CAL_META_BACKEND (cbmapi);
  898     lmd.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
  899     lmd.out_components = out_components;
  900 
  901     success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
  902 
  903     if (success) {
  904         success = e_mapi_connection_transfer_objects (cbmapi->priv->conn, &obj_folder, mids,
  905             transfer_calendar_objects_cb, &lmd, cancellable, &mapi_error);
  906 
  907         e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
  908     }
  909 
  910     if (mapi_error) {
  911         ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
  912         ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to transfer objects from a server"));
  913         g_error_free (mapi_error);
  914 
  915         success = FALSE;
  916     }
  917 
  918     ecb_mapi_unlock_connection (cbmapi);
  919 
  920     g_slist_free_full (mids, g_free);
  921 
  922     return success;
  923 }
  924 
  925 static gboolean
  926 ecb_mapi_preload_infos_sync (ECalBackendMAPI *cbmapi,
  927                  GSList *created_objects,
  928                  GSList *modified_objects,
  929                  GCancellable *cancellable,
  930                  GError **error)
  931 {
  932     GHashTable *infos;
  933     GSList *uids = NULL, *link;
  934     gboolean success = TRUE;
  935 
  936     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
  937 
  938     infos = g_hash_table_new (g_str_hash, g_str_equal);
  939 
  940     for (link = created_objects; link; link = g_slist_next (link)) {
  941         ECalMetaBackendInfo *nfo = link->data;
  942 
  943         if (nfo && nfo->extra) {
  944             uids = g_slist_prepend (uids, nfo->extra);
  945             g_hash_table_insert (infos, nfo->extra, nfo);
  946         } else if (nfo && nfo->uid) {
  947             uids = g_slist_prepend (uids, nfo->uid);
  948             g_hash_table_insert (infos, nfo->uid, nfo);
  949         }
  950     }
  951 
  952     for (link = modified_objects; link; link = g_slist_next (link)) {
  953         ECalMetaBackendInfo *nfo = link->data;
  954 
  955         if (nfo && nfo->extra) {
  956             uids = g_slist_prepend (uids, nfo->extra);
  957             g_hash_table_insert (infos, nfo->extra, nfo);
  958         } else if (nfo && nfo->uid) {
  959             uids = g_slist_prepend (uids, nfo->uid);
  960             g_hash_table_insert (infos, nfo->uid, nfo);
  961         }
  962     }
  963 
  964     uids = g_slist_reverse (uids);
  965     if (uids) {
  966         GSList *components = NULL;
  967 
  968         success = ecb_mapi_load_multiple_sync (cbmapi, uids, &components, cancellable, error);
  969         if (success) {
  970             for (link = components; link; link = g_slist_next (link)) {
  971                 ICalComponent *icomp = link->data;
  972 
  973                 if (icomp) {
  974                     ECalMetaBackendInfo *nfo;
  975                     const gchar *uid = NULL;
  976                     gchar *xmid = NULL;
  977 
  978                     if (i_cal_component_isa (icomp) == I_CAL_VCALENDAR_COMPONENT) {
  979                         ICalComponent *subcomp;
  980                         ICalComponentKind kind;
  981 
  982                         kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
  983 
  984                         for (subcomp = i_cal_component_get_first_component (icomp, kind);
  985                              subcomp && !uid;
  986                              g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (icomp, kind)) {
  987                             uid = i_cal_component_get_uid (subcomp);
  988                             xmid = e_cal_util_component_dup_x_property (subcomp, "X-EVOLUTION-MAPI-MID");
  989                         }
  990 
  991                         g_clear_object (&subcomp);
  992                     } else {
  993                         uid = i_cal_component_get_uid (icomp);
  994                         xmid = e_cal_util_component_dup_x_property (icomp, "X-EVOLUTION-MAPI-MID");
  995                     }
  996 
  997                     nfo = uid ? g_hash_table_lookup (infos, uid) : NULL;
  998                     if (!nfo && xmid)
  999                         nfo = g_hash_table_lookup (infos, xmid);
 1000 
 1001                     if (nfo && !nfo->object)
 1002                         nfo->object = i_cal_component_as_ical_string (icomp);
 1003 
 1004                     g_free (xmid);
 1005                 }
 1006             }
 1007         }
 1008 
 1009         g_slist_free_full (components, g_object_unref);
 1010     }
 1011 
 1012     g_hash_table_destroy (infos);
 1013     g_slist_free (uids);
 1014 
 1015     return success;
 1016 }
 1017 
 1018 static gboolean
 1019 ecb_mapi_get_changes_sync (ECalMetaBackend *meta_backend,
 1020                const gchar *last_sync_tag,
 1021                gboolean is_repeat,
 1022                gchar **out_new_sync_tag,
 1023                gboolean *out_repeat,
 1024                GSList **out_created_objects,
 1025                GSList **out_modified_objects,
 1026                GSList **out_removed_objects,
 1027                GCancellable *cancellable,
 1028                GError **error)
 1029 {
 1030     ECalBackendMAPI *cbmapi;
 1031 
 1032     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
 1033     g_return_val_if_fail (out_created_objects != NULL, FALSE);
 1034     g_return_val_if_fail (out_modified_objects != NULL, FALSE);
 1035 
 1036     /* Chain up to parent's method */
 1037     if (!E_CAL_META_BACKEND_CLASS (e_cal_backend_mapi_parent_class)->get_changes_sync (meta_backend,
 1038         last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
 1039         out_modified_objects, out_removed_objects, cancellable, error)) {
 1040         return FALSE;
 1041     }
 1042 
 1043     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
 1044 
 1045     /* Preload some of the components in chunk, to speed-up things;
 1046        ignore errors, to not break whole update process. */
 1047     ecb_mapi_preload_infos_sync (cbmapi, *out_created_objects, *out_modified_objects, cancellable, NULL);
 1048 
 1049     return TRUE;
 1050 }
 1051 
 1052 static gboolean
 1053 ecb_mapi_list_existing_uids_cb (EMapiConnection *conn,
 1054                 TALLOC_CTX *mem_ctx,
 1055                 const ListObjectsData *object_data,
 1056                 guint32 obj_index,
 1057                 guint32 obj_total,
 1058                 gpointer user_data,
 1059                 GCancellable *cancellable,
 1060                 GError **perror)
 1061 {
 1062     GSList **out_existing_objects = user_data;
 1063     gchar *uid;
 1064 
 1065     g_return_val_if_fail (conn != NULL, FALSE);
 1066     g_return_val_if_fail (object_data != NULL, FALSE);
 1067     g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 1068 
 1069     uid = e_mapi_util_mapi_id_to_string (object_data->mid);
 1070     if (uid) {
 1071         ICalTime *itt;
 1072         gchar *rev;
 1073 
 1074         itt = i_cal_time_new_from_timet_with_zone (object_data->last_modified, 0, i_cal_timezone_get_utc_timezone ());
 1075         rev = i_cal_time_as_ical_string (itt);
 1076         g_clear_object (&itt);
 1077 
 1078         *out_existing_objects = g_slist_prepend (*out_existing_objects,
 1079             e_cal_meta_backend_info_new (uid, rev, NULL, uid));
 1080 
 1081         g_free (uid);
 1082         g_free (rev);
 1083     }
 1084 
 1085     return TRUE;
 1086 }
 1087 
 1088 static gboolean
 1089 ecb_mapi_populate_mid_to_gid_cb (ECalCache *cal_cache,
 1090                  const gchar *uid,
 1091                  const gchar *rid,
 1092                  const gchar *revision,
 1093                  const gchar *object,
 1094                  const gchar *extra,
 1095                  guint32 custom_flags,
 1096                  EOfflineState offline_state,
 1097                  gpointer user_data)
 1098 {
 1099     GHashTable *mid_to_gid = user_data;
 1100 
 1101     g_return_val_if_fail (mid_to_gid != NULL, FALSE);
 1102 
 1103     if (uid && *uid && extra && *extra && g_strcmp0 (uid, extra) != 0)
 1104         g_hash_table_insert (mid_to_gid, g_strdup (extra), g_strdup (uid));
 1105 
 1106     return TRUE;
 1107 }
 1108 
 1109 static gboolean
 1110 ecb_mapi_list_existing_sync (ECalMetaBackend *meta_backend,
 1111                  gchar **out_new_sync_tag,
 1112                  GSList **out_existing_objects,
 1113                  GCancellable *cancellable,
 1114                  GError **error)
 1115 {
 1116     ECalBackendMAPI *cbmapi;
 1117     mapi_object_t obj_folder;
 1118     gboolean success;
 1119     GError *mapi_error = NULL;
 1120 
 1121     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
 1122     g_return_val_if_fail (out_existing_objects, FALSE);
 1123 
 1124     *out_existing_objects = NULL;
 1125 
 1126     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
 1127 
 1128     ecb_mapi_lock_connection (cbmapi);
 1129 
 1130     success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
 1131     if (success) {
 1132         success = e_mapi_connection_list_objects (cbmapi->priv->conn, &obj_folder, NULL, NULL,
 1133             ecb_mapi_list_existing_uids_cb, out_existing_objects, cancellable, &mapi_error);
 1134 
 1135         e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
 1136     }
 1137 
 1138     if (mapi_error) {
 1139         ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
 1140         ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to list items from a server"));
 1141         g_error_free (mapi_error);
 1142 
 1143         success = FALSE;
 1144     }
 1145 
 1146     ecb_mapi_unlock_connection (cbmapi);
 1147 
 1148     /* Components with GlobalId has UID the GlobalId, all other have MessageID,
 1149        but here the 'nfo->uid' is MessageID */
 1150     if (success) {
 1151         ECalCache *cal_cache;
 1152 
 1153         cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
 1154         if (cal_cache) {
 1155             GHashTable *mid_to_gid;
 1156 
 1157             mid_to_gid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 1158 
 1159             if (e_cal_cache_search_with_callback (cal_cache, NULL, ecb_mapi_populate_mid_to_gid_cb, mid_to_gid, cancellable, NULL) &&
 1160                 g_hash_table_size (mid_to_gid) > 0) {
 1161                 GSList *link;
 1162 
 1163                 for (link = *out_existing_objects; link; link = g_slist_next (link)) {
 1164                     ECalMetaBackendInfo *nfo = link->data;
 1165 
 1166                     if (nfo && nfo->uid) {
 1167                         const gchar *gid = g_hash_table_lookup (mid_to_gid, nfo->uid);
 1168 
 1169                         if (gid && *gid) {
 1170                             g_free (nfo->uid);
 1171                             nfo->uid = g_strdup (gid);
 1172                         }
 1173                     }
 1174                 }
 1175             }
 1176 
 1177             g_hash_table_destroy (mid_to_gid);
 1178             g_object_unref (cal_cache);
 1179         }
 1180     }
 1181 
 1182     return success;
 1183 }
 1184 
 1185 static gboolean
 1186 ecb_mapi_load_component_sync (ECalMetaBackend *meta_backend,
 1187                   const gchar *uid,
 1188                   const gchar *extra,
 1189                   ICalComponent **out_component,
 1190                   gchar **out_extra,
 1191                   GCancellable *cancellable,
 1192                   GError **error)
 1193 {
 1194     ECalBackendMAPI *cbmapi;
 1195     GSList *uids, *components = NULL;
 1196     gboolean success;
 1197     GError *local_error = NULL;
 1198 
 1199     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
 1200     g_return_val_if_fail (uid != NULL, FALSE);
 1201     g_return_val_if_fail (out_component != NULL, FALSE);
 1202 
 1203     *out_component = NULL;
 1204 
 1205     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
 1206 
 1207     uids = g_slist_prepend (NULL, (gpointer) uid);
 1208 
 1209     ecb_mapi_lock_connection (cbmapi);
 1210 
 1211     success = ecb_mapi_load_multiple_sync (cbmapi, uids, &components, cancellable, &local_error);
 1212     if (!success) {
 1213         mapi_object_t obj_folder;
 1214         mapi_id_t mid = 0;
 1215 
 1216         /* Not downloaded in the local cache yet, try to find it. */
 1217         if (ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, NULL)) {
 1218             if (e_mapi_connection_list_objects (cbmapi->priv->conn, &obj_folder,
 1219                 ecb_mapi_build_global_id_or_mid_restriction_from_uid, (gpointer) uid,
 1220                 ecb_mapi_list_for_one_mid_cb, &mid, cancellable, NULL) && mid) {
 1221                 LoadMultipleData lmd;
 1222 
 1223                 lmd.meta_backend = E_CAL_META_BACKEND (cbmapi);
 1224                 lmd.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
 1225                 lmd.out_components = &components;
 1226 
 1227                 success = e_mapi_connection_transfer_object (cbmapi->priv->conn, &obj_folder, mid,
 1228                     transfer_calendar_objects_cb, &lmd, cancellable, NULL);
 1229 
 1230                 if (success)
 1231                     g_clear_error (&local_error);
 1232             }
 1233 
 1234             e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, NULL);
 1235         }
 1236     }
 1237 
 1238     ecb_mapi_unlock_connection (cbmapi);
 1239 
 1240     if (success && components) {
 1241         *out_component = components->data;
 1242         g_slist_free (components);
 1243     } else {
 1244         g_slist_free_full (components, g_object_unref);
 1245     }
 1246 
 1247     if (local_error)
 1248         g_propagate_error (error, local_error);
 1249 
 1250     g_slist_free (uids);
 1251 
 1252     return success;
 1253 }
 1254 
 1255 static gboolean
 1256 ecb_mapi_save_component_sync (ECalMetaBackend *meta_backend,
 1257                   gboolean overwrite_existing,
 1258                   EConflictResolution conflict_resolution,
 1259                   const GSList *instances,
 1260                   const gchar *extra,
 1261                   guint32 opflags, /* bit-or of ECalOperationFlags */
 1262                   gchar **out_new_uid,
 1263                   gchar **out_new_extra,
 1264                   GCancellable *cancellable,
 1265                   GError **error)
 1266 {
 1267     ECalBackendMAPI *cbmapi;
 1268     ECalComponent *comp;
 1269     ICalComponent *icomp;
 1270     gboolean no_increment;
 1271     mapi_object_t obj_folder;
 1272     mapi_id_t mid = 0;
 1273     gboolean success;
 1274     GError *mapi_error = NULL;
 1275 
 1276     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
 1277     g_return_val_if_fail (instances != NULL, FALSE);
 1278     g_return_val_if_fail (out_new_uid != NULL, FALSE);
 1279 
 1280     *out_new_uid = NULL;
 1281 
 1282     if (instances->next ||
 1283         e_cal_component_is_instance (instances->data)) {
 1284         g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR,
 1285             _("Support for modifying single instances of a recurring appointment is not yet implemented. No change was made to the appointment on the server.")));
 1286         return FALSE;
 1287     }
 1288 
 1289     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
 1290 
 1291     icomp = i_cal_component_clone (e_cal_component_get_icalcomponent (instances->data));
 1292     no_increment = e_cal_util_component_remove_x_property (icomp, "X-EVOLUTION-IS-REPLY");
 1293 
 1294     comp = e_cal_component_new_from_icalcomponent (icomp);
 1295     if (!comp) {
 1296         g_propagate_error (error, ECC_ERROR (E_CAL_CLIENT_ERROR_INVALID_OBJECT));
 1297         return FALSE;
 1298     }
 1299 
 1300     ecb_mapi_lock_connection (cbmapi);
 1301 
 1302     success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
 1303     if (success) {
 1304         struct cal_cbdata cbdata = { 0 };
 1305         gboolean has_attendees = e_cal_component_has_attendees (comp);
 1306 
 1307         cbdata.kind = e_cal_backend_get_kind (E_CAL_BACKEND (meta_backend));
 1308         cbdata.comp = comp;
 1309         cbdata.is_modify = overwrite_existing;
 1310         cbdata.msgflags = MSGFLAG_READ;
 1311         cbdata.get_timezone = e_timezone_cache_get_timezone;
 1312         cbdata.get_tz_data = cbmapi;
 1313 
 1314         if (overwrite_existing) {
 1315             ecb_mapi_get_comp_mid (icomp, &mid);
 1316 
 1317             ecb_mapi_get_server_data (cbmapi, comp, &cbdata, cancellable);
 1318             if (ecb_mapi_modifier_is_organizer (cbmapi, comp)) {
 1319                 cbdata.meeting_type = has_attendees ? MEETING_OBJECT : NOT_A_MEETING;
 1320                 cbdata.resp = has_attendees ? olResponseOrganized : olResponseNone;
 1321                 if (!no_increment)
 1322                     cbdata.appt_seq += 1;
 1323                 free_and_dupe_str (cbdata.username, ecb_mapi_get_owner_name (cbmapi));
 1324                 free_and_dupe_str (cbdata.useridtype, "SMTP");
 1325                 free_and_dupe_str (cbdata.userid, ecb_mapi_get_owner_email (cbmapi));
 1326                 free_and_dupe_str (cbdata.ownername, ecb_mapi_get_owner_name (cbmapi));
 1327                 free_and_dupe_str (cbdata.owneridtype, "SMTP");
 1328                 free_and_dupe_str (cbdata.ownerid, ecb_mapi_get_owner_email (cbmapi));
 1329             } else {
 1330                 cbdata.resp = has_attendees ? ecb_mapi_find_my_response (cbmapi, comp) : olResponseNone;
 1331                 cbdata.meeting_type = has_attendees ? MEETING_OBJECT_RCVD : NOT_A_MEETING;
 1332             }
 1333 
 1334             success = e_mapi_connection_modify_object (cbmapi->priv->conn, &obj_folder, mid,
 1335                     e_mapi_cal_utils_comp_to_object, &cbdata, cancellable, &mapi_error);
 1336 
 1337             ecb_mapi_free_server_data (&cbdata);
 1338         } else {
 1339             cbdata.username = g_strdup (ecb_mapi_get_owner_name (cbmapi));
 1340             cbdata.useridtype = (gchar *) "SMTP";
 1341             cbdata.userid = g_strdup (ecb_mapi_get_owner_email (cbmapi));
 1342             cbdata.ownername = g_strdup (ecb_mapi_get_owner_name (cbmapi));
 1343             cbdata.owneridtype = (gchar *) "SMTP";
 1344             cbdata.ownerid = g_strdup (ecb_mapi_get_owner_email (cbmapi));
 1345 
 1346             cbdata.meeting_type = has_attendees ? MEETING_OBJECT : NOT_A_MEETING;
 1347             cbdata.resp = has_attendees ? olResponseOrganized : olResponseNone;
 1348             cbdata.appt_id = e_mapi_cal_util_get_new_appt_id (cbmapi->priv->conn, mapi_object_get_id (&obj_folder));
 1349             cbdata.appt_seq = 0;
 1350             cbdata.globalid = NULL;
 1351             cbdata.cleanglobalid = NULL;
 1352 
 1353             success = e_mapi_connection_create_object (cbmapi->priv->conn, &obj_folder, E_MAPI_CREATE_FLAG_NONE,
 1354                 e_mapi_cal_utils_comp_to_object, &cbdata, &mid, cancellable, &mapi_error);
 1355         }
 1356 
 1357         g_free (cbdata.username);
 1358         g_free (cbdata.userid);
 1359         g_free (cbdata.ownername);
 1360         g_free (cbdata.ownerid);
 1361 
 1362         e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
 1363     }
 1364 
 1365     if (mapi_error || !mid) {
 1366         ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
 1367         ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR,
 1368             overwrite_existing ? _("Failed to modify item on a server") : _("Failed to create item on a server"));
 1369         g_clear_error (&mapi_error);
 1370 
 1371         success = FALSE;
 1372     }
 1373 
 1374     ecb_mapi_unlock_connection (cbmapi);
 1375 
 1376     if (success)
 1377         *out_new_uid = e_mapi_util_mapi_id_to_string (mid);
 1378 
 1379     g_object_unref (comp);
 1380 
 1381     return success;
 1382 }
 1383 
 1384 static gboolean
 1385 ecb_mapi_remove_component_sync (ECalMetaBackend *meta_backend,
 1386                 EConflictResolution conflict_resolution,
 1387                 const gchar *uid,
 1388                 const gchar *extra,
 1389                 const gchar *object,
 1390                 guint32 opflags, /* bit-or of ECalOperationFlags */
 1391                 GCancellable *cancellable,
 1392                 GError **error)
 1393 {
 1394     ECalBackendMAPI *cbmapi;
 1395     mapi_id_t mid = 0;
 1396     gboolean success = TRUE;
 1397     GError *mapi_error = NULL;
 1398 
 1399     g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
 1400     g_return_val_if_fail (uid != NULL, FALSE);
 1401 
 1402     cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
 1403 
 1404     if (object) {
 1405         ICalComponent *icomp;
 1406 
 1407         icomp = i_cal_component_new_from_string (object);
 1408         if (icomp) {
 1409             ecb_mapi_get_comp_mid (icomp, &mid);
 1410             g_object_unref (icomp);
 1411         }
 1412     }
 1413 
 1414     if (mid || e_mapi_util_mapi_id_from_string (uid, &mid)) {
 1415         mapi_object_t obj_folder;
 1416 
 1417         ecb_mapi_lock_connection (cbmapi);
 1418 
 1419         success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
 1420         if (success) {
 1421             GSList *mids;
 1422 
 1423             mids = g_slist_prepend (NULL, &mid);
 1424 
 1425             success = e_mapi_connection_remove_items (cbmapi->priv->conn, &obj_folder, mids, cancellable, &mapi_error);
 1426 
 1427             e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
 1428 
 1429             g_slist_free (mids);
 1430         }
 1431 
 1432         ecb_mapi_unlock_connection (cbmapi);
 1433     }
 1434 
 1435     if (mapi_error || !mid) {
 1436         ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
 1437         ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to remove item from a server"));
 1438         g_clear_error (&mapi_error);
 1439 
 1440         success = FALSE;
 1441     }
 1442 
 1443     return success;
 1444 }
 1445 
 1446 static gchar *
 1447 ecb_mapi_get_backend_property (ECalBackend *backend,
 1448                    const gchar *prop_name)
 1449 {
 1450     ECalBackendMAPI *cbmapi;
 1451 
 1452     g_return_val_if_fail (prop_name != NULL, NULL);
 1453 
 1454     cbmapi = E_CAL_BACKEND_MAPI (backend);
 1455 
 1456     if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
 1457         return g_strjoin (
 1458             ",",
 1459             E_CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT,
 1460             E_CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS,
 1461             E_CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS,
 1462             E_CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS,
 1463             E_CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY,
 1464             E_CAL_STATIC_CAPABILITY_REMOVE_ALARMS,
 1465             E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE,
 1466             E_CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
 1467             E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES,
 1468             E_CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK,
 1469             E_CAL_STATIC_CAPABILITY_NO_CONV_TO_RECUR,
 1470             E_CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING,
 1471             E_CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED,
 1472             E_CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE,
 1473             E_CAL_STATIC_CAPABILITY_TASK_DATE_ONLY,
 1474             E_CAL_STATIC_CAPABILITY_TASK_NO_ALARM,
 1475             e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (backend)),
 1476             NULL);
 1477     } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) {
 1478         return g_strdup (ecb_mapi_get_owner_email (cbmapi));
 1479     } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
 1480         /* We don't support email alarms. This should not have been called. */
 1481         return NULL;
 1482     }
 1483 
 1484     /* Chain up to parent's method */
 1485     return E_CAL_BACKEND_CLASS (e_cal_backend_mapi_parent_class)->impl_get_backend_property (backend, prop_name);
 1486 }
 1487 
 1488 static void
 1489 ecb_mapi_send_objects_sync (ECalBackendSync *sync_backend,
 1490                 EDataCal *cal,
 1491                 GCancellable *cancellable,
 1492                 const gchar *calobj,
 1493                 guint32 opflags, /* bit-or of ECalOperationFlags */
 1494                 GSList **users,
 1495                 gchar **modified_calobj,
 1496                 GError **error)
 1497 {
 1498     ECalBackendMAPI *cbmapi;
 1499     EMapiConnection *conn;
 1500     ICalComponentKind kind;
 1501     ICalComponent *icomp;
 1502     GError *mapi_error = NULL;
 1503 
 1504     e_mapi_return_client_error_if_fail (E_IS_CAL_BACKEND_MAPI (sync_backend), E_CLIENT_ERROR_INVALID_ARG);
 1505     e_mapi_return_client_error_if_fail (calobj != NULL, E_CLIENT_ERROR_INVALID_ARG);
 1506 
 1507     cbmapi = E_CAL_BACKEND_MAPI (sync_backend);
 1508     kind = e_cal_backend_get_kind (E_CAL_BACKEND (sync_backend));
 1509 
 1510     ecb_mapi_lock_connection (cbmapi);
 1511 
 1512     if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbmapi), cancellable, &mapi_error) ||
 1513         !cbmapi->priv->conn) {
 1514         ecb_mapi_unlock_connection (cbmapi);
 1515 
 1516         if (!mapi_error)
 1517             g_propagate_error (error, EC_ERROR (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
 1518         else
 1519             ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL);
 1520         g_clear_error (&mapi_error);
 1521         return;
 1522     }
 1523 
 1524     conn = cbmapi->priv->conn;
 1525 
 1526     /* check the component for validity */
 1527     icomp = i_cal_parser_parse_string (calobj);
 1528     if (!icomp) {
 1529         ecb_mapi_unlock_connection (cbmapi);
 1530         g_propagate_error (error, ECC_ERROR (E_CAL_CLIENT_ERROR_INVALID_OBJECT));
 1531         return;
 1532     }
 1533 
 1534     *modified_calobj = NULL;
 1535     *users = NULL;
 1536 
 1537     if (i_cal_component_isa (icomp) == I_CAL_VCALENDAR_COMPONENT) {
 1538         ICalPropertyMethod method = i_cal_component_get_method (icomp);
 1539         ICalComponent *subcomp;
 1540 
 1541         for (subcomp = i_cal_component_get_first_component (icomp, kind);
 1542              subcomp;
 1543              g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (icomp, kind)) {
 1544             ECalComponent *comp;
 1545             struct cal_cbdata cbdata = { 0 };
 1546             mapi_id_t mid = 0;
 1547             const gchar *compuid;
 1548             gchar *propval;
 1549             struct SBinary_short globalid = { 0 }, cleanglobalid = { 0 };
 1550             struct timeval *exception_repleace_time = NULL, ex_rep_time = { 0 };
 1551             struct FILETIME creation_time = { 0 };
 1552             ICalTime *dtstamp;
 1553             mapi_object_t obj_folder;
 1554 
 1555             comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (subcomp));
 1556             if (!comp)
 1557                 continue;
 1558 
 1559             cbdata.kind = kind;
 1560             cbdata.comp = comp;
 1561             cbdata.is_modify = TRUE;
 1562             cbdata.msgflags = MSGFLAG_READ | MSGFLAG_SUBMIT | MSGFLAG_UNSENT;
 1563 
 1564             switch (method) {
 1565             case I_CAL_METHOD_REQUEST:
 1566                 cbdata.meeting_type = MEETING_REQUEST;
 1567                 cbdata.resp = olResponseNotResponded;
 1568                 break;
 1569             case I_CAL_METHOD_CANCEL:
 1570                 cbdata.meeting_type = MEETING_CANCEL;
 1571                 cbdata.resp = olResponseNotResponded;
 1572                 break;
 1573             case I_CAL_METHOD_REPLY:
 1574             case I_CAL_METHOD_RESPONSE:
 1575                 cbdata.meeting_type = MEETING_RESPONSE;
 1576                 cbdata.resp = ecb_mapi_find_my_response (cbmapi, comp);
 1577                 break;
 1578             default:
 1579                 cbdata.meeting_type = NOT_A_MEETING;
 1580                 cbdata.resp = olResponseNone;
 1581                 break;
 1582             }
 1583 
 1584             ecb_mapi_get_server_data (cbmapi, comp, &cbdata, cancellable);
 1585             free_and_dupe_str (cbdata.username, ecb_mapi_get_owner_name (cbmapi));
 1586             free_and_dupe_str (cbdata.useridtype, "SMTP");
 1587             free_and_dupe_str (cbdata.userid, ecb_mapi_get_owner_email (cbmapi));
 1588             free_and_dupe_str (cbdata.ownername, ecb_mapi_get_owner_name (cbmapi));
 1589             free_and_dupe_str (cbdata.owneridtype, "SMTP");
 1590             free_and_dupe_str (cbdata.ownerid, ecb_mapi_get_owner_email (cbmapi));
 1591             cbdata.get_timezone = e_timezone_cache_get_timezone;
 1592             cbdata.get_tz_data = cbmapi;
 1593 
 1594             compuid = e_cal_component_get_uid (comp);
 1595 
 1596             dtstamp = e_cal_component_get_dtstamp (comp);
 1597             if (!dtstamp)
 1598                 dtstamp = i_cal_time_new_null_time ();
 1599             e_mapi_util_time_t_to_filetime (i_cal_time_as_timet (dtstamp), &creation_time);
 1600 
 1601             propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-EXREPTIME");
 1602             if (propval && *propval) {
 1603                 mapi_id_t val64 = 0;
 1604 
 1605                 if (e_mapi_util_mapi_id_from_string (propval, &val64)) {
 1606                     memcpy (&ex_rep_time, &val64, 8);
 1607                     exception_repleace_time = &ex_rep_time;
 1608                 }
 1609             }
 1610             g_free (propval);
 1611 
 1612             /* inherit GlobalID from the source object, if available */
 1613             if (e_cal_component_get_icalcomponent (comp)) {
 1614                 propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-GLOBALID");
 1615                 if (propval && *propval) {
 1616                     gsize len = 0;
 1617 
 1618                     globalid.lpb = g_base64_decode (propval, &len);
 1619                     globalid.cb = len;
 1620 
 1621                     cleanglobalid.lpb = g_memdup (globalid.lpb, globalid.cb);
 1622                     cleanglobalid.cb = globalid.cb;
 1623 
 1624                     /* PidLidCleanGlobalObjectId is same as PidLidGlobalObjectId,
 1625                        only exception-information are zeros */
 1626                     if (cleanglobalid.lpb && cleanglobalid.cb > 20) {
 1627                         for (len = 16; len < 20; len++) {
 1628                             cleanglobalid.lpb[len] = 0;
 1629                         }
 1630                     }
 1631 
 1632                     compuid = NULL;
 1633                 }
 1634 
 1635                 g_free (propval);
 1636             }
 1637 
 1638             if (compuid) {
 1639                 e_mapi_cal_util_generate_globalobjectid (FALSE, compuid, exception_repleace_time,
 1640                     (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &globalid);
 1641                 e_mapi_cal_util_generate_globalobjectid (TRUE,  compuid, exception_repleace_time,
 1642                     (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &cleanglobalid);
 1643             }
 1644 
 1645             g_clear_object (&dtstamp);
 1646 
 1647             if (cbdata.globalid)
 1648                 e_mapi_util_free_sbinary_short (cbdata.globalid);
 1649             if (cbdata.cleanglobalid)
 1650                 e_mapi_util_free_sbinary_short (cbdata.cleanglobalid);
 1651             cbdata.globalid = &globalid;
 1652             cbdata.cleanglobalid = &cleanglobalid;
 1653 
 1654             mid = 0;
 1655             if (e_mapi_connection_open_default_folder (conn, olFolderSentMail, &obj_folder, cancellable, &mapi_error)) {
 1656                 e_mapi_connection_create_object (conn, &obj_folder, E_MAPI_CREATE_FLAG_SUBMIT,
 1657                                  e_mapi_cal_utils_comp_to_object, &cbdata,
 1658                                  &mid, cancellable, &mapi_error);
 1659 
 1660                 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 1661             }
 1662 
 1663             cbdata.globalid = NULL;
 1664             cbdata.cleanglobalid = NULL;
 1665             ecb_mapi_free_server_data (&cbdata);
 1666             g_free (globalid.lpb);
 1667             g_free (cleanglobalid.lpb);
 1668 
 1669             if (!mid) {
 1670                 ecb_mapi_unlock_connection (cbmapi);
 1671                 g_object_unref (comp);
 1672                 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to create item on a server"));
 1673                 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
 1674                 if (mapi_error)
 1675                     g_error_free (mapi_error);
 1676                 return;
 1677             }
 1678 
 1679             g_object_unref (comp);
 1680         }
 1681     }
 1682 
 1683     ecb_mapi_unlock_connection (cbmapi);
 1684 
 1685     *modified_calobj = g_strdup (calobj);
 1686 
 1687     g_object_unref (icomp);
 1688 }
 1689 
 1690 static void
 1691 ecb_mapi_get_free_busy_sync (ECalBackendSync *sync_backend,
 1692                  EDataCal *cal,
 1693                  GCancellable *cancellable,
 1694                  const GSList *users,
 1695                  time_t start,
 1696                  time_t end,
 1697                  GSList **freebusyobjs,
 1698                  GError **error)
 1699 {
 1700     ECalBackendMAPI *cbmapi;
 1701     GError *mapi_error = NULL;
 1702 
 1703     g_return_if_fail (E_IS_CAL_BACKEND_MAPI (sync_backend));
 1704 
 1705     cbmapi = E_CAL_BACKEND_MAPI (sync_backend);
 1706 
 1707     ecb_mapi_lock_connection (cbmapi);
 1708 
 1709     if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbmapi), cancellable, &mapi_error) ||
 1710         !cbmapi->priv->conn) {
 1711         ecb_mapi_unlock_connection (cbmapi);
 1712 
 1713         if (!mapi_error)
 1714             g_propagate_error (error, EC_ERROR (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
 1715         else
 1716             ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL);
 1717         g_clear_error (&mapi_error);
 1718         return;
 1719     }
 1720 
 1721     if (!e_mapi_cal_utils_get_free_busy_data (cbmapi->priv->conn, users, start, end, freebusyobjs, cancellable, &mapi_error)) {
 1722         ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to get Free/Busy data"));
 1723         ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
 1724 
 1725         if (mapi_error)
 1726             g_error_free (mapi_error);
 1727     }
 1728 
 1729     ecb_mapi_unlock_connection (cbmapi);
 1730 }
 1731 
 1732 static gboolean
 1733 ecb_mapi_get_destination_address (EBackend *backend,
 1734                   gchar **host,
 1735                   guint16 *port)
 1736 {
 1737     ESourceRegistry *registry;
 1738     ESource *source;
 1739     gboolean result = FALSE;
 1740 
 1741     g_return_val_if_fail (host != NULL, FALSE);
 1742     g_return_val_if_fail (port != NULL, FALSE);
 1743 
 1744     registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
 1745     source = e_backend_get_source (backend);
 1746 
 1747     /* Sanity checking */
 1748     if (!registry || !source || !e_source_get_parent (source))
 1749         return FALSE;
 1750 
 1751     source = e_source_registry_ref_source (registry, e_source_get_parent (source));
 1752     if (!source)
 1753         return FALSE;
 1754 
 1755     if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
 1756         ESourceAuthentication *auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
 1757 
 1758         *host = g_strdup (e_source_authentication_get_host (auth));
 1759         *port = e_source_authentication_get_port (auth);
 1760 
 1761         if (!*port)
 1762             *port = 135;
 1763 
 1764         result = *host && **host;
 1765         if (!result) {
 1766             g_free (*host);
 1767             *host = NULL;
 1768         }
 1769     }
 1770 
 1771     g_object_unref (source);
 1772 
 1773     return result;
 1774 }
 1775 
 1776 static gboolean
 1777 ecb_mapi_update_tzid_cb (ECache *cache,
 1778              const gchar *uid,
 1779              const gchar *revision,
 1780              const gchar *object,
 1781              EOfflineState offline_state,
 1782              gint ncols,
 1783              const gchar *column_names[],
 1784              const gchar *column_values[],
 1785              gchar **out_revision,
 1786              gchar **out_object,
 1787              EOfflineState *out_offline_state,
 1788              ECacheColumnValues **out_other_columns,
 1789              gpointer user_data)
 1790 {
 1791     ICalComponent *icomp;
 1792     ICalProperty *prop;
 1793     gboolean changed = FALSE;
 1794 
 1795     g_return_val_if_fail (object != NULL, FALSE);
 1796     g_return_val_if_fail (out_object != NULL, FALSE);
 1797 
 1798     icomp = i_cal_component_new_from_string (object);
 1799     if (!icomp)
 1800         return TRUE;
 1801 
 1802     prop = i_cal_component_get_first_property (icomp, I_CAL_DTSTART_PROPERTY);
 1803     if (prop && e_cal_util_property_has_parameter (prop, I_CAL_TZID_PARAMETER)) {
 1804         ICalTime *itt;
 1805 
 1806         itt = i_cal_property_get_dtstart (prop);
 1807         if (itt && i_cal_time_is_valid_time (itt) && i_cal_time_is_utc (itt)) {
 1808             i_cal_time_set_timezone (itt, NULL);
 1809             i_cal_property_set_dtstart (prop, itt);
 1810             changed = TRUE;
 1811         }
 1812 
 1813         g_clear_object (&itt);
 1814     }
 1815     g_clear_object (&prop);
 1816 
 1817     prop = i_cal_component_get_first_property (icomp, I_CAL_DTEND_PROPERTY);
 1818     if (prop && e_cal_util_property_has_parameter (prop, I_CAL_TZID_PARAMETER)) {
 1819         ICalTime *itt;
 1820 
 1821         itt = i_cal_property_get_dtend (prop);
 1822         if (itt && i_cal_time_is_valid_time (itt) && i_cal_time_is_utc (itt)) {
 1823             i_cal_time_set_timezone (itt, NULL);
 1824             i_cal_property_set_dtend (prop, itt);
 1825             changed = TRUE;
 1826         }
 1827 
 1828         g_clear_object (&itt);
 1829     }
 1830     g_clear_object (&prop);
 1831 
 1832     if (changed)
 1833         *out_object = i_cal_component_as_ical_string (icomp);
 1834 
 1835     g_object_unref (icomp);
 1836 
 1837     return TRUE;
 1838 }
 1839 
 1840 static void
 1841 ecb_mapi_migrate (ECalBackendMAPI *cbmapi,
 1842           ECalCache *cal_cache,
 1843           gint data_version)
 1844 {
 1845     if (data_version < 1) {
 1846         /* DTSTART/DTEND stores with both TZID and 'Z' suffix */
 1847         e_cache_foreach_update (E_CACHE (cal_cache), E_CACHE_EXCLUDE_DELETED, NULL,
 1848             ecb_mapi_update_tzid_cb, NULL, NULL, NULL);
 1849     }
 1850 }
 1851 
 1852 static gchar *
 1853 ecb_mapi_dup_component_revision_cb (ECalCache *cal_cache,
 1854                     ICalComponent *icomp)
 1855 {
 1856     ICalProperty *prop;
 1857     ICalTime *itt;
 1858     gchar *res;
 1859 
 1860     g_return_val_if_fail (I_CAL_IS_COMPONENT (icomp), NULL);
 1861 
 1862     prop = i_cal_component_get_first_property (icomp, I_CAL_LASTMODIFIED_PROPERTY);
 1863     if (!prop)
 1864         return NULL;
 1865 
 1866     itt = i_cal_property_get_lastmodified (prop);
 1867     res = i_cal_time_as_ical_string (itt);
 1868 
 1869     g_clear_object (&prop);
 1870     g_clear_object (&itt);
 1871 
 1872     return res;
 1873 }
 1874 
 1875 static void
 1876 ecb_mapi_constructed (GObject *object)
 1877 {
 1878     ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
 1879     ECalCache *cal_cache;
 1880     gint data_version;
 1881 
 1882     /* Chaing up to parent's method */
 1883     G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->constructed (object);
 1884 
 1885     /* Reset the connectable, it steals data from Authentication extension,
 1886        where is written no address */
 1887     e_backend_set_connectable (E_BACKEND (object), NULL);
 1888 
 1889     e_cal_backend_set_writable (E_CAL_BACKEND (cbmapi), TRUE);
 1890 
 1891     cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbmapi));
 1892 
 1893     g_signal_connect (cal_cache, "dup-component-revision",
 1894         G_CALLBACK (ecb_mapi_dup_component_revision_cb), NULL);
 1895 
 1896     data_version = e_cache_get_key_int (E_CACHE (cal_cache), EMA_DATA_VERSION_KEY, NULL);
 1897 
 1898     if (EMA_DATA_VERSION != data_version) {
 1899         GError *local_error = NULL;
 1900 
 1901         ecb_mapi_migrate (cbmapi, cal_cache, data_version);
 1902 
 1903         if (!e_cache_set_key_int (E_CACHE (cal_cache), EMA_DATA_VERSION_KEY, EMA_DATA_VERSION, &local_error)) {
 1904             g_warning ("%s: Failed to store data version: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
 1905         }
 1906 
 1907         g_clear_error (&local_error);
 1908     }
 1909 
 1910     g_clear_object (&cal_cache);
 1911 }
 1912 
 1913 static void
 1914 ecb_mapi_dispose (GObject *object)
 1915 {
 1916     ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
 1917 
 1918     g_clear_object (&cbmapi->priv->conn);
 1919 
 1920     /* Chain up to parent's method */
 1921     G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose (object);
 1922 }
 1923 
 1924 static void
 1925 ecb_mapi_finalize (GObject *object)
 1926 {
 1927     ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
 1928 
 1929     g_rec_mutex_clear (&cbmapi->priv->conn_lock);
 1930 
 1931     /* Chain up to parent's method */
 1932     G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->finalize (object);
 1933 }
 1934 
 1935 static void
 1936 e_cal_backend_mapi_class_init (ECalBackendMAPIClass *klass)
 1937 {
 1938     GObjectClass *object_class;
 1939     EBackendClass *backend_class;
 1940     ECalBackendClass *cal_backend_class;
 1941     ECalBackendSyncClass *sync_backend_class;
 1942     ECalMetaBackendClass *meta_backend_class;
 1943 
 1944     meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
 1945     meta_backend_class->connect_sync = ecb_mapi_connect_sync;
 1946     meta_backend_class->disconnect_sync = ecb_mapi_disconnect_sync;
 1947     meta_backend_class->get_changes_sync = ecb_mapi_get_changes_sync;
 1948     meta_backend_class->list_existing_sync = ecb_mapi_list_existing_sync;
 1949     meta_backend_class->load_component_sync = ecb_mapi_load_component_sync;
 1950     meta_backend_class->save_component_sync = ecb_mapi_save_component_sync;
 1951     meta_backend_class->remove_component_sync = ecb_mapi_remove_component_sync;
 1952 
 1953     cal_backend_class = E_CAL_BACKEND_CLASS (klass);
 1954     cal_backend_class->impl_get_backend_property = ecb_mapi_get_backend_property;
 1955 
 1956     sync_backend_class = E_CAL_BACKEND_SYNC_CLASS (klass);
 1957     sync_backend_class->send_objects_sync = ecb_mapi_send_objects_sync;
 1958     sync_backend_class->get_free_busy_sync = ecb_mapi_get_free_busy_sync;
 1959 
 1960     backend_class = E_BACKEND_CLASS (klass);
 1961     backend_class->get_destination_address = ecb_mapi_get_destination_address;
 1962 
 1963     object_class = G_OBJECT_CLASS (klass);
 1964     object_class->constructed = ecb_mapi_constructed;
 1965     object_class->dispose = ecb_mapi_dispose;
 1966     object_class->finalize = ecb_mapi_finalize;
 1967 }
 1968 
 1969 static void
 1970 e_cal_backend_mapi_init (ECalBackendMAPI *cbmapi)
 1971 {
 1972     cbmapi->priv = e_cal_backend_mapi_get_instance_private (cbmapi);
 1973 
 1974     g_rec_mutex_init (&cbmapi->priv->conn_lock);
 1975 }