"Fossies" - the Fresh Open Source Software Archive

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

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Authors:
   18  *     Johnny Jacob <jjohnny@novell.com>
   19  *
   20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
   21  *
   22  */
   23 
   24 #include "evolution-mapi-config.h"
   25 
   26 #include <string.h>
   27 #include <time.h>
   28 
   29 #include <glib.h>
   30 
   31 #include <libmapi/libmapi.h>
   32 #include <e-mapi-defs.h>
   33 #include <e-mapi-utils.h>
   34 #include <e-mapi-folder.h>
   35 #include <e-mapi-cal-utils.h>
   36 #include "e-mapi-mail-utils.h"
   37 
   38 #include "camel-mapi-store.h"
   39 #include "camel-mapi-store-summary.h"
   40 #include "camel-mapi-folder.h"
   41 #include "camel-mapi-folder-summary.h"
   42 
   43 #define DEBUG_FN( ) printf("----%p %s\n", g_thread_self(), G_STRFUNC);
   44 #define SUMMARY_FETCH_BATCH_COUNT 150
   45 #define d(x)
   46 
   47 #define CAMEL_MAPI_FOLDER_LOCK(f, l) \
   48     (g_mutex_lock(&((CamelMapiFolder *)f)->priv->l))
   49 #define CAMEL_MAPI_FOLDER_UNLOCK(f, l) \
   50     (g_mutex_unlock(&((CamelMapiFolder *)f)->priv->l))
   51 
   52 struct _CamelMapiFolderPrivate {
   53     GMutex search_lock; /* for locking the search object */
   54 
   55     gchar *foreign_username;
   56 };
   57 
   58 static gboolean
   59 cmf_open_folder (CamelMapiFolder *mapi_folder,
   60          EMapiConnection *conn,
   61          mapi_object_t *obj_folder,
   62          GCancellable *cancellable,
   63          GError **perror)
   64 {
   65     gboolean res;
   66     GError *mapi_error = NULL;
   67 
   68     g_return_val_if_fail (mapi_folder != NULL, FALSE);
   69     g_return_val_if_fail (conn != NULL, FALSE);
   70     g_return_val_if_fail (obj_folder != NULL, FALSE);
   71 
   72     if ((mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)
   73         res = e_mapi_connection_open_foreign_folder (conn, mapi_folder->priv->foreign_username, mapi_folder->folder_id, obj_folder, cancellable, &mapi_error);
   74     else if ((mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0)
   75         res = e_mapi_connection_open_public_folder (conn, mapi_folder->folder_id, obj_folder, cancellable, &mapi_error);
   76     else
   77         res = e_mapi_connection_open_personal_folder (conn, mapi_folder->folder_id, obj_folder, cancellable, &mapi_error);
   78 
   79     if (mapi_error) {
   80         CamelMapiStore *mapi_store;
   81 
   82         mapi_store = CAMEL_MAPI_STORE (camel_folder_get_parent_store (CAMEL_FOLDER (mapi_folder)));
   83         camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
   84 
   85         g_propagate_error (perror, mapi_error);
   86     }
   87 
   88     return res;
   89 }
   90 
   91 /*for syncing flags back to server*/
   92 typedef struct {
   93     guint32 changed;
   94     guint32 bits;
   95 } flags_diff_t;
   96 
   97 /*For collecting summary info from server*/
   98 
   99 static gboolean     mapi_folder_synchronize_sync
  100                         (CamelFolder *folder,
  101                          gboolean expunge,
  102                          GCancellable *cancellable,
  103                          GError **error);
  104 
  105 G_DEFINE_TYPE_WITH_PRIVATE (CamelMapiFolder, camel_mapi_folder, CAMEL_TYPE_OFFLINE_FOLDER)
  106 
  107 static GPtrArray *
  108 mapi_folder_search_by_expression (CamelFolder *folder,
  109                   const gchar *expression,
  110                   GCancellable *cancellable,
  111                   GError **error)
  112 {
  113     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER(folder);
  114     GPtrArray *matches;
  115 
  116     CAMEL_MAPI_FOLDER_LOCK(mapi_folder, search_lock);
  117     camel_folder_search_set_folder (mapi_folder->search, folder);
  118     matches = camel_folder_search_search(mapi_folder->search, expression, NULL, cancellable, error);
  119     CAMEL_MAPI_FOLDER_UNLOCK(mapi_folder, search_lock);
  120 
  121     return matches;
  122 }
  123 
  124 static GPtrArray *
  125 mapi_folder_search_by_uids (CamelFolder *folder,
  126                 const gchar *expression,
  127                 GPtrArray *uids,
  128                 GCancellable *cancellable,
  129                 GError **error)
  130 {
  131     GPtrArray *matches;
  132     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (folder);
  133 
  134     if (uids->len == 0)
  135         return g_ptr_array_new ();
  136 
  137     CAMEL_MAPI_FOLDER_LOCK (mapi_folder, search_lock);
  138     camel_folder_search_set_folder (mapi_folder->search, folder);
  139     matches = camel_folder_search_search (mapi_folder->search, expression, uids, cancellable, error);
  140     CAMEL_MAPI_FOLDER_UNLOCK (mapi_folder, search_lock);
  141 
  142     return matches;
  143 }
  144 
  145 static void
  146 mapi_set_message_id (CamelMessageInfo *mi,
  147              const gchar *message_id)
  148 {
  149     gchar *msgid;
  150     guint8 *digest;
  151     gsize length;
  152     CamelSummaryMessageID tmp_msgid;
  153 
  154     msgid = camel_header_msgid_decode (message_id);
  155     if (msgid) {
  156         GChecksum *checksum;
  157 
  158         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
  159         digest = g_alloca (length);
  160 
  161         checksum = g_checksum_new (G_CHECKSUM_MD5);
  162         g_checksum_update (checksum, (guchar *) msgid, -1);
  163         g_checksum_get_digest (checksum, digest, &length);
  164         g_checksum_free (checksum);
  165 
  166         memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash));
  167         g_free (msgid);
  168 
  169         camel_message_info_set_message_id (mi, tmp_msgid.id.id);
  170     }
  171 }
  172 
  173 static void
  174 mapi_set_message_references (CamelMessageInfo *mi,
  175                  const gchar *references,
  176                  const gchar *in_reply_to)
  177 {
  178     GSList *refs, *irt, *link;
  179     guint8 *digest;
  180     gsize length;
  181     CamelSummaryMessageID tmp_msgid;
  182 
  183     refs = camel_header_references_decode (references);
  184     irt = camel_header_references_decode (in_reply_to);
  185     if (refs || irt) {
  186         GArray *references_arr;
  187 
  188         if (irt) {
  189             /* The References field is populated from the "References" and/or "In-Reply-To"
  190                headers. If both headers exist, take the first thing in the In-Reply-To header
  191                that looks like a Message-ID, and append it to the References header. */
  192 
  193             refs = g_slist_concat (irt, refs);
  194         }
  195 
  196         references_arr = g_array_sized_new (FALSE, FALSE, sizeof (guint64), g_slist_length (refs));
  197 
  198         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
  199         digest = g_alloca (length);
  200 
  201         for (link = refs; link; link = g_slist_next (link)) {
  202             GChecksum *checksum;
  203 
  204             checksum = g_checksum_new (G_CHECKSUM_MD5);
  205             g_checksum_update (checksum, (guchar *) link->data, -1);
  206             g_checksum_get_digest (checksum, digest, &length);
  207             g_checksum_free (checksum);
  208 
  209             memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash));
  210 
  211             g_array_append_val (references_arr, tmp_msgid.id.id);
  212         }
  213 
  214         g_slist_free_full (refs, g_free);
  215 
  216         camel_message_info_take_references (mi, references_arr);
  217     }
  218 }
  219 
  220 static void
  221 mapi_utils_do_flags_diff (flags_diff_t *diff, guint32 old, guint32 _new)
  222 {
  223     diff->changed = old ^ _new;
  224     diff->bits = _new & diff->changed;
  225 }
  226 
  227 static void
  228 add_message_to_cache (CamelMapiFolder *mapi_folder, const gchar *uid, CamelMimeMessage **msg, GCancellable *cancellable)
  229 {
  230     CamelFolder *folder;
  231     GIOStream *base_stream;
  232 
  233     g_return_if_fail (mapi_folder != NULL);
  234     g_return_if_fail (msg != NULL);
  235     g_return_if_fail (*msg != NULL);
  236 
  237     folder = CAMEL_FOLDER (mapi_folder);
  238     g_return_if_fail (folder != NULL);
  239 
  240     camel_folder_summary_lock (camel_folder_get_folder_summary (folder));
  241 
  242     base_stream = camel_data_cache_add (mapi_folder->cache, "cache", uid, NULL);
  243     if (base_stream != NULL) {
  244         CamelStream *cache_stream;
  245 
  246         cache_stream = camel_stream_new (base_stream);
  247         g_object_unref (base_stream);
  248 
  249         if (camel_data_wrapper_write_to_stream_sync ((CamelDataWrapper *) (*msg), cache_stream, cancellable, NULL) == -1
  250             || camel_stream_flush (cache_stream, cancellable, NULL) == -1) {
  251             camel_data_cache_remove (mapi_folder->cache, "cache", uid, NULL);
  252         } else {
  253             CamelMimeMessage *msg2;
  254 
  255             /* workaround to get message back from cache, as that one is properly
  256                encoded with attachments and so on. Not sure what's going wrong when
  257                composing message in memory, but when they are read from the cache
  258                they appear properly in the UI. */
  259             msg2 = camel_mime_message_new ();
  260             g_seekable_seek (G_SEEKABLE (cache_stream), 0, G_SEEK_SET, NULL, NULL);
  261             if (!camel_data_wrapper_construct_from_stream_sync (CAMEL_DATA_WRAPPER (msg2), cache_stream, cancellable, NULL)) {
  262                 g_object_unref (msg2);
  263             } else {
  264                 g_object_unref (*msg);
  265                 *msg = msg2;
  266             }
  267         }
  268 
  269         g_object_unref (cache_stream);
  270     }
  271 
  272     camel_folder_summary_unlock (camel_folder_get_folder_summary (folder));
  273 }
  274 
  275 struct GatherChangedObjectsData
  276 {
  277     CamelFolderSummary *summary;
  278     mapi_id_t fid;
  279     GSList *to_update; /* mapi_id_t * */
  280     GHashTable *removed_uids;
  281     time_t latest_last_modify;
  282     gboolean is_public_folder;
  283 };
  284 
  285 static gboolean
  286 gather_changed_objects_to_slist (EMapiConnection *conn,
  287                  TALLOC_CTX *mem_ctx,
  288                  const ListObjectsData *object_data,
  289                  guint32 obj_index,
  290                  guint32 obj_total,
  291                  gpointer user_data,
  292                  GCancellable *cancellable,
  293                  GError **perror)
  294 {
  295     struct GatherChangedObjectsData *gco = user_data;
  296     gchar *uid_str;
  297     gboolean update = FALSE;
  298 
  299     g_return_val_if_fail (gco != NULL, FALSE);
  300     g_return_val_if_fail (object_data != NULL, FALSE);
  301 
  302     uid_str = e_mapi_util_mapi_id_to_string (object_data->mid);
  303     if (!uid_str)
  304         return FALSE;
  305 
  306     if (camel_folder_summary_check_uid (gco->summary, uid_str)) {
  307         CamelMessageInfo *info;
  308 
  309         if (gco->removed_uids)
  310             g_hash_table_remove (gco->removed_uids, uid_str);
  311 
  312         info = camel_folder_summary_get (gco->summary, uid_str);
  313         if (info) {
  314             CamelMapiMessageInfo *minfo = CAMEL_MAPI_MESSAGE_INFO (info);
  315 
  316             if (camel_mapi_message_info_get_last_modified (minfo) != object_data->last_modified
  317                 && (object_data->msg_flags & MSGFLAG_UNMODIFIED) == 0) {
  318                 update = TRUE;
  319             } else {
  320                 guint32 mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_ATTACHMENTS, flags = 0;
  321 
  322                 /* do not change unread state for known messages in public folders */
  323                 if (gco->is_public_folder)
  324                     mask &= ~CAMEL_MESSAGE_SEEN;
  325 
  326                 if ((object_data->msg_flags & MSGFLAG_READ) != 0)
  327                     flags |= CAMEL_MESSAGE_SEEN;
  328                 if ((object_data->msg_flags & MSGFLAG_HASATTACH) != 0)
  329                     flags |= CAMEL_MESSAGE_ATTACHMENTS;
  330 
  331                 if ((camel_message_info_get_flags (info) & CAMEL_MAPI_MESSAGE_WITH_READ_RECEIPT) != 0) {
  332                     if ((object_data->msg_flags & MSGFLAG_RN_PENDING) == 0 &&
  333                         !camel_message_info_get_user_flag (info, "receipt-handled")) {
  334                         camel_message_info_set_user_flag (info, "receipt-handled", TRUE);
  335                     }
  336                 }
  337 
  338                 if ((camel_message_info_get_flags (info) & mask) != (flags & mask)) {
  339                     camel_message_info_set_flags (info, mask, flags);
  340                     camel_mapi_message_info_set_server_flags (minfo, camel_message_info_get_flags (info));
  341                 }
  342             }
  343 
  344             g_clear_object (&info);
  345         }
  346     } else {
  347         update = TRUE;
  348     }
  349 
  350     if (update) {
  351         mapi_id_t *pmid = g_new0 (mapi_id_t, 1);
  352 
  353         *pmid = object_data->mid;
  354 
  355         gco->to_update = g_slist_prepend (gco->to_update, pmid);
  356     }
  357 
  358     if (gco->latest_last_modify < object_data->last_modified)
  359         gco->latest_last_modify = object_data->last_modified;
  360 
  361     if (obj_total > 0)
  362         camel_operation_progress (cancellable, obj_index * 100 / obj_total);
  363 
  364     g_free (uid_str);
  365 
  366     return TRUE;
  367 }
  368 
  369 static void
  370 update_message_info (CamelMessageInfo *info,
  371              /* const */ EMapiObject *object,
  372              gboolean is_new,
  373              gboolean is_public_folder,
  374              gboolean user_has_read)
  375 {
  376     guint32 flags = 0, mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_ATTACHMENTS | CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_FORWARDED | CAMEL_MAPI_MESSAGE_WITH_READ_RECEIPT;
  377     const uint32_t *pmsg_flags, *picon_index;
  378     const struct FILETIME *last_modified;
  379     const uint8_t *pread_receipt;
  380     const gchar *msg_class;
  381     uint32_t msg_flags;
  382 
  383     g_return_if_fail (info != NULL);
  384     g_return_if_fail (object != NULL);
  385 
  386     pmsg_flags = e_mapi_util_find_array_propval (&object->properties, PidTagMessageFlags);
  387     last_modified = e_mapi_util_find_array_propval (&object->properties, PidTagLastModificationTime);
  388     picon_index = e_mapi_util_find_array_propval (&object->properties, PidTagIconIndex);
  389     pread_receipt = e_mapi_util_find_array_propval (&object->properties, PidTagReadReceiptRequested);
  390     msg_class = e_mapi_util_find_array_propval (&object->properties, PidTagMessageClass);
  391 
  392     if (!camel_message_info_get_size (info)) {
  393         const uint32_t *msg_size;
  394 
  395         msg_size = e_mapi_util_find_array_propval (&object->properties, PidTagMessageSize);
  396         camel_message_info_set_size (info, msg_size ? *msg_size : 0);
  397     }
  398 
  399     if (msg_class && g_str_has_prefix (msg_class, "REPORT.IPM.Note.IPNRN"))
  400         pread_receipt = NULL;
  401 
  402     msg_flags = pmsg_flags ? *pmsg_flags : 0;
  403 
  404     if (!is_new && is_public_folder) {
  405         /* do not change unread state for known messages in public folders */
  406         if ((user_has_read ? 1 : 0) != ((msg_flags & MSGFLAG_READ) ? 1 : 0))
  407             msg_flags = (msg_flags & (~MSGFLAG_READ)) | (user_has_read ? MSGFLAG_READ : 0);
  408     }
  409 
  410     camel_mapi_message_info_set_last_modified (CAMEL_MAPI_MESSAGE_INFO (info),
  411         last_modified ? e_mapi_util_filetime_to_time_t (last_modified) : 0);
  412 
  413     if ((msg_flags & MSGFLAG_READ) != 0)
  414         flags |= CAMEL_MESSAGE_SEEN;
  415     if ((msg_flags & MSGFLAG_HASATTACH) != 0)
  416         flags |= CAMEL_MESSAGE_ATTACHMENTS;
  417     if (picon_index) {
  418         if (*picon_index == 0x105)
  419             flags |= CAMEL_MESSAGE_ANSWERED;
  420         if (*picon_index == 0x106)
  421             flags |= CAMEL_MESSAGE_FORWARDED;
  422     }
  423 
  424     if (pread_receipt && *pread_receipt)
  425         flags |= CAMEL_MAPI_MESSAGE_WITH_READ_RECEIPT;
  426 
  427     if (pread_receipt && *pread_receipt && (msg_flags & MSGFLAG_RN_PENDING) == 0)
  428         camel_message_info_set_user_flag (info, "receipt-handled", TRUE);
  429 
  430     if ((camel_message_info_get_flags (info) & mask) != flags) {
  431         if (is_new)
  432             camel_message_info_set_flags (info, ~0, flags);
  433         else
  434             camel_message_info_set_flags (info, mask, flags);
  435         camel_mapi_message_info_set_server_flags (CAMEL_MAPI_MESSAGE_INFO (info), camel_message_info_get_flags (info));
  436     }
  437 }
  438 
  439 static gsize
  440 camel_mapi_get_message_size (CamelMimeMessage *msg)
  441 {
  442     if (!CAMEL_IS_DATA_WRAPPER (msg))
  443         return 0;
  444 
  445     /* do not 'decode', let's be interested in the raw message size */
  446     return camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (msg), NULL, NULL);
  447 }
  448 
  449 struct GatherObjectSummaryData
  450 {
  451     CamelFolder *folder;
  452     CamelFolderChangeInfo *changes;
  453     gboolean is_public_folder;
  454 };
  455 
  456 static void
  457 remove_removed_uids_cb (gpointer uid_str, gpointer value, gpointer user_data)
  458 {
  459     struct GatherObjectSummaryData *gos = user_data;
  460 
  461     g_return_if_fail (gos != NULL);
  462     g_return_if_fail (gos->folder != NULL);
  463     g_return_if_fail (gos->changes != NULL);
  464 
  465     camel_folder_change_info_remove_uid (gos->changes, uid_str);
  466     camel_folder_summary_remove_uid (camel_folder_get_folder_summary (gos->folder), uid_str);
  467     camel_data_cache_remove (CAMEL_MAPI_FOLDER (gos->folder)->cache, "cache", uid_str, NULL);
  468 }
  469 
  470 static gboolean
  471 gather_object_for_offline_cb (EMapiConnection *conn,
  472                   TALLOC_CTX *mem_ctx,
  473                   /* const */ EMapiObject *object,
  474                   guint32 obj_index,
  475                   guint32 obj_total,
  476                   gpointer user_data,
  477                   GCancellable *cancellable,
  478                   GError **perror)
  479 {
  480     struct GatherObjectSummaryData *gos = user_data;
  481     CamelMimeMessage *msg;
  482 
  483     g_return_val_if_fail (gos != NULL, FALSE);
  484     g_return_val_if_fail (gos->folder != NULL, FALSE);
  485     g_return_val_if_fail (object != NULL, FALSE);
  486 
  487     msg = e_mapi_mail_utils_object_to_message (conn, object);
  488     if (msg) {
  489         CamelFolderSummary *folder_summary;
  490         gchar *uid_str;
  491         const mapi_id_t *pmid;
  492         CamelMessageInfo *info;
  493         gboolean is_new;
  494         gboolean user_has_read = FALSE;
  495 
  496         pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
  497 
  498         if (!pmid) {
  499             g_debug ("%s: Received message [%d/%d] without PidTagMid", G_STRFUNC, obj_index, obj_total);
  500             e_mapi_debug_dump_object (object, TRUE, 3);
  501             return TRUE;
  502         }
  503 
  504         if (!e_mapi_util_find_array_propval (&object->properties, PidTagLastModificationTime)) {
  505             g_debug ("%s: Received message [%d/%d] without PidTagLastModificationTime", G_STRFUNC, obj_index, obj_total);
  506             e_mapi_debug_dump_object (object, TRUE, 3);
  507         }
  508 
  509         uid_str = e_mapi_util_mapi_id_to_string (*pmid);
  510         if (!uid_str)
  511             return FALSE;
  512 
  513         folder_summary = camel_folder_get_folder_summary (gos->folder);
  514         is_new = !camel_folder_summary_check_uid (folder_summary, uid_str);
  515         if (!is_new) {
  516             /* keep local read/unread flag on messages from public folders */
  517             if (gos->is_public_folder) {
  518                 info = camel_folder_summary_get (folder_summary, uid_str);
  519                 if (info) {
  520                     user_has_read = (camel_message_info_get_flags (info) & CAMEL_MESSAGE_SEEN) != 0;
  521                     g_clear_object (&info);
  522                 }
  523             }
  524 
  525             camel_folder_summary_remove_uid (folder_summary, uid_str);
  526         }
  527 
  528         info = camel_folder_summary_info_new_from_message (folder_summary, msg);
  529         if (info) {
  530             camel_message_info_set_abort_notifications (info, TRUE);
  531 
  532             camel_message_info_set_uid (info, uid_str);
  533 
  534             update_message_info (info, object, is_new, gos->is_public_folder, user_has_read);
  535 
  536             if (!camel_message_info_get_size (info))
  537                 camel_message_info_set_size (info, camel_mapi_get_message_size (msg));
  538 
  539             camel_message_info_set_abort_notifications (info, FALSE);
  540             camel_folder_summary_add (folder_summary, info, FALSE);
  541 
  542             if (is_new) {
  543                 camel_folder_change_info_add_uid (gos->changes, uid_str);
  544                 camel_folder_change_info_recent_uid (gos->changes, uid_str);
  545             } else {
  546                 camel_folder_change_info_change_uid (gos->changes, uid_str);
  547             }
  548 
  549             add_message_to_cache (CAMEL_MAPI_FOLDER (gos->folder), uid_str, &msg, cancellable);
  550 
  551             g_clear_object (&info);
  552         } else {
  553             g_debug ("%s: Failed to create message info from message", G_STRFUNC);
  554         }
  555 
  556         g_free (uid_str);
  557         g_object_unref (msg);
  558     } else {
  559         g_debug ("%s: Failed to create message from object", G_STRFUNC);
  560     }
  561 
  562     if (obj_total > 0)
  563         camel_operation_progress (cancellable, obj_index * 100 / obj_total);
  564 
  565     return TRUE;
  566 }
  567 
  568 static gboolean
  569 gather_object_summary_cb (EMapiConnection *conn,
  570               TALLOC_CTX *mem_ctx,
  571               /* const */ EMapiObject *object,
  572               guint32 obj_index,
  573               guint32 obj_total,
  574               gpointer user_data,
  575               GCancellable *cancellable,
  576               GError **perror)
  577 {
  578     struct GatherObjectSummaryData *gos = user_data;
  579     gchar *uid_str;
  580     const mapi_id_t *pmid;
  581     const gchar *transport_headers;
  582     CamelMessageInfo *info;
  583     gboolean is_new = FALSE;
  584     gboolean user_has_read;
  585 
  586     g_return_val_if_fail (gos != NULL, FALSE);
  587     g_return_val_if_fail (gos->folder != NULL, FALSE);
  588     g_return_val_if_fail (object != NULL, FALSE);
  589 
  590     pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
  591     transport_headers = e_mapi_util_find_array_propval (&object->properties, PidTagTransportMessageHeaders);
  592 
  593     if (!pmid) {
  594         g_debug ("%s: Received message [%d/%d] without PidTagMid", G_STRFUNC, obj_index, obj_total);
  595         e_mapi_debug_dump_object (object, TRUE, 3);
  596         return TRUE;
  597     }
  598 
  599     if (!e_mapi_util_find_array_propval (&object->properties, PidTagLastModificationTime)) {
  600         g_debug ("%s: Received message [%d/%d] without PidTagLastModificationTime", G_STRFUNC, obj_index, obj_total);
  601         e_mapi_debug_dump_object (object, TRUE, 3);
  602     }
  603 
  604     uid_str = e_mapi_util_mapi_id_to_string (*pmid);
  605     if (!uid_str)
  606         return FALSE;
  607 
  608     info = camel_folder_summary_get (camel_folder_get_folder_summary (gos->folder), uid_str);
  609     if (!info) {
  610         is_new = TRUE;
  611 
  612         if (transport_headers && *transport_headers) {
  613             CamelMimePart *part = camel_mime_part_new ();
  614             CamelStream *stream;
  615             CamelMimeParser *parser;
  616 
  617             stream = camel_stream_mem_new_with_buffer (transport_headers, strlen (transport_headers));
  618             parser = camel_mime_parser_new ();
  619             camel_mime_parser_init_with_stream (parser, stream, NULL);
  620             camel_mime_parser_scan_from (parser, FALSE);
  621             g_object_unref (stream);
  622 
  623             if (camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) {
  624                 info = camel_folder_summary_info_new_from_headers (
  625                     camel_folder_get_folder_summary (gos->folder),
  626                     camel_medium_get_headers (CAMEL_MEDIUM (part)));
  627                 if (info) {
  628                     const uint32_t *msg_size;
  629 
  630                     camel_message_info_freeze_notifications (info);
  631                     camel_message_info_set_uid (info, uid_str);
  632 
  633                     msg_size = e_mapi_util_find_array_propval (&object->properties, PidTagMessageSize);
  634                     camel_message_info_set_size (info, msg_size ? *msg_size : 0);
  635                 }
  636             }
  637 
  638             g_object_unref (parser);
  639             g_object_unref (part);
  640         }
  641 
  642         if (!info) {
  643             const gchar *subject, *message_id, *references, *in_reply_to, *display_to, *display_cc;
  644             const struct FILETIME *delivery_time, *submit_time;
  645             const uint32_t *msg_size;
  646             gchar *formatted_addr, *from_name, *from_email;
  647             CamelAddress *to_addr, *cc_addr, *bcc_addr;
  648 
  649             subject = e_mapi_util_find_array_propval (&object->properties, PidTagSubject);
  650             delivery_time = e_mapi_util_find_array_propval (&object->properties, PidTagMessageDeliveryTime);
  651             submit_time = e_mapi_util_find_array_propval (&object->properties, PidTagClientSubmitTime);
  652             msg_size = e_mapi_util_find_array_propval (&object->properties, PidTagMessageSize);
  653             message_id = e_mapi_util_find_array_propval (&object->properties, PidTagInternetMessageId);
  654             references = e_mapi_util_find_array_propval (&object->properties, PidTagInternetReferences);
  655             in_reply_to = e_mapi_util_find_array_propval (&object->properties, PidTagInReplyToId);
  656             display_to = e_mapi_util_find_array_propval (&object->properties, PidTagDisplayTo);
  657             display_cc = e_mapi_util_find_array_propval (&object->properties, PidTagDisplayCc);
  658 
  659             info = camel_message_info_new (camel_folder_get_folder_summary (gos->folder));
  660 
  661             camel_message_info_freeze_notifications (info);
  662 
  663             camel_message_info_set_uid (info, uid_str);
  664             camel_message_info_set_subject (info, subject);
  665             camel_message_info_set_date_sent (info, e_mapi_util_filetime_to_time_t (submit_time));
  666             camel_message_info_set_date_received (info, e_mapi_util_filetime_to_time_t (delivery_time));
  667             camel_message_info_set_size (info, msg_size ? *msg_size : 0);
  668 
  669             /* Threading related properties */
  670             mapi_set_message_id (info, message_id);
  671             if (references || in_reply_to)
  672                 mapi_set_message_references (info, references, in_reply_to);
  673 
  674             /* Recipients */
  675             to_addr = (CamelAddress *) camel_internet_address_new ();
  676             cc_addr = (CamelAddress *) camel_internet_address_new ();
  677             bcc_addr = (CamelAddress *) camel_internet_address_new ();
  678 
  679             e_mapi_mail_utils_decode_recipients (conn, object->recipients, to_addr, cc_addr, bcc_addr);
  680 
  681             if (camel_address_length (to_addr) > 0) {
  682                 formatted_addr = camel_address_format (to_addr);
  683                 camel_message_info_set_to (info, formatted_addr);
  684                 g_free (formatted_addr);
  685             } else {
  686                 camel_message_info_set_to (info, display_to);
  687             }
  688 
  689             if (camel_address_length (cc_addr) > 0) {
  690                 formatted_addr = camel_address_format (cc_addr);
  691                 camel_message_info_set_cc (info, formatted_addr);
  692                 g_free (formatted_addr);
  693             } else {
  694                 camel_message_info_set_cc (info, display_cc);
  695             }
  696 
  697             g_object_unref (to_addr);
  698             g_object_unref (cc_addr);
  699             g_object_unref (bcc_addr);
  700 
  701             from_name = NULL;
  702             from_email = NULL;
  703 
  704             e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
  705                 PidTagSentRepresentingName,
  706                 PidTagSentRepresentingEmailAddress,
  707                 PidTagSentRepresentingAddressType,
  708                 &from_name, &from_email);
  709 
  710             if (from_email && *from_email) {
  711                 formatted_addr = camel_internet_address_format_address (from_name, from_email);
  712 
  713                 camel_message_info_set_from (info, formatted_addr);
  714 
  715                 g_free (formatted_addr);
  716             }
  717             
  718             g_free (from_name);
  719             g_free (from_email);
  720         }
  721 
  722         if (!camel_message_info_get_date_sent (info))
  723             camel_message_info_set_date_sent (info, camel_message_info_get_date_received (info));
  724         if (!camel_message_info_get_date_received (info))
  725             camel_message_info_set_date_received (info, camel_message_info_get_date_sent (info));
  726     } else {
  727         camel_message_info_freeze_notifications (info);
  728     }
  729 
  730     user_has_read = (camel_message_info_get_flags (info) & CAMEL_MESSAGE_SEEN) != 0;
  731 
  732     update_message_info (info, object, is_new, gos->is_public_folder, user_has_read);
  733 
  734     camel_message_info_thaw_notifications (info);
  735 
  736     if (is_new) {
  737         camel_folder_summary_add (camel_folder_get_folder_summary (gos->folder), info, FALSE);
  738         camel_folder_change_info_add_uid (gos->changes, camel_message_info_get_uid (info));
  739         camel_folder_change_info_recent_uid (gos->changes, camel_message_info_get_uid (info));
  740     } else {
  741         camel_folder_change_info_change_uid (gos->changes, camel_message_info_get_uid (info));
  742     }
  743 
  744     g_clear_object (&info);
  745 
  746     if (obj_total > 0)
  747         camel_operation_progress (cancellable, obj_index * 100 / obj_total);
  748 
  749     g_free (uid_str);
  750 
  751     return TRUE;
  752 }
  753 
  754 gboolean
  755 camel_mapi_folder_fetch_summary (CamelFolder *folder, GCancellable *cancellable, GError **mapi_error)
  756 {
  757     gboolean status, has_obj_folder;
  758     gboolean full_download;
  759     CamelStore *store = camel_folder_get_parent_store (folder);
  760     CamelStoreInfo *si = NULL;
  761     CamelMapiStoreInfo *msi = NULL;
  762     CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
  763     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (folder);
  764     EMapiConnection *conn = camel_mapi_store_ref_connection (mapi_store, cancellable, mapi_error);
  765     struct FolderBasicPropertiesData fbp;
  766     struct GatherChangedObjectsData gco;
  767     mapi_object_t obj_folder;
  768 
  769     if (!conn)
  770         return FALSE;
  771 
  772     camel_folder_freeze (folder);
  773 
  774     full_download = camel_offline_folder_can_downsync (CAMEL_OFFLINE_FOLDER (folder));
  775 
  776     camel_operation_push_message (cancellable, _("Refreshing folder “%s”"), camel_folder_get_display_name (folder));
  777 
  778     si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, mapi_folder->folder_id);
  779     msi = (CamelMapiStoreInfo *) si;
  780 
  781     if (!msi) {
  782         camel_operation_pop_message (cancellable);
  783         camel_folder_thaw (folder);
  784         g_object_unref (conn);
  785 
  786         g_return_val_if_fail (msi != NULL, FALSE);
  787 
  788         return FALSE;
  789     }
  790 
  791     status = cmf_open_folder (mapi_folder, conn, &obj_folder, cancellable, mapi_error);
  792     has_obj_folder = status;
  793 
  794     if (status) {
  795         status = e_mapi_connection_get_folder_properties (conn, &obj_folder, NULL, NULL, e_mapi_utils_get_folder_basic_properties_cb, &fbp, cancellable, mapi_error);
  796         if (status) {
  797             if (msi->last_obj_total != fbp.obj_total)
  798                 msi->latest_last_modify = 0;
  799         }
  800     }
  801 
  802     gco.latest_last_modify = 0;
  803     gco.fid = mapi_object_get_id (&obj_folder);
  804     gco.summary = camel_folder_get_folder_summary (folder);
  805     gco.to_update = NULL;
  806     gco.removed_uids = NULL;
  807     gco.is_public_folder = (mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0;
  808 
  809     if (msi->latest_last_modify <= 0) {
  810         GPtrArray *known_uids;
  811 
  812         camel_folder_summary_prepare_fetch_all (camel_folder_get_folder_summary (folder), NULL);
  813 
  814         gco.removed_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
  815         known_uids = camel_folder_summary_get_array (camel_folder_get_folder_summary (folder));
  816         if (known_uids) {
  817             gint ii;
  818 
  819             for (ii = 0; ii < known_uids->len; ii++) {
  820                 g_hash_table_insert (gco.removed_uids, (gpointer) camel_pstring_strdup (g_ptr_array_index (known_uids, ii)), GINT_TO_POINTER (1));
  821             }
  822 
  823             camel_folder_summary_free_array (known_uids);
  824         }
  825     }
  826 
  827     if (status) {
  828         status = e_mapi_connection_list_objects (conn, &obj_folder,
  829             full_download ? NULL : e_mapi_utils_build_last_modify_restriction, &msi->latest_last_modify,
  830             gather_changed_objects_to_slist, &gco, cancellable, mapi_error);
  831     }
  832 
  833     if (status && gco.to_update) {
  834         struct GatherObjectSummaryData gos;
  835 
  836         gos.folder = folder;
  837         gos.changes = camel_folder_change_info_new ();
  838         gos.is_public_folder = gco.is_public_folder;
  839 
  840         if (gco.removed_uids)
  841             g_hash_table_foreach (gco.removed_uids, remove_removed_uids_cb, &gos);
  842 
  843         if (full_download) {
  844             camel_operation_push_message (cancellable, _("Downloading messages in folder “%s”"), camel_folder_get_display_name (folder));
  845 
  846             status = e_mapi_connection_transfer_objects (conn, &obj_folder, gco.to_update, gather_object_for_offline_cb, &gos, cancellable, mapi_error);
  847 
  848             camel_operation_pop_message (cancellable);
  849         } else {
  850             status = e_mapi_connection_transfer_summary (conn, &obj_folder, gco.to_update, gather_object_summary_cb, &gos, cancellable, mapi_error);
  851         }
  852 
  853         if (camel_folder_change_info_changed (gos.changes))
  854             camel_folder_changed (folder, gos.changes);
  855         camel_folder_change_info_free (gos.changes);
  856     } else if (status && gco.removed_uids) {
  857         struct GatherObjectSummaryData gos;
  858 
  859         gos.folder = folder;
  860         gos.changes = camel_folder_change_info_new ();
  861         gos.is_public_folder = gco.is_public_folder;
  862 
  863         g_hash_table_foreach (gco.removed_uids, remove_removed_uids_cb, &gos);
  864 
  865         if (camel_folder_change_info_changed (gos.changes))
  866             camel_folder_changed (folder, gos.changes);
  867         camel_folder_change_info_free (gos.changes);
  868     }
  869 
  870     if (has_obj_folder)
  871         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, mapi_error);
  872 
  873     g_slist_free_full (gco.to_update, g_free);
  874     if (gco.removed_uids)
  875         g_hash_table_destroy (gco.removed_uids);
  876 
  877     camel_operation_pop_message (cancellable);
  878 
  879     if (status) {
  880         if (gco.latest_last_modify > 0)
  881             msi->latest_last_modify = gco.latest_last_modify;
  882         msi->last_obj_total = fbp.obj_total;
  883     }
  884 
  885     g_object_unref (conn);
  886     if (mapi_error && *mapi_error)
  887         camel_mapi_store_maybe_disconnect (mapi_store, *mapi_error);
  888 
  889     camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);
  890     camel_folder_thaw (folder);
  891 
  892     return status;
  893 }
  894 
  895 gboolean
  896 mapi_refresh_folder (CamelFolder *folder, GCancellable *cancellable, GError **error)
  897 {
  898 
  899     CamelMapiStore *mapi_store;
  900     CamelMapiFolder *mapi_folder;
  901     CamelStore *parent_store;
  902     gboolean status;
  903     gboolean success = TRUE;
  904     GError *mapi_error = NULL;
  905 
  906     parent_store = camel_folder_get_parent_store (folder);
  907 
  908     mapi_folder = CAMEL_MAPI_FOLDER (folder);
  909     mapi_store = CAMEL_MAPI_STORE (parent_store);
  910 
  911     if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store)))
  912         goto end1;
  913 
  914     /* Sync-up the (un)read changes before getting updates,
  915     so that the getFolderList will reflect the most recent changes too */
  916     mapi_folder_synchronize_sync (folder, FALSE, cancellable, NULL);
  917 
  918     if (!mapi_folder->folder_id) {
  919         d(printf ("\nERROR - Folder id not present. Cannot refresh info for %s\n", full_name));
  920         goto end1;
  921     }
  922 
  923     if (camel_folder_is_frozen (folder)) {
  924         mapi_folder->need_refresh = TRUE;
  925     }
  926 
  927     if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))) {
  928         /*BUG : Fix exception string.*/
  929         g_set_error (
  930             error, CAMEL_SERVICE_ERROR,
  931             CAMEL_SERVICE_ERROR_UNAVAILABLE,
  932             _("This message is not available in offline mode."));
  933         success = FALSE;
  934         goto end1;
  935     }
  936 
  937     if (!camel_mapi_store_connected (mapi_store, cancellable, &mapi_error)) {
  938         if (mapi_error) {
  939             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
  940                 g_set_error (
  941                     error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
  942                     _("Fetching items failed: %s"), mapi_error->message);
  943             g_error_free (mapi_error);
  944         } else {
  945             g_set_error_literal (
  946                 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
  947                 _("Fetching items failed"));
  948         }
  949         success = FALSE;
  950         goto end1;
  951     }
  952 
  953     status = camel_mapi_folder_fetch_summary (folder, cancellable, &mapi_error);
  954 
  955     if (!status) {
  956         if (mapi_error) {
  957             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
  958                 g_set_error (
  959                     error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_INVALID,
  960                     _("Fetching items failed: %s"), mapi_error->message);
  961             g_error_free (mapi_error);
  962         } else {
  963             g_set_error_literal (
  964                 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_INVALID,
  965                 _("Fetching items failed"));
  966         }
  967         success = FALSE;
  968         goto end1;
  969     }
  970 
  971     camel_folder_summary_touch (camel_folder_get_folder_summary (folder));
  972 
  973 end1:
  974     return success;
  975 }
  976 
  977 static void
  978 mapi_folder_search_free (CamelFolder *folder, GPtrArray *uids)
  979 {
  980     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER(folder);
  981 
  982     g_return_if_fail (mapi_folder->search);
  983 
  984     CAMEL_MAPI_FOLDER_LOCK(mapi_folder, search_lock);
  985 
  986     camel_folder_search_free_result (mapi_folder->search, uids);
  987 
  988     CAMEL_MAPI_FOLDER_UNLOCK(mapi_folder, search_lock);
  989 
  990 }
  991 
  992 static guint32
  993 mapi_folder_get_permanent_flags (CamelFolder *folder)
  994 {
  995     return CAMEL_MESSAGE_ANSWERED |
  996         CAMEL_MESSAGE_DELETED |
  997         CAMEL_MESSAGE_DRAFT |
  998         CAMEL_MESSAGE_FLAGGED |
  999         CAMEL_MESSAGE_SEEN |
 1000         CAMEL_MESSAGE_JUNK;
 1001 }
 1002 
 1003 static void
 1004 mapi_folder_rename (CamelFolder *folder, const gchar *new)
 1005 {
 1006     CamelStore *parent_store;
 1007 
 1008     parent_store = camel_folder_get_parent_store (folder);
 1009 
 1010     camel_store_summary_disconnect_folder_summary (
 1011         ((CamelMapiStore *) parent_store)->summary,
 1012         camel_folder_get_folder_summary (folder));
 1013 
 1014     ((CamelFolderClass *)camel_mapi_folder_parent_class)->rename(folder, new);
 1015 
 1016     camel_store_summary_connect_folder_summary (
 1017         ((CamelMapiStore *) parent_store)->summary,
 1018         camel_folder_get_full_name (folder), camel_folder_get_folder_summary (folder));
 1019 }
 1020 
 1021 static gint
 1022 mapi_cmp_uids (CamelFolder *folder, const gchar *uid1, const gchar *uid2)
 1023 {
 1024     g_return_val_if_fail (uid1 != NULL, 0);
 1025     g_return_val_if_fail (uid2 != NULL, 0);
 1026 
 1027     return strcmp (uid1, uid2);
 1028 }
 1029 
 1030 static void
 1031 mapi_folder_dispose (GObject *object)
 1032 {
 1033     CamelStore *parent_store;
 1034     CamelFolder *folder = CAMEL_FOLDER (object);
 1035     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (object);
 1036 
 1037     camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);
 1038 
 1039     if (mapi_folder->cache != NULL) {
 1040         g_object_unref (mapi_folder->cache);
 1041         mapi_folder->cache = NULL;
 1042     }
 1043 
 1044     if (mapi_folder->search) {
 1045         g_object_unref (mapi_folder->search);
 1046         mapi_folder->search = NULL;
 1047     }
 1048 
 1049     parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (mapi_folder));
 1050     if (parent_store) {
 1051         camel_store_summary_disconnect_folder_summary (
 1052             (CamelStoreSummary *) ((CamelMapiStore *) parent_store)->summary,
 1053             camel_folder_get_folder_summary (CAMEL_FOLDER (mapi_folder)));
 1054     }
 1055 
 1056     /* Chain up to parent's dispose() method. */
 1057     G_OBJECT_CLASS (camel_mapi_folder_parent_class)->dispose (object);
 1058 }
 1059 
 1060 static void
 1061 mapi_folder_finalize (GObject *object)
 1062 {
 1063     CamelMapiFolder *mapi_folder = CAMEL_MAPI_FOLDER (object);
 1064 
 1065     g_mutex_clear (&mapi_folder->priv->search_lock);
 1066 
 1067     /* Chain up to parent's finalize() method. */
 1068     G_OBJECT_CLASS (camel_mapi_folder_parent_class)->finalize (object);
 1069 }
 1070 
 1071 static void
 1072 mapi_folder_constructed (GObject *object)
 1073 {
 1074     CamelNetworkSettings *network_settings;
 1075     CamelSettings *settings;
 1076     CamelStore *parent_store;
 1077     CamelService *service;
 1078     CamelFolder *folder;
 1079     const gchar *full_name;
 1080     gchar *description;
 1081     gchar *host;
 1082     gchar *user;
 1083 
 1084     /* Chain up to parent's method. */
 1085     G_OBJECT_CLASS (camel_mapi_folder_parent_class)->constructed (object);
 1086 
 1087     folder = CAMEL_FOLDER (object);
 1088     full_name = camel_folder_get_full_name (folder);
 1089     parent_store = camel_folder_get_parent_store (folder);
 1090 
 1091     service = CAMEL_SERVICE (parent_store);
 1092 
 1093     settings = camel_service_ref_settings (service);
 1094 
 1095     network_settings = CAMEL_NETWORK_SETTINGS (settings);
 1096     host = camel_network_settings_dup_host (network_settings);
 1097     user = camel_network_settings_dup_user (network_settings);
 1098 
 1099     g_object_unref (settings);
 1100 
 1101     description = g_strdup_printf (
 1102         "%s@%s:%s", user, host, full_name);
 1103     camel_folder_set_description (folder, description);
 1104     g_free (description);
 1105 
 1106     g_free (host);
 1107     g_free (user);
 1108 }
 1109 
 1110 struct CamelMapiCreateData
 1111 {
 1112     CamelMimeMessage *message;
 1113     guint32 message_camel_flags;
 1114 };
 1115 
 1116 static gboolean
 1117 convert_message_to_object_cb (EMapiConnection *conn,
 1118                   TALLOC_CTX *mem_ctx,
 1119                   EMapiObject **object, /* out */
 1120                   gpointer user_data,
 1121                   GCancellable *cancellable,
 1122                   GError **perror)
 1123 {
 1124     struct CamelMapiCreateData *cmc = user_data;
 1125 
 1126     g_return_val_if_fail (conn != NULL, FALSE);
 1127     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 1128     g_return_val_if_fail (object != NULL, FALSE);
 1129     g_return_val_if_fail (cmc != NULL, FALSE);
 1130     g_return_val_if_fail (cmc->message != NULL, FALSE);
 1131 
 1132     return e_mapi_mail_utils_message_to_object (cmc->message, cmc->message_camel_flags, E_MAPI_CREATE_FLAG_NONE, object, mem_ctx, cancellable, perror);
 1133 }
 1134 
 1135 static gboolean
 1136 mapi_folder_append_message_sync (CamelFolder *folder,
 1137                                  CamelMimeMessage *message,
 1138                                  CamelMessageInfo *info,
 1139                                  gchar **appended_uid,
 1140                                  GCancellable *cancellable,
 1141                                  GError **error)
 1142 {
 1143     CamelMapiStore *mapi_store;
 1144     CamelStoreInfo *si;
 1145     CamelStore *parent_store;
 1146     mapi_id_t fid = 0, mid = 0;
 1147     const gchar *folder_id;
 1148     const gchar *full_name;
 1149     guint32 folder_flags = 0;
 1150     EMapiConnection *conn;
 1151     mapi_object_t obj_folder;
 1152     GError *mapi_error = NULL;
 1153 
 1154     full_name = camel_folder_get_full_name (folder);
 1155     parent_store = camel_folder_get_parent_store (folder);
 1156 
 1157     mapi_store = CAMEL_MAPI_STORE (parent_store);
 1158 
 1159     /*Reject outbox / sent & trash*/
 1160     si = camel_store_summary_path (mapi_store->summary, full_name);
 1161     if (si) {
 1162         folder_flags = si->flags;
 1163         camel_store_info_unref (si);
 1164     }
 1165 
 1166     if (((folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH) ||
 1167         ((folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_OUTBOX)) {
 1168         g_set_error (
 1169             error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1170             _("Cannot append message to folder “%s”"),
 1171             full_name);
 1172         return FALSE;
 1173     }
 1174 
 1175     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1176     if (!conn) {
 1177         g_set_error (
 1178             error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1179             _("Offline."));
 1180         return FALSE;
 1181     }
 1182 
 1183     folder_id = camel_mapi_store_folder_id_lookup (mapi_store, full_name);
 1184     e_mapi_util_mapi_id_from_string (folder_id, &fid);
 1185 
 1186     /* Convert MIME to Item */
 1187     if (cmf_open_folder (CAMEL_MAPI_FOLDER (folder), conn, &obj_folder, cancellable, &mapi_error)) {
 1188         struct CamelMapiCreateData cmc;
 1189 
 1190         cmc.message = message;
 1191         cmc.message_camel_flags = info ? camel_message_info_get_flags (info) : 0;
 1192 
 1193         e_mapi_connection_create_object (conn, &obj_folder, E_MAPI_CREATE_FLAG_NONE, convert_message_to_object_cb, &cmc, &mid, cancellable, &mapi_error);
 1194 
 1195         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 1196     }
 1197 
 1198     if (mid) {
 1199         mapi_refresh_folder (folder, cancellable, error);
 1200     } else {
 1201         g_object_unref (conn);
 1202 
 1203         if (mapi_error) {
 1204             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1205                 g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, mapi_error->message);
 1206             camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1207             g_error_free (mapi_error);
 1208         } else {
 1209             g_set_error (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Offline."));
 1210         }
 1211 
 1212         return FALSE;
 1213     }
 1214 
 1215     g_object_unref (conn);
 1216 
 1217     if (appended_uid)
 1218         *appended_uid = e_mapi_util_mapi_id_to_string (mid);
 1219 
 1220     return TRUE;
 1221 }
 1222 
 1223 static gboolean
 1224 mapi_folder_expunge_sync (CamelFolder *folder,
 1225                           GCancellable *cancellable,
 1226                           GError **error)
 1227 {
 1228     CamelMapiStore *mapi_store;
 1229     CamelMapiFolder *mapi_folder;
 1230     CamelMessageInfo *info;
 1231     CamelFolderChangeInfo *changes;
 1232     CamelFolderSummary *folder_summary;
 1233     CamelStore *parent_store;
 1234     GPtrArray *known_uids;
 1235     gint i;
 1236     gboolean delete = FALSE, status = FALSE;
 1237     GSList *deleted_items, *deleted_head;
 1238     GSList *deleted_items_uid, *deleted_items_uid_head;
 1239     EMapiConnection *conn;
 1240 
 1241     deleted_items = deleted_head = NULL;
 1242     deleted_items_uid = deleted_items_uid_head = NULL;
 1243 
 1244     parent_store = camel_folder_get_parent_store (folder);
 1245     folder_summary = camel_folder_get_folder_summary (folder);
 1246 
 1247     mapi_folder = CAMEL_MAPI_FOLDER (folder);
 1248     mapi_store = CAMEL_MAPI_STORE (parent_store);
 1249     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1250 
 1251     if (!conn)
 1252         return FALSE;
 1253 
 1254     if ((mapi_folder->camel_folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH) {
 1255         mapi_object_t obj_folder;
 1256         GError *mapi_error = NULL;
 1257         GPtrArray *folders;
 1258         gint ii;
 1259 
 1260         /* get deleted messages from all active folders too */
 1261         folders = camel_store_dup_opened_folders (parent_store);
 1262         for (ii = 0; ii < folders->len; ii++) {
 1263             CamelFolder *opened_folder = CAMEL_FOLDER (folders->pdata[ii]);
 1264             CamelMapiFolder *mf;
 1265 
 1266             if (!opened_folder)
 1267                 continue;
 1268 
 1269             mf = CAMEL_MAPI_FOLDER (opened_folder);
 1270             if (mf && (mf->camel_folder_flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_TRASH) {
 1271                 if (camel_folder_get_deleted_message_count (opened_folder) > 0)
 1272                     camel_folder_synchronize_sync (opened_folder, TRUE, cancellable, NULL);
 1273             }
 1274 
 1275             g_object_unref (opened_folder);
 1276         }
 1277         g_ptr_array_free (folders, TRUE);
 1278 
 1279         status = cmf_open_folder (mapi_folder, conn, &obj_folder, cancellable, &mapi_error);
 1280         if (status) {
 1281             status = e_mapi_connection_empty_folder (conn, &obj_folder, cancellable, &mapi_error);
 1282             e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 1283         }
 1284 
 1285         if (status) {
 1286             camel_folder_freeze (folder);
 1287             mapi_summary_clear (folder_summary, TRUE);
 1288             camel_folder_thaw (folder);
 1289         } else if (mapi_error) {
 1290             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1291                 g_set_error (
 1292                     error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1293                     _("Failed to empty Trash: %s"), mapi_error->message);
 1294             camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1295             g_error_free (mapi_error);
 1296         } else {
 1297             g_set_error_literal (
 1298                 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1299                 _("Failed to empty Trash"));
 1300         }
 1301 
 1302         g_object_unref (conn);
 1303 
 1304         return status;
 1305     }
 1306 
 1307     changes = camel_folder_change_info_new ();
 1308     folder_summary = camel_folder_get_folder_summary (folder);
 1309     known_uids = camel_folder_summary_get_array (folder_summary);
 1310 
 1311     /*Collect UIDs of deleted messages.*/
 1312     for (i = 0; known_uids && i < known_uids->len; i++) {
 1313         info = camel_folder_summary_get (folder_summary, g_ptr_array_index (known_uids, i));
 1314         if (info && (camel_message_info_get_flags (info) & CAMEL_MESSAGE_DELETED) != 0) {
 1315             const gchar *uid = camel_message_info_get_uid (info);
 1316             mapi_id_t *mid = g_new0 (mapi_id_t, 1);
 1317 
 1318             if (!e_mapi_util_mapi_id_from_string (uid, mid))
 1319                 continue;
 1320 
 1321             if (deleted_items)
 1322                 deleted_items = g_slist_prepend (deleted_items, mid);
 1323             else {
 1324                 g_slist_free (deleted_head);
 1325                 deleted_head = NULL;
 1326                 deleted_head = deleted_items = g_slist_prepend (deleted_items, mid);
 1327             }
 1328             deleted_items_uid = g_slist_prepend (deleted_items_uid, (gpointer) uid);
 1329         }
 1330         g_clear_object (&info);
 1331     }
 1332 
 1333     camel_folder_summary_free_array (known_uids);
 1334 
 1335     deleted_items_uid_head = deleted_items_uid;
 1336 
 1337     if (deleted_items) {
 1338         mapi_object_t obj_folder;
 1339         GError *mapi_error = NULL;
 1340 
 1341         status = cmf_open_folder (mapi_folder, conn, &obj_folder, cancellable, &mapi_error);
 1342         if (status) {
 1343             status = e_mapi_connection_remove_items (conn, &obj_folder, deleted_items, cancellable, &mapi_error);
 1344             e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 1345         }
 1346 
 1347         if (mapi_error) {
 1348             camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1349             g_clear_error (&mapi_error);
 1350         }
 1351 
 1352         if (status) {
 1353             while (deleted_items_uid) {
 1354                 const gchar *uid = (gchar *)deleted_items_uid->data;
 1355                 camel_folder_summary_lock (folder_summary);
 1356                 camel_folder_change_info_remove_uid (changes, uid);
 1357                 camel_folder_summary_remove_uid (folder_summary, uid);
 1358                 camel_data_cache_remove(mapi_folder->cache, "cache", uid, NULL);
 1359                 camel_folder_summary_unlock (folder_summary);
 1360                 deleted_items_uid = g_slist_next (deleted_items_uid);
 1361             }
 1362         }
 1363         delete = TRUE;
 1364 
 1365         g_slist_foreach (deleted_head, (GFunc)g_free, NULL);
 1366         g_slist_free (deleted_head);
 1367         g_slist_free (deleted_items_uid_head);
 1368     }
 1369 
 1370     if (delete)
 1371         camel_folder_changed (folder, changes);
 1372 
 1373     camel_folder_change_info_free (changes);
 1374     g_object_unref (conn);
 1375 
 1376     return TRUE;
 1377 }
 1378 
 1379 static CamelMimeMessage *
 1380 mapi_folder_get_message_cached (CamelFolder *folder,
 1381                  const gchar *message_uid,
 1382                  GCancellable *cancellable)
 1383 {
 1384     CamelMapiFolder *mapi_folder;
 1385     CamelMimeMessage *msg = NULL;
 1386     CamelStream *stream;
 1387     GIOStream *base_stream;
 1388 
 1389     mapi_folder = CAMEL_MAPI_FOLDER (folder);
 1390 
 1391     if (!camel_folder_summary_check_uid (camel_folder_get_folder_summary (folder), message_uid))
 1392         return NULL;
 1393 
 1394     stream = camel_stream_mem_new ();
 1395 
 1396     base_stream = camel_data_cache_get (mapi_folder->cache, "cache", message_uid, NULL);
 1397     if (base_stream != NULL) {
 1398         CamelStream *cache_stream;
 1399         GError *local_error = NULL;
 1400 
 1401         cache_stream = camel_stream_new (base_stream);
 1402         g_object_unref (base_stream);
 1403 
 1404         msg = camel_mime_message_new ();
 1405 
 1406         g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
 1407         camel_stream_write_to_stream (cache_stream, stream, cancellable, NULL);
 1408         g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
 1409         if (!camel_data_wrapper_construct_from_stream_sync ((CamelDataWrapper *) msg, stream, cancellable, &local_error)) {
 1410             g_object_unref (msg);
 1411             msg = NULL;
 1412         }
 1413 
 1414         g_clear_error (&local_error);
 1415         g_object_unref (cache_stream);
 1416     }
 1417 
 1418     g_object_unref (stream);
 1419 
 1420     return msg;
 1421 }
 1422 
 1423 static gboolean
 1424 transfer_mail_object_cb (EMapiConnection *conn,
 1425              TALLOC_CTX *mem_ctx,
 1426              /* const */ EMapiObject *object,
 1427              guint32 obj_index,
 1428              guint32 obj_total,
 1429              gpointer user_data,
 1430              GCancellable *cancellable,
 1431              GError **perror)
 1432 {
 1433     CamelMimeMessage **pmessage = user_data;
 1434 
 1435     g_return_val_if_fail (object != NULL, FALSE);
 1436     g_return_val_if_fail (pmessage != NULL, FALSE);
 1437 
 1438     *pmessage = e_mapi_mail_utils_object_to_message (conn, object);
 1439 
 1440     if (obj_total > 0)
 1441         camel_operation_progress (cancellable, obj_index * 100 / obj_total);
 1442 
 1443     return TRUE;
 1444 }
 1445 
 1446 static CamelMimeMessage *
 1447 mapi_folder_get_message_sync (CamelFolder *folder,
 1448                               const gchar *uid,
 1449                               GCancellable *cancellable,
 1450                               GError **error)
 1451 {
 1452     CamelMimeMessage *msg = NULL;
 1453     CamelMapiFolder *mapi_folder;
 1454     CamelMapiStore *mapi_store;
 1455     CamelMessageInfo *mi;
 1456     CamelStore *parent_store;
 1457     mapi_id_t id_message;
 1458     EMapiConnection *conn;
 1459     mapi_object_t obj_folder;
 1460     gboolean success;
 1461     GError *mapi_error = NULL;
 1462 
 1463     parent_store = camel_folder_get_parent_store (folder);
 1464 
 1465     mapi_folder = CAMEL_MAPI_FOLDER (folder);
 1466     mapi_store = CAMEL_MAPI_STORE (parent_store);
 1467 
 1468     /* see if it is there in cache */
 1469 
 1470     mi = camel_folder_summary_get (camel_folder_get_folder_summary (folder), uid);
 1471     if (mi == NULL) {
 1472         g_set_error (
 1473             error, CAMEL_FOLDER_ERROR,
 1474             CAMEL_FOLDER_ERROR_INVALID_UID,
 1475             /* Translators: The first %s is replaced with a message ID,
 1476                the second %s is replaced with a detailed error string */
 1477             _("Cannot get message %s: %s"), uid,
 1478             _("No such message"));
 1479         return NULL;
 1480     }
 1481 
 1482     msg = mapi_folder_get_message_cached (folder, uid, cancellable);
 1483     if (msg != NULL) {
 1484         g_clear_object (&mi);
 1485         return msg;
 1486     }
 1487 
 1488     if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))) {
 1489         g_set_error (
 1490             error, CAMEL_SERVICE_ERROR,
 1491             CAMEL_SERVICE_ERROR_UNAVAILABLE,
 1492             _("This message is not available in offline mode."));
 1493         g_clear_object (&mi);
 1494         return NULL;
 1495     }
 1496 
 1497     /* Check if we are really offline */
 1498     if (!camel_mapi_store_connected (mapi_store, cancellable, &mapi_error)) {
 1499         if (mapi_error) {
 1500             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1501                 g_set_error (
 1502                     error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_INVALID,
 1503                     _("Could not get message: %s"), mapi_error->message);
 1504             g_error_free (mapi_error);
 1505         } else {
 1506             g_set_error (
 1507                 error, CAMEL_SERVICE_ERROR,
 1508                 CAMEL_SERVICE_ERROR_INVALID,
 1509                 _("Could not get message"));
 1510         }
 1511         g_clear_object (&mi);
 1512         return NULL;
 1513     }
 1514 
 1515     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1516     if (!conn) {
 1517         g_clear_object (&mi);
 1518         return NULL;
 1519     }
 1520 
 1521     e_mapi_util_mapi_id_from_string (uid, &id_message);
 1522 
 1523     success = cmf_open_folder (mapi_folder, conn, &obj_folder, cancellable, &mapi_error);
 1524     if (success) {
 1525         success = e_mapi_connection_transfer_object (conn, &obj_folder, id_message, transfer_mail_object_cb, &msg, cancellable, &mapi_error);
 1526 
 1527         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, NULL);
 1528     }
 1529 
 1530     g_object_unref (conn);
 1531 
 1532     if (!msg) {
 1533         if (mapi_error) {
 1534             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1535                 g_set_error (
 1536                     error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_INVALID,
 1537                     _("Could not get message: %s"), mapi_error->message);
 1538             camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);             
 1539             g_error_free (mapi_error);
 1540         } else {
 1541             g_set_error (
 1542                 error, CAMEL_SERVICE_ERROR,
 1543                 CAMEL_SERVICE_ERROR_INVALID,
 1544                 _("Could not get message"));
 1545         }
 1546         g_clear_object (&mi);
 1547         return NULL;
 1548     }
 1549 
 1550     add_message_to_cache (mapi_folder, uid, &msg, cancellable);
 1551 
 1552     if (msg) {
 1553         CamelMessageFlags flags;
 1554         gboolean has_attachment;
 1555 
 1556         flags = camel_message_info_get_flags (mi);
 1557         has_attachment = camel_mime_message_has_attachment (msg);
 1558         if (((flags & CAMEL_MESSAGE_ATTACHMENTS) && !has_attachment) ||
 1559             ((flags & CAMEL_MESSAGE_ATTACHMENTS) == 0 && has_attachment)) {
 1560             camel_message_info_set_flags (
 1561                 mi, CAMEL_MESSAGE_ATTACHMENTS,
 1562                 has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
 1563         }
 1564     }
 1565 
 1566     g_clear_object (&mi);
 1567 
 1568     return msg;
 1569 }
 1570 
 1571 static gboolean
 1572 mapi_folder_refresh_info_sync (CamelFolder *folder,
 1573                                GCancellable *cancellable,
 1574                                GError **error)
 1575 {
 1576     return mapi_refresh_folder (folder, cancellable, error);
 1577 }
 1578 
 1579 static gboolean
 1580 mapi_folder_synchronize_sync (CamelFolder *folder,
 1581                               gboolean expunge,
 1582                               GCancellable *cancellable,
 1583                               GError **error)
 1584 {
 1585     CamelMapiStore *mapi_store;
 1586     CamelMapiFolder *mapi_folder;
 1587     CamelMessageInfo *info = NULL;
 1588     CamelStore *parent_store;
 1589     CamelFolderChangeInfo *changes = NULL;
 1590     CamelFolderSummary *folder_summary;
 1591     CamelServiceConnectionStatus status;
 1592     CamelService *service;
 1593     EMapiConnection *conn;
 1594     GPtrArray *known_uids;
 1595     GSList *read_items = NULL, *read_with_receipt = NULL, *unread_items = NULL, *to_free = NULL, *junk_items = NULL, *deleted_items = NULL, *l;
 1596     flags_diff_t diff, unset_flags;
 1597     const gchar *folder_id;
 1598     const gchar *full_name;
 1599     mapi_id_t fid;
 1600     gint i;
 1601     gboolean is_junk_folder, has_obj_folder = FALSE;
 1602     mapi_object_t obj_folder;
 1603     GError *mapi_error = NULL;
 1604 
 1605     full_name = camel_folder_get_full_name (folder);
 1606     parent_store = camel_folder_get_parent_store (folder);
 1607     folder_summary = camel_folder_get_folder_summary (folder);
 1608 
 1609     mapi_folder = CAMEL_MAPI_FOLDER (folder);
 1610     mapi_store = CAMEL_MAPI_STORE (parent_store);
 1611 
 1612     service = CAMEL_SERVICE (mapi_store);
 1613     status = camel_service_get_connection_status (service);
 1614 
 1615     if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store)) ||
 1616         status == CAMEL_SERVICE_DISCONNECTED) {
 1617         return TRUE;
 1618     }
 1619 
 1620     folder_id =  camel_mapi_store_folder_id_lookup (mapi_store, full_name);
 1621     e_mapi_util_mapi_id_from_string (folder_id, &fid);
 1622 
 1623     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1624     if (!conn)
 1625         return FALSE;
 1626 
 1627     is_junk_folder = (mapi_folder->camel_folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_JUNK;
 1628 
 1629     camel_folder_summary_lock (folder_summary);
 1630     camel_folder_summary_prepare_fetch_all (folder_summary, NULL);
 1631 
 1632     known_uids = camel_folder_summary_get_array (folder_summary);
 1633     for (i = 0; known_uids && i < known_uids->len; i++) {
 1634         info = camel_folder_summary_get (folder_summary, g_ptr_array_index (known_uids, i));
 1635 
 1636         if (info && camel_message_info_get_folder_flagged (info)) {
 1637             const gchar *uid;
 1638             mapi_id_t *mid = g_new0 (mapi_id_t, 1); /* FIXME : */
 1639             guint32 flags, server_flags;
 1640             gboolean used = FALSE;
 1641 
 1642             uid = camel_message_info_get_uid (info);
 1643             flags = camel_message_info_get_flags (info);
 1644 
 1645             /* Why are we getting so much noise here :-/ */
 1646             if (!e_mapi_util_mapi_id_from_string (uid, mid)) {
 1647                 g_clear_object (&info);
 1648                 g_free (mid);
 1649                 continue;
 1650             }
 1651 
 1652             server_flags = camel_mapi_message_info_get_server_flags (CAMEL_MAPI_MESSAGE_INFO (info));
 1653             mapi_utils_do_flags_diff (&diff, server_flags, flags);
 1654             mapi_utils_do_flags_diff (&unset_flags, flags, server_flags);
 1655 
 1656             diff.changed &= camel_folder_get_permanent_flags (folder);
 1657             if (!diff.changed) {
 1658                 g_clear_object (&info);
 1659                 g_free (mid);
 1660                 continue;
 1661             }
 1662             if (diff.bits & CAMEL_MESSAGE_DELETED) {
 1663                 deleted_items = g_slist_prepend (deleted_items, mid);
 1664                 used = TRUE;
 1665             } else if (!is_junk_folder && (diff.bits & CAMEL_MESSAGE_JUNK) != 0) {
 1666                 junk_items = g_slist_prepend (junk_items, mid);
 1667                 used = TRUE;
 1668             }
 1669 
 1670             if (diff.bits & CAMEL_MESSAGE_SEEN) {
 1671                 read_items = g_slist_prepend (read_items, mid);
 1672                 if (flags & CAMEL_MAPI_MESSAGE_WITH_READ_RECEIPT)
 1673                     read_with_receipt = g_slist_prepend (read_with_receipt, mid);
 1674                 used = TRUE;
 1675             } else if (unset_flags.bits & CAMEL_MESSAGE_SEEN) {
 1676                 unread_items = g_slist_prepend (unread_items, mid);
 1677                 used = TRUE;
 1678             }
 1679 
 1680             if (used)
 1681                 to_free = g_slist_prepend (to_free, mid);
 1682             else
 1683                 g_free (mid);
 1684 
 1685             camel_mapi_message_info_set_server_flags (CAMEL_MAPI_MESSAGE_INFO (info), camel_message_info_get_flags (info));
 1686         }
 1687 
 1688         g_clear_object (&info);
 1689     }
 1690 
 1691     camel_folder_summary_free_array (known_uids);
 1692     camel_folder_summary_unlock (folder_summary);
 1693 
 1694     /*
 1695        Sync up the READ changes before deleting the message.
 1696        Note that if a message is marked as unread and then deleted,
 1697        Evo doesnt not take care of it, as I find that scenario to be impractical.
 1698     */
 1699 
 1700     has_obj_folder = cmf_open_folder (mapi_folder, conn, &obj_folder, cancellable, &mapi_error);
 1701 
 1702     if (read_items && has_obj_folder) {
 1703         if (read_with_receipt)
 1704             e_mapi_connection_set_flags (conn, &obj_folder, read_with_receipt, CLEAR_RN_PENDING, cancellable, &mapi_error);
 1705         e_mapi_connection_set_flags (conn, &obj_folder, read_items, 0, cancellable, &mapi_error);
 1706     }
 1707 
 1708     if (unread_items && has_obj_folder) {
 1709         e_mapi_connection_set_flags (conn, &obj_folder, unread_items, CLEAR_READ_FLAG, cancellable, &mapi_error);
 1710     }
 1711 
 1712     /* Remove messages from server*/
 1713     if (deleted_items && has_obj_folder) {
 1714         if ((mapi_folder->camel_folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH) {
 1715             e_mapi_connection_remove_items (conn, &obj_folder, deleted_items, cancellable, &mapi_error);
 1716         } else {
 1717             mapi_id_t deleted_items_fid;
 1718             mapi_object_t deleted_obj_folder;
 1719 
 1720             e_mapi_util_mapi_id_from_string (camel_mapi_store_system_folder_fid (mapi_store, olFolderDeletedItems), &deleted_items_fid);
 1721             if (e_mapi_connection_open_personal_folder (conn, deleted_items_fid, &deleted_obj_folder, cancellable, &mapi_error)) {
 1722                 e_mapi_connection_copymove_items (conn, &obj_folder, &deleted_obj_folder, FALSE, deleted_items, cancellable, &mapi_error);
 1723                 e_mapi_connection_close_folder (conn, &deleted_obj_folder, cancellable, &mapi_error);
 1724             }
 1725         }
 1726     }
 1727 
 1728     if (junk_items && has_obj_folder) {
 1729         mapi_id_t junk_fid = 0;
 1730         mapi_object_t junk_obj_folder;
 1731 
 1732         if (has_obj_folder) {
 1733             e_mapi_util_mapi_id_from_string (camel_mapi_store_system_folder_fid (mapi_store, olFolderJunk), &junk_fid);
 1734             if (e_mapi_connection_open_personal_folder (conn, junk_fid, &junk_obj_folder, cancellable, &mapi_error)) {
 1735                 e_mapi_connection_copymove_items (conn, &obj_folder, &junk_obj_folder, FALSE, junk_items, cancellable, &mapi_error);
 1736                 e_mapi_connection_close_folder (conn, &junk_obj_folder, cancellable, &mapi_error);
 1737             }
 1738         }
 1739 
 1740         /* in junk_items are only emails which are not deleted */
 1741         deleted_items = g_slist_concat (deleted_items, g_slist_copy (junk_items));
 1742     }
 1743 
 1744     if (has_obj_folder)
 1745         e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 1746 
 1747     /*Remove messages from local cache*/
 1748     for (l = deleted_items; l; l = l->next) {
 1749         gchar *deleted_msg_uid = e_mapi_util_mapi_id_to_string (*((mapi_id_t *) l->data));
 1750 
 1751         if (!changes)
 1752             changes = camel_folder_change_info_new ();
 1753         camel_folder_change_info_remove_uid (changes, deleted_msg_uid);
 1754 
 1755         camel_folder_summary_lock (folder_summary);
 1756         camel_folder_summary_remove_uid (folder_summary, deleted_msg_uid);
 1757         camel_data_cache_remove(mapi_folder->cache, "cache", deleted_msg_uid, NULL);
 1758         camel_folder_summary_unlock (folder_summary);
 1759 
 1760         g_free (deleted_msg_uid);
 1761     }
 1762 
 1763     if (changes) {
 1764         camel_folder_changed (folder, changes);
 1765         camel_folder_change_info_free (changes);
 1766     }
 1767 
 1768     g_slist_free (read_items);
 1769     g_slist_free (unread_items);
 1770     g_slist_free (deleted_items);
 1771     g_slist_free (junk_items);
 1772 
 1773     g_slist_foreach (to_free, (GFunc) g_free, NULL);
 1774     g_slist_free (to_free);
 1775 
 1776     g_object_unref (conn);
 1777 
 1778     if (mapi_error) {
 1779         camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1780         g_clear_error (&mapi_error);
 1781     }
 1782 
 1783     if (expunge) {
 1784         /* TODO */
 1785     }
 1786 
 1787     return TRUE;
 1788 }
 1789 
 1790 static gboolean
 1791 mapi_folder_transfer_messages_to_sync (CamelFolder *source,
 1792                                        GPtrArray *uids,
 1793                                        CamelFolder *destination,
 1794                                        gboolean delete_originals,
 1795                                        GPtrArray **transferred_uids,
 1796                                        GCancellable *cancellable,
 1797                                        GError **error)
 1798 {
 1799     CamelOfflineStore *offline;
 1800     CamelMapiStore *mapi_store;
 1801     CamelFolderChangeInfo *changes = NULL;
 1802     CamelStore *source_parent_store;
 1803     CamelStore *destination_parent_store;
 1804     CamelMapiFolder *src_mapi_folder, *des_mapi_folder;
 1805     gint i = 0;
 1806     GSList *src_msg_ids = NULL;
 1807     gboolean success = TRUE;
 1808     GError *mapi_error = NULL;
 1809     mapi_object_t src_obj_folder, des_obj_folder;
 1810     gboolean copymoved = FALSE;
 1811     EMapiConnection *conn;
 1812 
 1813     if (CAMEL_IS_MAPI_FOLDER (source)) {
 1814         /* make sure changed flags are written into the server */
 1815         if (!mapi_folder_synchronize_sync (source, FALSE, cancellable, error))
 1816             return FALSE;
 1817     }
 1818 
 1819     source_parent_store = camel_folder_get_parent_store (source);
 1820     mapi_store = CAMEL_MAPI_STORE (source_parent_store);
 1821     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1822 
 1823     if (!conn || !CAMEL_IS_MAPI_FOLDER (source) || !CAMEL_IS_MAPI_FOLDER (destination) ||
 1824         (CAMEL_MAPI_FOLDER (source)->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 ||
 1825         (CAMEL_MAPI_FOLDER (destination)->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0) {
 1826         CamelFolderClass *folder_class;
 1827 
 1828         if (conn)
 1829             g_object_unref (conn);
 1830 
 1831         /* because cannot use MAPI to copy/move messages with public folders,
 1832            thus fallback to per-message copy/move */
 1833         folder_class = CAMEL_FOLDER_CLASS (camel_mapi_folder_parent_class);
 1834         return folder_class->transfer_messages_to_sync (
 1835             source, uids, destination, delete_originals,
 1836             transferred_uids, cancellable, error);
 1837     }
 1838 
 1839     destination_parent_store = camel_folder_get_parent_store (destination);
 1840 
 1841     offline = CAMEL_OFFLINE_STORE (destination_parent_store);
 1842 
 1843     /* check for offline operation */
 1844     if (!camel_offline_store_get_online (offline)) {
 1845         g_object_unref (conn);
 1846         return FALSE;
 1847     }
 1848 
 1849     src_mapi_folder = CAMEL_MAPI_FOLDER (source);
 1850     des_mapi_folder = CAMEL_MAPI_FOLDER (destination);
 1851 
 1852     for (i=0; i < uids->len; i++) {
 1853         mapi_id_t *mid = g_new0 (mapi_id_t, 1); /* FIXME : */
 1854         if (!e_mapi_util_mapi_id_from_string (g_ptr_array_index (uids, i), mid))
 1855             continue;
 1856 
 1857         src_msg_ids = g_slist_prepend (src_msg_ids, mid);
 1858     }
 1859 
 1860     if (cmf_open_folder (src_mapi_folder, conn, &src_obj_folder, cancellable, &mapi_error)) {
 1861         if (cmf_open_folder (des_mapi_folder, conn, &des_obj_folder, cancellable, &mapi_error)) {
 1862             copymoved = e_mapi_connection_copymove_items (conn, &src_obj_folder, &des_obj_folder, !delete_originals, src_msg_ids, cancellable, &mapi_error);
 1863             e_mapi_connection_close_folder (conn, &des_obj_folder, cancellable, &mapi_error);
 1864         }
 1865 
 1866         e_mapi_connection_close_folder (conn, &src_obj_folder, cancellable, &mapi_error);
 1867     }
 1868 
 1869     if (!copymoved) {
 1870         if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1871             g_set_error (
 1872                 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1873                 "%s", mapi_error ? mapi_error->message : _("Unknown error"));
 1874         camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1875         g_clear_error (&mapi_error);
 1876         success = FALSE;
 1877     } else if (delete_originals) {
 1878         CamelFolderSummary *source_summary;
 1879 
 1880         source_summary = camel_folder_get_folder_summary (source);
 1881         changes = camel_folder_change_info_new ();
 1882 
 1883         for (i = 0; i < uids->len; i++) {
 1884             camel_folder_summary_remove_uid (source_summary, uids->pdata[i]);
 1885             camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
 1886             camel_data_cache_remove (src_mapi_folder->cache, "cache", uids->pdata[i], NULL);
 1887         }
 1888         camel_folder_changed (source, changes);
 1889         camel_folder_change_info_free (changes);
 1890 
 1891     }
 1892 
 1893     g_clear_error (&mapi_error);
 1894 
 1895     g_slist_foreach (src_msg_ids, (GFunc) g_free, NULL);
 1896     g_slist_free (src_msg_ids);
 1897 
 1898     g_object_unref (conn);
 1899 
 1900     /* update destination folder only if not frozen, to not update
 1901        for each single message transfer during filtering
 1902      */
 1903     if (success && !camel_folder_is_frozen (destination))
 1904         success = mapi_folder_refresh_info_sync (destination, cancellable, error);
 1905 
 1906     return success;
 1907 }
 1908 
 1909 static CamelFolderQuotaInfo *
 1910 mapi_folder_get_quota_info_sync (CamelFolder *folder,
 1911                  GCancellable *cancellable,
 1912                  GError **error)
 1913 {
 1914     CamelMapiStore *mapi_store;
 1915     CamelFolderQuotaInfo *quota_info = NULL;
 1916     EMapiConnection *conn;
 1917     GError *mapi_error = NULL;
 1918     uint64_t current_size = -1, receive_quota = -1, send_quota = -1;
 1919 
 1920     g_return_val_if_fail (folder != NULL, NULL);
 1921     g_return_val_if_fail (CAMEL_IS_MAPI_FOLDER (folder), NULL);
 1922 
 1923     mapi_store = CAMEL_MAPI_STORE (camel_folder_get_parent_store (folder));
 1924     g_return_val_if_fail (mapi_store != NULL, NULL);
 1925     
 1926     /* check for offline operation */
 1927     if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store)))
 1928         return NULL;
 1929 
 1930     conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
 1931     if (conn && e_mapi_connection_get_store_quotas (conn, NULL, &current_size, &receive_quota, &send_quota, cancellable, &mapi_error)) {
 1932         if (current_size != -1) {
 1933             if (receive_quota != -1) {
 1934                 quota_info = camel_folder_quota_info_new (_("Receive quota"), current_size, receive_quota);
 1935             }
 1936 
 1937             if (send_quota != -1) {
 1938                 CamelFolderQuotaInfo *qi;
 1939 
 1940                 qi = camel_folder_quota_info_new (_("Send quota"), current_size, send_quota);
 1941                 if (quota_info)
 1942                     quota_info->next = qi;
 1943                 else
 1944                     quota_info = qi;
 1945             }
 1946         }
 1947     }
 1948 
 1949     if (conn)
 1950         g_object_unref (conn);
 1951 
 1952     if (!quota_info) {
 1953         if (mapi_error) {
 1954             if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
 1955                 g_set_error (
 1956                     error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 1957                     "%s", mapi_error ? mapi_error->message : _("Unknown error"));
 1958             camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
 1959             g_clear_error (&mapi_error);
 1960         } else {
 1961             g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
 1962                 _("No quota information available"));
 1963         }
 1964     }
 1965 
 1966     return quota_info;
 1967 }
 1968 
 1969 static void
 1970 camel_mapi_folder_class_init (CamelMapiFolderClass *class)
 1971 {
 1972     GObjectClass *object_class;
 1973     CamelFolderClass *folder_class;
 1974 
 1975     object_class = G_OBJECT_CLASS (class);
 1976     object_class->dispose = mapi_folder_dispose;
 1977     object_class->finalize = mapi_folder_finalize;
 1978     object_class->constructed = mapi_folder_constructed;
 1979 
 1980     folder_class = CAMEL_FOLDER_CLASS (class);
 1981     folder_class->get_permanent_flags = mapi_folder_get_permanent_flags;
 1982     folder_class->rename = mapi_folder_rename;
 1983     folder_class->search_by_expression = mapi_folder_search_by_expression;
 1984     folder_class->cmp_uids = mapi_cmp_uids;
 1985     folder_class->search_by_uids = mapi_folder_search_by_uids;
 1986     folder_class->search_free = mapi_folder_search_free;
 1987     folder_class->append_message_sync = mapi_folder_append_message_sync;
 1988     folder_class->expunge_sync = mapi_folder_expunge_sync;
 1989     folder_class->get_message_sync = mapi_folder_get_message_sync;
 1990     folder_class->get_message_cached = mapi_folder_get_message_cached;
 1991     folder_class->refresh_info_sync = mapi_folder_refresh_info_sync;
 1992     folder_class->synchronize_sync = mapi_folder_synchronize_sync;
 1993     folder_class->transfer_messages_to_sync = mapi_folder_transfer_messages_to_sync;
 1994     folder_class->get_quota_info_sync = mapi_folder_get_quota_info_sync;
 1995 }
 1996 
 1997 static void
 1998 camel_mapi_folder_init (CamelMapiFolder *mapi_folder)
 1999 {
 2000     CamelFolder *folder = CAMEL_FOLDER (mapi_folder);
 2001 
 2002     mapi_folder->priv = camel_mapi_folder_get_instance_private (mapi_folder);
 2003 
 2004     camel_folder_set_flags (folder, CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY);
 2005 
 2006     g_mutex_init (&mapi_folder->priv->search_lock);
 2007 
 2008     mapi_folder->need_rescan = TRUE;
 2009 }
 2010 
 2011 CamelFolder *
 2012 camel_mapi_folder_new (CamelStore *store,
 2013                const gchar *folder_name,
 2014                const gchar *folder_dir,
 2015                guint32 flags,
 2016                GError **error)
 2017 {
 2018 
 2019     CamelFolder *folder;
 2020     CamelFolderSummary *folder_summary;
 2021     CamelMapiFolder *mapi_folder;
 2022     CamelMapiStore *mapi_store = (CamelMapiStore *) store;
 2023     CamelService *service;
 2024     CamelSettings *settings;
 2025     gchar *state_file;
 2026     const gchar *short_name;
 2027     CamelStoreInfo *si;
 2028     gboolean filter_inbox;
 2029     gboolean offline_limit_by_age = FALSE;
 2030     CamelTimeUnit offline_limit_unit;
 2031     gint offline_limit_value;
 2032 
 2033     service = CAMEL_SERVICE (store);
 2034     settings = camel_service_ref_settings (service);
 2035 
 2036     g_object_get (
 2037         settings,
 2038         "filter-inbox", &filter_inbox,
 2039         "limit-by-age", &offline_limit_by_age,
 2040         "limit-unit", &offline_limit_unit,
 2041         "limit-value", &offline_limit_value,
 2042         NULL);
 2043 
 2044     g_object_unref (settings);
 2045 
 2046     short_name = strrchr (folder_name, '/');
 2047     if (short_name)
 2048         short_name++;
 2049     else
 2050         short_name = folder_name;
 2051 
 2052     folder = g_object_new (
 2053         CAMEL_TYPE_MAPI_FOLDER,
 2054         "display-name", short_name,
 2055         "full-name", folder_name,
 2056         "parent-store", store,
 2057         NULL);
 2058 
 2059     mapi_folder = CAMEL_MAPI_FOLDER (folder);
 2060 
 2061     folder_summary = camel_mapi_folder_summary_new (folder);
 2062 
 2063     if (!folder_summary) {
 2064         g_object_unref (folder);
 2065         g_set_error (
 2066             error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
 2067             _("Could not load summary for %s"),
 2068             folder_name);
 2069         return NULL;
 2070     }
 2071 
 2072     camel_folder_take_folder_summary (folder, folder_summary);
 2073 
 2074     /* set/load persistent state */
 2075     state_file = g_build_filename (folder_dir, short_name, "cmeta", NULL);
 2076     camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
 2077     g_free(state_file);
 2078     camel_object_state_read (CAMEL_OBJECT (folder));
 2079 
 2080     state_file = g_build_filename (folder_dir, short_name, NULL);
 2081     mapi_folder->cache = camel_data_cache_new (state_file, error);
 2082     g_free (state_file);
 2083     if (!mapi_folder->cache) {
 2084         g_object_unref (folder);
 2085         return NULL;
 2086     }
 2087 
 2088     if (camel_offline_folder_can_downsync (CAMEL_OFFLINE_FOLDER (folder))) {
 2089         time_t when = (time_t) 0;
 2090 
 2091         if (offline_limit_by_age)
 2092             when = camel_time_value_apply (when, offline_limit_unit, offline_limit_value);
 2093 
 2094         if (when <= (time_t) 0)
 2095             when = (time_t) -1;
 2096 
 2097         /* Ensure cache will expire when set up, otherwise
 2098          * it causes redownload of messages too soon. */
 2099         camel_data_cache_set_expire_age (mapi_folder->cache, when);
 2100         camel_data_cache_set_expire_access (mapi_folder->cache, when);
 2101     } else {
 2102         /* Set cache expiration for one week. */
 2103         camel_data_cache_set_expire_age (mapi_folder->cache, 60 * 60 * 24 * 7);
 2104         camel_data_cache_set_expire_access (mapi_folder->cache, 60 * 60 * 24 * 7);
 2105     }
 2106 
 2107     camel_binding_bind_property (store, "online",
 2108         mapi_folder->cache, "expire-enabled",
 2109         G_BINDING_SYNC_CREATE);
 2110 
 2111     if (filter_inbox) {
 2112         CamelFolderInfo *fi;
 2113 
 2114         fi = camel_store_get_folder_info_sync (store, folder_name, 0, NULL, NULL);
 2115         if (fi) {
 2116             if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) {
 2117                 camel_folder_set_flags (folder, camel_folder_get_flags (folder) | CAMEL_FOLDER_FILTER_RECENT);
 2118             }
 2119 
 2120             camel_folder_info_free (fi);
 2121         }
 2122     }
 2123 
 2124     mapi_folder->search = camel_folder_search_new ();
 2125     if (!mapi_folder->search) {
 2126         g_object_unref (folder);
 2127         return NULL;
 2128     }
 2129 
 2130     si = camel_store_summary_path (mapi_store->summary, folder_name);
 2131     if (si) {
 2132         CamelMapiStoreInfo *msi = (CamelMapiStoreInfo *) si;
 2133         guint32 add_folder_flags = 0;
 2134 
 2135         mapi_folder->mapi_folder_flags = msi->mapi_folder_flags;
 2136         mapi_folder->camel_folder_flags = msi->camel_folder_flags;
 2137         mapi_folder->folder_id = msi->folder_id;
 2138         if ((mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0) {
 2139             mapi_folder->priv->foreign_username = g_strdup (msi->foreign_username);
 2140         } else {
 2141             mapi_folder->priv->foreign_username = NULL;
 2142         }
 2143 
 2144         if ((si->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH)
 2145             add_folder_flags |= CAMEL_FOLDER_IS_TRASH;
 2146         else if ((si->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_JUNK)
 2147             add_folder_flags |= CAMEL_FOLDER_IS_JUNK;
 2148         camel_store_info_unref (si);
 2149 
 2150         camel_folder_set_flags (folder, camel_folder_get_flags (folder) | add_folder_flags);
 2151     } else {
 2152         g_warning ("%s: cannot find '%s' in known folders", G_STRFUNC, folder_name);
 2153     }
 2154 
 2155     camel_store_summary_connect_folder_summary (
 2156         ((CamelMapiStore *) store)->summary,
 2157         folder_name, folder_summary);
 2158 
 2159     /* sanity checking */
 2160     if ((mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)
 2161         g_return_val_if_fail (mapi_folder->priv->foreign_username != NULL, folder);
 2162     if ((mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0)
 2163         g_return_val_if_fail (mapi_folder->priv->foreign_username == NULL, folder);
 2164 
 2165     return folder;
 2166 }