1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-folder.c : class for a imap folder */
4 * Authors: Michael Zucchi <notzed@ximian.com>
6 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU Lesser General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
28 #include <glib/gi18n-lib.h>
30 #include "camel-imapx-utils.h"
31 #include "camel-imapx-store.h"
32 #include "camel-imapx-folder.h"
33 #include "camel-imapx-summary.h"
34 #include "camel-imapx-server.h"
39 #define d(...) camel_imapx_debug(debug, '?', __VA_ARGS__)
41 /* The custom property ID is a CamelArg artifact.
42 * It still identifies the property in state files. */
45 PROP_APPLY_FILTERS = 0x2501
48 G_DEFINE_TYPE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER)
50 static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder);
53 camel_imapx_folder_new (CamelStore *store,
54 const gchar *folder_dir,
55 const gchar *folder_name,
59 CamelService *service;
60 CamelSettings *settings;
61 CamelIMAPXFolder *ifolder;
62 const gchar *short_name;
65 gboolean filter_inbox;
67 gboolean filter_junk_inbox;
69 d("opening imap folder '%s'\n", folder_dir);
71 service = CAMEL_SERVICE (store);
72 settings = camel_service_get_settings (service);
76 "filter-all", &filter_all,
77 "filter-inbox", &filter_inbox,
78 "filter-junk", &filter_junk,
79 "filter-junk-inbox", &filter_junk_inbox,
82 short_name = strrchr (folder_name, '/');
86 short_name = folder_name;
88 folder = g_object_new (
89 CAMEL_TYPE_IMAPX_FOLDER,
90 "display-name", short_name,
91 "full_name", folder_name,
92 "parent-store", store, NULL);
93 ifolder = (CamelIMAPXFolder *) folder;
95 ((CamelIMAPXFolder *) folder)->raw_name = g_strdup (folder_name);
97 folder->summary = camel_imapx_summary_new (folder);
98 if (!folder->summary) {
100 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
101 _("Could not create folder summary for %s"),
106 ifolder->cache = camel_data_cache_new (folder_dir, error);
107 if (!ifolder->cache) {
109 error, _("Could not create cache for %s: "),
114 state_file = g_build_filename (folder_dir, "cmeta", NULL);
115 camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
117 camel_object_state_read (CAMEL_OBJECT (folder));
119 ifolder->search = camel_folder_search_new ();
120 ifolder->search_lock = g_mutex_new ();
121 ifolder->stream_lock = g_mutex_new ();
122 ifolder->ignore_recent = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
123 ifolder->exists_on_server = 0;
124 ifolder->unread_on_server = 0;
125 ifolder->modseq_on_server = 0;
126 ifolder->uidnext_on_server = 0;
128 if (!g_ascii_strcasecmp (folder_name, "INBOX")) {
129 if (filter_inbox || filter_all)
130 folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
132 folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
134 if (filter_junk && !filter_junk_inbox)
135 folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
137 if (filter_all || imapx_folder_get_apply_filters (ifolder))
138 folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
141 camel_store_summary_connect_folder_summary (
142 (CamelStoreSummary *) ((CamelIMAPXStore *) store)->summary,
143 folder_name, folder->summary);
149 imapx_folder_get_apply_filters (CamelIMAPXFolder *folder)
151 g_return_val_if_fail (folder != NULL, FALSE);
152 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
154 return folder->apply_filters;
158 imapx_folder_set_apply_filters (CamelIMAPXFolder *folder,
159 gboolean apply_filters)
161 g_return_if_fail (folder != NULL);
162 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
164 if ((folder->apply_filters ? 1 : 0) == (apply_filters ? 1 : 0))
167 folder->apply_filters = apply_filters;
169 g_object_notify (G_OBJECT (folder), "apply-filters");
173 imapx_folder_set_property (GObject *object,
178 switch (property_id) {
179 case PROP_APPLY_FILTERS:
180 imapx_folder_set_apply_filters (
181 CAMEL_IMAPX_FOLDER (object),
182 g_value_get_boolean (value));
186 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
190 imapx_folder_get_property (GObject *object,
195 switch (property_id) {
196 case PROP_APPLY_FILTERS:
197 g_value_set_boolean (
198 value, imapx_folder_get_apply_filters (
199 CAMEL_IMAPX_FOLDER (object)));
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
207 imapx_folder_dispose (GObject *object)
209 CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
210 CamelStore *parent_store;
212 if (folder->cache != NULL) {
213 g_object_unref (folder->cache);
214 folder->cache = NULL;
217 if (folder->search != NULL) {
218 g_object_unref (folder->search);
219 folder->search = NULL;
222 parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (folder));
224 camel_store_summary_disconnect_folder_summary (
225 (CamelStoreSummary *) ((CamelIMAPXStore *) parent_store)->summary,
226 CAMEL_FOLDER (folder)->summary);
229 /* Chain up to parent's dispose() method. */
230 G_OBJECT_CLASS (camel_imapx_folder_parent_class)->dispose (object);
234 imapx_folder_finalize (GObject *object)
236 CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
238 if (folder->ignore_recent != NULL)
239 g_hash_table_unref (folder->ignore_recent);
241 g_mutex_free (folder->search_lock);
242 g_mutex_free (folder->stream_lock);
244 /* Chain up to parent's finalize() method. */
245 G_OBJECT_CLASS (camel_imapx_folder_parent_class)->finalize (object);
249 imapx_get_filename (CamelFolder *folder,
253 CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
255 return camel_data_cache_get_filename (ifolder->cache, "cache", uid, error);
258 /* Algorithm for selecting a folder:
260 * - If uidvalidity == old uidvalidity
261 * and exsists == old exists
262 * and recent == old recent
263 * and unseen == old unseen
264 * Assume our summary is correct
265 * for each summary item
266 * mark the summary item as 'old/not updated'
268 * fetch flags from 1:*
269 * for each fetch response
270 * info = summary[index]
272 * info = summary_by_uid[uid]
275 * create new info @ index
281 * update based on header
284 * update based on imap body
288 * Async fetch response:
289 * info = summary[index]
292 * force resync/select?
293 * info = empty @ index
294 * else if uid && info.uid != uid
307 * info.state - 2 bit field in flags
308 * 0 = empty, nothing set
309 * 1 = uid & flags set
310 * 2 = update required
315 imapx_search_free (CamelFolder *folder,
318 CamelIMAPXFolder *ifolder = CAMEL_IMAPX_FOLDER (folder);
320 g_return_if_fail (ifolder->search);
322 g_mutex_lock (ifolder->search_lock);
324 camel_folder_search_free_result (ifolder->search, uids);
326 g_mutex_unlock (ifolder->search_lock);
330 imapx_search_by_uids (CamelFolder *folder,
331 const gchar *expression,
333 GCancellable *cancellable,
336 CamelIMAPXFolder *ifolder = CAMEL_IMAPX_FOLDER (folder);
340 return g_ptr_array_new ();
342 g_mutex_lock (ifolder->search_lock);
344 camel_folder_search_set_folder (ifolder->search, folder);
345 matches = camel_folder_search_search (ifolder->search, expression, uids, cancellable, error);
347 g_mutex_unlock (ifolder->search_lock);
353 imapx_count_by_expression (CamelFolder *folder,
354 const gchar *expression,
355 GCancellable *cancellable,
358 CamelIMAPXFolder *ifolder = CAMEL_IMAPX_FOLDER (folder);
361 g_mutex_lock (ifolder->search_lock);
363 camel_folder_search_set_folder (ifolder->search, folder);
364 matches = camel_folder_search_count (ifolder->search, expression, cancellable, error);
366 g_mutex_unlock (ifolder->search_lock);
372 imapx_search_by_expression (CamelFolder *folder,
373 const gchar *expression,
374 GCancellable *cancellable,
377 CamelIMAPXFolder *ifolder = CAMEL_IMAPX_FOLDER (folder);
380 g_mutex_lock (ifolder->search_lock);
382 camel_folder_search_set_folder (ifolder->search, folder);
383 matches = camel_folder_search_search (ifolder->search, expression, NULL, cancellable, error);
385 g_mutex_unlock (ifolder->search_lock);
391 imapx_append_message_sync (CamelFolder *folder,
392 CamelMimeMessage *message,
393 CamelMessageInfo *info,
394 gchar **appended_uid,
395 GCancellable *cancellable,
398 CamelStore *parent_store;
399 CamelIMAPXStore *istore;
400 CamelIMAPXServer *server;
401 gboolean success = FALSE;
403 parent_store = camel_folder_get_parent_store (folder);
404 istore = CAMEL_IMAPX_STORE (parent_store);
406 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
408 error, CAMEL_SERVICE_ERROR,
409 CAMEL_SERVICE_ERROR_UNAVAILABLE,
410 _("You must be working online to complete this operation"));
415 *appended_uid = NULL;
417 server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
419 success = camel_imapx_server_append_message (
420 server, folder, message, info, appended_uid, cancellable, error);
421 g_object_unref (server);
428 imapx_expunge_sync (CamelFolder *folder,
429 GCancellable *cancellable,
432 CamelStore *parent_store;
433 CamelIMAPXStore *istore;
434 CamelIMAPXServer *server;
436 parent_store = camel_folder_get_parent_store (folder);
437 istore = CAMEL_IMAPX_STORE (parent_store);
439 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
441 error, CAMEL_SERVICE_ERROR,
442 CAMEL_SERVICE_ERROR_UNAVAILABLE,
443 _("You must be working online to complete this operation"));
447 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
449 camel_imapx_server_expunge (server, folder, cancellable, error);
450 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
451 g_object_unref (server);
459 imapx_fetch_messages_sync (CamelFolder *folder,
462 GCancellable *cancellable,
465 CamelStore *parent_store;
466 CamelIMAPXStore *istore;
467 CamelIMAPXServer *server;
468 gboolean success = FALSE;
470 parent_store = camel_folder_get_parent_store (folder);
471 istore = CAMEL_IMAPX_STORE (parent_store);
473 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
475 error, CAMEL_SERVICE_ERROR,
476 CAMEL_SERVICE_ERROR_UNAVAILABLE,
477 _("You must be working online to complete this operation"));
481 if (!camel_service_connect_sync ((CamelService *) istore, error))
484 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
485 if (server != NULL) {
486 success = camel_imapx_server_fetch_messages (server, folder, type, limit, cancellable, error);
487 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
488 g_object_unref (server);
494 static CamelMimeMessage *
495 imapx_get_message_sync (CamelFolder *folder,
497 GCancellable *cancellable,
500 CamelMimeMessage *msg = NULL;
501 CamelStream *stream = NULL;
502 CamelStore *parent_store;
503 CamelIMAPXStore *istore;
504 CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
505 CamelIMAPXServer *server;
506 const gchar *path = NULL;
507 gboolean offline_message = FALSE;
509 parent_store = camel_folder_get_parent_store (folder);
510 istore = CAMEL_IMAPX_STORE (parent_store);
512 if (!strchr (uid, '-'))
516 offline_message = TRUE;
519 stream = camel_data_cache_get (ifolder->cache, path, uid, NULL);
521 if (offline_message) {
523 error, CAMEL_FOLDER_ERROR,
524 CAMEL_FOLDER_ERROR_INVALID_UID,
525 "Offline message vanished from disk: %s", uid);
529 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
531 error, CAMEL_SERVICE_ERROR,
532 CAMEL_SERVICE_ERROR_UNAVAILABLE,
533 _("You must be working online to complete this operation"));
537 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
539 stream = camel_imapx_server_get_message (server, folder, uid, cancellable, error);
540 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
541 g_object_unref (server);
546 if (stream != NULL) {
547 msg = camel_mime_message_new ();
549 g_mutex_lock (ifolder->stream_lock);
550 if (!camel_data_wrapper_construct_from_stream_sync (
551 (CamelDataWrapper *) msg, stream, cancellable, error)) {
552 g_object_unref (msg);
555 g_mutex_unlock (ifolder->stream_lock);
556 g_object_unref (stream);
559 CamelMessageInfo *mi = camel_folder_summary_get (folder->summary, uid);
562 gboolean has_attachment;
564 has_attachment = camel_mime_message_has_attachment (msg);
565 if (((camel_message_info_flags (mi) & CAMEL_MESSAGE_ATTACHMENTS) && !has_attachment) ||
566 ((camel_message_info_flags (mi) & CAMEL_MESSAGE_ATTACHMENTS) == 0 && has_attachment)) {
567 camel_message_info_set_flags (mi,
568 CAMEL_MESSAGE_ATTACHMENTS, has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
571 camel_message_info_free (mi);
580 imapx_purge_message_cache_sync (CamelFolder *folder,
583 GCancellable *cancellable,
586 /* Not Implemented for now. */
591 imapx_refresh_info_sync (CamelFolder *folder,
592 GCancellable *cancellable,
595 CamelStore *parent_store;
596 CamelIMAPXStore *istore;
597 CamelIMAPXServer *server;
598 gboolean success = FALSE;
600 parent_store = camel_folder_get_parent_store (folder);
601 istore = CAMEL_IMAPX_STORE (parent_store);
603 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
605 error, CAMEL_SERVICE_ERROR,
606 CAMEL_SERVICE_ERROR_UNAVAILABLE,
607 _("You must be working online to complete this operation"));
611 if (!camel_service_connect_sync ((CamelService *) istore, error))
614 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
615 if (server != NULL) {
616 success = camel_imapx_server_refresh_info (server, folder, cancellable, error);
617 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
618 g_object_unref (server);
625 imapx_synchronize_sync (CamelFolder *folder,
627 GCancellable *cancellable,
630 CamelStore *parent_store;
631 CamelIMAPXStore *istore;
632 CamelIMAPXServer *server;
634 parent_store = camel_folder_get_parent_store (folder);
635 istore = CAMEL_IMAPX_STORE (parent_store);
637 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
639 error, CAMEL_SERVICE_ERROR,
640 CAMEL_SERVICE_ERROR_UNAVAILABLE,
641 _("You must be working online to complete this operation"));
645 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
649 camel_imapx_server_sync_changes (server, folder, cancellable, NULL);
651 /* Sync twice - make sure deleted flags are written out,
652 * then sync again incase expunge changed anything */
655 camel_imapx_server_expunge (server, folder, cancellable, NULL);
657 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
658 g_object_unref (server);
664 imapx_synchronize_message_sync (CamelFolder *folder,
666 GCancellable *cancellable,
669 CamelStore *parent_store;
670 CamelIMAPXStore *istore;
671 CamelIMAPXServer *server;
674 parent_store = camel_folder_get_parent_store (folder);
675 istore = CAMEL_IMAPX_STORE (parent_store);
677 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
679 error, CAMEL_SERVICE_ERROR,
680 CAMEL_SERVICE_ERROR_UNAVAILABLE,
681 _("You must be working online to complete this operation"));
685 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (folder), cancellable, error);
689 success = camel_imapx_server_sync_message (server, folder, uid, cancellable, error);
690 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (folder));
691 g_object_unref (server);
697 imapx_transfer_messages_to_sync (CamelFolder *source,
700 gboolean delete_originals,
701 GPtrArray **transferred_uids,
702 GCancellable *cancellable,
705 CamelStore *parent_store;
706 CamelIMAPXStore *istore;
707 CamelIMAPXServer *server;
708 gboolean success = FALSE;
710 parent_store = camel_folder_get_parent_store (source);
711 istore = CAMEL_IMAPX_STORE (parent_store);
713 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
715 error, CAMEL_SERVICE_ERROR,
716 CAMEL_SERVICE_ERROR_UNAVAILABLE,
717 _("You must be working online to complete this operation"));
721 server = camel_imapx_store_get_server (istore, camel_folder_get_full_name (source), cancellable, error);
723 success = camel_imapx_server_copy_message (server, source, dest, uids, delete_originals, cancellable, error);
724 camel_imapx_store_op_done (istore, server, camel_folder_get_full_name (source));
725 g_object_unref (server);
728 imapx_refresh_info_sync (dest, cancellable, NULL);
734 imapx_rename (CamelFolder *folder,
735 const gchar *new_name)
737 CamelStore *parent_store;
739 parent_store = camel_folder_get_parent_store (folder);
741 camel_store_summary_disconnect_folder_summary (
742 (CamelStoreSummary *) ((CamelIMAPXStore *) parent_store)->summary,
745 CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->rename (folder, new_name);
747 camel_store_summary_connect_folder_summary (
748 (CamelStoreSummary *) ((CamelIMAPXStore *) parent_store)->summary,
749 camel_folder_get_full_name (folder), folder->summary);
753 camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
755 GObjectClass *object_class;
756 CamelFolderClass *folder_class;
758 object_class = G_OBJECT_CLASS (class);
759 object_class->set_property = imapx_folder_set_property;
760 object_class->get_property = imapx_folder_get_property;
761 object_class->dispose = imapx_folder_dispose;
762 object_class->finalize = imapx_folder_finalize;
764 folder_class = CAMEL_FOLDER_CLASS (class);
765 folder_class->rename = imapx_rename;
766 folder_class->search_by_expression = imapx_search_by_expression;
767 folder_class->search_by_uids = imapx_search_by_uids;
768 folder_class->count_by_expression = imapx_count_by_expression;
769 folder_class->search_free = imapx_search_free;
770 folder_class->get_filename = imapx_get_filename;
771 folder_class->append_message_sync = imapx_append_message_sync;
772 folder_class->expunge_sync = imapx_expunge_sync;
773 folder_class->fetch_messages_sync = imapx_fetch_messages_sync;
774 folder_class->get_message_sync = imapx_get_message_sync;
775 folder_class->purge_message_cache_sync = imapx_purge_message_cache_sync;
776 folder_class->refresh_info_sync = imapx_refresh_info_sync;
777 folder_class->synchronize_sync = imapx_synchronize_sync;
778 folder_class->synchronize_message_sync = imapx_synchronize_message_sync;
779 folder_class->transfer_messages_to_sync = imapx_transfer_messages_to_sync;
781 g_object_class_install_property (
784 g_param_spec_boolean (
787 _("Apply message _filters to this folder"),
790 CAMEL_PARAM_PERSISTENT));
794 camel_imapx_folder_init (CamelIMAPXFolder *imapx_folder)
796 CamelFolder *folder = CAMEL_FOLDER (imapx_folder);
798 folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
799 CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
801 folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
802 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_DRAFT |
803 CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER;
805 camel_folder_set_lock_async (folder, TRUE);