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