"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, ¤t_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 }