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, 2000 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 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #include "camel-folder.h"
32 #include "camel-exception.h"
33 #include "camel-store.h"
34 #include "camel-mime-message.h"
36 #include "e-util/e-memory.h"
37 #include "camel-operation.h"
39 #include "camel-session.h"
40 #include "camel-filter-driver.h"
41 #include "camel-private.h"
42 #include "camel-vtrash-folder.h"
47 static CamelObjectClass *parent_class = NULL;
49 /* Returns the class for a CamelFolder */
50 #define CF_CLASS(so) ((CamelFolderClass *)((CamelObject *)(so))->klass)
52 static void camel_folder_finalize (CamelObject *object);
54 static void refresh_info (CamelFolder *folder, CamelException *ex);
56 static void folder_sync (CamelFolder *folder, gboolean expunge,
59 static const gchar *get_name (CamelFolder *folder);
60 static const gchar *get_full_name (CamelFolder *folder);
61 static CamelStore *get_parent_store (CamelFolder *folder);
63 static guint32 get_permanent_flags (CamelFolder *folder);
64 static guint32 get_message_flags (CamelFolder *folder, const char *uid);
65 static void set_message_flags (CamelFolder *folder, const char *uid,
66 guint32 flags, guint32 set);
67 static gboolean get_message_user_flag (CamelFolder *folder, const char *uid, const char *name);
68 static void set_message_user_flag (CamelFolder *folder, const char *uid,
69 const char *name, gboolean value);
70 static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name);
71 static void set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value);
73 static gint get_message_count (CamelFolder *folder);
74 static gint get_unread_message_count (CamelFolder *folder);
76 static void expunge (CamelFolder *folder,
78 static int folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args);
79 static void folder_free(CamelObject *o, guint32 tag, void *val);
82 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
83 const CamelMessageInfo *info, char **appended_uid,
87 static GPtrArray *get_uids (CamelFolder *folder);
88 static void free_uids (CamelFolder *folder,
90 static GPtrArray *get_summary (CamelFolder *folder);
91 static void free_summary (CamelFolder *folder,
94 static CamelMimeMessage *get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
96 static CamelMessageInfo *get_message_info (CamelFolder *folder, const char *uid);
97 static void free_message_info (CamelFolder *folder, CamelMessageInfo *info);
98 static void ref_message_info (CamelFolder *folder, CamelMessageInfo *info);
100 static GPtrArray *search_by_expression (CamelFolder *folder, const char *exp, CamelException *ex);
101 static GPtrArray *search_by_uids (CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex);
102 static void search_free (CamelFolder * folder, GPtrArray *result);
104 static void transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest,
105 GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
107 static void delete (CamelFolder *folder);
108 static void folder_rename (CamelFolder *folder, const char *new);
110 static void freeze (CamelFolder *folder);
111 static void thaw (CamelFolder *folder);
112 static gboolean is_frozen (CamelFolder *folder);
114 static gboolean folder_changed (CamelObject *object,
115 gpointer event_data);
116 static gboolean message_changed (CamelObject *object,
117 /*const char *uid*/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->get_unread_message_count = get_unread_message_count;
135 camel_folder_class->append_message = append_message;
136 camel_folder_class->get_permanent_flags = get_permanent_flags;
137 camel_folder_class->get_message_flags = get_message_flags;
138 camel_folder_class->set_message_flags = set_message_flags;
139 camel_folder_class->get_message_user_flag = get_message_user_flag;
140 camel_folder_class->set_message_user_flag = set_message_user_flag;
141 camel_folder_class->get_message_user_tag = get_message_user_tag;
142 camel_folder_class->set_message_user_tag = set_message_user_tag;
143 camel_folder_class->get_message = get_message;
144 camel_folder_class->get_uids = get_uids;
145 camel_folder_class->free_uids = free_uids;
146 camel_folder_class->get_summary = get_summary;
147 camel_folder_class->free_summary = free_summary;
148 camel_folder_class->search_by_expression = search_by_expression;
149 camel_folder_class->search_by_uids = search_by_uids;
150 camel_folder_class->search_free = search_free;
151 camel_folder_class->get_message_info = get_message_info;
152 camel_folder_class->ref_message_info = ref_message_info;
153 camel_folder_class->free_message_info = free_message_info;
154 camel_folder_class->transfer_messages_to = transfer_messages_to;
155 camel_folder_class->delete = delete;
156 camel_folder_class->rename = folder_rename;
157 camel_folder_class->freeze = freeze;
158 camel_folder_class->thaw = thaw;
159 camel_folder_class->is_frozen = is_frozen;
161 /* virtual method overload */
162 camel_object_class->getv = folder_getv;
163 camel_object_class->free = folder_free;
166 camel_object_class_add_event(camel_object_class, "folder_changed", folder_changed);
167 camel_object_class_add_event(camel_object_class, "message_changed", message_changed);
168 camel_object_class_add_event(camel_object_class, "deleted", NULL);
169 camel_object_class_add_event(camel_object_class, "renamed", NULL);
173 camel_folder_init (gpointer object, gpointer klass)
175 CamelFolder *folder = object;
177 folder->priv = g_malloc0(sizeof(*folder->priv));
178 folder->priv->frozen = 0;
179 folder->priv->changed_frozen = camel_folder_change_info_new();
180 #ifdef ENABLE_THREADS
181 folder->priv->lock = e_mutex_new(E_MUTEX_REC);
182 folder->priv->change_lock = e_mutex_new(E_MUTEX_SIMPLE);
187 camel_folder_finalize (CamelObject *object)
189 CamelFolder *camel_folder = CAMEL_FOLDER (object);
190 struct _CamelFolderPrivate *p = camel_folder->priv;
192 g_free(camel_folder->name);
193 g_free(camel_folder->full_name);
194 g_free(camel_folder->description);
196 if (camel_folder->parent_store)
197 camel_object_unref (CAMEL_OBJECT (camel_folder->parent_store));
199 if (camel_folder->summary)
200 camel_object_unref((CamelObject *)camel_folder->summary);
202 camel_folder_change_info_free(p->changed_frozen);
203 #ifdef ENABLE_THREADS
204 e_mutex_destroy(p->lock);
205 e_mutex_destroy(p->change_lock);
211 camel_folder_get_type (void)
213 static CamelType camel_folder_type = CAMEL_INVALID_TYPE;
215 if (camel_folder_type == CAMEL_INVALID_TYPE) {
216 camel_folder_type = camel_type_register (CAMEL_OBJECT_TYPE, "CamelFolder",
217 sizeof (CamelFolder),
218 sizeof (CamelFolderClass),
219 (CamelObjectClassInitFunc) camel_folder_class_init,
221 (CamelObjectInitFunc) camel_folder_init,
222 (CamelObjectFinalizeFunc) camel_folder_finalize );
225 return camel_folder_type;
230 * camel_folder_construct:
231 * @folder: folder object to construct
232 * @parent_store: parent store object of the folder
233 * @full_name: full name of the folder
234 * @name: short name of the folder
236 * Initalizes the folder by setting the parent store and name.
239 camel_folder_construct (CamelFolder *folder, CamelStore *parent_store,
240 const char *full_name, const char *name)
242 g_return_if_fail (CAMEL_IS_FOLDER (folder));
243 g_return_if_fail (CAMEL_IS_STORE (parent_store));
244 g_return_if_fail (folder->parent_store == NULL);
245 g_return_if_fail (folder->name == NULL);
247 folder->parent_store = parent_store;
249 camel_object_ref (CAMEL_OBJECT (parent_store));
251 folder->name = g_strdup (name);
252 folder->full_name = g_strdup (full_name);
257 folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
259 w(g_warning ("CamelFolder::sync not implemented for `%s'",
260 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
265 * @folder: The folder object
266 * @expunge: whether or not to expunge deleted messages
267 * @ex: exception object
269 * Sync changes made to a folder to its backing store, possibly expunging
270 * deleted messages as well.
273 camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
275 g_return_if_fail (CAMEL_IS_FOLDER (folder));
277 CAMEL_FOLDER_LOCK(folder, lock);
279 if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
280 CF_CLASS (folder)->sync (folder, expunge, ex);
282 CAMEL_FOLDER_UNLOCK(folder, lock);
287 refresh_info (CamelFolder *folder, CamelException *ex)
293 * camel_folder_refresh_info:
294 * @folder: The folder object
295 * @ex: exception object
297 * Updates a folder's summary to be in sync with its backing store.
300 camel_folder_refresh_info (CamelFolder *folder, CamelException *ex)
302 g_return_if_fail (CAMEL_IS_FOLDER (folder));
304 CAMEL_FOLDER_LOCK(folder, lock);
306 CF_CLASS (folder)->refresh_info (folder, ex);
308 CAMEL_FOLDER_UNLOCK(folder, lock);
312 folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
314 CamelFolder *folder = (CamelFolder *)object;
315 int i, count=args->argc;
318 for (i=0;i<args->argc;i++) {
319 CamelArgGet *arg = &args->argv[i];
323 switch (tag & CAMEL_ARG_TAG) {
324 /* CamelObject args */
325 case CAMEL_OBJECT_ARG_DESCRIPTION:
326 if (folder->description == NULL)
327 folder->description = g_strdup_printf("%s", folder->full_name);
328 *arg->ca_str = folder->description;
331 /* CamelFolder args */
332 case CAMEL_FOLDER_ARG_NAME:
333 *arg->ca_str = folder->name;
335 case CAMEL_FOLDER_ARG_FULL_NAME:
336 *arg->ca_str = folder->full_name;
338 case CAMEL_FOLDER_ARG_STORE:
339 *arg->ca_object = folder->parent_store;
341 case CAMEL_FOLDER_ARG_PERMANENTFLAGS:
342 *arg->ca_int = folder->permanent_flags;
344 case CAMEL_FOLDER_ARG_TOTAL:
345 *arg->ca_int = camel_folder_summary_count(folder->summary);
347 case CAMEL_FOLDER_ARG_UNREAD: {
348 int j, unread = 0, count;
349 CamelMessageInfo *info;
351 count = camel_folder_summary_count(folder->summary);
352 for (j=0; j<count; j++) {
353 if ((info = camel_folder_summary_index(folder->summary, j))) {
354 if (!(info->flags & CAMEL_MESSAGE_SEEN))
356 camel_folder_summary_info_free(folder->summary, info);
360 *arg->ca_int = unread;
362 case CAMEL_FOLDER_ARG_UID_ARRAY: {
364 CamelMessageInfo *info;
367 count = camel_folder_summary_count(folder->summary);
368 array = g_ptr_array_new();
369 g_ptr_array_set_size(array, count);
370 for (j=0; j<count; j++) {
371 if ((info = camel_folder_summary_index(folder->summary, j))) {
372 array->pdata[i] = g_strdup(camel_message_info_uid(info));
373 camel_folder_summary_info_free(folder->summary, info);
376 *arg->ca_ptr = array;
378 case CAMEL_FOLDER_ARG_INFO_ARRAY:
379 *arg->ca_ptr = camel_folder_summary_array(folder->summary);
386 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
390 return parent_class->getv(object, ex, args);
396 folder_free(CamelObject *o, guint32 tag, void *val)
398 CamelFolder *folder = (CamelFolder *)o;
400 switch (tag & CAMEL_ARG_TAG) {
401 case CAMEL_FOLDER_ARG_UID_ARRAY: {
402 GPtrArray *array = val;
405 for (i=0; i<array->len; i++)
406 g_free(array->pdata[i]);
407 g_ptr_array_free(array, TRUE);
409 case CAMEL_FOLDER_ARG_INFO_ARRAY:
410 camel_folder_summary_array_free(folder->summary, val);
413 parent_class->free(o, tag, val);
418 get_name (CamelFolder *folder)
424 * camel_folder_get_name:
427 * Get the (short) name of the folder. The fully qualified name
428 * can be obtained with the get_full_name method.
430 * Return value: name of the folder
433 camel_folder_get_name (CamelFolder * folder)
435 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
437 return CF_CLASS (folder)->get_name (folder);
442 get_full_name (CamelFolder *folder)
444 return folder->full_name;
448 * camel_folder_get_full_name:
451 * Get the (full) name of the folder.
453 * Return value: full name of the folder
456 camel_folder_get_full_name (CamelFolder *folder)
458 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
460 return CF_CLASS (folder)->get_full_name (folder);
465 get_parent_store (CamelFolder * folder)
467 return folder->parent_store;
471 * camel_folder_get_parent_store:
472 * @folder: folder to get the parent of
474 * Return value: the parent store of the folder.
477 camel_folder_get_parent_store (CamelFolder *folder)
479 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
481 return CF_CLASS (folder)->get_parent_store (folder);
486 expunge (CamelFolder *folder, CamelException *ex)
488 w(g_warning ("CamelFolder::expunge not implemented for `%s'",
489 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
494 * camel_folder_expunge:
495 * @folder: the folder
496 * @ex: a CamelException
498 * Delete messages which have been marked as "DELETED"
501 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
503 g_return_if_fail (CAMEL_IS_FOLDER (folder));
505 CAMEL_FOLDER_LOCK(folder, lock);
507 if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
508 CF_CLASS (folder)->expunge (folder, ex);
510 CAMEL_FOLDER_UNLOCK(folder, lock);
514 get_message_count (CamelFolder *folder)
516 g_return_val_if_fail(folder->summary != NULL, -1);
518 return camel_folder_summary_count(folder->summary);
522 * camel_folder_get_message_count:
523 * @folder: A CamelFolder object
525 * Return value: the number of messages in the folder, or -1 if unknown.
528 camel_folder_get_message_count (CamelFolder *folder)
532 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
534 ret = CF_CLASS (folder)->get_message_count (folder);
540 get_unread_message_count(CamelFolder *folder)
542 int i, count, unread=0;
544 g_return_val_if_fail(folder->summary != NULL, -1);
546 count = camel_folder_summary_count(folder->summary);
547 for (i=0; i<count; i++) {
548 CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i);
551 if (!(info->flags & CAMEL_MESSAGE_SEEN))
553 camel_folder_summary_info_free(folder->summary, info);
561 * camel_folder_unread_get_message_count:
562 * @folder: A CamelFolder object
564 * Return value: the number of unread messages in the folder, or -1 if unknown.
567 camel_folder_get_unread_message_count (CamelFolder *folder)
571 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
573 ret = CF_CLASS (folder)->get_unread_message_count (folder);
579 append_message (CamelFolder *folder, CamelMimeMessage *message,
580 const CamelMessageInfo *info, char **appended_uid,
583 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
584 _("Unsupported operation: append message: for %s"),
585 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
587 w(g_warning ("CamelFolder::append_message not implemented for `%s'",
588 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
595 * camel_folder_append_message: add a message to a folder
596 * @folder: folder object to add the message to
597 * @message: message object
598 * @info: message info with additional flags/etc to set on
599 * new message, or %NULL
600 * @appended_uid: if non-%NULL, the UID of the appended message will
601 * be returned here, if it is known.
602 * @ex: exception object
604 * Add a message to a folder. Only the flag and tag data from @info
605 * is used. If @info is %NULL, no flags or tags will be set.
608 camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message,
609 const CamelMessageInfo *info, char **appended_uid,
612 g_return_if_fail (CAMEL_IS_FOLDER (folder));
614 CAMEL_FOLDER_LOCK(folder, lock);
616 CF_CLASS (folder)->append_message (folder, message, info, appended_uid, ex);
618 CAMEL_FOLDER_UNLOCK(folder, lock);
623 get_permanent_flags (CamelFolder *folder)
625 return folder->permanent_flags;
629 * camel_folder_get_permanent_flags:
630 * @folder: a CamelFolder
632 * Return value: the set of CamelMessageFlags that can be permanently
633 * stored on a message between sessions. If it includes %CAMEL_FLAG_USER,
634 * then user-defined flags will be remembered.
637 camel_folder_get_permanent_flags (CamelFolder *folder)
639 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
641 return CF_CLASS (folder)->get_permanent_flags (folder);
645 get_message_flags(CamelFolder *folder, const char *uid)
647 CamelMessageInfo *info;
650 g_return_val_if_fail(folder->summary != NULL, 0);
652 info = camel_folder_summary_uid(folder->summary, uid);
657 camel_folder_summary_info_free(folder->summary, info);
663 * camel_folder_get_message_flags:
664 * @folder: a CamelFolder
665 * @uid: the UID of a message in @folder
667 * Return value: the CamelMessageFlags that are set on the indicated
671 camel_folder_get_message_flags (CamelFolder *folder, const char *uid)
675 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
677 ret = CF_CLASS (folder)->get_message_flags (folder, uid);
683 set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
685 CamelMessageInfo *info;
688 g_return_if_fail(folder->summary != NULL);
690 info = camel_folder_summary_uid(folder->summary, uid);
694 new = (info->flags & ~flags) | (set & flags);
695 if (new == info->flags) {
696 camel_folder_summary_info_free(folder->summary, info);
700 info->flags = new | CAMEL_MESSAGE_FOLDER_FLAGGED;
701 camel_folder_summary_touch(folder->summary);
702 camel_folder_summary_info_free(folder->summary, info);
704 camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
708 * camel_folder_set_message_flags:
709 * @folder: a CamelFolder
710 * @uid: the UID of a message in @folder
711 * @flags: a set of CamelMessageFlag values to set
712 * @set: the mask of values in @flags to use.
714 * Sets those flags specified by @set to the values specified by @flags
715 * on the indicated message. (This may or may not persist after the
716 * folder or store is closed. See camel_folder_get_permanent_flags().)
719 camel_folder_set_message_flags (CamelFolder *folder, const char *uid,
720 guint32 flags, guint32 set)
722 g_return_if_fail (CAMEL_IS_FOLDER (folder));
724 CF_CLASS (folder)->set_message_flags (folder, uid, flags, set);
729 get_message_user_flag(CamelFolder *folder, const char *uid, const char *name)
731 CamelMessageInfo *info;
734 g_return_val_if_fail(folder->summary != NULL, FALSE);
736 info = camel_folder_summary_uid(folder->summary, uid);
740 ret = camel_flag_get(&info->user_flags, name);
741 camel_folder_summary_info_free(folder->summary, info);
747 * camel_folder_get_message_user_flag:
748 * @folder: a CamelFolder
749 * @uid: the UID of a message in @folder
750 * @name: the name of a user flag
752 * Return value: whether or not the given user flag is set on the message.
755 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
760 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
762 ret = CF_CLASS (folder)->get_message_user_flag (folder, uid, name);
768 set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
770 CamelMessageInfo *info;
772 g_return_if_fail(folder->summary != NULL);
774 info = camel_folder_summary_uid(folder->summary, uid);
778 if (camel_flag_set(&info->user_flags, name, value)) {
779 info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
780 camel_folder_summary_touch(folder->summary);
781 camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
783 camel_folder_summary_info_free(folder->summary, info);
787 * camel_folder_set_message_user_flag:
788 * @folder: a CamelFolder
789 * @uid: the UID of a message in @folder
790 * @name: the name of the user flag to set
791 * @value: the value to set it to
793 * Sets the user flag specified by @name to the value specified by @value
794 * on the indicated message. (This may or may not persist after the
795 * folder or store is closed. See camel_folder_get_permanent_flags().)
798 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
799 const char *name, gboolean value)
801 g_return_if_fail (CAMEL_IS_FOLDER (folder));
803 CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value);
807 get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
809 CamelMessageInfo *info;
812 g_return_val_if_fail(folder->summary != NULL, NULL);
814 info = camel_folder_summary_uid(folder->summary, uid);
818 /* FIXME: Need to duplicate tag string */
820 ret = camel_tag_get(&info->user_tags, name);
821 camel_folder_summary_info_free(folder->summary, info);
827 * camel_folder_get_message_user_tag:
828 * @folder: a CamelFolder
829 * @uid: the UID of a message in @folder
830 * @name: the name of a user tag
832 * Return value: Returns the value of the user tag.
835 camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid, const char *name)
839 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
841 /* FIXME: should duplicate string */
842 ret = CF_CLASS (folder)->get_message_user_tag (folder, uid, name);
848 set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
850 CamelMessageInfo *info;
852 g_return_if_fail(folder->summary != NULL);
854 info = camel_folder_summary_uid(folder->summary, uid);
858 if (camel_tag_set(&info->user_tags, name, value)) {
859 info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
860 camel_folder_summary_touch(folder->summary);
861 camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
863 camel_folder_summary_info_free(folder->summary, info);
867 * camel_folder_set_message_user_tag:
868 * @folder: a CamelFolder
869 * @uid: the UID of a message in @folder
870 * @name: the name of the user tag to set
871 * @value: the value to set it to
873 * Sets the user tag specified by @name to the value specified by @value
874 * on the indicated message. (This may or may not persist after the
875 * folder or store is closed. See camel_folder_get_permanent_flags().)
878 camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const char *name, const char *value)
880 g_return_if_fail (CAMEL_IS_FOLDER (folder));
882 CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value);
885 static CamelMessageInfo *
886 get_message_info (CamelFolder *folder, const char *uid)
888 g_return_val_if_fail(folder->summary != NULL, NULL);
890 return camel_folder_summary_uid(folder->summary, uid);
894 * camel_folder_get_message_info:
895 * @folder: a CamelFolder
896 * @uid: the uid of a message
898 * Retrieve the CamelMessageInfo for the specified @uid. This return
899 * must be freed using free_message_info().
901 * Return value: the summary information for the indicated message, or NULL
902 * if the uid does not exist.
905 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
907 CamelMessageInfo *ret;
909 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
910 g_return_val_if_fail (uid != NULL, NULL);
912 ret = CF_CLASS (folder)->get_message_info (folder, uid);
918 free_message_info (CamelFolder *folder, CamelMessageInfo *info)
920 g_return_if_fail(folder->summary != NULL);
922 camel_folder_summary_info_free(folder->summary, info);
926 * camel_folder_free_message_info:
930 * Free (unref) a CamelMessageInfo, previously obtained with get_message_info().
933 camel_folder_free_message_info(CamelFolder *folder, CamelMessageInfo *info)
935 g_return_if_fail(CAMEL_IS_FOLDER (folder));
936 g_return_if_fail(info != NULL);
938 CF_CLASS (folder)->free_message_info(folder, info);
942 ref_message_info (CamelFolder *folder, CamelMessageInfo *info)
944 g_return_if_fail(folder->summary != NULL);
946 camel_folder_summary_info_ref(folder->summary, info);
950 * camel_folder_ref_message_info:
954 * Ref a CamelMessageInfo, previously obtained with get_message_info().
957 camel_folder_ref_message_info(CamelFolder *folder, CamelMessageInfo *info)
959 g_return_if_fail(CAMEL_IS_FOLDER (folder));
960 g_return_if_fail(info != NULL);
962 CF_CLASS (folder)->ref_message_info(folder, info);
965 /* TODO: is this function required anyway? */
967 camel_folder_has_summary_capability (CamelFolder *folder)
969 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
971 return folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
977 static CamelMimeMessage *
978 get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
980 w(g_warning ("CamelFolder::get_message not implemented for `%s'",
981 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
987 * camel_folder_get_message:
988 * @folder: the folder object
990 * @ex: a CamelException
992 * Get a message from its UID in the folder. Messages are cached
993 * within a folder, that is, asking twice for the same UID returns the
994 * same message object. (FIXME: is this true?)
996 * Return value: Message corresponding to the UID
999 camel_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
1001 CamelMimeMessage *ret;
1003 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1005 CAMEL_FOLDER_LOCK(folder, lock);
1007 ret = CF_CLASS (folder)->get_message (folder, uid, ex);
1009 CAMEL_FOLDER_UNLOCK(folder, lock);
1015 get_uids(CamelFolder *folder)
1020 array = g_ptr_array_new();
1022 g_return_val_if_fail(folder->summary != NULL, array);
1024 count = camel_folder_summary_count(folder->summary);
1025 g_ptr_array_set_size(array, count);
1026 for (i = 0, j = 0; i < count; i++) {
1027 CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i);
1030 array->pdata[j++] = g_strdup (camel_message_info_uid (info));
1031 camel_folder_summary_info_free(folder->summary, info);
1035 g_ptr_array_set_size (array, j);
1041 * camel_folder_get_uids:
1042 * @folder: folder object
1044 * Get the list of UIDs available in a folder. This routine is useful
1045 * for finding what messages are available when the folder does not
1046 * support summaries. The returned array shoudl not be modified, and
1047 * must be freed by passing it to camel_folder_free_uids().
1049 * Return value: GPtrArray of UIDs corresponding to the messages
1050 * available in the folder.
1053 camel_folder_get_uids (CamelFolder *folder)
1057 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1059 ret = CF_CLASS (folder)->get_uids (folder);
1065 free_uids (CamelFolder *folder, GPtrArray *array)
1069 for (i=0; i<array->len; i++)
1070 g_free(array->pdata[i]);
1071 g_ptr_array_free(array, TRUE);
1075 * camel_folder_free_uids:
1076 * @folder: folder object
1077 * @array: the array of uids to free
1079 * Frees the array of UIDs returned by camel_folder_get_uids().
1082 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
1084 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1086 CF_CLASS (folder)->free_uids (folder, array);
1090 get_summary(CamelFolder *folder)
1092 g_assert(folder->summary != NULL);
1094 return camel_folder_summary_array(folder->summary);
1098 * camel_folder_get_summary:
1099 * @folder: a folder object
1101 * This returns the summary information for the folder. This array
1102 * should not be modified, and must be freed with
1103 * camel_folder_free_summary().
1105 * Return value: an array of CamelMessageInfo
1108 camel_folder_get_summary (CamelFolder *folder)
1112 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1114 ret = CF_CLASS (folder)->get_summary (folder);
1120 free_summary(CamelFolder *folder, GPtrArray *summary)
1122 g_assert(folder->summary != NULL);
1124 camel_folder_summary_array_free(folder->summary, summary);
1128 * camel_folder_free_summary:
1129 * @folder: folder object
1130 * @array: the summary array to free
1132 * Frees the summary array returned by camel_folder_get_summary().
1134 void camel_folder_free_summary(CamelFolder * folder, GPtrArray * array)
1136 g_return_if_fail(CAMEL_IS_FOLDER(folder));
1138 CF_CLASS(folder)->free_summary(folder, array);
1142 * camel_folder_has_search_capability:
1143 * @folder: Folder object
1145 * Checks if a folder supports searching.
1147 * Return value: %TRUE if the folder supports searching
1150 camel_folder_has_search_capability (CamelFolder *folder)
1152 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1154 return folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
1158 search_by_expression (CamelFolder *folder, const char *expression,
1161 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1162 _("Unsupported operation: search by expression: for %s"),
1163 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1165 w(g_warning ("CamelFolder::search_by_expression not implemented for "
1166 "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1172 * camel_folder_search_by_expression:
1173 * @folder: Folder object
1174 * @expression: a search expression
1175 * @ex: a CamelException
1177 * Searches the folder for messages matching the given search expression.
1179 * Return value: a list of uids of matching messages. The caller must
1180 * free the list and each of the elements when it is done.
1183 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1188 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1189 g_return_val_if_fail (folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1191 /* NOTE: that it is upto the callee to lock */
1193 ret = CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1199 search_by_uids(CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex)
1201 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1202 _("Unsupported operation: search by uids: for %s"),
1203 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1205 w(g_warning ("CamelFolder::search_by_expression not implemented for "
1206 "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1212 * camel_folder_search_by_uids:
1215 * @uids: array of uid's to match against.
1218 * Search a subset of uid's for an expression match.
1223 camel_folder_search_by_uids(CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex)
1227 g_return_val_if_fail(CAMEL_IS_FOLDER (folder), NULL);
1228 g_return_val_if_fail(folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1230 /* NOTE: that it is upto the callee to lock */
1232 ret = CF_CLASS(folder)->search_by_uids(folder, expr, uids, ex);
1238 search_free (CamelFolder *folder, GPtrArray *result)
1242 for (i = 0; i < result->len; i++)
1243 g_free (g_ptr_array_index (result, i));
1244 g_ptr_array_free (result, TRUE);
1248 * camel_folder_search_free:
1252 * Free the result of a search.
1255 camel_folder_search_free (CamelFolder *folder, GPtrArray *result)
1257 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1259 /* NOTE: upto the callee to lock */
1260 CF_CLASS (folder)->search_free (folder, result);
1265 transfer_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1266 char **transferred_uid, gboolean delete_original,
1269 CamelMimeMessage *msg;
1270 CamelMessageInfo *info = NULL;
1272 /* Default implementation. */
1274 /* we alredy have the lock, dont deadlock */
1275 msg = CF_CLASS (source)->get_message (source, uid, ex);
1279 if (source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1280 info = CF_CLASS (source)->get_message_info (source, uid);
1282 info = camel_message_info_new_from_header (((CamelMimePart *)msg)->headers);
1284 /* we don't want to retain the deleted flag */
1285 if (info && info->flags & CAMEL_MESSAGE_DELETED) {
1286 info->flags = info->flags & ~CAMEL_MESSAGE_DELETED;
1287 delete_original = TRUE;
1290 camel_folder_append_message (dest, msg, info, transferred_uid, ex);
1291 camel_object_unref (CAMEL_OBJECT (msg));
1293 if (delete_original && !camel_exception_is_set (ex))
1294 camel_folder_set_message_flags (source, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, ~0);
1297 if (source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1298 CF_CLASS (source)->free_message_info (source, info);
1300 camel_message_info_free (info);
1305 transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
1307 CamelException local;
1308 char **ret_uid = NULL;
1311 if (transferred_uids) {
1312 *transferred_uids = g_ptr_array_new ();
1313 g_ptr_array_set_size (*transferred_uids, uids->len);
1316 camel_exception_init(&local);
1320 camel_operation_start(NULL, delete_originals ? _("Moving messages") : _("Copying messages"));
1322 if (uids->len > 1) {
1323 camel_folder_freeze(dest);
1324 if (delete_originals)
1325 camel_folder_freeze(source);
1327 for (i = 0; i < uids->len && !camel_exception_is_set (ex); i++) {
1328 if (transferred_uids)
1329 ret_uid = (char **)&((*transferred_uids)->pdata[i]);
1330 transfer_message_to (source, uids->pdata[i], dest, ret_uid, delete_originals, ex);
1331 camel_operation_progress(NULL, i * 100 / uids->len);
1333 if (uids->len > 1) {
1334 camel_folder_thaw(dest);
1335 if (delete_originals)
1336 camel_folder_thaw(source);
1339 camel_operation_end(NULL);
1340 camel_exception_clear(&local);
1344 * camel_folder_transfer_messages_to:
1345 * @source: source folder
1346 * @uids: message UIDs in @source
1347 * @dest: destination folder
1348 * @transferred_uids: if non-%NULL, the UIDs of the resulting messages
1349 * in @dest will be stored here, if known.
1350 * @delete_originals: whether or not to delete the original messages
1351 * @ex: a CamelException
1353 * This copies or moves messages from one folder to another. If the
1354 * @source and @dest folders have the same parent_store, this may be
1355 * more efficient than using camel_folder_append_message().
1358 camel_folder_transfer_messages_to (CamelFolder *source, GPtrArray *uids,
1359 CamelFolder *dest, GPtrArray **transferred_uids,
1360 gboolean delete_originals, CamelException *ex)
1362 g_return_if_fail (CAMEL_IS_FOLDER (source));
1363 g_return_if_fail (CAMEL_IS_FOLDER (dest));
1364 g_return_if_fail (uids != NULL);
1366 if (source == dest || uids->len == 0) {
1367 /* source and destination folders are the same, or no work to do, do nothing. */
1371 CAMEL_FOLDER_LOCK(source, lock);
1373 if (source->parent_store == dest->parent_store) {
1374 /* If either folder is a vtrash, we need to use the
1375 * vtrash transfer method.
1377 if (CAMEL_IS_VTRASH_FOLDER (dest))
1378 CF_CLASS (dest)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1380 CF_CLASS (source)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1382 transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1384 CAMEL_FOLDER_UNLOCK(source, lock);
1388 delete (CamelFolder *folder)
1390 if (folder->summary)
1391 camel_folder_summary_clear (folder->summary);
1395 * camel_folder_delete:
1398 * Marks a folder object as deleted and performs any required cleanup.
1401 camel_folder_delete (CamelFolder *folder)
1403 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1405 CAMEL_FOLDER_LOCK (folder, lock);
1406 if (folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
1407 CAMEL_FOLDER_UNLOCK (folder, lock);
1411 folder->folder_flags |= CAMEL_FOLDER_HAS_BEEN_DELETED;
1413 CF_CLASS (folder)->delete (folder);
1415 CAMEL_FOLDER_UNLOCK (folder, lock);
1417 camel_object_trigger_event (CAMEL_OBJECT (folder), "deleted", NULL);
1421 folder_rename (CamelFolder *folder, const char *new)
1425 printf("CamelFolder:rename('%s')\n", new);
1427 g_free(folder->full_name);
1428 folder->full_name = g_strdup(new);
1429 g_free(folder->name);
1430 tmp = strrchr(new, folder->parent_store->dir_sep);
1431 folder->name = g_strdup(tmp?tmp+1:new);
1435 * camel_folder_rename:
1439 * Mark an active folder object as renamed.
1441 * NOTE: This is an internal function used by camel stores, no locking
1442 * is performed on the folder.
1445 camel_folder_rename(CamelFolder *folder, const char *new)
1449 old = g_strdup(folder->full_name);
1451 CF_CLASS (folder)->rename(folder, new);
1453 camel_object_trigger_event (CAMEL_OBJECT (folder), "renamed", old);
1458 freeze (CamelFolder *folder)
1460 CAMEL_FOLDER_LOCK(folder, change_lock);
1462 g_assert(folder->priv->frozen >= 0);
1464 folder->priv->frozen++;
1466 d(printf ("freeze(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1467 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1471 * camel_folder_freeze:
1474 * Freezes the folder so that a series of operation can be performed
1475 * without "message_changed" and "folder_changed" signals being emitted.
1476 * When the folder is later thawed with camel_folder_thaw(), the
1477 * suppressed signals will be emitted.
1480 camel_folder_freeze (CamelFolder * folder)
1482 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1484 CF_CLASS (folder)->freeze (folder);
1488 thaw (CamelFolder * folder)
1490 CamelFolderChangeInfo *info = NULL;
1492 CAMEL_FOLDER_LOCK(folder, change_lock);
1494 g_assert(folder->priv->frozen > 0);
1496 folder->priv->frozen--;
1498 d(printf ("thaw(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1500 if (folder->priv->frozen == 0
1501 && camel_folder_change_info_changed(folder->priv->changed_frozen)) {
1502 info = folder->priv->changed_frozen;
1503 folder->priv->changed_frozen = camel_folder_change_info_new();
1506 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1509 camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", info);
1510 camel_folder_change_info_free(info);
1515 * camel_folder_thaw:
1518 * Thaws the folder and emits any pending folder_changed or
1519 * message_changed signals.
1522 camel_folder_thaw (CamelFolder *folder)
1524 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1525 g_return_if_fail (folder->priv->frozen != 0);
1527 CF_CLASS (folder)->thaw (folder);
1531 is_frozen (CamelFolder *folder)
1533 return folder->priv->frozen != 0;
1537 * camel_folder_is_frozen:
1540 * Return value: whether or not the folder is frozen.
1543 camel_folder_is_frozen (CamelFolder *folder)
1545 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1547 return CF_CLASS (folder)->is_frozen (folder);
1550 #ifdef ENABLE_THREADS
1551 struct _folder_filter_msg {
1552 CamelSessionThreadMsg msg;
1555 CamelFolder *folder;
1556 CamelFilterDriver *driver;
1561 filter_filter(CamelSession *session, CamelSessionThreadMsg *msg)
1563 struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1564 CamelMessageInfo *info;
1570 /* FIXME: progress? (old code didn't have useful progress either) */
1572 source_url = camel_service_get_url((CamelService *)m->folder->parent_store);
1573 uri = camel_url_new(source_url, NULL);
1575 if (m->folder->full_name && m->folder->full_name[0] != '/') {
1576 char *tmp = alloca(strlen(m->folder->full_name)+2);
1578 sprintf(tmp, "/%s", m->folder->full_name);
1579 camel_url_set_path(uri, tmp);
1581 camel_url_set_path(uri, m->folder->full_name);
1582 source_url = camel_url_to_string(uri, CAMEL_URL_HIDE_ALL);
1583 camel_url_free(uri);
1585 for (i=0;status == 0 && i<m->recents->len;i++) {
1586 char *uid = m->recents->pdata[i];
1588 info = camel_folder_get_message_info(m->folder, uid);
1590 g_warning("uid %s vanished from folder: %s", uid, source_url);
1594 status = camel_filter_driver_filter_message(m->driver, NULL, info, uid, m->folder, source_url, source_url, &m->ex);
1596 camel_folder_free_message_info(m->folder, info);
1599 camel_exception_init(&ex);
1600 camel_filter_driver_flush(m->driver, &ex);
1601 if (!camel_exception_is_set(&m->ex))
1602 camel_exception_xfer(&m->ex, &ex);
1608 filter_free(CamelSession *session, CamelSessionThreadMsg *msg)
1610 struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1613 camel_folder_thaw(m->folder);
1614 camel_object_unref((CamelObject *)m->folder);
1615 camel_object_unref((CamelObject *)m->driver);
1616 for (i=0;i<m->recents->len;i++)
1617 g_free(m->recents->pdata[i]);
1618 g_ptr_array_free(m->recents, TRUE);
1621 static CamelSessionThreadOps filter_ops = {
1627 /* Event hooks that block emission when frozen */
1629 folder_changed (CamelObject *obj, gpointer event_data)
1631 CamelFolder *folder = CAMEL_FOLDER (obj);
1632 CamelFolderChangeInfo *changed = event_data;
1633 gboolean ret = TRUE;
1635 d(printf ("folder_changed(%p, %p), frozen=%d\n", obj, event_data, folder->priv->frozen));
1636 d(printf(" added %d remoded %d changed %d recent %d\n",
1637 changed->uid_added->len, changed->uid_removed->len,
1638 changed->uid_changed->len, changed->uid_recent->len));
1640 if (changed != NULL) {
1641 CamelSession *session = ((CamelService *)folder->parent_store)->session;
1642 CamelFilterDriver *driver = NULL;
1644 if ((folder->folder_flags & CAMEL_FOLDER_FILTER_RECENT)
1645 && changed->uid_recent->len > 0)
1646 driver = camel_session_get_filter_driver(session, "incoming", NULL);
1648 CAMEL_FOLDER_LOCK(folder, change_lock);
1651 #ifdef ENABLE_THREADS
1652 GPtrArray *recents = g_ptr_array_new();
1654 struct _folder_filter_msg *msg;
1656 d(printf("** Have '%d' recent messages, launching thread to process them\n", changed->uid_recent->len));
1658 folder->priv->frozen++;
1659 msg = camel_session_thread_msg_new(session, &filter_ops, sizeof(*msg));
1660 for (i=0;i<changed->uid_recent->len;i++)
1661 g_ptr_array_add(recents, g_strdup(changed->uid_recent->pdata[i]));
1662 msg->recents = recents;
1663 msg->folder = folder;
1664 camel_object_ref((CamelObject *)folder);
1665 msg->driver = driver;
1666 camel_exception_init(&msg->ex);
1667 camel_session_thread_queue(session, &msg->msg, 0);
1669 d(printf("Have '%d' recent messages, filtering\n", changed->recent->len));
1670 folder->priv->frozen++;
1671 camel_filter_driver_filter_folder(driver, folder, NULL, changed->recent, FALSE, NULL);
1672 camel_object_unref((CamelObject *)driver);
1673 folder->priv->frozen--;
1675 /* zero out the recent list so we dont reprocess */
1676 /* this pokes past abstraction, but changeinfo is our structure anyway */
1677 /* the only other alternative is to recognise when trigger is called from
1678 thaw(), but thats a pita */
1679 g_ptr_array_set_size(changed->uid_recent, 0);
1682 if (folder->priv->frozen) {
1683 camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1686 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1688 w(g_warning ("Class %s is passing NULL to folder_changed event",
1689 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1696 message_changed (CamelObject *obj, /*const char *uid*/gpointer event_data)
1698 CamelFolder *folder = CAMEL_FOLDER (obj);
1699 gboolean ret = TRUE;
1701 d(printf ("message_changed(%p, %p), frozen=%d\n", folder, event_data, folder->priv->frozen));
1703 if (folder->priv->frozen) {
1704 CAMEL_FOLDER_LOCK(folder, change_lock);
1706 camel_folder_change_info_change_uid(folder->priv->changed_frozen, (char *)event_data);
1709 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1717 * camel_folder_free_nop:
1719 * @array: an array of uids or CamelMessageInfo
1721 * "Frees" the provided array by doing nothing. Used by CamelFolder
1722 * subclasses as an implementation for free_uids, or free_summary when
1723 * the returned array is "static" information and should not be freed.
1726 camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
1732 * camel_folder_free_shallow:
1734 * @array: an array of uids or CamelMessageInfo
1736 * Frees the provided array but not its contents. Used by CamelFolder
1737 * subclasses as an implementation for free_uids or free_summary when
1738 * the returned array needs to be freed but its contents come from
1739 * "static" information.
1742 camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
1744 g_ptr_array_free (array, TRUE);
1748 * camel_folder_free_deep:
1750 * @array: an array of uids
1752 * Frees the provided array and its contents. Used by CamelFolder
1753 * subclasses as an implementation for free_uids when the provided
1754 * information was created explicitly by the corresponding get_ call.
1757 camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
1761 for (i = 0; i < array->len; i++)
1762 g_free (array->pdata[i]);
1763 g_ptr_array_free (array, TRUE);
1766 struct _CamelFolderChangeInfoPrivate {
1767 GHashTable *uid_stored; /* what we have stored, which array they're in */
1768 GHashTable *uid_source; /* used to create unique lists */
1769 struct _EMemPool *uid_pool; /* pool used to store copies of uid strings */
1773 * camel_folder_change_info_new:
1776 * Create a new folder change info structure.
1778 * Change info structures are not MT-SAFE and must be
1779 * locked for exclusive access externally.
1783 CamelFolderChangeInfo *
1784 camel_folder_change_info_new(void)
1786 CamelFolderChangeInfo *info;
1788 info = g_malloc(sizeof(*info));
1789 info->uid_added = g_ptr_array_new();
1790 info->uid_removed = g_ptr_array_new();
1791 info->uid_changed = g_ptr_array_new();
1792 info->uid_recent = g_ptr_array_new();
1793 info->priv = g_malloc0(sizeof(*info->priv));
1794 info->priv->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
1795 info->priv->uid_source = NULL;
1796 info->priv->uid_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
1802 * camel_folder_change_info_add_source:
1806 * Add a source uid for generating a changeset.
1809 camel_folder_change_info_add_source(CamelFolderChangeInfo *info, const char *uid)
1811 struct _CamelFolderChangeInfoPrivate *p;
1813 g_assert(info != NULL);
1817 if (p->uid_source == NULL)
1818 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1820 if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1821 g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), (void *)1);
1825 * camel_folder_change_info_add_source_list:
1829 * Add a list of source uid's for generating a changeset.
1832 camel_folder_change_info_add_source_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1834 struct _CamelFolderChangeInfoPrivate *p;
1837 g_assert(info != NULL);
1838 g_assert(list != NULL);
1842 if (p->uid_source == NULL)
1843 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1845 for (i=0;i<list->len;i++) {
1846 char *uid = list->pdata[i];
1848 if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1849 g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), (void *)1);
1854 * camel_folder_change_info_add_update:
1858 * Add a uid from the updated list, used to generate a changeset diff.
1861 camel_folder_change_info_add_update(CamelFolderChangeInfo *info, const char *uid)
1863 struct _CamelFolderChangeInfoPrivate *p;
1867 g_assert(info != NULL);
1871 if (p->uid_source == NULL) {
1872 camel_folder_change_info_add_uid(info, uid);
1876 if (g_hash_table_lookup_extended(p->uid_source, uid, (void **)&key, (void **)&value)) {
1877 g_hash_table_remove(p->uid_source, key);
1879 camel_folder_change_info_add_uid(info, uid);
1884 * camel_folder_change_info_add_update_list:
1888 * Add a list of uid's from the updated list.
1891 camel_folder_change_info_add_update_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1895 g_assert(info != NULL);
1896 g_assert(list != NULL);
1898 for (i=0;i<list->len;i++)
1899 camel_folder_change_info_add_update(info, list->pdata[i]);
1903 change_info_remove(char *key, void *value, CamelFolderChangeInfo *info)
1905 struct _CamelFolderChangeInfoPrivate *p = info->priv;
1909 if (g_hash_table_lookup_extended(p->uid_stored, key, (void **)&olduid, (void **)&olduids)) {
1910 /* if it was added/changed them removed, then remove it */
1911 if (olduids != info->uid_removed) {
1912 g_ptr_array_remove_fast(olduids, olduid);
1913 g_ptr_array_add(info->uid_removed, olduid);
1914 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
1919 /* we dont need to copy this, as they've already been copied into our pool */
1920 g_ptr_array_add(info->uid_removed, key);
1921 g_hash_table_insert(p->uid_stored, key, info->uid_removed);
1925 * camel_folder_change_info_build_diff:
1928 * Compare the source uid set to the updated uid set and generate the differences
1929 * into the added and removed lists.
1932 camel_folder_change_info_build_diff(CamelFolderChangeInfo *info)
1934 struct _CamelFolderChangeInfoPrivate *p;
1936 g_assert(info != NULL);
1940 if (p->uid_source) {
1941 g_hash_table_foreach(p->uid_source, (GHFunc)change_info_remove, info);
1942 g_hash_table_destroy(p->uid_source);
1943 p->uid_source = NULL;
1948 change_info_cat(CamelFolderChangeInfo *info, GPtrArray *source, void (*add)(CamelFolderChangeInfo *info, const char *uid))
1952 for (i=0;i<source->len;i++)
1953 add(info, source->pdata[i]);
1957 * camel_folder_change_info_cat:
1961 * Concatenate one change info onto antoher. Can be used to copy
1965 camel_folder_change_info_cat(CamelFolderChangeInfo *info, CamelFolderChangeInfo *source)
1967 g_assert(info != NULL);
1968 g_assert(source != NULL);
1970 change_info_cat(info, source->uid_added, camel_folder_change_info_add_uid);
1971 change_info_cat(info, source->uid_removed, camel_folder_change_info_remove_uid);
1972 change_info_cat(info, source->uid_changed, camel_folder_change_info_change_uid);
1973 change_info_cat(info, source->uid_recent, camel_folder_change_info_recent_uid);
1977 * camel_folder_change_info_add_uid:
1981 * Add a new uid to the changeinfo.
1984 camel_folder_change_info_add_uid(CamelFolderChangeInfo *info, const char *uid)
1986 struct _CamelFolderChangeInfoPrivate *p;
1990 g_assert(info != NULL);
1994 if (g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
1995 /* if it was removed then added, promote it to a changed */
1996 /* if it was changed then added, leave as changed */
1997 if (olduids == info->uid_removed) {
1998 g_ptr_array_remove_fast(olduids, olduid);
1999 g_ptr_array_add(info->uid_changed, olduid);
2000 g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2005 olduid = e_mempool_strdup(p->uid_pool, uid);
2006 g_ptr_array_add(info->uid_added, olduid);
2007 g_hash_table_insert(p->uid_stored, olduid, info->uid_added);
2011 * camel_folder_change_info_remove_uid:
2015 * Add a uid to the removed uid list.
2018 camel_folder_change_info_remove_uid(CamelFolderChangeInfo *info, const char *uid)
2020 struct _CamelFolderChangeInfoPrivate *p;
2024 g_assert(info != NULL);
2028 if (g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2029 /* if it was added/changed them removed, then remove it */
2030 if (olduids != info->uid_removed) {
2031 g_ptr_array_remove_fast(olduids, olduid);
2032 g_ptr_array_add(info->uid_removed, olduid);
2033 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2038 olduid = e_mempool_strdup(p->uid_pool, uid);
2039 g_ptr_array_add(info->uid_removed, olduid);
2040 g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2044 * camel_folder_change_info_change_uid:
2048 * Add a uid to the changed uid list.
2051 camel_folder_change_info_change_uid(CamelFolderChangeInfo *info, const char *uid)
2053 struct _CamelFolderChangeInfoPrivate *p;
2057 g_assert(info != NULL);
2061 if (g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2062 /* if we have it already, leave it as that */
2066 olduid = e_mempool_strdup(p->uid_pool, uid);
2067 g_ptr_array_add(info->uid_changed, olduid);
2068 g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2072 camel_folder_change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2074 struct _CamelFolderChangeInfoPrivate *p;
2078 g_assert(info != NULL);
2082 /* always add to recent, but dont let anyone else know */
2083 if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2084 olduid = e_mempool_strdup(p->uid_pool, uid);
2086 g_ptr_array_add(info->uid_recent, olduid);
2090 * camel_folder_change_info_changed:
2093 * Return true if the changeset contains any changes.
2098 camel_folder_change_info_changed(CamelFolderChangeInfo *info)
2100 g_assert(info != NULL);
2102 return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len || info->uid_recent->len);
2106 * camel_folder_change_info_clear:
2109 * Empty out the change info; called after changes have been processed.
2112 camel_folder_change_info_clear(CamelFolderChangeInfo *info)
2114 struct _CamelFolderChangeInfoPrivate *p;
2116 g_assert(info != NULL);
2120 g_ptr_array_set_size(info->uid_added, 0);
2121 g_ptr_array_set_size(info->uid_removed, 0);
2122 g_ptr_array_set_size(info->uid_changed, 0);
2123 g_ptr_array_set_size(info->uid_recent, 0);
2124 if (p->uid_source) {
2125 g_hash_table_destroy(p->uid_source);
2126 p->uid_source = NULL;
2128 g_hash_table_destroy(p->uid_stored);
2129 p->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
2130 e_mempool_flush(p->uid_pool, TRUE);
2134 * camel_folder_change_info_free:
2137 * Free memory associated with the folder change info lists.
2140 camel_folder_change_info_free(CamelFolderChangeInfo *info)
2142 struct _CamelFolderChangeInfoPrivate *p;
2144 g_assert(info != NULL);
2149 g_hash_table_destroy(p->uid_source);
2151 g_hash_table_destroy(p->uid_stored);
2152 e_mempool_destroy(p->uid_pool);
2155 g_ptr_array_free(info->uid_added, TRUE);
2156 g_ptr_array_free(info->uid_removed, TRUE);
2157 g_ptr_array_free(info->uid_changed, TRUE);
2158 g_ptr_array_free(info->uid_recent, TRUE);