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