Make use of G_DEFINE_QUARK()
[platform/upstream/evolution-data-server.git] / camel / camel-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-folder.c: Abstract class for an email folder */
3
4 /*
5  * Author:
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *
8  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include "camel-db.h"
34 #include "camel-debug.h"
35 #include "camel-filter-driver.h"
36 #include "camel-folder.h"
37 #include "camel-mempool.h"
38 #include "camel-mime-message.h"
39 #include "camel-operation.h"
40 #include "camel-session.h"
41 #include "camel-store.h"
42 #include "camel-vtrash-folder.h"
43 #include "camel-string-utils.h"
44
45 #define CAMEL_FOLDER_GET_PRIVATE(obj) \
46         (G_TYPE_INSTANCE_GET_PRIVATE \
47         ((obj), CAMEL_TYPE_FOLDER, CamelFolderPrivate))
48
49 #define d(x)
50 #define w(x)
51
52 typedef struct _AsyncContext AsyncContext;
53 typedef struct _SignalData SignalData;
54 typedef struct _FolderFilterData FolderFilterData;
55
56 struct _CamelFolderPrivate {
57         GRecMutex lock;
58         GMutex change_lock;
59         /* must require the 'change_lock' to access this */
60         gint frozen;
61         CamelFolderChangeInfo *changed_frozen; /* queues changed events */
62         gboolean skip_folder_lock;
63
64         /* Changes to be emitted from an idle callback. */
65         CamelFolderChangeInfo *pending_changes;
66
67         gpointer parent_store;  /* weak pointer */
68
69         GMutex property_lock;
70
71         gchar *full_name;
72         gchar *display_name;
73         gchar *description;
74 };
75
76 struct _AsyncContext {
77         /* arguments */
78         CamelMimeMessage *message;  /* also a result */
79         CamelMessageInfo *info;
80         CamelFolder *destination;
81         GPtrArray *message_uids;
82         gchar *message_uid;         /* also a result */
83         gboolean delete_originals;
84         gboolean expunge;
85         CamelFetchType fetch_type;
86         gint limit;
87         gchar *start_uid;
88         gchar *end_uid;
89
90         /* results */
91         GPtrArray *transferred_uids;
92         CamelFolderQuotaInfo *quota_info;
93         gboolean success; /* A result that mention that there are more messages in fetch_messages operation */
94 };
95
96 struct _CamelFolderChangeInfoPrivate {
97         GHashTable *uid_stored; /* what we have stored, which array they're in */
98         GHashTable *uid_source; /* used to create unique lists */
99         GPtrArray  *uid_filter; /* uids to be filtered */
100         CamelMemPool *uid_pool; /* pool used to store copies of uid strings */
101 };
102
103 struct _SignalData {
104         CamelFolder *folder;
105         gchar *folder_name;
106 };
107
108 struct _FolderFilterData {
109         GPtrArray *recents;
110         GPtrArray *junk;
111         GPtrArray *notjunk;
112         CamelFolder *folder;
113         CamelFilterDriver *driver;
114 };
115
116 enum {
117         PROP_0,
118         PROP_DESCRIPTION,
119         PROP_DISPLAY_NAME,
120         PROP_FULL_NAME,
121         PROP_PARENT_STORE
122 };
123
124 enum {
125         CHANGED,
126         DELETED,
127         RENAMED,
128         LAST_SIGNAL
129 };
130
131 static guint signals[LAST_SIGNAL];
132
133 G_DEFINE_ABSTRACT_TYPE (CamelFolder, camel_folder, CAMEL_TYPE_OBJECT)
134
135 static void
136 async_context_free (AsyncContext *async_context)
137 {
138         if (async_context->message != NULL)
139                 g_object_unref (async_context->message);
140
141         /* XXX This is actually an unref.  Good god, no wonder we
142          *     have so many crashes involving CamelMessageInfos! */
143         if (async_context->info != NULL)
144                 camel_message_info_free (async_context->info);
145
146         if (async_context->destination != NULL)
147                 g_object_unref (async_context->destination);
148
149         if (async_context->message_uids != NULL) {
150                 g_ptr_array_foreach (
151                         async_context->message_uids, (GFunc) g_free, NULL);
152                 g_ptr_array_free (async_context->message_uids, TRUE);
153         }
154
155         if (async_context->transferred_uids != NULL) {
156                 g_ptr_array_foreach (
157                         async_context->transferred_uids, (GFunc) g_free, NULL);
158                 g_ptr_array_free (async_context->transferred_uids, TRUE);
159         }
160
161         if (async_context->quota_info != NULL)
162                 camel_folder_quota_info_free (async_context->quota_info);
163
164         g_free (async_context->message_uid);
165         g_free (async_context->start_uid);
166         g_free (async_context->end_uid);
167
168         g_slice_free (AsyncContext, async_context);
169 }
170
171 static void
172 signal_data_free (SignalData *signal_data)
173 {
174         if (signal_data->folder != NULL)
175                 g_object_unref (signal_data->folder);
176
177         g_free (signal_data->folder_name);
178
179         g_slice_free (SignalData, signal_data);
180 }
181
182 static gboolean
183 folder_emit_changed_cb (gpointer user_data)
184 {
185         SignalData *signal_data = user_data;
186         CamelFolderChangeInfo *changes;
187
188         camel_folder_lock (signal_data->folder, CAMEL_FOLDER_CHANGE_LOCK);
189         changes = signal_data->folder->priv->pending_changes;
190         signal_data->folder->priv->pending_changes = NULL;
191         camel_folder_unlock (signal_data->folder, CAMEL_FOLDER_CHANGE_LOCK);
192
193         g_signal_emit (signal_data->folder, signals[CHANGED], 0, changes);
194
195         camel_folder_change_info_free (changes);
196
197         return FALSE;
198 }
199
200 static gboolean
201 folder_emit_deleted_cb (gpointer user_data)
202 {
203         SignalData *signal_data = user_data;
204
205         g_signal_emit (signal_data->folder, signals[DELETED], 0);
206
207         return FALSE;
208 }
209
210 static gboolean
211 folder_emit_renamed_cb (gpointer user_data)
212 {
213         SignalData *signal_data = user_data;
214
215         g_signal_emit (
216                 signal_data->folder,
217                 signals[RENAMED], 0,
218                 signal_data->folder_name);
219
220         return FALSE;
221 }
222
223 static void
224 folder_filter_data_free (FolderFilterData *data)
225 {
226         if (data->driver != NULL)
227                 g_object_unref (data->driver);
228         if (data->recents != NULL)
229                 camel_folder_free_deep (data->folder, data->recents);
230         if (data->junk != NULL)
231                 camel_folder_free_deep (data->folder, data->junk);
232         if (data->notjunk != NULL)
233                 camel_folder_free_deep (data->folder, data->notjunk);
234
235         /* XXX Too late to pass a GError here. */
236         camel_folder_summary_save_to_db (data->folder->summary, NULL);
237
238         camel_folder_thaw (data->folder);
239         g_object_unref (data->folder);
240
241         g_slice_free (FolderFilterData, data);
242 }
243
244 static void
245 folder_filter (CamelSession *session,
246                GCancellable *cancellable,
247                FolderFilterData *data,
248                GError **error)
249 {
250         CamelMessageInfo *info;
251         CamelStore *parent_store;
252         gint i, status = 0;
253         CamelJunkFilter *junk_filter;
254         gboolean synchronize = FALSE;
255         const gchar *display_name;
256
257         display_name = camel_folder_get_display_name (data->folder);
258         parent_store = camel_folder_get_parent_store (data->folder);
259         junk_filter = camel_session_get_junk_filter (session);
260
261         /* Keep the junk filter alive until we're done. */
262         if (junk_filter != NULL)
263                 g_object_ref (junk_filter);
264
265         if (data->junk) {
266                 gboolean success = TRUE;
267
268                 /* Translators: The %s is replaced with the
269                  * folder name where the operation is running. */
270                 camel_operation_push_message (
271                         cancellable, ngettext (
272                         "Learning new spam message in '%s'",
273                         "Learning new spam messages in '%s'",
274                         data->junk->len), display_name);
275
276                 for (i = 0; success && i < data->junk->len; i++) {
277                         CamelMimeMessage *message;
278                         gint pc = 100 * i / data->junk->len;
279
280                         if (g_cancellable_set_error_if_cancelled (
281                                 cancellable, error))
282                                 break;
283
284                         message = camel_folder_get_message_sync (
285                                 data->folder, data->junk->pdata[i],
286                                 cancellable, error);
287
288                         if (message == NULL)
289                                 break;
290
291                         camel_operation_progress (cancellable, pc);
292                         success = camel_junk_filter_learn_junk (
293                                 junk_filter, message, cancellable, error);
294                         g_object_unref (message);
295
296                         synchronize |= success;
297                 }
298
299                 camel_operation_pop_message (cancellable);
300         }
301
302         if (*error != NULL)
303                 goto exit;
304
305         if (data->notjunk) {
306                 gboolean success = TRUE;
307
308                 /* Translators: The %s is replaced with the
309                  * folder name where the operation is running. */
310                 camel_operation_push_message (
311                         cancellable, ngettext (
312                         "Learning new ham message in '%s'",
313                         "Learning new ham messages in '%s'",
314                         data->notjunk->len), display_name);
315
316                 for (i = 0; success && i < data->notjunk->len; i++) {
317                         CamelMimeMessage *message;
318                         gint pc = 100 * i / data->notjunk->len;
319
320                         if (g_cancellable_set_error_if_cancelled (
321                                 cancellable, error))
322                                 break;
323
324                         message = camel_folder_get_message_sync (
325                                 data->folder, data->notjunk->pdata[i],
326                                 cancellable, error);
327
328                         if (message == NULL)
329                                 break;
330
331                         camel_operation_progress (cancellable, pc);
332                         success = camel_junk_filter_learn_not_junk (
333                                 junk_filter, message, cancellable, error);
334                         g_object_unref (message);
335
336                         synchronize |= success;
337                 }
338
339                 camel_operation_pop_message (cancellable);
340         }
341
342         if (*error != NULL)
343                 goto exit;
344
345         if (synchronize)
346                 camel_junk_filter_synchronize (
347                         junk_filter, cancellable, error);
348
349         if (*error != NULL)
350                 goto exit;
351
352         if (data->driver && data->recents) {
353                 CamelService *service;
354                 const gchar *store_uid;
355
356                 /* Translators: The %s is replaced with the
357                  * folder name where the operation is running. */
358                 camel_operation_push_message (
359                         cancellable, ngettext (
360                         "Filtering new message in '%s'",
361                         "Filtering new messages in '%s'",
362                         data->recents->len), display_name);
363
364                 service = CAMEL_SERVICE (parent_store);
365                 store_uid = camel_service_get_uid (service);
366
367                 for (i = 0; status == 0 && i < data->recents->len; i++) {
368                         gchar *uid = data->recents->pdata[i];
369                         gint pc = 100 * i / data->recents->len;
370
371                         camel_operation_progress (cancellable, pc);
372
373                         info = camel_folder_get_message_info (
374                                 data->folder, uid);
375                         if (info == NULL) {
376                                 g_warning (
377                                         "uid '%s' vanished from folder '%s'",
378                                         uid, display_name);
379                                 continue;
380                         }
381
382                         status = camel_filter_driver_filter_message (
383                                 data->driver, NULL, info, uid, data->folder,
384                                 store_uid, store_uid, cancellable, error);
385
386                         camel_folder_free_message_info (data->folder, info);
387                 }
388
389                 camel_operation_pop_message (cancellable);
390
391                 camel_filter_driver_flush (data->driver, error);
392         }
393
394 exit:
395         if (junk_filter != NULL)
396                 g_object_unref (junk_filter);
397 }
398
399 static gint
400 cmp_array_uids (gconstpointer a,
401                 gconstpointer b,
402                 gpointer user_data)
403 {
404         const gchar *uid1 = *(const gchar **) a;
405         const gchar *uid2 = *(const gchar **) b;
406         CamelFolder *folder = user_data;
407
408         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
409
410         return camel_folder_cmp_uids (folder, uid1, uid2);
411 }
412
413 static void
414 folder_transfer_message_to (CamelFolder *source,
415                             const gchar *uid,
416                             CamelFolder *dest,
417                             gchar **transferred_uid,
418                             gboolean delete_original,
419                             GCancellable *cancellable,
420                             GError **error)
421 {
422         CamelMimeMessage *msg;
423         CamelMessageInfo *minfo, *info;
424         GError *local_error = NULL;
425
426         /* Default implementation. */
427
428         msg = camel_folder_get_message_sync (source, uid, cancellable, error);
429         if (!msg)
430                 return;
431
432         /* if its deleted we poke the flags, so we need to copy the messageinfo */
433         if ((source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
434                         && (minfo = camel_folder_get_message_info (source, uid))) {
435                 info = camel_message_info_clone (minfo);
436                 camel_folder_free_message_info (source, minfo);
437         } else
438                 info = camel_message_info_new_from_header (NULL, ((CamelMimePart *) msg)->headers);
439
440         /* unset deleted flag when transferring from trash folder */
441         if ((source->folder_flags & CAMEL_FOLDER_IS_TRASH) != 0)
442                 camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, 0);
443         /* unset junk flag when transferring from junk folder */
444         if ((source->folder_flags & CAMEL_FOLDER_IS_JUNK) != 0)
445                 camel_message_info_set_flags (info, CAMEL_MESSAGE_JUNK, 0);
446
447         camel_folder_append_message_sync (
448                 dest, msg, info, transferred_uid,
449                 cancellable, &local_error);
450         g_object_unref (msg);
451
452         if (local_error != NULL)
453                 g_propagate_error (error, local_error);
454         else if (delete_original)
455                 camel_folder_set_message_flags (
456                         source, uid, CAMEL_MESSAGE_DELETED |
457                         CAMEL_MESSAGE_SEEN, ~0);
458
459         camel_message_info_free (info);
460 }
461
462 static void
463 folder_set_parent_store (CamelFolder *folder,
464                          CamelStore *parent_store)
465 {
466         g_return_if_fail (CAMEL_IS_STORE (parent_store));
467         g_return_if_fail (folder->priv->parent_store == NULL);
468
469         folder->priv->parent_store = parent_store;
470
471         g_object_add_weak_pointer (
472                 G_OBJECT (parent_store), &folder->priv->parent_store);
473 }
474
475 static void
476 folder_set_property (GObject *object,
477                      guint property_id,
478                      const GValue *value,
479                      GParamSpec *pspec)
480 {
481         switch (property_id) {
482                 case PROP_DESCRIPTION:
483                         camel_folder_set_description (
484                                 CAMEL_FOLDER (object),
485                                 g_value_get_string (value));
486                         return;
487
488                 case PROP_DISPLAY_NAME:
489                         camel_folder_set_display_name (
490                                 CAMEL_FOLDER (object),
491                                 g_value_get_string (value));
492                         return;
493
494                 case PROP_FULL_NAME:
495                         camel_folder_set_full_name (
496                                 CAMEL_FOLDER (object),
497                                 g_value_get_string (value));
498                         return;
499
500                 case PROP_PARENT_STORE:
501                         folder_set_parent_store (
502                                 CAMEL_FOLDER (object),
503                                 g_value_get_object (value));
504                         return;
505         }
506
507         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
508 }
509
510 static void
511 folder_get_property (GObject *object,
512                      guint property_id,
513                      GValue *value,
514                      GParamSpec *pspec)
515 {
516         switch (property_id) {
517                 case PROP_DESCRIPTION:
518                         g_value_take_string (
519                                 value, camel_folder_dup_description (
520                                 CAMEL_FOLDER (object)));
521                         return;
522
523                 case PROP_DISPLAY_NAME:
524                         g_value_take_string (
525                                 value, camel_folder_dup_display_name (
526                                 CAMEL_FOLDER (object)));
527                         return;
528
529                 case PROP_FULL_NAME:
530                         g_value_take_string (
531                                 value, camel_folder_dup_full_name (
532                                 CAMEL_FOLDER (object)));
533                         return;
534
535                 case PROP_PARENT_STORE:
536                         g_value_set_object (
537                                 value, camel_folder_get_parent_store (
538                                 CAMEL_FOLDER (object)));
539                         return;
540         }
541
542         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
543 }
544
545 static void
546 folder_dispose (GObject *object)
547 {
548         CamelFolder *folder;
549
550         folder = CAMEL_FOLDER (object);
551
552         if (folder->priv->parent_store != NULL) {
553                 g_object_remove_weak_pointer (
554                         G_OBJECT (folder->priv->parent_store),
555                         &folder->priv->parent_store);
556                 folder->priv->parent_store = NULL;
557         }
558
559         if (folder->summary) {
560                 g_object_unref (folder->summary);
561                 folder->summary = NULL;
562         }
563
564         /* Chain up to parent's dispose () method. */
565         G_OBJECT_CLASS (camel_folder_parent_class)->dispose (object);
566 }
567
568 static void
569 folder_finalize (GObject *object)
570 {
571         CamelFolderPrivate *priv;
572
573         priv = CAMEL_FOLDER_GET_PRIVATE (object);
574
575         g_mutex_clear (&priv->property_lock);
576
577         g_free (priv->full_name);
578         g_free (priv->display_name);
579         g_free (priv->description);
580
581         camel_folder_change_info_free (priv->changed_frozen);
582
583         if (priv->pending_changes != NULL)
584                 camel_folder_change_info_free (priv->pending_changes);
585
586         g_rec_mutex_clear (&priv->lock);
587         g_mutex_clear (&priv->change_lock);
588
589         /* Chain up to parent's finalize () method. */
590         G_OBJECT_CLASS (camel_folder_parent_class)->finalize (object);
591 }
592
593 static gint
594 folder_get_message_count (CamelFolder *folder)
595 {
596         g_return_val_if_fail (folder->summary != NULL, -1);
597
598         return camel_folder_summary_count (folder->summary);
599 }
600
601 static CamelMessageFlags
602 folder_get_permanent_flags (CamelFolder *folder)
603 {
604         return folder->permanent_flags;
605 }
606
607 static CamelMessageFlags
608 folder_get_message_flags (CamelFolder *folder,
609                           const gchar *uid)
610 {
611         CamelMessageInfo *info;
612         CamelMessageFlags flags;
613
614         g_return_val_if_fail (folder->summary != NULL, 0);
615
616         info = camel_folder_summary_get (folder->summary, uid);
617         if (info == NULL)
618                 return 0;
619
620         flags = camel_message_info_flags (info);
621         camel_message_info_free (info);
622
623         return flags;
624 }
625
626 static gboolean
627 folder_set_message_flags (CamelFolder *folder,
628                           const gchar *uid,
629                           CamelMessageFlags flags,
630                           CamelMessageFlags set)
631 {
632         CamelMessageInfo *info;
633         gint res;
634
635         g_return_val_if_fail (folder->summary != NULL, FALSE);
636
637         info = camel_folder_summary_get (folder->summary, uid);
638         if (info == NULL)
639                 return FALSE;
640
641         res = camel_message_info_set_flags (info, flags, set);
642         camel_message_info_free (info);
643
644         return res;
645 }
646
647 static gboolean
648 folder_get_message_user_flag (CamelFolder *folder,
649                               const gchar *uid,
650                               const gchar *name)
651 {
652         CamelMessageInfo *info;
653         gboolean ret;
654
655         g_return_val_if_fail (folder->summary != NULL, FALSE);
656
657         info = camel_folder_summary_get (folder->summary, uid);
658         if (info == NULL)
659                 return FALSE;
660
661         ret = camel_message_info_user_flag (info, name);
662         camel_message_info_free (info);
663
664         return ret;
665 }
666
667 static void
668 folder_set_message_user_flag (CamelFolder *folder,
669                               const gchar *uid,
670                               const gchar *name,
671                               gboolean value)
672 {
673         CamelMessageInfo *info;
674
675         g_return_if_fail (folder->summary != NULL);
676
677         info = camel_folder_summary_get (folder->summary, uid);
678         if (info == NULL)
679                 return;
680
681         camel_message_info_set_user_flag (info, name, value);
682         camel_message_info_free (info);
683 }
684
685 static const gchar *
686 folder_get_message_user_tag (CamelFolder *folder,
687                              const gchar *uid,
688                              const gchar *name)
689 {
690         CamelMessageInfo *info;
691         const gchar *ret;
692
693         g_return_val_if_fail (folder->summary != NULL, NULL);
694
695         info = camel_folder_summary_get (folder->summary, uid);
696         if (info == NULL)
697                 return NULL;
698
699         ret = camel_message_info_user_tag (info, name);
700         camel_message_info_free (info);
701
702         return ret;
703 }
704
705 static void
706 folder_set_message_user_tag (CamelFolder *folder,
707                              const gchar *uid,
708                              const gchar *name,
709                              const gchar *value)
710 {
711         CamelMessageInfo *info;
712
713         g_return_if_fail (folder->summary != NULL);
714
715         info = camel_folder_summary_get (folder->summary, uid);
716         if (info == NULL)
717                 return;
718
719         camel_message_info_set_user_tag (info, name, value);
720         camel_message_info_free (info);
721 }
722
723 static GPtrArray *
724 folder_get_uids (CamelFolder *folder)
725 {
726         g_return_val_if_fail (folder->summary != NULL, NULL);
727
728         return camel_folder_summary_get_array (folder->summary);
729 }
730
731 static GPtrArray *
732 folder_get_uncached_uids (CamelFolder *folder,
733                           GPtrArray *uids,
734                           GError **error)
735 {
736         GPtrArray *result;
737         gint i;
738
739         result = g_ptr_array_new ();
740
741         g_ptr_array_set_size (result, uids->len);
742         for (i = 0; i < uids->len; i++)
743                 result->pdata[i] =
744                         (gpointer) camel_pstring_strdup (uids->pdata[i]);
745
746         return result;
747 }
748
749 static void
750 folder_free_uids (CamelFolder *folder,
751                   GPtrArray *array)
752 {
753         camel_folder_summary_free_array (array);
754 }
755
756 static gint
757 folder_cmp_uids (CamelFolder *folder,
758                  const gchar *uid1,
759                  const gchar *uid2)
760 {
761         g_return_val_if_fail (uid1 != NULL, 0);
762         g_return_val_if_fail (uid2 != NULL, 0);
763
764         return strtoul (uid1, NULL, 10) - strtoul (uid2, NULL, 10);
765 }
766
767 static void
768 folder_sort_uids (CamelFolder *folder,
769                   GPtrArray *uids)
770 {
771         g_qsort_with_data (
772                 uids->pdata, uids->len,
773                 sizeof (gpointer), cmp_array_uids, folder);
774 }
775
776 static GPtrArray *
777 folder_get_summary (CamelFolder *folder)
778 {
779         g_return_val_if_fail (folder->summary != NULL, NULL);
780
781         return camel_folder_summary_get_array (folder->summary);
782 }
783
784 static void
785 folder_free_summary (CamelFolder *folder,
786                      GPtrArray *array)
787 {
788         camel_folder_summary_free_array (array);
789 }
790
791 static void
792 folder_search_free (CamelFolder *folder,
793                     GPtrArray *result)
794 {
795         gint i;
796
797         for (i = 0; i < result->len; i++)
798                 camel_pstring_free (g_ptr_array_index (result, i));
799         g_ptr_array_free (result, TRUE);
800 }
801
802 static CamelMessageInfo *
803 folder_get_message_info (CamelFolder *folder,
804                          const gchar *uid)
805 {
806         g_return_val_if_fail (folder->summary != NULL, NULL);
807
808         return camel_folder_summary_get (folder->summary, uid);
809 }
810
811 static void
812 folder_ref_message_info (CamelFolder *folder,
813                          CamelMessageInfo *info)
814 {
815         g_return_if_fail (folder->summary != NULL);
816
817         camel_message_info_ref (info);
818 }
819
820 static void
821 folder_free_message_info (CamelFolder *folder,
822                           CamelMessageInfo *info)
823 {
824         g_return_if_fail (folder->summary != NULL);
825
826         camel_message_info_free (info);
827 }
828
829 static void
830 folder_delete (CamelFolder *folder)
831 {
832         if (folder->summary)
833                 camel_folder_summary_clear (folder->summary, NULL);
834 }
835
836 static void
837 folder_rename (CamelFolder *folder,
838                const gchar *new)
839 {
840         gchar *tmp;
841
842         d (printf ("CamelFolder:rename ('%s')\n", new));
843
844         camel_folder_set_full_name (folder, new);
845
846         tmp = strrchr (new, '/');
847         camel_folder_set_display_name (folder, (tmp != NULL) ? tmp + 1 : new);
848 }
849
850 static void
851 folder_freeze (CamelFolder *folder)
852 {
853         g_return_if_fail (folder->priv->frozen >= 0);
854
855         camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK);
856
857         folder->priv->frozen++;
858         if (folder->summary)
859                 g_object_freeze_notify (G_OBJECT (folder->summary));
860
861         d (printf ("freeze (%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
862         camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
863 }
864
865 static void
866 folder_thaw (CamelFolder *folder)
867 {
868         CamelFolderChangeInfo *info = NULL;
869
870         g_return_if_fail (folder->priv->frozen > 0);
871
872         camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK);
873
874         folder->priv->frozen--;
875         if (folder->summary)
876                 g_object_thaw_notify (G_OBJECT (folder->summary));
877
878         d (printf ("thaw (%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
879
880         if (folder->priv->frozen == 0
881             && camel_folder_change_info_changed (folder->priv->changed_frozen)) {
882                 info = folder->priv->changed_frozen;
883                 folder->priv->changed_frozen = camel_folder_change_info_new ();
884         }
885
886         camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
887
888         if (info) {
889                 camel_folder_changed (folder, info);
890                 camel_folder_change_info_free (info);
891         }
892 }
893
894 static gboolean
895 folder_is_frozen (CamelFolder *folder)
896 {
897         return folder->priv->frozen != 0;
898 }
899
900 static gboolean
901 folder_refresh_info_sync (CamelFolder *folder,
902                           GCancellable *cancellable,
903                           GError **error)
904 {
905         return TRUE;
906 }
907
908 static gboolean
909 folder_transfer_messages_to_sync (CamelFolder *source,
910                                   GPtrArray *uids,
911                                   CamelFolder *dest,
912                                   gboolean delete_originals,
913                                   GPtrArray **transferred_uids,
914                                   GCancellable *cancellable,
915                                   GError **error)
916 {
917         gchar **ret_uid = NULL;
918         gint i;
919         GError *local_error = NULL;
920         GCancellable *local_cancellable = camel_operation_new ();
921         gulong handler_id = 0;
922
923         if (transferred_uids) {
924                 *transferred_uids = g_ptr_array_new ();
925                 g_ptr_array_set_size (*transferred_uids, uids->len);
926         }
927
928         /* to not propagate status messages from sub-functions into UI */
929         if (cancellable)
930                 handler_id = g_signal_connect_swapped (cancellable, "cancelled", G_CALLBACK (g_cancellable_cancel), local_cancellable);
931
932         if (delete_originals)
933                 camel_operation_push_message (
934                         cancellable, _("Moving messages"));
935         else
936                 camel_operation_push_message (
937                         cancellable, _("Copying messages"));
938
939         if (uids->len > 1) {
940                 camel_folder_freeze (dest);
941                 if (delete_originals)
942                         camel_folder_freeze (source);
943         }
944
945         for (i = 0; i < uids->len && local_error == NULL; i++) {
946                 if (transferred_uids)
947                         ret_uid = (gchar **) &((*transferred_uids)->pdata[i]);
948                 folder_transfer_message_to (
949                         source, uids->pdata[i], dest, ret_uid,
950                         delete_originals, local_cancellable, &local_error);
951                 camel_operation_progress (
952                         cancellable, i * 100 / uids->len);
953         }
954
955         if (uids->len > 1) {
956                 camel_folder_thaw (dest);
957                 if (delete_originals)
958                         camel_folder_thaw (source);
959         }
960
961         camel_operation_pop_message (cancellable);
962
963         if (local_error != NULL)
964                 g_propagate_error (error, local_error);
965         g_object_unref (local_cancellable);
966         if (cancellable)
967                 g_signal_handler_disconnect (cancellable, handler_id);
968
969         return TRUE;
970 }
971
972 static void
973 folder_append_message_thread (GSimpleAsyncResult *simple,
974                               GObject *object,
975                               GCancellable *cancellable)
976 {
977         AsyncContext *async_context;
978         GError *error = NULL;
979
980         async_context = g_simple_async_result_get_op_res_gpointer (simple);
981
982         camel_folder_append_message_sync (
983                 CAMEL_FOLDER (object), async_context->message,
984                 async_context->info, &async_context->message_uid,
985                 cancellable, &error);
986
987         if (error != NULL)
988                 g_simple_async_result_take_error (simple, error);
989 }
990
991 static void
992 folder_append_message (CamelFolder *folder,
993                        CamelMimeMessage *message,
994                        CamelMessageInfo *info,
995                        gint io_priority,
996                        GCancellable *cancellable,
997                        GAsyncReadyCallback callback,
998                        gpointer user_data)
999 {
1000         GSimpleAsyncResult *simple;
1001         AsyncContext *async_context;
1002
1003         async_context = g_slice_new0 (AsyncContext);
1004         async_context->message = g_object_ref (message);
1005         async_context->info = camel_message_info_ref (info);
1006
1007         simple = g_simple_async_result_new (
1008                 G_OBJECT (folder), callback,
1009                 user_data, folder_append_message);
1010
1011         g_simple_async_result_set_check_cancellable (simple, cancellable);
1012
1013         g_simple_async_result_set_op_res_gpointer (
1014                 simple, async_context, (GDestroyNotify) async_context_free);
1015
1016         g_simple_async_result_run_in_thread (
1017                 simple, folder_append_message_thread,
1018                 io_priority, cancellable);
1019
1020         g_object_unref (simple);
1021 }
1022
1023 static gboolean
1024 folder_append_message_finish (CamelFolder *folder,
1025                               GAsyncResult *result,
1026                               gchar **appended_uid,
1027                               GError **error)
1028 {
1029         GSimpleAsyncResult *simple;
1030         AsyncContext *async_context;
1031
1032         g_return_val_if_fail (
1033                 g_simple_async_result_is_valid (
1034                 result, G_OBJECT (folder), folder_append_message), FALSE);
1035
1036         simple = G_SIMPLE_ASYNC_RESULT (result);
1037         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1038
1039         if (appended_uid != NULL) {
1040                 *appended_uid = async_context->message_uid;
1041                 async_context->message_uid = NULL;
1042         }
1043
1044         /* Assume success unless a GError is set. */
1045         return !g_simple_async_result_propagate_error (simple, error);
1046 }
1047
1048 static void
1049 folder_expunge_thread (GSimpleAsyncResult *simple,
1050                        GObject *object,
1051                        GCancellable *cancellable)
1052 {
1053         GError *error = NULL;
1054
1055         camel_folder_expunge_sync (
1056                 CAMEL_FOLDER (object), cancellable, &error);
1057
1058         if (error != NULL)
1059                 g_simple_async_result_take_error (simple, error);
1060 }
1061
1062 static void
1063 folder_expunge (CamelFolder *folder,
1064                 gint io_priority,
1065                 GCancellable *cancellable,
1066                 GAsyncReadyCallback callback,
1067                 gpointer user_data)
1068 {
1069         GSimpleAsyncResult *simple;
1070
1071         simple = g_simple_async_result_new (
1072                 G_OBJECT (folder), callback, user_data, folder_expunge);
1073
1074         g_simple_async_result_set_check_cancellable (simple, cancellable);
1075
1076         g_simple_async_result_run_in_thread (
1077                 simple, folder_expunge_thread, io_priority, cancellable);
1078
1079         g_object_unref (simple);
1080 }
1081
1082 static gboolean
1083 folder_expunge_finish (CamelFolder *folder,
1084                        GAsyncResult *result,
1085                        GError **error)
1086 {
1087         GSimpleAsyncResult *simple;
1088
1089         g_return_val_if_fail (
1090                 g_simple_async_result_is_valid (
1091                 result, G_OBJECT (folder), folder_expunge), FALSE);
1092
1093         simple = G_SIMPLE_ASYNC_RESULT (result);
1094
1095         /* Assume success unless a GError is set. */
1096         return !g_simple_async_result_propagate_error (simple, error);
1097 }
1098
1099 static void
1100 fetch_messages_thread (GSimpleAsyncResult *simple,
1101                        GObject *object,
1102                        GCancellable *cancellable)
1103 {
1104         AsyncContext *async_context;
1105         GError *error = NULL;
1106
1107         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1108
1109         async_context->success = camel_folder_fetch_messages_sync (
1110                 CAMEL_FOLDER (object), async_context->fetch_type, async_context->limit, cancellable, &error);
1111
1112         if (error != NULL)
1113                 g_simple_async_result_take_error (simple, error);
1114 }
1115
1116 static void
1117 fetch_messages (CamelFolder *folder,
1118                 CamelFetchType type,
1119                 gint limit,
1120                 gint io_priority,
1121                 GCancellable *cancellable,
1122                 GAsyncReadyCallback callback,
1123                 gpointer user_data)
1124 {
1125         GSimpleAsyncResult *simple;
1126         AsyncContext *async_context;
1127
1128         async_context = g_slice_new0 (AsyncContext);
1129         async_context->fetch_type = type;
1130         async_context->limit = limit;
1131
1132         simple = g_simple_async_result_new (
1133                 G_OBJECT (folder), callback, user_data, fetch_messages);
1134
1135         g_simple_async_result_set_check_cancellable (simple, cancellable);
1136
1137         g_simple_async_result_set_op_res_gpointer (
1138                 simple, async_context, (GDestroyNotify) async_context_free);
1139
1140         g_simple_async_result_run_in_thread (
1141                 simple, fetch_messages_thread, io_priority, cancellable);
1142
1143         g_object_unref (simple);
1144 }
1145
1146 static gboolean
1147 fetch_messages_finish (CamelFolder *folder,
1148                        GAsyncResult *result,
1149                        GError **error)
1150 {
1151         GSimpleAsyncResult *simple;
1152         AsyncContext *async_context;
1153
1154         g_return_val_if_fail (
1155                 g_simple_async_result_is_valid (
1156                 result, G_OBJECT (folder), fetch_messages), FALSE);
1157
1158         simple = G_SIMPLE_ASYNC_RESULT (result);
1159         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1160
1161         /* Assume success unless a GError is set. */
1162         return !g_simple_async_result_propagate_error (simple, error)
1163                 && async_context->success;
1164 }
1165
1166 static void
1167 folder_get_message_thread (GSimpleAsyncResult *simple,
1168                            GObject *object,
1169                            GCancellable *cancellable)
1170 {
1171         AsyncContext *async_context;
1172         GError *error = NULL;
1173
1174         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1175
1176         async_context->message = camel_folder_get_message_sync (
1177                 CAMEL_FOLDER (object), async_context->message_uid,
1178                 cancellable, &error);
1179
1180         if (error != NULL)
1181                 g_simple_async_result_take_error (simple, error);
1182 }
1183
1184 static void
1185 folder_get_message (CamelFolder *folder,
1186                     const gchar *message_uid,
1187                     gint io_priority,
1188                     GCancellable *cancellable,
1189                     GAsyncReadyCallback callback,
1190                     gpointer user_data)
1191 {
1192         GSimpleAsyncResult *simple;
1193         AsyncContext *async_context;
1194
1195         async_context = g_slice_new0 (AsyncContext);
1196         async_context->message_uid = g_strdup (message_uid);
1197
1198         simple = g_simple_async_result_new (
1199                 G_OBJECT (folder), callback, user_data, folder_get_message);
1200
1201         g_simple_async_result_set_check_cancellable (simple, cancellable);
1202
1203         g_simple_async_result_set_op_res_gpointer (
1204                 simple, async_context, (GDestroyNotify) async_context_free);
1205
1206         g_simple_async_result_run_in_thread (
1207                 simple, folder_get_message_thread, io_priority, cancellable);
1208
1209         g_object_unref (simple);
1210 }
1211
1212 static CamelMimeMessage *
1213 folder_get_message_finish (CamelFolder *folder,
1214                            GAsyncResult *result,
1215                            GError **error)
1216 {
1217         GSimpleAsyncResult *simple;
1218         AsyncContext *async_context;
1219
1220         g_return_val_if_fail (
1221                 g_simple_async_result_is_valid (
1222                 result, G_OBJECT (folder), folder_get_message), NULL);
1223
1224         simple = G_SIMPLE_ASYNC_RESULT (result);
1225         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1226
1227         if (g_simple_async_result_propagate_error (simple, error))
1228                 return NULL;
1229
1230         return g_object_ref (async_context->message);
1231 }
1232
1233 static void
1234 folder_get_quota_info_thread (GSimpleAsyncResult *simple,
1235                               GObject *object,
1236                               GCancellable *cancellable)
1237 {
1238         AsyncContext *async_context;
1239         GError *error = NULL;
1240
1241         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1242
1243         async_context->quota_info = camel_folder_get_quota_info_sync (
1244                 CAMEL_FOLDER (object), cancellable, &error);
1245
1246         if (error != NULL)
1247                 g_simple_async_result_take_error (simple, error);
1248 }
1249
1250 static CamelFolderQuotaInfo *
1251 folder_get_quota_info_sync (CamelFolder *folder,
1252                             GCancellable *cancellable,
1253                             GError **error)
1254 {
1255         g_set_error (
1256                 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1257                 _("Quota information not supported for folder '%s'"),
1258                 camel_folder_get_display_name (folder));
1259
1260         return NULL;
1261 }
1262
1263 static void
1264 folder_get_quota_info (CamelFolder *folder,
1265                        gint io_priority,
1266                        GCancellable *cancellable,
1267                        GAsyncReadyCallback callback,
1268                        gpointer user_data)
1269 {
1270         GSimpleAsyncResult *simple;
1271         AsyncContext *async_context;
1272
1273         async_context = g_slice_new0 (AsyncContext);
1274
1275         simple = g_simple_async_result_new (
1276                 G_OBJECT (folder), callback,
1277                 user_data, folder_get_quota_info);
1278
1279         g_simple_async_result_set_check_cancellable (simple, cancellable);
1280
1281         g_simple_async_result_set_op_res_gpointer (
1282                 simple, async_context, (GDestroyNotify) async_context_free);
1283
1284         g_simple_async_result_run_in_thread (
1285                 simple, folder_get_quota_info_thread,
1286                 io_priority, cancellable);
1287
1288         g_object_unref (simple);
1289 }
1290
1291 static CamelFolderQuotaInfo *
1292 folder_get_quota_info_finish (CamelFolder *folder,
1293                               GAsyncResult *result,
1294                               GError **error)
1295 {
1296         GSimpleAsyncResult *simple;
1297         AsyncContext *async_context;
1298         CamelFolderQuotaInfo *quota_info;
1299
1300         g_return_val_if_fail (
1301                 g_simple_async_result_is_valid (
1302                 result, G_OBJECT (folder), folder_get_quota_info), NULL);
1303
1304         simple = G_SIMPLE_ASYNC_RESULT (result);
1305         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1306
1307         if (g_simple_async_result_propagate_error (simple, error))
1308                 return NULL;
1309
1310         quota_info = async_context->quota_info;
1311         async_context->quota_info = NULL;
1312
1313         return quota_info;
1314 }
1315
1316 static void
1317 purge_message_cache_thread (GSimpleAsyncResult *simple,
1318                        GObject *object,
1319                        GCancellable *cancellable)
1320 {
1321         AsyncContext *async_context;
1322         GError *error = NULL;
1323
1324         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1325
1326         async_context->success = camel_folder_purge_message_cache_sync (
1327                 CAMEL_FOLDER (object), async_context->start_uid, async_context->end_uid, cancellable, &error);
1328
1329         if (error != NULL)
1330                 g_simple_async_result_take_error (simple, error);
1331 }
1332
1333 static void
1334 purge_message_cache (CamelFolder *folder,
1335                      gchar *start_uid,
1336                      gchar *end_uid,
1337                      gint io_priority,
1338                      GCancellable *cancellable,
1339                      GAsyncReadyCallback callback,
1340                      gpointer user_data)
1341 {
1342         GSimpleAsyncResult *simple;
1343         AsyncContext *async_context;
1344
1345         async_context = g_slice_new0 (AsyncContext);
1346         async_context->start_uid = g_strdup (start_uid);
1347         async_context->end_uid = g_strdup (end_uid);
1348
1349         simple = g_simple_async_result_new (
1350                 G_OBJECT (folder), callback, user_data, purge_message_cache);
1351
1352         g_simple_async_result_set_check_cancellable (simple, cancellable);
1353
1354         g_simple_async_result_set_op_res_gpointer (
1355                 simple, async_context, (GDestroyNotify) async_context_free);
1356
1357         g_simple_async_result_run_in_thread (
1358                 simple, purge_message_cache_thread, io_priority, cancellable);
1359
1360         g_object_unref (simple);
1361 }
1362
1363 static gboolean
1364 purge_message_cache_finish (CamelFolder *folder,
1365                             GAsyncResult *result,
1366                             GError **error)
1367 {
1368         GSimpleAsyncResult *simple;
1369         AsyncContext *async_context;
1370
1371         g_return_val_if_fail (
1372                 g_simple_async_result_is_valid (
1373                 result, G_OBJECT (folder), purge_message_cache), FALSE);
1374
1375         simple = G_SIMPLE_ASYNC_RESULT (result);
1376         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1377
1378         /* Assume success unless a GError is set. */
1379         return !g_simple_async_result_propagate_error (simple, error)
1380                 && async_context->success;
1381 }
1382
1383 static void
1384 folder_refresh_info_thread (GSimpleAsyncResult *simple,
1385                             GObject *object,
1386                             GCancellable *cancellable)
1387 {
1388         GError *error = NULL;
1389
1390         camel_folder_refresh_info_sync (
1391                 CAMEL_FOLDER (object), cancellable, &error);
1392
1393         if (error != NULL)
1394                 g_simple_async_result_take_error (simple, error);
1395 }
1396
1397 static void
1398 folder_refresh_info (CamelFolder *folder,
1399                      gint io_priority,
1400                      GCancellable *cancellable,
1401                      GAsyncReadyCallback callback,
1402                      gpointer user_data)
1403 {
1404         GSimpleAsyncResult *simple;
1405
1406         simple = g_simple_async_result_new (
1407                 G_OBJECT (folder), callback, user_data, folder_refresh_info);
1408
1409         g_simple_async_result_set_check_cancellable (simple, cancellable);
1410
1411         g_simple_async_result_run_in_thread (
1412                 simple, folder_refresh_info_thread, io_priority, cancellable);
1413
1414         g_object_unref (simple);
1415 }
1416
1417 static gboolean
1418 folder_refresh_info_finish (CamelFolder *folder,
1419                             GAsyncResult *result,
1420                             GError **error)
1421 {
1422         GSimpleAsyncResult *simple;
1423
1424         g_return_val_if_fail (
1425                 g_simple_async_result_is_valid (
1426                 result, G_OBJECT (folder), folder_refresh_info), FALSE);
1427
1428         simple = G_SIMPLE_ASYNC_RESULT (result);
1429
1430         /* Assume success unless a GError is set. */
1431         return !g_simple_async_result_propagate_error (simple, error);
1432 }
1433
1434 static void
1435 folder_synchronize_thread (GSimpleAsyncResult *simple,
1436                            GObject *object,
1437                            GCancellable *cancellable)
1438 {
1439         AsyncContext *async_context;
1440         GError *error = NULL;
1441
1442         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1443
1444         camel_folder_synchronize_sync (
1445                 CAMEL_FOLDER (object), async_context->expunge,
1446                 cancellable, &error);
1447
1448         if (error != NULL)
1449                 g_simple_async_result_take_error (simple, error);
1450 }
1451
1452 static void
1453 folder_synchronize (CamelFolder *folder,
1454                     gboolean expunge,
1455                     gint io_priority,
1456                     GCancellable *cancellable,
1457                     GAsyncReadyCallback callback,
1458                     gpointer user_data)
1459 {
1460         GSimpleAsyncResult *simple;
1461         AsyncContext *async_context;
1462
1463         async_context = g_slice_new0 (AsyncContext);
1464         async_context->expunge = expunge;
1465
1466         simple = g_simple_async_result_new (
1467                 G_OBJECT (folder), callback, user_data, folder_synchronize);
1468
1469         g_simple_async_result_set_check_cancellable (simple, cancellable);
1470
1471         g_simple_async_result_set_op_res_gpointer (
1472                 simple, async_context, (GDestroyNotify) async_context_free);
1473
1474         g_simple_async_result_run_in_thread (
1475                 simple, folder_synchronize_thread, io_priority, cancellable);
1476
1477         g_object_unref (simple);
1478 }
1479
1480 static gboolean
1481 folder_synchronize_finish (CamelFolder *folder,
1482                            GAsyncResult *result,
1483                            GError **error)
1484 {
1485         GSimpleAsyncResult *simple;
1486
1487         g_return_val_if_fail (
1488                 g_simple_async_result_is_valid (
1489                 result, G_OBJECT (folder), folder_synchronize), FALSE);
1490
1491         simple = G_SIMPLE_ASYNC_RESULT (result);
1492
1493         /* Assume success unless a GError is set. */
1494         return !g_simple_async_result_propagate_error (simple, error);
1495 }
1496
1497 static void
1498 folder_synchronize_message_thread (GSimpleAsyncResult *simple,
1499                                    GObject *object,
1500                                    GCancellable *cancellable)
1501 {
1502         AsyncContext *async_context;
1503         GError *error = NULL;
1504
1505         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1506
1507         camel_folder_synchronize_message_sync (
1508                 CAMEL_FOLDER (object), async_context->message_uid,
1509                 cancellable, &error);
1510
1511         if (error != NULL)
1512                 g_simple_async_result_take_error (simple, error);
1513 }
1514
1515 static void
1516 folder_synchronize_message (CamelFolder *folder,
1517                             const gchar *message_uid,
1518                             gint io_priority,
1519                             GCancellable *cancellable,
1520                             GAsyncReadyCallback callback,
1521                             gpointer user_data)
1522 {
1523         GSimpleAsyncResult *simple;
1524         AsyncContext *async_context;
1525
1526         async_context = g_slice_new0 (AsyncContext);
1527         async_context->message_uid = g_strdup (message_uid);
1528
1529         simple = g_simple_async_result_new (
1530                 G_OBJECT (folder), callback,
1531                 user_data, folder_synchronize_message);
1532
1533         g_simple_async_result_set_check_cancellable (simple, cancellable);
1534
1535         g_simple_async_result_set_op_res_gpointer (
1536                 simple, async_context, (GDestroyNotify) async_context_free);
1537
1538         g_simple_async_result_run_in_thread (
1539                 simple, folder_synchronize_message_thread,
1540                 io_priority, cancellable);
1541
1542         g_object_unref (simple);
1543 }
1544
1545 static gboolean
1546 folder_synchronize_message_finish (CamelFolder *folder,
1547                                    GAsyncResult *result,
1548                                    GError **error)
1549 {
1550         GSimpleAsyncResult *simple;
1551
1552         g_return_val_if_fail (
1553                 g_simple_async_result_is_valid (
1554                 result, G_OBJECT (folder),
1555                 folder_synchronize_message), FALSE);
1556
1557         simple = G_SIMPLE_ASYNC_RESULT (result);
1558
1559         /* Assume success unless a GError is set. */
1560         return !g_simple_async_result_propagate_error (simple, error);
1561 }
1562
1563 static void
1564 folder_transfer_messages_to_thread (GSimpleAsyncResult *simple,
1565                                     GObject *object,
1566                                     GCancellable *cancellable)
1567 {
1568         AsyncContext *async_context;
1569         GError *error = NULL;
1570
1571         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1572
1573         camel_folder_transfer_messages_to_sync (
1574                 CAMEL_FOLDER (object), async_context->message_uids,
1575                 async_context->destination, async_context->delete_originals,
1576                 &async_context->transferred_uids, cancellable, &error);
1577
1578         if (error != NULL)
1579                 g_simple_async_result_take_error (simple, error);
1580 }
1581
1582 static void
1583 folder_transfer_messages_to (CamelFolder *source,
1584                              GPtrArray *message_uids,
1585                              CamelFolder *destination,
1586                              gboolean delete_originals,
1587                              gint io_priority,
1588                              GCancellable *cancellable,
1589                              GAsyncReadyCallback callback,
1590                              gpointer user_data)
1591 {
1592         GSimpleAsyncResult *simple;
1593         AsyncContext *async_context;
1594         guint ii;
1595
1596         async_context = g_slice_new0 (AsyncContext);
1597         async_context->message_uids = g_ptr_array_new ();
1598         async_context->destination = g_object_ref (destination);
1599         async_context->delete_originals = delete_originals;
1600
1601         for (ii = 0; ii < message_uids->len; ii++)
1602                 g_ptr_array_add (
1603                         async_context->message_uids,
1604                         g_strdup (message_uids->pdata[ii]));
1605
1606         simple = g_simple_async_result_new (
1607                 G_OBJECT (source), callback,
1608                 user_data, folder_transfer_messages_to);
1609
1610         g_simple_async_result_set_check_cancellable (simple, cancellable);
1611
1612         g_simple_async_result_set_op_res_gpointer (
1613                 simple, async_context, (GDestroyNotify) async_context_free);
1614
1615         g_simple_async_result_run_in_thread (
1616                 simple, folder_transfer_messages_to_thread,
1617                 io_priority, cancellable);
1618
1619         g_object_unref (simple);
1620 }
1621
1622 static gboolean
1623 folder_transfer_messages_to_finish (CamelFolder *source,
1624                                     GAsyncResult *result,
1625                                     GPtrArray **transferred_uids,
1626                                     GError **error)
1627 {
1628         GSimpleAsyncResult *simple;
1629         AsyncContext *async_context;
1630
1631         g_return_val_if_fail (
1632                 g_simple_async_result_is_valid (
1633                 result, G_OBJECT (source),
1634                 folder_transfer_messages_to), FALSE);
1635
1636         simple = G_SIMPLE_ASYNC_RESULT (result);
1637         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1638
1639         if (transferred_uids != NULL) {
1640                 *transferred_uids = async_context->transferred_uids;
1641                 async_context->transferred_uids = NULL;
1642         }
1643
1644         /* Assume success unless a GError is set. */
1645         return !g_simple_async_result_propagate_error (simple, error);
1646 }
1647
1648 /* Signal callback that stops emission when folder is frozen. */
1649 static void
1650 folder_changed (CamelFolder *folder,
1651                 CamelFolderChangeInfo *info)
1652 {
1653         CamelStore *parent_store;
1654         struct _CamelFolderChangeInfoPrivate *p = info->priv;
1655         CamelSession *session;
1656         CamelFilterDriver *driver = NULL;
1657         CamelJunkFilter *junk_filter;
1658         GPtrArray *junk = NULL;
1659         GPtrArray *notjunk = NULL;
1660         GPtrArray *recents = NULL;
1661         gint i;
1662
1663         g_return_if_fail (info != NULL);
1664
1665         parent_store = camel_folder_get_parent_store (folder);
1666         session = camel_service_get_session (CAMEL_SERVICE (parent_store));
1667         junk_filter = camel_session_get_junk_filter (session);
1668
1669         camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK);
1670         if (folder->priv->frozen) {
1671                 camel_folder_change_info_cat (folder->priv->changed_frozen, info);
1672                 camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
1673                 g_signal_stop_emission (folder, signals[CHANGED], 0);
1674                 return;
1675         }
1676         camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
1677
1678         if (junk_filter != NULL && info->uid_changed->len) {
1679                 CamelMessageFlags flags;
1680
1681                 for (i = 0; i < info->uid_changed->len; i++) {
1682                         flags = camel_folder_get_message_flags (folder, info->uid_changed->pdata[i]);
1683                         if (flags & CAMEL_MESSAGE_JUNK_LEARN) {
1684                                 if (flags & CAMEL_MESSAGE_JUNK) {
1685                                         if (!junk)
1686                                                 junk = g_ptr_array_new ();
1687                                         g_ptr_array_add (junk, g_strdup (info->uid_changed->pdata[i]));
1688                                 } else {
1689                                         if (!notjunk)
1690                                                 notjunk = g_ptr_array_new ();
1691                                         g_ptr_array_add (notjunk, g_strdup (info->uid_changed->pdata[i]));
1692                                 }
1693                                 /* reset junk learn flag so that we don't process it again*/
1694                                 camel_folder_set_message_flags (
1695                                         folder, info->uid_changed->pdata[i],
1696                                         CAMEL_MESSAGE_JUNK_LEARN, 0);
1697                         }
1698                 }
1699         }
1700
1701         if ((folder->folder_flags & (CAMEL_FOLDER_FILTER_RECENT | CAMEL_FOLDER_FILTER_JUNK))
1702             && p->uid_filter->len > 0)
1703                 driver = camel_session_get_filter_driver (
1704                         session,
1705                         (folder->folder_flags & CAMEL_FOLDER_FILTER_RECENT)
1706                         ? "incoming" : "junktest", NULL);
1707
1708         if (driver) {
1709                 recents = g_ptr_array_new ();
1710                 for (i = 0; i < p->uid_filter->len; i++)
1711                         g_ptr_array_add (recents, g_strdup (p->uid_filter->pdata[i]));
1712
1713                 g_ptr_array_set_size (p->uid_filter, 0);
1714         }
1715
1716         if (driver || junk || notjunk) {
1717                 FolderFilterData *data;
1718
1719                 data = g_slice_new0 (FolderFilterData);
1720                 data->recents = recents;
1721                 data->junk = junk;
1722                 data->notjunk = notjunk;
1723                 data->folder = g_object_ref (folder);
1724                 data->driver = driver;
1725
1726                 camel_folder_freeze (folder);
1727
1728                 /* Copy changes back to changed_frozen list to retain
1729                  * them while we are filtering */
1730                 camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK);
1731                 camel_folder_change_info_cat (
1732                         folder->priv->changed_frozen, info);
1733                 camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
1734
1735                 camel_session_submit_job (
1736                         session, (CamelSessionCallback) folder_filter,
1737                         data, (GDestroyNotify) folder_filter_data_free);
1738
1739                 g_signal_stop_emission (folder, signals[CHANGED], 0);
1740         }
1741 }
1742
1743 static void
1744 camel_folder_class_init (CamelFolderClass *class)
1745 {
1746         GObjectClass *object_class;
1747
1748         g_type_class_add_private (class, sizeof (CamelFolderPrivate));
1749
1750         object_class = G_OBJECT_CLASS (class);
1751         object_class->set_property = folder_set_property;
1752         object_class->get_property = folder_get_property;
1753         object_class->dispose = folder_dispose;
1754         object_class->finalize = folder_finalize;
1755
1756         class->get_message_count = folder_get_message_count;
1757         class->get_permanent_flags = folder_get_permanent_flags;
1758         class->get_message_flags = folder_get_message_flags;
1759         class->set_message_flags = folder_set_message_flags;
1760         class->get_message_user_flag = folder_get_message_user_flag;
1761         class->set_message_user_flag = folder_set_message_user_flag;
1762         class->get_message_user_tag = folder_get_message_user_tag;
1763         class->set_message_user_tag = folder_set_message_user_tag;
1764         class->get_uids = folder_get_uids;
1765         class->get_uncached_uids = folder_get_uncached_uids;
1766         class->free_uids = folder_free_uids;
1767         class->cmp_uids = folder_cmp_uids;
1768         class->sort_uids = folder_sort_uids;
1769         class->get_summary = folder_get_summary;
1770         class->free_summary = folder_free_summary;
1771         class->search_free = folder_search_free;
1772         class->get_message_info = folder_get_message_info;
1773         class->ref_message_info = folder_ref_message_info;
1774         class->free_message_info = folder_free_message_info;
1775         class->delete_ = folder_delete;
1776         class->rename = folder_rename;
1777         class->freeze = folder_freeze;
1778         class->thaw = folder_thaw;
1779         class->is_frozen = folder_is_frozen;
1780         class->get_quota_info_sync = folder_get_quota_info_sync;
1781         class->refresh_info_sync = folder_refresh_info_sync;
1782         class->transfer_messages_to_sync = folder_transfer_messages_to_sync;
1783         class->changed = folder_changed;
1784
1785         class->append_message = folder_append_message;
1786         class->append_message_finish = folder_append_message_finish;
1787         class->expunge = folder_expunge;
1788         class->expunge_finish = folder_expunge_finish;
1789         class->fetch_messages= fetch_messages;
1790         class->fetch_messages_finish = fetch_messages_finish;
1791         class->get_message = folder_get_message;
1792         class->get_message_finish = folder_get_message_finish;
1793         class->get_quota_info = folder_get_quota_info;
1794         class->get_quota_info_finish = folder_get_quota_info_finish;
1795         class->purge_message_cache= purge_message_cache;
1796         class->purge_message_cache_finish = purge_message_cache_finish;
1797         class->refresh_info = folder_refresh_info;
1798         class->refresh_info_finish = folder_refresh_info_finish;
1799         class->synchronize = folder_synchronize;
1800         class->synchronize_finish = folder_synchronize_finish;
1801         class->synchronize_message = folder_synchronize_message;
1802         class->synchronize_message_finish = folder_synchronize_message_finish;
1803         class->transfer_messages_to = folder_transfer_messages_to;
1804         class->transfer_messages_to_finish = folder_transfer_messages_to_finish;
1805
1806         /**
1807          * CamelFolder:description
1808          *
1809          * The folder's description.
1810          **/
1811         g_object_class_install_property (
1812                 object_class,
1813                 PROP_DESCRIPTION,
1814                 g_param_spec_string (
1815                         "description",
1816                         "Description",
1817                         "The folder's description",
1818                         NULL,
1819                         G_PARAM_READWRITE |
1820                         G_PARAM_CONSTRUCT));
1821
1822         /**
1823          * CamelFolder:display-name
1824          *
1825          * The folder's display name.
1826          **/
1827         g_object_class_install_property (
1828                 object_class,
1829                 PROP_DISPLAY_NAME,
1830                 g_param_spec_string (
1831                         "display-name",
1832                         "Display Name",
1833                         "The folder's display name",
1834                         NULL,
1835                         G_PARAM_READWRITE |
1836                         G_PARAM_CONSTRUCT));
1837
1838         /**
1839          * CamelFolder:full-name
1840          *
1841          * The folder's fully qualified name.
1842          **/
1843         g_object_class_install_property (
1844                 object_class,
1845                 PROP_FULL_NAME,
1846                 g_param_spec_string (
1847                         "full-name",
1848                         "Full Name",
1849                         "The folder's fully qualified name",
1850                         NULL,
1851                         G_PARAM_READWRITE |
1852                         G_PARAM_CONSTRUCT));
1853
1854         /**
1855          * CamelFolder:parent-store
1856          *
1857          * The #CamelStore to which the folder belongs.
1858          **/
1859         g_object_class_install_property (
1860                 object_class,
1861                 PROP_PARENT_STORE,
1862                 g_param_spec_object (
1863                         "parent-store",
1864                         "Parent Store",
1865                         "The store to which the folder belongs",
1866                         CAMEL_TYPE_STORE,
1867                         G_PARAM_READWRITE |
1868                         G_PARAM_CONSTRUCT_ONLY));
1869
1870         /**
1871          * CamelFolder::changed
1872          * @folder: the #CamelFolder which emitted the signal
1873          **/
1874         signals[CHANGED] = g_signal_new (
1875                 "changed",
1876                 G_OBJECT_CLASS_TYPE (class),
1877                 G_SIGNAL_RUN_FIRST,
1878                 G_STRUCT_OFFSET (CamelFolderClass, changed),
1879                 NULL, NULL,
1880                 g_cclosure_marshal_VOID__POINTER,
1881                 G_TYPE_NONE, 1,
1882                 G_TYPE_POINTER);
1883
1884         /**
1885          * CamelFolder::deleted
1886          * @folder: the #CamelFolder which emitted the signal
1887          **/
1888         signals[DELETED] = g_signal_new (
1889                 "deleted",
1890                 G_OBJECT_CLASS_TYPE (class),
1891                 G_SIGNAL_RUN_FIRST,
1892                 G_STRUCT_OFFSET (CamelFolderClass, deleted),
1893                 NULL, NULL,
1894                 g_cclosure_marshal_VOID__VOID,
1895                 G_TYPE_NONE, 0);
1896
1897         /**
1898          * CamelFolder::renamed
1899          * @folder: the #CamelFolder which emitted the signal
1900          * @old_name: the previous folder name
1901          **/
1902         signals[RENAMED] = g_signal_new (
1903                 "renamed",
1904                 G_OBJECT_CLASS_TYPE (class),
1905                 G_SIGNAL_RUN_FIRST,
1906                 G_STRUCT_OFFSET (CamelFolderClass, renamed),
1907                 NULL, NULL,
1908                 g_cclosure_marshal_VOID__STRING,
1909                 G_TYPE_NONE, 1,
1910                 G_TYPE_STRING);
1911 }
1912
1913 static void
1914 camel_folder_init (CamelFolder *folder)
1915 {
1916         folder->priv = CAMEL_FOLDER_GET_PRIVATE (folder);
1917         folder->priv->frozen = 0;
1918         folder->priv->changed_frozen = camel_folder_change_info_new ();
1919
1920         g_rec_mutex_init (&folder->priv->lock);
1921         g_mutex_init (&folder->priv->change_lock);
1922         g_mutex_init (&folder->priv->property_lock);
1923 }
1924
1925 G_DEFINE_QUARK (camel-folder-error-quark, camel_folder_error)
1926
1927 /**
1928  * camel_folder_set_lock_async:
1929  * @folder: a #CamelFolder
1930  * @skip_folder_lock:
1931  *
1932  * FIXME Document me!
1933  *
1934  * Since: 2.30
1935  **/
1936 void
1937 camel_folder_set_lock_async (CamelFolder *folder,
1938                              gboolean skip_folder_lock)
1939 {
1940         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1941
1942         folder->priv->skip_folder_lock = skip_folder_lock;
1943 }
1944
1945 /**
1946  * camel_folder_get_filename:
1947  *
1948  * Since: 2.26
1949  **/
1950 gchar *
1951 camel_folder_get_filename (CamelFolder *folder,
1952                            const gchar *uid,
1953                            GError **error)
1954 {
1955         CamelFolderClass *class;
1956         gchar *filename;
1957
1958         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1959         g_return_val_if_fail (uid != NULL, NULL);
1960
1961         class = CAMEL_FOLDER_GET_CLASS (folder);
1962         g_return_val_if_fail (class->get_filename != NULL, NULL);
1963
1964         filename = class->get_filename (folder, uid, error);
1965         CAMEL_CHECK_GERROR (folder, get_filename, filename != NULL, error);
1966
1967         return filename;
1968 }
1969
1970 /**
1971  * camel_folder_get_full_name:
1972  * @folder: a #CamelFolder
1973  *
1974  * Returns the fully qualified name of the folder.
1975  *
1976  * Returns: the fully qualified name of the folder
1977  **/
1978 const gchar *
1979 camel_folder_get_full_name (CamelFolder *folder)
1980 {
1981         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1982
1983         return folder->priv->full_name;
1984 }
1985
1986 /**
1987  * camel_folder_dup_full_name:
1988  * @folder: a #CamelFolder
1989  *
1990  * Thread-safe variation of camel_folder_get_full_name().
1991  * Use this function when accessing @folder from multiple threads.
1992  *
1993  * The returned string should be freed with g_free() when no longer needed.
1994  *
1995  * Returns: a newly-allocated copy of #CamelFolder:full-name
1996  *
1997  * Since: 3.8
1998  **/
1999 gchar *
2000 camel_folder_dup_full_name (CamelFolder *folder)
2001 {
2002         const gchar *protected;
2003         gchar *duplicate;
2004
2005         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2006
2007         g_mutex_lock (&folder->priv->property_lock);
2008
2009         protected = camel_folder_get_full_name (folder);
2010         duplicate = g_strdup (protected);
2011
2012         g_mutex_unlock (&folder->priv->property_lock);
2013
2014         return duplicate;
2015 }
2016
2017 /**
2018  * camel_folder_set_full_name:
2019  * @folder: a #CamelFolder
2020  * @full_name: a fully qualified name for the folder
2021  *
2022  * Sets the fully qualified name of the folder.
2023  *
2024  * Since: 2.32
2025  **/
2026 void
2027 camel_folder_set_full_name (CamelFolder *folder,
2028                             const gchar *full_name)
2029 {
2030         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2031
2032         g_mutex_lock (&folder->priv->property_lock);
2033
2034         if (g_strcmp0 (folder->priv->full_name, full_name) == 0) {
2035                 g_mutex_unlock (&folder->priv->property_lock);
2036                 return;
2037         }
2038
2039         g_free (folder->priv->full_name);
2040         folder->priv->full_name = g_strdup (full_name);
2041
2042         g_mutex_unlock (&folder->priv->property_lock);
2043
2044         g_object_notify (G_OBJECT (folder), "full-name");
2045 }
2046
2047 /**
2048  * camel_folder_get_display_name:
2049  * @folder: a #CamelFolder
2050  *
2051  * Returns the display name for the folder.  The fully qualified name
2052  * can be obtained with camel_folder_get_full_name().
2053  *
2054  * Returns: the display name of the folder
2055  *
2056  * Since: 3.2
2057  **/
2058 const gchar *
2059 camel_folder_get_display_name (CamelFolder *folder)
2060 {
2061         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2062
2063         return folder->priv->display_name;
2064 }
2065
2066 /**
2067  * camel_folder_dup_display_name:
2068  * @folder: a #CamelFolder
2069  *
2070  * Thread-safe variation of camel_folder_get_display_name().
2071  * Use this function when accessing @folder from multiple threads.
2072  *
2073  * The returned string should be freed with g_free() when no longer needed.
2074  *
2075  * Returns: a newly-allocated copy of #CamelFolder:display-name
2076  *
2077  * Since: 3.8
2078  **/
2079 gchar *
2080 camel_folder_dup_display_name (CamelFolder *folder)
2081 {
2082         const gchar *protected;
2083         gchar *duplicate;
2084
2085         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2086
2087         g_mutex_lock (&folder->priv->property_lock);
2088
2089         protected = camel_folder_get_display_name (folder);
2090         duplicate = g_strdup (protected);
2091
2092         g_mutex_unlock (&folder->priv->property_lock);
2093
2094         return duplicate;
2095 }
2096
2097 /**
2098  * camel_folder_set_display_name:
2099  * @folder: a #CamelFolder
2100  * @display_name: a display name for the folder
2101  *
2102  * Sets the display name for the folder.
2103  *
2104  * Since: 3.2
2105  **/
2106 void
2107 camel_folder_set_display_name (CamelFolder *folder,
2108                                const gchar *display_name)
2109 {
2110         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2111
2112         g_mutex_lock (&folder->priv->property_lock);
2113
2114         if (g_strcmp0 (folder->priv->display_name, display_name) == 0) {
2115                 g_mutex_unlock (&folder->priv->property_lock);
2116                 return;
2117         }
2118
2119         g_free (folder->priv->display_name);
2120         folder->priv->display_name = g_strdup (display_name);
2121
2122         g_mutex_unlock (&folder->priv->property_lock);
2123
2124         g_object_notify (G_OBJECT (folder), "display-name");
2125 }
2126
2127 /**
2128  * camel_folder_get_description:
2129  * @folder: a #CamelFolder
2130  *
2131  * Returns a description of the folder suitable for displaying to the user.
2132  *
2133  * Returns: a description of the folder
2134  *
2135  * Since: 2.32
2136  **/
2137 const gchar *
2138 camel_folder_get_description (CamelFolder *folder)
2139 {
2140         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2141
2142         /* Default to full-name if there's no custom description. */
2143         if (folder->priv->description == NULL)
2144                 return camel_folder_get_full_name (folder);
2145
2146         return folder->priv->description;
2147 }
2148
2149 /**
2150  * camel_folder_dup_description:
2151  * @folder: a #CamelFolder
2152  *
2153  * Thread-safe variation of camel_folder_get_description().
2154  * Use this function when accessing @folder from multiple threads.
2155  *
2156  * The returned string should be freed with g_free() when no longer needed.
2157  *
2158  * Returns: a newly-allocated copy of #CamelFolder:description
2159  *
2160  * Since: 3.8
2161  **/
2162 gchar *
2163 camel_folder_dup_description (CamelFolder *folder)
2164 {
2165         const gchar *protected;
2166         gchar *duplicate;
2167
2168         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2169
2170         g_mutex_lock (&folder->priv->property_lock);
2171
2172         protected = camel_folder_get_description (folder);
2173         duplicate = g_strdup (protected);
2174
2175         g_mutex_unlock (&folder->priv->property_lock);
2176
2177         return duplicate;
2178 }
2179
2180 /**
2181  * camel_folder_set_description:
2182  * @folder: a #CamelFolder
2183  * @description: a description of the folder
2184  *
2185  * Sets a description of the folder suitable for displaying to the user.
2186  *
2187  * Since: 2.32
2188  **/
2189 void
2190 camel_folder_set_description (CamelFolder *folder,
2191                               const gchar *description)
2192 {
2193         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2194
2195         g_mutex_lock (&folder->priv->property_lock);
2196
2197         if (g_strcmp0 (folder->priv->description, description) == 0) {
2198                 g_mutex_unlock (&folder->priv->property_lock);
2199                 return;
2200         }
2201
2202         g_free (folder->priv->description);
2203         folder->priv->description = g_strdup (description);
2204
2205         g_mutex_unlock (&folder->priv->property_lock);
2206
2207         g_object_notify (G_OBJECT (folder), "description");
2208 }
2209
2210 /**
2211  * camel_folder_get_parent_store:
2212  * @folder: a #CamelFolder
2213  *
2214  * Returns: the parent #CamelStore of the folder
2215  **/
2216 CamelStore *
2217 camel_folder_get_parent_store (CamelFolder *folder)
2218 {
2219         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2220
2221         return CAMEL_STORE (folder->priv->parent_store);
2222 }
2223
2224 /**
2225  * camel_folder_get_message_count:
2226  * @folder: a #CamelFolder
2227  *
2228  * Returns: the number of messages in the folder, or %-1 if unknown
2229  **/
2230 gint
2231 camel_folder_get_message_count (CamelFolder *folder)
2232 {
2233         CamelFolderClass *class;
2234
2235         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
2236
2237         class = CAMEL_FOLDER_GET_CLASS (folder);
2238         g_return_val_if_fail (class->get_message_count != NULL, -1);
2239
2240         return class->get_message_count (folder);
2241 }
2242
2243 /**
2244  * camel_folder_get_unread_message_count:
2245  * @folder: a #CamelFolder
2246  *
2247  * DEPRECATED: use camel_object_get() instead.
2248  *
2249  * Returns: the number of unread messages in the folder, or %-1 if
2250  * unknown
2251  **/
2252 gint
2253 camel_folder_get_unread_message_count (CamelFolder *folder)
2254 {
2255         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
2256         g_return_val_if_fail (folder->summary != NULL, -1);
2257
2258         return camel_folder_summary_get_unread_count (folder->summary);
2259 }
2260
2261 /**
2262  * camel_folder_get_deleted_message_count:
2263  * @folder: a #CamelFolder
2264  *
2265  * Returns: the number of deleted messages in the folder, or %-1 if
2266  * unknown
2267  **/
2268 gint
2269 camel_folder_get_deleted_message_count (CamelFolder *folder)
2270 {
2271         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
2272         g_return_val_if_fail (folder->summary != NULL, -1);
2273
2274         return camel_folder_summary_get_deleted_count (folder->summary);
2275 }
2276
2277 /**
2278  * camel_folder_get_permanent_flags:
2279  * @folder: a #CamelFolder
2280  *
2281  * Returns: the set of #CamelMessageFlags that can be permanently
2282  * stored on a message between sessions. If it includes
2283  * #CAMEL_FLAG_USER, then user-defined flags will be remembered.
2284  **/
2285 CamelMessageFlags
2286 camel_folder_get_permanent_flags (CamelFolder *folder)
2287 {
2288         CamelFolderClass *class;
2289
2290         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
2291
2292         class = CAMEL_FOLDER_GET_CLASS (folder);
2293         g_return_val_if_fail (class->get_permanent_flags != NULL, 0);
2294
2295         return class->get_permanent_flags (folder);
2296 }
2297
2298 /**
2299  * camel_folder_get_message_flags:
2300  * @folder: a #CamelFolder
2301  * @uid: the UID of a message in @folder
2302  *
2303  * Deprecated: Use camel_folder_get_message_info() instead.
2304  *
2305  * Returns: the #CamelMessageFlags that are set on the indicated
2306  * message.
2307  **/
2308 CamelMessageFlags
2309 camel_folder_get_message_flags (CamelFolder *folder,
2310                                 const gchar *uid)
2311 {
2312         CamelFolderClass *class;
2313
2314         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
2315         g_return_val_if_fail (uid != NULL, 0);
2316
2317         class = CAMEL_FOLDER_GET_CLASS (folder);
2318         g_return_val_if_fail (class->get_message_flags != NULL, 0);
2319
2320         return class->get_message_flags (folder, uid);
2321 }
2322
2323 /**
2324  * camel_folder_set_message_flags:
2325  * @folder: a #CamelFolder
2326  * @uid: the UID of a message in @folder
2327  * @flags: a set of #CamelMessageFlag values to set
2328  * @set: the mask of values in @flags to use.
2329  *
2330  * Sets those flags specified by @flags to the values specified by @set
2331  * on the indicated message. (This may or may not persist after the
2332  * folder or store is closed. See camel_folder_get_permanent_flags())
2333  *
2334  * E.g. to set the deleted flag and clear the draft flag, use
2335  * camel_folder_set_message_flags (folder, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_DRAFT, CAMEL_MESSAGE_DELETED);
2336  *
2337  * DEPRECATED: Use camel_message_info_set_flags() on the message info directly
2338  * (when it works)
2339  *
2340  * Returns: %TRUE if the flags were changed or %FALSE otherwise
2341  **/
2342 gboolean
2343 camel_folder_set_message_flags (CamelFolder *folder,
2344                                 const gchar *uid,
2345                                 CamelMessageFlags flags,
2346                                 CamelMessageFlags set)
2347 {
2348         CamelFolderClass *class;
2349
2350         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
2351         g_return_val_if_fail (uid != NULL, FALSE);
2352
2353         class = CAMEL_FOLDER_GET_CLASS (folder);
2354         g_return_val_if_fail (class->set_message_flags != NULL, FALSE);
2355
2356         if ((flags & (CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_JUNK_LEARN)) == CAMEL_MESSAGE_JUNK) {
2357                 flags |= CAMEL_MESSAGE_JUNK_LEARN;
2358                 set &= ~CAMEL_MESSAGE_JUNK_LEARN;
2359         }
2360
2361         return class->set_message_flags (folder, uid, flags, set);
2362 }
2363
2364 /**
2365  * camel_folder_get_message_user_flag:
2366  * @folder: a #CamelFolder
2367  * @uid: the UID of a message in @folder
2368  * @name: the name of a user flag
2369  *
2370  * DEPRECATED: Use camel_message_info_get_user_flag() on the message
2371  * info directly
2372  *
2373  * Returns: %TRUE if the given user flag is set on the message or
2374  * %FALSE otherwise
2375  **/
2376 gboolean
2377 camel_folder_get_message_user_flag (CamelFolder *folder,
2378                                     const gchar *uid,
2379                                     const gchar *name)
2380 {
2381         CamelFolderClass *class;
2382
2383         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
2384         g_return_val_if_fail (uid != NULL, 0);
2385         g_return_val_if_fail (name != NULL, 0);
2386
2387         class = CAMEL_FOLDER_GET_CLASS (folder);
2388         g_return_val_if_fail (class->get_message_user_flag != NULL, 0);
2389
2390         return class->get_message_user_flag (folder, uid, name);
2391 }
2392
2393 /**
2394  * camel_folder_set_message_user_flag:
2395  * @folder: a #CamelFolder
2396  * @uid: the UID of a message in @folder
2397  * @name: the name of the user flag to set
2398  * @value: the value to set it to
2399  *
2400  * DEPRECATED: Use camel_message_info_set_user_flag() on the
2401  * #CamelMessageInfo directly (when it works)
2402  *
2403  * Sets the user flag specified by @name to the value specified by @value
2404  * on the indicated message. (This may or may not persist after the
2405  * folder or store is closed. See camel_folder_get_permanent_flags())
2406  **/
2407 void
2408 camel_folder_set_message_user_flag (CamelFolder *folder,
2409                                     const gchar *uid,
2410                                     const gchar *name,
2411                                     gboolean value)
2412 {
2413         CamelFolderClass *class;
2414
2415         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2416         g_return_if_fail (uid != NULL);
2417         g_return_if_fail (name != NULL);
2418
2419         class = CAMEL_FOLDER_GET_CLASS (folder);
2420         g_return_if_fail (class->set_message_user_flag != NULL);
2421
2422         class->set_message_user_flag (folder, uid, name, value);
2423 }
2424
2425 /**
2426  * camel_folder_get_message_user_tag:
2427  * @folder: a #CamelFolder
2428  * @uid: the UID of a message in @folder
2429  * @name: the name of a user tag
2430  *
2431  * DEPRECATED: Use camel_message_info_get_user_tag() on the
2432  * #CamelMessageInfo directly.
2433  *
2434  * Returns: the value of the user tag
2435  **/
2436 const gchar *
2437 camel_folder_get_message_user_tag (CamelFolder *folder,
2438                                    const gchar *uid,
2439                                    const gchar *name)
2440 {
2441         CamelFolderClass *class;
2442
2443         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2444         g_return_val_if_fail (uid != NULL, NULL);
2445         g_return_val_if_fail (name != NULL, NULL);
2446
2447         class = CAMEL_FOLDER_GET_CLASS (folder);
2448         g_return_val_if_fail (class->get_message_user_tag != NULL, NULL);
2449
2450         /* FIXME: should duplicate string */
2451         return class->get_message_user_tag (folder, uid, name);
2452 }
2453
2454 /**
2455  * camel_folder_set_message_user_tag:
2456  * @folder: a #CamelFolder
2457  * @uid: the UID of a message in @folder
2458  * @name: the name of the user tag to set
2459  * @value: the value to set it to
2460  *
2461  * DEPRECATED: Use camel_message_info_set_user_tag() on the
2462  * #CamelMessageInfo directly (when it works).
2463  *
2464  * Sets the user tag specified by @name to the value specified by @value
2465  * on the indicated message. (This may or may not persist after the
2466  * folder or store is closed. See camel_folder_get_permanent_flags())
2467  **/
2468 void
2469 camel_folder_set_message_user_tag (CamelFolder *folder,
2470                                    const gchar *uid,
2471                                    const gchar *name,
2472                                    const gchar *value)
2473 {
2474         CamelFolderClass *class;
2475
2476         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2477         g_return_if_fail (uid != NULL);
2478         g_return_if_fail (name != NULL);
2479
2480         class = CAMEL_FOLDER_GET_CLASS (folder);
2481         g_return_if_fail (class->set_message_user_tag != NULL);
2482
2483         class->set_message_user_tag (folder, uid, name, value);
2484 }
2485
2486 /**
2487  * camel_folder_get_message_info:
2488  * @folder: a #CamelFolder
2489  * @uid: the uid of a message
2490  *
2491  * Retrieve the #CamelMessageInfo for the specified @uid.  This return
2492  * must be freed using camel_folder_free_message_info().
2493  *
2494  * Returns: the summary information for the indicated message, or %NULL
2495  * if the uid does not exist
2496  **/
2497 CamelMessageInfo *
2498 camel_folder_get_message_info (CamelFolder *folder,
2499                                const gchar *uid)
2500 {
2501         CamelFolderClass *class;
2502
2503         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2504         g_return_val_if_fail (uid != NULL, NULL);
2505
2506         class = CAMEL_FOLDER_GET_CLASS (folder);
2507         g_return_val_if_fail (class->get_message_info != NULL, NULL);
2508
2509         return class->get_message_info (folder, uid);
2510 }
2511
2512 /**
2513  * camel_folder_free_message_info:
2514  * @folder: a #CamelFolder
2515  * @info: a #CamelMessageInfo
2516  *
2517  * Free (unref) a #CamelMessageInfo, previously obtained with
2518  * camel_folder_get_message_info().
2519  **/
2520 void
2521 camel_folder_free_message_info (CamelFolder *folder,
2522                                 CamelMessageInfo *info)
2523 {
2524         CamelFolderClass *class;
2525
2526         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2527         g_return_if_fail (info != NULL);
2528
2529         class = CAMEL_FOLDER_GET_CLASS (folder);
2530         g_return_if_fail (class->free_message_info != NULL);
2531
2532         class->free_message_info (folder, info);
2533 }
2534
2535 /**
2536  * camel_folder_ref_message_info:
2537  * @folder: a #CamelFolder
2538  * @info: a #CamelMessageInfo
2539  *
2540  * DEPRECATED: Use camel_message_info_ref() directly.
2541  *
2542  * Ref a #CamelMessageInfo, previously obtained with
2543  * camel_folder_get_message_info().
2544  **/
2545 void
2546 camel_folder_ref_message_info (CamelFolder *folder,
2547                                CamelMessageInfo *info)
2548 {
2549         CamelFolderClass *class;
2550
2551         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2552         g_return_if_fail (info != NULL);
2553
2554         class = CAMEL_FOLDER_GET_CLASS (folder);
2555         g_return_if_fail (class->ref_message_info != NULL);
2556
2557         class->ref_message_info (folder, info);
2558 }
2559
2560 /* TODO: is this function required anyway? */
2561 /**
2562  * camel_folder_has_summary_capability:
2563  * @folder: a #CamelFolder
2564  *
2565  * Get whether or not the folder has a summary.
2566  *
2567  * Returns: %TRUE if a summary is available or %FALSE otherwise
2568  **/
2569 gboolean
2570 camel_folder_has_summary_capability (CamelFolder *folder)
2571 {
2572         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
2573
2574         return folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
2575 }
2576
2577 /* UIDs stuff */
2578
2579 /**
2580  * camel_folder_get_uids:
2581  * @folder: a #CamelFolder
2582  *
2583  * Get the list of UIDs available in a folder. This routine is useful
2584  * for finding what messages are available when the folder does not
2585  * support summaries. The returned array should not be modified, and
2586  * must be freed by passing it to camel_folder_free_uids().
2587  *
2588  * Returns: a GPtrArray of UIDs corresponding to the messages available
2589  * in the folder
2590  **/
2591 GPtrArray *
2592 camel_folder_get_uids (CamelFolder *folder)
2593 {
2594         CamelFolderClass *class;
2595
2596         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2597
2598         class = CAMEL_FOLDER_GET_CLASS (folder);
2599         g_return_val_if_fail (class->get_uids != NULL, NULL);
2600
2601         return class->get_uids (folder);
2602 }
2603
2604 /**
2605  * camel_folder_free_uids:
2606  * @folder: a #CamelFolder
2607  * @array: the array of uids to free
2608  *
2609  * Frees the array of UIDs returned by camel_folder_get_uids().
2610  **/
2611 void
2612 camel_folder_free_uids (CamelFolder *folder,
2613                         GPtrArray *array)
2614 {
2615         CamelFolderClass *class;
2616
2617         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2618         g_return_if_fail (array != NULL);
2619
2620         class = CAMEL_FOLDER_GET_CLASS (folder);
2621         g_return_if_fail (class->free_uids != NULL);
2622
2623         class->free_uids (folder, array);
2624 }
2625
2626 /**
2627  * camel_folder_get_uncached_uids:
2628  * @folder: a #CamelFolder
2629  * @uids: the array of uids to filter down to uncached ones.
2630  *
2631  * Returns the known-uncached uids from a list of uids. It may return uids
2632  * which are locally cached but should never filter out a uid which is not
2633  * locally cached. Free the result by called camel_folder_free_uids().
2634  * Frees the array of UIDs returned by camel_folder_get_uids().
2635  *
2636  * Since: 2.26
2637  **/
2638 GPtrArray *
2639 camel_folder_get_uncached_uids (CamelFolder *folder,
2640                                 GPtrArray *uids,
2641                                 GError **error)
2642 {
2643         CamelFolderClass *class;
2644         GPtrArray *uncached_uids;
2645
2646         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2647         g_return_val_if_fail (uids != NULL, NULL);
2648
2649         class = CAMEL_FOLDER_GET_CLASS (folder);
2650         g_return_val_if_fail (class->get_uncached_uids != NULL, NULL);
2651
2652         uncached_uids = class->get_uncached_uids (folder, uids, error);
2653         CAMEL_CHECK_GERROR (folder, get_uncached_uids, uncached_uids != NULL, error);
2654
2655         return uncached_uids;
2656 }
2657
2658 /**
2659  * camel_folder_cmp_uids:
2660  * @folder: a #CamelFolder
2661  * @uid1: The first uid.
2662  * @uid2: the second uid.
2663  *
2664  * Compares two uids. The return value meaning is the same as in any other compare function.
2665  *
2666  * Note that the default compare function expects a decimal number at the beginning of a uid,
2667  * thus if provider uses different uid values, then it should subclass this function.
2668  *
2669  * Since: 2.28
2670  **/
2671 gint
2672 camel_folder_cmp_uids (CamelFolder *folder,
2673                        const gchar *uid1,
2674                        const gchar *uid2)
2675 {
2676         CamelFolderClass *class;
2677
2678         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
2679         g_return_val_if_fail (uid1 != NULL, 0);
2680         g_return_val_if_fail (uid2 != NULL, 0);
2681
2682         class = CAMEL_FOLDER_GET_CLASS (folder);
2683         g_return_val_if_fail (class->cmp_uids != NULL, 0);
2684
2685         return class->cmp_uids (folder, uid1, uid2);
2686 }
2687
2688 /**
2689  * camel_folder_sort_uids:
2690  * @folder: a #CamelFolder
2691  * @uids: array of uids
2692  *
2693  * Sorts the array of UIDs.
2694  *
2695  * Since: 2.24
2696  **/
2697 void
2698 camel_folder_sort_uids (CamelFolder *folder,
2699                         GPtrArray *uids)
2700 {
2701         CamelFolderClass *class;
2702
2703         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2704         g_return_if_fail (uids != NULL);
2705
2706         class = CAMEL_FOLDER_GET_CLASS (folder);
2707         g_return_if_fail (class->sort_uids != NULL);
2708
2709         class->sort_uids (folder, uids);
2710 }
2711
2712 /**
2713  * camel_folder_get_summary:
2714  * @folder: a #CamelFolder
2715  *
2716  * This returns the summary information for the folder. This array
2717  * should not be modified, and must be freed with
2718  * camel_folder_free_summary().
2719  *
2720  * Returns: an array of #CamelMessageInfo
2721  **/
2722 GPtrArray *
2723 camel_folder_get_summary (CamelFolder *folder)
2724 {
2725         CamelFolderClass *class;
2726
2727         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2728
2729         class = CAMEL_FOLDER_GET_CLASS (folder);
2730         g_return_val_if_fail (class->get_summary != NULL, NULL);
2731
2732         return class->get_summary (folder);
2733 }
2734
2735 /**
2736  * camel_folder_free_summary:
2737  * @folder: a #CamelFolder
2738  * @array: the summary array to free
2739  *
2740  * Frees the summary array returned by camel_folder_get_summary().
2741  **/
2742 void
2743 camel_folder_free_summary (CamelFolder *folder,
2744                            GPtrArray *array)
2745 {
2746         CamelFolderClass *class;
2747
2748         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2749         g_return_if_fail (array != NULL);
2750
2751         class = CAMEL_FOLDER_GET_CLASS (folder);
2752         g_return_if_fail (class->free_summary != NULL);
2753
2754         class->free_summary (folder, array);
2755 }
2756
2757 /**
2758  * camel_folder_search_by_expression:
2759  * @folder: a #CamelFolder
2760  * @expr: a search expression
2761  * @cancellable: a #GCancellable
2762  * @error: return location for a #GError, or %NULL
2763  *
2764  * Searches the folder for messages matching the given search expression.
2765  *
2766  * Returns: a #GPtrArray of uids of matching messages. The caller must
2767  * free the list and each of the elements when it is done.
2768  **/
2769 GPtrArray *
2770 camel_folder_search_by_expression (CamelFolder *folder,
2771                                    const gchar *expression,
2772                                    GCancellable *cancellable,
2773                                    GError **error)
2774 {
2775         CamelFolderClass *class;
2776         GPtrArray *matches;
2777
2778         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2779
2780         class = CAMEL_FOLDER_GET_CLASS (folder);
2781         g_return_val_if_fail (class->search_by_expression != NULL, NULL);
2782
2783         /* NOTE: that it is upto the callee to CAMEL_FOLDER_REC_LOCK */
2784
2785         matches = class->search_by_expression (folder, expression, cancellable, error);
2786         CAMEL_CHECK_GERROR (folder, search_by_expression, matches != NULL, error);
2787
2788         return matches;
2789 }
2790
2791 /**
2792  * camel_folder_count_by_expression:
2793  * @folder: a #CamelFolder
2794  * @expression: a search expression
2795  * @cancellable: a #GCancellable
2796  * @error: return location for a #GError, or %NULL
2797  *
2798  * Searches the folder for count of messages matching the given search expression.
2799  *
2800  * Returns: an interger
2801  *
2802  * Since: 2.26
2803  **/
2804 guint32
2805 camel_folder_count_by_expression (CamelFolder *folder,
2806                                   const gchar *expression,
2807                                   GCancellable *cancellable,
2808                                   GError **error)
2809 {
2810         CamelFolderClass *class;
2811
2812         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
2813
2814         class = CAMEL_FOLDER_GET_CLASS (folder);
2815         g_return_val_if_fail (class->count_by_expression != NULL, 0);
2816
2817         /* NOTE: that it is upto the callee to CAMEL_FOLDER_REC_LOCK */
2818
2819         return class->count_by_expression (folder, expression, cancellable, error);
2820 }
2821
2822 /**
2823  * camel_folder_search_by_uids:
2824  * @folder: a #CamelFolder
2825  * @expr: search expression
2826  * @uids: array of uid's to match against.
2827  * @cancellable: a #GCancellable
2828  * @error: return location for a #GError, or %NULL
2829  *
2830  * Search a subset of uid's for an expression match.
2831  *
2832  * Returns: a #GPtrArray of uids of matching messages. The caller must
2833  * free the list and each of the elements when it is done.
2834  **/
2835 GPtrArray *
2836 camel_folder_search_by_uids (CamelFolder *folder,
2837                              const gchar *expr,
2838                              GPtrArray *uids,
2839                              GCancellable *cancellable,
2840                              GError **error)
2841 {
2842         CamelFolderClass *class;
2843         GPtrArray *matches;
2844
2845         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2846
2847         class = CAMEL_FOLDER_GET_CLASS (folder);
2848         g_return_val_if_fail (class->search_by_uids != NULL, NULL);
2849
2850         /* NOTE: that it is upto the callee to CAMEL_FOLDER_REC_LOCK */
2851
2852         matches = class->search_by_uids (folder, expr, uids, cancellable, error);
2853         CAMEL_CHECK_GERROR (folder, search_by_uids, matches != NULL, error);
2854
2855         return matches;
2856 }
2857
2858 /**
2859  * camel_folder_search_free:
2860  * @folder: a #CamelFolder
2861  * @result: search results to free
2862  *
2863  * Free the result of a search as gotten by camel_folder_search() or
2864  * camel_folder_search_by_uids().
2865  **/
2866 void
2867 camel_folder_search_free (CamelFolder *folder,
2868                           GPtrArray *result)
2869 {
2870         CamelFolderClass *class;
2871
2872         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2873         g_return_if_fail (result != NULL);
2874
2875         class = CAMEL_FOLDER_GET_CLASS (folder);
2876         g_return_if_fail (class->search_free != NULL);
2877
2878         /* NOTE: upto the callee to CAMEL_FOLDER_REC_LOCK */
2879
2880         class->search_free (folder, result);
2881 }
2882
2883 /**
2884  * camel_folder_delete:
2885  * @folder: a #CamelFolder
2886  *
2887  * Marks @folder as deleted and performs any required cleanup.
2888  *
2889  * This also emits the #CamelFolder::deleted signal from an idle source on
2890  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
2891  **/
2892 void
2893 camel_folder_delete (CamelFolder *folder)
2894 {
2895         CamelFolderClass *class;
2896         CamelStore *parent_store;
2897         CamelService *service;
2898         CamelSession *session;
2899         SignalData *signal_data;
2900         const gchar *full_name;
2901
2902         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2903
2904         class = CAMEL_FOLDER_GET_CLASS (folder);
2905         g_return_if_fail (class->delete_ != NULL);
2906
2907         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
2908         if (folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
2909                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2910                 return;
2911         }
2912
2913         folder->folder_flags |= CAMEL_FOLDER_HAS_BEEN_DELETED;
2914
2915         class->delete_ (folder);
2916
2917         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2918
2919         /* Delete the references of the folder from the DB.*/
2920         full_name = camel_folder_get_full_name (folder);
2921         parent_store = camel_folder_get_parent_store (folder);
2922         camel_db_delete_folder (parent_store->cdb_w, full_name, NULL);
2923
2924         service = CAMEL_SERVICE (parent_store);
2925         session = camel_service_get_session (service);
2926
2927         signal_data = g_slice_new0 (SignalData);
2928         signal_data->folder = g_object_ref (folder);
2929
2930         camel_session_idle_add (
2931                 session, G_PRIORITY_DEFAULT_IDLE,
2932                 folder_emit_deleted_cb,
2933                 signal_data, (GDestroyNotify) signal_data_free);
2934 }
2935
2936 /**
2937  * camel_folder_rename:
2938  * @folder: a #CamelFolder
2939  * @new_name: new name for the folder
2940  *
2941  * Marks @folder as renamed.
2942  *
2943  * This also emits the #CamelFolder::renamed signal from an idle source on
2944  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
2945  *
2946  * NOTE: This is an internal function used by camel stores, no locking
2947  * is performed on the folder.
2948  **/
2949 void
2950 camel_folder_rename (CamelFolder *folder,
2951                      const gchar *new_name)
2952 {
2953         CamelFolderClass *class;
2954         CamelStore *parent_store;
2955         CamelService *service;
2956         CamelSession *session;
2957         SignalData *signal_data;
2958         gchar *old_name;
2959
2960         g_return_if_fail (CAMEL_IS_FOLDER (folder));
2961         g_return_if_fail (new_name != NULL);
2962
2963         class = CAMEL_FOLDER_GET_CLASS (folder);
2964         g_return_if_fail (class->rename != NULL);
2965
2966         old_name = g_strdup (camel_folder_get_full_name (folder));
2967
2968         class->rename (folder, new_name);
2969
2970         parent_store = camel_folder_get_parent_store (folder);
2971         camel_db_rename_folder (parent_store->cdb_w, old_name, new_name, NULL);
2972
2973         service = CAMEL_SERVICE (parent_store);
2974         session = camel_service_get_session (service);
2975
2976         signal_data = g_slice_new0 (SignalData);
2977         signal_data->folder = g_object_ref (folder);
2978         signal_data->folder_name = old_name;  /* transfer ownership */
2979
2980         camel_session_idle_add (
2981                 session, G_PRIORITY_DEFAULT_IDLE,
2982                 folder_emit_renamed_cb,
2983                 signal_data, (GDestroyNotify) signal_data_free);
2984 }
2985
2986 /**
2987  * camel_folder_changed:
2988  * @folder: a #CamelFolder
2989  * @changes: change information for @folder
2990  *
2991  * Emits the #CamelFolder::changed signal from an idle source on the
2992  * main loop.  The idle source's priority is #G_PRIORITY_LOW.
2993  *
2994  * Since: 2.32
2995  **/
2996 void
2997 camel_folder_changed (CamelFolder *folder,
2998                       CamelFolderChangeInfo *changes)
2999 {
3000         CamelFolderChangeInfo *pending_changes;
3001
3002         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3003         g_return_if_fail (changes != NULL);
3004
3005         if (camel_folder_is_frozen (folder)) {
3006                 /* folder_changed() will catch this case and pile
3007                  * the changes into folder->changed_frozen */
3008                 g_signal_emit (folder, signals[CHANGED], 0, changes);
3009                 return;
3010         }
3011
3012         /* If a "changed" signal has already been scheduled but not yet
3013          * emitted, just append our changes to the pending changes, and
3014          * skip scheduling our own "changed" signal.  This helps to cut
3015          * down on the frequency of signal emissions so virtual folders
3016          * won't have to work so hard. */
3017
3018         camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK);
3019
3020         pending_changes = folder->priv->pending_changes;
3021
3022         if (pending_changes == NULL) {
3023                 CamelStore *parent_store;
3024                 CamelService *service;
3025                 CamelSession *session;
3026                 SignalData *signal_data;
3027
3028                 parent_store = camel_folder_get_parent_store (folder);
3029
3030                 service = CAMEL_SERVICE (parent_store);
3031                 session = camel_service_get_session (service);
3032
3033                 pending_changes = camel_folder_change_info_new ();
3034                 folder->priv->pending_changes = pending_changes;
3035
3036                 signal_data = g_slice_new0 (SignalData);
3037                 signal_data->folder = g_object_ref (folder);
3038
3039                 camel_session_idle_add (
3040                         session, G_PRIORITY_LOW,
3041                         folder_emit_changed_cb,
3042                         signal_data, (GDestroyNotify) signal_data_free);
3043         }
3044
3045         camel_folder_change_info_cat (pending_changes, changes);
3046
3047         camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK);
3048 }
3049
3050 /**
3051  * camel_folder_freeze:
3052  * @folder: a #CamelFolder
3053  *
3054  * Freezes the folder so that a series of operation can be performed
3055  * without "folder_changed" signals being emitted.  When the folder is
3056  * later thawed with camel_folder_thaw(), the suppressed signals will
3057  * be emitted.
3058  **/
3059 void
3060 camel_folder_freeze (CamelFolder *folder)
3061 {
3062         CamelFolderClass *class;
3063
3064         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3065
3066         class = CAMEL_FOLDER_GET_CLASS (folder);
3067         g_return_if_fail (class->freeze != NULL);
3068
3069         class->freeze (folder);
3070 }
3071
3072 /**
3073  * camel_folder_thaw:
3074  * @folder: a #CamelFolder
3075  *
3076  * Thaws the folder and emits any pending folder_changed
3077  * signals.
3078  **/
3079 void
3080 camel_folder_thaw (CamelFolder *folder)
3081 {
3082         CamelFolderClass *class;
3083
3084         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3085         g_return_if_fail (folder->priv->frozen != 0);
3086
3087         class = CAMEL_FOLDER_GET_CLASS (folder);
3088         g_return_if_fail (class->thaw != NULL);
3089
3090         class->thaw (folder);
3091 }
3092
3093 /**
3094  * camel_folder_is_frozen:
3095  * @folder: a #CamelFolder
3096  *
3097  * Returns: whether or not the folder is frozen
3098  **/
3099 gboolean
3100 camel_folder_is_frozen (CamelFolder *folder)
3101 {
3102         CamelFolderClass *class;
3103
3104         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3105
3106         class = CAMEL_FOLDER_GET_CLASS (folder);
3107         g_return_val_if_fail (class->is_frozen != NULL, FALSE);
3108
3109         return class->is_frozen (folder);
3110 }
3111
3112 /**
3113  * camel_folder_get_frozen_count:
3114  * @folder: a #CamelFolder
3115  *
3116  * Since: 2.32
3117  **/
3118 gint
3119 camel_folder_get_frozen_count (CamelFolder *folder)
3120 {
3121         /* FIXME This function shouldn't be needed,
3122          *       but it's used in CamelVeeFolder */
3123         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
3124
3125         return folder->priv->frozen;
3126 }
3127
3128 /**
3129  * camel_folder_quota_info_new:
3130  * @name: Name of the quota.
3131  * @used: Current usage of the quota.
3132  * @total: Total available size of the quota.
3133  *
3134  * Returns: newly allocated #CamelFolderQuotaInfo structure with
3135  * initialized values based on the parameters, with next member set to NULL.
3136  *
3137  * Since: 2.24
3138  **/
3139 CamelFolderQuotaInfo *
3140 camel_folder_quota_info_new (const gchar *name,
3141                              guint64 used,
3142                              guint64 total)
3143 {
3144         CamelFolderQuotaInfo *info;
3145
3146         info = g_malloc0 (sizeof (CamelFolderQuotaInfo));
3147         info->name = g_strdup (name);
3148         info->used = used;
3149         info->total = total;
3150         info->next = NULL;
3151
3152         return info;
3153 }
3154
3155 /**
3156  * camel_folder_quota_info_clone:
3157  * @info: a #CamelFolderQuotaInfo object to clone.
3158  *
3159  * Makes a copy of the given info and all next-s.
3160  *
3161  * Since: 2.24
3162  **/
3163 CamelFolderQuotaInfo *
3164 camel_folder_quota_info_clone (const CamelFolderQuotaInfo *info)
3165 {
3166         CamelFolderQuotaInfo *clone = NULL, *last = NULL;
3167         const CamelFolderQuotaInfo *iter;
3168
3169         for (iter = info; iter != NULL; iter = iter->next) {
3170                 CamelFolderQuotaInfo *n = camel_folder_quota_info_new (iter->name, iter->used, iter->total);
3171
3172                 if (last)
3173                         last->next = n;
3174                 else
3175                         clone = n;
3176
3177                 last = n;
3178         }
3179
3180         return clone;
3181 }
3182
3183 /**
3184  * camel_folder_quota_info_free:
3185  * @info: a #CamelFolderQuotaInfo object to free.
3186  *
3187  * Frees this and all next objects.
3188  *
3189  * Since: 2.24
3190  **/
3191 void
3192 camel_folder_quota_info_free (CamelFolderQuotaInfo *info)
3193 {
3194         CamelFolderQuotaInfo *next = info;
3195
3196         while (next) {
3197                 info = next;
3198                 next = next->next;
3199
3200                 g_free (info->name);
3201                 g_free (info);
3202         }
3203 }
3204
3205 /**
3206  * camel_folder_free_nop:
3207  * @folder: a #CamelFolder
3208  * @array: an array of uids or #CamelMessageInfo
3209  *
3210  * "Frees" the provided array by doing nothing. Used by #CamelFolder
3211  * subclasses as an implementation for free_uids, or free_summary when
3212  * the returned array is "static" information and should not be freed.
3213  **/
3214 void
3215 camel_folder_free_nop (CamelFolder *folder,
3216                        GPtrArray *array)
3217 {
3218         ;
3219 }
3220
3221 /**
3222  * camel_folder_free_shallow:
3223  * @folder: a #CamelFolder
3224  * @array: an array of uids or #CamelMessageInfo
3225  *
3226  * Frees the provided array but not its contents. Used by #CamelFolder
3227  * subclasses as an implementation for free_uids or free_summary when
3228  * the returned array needs to be freed but its contents come from
3229  * "static" information.
3230  **/
3231 void
3232 camel_folder_free_shallow (CamelFolder *folder,
3233                            GPtrArray *array)
3234 {
3235         g_ptr_array_free (array, TRUE);
3236 }
3237
3238 /**
3239  * camel_folder_free_deep:
3240  * @folder: a #CamelFolder
3241  * @array: an array of uids
3242  *
3243  * Frees the provided array and its contents. Used by #CamelFolder
3244  * subclasses as an implementation for free_uids when the provided
3245  * information was created explicitly by the corresponding get_ call.
3246  **/
3247 void
3248 camel_folder_free_deep (CamelFolder *folder,
3249                         GPtrArray *array)
3250 {
3251         gint i;
3252
3253         g_return_if_fail (array != NULL);
3254
3255         for (i = 0; i < array->len; i++)
3256                 g_free (array->pdata[i]);
3257         g_ptr_array_free (array, TRUE);
3258 }
3259
3260 /**
3261  * camel_folder_lock:
3262  * @folder: a #CamelFolder
3263  * @lock: lock type to lock
3264  *
3265  * Locks @folder's @lock. Unlock it with camel_folder_unlock().
3266  *
3267  * Since: 2.32
3268  **/
3269 void
3270 camel_folder_lock (CamelFolder *folder,
3271                    CamelFolderLock lock)
3272 {
3273         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3274
3275         switch (lock) {
3276                 case CAMEL_FOLDER_CHANGE_LOCK:
3277                         g_mutex_lock (&folder->priv->change_lock);
3278                         break;
3279                 case CAMEL_FOLDER_REC_LOCK:
3280                         if (folder->priv->skip_folder_lock == FALSE)
3281                                 g_rec_mutex_lock (&folder->priv->lock);
3282                         break;
3283                 default:
3284                         g_return_if_reached ();
3285         }
3286 }
3287
3288 /**
3289  * camel_folder_unlock:
3290  * @folder: a #CamelFolder
3291  * @lock: lock type to unlock
3292  *
3293  * Unlocks @folder's @lock, previously locked with camel_folder_lock().
3294  *
3295  * Since: 2.32
3296  **/
3297 void
3298 camel_folder_unlock (CamelFolder *folder,
3299                      CamelFolderLock lock)
3300 {
3301         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3302
3303         switch (lock) {
3304                 case CAMEL_FOLDER_CHANGE_LOCK:
3305                         g_mutex_unlock (&folder->priv->change_lock);
3306                         break;
3307                 case CAMEL_FOLDER_REC_LOCK:
3308                         if (folder->priv->skip_folder_lock == FALSE)
3309                                 g_rec_mutex_unlock (&folder->priv->lock);
3310                         break;
3311                 default:
3312                         g_return_if_reached ();
3313         }
3314 }
3315
3316 /**
3317  * camel_folder_append_message_sync:
3318  * @folder: a #CamelFolder
3319  * @message: a #CamelMimeMessage
3320  * @info: a #CamelMessageInfo with additional flags/etc to set on the
3321  *        new message, or %NULL
3322  * @appended_uid: if non-%NULL, the UID of the appended message will
3323  *                be returned here, if it is known
3324  * @cancellable: optional #GCancellable object, or %NULL
3325  * @error: return location for a #GError, or %NULL
3326  *
3327  * Appends @message to @folder.  Only the flag and tag data from @info
3328  * are used.  If @info is %NULL, no flags or tags will be set.
3329  *
3330  * Returns: %TRUE on success, %FALSE on error
3331  *
3332  * Since: 3.0
3333  **/
3334 gboolean
3335 camel_folder_append_message_sync (CamelFolder *folder,
3336                                   CamelMimeMessage *message,
3337                                   CamelMessageInfo *info,
3338                                   gchar **appended_uid,
3339                                   GCancellable *cancellable,
3340                                   GError **error)
3341 {
3342         CamelFolderClass *class;
3343         gboolean success;
3344
3345         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3346         g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
3347
3348         class = CAMEL_FOLDER_GET_CLASS (folder);
3349         g_return_val_if_fail (class->append_message_sync != NULL, FALSE);
3350
3351         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
3352
3353         /* Check for cancellation after locking. */
3354         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
3355                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3356                 return FALSE;
3357         }
3358
3359         success = class->append_message_sync (
3360                 folder, message, info, appended_uid, cancellable, error);
3361         CAMEL_CHECK_GERROR (folder, append_message_sync, success, error);
3362
3363         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3364
3365         return success;
3366 }
3367
3368 /**
3369  * camel_folder_append_message:
3370  * @folder a #CamelFolder
3371  * @message: a #CamelMimeMessage
3372  * @info: a #CamelMessageInfo with additional flags/etc to set on the
3373  *        new message, or %NULL
3374  * @io_priority: the I/O priority of the request
3375  * @cancellable: optional #GCancellable object, or %NULL
3376  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3377  * @user_data: data to pass to the callback function
3378  *
3379  * Appends @message to @folder asynchronously.  Only the flag and tag data
3380  * from @info are used.  If @info is %NULL, no flags or tags will be set.
3381  *
3382  * When the operation is finished, @callback will be called.  You can
3383  * then call camel_folder_append_message_finish() to get the result of
3384  * the operation.
3385  *
3386  * Since: 3.0
3387  **/
3388 void
3389 camel_folder_append_message (CamelFolder *folder,
3390                              CamelMimeMessage *message,
3391                              CamelMessageInfo *info,
3392                              gint io_priority,
3393                              GCancellable *cancellable,
3394                              GAsyncReadyCallback callback,
3395                              gpointer user_data)
3396 {
3397         CamelFolderClass *class;
3398
3399         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3400         g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
3401
3402         class = CAMEL_FOLDER_GET_CLASS (folder);
3403         g_return_if_fail (class->append_message != NULL);
3404
3405         class->append_message (
3406                 folder, message, info, io_priority,
3407                 cancellable, callback, user_data);
3408 }
3409
3410 /**
3411  * camel_folder_append_message_finish:
3412  * @folder: a #CamelFolder
3413  * @result: a #GAsyncResult
3414  * @appended_uid: if non-%NULL, the UID of the appended message will
3415  *                be returned here, if it is known
3416  * @error: return location for a #GError, or %NULL
3417  *
3418  * Finishes the operation started with camel_folder_append_message_finish().
3419  *
3420  * Returns: %TRUE on success, %FALSE on error
3421  *
3422  * Since: 3.0
3423  **/
3424 gboolean
3425 camel_folder_append_message_finish (CamelFolder *folder,
3426                                     GAsyncResult *result,
3427                                     gchar **appended_uid,
3428                                     GError **error)
3429 {
3430         CamelFolderClass *class;
3431
3432         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3433         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3434
3435         class = CAMEL_FOLDER_GET_CLASS (folder);
3436         g_return_val_if_fail (class->append_message_finish != NULL, FALSE);
3437
3438         return class->append_message_finish (
3439                 folder, result, appended_uid, error);
3440 }
3441
3442 /**
3443  * camel_folder_expunge_sync:
3444  * @folder: a #CamelFolder
3445  * @cancellable: optional #GCancellable object, or %NULL
3446  * @error: return location for a #GError, or %NULL
3447  *
3448  * Deletes messages which have been marked as "DELETED".
3449  *
3450  * Returns: %TRUE on success, %FALSE on error
3451  *
3452  * Since: 3.0
3453  **/
3454 gboolean
3455 camel_folder_expunge_sync (CamelFolder *folder,
3456                            GCancellable *cancellable,
3457                            GError **error)
3458 {
3459         CamelFolderClass *class;
3460         const gchar *display_name;
3461         const gchar *message;
3462         gboolean success = TRUE;
3463
3464         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3465
3466         class = CAMEL_FOLDER_GET_CLASS (folder);
3467         g_return_val_if_fail (class->expunge_sync != NULL, FALSE);
3468
3469         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
3470
3471         /* Check for cancellation after locking. */
3472         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
3473                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3474                 return FALSE;
3475         }
3476
3477         message = _("Expunging folder '%s'");
3478         display_name = camel_folder_get_display_name (folder);
3479         camel_operation_push_message (cancellable, message, display_name);
3480
3481         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED)) {
3482                 success = class->expunge_sync (folder, cancellable, error);
3483                 CAMEL_CHECK_GERROR (folder, expunge_sync, success, error);
3484         }
3485
3486         camel_operation_pop_message (cancellable);
3487
3488         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3489
3490         return success;
3491 }
3492
3493 /**
3494  * camel_folder_expunge:
3495  * @folder: a #CamelFolder
3496  * @io_priority: the I/O priority of the request
3497  * @cancellable: optional #GCancellable object, or %NULL
3498  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3499  * @user_data: data to pass to the callback function
3500  *
3501  * Asynchronously deletes messages which have been marked as "DELETED".
3502  *
3503  * When the operation is finished, @callback will be called.  You can then
3504  * call camel_folder_expunge_finish() to get the result of the operation.
3505  *
3506  * Since: 3.0
3507  **/
3508 void
3509 camel_folder_expunge (CamelFolder *folder,
3510                       gint io_priority,
3511                       GCancellable *cancellable,
3512                       GAsyncReadyCallback callback,
3513                       gpointer user_data)
3514 {
3515         CamelFolderClass *class;
3516
3517         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3518
3519         class = CAMEL_FOLDER_GET_CLASS (folder);
3520         g_return_if_fail (class->expunge != NULL);
3521
3522         class->expunge (folder, io_priority, cancellable, callback, user_data);
3523 }
3524
3525 /**
3526  * camel_folder_expunge_finish:
3527  * @folder: a #CamelFolder
3528  * @result: a #GAsyncResult
3529  * @error: return location for a #GError, or %NULL
3530  *
3531  * Finishes the operation started with camel_folder_expunge().
3532  *
3533  * Returns: %TRUE on success, %FALSE on error
3534  *
3535  * Since: 3.0
3536  **/
3537 gboolean
3538 camel_folder_expunge_finish (CamelFolder *folder,
3539                              GAsyncResult *result,
3540                              GError **error)
3541 {
3542         CamelFolderClass *class;
3543
3544         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3545         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3546
3547         class = CAMEL_FOLDER_GET_CLASS (folder);
3548         g_return_val_if_fail (class->expunge_finish != NULL, FALSE);
3549
3550         return class->expunge_finish (folder, result, error);
3551 }
3552
3553 /**
3554  * camel_folder_fetch_messages_sync :
3555  * @folder: a #CamelFolder
3556  * @type: Type to specify fetch old or new messages.
3557  * #limit: Limit to specify the number of messages to download.
3558  * @cancellable: optional #GCancellable object, or %NULL
3559  * @error: return location for a #GError, or %NULL
3560  *
3561  * Downloads old or new specified number of messages from the server. It is
3562  * optimized for mobile client usage. Desktop clients should keep away from
3563  * this api and use @camel_folder_refresh_info.
3564  *
3565  * Returns: %TRUE if there are more messages to fetch,
3566  *          %FALSE if there are no more messages
3567  *
3568  * Since: 3.4
3569  **/
3570 gboolean
3571 camel_folder_fetch_messages_sync (CamelFolder *folder,
3572                                   CamelFetchType type,
3573                                   gint limit,
3574                                   GCancellable *cancellable,
3575                                   GError **error)
3576 {
3577         CamelFolderClass *class;
3578         gboolean success = TRUE;
3579
3580         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3581
3582         class = CAMEL_FOLDER_GET_CLASS (folder);
3583
3584         /* Some backends that wont support mobile
3585          * mode, won't have this method implemented. */
3586         if (class->fetch_messages_sync == NULL)
3587                 return FALSE;
3588
3589         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
3590
3591         /* Check for cancellation after locking. */
3592         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
3593                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3594                 return FALSE;
3595         }
3596
3597         success = class->fetch_messages_sync (
3598                 folder, type, limit, cancellable, error);
3599         CAMEL_CHECK_GERROR (folder, fetch_messages_sync, success, error);
3600
3601         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3602
3603         return success;
3604 }
3605
3606 /**
3607  * camel_folder_fetch_messages:
3608  * @folder: a #CamelFolder
3609  * @type: Type to specify fetch old or new messages.
3610  * #limit: Limit to specify the number of messages to download.
3611  * @io_priority: the I/O priority of the request
3612  * @cancellable: optional #GCancellable object, or %NULL
3613  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3614  * @user_data: data to pass to the callback function
3615  *
3616  * Asynchronously download new or old messages from the server. It is assumes
3617  * that the client has only a window of interested messages of what server has.
3618  * And old/new type helps to expand that window.
3619  *
3620  * type = CAMEL_FETCH_OLD_MESSAGES: Downloads messages older than what the
3621  * client already has.
3622  * type = CAMEL_FETCH_NEW_MESSAGES: Downloads messages newer than what the
3623  * client already has.
3624  *
3625  * When the operation is finished, @callback will be called.  You can then
3626  * call camel_folder_fetch_messages_finish() to get the result of the operation.
3627  *
3628  * Since: 3.4
3629  **/
3630 void
3631 camel_folder_fetch_messages (CamelFolder *folder,
3632                              CamelFetchType type,
3633                              gint limit,
3634                              gint io_priority,
3635                              GCancellable *cancellable,
3636                              GAsyncReadyCallback callback,
3637                              gpointer user_data)
3638 {
3639         CamelFolderClass *class;
3640
3641         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3642
3643         class = CAMEL_FOLDER_GET_CLASS (folder);
3644         g_return_if_fail (class->fetch_messages != NULL);
3645
3646         class->fetch_messages (
3647                 folder, type, limit, io_priority,
3648                 cancellable, callback, user_data);
3649 }
3650
3651 /**
3652  * camel_folder_fetch_messages_finish:
3653  * @folder: a #CamelFolder
3654  * @result: a #GAsyncResult
3655  * @error: return location for a #GError, or %NULL
3656  *
3657  * Finishes the operation started with camel_folder_fetch_messages().
3658  *
3659  * Returns: %TRUE if there are more messages to fetch,
3660  *          %FALSE if there are no more messages
3661  *
3662  * Since: 3.4
3663  **/
3664 gboolean
3665 camel_folder_fetch_messages_finish (CamelFolder *folder,
3666                                     GAsyncResult *result,
3667                                     GError **error)
3668 {
3669         CamelFolderClass *class;
3670
3671         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3672         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3673
3674         class = CAMEL_FOLDER_GET_CLASS (folder);
3675         g_return_val_if_fail (class->fetch_messages_finish != NULL, FALSE);
3676
3677         return class->fetch_messages_finish (folder, result, error);
3678 }
3679
3680 /**
3681  * camel_folder_get_message_sync:
3682  * @folder: a #CamelFolder
3683  * @message_uid: the message UID
3684  * @cancellable: optional #GCancellable object, or %NULL
3685  * @error: return location for a #GError, or %NULL
3686  *
3687  * Gets the message corresponding to @message_uid from @folder.
3688  *
3689  * Returns: a #CamelMimeMessage corresponding to the requested UID
3690  *
3691  * Since: 3.0
3692  **/
3693 CamelMimeMessage *
3694 camel_folder_get_message_sync (CamelFolder *folder,
3695                                const gchar *message_uid,
3696                                GCancellable *cancellable,
3697                                GError **error)
3698 {
3699         CamelFolderClass *class;
3700         CamelMimeMessage *message = NULL;
3701
3702         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
3703         g_return_val_if_fail (message_uid != NULL, NULL);
3704
3705         class = CAMEL_FOLDER_GET_CLASS (folder);
3706         g_return_val_if_fail (class->get_message_sync != NULL, NULL);
3707
3708         camel_operation_push_message (
3709                 cancellable, _("Retrieving message '%s' in %s"),
3710                 message_uid, camel_folder_get_display_name (folder));
3711
3712         if (class->get_message_cached) {
3713                 /* Return cached message, if available locally; this should
3714                  * not do any network I/O, only check if message is already
3715                  * downloaded and return it quicker, not being blocked by
3716                  * the folder's lock.  Returning NULL is not considered as
3717                  * an error, it just means that the message is still
3718                  * to-be-downloaded. */
3719                 message = class->get_message_cached (
3720                         folder, message_uid, cancellable);
3721         }
3722
3723         if (!message) {
3724                 camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
3725
3726                 /* Check for cancellation after locking. */
3727                 if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
3728                         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3729                         camel_operation_pop_message (cancellable);
3730                         return NULL;
3731                 }
3732
3733                 message = class->get_message_sync (
3734                         folder, message_uid, cancellable, error);
3735                 CAMEL_CHECK_GERROR (
3736                         folder, get_message_sync, message != NULL, error);
3737
3738                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3739         }
3740
3741         if (message && camel_mime_message_get_source (message) == NULL) {
3742                 CamelStore *store;
3743                 const gchar *uid;
3744
3745                 store = camel_folder_get_parent_store (folder);
3746                 uid = camel_service_get_uid (CAMEL_SERVICE (store));
3747
3748                 camel_mime_message_set_source (message, uid);
3749         }
3750
3751         camel_operation_pop_message (cancellable);
3752
3753         if (message != NULL && camel_debug_start (":folder")) {
3754                 printf (
3755                         "CamelFolder:get_message ('%s', '%s') =\n",
3756                         camel_folder_get_full_name (folder), message_uid);
3757                 camel_mime_message_dump (message, FALSE);
3758                 camel_debug_end ();
3759         }
3760
3761         return message;
3762 }
3763
3764 /**
3765  * camel_folder_get_message:
3766  * @folder: a #CamelFolder
3767  * @message_uid: the message UID
3768  * @io_priority: the I/O priority of the request
3769  * @cancellable: optional #GCancellable object, or %NULL
3770  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3771  * @user_data: data to pass to the callback function
3772  *
3773  * Asynchronously gets the message corresponding to @message_uid from @folder.
3774  *
3775  * When the operation is finished, @callback will be called.  You can then
3776  * call camel_folder_get_message_finish() to get the result of the operation.
3777  *
3778  * Since: 3.0
3779  **/
3780 void
3781 camel_folder_get_message (CamelFolder *folder,
3782                           const gchar *message_uid,
3783                           gint io_priority,
3784                           GCancellable *cancellable,
3785                           GAsyncReadyCallback callback,
3786                           gpointer user_data)
3787 {
3788         CamelFolderClass *class;
3789
3790         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3791         g_return_if_fail (message_uid != NULL);
3792
3793         class = CAMEL_FOLDER_GET_CLASS (folder);
3794         g_return_if_fail (class->get_message != NULL);
3795
3796         class->get_message (
3797                 folder, message_uid, io_priority,
3798                 cancellable, callback, user_data);
3799 }
3800
3801 /**
3802  * camel_folder_get_message_finish:
3803  * @folder: a #CamelFolder
3804  * @result: a #GAsyncResult
3805  * @error: return location for a #GError or %NULL
3806  *
3807  * Finishes the operation started with camel_folder_get_message().
3808  *
3809  * Returns: a #CamelMimeMessage corresponding to the requested UID
3810  *
3811  * Since: 3.0
3812  **/
3813 CamelMimeMessage *
3814 camel_folder_get_message_finish (CamelFolder *folder,
3815                                  GAsyncResult *result,
3816                                  GError **error)
3817 {
3818         CamelFolderClass *class;
3819
3820         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
3821         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
3822
3823         class = CAMEL_FOLDER_GET_CLASS (folder);
3824         g_return_val_if_fail (class->get_message_finish != NULL, NULL);
3825
3826         return class->get_message_finish (folder, result, error);
3827 }
3828
3829 /**
3830  * camel_folder_get_quota_info_sync:
3831  * @folder: a #CamelFolder
3832  * @cancellable: optional #GCancellable object, or %NULL
3833  * @error: return location for a #GError, or %NULL
3834  *
3835  * Gets a list of known quotas for @folder.  Free the returned
3836  * #CamelFolderQuotaInfo struct with camel_folder_quota_info_free().
3837  *
3838  * If quotas are not supported for @folder, the function returns %NULL
3839  * and sets @error to #G_IO_ERROR_NOT_SUPPORTED.
3840  *
3841  * Returns: a #CamelFolderQuotaInfo, or %NULL
3842  *
3843  * Since: 3.2
3844  **/
3845 CamelFolderQuotaInfo *
3846 camel_folder_get_quota_info_sync (CamelFolder *folder,
3847                                   GCancellable *cancellable,
3848                                   GError **error)
3849 {
3850         CamelFolderClass *class;
3851         CamelFolderQuotaInfo *quota_info;
3852         const gchar *display_name;
3853         const gchar *message;
3854
3855         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
3856
3857         class = CAMEL_FOLDER_GET_CLASS (folder);
3858         g_return_val_if_fail (class->get_quota_info_sync != NULL, NULL);
3859
3860         message = _("Retrieving quota information for '%s'");
3861         display_name = camel_folder_get_display_name (folder);
3862         camel_operation_push_message (cancellable, message, display_name);
3863
3864         quota_info = class->get_quota_info_sync (folder, cancellable, error);
3865         CAMEL_CHECK_GERROR (
3866                 folder, get_quota_info_sync, quota_info != NULL, error);
3867
3868         camel_operation_pop_message (cancellable);
3869
3870         return quota_info;
3871 }
3872
3873 /**
3874  * camel_folder_get_quota_info:
3875  * @folder: a #CamelFolder
3876  * @io_priority: the I/O priority of the request
3877  * @cancellable: optional #GCancellable object, or %NULL
3878  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3879  * @user_data: data to pass to the callback function
3880  *
3881  * Asynchronously gets a list of known quotas for @folder.
3882  *
3883  * When the operation is finished, @callback will be called.  You can
3884  * then call camel_folder_get_quota_info_finish() to get the result of
3885  * the operation.
3886  *
3887  * Since: 3.2
3888  **/
3889 void
3890 camel_folder_get_quota_info (CamelFolder *folder,
3891                              gint io_priority,
3892                              GCancellable *cancellable,
3893                              GAsyncReadyCallback callback,
3894                              gpointer user_data)
3895 {
3896         CamelFolderClass *class;
3897
3898         g_return_if_fail (CAMEL_IS_FOLDER (folder));
3899
3900         class = CAMEL_FOLDER_GET_CLASS (folder);
3901         g_return_if_fail (class->get_quota_info != NULL);
3902
3903         class->get_quota_info (
3904                 folder, io_priority,
3905                 cancellable, callback, user_data);
3906 }
3907
3908 /**
3909  * camel_folder_get_quota_info_finish:
3910  * @folder: a #CamelFolder
3911  * @result: a #GAsyncResult
3912  * @error: return location for a #GError or %NULL
3913  *
3914  * Finishes the operation started with camel_folder_get_quota_info().
3915  * Free the returned #CamelFolderQuotaInfo struct with
3916  * camel_folder_quota_info_free().
3917  *
3918  * If quotas are not supported for @folder, the function returns %NULL
3919  * and sets @error to #G_IO_ERROR_NOT_SUPPORTED.
3920  *
3921  * Returns: a #CamelFolderQuotaInfo, or %NULL
3922  *
3923  * Since: 3.2
3924  **/
3925 CamelFolderQuotaInfo *
3926 camel_folder_get_quota_info_finish (CamelFolder *folder,
3927                                     GAsyncResult *result,
3928                                     GError **error)
3929 {
3930         CamelFolderClass *class;
3931
3932         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
3933
3934         class = CAMEL_FOLDER_GET_CLASS (folder);
3935         g_return_val_if_fail (class->get_quota_info_finish != NULL, NULL);
3936
3937         return class->get_quota_info_finish (folder, result, error);
3938 }
3939
3940 /**
3941  * camel_folder_purge_message_cache_sync :
3942  * @folder: a #CamelFolder
3943  * @start_uid: the start message UID
3944  * @end_uid: the end message UID
3945  * @cancellable: optional #GCancellable object, or %NULL
3946  * @error: return location for a #GError, or %NULL
3947  *
3948  * Delete the local cache of all messages between these uids.
3949  *
3950  * Returns: %TRUE if success, %FALSE if there are any errors. 
3951  *
3952  * Since: 3.4
3953  **/
3954 gboolean
3955 camel_folder_purge_message_cache_sync (CamelFolder *folder,
3956                                        gchar *start_uid,
3957                                        gchar *end_uid,
3958                                        GCancellable *cancellable,
3959                                        GError **error)
3960 {
3961         CamelFolderClass *class;
3962         gboolean success = TRUE;
3963
3964         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3965
3966         class = CAMEL_FOLDER_GET_CLASS (folder);
3967
3968         /* Some backends that wont support mobile
3969          * mode, won't have this api implemented. */
3970         if (class->purge_message_cache_sync == NULL)
3971                 return FALSE;
3972
3973         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
3974
3975         /* Check for cancellation after locking. */
3976         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
3977                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3978                 return FALSE;
3979         }
3980
3981         success = class->purge_message_cache_sync (
3982                 folder, start_uid, end_uid, cancellable, error);
3983         CAMEL_CHECK_GERROR (folder, purge_message_cache_sync, success, error);
3984
3985         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
3986
3987         return success;
3988 }
3989
3990 /**
3991  * camel_folder_purge_message_cache:
3992  * @folder: a #CamelFolder
3993  * @start_uid: the start message UID
3994  * @end_uid: the end message UID 
3995  * @io_priority: the I/O priority of the request
3996  * @cancellable: optional #GCancellable object, or %NULL
3997  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3998  * @user_data: data to pass to the callback function
3999  *
4000  * Delete the local cache of all messages between these uids.
4001  * 
4002  * When the operation is finished, @callback will be called.  You can then
4003  * call camel_folder_purge_message_cache_finish() to get the result of the
4004  * operation.
4005  *
4006  * Since: 3.4
4007  **/
4008 void
4009 camel_folder_purge_message_cache (CamelFolder *folder,
4010                                   gchar *start_uid,
4011                                   gchar *end_uid,
4012                                   gint io_priority,
4013                                   GCancellable *cancellable,
4014                                   GAsyncReadyCallback callback,
4015                                   gpointer user_data)
4016 {
4017         CamelFolderClass *class;
4018
4019         g_return_if_fail (CAMEL_IS_FOLDER (folder));
4020
4021         class = CAMEL_FOLDER_GET_CLASS (folder);
4022         g_return_if_fail (class->purge_message_cache != NULL);
4023
4024         class->purge_message_cache (
4025                 folder, start_uid, end_uid, io_priority,
4026                 cancellable, callback, user_data);
4027 }
4028
4029 /**
4030  * camel_folder_purge_message_cache_finish:
4031  * @folder: a #CamelFolder
4032  * @result: a #GAsyncResult
4033  * @error: return location for a #GError, or %NULL
4034  *
4035  * Finishes the operation started with camel_folder_purge_message_cache().
4036  *
4037  * Returns: %TRUE if cache is deleted, %FALSE if there are any errors
4038  *
4039  * Since: 3.4
4040  **/
4041 gboolean
4042 camel_folder_purge_message_cache_finish (CamelFolder *folder,
4043                                          GAsyncResult *result,
4044                                     GError **error)
4045 {
4046         CamelFolderClass *class;
4047
4048         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4049         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
4050
4051         class = CAMEL_FOLDER_GET_CLASS (folder);
4052         g_return_val_if_fail (class->purge_message_cache_finish != NULL, FALSE);
4053
4054         return class->purge_message_cache_finish (folder, result, error);
4055 }
4056
4057 /**
4058  * camel_folder_refresh_info_sync:
4059  * @folder: a #CamelFolder
4060  * @cancellable: optional #GCancellable object, or %NULL
4061  * @error: return location for a #GError, or %NULL
4062  *
4063  * Synchronizes a folder's summary with its backing store.
4064  *
4065  * Returns: %TRUE on success, %FALSE on error
4066  *
4067  * Since: 3.0
4068  **/
4069 gboolean
4070 camel_folder_refresh_info_sync (CamelFolder *folder,
4071                                 GCancellable *cancellable,
4072                                 GError **error)
4073 {
4074         CamelFolderClass *class;
4075         const gchar *display_name;
4076         const gchar *message;
4077         gboolean success;
4078
4079         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4080
4081         class = CAMEL_FOLDER_GET_CLASS (folder);
4082         g_return_val_if_fail (class->refresh_info_sync != NULL, FALSE);
4083
4084         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
4085
4086         /* Check for cancellation after locking. */
4087         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
4088                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4089                 return FALSE;
4090         }
4091
4092         message = _("Refreshing folder '%s'");
4093         display_name = camel_folder_get_display_name (folder);
4094         camel_operation_push_message (cancellable, message, display_name);
4095
4096         success = class->refresh_info_sync (folder, cancellable, error);
4097         CAMEL_CHECK_GERROR (folder, refresh_info_sync, success, error);
4098
4099         camel_operation_pop_message (cancellable);
4100
4101         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4102
4103         return success;
4104 }
4105
4106 /**
4107  * camel_folder_refresh_info:
4108  * @folder: a #CamelFolder
4109  * @io_priority: the I/O priority of the request
4110  * @cancellable: optional #GCancellable object, or %NULL
4111  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4112  * @user_data: data to pass to the callback function
4113  *
4114  * Asynchronously synchronizes a folder's summary with its backing store.
4115  *
4116  * When the operation is finished, @callback will be called.  You can then
4117  * call camel_folder_refresh_info_finish() to get the result of the operation.
4118  *
4119  * Since: 3.2
4120  **/
4121 void
4122 camel_folder_refresh_info (CamelFolder *folder,
4123                            gint io_priority,
4124                            GCancellable *cancellable,
4125                            GAsyncReadyCallback callback,
4126                            gpointer user_data)
4127 {
4128         CamelFolderClass *class;
4129
4130         g_return_if_fail (CAMEL_IS_FOLDER (folder));
4131
4132         class = CAMEL_FOLDER_GET_CLASS (folder);
4133         g_return_if_fail (class->refresh_info != NULL);
4134
4135         class->refresh_info (
4136                 folder, io_priority, cancellable, callback, user_data);
4137 }
4138
4139 /**
4140  * camel_folder_refresh_info_finish:
4141  * @folder: a #CamelFolder
4142  * @result: a #GAsyncResult
4143  * @error: return location for a #GError, or %NULL
4144  *
4145  * Finishes the operation started with camel_folder_refresh_info().
4146  *
4147  * Returns: %TRUE on success, %FALSE on error
4148  *
4149  * Since: 3.2
4150  **/
4151 gboolean
4152 camel_folder_refresh_info_finish (CamelFolder *folder,
4153                                   GAsyncResult *result,
4154                                   GError **error)
4155 {
4156         CamelFolderClass *class;
4157
4158         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4159         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
4160
4161         class = CAMEL_FOLDER_GET_CLASS (folder);
4162         g_return_val_if_fail (class->refresh_info_finish != NULL, FALSE);
4163
4164         return class->refresh_info_finish (folder, result, error);
4165 }
4166
4167 /**
4168  * camel_folder_synchronize_sync:
4169  * @folder: a #CamelFolder
4170  * @expunge: whether to expunge after synchronizing
4171  * @cancellable: optional #GCancellable object, or %NULL
4172  * @error: return location for a #GError, or %NULL
4173  *
4174  * Synchronizes any changes that have been made to @folder to its
4175  * backing store, optionally expunging deleted messages as well.
4176  *
4177  * Returns: %TRUE on success, %FALSE on error
4178  *
4179  * Since: 3.0
4180  **/
4181 gboolean
4182 camel_folder_synchronize_sync (CamelFolder *folder,
4183                                gboolean expunge,
4184                                GCancellable *cancellable,
4185                                GError **error)
4186 {
4187         CamelFolderClass *class;
4188         gboolean success = TRUE;
4189
4190         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4191
4192         class = CAMEL_FOLDER_GET_CLASS (folder);
4193         g_return_val_if_fail (class->synchronize_sync != NULL, FALSE);
4194
4195         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
4196
4197         /* Check for cancellation after locking. */
4198         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
4199                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4200                 return FALSE;
4201         }
4202
4203         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED)) {
4204                 success = class->synchronize_sync (
4205                         folder, expunge, cancellable, error);
4206                 CAMEL_CHECK_GERROR (folder, synchronize_sync, success, error);
4207         }
4208
4209         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4210
4211         return success;
4212 }
4213
4214 /**
4215  * camel_folder_synchronize:
4216  * @folder: a #CamelFolder
4217  * @expunge: whether to expunge after synchronizing
4218  * @io_priority: the I/O priority of the request
4219  * @cancellable: optional #GCancellable object, or %NULL
4220  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4221  * @user_data: data to pass to the callback function
4222  *
4223  * Synchronizes any changes that have been made to @folder to its backing
4224  * store asynchronously, optionally expunging deleted messages as well.
4225  *
4226  * When the operation is finished, @callback will be called.  You can then
4227  * call camel_folder_synchronize_finish() to get the result of the operation.
4228  *
4229  * Since: 3.0
4230  **/
4231 void
4232 camel_folder_synchronize (CamelFolder *folder,
4233                           gboolean expunge,
4234                           gint io_priority,
4235                           GCancellable *cancellable,
4236                           GAsyncReadyCallback callback,
4237                           gpointer user_data)
4238 {
4239         CamelFolderClass *class;
4240
4241         g_return_if_fail (CAMEL_IS_FOLDER (folder));
4242
4243         class = CAMEL_FOLDER_GET_CLASS (folder);
4244         g_return_if_fail (class->synchronize != NULL);
4245
4246         class->synchronize (
4247                 folder, expunge, io_priority,
4248                 cancellable, callback, user_data);
4249 }
4250
4251 /**
4252  * camel_folder_synchronize_finish:
4253  * @folder: a #CamelFolder
4254  * @result: a #GAsyncResult
4255  * @error: return location for a #GError, or %NULL
4256  *
4257  * Finishes the operation started with camel_folder_synchronize().
4258  *
4259  * Returns: %TRUE on sucess, %FALSE on error
4260  *
4261  * Since: 3.0
4262  **/
4263 gboolean
4264 camel_folder_synchronize_finish (CamelFolder *folder,
4265                                  GAsyncResult *result,
4266                                  GError **error)
4267 {
4268         CamelFolderClass *class;
4269
4270         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4271         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
4272
4273         class = CAMEL_FOLDER_GET_CLASS (folder);
4274         g_return_val_if_fail (class->synchronize_finish != NULL, FALSE);
4275
4276         return class->synchronize_finish (folder, result, error);
4277 }
4278
4279 /**
4280  * camel_folder_synchronize_message_sync:
4281  * @folder: a #CamelFolder
4282  * @message_uid: a message UID
4283  * @cancellable: optional #GCancellable object, or %NULL
4284  * @error: return location for a #GError, or %NULL
4285  *
4286  * Ensure that a message identified by @message_uid has been synchronized in
4287  * @folder so that calling camel_folder_get_message() on it later will work
4288  * in offline mode.
4289  *
4290  * Returns: %TRUE on success, %FALSE on error
4291  *
4292  * Since: 3.0
4293  **/
4294 gboolean
4295 camel_folder_synchronize_message_sync (CamelFolder *folder,
4296                                        const gchar *message_uid,
4297                                        GCancellable *cancellable,
4298                                        GError **error)
4299 {
4300         CamelFolderClass *class;
4301         gboolean success = FALSE;
4302
4303         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4304         g_return_val_if_fail (message_uid != NULL, FALSE);
4305
4306         class = CAMEL_FOLDER_GET_CLASS (folder);
4307         g_return_val_if_fail (class->get_message_sync != NULL, FALSE);
4308
4309         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
4310
4311         /* Check for cancellation after locking. */
4312         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
4313                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4314                 return FALSE;
4315         }
4316
4317         /* Use the sync_message method if the class implements it. */
4318         if (class->synchronize_message_sync != NULL) {
4319                 success = class->synchronize_message_sync (
4320                         folder, message_uid, cancellable, error);
4321                 CAMEL_CHECK_GERROR (
4322                         folder, synchronize_message_sync, success, error);
4323         } else {
4324                 CamelMimeMessage *message;
4325
4326                 message = class->get_message_sync (
4327                         folder, message_uid, cancellable, error);
4328                 CAMEL_CHECK_GERROR (
4329                         folder, get_message_sync, message != NULL, error);
4330
4331                 if (message != NULL) {
4332                         g_object_unref (message);
4333                         success = TRUE;
4334                 }
4335         }
4336
4337         camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
4338
4339         return success;
4340 }
4341
4342 /**
4343  * camel_folder_synchronize_message;
4344  * @folder: a #CamelFolder
4345  * @message_uid: a message UID
4346  * @io_priority: the I/O priority of the request
4347  * @cancellable: optional #GCancellable object, or %NULL
4348  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4349  * @user_data: data to pass to the callback function
4350  *
4351  * Asynchronously ensure that a message identified by @message_uid has been
4352  * synchronized in @folder so that calling camel_folder_get_message() on it
4353  * later will work in offline mode.
4354  *
4355  * When the operation is finished, @callback will be called.  You can then
4356  * call camel_folder_synchronize_message_finish() to get the result of the
4357  * operation.
4358  *
4359  * Since: 3.0
4360  **/
4361 void
4362 camel_folder_synchronize_message (CamelFolder *folder,
4363                                   const gchar *message_uid,
4364                                   gint io_priority,
4365                                   GCancellable *cancellable,
4366                                   GAsyncReadyCallback callback,
4367                                   gpointer user_data)
4368 {
4369         CamelFolderClass *class;
4370
4371         g_return_if_fail (CAMEL_IS_FOLDER (folder));
4372         g_return_if_fail (message_uid != NULL);
4373
4374         class = CAMEL_FOLDER_GET_CLASS (folder);
4375         g_return_if_fail (class->synchronize_message != NULL);
4376
4377         class->synchronize_message (
4378                 folder, message_uid, io_priority,
4379                 cancellable, callback, user_data);
4380 }
4381
4382 /**
4383  * camel_folder_synchronize_message_finish:
4384  * @folder: a #CamelFolder
4385  * @result: a #GAsyncResult
4386  * @error: return location for a #GError, or %NULL
4387  *
4388  * Finishes the operation started with camel_folder_synchronize_message().
4389  *
4390  * Returns: %TRUE on success, %FALSE on error
4391  *
4392  * Since: 3.0
4393  **/
4394 gboolean
4395 camel_folder_synchronize_message_finish (CamelFolder *folder,
4396                                          GAsyncResult *result,
4397                                          GError **error)
4398 {
4399         CamelFolderClass *class;
4400
4401         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
4402         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
4403
4404         class = CAMEL_FOLDER_GET_CLASS (folder);
4405         g_return_val_if_fail (class->synchronize_message_finish != NULL, FALSE);
4406
4407         return class->synchronize_message_finish (folder, result, error);
4408 }
4409
4410 /**
4411  * camel_folder_transfer_messages_to_sync:
4412  * @source: the source #CamelFolder
4413  * @message_uids: message UIDs in @source
4414  * @destination: the destination #CamelFolder
4415  * @delete_originals: whether or not to delete the original messages
4416  * @transferred_uids: if non-%NULL, the UIDs of the resulting messages
4417  *                    in @destination will be stored here, if known.
4418  * @cancellable: optional #GCancellable object, or %NULL
4419  * @error: return location for a #GError, or %NULL
4420  *
4421  * Copies or moves messages from one folder to another.  If the
4422  * @source and @destination folders have the same parent_store, this
4423  * may be more efficient than using camel_folder_append_message_sync().
4424  *
4425  * Returns: %TRUE on success, %FALSE on failure
4426  *
4427  * Since: 3.0
4428  **/
4429 gboolean
4430 camel_folder_transfer_messages_to_sync (CamelFolder *source,
4431                                         GPtrArray *message_uids,
4432                                         CamelFolder *destination,
4433                                         gboolean delete_originals,
4434                                         GPtrArray **transferred_uids,
4435                                         GCancellable *cancellable,
4436                                         GError **error)
4437 {
4438         CamelFolderClass *class;
4439         gboolean success;
4440
4441         g_return_val_if_fail (CAMEL_IS_FOLDER (source), FALSE);
4442         g_return_val_if_fail (CAMEL_IS_FOLDER (destination), FALSE);
4443         g_return_val_if_fail (message_uids != NULL, FALSE);
4444
4445         if (source == destination || message_uids->len == 0)
4446                 return TRUE;
4447
4448         if (source->priv->parent_store == destination->priv->parent_store) {
4449                 /* If either folder is a vtrash, we need to use the
4450                  * vtrash transfer method. */
4451                 if (CAMEL_IS_VTRASH_FOLDER (destination))
4452                         class = CAMEL_FOLDER_GET_CLASS (destination);
4453                 else
4454                         class = CAMEL_FOLDER_GET_CLASS (source);
4455                 success = class->transfer_messages_to_sync (
4456                         source, message_uids, destination, delete_originals,
4457                         transferred_uids, cancellable, error);
4458         } else
4459                 success = folder_transfer_messages_to_sync (
4460                         source, message_uids, destination, delete_originals,
4461                         transferred_uids, cancellable, error);
4462
4463         return success;
4464 }
4465
4466 /**
4467  * camel_folder_transfer_messages_to:
4468  * @source: the source #CamelFolder
4469  * @message_uids: message UIDs in @source
4470  * @destination: the destination #CamelFolder
4471  * @delete_originals: whether or not to delete the original messages
4472  * @io_priority: the I/O priority of the request
4473  * @cancellable: optional #GCancellable object, or %NULL
4474  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4475  * @user_data: data to pass to the callback function
4476  *
4477  * Asynchronously copies or moves messages from one folder to another.
4478  * If the @source or @destination folders have the same parent store,
4479  * this may be more efficient than using camel_folder_append_message().
4480  *
4481  * When the operation is finished, @callback will be called.  You can then
4482  * call camel_folder_transfer_messages_to_finish() to get the result of the
4483  * operation.
4484  *
4485  * Since: 3.0
4486  **/
4487 void
4488 camel_folder_transfer_messages_to (CamelFolder *source,
4489                                    GPtrArray *message_uids,
4490                                    CamelFolder *destination,
4491                                    gboolean delete_originals,
4492                                    gint io_priority,
4493                                    GCancellable *cancellable,
4494                                    GAsyncReadyCallback callback,
4495                                    gpointer user_data)
4496 {
4497         CamelFolderClass *class;
4498
4499         g_return_if_fail (CAMEL_IS_FOLDER (source));
4500         g_return_if_fail (CAMEL_IS_FOLDER (destination));
4501         g_return_if_fail (message_uids != NULL);
4502
4503         class = CAMEL_FOLDER_GET_CLASS (source);
4504         g_return_if_fail (class->transfer_messages_to != NULL);
4505
4506         class->transfer_messages_to (
4507                 source, message_uids, destination, delete_originals,
4508                 io_priority, cancellable, callback, user_data);
4509 }
4510
4511 /**
4512  * camel_folder_transfer_messages_to_finish:
4513  * @source: a #CamelFolder
4514  * @result: a #GAsyncResult
4515  * @transferred_uids: if non-%NULL, the UIDs of the resulting messages
4516  *                    in @destination will be stored here, if known.
4517  * @error: return location for a #GError, or %NULL
4518  *
4519  * Finishes the operation started with camel_folder_transfer_messages_to().
4520  *
4521  * Returns: %TRUE on success, %FALSE on error
4522  *
4523  * Since: 3.0
4524  **/
4525 gboolean
4526 camel_folder_transfer_messages_to_finish (CamelFolder *source,
4527                                           GAsyncResult *result,
4528                                           GPtrArray **transferred_uids,
4529                                           GError **error)
4530 {
4531         CamelFolderClass *class;
4532
4533         g_return_val_if_fail (CAMEL_IS_FOLDER (source), FALSE);
4534         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
4535
4536         class = CAMEL_FOLDER_GET_CLASS (source);
4537         g_return_val_if_fail (class->transfer_messages_to_finish != NULL, FALSE);
4538
4539         return class->transfer_messages_to_finish (
4540                 source, result, transferred_uids, error);
4541 }
4542
4543 /**
4544  * camel_folder_change_info_new:
4545  *
4546  * Create a new folder change info structure.
4547  *
4548  * Change info structures are not MT-SAFE and must be
4549  * locked for exclusive access externally.
4550  *
4551  * Returns: a new #CamelFolderChangeInfo
4552  **/
4553 CamelFolderChangeInfo *
4554 camel_folder_change_info_new (void)
4555 {
4556         CamelFolderChangeInfo *info;
4557
4558         info = g_slice_new (CamelFolderChangeInfo);
4559         info->uid_added = g_ptr_array_new ();
4560         info->uid_removed = g_ptr_array_new ();
4561         info->uid_changed = g_ptr_array_new ();
4562         info->uid_recent = g_ptr_array_new ();
4563         info->priv = g_slice_new (struct _CamelFolderChangeInfoPrivate);
4564         info->priv->uid_stored = g_hash_table_new (g_str_hash, g_str_equal);
4565         info->priv->uid_source = NULL;
4566         info->priv->uid_filter = g_ptr_array_new ();
4567         info->priv->uid_pool = camel_mempool_new (512, 256, CAMEL_MEMPOOL_ALIGN_BYTE);
4568
4569         return info;
4570 }
4571
4572 /**
4573  * camel_folder_change_info_add_source:
4574  * @info: a #CamelFolderChangeInfo
4575  * @uid: a uid
4576  *
4577  * Add a source uid for generating a changeset.
4578  **/
4579 void
4580 camel_folder_change_info_add_source (CamelFolderChangeInfo *info,
4581                                      const gchar *uid)
4582 {
4583         struct _CamelFolderChangeInfoPrivate *p;
4584
4585         g_return_if_fail (info != NULL);
4586         g_return_if_fail (uid != NULL);
4587
4588         p = info->priv;
4589
4590         if (p->uid_source == NULL)
4591                 p->uid_source = g_hash_table_new (g_str_hash, g_str_equal);
4592
4593         if (g_hash_table_lookup (p->uid_source, uid) == NULL)
4594                 g_hash_table_insert (p->uid_source, camel_mempool_strdup (p->uid_pool, uid), GINT_TO_POINTER (1));
4595 }
4596
4597 /**
4598  * camel_folder_change_info_add_source_list:
4599  * @info: a #CamelFolderChangeInfo
4600  * @list: a list of uids
4601  *
4602  * Add a list of source uid's for generating a changeset.
4603  **/
4604 void
4605 camel_folder_change_info_add_source_list (CamelFolderChangeInfo *info,
4606                                           const GPtrArray *list)
4607 {
4608         struct _CamelFolderChangeInfoPrivate *p;
4609         gint i;
4610
4611         g_return_if_fail (info != NULL);
4612         g_return_if_fail (list != NULL);
4613
4614         p = info->priv;
4615
4616         if (p->uid_source == NULL)
4617                 p->uid_source = g_hash_table_new (g_str_hash, g_str_equal);
4618
4619         for (i = 0; i < list->len; i++) {
4620                 gchar *uid = list->pdata[i];
4621
4622                 if (g_hash_table_lookup (p->uid_source, uid) == NULL)
4623                         g_hash_table_insert (p->uid_source, camel_mempool_strdup (p->uid_pool, uid), GINT_TO_POINTER (1));
4624         }
4625 }
4626
4627 /**
4628  * camel_folder_change_info_add_update:
4629  * @info: a #CamelFolderChangeInfo
4630  * @uid: a uid
4631  *
4632  * Add a uid from the updated list, used to generate a changeset diff.
4633  **/
4634 void
4635 camel_folder_change_info_add_update (CamelFolderChangeInfo *info,
4636                                      const gchar *uid)
4637 {
4638         struct _CamelFolderChangeInfoPrivate *p;
4639         gchar *key;
4640         gint value;
4641
4642         g_return_if_fail (info != NULL);
4643         g_return_if_fail (uid != NULL);
4644
4645         p = info->priv;
4646
4647         if (p->uid_source == NULL) {
4648                 camel_folder_change_info_add_uid (info, uid);
4649                 return;
4650         }
4651
4652         if (g_hash_table_lookup_extended (p->uid_source, uid, (gpointer) &key, (gpointer) &value)) {
4653                 g_hash_table_remove (p->uid_source, key);
4654         } else {
4655                 camel_folder_change_info_add_uid (info, uid);
4656         }
4657 }
4658
4659 /**
4660  * camel_folder_change_info_add_update_list:
4661  * @info: a #CamelFolderChangeInfo
4662  * @list: a list of uids
4663  *
4664  * Add a list of uid's from the updated list.
4665  **/
4666 void
4667 camel_folder_change_info_add_update_list (CamelFolderChangeInfo *info,
4668                                           const GPtrArray *list)
4669 {
4670         gint i;
4671
4672         g_return_if_fail (info != NULL);
4673         g_return_if_fail (list != NULL);
4674
4675         for (i = 0; i < list->len; i++)
4676                 camel_folder_change_info_add_update (info, list->pdata[i]);
4677 }
4678
4679 static void
4680 change_info_remove (gchar *key,
4681                     gpointer value,
4682                     CamelFolderChangeInfo *info)
4683 {
4684         struct _CamelFolderChangeInfoPrivate *p = info->priv;
4685         GPtrArray *olduids;
4686         gchar *olduid;
4687
4688         if (g_hash_table_lookup_extended (p->uid_stored, key, (gpointer) &olduid, (gpointer) &olduids)) {
4689                 /* if it was added/changed them removed, then remove it */
4690                 if (olduids != info->uid_removed) {
4691                         g_ptr_array_remove_fast (olduids, olduid);
4692                         g_ptr_array_add (info->uid_removed, olduid);
4693                         g_hash_table_insert (p->uid_stored, olduid, info->uid_removed);
4694                 }
4695                 return;
4696         }
4697
4698         /* we dont need to copy this, as they've already been copied into our pool */
4699         g_ptr_array_add (info->uid_removed, key);
4700         g_hash_table_insert (p->uid_stored, key, info->uid_removed);
4701 }
4702
4703 /**
4704  * camel_folder_change_info_build_diff:
4705  * @info: a #CamelFolderChangeInfo
4706  *
4707  * Compare the source uid set to the updated uid set and generate the
4708  * differences into the added and removed lists.
4709  **/
4710 void
4711 camel_folder_change_info_build_diff (CamelFolderChangeInfo *info)
4712 {
4713         struct _CamelFolderChangeInfoPrivate *p;
4714
4715         g_return_if_fail (info != NULL);
4716
4717         p = info->priv;
4718
4719         if (p->uid_source) {
4720                 g_hash_table_foreach (p->uid_source, (GHFunc) change_info_remove, info);
4721                 g_hash_table_destroy (p->uid_source);
4722                 p->uid_source = NULL;
4723         }
4724 }
4725
4726 static void
4727 change_info_recent_uid (CamelFolderChangeInfo *info,
4728                         const gchar *uid)
4729 {
4730         struct _CamelFolderChangeInfoPrivate *p;
4731         GPtrArray *olduids;
4732         gchar *olduid;
4733
4734         p = info->priv;
4735
4736         /* always add to recent, but dont let anyone else know */
4737         if (!g_hash_table_lookup_extended (p->uid_stored, uid, (gpointer *) &olduid, (gpointer *) &olduids)) {
4738                 olduid = camel_mempool_strdup (p->uid_pool, uid);
4739         }
4740         g_ptr_array_add (info->uid_recent, olduid);
4741 }
4742
4743 static void
4744 change_info_filter_uid (CamelFolderChangeInfo *info,
4745                         const gchar *uid)
4746 {
4747         struct _CamelFolderChangeInfoPrivate *p;
4748         GPtrArray *olduids;
4749         gchar *olduid;
4750
4751         p = info->priv;
4752
4753         /* always add to filter, but dont let anyone else know */
4754         if (!g_hash_table_lookup_extended (p->uid_stored, uid, (gpointer *) &olduid, (gpointer *) &olduids)) {
4755                 olduid = camel_mempool_strdup (p->uid_pool, uid);
4756         }
4757         g_ptr_array_add (p->uid_filter, olduid);
4758 }
4759
4760 static void
4761 change_info_cat (CamelFolderChangeInfo *info,
4762                  GPtrArray *source,
4763                  void (*add)(CamelFolderChangeInfo *info,
4764                  const gchar *uid))
4765 {
4766         gint i;
4767
4768         for (i = 0; i < source->len; i++)
4769                 add (info, source->pdata[i]);
4770 }
4771
4772 /**
4773  * camel_folder_change_info_cat:
4774  * @info: a #CamelFolderChangeInfo to append to
4775  * @src: a #CamelFolderChangeInfo to append from
4776  *
4777  * Concatenate one change info onto antoher.  Can be used to copy them
4778  * too.
4779  **/
4780 void
4781 camel_folder_change_info_cat (CamelFolderChangeInfo *info,
4782                               CamelFolderChangeInfo *source)
4783 {
4784         g_return_if_fail (info != NULL);
4785         g_return_if_fail (source != NULL);
4786
4787         change_info_cat (info, source->uid_added, camel_folder_change_info_add_uid);
4788         change_info_cat (info, source->uid_removed, camel_folder_change_info_remove_uid);
4789         change_info_cat (info, source->uid_changed, camel_folder_change_info_change_uid);
4790         change_info_cat (info, source->uid_recent, change_info_recent_uid);
4791         change_info_cat (info, source->priv->uid_filter, change_info_filter_uid);
4792 }
4793
4794 /**
4795  * camel_folder_change_info_add_uid:
4796  * @info: a #CamelFolderChangeInfo
4797  * @uid: a uid
4798  *
4799  * Add a new uid to the changeinfo.
4800  **/
4801 void
4802 camel_folder_change_info_add_uid (CamelFolderChangeInfo *info,
4803                                   const gchar *uid)
4804 {
4805         struct _CamelFolderChangeInfoPrivate *p;
4806         GPtrArray *olduids;
4807         gchar *olduid;
4808
4809         g_return_if_fail (info != NULL);
4810         g_return_if_fail (uid != NULL);
4811
4812         p = info->priv;
4813
4814         if (g_hash_table_lookup_extended (p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
4815                 /* if it was removed then added, promote it to a changed */
4816                 /* if it was changed then added, leave as changed */
4817                 if (olduids == info->uid_removed) {
4818                         g_ptr_array_remove_fast (olduids, olduid);
4819                         g_ptr_array_add (info->uid_changed, olduid);
4820                         g_hash_table_insert (p->uid_stored, olduid, info->uid_changed);
4821                 }
4822                 return;
4823         }
4824
4825         olduid = camel_mempool_strdup (p->uid_pool, uid);
4826         g_ptr_array_add (info->uid_added, olduid);
4827         g_hash_table_insert (p->uid_stored, olduid, info->uid_added);
4828 }
4829
4830 /**
4831  * camel_folder_change_info_remove_uid:
4832  * @info: a #CamelFolderChangeInfo
4833  * @uid: a uid
4834  *
4835  * Add a uid to the removed uid list.
4836  **/
4837 void
4838 camel_folder_change_info_remove_uid (CamelFolderChangeInfo *info,
4839                                      const gchar *uid)
4840 {
4841         struct _CamelFolderChangeInfoPrivate *p;
4842         GPtrArray *olduids;
4843         gchar *olduid;
4844
4845         g_return_if_fail (info != NULL);
4846         g_return_if_fail (uid != NULL);
4847
4848         p = info->priv;
4849
4850         if (g_hash_table_lookup_extended (p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
4851                 /* if it was added/changed them removed, then remove it */
4852                 if (olduids != info->uid_removed) {
4853                         g_ptr_array_remove_fast (olduids, olduid);
4854                         g_ptr_array_add (info->uid_removed, olduid);
4855                         g_hash_table_insert (p->uid_stored, olduid, info->uid_removed);
4856                 }
4857                 return;
4858         }
4859
4860         olduid = camel_mempool_strdup (p->uid_pool, uid);
4861         g_ptr_array_add (info->uid_removed, olduid);
4862         g_hash_table_insert (p->uid_stored, olduid, info->uid_removed);
4863 }
4864
4865 /**
4866  * camel_folder_change_info_change_uid:
4867  * @info: a #CamelFolderChangeInfo
4868  * @uid: a uid
4869  *
4870  * Add a uid to the changed uid list.
4871  **/
4872 void
4873 camel_folder_change_info_change_uid (CamelFolderChangeInfo *info,
4874                                      const gchar *uid)
4875 {
4876         struct _CamelFolderChangeInfoPrivate *p;
4877         GPtrArray *olduids;
4878         gchar *olduid;
4879
4880         g_return_if_fail (info != NULL);
4881         g_return_if_fail (uid != NULL);
4882
4883         p = info->priv;
4884
4885         if (g_hash_table_lookup_extended (p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
4886                 /* if we have it already, leave it as that */
4887                 return;
4888         }
4889
4890         olduid = camel_mempool_strdup (p->uid_pool, uid);
4891         g_ptr_array_add (info->uid_changed, olduid);
4892         g_hash_table_insert (p->uid_stored, olduid, info->uid_changed);
4893 }
4894
4895 /**
4896  * camel_folder_change_info_recent_uid:
4897  * @info: a #CamelFolderChangeInfo
4898  * @uid: a uid
4899  *
4900  * Add a recent uid to the changedinfo.
4901  * This will also add the uid to the uid_filter array for potential
4902  * filtering
4903  **/
4904 void
4905 camel_folder_change_info_recent_uid (CamelFolderChangeInfo *info,
4906                                      const gchar *uid)
4907 {
4908         g_return_if_fail (info != NULL);
4909         g_return_if_fail (uid != NULL);
4910
4911         change_info_recent_uid (info, uid);
4912         change_info_filter_uid (info, uid);
4913 }
4914
4915 /**
4916  * camel_folder_change_info_changed:
4917  * @info: a #CamelFolderChangeInfo
4918  *
4919  * Gets whether or not there have been any changes.
4920  *
4921  * Returns: %TRUE if the changeset contains any changes or %FALSE
4922  * otherwise
4923  **/
4924 gboolean
4925 camel_folder_change_info_changed (CamelFolderChangeInfo *info)
4926 {
4927         g_return_val_if_fail (info != NULL, FALSE);
4928
4929         return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len || info->uid_recent->len);
4930 }
4931
4932 /**
4933  * camel_folder_change_info_clear:
4934  * @info: a #CamelFolderChangeInfo
4935  *
4936  * Empty out the change info; called after changes have been
4937  * processed.
4938  **/
4939 void
4940 camel_folder_change_info_clear (CamelFolderChangeInfo *info)
4941 {
4942         struct _CamelFolderChangeInfoPrivate *p;
4943
4944         g_return_if_fail (info != NULL);
4945
4946         p = info->priv;
4947
4948         g_ptr_array_set_size (info->uid_added, 0);
4949         g_ptr_array_set_size (info->uid_removed, 0);
4950         g_ptr_array_set_size (info->uid_changed, 0);
4951         g_ptr_array_set_size (info->uid_recent, 0);
4952         if (p->uid_source) {
4953                 g_hash_table_destroy (p->uid_source);
4954                 p->uid_source = NULL;
4955         }
4956         g_hash_table_destroy (p->uid_stored);
4957         p->uid_stored = g_hash_table_new (g_str_hash, g_str_equal);
4958         g_ptr_array_set_size (p->uid_filter, 0);
4959         camel_mempool_flush (p->uid_pool, TRUE);
4960 }
4961
4962 /**
4963  * camel_folder_change_info_free:
4964  * @info: a #CamelFolderChangeInfo
4965  *
4966  * Free memory associated with the folder change info lists.
4967  **/
4968 void
4969 camel_folder_change_info_free (CamelFolderChangeInfo *info)
4970 {
4971         struct _CamelFolderChangeInfoPrivate *p;
4972
4973         g_return_if_fail (info != NULL);
4974
4975         p = info->priv;
4976
4977         if (p->uid_source)
4978                 g_hash_table_destroy (p->uid_source);
4979
4980         g_hash_table_destroy (p->uid_stored);
4981         g_ptr_array_free (p->uid_filter, TRUE);
4982         camel_mempool_destroy (p->uid_pool);
4983         g_slice_free (struct _CamelFolderChangeInfoPrivate, p);
4984
4985         g_ptr_array_free (info->uid_added, TRUE);
4986         g_ptr_array_free (info->uid_removed, TRUE);
4987         g_ptr_array_free (info->uid_changed, TRUE);
4988         g_ptr_array_free (info->uid_recent, TRUE);
4989         g_slice_free (CamelFolderChangeInfo, info);
4990 }