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