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