Make use of G_DEFINE_QUARK()
[platform/upstream/evolution-data-server.git] / camel / camel-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-store.c : Abstract class for an email store */
3
4 /*
5  * Authors:
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *  Dan Winship <danw@ximian.com>
8  *
9  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
23  * USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include <glib/gi18n-lib.h>
36
37 #include "camel-db.h"
38 #include "camel-debug.h"
39 #include "camel-folder.h"
40 #include "camel-marshal.h"
41 #include "camel-session.h"
42 #include "camel-store.h"
43 #include "camel-store-settings.h"
44 #include "camel-subscribable.h"
45 #include "camel-vtrash-folder.h"
46
47 #define d(x)
48 #define w(x)
49
50 #define CAMEL_STORE_GET_PRIVATE(obj) \
51         (G_TYPE_INSTANCE_GET_PRIVATE \
52         ((obj), CAMEL_TYPE_STORE, CamelStorePrivate))
53
54 typedef struct _AsyncContext AsyncContext;
55 typedef struct _SignalData SignalData;
56
57 struct _CamelStorePrivate {
58         GRecMutex folder_lock;  /* for locking folder operations */
59 };
60
61 struct _AsyncContext {
62         /* arguments */
63         gchar *folder_name_1;
64         gchar *folder_name_2;
65         gboolean expunge;
66         guint32 flags;
67
68         /* results */
69         CamelFolder *folder;
70         CamelFolderInfo *folder_info;
71 };
72
73 struct _SignalData {
74         CamelStore *store;
75         CamelFolder *folder;
76         CamelFolderInfo *folder_info;
77         gchar *folder_name;
78 };
79
80 enum {
81         FOLDER_CREATED,
82         FOLDER_DELETED,
83         FOLDER_OPENED,
84         FOLDER_RENAMED,
85         LAST_SIGNAL
86 };
87
88 static guint signals[LAST_SIGNAL];
89 static GInitableIface *parent_initable_interface;
90
91 /* Forward Declarations */
92 static void camel_store_initable_init (GInitableIface *interface);
93
94 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
95         CamelStore, camel_store, CAMEL_TYPE_SERVICE,
96         G_IMPLEMENT_INTERFACE (
97                 G_TYPE_INITABLE, camel_store_initable_init))
98
99 static void
100 async_context_free (AsyncContext *async_context)
101 {
102         g_free (async_context->folder_name_1);
103         g_free (async_context->folder_name_2);
104
105         if (async_context->folder != NULL)
106                 g_object_unref (async_context->folder);
107
108         camel_folder_info_free (async_context->folder_info);
109
110         g_slice_free (AsyncContext, async_context);
111 }
112
113 static void
114 signal_data_free (SignalData *signal_data)
115 {
116         if (signal_data->store != NULL)
117                 g_object_unref (signal_data->store);
118
119         if (signal_data->folder != NULL)
120                 g_object_unref (signal_data->folder);
121
122         if (signal_data->folder_info != NULL)
123                 camel_folder_info_free (signal_data->folder_info);
124
125         g_free (signal_data->folder_name);
126
127         g_slice_free (SignalData, signal_data);
128 }
129
130 static gboolean
131 store_emit_folder_created_cb (gpointer user_data)
132 {
133         SignalData *signal_data = user_data;
134
135         g_signal_emit (
136                 signal_data->store,
137                 signals[FOLDER_CREATED], 0,
138                 signal_data->folder_info);
139
140         return FALSE;
141 }
142
143 static gboolean
144 store_emit_folder_deleted_cb (gpointer user_data)
145 {
146         SignalData *signal_data = user_data;
147
148         g_signal_emit (
149                 signal_data->store,
150                 signals[FOLDER_DELETED], 0,
151                 signal_data->folder_info);
152
153         return FALSE;
154 }
155
156 static gboolean
157 store_emit_folder_opened_cb (gpointer user_data)
158 {
159         SignalData *signal_data = user_data;
160
161         g_signal_emit (
162                 signal_data->store,
163                 signals[FOLDER_OPENED], 0,
164                 signal_data->folder);
165
166         return FALSE;
167 }
168
169 static gboolean
170 store_emit_folder_renamed_cb (gpointer user_data)
171 {
172         SignalData *signal_data = user_data;
173
174         g_signal_emit (
175                 signal_data->store,
176                 signals[FOLDER_RENAMED], 0,
177                 signal_data->folder_name,
178                 signal_data->folder_info);
179
180         return FALSE;
181 }
182
183 /**
184  * ignore_no_such_table_exception:
185  * Clears the exception 'ex' when it's the 'no such table' exception.
186  **/
187 static void
188 ignore_no_such_table_exception (GError **error)
189 {
190         if (error == NULL || *error == NULL)
191                 return;
192
193         if (g_ascii_strncasecmp ((*error)->message, "no such table", 13) == 0)
194                 g_clear_error (error);
195 }
196
197 /* deletes folder/removes it from the folder cache, if it's there */
198 static void
199 cs_delete_cached_folder (CamelStore *store,
200                          const gchar *folder_name)
201 {
202         CamelFolder *folder;
203         CamelVeeFolder *vfolder;
204
205         if (store->folders == NULL)
206                 return;
207
208         folder = camel_object_bag_get (store->folders, folder_name);
209         if (folder == NULL)
210                 return;
211
212         if (store->flags & CAMEL_STORE_VTRASH) {
213                 vfolder = camel_object_bag_get (
214                         store->folders, CAMEL_VTRASH_NAME);
215                 if (vfolder != NULL) {
216                         camel_vee_folder_remove_folder (vfolder, folder, NULL);
217                         g_object_unref (vfolder);
218                 }
219         }
220
221         if (store->flags & CAMEL_STORE_VJUNK) {
222                 vfolder = camel_object_bag_get (
223                         store->folders, CAMEL_VJUNK_NAME);
224                 if (vfolder != NULL) {
225                         camel_vee_folder_remove_folder (vfolder, folder, NULL);
226                         g_object_unref (vfolder);
227                 }
228         }
229
230         camel_folder_delete (folder);
231
232         camel_object_bag_remove (store->folders, folder);
233         g_object_unref (folder);
234 }
235
236 static CamelFolder *
237 store_get_special (CamelStore *store,
238                    camel_vtrash_folder_t type)
239 {
240         CamelFolder *folder;
241         GPtrArray *folders;
242         gint i;
243
244         folder = camel_vtrash_folder_new (store, type);
245         folders = camel_object_bag_list (store->folders);
246         for (i = 0; i < folders->len; i++) {
247                 if (!CAMEL_IS_VTRASH_FOLDER (folders->pdata[i]))
248                         camel_vee_folder_add_folder ((CamelVeeFolder *) folder, (CamelFolder *) folders->pdata[i], NULL);
249                 g_object_unref (folders->pdata[i]);
250         }
251         g_ptr_array_free (folders, TRUE);
252
253         return folder;
254 }
255
256 static void
257 store_finalize (GObject *object)
258 {
259         CamelStore *store = CAMEL_STORE (object);
260
261         if (store->folders != NULL)
262                 camel_object_bag_destroy (store->folders);
263
264         g_rec_mutex_clear (&store->priv->folder_lock);
265
266         if (store->cdb_r != NULL) {
267                 camel_db_close (store->cdb_r);
268                 store->cdb_r = NULL;
269                 store->cdb_w = NULL;
270         }
271
272         /* Chain up to parent's finalize() method. */
273         G_OBJECT_CLASS (camel_store_parent_class)->finalize (object);
274 }
275
276 static void
277 store_constructed (GObject *object)
278 {
279         CamelStore *store;
280         CamelStoreClass *class;
281
282         /* Chain up to parent's constructed() method. */
283         G_OBJECT_CLASS (camel_store_parent_class)->constructed (object);
284
285         store = CAMEL_STORE (object);
286         class = CAMEL_STORE_GET_CLASS (store);
287
288         g_return_if_fail (class->hash_folder_name != NULL);
289         g_return_if_fail (class->equal_folder_name != NULL);
290
291         store->folders = camel_object_bag_new (
292                 class->hash_folder_name,
293                 class->equal_folder_name,
294                 (CamelCopyFunc) g_strdup, g_free);
295 }
296
297 static gboolean
298 store_can_refresh_folder (CamelStore *store,
299                           CamelFolderInfo *info,
300                           GError **error)
301 {
302         return ((info->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX);
303 }
304
305 static CamelFolder *
306 store_get_inbox_folder_sync (CamelStore *store,
307                              GCancellable *cancellable,
308                              GError **error)
309 {
310         CamelStoreClass *class;
311         CamelFolder *folder;
312
313         class = CAMEL_STORE_GET_CLASS (store);
314         g_return_val_if_fail (class->get_folder_sync != NULL, NULL);
315
316         /* Assume the inbox's name is "inbox" and open with default flags. */
317         folder = class->get_folder_sync (store, "inbox", 0, cancellable, error);
318         CAMEL_CHECK_GERROR (store, get_folder_sync, folder != NULL, error);
319
320         return folder;
321 }
322
323 static CamelFolder *
324 store_get_junk_folder_sync (CamelStore *store,
325                             GCancellable *cancellable,
326                             GError **error)
327 {
328         return store_get_special (store, CAMEL_VTRASH_FOLDER_JUNK);
329 }
330
331 static CamelFolder *
332 store_get_trash_folder_sync (CamelStore *store,
333                              GCancellable *cancellable,
334                              GError **error)
335 {
336         return store_get_special (store, CAMEL_VTRASH_FOLDER_TRASH);
337 }
338
339 static gboolean
340 store_synchronize_sync (CamelStore *store,
341                         gboolean expunge,
342                         GCancellable *cancellable,
343                         GError **error)
344 {
345         GPtrArray *folders;
346         CamelFolder *folder;
347         gboolean success = TRUE;
348         gint i;
349         GError *local_error = NULL;
350
351         if (expunge) {
352                 /* ensure all folders are used when expunging */
353                 CamelFolderInfo *root, *fi;
354
355                 folders = g_ptr_array_new ();
356                 root = camel_store_get_folder_info_sync (store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, NULL, NULL);
357                 fi = root;
358                 while (fi) {
359                         CamelFolderInfo *next;
360
361                         if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0) {
362                                 CamelFolder *fldr;
363
364                                 fldr = camel_store_get_folder_sync (store, fi->full_name, 0, NULL, NULL);
365                                 if (fldr)
366                                         g_ptr_array_add (folders, fldr);
367                         }
368
369                         /* pick the next */
370                         next = fi->child;
371                         if (!next)
372                                 next = fi->next;
373                         if (!next) {
374                                 next = fi->parent;
375                                 while (next) {
376                                         if (next->next) {
377                                                 next = next->next;
378                                                 break;
379                                         }
380
381                                         next = next->parent;
382                                 }
383                         }
384
385                         fi = next;
386                 }
387
388                 if (root)
389                         camel_store_free_folder_info_full (store, root);
390         } else {
391                 /* sync only folders opened until now */
392                 folders = camel_object_bag_list (store->folders);
393         }
394
395         /* We don't sync any vFolders, that is used to update certain
396          * vfolder queries mainly, and we're really only interested in
397          * storing/expunging the physical mails. */
398         for (i = 0; i < folders->len; i++) {
399                 folder = folders->pdata[i];
400                 if (!CAMEL_IS_VEE_FOLDER (folder)
401                     && local_error == NULL) {
402                         camel_folder_synchronize_sync (
403                                 folder, expunge, cancellable, &local_error);
404                         ignore_no_such_table_exception (&local_error);
405                 }
406                 g_object_unref (folder);
407         }
408
409         if (local_error != NULL) {
410                 g_propagate_error (error, local_error);
411                 success = FALSE;
412         }
413
414         g_ptr_array_free (folders, TRUE);
415
416         return success;
417 }
418
419 static gboolean
420 store_noop_sync (CamelStore *store,
421                  GCancellable *cancellable,
422                  GError **error)
423 {
424         return TRUE;
425 }
426
427 static void
428 store_get_folder_thread (GSimpleAsyncResult *simple,
429                          GObject *object,
430                          GCancellable *cancellable)
431 {
432         AsyncContext *async_context;
433         GError *error = NULL;
434
435         async_context = g_simple_async_result_get_op_res_gpointer (simple);
436
437         async_context->folder = camel_store_get_folder_sync (
438                 CAMEL_STORE (object), async_context->folder_name_1,
439                 async_context->flags, cancellable, &error);
440
441         if (error != NULL)
442                 g_simple_async_result_take_error (simple, error);
443 }
444
445 static void
446 store_get_folder (CamelStore *store,
447                   const gchar *folder_name,
448                   CamelStoreGetFolderFlags flags,
449                   gint io_priority,
450                   GCancellable *cancellable,
451                   GAsyncReadyCallback callback,
452                   gpointer user_data)
453 {
454         GSimpleAsyncResult *simple;
455         AsyncContext *async_context;
456
457         async_context = g_slice_new0 (AsyncContext);
458         async_context->folder_name_1 = g_strdup (folder_name);
459         async_context->flags = flags;
460
461         simple = g_simple_async_result_new (
462                 G_OBJECT (store), callback, user_data, store_get_folder);
463
464         g_simple_async_result_set_check_cancellable (simple, cancellable);
465
466         g_simple_async_result_set_op_res_gpointer (
467                 simple, async_context, (GDestroyNotify) async_context_free);
468
469         g_simple_async_result_run_in_thread (
470                 simple, store_get_folder_thread, io_priority, cancellable);
471
472         g_object_unref (simple);
473 }
474
475 static CamelFolder *
476 store_get_folder_finish (CamelStore *store,
477                          GAsyncResult *result,
478                          GError **error)
479 {
480         GSimpleAsyncResult *simple;
481         AsyncContext *async_context;
482
483         g_return_val_if_fail (
484                 g_simple_async_result_is_valid (
485                 result, G_OBJECT (store), store_get_folder), NULL);
486
487         simple = G_SIMPLE_ASYNC_RESULT (result);
488         async_context = g_simple_async_result_get_op_res_gpointer (simple);
489
490         if (g_simple_async_result_propagate_error (simple, error))
491                 return NULL;
492
493         return g_object_ref (async_context->folder);
494 }
495
496 static void
497 store_get_folder_info_thread (GSimpleAsyncResult *simple,
498                               GObject *object,
499                               GCancellable *cancellable)
500 {
501         AsyncContext *async_context;
502         GError *error = NULL;
503
504         async_context = g_simple_async_result_get_op_res_gpointer (simple);
505
506         async_context->folder_info = camel_store_get_folder_info_sync (
507                 CAMEL_STORE (object), async_context->folder_name_1,
508                 async_context->flags, cancellable, &error);
509
510         if (error != NULL)
511                 g_simple_async_result_take_error (simple, error);
512 }
513
514 static void
515 store_get_folder_info (CamelStore *store,
516                        const gchar *top,
517                        CamelStoreGetFolderInfoFlags flags,
518                        gint io_priority,
519                        GCancellable *cancellable,
520                        GAsyncReadyCallback callback,
521                        gpointer user_data)
522 {
523         GSimpleAsyncResult *simple;
524         AsyncContext *async_context;
525
526         async_context = g_slice_new0 (AsyncContext);
527         async_context->folder_name_1 = g_strdup (top);
528         async_context->flags = flags;
529
530         simple = g_simple_async_result_new (
531                 G_OBJECT (store), callback,
532                 user_data, store_get_folder_info);
533
534         g_simple_async_result_set_check_cancellable (simple, cancellable);
535
536         g_simple_async_result_set_op_res_gpointer (
537                 simple, async_context, (GDestroyNotify) async_context_free);
538
539         g_simple_async_result_run_in_thread (
540                 simple, store_get_folder_info_thread,
541                 io_priority, cancellable);
542
543         g_object_unref (simple);
544 }
545
546 static CamelFolderInfo *
547 store_get_folder_info_finish (CamelStore *store,
548                               GAsyncResult *result,
549                               GError **error)
550 {
551         GSimpleAsyncResult *simple;
552         AsyncContext *async_context;
553         CamelFolderInfo *folder_info;
554
555         g_return_val_if_fail (
556                 g_simple_async_result_is_valid (
557                 result, G_OBJECT (store), store_get_folder_info), NULL);
558
559         simple = G_SIMPLE_ASYNC_RESULT (result);
560         async_context = g_simple_async_result_get_op_res_gpointer (simple);
561
562         if (g_simple_async_result_propagate_error (simple, error))
563                 return NULL;
564
565         folder_info = async_context->folder_info;
566         async_context->folder_info = NULL;
567
568         return folder_info;
569 }
570
571 static void
572 store_get_inbox_folder_thread (GSimpleAsyncResult *simple,
573                                GObject *object,
574                                GCancellable *cancellable)
575 {
576         AsyncContext *async_context;
577         GError *error = NULL;
578
579         async_context = g_simple_async_result_get_op_res_gpointer (simple);
580
581         async_context->folder = camel_store_get_inbox_folder_sync (
582                 CAMEL_STORE (object), cancellable, &error);
583
584         if (error != NULL)
585                 g_simple_async_result_take_error (simple, error);
586 }
587
588 static void
589 store_get_inbox_folder (CamelStore *store,
590                         gint io_priority,
591                         GCancellable *cancellable,
592                         GAsyncReadyCallback callback,
593                         gpointer user_data)
594 {
595         GSimpleAsyncResult *simple;
596         AsyncContext *async_context;
597
598         async_context = g_slice_new0 (AsyncContext);
599
600         simple = g_simple_async_result_new (
601                 G_OBJECT (store), callback,
602                 user_data, store_get_inbox_folder);
603
604         g_simple_async_result_set_check_cancellable (simple, cancellable);
605
606         g_simple_async_result_set_op_res_gpointer (
607                 simple, async_context, (GDestroyNotify) async_context_free);
608
609         g_simple_async_result_run_in_thread (
610                 simple, store_get_inbox_folder_thread,
611                 io_priority, cancellable);
612
613         g_object_unref (simple);
614 }
615
616 static CamelFolder *
617 store_get_inbox_folder_finish (CamelStore *store,
618                                GAsyncResult *result,
619                                GError **error)
620 {
621         GSimpleAsyncResult *simple;
622         AsyncContext *async_context;
623
624         g_return_val_if_fail (
625                 g_simple_async_result_is_valid (
626                 result, G_OBJECT (store), store_get_inbox_folder), NULL);
627
628         simple = G_SIMPLE_ASYNC_RESULT (result);
629         async_context = g_simple_async_result_get_op_res_gpointer (simple);
630
631         if (g_simple_async_result_propagate_error (simple, error))
632                 return NULL;
633
634         return g_object_ref (async_context->folder);
635 }
636
637 static void
638 store_get_junk_folder_thread (GSimpleAsyncResult *simple,
639                               GObject *object,
640                               GCancellable *cancellable)
641 {
642         AsyncContext *async_context;
643         GError *error = NULL;
644
645         async_context = g_simple_async_result_get_op_res_gpointer (simple);
646
647         async_context->folder = camel_store_get_junk_folder_sync (
648                 CAMEL_STORE (object), cancellable, &error);
649
650         if (error != NULL)
651                 g_simple_async_result_take_error (simple, error);
652 }
653
654 static void
655 store_get_junk_folder (CamelStore *store,
656                        gint io_priority,
657                        GCancellable *cancellable,
658                        GAsyncReadyCallback callback,
659                        gpointer user_data)
660 {
661         GSimpleAsyncResult *simple;
662         AsyncContext *async_context;
663
664         async_context = g_slice_new0 (AsyncContext);
665
666         simple = g_simple_async_result_new (
667                 G_OBJECT (store), callback,
668                 user_data, store_get_junk_folder);
669
670         g_simple_async_result_set_check_cancellable (simple, cancellable);
671
672         g_simple_async_result_set_op_res_gpointer (
673                 simple, async_context, (GDestroyNotify) async_context_free);
674
675         g_simple_async_result_run_in_thread (
676                 simple, store_get_junk_folder_thread,
677                 io_priority, cancellable);
678
679         g_object_unref (simple);
680 }
681
682 static CamelFolder *
683 store_get_junk_folder_finish (CamelStore *store,
684                               GAsyncResult *result,
685                               GError **error)
686 {
687         GSimpleAsyncResult *simple;
688         AsyncContext *async_context;
689
690         g_return_val_if_fail (
691                 g_simple_async_result_is_valid (
692                 result, G_OBJECT (store), store_get_junk_folder), NULL);
693
694         simple = G_SIMPLE_ASYNC_RESULT (result);
695         async_context = g_simple_async_result_get_op_res_gpointer (simple);
696
697         if (g_simple_async_result_propagate_error (simple, error))
698                 return NULL;
699
700         return g_object_ref (async_context->folder);
701 }
702
703 static void
704 store_get_trash_folder_thread (GSimpleAsyncResult *simple,
705                                GObject *object,
706                                GCancellable *cancellable)
707 {
708         AsyncContext *async_context;
709         GError *error = NULL;
710
711         async_context = g_simple_async_result_get_op_res_gpointer (simple);
712
713         async_context->folder = camel_store_get_trash_folder_sync (
714                 CAMEL_STORE (object), cancellable, &error);
715
716         if (error != NULL)
717                 g_simple_async_result_take_error (simple, error);
718 }
719
720 static void
721 store_get_trash_folder (CamelStore *store,
722                         gint io_priority,
723                         GCancellable *cancellable,
724                         GAsyncReadyCallback callback,
725                         gpointer user_data)
726 {
727         GSimpleAsyncResult *simple;
728         AsyncContext *async_context;
729
730         async_context = g_slice_new0 (AsyncContext);
731
732         simple = g_simple_async_result_new (
733                 G_OBJECT (store), callback,
734                 user_data, store_get_trash_folder);
735
736         g_simple_async_result_set_check_cancellable (simple, cancellable);
737
738         g_simple_async_result_set_op_res_gpointer (
739                 simple, async_context, (GDestroyNotify) async_context_free);
740
741         g_simple_async_result_run_in_thread (
742                 simple, store_get_trash_folder_thread,
743                 io_priority, cancellable);
744
745         g_object_unref (simple);
746 }
747
748 static CamelFolder *
749 store_get_trash_folder_finish (CamelStore *store,
750                                GAsyncResult *result,
751                                GError **error)
752 {
753         GSimpleAsyncResult *simple;
754         AsyncContext *async_context;
755
756         g_return_val_if_fail (
757                 g_simple_async_result_is_valid (
758                 result, G_OBJECT (store), store_get_trash_folder), NULL);
759
760         simple = G_SIMPLE_ASYNC_RESULT (result);
761         async_context = g_simple_async_result_get_op_res_gpointer (simple);
762
763         if (g_simple_async_result_propagate_error (simple, error))
764                 return NULL;
765
766         return g_object_ref (async_context->folder);
767 }
768
769 static void
770 store_create_folder_thread (GSimpleAsyncResult *simple,
771                             GObject *object,
772                             GCancellable *cancellable)
773 {
774         AsyncContext *async_context;
775         GError *error = NULL;
776
777         async_context = g_simple_async_result_get_op_res_gpointer (simple);
778
779         async_context->folder_info = camel_store_create_folder_sync (
780                 CAMEL_STORE (object), async_context->folder_name_1,
781                 async_context->folder_name_2, cancellable, &error);
782
783         if (error != NULL)
784                 g_simple_async_result_take_error (simple, error);
785 }
786
787 static void
788 store_create_folder (CamelStore *store,
789                      const gchar *parent_name,
790                      const gchar *folder_name,
791                      gint io_priority,
792                      GCancellable *cancellable,
793                      GAsyncReadyCallback callback,
794                      gpointer user_data)
795 {
796         GSimpleAsyncResult *simple;
797         AsyncContext *async_context;
798
799         async_context = g_slice_new0 (AsyncContext);
800         async_context->folder_name_1 = g_strdup (parent_name);
801         async_context->folder_name_2 = g_strdup (folder_name);
802
803         simple = g_simple_async_result_new (
804                 G_OBJECT (store), callback, user_data, store_create_folder);
805
806         g_simple_async_result_set_check_cancellable (simple, cancellable);
807
808         g_simple_async_result_set_op_res_gpointer (
809                 simple, async_context, (GDestroyNotify) async_context_free);
810
811         g_simple_async_result_run_in_thread (
812                 simple, store_create_folder_thread, io_priority, cancellable);
813
814         g_object_unref (simple);
815 }
816
817 static CamelFolderInfo *
818 store_create_folder_finish (CamelStore *store,
819                             GAsyncResult *result,
820                             GError **error)
821 {
822         GSimpleAsyncResult *simple;
823         AsyncContext *async_context;
824         CamelFolderInfo *folder_info;
825
826         g_return_val_if_fail (
827                 g_simple_async_result_is_valid (
828                 result, G_OBJECT (store), store_create_folder), NULL);
829
830         simple = G_SIMPLE_ASYNC_RESULT (result);
831         async_context = g_simple_async_result_get_op_res_gpointer (simple);
832
833         if (g_simple_async_result_propagate_error (simple, error))
834                 return NULL;
835
836         folder_info = async_context->folder_info;
837         async_context->folder_info = NULL;
838
839         return folder_info;
840 }
841
842 static void
843 store_delete_folder_thread (GSimpleAsyncResult *simple,
844                             GObject *object,
845                             GCancellable *cancellable)
846 {
847         AsyncContext *async_context;
848         GError *error = NULL;
849
850         async_context = g_simple_async_result_get_op_res_gpointer (simple);
851
852         camel_store_delete_folder_sync (
853                 CAMEL_STORE (object), async_context->folder_name_1,
854                 cancellable, &error);
855
856         if (error != NULL)
857                 g_simple_async_result_take_error (simple, error);
858 }
859
860 static void
861 store_delete_folder (CamelStore *store,
862                      const gchar *folder_name,
863                      gint io_priority,
864                      GCancellable *cancellable,
865                      GAsyncReadyCallback callback,
866                      gpointer user_data)
867 {
868         GSimpleAsyncResult *simple;
869         AsyncContext *async_context;
870
871         async_context = g_slice_new0 (AsyncContext);
872         async_context->folder_name_1 = g_strdup (folder_name);
873
874         simple = g_simple_async_result_new (
875                 G_OBJECT (store), callback, user_data, store_delete_folder);
876
877         g_simple_async_result_set_check_cancellable (simple, cancellable);
878
879         g_simple_async_result_set_op_res_gpointer (
880                 simple, async_context, (GDestroyNotify) async_context_free);
881
882         g_simple_async_result_run_in_thread (
883                 simple, store_delete_folder_thread, io_priority, cancellable);
884
885         g_object_unref (simple);
886 }
887
888 static gboolean
889 store_delete_folder_finish (CamelStore *store,
890                             GAsyncResult *result,
891                             GError **error)
892 {
893         GSimpleAsyncResult *simple;
894
895         g_return_val_if_fail (
896                 g_simple_async_result_is_valid (
897                 result, G_OBJECT (store), store_delete_folder), FALSE);
898
899         simple = G_SIMPLE_ASYNC_RESULT (result);
900
901         /* Assume success unless a GError is set. */
902         return !g_simple_async_result_propagate_error (simple, error);
903 }
904
905 static void
906 store_rename_folder_thread (GSimpleAsyncResult *simple,
907                             GObject *object,
908                             GCancellable *cancellable)
909 {
910         AsyncContext *async_context;
911         GError *error = NULL;
912
913         async_context = g_simple_async_result_get_op_res_gpointer (simple);
914
915         camel_store_rename_folder_sync (
916                 CAMEL_STORE (object), async_context->folder_name_1,
917                 async_context->folder_name_2, cancellable, &error);
918
919         if (error != NULL)
920                 g_simple_async_result_take_error (simple, error);
921 }
922
923 static void
924 store_rename_folder (CamelStore *store,
925                      const gchar *old_name,
926                      const gchar *new_name,
927                      gint io_priority,
928                      GCancellable *cancellable,
929                      GAsyncReadyCallback callback,
930                      gpointer user_data)
931 {
932         GSimpleAsyncResult *simple;
933         AsyncContext *async_context;
934
935         async_context = g_slice_new0 (AsyncContext);
936         async_context->folder_name_1 = g_strdup (old_name);
937         async_context->folder_name_2 = g_strdup (new_name);
938
939         simple = g_simple_async_result_new (
940                 G_OBJECT (store), callback, user_data, store_rename_folder);
941
942         g_simple_async_result_set_check_cancellable (simple, cancellable);
943
944         g_simple_async_result_set_op_res_gpointer (
945                 simple, async_context, (GDestroyNotify) async_context_free);
946
947         g_simple_async_result_run_in_thread (
948                 simple, store_rename_folder_thread, io_priority, cancellable);
949
950         g_object_unref (simple);
951 }
952
953 static gboolean
954 store_rename_folder_finish (CamelStore *store,
955                             GAsyncResult *result,
956                             GError **error)
957 {
958         GSimpleAsyncResult *simple;
959
960         g_return_val_if_fail (
961                 g_simple_async_result_is_valid (
962                 result, G_OBJECT (store), store_rename_folder), FALSE);
963
964         simple = G_SIMPLE_ASYNC_RESULT (result);
965
966         /* Assume success unless a GError is set. */
967         return !g_simple_async_result_propagate_error (simple, error);
968 }
969
970 static void
971 store_synchronize_thread (GSimpleAsyncResult *simple,
972                           GObject *object,
973                           GCancellable *cancellable)
974 {
975         AsyncContext *async_context;
976         GError *error = NULL;
977
978         async_context = g_simple_async_result_get_op_res_gpointer (simple);
979
980         camel_store_synchronize_sync (
981                 CAMEL_STORE (object), async_context->expunge,
982                 cancellable, &error);
983
984         if (error != NULL)
985                 g_simple_async_result_take_error (simple, error);
986 }
987
988 static void
989 store_synchronize (CamelStore *store,
990                    gboolean expunge,
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->expunge = expunge;
1001
1002         simple = g_simple_async_result_new (
1003                 G_OBJECT (store), callback, user_data, store_synchronize);
1004
1005         g_simple_async_result_set_check_cancellable (simple, cancellable);
1006
1007         g_simple_async_result_set_op_res_gpointer (
1008                 simple, async_context, (GDestroyNotify) async_context_free);
1009
1010         g_simple_async_result_run_in_thread (
1011                 simple, store_synchronize_thread, io_priority, cancellable);
1012
1013         g_object_unref (simple);
1014 }
1015
1016 static gboolean
1017 store_synchronize_finish (CamelStore *store,
1018                           GAsyncResult *result,
1019                           GError **error)
1020 {
1021         GSimpleAsyncResult *simple;
1022
1023         g_return_val_if_fail (
1024                 g_simple_async_result_is_valid (
1025                 result, G_OBJECT (store), store_synchronize), FALSE);
1026
1027         simple = G_SIMPLE_ASYNC_RESULT (result);
1028
1029         /* Assume success unless a GError is set. */
1030         return !g_simple_async_result_propagate_error (simple, error);
1031 }
1032
1033 static void
1034 store_noop_thread (GSimpleAsyncResult *simple,
1035                    GObject *object,
1036                    GCancellable *cancellable)
1037 {
1038         GError *error = NULL;
1039
1040         camel_store_noop_sync (CAMEL_STORE (object), cancellable, &error);
1041
1042         if (error != NULL)
1043                 g_simple_async_result_take_error (simple, error);
1044 }
1045
1046 static void
1047 store_noop (CamelStore *store,
1048             gint io_priority,
1049             GCancellable *cancellable,
1050             GAsyncReadyCallback callback,
1051             gpointer user_data)
1052 {
1053         GSimpleAsyncResult *simple;
1054
1055         simple = g_simple_async_result_new (
1056                 G_OBJECT (store), callback, user_data, store_noop);
1057
1058         g_simple_async_result_set_check_cancellable (simple, cancellable);
1059
1060         g_simple_async_result_run_in_thread (
1061                 simple, store_noop_thread, io_priority, cancellable);
1062
1063         g_object_unref (simple);
1064 }
1065
1066 static gboolean
1067 store_noop_finish (CamelStore *store,
1068                    GAsyncResult *result,
1069                    GError **error)
1070 {
1071         GSimpleAsyncResult *simple;
1072
1073         g_return_val_if_fail (
1074                 g_simple_async_result_is_valid (
1075                 result, G_OBJECT (store), store_noop), FALSE);
1076
1077         simple = G_SIMPLE_ASYNC_RESULT (result);
1078
1079         /* Assume success unless a GError is set. */
1080         return !g_simple_async_result_propagate_error (simple, error);
1081 }
1082
1083 static gboolean
1084 store_initable_init (GInitable *initable,
1085                      GCancellable *cancellable,
1086                      GError **error)
1087 {
1088         CamelStore *store;
1089         CamelService *service;
1090         const gchar *user_dir;
1091         gchar *filename;
1092
1093         store = CAMEL_STORE (initable);
1094
1095         /* Chain up to parent interface's init() method. */
1096         if (!parent_initable_interface->init (initable, cancellable, error))
1097                 return FALSE;
1098
1099         service = CAMEL_SERVICE (initable);
1100         if ((store->flags & CAMEL_STORE_USE_CACHE_DIR) != 0)
1101                 user_dir = camel_service_get_user_cache_dir (service);
1102         else
1103                 user_dir = camel_service_get_user_data_dir (service);
1104
1105         if (g_mkdir_with_parents (user_dir, S_IRWXU) == -1) {
1106                 g_set_error_literal (
1107                         error, G_FILE_ERROR,
1108                         g_file_error_from_errno (errno),
1109                         g_strerror (errno));
1110                 return FALSE;
1111         }
1112
1113         /* This is for reading from the store */
1114         filename = g_build_filename (user_dir, CAMEL_DB_FILE, NULL);
1115         store->cdb_r = camel_db_open (filename, error);
1116         g_free (filename);
1117
1118         if (store->cdb_r == NULL)
1119                 return FALSE;
1120
1121         if (camel_db_create_folders_table (store->cdb_r, error))
1122                 return FALSE;
1123
1124         /* keep cb_w to not break the ABI */
1125         store->cdb_w = store->cdb_r;
1126
1127         return TRUE;
1128 }
1129
1130 static void
1131 camel_store_class_init (CamelStoreClass *class)
1132 {
1133         GObjectClass *object_class;
1134         CamelServiceClass *service_class;
1135
1136         g_type_class_add_private (class, sizeof (CamelStorePrivate));
1137
1138         object_class = G_OBJECT_CLASS (class);
1139         object_class->finalize = store_finalize;
1140         object_class->constructed = store_constructed;
1141
1142         service_class = CAMEL_SERVICE_CLASS (class);
1143         service_class->settings_type = CAMEL_TYPE_STORE_SETTINGS;
1144
1145         class->hash_folder_name = g_str_hash;
1146         class->equal_folder_name = g_str_equal;
1147         class->can_refresh_folder = store_can_refresh_folder;
1148
1149         class->get_inbox_folder_sync = store_get_inbox_folder_sync;
1150         class->get_junk_folder_sync = store_get_junk_folder_sync;
1151         class->get_trash_folder_sync = store_get_trash_folder_sync;
1152         class->synchronize_sync = store_synchronize_sync;
1153         class->noop_sync = store_noop_sync;
1154
1155         class->get_folder = store_get_folder;
1156         class->get_folder_finish = store_get_folder_finish;
1157         class->get_folder_info = store_get_folder_info;
1158         class->get_folder_info_finish = store_get_folder_info_finish;
1159         class->get_inbox_folder = store_get_inbox_folder;
1160         class->get_inbox_folder_finish = store_get_inbox_folder_finish;
1161         class->get_junk_folder = store_get_junk_folder;
1162         class->get_junk_folder_finish = store_get_junk_folder_finish;
1163         class->get_trash_folder = store_get_trash_folder;
1164         class->get_trash_folder_finish = store_get_trash_folder_finish;
1165         class->create_folder = store_create_folder;
1166         class->create_folder_finish = store_create_folder_finish;
1167         class->delete_folder = store_delete_folder;
1168         class->delete_folder_finish = store_delete_folder_finish;
1169         class->rename_folder = store_rename_folder;
1170         class->rename_folder_finish = store_rename_folder_finish;
1171         class->synchronize = store_synchronize;
1172         class->synchronize_finish = store_synchronize_finish;
1173         class->noop = store_noop;
1174         class->noop_finish = store_noop_finish;
1175
1176         signals[FOLDER_CREATED] = g_signal_new (
1177                 "folder-created",
1178                 G_OBJECT_CLASS_TYPE (class),
1179                 G_SIGNAL_RUN_FIRST,
1180                 G_STRUCT_OFFSET (CamelStoreClass, folder_created),
1181                 NULL, NULL,
1182                 g_cclosure_marshal_VOID__POINTER,
1183                 G_TYPE_NONE, 1,
1184                 G_TYPE_POINTER);
1185
1186         signals[FOLDER_DELETED] = g_signal_new (
1187                 "folder-deleted",
1188                 G_OBJECT_CLASS_TYPE (class),
1189                 G_SIGNAL_RUN_FIRST,
1190                 G_STRUCT_OFFSET (CamelStoreClass, folder_deleted),
1191                 NULL, NULL,
1192                 g_cclosure_marshal_VOID__POINTER,
1193                 G_TYPE_NONE, 1,
1194                 G_TYPE_POINTER);
1195
1196         signals[FOLDER_OPENED] = g_signal_new (
1197                 "folder-opened",
1198                 G_OBJECT_CLASS_TYPE (class),
1199                 G_SIGNAL_RUN_FIRST,
1200                 G_STRUCT_OFFSET (CamelStoreClass, folder_opened),
1201                 NULL, NULL,
1202                 g_cclosure_marshal_VOID__OBJECT,
1203                 G_TYPE_NONE, 1,
1204                 CAMEL_TYPE_FOLDER);
1205
1206         signals[FOLDER_RENAMED] = g_signal_new (
1207                 "folder-renamed",
1208                 G_OBJECT_CLASS_TYPE (class),
1209                 G_SIGNAL_RUN_FIRST,
1210                 G_STRUCT_OFFSET (CamelStoreClass, folder_renamed),
1211                 NULL, NULL,
1212                 camel_marshal_VOID__STRING_POINTER,
1213                 G_TYPE_NONE, 2,
1214                 G_TYPE_STRING,
1215                 G_TYPE_POINTER);
1216 }
1217
1218 static void
1219 camel_store_initable_init (GInitableIface *interface)
1220 {
1221         parent_initable_interface = g_type_interface_peek_parent (interface);
1222
1223         interface->init = store_initable_init;
1224 }
1225
1226 static void
1227 camel_store_init (CamelStore *store)
1228 {
1229         store->priv = CAMEL_STORE_GET_PRIVATE (store);
1230
1231         /* Default CamelStore capabilities:
1232          *
1233          *  - Include a virtual Junk folder.
1234          *  - Include a virtual Trash folder.
1235          *  - Allow creating/deleting/renaming folders.
1236          */
1237         store->flags =
1238                 CAMEL_STORE_VJUNK |
1239                 CAMEL_STORE_VTRASH |
1240                 CAMEL_STORE_CAN_EDIT_FOLDERS;
1241
1242         store->mode = CAMEL_STORE_READ | CAMEL_STORE_WRITE;
1243
1244         g_rec_mutex_init (&store->priv->folder_lock);
1245 }
1246
1247 G_DEFINE_QUARK (camel-store-error-quark, camel_store_error)
1248
1249 /**
1250  * camel_store_folder_created:
1251  * @store: a #CamelStore
1252  * @folder_info: information about the created folder
1253  *
1254  * Emits the #CamelStore::folder-created signal from an idle source on
1255  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1256  *
1257  * This function is only intended for Camel providers.
1258  *
1259  * Since: 2.32
1260  **/
1261 void
1262 camel_store_folder_created (CamelStore *store,
1263                             CamelFolderInfo *folder_info)
1264 {
1265         CamelSession *session;
1266         SignalData *signal_data;
1267
1268         g_return_if_fail (CAMEL_IS_STORE (store));
1269         g_return_if_fail (folder_info != NULL);
1270
1271         session = camel_service_get_session (CAMEL_SERVICE (store));
1272
1273         signal_data = g_slice_new0 (SignalData);
1274         signal_data->store = g_object_ref (store);
1275         signal_data->folder_info = camel_folder_info_clone (folder_info);
1276
1277         camel_session_idle_add (
1278                 session, G_PRIORITY_DEFAULT_IDLE,
1279                 store_emit_folder_created_cb,
1280                 signal_data, (GDestroyNotify) signal_data_free);
1281 }
1282
1283 /**
1284  * camel_store_folder_deleted:
1285  * @store: a #CamelStore
1286  * @folder_info: information about the deleted folder
1287  *
1288  * Emits the #CamelStore::folder-deleted signal from an idle source on
1289  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1290  *
1291  * This function is only intended for Camel providers.
1292  *
1293  * Since: 2.32
1294  **/
1295 void
1296 camel_store_folder_deleted (CamelStore *store,
1297                             CamelFolderInfo *folder_info)
1298 {
1299         CamelSession *session;
1300         SignalData *signal_data;
1301
1302         g_return_if_fail (CAMEL_IS_STORE (store));
1303         g_return_if_fail (folder_info != NULL);
1304
1305         session = camel_service_get_session (CAMEL_SERVICE (store));
1306
1307         signal_data = g_slice_new0 (SignalData);
1308         signal_data->store = g_object_ref (store);
1309         signal_data->folder_info = camel_folder_info_clone (folder_info);
1310
1311         camel_session_idle_add (
1312                 session, G_PRIORITY_DEFAULT_IDLE,
1313                 store_emit_folder_deleted_cb,
1314                 signal_data, (GDestroyNotify) signal_data_free);
1315 }
1316
1317 /**
1318  * camel_store_folder_opened:
1319  * @store: a #CamelStore
1320  * @folder: the #CamelFolder that was opened
1321  *
1322  * Emits the #CamelStore::folder-opened signal from an idle source on
1323  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1324  *
1325  * This function is only intended for Camel providers.
1326  *
1327  * Since: 3.0
1328  **/
1329 void
1330 camel_store_folder_opened (CamelStore *store,
1331                            CamelFolder *folder)
1332 {
1333         CamelSession *session;
1334         SignalData *signal_data;
1335
1336         g_return_if_fail (CAMEL_IS_STORE (store));
1337         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1338
1339         session = camel_service_get_session (CAMEL_SERVICE (store));
1340
1341         signal_data = g_slice_new0 (SignalData);
1342         signal_data->store = g_object_ref (store);
1343         signal_data->folder = g_object_ref (folder);
1344
1345         camel_session_idle_add (
1346                 session, G_PRIORITY_DEFAULT_IDLE,
1347                 store_emit_folder_opened_cb,
1348                 signal_data, (GDestroyNotify) signal_data_free);
1349 }
1350
1351 /**
1352  * camel_store_folder_renamed:
1353  * @store: a #CamelStore
1354  * @old_name: the old name of the folder
1355  * @folder_info: information about the renamed folder
1356  *
1357  * Emits the #CamelStore::folder-renamed signal from an idle source on
1358  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1359  *
1360  * This function is only intended for Camel providers.
1361  *
1362  * Since: 2.32
1363  **/
1364 void
1365 camel_store_folder_renamed (CamelStore *store,
1366                             const gchar *old_name,
1367                             CamelFolderInfo *folder_info)
1368 {
1369         CamelSession *session;
1370         SignalData *signal_data;
1371
1372         g_return_if_fail (CAMEL_IS_STORE (store));
1373         g_return_if_fail (old_name != NULL);
1374         g_return_if_fail (folder_info != NULL);
1375
1376         session = camel_service_get_session (CAMEL_SERVICE (store));
1377
1378         signal_data = g_slice_new0 (SignalData);
1379         signal_data->store = g_object_ref (store);
1380         signal_data->folder_info = camel_folder_info_clone (folder_info);
1381         signal_data->folder_name = g_strdup (old_name);
1382
1383         camel_session_idle_add (
1384                 session, G_PRIORITY_DEFAULT_IDLE,
1385                 store_emit_folder_renamed_cb,
1386                 signal_data, (GDestroyNotify) signal_data_free);
1387 }
1388
1389 static void
1390 add_special_info (CamelStore *store,
1391                   CamelFolderInfo *info,
1392                   const gchar *name,
1393                   const gchar *translated,
1394                   gboolean unread_count,
1395                   CamelFolderInfoFlags flags)
1396 {
1397         CamelFolderInfo *fi, *vinfo, *parent;
1398
1399         g_return_if_fail (CAMEL_IS_STORE (store));
1400         g_return_if_fail (info != NULL);
1401
1402         parent = NULL;
1403         for (fi = info; fi; fi = fi->next) {
1404                 if (!strcmp (fi->full_name, name))
1405                         break;
1406                 parent = fi;
1407         }
1408
1409         if (fi) {
1410                 /* We're going to replace the physical Trash/Junk
1411                  * folder with our vTrash/vJunk folder. */
1412                 vinfo = fi;
1413                 g_free (vinfo->full_name);
1414                 g_free (vinfo->display_name);
1415         } else {
1416                 /* There wasn't a Trash/Junk folder so create a new
1417                  * folder entry. */
1418                 vinfo = camel_folder_info_new ();
1419
1420                 g_assert (parent != NULL);
1421
1422                 vinfo->flags |=
1423                         CAMEL_FOLDER_NOINFERIORS |
1424                         CAMEL_FOLDER_SUBSCRIBED;
1425
1426                 /* link it into the right spot */
1427                 vinfo->next = parent->next;
1428                 parent->next = vinfo;
1429         }
1430
1431         /* Fill in the new fields */
1432         vinfo->flags |= flags;
1433         vinfo->full_name = g_strdup (name);
1434         vinfo->display_name = g_strdup (translated);
1435
1436         if (!unread_count)
1437                 vinfo->unread = -1;
1438 }
1439
1440 static void
1441 dump_fi (CamelFolderInfo *fi,
1442          gint depth)
1443 {
1444         gchar *s;
1445
1446         s = g_alloca (depth + 1);
1447         memset (s, ' ', depth);
1448         s[depth] = 0;
1449
1450         while (fi) {
1451                 printf ("%sfull_name: %s\n", s, fi->full_name);
1452                 printf ("%sflags: %08x\n", s, fi->flags);
1453                 dump_fi (fi->child, depth + 2);
1454                 fi = fi->next;
1455         }
1456 }
1457
1458 /**
1459  * camel_store_free_folder_info:
1460  * @store: a #CamelStore
1461  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1462  *
1463  * Frees the data returned by camel_store_get_folder_info(). If @fi is %NULL,
1464  * nothing is done, the routine simply returns.
1465  **/
1466 void
1467 camel_store_free_folder_info (CamelStore *store,
1468                               CamelFolderInfo *fi)
1469 {
1470         CamelStoreClass *class;
1471
1472         g_return_if_fail (CAMEL_IS_STORE (store));
1473
1474         if (fi == NULL)
1475                 return;
1476
1477         class = CAMEL_STORE_GET_CLASS (store);
1478         g_return_if_fail (class->free_folder_info != NULL);
1479
1480         class->free_folder_info (store, fi);
1481 }
1482
1483 /**
1484  * camel_store_free_folder_info_full:
1485  * @store: a #CamelStore
1486  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1487  *
1488  * An implementation for #CamelStore::free_folder_info. Frees all
1489  * of the data.
1490  **/
1491 void
1492 camel_store_free_folder_info_full (CamelStore *store,
1493                                    CamelFolderInfo *fi)
1494 {
1495         camel_folder_info_free (fi);
1496 }
1497
1498 /**
1499  * camel_store_free_folder_info_nop:
1500  * @store: a #CamelStore
1501  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1502  *
1503  * An implementation for #CamelStore::free_folder_info. Does nothing.
1504  **/
1505 void
1506 camel_store_free_folder_info_nop (CamelStore *store,
1507                                   CamelFolderInfo *fi)
1508 {
1509         ;
1510 }
1511
1512 /**
1513  * camel_folder_info_free:
1514  * @fi: a #CamelFolderInfo
1515  *
1516  * Frees @fi.
1517  **/
1518 void
1519 camel_folder_info_free (CamelFolderInfo *fi)
1520 {
1521         if (fi != NULL) {
1522                 camel_folder_info_free (fi->next);
1523                 camel_folder_info_free (fi->child);
1524                 g_free (fi->full_name);
1525                 g_free (fi->display_name);
1526                 g_slice_free (CamelFolderInfo, fi);
1527         }
1528 }
1529
1530 /**
1531  * camel_folder_info_new:
1532  *
1533  * Allocates a new #CamelFolderInfo instance.  Free it with
1534  * camel_folder_info_free().
1535  *
1536  * Returns: a new #CamelFolderInfo instance
1537  *
1538  * Since: 2.22
1539  **/
1540 CamelFolderInfo *
1541 camel_folder_info_new (void)
1542 {
1543         return g_slice_new0 (CamelFolderInfo);
1544 }
1545
1546 static gint
1547 folder_info_cmp (gconstpointer ap,
1548                  gconstpointer bp)
1549 {
1550         const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
1551         const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];
1552
1553         return strcmp (a->full_name, b->full_name);
1554 }
1555
1556 /**
1557  * camel_folder_info_build:
1558  * @folders: an array of #CamelFolderInfo
1559  * @namespace_: an ignorable prefix on the folder names
1560  * @separator: the hieararchy separator character
1561  * @short_names: %TRUE if the (short) name of a folder is the part after
1562  * the last @separator in the full name. %FALSE if it is the full name.
1563  *
1564  * This takes an array of folders and attaches them together according
1565  * to the hierarchy described by their full_names and @separator. If
1566  * @namespace_ is non-%NULL, then it will be ignored as a full_name
1567  * prefix, for purposes of comparison. If necessary,
1568  * camel_folder_info_build() will create additional #CamelFolderInfo with
1569  * %NULL urls to fill in gaps in the tree. The value of @short_names
1570  * is used in constructing the names of these intermediate folders.
1571  *
1572  * NOTE: This is deprected, do not use this.
1573  * FIXME: remove this/move it to imap, which is the only user of it now.
1574  *
1575  * Returns: the top level of the tree of linked folder info.
1576  **/
1577 CamelFolderInfo *
1578 camel_folder_info_build (GPtrArray *folders,
1579                          const gchar *namespace_,
1580                          gchar separator,
1581                          gboolean short_names)
1582 {
1583         CamelFolderInfo *fi, *pfi, *top = NULL, *tail = NULL;
1584         GHashTable *hash;
1585         gchar *p, *pname;
1586         gint i, nlen;
1587
1588         if (namespace_ == NULL)
1589                 namespace_ = "";
1590         nlen = strlen (namespace_);
1591
1592         qsort (folders->pdata, folders->len, sizeof (folders->pdata[0]), folder_info_cmp);
1593
1594         /* Hash the folders. */
1595         hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1596         for (i = 0; i < folders->len; i++) {
1597                 fi = folders->pdata[i];
1598                 g_hash_table_insert (hash, g_strdup (fi->full_name), fi);
1599         }
1600
1601         /* Now find parents. */
1602         for (i = 0; i < folders->len; i++) {
1603                 fi = folders->pdata[i];
1604                 if (!strncmp (namespace_, fi->full_name, nlen)
1605                     && (p = strrchr (fi->full_name + nlen, separator))) {
1606                         pname = g_strndup (fi->full_name, p - fi->full_name);
1607                         pfi = g_hash_table_lookup (hash, pname);
1608                         if (pfi) {
1609                                 g_free (pname);
1610                         } else {
1611                                 /* we are missing a folder in the heirarchy so
1612                                  * create a fake folder node */
1613
1614                                 pfi = camel_folder_info_new ();
1615
1616                                 if (short_names) {
1617                                         pfi->display_name = strrchr (pname, separator);
1618                                         if (pfi->display_name != NULL)
1619                                                 pfi->display_name = g_strdup (pfi->display_name + 1);
1620                                         else
1621                                                 pfi->display_name = g_strdup (pname);
1622                                 } else
1623                                         pfi->display_name = g_strdup (pname);
1624
1625                                 pfi->full_name = g_strdup (pname);
1626
1627                                 /* Since this is a "fake" folder
1628                                  * node, it is not selectable. */
1629                                 pfi->flags |= CAMEL_FOLDER_NOSELECT;
1630
1631                                 g_hash_table_insert (hash, pname, pfi);
1632                                 g_ptr_array_add (folders, pfi);
1633                         }
1634                         tail = (CamelFolderInfo *) &pfi->child;
1635                         while (tail->next)
1636                                 tail = tail->next;
1637                         tail->next = fi;
1638                         fi->parent = pfi;
1639                 } else if (!top || !g_ascii_strcasecmp (fi->full_name, "Inbox"))
1640                         top = fi;
1641         }
1642         g_hash_table_destroy (hash);
1643
1644         /* Link together the top-level folders */
1645         tail = top;
1646         for (i = 0; i < folders->len; i++) {
1647                 fi = folders->pdata[i];
1648
1649                 if (fi->child)
1650                         fi->flags &= ~CAMEL_FOLDER_NOCHILDREN;
1651
1652                 if (fi->parent || fi == top)
1653                         continue;
1654                 if (tail == NULL) {
1655                         tail = fi;
1656                         top = fi;
1657                 } else {
1658                         tail->next = fi;
1659                         tail = fi;
1660                 }
1661         }
1662
1663         return top;
1664 }
1665
1666 static CamelFolderInfo *
1667 folder_info_clone_rec (CamelFolderInfo *fi,
1668                        CamelFolderInfo *parent)
1669 {
1670         CamelFolderInfo *info;
1671
1672         info = camel_folder_info_new ();
1673         info->parent = parent;
1674         info->full_name = g_strdup (fi->full_name);
1675         info->display_name = g_strdup (fi->display_name);
1676         info->unread = fi->unread;
1677         info->flags = fi->flags;
1678
1679         if (fi->next)
1680                 info->next = folder_info_clone_rec (fi->next, parent);
1681         else
1682                 info->next = NULL;
1683
1684         if (fi->child)
1685                 info->child = folder_info_clone_rec (fi->child, info);
1686         else
1687                 info->child = NULL;
1688
1689         return info;
1690 }
1691
1692 /**
1693  * camel_folder_info_clone:
1694  * @fi: a #CamelFolderInfo
1695  *
1696  * Clones @fi recursively.
1697  *
1698  * Returns: the cloned #CamelFolderInfo tree.
1699  **/
1700 CamelFolderInfo *
1701 camel_folder_info_clone (CamelFolderInfo *fi)
1702 {
1703         if (fi == NULL)
1704                 return NULL;
1705
1706         return folder_info_clone_rec (fi, NULL);
1707 }
1708
1709 /**
1710  * camel_store_can_refresh_folder
1711  * @store: a #CamelStore
1712  * @info: a #CamelFolderInfo
1713  * @error: return location for a #GError, or %NULL
1714  *
1715  * Returns if this folder (param info) should be checked for new mail or not.
1716  * It should not look into sub infos (info->child) or next infos, it should
1717  * return value only for the actual folder info.
1718  * Default behavior is that all Inbox folders are intended to be refreshed.
1719  *
1720  * Returns: whether folder should be checked for new mails
1721  *
1722  * Since: 2.22
1723  **/
1724 gboolean
1725 camel_store_can_refresh_folder (CamelStore *store,
1726                                 CamelFolderInfo *info,
1727                                 GError **error)
1728 {
1729         CamelStoreClass *class;
1730
1731         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
1732         g_return_val_if_fail (info != NULL, FALSE);
1733
1734         class = CAMEL_STORE_GET_CLASS (store);
1735         g_return_val_if_fail (class->can_refresh_folder != NULL, FALSE);
1736
1737         return class->can_refresh_folder (store, info, error);
1738 }
1739
1740 /**
1741  * camel_store_lock:
1742  * @store: a #CamelStore
1743  * @lock: lock type to lock
1744  *
1745  * Locks @store's @lock. Unlock it with camel_store_unlock().
1746  *
1747  * Since: 2.32
1748  **/
1749 void
1750 camel_store_lock (CamelStore *store,
1751                   CamelStoreLock lock)
1752 {
1753         g_return_if_fail (CAMEL_IS_STORE (store));
1754
1755         switch (lock) {
1756                 case CAMEL_STORE_FOLDER_LOCK:
1757                         g_rec_mutex_lock (&store->priv->folder_lock);
1758                         break;
1759                 default:
1760                         g_return_if_reached ();
1761         }
1762 }
1763
1764 /**
1765  * camel_store_unlock:
1766  * @store: a #CamelStore
1767  * @lock: lock type to unlock
1768  *
1769  * Unlocks @store's @lock, previously locked with camel_store_lock().
1770  *
1771  * Since: 2.32
1772  **/
1773 void
1774 camel_store_unlock (CamelStore *store,
1775                     CamelStoreLock lock)
1776 {
1777         g_return_if_fail (CAMEL_IS_STORE (store));
1778
1779         switch (lock) {
1780                 case CAMEL_STORE_FOLDER_LOCK:
1781                         g_rec_mutex_unlock (&store->priv->folder_lock);
1782                         break;
1783                 default:
1784                         g_return_if_reached ();
1785         }
1786 }
1787
1788 /**
1789  * camel_store_get_folder_sync:
1790  * @store: a #CamelStore
1791  * @folder_name: name of the folder to get
1792  * @flags: folder flags (create, save body index, etc)
1793  * @cancellable: optional #GCancellable object, or %NULL
1794  * @error: return location for a #GError, or %NULL
1795  *
1796  * Gets a specific folder object from @store by name.
1797  *
1798  * Returns: the requested #CamelFolder object, or %NULL on error
1799  *
1800  * Since: 3.0
1801  **/
1802 CamelFolder *
1803 camel_store_get_folder_sync (CamelStore *store,
1804                              const gchar *folder_name,
1805                              CamelStoreGetFolderFlags flags,
1806                              GCancellable *cancellable,
1807                              GError **error)
1808 {
1809         CamelStoreClass *class;
1810         CamelFolder *folder = NULL;
1811
1812         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1813         g_return_val_if_fail (folder_name != NULL, NULL);
1814
1815         class = CAMEL_STORE_GET_CLASS (store);
1816
1817         /* O_EXCL doesn't make sense if we aren't requesting
1818          * to also create the folder if it doesn't exist. */
1819         if (!(flags & CAMEL_STORE_FOLDER_CREATE))
1820                 flags &= ~CAMEL_STORE_FOLDER_EXCL;
1821
1822         /* Try cache first. */
1823         folder = camel_object_bag_reserve (store->folders, folder_name);
1824         if (folder != NULL && (flags & CAMEL_STORE_FOLDER_EXCL)) {
1825                 g_set_error (
1826                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1827                         _("Cannot create folder '%s': folder exists"),
1828                         folder_name);
1829                 camel_object_bag_abort (store->folders, folder_name);
1830                 g_object_unref (folder);
1831                 return NULL;
1832         }
1833
1834         if (folder == NULL) {
1835                 CamelVeeFolder *vjunk = NULL;
1836                 CamelVeeFolder *vtrash = NULL;
1837                 gboolean folder_name_is_vjunk;
1838                 gboolean folder_name_is_vtrash;
1839                 gboolean store_uses_vjunk;
1840                 gboolean store_uses_vtrash;
1841
1842                 store_uses_vjunk =
1843                         ((store->flags & CAMEL_STORE_VJUNK) != 0);
1844                 store_uses_vtrash =
1845                         ((store->flags & CAMEL_STORE_VTRASH) != 0);
1846                 folder_name_is_vjunk =
1847                         store_uses_vjunk &&
1848                         (strcmp (folder_name, CAMEL_VJUNK_NAME) == 0);
1849                 folder_name_is_vtrash =
1850                         store_uses_vtrash &&
1851                         (strcmp (folder_name, CAMEL_VTRASH_NAME) == 0);
1852
1853                 if (flags & CAMEL_STORE_IS_MIGRATING) {
1854                         if (folder_name_is_vtrash) {
1855                                 if (store->folders != NULL)
1856                                         camel_object_bag_abort (
1857                                                 store->folders, folder_name);
1858                                 return NULL;
1859                         }
1860
1861                         if (folder_name_is_vjunk) {
1862                                 if (store->folders != NULL)
1863                                         camel_object_bag_abort (
1864                                                 store->folders, folder_name);
1865                                 return NULL;
1866                         }
1867                 }
1868
1869                 camel_operation_push_message (
1870                         cancellable, _("Opening folder '%s'"), folder_name);
1871
1872                 if (folder_name_is_vtrash) {
1873                         folder = class->get_trash_folder_sync (
1874                                 store, cancellable, error);
1875                         CAMEL_CHECK_GERROR (
1876                                 store, get_trash_folder_sync,
1877                                 folder != NULL, error);
1878                 } else if (folder_name_is_vjunk) {
1879                         folder = class->get_junk_folder_sync (
1880                                 store, cancellable, error);
1881                         CAMEL_CHECK_GERROR (
1882                                 store, get_junk_folder_sync,
1883                                 folder != NULL, error);
1884                 } else {
1885                         folder = class->get_folder_sync (
1886                                 store, folder_name, flags,
1887                                 cancellable, error);
1888                         CAMEL_CHECK_GERROR (
1889                                 store, get_folder_sync,
1890                                 folder != NULL, error);
1891
1892                         if (folder != NULL && store_uses_vjunk)
1893                                 vjunk = camel_object_bag_get (
1894                                         store->folders, CAMEL_VJUNK_NAME);
1895
1896                         if (folder != NULL && store_uses_vtrash)
1897                                 vtrash = camel_object_bag_get (
1898                                         store->folders, CAMEL_VTRASH_NAME);
1899                 }
1900
1901                 /* Release the folder name reservation before adding the
1902                  * folder to the virtual Junk and Trash folders, just to
1903                  * reduce the chance of deadlock. */
1904                 if (folder != NULL)
1905                         camel_object_bag_add (
1906                                 store->folders, folder_name, folder);
1907                 else
1908                         camel_object_bag_abort (
1909                                 store->folders, folder_name);
1910
1911                 /* If this is a normal folder and the store uses a
1912                  * virtual Junk folder, let the virtual Junk folder
1913                  * track this folder. */
1914                 if (vjunk != NULL) {
1915                         camel_vee_folder_add_folder (vjunk, folder, NULL);
1916                         g_object_unref (vjunk);
1917                 }
1918
1919                 /* If this is a normal folder and the store uses a
1920                  * virtual Trash folder, let the virtual Trash folder
1921                  * track this folder. */
1922                 if (vtrash != NULL) {
1923                         camel_vee_folder_add_folder (vtrash, folder, NULL);
1924                         g_object_unref (vtrash);
1925                 }
1926
1927                 camel_operation_pop_message (cancellable);
1928
1929                 if (folder != NULL)
1930                         camel_store_folder_opened (store, folder);
1931         }
1932
1933         return folder;
1934 }
1935
1936 /**
1937  * camel_store_get_folder:
1938  * @store: a #CamelStore
1939  * @folder_name: name of the folder to get
1940  * @flags: folder flags (create, save body index, etc)
1941  * @io_priority: the I/O priority of the request
1942  * @cancellable: optional #GCancellable object, or %NULL
1943  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1944  * @user_data: data to pass to the callback function
1945  *
1946  * Asynchronously gets a specific folder object from @store by name.
1947  *
1948  * When the operation is finished, @callback will be called.  You can then
1949  * call camel_store_get_folder_finish() to get the result of the operation.
1950  *
1951  * Since: 3.0
1952  **/
1953 void
1954 camel_store_get_folder (CamelStore *store,
1955                         const gchar *folder_name,
1956                         CamelStoreGetFolderFlags flags,
1957                         gint io_priority,
1958                         GCancellable *cancellable,
1959                         GAsyncReadyCallback callback,
1960                         gpointer user_data)
1961 {
1962         CamelStoreClass *class;
1963
1964         g_return_if_fail (CAMEL_IS_STORE (store));
1965         g_return_if_fail (folder_name != NULL);
1966
1967         class = CAMEL_STORE_GET_CLASS (store);
1968         g_return_if_fail (class->get_folder != NULL);
1969
1970         class->get_folder (
1971                 store, folder_name, flags, io_priority,
1972                 cancellable, callback, user_data);
1973 }
1974
1975 /**
1976  * camel_store_get_folder_finish:
1977  * @store: a #CamelStore
1978  * @result: a #GAsyncResult
1979  * @error: return location for a #GError, or %NULL
1980  *
1981  * Finishes the operation started with camel_store_get_folder().
1982  *
1983  * Returns: the requested #CamelFolder object, or %NULL on error
1984  *
1985  * Since: 3.0
1986  **/
1987 CamelFolder *
1988 camel_store_get_folder_finish (CamelStore *store,
1989                                GAsyncResult *result,
1990                                GError **error)
1991 {
1992         CamelStoreClass *class;
1993
1994         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1995         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
1996
1997         class = CAMEL_STORE_GET_CLASS (store);
1998         g_return_val_if_fail (class->get_folder_finish != NULL, NULL);
1999
2000         return class->get_folder_finish (store, result, error);
2001 }
2002
2003 /**
2004  * camel_store_get_folder_info_sync:
2005  * @store: a #CamelStore
2006  * @top: the name of the folder to start from
2007  * @flags: various CAMEL_STORE_FOLDER_INFO_* flags to control behavior
2008  * @cancellable: optional #GCancellable object, or %NULL
2009  * @error: return location for a #GError, or %NULL
2010  *
2011  * This fetches information about the folder structure of @store,
2012  * starting with @top, and returns a tree of #CamelFolderInfo
2013  * structures. If @flags includes %CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
2014  * only subscribed folders will be listed.   If the store doesn't support
2015  * subscriptions, then it will list all folders.  If @flags includes
2016  * %CAMEL_STORE_FOLDER_INFO_RECURSIVE, the returned tree will include
2017  * all levels of hierarchy below @top. If not, it will only include
2018  * the immediate subfolders of @top. If @flags includes
2019  * %CAMEL_STORE_FOLDER_INFO_FAST, the unread_message_count fields of
2020  * some or all of the structures may be set to %-1, if the store cannot
2021  * determine that information quickly.  If @flags includes
2022  * %CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, don't include special virtual
2023  * folders (such as vTrash or vJunk).
2024  *
2025  * The returned #CamelFolderInfo tree should be freed with
2026  * camel_store_free_folder_info().
2027  *
2028  * The CAMEL_STORE_FOLDER_INFO_FAST flag should be considered
2029  * deprecated; most backends will behave the same whether it is
2030  * supplied or not.  The only guaranteed way to get updated folder
2031  * counts is to both open the folder and invoke refresh_info() it.
2032  *
2033  * Returns: a #CamelFolderInfo tree, or %NULL on error
2034  *
2035  * Since: 3.0
2036  **/
2037 CamelFolderInfo *
2038 camel_store_get_folder_info_sync (CamelStore *store,
2039                                   const gchar *top,
2040                                   CamelStoreGetFolderInfoFlags flags,
2041                                   GCancellable *cancellable,
2042                                   GError **error)
2043 {
2044         CamelStoreClass *class;
2045         CamelFolderInfo *info;
2046         gchar *name;
2047
2048         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2049
2050         class = CAMEL_STORE_GET_CLASS (store);
2051         g_return_val_if_fail (class->get_folder_info_sync != NULL, NULL);
2052
2053         name = camel_service_get_name (CAMEL_SERVICE (store), TRUE);
2054         camel_operation_push_message (
2055                 cancellable, _("Scanning folders in '%s'"), name);
2056         g_free (name);
2057
2058         info = class->get_folder_info_sync (
2059                 store, top, flags, cancellable, error);
2060         if (!(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED))
2061                 CAMEL_CHECK_GERROR (
2062                         store, get_folder_info_sync, info != NULL, error);
2063
2064         if (info && (top == NULL || *top == '\0') && (flags & CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL) == 0) {
2065                 if (store->flags & CAMEL_STORE_VTRASH)
2066                         /* the name of the Trash folder, used for deleted messages */
2067                         add_special_info (store, info, CAMEL_VTRASH_NAME, _("Trash"), FALSE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_TRASH);
2068                 if (store->flags & CAMEL_STORE_VJUNK)
2069                         /* the name of the Junk folder, used for spam messages */
2070                         add_special_info (store, info, CAMEL_VJUNK_NAME, _("Junk"), TRUE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_JUNK);
2071         } else if (!info && top && (flags & CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL) == 0) {
2072                 CamelFolderInfo *root_info = NULL;
2073
2074                 if ((store->flags & CAMEL_STORE_VTRASH) != 0 && g_str_equal (top, CAMEL_VTRASH_NAME)) {
2075                         root_info = class->get_folder_info_sync (store, NULL, flags & (~CAMEL_STORE_FOLDER_INFO_RECURSIVE), cancellable, error);
2076                         if (root_info)
2077                                 add_special_info (store, root_info, CAMEL_VTRASH_NAME, _("Trash"), FALSE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_TRASH);
2078                 } else if ((store->flags & CAMEL_STORE_VJUNK) != 0 && g_str_equal (top, CAMEL_VJUNK_NAME)) {
2079                         root_info = class->get_folder_info_sync (store, NULL, flags & (~CAMEL_STORE_FOLDER_INFO_RECURSIVE), cancellable, error);
2080                         if (root_info)
2081                                 add_special_info (store, root_info, CAMEL_VJUNK_NAME, _("Junk"), TRUE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_JUNK);
2082                 }
2083
2084                 if (root_info) {
2085                         info = root_info->next;
2086                         root_info->next = NULL;
2087                         info->next = NULL;
2088                         info->parent = NULL;
2089
2090                         camel_store_free_folder_info (store, root_info);
2091                 }
2092         }
2093
2094         camel_operation_pop_message (cancellable);
2095
2096         if (camel_debug_start ("store:folder_info")) {
2097                 const gchar *uid;
2098
2099                 uid = camel_service_get_uid (CAMEL_SERVICE (store));
2100                 printf (
2101                         "Get folder info(%p:%s, '%s') =\n",
2102                         (gpointer) store, uid, top ? top : "<null>");
2103                 dump_fi (info, 2);
2104                 camel_debug_end ();
2105         }
2106
2107         return info;
2108 }
2109
2110 /**
2111  * camel_store_get_folder_info:
2112  * @store: a #CamelStore
2113  * @top: the name of the folder to start from
2114  * @flags: various CAMEL_STORE_FOLDER_INFO_* flags to control behavior
2115  * @io_priority: the I/O priority of the request
2116  * @cancellable: optional #GCancellable object, or %NULL
2117  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2118  * @user_data: data to pass to the callback function
2119  *
2120  * Asynchronously fetches information about the folder structure of @store,
2121  * starting with @top.  For details of the behavior, see
2122  * camel_store_get_folder_info_sync().
2123  *
2124  * When the operation is finished, @callback will be called.  You can
2125  * then call camel_store_get_folder_info_finish() to get the result of
2126  * the operation.
2127  *
2128  * Since: 3.0
2129  **/
2130 void
2131 camel_store_get_folder_info (CamelStore *store,
2132                              const gchar *top,
2133                              CamelStoreGetFolderInfoFlags flags,
2134                              gint io_priority,
2135                              GCancellable *cancellable,
2136                              GAsyncReadyCallback callback,
2137                              gpointer user_data)
2138 {
2139         CamelStoreClass *class;
2140
2141         g_return_if_fail (CAMEL_IS_STORE (store));
2142
2143         class = CAMEL_STORE_GET_CLASS (store);
2144         g_return_if_fail (class->get_folder_info != NULL);
2145
2146         class->get_folder_info (
2147                 store, top, flags, io_priority,
2148                 cancellable, callback, user_data);
2149 }
2150
2151 /**
2152  * camel_store_get_folder_info_finish:
2153  * @store: a #CamelStore
2154  * @result: a #GAsyncResult
2155  * @error: return location for a #GError, or %NULL
2156  *
2157  * Finishes the operation started with camel_store_get_folder_info().
2158  * The returned #CamelFolderInfo tree should be freed with
2159  * camel_store_free_folder_info().
2160  *
2161  * Returns: a #CamelFolderInfo tree, or %NULL on error
2162  *
2163  * Since: 3.0
2164  **/
2165 CamelFolderInfo *
2166 camel_store_get_folder_info_finish (CamelStore *store,
2167                                     GAsyncResult *result,
2168                                     GError **error)
2169 {
2170         CamelStoreClass *class;
2171
2172         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2173         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2174
2175         class = CAMEL_STORE_GET_CLASS (store);
2176         g_return_val_if_fail (class->get_folder_info_finish != NULL, NULL);
2177
2178         return class->get_folder_info_finish (store, result, error);
2179 }
2180
2181 /**
2182  * camel_store_get_inbox_folder_sync:
2183  * @store: a #CamelStore
2184  * @cancellable: optional #GCancellable object, or %NULL
2185  * @error: return location for a #GError, or %NULL
2186  *
2187  * Gets the folder in @store into which new mail is delivered.
2188  *
2189  * Returns: the inbox folder for @store, or %NULL on error or if no such
2190  * folder exists
2191  *
2192  * Since: 3.0
2193  **/
2194 CamelFolder *
2195 camel_store_get_inbox_folder_sync (CamelStore *store,
2196                                    GCancellable *cancellable,
2197                                    GError **error)
2198 {
2199         CamelStoreClass *class;
2200         CamelFolder *folder;
2201
2202         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2203
2204         class = CAMEL_STORE_GET_CLASS (store);
2205         g_return_val_if_fail (class->get_inbox_folder_sync != NULL, NULL);
2206
2207         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2208
2209         /* Check for cancellation after locking. */
2210         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2211                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2212                 return NULL;
2213         }
2214
2215         folder = class->get_inbox_folder_sync (store, cancellable, error);
2216         CAMEL_CHECK_GERROR (
2217                 store, get_inbox_folder_sync, folder != NULL, error);
2218
2219         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2220
2221         return folder;
2222 }
2223
2224 /**
2225  * camel_store_get_inbox_folder:
2226  * @store: a #CamelStore
2227  * @io_priority: the I/O priority of the request
2228  * @cancellable: optional #GCancellable object, or %NULL
2229  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2230  * @user_data: data to pass to the callback function
2231  *
2232  * Asynchronously gets the folder in @store into which new mail is delivered.
2233  *
2234  * When the operation is finished, @callback will be called.  You can
2235  * then call camel_store_get_inbox_folder_finish() to get the result of
2236  * the operation.
2237  *
2238  * Since: 3.0
2239  **/
2240 void
2241 camel_store_get_inbox_folder (CamelStore *store,
2242                               gint io_priority,
2243                               GCancellable *cancellable,
2244                               GAsyncReadyCallback callback,
2245                               gpointer user_data)
2246 {
2247         CamelStoreClass *class;
2248
2249         g_return_if_fail (CAMEL_IS_STORE (store));
2250
2251         class = CAMEL_STORE_GET_CLASS (store);
2252         g_return_if_fail (class->get_inbox_folder != NULL);
2253
2254         class->get_inbox_folder (
2255                 store, io_priority, cancellable, callback, user_data);
2256 }
2257
2258 /**
2259  * camel_store_get_inbox_folder_finish:
2260  * @store: a #CamelStore
2261  * @result: a #GAsyncResult
2262  * @error: return location for a #GError, or %NULL
2263  *
2264  * Finishes the operation started with camel_store_get_inbox_folder().
2265  *
2266  * Returns: the inbox folder for @store, or %NULL on error or if no such
2267  * folder exists
2268  *
2269  * Since: 3.0
2270  **/
2271 CamelFolder *
2272 camel_store_get_inbox_folder_finish (CamelStore *store,
2273                                      GAsyncResult *result,
2274                                      GError **error)
2275 {
2276         CamelStoreClass *class;
2277
2278         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2279         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2280
2281         class = CAMEL_STORE_GET_CLASS (store);
2282         g_return_val_if_fail (class->get_inbox_folder_finish != NULL, NULL);
2283
2284         return class->get_inbox_folder_finish (store, result, error);
2285 }
2286
2287 /**
2288  * camel_store_get_junk_folder_sync:
2289  * @store: a #CamelStore
2290  * @cancellable: optional #GCancellable object, or %NULL
2291  * @error: return location for a #GError, or %NULL
2292  *
2293  * Gets the folder in @store into which junk is delivered.
2294  *
2295  * Returns: the junk folder for @store, or %NULL on error or if no such
2296  * folder exists
2297  *
2298  * Since: 3.0
2299  **/
2300 CamelFolder *
2301 camel_store_get_junk_folder_sync (CamelStore *store,
2302                                   GCancellable *cancellable,
2303                                   GError **error)
2304 {
2305         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2306
2307         if ((store->flags & CAMEL_STORE_VJUNK) == 0) {
2308                 CamelStoreClass *class;
2309                 CamelFolder *folder;
2310
2311                 class = CAMEL_STORE_GET_CLASS (store);
2312                 g_return_val_if_fail (class->get_junk_folder_sync != NULL, NULL);
2313
2314                 folder = class->get_junk_folder_sync (store, cancellable, error);
2315                 CAMEL_CHECK_GERROR (
2316                         store, get_junk_folder_sync, folder != NULL, error);
2317
2318                 return folder;
2319         }
2320
2321         return camel_store_get_folder_sync (
2322                 store, CAMEL_VJUNK_NAME, 0, cancellable, error);
2323 }
2324
2325 /**
2326  * camel_store_get_junk_folder:
2327  * @store: a #CamelStore
2328  * @io_priority: the I/O priority of the request
2329  * @cancellable: optional #GCancellable object, or %NULL
2330  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2331  * @user_data: data to pass to the callback function
2332  *
2333  * Asynchronously gets the folder in @store into which junk is delivered.
2334  *
2335  * When the operation is finished, @callback will be called.  You can
2336  * then call camel_store_get_junk_folder_finish() to get the result of
2337  * the operation.
2338  *
2339  * Since: 3.0
2340  **/
2341 void
2342 camel_store_get_junk_folder (CamelStore *store,
2343                              gint io_priority,
2344                              GCancellable *cancellable,
2345                              GAsyncReadyCallback callback,
2346                              gpointer user_data)
2347 {
2348         CamelStoreClass *class;
2349
2350         g_return_if_fail (CAMEL_IS_STORE (store));
2351
2352         class = CAMEL_STORE_GET_CLASS (store);
2353         g_return_if_fail (class->get_junk_folder != NULL);
2354
2355         class->get_junk_folder (
2356                 store, io_priority, cancellable, callback, user_data);
2357 }
2358
2359 /**
2360  * camel_store_get_junk_folder_finish:
2361  * @store: a #CamelStore
2362  * @result: a #GAsyncResult
2363  * @error: return location for a #GError, or %NULL
2364  *
2365  * Finishes the operation started with camel_store_get_junk_folder().
2366  *
2367  * Returns: the junk folder for @store, or %NULL on error or if no such
2368  * folder exists
2369  *
2370  * Since: 3.0
2371  **/
2372 CamelFolder *
2373 camel_store_get_junk_folder_finish (CamelStore *store,
2374                                     GAsyncResult *result,
2375                                     GError **error)
2376 {
2377         CamelStoreClass *class;
2378
2379         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2380         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2381
2382         class = CAMEL_STORE_GET_CLASS (store);
2383         g_return_val_if_fail (class->get_junk_folder_finish != NULL, NULL);
2384
2385         return class->get_junk_folder_finish (store, result, error);
2386 }
2387
2388 /**
2389  * camel_store_get_trash_folder_sync:
2390  * @store: a #CamelStore
2391  * @cancellable: optional #GCancellable object, or %NULL
2392  * @error: return location for a #GError, or %NULL
2393  *
2394  * Gets the folder in @store into which trash is delivered.
2395  *
2396  * Returns: the trash folder for @store, or %NULL on error or if no such
2397  * folder exists
2398  *
2399  * Since: 3.0
2400  **/
2401 CamelFolder *
2402 camel_store_get_trash_folder_sync (CamelStore *store,
2403                                    GCancellable *cancellable,
2404                                    GError **error)
2405 {
2406         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2407
2408         if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
2409                 CamelStoreClass *class;
2410                 CamelFolder *folder;
2411
2412                 class = CAMEL_STORE_GET_CLASS (store);
2413                 g_return_val_if_fail (class->get_trash_folder_sync != NULL, NULL);
2414
2415                 folder = class->get_trash_folder_sync (
2416                         store, cancellable, error);
2417                 CAMEL_CHECK_GERROR (
2418                         store, get_trash_folder_sync, folder != NULL, error);
2419
2420                 return folder;
2421         }
2422
2423         return camel_store_get_folder_sync (
2424                 store, CAMEL_VTRASH_NAME, 0, cancellable, error);
2425 }
2426
2427 /**
2428  * camel_store_get_trash_folder:
2429  * @store: a #CamelStore
2430  * @io_priority: the I/O priority of the request
2431  * @cancellable: optional #GCancellable object, or %NULL
2432  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2433  * @user_data: data to pass to the callback function
2434  *
2435  * Asynchronously gets the folder in @store into which trash is delivered.
2436  *
2437  * When the operation is finished, @callback will be called.  You can
2438  * then call camel_store_get_trash_folder_finish() to get the result of
2439  * the operation.
2440  *
2441  * Since: 3.0
2442  **/
2443 void
2444 camel_store_get_trash_folder (CamelStore *store,
2445                               gint io_priority,
2446                               GCancellable *cancellable,
2447                               GAsyncReadyCallback callback,
2448                               gpointer user_data)
2449 {
2450         CamelStoreClass *class;
2451
2452         g_return_if_fail (CAMEL_IS_STORE (store));
2453
2454         class = CAMEL_STORE_GET_CLASS (store);
2455         g_return_if_fail (class->get_trash_folder != NULL);
2456
2457         class->get_trash_folder (
2458                 store, io_priority, cancellable, callback, user_data);
2459 }
2460
2461 /**
2462  * camel_store_get_trash_folder_finish:
2463  * @store: a #CamelStore
2464  * @result: a #GAsyncResult
2465  * @error: return location for a #GError, or %NULL
2466  *
2467  * Finishes the operation started with camel_store_get_trash_folder().
2468  *
2469  * Returns: the trash folder for @store, or %NULL on error or if no such
2470  * folder exists
2471  *
2472  * Since: 3.0
2473  **/
2474 CamelFolder *
2475 camel_store_get_trash_folder_finish (CamelStore *store,
2476                                      GAsyncResult *result,
2477                                      GError **error)
2478 {
2479         CamelStoreClass *class;
2480
2481         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2482         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2483
2484         class = CAMEL_STORE_GET_CLASS (store);
2485         g_return_val_if_fail (class->get_trash_folder_finish != NULL, NULL);
2486
2487         return class->get_trash_folder_finish (store, result, error);
2488 }
2489
2490 /**
2491  * camel_store_create_folder_sync:
2492  * @store: a #CamelStore
2493  * @parent_name: name of the new folder's parent, or %NULL
2494  * @folder_name: name of the folder to create
2495  * @cancellable: optional #GCancellable object, or %NULL
2496  * @error: return location for a #GError, or %NULL
2497  *
2498  * Creates a new folder as a child of an existing folder.
2499  * @parent_name can be %NULL to create a new top-level folder.
2500  * The returned #CamelFolderInfo struct should be freed with
2501  * camel_store_free_folder_info().
2502  *
2503  * Returns: info about the created folder, or %NULL on error
2504  *
2505  * Since: 3.0
2506  **/
2507 CamelFolderInfo *
2508 camel_store_create_folder_sync (CamelStore *store,
2509                                 const gchar *parent_name,
2510                                 const gchar *folder_name,
2511                                 GCancellable *cancellable,
2512                                 GError **error)
2513 {
2514         CamelStoreClass *class;
2515         CamelFolderInfo *fi;
2516
2517         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2518         g_return_val_if_fail (folder_name != NULL, NULL);
2519
2520         class = CAMEL_STORE_GET_CLASS (store);
2521         g_return_val_if_fail (class->create_folder_sync != NULL, NULL);
2522
2523         if ((parent_name == NULL || parent_name[0] == 0)
2524             && (((store->flags & CAMEL_STORE_VTRASH) && strcmp (folder_name, CAMEL_VTRASH_NAME) == 0)
2525                 || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (folder_name, CAMEL_VJUNK_NAME) == 0))) {
2526                 g_set_error (
2527                         error, CAMEL_STORE_ERROR,
2528                         CAMEL_STORE_ERROR_INVALID,
2529                         _("Cannot create folder: %s: folder exists"),
2530                         folder_name);
2531                 return NULL;
2532         }
2533
2534         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2535
2536         /* Check for cancellation after locking. */
2537         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2538                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2539                 return NULL;
2540         }
2541
2542         camel_operation_push_message (
2543                 cancellable, _("Creating folder '%s'"), folder_name);
2544
2545         fi = class->create_folder_sync (
2546                 store, parent_name, folder_name, cancellable, error);
2547         CAMEL_CHECK_GERROR (store, create_folder_sync, fi != NULL, error);
2548
2549         camel_operation_pop_message (cancellable);
2550
2551         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2552
2553         return fi;
2554 }
2555
2556 /**
2557  * camel_store_create_folder:
2558  * @store: a #CamelStore
2559  * @parent_name: name of the new folder's parent, or %NULL
2560  * @folder_name: name of the folder to create
2561  * @io_priority: the I/O priority of the request
2562  * @cancellable: optional #GCancellable object, or %NULL
2563  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2564  * @user_data: data to pass to the callback function
2565  *
2566  * Asynchronously creates a new folder as a child of an existing folder.
2567  * @parent_name can be %NULL to create a new top-level folder.
2568  *
2569  * When the operation is finished, @callback will be called.  You can then
2570  * call camel_store_create_folder_finish() to get the result of the operation.
2571  *
2572  * Since: 3.0
2573  **/
2574 void
2575 camel_store_create_folder (CamelStore *store,
2576                            const gchar *parent_name,
2577                            const gchar *folder_name,
2578                            gint io_priority,
2579                            GCancellable *cancellable,
2580                            GAsyncReadyCallback callback,
2581                            gpointer user_data)
2582 {
2583         CamelStoreClass *class;
2584
2585         g_return_if_fail (CAMEL_IS_STORE (store));
2586         g_return_if_fail (folder_name != NULL);
2587
2588         class = CAMEL_STORE_GET_CLASS (store);
2589         g_return_if_fail (class->create_folder != NULL);
2590
2591         class->create_folder (
2592                 store, parent_name, folder_name, io_priority,
2593                 cancellable, callback, user_data);
2594 }
2595
2596 /**
2597  * camel_store_create_folder_finish:
2598  * @store: a #CamelStore
2599  * @result: a #GAsyncResult
2600  * @error: return location for a #GError, or %NULL
2601  *
2602  * Finishes the operation started with camel_store_create_folder().
2603  * The returned #CamelFolderInfo struct should be freed with
2604  * camel_store_free_folder_info().
2605  *
2606  * Returns: info about the created folder, or %NULL on error
2607  *
2608  * Since: 3.0
2609  **/
2610 CamelFolderInfo *
2611 camel_store_create_folder_finish (CamelStore *store,
2612                                   GAsyncResult *result,
2613                                   GError **error)
2614 {
2615         CamelStoreClass *class;
2616
2617         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2618         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2619
2620         class = CAMEL_STORE_GET_CLASS (store);
2621         g_return_val_if_fail (class->create_folder_finish != NULL, NULL);
2622
2623         return class->create_folder_finish (store, result, error);
2624 }
2625
2626 /**
2627  * camel_store_delete_folder_sync:
2628  * @store: a #CamelStore
2629  * @folder_name: name of the folder to delete
2630  * @cancellable: optional #GCancellable object, or %NULL
2631  * @error: return location for a #GError, or %NULL
2632  *
2633  * Deletes the folder described by @folder_name.  The folder must be empty.
2634  *
2635  * Returns: %TRUE on success, %FALSE on failure
2636  *
2637  * Since: 3.0
2638  **/
2639 gboolean
2640 camel_store_delete_folder_sync (CamelStore *store,
2641                                 const gchar *folder_name,
2642                                 GCancellable *cancellable,
2643                                 GError **error)
2644 {
2645         CamelStoreClass *class;
2646         gboolean success;
2647         GError *local_error = NULL, **check_error = NULL;
2648
2649         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2650         g_return_val_if_fail (folder_name != NULL, FALSE);
2651
2652         class = CAMEL_STORE_GET_CLASS (store);
2653         g_return_val_if_fail (class->delete_folder_sync != NULL, FALSE);
2654
2655         /* TODO: should probably be a parameter/bit on the storeinfo */
2656         if (((store->flags & CAMEL_STORE_VTRASH) && strcmp (folder_name, CAMEL_VTRASH_NAME) == 0)
2657             || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (folder_name, CAMEL_VJUNK_NAME) == 0)) {
2658                 g_set_error (
2659                         error, CAMEL_STORE_ERROR,
2660                         CAMEL_STORE_ERROR_NO_FOLDER,
2661                         _("Cannot delete folder: %s: Invalid operation"),
2662                         folder_name);
2663                 return FALSE;
2664         }
2665
2666         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2667
2668         /* Check for cancellation after locking. */
2669         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2670                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2671                 return FALSE;
2672         }
2673
2674         success = class->delete_folder_sync (
2675                 store, folder_name, cancellable, &local_error);
2676         if (local_error)
2677                 check_error = &local_error;
2678         CAMEL_CHECK_GERROR (store, delete_folder_sync, success, check_error);
2679
2680         /* ignore 'no such table' errors */
2681         if (local_error != NULL &&
2682             g_ascii_strncasecmp (local_error->message, "no such table", 13) == 0)
2683                 g_clear_error (&local_error);
2684
2685         if (local_error == NULL)
2686                 cs_delete_cached_folder (store, folder_name);
2687         else
2688                 g_propagate_error (error, local_error);
2689
2690         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2691
2692         return success;
2693 }
2694
2695 /**
2696  * camel_store_delete_folder:
2697  * @store: a #CamelStore
2698  * @folder_name: name of the folder to delete
2699  * @io_priority: the I/O priority of the request
2700  * @cancellable: optional #GCancellable object, or %NULL
2701  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2702  * @user_data: data to pass to the callback function
2703  *
2704  * Asynchronously deletes the folder described by @folder_name.  The
2705  * folder must be empty.
2706  *
2707  * When the operation is finished, @callback will be called.  You can then
2708  * call camel_store_delete_folder_finish() to get the result of the operation.
2709  *
2710  * Since: 3.0
2711  **/
2712 void
2713 camel_store_delete_folder (CamelStore *store,
2714                            const gchar *folder_name,
2715                            gint io_priority,
2716                            GCancellable *cancellable,
2717                            GAsyncReadyCallback callback,
2718                            gpointer user_data)
2719 {
2720         CamelStoreClass *class;
2721
2722         g_return_if_fail (CAMEL_IS_STORE (store));
2723         g_return_if_fail (folder_name != NULL);
2724
2725         class = CAMEL_STORE_GET_CLASS (store);
2726         g_return_if_fail (class->delete_folder != NULL);
2727
2728         class->delete_folder (
2729                 store, folder_name, io_priority,
2730                 cancellable, callback, user_data);
2731 }
2732
2733 /**
2734  * camel_store_delete_folder_finish:
2735  * @store: a #CamelStore
2736  * @result: a #GAsyncResult
2737  * @error: return location for a #GError, or %NULL
2738  *
2739  * Finishes the operation started with camel_store_delete_folder().
2740  *
2741  * Returns: %TRUE on success, %FALSE on error
2742  *
2743  * Since: 3.0
2744  **/
2745 gboolean
2746 camel_store_delete_folder_finish (CamelStore *store,
2747                                   GAsyncResult *result,
2748                                   GError **error)
2749 {
2750         CamelStoreClass *class;
2751
2752         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2753         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2754
2755         class = CAMEL_STORE_GET_CLASS (store);
2756         g_return_val_if_fail (class->delete_folder_finish != NULL, FALSE);
2757
2758         return class->delete_folder_finish (store, result, error);
2759 }
2760
2761 /**
2762  * camel_store_rename_folder_sync:
2763  * @store: a #CamelStore
2764  * @old_name: the current name of the folder
2765  * @new_name: the new name of the folder
2766  * @cancellable: optional #GCancellable object, or %NULL
2767  * @error: return location for a #GError, or %NULL
2768  *
2769  * Renames the folder described by @old_name to @new_name.
2770  *
2771  * Returns: %TRUE on success, %FALSE on error
2772  *
2773  * Since: 3.0
2774  **/
2775 gboolean
2776 camel_store_rename_folder_sync (CamelStore *store,
2777                                 const gchar *old_namein,
2778                                 const gchar *new_name,
2779                                 GCancellable *cancellable,
2780                                 GError **error)
2781 {
2782         CamelStoreClass *class;
2783         CamelFolder *folder;
2784         gint i, oldlen, namelen;
2785         GPtrArray *folders = NULL;
2786         gchar *old_name;
2787         gboolean success;
2788
2789         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2790         g_return_val_if_fail (old_namein != NULL, FALSE);
2791         g_return_val_if_fail (new_name != NULL, FALSE);
2792
2793         class = CAMEL_STORE_GET_CLASS (store);
2794         g_return_val_if_fail (class->rename_folder_sync != NULL, FALSE);
2795
2796         if (strcmp (old_namein, new_name) == 0)
2797                 return TRUE;
2798
2799         if (((store->flags & CAMEL_STORE_VTRASH) && strcmp (old_namein, CAMEL_VTRASH_NAME) == 0)
2800             || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (old_namein, CAMEL_VJUNK_NAME) == 0)) {
2801                 g_set_error (
2802                         error, CAMEL_STORE_ERROR,
2803                         CAMEL_STORE_ERROR_NO_FOLDER,
2804                         _("Cannot rename folder: %s: Invalid operation"),
2805                         old_namein);
2806                 return FALSE;
2807         }
2808
2809         /* need to save this, since old_namein might be folder->full_name, which could go away */
2810         old_name = g_strdup (old_namein);
2811         oldlen = strlen (old_name);
2812
2813         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2814
2815         /* Check for cancellation after locking. */
2816         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2817                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2818                 return FALSE;
2819         }
2820
2821         /* If the folder is open (or any subfolders of the open folder)
2822          * We need to rename them atomically with renaming the actual
2823          * folder path. */
2824         folders = camel_object_bag_list (store->folders);
2825         for (i = 0; folders && i < folders->len; i++) {
2826                 const gchar *full_name;
2827
2828                 folder = folders->pdata[i];
2829                 full_name = camel_folder_get_full_name (folder);
2830
2831                 namelen = strlen (full_name);
2832                 if ((namelen == oldlen &&
2833                      strcmp (full_name, old_name) == 0)
2834                     || ((namelen > oldlen)
2835                         && strncmp (full_name, old_name, oldlen) == 0
2836                         && full_name[oldlen] == '/')) {
2837                         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
2838                 } else {
2839                         g_ptr_array_remove_index_fast (folders, i);
2840                         i--;
2841                         g_object_unref (folder);
2842                 }
2843         }
2844
2845         /* Now try the real rename (will emit renamed signal) */
2846         success = class->rename_folder_sync (
2847                 store, old_name, new_name, cancellable, error);
2848         CAMEL_CHECK_GERROR (store, rename_folder_sync, success, error);
2849
2850         /* If it worked, update all open folders/unlock them */
2851         if (folders) {
2852                 if (success) {
2853                         CamelStoreGetFolderInfoFlags flags;
2854                         CamelFolderInfo *folder_info;
2855
2856                         flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE;
2857
2858                         for (i = 0; i < folders->len; i++) {
2859                                 const gchar *full_name;
2860                                 gchar *new;
2861
2862                                 folder = folders->pdata[i];
2863                                 full_name = camel_folder_get_full_name (folder);
2864
2865                                 new = g_strdup_printf ("%s%s", new_name, full_name + strlen (old_name));
2866                                 camel_object_bag_rekey (store->folders, folder, new);
2867                                 camel_folder_rename (folder, new);
2868                                 g_free (new);
2869
2870                                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2871                                 g_object_unref (folder);
2872                         }
2873
2874                         /* Emit renamed signal */
2875                         if (CAMEL_IS_SUBSCRIBABLE (store))
2876                                 flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
2877
2878                         folder_info = class->get_folder_info_sync (
2879                                 store, new_name, flags, cancellable, error);
2880                         CAMEL_CHECK_GERROR (store, get_folder_info, folder_info != NULL, error);
2881
2882                         if (folder_info != NULL) {
2883                                 camel_store_folder_renamed (store, old_name, folder_info);
2884                                 class->free_folder_info (store, folder_info);
2885                         }
2886                 } else {
2887                         /* Failed, just unlock our folders for re-use */
2888                         for (i = 0; i < folders->len; i++) {
2889                                 folder = folders->pdata[i];
2890                                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2891                                 g_object_unref (folder);
2892                         }
2893                 }
2894         }
2895
2896         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2897
2898         g_ptr_array_free (folders, TRUE);
2899         g_free (old_name);
2900
2901         return success;
2902 }
2903
2904 /**
2905  * camel_store_rename_folder:
2906  * @store: a #CamelStore
2907  * @old_name: the current name of the folder
2908  * @new_name: the new name of the folder
2909  * @io_priority: the I/O priority of the request
2910  * @cancellable: optional #GCancellable object, or %NULL
2911  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2912  * @user_data: data to pass to the callback function
2913  *
2914  * Asynchronously renames the folder described by @old_name to @new_name.
2915  *
2916  * When the operation is finished, @callback will be called.  You can then
2917  * call camel_store_rename_folder_finish() to get the result of the operation.
2918  *
2919  * Since: 3.0
2920  **/
2921 void
2922 camel_store_rename_folder (CamelStore *store,
2923                            const gchar *old_name,
2924                            const gchar *new_name,
2925                            gint io_priority,
2926                            GCancellable *cancellable,
2927                            GAsyncReadyCallback callback,
2928                            gpointer user_data)
2929 {
2930         CamelStoreClass *class;
2931
2932         g_return_if_fail (CAMEL_IS_STORE (store));
2933         g_return_if_fail (old_name != NULL);
2934         g_return_if_fail (new_name != NULL);
2935
2936         class = CAMEL_STORE_GET_CLASS (store);
2937         g_return_if_fail (class->rename_folder != NULL);
2938
2939         class->rename_folder (
2940                 store, old_name, new_name, io_priority,
2941                 cancellable, callback, user_data);
2942 }
2943
2944 /**
2945  * camel_store_rename_folder_finish:
2946  * @store: a #CamelStore
2947  * @result: a #GAsyncResult
2948  * @error: return location for a #GError, or %NULL
2949  *
2950  * Finishes the operation started with camel_store_rename_folder().
2951  *
2952  * Returns: %TRUE on success, %FALSE on error
2953  *
2954  * Since: 3.0
2955  **/
2956 gboolean
2957 camel_store_rename_folder_finish (CamelStore *store,
2958                                   GAsyncResult *result,
2959                                   GError **error)
2960 {
2961         CamelStoreClass *class;
2962
2963         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2964         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2965
2966         class = CAMEL_STORE_GET_CLASS (store);
2967         g_return_val_if_fail (class->rename_folder_finish != NULL, FALSE);
2968
2969         return class->rename_folder_finish (store, result, error);
2970 }
2971
2972 /**
2973  * camel_store_synchronize_sync:
2974  * @store: a #CamelStore
2975  * @expunge: whether to expunge after synchronizing
2976  * @cancellable: optional #GCancellable object, or %NULL
2977  * @error: return location for a #GError, or %NULL
2978  *
2979  * Synchronizes any changes that have been made to @store and its folders
2980  * with the real store.
2981  *
2982  * Returns: %TRUE on success, %FALSE on error
2983  *
2984  * Since: 3.0
2985  **/
2986 gboolean
2987 camel_store_synchronize_sync (CamelStore *store,
2988                               gboolean expunge,
2989                               GCancellable *cancellable,
2990                               GError **error)
2991 {
2992         CamelStoreClass *class;
2993         gboolean success;
2994
2995         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2996
2997         class = CAMEL_STORE_GET_CLASS (store);
2998         g_return_val_if_fail (class->synchronize_sync != NULL, FALSE);
2999
3000         success = class->synchronize_sync (store, expunge, cancellable, error);
3001         CAMEL_CHECK_GERROR (store, synchronize_sync, success, error);
3002
3003         return success;
3004 }
3005
3006 /**
3007  * camel_store_synchronize:
3008  * @store: a #CamelStore
3009  * @expunge: whether to expunge after synchronizing
3010  * @io_priority: the I/O priority of the request
3011  * @cancellable: optional #GCancellable object, or %NULL
3012  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3013  * @user_data: data to pass to the callback function
3014  *
3015  * Synchronizes any changes that have been made to @store and its folders
3016  * with the real store asynchronously.
3017  *
3018  * When the operation is finished, @callback will be called.  You can then
3019  * call camel_store_synchronize_finish() to get the result of the operation.
3020  *
3021  * Since: 3.0
3022  **/
3023 void
3024 camel_store_synchronize (CamelStore *store,
3025                          gboolean expunge,
3026                          gint io_priority,
3027                          GCancellable *cancellable,
3028                          GAsyncReadyCallback callback,
3029                          gpointer user_data)
3030 {
3031         CamelStoreClass *class;
3032
3033         g_return_if_fail (CAMEL_IS_STORE (store));
3034
3035         class = CAMEL_STORE_GET_CLASS (store);
3036         g_return_if_fail (class->synchronize != NULL);
3037
3038         class->synchronize (
3039                 store, expunge, io_priority,
3040                 cancellable, callback, user_data);
3041 }
3042
3043 /**
3044  * camel_store_synchronize_finish:
3045  * @store: a #CamelStore
3046  * @result: a #GAsyncResult
3047  * @error: return location for a #GError, or %NULL
3048  *
3049  * Finishes the operation started with camel_store_synchronize().
3050  *
3051  * Returns: %TRUE on success, %FALSE on error
3052  *
3053  * Since: 3.0
3054  **/
3055 gboolean
3056 camel_store_synchronize_finish (CamelStore *store,
3057                                 GAsyncResult *result,
3058                                 GError **error)
3059 {
3060         CamelStoreClass *class;
3061
3062         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3063         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3064
3065         class = CAMEL_STORE_GET_CLASS (store);
3066         g_return_val_if_fail (class->synchronize_finish != NULL, FALSE);
3067
3068         return class->synchronize_finish (store, result, error);
3069 }
3070
3071 /**
3072  * camel_store_noop_sync:
3073  * @store: a #CamelStore
3074  * @cancellable: optional #GCancellable object, or %NULL
3075  * @error: return location for a #GError, or %NULL
3076  *
3077  * Pings @store so its connection doesn't time out.
3078  *
3079  * Returns: %TRUE on success, %FALSE on error
3080  *
3081  * Since: 3.0
3082  **/
3083 gboolean
3084 camel_store_noop_sync (CamelStore *store,
3085                        GCancellable *cancellable,
3086                        GError **error)
3087 {
3088         CamelStoreClass *class;
3089         gboolean success;
3090
3091         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3092
3093         class = CAMEL_STORE_GET_CLASS (store);
3094         g_return_val_if_fail (class->noop_sync != NULL, FALSE);
3095
3096         success = class->noop_sync (store, cancellable, error);
3097         CAMEL_CHECK_GERROR (store, noop_sync, success, error);
3098
3099         return success;
3100 }
3101
3102 /**
3103  * camel_store_noop:
3104  * @store: a #CamelStore
3105  * @io_priority: the I/O priority of the request
3106  * @cancellable: optional #GCancellable object, or %NULL
3107  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3108  * @user_data: data to pass to the callback function
3109  *
3110  * Pings @store asynchronously so its connection doesn't time out.
3111  *
3112  * When the operation is finished, @callback will be called.  You can then
3113  * call camel_store_noop_finish() to get the result of the operation.
3114  *
3115  * Since: 3.0
3116  **/
3117 void
3118 camel_store_noop (CamelStore *store,
3119                   gint io_priority,
3120                   GCancellable *cancellable,
3121                   GAsyncReadyCallback callback,
3122                   gpointer user_data)
3123 {
3124         CamelStoreClass *class;
3125
3126         g_return_if_fail (CAMEL_IS_STORE (store));
3127
3128         class = CAMEL_STORE_GET_CLASS (store);
3129         g_return_if_fail (class->noop != NULL);
3130
3131         class->noop (store, io_priority, cancellable, callback, user_data);
3132 }
3133
3134 /**
3135  * camel_store_noop_finish:
3136  * @store: a #CamelStore
3137  * @result: a #GAsyncResult
3138  * @error: return location for a #GError, or %NULL
3139  *
3140  * Finishes the operation started with camel_store_noop().
3141  *
3142  * Returns: %TRUE on success, %FALSE on error
3143  *
3144  * Since: 3.0
3145  **/
3146 gboolean
3147 camel_store_noop_finish (CamelStore *store,
3148                          GAsyncResult *result,
3149                          GError **error)
3150 {
3151         CamelStoreClass *class;
3152
3153         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3154         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3155
3156         class = CAMEL_STORE_GET_CLASS (store);
3157         g_return_val_if_fail (class->noop_finish != NULL, FALSE);
3158
3159         return class->noop_finish (store, result, error);
3160 }