1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-folder.c: Abstract class for an email folder */
6 * Bertrand Guiheneuf <bertrand@helixcode.com>
8 * Copyright 1999-2003 Ximian, Inc. (www.ximian.com)
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU Lesser General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
32 #include <glib/gi18n-lib.h>
34 #include <libedataserver/e-memory.h>
36 #include "camel-debug.h"
37 #include "camel-exception.h"
38 #include "camel-filter-driver.h"
39 #include "camel-folder.h"
40 #include "camel-mime-message.h"
41 #include "camel-operation.h"
42 #include "camel-private.h"
43 #include "camel-session.h"
44 #include "camel-store.h"
45 #include "camel-vtrash-folder.h"
50 extern int camel_verbose_debug;
52 static CamelObjectClass *parent_class = NULL;
54 /* Returns the class for a CamelFolder */
55 #define CF_CLASS(so) ((CamelFolderClass *)((CamelObject *)(so))->klass)
57 static void camel_folder_finalize (CamelObject *object);
59 static void refresh_info (CamelFolder *folder, CamelException *ex);
61 static void folder_sync (CamelFolder *folder, gboolean expunge,
64 static const char *get_name (CamelFolder *folder);
65 static const char *get_full_name (CamelFolder *folder);
66 static CamelStore *get_parent_store (CamelFolder *folder);
68 static guint32 get_permanent_flags(CamelFolder *folder);
69 static guint32 get_message_flags(CamelFolder *folder, const char *uid);
70 static gboolean set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
71 static gboolean get_message_user_flag(CamelFolder *folder, const char *uid, const char *name);
72 static void set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value);
73 static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name);
74 static void set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value);
76 static int get_message_count(CamelFolder *folder);
78 static void expunge (CamelFolder *folder,
80 static int folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args);
81 static void folder_free(CamelObject *o, guint32 tag, void *val);
84 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
85 const CamelMessageInfo *info, char **appended_uid,
89 static GPtrArray *get_uids (CamelFolder *folder);
90 static void free_uids (CamelFolder *folder,
92 static GPtrArray *get_summary (CamelFolder *folder);
93 static void free_summary (CamelFolder *folder,
96 static CamelMimeMessage *get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
98 static CamelMessageInfo *get_message_info (CamelFolder *folder, const char *uid);
99 static void free_message_info (CamelFolder *folder, CamelMessageInfo *info);
100 static void ref_message_info (CamelFolder *folder, CamelMessageInfo *info);
102 static GPtrArray *search_by_expression (CamelFolder *folder, const char *exp, CamelException *ex);
103 static GPtrArray *search_by_uids (CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex);
104 static void search_free (CamelFolder * folder, GPtrArray *result);
106 static void transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest,
107 GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
109 static void delete (CamelFolder *folder);
110 static void folder_rename (CamelFolder *folder, const char *new);
112 static void freeze (CamelFolder *folder);
113 static void thaw (CamelFolder *folder);
114 static gboolean is_frozen (CamelFolder *folder);
116 static gboolean folder_changed (CamelObject *object,
117 gpointer event_data);
120 camel_folder_class_init (CamelFolderClass *camel_folder_class)
122 CamelObjectClass *camel_object_class = CAMEL_OBJECT_CLASS (camel_folder_class);
124 parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
126 /* virtual method definition */
127 camel_folder_class->sync = folder_sync;
128 camel_folder_class->refresh_info = refresh_info;
129 camel_folder_class->get_name = get_name;
130 camel_folder_class->get_full_name = get_full_name;
131 camel_folder_class->get_parent_store = get_parent_store;
132 camel_folder_class->expunge = expunge;
133 camel_folder_class->get_message_count = get_message_count;
134 camel_folder_class->append_message = append_message;
135 camel_folder_class->get_permanent_flags = get_permanent_flags;
136 camel_folder_class->get_message_flags = get_message_flags;
137 camel_folder_class->set_message_flags = set_message_flags;
138 camel_folder_class->get_message_user_flag = get_message_user_flag;
139 camel_folder_class->set_message_user_flag = set_message_user_flag;
140 camel_folder_class->get_message_user_tag = get_message_user_tag;
141 camel_folder_class->set_message_user_tag = set_message_user_tag;
142 camel_folder_class->get_message = get_message;
143 camel_folder_class->get_uids = get_uids;
144 camel_folder_class->free_uids = free_uids;
145 camel_folder_class->get_summary = get_summary;
146 camel_folder_class->free_summary = free_summary;
147 camel_folder_class->search_by_expression = search_by_expression;
148 camel_folder_class->search_by_uids = search_by_uids;
149 camel_folder_class->search_free = search_free;
150 camel_folder_class->get_message_info = get_message_info;
151 camel_folder_class->ref_message_info = ref_message_info;
152 camel_folder_class->free_message_info = free_message_info;
153 camel_folder_class->transfer_messages_to = transfer_messages_to;
154 camel_folder_class->delete = delete;
155 camel_folder_class->rename = folder_rename;
156 camel_folder_class->freeze = freeze;
157 camel_folder_class->thaw = thaw;
158 camel_folder_class->is_frozen = is_frozen;
160 /* virtual method overload */
161 camel_object_class->getv = folder_getv;
162 camel_object_class->free = folder_free;
165 camel_object_class_add_event(camel_object_class, "folder_changed", folder_changed);
166 camel_object_class_add_event(camel_object_class, "deleted", NULL);
167 camel_object_class_add_event(camel_object_class, "renamed", NULL);
171 camel_folder_init (gpointer object, gpointer klass)
173 CamelFolder *folder = object;
175 folder->priv = g_malloc0(sizeof(*folder->priv));
176 folder->priv->frozen = 0;
177 folder->priv->changed_frozen = camel_folder_change_info_new();
178 g_static_rec_mutex_init(&folder->priv->lock);
179 g_static_mutex_init(&folder->priv->change_lock);
183 camel_folder_finalize (CamelObject *object)
185 CamelFolder *camel_folder = CAMEL_FOLDER (object);
186 struct _CamelFolderPrivate *p = camel_folder->priv;
188 g_free(camel_folder->name);
189 g_free(camel_folder->full_name);
190 g_free(camel_folder->description);
192 if (camel_folder->parent_store)
193 camel_object_unref (camel_folder->parent_store);
195 if (camel_folder->summary)
196 camel_object_unref (camel_folder->summary);
198 camel_folder_change_info_free(p->changed_frozen);
200 g_static_rec_mutex_free(&p->lock);
201 g_static_mutex_free(&p->change_lock);
207 camel_folder_get_type (void)
209 static CamelType camel_folder_type = CAMEL_INVALID_TYPE;
211 if (camel_folder_type == CAMEL_INVALID_TYPE) {
212 camel_folder_type = camel_type_register (CAMEL_OBJECT_TYPE, "CamelFolder",
213 sizeof (CamelFolder),
214 sizeof (CamelFolderClass),
215 (CamelObjectClassInitFunc) camel_folder_class_init,
217 (CamelObjectInitFunc) camel_folder_init,
218 (CamelObjectFinalizeFunc) camel_folder_finalize );
221 return camel_folder_type;
226 * camel_folder_construct:
227 * @folder: a #CamelFolder object to construct
228 * @parent_store: parent #CamelStore object of the folder
229 * @full_name: full name of the folder
230 * @name: short name of the folder
232 * Initalizes the folder by setting the parent store and name.
235 camel_folder_construct (CamelFolder *folder, CamelStore *parent_store,
236 const char *full_name, const char *name)
238 g_return_if_fail (CAMEL_IS_FOLDER (folder));
239 g_return_if_fail (CAMEL_IS_STORE (parent_store));
240 g_return_if_fail (folder->parent_store == NULL);
241 g_return_if_fail (folder->name == NULL);
243 folder->parent_store = parent_store;
245 camel_object_ref(parent_store);
247 folder->name = g_strdup (name);
248 folder->full_name = g_strdup (full_name);
253 folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
255 w(g_warning ("CamelFolder::sync not implemented for `%s'",
256 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
262 * @folder: a #CamelFolder object
263 * @expunge: whether or not to expunge deleted messages
264 * @ex: a #CamelException
266 * Sync changes made to a folder to its backing store, possibly
267 * expunging deleted messages as well.
270 camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
272 g_return_if_fail (CAMEL_IS_FOLDER (folder));
274 CAMEL_FOLDER_REC_LOCK(folder, lock);
276 if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
277 CF_CLASS (folder)->sync (folder, expunge, ex);
279 CAMEL_FOLDER_REC_UNLOCK(folder, lock);
284 refresh_info (CamelFolder *folder, CamelException *ex)
291 * camel_folder_refresh_info:
292 * @folder: a #CamelFolder object
293 * @ex: a #CamelException
295 * Updates a folder's summary to be in sync with its backing store.
298 camel_folder_refresh_info (CamelFolder *folder, CamelException *ex)
300 g_return_if_fail (CAMEL_IS_FOLDER (folder));
302 CF_CLASS (folder)->refresh_info (folder, ex);
306 folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
308 CamelFolder *folder = (CamelFolder *)object;
311 int unread = -1, deleted = 0, junked = 0, visible = 0, count = -1;
313 for (i=0;i<args->argc;i++) {
314 CamelArgGet *arg = &args->argv[i];
318 switch (tag & CAMEL_ARG_TAG) {
319 /* CamelObject args */
320 case CAMEL_OBJECT_ARG_DESCRIPTION:
321 if (folder->description == NULL)
322 folder->description = g_strdup_printf("%s", folder->full_name);
323 *arg->ca_str = folder->description;
326 /* CamelFolder args */
327 case CAMEL_FOLDER_ARG_NAME:
328 *arg->ca_str = folder->name;
330 case CAMEL_FOLDER_ARG_FULL_NAME:
331 *arg->ca_str = folder->full_name;
333 case CAMEL_FOLDER_ARG_STORE:
334 *arg->ca_object = folder->parent_store;
336 case CAMEL_FOLDER_ARG_PERMANENTFLAGS:
337 *arg->ca_int = folder->permanent_flags;
339 case CAMEL_FOLDER_ARG_TOTAL:
340 *arg->ca_int = camel_folder_summary_count(folder->summary);
342 case CAMEL_FOLDER_ARG_UNREAD:
343 case CAMEL_FOLDER_ARG_DELETED:
344 case CAMEL_FOLDER_ARG_JUNKED:
345 case CAMEL_FOLDER_ARG_VISIBLE:
346 /* This is so we can get the values atomically, and also so we can calculate them only once */
349 CamelMessageInfo *info;
353 count = camel_folder_summary_count(folder->summary);
354 for (j=0; j<count; j++) {
355 if ((info = camel_folder_summary_index(folder->summary, j))) {
356 guint32 flags = camel_message_info_flags(info);
358 if ((flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
360 if (flags & CAMEL_MESSAGE_DELETED)
362 if (flags & CAMEL_MESSAGE_JUNK)
364 if ((flags & (CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
366 camel_message_info_free(info);
371 switch (tag & CAMEL_ARG_TAG) {
372 case CAMEL_FOLDER_ARG_UNREAD:
375 case CAMEL_FOLDER_ARG_DELETED:
378 case CAMEL_FOLDER_ARG_JUNKED:
381 case CAMEL_FOLDER_ARG_VISIBLE:
386 *arg->ca_int = count;
388 case CAMEL_FOLDER_ARG_UID_ARRAY: {
390 CamelMessageInfo *info;
393 count = camel_folder_summary_count(folder->summary);
394 array = g_ptr_array_new();
395 g_ptr_array_set_size(array, count);
396 for (j=0; j<count; j++) {
397 if ((info = camel_folder_summary_index(folder->summary, j))) {
398 array->pdata[i] = g_strdup(camel_message_info_uid(info));
399 camel_message_info_free(info);
402 *arg->ca_ptr = array;
404 case CAMEL_FOLDER_ARG_INFO_ARRAY:
405 *arg->ca_ptr = camel_folder_summary_array(folder->summary);
407 case CAMEL_FOLDER_ARG_PROPERTIES:
414 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
417 return parent_class->getv(object, ex, args);
421 folder_free(CamelObject *o, guint32 tag, void *val)
423 CamelFolder *folder = (CamelFolder *)o;
425 switch (tag & CAMEL_ARG_TAG) {
426 case CAMEL_FOLDER_ARG_UID_ARRAY: {
427 GPtrArray *array = val;
430 for (i=0; i<array->len; i++)
431 g_free(array->pdata[i]);
432 g_ptr_array_free(array, TRUE);
434 case CAMEL_FOLDER_ARG_INFO_ARRAY:
435 camel_folder_summary_array_free(folder->summary, val);
437 case CAMEL_FOLDER_ARG_PROPERTIES:
441 parent_class->free(o, tag, val);
446 get_name (CamelFolder *folder)
453 * camel_folder_get_name:
454 * @folder: a #CamelFolder object
456 * Get the (short) name of the folder. The fully qualified name
457 * can be obtained with the #camel_folder_get_full_name method.
459 * Returns the short name of the folder
462 camel_folder_get_name (CamelFolder *folder)
464 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
466 return CF_CLASS (folder)->get_name (folder);
471 get_full_name (CamelFolder *folder)
473 return folder->full_name;
478 * camel_folder_get_full_name:
479 * @folder: a #CamelFolder object
481 * Get the full name of the folder.
483 * Returns the full name of the folder
486 camel_folder_get_full_name (CamelFolder *folder)
488 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
490 return CF_CLASS (folder)->get_full_name (folder);
495 get_parent_store (CamelFolder * folder)
497 return folder->parent_store;
502 * camel_folder_get_parent_store:
503 * @folder: a #CamelFolder object
505 * Returns the parent #CamelStore of the folder
508 camel_folder_get_parent_store (CamelFolder *folder)
510 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
512 return CF_CLASS (folder)->get_parent_store (folder);
517 expunge (CamelFolder *folder, CamelException *ex)
519 w(g_warning ("CamelFolder::expunge not implemented for `%s'",
520 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
525 * camel_folder_expunge:
526 * @folder: a #CamelFolder object
527 * @ex: a #CamelException
529 * Delete messages which have been marked as "DELETED"
532 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
534 g_return_if_fail (CAMEL_IS_FOLDER (folder));
536 CAMEL_FOLDER_REC_LOCK(folder, lock);
538 if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
539 CF_CLASS (folder)->expunge (folder, ex);
541 CAMEL_FOLDER_REC_UNLOCK(folder, lock);
545 get_message_count (CamelFolder *folder)
547 g_return_val_if_fail(folder->summary != NULL, -1);
549 return camel_folder_summary_count(folder->summary);
554 * camel_folder_get_message_count:
555 * @folder: a #CamelFolder object
557 * Returns the number of messages in the folder, or %-1 if unknown
560 camel_folder_get_message_count (CamelFolder *folder)
564 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
566 ret = CF_CLASS (folder)->get_message_count (folder);
573 * camel_folder_get_unread_message_count:
574 * @folder: a #CamelFolder object
576 * DEPRECATED: use #camel_object_get instead.
578 * Returns the number of unread messages in the folder, or %-1 if
582 camel_folder_get_unread_message_count (CamelFolder *folder)
586 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
588 camel_object_get(folder, NULL, CAMEL_FOLDER_UNREAD, &count, 0);
595 * camel_folder_get_deleted_message_count:
596 * @folder: a #CamelFolder object
598 * Returns the number of deleted messages in the folder, or %-1 if
602 camel_folder_get_deleted_message_count (CamelFolder *folder)
606 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
608 camel_object_get(folder, NULL, CAMEL_FOLDER_DELETED, &count, 0);
614 append_message (CamelFolder *folder, CamelMimeMessage *message,
615 const CamelMessageInfo *info, char **appended_uid,
618 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
619 _("Unsupported operation: append message: for %s"),
620 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
622 w(g_warning ("CamelFolder::append_message not implemented for `%s'",
623 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
631 * camel_folder_append_message:
632 * @folder: a #CamelFolder object
633 * @message: a #CamelMimeMessage object
634 * @info: a #CamelMessageInfo with additional flags/etc to set on
635 * new message, or %NULL
636 * @appended_uid: if non-%NULL, the UID of the appended message will
637 * be returned here, if it is known.
638 * @ex: a #CamelException
640 * Append @message to @folder. Only the flag and tag data from @info
641 * are used. If @info is %NULL, no flags or tags will be set.
644 camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message,
645 const CamelMessageInfo *info, char **appended_uid,
648 g_return_if_fail (CAMEL_IS_FOLDER (folder));
650 CAMEL_FOLDER_REC_LOCK(folder, lock);
652 CF_CLASS (folder)->append_message (folder, message, info, appended_uid, ex);
654 CAMEL_FOLDER_REC_UNLOCK(folder, lock);
659 get_permanent_flags (CamelFolder *folder)
661 return folder->permanent_flags;
666 * camel_folder_get_permanent_flags:
667 * @folder: a #CamelFolder object
669 * Returns the set of #CamelMessageFlags that can be permanently
670 * stored on a message between sessions. If it includes
671 * #CAMEL_FLAG_USER, then user-defined flags will be remembered.
674 camel_folder_get_permanent_flags (CamelFolder *folder)
676 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
678 return CF_CLASS (folder)->get_permanent_flags (folder);
682 get_message_flags(CamelFolder *folder, const char *uid)
684 CamelMessageInfo *info;
687 g_return_val_if_fail(folder->summary != NULL, 0);
689 info = camel_folder_summary_uid(folder->summary, uid);
693 flags = camel_message_info_flags(info);
694 camel_message_info_free(info);
701 * camel_folder_get_message_flags:
702 * @folder: a #CamelFolder object
703 * @uid: the UID of a message in @folder
705 * Deprecated: Use #camel_folder_get_message_info instead.
707 * Returns the #CamelMessageFlags that are set on the indicated
711 camel_folder_get_message_flags (CamelFolder *folder, const char *uid)
715 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
717 ret = CF_CLASS (folder)->get_message_flags (folder, uid);
723 set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
725 CamelMessageInfo *info;
728 g_return_val_if_fail(folder->summary != NULL, FALSE);
730 info = camel_folder_summary_uid(folder->summary, uid);
734 res = camel_message_info_set_flags(info, flags, set);
735 camel_message_info_free(info);
742 * camel_folder_set_message_flags:
743 * @folder: a #CamelFolder object
744 * @uid: the UID of a message in @folder
745 * @flags: a set of #CamelMessageFlag values to set
746 * @set: the mask of values in @flags to use.
748 * Sets those flags specified by @flags to the values specified by @set
749 * on the indicated message. (This may or may not persist after the
750 * folder or store is closed. See #camel_folder_get_permanent_flags)
752 * E.g. to set the deleted flag and clear the draft flag, use
753 * #camel_folder_set_message_flags(folder, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_DRAFT, CAMEL_MESSAGE_DELETED);
755 * DEPRECATED: Use #camel_message_info_set_flags on the message info directly
758 * Returns %TRUE if the flags were changed or %FALSE otherwise
761 camel_folder_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
763 g_return_val_if_fail(CAMEL_IS_FOLDER(folder), FALSE);
765 if ((flags & (CAMEL_MESSAGE_JUNK|CAMEL_MESSAGE_JUNK_LEARN)) == CAMEL_MESSAGE_JUNK) {
766 flags |= CAMEL_MESSAGE_JUNK_LEARN;
767 set &= ~CAMEL_MESSAGE_JUNK_LEARN;
770 return CF_CLASS(folder)->set_message_flags(folder, uid, flags, set);
774 get_message_user_flag(CamelFolder *folder, const char *uid, const char *name)
776 CamelMessageInfo *info;
779 g_return_val_if_fail(folder->summary != NULL, FALSE);
781 info = camel_folder_summary_uid(folder->summary, uid);
785 ret = camel_message_info_user_flag(info, name);
786 camel_message_info_free(info);
793 * camel_folder_get_message_user_flag:
794 * @folder: a #CamelFolder object
795 * @uid: the UID of a message in @folder
796 * @name: the name of a user flag
798 * DEPRECATED: Use #camel_message_info_get_user_flag on the message
801 * Returns %TRUE if the given user flag is set on the message or
805 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
810 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
812 ret = CF_CLASS (folder)->get_message_user_flag (folder, uid, name);
818 set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
820 CamelMessageInfo *info;
822 g_return_if_fail(folder->summary != NULL);
824 info = camel_folder_summary_uid(folder->summary, uid);
828 camel_message_info_set_user_flag(info, name, value);
829 camel_message_info_free(info);
834 * camel_folder_set_message_user_flag:
835 * @folder: a #CamelFolder object
836 * @uid: the UID of a message in @folder
837 * @name: the name of the user flag to set
838 * @value: the value to set it to
840 * DEPRECATED: Use #camel_message_info_set_user_flag on the
841 * #CamelMessageInfo directly (when it works)
843 * Sets the user flag specified by @name to the value specified by @value
844 * on the indicated message. (This may or may not persist after the
845 * folder or store is closed. See #camel_folder_get_permanent_flags)
848 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
849 const char *name, gboolean value)
851 g_return_if_fail (CAMEL_IS_FOLDER (folder));
853 CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value);
857 get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
859 CamelMessageInfo *info;
862 g_return_val_if_fail(folder->summary != NULL, NULL);
864 info = camel_folder_summary_uid(folder->summary, uid);
868 ret = camel_message_info_user_tag(info, name);
869 camel_message_info_free(info);
876 * camel_folder_get_message_user_tag:
877 * @folder: a #CamelFolder object
878 * @uid: the UID of a message in @folder
879 * @name: the name of a user tag
881 * DEPRECATED: Use #camel_message_info_get_user_tag on the
882 * #CamelMessageInfo directly.
884 * Returns the value of the user tag
887 camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid, const char *name)
891 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
893 /* FIXME: should duplicate string */
894 ret = CF_CLASS (folder)->get_message_user_tag (folder, uid, name);
900 set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
902 CamelMessageInfo *info;
904 g_return_if_fail(folder->summary != NULL);
906 info = camel_folder_summary_uid(folder->summary, uid);
910 camel_message_info_set_user_tag(info, name, value);
911 camel_message_info_free(info);
916 * camel_folder_set_message_user_tag:
917 * @folder: a #CamelFolder object
918 * @uid: the UID of a message in @folder
919 * @name: the name of the user tag to set
920 * @value: the value to set it to
922 * DEPRECATED: Use #camel_message_info_set_user_tag on the
923 * #CamelMessageInfo directly (when it works).
925 * Sets the user tag specified by @name to the value specified by @value
926 * on the indicated message. (This may or may not persist after the
927 * folder or store is closed. See #camel_folder_get_permanent_flags)
930 camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const char *name, const char *value)
932 g_return_if_fail (CAMEL_IS_FOLDER (folder));
934 CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value);
937 static CamelMessageInfo *
938 get_message_info (CamelFolder *folder, const char *uid)
940 g_return_val_if_fail(folder->summary != NULL, NULL);
942 return camel_folder_summary_uid(folder->summary, uid);
947 * camel_folder_get_message_info:
948 * @folder: a #CamelFolder object
949 * @uid: the uid of a message
951 * Retrieve the #CamelMessageInfo for the specified @uid. This return
952 * must be freed using #camel_folder_free_message_info.
954 * Returns the summary information for the indicated message, or %NULL
955 * if the uid does not exist
958 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
960 CamelMessageInfo *ret;
962 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
963 g_return_val_if_fail (uid != NULL, NULL);
965 ret = CF_CLASS (folder)->get_message_info (folder, uid);
971 free_message_info (CamelFolder *folder, CamelMessageInfo *info)
973 g_return_if_fail(folder->summary != NULL);
975 camel_message_info_free(info);
980 * camel_folder_free_message_info:
981 * @folder: a #CamelFolder object
982 * @info: a #CamelMessageInfo
984 * Free (unref) a #CamelMessageInfo, previously obtained with
985 * #camel_folder_get_message_info.
988 camel_folder_free_message_info(CamelFolder *folder, CamelMessageInfo *info)
990 g_return_if_fail(CAMEL_IS_FOLDER (folder));
991 g_return_if_fail(info != NULL);
993 CF_CLASS (folder)->free_message_info(folder, info);
997 ref_message_info (CamelFolder *folder, CamelMessageInfo *info)
999 g_return_if_fail(folder->summary != NULL);
1001 camel_message_info_ref(info);
1006 * camel_folder_ref_message_info:
1007 * @folder: a #CamelFolder object
1008 * @info: a #CamelMessageInfo
1010 * DEPRECATED: Use #camel_message_info_ref directly.
1012 * Ref a #CamelMessageInfo, previously obtained with
1013 * #camel_folder_get_message_info.
1016 camel_folder_ref_message_info(CamelFolder *folder, CamelMessageInfo *info)
1018 g_return_if_fail(CAMEL_IS_FOLDER (folder));
1019 g_return_if_fail(info != NULL);
1021 CF_CLASS (folder)->ref_message_info(folder, info);
1025 /* TODO: is this function required anyway? */
1027 * camel_folder_has_summary_capability:
1028 * @folder: a #CamelFolder object
1030 * Get whether or not the folder has a summary.
1032 * Returns %TRUE if a summary is available or %FALSE otherwise
1035 camel_folder_has_summary_capability (CamelFolder *folder)
1037 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1039 return folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
1045 static CamelMimeMessage *
1046 get_message (CamelFolder *folder, const char *uid, CamelException *ex)
1048 w(g_warning ("CamelFolder::get_message not implemented for `%s'",
1049 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1056 * camel_folder_get_message:
1057 * @folder: a #CamelFolder object
1059 * @ex: a #CamelException
1061 * Get a message from its UID in the folder.
1063 * Returns a #CamelMimeMessage corresponding to @uid
1066 camel_folder_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
1068 CamelMimeMessage *ret;
1070 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1072 CAMEL_FOLDER_REC_LOCK(folder, lock);
1074 ret = CF_CLASS (folder)->get_message (folder, uid, ex);
1076 CAMEL_FOLDER_REC_UNLOCK(folder, lock);
1078 if (ret && camel_debug_start(":folder")) {
1079 printf("CamelFolder:get_message('%s', '%s') =\n", folder->full_name, uid);
1080 camel_mime_message_dump(ret, FALSE);
1088 get_uids(CamelFolder *folder)
1093 array = g_ptr_array_new();
1095 g_return_val_if_fail(folder->summary != NULL, array);
1097 count = camel_folder_summary_count(folder->summary);
1098 g_ptr_array_set_size(array, count);
1099 for (i = 0, j = 0; i < count; i++) {
1100 CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i);
1103 array->pdata[j++] = g_strdup (camel_message_info_uid (info));
1104 camel_message_info_free(info);
1108 g_ptr_array_set_size (array, j);
1115 * camel_folder_get_uids:
1116 * @folder: a #CamelFolder object
1118 * Get the list of UIDs available in a folder. This routine is useful
1119 * for finding what messages are available when the folder does not
1120 * support summaries. The returned array should not be modified, and
1121 * must be freed by passing it to #camel_folder_free_uids.
1123 * Returns a GPtrArray of UIDs corresponding to the messages available
1127 camel_folder_get_uids (CamelFolder *folder)
1131 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1133 ret = CF_CLASS (folder)->get_uids (folder);
1139 free_uids (CamelFolder *folder, GPtrArray *array)
1143 for (i=0; i<array->len; i++)
1144 g_free(array->pdata[i]);
1145 g_ptr_array_free(array, TRUE);
1150 * camel_folder_free_uids:
1151 * @folder: a #CamelFolder object
1152 * @array: the array of uids to free
1154 * Frees the array of UIDs returned by #camel_folder_get_uids.
1157 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
1159 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1161 CF_CLASS (folder)->free_uids (folder, array);
1165 get_summary(CamelFolder *folder)
1167 g_assert(folder->summary != NULL);
1169 return camel_folder_summary_array(folder->summary);
1174 * camel_folder_get_summary:
1175 * @folder: a #CamelFolder object
1177 * This returns the summary information for the folder. This array
1178 * should not be modified, and must be freed with
1179 * #camel_folder_free_summary.
1181 * Returns an array of #CamelMessageInfo
1184 camel_folder_get_summary (CamelFolder *folder)
1188 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1190 ret = CF_CLASS (folder)->get_summary (folder);
1196 free_summary(CamelFolder *folder, GPtrArray *summary)
1198 g_assert(folder->summary != NULL);
1200 camel_folder_summary_array_free(folder->summary, summary);
1205 * camel_folder_free_summary:
1206 * @folder: a #CamelFolder object
1207 * @array: the summary array to free
1209 * Frees the summary array returned by #camel_folder_get_summary.
1212 camel_folder_free_summary(CamelFolder *folder, GPtrArray *array)
1214 g_return_if_fail(CAMEL_IS_FOLDER(folder));
1216 CF_CLASS(folder)->free_summary(folder, array);
1221 * camel_folder_has_search_capability:
1222 * @folder: a #CamelFolder object
1224 * Checks if a folder supports searching.
1226 * Returns %TRUE if the folder supports searching or %FALSE otherwise
1229 camel_folder_has_search_capability (CamelFolder *folder)
1231 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1233 return folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
1237 search_by_expression (CamelFolder *folder, const char *expression,
1240 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1241 _("Unsupported operation: search by expression: for %s"),
1242 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1244 w(g_warning ("CamelFolder::search_by_expression not implemented for "
1245 "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1252 * camel_folder_search_by_expression:
1253 * @folder: a #CamelFolder object
1254 * @expr: a search expression
1255 * @ex: a #CamelException
1257 * Searches the folder for messages matching the given search expression.
1259 * Returns a #GPtrArray of uids of matching messages. The caller must
1260 * free the list and each of the elements when it is done.
1263 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1268 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1269 g_return_val_if_fail (folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1271 /* NOTE: that it is upto the callee to lock */
1273 ret = CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1279 search_by_uids(CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex)
1281 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1282 _("Unsupported operation: search by UIDs: for %s"),
1283 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1285 w(g_warning ("CamelFolder::search_by_expression not implemented for "
1286 "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1293 * camel_folder_search_by_uids:
1294 * @folder: a #CamelFolder object
1295 * @expr: search expression
1296 * @uids: array of uid's to match against.
1297 * @ex: a #CamelException
1299 * Search a subset of uid's for an expression match.
1301 * Returns a #GPtrArray of uids of matching messages. The caller must
1302 * free the list and each of the elements when it is done.
1305 camel_folder_search_by_uids(CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex)
1309 g_return_val_if_fail(CAMEL_IS_FOLDER (folder), NULL);
1310 g_return_val_if_fail(folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1312 /* NOTE: that it is upto the callee to lock */
1314 ret = CF_CLASS(folder)->search_by_uids(folder, expr, uids, ex);
1320 search_free (CamelFolder *folder, GPtrArray *result)
1324 for (i = 0; i < result->len; i++)
1325 g_free (g_ptr_array_index (result, i));
1326 g_ptr_array_free (result, TRUE);
1331 * camel_folder_search_free:
1332 * @folder: a #CamelFolder object
1333 * @result: search results to free
1335 * Free the result of a search as gotten by #camel_folder_search or
1336 * #camel_folder_search_by_uids.
1339 camel_folder_search_free (CamelFolder *folder, GPtrArray *result)
1341 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1343 /* NOTE: upto the callee to lock */
1344 CF_CLASS (folder)->search_free (folder, result);
1349 transfer_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1350 char **transferred_uid, gboolean delete_original,
1353 CamelMimeMessage *msg;
1354 CamelMessageInfo *minfo, *info;
1356 /* Default implementation. */
1358 msg = camel_folder_get_message(source, uid, ex);
1362 /* if its deleted we poke the flags, so we need to copy the messageinfo */
1363 if ((source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1364 && (minfo = camel_folder_get_message_info(source, uid))) {
1365 info = camel_message_info_clone(minfo);
1366 camel_folder_free_message_info(source, minfo);
1368 info = camel_message_info_new_from_header(NULL, ((CamelMimePart *)msg)->headers);
1370 /* we don't want to retain the deleted flag */
1371 camel_message_info_set_flags(info, CAMEL_MESSAGE_DELETED, 0);
1373 camel_folder_append_message (dest, msg, info, transferred_uid, ex);
1374 camel_object_unref (msg);
1376 if (delete_original && !camel_exception_is_set (ex))
1377 camel_folder_set_message_flags (source, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, ~0);
1379 camel_message_info_free (info);
1383 transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
1385 CamelException local;
1386 char **ret_uid = NULL;
1389 if (transferred_uids) {
1390 *transferred_uids = g_ptr_array_new ();
1391 g_ptr_array_set_size (*transferred_uids, uids->len);
1394 camel_exception_init(&local);
1398 camel_operation_start(NULL, delete_originals ? _("Moving messages") : _("Copying messages"));
1400 if (uids->len > 1) {
1401 camel_folder_freeze(dest);
1402 if (delete_originals)
1403 camel_folder_freeze(source);
1405 for (i = 0; i < uids->len && !camel_exception_is_set (ex); i++) {
1406 if (transferred_uids)
1407 ret_uid = (char **)&((*transferred_uids)->pdata[i]);
1408 transfer_message_to (source, uids->pdata[i], dest, ret_uid, delete_originals, ex);
1409 camel_operation_progress(NULL, i * 100 / uids->len);
1411 if (uids->len > 1) {
1412 camel_folder_thaw(dest);
1413 if (delete_originals)
1414 camel_folder_thaw(source);
1417 camel_operation_end(NULL);
1418 camel_exception_clear(&local);
1423 * camel_folder_transfer_messages_to:
1424 * @source: the source #CamelFolder object
1425 * @uids: message UIDs in @source
1426 * @dest: the destination #CamelFolder object
1427 * @transferred_uids: if non-%NULL, the UIDs of the resulting messages
1428 * in @dest will be stored here, if known.
1429 * @delete_originals: whether or not to delete the original messages
1430 * @ex: a #CamelException
1432 * This copies or moves messages from one folder to another. If the
1433 * @source and @dest folders have the same parent_store, this may be
1434 * more efficient than using #camel_folder_append_message.
1437 camel_folder_transfer_messages_to (CamelFolder *source, GPtrArray *uids,
1438 CamelFolder *dest, GPtrArray **transferred_uids,
1439 gboolean delete_originals, CamelException *ex)
1441 g_return_if_fail (CAMEL_IS_FOLDER (source));
1442 g_return_if_fail (CAMEL_IS_FOLDER (dest));
1443 g_return_if_fail (uids != NULL);
1445 if (source == dest || uids->len == 0) {
1446 /* source and destination folders are the same, or no work to do, do nothing. */
1450 if (source->parent_store == dest->parent_store) {
1451 /* If either folder is a vtrash, we need to use the
1452 * vtrash transfer method.
1454 if (CAMEL_IS_VTRASH_FOLDER (dest))
1455 CF_CLASS (dest)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1457 CF_CLASS (source)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1459 transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1463 delete (CamelFolder *folder)
1465 if (folder->summary)
1466 camel_folder_summary_clear (folder->summary);
1471 * camel_folder_delete:
1472 * @folder: a #CamelFolder object
1474 * Marks a folder object as deleted and performs any required cleanup.
1477 camel_folder_delete (CamelFolder *folder)
1479 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1481 CAMEL_FOLDER_REC_LOCK (folder, lock);
1482 if (folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
1483 CAMEL_FOLDER_REC_UNLOCK (folder, lock);
1487 folder->folder_flags |= CAMEL_FOLDER_HAS_BEEN_DELETED;
1489 CF_CLASS (folder)->delete (folder);
1491 CAMEL_FOLDER_REC_UNLOCK (folder, lock);
1493 camel_object_trigger_event (folder, "deleted", NULL);
1497 folder_rename (CamelFolder *folder, const char *new)
1501 d(printf("CamelFolder:rename('%s')\n", new));
1503 g_free(folder->full_name);
1504 folder->full_name = g_strdup(new);
1505 g_free(folder->name);
1506 tmp = strrchr(new, '/');
1507 folder->name = g_strdup(tmp?tmp+1:new);
1512 * camel_folder_rename:
1513 * @folder: a #CamelFolder object
1514 * @new: new name for the folder
1516 * Mark an active folder object as renamed.
1518 * NOTE: This is an internal function used by camel stores, no locking
1519 * is performed on the folder.
1522 camel_folder_rename(CamelFolder *folder, const char *new)
1526 old = g_strdup(folder->full_name);
1528 CF_CLASS (folder)->rename(folder, new);
1530 camel_object_trigger_event (folder, "renamed", old);
1535 freeze (CamelFolder *folder)
1537 CAMEL_FOLDER_LOCK(folder, change_lock);
1539 g_assert(folder->priv->frozen >= 0);
1541 folder->priv->frozen++;
1543 d(printf ("freeze(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1544 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1549 * camel_folder_freeze:
1550 * @folder: a #CamelFolder
1552 * Freezes the folder so that a series of operation can be performed
1553 * without "folder_changed" signals being emitted. When the folder is
1554 * later thawed with #camel_folder_thaw, the suppressed signals will
1558 camel_folder_freeze (CamelFolder * folder)
1560 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1562 CF_CLASS (folder)->freeze (folder);
1566 thaw (CamelFolder * folder)
1568 CamelFolderChangeInfo *info = NULL;
1570 CAMEL_FOLDER_LOCK(folder, change_lock);
1572 g_assert(folder->priv->frozen > 0);
1574 folder->priv->frozen--;
1576 d(printf ("thaw(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1578 if (folder->priv->frozen == 0
1579 && camel_folder_change_info_changed(folder->priv->changed_frozen)) {
1580 info = folder->priv->changed_frozen;
1581 folder->priv->changed_frozen = camel_folder_change_info_new();
1584 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1587 camel_object_trigger_event (folder, "folder_changed", info);
1588 camel_folder_change_info_free(info);
1593 * camel_folder_thaw:
1594 * @folder: a #CamelFolder object
1596 * Thaws the folder and emits any pending folder_changed
1600 camel_folder_thaw (CamelFolder *folder)
1602 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1603 g_return_if_fail (folder->priv->frozen != 0);
1605 CF_CLASS (folder)->thaw (folder);
1609 is_frozen (CamelFolder *folder)
1611 return folder->priv->frozen != 0;
1616 * camel_folder_is_frozen:
1617 * @folder: a #CamelFolder object
1619 * Returns whether or not the folder is frozen
1622 camel_folder_is_frozen (CamelFolder *folder)
1624 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1626 return CF_CLASS (folder)->is_frozen (folder);
1629 struct _folder_filter_msg {
1630 CamelSessionThreadMsg msg;
1635 CamelFolder *folder;
1636 CamelFilterDriver *driver;
1641 filter_filter(CamelSession *session, CamelSessionThreadMsg *tmsg)
1643 struct _folder_filter_msg *m = (struct _folder_filter_msg *) tmsg;
1644 CamelMessageInfo *info;
1649 CamelJunkPlugin *csp = ((CamelService *)m->folder->parent_store)->session->junk_plugin;
1652 camel_operation_start (NULL, _("Learning junk"));
1654 for (i = 0; i < m->junk->len; i ++) {
1655 CamelMimeMessage *msg = camel_folder_get_message(m->folder, m->junk->pdata[i], NULL);
1656 int pc = 100 * i / m->junk->len;
1658 camel_operation_progress(NULL, pc);
1661 camel_junk_plugin_report_junk (csp, msg);
1662 camel_object_unref (msg);
1665 camel_operation_end (NULL);
1669 camel_operation_start (NULL, _("Learning non-junk"));
1670 for (i = 0; i < m->notjunk->len; i ++) {
1671 CamelMimeMessage *msg = camel_folder_get_message(m->folder, m->notjunk->pdata[i], NULL);
1672 int pc = 100 * i / m->notjunk->len;
1674 camel_operation_progress(NULL, pc);
1677 camel_junk_plugin_report_notjunk (csp, msg);
1678 camel_object_unref (msg);
1681 camel_operation_end (NULL);
1684 if (m->junk || m->notjunk)
1685 camel_junk_plugin_commit_reports (csp);
1687 if (m->driver && m->recents) {
1688 camel_operation_start(NULL, _("Filtering new message(s)"));
1690 source_url = camel_service_get_url((CamelService *)m->folder->parent_store);
1691 uri = camel_url_new(source_url, NULL);
1693 if (m->folder->full_name && m->folder->full_name[0] != '/') {
1694 char *tmp = alloca(strlen(m->folder->full_name)+2);
1696 sprintf(tmp, "/%s", m->folder->full_name);
1697 camel_url_set_path(uri, tmp);
1699 camel_url_set_path(uri, m->folder->full_name);
1700 source_url = camel_url_to_string(uri, CAMEL_URL_HIDE_ALL);
1701 camel_url_free(uri);
1703 for (i=0;status == 0 && i<m->recents->len;i++) {
1704 char *uid = m->recents->pdata[i];
1705 int pc = 100 * i / m->recents->len;
1707 camel_operation_progress(NULL, pc);
1709 info = camel_folder_get_message_info(m->folder, uid);
1711 g_warning("uid %s vanished from folder: %s", uid, source_url);
1715 status = camel_filter_driver_filter_message(m->driver, NULL, info, uid, m->folder, source_url, source_url, &m->ex);
1717 camel_folder_free_message_info(m->folder, info);
1720 camel_exception_init(&ex);
1721 camel_filter_driver_flush(m->driver, &ex);
1722 if (!camel_exception_is_set(&m->ex))
1723 camel_exception_xfer(&m->ex, &ex);
1727 camel_operation_end(NULL);
1732 filter_free(CamelSession *session, CamelSessionThreadMsg *msg)
1734 struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1737 camel_object_unref(m->driver);
1739 camel_folder_free_deep(m->folder, m->recents);
1741 camel_folder_free_deep(m->folder, m->junk);
1743 camel_folder_free_deep(m->folder, m->notjunk);
1745 camel_folder_thaw(m->folder);
1746 camel_object_unref(m->folder);
1749 static CamelSessionThreadOps filter_ops = {
1754 struct _CamelFolderChangeInfoPrivate {
1755 GHashTable *uid_stored; /* what we have stored, which array they're in */
1756 GHashTable *uid_source; /* used to create unique lists */
1757 GPtrArray *uid_filter; /* uids to be filtered */
1758 struct _EMemPool *uid_pool; /* pool used to store copies of uid strings */
1762 /* Event hooks that block emission when frozen */
1764 folder_changed (CamelObject *obj, gpointer event_data)
1766 CamelFolder *folder = (CamelFolder *)obj;
1767 CamelFolderChangeInfo *changed = event_data;
1768 struct _CamelFolderChangeInfoPrivate *p = changed->priv;
1769 CamelSession *session = ((CamelService *)folder->parent_store)->session;
1770 CamelFilterDriver *driver = NULL;
1771 GPtrArray *junk = NULL;
1772 GPtrArray *notjunk = NULL;
1773 GPtrArray *recents = NULL;
1776 d(printf ("folder_changed(%p:'%s', %p), frozen=%d\n", obj, folder->full_name, event_data, folder->priv->frozen));
1777 d(printf(" added %d removed %d changed %d recent %d filter %d\n",
1778 changed->uid_added->len, changed->uid_removed->len,
1779 changed->uid_changed->len, changed->uid_recent->len,
1780 p->uid_filter->len));
1782 if (changed == NULL) {
1783 w(g_warning ("Class %s is passing NULL to folder_changed event",
1784 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1788 CAMEL_FOLDER_LOCK(folder, change_lock);
1789 if (folder->priv->frozen) {
1790 camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1791 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1795 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1797 if (session->junk_plugin && changed->uid_changed->len) {
1800 for (i = 0; i < changed->uid_changed->len; i++) {
1801 flags = camel_folder_get_message_flags (folder, changed->uid_changed->pdata [i]);
1802 if (flags & CAMEL_MESSAGE_JUNK_LEARN) {
1803 if (flags & CAMEL_MESSAGE_JUNK) {
1805 junk = g_ptr_array_new();
1806 g_ptr_array_add (junk, g_strdup (changed->uid_changed->pdata [i]));
1809 notjunk = g_ptr_array_new();
1810 g_ptr_array_add (notjunk, g_strdup (changed->uid_changed->pdata [i]));
1812 /* reset junk learn flag so that we don't process it again*/
1813 camel_folder_set_message_flags (folder, changed->uid_changed->pdata [i], CAMEL_MESSAGE_JUNK_LEARN, 0);
1818 if ((folder->folder_flags & (CAMEL_FOLDER_FILTER_RECENT|CAMEL_FOLDER_FILTER_JUNK))
1819 && p->uid_filter->len > 0)
1820 driver = camel_session_get_filter_driver(session,
1821 (folder->folder_flags & CAMEL_FOLDER_FILTER_RECENT)
1822 ? "incoming":"junktest", NULL);
1825 recents = g_ptr_array_new();
1826 for (i = 0; i < p->uid_filter->len; i++)
1827 g_ptr_array_add (recents, g_strdup (p->uid_filter->pdata[i]));
1829 g_ptr_array_set_size (p->uid_filter, 0);
1832 if (driver || junk || notjunk) {
1833 struct _folder_filter_msg *msg;
1835 d(printf("* launching filter thread %d new mail, %d junk and %d not junk\n",
1836 recents?recents->len:0, junk?junk->len:0, notjunk?notjunk->len:0));
1838 msg = camel_session_thread_msg_new(session, &filter_ops, sizeof(*msg));
1839 msg->recents = recents;
1841 msg->notjunk = notjunk;
1842 msg->folder = folder;
1843 camel_object_ref(folder);
1844 camel_folder_freeze(folder);
1845 /* Copy changes back to changed_frozen list to retain
1846 * them while we are filtering */
1847 CAMEL_FOLDER_LOCK(folder, change_lock);
1848 camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1849 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1850 msg->driver = driver;
1851 camel_exception_init(&msg->ex);
1852 camel_session_thread_queue(session, &msg->msg, 0);
1861 * camel_folder_free_nop:
1862 * @folder: a #CamelFolder object
1863 * @array: an array of uids or #CamelMessageInfo
1865 * "Frees" the provided array by doing nothing. Used by #CamelFolder
1866 * subclasses as an implementation for free_uids, or free_summary when
1867 * the returned array is "static" information and should not be freed.
1870 camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
1877 * camel_folder_free_shallow:
1878 * @folder: a #CamelFolder object
1879 * @array: an array of uids or #CamelMessageInfo
1881 * Frees the provided array but not its contents. Used by #CamelFolder
1882 * subclasses as an implementation for free_uids or free_summary when
1883 * the returned array needs to be freed but its contents come from
1884 * "static" information.
1887 camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
1889 g_ptr_array_free (array, TRUE);
1894 * camel_folder_free_deep:
1895 * @folder: a #CamelFolder object
1896 * @array: an array of uids
1898 * Frees the provided array and its contents. Used by #CamelFolder
1899 * subclasses as an implementation for free_uids when the provided
1900 * information was created explicitly by the corresponding get_ call.
1903 camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
1907 for (i = 0; i < array->len; i++)
1908 g_free (array->pdata[i]);
1909 g_ptr_array_free (array, TRUE);
1914 * camel_folder_change_info_new:
1916 * Create a new folder change info structure.
1918 * Change info structures are not MT-SAFE and must be
1919 * locked for exclusive access externally.
1921 * Returns a new #CamelFolderChangeInfo
1923 CamelFolderChangeInfo *
1924 camel_folder_change_info_new(void)
1926 CamelFolderChangeInfo *info;
1928 info = g_malloc(sizeof(*info));
1929 info->uid_added = g_ptr_array_new();
1930 info->uid_removed = g_ptr_array_new();
1931 info->uid_changed = g_ptr_array_new();
1932 info->uid_recent = g_ptr_array_new();
1933 info->priv = g_malloc0(sizeof(*info->priv));
1934 info->priv->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
1935 info->priv->uid_source = NULL;
1936 info->priv->uid_filter = g_ptr_array_new();
1937 info->priv->uid_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
1944 * camel_folder_change_info_add_source:
1945 * @info: a #CamelFolderChangeInfo
1948 * Add a source uid for generating a changeset.
1951 camel_folder_change_info_add_source(CamelFolderChangeInfo *info, const char *uid)
1953 struct _CamelFolderChangeInfoPrivate *p;
1955 g_assert(info != NULL);
1959 if (p->uid_source == NULL)
1960 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1962 if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1963 g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1));
1968 * camel_folder_change_info_add_source_list:
1969 * @info: a #CamelFolderChangeInfo
1970 * @list: a list of uids
1972 * Add a list of source uid's for generating a changeset.
1975 camel_folder_change_info_add_source_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1977 struct _CamelFolderChangeInfoPrivate *p;
1980 g_assert(info != NULL);
1981 g_assert(list != NULL);
1985 if (p->uid_source == NULL)
1986 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1988 for (i=0;i<list->len;i++) {
1989 char *uid = list->pdata[i];
1991 if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1992 g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1));
1998 * camel_folder_change_info_add_update:
1999 * @info: a #CamelFolderChangeInfo
2002 * Add a uid from the updated list, used to generate a changeset diff.
2005 camel_folder_change_info_add_update(CamelFolderChangeInfo *info, const char *uid)
2007 struct _CamelFolderChangeInfoPrivate *p;
2011 g_assert(info != NULL);
2015 if (p->uid_source == NULL) {
2016 camel_folder_change_info_add_uid(info, uid);
2020 if (g_hash_table_lookup_extended(p->uid_source, uid, (gpointer) &key, (gpointer) &value)) {
2021 g_hash_table_remove(p->uid_source, key);
2023 camel_folder_change_info_add_uid(info, uid);
2029 * camel_folder_change_info_add_update_list:
2030 * @info: a #CamelFolderChangeInfo
2031 * @list: a list of uids
2033 * Add a list of uid's from the updated list.
2036 camel_folder_change_info_add_update_list(CamelFolderChangeInfo *info, const GPtrArray *list)
2040 g_assert(info != NULL);
2041 g_assert(list != NULL);
2043 for (i=0;i<list->len;i++)
2044 camel_folder_change_info_add_update(info, list->pdata[i]);
2048 change_info_remove(char *key, void *value, CamelFolderChangeInfo *info)
2050 struct _CamelFolderChangeInfoPrivate *p = info->priv;
2054 if (g_hash_table_lookup_extended(p->uid_stored, key, (gpointer) &olduid, (gpointer) &olduids)) {
2055 /* if it was added/changed them removed, then remove it */
2056 if (olduids != info->uid_removed) {
2057 g_ptr_array_remove_fast(olduids, olduid);
2058 g_ptr_array_add(info->uid_removed, olduid);
2059 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2064 /* we dont need to copy this, as they've already been copied into our pool */
2065 g_ptr_array_add(info->uid_removed, key);
2066 g_hash_table_insert(p->uid_stored, key, info->uid_removed);
2071 * camel_folder_change_info_build_diff:
2072 * @info: a #CamelFolderChangeInfo
2074 * Compare the source uid set to the updated uid set and generate the
2075 * differences into the added and removed lists.
2078 camel_folder_change_info_build_diff(CamelFolderChangeInfo *info)
2080 struct _CamelFolderChangeInfoPrivate *p;
2082 g_assert(info != NULL);
2086 if (p->uid_source) {
2087 g_hash_table_foreach(p->uid_source, (GHFunc)change_info_remove, info);
2088 g_hash_table_destroy(p->uid_source);
2089 p->uid_source = NULL;
2094 change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2096 struct _CamelFolderChangeInfoPrivate *p;
2102 /* always add to recent, but dont let anyone else know */
2103 if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2104 olduid = e_mempool_strdup(p->uid_pool, uid);
2106 g_ptr_array_add(info->uid_recent, olduid);
2110 change_info_filter_uid(CamelFolderChangeInfo *info, const char *uid)
2112 struct _CamelFolderChangeInfoPrivate *p;
2118 /* always add to filter, but dont let anyone else know */
2119 if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2120 olduid = e_mempool_strdup(p->uid_pool, uid);
2122 g_ptr_array_add(p->uid_filter, olduid);
2126 change_info_cat(CamelFolderChangeInfo *info, GPtrArray *source, void (*add)(CamelFolderChangeInfo *info, const char *uid))
2130 for (i=0;i<source->len;i++)
2131 add(info, source->pdata[i]);
2136 * camel_folder_change_info_cat:
2137 * @info: a #CamelFolderChangeInfo to append to
2138 * @src: a #CamelFolderChangeInfo to append from
2140 * Concatenate one change info onto antoher. Can be used to copy them
2144 camel_folder_change_info_cat(CamelFolderChangeInfo *info, CamelFolderChangeInfo *source)
2146 g_assert(info != NULL);
2147 g_assert(source != NULL);
2149 change_info_cat(info, source->uid_added, camel_folder_change_info_add_uid);
2150 change_info_cat(info, source->uid_removed, camel_folder_change_info_remove_uid);
2151 change_info_cat(info, source->uid_changed, camel_folder_change_info_change_uid);
2152 change_info_cat(info, source->uid_recent, change_info_recent_uid);
2153 change_info_cat(info, source->priv->uid_filter, change_info_filter_uid);
2157 * camel_folder_change_info_add_uid:
2158 * @info: a #CamelFolderChangeInfo
2161 * Add a new uid to the changeinfo.
2164 camel_folder_change_info_add_uid(CamelFolderChangeInfo *info, const char *uid)
2166 struct _CamelFolderChangeInfoPrivate *p;
2170 g_assert(info != NULL);
2174 if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2175 /* if it was removed then added, promote it to a changed */
2176 /* if it was changed then added, leave as changed */
2177 if (olduids == info->uid_removed) {
2178 g_ptr_array_remove_fast(olduids, olduid);
2179 g_ptr_array_add(info->uid_changed, olduid);
2180 g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2185 olduid = e_mempool_strdup(p->uid_pool, uid);
2186 g_ptr_array_add(info->uid_added, olduid);
2187 g_hash_table_insert(p->uid_stored, olduid, info->uid_added);
2192 * camel_folder_change_info_remove_uid:
2193 * @info: a #CamelFolderChangeInfo
2196 * Add a uid to the removed uid list.
2199 camel_folder_change_info_remove_uid(CamelFolderChangeInfo *info, const char *uid)
2201 struct _CamelFolderChangeInfoPrivate *p;
2205 g_assert(info != NULL);
2209 if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2210 /* if it was added/changed them removed, then remove it */
2211 if (olduids != info->uid_removed) {
2212 g_ptr_array_remove_fast(olduids, olduid);
2213 g_ptr_array_add(info->uid_removed, olduid);
2214 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2219 olduid = e_mempool_strdup(p->uid_pool, uid);
2220 g_ptr_array_add(info->uid_removed, olduid);
2221 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2226 * camel_folder_change_info_change_uid:
2227 * @info: a #CamelFolderChangeInfo
2230 * Add a uid to the changed uid list.
2233 camel_folder_change_info_change_uid(CamelFolderChangeInfo *info, const char *uid)
2235 struct _CamelFolderChangeInfoPrivate *p;
2239 g_assert(info != NULL);
2243 if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2244 /* if we have it already, leave it as that */
2248 olduid = e_mempool_strdup(p->uid_pool, uid);
2249 g_ptr_array_add(info->uid_changed, olduid);
2250 g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2255 * camel_folder_change_info_recent_uid:
2256 * @info: a #CamelFolderChangeInfo
2259 * Add a recent uid to the changedinfo.
2260 * This will also add the uid to the uid_filter array for potential
2264 camel_folder_change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2266 g_assert(info != NULL);
2268 change_info_recent_uid(info, uid);
2269 change_info_filter_uid(info, uid);
2273 * camel_folder_change_info_changed:
2274 * @info: a #CamelFolderChangeInfo
2276 * Gets whether or not there have been any changes.
2278 * Returns %TRUE if the changeset contains any changes or %FALSE
2282 camel_folder_change_info_changed(CamelFolderChangeInfo *info)
2284 g_assert(info != NULL);
2286 return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len || info->uid_recent->len);
2291 * camel_folder_change_info_clear:
2292 * @info: a #CamelFolderChangeInfo
2294 * Empty out the change info; called after changes have been
2298 camel_folder_change_info_clear(CamelFolderChangeInfo *info)
2300 struct _CamelFolderChangeInfoPrivate *p;
2302 g_assert(info != NULL);
2306 g_ptr_array_set_size(info->uid_added, 0);
2307 g_ptr_array_set_size(info->uid_removed, 0);
2308 g_ptr_array_set_size(info->uid_changed, 0);
2309 g_ptr_array_set_size(info->uid_recent, 0);
2310 if (p->uid_source) {
2311 g_hash_table_destroy(p->uid_source);
2312 p->uid_source = NULL;
2314 g_hash_table_destroy(p->uid_stored);
2315 p->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
2316 g_ptr_array_set_size(p->uid_filter, 0);
2317 e_mempool_flush(p->uid_pool, TRUE);
2322 * camel_folder_change_info_free:
2323 * @info: a #CamelFolderChangeInfo
2325 * Free memory associated with the folder change info lists.
2328 camel_folder_change_info_free(CamelFolderChangeInfo *info)
2330 struct _CamelFolderChangeInfoPrivate *p;
2332 g_assert(info != NULL);
2337 g_hash_table_destroy(p->uid_source);
2339 g_hash_table_destroy(p->uid_stored);
2340 g_ptr_array_free(p->uid_filter, TRUE);
2341 e_mempool_destroy(p->uid_pool);
2344 g_ptr_array_free(info->uid_added, TRUE);
2345 g_ptr_array_free(info->uid_removed, TRUE);
2346 g_ptr_array_free(info->uid_changed, TRUE);
2347 g_ptr_array_free(info->uid_recent, TRUE);