Fix FSF address (Tobias Mueller, #470445)
[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-2003 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 Lesser 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30
31 #include <glib.h>
32 #include <glib/gi18n-lib.h>
33
34 #include <libedataserver/e-memory.h>
35
36 #include "camel-debug.h"
37 #include "camel-exception.h"
38 #include "camel-filter-driver.h"
39 #include "camel-folder.h"
40 #include "camel-mime-message.h"
41 #include "camel-operation.h"
42 #include "camel-private.h"
43 #include "camel-session.h"
44 #include "camel-store.h"
45 #include "camel-vtrash-folder.h"
46
47 #define d(x)
48 #define w(x)
49
50 extern int camel_verbose_debug;
51
52 static CamelObjectClass *parent_class = NULL;
53
54 /* Returns the class for a CamelFolder */
55 #define CF_CLASS(so) ((CamelFolderClass *)((CamelObject *)(so))->klass)
56
57 static void camel_folder_finalize (CamelObject *object);
58
59 static void refresh_info (CamelFolder *folder, CamelException *ex);
60
61 static void folder_sync (CamelFolder *folder, gboolean expunge,
62                          CamelException *ex);
63
64 static const char *get_name (CamelFolder *folder);
65 static const char *get_full_name (CamelFolder *folder);
66 static CamelStore *get_parent_store   (CamelFolder *folder);
67
68 static guint32 get_permanent_flags(CamelFolder *folder);
69 static guint32 get_message_flags(CamelFolder *folder, const char *uid);
70 static gboolean set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
71 static gboolean get_message_user_flag(CamelFolder *folder, const char *uid, const char *name);
72 static void set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value);
73 static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name);
74 static void set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value);
75
76 static int get_message_count(CamelFolder *folder);
77
78 static void expunge             (CamelFolder *folder,
79                                  CamelException *ex);
80 static int folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args);
81 static void folder_free(CamelObject *o, guint32 tag, void *val);
82
83
84 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
85                             const CamelMessageInfo *info, char **appended_uid,
86                             CamelException *ex);
87
88
89 static GPtrArray        *get_uids            (CamelFolder *folder);
90 static void              free_uids           (CamelFolder *folder,
91                                               GPtrArray *array);
92 static GPtrArray        *get_summary         (CamelFolder *folder);
93 static void              free_summary        (CamelFolder *folder,
94                                               GPtrArray *array);
95
96 static CamelMimeMessage *get_message         (CamelFolder *folder, const gchar *uid, CamelException *ex);
97
98 static CamelMessageInfo *get_message_info    (CamelFolder *folder, const char *uid);
99 static void              free_message_info   (CamelFolder *folder, CamelMessageInfo *info);
100 static void              ref_message_info    (CamelFolder *folder, CamelMessageInfo *info);
101
102 static GPtrArray      *search_by_expression  (CamelFolder *folder, const char *exp, CamelException *ex);
103 static GPtrArray      *search_by_uids        (CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex);
104 static void            search_free           (CamelFolder * folder, GPtrArray *result);
105
106 static void            transfer_messages_to  (CamelFolder *source, GPtrArray *uids, CamelFolder *dest,
107                                               GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
108
109 static void            delete                (CamelFolder *folder);
110 static void            folder_rename         (CamelFolder *folder, const char *new);
111
112 static void            freeze                (CamelFolder *folder);
113 static void            thaw                  (CamelFolder *folder);
114 static gboolean        is_frozen             (CamelFolder *folder);
115
116 static gboolean        folder_changed        (CamelObject *object,
117                                               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->append_message = append_message;
135         camel_folder_class->get_permanent_flags = get_permanent_flags;
136         camel_folder_class->get_message_flags = get_message_flags;
137         camel_folder_class->set_message_flags = set_message_flags;
138         camel_folder_class->get_message_user_flag = get_message_user_flag;
139         camel_folder_class->set_message_user_flag = set_message_user_flag;
140         camel_folder_class->get_message_user_tag = get_message_user_tag;
141         camel_folder_class->set_message_user_tag = set_message_user_tag;
142         camel_folder_class->get_message = get_message;
143         camel_folder_class->get_uids = get_uids;
144         camel_folder_class->free_uids = free_uids;
145         camel_folder_class->get_summary = get_summary;
146         camel_folder_class->free_summary = free_summary;
147         camel_folder_class->search_by_expression = search_by_expression;
148         camel_folder_class->search_by_uids = search_by_uids;
149         camel_folder_class->search_free = search_free;
150         camel_folder_class->get_message_info = get_message_info;
151         camel_folder_class->ref_message_info = ref_message_info;
152         camel_folder_class->free_message_info = free_message_info;
153         camel_folder_class->transfer_messages_to = transfer_messages_to;
154         camel_folder_class->delete = delete;
155         camel_folder_class->rename = folder_rename;
156         camel_folder_class->freeze = freeze;
157         camel_folder_class->thaw = thaw;
158         camel_folder_class->is_frozen = is_frozen;
159
160         /* virtual method overload */
161         camel_object_class->getv = folder_getv;
162         camel_object_class->free = folder_free;
163
164         /* events */
165         camel_object_class_add_event(camel_object_class, "folder_changed", folder_changed);
166         camel_object_class_add_event(camel_object_class, "deleted", NULL);
167         camel_object_class_add_event(camel_object_class, "renamed", NULL);
168 }
169
170 static void
171 camel_folder_init (gpointer object, gpointer klass)
172 {
173         CamelFolder *folder = object;
174
175         folder->priv = g_malloc0(sizeof(*folder->priv));
176         folder->priv->frozen = 0;
177         folder->priv->changed_frozen = camel_folder_change_info_new();
178         g_static_rec_mutex_init(&folder->priv->lock);
179         g_static_mutex_init(&folder->priv->change_lock);
180 }
181
182 static void
183 camel_folder_finalize (CamelObject *object)
184 {
185         CamelFolder *camel_folder = CAMEL_FOLDER (object);
186         struct _CamelFolderPrivate *p = camel_folder->priv;
187
188         g_free(camel_folder->name);
189         g_free(camel_folder->full_name);
190         g_free(camel_folder->description);
191
192         if (camel_folder->parent_store)
193                 camel_object_unref (camel_folder->parent_store);
194
195         if (camel_folder->summary)
196                 camel_object_unref (camel_folder->summary);
197
198         camel_folder_change_info_free(p->changed_frozen);
199         
200         g_static_rec_mutex_free(&p->lock);
201         g_static_mutex_free(&p->change_lock);
202         
203         g_free(p);
204 }
205
206 CamelType
207 camel_folder_get_type (void)
208 {
209         static CamelType camel_folder_type = CAMEL_INVALID_TYPE;
210
211         if (camel_folder_type == CAMEL_INVALID_TYPE)    {
212                 camel_folder_type = camel_type_register (CAMEL_OBJECT_TYPE, "CamelFolder",
213                                                          sizeof (CamelFolder),
214                                                          sizeof (CamelFolderClass),
215                                                          (CamelObjectClassInitFunc) camel_folder_class_init,
216                                                          NULL,
217                                                          (CamelObjectInitFunc) camel_folder_init,
218                                                          (CamelObjectFinalizeFunc) camel_folder_finalize );
219         }
220
221         return camel_folder_type;
222 }
223
224
225 /**
226  * camel_folder_construct:
227  * @folder: a #CamelFolder object to construct
228  * @parent_store: parent #CamelStore object of the folder
229  * @full_name: full name of the folder
230  * @name: short name of the folder
231  *
232  * Initalizes the folder by setting the parent store and name.
233  **/
234 void
235 camel_folder_construct (CamelFolder *folder, CamelStore *parent_store,
236                         const char *full_name, const char *name)
237 {
238         g_return_if_fail (CAMEL_IS_FOLDER (folder));
239         g_return_if_fail (CAMEL_IS_STORE (parent_store));
240         g_return_if_fail (folder->parent_store == NULL);
241         g_return_if_fail (folder->name == NULL);
242
243         folder->parent_store = parent_store;
244         if (parent_store)
245                 camel_object_ref(parent_store);
246
247         folder->name = g_strdup (name);
248         folder->full_name = g_strdup (full_name);
249 }
250
251
252 static void
253 folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
254 {
255         w(g_warning ("CamelFolder::sync not implemented for `%s'",
256                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
257 }
258
259
260 /**
261  * camel_folder_sync:
262  * @folder: a #CamelFolder object
263  * @expunge: whether or not to expunge deleted messages
264  * @ex: a #CamelException
265  *
266  * Sync changes made to a folder to its backing store, possibly
267  * expunging deleted messages as well.
268  **/
269 void
270 camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
271 {
272         g_return_if_fail (CAMEL_IS_FOLDER (folder));
273
274         CAMEL_FOLDER_REC_LOCK(folder, lock);
275         
276         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
277                 CF_CLASS (folder)->sync (folder, expunge, ex);
278         
279         CAMEL_FOLDER_REC_UNLOCK(folder, lock);
280 }
281
282
283 static void
284 refresh_info (CamelFolder *folder, CamelException *ex)
285 {
286         /* No op */
287 }
288
289
290 /**
291  * camel_folder_refresh_info:
292  * @folder: a #CamelFolder object
293  * @ex: a #CamelException
294  *
295  * Updates a folder's summary to be in sync with its backing store.
296  **/
297 void
298 camel_folder_refresh_info (CamelFolder *folder, CamelException *ex)
299 {
300         g_return_if_fail (CAMEL_IS_FOLDER (folder));
301
302         CF_CLASS (folder)->refresh_info (folder, ex);
303 }
304
305 static int
306 folder_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
307 {
308         CamelFolder *folder = (CamelFolder *)object;
309         int i;
310         guint32 tag;
311         int unread = -1, deleted = 0, junked = 0, visible = 0, count = -1;
312
313         for (i=0;i<args->argc;i++) {
314                 CamelArgGet *arg = &args->argv[i];
315
316                 tag = arg->tag;
317
318                 switch (tag & CAMEL_ARG_TAG) {
319                         /* CamelObject args */
320                 case CAMEL_OBJECT_ARG_DESCRIPTION:
321                         if (folder->description == NULL)
322                                 folder->description = g_strdup_printf("%s", folder->full_name);
323                         *arg->ca_str = folder->description;
324                         break;
325
326                         /* CamelFolder args */
327                 case CAMEL_FOLDER_ARG_NAME:
328                         *arg->ca_str = folder->name;
329                         break;
330                 case CAMEL_FOLDER_ARG_FULL_NAME:
331                         *arg->ca_str = folder->full_name;
332                         break;
333                 case CAMEL_FOLDER_ARG_STORE:
334                         *arg->ca_object = folder->parent_store;
335                         break;
336                 case CAMEL_FOLDER_ARG_PERMANENTFLAGS:
337                         *arg->ca_int = folder->permanent_flags;
338                         break;
339                 case CAMEL_FOLDER_ARG_TOTAL:
340                         *arg->ca_int = camel_folder_summary_count(folder->summary);
341                         break;
342                 case CAMEL_FOLDER_ARG_UNREAD:
343                 case CAMEL_FOLDER_ARG_DELETED:
344                 case CAMEL_FOLDER_ARG_JUNKED:
345                 case CAMEL_FOLDER_ARG_VISIBLE:
346                         /* This is so we can get the values atomically, and also so we can calculate them only once */
347                         if (unread == -1) {
348                                 int j;
349                                 CamelMessageInfo *info;
350
351                                 /* TODO: Locking? */
352                                 unread = 0;
353                                 count = camel_folder_summary_count(folder->summary);
354                                 for (j=0; j<count; j++) {
355                                         if ((info = camel_folder_summary_index(folder->summary, j))) {
356                                                 guint32 flags = camel_message_info_flags(info);
357
358                                                 if ((flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
359                                                         unread++;
360                                                 if (flags & CAMEL_MESSAGE_DELETED)
361                                                         deleted++;
362                                                 if (flags & CAMEL_MESSAGE_JUNK)
363                                                         junked++;
364                                                 if ((flags & (CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
365                                                         visible++;
366                                                 camel_message_info_free(info);
367                                         }
368                                 }
369                         }
370
371                         switch (tag & CAMEL_ARG_TAG) {
372                         case CAMEL_FOLDER_ARG_UNREAD:
373                                 count = unread;
374                                 break;
375                         case CAMEL_FOLDER_ARG_DELETED:
376                                 count = deleted;
377                                 break;
378                         case CAMEL_FOLDER_ARG_JUNKED:
379                                 count = junked;
380                                 break;
381                         case CAMEL_FOLDER_ARG_VISIBLE:
382                                 count = visible;
383                                 break;
384                         }
385
386                         *arg->ca_int = count;
387                         break;
388                 case CAMEL_FOLDER_ARG_UID_ARRAY: {
389                         int j;
390                         CamelMessageInfo *info;
391                         GPtrArray *array;
392
393                         count = camel_folder_summary_count(folder->summary);
394                         array = g_ptr_array_new();
395                         g_ptr_array_set_size(array, count);
396                         for (j=0; j<count; j++) {
397                                 if ((info = camel_folder_summary_index(folder->summary, j))) {
398                                         array->pdata[i] = g_strdup(camel_message_info_uid(info));
399                                         camel_message_info_free(info);
400                                 }
401                         }
402                         *arg->ca_ptr = array;
403                         break; }
404                 case CAMEL_FOLDER_ARG_INFO_ARRAY:
405                         *arg->ca_ptr = camel_folder_summary_array(folder->summary);
406                         break;
407                 case CAMEL_FOLDER_ARG_PROPERTIES:
408                         *arg->ca_ptr = NULL;
409                         break;
410                 default:
411                         continue;
412                 }
413
414                 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
415         }
416
417         return parent_class->getv(object, ex, args);
418 }
419
420 static void
421 folder_free(CamelObject *o, guint32 tag, void *val)
422 {
423         CamelFolder *folder = (CamelFolder *)o;
424
425         switch (tag & CAMEL_ARG_TAG) {
426         case CAMEL_FOLDER_ARG_UID_ARRAY: {
427                 GPtrArray *array = val;
428                 int i;
429
430                 for (i=0; i<array->len; i++)
431                         g_free(array->pdata[i]);
432                 g_ptr_array_free(array, TRUE);
433                 break; }
434         case CAMEL_FOLDER_ARG_INFO_ARRAY:
435                 camel_folder_summary_array_free(folder->summary, val);
436                 break;
437         case CAMEL_FOLDER_ARG_PROPERTIES:
438                 g_slist_free(val);
439                 break;
440         default:
441                 parent_class->free(o, tag, val);
442         }
443 }
444
445 static const char *
446 get_name (CamelFolder *folder)
447 {
448         return folder->name;
449 }
450
451
452 /**
453  * camel_folder_get_name:
454  * @folder: a #CamelFolder object
455  *
456  * Get the (short) name of the folder. The fully qualified name
457  * can be obtained with the #camel_folder_get_full_name method.
458  *
459  * Returns the short name of the folder
460  **/
461 const char *
462 camel_folder_get_name (CamelFolder *folder)
463 {
464         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
465
466         return CF_CLASS (folder)->get_name (folder);
467 }
468
469
470 static const char *
471 get_full_name (CamelFolder *folder)
472 {
473         return folder->full_name;
474 }
475
476
477 /**
478  * camel_folder_get_full_name:
479  * @folder: a #CamelFolder object
480  *
481  * Get the full name of the folder.
482  *
483  * Returns the full name of the folder
484  **/
485 const char *
486 camel_folder_get_full_name (CamelFolder *folder)
487 {
488         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
489
490         return CF_CLASS (folder)->get_full_name (folder);
491 }
492
493
494 static CamelStore *
495 get_parent_store (CamelFolder * folder)
496 {
497         return folder->parent_store;
498 }
499
500
501 /**
502  * camel_folder_get_parent_store:
503  * @folder: a #CamelFolder object
504  *
505  * Returns the parent #CamelStore of the folder
506  **/
507 CamelStore *
508 camel_folder_get_parent_store (CamelFolder *folder)
509 {
510         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
511
512         return CF_CLASS (folder)->get_parent_store (folder);
513 }
514
515
516 static void
517 expunge (CamelFolder *folder, CamelException *ex)
518 {
519         w(g_warning ("CamelFolder::expunge not implemented for `%s'",
520                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
521 }
522
523
524 /**
525  * camel_folder_expunge:
526  * @folder: a #CamelFolder object
527  * @ex: a #CamelException
528  *
529  * Delete messages which have been marked as "DELETED"
530  **/
531 void
532 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
533 {
534         g_return_if_fail (CAMEL_IS_FOLDER (folder));
535         
536         CAMEL_FOLDER_REC_LOCK(folder, lock);
537         
538         if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
539                 CF_CLASS (folder)->expunge (folder, ex);
540         
541         CAMEL_FOLDER_REC_UNLOCK(folder, lock);
542 }
543
544 static int
545 get_message_count (CamelFolder *folder)
546 {
547         g_return_val_if_fail(folder->summary != NULL, -1);
548
549         return camel_folder_summary_count(folder->summary);
550 }
551
552
553 /**
554  * camel_folder_get_message_count:
555  * @folder: a #CamelFolder object
556  *
557  * Returns the number of messages in the folder, or %-1 if unknown
558  **/
559 int
560 camel_folder_get_message_count (CamelFolder *folder)
561 {
562         int ret;
563
564         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
565
566         ret = CF_CLASS (folder)->get_message_count (folder);
567
568         return ret;
569 }
570
571
572 /**
573  * camel_folder_get_unread_message_count:
574  * @folder: a #CamelFolder object
575  *
576  * DEPRECATED: use #camel_object_get instead.
577  *
578  * Returns the number of unread messages in the folder, or %-1 if
579  * unknown
580  **/
581 int
582 camel_folder_get_unread_message_count (CamelFolder *folder)
583 {
584         int count = -1;
585
586         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
587         
588         camel_object_get(folder, NULL, CAMEL_FOLDER_UNREAD, &count, 0);
589
590         return count;
591 }
592
593
594 /**
595  * camel_folder_get_deleted_message_count:
596  * @folder: a #CamelFolder object
597  *
598  * Returns the number of deleted messages in the folder, or %-1 if
599  * unknown
600  **/
601 int
602 camel_folder_get_deleted_message_count (CamelFolder *folder)
603 {
604         int count = -1;
605
606         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
607
608         camel_object_get(folder, NULL, CAMEL_FOLDER_DELETED, &count, 0);
609
610         return count;
611 }
612
613 static void
614 append_message (CamelFolder *folder, CamelMimeMessage *message,
615                 const CamelMessageInfo *info, char **appended_uid,
616                 CamelException *ex)
617 {
618         camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
619                               _("Unsupported operation: append message: for %s"),
620                               camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
621         
622         w(g_warning ("CamelFolder::append_message not implemented for `%s'",
623                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
624         
625         return;
626
627 }
628
629
630 /**
631  * camel_folder_append_message:
632  * @folder: a #CamelFolder object
633  * @message: a #CamelMimeMessage object
634  * @info: a #CamelMessageInfo with additional flags/etc to set on
635  * new message, or %NULL
636  * @appended_uid: if non-%NULL, the UID of the appended message will
637  * be returned here, if it is known.
638  * @ex: a #CamelException
639  *
640  * Append @message to @folder. Only the flag and tag data from @info
641  * are used. If @info is %NULL, no flags or tags will be set.
642  **/
643 void
644 camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message,
645                              const CamelMessageInfo *info, char **appended_uid,
646                              CamelException *ex)
647 {
648         g_return_if_fail (CAMEL_IS_FOLDER (folder));
649
650         CAMEL_FOLDER_REC_LOCK(folder, lock);
651
652         CF_CLASS (folder)->append_message (folder, message, info, appended_uid, ex);
653
654         CAMEL_FOLDER_REC_UNLOCK(folder, lock);
655 }
656
657
658 static guint32
659 get_permanent_flags (CamelFolder *folder)
660 {
661         return folder->permanent_flags;
662 }
663
664
665 /**
666  * camel_folder_get_permanent_flags:
667  * @folder: a #CamelFolder object
668  *
669  * Returns the set of #CamelMessageFlags that can be permanently
670  * stored on a message between sessions. If it includes
671  * #CAMEL_FLAG_USER, then user-defined flags will be remembered.
672  **/
673 guint32
674 camel_folder_get_permanent_flags (CamelFolder *folder)
675 {
676         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
677
678         return CF_CLASS (folder)->get_permanent_flags (folder);
679 }
680
681 static guint32
682 get_message_flags(CamelFolder *folder, const char *uid)
683 {
684         CamelMessageInfo *info;
685         guint32 flags;
686
687         g_return_val_if_fail(folder->summary != NULL, 0);
688
689         info = camel_folder_summary_uid(folder->summary, uid);
690         if (info == NULL)
691                 return 0;
692
693         flags = camel_message_info_flags(info);
694         camel_message_info_free(info);
695
696         return flags;
697 }
698
699
700 /**
701  * camel_folder_get_message_flags:
702  * @folder: a #CamelFolder object
703  * @uid: the UID of a message in @folder
704  *
705  * Deprecated: Use #camel_folder_get_message_info instead.
706  *
707  * Returns the #CamelMessageFlags that are set on the indicated
708  * message.
709  **/
710 guint32
711 camel_folder_get_message_flags (CamelFolder *folder, const char *uid)
712 {
713         guint32 ret;
714
715         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
716
717         ret = CF_CLASS (folder)->get_message_flags (folder, uid);
718
719         return ret;
720 }
721
722 static gboolean
723 set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
724 {
725         CamelMessageInfo *info;
726         int res;
727
728         g_return_val_if_fail(folder->summary != NULL, FALSE);
729
730         info = camel_folder_summary_uid(folder->summary, uid);
731         if (info == NULL)
732                 return FALSE;
733
734         res = camel_message_info_set_flags(info, flags, set);
735         camel_message_info_free(info);
736
737         return res;
738 }
739
740
741 /**
742  * camel_folder_set_message_flags:
743  * @folder: a #CamelFolder object
744  * @uid: the UID of a message in @folder
745  * @flags: a set of #CamelMessageFlag values to set
746  * @set: the mask of values in @flags to use.
747  *
748  * Sets those flags specified by @flags to the values specified by @set
749  * on the indicated message. (This may or may not persist after the
750  * folder or store is closed. See #camel_folder_get_permanent_flags)
751  *
752  * E.g. to set the deleted flag and clear the draft flag, use
753  * #camel_folder_set_message_flags(folder, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_DRAFT, CAMEL_MESSAGE_DELETED);
754  *
755  * DEPRECATED: Use #camel_message_info_set_flags on the message info directly
756  * (when it works)
757  *
758  * Returns %TRUE if the flags were changed or %FALSE otherwise
759  **/
760 gboolean
761 camel_folder_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
762 {
763         g_return_val_if_fail(CAMEL_IS_FOLDER(folder), FALSE);
764
765         if ((flags & (CAMEL_MESSAGE_JUNK|CAMEL_MESSAGE_JUNK_LEARN)) == CAMEL_MESSAGE_JUNK) {
766                 flags |= CAMEL_MESSAGE_JUNK_LEARN;
767                 set &= ~CAMEL_MESSAGE_JUNK_LEARN;
768         }
769
770         return CF_CLASS(folder)->set_message_flags(folder, uid, flags, set);
771 }
772
773 static gboolean
774 get_message_user_flag(CamelFolder *folder, const char *uid, const char *name)
775 {
776         CamelMessageInfo *info;
777         gboolean ret;
778
779         g_return_val_if_fail(folder->summary != NULL, FALSE);
780
781         info = camel_folder_summary_uid(folder->summary, uid);
782         if (info == NULL)
783                 return FALSE;
784
785         ret = camel_message_info_user_flag(info, name);
786         camel_message_info_free(info);
787
788         return ret;
789 }
790
791
792 /**
793  * camel_folder_get_message_user_flag:
794  * @folder: a #CamelFolder object
795  * @uid: the UID of a message in @folder
796  * @name: the name of a user flag
797  *
798  * DEPRECATED: Use #camel_message_info_get_user_flag on the message
799  * info directly
800  *
801  * Returns %TRUE if the given user flag is set on the message or
802  * %FALSE otherwise
803  **/
804 gboolean
805 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
806                                     const char *name)
807 {
808         gboolean ret;
809
810         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
811
812         ret = CF_CLASS (folder)->get_message_user_flag (folder, uid, name);
813
814         return ret;
815 }
816
817 static void
818 set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
819 {
820         CamelMessageInfo *info;
821
822         g_return_if_fail(folder->summary != NULL);
823
824         info = camel_folder_summary_uid(folder->summary, uid);
825         if (info == NULL)
826                 return;
827
828         camel_message_info_set_user_flag(info, name, value);
829         camel_message_info_free(info);
830 }
831
832
833 /**
834  * camel_folder_set_message_user_flag:
835  * @folder: a #CamelFolder object
836  * @uid: the UID of a message in @folder
837  * @name: the name of the user flag to set
838  * @value: the value to set it to
839  *
840  * DEPRECATED: Use #camel_message_info_set_user_flag on the
841  * #CamelMessageInfo directly (when it works)
842  *
843  * Sets the user flag specified by @name to the value specified by @value
844  * on the indicated message. (This may or may not persist after the
845  * folder or store is closed. See #camel_folder_get_permanent_flags)
846  **/
847 void
848 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
849                                     const char *name, gboolean value)
850 {
851         g_return_if_fail (CAMEL_IS_FOLDER (folder));
852
853         CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value);
854 }
855
856 static const char *
857 get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
858 {
859         CamelMessageInfo *info;
860         const char *ret;
861
862         g_return_val_if_fail(folder->summary != NULL, NULL);
863
864         info = camel_folder_summary_uid(folder->summary, uid);
865         if (info == NULL)
866                 return NULL;
867
868         ret = camel_message_info_user_tag(info, name);
869         camel_message_info_free(info);
870
871         return ret;
872 }
873
874
875 /**
876  * camel_folder_get_message_user_tag:
877  * @folder: a #CamelFolder object
878  * @uid: the UID of a message in @folder
879  * @name: the name of a user tag
880  *
881  * DEPRECATED: Use #camel_message_info_get_user_tag on the
882  * #CamelMessageInfo directly.
883  *
884  * Returns the value of the user tag
885  **/
886 const char *
887 camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid,  const char *name)
888 {
889         const char *ret;
890
891         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
892
893         /* FIXME: should duplicate string */
894         ret = CF_CLASS (folder)->get_message_user_tag (folder, uid, name);
895
896         return ret;
897 }
898
899 static void
900 set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
901 {
902         CamelMessageInfo *info;
903
904         g_return_if_fail(folder->summary != NULL);
905
906         info = camel_folder_summary_uid(folder->summary, uid);
907         if (info == NULL)
908                 return;
909
910         camel_message_info_set_user_tag(info, name, value);
911         camel_message_info_free(info);
912 }
913
914
915 /**
916  * camel_folder_set_message_user_tag:
917  * @folder: a #CamelFolder object
918  * @uid: the UID of a message in @folder
919  * @name: the name of the user tag to set
920  * @value: the value to set it to
921  *
922  * DEPRECATED: Use #camel_message_info_set_user_tag on the
923  * #CamelMessageInfo directly (when it works).
924  *
925  * Sets the user tag specified by @name to the value specified by @value
926  * on the indicated message. (This may or may not persist after the
927  * folder or store is closed. See #camel_folder_get_permanent_flags)
928  **/
929 void
930 camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const char *name, const char *value)
931 {
932         g_return_if_fail (CAMEL_IS_FOLDER (folder));
933
934         CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value);
935 }
936
937 static CamelMessageInfo *
938 get_message_info (CamelFolder *folder, const char *uid)
939 {
940         g_return_val_if_fail(folder->summary != NULL, NULL);
941
942         return camel_folder_summary_uid(folder->summary, uid);
943 }
944
945
946 /**
947  * camel_folder_get_message_info:
948  * @folder: a #CamelFolder object
949  * @uid: the uid of a message
950  *
951  * Retrieve the #CamelMessageInfo for the specified @uid.  This return
952  * must be freed using #camel_folder_free_message_info.
953  *
954  * Returns the summary information for the indicated message, or %NULL
955  * if the uid does not exist
956  **/
957 CamelMessageInfo *
958 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
959 {
960         CamelMessageInfo *ret;
961
962         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
963         g_return_val_if_fail (uid != NULL, NULL);
964
965         ret = CF_CLASS (folder)->get_message_info (folder, uid);
966
967         return ret;
968 }
969
970 static void
971 free_message_info (CamelFolder *folder, CamelMessageInfo *info)
972 {
973         g_return_if_fail(folder->summary != NULL);
974
975         camel_message_info_free(info);
976 }
977
978
979 /**
980  * camel_folder_free_message_info:
981  * @folder: a #CamelFolder object
982  * @info: a #CamelMessageInfo
983  * 
984  * Free (unref) a #CamelMessageInfo, previously obtained with
985  * #camel_folder_get_message_info.
986  **/
987 void
988 camel_folder_free_message_info(CamelFolder *folder, CamelMessageInfo *info)
989 {
990         g_return_if_fail(CAMEL_IS_FOLDER (folder));
991         g_return_if_fail(info != NULL);
992
993         CF_CLASS (folder)->free_message_info(folder, info);
994 }
995
996 static void
997 ref_message_info (CamelFolder *folder, CamelMessageInfo *info)
998 {
999         g_return_if_fail(folder->summary != NULL);
1000
1001         camel_message_info_ref(info);
1002 }
1003
1004
1005 /**
1006  * camel_folder_ref_message_info:
1007  * @folder: a #CamelFolder object
1008  * @info: a #CamelMessageInfo
1009  * 
1010  * DEPRECATED: Use #camel_message_info_ref directly.
1011  *
1012  * Ref a #CamelMessageInfo, previously obtained with
1013  * #camel_folder_get_message_info.
1014  **/
1015 void
1016 camel_folder_ref_message_info(CamelFolder *folder, CamelMessageInfo *info)
1017 {
1018         g_return_if_fail(CAMEL_IS_FOLDER (folder));
1019         g_return_if_fail(info != NULL);
1020
1021         CF_CLASS (folder)->ref_message_info(folder, info);
1022 }
1023
1024
1025 /* TODO: is this function required anyway? */
1026 /**
1027  * camel_folder_has_summary_capability:
1028  * @folder: a #CamelFolder object
1029  *
1030  * Get whether or not the folder has a summary.
1031  *
1032  * Returns %TRUE if a summary is available or %FALSE otherwise
1033  **/
1034 gboolean
1035 camel_folder_has_summary_capability (CamelFolder *folder)
1036 {
1037         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1038
1039         return folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
1040 }
1041
1042
1043 /* UIDs stuff */
1044
1045 static CamelMimeMessage *
1046 get_message (CamelFolder *folder, const char *uid, CamelException *ex)
1047 {
1048         w(g_warning ("CamelFolder::get_message not implemented for `%s'",
1049                      camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1050         
1051         return NULL;
1052 }
1053
1054
1055 /**
1056  * camel_folder_get_message:
1057  * @folder: a #CamelFolder object
1058  * @uid: the UID
1059  * @ex: a #CamelException
1060  *
1061  * Get a message from its UID in the folder.
1062  *
1063  * Returns a #CamelMimeMessage corresponding to @uid
1064  **/
1065 CamelMimeMessage *
1066 camel_folder_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
1067 {
1068         CamelMimeMessage *ret;
1069
1070         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1071
1072         CAMEL_FOLDER_REC_LOCK(folder, lock);
1073
1074         ret = CF_CLASS (folder)->get_message (folder, uid, ex);
1075
1076         CAMEL_FOLDER_REC_UNLOCK(folder, lock);
1077
1078         if (ret && camel_debug_start(":folder")) {
1079                 printf("CamelFolder:get_message('%s', '%s') =\n", folder->full_name, uid);
1080                 camel_mime_message_dump(ret, FALSE);
1081                 camel_debug_end();
1082         }
1083
1084         return ret;
1085 }
1086
1087 static GPtrArray *
1088 get_uids(CamelFolder *folder)
1089 {
1090         GPtrArray *array;
1091         int i, j, count;
1092
1093         array = g_ptr_array_new();
1094
1095         g_return_val_if_fail(folder->summary != NULL, array);
1096
1097         count = camel_folder_summary_count(folder->summary);
1098         g_ptr_array_set_size(array, count);
1099         for (i = 0, j = 0; i < count; i++) {
1100                 CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i);
1101                 
1102                 if (info) {
1103                         array->pdata[j++] = g_strdup (camel_message_info_uid (info));
1104                         camel_message_info_free(info);
1105                 }
1106         }
1107         
1108         g_ptr_array_set_size (array, j);
1109         
1110         return array;
1111 }
1112
1113
1114 /**
1115  * camel_folder_get_uids:
1116  * @folder: a #CamelFolder object
1117  *
1118  * Get the list of UIDs available in a folder. This routine is useful
1119  * for finding what messages are available when the folder does not
1120  * support summaries. The returned array should not be modified, and
1121  * must be freed by passing it to #camel_folder_free_uids.
1122  *
1123  * Returns a GPtrArray of UIDs corresponding to the messages available
1124  * in the folder
1125  **/
1126 GPtrArray *
1127 camel_folder_get_uids (CamelFolder *folder)
1128 {
1129         GPtrArray *ret;
1130
1131         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1132
1133         ret = CF_CLASS (folder)->get_uids (folder);
1134
1135         return ret;
1136 }
1137
1138 static void
1139 free_uids (CamelFolder *folder, GPtrArray *array)
1140 {
1141         int i;
1142
1143         for (i=0; i<array->len; i++)
1144                 g_free(array->pdata[i]);
1145         g_ptr_array_free(array, TRUE);
1146 }
1147
1148
1149 /**
1150  * camel_folder_free_uids:
1151  * @folder: a #CamelFolder object
1152  * @array: the array of uids to free
1153  *
1154  * Frees the array of UIDs returned by #camel_folder_get_uids.
1155  **/
1156 void
1157 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
1158 {
1159         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1160
1161         CF_CLASS (folder)->free_uids (folder, array);
1162 }
1163
1164 static GPtrArray *
1165 get_summary(CamelFolder *folder)
1166 {
1167         g_assert(folder->summary != NULL);
1168
1169         return camel_folder_summary_array(folder->summary);
1170 }
1171
1172
1173 /**
1174  * camel_folder_get_summary:
1175  * @folder: a #CamelFolder object
1176  *
1177  * This returns the summary information for the folder. This array
1178  * should not be modified, and must be freed with
1179  * #camel_folder_free_summary.
1180  *
1181  * Returns an array of #CamelMessageInfo
1182  **/
1183 GPtrArray *
1184 camel_folder_get_summary (CamelFolder *folder)
1185 {
1186         GPtrArray *ret;
1187
1188         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1189
1190         ret = CF_CLASS (folder)->get_summary (folder);
1191
1192         return ret;
1193 }
1194
1195 static void
1196 free_summary(CamelFolder *folder, GPtrArray *summary)
1197 {
1198         g_assert(folder->summary != NULL);
1199
1200         camel_folder_summary_array_free(folder->summary, summary);
1201 }
1202
1203
1204 /**
1205  * camel_folder_free_summary:
1206  * @folder: a #CamelFolder object
1207  * @array: the summary array to free
1208  *
1209  * Frees the summary array returned by #camel_folder_get_summary.
1210  **/
1211 void
1212 camel_folder_free_summary(CamelFolder *folder, GPtrArray *array)
1213 {
1214         g_return_if_fail(CAMEL_IS_FOLDER(folder));
1215
1216         CF_CLASS(folder)->free_summary(folder, array);
1217 }
1218
1219
1220 /**
1221  * camel_folder_has_search_capability:
1222  * @folder: a #CamelFolder object
1223  *
1224  * Checks if a folder supports searching.
1225  *
1226  * Returns %TRUE if the folder supports searching or %FALSE otherwise
1227  **/
1228 gboolean
1229 camel_folder_has_search_capability (CamelFolder *folder)
1230 {
1231         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1232
1233         return folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
1234 }
1235
1236 static GPtrArray *
1237 search_by_expression (CamelFolder *folder, const char *expression,
1238                       CamelException *ex)
1239 {
1240         camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1241                               _("Unsupported operation: search by expression: for %s"),
1242                               camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1243         
1244         w(g_warning ("CamelFolder::search_by_expression not implemented for "
1245                      "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1246         
1247         return NULL;
1248 }
1249
1250
1251 /**
1252  * camel_folder_search_by_expression:
1253  * @folder: a #CamelFolder object
1254  * @expr: a search expression
1255  * @ex: a #CamelException
1256  *
1257  * Searches the folder for messages matching the given search expression.
1258  *
1259  * Returns a #GPtrArray of uids of matching messages. The caller must
1260  * free the list and each of the elements when it is done.
1261  **/
1262 GPtrArray *
1263 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1264                                    CamelException *ex)
1265 {
1266         GPtrArray *ret;
1267
1268         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1269         g_return_val_if_fail (folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1270
1271         /* NOTE: that it is upto the callee to lock */
1272
1273         ret = CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1274
1275         return ret;
1276 }
1277
1278 static GPtrArray *
1279 search_by_uids(CamelFolder *folder, const char *exp, GPtrArray *uids, CamelException *ex)
1280 {
1281         camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
1282                               _("Unsupported operation: search by UIDs: for %s"),
1283                               camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
1284         
1285         w(g_warning ("CamelFolder::search_by_expression not implemented for "
1286                      "`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1287         
1288         return NULL;
1289 }
1290
1291
1292 /**
1293  * camel_folder_search_by_uids:
1294  * @folder: a #CamelFolder object
1295  * @expr: search expression
1296  * @uids: array of uid's to match against.
1297  * @ex: a #CamelException
1298  *
1299  * Search a subset of uid's for an expression match.
1300  *
1301  * Returns a #GPtrArray of uids of matching messages. The caller must
1302  * free the list and each of the elements when it is done.
1303  **/
1304 GPtrArray *
1305 camel_folder_search_by_uids(CamelFolder *folder, const char *expr, GPtrArray *uids, CamelException *ex)
1306 {
1307         GPtrArray *ret;
1308
1309         g_return_val_if_fail(CAMEL_IS_FOLDER (folder), NULL);
1310         g_return_val_if_fail(folder->folder_flags & CAMEL_FOLDER_HAS_SEARCH_CAPABILITY, NULL);
1311
1312         /* NOTE: that it is upto the callee to lock */
1313
1314         ret = CF_CLASS(folder)->search_by_uids(folder, expr, uids, ex);
1315
1316         return ret;
1317 }
1318
1319 static void
1320 search_free (CamelFolder *folder, GPtrArray *result)
1321 {
1322         int i;
1323
1324         for (i = 0; i < result->len; i++)
1325                 g_free (g_ptr_array_index (result, i));
1326         g_ptr_array_free (result, TRUE);
1327 }
1328
1329
1330 /**
1331  * camel_folder_search_free:
1332  * @folder: a #CamelFolder object
1333  * @result: search results to free
1334  * 
1335  * Free the result of a search as gotten by #camel_folder_search or
1336  * #camel_folder_search_by_uids.
1337  **/
1338 void 
1339 camel_folder_search_free (CamelFolder *folder, GPtrArray *result)
1340 {
1341         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1342
1343         /* NOTE: upto the callee to lock */
1344         CF_CLASS (folder)->search_free (folder, result);
1345 }
1346
1347
1348 static void
1349 transfer_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1350                      char **transferred_uid, gboolean delete_original,
1351                      CamelException *ex)
1352 {
1353         CamelMimeMessage *msg;
1354         CamelMessageInfo *minfo, *info;
1355         
1356         /* Default implementation. */
1357         
1358         msg = camel_folder_get_message(source, uid, ex);
1359         if (!msg)
1360                 return;
1361
1362         /* if its deleted we poke the flags, so we need to copy the messageinfo */
1363         if ((source->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY)
1364             && (minfo = camel_folder_get_message_info(source, uid))) {
1365                 info = camel_message_info_clone(minfo);
1366                 camel_folder_free_message_info(source, minfo);
1367         } else
1368                 info = camel_message_info_new_from_header(NULL, ((CamelMimePart *)msg)->headers);
1369         
1370         /* we don't want to retain the deleted flag */
1371         camel_message_info_set_flags(info, CAMEL_MESSAGE_DELETED, 0);
1372         
1373         camel_folder_append_message (dest, msg, info, transferred_uid, ex);
1374         camel_object_unref (msg);
1375         
1376         if (delete_original && !camel_exception_is_set (ex))
1377                 camel_folder_set_message_flags (source, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, ~0);
1378         
1379         camel_message_info_free (info);
1380 }
1381
1382 static void
1383 transfer_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
1384 {
1385         CamelException local;
1386         char **ret_uid = NULL;
1387         int i;
1388
1389         if (transferred_uids) {
1390                 *transferred_uids = g_ptr_array_new ();
1391                 g_ptr_array_set_size (*transferred_uids, uids->len);
1392         }
1393
1394         camel_exception_init(&local);
1395         if (ex == NULL)
1396                 ex = &local;
1397
1398         camel_operation_start(NULL, delete_originals ? _("Moving messages") : _("Copying messages"));
1399
1400         if (uids->len > 1) {
1401                 camel_folder_freeze(dest);
1402                 if (delete_originals)
1403                         camel_folder_freeze(source);
1404         }
1405         for (i = 0; i < uids->len && !camel_exception_is_set (ex); i++) {
1406                 if (transferred_uids)
1407                         ret_uid = (char **)&((*transferred_uids)->pdata[i]);
1408                 transfer_message_to (source, uids->pdata[i], dest, ret_uid, delete_originals, ex);
1409                 camel_operation_progress(NULL, i * 100 / uids->len);
1410         }
1411         if (uids->len > 1) {
1412                 camel_folder_thaw(dest);
1413                 if (delete_originals)
1414                         camel_folder_thaw(source);
1415         }
1416
1417         camel_operation_end(NULL);
1418         camel_exception_clear(&local);
1419 }
1420
1421
1422 /**
1423  * camel_folder_transfer_messages_to:
1424  * @source: the source #CamelFolder object
1425  * @uids: message UIDs in @source
1426  * @dest: the destination #CamelFolder object
1427  * @transferred_uids: if non-%NULL, the UIDs of the resulting messages
1428  * in @dest will be stored here, if known.
1429  * @delete_originals: whether or not to delete the original messages
1430  * @ex: a #CamelException
1431  *
1432  * This copies or moves messages from one folder to another. If the
1433  * @source and @dest folders have the same parent_store, this may be
1434  * more efficient than using #camel_folder_append_message.
1435  **/
1436 void
1437 camel_folder_transfer_messages_to (CamelFolder *source, GPtrArray *uids,
1438                                    CamelFolder *dest, GPtrArray **transferred_uids,
1439                                    gboolean delete_originals, CamelException *ex)
1440 {
1441         g_return_if_fail (CAMEL_IS_FOLDER (source));
1442         g_return_if_fail (CAMEL_IS_FOLDER (dest));
1443         g_return_if_fail (uids != NULL);
1444         
1445         if (source == dest || uids->len == 0) {
1446                 /* source and destination folders are the same, or no work to do, do nothing. */
1447                 return;
1448         }
1449         
1450         if (source->parent_store == dest->parent_store) {
1451                 /* If either folder is a vtrash, we need to use the
1452                  * vtrash transfer method.
1453                  */
1454                 if (CAMEL_IS_VTRASH_FOLDER (dest))
1455                         CF_CLASS (dest)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1456                 else
1457                         CF_CLASS (source)->transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1458         } else
1459                 transfer_messages_to (source, uids, dest, transferred_uids, delete_originals, ex);
1460 }
1461
1462 static void
1463 delete (CamelFolder *folder)
1464 {
1465         if (folder->summary)
1466                 camel_folder_summary_clear (folder->summary);
1467 }
1468
1469
1470 /**
1471  * camel_folder_delete:
1472  * @folder: a #CamelFolder object
1473  *
1474  * Marks a folder object as deleted and performs any required cleanup.
1475  **/
1476 void
1477 camel_folder_delete (CamelFolder *folder)
1478 {
1479         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1480         
1481         CAMEL_FOLDER_REC_LOCK (folder, lock);
1482         if (folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
1483                 CAMEL_FOLDER_REC_UNLOCK (folder, lock);
1484                 return;
1485         }
1486         
1487         folder->folder_flags |= CAMEL_FOLDER_HAS_BEEN_DELETED;
1488         
1489         CF_CLASS (folder)->delete (folder);
1490
1491         CAMEL_FOLDER_REC_UNLOCK (folder, lock);
1492
1493         camel_object_trigger_event (folder, "deleted", NULL);
1494 }
1495
1496 static void
1497 folder_rename (CamelFolder *folder, const char *new)
1498 {
1499         char *tmp;
1500
1501         d(printf("CamelFolder:rename('%s')\n", new));
1502
1503         g_free(folder->full_name);
1504         folder->full_name = g_strdup(new);
1505         g_free(folder->name);
1506         tmp = strrchr(new, '/');
1507         folder->name = g_strdup(tmp?tmp+1:new);
1508 }
1509
1510
1511 /**
1512  * camel_folder_rename:
1513  * @folder: a #CamelFolder object
1514  * @new: new name for the folder
1515  * 
1516  * Mark an active folder object as renamed.
1517  *
1518  * NOTE: This is an internal function used by camel stores, no locking
1519  * is performed on the folder.
1520  **/
1521 void
1522 camel_folder_rename(CamelFolder *folder, const char *new)
1523 {
1524         char *old;
1525
1526         old = g_strdup(folder->full_name);
1527
1528         CF_CLASS (folder)->rename(folder, new);
1529
1530         camel_object_trigger_event (folder, "renamed", old);
1531         g_free(old);
1532 }
1533
1534 static void
1535 freeze (CamelFolder *folder)
1536 {
1537         CAMEL_FOLDER_LOCK(folder, change_lock);
1538
1539         g_assert(folder->priv->frozen >= 0);
1540
1541         folder->priv->frozen++;
1542
1543         d(printf ("freeze(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1544         CAMEL_FOLDER_UNLOCK(folder, change_lock);
1545 }
1546
1547
1548 /**
1549  * camel_folder_freeze:
1550  * @folder: a #CamelFolder
1551  *
1552  * Freezes the folder so that a series of operation can be performed
1553  * without "folder_changed" signals being emitted.  When the folder is
1554  * later thawed with #camel_folder_thaw, the suppressed signals will
1555  * be emitted.
1556  **/
1557 void
1558 camel_folder_freeze (CamelFolder * folder)
1559 {
1560         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1561
1562         CF_CLASS (folder)->freeze (folder);
1563 }
1564
1565 static void
1566 thaw (CamelFolder * folder)
1567 {
1568         CamelFolderChangeInfo *info = NULL;
1569
1570         CAMEL_FOLDER_LOCK(folder, change_lock);
1571
1572         g_assert(folder->priv->frozen > 0);
1573
1574         folder->priv->frozen--;
1575
1576         d(printf ("thaw(%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen));
1577
1578         if (folder->priv->frozen == 0
1579             && camel_folder_change_info_changed(folder->priv->changed_frozen)) {
1580                 info = folder->priv->changed_frozen;
1581                 folder->priv->changed_frozen = camel_folder_change_info_new();
1582         }
1583         
1584         CAMEL_FOLDER_UNLOCK(folder, change_lock);
1585
1586         if (info) {
1587                 camel_object_trigger_event (folder, "folder_changed", info);
1588                 camel_folder_change_info_free(info);
1589         }
1590 }
1591
1592 /**
1593  * camel_folder_thaw:
1594  * @folder: a #CamelFolder object
1595  *
1596  * Thaws the folder and emits any pending folder_changed
1597  * signals.
1598  **/
1599 void
1600 camel_folder_thaw (CamelFolder *folder)
1601 {
1602         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1603         g_return_if_fail (folder->priv->frozen != 0);
1604
1605         CF_CLASS (folder)->thaw (folder);
1606 }
1607
1608 static gboolean
1609 is_frozen (CamelFolder *folder)
1610 {
1611         return folder->priv->frozen != 0;
1612 }
1613
1614
1615 /**
1616  * camel_folder_is_frozen:
1617  * @folder: a #CamelFolder object
1618  *
1619  * Returns whether or not the folder is frozen
1620  **/
1621 gboolean
1622 camel_folder_is_frozen (CamelFolder *folder)
1623 {
1624         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1625
1626         return CF_CLASS (folder)->is_frozen (folder);
1627 }
1628
1629 struct _folder_filter_msg {
1630         CamelSessionThreadMsg msg;
1631
1632         GPtrArray *recents;
1633         GPtrArray *junk;
1634         GPtrArray *notjunk;
1635         CamelFolder *folder;
1636         CamelFilterDriver *driver;
1637         CamelException ex;
1638 };
1639
1640 static void
1641 filter_filter(CamelSession *session, CamelSessionThreadMsg *tmsg)
1642 {
1643         struct _folder_filter_msg *m = (struct _folder_filter_msg *) tmsg;
1644         CamelMessageInfo *info;
1645         int i, status = 0;
1646         CamelURL *uri;
1647         char *source_url;
1648         CamelException ex;
1649         CamelJunkPlugin *csp = ((CamelService *)m->folder->parent_store)->session->junk_plugin;
1650
1651         if (m->junk) {
1652                 camel_operation_start (NULL, _("Learning junk"));
1653
1654                 for (i = 0; i < m->junk->len; i ++) {
1655                         CamelMimeMessage *msg = camel_folder_get_message(m->folder, m->junk->pdata[i], NULL);
1656                         int pc = 100 * i / m->junk->len;
1657                         
1658                         camel_operation_progress(NULL, pc);
1659
1660                         if (msg) {
1661                                 camel_junk_plugin_report_junk (csp, msg);
1662                                 camel_object_unref (msg);
1663                         }
1664                 }
1665                 camel_operation_end (NULL);
1666         }
1667
1668         if (m->notjunk) {
1669                 camel_operation_start (NULL, _("Learning non-junk"));
1670                 for (i = 0; i < m->notjunk->len; i ++) {
1671                         CamelMimeMessage *msg = camel_folder_get_message(m->folder, m->notjunk->pdata[i], NULL);
1672                         int pc = 100 * i / m->notjunk->len;
1673
1674                         camel_operation_progress(NULL, pc);
1675
1676                         if (msg) {
1677                                 camel_junk_plugin_report_notjunk (csp, msg);
1678                                 camel_object_unref (msg);
1679                         }
1680                 }
1681                 camel_operation_end (NULL);
1682         }
1683
1684         if (m->junk || m->notjunk)
1685                 camel_junk_plugin_commit_reports (csp);
1686
1687         if (m->driver && m->recents) {
1688                 camel_operation_start(NULL, _("Filtering new message(s)"));
1689
1690                 source_url = camel_service_get_url((CamelService *)m->folder->parent_store);
1691                 uri = camel_url_new(source_url, NULL);
1692                 g_free(source_url);
1693                 if (m->folder->full_name && m->folder->full_name[0] != '/') {
1694                         char *tmp = alloca(strlen(m->folder->full_name)+2);
1695
1696                         sprintf(tmp, "/%s", m->folder->full_name);
1697                         camel_url_set_path(uri, tmp);
1698                 } else
1699                         camel_url_set_path(uri, m->folder->full_name);
1700                 source_url = camel_url_to_string(uri, CAMEL_URL_HIDE_ALL);
1701                 camel_url_free(uri);
1702
1703                 for (i=0;status == 0 && i<m->recents->len;i++) {
1704                         char *uid = m->recents->pdata[i];
1705                         int pc = 100 * i / m->recents->len;
1706
1707                         camel_operation_progress(NULL, pc);
1708
1709                         info = camel_folder_get_message_info(m->folder, uid);
1710                         if (info == NULL) {
1711                                 g_warning("uid %s vanished from folder: %s", uid, source_url);
1712                                 continue;
1713                         }
1714
1715                         status = camel_filter_driver_filter_message(m->driver, NULL, info, uid, m->folder, source_url, source_url, &m->ex);
1716
1717                         camel_folder_free_message_info(m->folder, info);
1718                 }
1719
1720                 camel_exception_init(&ex);
1721                 camel_filter_driver_flush(m->driver, &ex);
1722                 if (!camel_exception_is_set(&m->ex))
1723                         camel_exception_xfer(&m->ex, &ex);
1724
1725                 g_free(source_url);
1726
1727                 camel_operation_end(NULL);
1728         }
1729 }
1730
1731 static void
1732 filter_free(CamelSession *session, CamelSessionThreadMsg *msg)
1733 {
1734         struct _folder_filter_msg *m = (struct _folder_filter_msg *)msg;
1735
1736         if (m->driver)
1737                 camel_object_unref(m->driver);
1738         if (m->recents)
1739                 camel_folder_free_deep(m->folder, m->recents);
1740         if (m->junk)
1741                 camel_folder_free_deep(m->folder, m->junk);
1742         if (m->notjunk)
1743                 camel_folder_free_deep(m->folder, m->notjunk);
1744
1745         camel_folder_thaw(m->folder);
1746         camel_object_unref(m->folder);
1747 }
1748
1749 static CamelSessionThreadOps filter_ops = {
1750         filter_filter,
1751         filter_free,
1752 };
1753
1754 struct _CamelFolderChangeInfoPrivate {
1755         GHashTable *uid_stored; /* what we have stored, which array they're in */
1756         GHashTable *uid_source; /* used to create unique lists */
1757         GPtrArray  *uid_filter; /* uids to be filtered */
1758         struct _EMemPool *uid_pool;     /* pool used to store copies of uid strings */
1759 };
1760
1761
1762 /* Event hooks that block emission when frozen */
1763 static gboolean
1764 folder_changed (CamelObject *obj, gpointer event_data)
1765 {
1766         CamelFolder *folder = (CamelFolder *)obj;
1767         CamelFolderChangeInfo *changed = event_data;
1768         struct _CamelFolderChangeInfoPrivate *p = changed->priv;
1769         CamelSession *session = ((CamelService *)folder->parent_store)->session;
1770         CamelFilterDriver *driver = NULL;
1771         GPtrArray *junk = NULL;
1772         GPtrArray *notjunk = NULL;
1773         GPtrArray *recents = NULL;
1774         int i;
1775
1776         d(printf ("folder_changed(%p:'%s', %p), frozen=%d\n", obj, folder->full_name, event_data, folder->priv->frozen));
1777         d(printf(" added %d removed %d changed %d recent %d filter %d\n",
1778                  changed->uid_added->len, changed->uid_removed->len,
1779                  changed->uid_changed->len, changed->uid_recent->len,
1780                  p->uid_filter->len));
1781
1782         if (changed == NULL) {
1783                 w(g_warning ("Class %s is passing NULL to folder_changed event",
1784                              camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))));
1785                 return TRUE;
1786         }
1787
1788         CAMEL_FOLDER_LOCK(folder, change_lock);
1789         if (folder->priv->frozen) {
1790                 camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1791                 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1792
1793                 return FALSE;
1794         }
1795         CAMEL_FOLDER_UNLOCK(folder, change_lock);
1796
1797         if (session->junk_plugin && changed->uid_changed->len) {
1798                 guint32 flags;
1799
1800                 for (i = 0; i < changed->uid_changed->len; i++) {
1801                         flags = camel_folder_get_message_flags (folder, changed->uid_changed->pdata [i]);
1802                         if (flags & CAMEL_MESSAGE_JUNK_LEARN) {
1803                                 if (flags & CAMEL_MESSAGE_JUNK) {
1804                                         if (!junk)
1805                                                 junk = g_ptr_array_new();
1806                                         g_ptr_array_add (junk, g_strdup (changed->uid_changed->pdata [i]));
1807                                 } else {
1808                                         if (!notjunk)
1809                                                 notjunk = g_ptr_array_new();
1810                                         g_ptr_array_add (notjunk, g_strdup (changed->uid_changed->pdata [i]));
1811                                 }
1812                                 /* reset junk learn flag so that we don't process it again*/ 
1813                                 camel_folder_set_message_flags (folder, changed->uid_changed->pdata [i], CAMEL_MESSAGE_JUNK_LEARN, 0);
1814                         }
1815                 }
1816         }
1817
1818         if ((folder->folder_flags & (CAMEL_FOLDER_FILTER_RECENT|CAMEL_FOLDER_FILTER_JUNK))
1819             && p->uid_filter->len > 0)
1820                 driver = camel_session_get_filter_driver(session,
1821                                                          (folder->folder_flags & CAMEL_FOLDER_FILTER_RECENT) 
1822                                                          ? "incoming":"junktest", NULL);
1823                 
1824         if (driver) {
1825                 recents = g_ptr_array_new();
1826                 for (i = 0; i < p->uid_filter->len; i++)
1827                         g_ptr_array_add (recents, g_strdup (p->uid_filter->pdata[i]));
1828                 
1829                 g_ptr_array_set_size (p->uid_filter, 0);
1830         }
1831         
1832         if (driver || junk || notjunk) {
1833                 struct _folder_filter_msg *msg;
1834
1835                 d(printf("* launching filter thread %d new mail, %d junk and %d not junk\n",
1836                          recents?recents->len:0, junk?junk->len:0, notjunk?notjunk->len:0));
1837
1838                 msg = camel_session_thread_msg_new(session, &filter_ops, sizeof(*msg));
1839                 msg->recents = recents;
1840                 msg->junk = junk;
1841                 msg->notjunk = notjunk;
1842                 msg->folder = folder;
1843                 camel_object_ref(folder);
1844                 camel_folder_freeze(folder);
1845                 /* Copy changes back to changed_frozen list to retain
1846                  * them while we are filtering */
1847                 CAMEL_FOLDER_LOCK(folder, change_lock);
1848                 camel_folder_change_info_cat(folder->priv->changed_frozen, changed);
1849                 CAMEL_FOLDER_UNLOCK(folder, change_lock);
1850                 msg->driver = driver;
1851                 camel_exception_init(&msg->ex);
1852                 camel_session_thread_queue(session, &msg->msg, 0);
1853                 return FALSE;
1854         }
1855
1856         return TRUE;
1857 }
1858
1859
1860 /**
1861  * camel_folder_free_nop:
1862  * @folder: a #CamelFolder object
1863  * @array: an array of uids or #CamelMessageInfo
1864  *
1865  * "Frees" the provided array by doing nothing. Used by #CamelFolder
1866  * subclasses as an implementation for free_uids, or free_summary when
1867  * the returned array is "static" information and should not be freed.
1868  **/
1869 void
1870 camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
1871 {
1872         ;
1873 }
1874
1875
1876 /**
1877  * camel_folder_free_shallow:
1878  * @folder: a #CamelFolder object
1879  * @array: an array of uids or #CamelMessageInfo
1880  *
1881  * Frees the provided array but not its contents. Used by #CamelFolder
1882  * subclasses as an implementation for free_uids or free_summary when
1883  * the returned array needs to be freed but its contents come from
1884  * "static" information.
1885  **/
1886 void
1887 camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
1888 {
1889         g_ptr_array_free (array, TRUE);
1890 }
1891
1892
1893 /**
1894  * camel_folder_free_deep:
1895  * @folder: a #CamelFolder object
1896  * @array: an array of uids
1897  *
1898  * Frees the provided array and its contents. Used by #CamelFolder
1899  * subclasses as an implementation for free_uids when the provided
1900  * information was created explicitly by the corresponding get_ call.
1901  **/
1902 void
1903 camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
1904 {
1905         int i;
1906
1907         for (i = 0; i < array->len; i++)
1908                 g_free (array->pdata[i]);
1909         g_ptr_array_free (array, TRUE);
1910 }
1911
1912
1913 /**
1914  * camel_folder_change_info_new:
1915  * 
1916  * Create a new folder change info structure.
1917  *
1918  * Change info structures are not MT-SAFE and must be
1919  * locked for exclusive access externally.
1920  *
1921  * Returns a new #CamelFolderChangeInfo
1922  **/
1923 CamelFolderChangeInfo *
1924 camel_folder_change_info_new(void)
1925 {
1926         CamelFolderChangeInfo *info;
1927
1928         info = g_malloc(sizeof(*info));
1929         info->uid_added = g_ptr_array_new();
1930         info->uid_removed = g_ptr_array_new();
1931         info->uid_changed = g_ptr_array_new();
1932         info->uid_recent = g_ptr_array_new();
1933         info->priv = g_malloc0(sizeof(*info->priv));
1934         info->priv->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
1935         info->priv->uid_source = NULL;
1936         info->priv->uid_filter = g_ptr_array_new();
1937         info->priv->uid_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
1938
1939         return info;
1940 }
1941
1942
1943 /**
1944  * camel_folder_change_info_add_source:
1945  * @info: a #CamelFolderChangeInfo
1946  * @uid: a uid
1947  * 
1948  * Add a source uid for generating a changeset.
1949  **/
1950 void
1951 camel_folder_change_info_add_source(CamelFolderChangeInfo *info, const char *uid)
1952 {
1953         struct _CamelFolderChangeInfoPrivate *p;
1954         
1955         g_assert(info != NULL);
1956         
1957         p = info->priv;
1958         
1959         if (p->uid_source == NULL)
1960                 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1961
1962         if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1963                 g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1));
1964 }
1965
1966
1967 /**
1968  * camel_folder_change_info_add_source_list:
1969  * @info: a #CamelFolderChangeInfo
1970  * @list: a list of uids
1971  * 
1972  * Add a list of source uid's for generating a changeset.
1973  **/
1974 void
1975 camel_folder_change_info_add_source_list(CamelFolderChangeInfo *info, const GPtrArray *list)
1976 {
1977         struct _CamelFolderChangeInfoPrivate *p;
1978         int i;
1979         
1980         g_assert(info != NULL);
1981         g_assert(list != NULL);
1982         
1983         p = info->priv;
1984
1985         if (p->uid_source == NULL)
1986                 p->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
1987
1988         for (i=0;i<list->len;i++) {
1989                 char *uid = list->pdata[i];
1990
1991                 if (g_hash_table_lookup(p->uid_source, uid) == NULL)
1992                         g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1));
1993         }
1994 }
1995
1996
1997 /**
1998  * camel_folder_change_info_add_update:
1999  * @info: a #CamelFolderChangeInfo
2000  * @uid: a uid
2001  * 
2002  * Add a uid from the updated list, used to generate a changeset diff.
2003  **/
2004 void
2005 camel_folder_change_info_add_update(CamelFolderChangeInfo *info, const char *uid)
2006 {
2007         struct _CamelFolderChangeInfoPrivate *p;
2008         char *key;
2009         int value;
2010         
2011         g_assert(info != NULL);
2012         
2013         p = info->priv;
2014         
2015         if (p->uid_source == NULL) {
2016                 camel_folder_change_info_add_uid(info, uid);
2017                 return;
2018         }
2019
2020         if (g_hash_table_lookup_extended(p->uid_source, uid, (gpointer) &key, (gpointer) &value)) {
2021                 g_hash_table_remove(p->uid_source, key);
2022         } else {
2023                 camel_folder_change_info_add_uid(info, uid);
2024         }
2025 }
2026
2027
2028 /**
2029  * camel_folder_change_info_add_update_list:
2030  * @info: a #CamelFolderChangeInfo
2031  * @list: a list of uids
2032  * 
2033  * Add a list of uid's from the updated list.
2034  **/
2035 void
2036 camel_folder_change_info_add_update_list(CamelFolderChangeInfo *info, const GPtrArray *list)
2037 {
2038         int i;
2039         
2040         g_assert(info != NULL);
2041         g_assert(list != NULL);
2042         
2043         for (i=0;i<list->len;i++)
2044                 camel_folder_change_info_add_update(info, list->pdata[i]);
2045 }
2046
2047 static void
2048 change_info_remove(char *key, void *value, CamelFolderChangeInfo *info)
2049 {
2050         struct _CamelFolderChangeInfoPrivate *p = info->priv;
2051         GPtrArray *olduids;
2052         char *olduid;
2053
2054         if (g_hash_table_lookup_extended(p->uid_stored, key, (gpointer) &olduid, (gpointer) &olduids)) {
2055                 /* if it was added/changed them removed, then remove it */
2056                 if (olduids != info->uid_removed) {
2057                         g_ptr_array_remove_fast(olduids, olduid);
2058                         g_ptr_array_add(info->uid_removed, olduid);
2059                         g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2060                 }
2061                 return;
2062         }
2063
2064         /* we dont need to copy this, as they've already been copied into our pool */
2065         g_ptr_array_add(info->uid_removed, key);
2066         g_hash_table_insert(p->uid_stored, key, info->uid_removed);
2067 }
2068
2069
2070 /**
2071  * camel_folder_change_info_build_diff:
2072  * @info: a #CamelFolderChangeInfo
2073  * 
2074  * Compare the source uid set to the updated uid set and generate the
2075  * differences into the added and removed lists.
2076  **/
2077 void
2078 camel_folder_change_info_build_diff(CamelFolderChangeInfo *info)
2079 {
2080         struct _CamelFolderChangeInfoPrivate *p;
2081         
2082         g_assert(info != NULL);
2083         
2084         p = info->priv;
2085         
2086         if (p->uid_source) {
2087                 g_hash_table_foreach(p->uid_source, (GHFunc)change_info_remove, info);
2088                 g_hash_table_destroy(p->uid_source);
2089                 p->uid_source = NULL;
2090         }
2091 }
2092
2093 static void
2094 change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2095 {
2096         struct _CamelFolderChangeInfoPrivate *p;
2097         GPtrArray *olduids;
2098         char *olduid;
2099         
2100         p = info->priv;
2101
2102         /* always add to recent, but dont let anyone else know */       
2103         if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2104                 olduid = e_mempool_strdup(p->uid_pool, uid);
2105         }
2106         g_ptr_array_add(info->uid_recent, olduid);
2107 }
2108
2109 static void
2110 change_info_filter_uid(CamelFolderChangeInfo *info, const char *uid)
2111 {
2112         struct _CamelFolderChangeInfoPrivate *p;
2113         GPtrArray *olduids;
2114         char *olduid;
2115         
2116         p = info->priv;
2117
2118         /* always add to filter, but dont let anyone else know */       
2119         if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) {
2120                 olduid = e_mempool_strdup(p->uid_pool, uid);
2121         }
2122         g_ptr_array_add(p->uid_filter, olduid);
2123 }
2124
2125 static void
2126 change_info_cat(CamelFolderChangeInfo *info, GPtrArray *source, void (*add)(CamelFolderChangeInfo *info, const char *uid))
2127 {
2128         int i;
2129
2130         for (i=0;i<source->len;i++)
2131                 add(info, source->pdata[i]);
2132 }
2133
2134
2135 /**
2136  * camel_folder_change_info_cat:
2137  * @info: a #CamelFolderChangeInfo to append to
2138  * @src: a #CamelFolderChangeInfo to append from
2139  * 
2140  * Concatenate one change info onto antoher.  Can be used to copy them
2141  * too.
2142  **/
2143 void
2144 camel_folder_change_info_cat(CamelFolderChangeInfo *info, CamelFolderChangeInfo *source)
2145 {
2146         g_assert(info != NULL);
2147         g_assert(source != NULL);
2148         
2149         change_info_cat(info, source->uid_added, camel_folder_change_info_add_uid);
2150         change_info_cat(info, source->uid_removed, camel_folder_change_info_remove_uid);
2151         change_info_cat(info, source->uid_changed, camel_folder_change_info_change_uid);
2152         change_info_cat(info, source->uid_recent, change_info_recent_uid);
2153         change_info_cat(info, source->priv->uid_filter, change_info_filter_uid);
2154 }
2155
2156 /**
2157  * camel_folder_change_info_add_uid:
2158  * @info: a #CamelFolderChangeInfo
2159  * @uid: a uid
2160  * 
2161  * Add a new uid to the changeinfo.
2162  **/
2163 void
2164 camel_folder_change_info_add_uid(CamelFolderChangeInfo *info, const char *uid)
2165 {
2166         struct _CamelFolderChangeInfoPrivate *p;
2167         GPtrArray *olduids;
2168         char *olduid;
2169         
2170         g_assert(info != NULL);
2171         
2172         p = info->priv;
2173         
2174         if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2175                 /* if it was removed then added, promote it to a changed */
2176                 /* if it was changed then added, leave as changed */
2177                 if (olduids == info->uid_removed) {
2178                         g_ptr_array_remove_fast(olduids, olduid);
2179                         g_ptr_array_add(info->uid_changed, olduid);
2180                         g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2181                 }
2182                 return;
2183         }
2184
2185         olduid = e_mempool_strdup(p->uid_pool, uid);
2186         g_ptr_array_add(info->uid_added, olduid);
2187         g_hash_table_insert(p->uid_stored, olduid, info->uid_added);
2188 }
2189
2190
2191 /**
2192  * camel_folder_change_info_remove_uid:
2193  * @info: a #CamelFolderChangeInfo
2194  * @uid: a uid
2195  * 
2196  * Add a uid to the removed uid list.
2197  **/
2198 void
2199 camel_folder_change_info_remove_uid(CamelFolderChangeInfo *info, const char *uid)
2200 {
2201         struct _CamelFolderChangeInfoPrivate *p;
2202         GPtrArray *olduids;
2203         char *olduid;
2204         
2205         g_assert(info != NULL);
2206         
2207         p = info->priv;
2208         
2209         if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2210                 /* if it was added/changed them removed, then remove it */
2211                 if (olduids != info->uid_removed) {
2212                         g_ptr_array_remove_fast(olduids, olduid);
2213                         g_ptr_array_add(info->uid_removed, olduid);
2214                         g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2215                 }
2216                 return;
2217         }
2218
2219         olduid = e_mempool_strdup(p->uid_pool, uid);
2220         g_ptr_array_add(info->uid_removed, olduid);
2221         g_hash_table_insert(p->uid_stored, olduid, info->uid_removed);
2222 }
2223
2224
2225 /**
2226  * camel_folder_change_info_change_uid:
2227  * @info: a #CamelFolderChangeInfo
2228  * @uid: a uid
2229  * 
2230  * Add a uid to the changed uid list.
2231  **/
2232 void
2233 camel_folder_change_info_change_uid(CamelFolderChangeInfo *info, const char *uid)
2234 {
2235         struct _CamelFolderChangeInfoPrivate *p;
2236         GPtrArray *olduids;
2237         char *olduid;
2238         
2239         g_assert(info != NULL);
2240         
2241         p = info->priv;
2242         
2243         if (g_hash_table_lookup_extended(p->uid_stored, uid, (gpointer) &olduid, (gpointer) &olduids)) {
2244                 /* if we have it already, leave it as that */
2245                 return;
2246         }
2247
2248         olduid = e_mempool_strdup(p->uid_pool, uid);
2249         g_ptr_array_add(info->uid_changed, olduid);
2250         g_hash_table_insert(p->uid_stored, olduid, info->uid_changed);
2251 }
2252
2253
2254 /**
2255  * camel_folder_change_info_recent_uid:
2256  * @info: a #CamelFolderChangeInfo
2257  * @uid: a uid
2258  *
2259  * Add a recent uid to the changedinfo.
2260  * This will also add the uid to the uid_filter array for potential
2261  * filtering
2262  **/
2263 void
2264 camel_folder_change_info_recent_uid(CamelFolderChangeInfo *info, const char *uid)
2265 {
2266         g_assert(info != NULL);
2267         
2268         change_info_recent_uid(info, uid);
2269         change_info_filter_uid(info, uid);
2270 }
2271
2272 /**
2273  * camel_folder_change_info_changed:
2274  * @info: a #CamelFolderChangeInfo
2275  *
2276  * Gets whether or not there have been any changes.
2277  *
2278  * Returns %TRUE if the changeset contains any changes or %FALSE
2279  * otherwise
2280  **/
2281 gboolean
2282 camel_folder_change_info_changed(CamelFolderChangeInfo *info)
2283 {
2284         g_assert(info != NULL);
2285         
2286         return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len || info->uid_recent->len);
2287 }
2288
2289
2290 /**
2291  * camel_folder_change_info_clear:
2292  * @info: a #CamelFolderChangeInfo
2293  * 
2294  * Empty out the change info; called after changes have been
2295  * processed.
2296  **/
2297 void
2298 camel_folder_change_info_clear(CamelFolderChangeInfo *info)
2299 {
2300         struct _CamelFolderChangeInfoPrivate *p;
2301         
2302         g_assert(info != NULL);
2303         
2304         p = info->priv;
2305         
2306         g_ptr_array_set_size(info->uid_added, 0);
2307         g_ptr_array_set_size(info->uid_removed, 0);
2308         g_ptr_array_set_size(info->uid_changed, 0);
2309         g_ptr_array_set_size(info->uid_recent, 0);
2310         if (p->uid_source) {
2311                 g_hash_table_destroy(p->uid_source);
2312                 p->uid_source = NULL;
2313         }
2314         g_hash_table_destroy(p->uid_stored);
2315         p->uid_stored = g_hash_table_new(g_str_hash, g_str_equal);
2316         g_ptr_array_set_size(p->uid_filter, 0);
2317         e_mempool_flush(p->uid_pool, TRUE);
2318 }
2319
2320
2321 /**
2322  * camel_folder_change_info_free:
2323  * @info: a #CamelFolderChangeInfo
2324  * 
2325  * Free memory associated with the folder change info lists.
2326  **/
2327 void
2328 camel_folder_change_info_free(CamelFolderChangeInfo *info)
2329 {
2330         struct _CamelFolderChangeInfoPrivate *p;
2331
2332         g_assert(info != NULL);
2333         
2334         p = info->priv;
2335         
2336         if (p->uid_source)
2337                 g_hash_table_destroy(p->uid_source);
2338
2339         g_hash_table_destroy(p->uid_stored);
2340         g_ptr_array_free(p->uid_filter, TRUE);
2341         e_mempool_destroy(p->uid_pool);
2342         g_free(p);
2343
2344         g_ptr_array_free(info->uid_added, TRUE);
2345         g_ptr_array_free(info->uid_removed, TRUE);
2346         g_ptr_array_free(info->uid_changed, TRUE);
2347         g_ptr_array_free(info->uid_recent, TRUE);
2348         g_free(info);
2349 }