Updated for string-utils namespace changes.
[platform/upstream/evolution-data-server.git] / camel / camel-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-folder.c: Abstract class for an email folder */
3
4 /*
5  * Author:
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *
8  * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
9  *
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.
13  *
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.
18  *
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
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30
31 #include "camel-folder.h"
32 #include "camel-exception.h"
33 #include "camel-store.h"
34 #include "camel-mime-message.h"
35
36 #include "e-util/e-memory.h"
37 #include "camel-operation.h"
38
39 #include "camel-session.h"
40 #include "camel-filter-driver.h"
41 #include "camel-private.h"
42 #include "camel-vtrash-folder.h"
43
44 #define d(x) 
45 #define w(x)
46
47 static CamelObjectClass *parent_class = NULL;
48
49 /* Returns the class for a CamelFolder */
50 #define CF_CLASS(so) ((CamelFolderClass *)((CamelObject *)(so))->klass)
51
52 static void camel_folder_finalize (CamelObject *object);
53
54 static void refresh_info (CamelFolder *folder, CamelException *ex);
55
56 static void folder_sync (CamelFolder *folder, gboolean expunge,
57                          CamelException *ex);
58
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);
62
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);
72
73 static gint get_message_count (CamelFolder *folder);
74 static gint get_unread_message_count (CamelFolder *folder);
75
76 static void expunge             (CamelFolder *folder,
77                                  CamelException *ex);
78 static int folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args);
79 static void folder_free(CamelObject *o, guint32 tag, void *val);
80
81
82 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
83                             const CamelMessageInfo *info, char **appended_uid,
84                             CamelException *ex);
85
86
87 static GPtrArray        *get_uids            (CamelFolder *folder);
88 static void              free_uids           (CamelFolder *folder,
89                                               GPtrArray *array);
90 static GPtrArray        *get_summary         (CamelFolder *folder);
91 static void              free_summary        (CamelFolder *folder,
92                                               GPtrArray *array);
93
94 static CamelMimeMessage *get_message         (CamelFolder *folder, const gchar *uid, CamelException *ex);
95
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);
99
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);
103
104 static void            transfer_messages_to  (CamelFolder *source, GPtrArray *uids, CamelFolder *dest,
105                                               GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
106
107 static void            delete                (CamelFolder *folder);
108 static void            folder_rename         (CamelFolder *folder, const char *new);
109
110 static void            freeze                (CamelFolder *folder);
111 static void            thaw                  (CamelFolder *folder);
112 static gboolean        is_frozen             (CamelFolder *folder);
113
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);
118
119 static void
120 camel_folder_class_init (CamelFolderClass *camel_folder_class)
121 {
122         CamelObjectClass *camel_object_class = CAMEL_OBJECT_CLASS (camel_folder_class);
123
124         parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
125
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;
160
161         /* virtual method overload */
162         camel_object_class->getv = folder_getv;
163         camel_object_class->free = folder_free;
164
165         /* events */
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);
170 }
171
172 static void
173 camel_folder_init (gpointer object, gpointer klass)
174 {
175         CamelFolder *folder = object;
176
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);
183 #endif
184 }
185
186 static void
187 camel_folder_finalize (CamelObject *object)
188 {
189         CamelFolder *camel_folder = CAMEL_FOLDER (object);
190         struct _CamelFolderPrivate *p = camel_folder->priv;
191
192         g_free(camel_folder->name);
193         g_free(camel_folder->full_name);
194         g_free(camel_folder->description);
195
196         if (camel_folder->parent_store)
197                 camel_object_unref (CAMEL_OBJECT (camel_folder->parent_store));
198
199         if (camel_folder->summary)
200                 camel_object_unref((CamelObject *)camel_folder->summary);
201
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);
206 #endif
207         g_free(p);
208 }
209
210 CamelType
211 camel_folder_get_type (void)
212 {
213         static CamelType camel_folder_type = CAMEL_INVALID_TYPE;
214
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,
220                                                          NULL,
221                                                          (CamelObjectInitFunc) camel_folder_init,
222                                                          (CamelObjectFinalizeFunc) camel_folder_finalize );
223         }
224
225         return camel_folder_type;
226 }
227
228
229 /**
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
235  *
236  * Initalizes the folder by setting the parent store and name.
237  **/
238 void
239 camel_folder_construct (CamelFolder *folder, CamelStore *parent_store,
240                         const char *full_name, const char *name)
241 {
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);
246
247         folder->parent_store = parent_store;
248         if (parent_store)
249                 camel_object_ref (CAMEL_OBJECT (parent_store));
250
251         folder->name = g_strdup (name);
252         folder->full_name = g_strdup (full_name);
253 }
254
255
256 static void
257 folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
258 {
259         w(g_warning ("CamelFolder::sync not implemented for `%s'",
260                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
261 }
262
263 /**
264  * camel_folder_sync:
265  * @folder: The folder object
266  * @expunge: whether or not to expunge deleted messages
267  * @ex: exception object
268  *
269  * Sync changes made to a folder to its backing store, possibly expunging
270  * deleted messages as well.
271  **/
272 void
273 camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
274 {
275         g_return_if_fail (CAMEL_IS_FOLDER (folder));
276
277         CAMEL_FOLDER_LOCK(folder, lock);
278         
279         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
280                 CF_CLASS (folder)->sync (folder, expunge, ex);
281         
282         CAMEL_FOLDER_UNLOCK(folder, lock);
283 }
284
285
286 static void
287 refresh_info (CamelFolder *folder, CamelException *ex)
288 {
289         /* No op */
290 }
291
292 /**
293  * camel_folder_refresh_info:
294  * @folder: The folder object
295  * @ex: exception object
296  *
297  * Updates a folder's summary to be in sync with its backing store.
298  **/
299 void
300 camel_folder_refresh_info (CamelFolder *folder, CamelException *ex)
301 {
302         g_return_if_fail (CAMEL_IS_FOLDER (folder));
303
304         CAMEL_FOLDER_LOCK(folder, lock);
305
306         CF_CLASS (folder)->refresh_info (folder, ex);
307
308         CAMEL_FOLDER_UNLOCK(folder, lock);
309 }
310
311 static int
312 folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
313 {
314         CamelFolder *folder = (CamelFolder *)object;
315         int i, count=args->argc;
316         guint32 tag;
317
318         for (i=0;i<args->argc;i++) {
319                 CamelArgGet *arg = &args->argv[i];
320
321                 tag = arg->tag;
322
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;
329                         break;
330
331                         /* CamelFolder args */
332                 case CAMEL_FOLDER_ARG_NAME:
333                         *arg->ca_str = folder->name;
334                         break;
335                 case CAMEL_FOLDER_ARG_FULL_NAME:
336                         *arg->ca_str = folder->full_name;
337                         break;
338                 case CAMEL_FOLDER_ARG_STORE:
339                         *arg->ca_object = folder->parent_store;
340                         break;
341                 case CAMEL_FOLDER_ARG_PERMANENTFLAGS:
342                         *arg->ca_int = folder->permanent_flags;
343                         break;
344                 case CAMEL_FOLDER_ARG_TOTAL:
345                         *arg->ca_int = camel_folder_summary_count(folder->summary);
346                         break;
347                 case CAMEL_FOLDER_ARG_UNREAD: {
348                         int j, unread = 0, count;
349                         CamelMessageInfo *info;
350
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))
355                                                 unread++;
356                                         camel_folder_summary_info_free(folder->summary, info);
357                                 }
358                         }
359
360                         *arg->ca_int = unread;
361                         break; }
362                 case CAMEL_FOLDER_ARG_UID_ARRAY: {
363                         int j, count;
364                         CamelMessageInfo *info;
365                         GPtrArray *array;
366
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);
374                                 }
375                         }
376                         *arg->ca_ptr = array;
377                         break; }
378                 case CAMEL_FOLDER_ARG_INFO_ARRAY:
379                         *arg->ca_ptr = camel_folder_summary_array(folder->summary);
380                         break;
381                 default:
382                         count--;
383                         continue;
384                 }
385
386                 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
387         }
388
389         if (count)
390                 return parent_class->getv(object, ex, args);
391
392         return 0;
393 }
394
395 static void
396 folder_free(CamelObject *o, guint32 tag, void *val)
397 {
398         CamelFolder *folder = (CamelFolder *)o;
399
400         switch (tag & CAMEL_ARG_TAG) {
401         case CAMEL_FOLDER_ARG_UID_ARRAY: {
402                 GPtrArray *array = val;
403                 int i;
404
405                 for (i=0; i<array->len; i++)
406                         g_free(array->pdata[i]);
407                 g_ptr_array_free(array, TRUE);
408                 break; }
409         case CAMEL_FOLDER_ARG_INFO_ARRAY:
410                 camel_folder_summary_array_free(folder->summary, val);
411                 break;
412         default:
413                 parent_class->free(o, tag, val);
414         }
415 }
416
417 static const char *
418 get_name (CamelFolder *folder)
419 {
420         return folder->name;
421 }
422
423 /**
424  * camel_folder_get_name:
425  * @folder: a folder
426  *
427  * Get the (short) name of the folder. The fully qualified name
428  * can be obtained with the get_full_name method.
429  *
430  * Return value: name of the folder
431  **/
432 const char *
433 camel_folder_get_name (CamelFolder * folder)
434 {
435         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
436
437         return CF_CLASS (folder)->get_name (folder);
438 }
439
440
441 static const char *
442 get_full_name (CamelFolder *folder)
443 {
444         return folder->full_name;
445 }
446
447 /**
448  * camel_folder_get_full_name:
449  * @folder: a folder
450  *
451  * Get the (full) name of the folder.
452  *
453  * Return value: full name of the folder
454  **/
455 const char *
456 camel_folder_get_full_name (CamelFolder *folder)
457 {
458         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
459
460         return CF_CLASS (folder)->get_full_name (folder);
461 }
462
463
464 static CamelStore *
465 get_parent_store (CamelFolder * folder)
466 {
467         return folder->parent_store;
468 }
469
470 /**
471  * camel_folder_get_parent_store:
472  * @folder: folder to get the parent of
473  *
474  * Return value: the parent store of the folder.
475  **/
476 CamelStore *
477 camel_folder_get_parent_store (CamelFolder *folder)
478 {
479         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
480
481         return CF_CLASS (folder)->get_parent_store (folder);
482 }
483
484
485 static void
486 expunge (CamelFolder *folder, CamelException *ex)
487 {
488         w(g_warning ("CamelFolder::expunge not implemented for `%s'",
489                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
490 }
491
492
493 /**
494  * camel_folder_expunge:
495  * @folder: the folder
496  * @ex: a CamelException
497  *
498  * Delete messages which have been marked as "DELETED"
499  **/
500 void
501 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
502 {
503         g_return_if_fail (CAMEL_IS_FOLDER (folder));
504         
505         CAMEL_FOLDER_LOCK(folder, lock);
506         
507         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
508                 CF_CLASS (folder)->expunge (folder, ex);
509         
510         CAMEL_FOLDER_UNLOCK(folder, lock);
511 }
512
513 static int
514 get_message_count (CamelFolder *folder)
515 {
516         g_return_val_if_fail(folder->summary != NULL, -1);
517
518         return camel_folder_summary_count(folder->summary);
519 }
520
521 /**
522  * camel_folder_get_message_count:
523  * @folder: A CamelFolder object
524  *
525  * Return value: the number of messages in the folder, or -1 if unknown.
526  **/
527 int
528 camel_folder_get_message_count (CamelFolder *folder)
529 {
530         int ret;
531
532         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
533
534         ret = CF_CLASS (folder)->get_message_count (folder);
535
536         return ret;
537 }
538
539 static int
540 get_unread_message_count(CamelFolder *folder)
541 {
542         int i, count, unread=0;
543
544         g_return_val_if_fail(folder->summary != NULL, -1);
545
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);
549
550                 if (info) {
551                         if (!(info->flags & CAMEL_MESSAGE_SEEN))
552                                 unread++;
553                         camel_folder_summary_info_free(folder->summary, info);
554                 }
555         }
556
557         return unread;
558 }
559
560 /**
561  * camel_folder_unread_get_message_count:
562  * @folder: A CamelFolder object
563  *
564  * Return value: the number of unread messages in the folder, or -1 if unknown.
565  **/
566 int
567 camel_folder_get_unread_message_count (CamelFolder *folder)
568 {
569         int ret;
570
571         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
572         
573         ret = CF_CLASS (folder)->get_unread_message_count (folder);
574
575         return ret;
576 }
577
578 static void
579 append_message (CamelFolder *folder, CamelMimeMessage *message,
580                 const CamelMessageInfo *info, char **appended_uid,
581                 CamelException *ex)
582 {
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)));
586         
587         w(g_warning ("CamelFolder::append_message not implemented for `%s'",
588                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
589         
590         return;
591
592 }
593
594 /**
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
603  *
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.
606  **/
607 void
608 camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message,
609                              const CamelMessageInfo *info, char **appended_uid,
610                              CamelException *ex)
611 {
612         g_return_if_fail (CAMEL_IS_FOLDER (folder));
613
614         CAMEL_FOLDER_LOCK(folder, lock);
615
616         CF_CLASS (folder)->append_message (folder, message, info, appended_uid, ex);
617
618         CAMEL_FOLDER_UNLOCK(folder, lock);
619 }
620
621
622 static guint32
623 get_permanent_flags (CamelFolder *folder)
624 {
625         return folder->permanent_flags;
626 }
627
628 /**
629  * camel_folder_get_permanent_flags:
630  * @folder: a CamelFolder
631  *
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.
635  **/
636 guint32
637 camel_folder_get_permanent_flags (CamelFolder *folder)
638 {
639         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
640
641         return CF_CLASS (folder)->get_permanent_flags (folder);
642 }
643
644 static guint32
645 get_message_flags(CamelFolder *folder, const char *uid)
646 {
647         CamelMessageInfo *info;
648         guint32 flags;
649
650         g_return_val_if_fail(folder->summary != NULL, 0);
651
652         info = camel_folder_summary_uid(folder->summary, uid);
653         if (info == NULL)
654                 return 0;
655
656         flags = info->flags;
657         camel_folder_summary_info_free(folder->summary, info);
658
659         return flags;
660 }
661
662 /**
663  * camel_folder_get_message_flags:
664  * @folder: a CamelFolder
665  * @uid: the UID of a message in @folder
666  *
667  * Return value: the CamelMessageFlags that are set on the indicated
668  * message.
669  **/
670 guint32
671 camel_folder_get_message_flags (CamelFolder *folder, const char *uid)
672 {
673         guint32 ret;
674
675         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
676
677         ret = CF_CLASS (folder)->get_message_flags (folder, uid);
678
679         return ret;
680 }
681
682 static void
683 set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
684 {
685         CamelMessageInfo *info;
686         guint32 new;
687
688         g_return_if_fail(folder->summary != NULL);
689
690         info = camel_folder_summary_uid(folder->summary, uid);
691         if (info == NULL)
692                 return;
693
694         new = (info->flags & ~flags) | (set & flags);
695         if (new == info->flags) {
696                 camel_folder_summary_info_free(folder->summary, info);
697                 return;
698         }
699         
700         info->flags = new | CAMEL_MESSAGE_FOLDER_FLAGGED;
701         camel_folder_summary_touch(folder->summary);
702         camel_folder_summary_info_free(folder->summary, info);
703
704         camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
705 }
706
707 /**
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.
713  *
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().)
717  **/
718 void
719 camel_folder_set_message_flags (CamelFolder *folder, const char *uid,
720                                 guint32 flags, guint32 set)
721 {
722         g_return_if_fail (CAMEL_IS_FOLDER (folder));
723
724         CF_CLASS (folder)->set_message_flags (folder, uid, flags, set);
725 }
726
727
728 static gboolean
729 get_message_user_flag(CamelFolder *folder, const char *uid, const char *name)
730 {
731         CamelMessageInfo *info;
732         gboolean ret;
733
734         g_return_val_if_fail(folder->summary != NULL, FALSE);
735
736         info = camel_folder_summary_uid(folder->summary, uid);
737         if (info == NULL)
738                 return FALSE;
739
740         ret = camel_flag_get(&info->user_flags, name);
741         camel_folder_summary_info_free(folder->summary, info);
742
743         return ret;
744 }
745
746 /**
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
751  *
752  * Return value: whether or not the given user flag is set on the message.
753  **/
754 gboolean
755 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
756                                     const char *name)
757 {
758         gboolean ret;
759
760         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
761
762         ret = CF_CLASS (folder)->get_message_user_flag (folder, uid, name);
763
764         return ret;
765 }
766
767 static void
768 set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
769 {
770         CamelMessageInfo *info;
771
772         g_return_if_fail(folder->summary != NULL);
773
774         info = camel_folder_summary_uid(folder->summary, uid);
775         if (info == NULL)
776                 return;
777
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);
782         }
783         camel_folder_summary_info_free(folder->summary, info);
784 }
785
786 /**
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
792  *
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().)
796  **/
797 void
798 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
799                                     const char *name, gboolean value)
800 {
801         g_return_if_fail (CAMEL_IS_FOLDER (folder));
802
803         CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value);
804 }
805
806 static const char *
807 get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
808 {
809         CamelMessageInfo *info;
810         const char *ret;
811
812         g_return_val_if_fail(folder->summary != NULL, NULL);
813
814         info = camel_folder_summary_uid(folder->summary, uid);
815         if (info == NULL)
816                 return NULL;
817
818         /* FIXME: Need to duplicate tag string */
819
820         ret = camel_tag_get(&info->user_tags, name);
821         camel_folder_summary_info_free(folder->summary, info);
822
823         return ret;
824 }
825
826 /**
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
831  *
832  * Return value: Returns the value of the user tag.
833  **/
834 const char *
835 camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid,  const char *name)
836 {
837         const char *ret;
838
839         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
840
841         /* FIXME: should duplicate string */
842         ret = CF_CLASS (folder)->get_message_user_tag (folder, uid, name);
843
844         return ret;
845 }
846
847 static void
848 set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
849 {
850         CamelMessageInfo *info;
851
852         g_return_if_fail(folder->summary != NULL);
853
854         info = camel_folder_summary_uid(folder->summary, uid);
855         if (info == NULL)
856                 return;
857
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);
862         }
863         camel_folder_summary_info_free(folder->summary, info);
864 }
865
866 /**
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
872  *
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().)
876  **/
877 void
878 camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const char *name, const char *value)
879 {
880         g_return_if_fail (CAMEL_IS_FOLDER (folder));
881
882         CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value);
883 }
884
885 static CamelMessageInfo *
886 get_message_info (CamelFolder *folder, const char *uid)
887 {
888         g_return_val_if_fail(folder->summary != NULL, NULL);
889
890         return camel_folder_summary_uid(folder->summary, uid);
891 }
892
893 /**
894  * camel_folder_get_message_info:
895  * @folder: a CamelFolder
896  * @uid: the uid of a message
897  *
898  * Retrieve the CamelMessageInfo for the specified @uid.  This return
899  * must be freed using free_message_info().
900  *
901  * Return value: the summary information for the indicated message, or NULL
902  * if the uid does not exist.
903  **/
904 CamelMessageInfo *
905 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
906 {
907         CamelMessageInfo *ret;
908
909         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
910         g_return_val_if_fail (uid != NULL, NULL);
911
912         ret = CF_CLASS (folder)->get_message_info (folder, uid);
913
914         return ret;
915 }
916
917 static void
918 free_message_info (CamelFolder *folder, CamelMessageInfo *info)
919 {
920         g_return_if_fail(folder->summary != NULL);
921
922         camel_folder_summary_info_free(folder->summary, info);
923 }
924
925 /**
926  * camel_folder_free_message_info:
927  * @folder: 
928  * @info: 
929  * 
930  * Free (unref) a CamelMessageInfo, previously obtained with get_message_info().
931  **/
932 void
933 camel_folder_free_message_info(CamelFolder *folder, CamelMessageInfo *info)
934 {
935         g_return_if_fail(CAMEL_IS_FOLDER (folder));
936         g_return_if_fail(info != NULL);
937
938         CF_CLASS (folder)->free_message_info(folder, info);
939 }
940
941 static void
942 ref_message_info (CamelFolder *folder, CamelMessageInfo *info)
943 {
944         g_return_if_fail(folder->summary != NULL);
945
946         camel_folder_summary_info_ref(folder->summary, info);
947 }
948
949 /**
950  * camel_folder_ref_message_info:
951  * @folder: 
952  * @info: 
953  * 
954  * Ref a CamelMessageInfo, previously obtained with get_message_info().
955  **/
956 void
957 camel_folder_ref_message_info(CamelFolder *folder, CamelMessageInfo *info)
958 {
959         g_return_if_fail(CAMEL_IS_FOLDER (folder));
960         g_return_if_fail(info != NULL);
961
962         CF_CLASS (folder)->ref_message_info(folder, info);
963 }
964
965 /* TODO: is this function required anyway? */
966 gboolean
967 camel_folder_has_summary_capability (CamelFolder *folder)
968 {
969         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
970
971         return folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
972 }
973
974
975 /* UIDs stuff */
976
977 static CamelMimeMessage *
978 get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
979 {
980         w(g_warning ("CamelFolder::get_message not implemented for `%s'",
981                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
982         
983         return NULL;
984 }
985
986 /**
987  * camel_folder_get_message:
988  * @folder: the folder object
989  * @uid: the UID
990  * @ex: a CamelException
991  *
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?)
995  *
996  * Return value: Message corresponding to the UID
997  **/
998 CamelMimeMessage *
999 camel_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
1000 {
1001         CamelMimeMessage *ret;
1002
1003         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1004
1005         CAMEL_FOLDER_LOCK(folder, lock);
1006
1007         ret = CF_CLASS (folder)->get_message (folder, uid, ex);
1008
1009         CAMEL_FOLDER_UNLOCK(folder, lock);
1010
1011         return ret;
1012 }
1013
1014 static GPtrArray *
1015 get_uids(CamelFolder *folder)
1016 {
1017         GPtrArray *array;
1018         int i, j, count;
1019
1020         array = g_ptr_array_new();
1021
1022         g_return_val_if_fail(folder->summary != NULL, array);
1023
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);
1028                 
1029                 if (info) {
1030                         array->pdata[j++] = g_strdup (camel_message_info_uid (info));
1031                         camel_folder_summary_info_free(folder->summary, info);
1032                 }
1033         }
1034         
1035         g_ptr_array_set_size (array, j);
1036         
1037         return array;
1038 }
1039
1040 /**
1041  * camel_folder_get_uids:
1042  * @folder: folder object
1043  *
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().
1048  *
1049  * Return value: GPtrArray of UIDs corresponding to the messages
1050  * available in the folder.
1051  **/
1052 GPtrArray *
1053 camel_folder_get_uids (CamelFolder *folder)
1054 {
1055         GPtrArray *ret;
1056
1057         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1058
1059         ret = CF_CLASS (folder)->get_uids (folder);
1060
1061         return ret;
1062 }
1063
1064 static void
1065 free_uids (CamelFolder *folder, GPtrArray *array)
1066 {
1067         int i;
1068
1069         for (i=0; i<array->len; i++)
1070                 g_free(array->pdata[i]);
1071         g_ptr_array_free(array, TRUE);
1072 }
1073
1074 /**
1075  * camel_folder_free_uids:
1076  * @folder: folder object
1077  * @array: the array of uids to free
1078  *
1079  * Frees the array of UIDs returned by camel_folder_get_uids().
1080  **/
1081 void
1082 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
1083 {
1084         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1085
1086         CF_CLASS (folder)->free_uids (folder, array);
1087 }
1088
1089 static GPtrArray *
1090 get_summary(CamelFolder *folder)
1091 {
1092         g_assert(folder->summary != NULL);
1093
1094         return camel_folder_summary_array(folder->summary);
1095 }
1096
1097 /**
1098  * camel_folder_get_summary:
1099  * @folder: a folder object
1100  *
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().
1104  *
1105  * Return value: an array of CamelMessageInfo
1106  **/
1107 GPtrArray *
1108 camel_folder_get_summary (CamelFolder *folder)
1109 {
1110         GPtrArray *ret;
1111
1112         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1113
1114         ret = CF_CLASS (folder)->get_summary (folder);
1115
1116         return ret;
1117 }
1118
1119 static void
1120 free_summary(CamelFolder *folder, GPtrArray *summary)
1121 {
1122         g_assert(folder->summary != NULL);
1123
1124         camel_folder_summary_array_free(folder->summary, summary);
1125 }
1126
1127 /**
1128  * camel_folder_free_summary:
1129  * @folder: folder object
1130  * @array: the summary array to free
1131  *
1132  * Frees the summary array returned by camel_folder_get_summary().
1133  **/
1134 void camel_folder_free_summary(CamelFolder * folder, GPtrArray * array)
1135 {
1136         g_return_if_fail(CAMEL_IS_FOLDER(folder));
1137
1138         CF_CLASS(folder)->free_summary(folder, array);
1139 }
1140
1141 /**
1142  * camel_folder_has_search_capability:
1143  * @folder: Folder object
1144  *
1145  * Checks if a folder supports searching.
1146  *
1147  * Return value: %TRUE if the folder supports searching
1148  **/
1149 gboolean
1150 camel_folder_has_search_capability (CamelFolder *folder)
1151 {
1152         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1153
1154         return folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
1155 }
1156
1157 static GPtrArray *
1158 search_by_expression (CamelFolder *folder, const char *expression,
1159                       CamelException *ex)
1160 {
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)));
1164         
1165         w(g_warning ("CamelFolder::search_by_expression not implemented for "
1166                      "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1167         
1168         return NULL;
1169 }
1170
1171 /**
1172  * camel_folder_search_by_expression:
1173  * @folder: Folder object
1174  * @expression: a search expression
1175  * @ex: a CamelException
1176  *
1177  * Searches the folder for messages matching the given search expression.
1178  *
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.
1181  **/
1182 GPtrArray *
1183 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1184                                    CamelException *ex)
1185 {
1186         GPtrArray *ret;
1187
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);
1190
1191         /* NOTE: that it is upto the callee to lock */
1192
1193         ret = CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1194
1195         return ret;
1196 }
1197
1198 static GPtrArray *
1199 search_by_uids(CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex)
1200 {
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)));
1204         
1205         w(g_warning ("CamelFolder::search_by_expression not implemented for "
1206                      "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1207         
1208         return NULL;
1209 }
1210
1211 /**
1212  * camel_folder_search_by_uids:
1213  * @folder: 
1214  * @expr: 
1215  * @uids: array of uid's to match against.
1216  * @ex: 
1217  * 
1218  * Search a subset of uid's for an expression match.
1219  * 
1220  * Return value: 
1221  **/
1222 GPtrArray *
1223 camel_folder_search_by_uids(CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex)
1224 {
1225         GPtrArray *ret;
1226
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);
1229
1230         /* NOTE: that it is upto the callee to lock */
1231
1232         ret = CF_CLASS(folder)->search_by_uids(folder, expr, uids, ex);
1233
1234         return ret;
1235 }
1236
1237 static void
1238 search_free (CamelFolder *folder, GPtrArray *result)
1239 {
1240         int i;
1241
1242         for (i = 0; i < result->len; i++)
1243                 g_free (g_ptr_array_index (result, i));
1244         g_ptr_array_free (result, TRUE);
1245 }
1246
1247 /**
1248  * camel_folder_search_free:
1249  * @folder: 
1250  * @result: 
1251  * 
1252  * Free the result of a search.
1253  **/
1254 void 
1255 camel_folder_search_free (CamelFolder *folder, GPtrArray *result)
1256 {
1257         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1258
1259         /* NOTE: upto the callee to lock */
1260         CF_CLASS (folder)->search_free (folder, result);
1261 }
1262
1263
1264 static void
1265 transfer_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1266                      char **transferred_uid, gboolean delete_original,
1267                      CamelException *ex)
1268 {
1269         CamelMimeMessage *msg;
1270         CamelMessageInfo *info = NULL;
1271         
1272         /* Default implementation. */
1273         
1274         /* we alredy have the lock, dont deadlock */
1275         msg = CF_CLASS (source)->get_message (source, uid, ex);
1276         if (!msg)
1277                 return;
1278         
1279         if (source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1280                 info = CF_CLASS (source)->get_message_info (source, uid);
1281         else
1282                 info = camel_message_info_new_from_header (((CamelMimePart *)msg)->headers);
1283         
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;
1288         }
1289         
1290         camel_folder_append_message (dest, msg, info, transferred_uid, ex);
1291         camel_object_unref (CAMEL_OBJECT (msg));
1292         
1293         if (delete_original && !camel_exception_is_set (ex))
1294                 camel_folder_set_message_flags (source, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, ~0);
1295         
1296         if (info) {
1297                 if (source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1298                         CF_CLASS (source)->free_message_info (source, info);
1299                 else
1300                         camel_message_info_free (info);
1301         }
1302 }
1303
1304 static void
1305 transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
1306 {
1307         CamelException local;
1308         char **ret_uid = NULL;
1309         int i;
1310
1311         if (transferred_uids) {
1312                 *transferred_uids = g_ptr_array_new ();
1313                 g_ptr_array_set_size (*transferred_uids, uids->len);
1314         }
1315
1316         camel_exception_init(&local);
1317         if (ex == NULL)
1318                 ex = &local;
1319
1320         camel_operation_start(NULL, delete_originals ? _("Moving messages") : _("Copying messages"));
1321
1322         if (uids->len > 1) {
1323                 camel_folder_freeze(dest);
1324                 if (delete_originals)
1325                         camel_folder_freeze(source);
1326         }
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);
1332         }
1333         if (uids->len > 1) {
1334                 camel_folder_thaw(dest);
1335                 if (delete_originals)
1336                         camel_folder_thaw(source);
1337         }
1338
1339         camel_operation_end(NULL);
1340         camel_exception_clear(&local);
1341 }
1342
1343 /**
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
1352  *
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().
1356  **/
1357 void
1358 camel_folder_transfer_messages_to (CamelFolder *source, GPtrArray *uids,
1359                                    CamelFolder *dest, GPtrArray **transferred_uids,
1360                                    gboolean delete_originals, CamelException *ex)
1361 {
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);
1365         
1366         if (source == dest || uids->len == 0) {
1367                 /* source and destination folders are the same, or no work to do, do nothing. */
1368                 return;
1369         }
1370         
1371         CAMEL_FOLDER_LOCK(source, lock);
1372         
1373         if (source->parent_store == dest->parent_store) {
1374                 /* If either folder is a vtrash, we need to use the
1375                  * vtrash transfer method.
1376                  */
1377                 if (CAMEL_IS_VTRASH_FOLDER (dest))
1378                         CF_CLASS (dest)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1379                 else
1380                         CF_CLASS (source)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1381         } else
1382                 transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1383         
1384         CAMEL_FOLDER_UNLOCK(source, lock);
1385 }
1386
1387 static void
1388 delete (CamelFolder *folder)
1389 {
1390         if (folder->summary)
1391                 camel_folder_summary_clear (folder->summary);
1392 }
1393
1394 /**
1395  * camel_folder_delete:
1396  * @folder: folder
1397  *
1398  * Marks a folder object as deleted and performs any required cleanup.
1399  **/
1400 void
1401 camel_folder_delete (CamelFolder *folder)
1402 {
1403         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1404         
1405         CAMEL_FOLDER_LOCK (folder, lock);
1406         if (folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
1407                 CAMEL_FOLDER_UNLOCK (folder, lock);
1408                 return;
1409         }
1410         
1411         folder->folder_flags |= CAMEL_FOLDER_HAS_BEEN_DELETED;
1412         
1413         CF_CLASS (folder)->delete (folder);
1414
1415         CAMEL_FOLDER_UNLOCK (folder, lock);
1416
1417         camel_object_trigger_event (CAMEL_OBJECT (folder), "deleted", NULL);
1418 }
1419
1420 static void
1421 folder_rename (CamelFolder *folder, const char *new)
1422 {
1423         char *tmp;
1424
1425         printf("CamelFolder:rename('%s')\n", new);
1426
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);
1432 }
1433
1434 /**
1435  * camel_folder_rename:
1436  * @folder: 
1437  * @new: 
1438  * 
1439  * Mark an active folder object as renamed.
1440  *
1441  * NOTE: This is an internal function used by camel stores, no locking
1442  * is performed on the folder.
1443  **/
1444 void
1445 camel_folder_rename(CamelFolder *folder, const char *new)
1446 {
1447         char *old;
1448
1449         old = g_strdup(folder->full_name);
1450
1451         CF_CLASS (folder)->rename(folder, new);
1452
1453         camel_object_trigger_event (CAMEL_OBJECT (folder), "renamed", old);
1454         g_free(old);
1455 }
1456
1457 static void
1458 freeze (CamelFolder *folder)
1459 {
1460         CAMEL_FOLDER_LOCK(folder, change_lock);
1461
1462         g_assert(folder->priv->frozen >= 0);
1463
1464         folder->priv->frozen++;
1465
1466         d(printf ("freeze(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1467         CAMEL_FOLDER_UNLOCK(folder, change_lock);
1468 }
1469
1470 /**
1471  * camel_folder_freeze:
1472  * @folder: a folder
1473  *
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.
1478  **/
1479 void
1480 camel_folder_freeze (CamelFolder * folder)
1481 {
1482         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1483
1484         CF_CLASS (folder)->freeze (folder);
1485 }
1486
1487 static void
1488 thaw (CamelFolder * folder)
1489 {
1490         CamelFolderChangeInfo *info = NULL;
1491
1492         CAMEL_FOLDER_LOCK(folder, change_lock);
1493
1494         g_assert(folder->priv->frozen > 0);
1495
1496         folder->priv->frozen--;
1497
1498         d(printf ("thaw(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1499
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();
1504         }
1505         
1506         CAMEL_FOLDER_UNLOCK(folder, change_lock);
1507
1508         if (info) {
1509                 camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", info);
1510                 camel_folder_change_info_free(info);
1511         }
1512 }
1513
1514 /**
1515  * camel_folder_thaw:
1516  * @folder: a folder
1517  *
1518  * Thaws the folder and emits any pending folder_changed or
1519  * message_changed signals.
1520  **/
1521 void
1522 camel_folder_thaw (CamelFolder *folder)
1523 {
1524         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1525         g_return_if_fail (folder->priv->frozen != 0);
1526
1527         CF_CLASS (folder)->thaw (folder);
1528 }
1529
1530 static gboolean
1531 is_frozen (CamelFolder *folder)
1532 {
1533         return folder->priv->frozen != 0;
1534 }
1535
1536 /**
1537  * camel_folder_is_frozen:
1538  * @folder: a folder
1539  *
1540  * Return value: whether or not the folder is frozen.
1541  **/
1542 gboolean
1543 camel_folder_is_frozen (CamelFolder *folder)
1544 {
1545         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1546
1547         return CF_CLASS (folder)->is_frozen (folder);
1548 }
1549
1550 #ifdef ENABLE_THREADS
1551 struct _folder_filter_msg {
1552         CamelSessionThreadMsg msg;
1553
1554         GPtrArray *recents;
1555         CamelFolder *folder;
1556         CamelFilterDriver *driver;
1557         CamelException ex;
1558 };
1559
1560 static void
1561 filter_filter(CamelSession *session, CamelSessionThreadMsg *msg)
1562 {
1563         struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1564         CamelMessageInfo *info;
1565         int i, status = 0;
1566         CamelURL *uri;
1567         char *source_url;
1568         CamelException ex;
1569
1570         /* FIXME: progress? (old code didn't have useful progress either) */
1571
1572         source_url = camel_service_get_url((CamelService *)m->folder->parent_store);
1573         uri = camel_url_new(source_url, NULL);
1574         g_free(source_url);
1575         if (m->folder->full_name && m->folder->full_name[0] != '/') {
1576                 char *tmp = alloca(strlen(m->folder->full_name)+2);
1577
1578                 sprintf(tmp, "/%s", m->folder->full_name);
1579                 camel_url_set_path(uri, tmp);
1580         } else
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);
1584
1585         for (i=0;status == 0 && i<m->recents->len;i++) {
1586                 char *uid = m->recents->pdata[i];
1587
1588                 info = camel_folder_get_message_info(m->folder, uid);
1589                 if (info == NULL) {
1590                         g_warning("uid %s vanished from folder: %s", uid, source_url);
1591                         continue;
1592                 }
1593
1594                 status = camel_filter_driver_filter_message(m->driver, NULL, info, uid, m->folder, source_url, source_url, &m->ex);
1595
1596                 camel_folder_free_message_info(m->folder, info);
1597         }
1598
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);
1603
1604         g_free(source_url);
1605 }
1606
1607 static void
1608 filter_free(CamelSession *session, CamelSessionThreadMsg *msg)
1609 {
1610         struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1611         int i;
1612
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);
1619 }
1620
1621 static CamelSessionThreadOps filter_ops = {
1622         filter_filter,
1623         filter_free,
1624 };
1625 #endif
1626
1627 /* Event hooks that block emission when frozen */
1628 static gboolean
1629 folder_changed (CamelObject *obj, gpointer event_data)
1630 {
1631         CamelFolder *folder = CAMEL_FOLDER (obj);
1632         CamelFolderChangeInfo *changed = event_data;
1633         gboolean ret = TRUE;
1634
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));
1639
1640         if (changed != NULL) {
1641                 CamelSession *session = ((CamelService *)folder->parent_store)->session;
1642                 CamelFilterDriver *driver = NULL;
1643
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);
1647                         
1648                 CAMEL_FOLDER_LOCK(folder, change_lock);
1649
1650                 if (driver) {
1651 #ifdef ENABLE_THREADS
1652                         GPtrArray *recents = g_ptr_array_new();
1653                         int i;
1654                         struct _folder_filter_msg *msg;
1655                         
1656                         d(printf("** Have '%d' recent messages, launching thread to process them\n", changed->uid_recent->len));
1657
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);
1668 #else
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--;
1674 #endif
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);
1680                 }
1681                 
1682                 if (folder->priv->frozen) {
1683                         camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1684                         ret = FALSE;
1685                 }
1686                 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1687         } else {
1688                         w(g_warning ("Class %s is passing NULL to folder_changed event",
1689                                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1690         }
1691
1692         return ret;
1693 }
1694
1695 static gboolean
1696 message_changed (CamelObject *obj, /*const char *uid*/gpointer event_data)
1697 {
1698         CamelFolder *folder = CAMEL_FOLDER (obj);
1699         gboolean ret = TRUE;
1700
1701         d(printf ("message_changed(%p, %p), frozen=%d\n", folder, event_data, folder->priv->frozen));
1702
1703         if (folder->priv->frozen) {
1704                 CAMEL_FOLDER_LOCK(folder, change_lock);
1705         
1706                 camel_folder_change_info_change_uid(folder->priv->changed_frozen, (char *)event_data);
1707                 ret = FALSE;
1708
1709                 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1710         }
1711
1712         return ret;
1713 }
1714
1715
1716 /**
1717  * camel_folder_free_nop:
1718  * @folder: a folder
1719  * @array: an array of uids or CamelMessageInfo
1720  *
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.
1724  **/
1725 void
1726 camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
1727 {
1728         ;
1729 }
1730
1731 /**
1732  * camel_folder_free_shallow:
1733  * @folder: a folder
1734  * @array: an array of uids or CamelMessageInfo
1735  *
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.
1740  **/
1741 void
1742 camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
1743 {
1744         g_ptr_array_free (array, TRUE);
1745 }
1746
1747 /**
1748  * camel_folder_free_deep:
1749  * @folder: a folder
1750  * @array: an array of uids
1751  *
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.
1755  **/
1756 void
1757 camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
1758 {
1759         int i;
1760
1761         for (i = 0; i < array->len; i++)
1762                 g_free (array->pdata[i]);
1763         g_ptr_array_free (array, TRUE);
1764 }
1765
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 */
1770 };
1771
1772 /**
1773  * camel_folder_change_info_new:
1774  * @void: 
1775  * 
1776  * Create a new folder change info structure.
1777  *
1778  * Change info structures are not MT-SAFE and must be
1779  * locked for exclusive access externally.
1780  * 
1781  * Return value: 
1782  **/
1783 CamelFolderChangeInfo *
1784 camel_folder_change_info_new(void)
1785 {
1786         CamelFolderChangeInfo *info;
1787
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);
1797
1798         return info;
1799 }
1800
1801 /**
1802  * camel_folder_change_info_add_source:
1803  * @info: 
1804  * @uid: 
1805  * 
1806  * Add a source uid for generating a changeset.
1807  **/
1808 void
1809 camel_folder_change_info_add_source(CamelFolderChangeInfo *info, const char *uid)
1810 {
1811         struct _CamelFolderChangeInfoPrivate *p;
1812         
1813         g_assert(info != NULL);
1814         
1815         p = info->priv;
1816         
1817         if (p->uid_source == NULL)
1818                 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1819
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);
1822 }
1823
1824 /**
1825  * camel_folder_change_info_add_source_list:
1826  * @info: 
1827  * @list: 
1828  * 
1829  * Add a list of source uid's for generating a changeset.
1830  **/
1831 void
1832 camel_folder_change_info_add_source_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1833 {
1834         struct _CamelFolderChangeInfoPrivate *p;
1835         int i;
1836         
1837         g_assert(info != NULL);
1838         g_assert(list != NULL);
1839         
1840         p = info->priv;
1841
1842         if (p->uid_source == NULL)
1843                 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1844
1845         for (i=0;i<list->len;i++) {
1846                 char *uid = list->pdata[i];
1847
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);
1850         }
1851 }
1852
1853 /**
1854  * camel_folder_change_info_add_update:
1855  * @info: 
1856  * @uid: 
1857  * 
1858  * Add a uid from the updated list, used to generate a changeset diff.
1859  **/
1860 void
1861 camel_folder_change_info_add_update(CamelFolderChangeInfo *info, const char *uid)
1862 {
1863         struct _CamelFolderChangeInfoPrivate *p;
1864         char *key;
1865         int value;
1866         
1867         g_assert(info != NULL);
1868         
1869         p = info->priv;
1870         
1871         if (p->uid_source == NULL) {
1872                 camel_folder_change_info_add_uid(info, uid);
1873                 return;
1874         }
1875
1876         if (g_hash_table_lookup_extended(p->uid_source, uid, (void **)&key, (void **)&value)) {
1877                 g_hash_table_remove(p->uid_source, key);
1878         } else {
1879                 camel_folder_change_info_add_uid(info, uid);
1880         }
1881 }
1882
1883 /**
1884  * camel_folder_change_info_add_update_list:
1885  * @info: 
1886  * @list: 
1887  * 
1888  * Add a list of uid's from the updated list.
1889  **/
1890 void
1891 camel_folder_change_info_add_update_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1892 {
1893         int i;
1894         
1895         g_assert(info != NULL);
1896         g_assert(list != NULL);
1897         
1898         for (i=0;i<list->len;i++)
1899                 camel_folder_change_info_add_update(info, list->pdata[i]);
1900 }
1901
1902 static void
1903 change_info_remove(char *key, void *value, CamelFolderChangeInfo *info)
1904 {
1905         struct _CamelFolderChangeInfoPrivate *p = info->priv;
1906         GPtrArray *olduids;
1907         char *olduid;
1908
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);
1915                 }
1916                 return;
1917         }
1918
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);
1922 }
1923
1924 /**
1925  * camel_folder_change_info_build_diff:
1926  * @info: 
1927  * 
1928  * Compare the source uid set to the updated uid set and generate the differences
1929  * into the added and removed lists.
1930  **/
1931 void
1932 camel_folder_change_info_build_diff(CamelFolderChangeInfo *info)
1933 {
1934         struct _CamelFolderChangeInfoPrivate *p;
1935         
1936         g_assert(info != NULL);
1937         
1938         p = info->priv;
1939         
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;
1944         }
1945 }
1946
1947 static void
1948 change_info_cat(CamelFolderChangeInfo *info, GPtrArray *source, void (*add)(CamelFolderChangeInfo *info, const char *uid))
1949 {
1950         int i;
1951
1952         for (i=0;i<source->len;i++)
1953                 add(info, source->pdata[i]);
1954 }
1955
1956 /**
1957  * camel_folder_change_info_cat:
1958  * @info: 
1959  * @source: 
1960  * 
1961  * Concatenate one change info onto antoher.  Can be used to copy
1962  * them too.
1963  **/
1964 void
1965 camel_folder_change_info_cat(CamelFolderChangeInfo *info, CamelFolderChangeInfo *source)
1966 {
1967         g_assert(info != NULL);
1968         g_assert(source != NULL);
1969         
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);
1974 }
1975
1976 /**
1977  * camel_folder_change_info_add_uid:
1978  * @info: 
1979  * @uid: 
1980  * 
1981  * Add a new uid to the changeinfo.
1982  **/
1983 void
1984 camel_folder_change_info_add_uid(CamelFolderChangeInfo *info, const char *uid)
1985 {
1986         struct _CamelFolderChangeInfoPrivate *p;
1987         GPtrArray *olduids;
1988         char *olduid;
1989         
1990         g_assert(info != NULL);
1991         
1992         p = info->priv;
1993         
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);
2001                 }
2002                 return;
2003         }
2004
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);
2008 }
2009
2010 /**
2011  * camel_folder_change_info_remove_uid:
2012  * @info: 
2013  * @uid: 
2014  * 
2015  * Add a uid to the removed uid list.
2016  **/
2017 void
2018 camel_folder_change_info_remove_uid(CamelFolderChangeInfo *info, const char *uid)
2019 {
2020         struct _CamelFolderChangeInfoPrivate *p;
2021         GPtrArray *olduids;
2022         char *olduid;
2023         
2024         g_assert(info != NULL);
2025         
2026         p = info->priv;
2027         
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);
2034                 }
2035                 return;
2036         }
2037
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);
2041 }
2042
2043 /**
2044  * camel_folder_change_info_change_uid:
2045  * @info: 
2046  * @uid: 
2047  * 
2048  * Add a uid to the changed uid list.
2049  **/
2050 void
2051 camel_folder_change_info_change_uid(CamelFolderChangeInfo *info, const char *uid)
2052 {
2053         struct _CamelFolderChangeInfoPrivate *p;
2054         GPtrArray *olduids;
2055         char *olduid;
2056         
2057         g_assert(info != NULL);
2058         
2059         p = info->priv;
2060         
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 */
2063                 return;
2064         }
2065
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);
2069 }
2070
2071 void
2072 camel_folder_change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2073 {
2074         struct _CamelFolderChangeInfoPrivate *p;
2075         GPtrArray *olduids;
2076         char *olduid;
2077         
2078         g_assert(info != NULL);
2079         
2080         p = info->priv;
2081
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);
2085         }
2086         g_ptr_array_add(info->uid_recent, olduid);
2087 }
2088
2089 /**
2090  * camel_folder_change_info_changed:
2091  * @info: 
2092  * 
2093  * Return true if the changeset contains any changes.
2094  *
2095  * Return Value:
2096  **/
2097 gboolean
2098 camel_folder_change_info_changed(CamelFolderChangeInfo *info)
2099 {
2100         g_assert(info != NULL);
2101         
2102         return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len || info->uid_recent->len);
2103 }
2104
2105 /**
2106  * camel_folder_change_info_clear:
2107  * @info: 
2108  * 
2109  * Empty out the change info; called after changes have been processed.
2110  **/
2111 void
2112 camel_folder_change_info_clear(CamelFolderChangeInfo *info)
2113 {
2114         struct _CamelFolderChangeInfoPrivate *p;
2115         
2116         g_assert(info != NULL);
2117         
2118         p = info->priv;
2119         
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;
2127         }
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);
2131 }
2132
2133 /**
2134  * camel_folder_change_info_free:
2135  * @info: 
2136  * 
2137  * Free memory associated with the folder change info lists.
2138  **/
2139 void
2140 camel_folder_change_info_free(CamelFolderChangeInfo *info)
2141 {
2142         struct _CamelFolderChangeInfoPrivate *p;
2143
2144         g_assert(info != NULL);
2145         
2146         p = info->priv;
2147         
2148         if (p->uid_source)
2149                 g_hash_table_destroy(p->uid_source);
2150
2151         g_hash_table_destroy(p->uid_stored);
2152         e_mempool_destroy(p->uid_pool);
2153         g_free(p);
2154
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);
2159         g_free(info);
2160 }