Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[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 GQuark
1248 camel_store_error_quark (void)
1249 {
1250         static GQuark quark = 0;
1251
1252         if (G_UNLIKELY (quark == 0)) {
1253                 const gchar *string = "camel-store-error-quark";
1254                 quark = g_quark_from_static_string (string);
1255         }
1256
1257         return quark;
1258 }
1259
1260 /**
1261  * camel_store_folder_created:
1262  * @store: a #CamelStore
1263  * @folder_info: information about the created folder
1264  *
1265  * Emits the #CamelStore::folder-created signal from an idle source on
1266  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1267  *
1268  * This function is only intended for Camel providers.
1269  *
1270  * Since: 2.32
1271  **/
1272 void
1273 camel_store_folder_created (CamelStore *store,
1274                             CamelFolderInfo *folder_info)
1275 {
1276         CamelSession *session;
1277         SignalData *signal_data;
1278
1279         g_return_if_fail (CAMEL_IS_STORE (store));
1280         g_return_if_fail (folder_info != NULL);
1281
1282         session = camel_service_get_session (CAMEL_SERVICE (store));
1283
1284         signal_data = g_slice_new0 (SignalData);
1285         signal_data->store = g_object_ref (store);
1286         signal_data->folder_info = camel_folder_info_clone (folder_info);
1287
1288         camel_session_idle_add (
1289                 session, G_PRIORITY_DEFAULT_IDLE,
1290                 store_emit_folder_created_cb,
1291                 signal_data, (GDestroyNotify) signal_data_free);
1292 }
1293
1294 /**
1295  * camel_store_folder_deleted:
1296  * @store: a #CamelStore
1297  * @folder_info: information about the deleted folder
1298  *
1299  * Emits the #CamelStore::folder-deleted signal from an idle source on
1300  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1301  *
1302  * This function is only intended for Camel providers.
1303  *
1304  * Since: 2.32
1305  **/
1306 void
1307 camel_store_folder_deleted (CamelStore *store,
1308                             CamelFolderInfo *folder_info)
1309 {
1310         CamelSession *session;
1311         SignalData *signal_data;
1312
1313         g_return_if_fail (CAMEL_IS_STORE (store));
1314         g_return_if_fail (folder_info != NULL);
1315
1316         session = camel_service_get_session (CAMEL_SERVICE (store));
1317
1318         signal_data = g_slice_new0 (SignalData);
1319         signal_data->store = g_object_ref (store);
1320         signal_data->folder_info = camel_folder_info_clone (folder_info);
1321
1322         camel_session_idle_add (
1323                 session, G_PRIORITY_DEFAULT_IDLE,
1324                 store_emit_folder_deleted_cb,
1325                 signal_data, (GDestroyNotify) signal_data_free);
1326 }
1327
1328 /**
1329  * camel_store_folder_opened:
1330  * @store: a #CamelStore
1331  * @folder: the #CamelFolder that was opened
1332  *
1333  * Emits the #CamelStore::folder-opened signal from an idle source on
1334  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1335  *
1336  * This function is only intended for Camel providers.
1337  *
1338  * Since: 3.0
1339  **/
1340 void
1341 camel_store_folder_opened (CamelStore *store,
1342                            CamelFolder *folder)
1343 {
1344         CamelSession *session;
1345         SignalData *signal_data;
1346
1347         g_return_if_fail (CAMEL_IS_STORE (store));
1348         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1349
1350         session = camel_service_get_session (CAMEL_SERVICE (store));
1351
1352         signal_data = g_slice_new0 (SignalData);
1353         signal_data->store = g_object_ref (store);
1354         signal_data->folder = g_object_ref (folder);
1355
1356         camel_session_idle_add (
1357                 session, G_PRIORITY_DEFAULT_IDLE,
1358                 store_emit_folder_opened_cb,
1359                 signal_data, (GDestroyNotify) signal_data_free);
1360 }
1361
1362 /**
1363  * camel_store_folder_renamed:
1364  * @store: a #CamelStore
1365  * @old_name: the old name of the folder
1366  * @folder_info: information about the renamed folder
1367  *
1368  * Emits the #CamelStore::folder-renamed signal from an idle source on
1369  * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
1370  *
1371  * This function is only intended for Camel providers.
1372  *
1373  * Since: 2.32
1374  **/
1375 void
1376 camel_store_folder_renamed (CamelStore *store,
1377                             const gchar *old_name,
1378                             CamelFolderInfo *folder_info)
1379 {
1380         CamelSession *session;
1381         SignalData *signal_data;
1382
1383         g_return_if_fail (CAMEL_IS_STORE (store));
1384         g_return_if_fail (old_name != NULL);
1385         g_return_if_fail (folder_info != NULL);
1386
1387         session = camel_service_get_session (CAMEL_SERVICE (store));
1388
1389         signal_data = g_slice_new0 (SignalData);
1390         signal_data->store = g_object_ref (store);
1391         signal_data->folder_info = camel_folder_info_clone (folder_info);
1392         signal_data->folder_name = g_strdup (old_name);
1393
1394         camel_session_idle_add (
1395                 session, G_PRIORITY_DEFAULT_IDLE,
1396                 store_emit_folder_renamed_cb,
1397                 signal_data, (GDestroyNotify) signal_data_free);
1398 }
1399
1400 static void
1401 add_special_info (CamelStore *store,
1402                   CamelFolderInfo *info,
1403                   const gchar *name,
1404                   const gchar *translated,
1405                   gboolean unread_count,
1406                   CamelFolderInfoFlags flags)
1407 {
1408         CamelFolderInfo *fi, *vinfo, *parent;
1409
1410         g_return_if_fail (CAMEL_IS_STORE (store));
1411         g_return_if_fail (info != NULL);
1412
1413         parent = NULL;
1414         for (fi = info; fi; fi = fi->next) {
1415                 if (!strcmp (fi->full_name, name))
1416                         break;
1417                 parent = fi;
1418         }
1419
1420         if (fi) {
1421                 /* We're going to replace the physical Trash/Junk
1422                  * folder with our vTrash/vJunk folder. */
1423                 vinfo = fi;
1424                 g_free (vinfo->full_name);
1425                 g_free (vinfo->display_name);
1426         } else {
1427                 /* There wasn't a Trash/Junk folder so create a new
1428                  * folder entry. */
1429                 vinfo = camel_folder_info_new ();
1430
1431                 g_assert (parent != NULL);
1432
1433                 vinfo->flags |=
1434                         CAMEL_FOLDER_NOINFERIORS |
1435                         CAMEL_FOLDER_SUBSCRIBED;
1436
1437                 /* link it into the right spot */
1438                 vinfo->next = parent->next;
1439                 parent->next = vinfo;
1440         }
1441
1442         /* Fill in the new fields */
1443         vinfo->flags |= flags;
1444         vinfo->full_name = g_strdup (name);
1445         vinfo->display_name = g_strdup (translated);
1446
1447         if (!unread_count)
1448                 vinfo->unread = -1;
1449 }
1450
1451 static void
1452 dump_fi (CamelFolderInfo *fi,
1453          gint depth)
1454 {
1455         gchar *s;
1456
1457         s = g_alloca (depth + 1);
1458         memset (s, ' ', depth);
1459         s[depth] = 0;
1460
1461         while (fi) {
1462                 printf ("%sfull_name: %s\n", s, fi->full_name);
1463                 printf ("%sflags: %08x\n", s, fi->flags);
1464                 dump_fi (fi->child, depth + 2);
1465                 fi = fi->next;
1466         }
1467 }
1468
1469 /**
1470  * camel_store_free_folder_info:
1471  * @store: a #CamelStore
1472  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1473  *
1474  * Frees the data returned by camel_store_get_folder_info(). If @fi is %NULL,
1475  * nothing is done, the routine simply returns.
1476  **/
1477 void
1478 camel_store_free_folder_info (CamelStore *store,
1479                               CamelFolderInfo *fi)
1480 {
1481         CamelStoreClass *class;
1482
1483         g_return_if_fail (CAMEL_IS_STORE (store));
1484
1485         if (fi == NULL)
1486                 return;
1487
1488         class = CAMEL_STORE_GET_CLASS (store);
1489         g_return_if_fail (class->free_folder_info != NULL);
1490
1491         class->free_folder_info (store, fi);
1492 }
1493
1494 /**
1495  * camel_store_free_folder_info_full:
1496  * @store: a #CamelStore
1497  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1498  *
1499  * An implementation for #CamelStore::free_folder_info. Frees all
1500  * of the data.
1501  **/
1502 void
1503 camel_store_free_folder_info_full (CamelStore *store,
1504                                    CamelFolderInfo *fi)
1505 {
1506         camel_folder_info_free (fi);
1507 }
1508
1509 /**
1510  * camel_store_free_folder_info_nop:
1511  * @store: a #CamelStore
1512  * @fi: a #CamelFolderInfo as gotten via camel_store_get_folder_info()
1513  *
1514  * An implementation for #CamelStore::free_folder_info. Does nothing.
1515  **/
1516 void
1517 camel_store_free_folder_info_nop (CamelStore *store,
1518                                   CamelFolderInfo *fi)
1519 {
1520         ;
1521 }
1522
1523 /**
1524  * camel_folder_info_free:
1525  * @fi: a #CamelFolderInfo
1526  *
1527  * Frees @fi.
1528  **/
1529 void
1530 camel_folder_info_free (CamelFolderInfo *fi)
1531 {
1532         if (fi != NULL) {
1533                 camel_folder_info_free (fi->next);
1534                 camel_folder_info_free (fi->child);
1535                 g_free (fi->full_name);
1536                 g_free (fi->display_name);
1537                 g_slice_free (CamelFolderInfo, fi);
1538         }
1539 }
1540
1541 /**
1542  * camel_folder_info_new:
1543  *
1544  * Allocates a new #CamelFolderInfo instance.  Free it with
1545  * camel_folder_info_free().
1546  *
1547  * Returns: a new #CamelFolderInfo instance
1548  *
1549  * Since: 2.22
1550  **/
1551 CamelFolderInfo *
1552 camel_folder_info_new (void)
1553 {
1554         return g_slice_new0 (CamelFolderInfo);
1555 }
1556
1557 static gint
1558 folder_info_cmp (gconstpointer ap,
1559                  gconstpointer bp)
1560 {
1561         const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
1562         const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];
1563
1564         return strcmp (a->full_name, b->full_name);
1565 }
1566
1567 /**
1568  * camel_folder_info_build:
1569  * @folders: an array of #CamelFolderInfo
1570  * @namespace_: an ignorable prefix on the folder names
1571  * @separator: the hieararchy separator character
1572  * @short_names: %TRUE if the (short) name of a folder is the part after
1573  * the last @separator in the full name. %FALSE if it is the full name.
1574  *
1575  * This takes an array of folders and attaches them together according
1576  * to the hierarchy described by their full_names and @separator. If
1577  * @namespace_ is non-%NULL, then it will be ignored as a full_name
1578  * prefix, for purposes of comparison. If necessary,
1579  * camel_folder_info_build() will create additional #CamelFolderInfo with
1580  * %NULL urls to fill in gaps in the tree. The value of @short_names
1581  * is used in constructing the names of these intermediate folders.
1582  *
1583  * NOTE: This is deprected, do not use this.
1584  * FIXME: remove this/move it to imap, which is the only user of it now.
1585  *
1586  * Returns: the top level of the tree of linked folder info.
1587  **/
1588 CamelFolderInfo *
1589 camel_folder_info_build (GPtrArray *folders,
1590                          const gchar *namespace_,
1591                          gchar separator,
1592                          gboolean short_names)
1593 {
1594         CamelFolderInfo *fi, *pfi, *top = NULL, *tail = NULL;
1595         GHashTable *hash;
1596         gchar *p, *pname;
1597         gint i, nlen;
1598
1599         if (namespace_ == NULL)
1600                 namespace_ = "";
1601         nlen = strlen (namespace_);
1602
1603         qsort (folders->pdata, folders->len, sizeof (folders->pdata[0]), folder_info_cmp);
1604
1605         /* Hash the folders. */
1606         hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1607         for (i = 0; i < folders->len; i++) {
1608                 fi = folders->pdata[i];
1609                 g_hash_table_insert (hash, g_strdup (fi->full_name), fi);
1610         }
1611
1612         /* Now find parents. */
1613         for (i = 0; i < folders->len; i++) {
1614                 fi = folders->pdata[i];
1615                 if (!strncmp (namespace_, fi->full_name, nlen)
1616                     && (p = strrchr (fi->full_name + nlen, separator))) {
1617                         pname = g_strndup (fi->full_name, p - fi->full_name);
1618                         pfi = g_hash_table_lookup (hash, pname);
1619                         if (pfi) {
1620                                 g_free (pname);
1621                         } else {
1622                                 /* we are missing a folder in the heirarchy so
1623                                  * create a fake folder node */
1624
1625                                 pfi = camel_folder_info_new ();
1626
1627                                 if (short_names) {
1628                                         pfi->display_name = strrchr (pname, separator);
1629                                         if (pfi->display_name != NULL)
1630                                                 pfi->display_name = g_strdup (pfi->display_name + 1);
1631                                         else
1632                                                 pfi->display_name = g_strdup (pname);
1633                                 } else
1634                                         pfi->display_name = g_strdup (pname);
1635
1636                                 pfi->full_name = g_strdup (pname);
1637
1638                                 /* Since this is a "fake" folder
1639                                  * node, it is not selectable. */
1640                                 pfi->flags |= CAMEL_FOLDER_NOSELECT;
1641
1642                                 g_hash_table_insert (hash, pname, pfi);
1643                                 g_ptr_array_add (folders, pfi);
1644                         }
1645                         tail = (CamelFolderInfo *) &pfi->child;
1646                         while (tail->next)
1647                                 tail = tail->next;
1648                         tail->next = fi;
1649                         fi->parent = pfi;
1650                 } else if (!top || !g_ascii_strcasecmp (fi->full_name, "Inbox"))
1651                         top = fi;
1652         }
1653         g_hash_table_destroy (hash);
1654
1655         /* Link together the top-level folders */
1656         tail = top;
1657         for (i = 0; i < folders->len; i++) {
1658                 fi = folders->pdata[i];
1659
1660                 if (fi->child)
1661                         fi->flags &= ~CAMEL_FOLDER_NOCHILDREN;
1662
1663                 if (fi->parent || fi == top)
1664                         continue;
1665                 if (tail == NULL) {
1666                         tail = fi;
1667                         top = fi;
1668                 } else {
1669                         tail->next = fi;
1670                         tail = fi;
1671                 }
1672         }
1673
1674         return top;
1675 }
1676
1677 static CamelFolderInfo *
1678 folder_info_clone_rec (CamelFolderInfo *fi,
1679                        CamelFolderInfo *parent)
1680 {
1681         CamelFolderInfo *info;
1682
1683         info = camel_folder_info_new ();
1684         info->parent = parent;
1685         info->full_name = g_strdup (fi->full_name);
1686         info->display_name = g_strdup (fi->display_name);
1687         info->unread = fi->unread;
1688         info->flags = fi->flags;
1689
1690         if (fi->next)
1691                 info->next = folder_info_clone_rec (fi->next, parent);
1692         else
1693                 info->next = NULL;
1694
1695         if (fi->child)
1696                 info->child = folder_info_clone_rec (fi->child, info);
1697         else
1698                 info->child = NULL;
1699
1700         return info;
1701 }
1702
1703 /**
1704  * camel_folder_info_clone:
1705  * @fi: a #CamelFolderInfo
1706  *
1707  * Clones @fi recursively.
1708  *
1709  * Returns: the cloned #CamelFolderInfo tree.
1710  **/
1711 CamelFolderInfo *
1712 camel_folder_info_clone (CamelFolderInfo *fi)
1713 {
1714         if (fi == NULL)
1715                 return NULL;
1716
1717         return folder_info_clone_rec (fi, NULL);
1718 }
1719
1720 /**
1721  * camel_store_can_refresh_folder
1722  * @store: a #CamelStore
1723  * @info: a #CamelFolderInfo
1724  * @error: return location for a #GError, or %NULL
1725  *
1726  * Returns if this folder (param info) should be checked for new mail or not.
1727  * It should not look into sub infos (info->child) or next infos, it should
1728  * return value only for the actual folder info.
1729  * Default behavior is that all Inbox folders are intended to be refreshed.
1730  *
1731  * Returns: whether folder should be checked for new mails
1732  *
1733  * Since: 2.22
1734  **/
1735 gboolean
1736 camel_store_can_refresh_folder (CamelStore *store,
1737                                 CamelFolderInfo *info,
1738                                 GError **error)
1739 {
1740         CamelStoreClass *class;
1741
1742         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
1743         g_return_val_if_fail (info != NULL, FALSE);
1744
1745         class = CAMEL_STORE_GET_CLASS (store);
1746         g_return_val_if_fail (class->can_refresh_folder != NULL, FALSE);
1747
1748         return class->can_refresh_folder (store, info, error);
1749 }
1750
1751 /**
1752  * camel_store_lock:
1753  * @store: a #CamelStore
1754  * @lock: lock type to lock
1755  *
1756  * Locks @store's @lock. Unlock it with camel_store_unlock().
1757  *
1758  * Since: 2.32
1759  **/
1760 void
1761 camel_store_lock (CamelStore *store,
1762                   CamelStoreLock lock)
1763 {
1764         g_return_if_fail (CAMEL_IS_STORE (store));
1765
1766         switch (lock) {
1767                 case CAMEL_STORE_FOLDER_LOCK:
1768                         g_rec_mutex_lock (&store->priv->folder_lock);
1769                         break;
1770                 default:
1771                         g_return_if_reached ();
1772         }
1773 }
1774
1775 /**
1776  * camel_store_unlock:
1777  * @store: a #CamelStore
1778  * @lock: lock type to unlock
1779  *
1780  * Unlocks @store's @lock, previously locked with camel_store_lock().
1781  *
1782  * Since: 2.32
1783  **/
1784 void
1785 camel_store_unlock (CamelStore *store,
1786                     CamelStoreLock lock)
1787 {
1788         g_return_if_fail (CAMEL_IS_STORE (store));
1789
1790         switch (lock) {
1791                 case CAMEL_STORE_FOLDER_LOCK:
1792                         g_rec_mutex_unlock (&store->priv->folder_lock);
1793                         break;
1794                 default:
1795                         g_return_if_reached ();
1796         }
1797 }
1798
1799 /**
1800  * camel_store_get_folder_sync:
1801  * @store: a #CamelStore
1802  * @folder_name: name of the folder to get
1803  * @flags: folder flags (create, save body index, etc)
1804  * @cancellable: optional #GCancellable object, or %NULL
1805  * @error: return location for a #GError, or %NULL
1806  *
1807  * Gets a specific folder object from @store by name.
1808  *
1809  * Returns: the requested #CamelFolder object, or %NULL on error
1810  *
1811  * Since: 3.0
1812  **/
1813 CamelFolder *
1814 camel_store_get_folder_sync (CamelStore *store,
1815                              const gchar *folder_name,
1816                              CamelStoreGetFolderFlags flags,
1817                              GCancellable *cancellable,
1818                              GError **error)
1819 {
1820         CamelStoreClass *class;
1821         CamelFolder *folder = NULL;
1822
1823         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1824         g_return_val_if_fail (folder_name != NULL, NULL);
1825
1826         class = CAMEL_STORE_GET_CLASS (store);
1827
1828         /* O_EXCL doesn't make sense if we aren't requesting
1829          * to also create the folder if it doesn't exist. */
1830         if (!(flags & CAMEL_STORE_FOLDER_CREATE))
1831                 flags &= ~CAMEL_STORE_FOLDER_EXCL;
1832
1833         /* Try cache first. */
1834         folder = camel_object_bag_reserve (store->folders, folder_name);
1835         if (folder != NULL && (flags & CAMEL_STORE_FOLDER_EXCL)) {
1836                 g_set_error (
1837                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1838                         _("Cannot create folder '%s': folder exists"),
1839                         folder_name);
1840                 camel_object_bag_abort (store->folders, folder_name);
1841                 g_object_unref (folder);
1842                 return NULL;
1843         }
1844
1845         if (folder == NULL) {
1846                 CamelVeeFolder *vjunk = NULL;
1847                 CamelVeeFolder *vtrash = NULL;
1848                 gboolean folder_name_is_vjunk;
1849                 gboolean folder_name_is_vtrash;
1850                 gboolean store_uses_vjunk;
1851                 gboolean store_uses_vtrash;
1852
1853                 store_uses_vjunk =
1854                         ((store->flags & CAMEL_STORE_VJUNK) != 0);
1855                 store_uses_vtrash =
1856                         ((store->flags & CAMEL_STORE_VTRASH) != 0);
1857                 folder_name_is_vjunk =
1858                         store_uses_vjunk &&
1859                         (strcmp (folder_name, CAMEL_VJUNK_NAME) == 0);
1860                 folder_name_is_vtrash =
1861                         store_uses_vtrash &&
1862                         (strcmp (folder_name, CAMEL_VTRASH_NAME) == 0);
1863
1864                 if (flags & CAMEL_STORE_IS_MIGRATING) {
1865                         if (folder_name_is_vtrash) {
1866                                 if (store->folders != NULL)
1867                                         camel_object_bag_abort (
1868                                                 store->folders, folder_name);
1869                                 return NULL;
1870                         }
1871
1872                         if (folder_name_is_vjunk) {
1873                                 if (store->folders != NULL)
1874                                         camel_object_bag_abort (
1875                                                 store->folders, folder_name);
1876                                 return NULL;
1877                         }
1878                 }
1879
1880                 camel_operation_push_message (
1881                         cancellable, _("Opening folder '%s'"), folder_name);
1882
1883                 if (folder_name_is_vtrash) {
1884                         folder = class->get_trash_folder_sync (
1885                                 store, cancellable, error);
1886                         CAMEL_CHECK_GERROR (
1887                                 store, get_trash_folder_sync,
1888                                 folder != NULL, error);
1889                 } else if (folder_name_is_vjunk) {
1890                         folder = class->get_junk_folder_sync (
1891                                 store, cancellable, error);
1892                         CAMEL_CHECK_GERROR (
1893                                 store, get_junk_folder_sync,
1894                                 folder != NULL, error);
1895                 } else {
1896                         folder = class->get_folder_sync (
1897                                 store, folder_name, flags,
1898                                 cancellable, error);
1899                         CAMEL_CHECK_GERROR (
1900                                 store, get_folder_sync,
1901                                 folder != NULL, error);
1902
1903                         if (folder != NULL && store_uses_vjunk)
1904                                 vjunk = camel_object_bag_get (
1905                                         store->folders, CAMEL_VJUNK_NAME);
1906
1907                         if (folder != NULL && store_uses_vtrash)
1908                                 vtrash = camel_object_bag_get (
1909                                         store->folders, CAMEL_VTRASH_NAME);
1910                 }
1911
1912                 /* Release the folder name reservation before adding the
1913                  * folder to the virtual Junk and Trash folders, just to
1914                  * reduce the chance of deadlock. */
1915                 if (folder != NULL)
1916                         camel_object_bag_add (
1917                                 store->folders, folder_name, folder);
1918                 else
1919                         camel_object_bag_abort (
1920                                 store->folders, folder_name);
1921
1922                 /* If this is a normal folder and the store uses a
1923                  * virtual Junk folder, let the virtual Junk folder
1924                  * track this folder. */
1925                 if (vjunk != NULL) {
1926                         camel_vee_folder_add_folder (vjunk, folder, NULL);
1927                         g_object_unref (vjunk);
1928                 }
1929
1930                 /* If this is a normal folder and the store uses a
1931                  * virtual Trash folder, let the virtual Trash folder
1932                  * track this folder. */
1933                 if (vtrash != NULL) {
1934                         camel_vee_folder_add_folder (vtrash, folder, NULL);
1935                         g_object_unref (vtrash);
1936                 }
1937
1938                 camel_operation_pop_message (cancellable);
1939
1940                 if (folder != NULL)
1941                         camel_store_folder_opened (store, folder);
1942         }
1943
1944         return folder;
1945 }
1946
1947 /**
1948  * camel_store_get_folder:
1949  * @store: a #CamelStore
1950  * @folder_name: name of the folder to get
1951  * @flags: folder flags (create, save body index, etc)
1952  * @io_priority: the I/O priority of the request
1953  * @cancellable: optional #GCancellable object, or %NULL
1954  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1955  * @user_data: data to pass to the callback function
1956  *
1957  * Asynchronously gets a specific folder object from @store by name.
1958  *
1959  * When the operation is finished, @callback will be called.  You can then
1960  * call camel_store_get_folder_finish() to get the result of the operation.
1961  *
1962  * Since: 3.0
1963  **/
1964 void
1965 camel_store_get_folder (CamelStore *store,
1966                         const gchar *folder_name,
1967                         CamelStoreGetFolderFlags flags,
1968                         gint io_priority,
1969                         GCancellable *cancellable,
1970                         GAsyncReadyCallback callback,
1971                         gpointer user_data)
1972 {
1973         CamelStoreClass *class;
1974
1975         g_return_if_fail (CAMEL_IS_STORE (store));
1976         g_return_if_fail (folder_name != NULL);
1977
1978         class = CAMEL_STORE_GET_CLASS (store);
1979         g_return_if_fail (class->get_folder != NULL);
1980
1981         class->get_folder (
1982                 store, folder_name, flags, io_priority,
1983                 cancellable, callback, user_data);
1984 }
1985
1986 /**
1987  * camel_store_get_folder_finish:
1988  * @store: a #CamelStore
1989  * @result: a #GAsyncResult
1990  * @error: return location for a #GError, or %NULL
1991  *
1992  * Finishes the operation started with camel_store_get_folder().
1993  *
1994  * Returns: the requested #CamelFolder object, or %NULL on error
1995  *
1996  * Since: 3.0
1997  **/
1998 CamelFolder *
1999 camel_store_get_folder_finish (CamelStore *store,
2000                                GAsyncResult *result,
2001                                GError **error)
2002 {
2003         CamelStoreClass *class;
2004
2005         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2006         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2007
2008         class = CAMEL_STORE_GET_CLASS (store);
2009         g_return_val_if_fail (class->get_folder_finish != NULL, NULL);
2010
2011         return class->get_folder_finish (store, result, error);
2012 }
2013
2014 /**
2015  * camel_store_get_folder_info_sync:
2016  * @store: a #CamelStore
2017  * @top: the name of the folder to start from
2018  * @flags: various CAMEL_STORE_FOLDER_INFO_* flags to control behavior
2019  * @cancellable: optional #GCancellable object, or %NULL
2020  * @error: return location for a #GError, or %NULL
2021  *
2022  * This fetches information about the folder structure of @store,
2023  * starting with @top, and returns a tree of #CamelFolderInfo
2024  * structures. If @flags includes %CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
2025  * only subscribed folders will be listed.   If the store doesn't support
2026  * subscriptions, then it will list all folders.  If @flags includes
2027  * %CAMEL_STORE_FOLDER_INFO_RECURSIVE, the returned tree will include
2028  * all levels of hierarchy below @top. If not, it will only include
2029  * the immediate subfolders of @top. If @flags includes
2030  * %CAMEL_STORE_FOLDER_INFO_FAST, the unread_message_count fields of
2031  * some or all of the structures may be set to %-1, if the store cannot
2032  * determine that information quickly.  If @flags includes
2033  * %CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, don't include special virtual
2034  * folders (such as vTrash or vJunk).
2035  *
2036  * The returned #CamelFolderInfo tree should be freed with
2037  * camel_store_free_folder_info().
2038  *
2039  * The CAMEL_STORE_FOLDER_INFO_FAST flag should be considered
2040  * deprecated; most backends will behave the same whether it is
2041  * supplied or not.  The only guaranteed way to get updated folder
2042  * counts is to both open the folder and invoke refresh_info() it.
2043  *
2044  * Returns: a #CamelFolderInfo tree, or %NULL on error
2045  *
2046  * Since: 3.0
2047  **/
2048 CamelFolderInfo *
2049 camel_store_get_folder_info_sync (CamelStore *store,
2050                                   const gchar *top,
2051                                   CamelStoreGetFolderInfoFlags flags,
2052                                   GCancellable *cancellable,
2053                                   GError **error)
2054 {
2055         CamelStoreClass *class;
2056         CamelFolderInfo *info;
2057         gchar *name;
2058
2059         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2060
2061         class = CAMEL_STORE_GET_CLASS (store);
2062         g_return_val_if_fail (class->get_folder_info_sync != NULL, NULL);
2063
2064         name = camel_service_get_name (CAMEL_SERVICE (store), TRUE);
2065         camel_operation_push_message (
2066                 cancellable, _("Scanning folders in '%s'"), name);
2067         g_free (name);
2068
2069         info = class->get_folder_info_sync (
2070                 store, top, flags, cancellable, error);
2071         if (!(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED))
2072                 CAMEL_CHECK_GERROR (
2073                         store, get_folder_info_sync, info != NULL, error);
2074
2075         if (info && (top == NULL || *top == '\0') && (flags & CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL) == 0) {
2076                 if (store->flags & CAMEL_STORE_VTRASH)
2077                         /* the name of the Trash folder, used for deleted messages */
2078                         add_special_info (store, info, CAMEL_VTRASH_NAME, _("Trash"), FALSE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_TRASH);
2079                 if (store->flags & CAMEL_STORE_VJUNK)
2080                         /* the name of the Junk folder, used for spam messages */
2081                         add_special_info (store, info, CAMEL_VJUNK_NAME, _("Junk"), TRUE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_JUNK);
2082         } else if (!info && top && (flags & CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL) == 0) {
2083                 CamelFolderInfo *root_info = NULL;
2084
2085                 if ((store->flags & CAMEL_STORE_VTRASH) != 0 && g_str_equal (top, CAMEL_VTRASH_NAME)) {
2086                         root_info = class->get_folder_info_sync (store, NULL, flags & (~CAMEL_STORE_FOLDER_INFO_RECURSIVE), cancellable, error);
2087                         if (root_info)
2088                                 add_special_info (store, root_info, CAMEL_VTRASH_NAME, _("Trash"), FALSE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_TRASH);
2089                 } else if ((store->flags & CAMEL_STORE_VJUNK) != 0 && g_str_equal (top, CAMEL_VJUNK_NAME)) {
2090                         root_info = class->get_folder_info_sync (store, NULL, flags & (~CAMEL_STORE_FOLDER_INFO_RECURSIVE), cancellable, error);
2091                         if (root_info)
2092                                 add_special_info (store, root_info, CAMEL_VJUNK_NAME, _("Junk"), TRUE, CAMEL_FOLDER_VIRTUAL | CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_VTRASH | CAMEL_FOLDER_TYPE_JUNK);
2093                 }
2094
2095                 if (root_info) {
2096                         info = root_info->next;
2097                         root_info->next = NULL;
2098                         info->next = NULL;
2099                         info->parent = NULL;
2100
2101                         camel_store_free_folder_info (store, root_info);
2102                 }
2103         }
2104
2105         camel_operation_pop_message (cancellable);
2106
2107         if (camel_debug_start ("store:folder_info")) {
2108                 const gchar *uid;
2109
2110                 uid = camel_service_get_uid (CAMEL_SERVICE (store));
2111                 printf (
2112                         "Get folder info(%p:%s, '%s') =\n",
2113                         (gpointer) store, uid, top ? top : "<null>");
2114                 dump_fi (info, 2);
2115                 camel_debug_end ();
2116         }
2117
2118         return info;
2119 }
2120
2121 /**
2122  * camel_store_get_folder_info:
2123  * @store: a #CamelStore
2124  * @top: the name of the folder to start from
2125  * @flags: various CAMEL_STORE_FOLDER_INFO_* flags to control behavior
2126  * @io_priority: the I/O priority of the request
2127  * @cancellable: optional #GCancellable object, or %NULL
2128  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2129  * @user_data: data to pass to the callback function
2130  *
2131  * Asynchronously fetches information about the folder structure of @store,
2132  * starting with @top.  For details of the behavior, see
2133  * camel_store_get_folder_info_sync().
2134  *
2135  * When the operation is finished, @callback will be called.  You can
2136  * then call camel_store_get_folder_info_finish() to get the result of
2137  * the operation.
2138  *
2139  * Since: 3.0
2140  **/
2141 void
2142 camel_store_get_folder_info (CamelStore *store,
2143                              const gchar *top,
2144                              CamelStoreGetFolderInfoFlags flags,
2145                              gint io_priority,
2146                              GCancellable *cancellable,
2147                              GAsyncReadyCallback callback,
2148                              gpointer user_data)
2149 {
2150         CamelStoreClass *class;
2151
2152         g_return_if_fail (CAMEL_IS_STORE (store));
2153
2154         class = CAMEL_STORE_GET_CLASS (store);
2155         g_return_if_fail (class->get_folder_info != NULL);
2156
2157         class->get_folder_info (
2158                 store, top, flags, io_priority,
2159                 cancellable, callback, user_data);
2160 }
2161
2162 /**
2163  * camel_store_get_folder_info_finish:
2164  * @store: a #CamelStore
2165  * @result: a #GAsyncResult
2166  * @error: return location for a #GError, or %NULL
2167  *
2168  * Finishes the operation started with camel_store_get_folder_info().
2169  * The returned #CamelFolderInfo tree should be freed with
2170  * camel_store_free_folder_info().
2171  *
2172  * Returns: a #CamelFolderInfo tree, or %NULL on error
2173  *
2174  * Since: 3.0
2175  **/
2176 CamelFolderInfo *
2177 camel_store_get_folder_info_finish (CamelStore *store,
2178                                     GAsyncResult *result,
2179                                     GError **error)
2180 {
2181         CamelStoreClass *class;
2182
2183         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2184         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2185
2186         class = CAMEL_STORE_GET_CLASS (store);
2187         g_return_val_if_fail (class->get_folder_info_finish != NULL, NULL);
2188
2189         return class->get_folder_info_finish (store, result, error);
2190 }
2191
2192 /**
2193  * camel_store_get_inbox_folder_sync:
2194  * @store: a #CamelStore
2195  * @cancellable: optional #GCancellable object, or %NULL
2196  * @error: return location for a #GError, or %NULL
2197  *
2198  * Gets the folder in @store into which new mail is delivered.
2199  *
2200  * Returns: the inbox folder for @store, or %NULL on error or if no such
2201  * folder exists
2202  *
2203  * Since: 3.0
2204  **/
2205 CamelFolder *
2206 camel_store_get_inbox_folder_sync (CamelStore *store,
2207                                    GCancellable *cancellable,
2208                                    GError **error)
2209 {
2210         CamelStoreClass *class;
2211         CamelFolder *folder;
2212
2213         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2214
2215         class = CAMEL_STORE_GET_CLASS (store);
2216         g_return_val_if_fail (class->get_inbox_folder_sync != NULL, NULL);
2217
2218         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2219
2220         /* Check for cancellation after locking. */
2221         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2222                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2223                 return NULL;
2224         }
2225
2226         folder = class->get_inbox_folder_sync (store, cancellable, error);
2227         CAMEL_CHECK_GERROR (
2228                 store, get_inbox_folder_sync, folder != NULL, error);
2229
2230         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2231
2232         return folder;
2233 }
2234
2235 /**
2236  * camel_store_get_inbox_folder:
2237  * @store: a #CamelStore
2238  * @io_priority: the I/O priority of the request
2239  * @cancellable: optional #GCancellable object, or %NULL
2240  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2241  * @user_data: data to pass to the callback function
2242  *
2243  * Asynchronously gets the folder in @store into which new mail is delivered.
2244  *
2245  * When the operation is finished, @callback will be called.  You can
2246  * then call camel_store_get_inbox_folder_finish() to get the result of
2247  * the operation.
2248  *
2249  * Since: 3.0
2250  **/
2251 void
2252 camel_store_get_inbox_folder (CamelStore *store,
2253                               gint io_priority,
2254                               GCancellable *cancellable,
2255                               GAsyncReadyCallback callback,
2256                               gpointer user_data)
2257 {
2258         CamelStoreClass *class;
2259
2260         g_return_if_fail (CAMEL_IS_STORE (store));
2261
2262         class = CAMEL_STORE_GET_CLASS (store);
2263         g_return_if_fail (class->get_inbox_folder != NULL);
2264
2265         class->get_inbox_folder (
2266                 store, io_priority, cancellable, callback, user_data);
2267 }
2268
2269 /**
2270  * camel_store_get_inbox_folder_finish:
2271  * @store: a #CamelStore
2272  * @result: a #GAsyncResult
2273  * @error: return location for a #GError, or %NULL
2274  *
2275  * Finishes the operation started with camel_store_get_inbox_folder().
2276  *
2277  * Returns: the inbox folder for @store, or %NULL on error or if no such
2278  * folder exists
2279  *
2280  * Since: 3.0
2281  **/
2282 CamelFolder *
2283 camel_store_get_inbox_folder_finish (CamelStore *store,
2284                                      GAsyncResult *result,
2285                                      GError **error)
2286 {
2287         CamelStoreClass *class;
2288
2289         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2290         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2291
2292         class = CAMEL_STORE_GET_CLASS (store);
2293         g_return_val_if_fail (class->get_inbox_folder_finish != NULL, NULL);
2294
2295         return class->get_inbox_folder_finish (store, result, error);
2296 }
2297
2298 /**
2299  * camel_store_get_junk_folder_sync:
2300  * @store: a #CamelStore
2301  * @cancellable: optional #GCancellable object, or %NULL
2302  * @error: return location for a #GError, or %NULL
2303  *
2304  * Gets the folder in @store into which junk is delivered.
2305  *
2306  * Returns: the junk folder for @store, or %NULL on error or if no such
2307  * folder exists
2308  *
2309  * Since: 3.0
2310  **/
2311 CamelFolder *
2312 camel_store_get_junk_folder_sync (CamelStore *store,
2313                                   GCancellable *cancellable,
2314                                   GError **error)
2315 {
2316         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2317
2318         if ((store->flags & CAMEL_STORE_VJUNK) == 0) {
2319                 CamelStoreClass *class;
2320                 CamelFolder *folder;
2321
2322                 class = CAMEL_STORE_GET_CLASS (store);
2323                 g_return_val_if_fail (class->get_junk_folder_sync != NULL, NULL);
2324
2325                 folder = class->get_junk_folder_sync (store, cancellable, error);
2326                 CAMEL_CHECK_GERROR (
2327                         store, get_junk_folder_sync, folder != NULL, error);
2328
2329                 return folder;
2330         }
2331
2332         return camel_store_get_folder_sync (
2333                 store, CAMEL_VJUNK_NAME, 0, cancellable, error);
2334 }
2335
2336 /**
2337  * camel_store_get_junk_folder:
2338  * @store: a #CamelStore
2339  * @io_priority: the I/O priority of the request
2340  * @cancellable: optional #GCancellable object, or %NULL
2341  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2342  * @user_data: data to pass to the callback function
2343  *
2344  * Asynchronously gets the folder in @store into which junk is delivered.
2345  *
2346  * When the operation is finished, @callback will be called.  You can
2347  * then call camel_store_get_junk_folder_finish() to get the result of
2348  * the operation.
2349  *
2350  * Since: 3.0
2351  **/
2352 void
2353 camel_store_get_junk_folder (CamelStore *store,
2354                              gint io_priority,
2355                              GCancellable *cancellable,
2356                              GAsyncReadyCallback callback,
2357                              gpointer user_data)
2358 {
2359         CamelStoreClass *class;
2360
2361         g_return_if_fail (CAMEL_IS_STORE (store));
2362
2363         class = CAMEL_STORE_GET_CLASS (store);
2364         g_return_if_fail (class->get_junk_folder != NULL);
2365
2366         class->get_junk_folder (
2367                 store, io_priority, cancellable, callback, user_data);
2368 }
2369
2370 /**
2371  * camel_store_get_junk_folder_finish:
2372  * @store: a #CamelStore
2373  * @result: a #GAsyncResult
2374  * @error: return location for a #GError, or %NULL
2375  *
2376  * Finishes the operation started with camel_store_get_junk_folder().
2377  *
2378  * Returns: the junk folder for @store, or %NULL on error or if no such
2379  * folder exists
2380  *
2381  * Since: 3.0
2382  **/
2383 CamelFolder *
2384 camel_store_get_junk_folder_finish (CamelStore *store,
2385                                     GAsyncResult *result,
2386                                     GError **error)
2387 {
2388         CamelStoreClass *class;
2389
2390         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2391         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2392
2393         class = CAMEL_STORE_GET_CLASS (store);
2394         g_return_val_if_fail (class->get_junk_folder_finish != NULL, NULL);
2395
2396         return class->get_junk_folder_finish (store, result, error);
2397 }
2398
2399 /**
2400  * camel_store_get_trash_folder_sync:
2401  * @store: a #CamelStore
2402  * @cancellable: optional #GCancellable object, or %NULL
2403  * @error: return location for a #GError, or %NULL
2404  *
2405  * Gets the folder in @store into which trash is delivered.
2406  *
2407  * Returns: the trash folder for @store, or %NULL on error or if no such
2408  * folder exists
2409  *
2410  * Since: 3.0
2411  **/
2412 CamelFolder *
2413 camel_store_get_trash_folder_sync (CamelStore *store,
2414                                    GCancellable *cancellable,
2415                                    GError **error)
2416 {
2417         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2418
2419         if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
2420                 CamelStoreClass *class;
2421                 CamelFolder *folder;
2422
2423                 class = CAMEL_STORE_GET_CLASS (store);
2424                 g_return_val_if_fail (class->get_trash_folder_sync != NULL, NULL);
2425
2426                 folder = class->get_trash_folder_sync (
2427                         store, cancellable, error);
2428                 CAMEL_CHECK_GERROR (
2429                         store, get_trash_folder_sync, folder != NULL, error);
2430
2431                 return folder;
2432         }
2433
2434         return camel_store_get_folder_sync (
2435                 store, CAMEL_VTRASH_NAME, 0, cancellable, error);
2436 }
2437
2438 /**
2439  * camel_store_get_trash_folder:
2440  * @store: a #CamelStore
2441  * @io_priority: the I/O priority of the request
2442  * @cancellable: optional #GCancellable object, or %NULL
2443  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2444  * @user_data: data to pass to the callback function
2445  *
2446  * Asynchronously gets the folder in @store into which trash is delivered.
2447  *
2448  * When the operation is finished, @callback will be called.  You can
2449  * then call camel_store_get_trash_folder_finish() to get the result of
2450  * the operation.
2451  *
2452  * Since: 3.0
2453  **/
2454 void
2455 camel_store_get_trash_folder (CamelStore *store,
2456                               gint io_priority,
2457                               GCancellable *cancellable,
2458                               GAsyncReadyCallback callback,
2459                               gpointer user_data)
2460 {
2461         CamelStoreClass *class;
2462
2463         g_return_if_fail (CAMEL_IS_STORE (store));
2464
2465         class = CAMEL_STORE_GET_CLASS (store);
2466         g_return_if_fail (class->get_trash_folder != NULL);
2467
2468         class->get_trash_folder (
2469                 store, io_priority, cancellable, callback, user_data);
2470 }
2471
2472 /**
2473  * camel_store_get_trash_folder_finish:
2474  * @store: a #CamelStore
2475  * @result: a #GAsyncResult
2476  * @error: return location for a #GError, or %NULL
2477  *
2478  * Finishes the operation started with camel_store_get_trash_folder().
2479  *
2480  * Returns: the trash folder for @store, or %NULL on error or if no such
2481  * folder exists
2482  *
2483  * Since: 3.0
2484  **/
2485 CamelFolder *
2486 camel_store_get_trash_folder_finish (CamelStore *store,
2487                                      GAsyncResult *result,
2488                                      GError **error)
2489 {
2490         CamelStoreClass *class;
2491
2492         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2493         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2494
2495         class = CAMEL_STORE_GET_CLASS (store);
2496         g_return_val_if_fail (class->get_trash_folder_finish != NULL, NULL);
2497
2498         return class->get_trash_folder_finish (store, result, error);
2499 }
2500
2501 /**
2502  * camel_store_create_folder_sync:
2503  * @store: a #CamelStore
2504  * @parent_name: name of the new folder's parent, or %NULL
2505  * @folder_name: name of the folder to create
2506  * @cancellable: optional #GCancellable object, or %NULL
2507  * @error: return location for a #GError, or %NULL
2508  *
2509  * Creates a new folder as a child of an existing folder.
2510  * @parent_name can be %NULL to create a new top-level folder.
2511  * The returned #CamelFolderInfo struct should be freed with
2512  * camel_store_free_folder_info().
2513  *
2514  * Returns: info about the created folder, or %NULL on error
2515  *
2516  * Since: 3.0
2517  **/
2518 CamelFolderInfo *
2519 camel_store_create_folder_sync (CamelStore *store,
2520                                 const gchar *parent_name,
2521                                 const gchar *folder_name,
2522                                 GCancellable *cancellable,
2523                                 GError **error)
2524 {
2525         CamelStoreClass *class;
2526         CamelFolderInfo *fi;
2527
2528         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2529         g_return_val_if_fail (folder_name != NULL, NULL);
2530
2531         class = CAMEL_STORE_GET_CLASS (store);
2532         g_return_val_if_fail (class->create_folder_sync != NULL, NULL);
2533
2534         if ((parent_name == NULL || parent_name[0] == 0)
2535             && (((store->flags & CAMEL_STORE_VTRASH) && strcmp (folder_name, CAMEL_VTRASH_NAME) == 0)
2536                 || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (folder_name, CAMEL_VJUNK_NAME) == 0))) {
2537                 g_set_error (
2538                         error, CAMEL_STORE_ERROR,
2539                         CAMEL_STORE_ERROR_INVALID,
2540                         _("Cannot create folder: %s: folder exists"),
2541                         folder_name);
2542                 return NULL;
2543         }
2544
2545         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2546
2547         /* Check for cancellation after locking. */
2548         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2549                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2550                 return NULL;
2551         }
2552
2553         camel_operation_push_message (
2554                 cancellable, _("Creating folder '%s'"), folder_name);
2555
2556         fi = class->create_folder_sync (
2557                 store, parent_name, folder_name, cancellable, error);
2558         CAMEL_CHECK_GERROR (store, create_folder_sync, fi != NULL, error);
2559
2560         camel_operation_pop_message (cancellable);
2561
2562         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2563
2564         return fi;
2565 }
2566
2567 /**
2568  * camel_store_create_folder:
2569  * @store: a #CamelStore
2570  * @parent_name: name of the new folder's parent, or %NULL
2571  * @folder_name: name of the folder to create
2572  * @io_priority: the I/O priority of the request
2573  * @cancellable: optional #GCancellable object, or %NULL
2574  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2575  * @user_data: data to pass to the callback function
2576  *
2577  * Asynchronously creates a new folder as a child of an existing folder.
2578  * @parent_name can be %NULL to create a new top-level folder.
2579  *
2580  * When the operation is finished, @callback will be called.  You can then
2581  * call camel_store_create_folder_finish() to get the result of the operation.
2582  *
2583  * Since: 3.0
2584  **/
2585 void
2586 camel_store_create_folder (CamelStore *store,
2587                            const gchar *parent_name,
2588                            const gchar *folder_name,
2589                            gint io_priority,
2590                            GCancellable *cancellable,
2591                            GAsyncReadyCallback callback,
2592                            gpointer user_data)
2593 {
2594         CamelStoreClass *class;
2595
2596         g_return_if_fail (CAMEL_IS_STORE (store));
2597         g_return_if_fail (folder_name != NULL);
2598
2599         class = CAMEL_STORE_GET_CLASS (store);
2600         g_return_if_fail (class->create_folder != NULL);
2601
2602         class->create_folder (
2603                 store, parent_name, folder_name, io_priority,
2604                 cancellable, callback, user_data);
2605 }
2606
2607 /**
2608  * camel_store_create_folder_finish:
2609  * @store: a #CamelStore
2610  * @result: a #GAsyncResult
2611  * @error: return location for a #GError, or %NULL
2612  *
2613  * Finishes the operation started with camel_store_create_folder().
2614  * The returned #CamelFolderInfo struct should be freed with
2615  * camel_store_free_folder_info().
2616  *
2617  * Returns: info about the created folder, or %NULL on error
2618  *
2619  * Since: 3.0
2620  **/
2621 CamelFolderInfo *
2622 camel_store_create_folder_finish (CamelStore *store,
2623                                   GAsyncResult *result,
2624                                   GError **error)
2625 {
2626         CamelStoreClass *class;
2627
2628         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2629         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2630
2631         class = CAMEL_STORE_GET_CLASS (store);
2632         g_return_val_if_fail (class->create_folder_finish != NULL, NULL);
2633
2634         return class->create_folder_finish (store, result, error);
2635 }
2636
2637 /**
2638  * camel_store_delete_folder_sync:
2639  * @store: a #CamelStore
2640  * @folder_name: name of the folder to delete
2641  * @cancellable: optional #GCancellable object, or %NULL
2642  * @error: return location for a #GError, or %NULL
2643  *
2644  * Deletes the folder described by @folder_name.  The folder must be empty.
2645  *
2646  * Returns: %TRUE on success, %FALSE on failure
2647  *
2648  * Since: 3.0
2649  **/
2650 gboolean
2651 camel_store_delete_folder_sync (CamelStore *store,
2652                                 const gchar *folder_name,
2653                                 GCancellable *cancellable,
2654                                 GError **error)
2655 {
2656         CamelStoreClass *class;
2657         gboolean success;
2658         GError *local_error = NULL, **check_error = NULL;
2659
2660         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2661         g_return_val_if_fail (folder_name != NULL, FALSE);
2662
2663         class = CAMEL_STORE_GET_CLASS (store);
2664         g_return_val_if_fail (class->delete_folder_sync != NULL, FALSE);
2665
2666         /* TODO: should probably be a parameter/bit on the storeinfo */
2667         if (((store->flags & CAMEL_STORE_VTRASH) && strcmp (folder_name, CAMEL_VTRASH_NAME) == 0)
2668             || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (folder_name, CAMEL_VJUNK_NAME) == 0)) {
2669                 g_set_error (
2670                         error, CAMEL_STORE_ERROR,
2671                         CAMEL_STORE_ERROR_NO_FOLDER,
2672                         _("Cannot delete folder: %s: Invalid operation"),
2673                         folder_name);
2674                 return FALSE;
2675         }
2676
2677         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2678
2679         /* Check for cancellation after locking. */
2680         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2681                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2682                 return FALSE;
2683         }
2684
2685         success = class->delete_folder_sync (
2686                 store, folder_name, cancellable, &local_error);
2687         if (local_error)
2688                 check_error = &local_error;
2689         CAMEL_CHECK_GERROR (store, delete_folder_sync, success, check_error);
2690
2691         /* ignore 'no such table' errors */
2692         if (local_error != NULL &&
2693             g_ascii_strncasecmp (local_error->message, "no such table", 13) == 0)
2694                 g_clear_error (&local_error);
2695
2696         if (local_error == NULL)
2697                 cs_delete_cached_folder (store, folder_name);
2698         else
2699                 g_propagate_error (error, local_error);
2700
2701         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2702
2703         return success;
2704 }
2705
2706 /**
2707  * camel_store_delete_folder:
2708  * @store: a #CamelStore
2709  * @folder_name: name of the folder to delete
2710  * @io_priority: the I/O priority of the request
2711  * @cancellable: optional #GCancellable object, or %NULL
2712  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2713  * @user_data: data to pass to the callback function
2714  *
2715  * Asynchronously deletes the folder described by @folder_name.  The
2716  * folder must be empty.
2717  *
2718  * When the operation is finished, @callback will be called.  You can then
2719  * call camel_store_delete_folder_finish() to get the result of the operation.
2720  *
2721  * Since: 3.0
2722  **/
2723 void
2724 camel_store_delete_folder (CamelStore *store,
2725                            const gchar *folder_name,
2726                            gint io_priority,
2727                            GCancellable *cancellable,
2728                            GAsyncReadyCallback callback,
2729                            gpointer user_data)
2730 {
2731         CamelStoreClass *class;
2732
2733         g_return_if_fail (CAMEL_IS_STORE (store));
2734         g_return_if_fail (folder_name != NULL);
2735
2736         class = CAMEL_STORE_GET_CLASS (store);
2737         g_return_if_fail (class->delete_folder != NULL);
2738
2739         class->delete_folder (
2740                 store, folder_name, io_priority,
2741                 cancellable, callback, user_data);
2742 }
2743
2744 /**
2745  * camel_store_delete_folder_finish:
2746  * @store: a #CamelStore
2747  * @result: a #GAsyncResult
2748  * @error: return location for a #GError, or %NULL
2749  *
2750  * Finishes the operation started with camel_store_delete_folder().
2751  *
2752  * Returns: %TRUE on success, %FALSE on error
2753  *
2754  * Since: 3.0
2755  **/
2756 gboolean
2757 camel_store_delete_folder_finish (CamelStore *store,
2758                                   GAsyncResult *result,
2759                                   GError **error)
2760 {
2761         CamelStoreClass *class;
2762
2763         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2764         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2765
2766         class = CAMEL_STORE_GET_CLASS (store);
2767         g_return_val_if_fail (class->delete_folder_finish != NULL, FALSE);
2768
2769         return class->delete_folder_finish (store, result, error);
2770 }
2771
2772 /**
2773  * camel_store_rename_folder_sync:
2774  * @store: a #CamelStore
2775  * @old_name: the current name of the folder
2776  * @new_name: the new name of the folder
2777  * @cancellable: optional #GCancellable object, or %NULL
2778  * @error: return location for a #GError, or %NULL
2779  *
2780  * Renames the folder described by @old_name to @new_name.
2781  *
2782  * Returns: %TRUE on success, %FALSE on error
2783  *
2784  * Since: 3.0
2785  **/
2786 gboolean
2787 camel_store_rename_folder_sync (CamelStore *store,
2788                                 const gchar *old_namein,
2789                                 const gchar *new_name,
2790                                 GCancellable *cancellable,
2791                                 GError **error)
2792 {
2793         CamelStoreClass *class;
2794         CamelFolder *folder;
2795         gint i, oldlen, namelen;
2796         GPtrArray *folders = NULL;
2797         gchar *old_name;
2798         gboolean success;
2799
2800         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2801         g_return_val_if_fail (old_namein != NULL, FALSE);
2802         g_return_val_if_fail (new_name != NULL, FALSE);
2803
2804         class = CAMEL_STORE_GET_CLASS (store);
2805         g_return_val_if_fail (class->rename_folder_sync != NULL, FALSE);
2806
2807         if (strcmp (old_namein, new_name) == 0)
2808                 return TRUE;
2809
2810         if (((store->flags & CAMEL_STORE_VTRASH) && strcmp (old_namein, CAMEL_VTRASH_NAME) == 0)
2811             || ((store->flags & CAMEL_STORE_VJUNK) && strcmp (old_namein, CAMEL_VJUNK_NAME) == 0)) {
2812                 g_set_error (
2813                         error, CAMEL_STORE_ERROR,
2814                         CAMEL_STORE_ERROR_NO_FOLDER,
2815                         _("Cannot rename folder: %s: Invalid operation"),
2816                         old_namein);
2817                 return FALSE;
2818         }
2819
2820         /* need to save this, since old_namein might be folder->full_name, which could go away */
2821         old_name = g_strdup (old_namein);
2822         oldlen = strlen (old_name);
2823
2824         camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
2825
2826         /* Check for cancellation after locking. */
2827         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
2828                 camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2829                 return FALSE;
2830         }
2831
2832         /* If the folder is open (or any subfolders of the open folder)
2833          * We need to rename them atomically with renaming the actual
2834          * folder path. */
2835         folders = camel_object_bag_list (store->folders);
2836         for (i = 0; folders && i < folders->len; i++) {
2837                 const gchar *full_name;
2838
2839                 folder = folders->pdata[i];
2840                 full_name = camel_folder_get_full_name (folder);
2841
2842                 namelen = strlen (full_name);
2843                 if ((namelen == oldlen &&
2844                      strcmp (full_name, old_name) == 0)
2845                     || ((namelen > oldlen)
2846                         && strncmp (full_name, old_name, oldlen) == 0
2847                         && full_name[oldlen] == '/')) {
2848                         camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
2849                 } else {
2850                         g_ptr_array_remove_index_fast (folders, i);
2851                         i--;
2852                         g_object_unref (folder);
2853                 }
2854         }
2855
2856         /* Now try the real rename (will emit renamed signal) */
2857         success = class->rename_folder_sync (
2858                 store, old_name, new_name, cancellable, error);
2859         CAMEL_CHECK_GERROR (store, rename_folder_sync, success, error);
2860
2861         /* If it worked, update all open folders/unlock them */
2862         if (folders) {
2863                 if (success) {
2864                         CamelStoreGetFolderInfoFlags flags;
2865                         CamelFolderInfo *folder_info;
2866
2867                         flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE;
2868
2869                         for (i = 0; i < folders->len; i++) {
2870                                 const gchar *full_name;
2871                                 gchar *new;
2872
2873                                 folder = folders->pdata[i];
2874                                 full_name = camel_folder_get_full_name (folder);
2875
2876                                 new = g_strdup_printf ("%s%s", new_name, full_name + strlen (old_name));
2877                                 camel_object_bag_rekey (store->folders, folder, new);
2878                                 camel_folder_rename (folder, new);
2879                                 g_free (new);
2880
2881                                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2882                                 g_object_unref (folder);
2883                         }
2884
2885                         /* Emit renamed signal */
2886                         if (CAMEL_IS_SUBSCRIBABLE (store))
2887                                 flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
2888
2889                         folder_info = class->get_folder_info_sync (
2890                                 store, new_name, flags, cancellable, error);
2891                         CAMEL_CHECK_GERROR (store, get_folder_info, folder_info != NULL, error);
2892
2893                         if (folder_info != NULL) {
2894                                 camel_store_folder_renamed (store, old_name, folder_info);
2895                                 class->free_folder_info (store, folder_info);
2896                         }
2897                 } else {
2898                         /* Failed, just unlock our folders for re-use */
2899                         for (i = 0; i < folders->len; i++) {
2900                                 folder = folders->pdata[i];
2901                                 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2902                                 g_object_unref (folder);
2903                         }
2904                 }
2905         }
2906
2907         camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
2908
2909         g_ptr_array_free (folders, TRUE);
2910         g_free (old_name);
2911
2912         return success;
2913 }
2914
2915 /**
2916  * camel_store_rename_folder:
2917  * @store: a #CamelStore
2918  * @old_name: the current name of the folder
2919  * @new_name: the new name of the folder
2920  * @io_priority: the I/O priority of the request
2921  * @cancellable: optional #GCancellable object, or %NULL
2922  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2923  * @user_data: data to pass to the callback function
2924  *
2925  * Asynchronously renames the folder described by @old_name to @new_name.
2926  *
2927  * When the operation is finished, @callback will be called.  You can then
2928  * call camel_store_rename_folder_finish() to get the result of the operation.
2929  *
2930  * Since: 3.0
2931  **/
2932 void
2933 camel_store_rename_folder (CamelStore *store,
2934                            const gchar *old_name,
2935                            const gchar *new_name,
2936                            gint io_priority,
2937                            GCancellable *cancellable,
2938                            GAsyncReadyCallback callback,
2939                            gpointer user_data)
2940 {
2941         CamelStoreClass *class;
2942
2943         g_return_if_fail (CAMEL_IS_STORE (store));
2944         g_return_if_fail (old_name != NULL);
2945         g_return_if_fail (new_name != NULL);
2946
2947         class = CAMEL_STORE_GET_CLASS (store);
2948         g_return_if_fail (class->rename_folder != NULL);
2949
2950         class->rename_folder (
2951                 store, old_name, new_name, io_priority,
2952                 cancellable, callback, user_data);
2953 }
2954
2955 /**
2956  * camel_store_rename_folder_finish:
2957  * @store: a #CamelStore
2958  * @result: a #GAsyncResult
2959  * @error: return location for a #GError, or %NULL
2960  *
2961  * Finishes the operation started with camel_store_rename_folder().
2962  *
2963  * Returns: %TRUE on success, %FALSE on error
2964  *
2965  * Since: 3.0
2966  **/
2967 gboolean
2968 camel_store_rename_folder_finish (CamelStore *store,
2969                                   GAsyncResult *result,
2970                                   GError **error)
2971 {
2972         CamelStoreClass *class;
2973
2974         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
2975         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2976
2977         class = CAMEL_STORE_GET_CLASS (store);
2978         g_return_val_if_fail (class->rename_folder_finish != NULL, FALSE);
2979
2980         return class->rename_folder_finish (store, result, error);
2981 }
2982
2983 /**
2984  * camel_store_synchronize_sync:
2985  * @store: a #CamelStore
2986  * @expunge: whether to expunge after synchronizing
2987  * @cancellable: optional #GCancellable object, or %NULL
2988  * @error: return location for a #GError, or %NULL
2989  *
2990  * Synchronizes any changes that have been made to @store and its folders
2991  * with the real store.
2992  *
2993  * Returns: %TRUE on success, %FALSE on error
2994  *
2995  * Since: 3.0
2996  **/
2997 gboolean
2998 camel_store_synchronize_sync (CamelStore *store,
2999                               gboolean expunge,
3000                               GCancellable *cancellable,
3001                               GError **error)
3002 {
3003         CamelStoreClass *class;
3004         gboolean success;
3005
3006         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3007
3008         class = CAMEL_STORE_GET_CLASS (store);
3009         g_return_val_if_fail (class->synchronize_sync != NULL, FALSE);
3010
3011         success = class->synchronize_sync (store, expunge, cancellable, error);
3012         CAMEL_CHECK_GERROR (store, synchronize_sync, success, error);
3013
3014         return success;
3015 }
3016
3017 /**
3018  * camel_store_synchronize:
3019  * @store: a #CamelStore
3020  * @expunge: whether to expunge after synchronizing
3021  * @io_priority: the I/O priority of the request
3022  * @cancellable: optional #GCancellable object, or %NULL
3023  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3024  * @user_data: data to pass to the callback function
3025  *
3026  * Synchronizes any changes that have been made to @store and its folders
3027  * with the real store asynchronously.
3028  *
3029  * When the operation is finished, @callback will be called.  You can then
3030  * call camel_store_synchronize_finish() to get the result of the operation.
3031  *
3032  * Since: 3.0
3033  **/
3034 void
3035 camel_store_synchronize (CamelStore *store,
3036                          gboolean expunge,
3037                          gint io_priority,
3038                          GCancellable *cancellable,
3039                          GAsyncReadyCallback callback,
3040                          gpointer user_data)
3041 {
3042         CamelStoreClass *class;
3043
3044         g_return_if_fail (CAMEL_IS_STORE (store));
3045
3046         class = CAMEL_STORE_GET_CLASS (store);
3047         g_return_if_fail (class->synchronize != NULL);
3048
3049         class->synchronize (
3050                 store, expunge, io_priority,
3051                 cancellable, callback, user_data);
3052 }
3053
3054 /**
3055  * camel_store_synchronize_finish:
3056  * @store: a #CamelStore
3057  * @result: a #GAsyncResult
3058  * @error: return location for a #GError, or %NULL
3059  *
3060  * Finishes the operation started with camel_store_synchronize().
3061  *
3062  * Returns: %TRUE on success, %FALSE on error
3063  *
3064  * Since: 3.0
3065  **/
3066 gboolean
3067 camel_store_synchronize_finish (CamelStore *store,
3068                                 GAsyncResult *result,
3069                                 GError **error)
3070 {
3071         CamelStoreClass *class;
3072
3073         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3074         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3075
3076         class = CAMEL_STORE_GET_CLASS (store);
3077         g_return_val_if_fail (class->synchronize_finish != NULL, FALSE);
3078
3079         return class->synchronize_finish (store, result, error);
3080 }
3081
3082 /**
3083  * camel_store_noop_sync:
3084  * @store: a #CamelStore
3085  * @cancellable: optional #GCancellable object, or %NULL
3086  * @error: return location for a #GError, or %NULL
3087  *
3088  * Pings @store so its connection doesn't time out.
3089  *
3090  * Returns: %TRUE on success, %FALSE on error
3091  *
3092  * Since: 3.0
3093  **/
3094 gboolean
3095 camel_store_noop_sync (CamelStore *store,
3096                        GCancellable *cancellable,
3097                        GError **error)
3098 {
3099         CamelStoreClass *class;
3100         gboolean success;
3101
3102         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3103
3104         class = CAMEL_STORE_GET_CLASS (store);
3105         g_return_val_if_fail (class->noop_sync != NULL, FALSE);
3106
3107         success = class->noop_sync (store, cancellable, error);
3108         CAMEL_CHECK_GERROR (store, noop_sync, success, error);
3109
3110         return success;
3111 }
3112
3113 /**
3114  * camel_store_noop:
3115  * @store: a #CamelStore
3116  * @io_priority: the I/O priority of the request
3117  * @cancellable: optional #GCancellable object, or %NULL
3118  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3119  * @user_data: data to pass to the callback function
3120  *
3121  * Pings @store asynchronously so its connection doesn't time out.
3122  *
3123  * When the operation is finished, @callback will be called.  You can then
3124  * call camel_store_noop_finish() to get the result of the operation.
3125  *
3126  * Since: 3.0
3127  **/
3128 void
3129 camel_store_noop (CamelStore *store,
3130                   gint io_priority,
3131                   GCancellable *cancellable,
3132                   GAsyncReadyCallback callback,
3133                   gpointer user_data)
3134 {
3135         CamelStoreClass *class;
3136
3137         g_return_if_fail (CAMEL_IS_STORE (store));
3138
3139         class = CAMEL_STORE_GET_CLASS (store);
3140         g_return_if_fail (class->noop != NULL);
3141
3142         class->noop (store, io_priority, cancellable, callback, user_data);
3143 }
3144
3145 /**
3146  * camel_store_noop_finish:
3147  * @store: a #CamelStore
3148  * @result: a #GAsyncResult
3149  * @error: return location for a #GError, or %NULL
3150  *
3151  * Finishes the operation started with camel_store_noop().
3152  *
3153  * Returns: %TRUE on success, %FALSE on error
3154  *
3155  * Since: 3.0
3156  **/
3157 gboolean
3158 camel_store_noop_finish (CamelStore *store,
3159                          GAsyncResult *result,
3160                          GError **error)
3161 {
3162         CamelStoreClass *class;
3163
3164         g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
3165         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3166
3167         class = CAMEL_STORE_GET_CLASS (store);
3168         g_return_val_if_fail (class->noop_finish != NULL, FALSE);
3169
3170         return class->noop_finish (store, result, error);
3171 }