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