e5962f52e10d4e924571ae6d3999180bbe82722d
[platform/upstream/evolution-data-server.git] / camel / providers / imapx / camel-imapx-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-folder.c : class for a imap folder */
3 /*
4  * Authors: Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  *
8  * This library is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26 #include <glib/gi18n-lib.h>
27
28 #include "camel-imapx-folder.h"
29 #include "camel-imapx-search.h"
30 #include "camel-imapx-server.h"
31 #include "camel-imapx-settings.h"
32 #include "camel-imapx-store.h"
33 #include "camel-imapx-summary.h"
34 #include "camel-imapx-utils.h"
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #define d(...) camel_imapx_debug(debug, '?', __VA_ARGS__)
40
41 #define CAMEL_IMAPX_FOLDER_GET_PRIVATE(obj) \
42         (G_TYPE_INSTANCE_GET_PRIVATE \
43         ((obj), CAMEL_TYPE_IMAPX_FOLDER, CamelIMAPXFolderPrivate))
44
45 struct _CamelIMAPXFolderPrivate {
46         GMutex property_lock;
47         GWeakRef mailbox;
48
49         GMutex move_to_hash_table_lock;
50         GHashTable *move_to_real_junk_uids;
51         GHashTable *move_to_real_trash_uids;
52 };
53
54 /* The custom property ID is a CamelArg artifact.
55  * It still identifies the property in state files. */
56 enum {
57         PROP_0,
58         PROP_MAILBOX,
59         PROP_APPLY_FILTERS = 0x2501
60 };
61
62 G_DEFINE_TYPE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER)
63
64 static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder);
65
66 static void
67 imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder,
68                                            GPtrArray *out_uids_to_copy)
69 {
70         GList *keys;
71
72         g_mutex_lock (&folder->priv->move_to_hash_table_lock);
73
74         keys = g_hash_table_get_keys (folder->priv->move_to_real_junk_uids);
75         g_hash_table_steal_all (folder->priv->move_to_real_junk_uids);
76
77         g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
78
79         while (keys != NULL) {
80                 g_ptr_array_add (out_uids_to_copy, keys->data);
81                 keys = g_list_delete_link (keys, keys);
82         }
83 }
84
85 static void
86 imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder,
87                                             GPtrArray *out_uids_to_copy)
88 {
89         GList *keys;
90
91         g_mutex_lock (&folder->priv->move_to_hash_table_lock);
92
93         keys = g_hash_table_get_keys (folder->priv->move_to_real_trash_uids);
94         g_hash_table_steal_all (folder->priv->move_to_real_trash_uids);
95
96         g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
97
98         while (keys != NULL) {
99                 g_ptr_array_add (out_uids_to_copy, keys->data);
100                 keys = g_list_delete_link (keys, keys);
101         }
102 }
103
104 static gboolean
105 imapx_folder_get_apply_filters (CamelIMAPXFolder *folder)
106 {
107         g_return_val_if_fail (folder != NULL, FALSE);
108         g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
109
110         return folder->apply_filters;
111 }
112
113 static void
114 imapx_folder_set_apply_filters (CamelIMAPXFolder *folder,
115                                 gboolean apply_filters)
116 {
117         g_return_if_fail (folder != NULL);
118         g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
119
120         if (folder->apply_filters == apply_filters)
121                 return;
122
123         folder->apply_filters = apply_filters;
124
125         g_object_notify (G_OBJECT (folder), "apply-filters");
126 }
127
128 static void
129 imapx_folder_set_property (GObject *object,
130                            guint property_id,
131                            const GValue *value,
132                            GParamSpec *pspec)
133 {
134         switch (property_id) {
135                 case PROP_APPLY_FILTERS:
136                         imapx_folder_set_apply_filters (
137                                 CAMEL_IMAPX_FOLDER (object),
138                                 g_value_get_boolean (value));
139                         return;
140
141                 case PROP_MAILBOX:
142                         camel_imapx_folder_set_mailbox (
143                                 CAMEL_IMAPX_FOLDER (object),
144                                 g_value_get_object (value));
145                         return;
146         }
147
148         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
149 }
150
151 static void
152 imapx_folder_get_property (GObject *object,
153                            guint property_id,
154                            GValue *value,
155                            GParamSpec *pspec)
156 {
157         switch (property_id) {
158                 case PROP_APPLY_FILTERS:
159                         g_value_set_boolean (
160                                 value,
161                                 imapx_folder_get_apply_filters (
162                                 CAMEL_IMAPX_FOLDER (object)));
163                         return;
164
165                 case PROP_MAILBOX:
166                         g_value_take_object (
167                                 value,
168                                 camel_imapx_folder_ref_mailbox (
169                                 CAMEL_IMAPX_FOLDER (object)));
170                         return;
171         }
172
173         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
174 }
175
176 static void
177 imapx_folder_dispose (GObject *object)
178 {
179         CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
180         CamelStore *store;
181
182         if (folder->cache != NULL) {
183                 g_object_unref (folder->cache);
184                 folder->cache = NULL;
185         }
186
187         if (folder->search != NULL) {
188                 g_object_unref (folder->search);
189                 folder->search = NULL;
190         }
191
192         store = camel_folder_get_parent_store (CAMEL_FOLDER (folder));
193         if (store != NULL) {
194                 camel_store_summary_disconnect_folder_summary (
195                         CAMEL_IMAPX_STORE (store)->summary,
196                         CAMEL_FOLDER (folder)->summary);
197         }
198
199         g_weak_ref_set (&folder->priv->mailbox, NULL);
200
201         /* Chain up to parent's dispose() method. */
202         G_OBJECT_CLASS (camel_imapx_folder_parent_class)->dispose (object);
203 }
204
205 static void
206 imapx_folder_finalize (GObject *object)
207 {
208         CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
209
210         g_mutex_clear (&folder->search_lock);
211         g_mutex_clear (&folder->stream_lock);
212
213         g_mutex_clear (&folder->priv->property_lock);
214
215         g_mutex_clear (&folder->priv->move_to_hash_table_lock);
216         g_hash_table_destroy (folder->priv->move_to_real_junk_uids);
217         g_hash_table_destroy (folder->priv->move_to_real_trash_uids);
218
219         /* Chain up to parent's finalize() method. */
220         G_OBJECT_CLASS (camel_imapx_folder_parent_class)->finalize (object);
221 }
222
223 /* Algorithm for selecting a folder:
224  *
225  *  - If uidvalidity == old uidvalidity
226  *    and exsists == old exists
227  *    and recent == old recent
228  *    and unseen == old unseen
229  *    Assume our summary is correct
230  *  for each summary item
231  *    mark the summary item as 'old/not updated'
232  *  rof
233  *  fetch flags from 1:*
234  *  for each fetch response
235  *    info = summary[index]
236  *    if info.uid != uid
237  *      info = summary_by_uid[uid]
238  *    fi
239  *    if info == NULL
240  *      create new info @ index
241  *    fi
242  *    if got.flags
243  *      update flags
244  *    fi
245  *    if got.header
246  *      update based on header
247  *      mark as retrieved
248  *    else if got.body
249  *      update based on imap body
250  *      mark as retrieved
251  *    fi
252  *
253  *  Async fetch response:
254  *    info = summary[index]
255  *    if info == null
256  *       if uid == null
257  *          force resync/select?
258  *       info = empty @ index
259  *    else if uid && info.uid != uid
260  *       force a resync?
261  *       return
262  *    fi
263  *
264  *    if got.flags {
265  *      info.flags = flags
266  *    }
267  *    if got.header {
268  *      info.init (header)
269  *      info.empty = false
270  *    }
271  *
272  * info.state - 2 bit field in flags
273  *   0 = empty, nothing set
274  *   1 = uid & flags set
275  *   2 = update required
276  *   3 = up to date
277  */
278
279 static void
280 imapx_search_free (CamelFolder *folder,
281                    GPtrArray *uids)
282 {
283         CamelIMAPXFolder *imapx_folder;
284
285         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
286
287         g_return_if_fail (imapx_folder->search);
288
289         g_mutex_lock (&imapx_folder->search_lock);
290
291         camel_folder_search_free_result (imapx_folder->search, uids);
292
293         g_mutex_unlock (&imapx_folder->search_lock);
294 }
295
296 static GPtrArray *
297 imapx_search_by_uids (CamelFolder *folder,
298                       const gchar *expression,
299                       GPtrArray *uids,
300                       GCancellable *cancellable,
301                       GError **error)
302 {
303         CamelIMAPXStore *imapx_store;
304         CamelIMAPXFolder *imapx_folder;
305         CamelIMAPXSearch *imapx_search;
306         CamelIMAPXServer *imapx_server = NULL;
307         const gchar *folder_name;
308         CamelStore *store;
309         GPtrArray *matches;
310
311         if (uids->len == 0)
312                 return g_ptr_array_new ();
313
314         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
315         store = camel_folder_get_parent_store (folder);
316         folder_name = camel_folder_get_full_name (folder);
317
318         imapx_store = CAMEL_IMAPX_STORE (store);
319         if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
320                 imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, NULL);
321
322         g_mutex_lock (&imapx_folder->search_lock);
323
324         imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
325         camel_imapx_search_set_server (imapx_search, imapx_server);
326
327         camel_folder_search_set_folder (imapx_folder->search, folder);
328
329         matches = camel_folder_search_search (
330                 imapx_folder->search, expression, uids, cancellable, error);
331
332         camel_imapx_search_set_server (imapx_search, NULL);
333
334         if (imapx_server)
335                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
336
337         g_mutex_unlock (&imapx_folder->search_lock);
338
339         g_clear_object (&imapx_server);
340
341         return matches;
342 }
343
344 static guint32
345 imapx_count_by_expression (CamelFolder *folder,
346                            const gchar *expression,
347                            GCancellable *cancellable,
348                            GError **error)
349 {
350         CamelIMAPXStore *imapx_store;
351         CamelIMAPXFolder *imapx_folder;
352         CamelIMAPXSearch *imapx_search;
353         CamelIMAPXServer *imapx_server = NULL;
354         const gchar *folder_name;
355         CamelStore *store;
356         guint32 matches;
357
358         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
359         store = camel_folder_get_parent_store (folder);
360         folder_name = camel_folder_get_full_name (folder);
361
362         imapx_store = CAMEL_IMAPX_STORE (store);
363         if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
364                 imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, NULL);
365
366         g_mutex_lock (&imapx_folder->search_lock);
367
368         imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
369         camel_imapx_search_set_server (imapx_search, imapx_server);
370
371         camel_folder_search_set_folder (imapx_folder->search, folder);
372
373         matches = camel_folder_search_count (
374                 imapx_folder->search, expression, cancellable, error);
375
376         camel_imapx_search_set_server (imapx_search, NULL);
377
378         if (imapx_server)
379                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
380
381         g_mutex_unlock (&imapx_folder->search_lock);
382
383         g_clear_object (&imapx_server);
384
385         return matches;
386 }
387
388 static GPtrArray *
389 imapx_search_by_expression (CamelFolder *folder,
390                             const gchar *expression,
391                             GCancellable *cancellable,
392                             GError **error)
393 {
394         CamelIMAPXStore *imapx_store;
395         CamelIMAPXFolder *imapx_folder;
396         CamelIMAPXSearch *imapx_search;
397         CamelIMAPXServer *imapx_server = NULL;
398         const gchar *folder_name;
399         CamelStore *store;
400         GPtrArray *matches;
401
402         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
403         store = camel_folder_get_parent_store (folder);
404         folder_name = camel_folder_get_full_name (folder);
405
406         imapx_store = CAMEL_IMAPX_STORE (store);
407         if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
408                 imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, NULL);
409
410         g_mutex_lock (&imapx_folder->search_lock);
411
412         imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
413         camel_imapx_search_set_server (imapx_search, imapx_server);
414
415         camel_folder_search_set_folder (imapx_folder->search, folder);
416
417         matches = camel_folder_search_search (
418                 imapx_folder->search, expression, NULL, cancellable, error);
419
420         camel_imapx_search_set_server (imapx_search, NULL);
421
422         if (imapx_server)
423                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
424
425         g_mutex_unlock (&imapx_folder->search_lock);
426
427         g_clear_object (&imapx_server);
428
429         return matches;
430 }
431
432 static gchar *
433 imapx_get_filename (CamelFolder *folder,
434                     const gchar *uid,
435                     GError **error)
436 {
437         CamelIMAPXFolder *imapx_folder;
438
439         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
440
441         return camel_data_cache_get_filename (
442                 imapx_folder->cache, "cache", uid);
443 }
444
445 static gboolean
446 imapx_append_message_sync (CamelFolder *folder,
447                            CamelMimeMessage *message,
448                            CamelMessageInfo *info,
449                            gchar **appended_uid,
450                            GCancellable *cancellable,
451                            GError **error)
452 {
453         CamelStore *store;
454         CamelIMAPXStore *imapx_store;
455         CamelIMAPXServer *imapx_server;
456         CamelIMAPXMailbox *mailbox = NULL;
457         const gchar *folder_name;
458         gboolean success = FALSE;
459
460         if (appended_uid != NULL)
461                 *appended_uid = NULL;
462
463         store = camel_folder_get_parent_store (folder);
464         folder_name = camel_folder_get_full_name (folder);
465
466         imapx_store = CAMEL_IMAPX_STORE (store);
467         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
468
469         if (imapx_server == NULL)
470                 goto exit;
471
472         mailbox = camel_imapx_folder_list_mailbox (
473                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
474
475         if (mailbox == NULL) {
476                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
477                 goto exit;
478         }
479
480         success = camel_imapx_server_append_message (
481                 imapx_server, mailbox, folder->summary,
482                 CAMEL_IMAPX_FOLDER (folder)->cache, message,
483                 info, appended_uid, cancellable, error);
484
485         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
486
487 exit:
488         g_clear_object (&mailbox);
489         g_clear_object (&imapx_server);
490
491         return success;
492 }
493
494 static gboolean
495 imapx_expunge_sync (CamelFolder *folder,
496                     GCancellable *cancellable,
497                     GError **error)
498 {
499         CamelStore *store;
500         CamelIMAPXStore *imapx_store;
501         CamelIMAPXServer *imapx_server;
502         CamelIMAPXMailbox *mailbox = NULL;
503         const gchar *folder_name;
504         gboolean success = FALSE;
505
506         store = camel_folder_get_parent_store (folder);
507         folder_name = camel_folder_get_full_name (folder);
508
509         imapx_store = CAMEL_IMAPX_STORE (store);
510         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
511
512         if (imapx_server == NULL)
513                 goto exit;
514
515         mailbox = camel_imapx_folder_list_mailbox (
516                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
517
518         if (mailbox == NULL) {
519                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
520                 goto exit;
521         }
522
523         if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
524                 CamelFolder *trash;
525                 const gchar *full_name;
526                 GError *local_error = NULL;
527
528                 full_name = camel_folder_get_full_name (folder);
529
530                 trash = camel_store_get_trash_folder_sync (store, cancellable, &local_error);
531
532                 if (local_error == NULL && trash && (folder == trash || g_ascii_strcasecmp (full_name, camel_folder_get_full_name (trash)) == 0)) {
533                         CamelMessageInfo *info;
534                         GPtrArray *known_uids;
535                         gint ii;
536
537                         camel_folder_summary_prepare_fetch_all (folder->summary, NULL);
538                         known_uids = camel_folder_summary_get_array (folder->summary);
539
540                         /* it's a real trash folder, thus delete all mails from there */
541                         for (ii = 0; known_uids && ii < known_uids->len; ii++) {
542                                 info = camel_folder_summary_get (folder->summary, g_ptr_array_index (known_uids, ii));
543                                 if (info) {
544                                         camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
545                                         camel_message_info_unref (info);
546                                 }
547                         }
548
549                         camel_folder_summary_free_array (known_uids);
550                 }
551
552                 g_clear_object (&trash);
553                 g_clear_error (&local_error);
554         }
555
556         success = camel_imapx_server_expunge (
557                 imapx_server, mailbox, cancellable, error);
558
559         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
560
561 exit:
562         g_clear_object (&mailbox);
563         g_clear_object (&imapx_server);
564
565         return success;
566 }
567
568 static CamelMimeMessage *
569 imapx_get_message_cached (CamelFolder *folder,
570                           const gchar *message_uid,
571                           GCancellable *cancellable)
572 {
573         CamelIMAPXFolder *imapx_folder;
574         CamelMimeMessage *msg = NULL;
575         CamelStream *stream = NULL;
576         GIOStream *base_stream;
577
578         g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
579         g_return_val_if_fail (message_uid != NULL, NULL);
580
581         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
582
583         base_stream = camel_data_cache_get (imapx_folder->cache, "cur", message_uid, NULL);
584         if (base_stream != NULL) {
585                 stream = camel_stream_new (base_stream);
586                 g_object_unref (base_stream);
587         }
588
589         if (stream != NULL) {
590                 gboolean success;
591
592                 msg = camel_mime_message_new ();
593
594                 g_mutex_lock (&imapx_folder->stream_lock);
595                 success = camel_data_wrapper_construct_from_stream_sync (
596                         CAMEL_DATA_WRAPPER (msg), stream, cancellable, NULL);
597                 if (!success) {
598                         g_object_unref (msg);
599                         msg = NULL;
600                 }
601                 g_mutex_unlock (&imapx_folder->stream_lock);
602                 g_object_unref (stream);
603         }
604
605         return msg;
606 }
607
608 static CamelMimeMessage *
609 imapx_get_message_sync (CamelFolder *folder,
610                         const gchar *uid,
611                         GCancellable *cancellable,
612                         GError **error)
613 {
614         CamelMimeMessage *msg = NULL;
615         CamelStream *stream;
616         CamelStore *store;
617         CamelIMAPXFolder *imapx_folder;
618         GIOStream *base_stream;
619         const gchar *path = NULL;
620         gboolean offline_message = FALSE;
621
622         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
623         store = camel_folder_get_parent_store (folder);
624
625         if (!strchr (uid, '-'))
626                 path = "cur";
627         else {
628                 path = "new";
629                 offline_message = TRUE;
630         }
631
632         base_stream = camel_data_cache_get (
633                 imapx_folder->cache, path, uid, NULL);
634         if (base_stream != NULL) {
635                 stream = camel_stream_new (base_stream);
636                 g_object_unref (base_stream);
637         } else {
638                 CamelIMAPXServer *imapx_server;
639                 CamelIMAPXMailbox *mailbox;
640                 const gchar *folder_name;
641
642                 if (offline_message) {
643                         g_set_error (
644                                 error, CAMEL_FOLDER_ERROR,
645                                 CAMEL_FOLDER_ERROR_INVALID_UID,
646                                 "Offline message vanished from disk: %s", uid);
647                         return NULL;
648                 }
649
650                 folder_name = camel_folder_get_full_name (folder);
651                 imapx_server = camel_imapx_store_ref_server (
652                         CAMEL_IMAPX_STORE (store), folder_name, FALSE, cancellable, error);
653
654                 if (imapx_server == NULL)
655                         return NULL;
656
657                 mailbox = camel_imapx_folder_list_mailbox (
658                         CAMEL_IMAPX_FOLDER (folder), cancellable, error);
659
660                 if (mailbox == NULL) {
661                         camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name);
662                         g_object_unref (imapx_server);
663                         return NULL;
664                 }
665
666                 stream = camel_imapx_server_get_message (
667                         imapx_server, mailbox, folder->summary,
668                         CAMEL_IMAPX_FOLDER (folder)->cache, uid,
669                         cancellable, error);
670
671                 camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name);
672
673                 g_clear_object (&mailbox);
674                 g_clear_object (&imapx_server);
675         }
676
677         if (stream != NULL) {
678                 gboolean success;
679
680                 msg = camel_mime_message_new ();
681
682                 g_mutex_lock (&imapx_folder->stream_lock);
683                 success = camel_data_wrapper_construct_from_stream_sync (
684                         CAMEL_DATA_WRAPPER (msg), stream, cancellable, error);
685                 if (!success) {
686                         g_object_unref (msg);
687                         msg = NULL;
688                 }
689                 g_mutex_unlock (&imapx_folder->stream_lock);
690                 g_object_unref (stream);
691         }
692
693         if (msg != NULL) {
694                 CamelMessageInfo *mi;
695
696                 mi = camel_folder_summary_get (folder->summary, uid);
697                 if (mi != NULL) {
698                         CamelMessageFlags flags;
699                         gboolean has_attachment;
700
701                         flags = camel_message_info_flags (mi);
702                         has_attachment = camel_mime_message_has_attachment (msg);
703                         if (((flags & CAMEL_MESSAGE_ATTACHMENTS) && !has_attachment) ||
704                             ((flags & CAMEL_MESSAGE_ATTACHMENTS) == 0 && has_attachment)) {
705                                 camel_message_info_set_flags (
706                                         mi, CAMEL_MESSAGE_ATTACHMENTS,
707                                         has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
708                         }
709
710                         camel_message_info_unref (mi);
711                 }
712         }
713
714         return msg;
715 }
716
717 static CamelFolderQuotaInfo *
718 imapx_get_quota_info_sync (CamelFolder *folder,
719                            GCancellable *cancellable,
720                            GError **error)
721 {
722         CamelStore *store;
723         CamelIMAPXStore *imapx_store;
724         CamelIMAPXServer *imapx_server;
725         CamelIMAPXMailbox *mailbox = NULL;
726         CamelFolderQuotaInfo *quota_info = NULL;
727         const gchar *folder_name;
728         gchar **quota_roots;
729         gboolean success = FALSE;
730
731         store = camel_folder_get_parent_store (folder);
732         folder_name = camel_folder_get_full_name (folder);
733
734         imapx_store = CAMEL_IMAPX_STORE (store);
735         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
736
737         if (imapx_server == NULL)
738                 goto exit;
739
740         mailbox = camel_imapx_folder_list_mailbox (
741                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
742         if (mailbox == NULL) {
743                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
744                 goto exit;
745         }
746
747         success = camel_imapx_server_update_quota_info (
748                 imapx_server, mailbox, cancellable, error);
749
750         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
751
752         if (!success)
753                 goto exit;
754
755         quota_roots = camel_imapx_mailbox_dup_quota_roots (mailbox);
756
757         /* XXX Just return info for the first quota root, I guess. */
758         if (quota_roots != NULL && quota_roots[0] != NULL) {
759                 quota_info = camel_imapx_store_dup_quota_info (
760                         CAMEL_IMAPX_STORE (store), quota_roots[0]);
761         }
762
763         g_strfreev (quota_roots);
764
765         if (quota_info == NULL)
766                 g_set_error (
767                         error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
768                         _("No quota information available for folder '%s'"),
769                         camel_folder_get_full_name (folder));
770
771 exit:
772         g_clear_object (&mailbox);
773         g_clear_object (&imapx_server);
774
775         return quota_info;
776 }
777
778 static gboolean
779 imapx_purge_message_cache_sync (CamelFolder *folder,
780                                 gchar *start_uid,
781                                 gchar *end_uid,
782                                 GCancellable *cancellable,
783                                 GError **error)
784 {
785         /* Not Implemented for now. */
786         return TRUE;
787 }
788
789 static gboolean
790 imapx_refresh_info_sync (CamelFolder *folder,
791                          GCancellable *cancellable,
792                          GError **error)
793 {
794         CamelStore *store;
795         CamelIMAPXStore *imapx_store;
796         CamelIMAPXServer *imapx_server;
797         CamelIMAPXMailbox *mailbox = NULL;
798         CamelFolderChangeInfo *changes;
799         const gchar *folder_name;
800         gboolean success = FALSE;
801
802         store = camel_folder_get_parent_store (folder);
803         folder_name = camel_folder_get_full_name (folder);
804
805         imapx_store = CAMEL_IMAPX_STORE (store);
806         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, cancellable, error);
807
808         if (imapx_server == NULL)
809                 goto exit;
810
811         mailbox = camel_imapx_folder_list_mailbox (
812                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
813
814         if (mailbox == NULL) {
815                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
816                 goto exit;
817         }
818
819         changes = camel_imapx_server_refresh_info (
820                 imapx_server, mailbox, cancellable, error);
821
822         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
823
824         if (changes != NULL) {
825                 if (camel_folder_change_info_changed (changes))
826                         camel_folder_changed (folder, changes);
827                 camel_folder_change_info_free (changes);
828                 success = TRUE;
829         }
830
831 exit:
832         g_clear_object (&mailbox);
833         g_clear_object (&imapx_server);
834
835         return success;
836 }
837
838 /* Helper for imapx_synchronize_sync() */
839 static gboolean
840 imapx_move_to_real_junk (CamelIMAPXServer *imapx_server,
841                          CamelFolder *folder,
842                          GCancellable *cancellable,
843                          gboolean *out_need_to_expunge,
844                          GError **error)
845 {
846         CamelIMAPXFolder *imapx_folder;
847         CamelIMAPXMailbox *mailbox;
848         CamelIMAPXSettings *settings;
849         GPtrArray *uids_to_copy;
850         gchar *real_junk_path = NULL;
851         gboolean success = TRUE;
852
853         *out_need_to_expunge = FALSE;
854
855         /* Caller already obtained the mailbox from the folder,
856          * so the folder should still have it readily available. */
857         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
858         mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
859         g_return_val_if_fail (mailbox != NULL, FALSE);
860
861         uids_to_copy = g_ptr_array_new_with_free_func (
862                 (GDestroyNotify) camel_pstring_free);
863
864         settings = camel_imapx_server_ref_settings (imapx_server);
865         if (camel_imapx_settings_get_use_real_junk_path (settings)) {
866                 real_junk_path =
867                         camel_imapx_settings_dup_real_junk_path (settings);
868                 imapx_folder_claim_move_to_real_junk_uids (
869                         imapx_folder, uids_to_copy);
870         }
871         g_object_unref (settings);
872
873         if (uids_to_copy->len > 0) {
874                 CamelIMAPXStore *imapx_store;
875                 CamelIMAPXMailbox *destination = NULL;
876
877                 imapx_store = camel_imapx_server_ref_store (imapx_server);
878
879                 if (real_junk_path != NULL) {
880                         folder = camel_store_get_folder_sync (
881                                 CAMEL_STORE (imapx_store),
882                                 real_junk_path, 0,
883                                 cancellable, error);
884                 } else {
885                         g_set_error (
886                                 error, CAMEL_FOLDER_ERROR,
887                                 CAMEL_FOLDER_ERROR_INVALID_PATH,
888                                 _("No destination folder specified"));
889                         folder = NULL;
890                 }
891
892                 if (folder != NULL) {
893                         destination = camel_imapx_folder_list_mailbox (
894                                 CAMEL_IMAPX_FOLDER (folder),
895                                 cancellable, error);
896                         g_object_unref (folder);
897                 }
898
899                 /* Avoid duplicating messages in the Junk folder. */
900                 if (destination == mailbox) {
901                         success = TRUE;
902                 } else if (destination != NULL) {
903                         success = camel_imapx_server_copy_message (
904                                 imapx_server,
905                                 mailbox, destination,
906                                 uids_to_copy, TRUE,
907                                 cancellable, error);
908                         *out_need_to_expunge = success;
909                 } else {
910                         success = FALSE;
911                 }
912
913                 if (!success) {
914                         g_prefix_error (
915                                 error, "%s: ",
916                                 _("Unable to move junk messages"));
917                 }
918
919                 g_clear_object (&destination);
920                 g_clear_object (&imapx_store);
921         }
922
923         g_ptr_array_unref (uids_to_copy);
924         g_free (real_junk_path);
925
926         g_clear_object (&mailbox);
927
928         return success;
929 }
930
931 /* Helper for imapx_synchronize_sync() */
932 static gboolean
933 imapx_move_to_real_trash (CamelIMAPXServer *imapx_server,
934                           CamelFolder *folder,
935                           GCancellable *cancellable,
936                           gboolean *out_need_to_expunge,
937                           GError **error)
938 {
939         CamelIMAPXFolder *imapx_folder;
940         CamelIMAPXMailbox *mailbox;
941         CamelIMAPXSettings *settings;
942         GPtrArray *uids_to_copy;
943         gchar *real_trash_path = NULL;
944         gboolean success = TRUE;
945
946         *out_need_to_expunge = FALSE;
947
948         /* Caller already obtained the mailbox from the folder,
949          * so the folder should still have it readily available. */
950         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
951         mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
952         g_return_val_if_fail (mailbox != NULL, FALSE);
953
954         uids_to_copy = g_ptr_array_new_with_free_func (
955                 (GDestroyNotify) camel_pstring_free);
956
957         settings = camel_imapx_server_ref_settings (imapx_server);
958         if (camel_imapx_settings_get_use_real_trash_path (settings)) {
959                 real_trash_path =
960                         camel_imapx_settings_dup_real_trash_path (settings);
961                 imapx_folder_claim_move_to_real_trash_uids (
962                         CAMEL_IMAPX_FOLDER (folder), uids_to_copy);
963         }
964         g_object_unref (settings);
965
966         if (uids_to_copy->len > 0) {
967                 CamelIMAPXStore *imapx_store;
968                 CamelIMAPXMailbox *destination = NULL;
969
970                 imapx_store = camel_imapx_server_ref_store (imapx_server);
971
972                 if (real_trash_path != NULL) {
973                         folder = camel_store_get_folder_sync (
974                                 CAMEL_STORE (imapx_store),
975                                 real_trash_path, 0,
976                                 cancellable, error);
977                 } else {
978                         g_set_error (
979                                 error, CAMEL_FOLDER_ERROR,
980                                 CAMEL_FOLDER_ERROR_INVALID_PATH,
981                                 _("No destination folder specified"));
982                         folder = NULL;
983                 }
984
985                 if (folder != NULL) {
986                         destination = camel_imapx_folder_list_mailbox (
987                                 CAMEL_IMAPX_FOLDER (folder),
988                                 cancellable, error);
989                         g_object_unref (folder);
990                 }
991
992                 /* Avoid duplicating messages in the Trash folder. */
993                 if (destination == mailbox) {
994                         success = TRUE;
995                 } else if (destination != NULL) {
996                         success = camel_imapx_server_copy_message (
997                                 imapx_server,
998                                 mailbox, destination,
999                                 uids_to_copy, TRUE,
1000                                 cancellable, error);
1001                         *out_need_to_expunge = success;
1002                 } else {
1003                         success = FALSE;
1004                 }
1005
1006                 if (!success) {
1007                         g_prefix_error (
1008                                 error, "%s: ",
1009                                 _("Unable to move deleted messages"));
1010                 }
1011
1012                 g_clear_object (&destination);
1013                 g_clear_object (&imapx_store);
1014         }
1015
1016         g_ptr_array_unref (uids_to_copy);
1017         g_free (real_trash_path);
1018
1019         g_clear_object (&mailbox);
1020
1021         return success;
1022 }
1023
1024 static gboolean
1025 imapx_synchronize_sync (CamelFolder *folder,
1026                         gboolean expunge,
1027                         GCancellable *cancellable,
1028                         GError **error)
1029 {
1030         CamelStore *store;
1031         CamelIMAPXStore *imapx_store;
1032         CamelIMAPXServer *imapx_server;
1033         CamelIMAPXMailbox *mailbox = NULL;
1034         const gchar *folder_name;
1035         gboolean need_to_expunge;
1036         gboolean success = FALSE;
1037
1038         store = camel_folder_get_parent_store (folder);
1039         folder_name = camel_folder_get_full_name (folder);
1040
1041         imapx_store = CAMEL_IMAPX_STORE (store);
1042         /* while it can be expensive job, do not treat it as such, to avoid a blockage
1043            by really expensive jobs */
1044         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
1045
1046         if (imapx_server == NULL)
1047                 goto exit;
1048
1049         mailbox = camel_imapx_folder_list_mailbox (
1050                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1051         if (mailbox == NULL) {
1052                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1053                 goto exit;
1054         }
1055
1056         success = camel_imapx_server_sync_changes (
1057                 imapx_server, mailbox, cancellable, error);
1058
1059         if (success) {
1060                 success = imapx_move_to_real_junk (
1061                         imapx_server, folder, cancellable,
1062                         &need_to_expunge, error);
1063                 expunge |= need_to_expunge;
1064         }
1065
1066         if (success) {
1067                 success = imapx_move_to_real_trash (
1068                         imapx_server, folder, cancellable,
1069                         &need_to_expunge, error);
1070                 expunge |= need_to_expunge;
1071         }
1072
1073         /* Sync twice - make sure deleted flags are written out,
1074          * then sync again incase expunge changed anything */
1075
1076         if (success && expunge) {
1077                 success = camel_imapx_server_expunge (
1078                         imapx_server, mailbox, cancellable, error);
1079         }
1080
1081         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1082
1083 exit:
1084         g_clear_object (&mailbox);
1085         g_clear_object (&imapx_server);
1086
1087         return success;
1088 }
1089
1090 static gboolean
1091 imapx_synchronize_message_sync (CamelFolder *folder,
1092                                 const gchar *uid,
1093                                 GCancellable *cancellable,
1094                                 GError **error)
1095 {
1096         CamelStore *store;
1097         CamelIMAPXStore *imapx_store;
1098         CamelIMAPXServer *imapx_server;
1099         CamelIMAPXMailbox *mailbox = NULL;
1100         const gchar *folder_name;
1101         gboolean success = FALSE;
1102
1103         store = camel_folder_get_parent_store (folder);
1104         folder_name = camel_folder_get_full_name (folder);
1105
1106         imapx_store = CAMEL_IMAPX_STORE (store);
1107         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
1108
1109         if (imapx_server == NULL)
1110                 goto exit;
1111
1112         mailbox = camel_imapx_folder_list_mailbox (
1113                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1114
1115         if (mailbox == NULL) {
1116                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1117                 goto exit;
1118         }
1119
1120         success = camel_imapx_server_sync_message (
1121                 imapx_server, mailbox, folder->summary,
1122                 CAMEL_IMAPX_FOLDER (folder)->cache, uid,
1123                 cancellable, error);
1124
1125         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1126
1127 exit:
1128         g_clear_object (&mailbox);
1129         g_clear_object (&imapx_server);
1130
1131         return success;
1132 }
1133
1134 static gboolean
1135 imapx_transfer_messages_to_sync (CamelFolder *source,
1136                                  GPtrArray *uids,
1137                                  CamelFolder *dest,
1138                                  gboolean delete_originals,
1139                                  GPtrArray **transferred_uids,
1140                                  GCancellable *cancellable,
1141                                  GError **error)
1142 {
1143         CamelStore *store;
1144         CamelIMAPXStore *imapx_store;
1145         CamelIMAPXServer *imapx_server;
1146         CamelIMAPXMailbox *src_mailbox = NULL;
1147         CamelIMAPXMailbox *dst_mailbox = NULL;
1148         const gchar *folder_name;
1149         gboolean success = FALSE;
1150
1151         store = camel_folder_get_parent_store (source);
1152         folder_name = camel_folder_get_full_name (source);
1153
1154         imapx_store = CAMEL_IMAPX_STORE (store);
1155         imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
1156
1157         if (imapx_server == NULL)
1158                 goto exit;
1159
1160         src_mailbox = camel_imapx_folder_list_mailbox (
1161                 CAMEL_IMAPX_FOLDER (source), cancellable, error);
1162
1163         if (src_mailbox == NULL) {
1164                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1165                 goto exit;
1166         }
1167
1168         dst_mailbox = camel_imapx_folder_list_mailbox (
1169                 CAMEL_IMAPX_FOLDER (dest), cancellable, error);
1170
1171         if (dst_mailbox == NULL) {
1172                 camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1173                 goto exit;
1174         }
1175
1176         success = camel_imapx_server_copy_message (
1177                 imapx_server, src_mailbox, dst_mailbox, uids,
1178                 delete_originals, cancellable, error);
1179
1180         camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
1181
1182         /* Update destination folder only if it's not frozen,
1183          * to avoid updating for each "move" action on a single
1184          * message while filtering. */
1185         if (!camel_folder_is_frozen (dest))
1186                 imapx_refresh_info_sync (dest, cancellable, NULL);
1187
1188 exit:
1189         g_clear_object (&src_mailbox);
1190         g_clear_object (&dst_mailbox);
1191         g_clear_object (&imapx_server);
1192
1193         return success;
1194 }
1195
1196 static void
1197 imapx_rename (CamelFolder *folder,
1198               const gchar *new_name)
1199 {
1200         CamelStore *store;
1201         CamelIMAPXStore *imapx_store;
1202         const gchar *folder_name;
1203
1204         store = camel_folder_get_parent_store (folder);
1205         imapx_store = CAMEL_IMAPX_STORE (store);
1206
1207         camel_store_summary_disconnect_folder_summary (
1208                 imapx_store->summary, folder->summary);
1209
1210         /* Chain up to parent's rename() method. */
1211         CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->
1212                 rename (folder, new_name);
1213
1214         folder_name = camel_folder_get_full_name (folder);
1215
1216         camel_store_summary_connect_folder_summary (
1217                 imapx_store->summary, folder_name, folder->summary);
1218 }
1219
1220 static void
1221 camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
1222 {
1223         GObjectClass *object_class;
1224         CamelFolderClass *folder_class;
1225
1226         g_type_class_add_private (class, sizeof (CamelIMAPXFolderPrivate));
1227
1228         object_class = G_OBJECT_CLASS (class);
1229         object_class->set_property = imapx_folder_set_property;
1230         object_class->get_property = imapx_folder_get_property;
1231         object_class->dispose = imapx_folder_dispose;
1232         object_class->finalize = imapx_folder_finalize;
1233
1234         folder_class = CAMEL_FOLDER_CLASS (class);
1235         folder_class->rename = imapx_rename;
1236         folder_class->search_by_expression = imapx_search_by_expression;
1237         folder_class->search_by_uids = imapx_search_by_uids;
1238         folder_class->count_by_expression = imapx_count_by_expression;
1239         folder_class->search_free = imapx_search_free;
1240         folder_class->get_filename = imapx_get_filename;
1241         folder_class->append_message_sync = imapx_append_message_sync;
1242         folder_class->expunge_sync = imapx_expunge_sync;
1243         folder_class->get_message_cached = imapx_get_message_cached;
1244         folder_class->get_message_sync = imapx_get_message_sync;
1245         folder_class->get_quota_info_sync = imapx_get_quota_info_sync;
1246         folder_class->purge_message_cache_sync = imapx_purge_message_cache_sync;
1247         folder_class->refresh_info_sync = imapx_refresh_info_sync;
1248         folder_class->synchronize_sync = imapx_synchronize_sync;
1249         folder_class->synchronize_message_sync = imapx_synchronize_message_sync;
1250         folder_class->transfer_messages_to_sync = imapx_transfer_messages_to_sync;
1251
1252         g_object_class_install_property (
1253                 object_class,
1254                 PROP_APPLY_FILTERS,
1255                 g_param_spec_boolean (
1256                         "apply-filters",
1257                         "Apply Filters",
1258                         _("Apply message _filters to this folder"),
1259                         FALSE,
1260                         G_PARAM_READWRITE |
1261                         CAMEL_PARAM_PERSISTENT));
1262
1263         g_object_class_install_property (
1264                 object_class,
1265                 PROP_MAILBOX,
1266                 g_param_spec_object (
1267                         "mailbox",
1268                         "Mailbox",
1269                         "IMAP mailbox for this folder",
1270                         CAMEL_TYPE_IMAPX_MAILBOX,
1271                         G_PARAM_READWRITE |
1272                         G_PARAM_STATIC_STRINGS));
1273 }
1274
1275 static void
1276 camel_imapx_folder_init (CamelIMAPXFolder *imapx_folder)
1277 {
1278         CamelFolder *folder = CAMEL_FOLDER (imapx_folder);
1279         GHashTable *move_to_real_junk_uids;
1280         GHashTable *move_to_real_trash_uids;
1281
1282         move_to_real_junk_uids = g_hash_table_new_full (
1283                 (GHashFunc) g_direct_hash,
1284                 (GEqualFunc) g_direct_equal,
1285                 (GDestroyNotify) camel_pstring_free,
1286                 (GDestroyNotify) NULL);
1287
1288         move_to_real_trash_uids = g_hash_table_new_full (
1289                 (GHashFunc) g_direct_hash,
1290                 (GEqualFunc) g_direct_equal,
1291                 (GDestroyNotify) camel_pstring_free,
1292                 (GDestroyNotify) NULL);
1293
1294         imapx_folder->priv = CAMEL_IMAPX_FOLDER_GET_PRIVATE (imapx_folder);
1295
1296         folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
1297
1298         folder->permanent_flags =
1299                 CAMEL_MESSAGE_ANSWERED |
1300                 CAMEL_MESSAGE_DELETED |
1301                 CAMEL_MESSAGE_DRAFT |
1302                 CAMEL_MESSAGE_FLAGGED |
1303                 CAMEL_MESSAGE_SEEN |
1304                 CAMEL_MESSAGE_USER;
1305
1306         camel_folder_set_lock_async (folder, TRUE);
1307
1308         g_mutex_init (&imapx_folder->priv->property_lock);
1309
1310         g_mutex_init (&imapx_folder->priv->move_to_hash_table_lock);
1311         imapx_folder->priv->move_to_real_junk_uids = move_to_real_junk_uids;
1312         imapx_folder->priv->move_to_real_trash_uids = move_to_real_trash_uids;
1313 }
1314
1315 CamelFolder *
1316 camel_imapx_folder_new (CamelStore *store,
1317                         const gchar *folder_dir,
1318                         const gchar *folder_name,
1319                         GError **error)
1320 {
1321         CamelFolder *folder;
1322         CamelService *service;
1323         CamelSettings *settings;
1324         CamelIMAPXFolder *imapx_folder;
1325         const gchar *short_name;
1326         gchar *state_file;
1327         gboolean filter_all;
1328         gboolean filter_inbox;
1329         gboolean filter_junk;
1330         gboolean filter_junk_inbox;
1331
1332         d ("opening imap folder '%s'\n", folder_dir);
1333
1334         service = CAMEL_SERVICE (store);
1335
1336         settings = camel_service_ref_settings (service);
1337
1338         g_object_get (
1339                 settings,
1340                 "filter-all", &filter_all,
1341                 "filter-inbox", &filter_inbox,
1342                 "filter-junk", &filter_junk,
1343                 "filter-junk-inbox", &filter_junk_inbox,
1344                 NULL);
1345
1346         g_object_unref (settings);
1347
1348         short_name = strrchr (folder_name, '/');
1349         if (short_name)
1350                 short_name++;
1351         else
1352                 short_name = folder_name;
1353
1354         folder = g_object_new (
1355                 CAMEL_TYPE_IMAPX_FOLDER,
1356                 "display-name", short_name,
1357                 "full_name", folder_name,
1358                 "parent-store", store, NULL);
1359
1360         folder->summary = camel_imapx_summary_new (folder);
1361         if (folder->summary == NULL) {
1362                 g_set_error (
1363                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1364                         _("Could not create folder summary for %s"),
1365                         short_name);
1366                 return NULL;
1367         }
1368
1369         imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1370         imapx_folder->cache = camel_data_cache_new (folder_dir, error);
1371         if (imapx_folder->cache == NULL) {
1372                 g_prefix_error (
1373                         error, _("Could not create cache for %s: "),
1374                         short_name);
1375                 return NULL;
1376         }
1377
1378         state_file = g_build_filename (folder_dir, "cmeta", NULL);
1379         camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
1380         g_free (state_file);
1381         camel_object_state_read (CAMEL_OBJECT (folder));
1382
1383         imapx_folder->search = camel_imapx_search_new ();
1384         g_mutex_init (&imapx_folder->search_lock);
1385         g_mutex_init (&imapx_folder->stream_lock);
1386
1387         if (filter_all)
1388                 folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1389
1390         if (camel_imapx_mailbox_is_inbox (folder_name)) {
1391                 if (filter_inbox)
1392                         folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1393
1394                 if (filter_junk)
1395                         folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
1396         } else {
1397                 if (filter_junk && !filter_junk_inbox)
1398                         folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
1399
1400                 if (imapx_folder_get_apply_filters (imapx_folder))
1401                         folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1402         }
1403
1404         camel_store_summary_connect_folder_summary (
1405                 CAMEL_IMAPX_STORE (store)->summary,
1406                 folder_name, folder->summary);
1407
1408         return folder;
1409 }
1410
1411 /**
1412  * camel_imapx_folder_ref_mailbox:
1413  * @folder: a #CamelIMAPXFolder
1414  *
1415  * Returns the #CamelIMAPXMailbox for @folder from the current IMAP server
1416  * connection, or %NULL if @folder's #CamelFolder:parent-store is disconnected
1417  * from the IMAP server.
1418  *
1419  * The returned #CamelIMAPXMailbox is referenced for thread-safety and
1420  * should be unreferenced with g_object_unref() when finished with it.
1421  *
1422  * Returns: a #CamelIMAPXMailbox, or %NULL
1423  *
1424  * Since: 3.12
1425  **/
1426 CamelIMAPXMailbox *
1427 camel_imapx_folder_ref_mailbox (CamelIMAPXFolder *folder)
1428 {
1429         g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
1430
1431         return g_weak_ref_get (&folder->priv->mailbox);
1432 }
1433
1434 /**
1435  * camel_imapx_folder_set_mailbox:
1436  * @folder: a #CamelIMAPXFolder
1437  * @mailbox: a #CamelIMAPXMailbox
1438  *
1439  * Sets the #CamelIMAPXMailbox for @folder from the current IMAP server
1440  * connection.  Note that #CamelIMAPXFolder only holds a weak reference
1441  * on its #CamelIMAPXMailbox so that when the IMAP server connection is
1442  * lost, all mailbox instances are automatically destroyed.
1443  *
1444  * Since: 3.12
1445  **/
1446 void
1447 camel_imapx_folder_set_mailbox (CamelIMAPXFolder *folder,
1448                                 CamelIMAPXMailbox *mailbox)
1449 {
1450         g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1451         g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
1452
1453         g_weak_ref_set (&folder->priv->mailbox, mailbox);
1454
1455         g_object_notify (G_OBJECT (folder), "mailbox");
1456 }
1457
1458 /**
1459  * camel_imapx_folder_list_mailbox:
1460  * @folder: a #CamelIMAPXFolder
1461  * @cancellable: optional #GCancellable object, or %NULL
1462  * @error: return location for a #GError, or %NULL
1463  *
1464  * Ensures that @folder's #CamelIMAPXFolder:mailbox property is set,
1465  * going so far as to issue a LIST command if necessary (but should
1466  * be a rarely needed last resort).
1467  *
1468  * If @folder's #CamelFolder:parent-store is disconnected from the IMAP
1469  * server or an error occurs during the LIST command, the function sets
1470  * @error and returns %NULL.
1471  *
1472  * The returned #CamelIMAPXMailbox is referenced for thread-safety and
1473  * should be unreferenced with g_object_unref() when finished with it.
1474  *
1475  * Returns: a #CamelIMAPXMailbox, or %NULL
1476  *
1477  * Since: 3.12
1478  **/
1479 CamelIMAPXMailbox *
1480 camel_imapx_folder_list_mailbox (CamelIMAPXFolder *folder,
1481                                  GCancellable *cancellable,
1482                                  GError **error)
1483 {
1484         CamelIMAPXStore *imapx_store;
1485         CamelIMAPXServer *server = NULL;
1486         CamelIMAPXMailbox *mailbox;
1487         CamelStore *parent_store;
1488         CamelStoreInfo *store_info;
1489         CamelIMAPXStoreInfo *imapx_store_info;
1490         gchar *folder_path = NULL;
1491         gchar *mailbox_name = NULL;
1492         gchar *pattern;
1493         gboolean success;
1494
1495         g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
1496
1497         /* First check if we already have a mailbox. */
1498
1499         mailbox = camel_imapx_folder_ref_mailbox (folder);
1500         if (mailbox != NULL)
1501                 goto exit;
1502
1503         /* Obtain the mailbox name from the store summary. */
1504
1505         folder_path = camel_folder_dup_full_name (CAMEL_FOLDER (folder));
1506         parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (folder));
1507
1508         imapx_store = CAMEL_IMAPX_STORE (parent_store);
1509
1510         store_info = camel_store_summary_path (
1511                 imapx_store->summary, folder_path);
1512
1513         /* This should never fail.  We needed the CamelStoreInfo
1514          * to instantiate the CamelIMAPXFolder in the first place. */
1515         g_return_val_if_fail (store_info != NULL, FALSE);
1516
1517         imapx_store_info = (CamelIMAPXStoreInfo *) store_info;
1518         mailbox_name = g_strdup (imapx_store_info->mailbox_name);
1519
1520         camel_store_summary_info_unref (imapx_store->summary, store_info);
1521
1522         /* See if the CamelIMAPXServer already has the mailbox. */
1523
1524         server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1525
1526         if (server == NULL)
1527                 goto exit;
1528
1529         mailbox = camel_imapx_server_ref_mailbox (server, mailbox_name);
1530         if (mailbox != NULL) {
1531                 camel_imapx_folder_set_mailbox (folder, mailbox);
1532                 goto exit;
1533         }
1534
1535         /* Last resort is to issue a LIST command.  Maintainer should
1536          * monitor IMAP logs to make sure this is rarely if ever used. */
1537
1538         pattern = camel_utf8_utf7 (mailbox_name);
1539
1540         /* This creates a mailbox instance from the LIST response. */
1541         success = camel_imapx_server_list (
1542                 server, pattern, 0, cancellable, error);
1543
1544         g_free (pattern);
1545
1546         if (!success)
1547                 goto exit;
1548
1549         /* This might still return NULL if the mailbox has a
1550          * /NonExistent attribute.  Otherwise this should work. */
1551         mailbox = camel_imapx_server_ref_mailbox (server, mailbox_name);
1552         if (mailbox != NULL) {
1553                 camel_imapx_folder_set_mailbox (folder, mailbox);
1554         } else {
1555                 g_set_error (
1556                         error, CAMEL_FOLDER_ERROR,
1557                         CAMEL_FOLDER_ERROR_INVALID_STATE,
1558                         _("No IMAP mailbox available for folder '%s'"),
1559                         camel_folder_get_display_name (CAMEL_FOLDER (folder)));
1560         }
1561
1562 exit:
1563         g_clear_object (&server);
1564
1565         g_free (folder_path);
1566         g_free (mailbox_name);
1567
1568         return mailbox;
1569 }
1570
1571 /**
1572  * camel_imapx_folder_copy_message_map:
1573  * @folder: a #CamelIMAPXFolder
1574  *
1575  * Returns a #GSequence of 32-bit integers representing the locally cached
1576  * mapping of message sequence numbers to unique identifiers.
1577  *
1578  * Free the returns #GSequence with g_sequence_free().
1579  *
1580  * Returns: a #GSequence
1581  *
1582  * Since: 3.12
1583  **/
1584 GSequence *
1585 camel_imapx_folder_copy_message_map (CamelIMAPXFolder *folder)
1586 {
1587         CamelFolderSummary *summary;
1588         GSequence *message_map;
1589         GPtrArray *array;
1590         guint ii;
1591
1592         g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
1593
1594         summary = CAMEL_FOLDER (folder)->summary;
1595         array = camel_folder_summary_get_array (summary);
1596         camel_folder_sort_uids (CAMEL_FOLDER (folder), array);
1597
1598         message_map = g_sequence_new (NULL);
1599
1600         for (ii = 0; ii < array->len; ii++) {
1601                 guint32 uid = strtoul (array->pdata[ii], NULL, 10);
1602                 g_sequence_append (message_map, GUINT_TO_POINTER (uid));
1603         }
1604
1605         camel_folder_summary_free_array (array);
1606
1607         return message_map;
1608 }
1609
1610 /**
1611  * camel_imapx_folder_add_move_to_real_junk:
1612  * @folder: a #CamelIMAPXFolder
1613  * @message_uid: a message UID
1614  *
1615  * Adds @message_uid to a pool of messages to be moved to a real junk
1616  * folder the next time @folder is explicitly synchronized by way of
1617  * camel_folder_synchronize() or camel_folder_synchronize_sync().
1618  *
1619  * This only applies when using a real folder to track junk messages,
1620  * as specified by the #CamelIMAPXSettings:use-real-junk-path setting.
1621  *
1622  * Since: 3.8
1623  **/
1624 void
1625 camel_imapx_folder_add_move_to_real_junk (CamelIMAPXFolder *folder,
1626                                           const gchar *message_uid)
1627 {
1628         g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1629         g_return_if_fail (message_uid != NULL);
1630
1631         g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1632
1633         g_hash_table_add (
1634                 folder->priv->move_to_real_junk_uids,
1635                 (gpointer) camel_pstring_strdup (message_uid));
1636
1637         g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1638 }
1639
1640 /**
1641  * camel_imapx_folder_add_move_to_real_trash:
1642  * @folder: a #CamelIMAPXFolder
1643  * @message_uid: a message UID
1644  *
1645  * Adds @message_uid to a pool of messages to be moved to a real trash
1646  * folder the next time @folder is explicitly synchronized by way of
1647  * camel_folder_synchronize() or camel_folder_synchronize_sync().
1648  *
1649  * This only applies when using a real folder to track deleted messages,
1650  * as specified by the #CamelIMAPXSettings:use-real-trash-path setting.
1651  *
1652  * Since: 3.8
1653  **/
1654 void
1655 camel_imapx_folder_add_move_to_real_trash (CamelIMAPXFolder *folder,
1656                                            const gchar *message_uid)
1657 {
1658         g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1659         g_return_if_fail (message_uid != NULL);
1660
1661         g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1662
1663         g_hash_table_add (
1664                 folder->priv->move_to_real_trash_uids,
1665                 (gpointer) camel_pstring_strdup (message_uid));
1666
1667         g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1668 }
1669
1670 /**
1671  * camel_imapx_folder_invalidate_local_cache:
1672  * @folder: a #CamelIMAPXFolder
1673  * @new_uidvalidity: the new UIDVALIDITY value
1674  *
1675  * Call this function when the IMAP server reports a different UIDVALIDITY
1676  * value than what is presently cached.  This means all cached message UIDs
1677  * are now invalid and must be discarded.
1678  *
1679  * The local cache for @folder is reset and the @new_uidvalidity value is
1680  * recorded in the newly-reset cache.
1681  *
1682  * Since: 3.10
1683  **/
1684 void
1685 camel_imapx_folder_invalidate_local_cache (CamelIMAPXFolder *folder,
1686                                            guint64 new_uidvalidity)
1687 {
1688         CamelFolderSummary *summary;
1689         CamelFolderChangeInfo *changes;
1690         GPtrArray *array;
1691         guint ii;
1692
1693         g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1694         g_return_if_fail (new_uidvalidity > 0);
1695
1696         summary = CAMEL_FOLDER (folder)->summary;
1697
1698         changes = camel_folder_change_info_new ();
1699         array = camel_folder_summary_get_array (summary);
1700
1701         for (ii = 0; ii < array->len; ii++) {
1702                 const gchar *uid = array->pdata[ii];
1703                 camel_folder_change_info_change_uid (changes, uid);
1704         }
1705
1706         CAMEL_IMAPX_SUMMARY (summary)->validity = new_uidvalidity;
1707         camel_folder_summary_touch (summary);
1708         camel_folder_summary_save_to_db (summary, NULL);
1709
1710         camel_data_cache_clear (folder->cache, "cache");
1711         camel_data_cache_clear (folder->cache, "cur");
1712
1713         camel_folder_changed (CAMEL_FOLDER (folder), changes);
1714
1715         camel_folder_change_info_free (changes);
1716         camel_folder_summary_free_array (array);
1717 }
1718