Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-vee-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Copyright (C) 2000-2003 Ximian Inc.
4  *
5  *  Authors: Michael Zucchi <notzed@ximian.com>
6  *           Jeffrey Stedfast <fejj@ximian.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28
29 #include <glib.h>
30 #include <glib/gi18n-lib.h>
31
32 #include <libedataserver/md5-utils.h>
33
34 #if defined (DOEPOOLV) || defined (DOESTRV)
35 #include <libedataserver/e-memory.h>
36 #endif
37
38 #include "camel-debug.h"
39 #include "camel-exception.h"
40 #include "camel-folder-search.h"
41 #include "camel-mime-message.h"
42 #include "camel-private.h"
43 #include "camel-session.h"
44 #include "camel-store.h"
45 #include "camel-vee-folder.h"
46 #include "camel-vee-store.h"    /* for open flags */
47 #include "camel-vee-summary.h"
48
49 #define d(x) 
50 #define dd(x) (camel_debug("vfolder")?(x):0)
51
52 #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv)
53
54 #if 0
55 static void vee_refresh_info(CamelFolder *folder, CamelException *ex);
56
57 static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
58 static void vee_expunge (CamelFolder *folder, CamelException *ex);
59
60 static void vee_freeze(CamelFolder *folder);
61 static void vee_thaw(CamelFolder *folder);
62
63 static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
64 static void vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex);
65 static void vee_transfer_messages_to(CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
66
67 static GPtrArray *vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
68 static GPtrArray *vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex);
69
70 static void vee_rename(CamelFolder *folder, const char *new);
71 #endif
72
73 static void camel_vee_folder_class_init (CamelVeeFolderClass *klass);
74 static void camel_vee_folder_init       (CamelVeeFolder *obj);
75 static void camel_vee_folder_finalise   (CamelObject *obj);
76
77 static int vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex);
78 static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source);
79
80 static void folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf);
81 static void subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf);
82 static void folder_renamed(CamelFolder *f, const char *old, CamelVeeFolder *vf);
83
84 static void folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf);
85
86 static CamelFolderClass *camel_vee_folder_parent;
87
88 CamelType
89 camel_vee_folder_get_type (void)
90 {
91         static CamelType type = CAMEL_INVALID_TYPE;
92         
93         if (type == CAMEL_INVALID_TYPE) {
94                 type = camel_type_register (camel_folder_get_type (), "CamelVeeFolder",
95                                             sizeof (CamelVeeFolder),
96                                             sizeof (CamelVeeFolderClass),
97                                             (CamelObjectClassInitFunc) camel_vee_folder_class_init,
98                                             NULL,
99                                             (CamelObjectInitFunc) camel_vee_folder_init,
100                                             (CamelObjectFinalizeFunc) camel_vee_folder_finalise);
101         }
102         
103         return type;
104 }
105
106 void
107 camel_vee_folder_construct (CamelVeeFolder *vf, CamelStore *parent_store, const char *full, const char *name, guint32 flags)
108 {
109         CamelFolder *folder = (CamelFolder *)vf;
110
111         vf->flags = flags;
112         camel_folder_construct(folder, parent_store, full, name);
113
114         folder->summary = camel_vee_summary_new(folder);
115
116         if (CAMEL_IS_VEE_STORE(parent_store))
117                 vf->parent_vee_store = (CamelVeeStore *)parent_store;
118 }
119
120 /**
121  * camel_vee_folder_new:
122  * @parent_store: the parent CamelVeeStore
123  * @full: the full path to the vfolder.
124  * @ex: a CamelException
125  *
126  * Create a new CamelVeeFolder object.
127  *
128  * Return value: A new CamelVeeFolder widget.
129  **/
130 CamelFolder *
131 camel_vee_folder_new(CamelStore *parent_store, const char *full, guint32 flags)
132 {
133         CamelVeeFolder *vf;
134         char *tmp;
135         
136         if (CAMEL_IS_VEE_STORE(parent_store) && strcmp(full, CAMEL_UNMATCHED_NAME) == 0) {
137                 vf = ((CamelVeeStore *)parent_store)->folder_unmatched;
138                 camel_object_ref(vf);
139         } else {
140                 const char *name = strrchr(full, '/');
141
142                 if (name == NULL)
143                         name = full;
144                 else
145                         name++;
146                 vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
147                 camel_vee_folder_construct(vf, parent_store, full, name, flags);
148         }
149
150         d(printf("returning folder %s %p, count = %d\n", name, vf, camel_folder_get_message_count((CamelFolder *)vf)));
151
152         tmp = g_strdup_printf("%s/%s.cmeta", ((CamelService *)parent_store)->url->path, full);
153         camel_object_set(vf, NULL, CAMEL_OBJECT_STATE_FILE, tmp, NULL);
154         g_free(tmp);
155         if (camel_object_state_read(vf) == -1) {
156                 /* setup defaults: we have none currently */
157         }
158
159         return (CamelFolder *)vf;
160 }
161
162 void
163 camel_vee_folder_set_expression(CamelVeeFolder *vf, const char *query)
164 {
165         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->set_expression(vf, query);
166 }
167
168 /**
169  * camel_vee_folder_add_folder:
170  * @vf: Virtual Folder object
171  * @sub: source CamelFolder to add to @vf
172  *
173  * Adds @sub as a source folder to @vf.
174  **/
175 void
176 camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
177 {
178         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
179         int i;
180         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
181         
182         if (vf == (CamelVeeFolder *)sub) {
183                 g_warning("Adding a virtual folder to itself as source, ignored");
184                 return;
185         }
186
187         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
188
189         /* for normal vfolders we want only unique ones, for unmatched we want them all recorded */
190         if (g_list_find(p->folders, sub) == NULL) {
191                 camel_object_ref((CamelObject *)sub);
192                 p->folders = g_list_append(p->folders, sub);
193                 
194                 CAMEL_FOLDER_LOCK(vf, change_lock);
195
196                 /* update the freeze state of 'sub' to match our freeze state */
197                 for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
198                         camel_folder_freeze(sub);
199
200                 CAMEL_FOLDER_UNLOCK(vf, change_lock);
201         }
202         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
203                 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
204                 camel_object_ref((CamelObject *)sub);
205                 up->folders = g_list_append(up->folders, sub);
206
207                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
208
209                 /* update the freeze state of 'sub' to match Unmatched's freeze state */
210                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
211                         camel_folder_freeze(sub);
212
213                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
214         }
215
216         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
217
218         d(printf("camel_vee_folder_add_folde(%p, %p)\n", vf, sub));
219
220         camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc)folder_changed, vf);
221         camel_object_hook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc)subfolder_deleted, vf);
222         camel_object_hook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc)folder_renamed, vf);
223
224         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->add_folder(vf, sub);
225 }
226
227 /**
228  * camel_vee_folder_remove_folder:
229  * @vf: Virtual Folder object
230  * @sub: source CamelFolder to remove from @vf
231  *
232  * Removed the source folder, @sub, from the virtual folder, @vf.
233  **/
234 void
235
236 camel_vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
237 {
238         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
239         int i;
240         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
241         
242         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
243
244         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
245         p->folders_changed = g_list_remove(p->folders_changed, sub);
246         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
247
248         if (g_list_find(p->folders, sub) == NULL) {
249                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
250                 return;
251         }
252         
253         camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
254         camel_object_unhook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
255         camel_object_unhook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc) folder_renamed, vf);
256
257         p->folders = g_list_remove(p->folders, sub);
258         
259         /* undo the freeze state that we have imposed on this source folder */
260         CAMEL_FOLDER_LOCK(vf, change_lock);
261         for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
262                 camel_folder_thaw(sub);
263         CAMEL_FOLDER_UNLOCK(vf, change_lock);
264         
265         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
266
267         if (folder_unmatched != NULL) {
268                 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
269
270                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
271                 /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */
272                 if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
273                         while (g_list_find(up->folders, sub)) {
274                                 up->folders = g_list_remove(up->folders, sub);
275                                 camel_object_unref((CamelObject *)sub);
276                                 
277                                 /* undo the freeze state that Unmatched has imposed on this source folder */
278                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
279                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
280                                         camel_folder_thaw(sub);
281                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
282                         }
283                 } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
284                         if (g_list_find(up->folders, sub) != NULL) {
285                                 up->folders = g_list_remove(up->folders, sub);
286                                 camel_object_unref((CamelObject *)sub);
287                                 
288                                 /* undo the freeze state that Unmatched has imposed on this source folder */
289                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
290                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
291                                         camel_folder_thaw(sub);
292                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
293                         }
294                 }
295                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
296         }
297
298         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->remove_folder(vf, sub);
299
300         if (CAMEL_IS_VEE_FOLDER(sub))
301                 return;
302                         
303         camel_object_unref((CamelObject *)sub);
304 }
305
306 /**
307  * camel_vee_folder_rebuild_folder:
308  * @vf: Virtual Folder object
309  * @sub: source CamelFolder to add to @vf
310  * @ex: Exception.
311  *
312  * Rebuild the folder @sub, if it should be.
313  **/
314 int
315 camel_vee_folder_rebuild_folder(CamelVeeFolder *vf, CamelFolder *sub, CamelException *ex)
316 {
317         return ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->rebuild_folder(vf, sub, ex);
318 }
319
320 static void
321 remove_folders(CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf)
322 {
323         camel_vee_folder_remove_folder(vf, folder);
324         camel_object_unref((CamelObject *)folder);
325 }
326
327 /**
328  * camel_vee_folder_set_folders:
329  * @vf: 
330  * @folders: 
331  * 
332  * Set the whole list of folder sources on a vee folder.
333  **/
334 void
335 camel_vee_folder_set_folders(CamelVeeFolder *vf, GList *folders)
336 {
337         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
338         GHashTable *remove = g_hash_table_new(NULL, NULL);
339         GList *l;
340         CamelFolder *folder;
341
342         /* setup a table of all folders we have currently */
343         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
344         l = p->folders;
345         while (l) {
346                 g_hash_table_insert(remove, l->data, l->data);
347                 camel_object_ref((CamelObject *)l->data);
348                 l = l->next;
349         }
350         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
351
352         /* if we already have the folder, ignore it, otherwise add it */
353         l = folders;
354         while (l) {
355                 if ((folder = g_hash_table_lookup(remove, l->data))) {
356                         g_hash_table_remove(remove, folder);
357                         camel_object_unref((CamelObject *)folder);
358                 } else {
359                         camel_vee_folder_add_folder(vf, l->data);
360                 }
361                 l = l->next;
362         }
363
364         /* then remove any we still have */
365         g_hash_table_foreach(remove, (GHFunc)remove_folders, vf);
366         g_hash_table_destroy(remove);
367 }
368
369 /**
370  * camel_vee_folder_hash_folder:
371  * @folder: 
372  * @: 
373  * 
374  * Create a hash string representing the folder name, which should be
375  * unique, and remain static for a given folder.
376  **/
377 void
378 camel_vee_folder_hash_folder(CamelFolder *folder, char buffer[8])
379 {
380         MD5Context ctx;
381         unsigned char digest[16];
382         unsigned int state = 0, save = 0;
383         char *tmp;
384         int i;
385
386         md5_init(&ctx);
387         tmp = camel_service_get_url((CamelService *)folder->parent_store);
388         md5_update(&ctx, tmp, strlen(tmp));
389         g_free(tmp);
390         md5_update(&ctx, folder->full_name, strlen(folder->full_name));
391         md5_final(&ctx, digest);
392         camel_base64_encode_close(digest, 6, FALSE, buffer, &state, &save);
393
394         for (i=0;i<8;i++) {
395                 if (buffer[i] == '+')
396                         buffer[i] = '.';
397                 if (buffer[i] == '/')
398                         buffer[i] = '_';
399         }
400 }
401
402 /**
403  * camel_vee_folder_get_location:
404  * @vf: 
405  * @vinfo: 
406  * @realuid: if not NULL, set to the uid of the real message, must be
407  * g_free'd by caller.
408  * 
409  * Find the real folder (and uid)
410  * 
411  * Return value: 
412  **/
413 CamelFolder *
414 camel_vee_folder_get_location(CamelVeeFolder *vf, const CamelVeeMessageInfo *vinfo, char **realuid)
415 {
416         CamelFolder *folder;
417
418         folder = vinfo->real->summary->folder;
419
420         /* locking?  yes?  no?  although the vfolderinfo is valid when obtained
421            the folder in it might not necessarily be so ...? */
422         if (CAMEL_IS_VEE_FOLDER(folder)) {
423                 CamelFolder *res;
424                 const CamelVeeMessageInfo *vfinfo;
425
426                 vfinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info(folder, camel_message_info_uid(vinfo)+8);
427                 res = camel_vee_folder_get_location((CamelVeeFolder *)folder, vfinfo, realuid);
428                 camel_folder_free_message_info(folder, (CamelMessageInfo *)vfinfo);
429                 return res;
430         } else {
431                 if (realuid)
432                         *realuid = g_strdup(camel_message_info_uid(vinfo)+8);
433
434                 return folder;
435         }
436 }
437
438 static void vee_refresh_info(CamelFolder *folder, CamelException *ex)
439 {
440         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
441         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
442         GList *node, *list;
443
444         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
445         list = p->folders_changed;
446         p->folders_changed = NULL;
447         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
448
449         node = list;
450         while (node) {
451                 CamelFolder *f = node->data;
452
453                 if (camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
454                         break;
455
456                 node = node->next;
457         }
458
459         g_list_free(list);
460 }
461
462 static void
463 vee_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
464 {
465         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
466         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
467         GList *node;
468
469         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
470
471         node = p->folders;
472         while (node) {
473                 CamelFolder *f = node->data;
474
475                 camel_folder_sync(f, expunge, ex);
476                 if (camel_exception_is_set(ex)) {
477                         char *desc;
478
479                         camel_object_get(f, NULL, CAMEL_OBJECT_DESCRIPTION, &desc, NULL);
480                         camel_exception_setv(ex, ex->id, _("Error storing `%s': %s"), desc, ex->desc);
481                         break;
482                 }
483
484                 /* auto update vfolders shouldn't need a rebuild */
485                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
486                     && camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
487                         break;
488
489                 node = node->next;
490         }
491
492         if (node == NULL) {
493                 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
494                 g_list_free(p->folders_changed);
495                 p->folders_changed = NULL;
496                 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
497         }
498
499         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
500
501         camel_object_state_write(vf);
502 }
503
504 static void
505 vee_expunge (CamelFolder *folder, CamelException *ex)
506 {
507         ((CamelFolderClass *)((CamelObject *)folder)->klass)->sync(folder, TRUE, ex);
508 }
509
510 static CamelMimeMessage *
511 vee_get_message(CamelFolder *folder, const char *uid, CamelException *ex)
512 {
513         CamelVeeMessageInfo *mi;
514         CamelMimeMessage *msg = NULL;
515
516         mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
517         if (mi) {
518                 msg =  camel_folder_get_message(mi->real->summary->folder, camel_message_info_uid(mi)+8, ex);
519                 camel_message_info_free((CamelMessageInfo *)mi);
520         } else {
521                 camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
522                                      _("No such message %s in %s"), uid,
523                                      folder->name);
524         }
525
526         return msg;
527 }
528
529 static GPtrArray *
530 vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
531 {
532         GList *node;
533         GPtrArray *matches, *result = g_ptr_array_new ();
534         char *expr;
535         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
536         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
537         GHashTable *searched = g_hash_table_new(NULL, NULL);
538         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
539         
540         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
541         
542         if (vf != folder_unmatched)
543                 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
544         else
545                 expr = g_strdup (expression);
546         
547         node = p->folders;
548         while (node) {
549                 CamelFolder *f = node->data;
550                 int i;
551                 char hash[8];
552                 
553                 /* make sure we only search each folder once - for unmatched folder to work right */
554                 if (g_hash_table_lookup(searched, f) == NULL) {
555                         camel_vee_folder_hash_folder(f, hash);
556                         /* FIXME: shouldn't ignore search exception */
557                         matches = camel_folder_search_by_expression(f, expression, NULL);
558                         if (matches) {
559                                 for (i = 0; i < matches->len; i++) {
560                                         char *uid = matches->pdata[i], *vuid;
561
562                                         vuid = g_malloc(strlen(uid)+9);
563                                         memcpy(vuid, hash, 8);
564                                         strcpy(vuid+8, uid);
565                                         g_ptr_array_add(result, vuid);
566                                 }
567                                 camel_folder_search_free(f, matches);
568                         }
569                         g_hash_table_insert(searched, f, f);
570                 }
571                 node = g_list_next(node);
572         }
573
574         g_free(expr);
575         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
576
577         g_hash_table_destroy(searched);
578
579         return result;
580 }
581
582 static GPtrArray *
583 vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex)
584 {
585         GList *node;
586         GPtrArray *matches, *result = g_ptr_array_new ();
587         GPtrArray *folder_uids = g_ptr_array_new();
588         char *expr;
589         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
590         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
591         GHashTable *searched = g_hash_table_new(NULL, NULL);
592
593         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
594
595         expr = g_strdup_printf("(and %s %s)", vf->expression ? vf->expression : "", expression);
596         node = p->folders;
597         while (node) {
598                 CamelFolder *f = node->data;
599                 int i;
600                 char hash[8];
601                 
602                 /* make sure we only search each folder once - for unmatched folder to work right */
603                 if (g_hash_table_lookup(searched, f) == NULL) {
604                         camel_vee_folder_hash_folder(f, hash);
605
606                         /* map the vfolder uid's to the source folder uid's first */
607                         g_ptr_array_set_size(folder_uids, 0);
608                         for (i=0;i<uids->len;i++) {
609                                 char *uid = uids->pdata[i];
610
611                                 if (strlen(uid) >= 8 && strncmp(uid, hash, 8) == 0)
612                                         g_ptr_array_add(folder_uids, uid+8);
613                         }
614                         if (folder_uids->len > 0) {
615                                 matches = camel_folder_search_by_uids(f, expression, folder_uids, ex);
616                                 if (matches) {
617                                         for (i = 0; i < matches->len; i++) {
618                                                 char *uid = matches->pdata[i], *vuid;
619                                 
620                                                 vuid = g_malloc(strlen(uid)+9);
621                                                 memcpy(vuid, hash, 8);
622                                                 strcpy(vuid+8, uid);
623                                                 g_ptr_array_add(result, vuid);
624                                         }
625                                         camel_folder_search_free(f, matches);
626                                 } else {
627                                         g_warning("Search failed: %s", camel_exception_get_description(ex));
628                                 }
629                         }
630                         g_hash_table_insert(searched, f, f);
631                 }
632                 node = g_list_next(node);
633         }
634
635         g_free(expr);
636         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
637
638         g_hash_table_destroy(searched);
639         g_ptr_array_free(folder_uids, TRUE);
640
641         return result;
642 }
643
644 static void
645 vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex)
646 {
647         camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
648 }
649
650 static void
651 vee_transfer_messages_to (CamelFolder *folder, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
652 {
653         camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
654 }
655
656 static void vee_rename(CamelFolder *folder, const char *new)
657 {
658         /*CamelVeeFolder *vf = (CamelVeeFolder *)folder;*/
659
660         ((CamelFolderClass *)camel_vee_folder_parent)->rename(folder, new);
661 }
662
663 static void vee_delete(CamelFolder *folder)
664 {
665         struct _CamelVeeFolderPrivate *p = _PRIVATE(folder);
666
667         /* NB: this is never called on UNMTACHED */
668
669         CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
670         while (p->folders) {
671                 CamelFolder *f = p->folders->data;
672
673                 camel_object_ref(f);
674                 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
675
676                 camel_vee_folder_remove_folder((CamelVeeFolder *)folder, f);
677                 camel_object_unref(f);
678                 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
679         }
680         CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
681
682         ((CamelFolderClass *)camel_vee_folder_parent)->delete(folder);
683 }
684
685 /* ********************************************************************** *
686    utility functions */
687
688 /* must be called with summary_lock held */
689 static CamelVeeMessageInfo *
690 vee_folder_add_uid(CamelVeeFolder *vf, CamelFolder *f, const char *inuid, const char hash[8])
691 {
692         CamelMessageInfo *info;
693         CamelVeeMessageInfo *mi = NULL;
694
695         info = camel_folder_get_message_info(f, inuid);
696         if (info) {
697                 mi = camel_vee_summary_add((CamelVeeSummary *)((CamelFolder *)vf)->summary, info, hash);
698                 camel_folder_free_message_info(f, info);
699         }
700         return mi;
701 }
702
703 static void
704 vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source)
705 {
706         int i, count, n, still = FALSE, start, last;
707         char *oldkey;
708         CamelFolder *folder = (CamelFolder *)vf;
709         char hash[8];
710         /*struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);*/
711         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
712         void *oldval;
713         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
714         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
715         CamelFolderSummary *ssummary = source->summary;
716         int killun = FALSE;
717
718         if (vf == folder_unmatched)
719                 return;
720
721         if ((source->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
722                 killun = TRUE;
723
724         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
725
726         if (folder_unmatched != NULL) {
727                 /* check if this folder is still to be part of unmatched */
728                 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !killun) {
729                         CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
730                         still = g_list_find(_PRIVATE(folder_unmatched)->folders, source) != NULL;
731                         CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
732                         camel_vee_folder_hash_folder(source, hash);
733                 }
734
735                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
736
737                 /* See if we just blow all uid's from this folder away from unmatched, regardless */
738                 if (killun) {
739                         start = -1;
740                         last = -1;
741                         count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
742                         for (i=0;i<count;i++) {
743                                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
744                                 
745                                 if (mi) {
746                                         if (mi->real->summary == ssummary) {
747                                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
748                                                 if (last == -1) {
749                                                         last = start = i;
750                                                 } else if (last+1 == i) {
751                                                         last = i;
752                                                 } else {
753                                                         camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
754                                                         i -= (last-start)+1;
755                                                         start = last = i;
756                                                 }
757                                         }
758                                         camel_message_info_free((CamelMessageInfo *)mi);
759                                 }
760                         }
761                         if (last != -1)
762                                 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
763                 }
764         }
765
766         start = -1;
767         last = -1;
768         count = camel_folder_summary_count(folder->summary);
769         for (i=0;i<count;i++) {
770                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
771                 if (mi) {
772                         if (mi->real->summary == ssummary) {
773                                 const char *uid = camel_message_info_uid(mi);
774
775                                 camel_folder_change_info_remove_uid(vf->changes, uid);
776
777                                 if (last == -1) {
778                                         last = start = i;
779                                 } else if (last+1 == i) {
780                                         last = i;
781                                 } else {
782                                         camel_folder_summary_remove_range(folder->summary, start, last);
783                                         i -= (last-start)+1;
784                                         start = last = i;
785                                 }
786                                 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && folder_unmatched != NULL) {
787                                         if (still) {
788                                                 if (g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
789                                                         n = GPOINTER_TO_INT (oldval);
790                                                         if (n == 1) {
791                                                                 g_hash_table_remove(unmatched_uids, oldkey);
792                                                                 if (vee_folder_add_uid(folder_unmatched, source, oldkey+8, hash))
793                                                                         camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
794                                                                 g_free(oldkey);
795                                                         } else {
796                                                                 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
797                                                         }
798                                                 }
799                                         } else {
800                                                 if (g_hash_table_lookup_extended(unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, &oldval)) {
801                                                         g_hash_table_remove(unmatched_uids, oldkey);
802                                                         g_free(oldkey);
803                                                 }
804                                         }
805                                 }
806                         }
807                         camel_message_info_free((CamelMessageInfo *)mi);
808                 }
809         }
810
811         if (last != -1)
812                 camel_folder_summary_remove_range(folder->summary, start, last);
813
814         if (folder_unmatched) {
815                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
816                         unmatched_changes = folder_unmatched->changes;
817                         folder_unmatched->changes = camel_folder_change_info_new();
818                 }
819
820                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
821         }
822
823         if (camel_folder_change_info_changed(vf->changes)) {
824                 vf_changes = vf->changes;
825                 vf->changes = camel_folder_change_info_new();
826         }
827
828         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
829
830         if (unmatched_changes) {
831                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
832                 camel_folder_change_info_free(unmatched_changes);
833         }
834
835         if (vf_changes) {
836                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
837                 camel_folder_change_info_free(vf_changes);
838         }
839 }
840
841 struct _update_data {
842         CamelFolder *source;
843         CamelVeeFolder *vf;
844         char hash[8];
845         CamelVeeFolder *folder_unmatched;
846         GHashTable *unmatched_uids;
847 };
848
849 static void
850 unmatched_check_uid(char *uidin, void *value, struct _update_data *u)
851 {
852         char *uid;
853         int n;
854
855         uid = alloca(strlen(uidin)+9);
856         memcpy(uid, u->hash, 8);
857         strcpy(uid+8, uidin);
858         n = GPOINTER_TO_INT(g_hash_table_lookup(u->unmatched_uids, uid));
859         if (n == 0) {
860                 if (vee_folder_add_uid(u->folder_unmatched, u->source, uidin, u->hash))
861                         camel_folder_change_info_add_uid(u->folder_unmatched->changes, uid);
862         } else {
863                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)u->folder_unmatched)->summary, uid);
864                 if (mi) {
865                         camel_folder_summary_remove(((CamelFolder *)u->folder_unmatched)->summary, (CamelMessageInfo *)mi);
866                         camel_folder_change_info_remove_uid(u->folder_unmatched->changes, uid);
867                         camel_message_info_free((CamelMessageInfo *)mi);
868                 }
869         }
870 }
871
872 static void
873 folder_added_uid(char *uidin, void *value, struct _update_data *u)
874 {
875         CamelVeeMessageInfo *mi;
876         char *oldkey;
877         void *oldval;
878         int n;
879
880         if ( (mi = vee_folder_add_uid(u->vf, u->source, uidin, u->hash)) ) {
881                 camel_folder_change_info_add_uid(u->vf->changes, camel_message_info_uid(mi));
882
883                 if (!CAMEL_IS_VEE_FOLDER(u->source) && u->unmatched_uids != NULL) {
884                         if (g_hash_table_lookup_extended(u->unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, &oldval)) {
885                                 n = GPOINTER_TO_INT (oldval);
886                                 g_hash_table_insert(u->unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
887                         } else {
888                                 g_hash_table_insert(u->unmatched_uids, g_strdup(camel_message_info_uid(mi)), GINT_TO_POINTER(1));
889                         }
890                 }
891         }
892 }
893
894 /* build query contents for a single folder */
895 static int
896 vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex)
897 {
898         GPtrArray *match, *all;
899         GHashTable *allhash, *matchhash;
900         CamelFolder *f = source;
901         CamelFolder *folder = (CamelFolder *)vf;
902         int i, n, count, start, last;
903         struct _update_data u;
904         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
905         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
906         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
907         CamelFolderSummary *ssummary = source->summary;
908
909         if (vf == folder_unmatched)
910                 return 0;
911
912         /* if we have no expression, or its been cleared, then act as if no matches */
913         if (vf->expression == NULL) {
914                 match = g_ptr_array_new();
915         } else {
916                 match = camel_folder_search_by_expression(f, vf->expression, ex);
917                 if (match == NULL)
918                         return -1;
919         }
920
921         u.source = source;
922         u.vf = vf;
923         u.folder_unmatched = folder_unmatched;
924         u.unmatched_uids = unmatched_uids;
925         camel_vee_folder_hash_folder(source, u.hash);
926
927         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
928
929         /* we build 2 hash tables, one for all uid's not matched, the other for all matched uid's,
930            we just ref the real memory */
931         matchhash = g_hash_table_new(g_str_hash, g_str_equal);
932         for (i=0;i<match->len;i++)
933                 g_hash_table_insert(matchhash, match->pdata[i], GINT_TO_POINTER (1));
934
935         allhash = g_hash_table_new(g_str_hash, g_str_equal);
936         all = camel_folder_get_uids(f);
937         for (i=0;i<all->len;i++)
938                 if (g_hash_table_lookup(matchhash, all->pdata[i]) == NULL)
939                         g_hash_table_insert(allhash, all->pdata[i], GINT_TO_POINTER (1));
940
941         if (folder_unmatched != NULL)
942                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
943
944         /* scan, looking for "old" uid's to be removed */
945         start = -1;
946         last = -1;
947         count = camel_folder_summary_count(folder->summary);
948         for (i=0;i<count;i++) {
949                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
950
951                 if (mi) {
952                         if (mi->real->summary == ssummary) {
953                                 char *uid = (char *)camel_message_info_uid(mi), *oldkey;
954                                 void *oldval;
955                                 
956                                 if (g_hash_table_lookup(matchhash, uid+8) == NULL) {
957                                         if (last == -1) {
958                                                 last = start = i;
959                                         } else if (last+1 == i) {
960                                                 last = i;
961                                         } else {
962                                                 camel_folder_summary_remove_range(folder->summary, start, last);
963                                                 i -= (last-start)+1;
964                                                 start = last = i;
965                                         }
966                                         camel_folder_change_info_remove_uid(vf->changes, camel_message_info_uid(mi));
967                                         if (!CAMEL_IS_VEE_FOLDER(source)
968                                             && unmatched_uids != NULL
969                                             && g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
970                                                 n = GPOINTER_TO_INT (oldval);
971                                                 if (n == 1) {
972                                                         g_hash_table_remove(unmatched_uids, oldkey);
973                                                         g_free(oldkey);
974                                                 } else {
975                                                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
976                                                 }
977                                         }
978                                 } else {
979                                         g_hash_table_remove(matchhash, uid+8);
980                                 }
981                         }
982                         camel_message_info_free((CamelMessageInfo *)mi);
983                 }
984         }
985         if (last != -1)
986                 camel_folder_summary_remove_range(folder->summary, start, last);
987
988         /* now matchhash contains any new uid's, add them, etc */
989         g_hash_table_foreach(matchhash, (GHFunc)folder_added_uid, &u);
990
991         if (folder_unmatched != NULL) {
992                 /* scan unmatched, remove any that have vanished, etc */
993                 count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
994                 for (i=0;i<count;i++) {
995                         CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
996
997                         if (mi) {
998                                 if (mi->real->summary == ssummary) {
999                                         char *uid = (char *)camel_message_info_uid(mi);
1000
1001                                         if (g_hash_table_lookup(allhash, uid+8) == NULL) {
1002                                                 /* no longer exists at all, just remove it entirely */
1003                                                 camel_folder_summary_remove_index(((CamelFolder *)folder_unmatched)->summary, i);
1004                                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
1005                                                 i--;
1006                                         } else {
1007                                                 g_hash_table_remove(allhash, uid+8);
1008                                         }
1009                                 }
1010                                 camel_message_info_free((CamelMessageInfo *)mi);
1011                         }
1012                 }
1013
1014                 /* now allhash contains all potentially new uid's for the unmatched folder, process */
1015                 if (!CAMEL_IS_VEE_FOLDER(source))
1016                         g_hash_table_foreach(allhash, (GHFunc)unmatched_check_uid, &u);
1017
1018                 /* copy any changes so we can raise them outside the lock */
1019                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1020                         unmatched_changes = folder_unmatched->changes;
1021                         folder_unmatched->changes = camel_folder_change_info_new();
1022                 }
1023
1024                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1025         }
1026
1027         if (camel_folder_change_info_changed(vf->changes)) {
1028                 vf_changes = vf->changes;
1029                 vf->changes = camel_folder_change_info_new();
1030         }
1031
1032         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1033
1034         g_hash_table_destroy(matchhash);
1035         g_hash_table_destroy(allhash);
1036         /* if expression not set, we only had a null list */
1037         if (vf->expression == NULL)
1038                 g_ptr_array_free(match, TRUE);
1039         else
1040                 camel_folder_search_free(f, match);
1041         camel_folder_free_uids(f, all);
1042
1043         if (unmatched_changes) {
1044                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1045                 camel_folder_change_info_free(unmatched_changes);
1046         }
1047
1048         if (vf_changes) {
1049                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1050                 camel_folder_change_info_free(vf_changes);
1051         }
1052
1053         return 0;
1054 }
1055
1056 /*
1057
1058   (match-folder "folder1" "folder2")
1059
1060  */
1061
1062
1063 /* Hold all these with summary lock and unmatched summary lock held */
1064 static void
1065 folder_changed_add_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
1066 {
1067         CamelVeeMessageInfo *vinfo;
1068         const char *vuid;
1069         char *oldkey;
1070         void *oldval;
1071         int n;
1072         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1073         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1074
1075         vinfo = vee_folder_add_uid(vf, sub, uid, hash);
1076         if (vinfo == NULL)
1077                 return;
1078         
1079         vuid = camel_message_info_uid(vinfo);
1080         camel_folder_change_info_add_uid(vf->changes,  vuid);
1081
1082         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1083                 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1084                         n = GPOINTER_TO_INT (oldval);
1085                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
1086                 } else {
1087                         g_hash_table_insert(unmatched_uids, g_strdup(vuid), GINT_TO_POINTER (1));
1088                 }
1089                 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1090                 if (vinfo) {
1091                         camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1092                         camel_folder_summary_remove(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)vinfo);
1093                         camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1094                 }
1095         }
1096 }
1097
1098 static void
1099 folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf)
1100 {
1101         CamelFolder *folder = (CamelFolder *)vf;
1102         char *vuid, *oldkey;
1103         void *oldval;
1104         int n;
1105         CamelVeeMessageInfo *vinfo;
1106         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1107         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1108
1109         vuid = alloca(strlen(uid)+9);
1110         memcpy(vuid, hash, 8);
1111         strcpy(vuid+8, uid);
1112
1113         vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1114         if (vinfo) {
1115                 camel_folder_change_info_remove_uid(vf->changes, vuid);
1116                 camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)vinfo);
1117                 camel_message_info_free((CamelMessageInfo *)vinfo);
1118         }
1119
1120         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1121                 if (keep) {
1122                         if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1123                                 n = GPOINTER_TO_INT (oldval);
1124                                 if (n == 1) {
1125                                         g_hash_table_remove(unmatched_uids, oldkey);
1126                                         if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1127                                                 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1128                                         g_free(oldkey);
1129                                 } else {
1130                                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1131                                 }
1132                         } else {
1133                                 if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1134                                         camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1135                         }
1136                 } else {
1137                         if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1138                                 g_hash_table_remove(unmatched_uids, oldkey);
1139                                 g_free(oldkey);
1140                         }
1141
1142                         vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1143                         if (vinfo) {
1144                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1145                                 camel_folder_summary_remove_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
1146                                 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1147                         }
1148                 }
1149         }
1150 }
1151
1152 static void
1153 folder_changed_change_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
1154 {
1155         char *vuid;
1156         CamelVeeMessageInfo *vinfo, *uinfo = NULL;
1157         CamelMessageInfo *info;
1158         CamelFolder *folder = (CamelFolder *)vf;
1159         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1160
1161         vuid = alloca(strlen(uid)+9);
1162         memcpy(vuid, hash, 8);
1163         strcpy(vuid+8, uid);
1164
1165         vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1166         if (folder_unmatched != NULL)
1167                 uinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
1168         if (vinfo || uinfo) {
1169                 info = camel_folder_get_message_info(sub, uid);
1170                 if (info) {
1171                         if (vinfo) {
1172                                 camel_folder_change_info_change_uid(vf->changes, vuid);
1173                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1174                         }
1175
1176                         if (uinfo) {
1177                                 camel_folder_change_info_change_uid(folder_unmatched->changes, vuid);
1178                                 camel_message_info_free((CamelMessageInfo *)uinfo);
1179                         }
1180
1181                         camel_folder_free_message_info(sub, info);
1182                 } else {
1183                         if (vinfo) {
1184                                 folder_changed_remove_uid(sub, uid, hash, FALSE, vf);
1185                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1186                         }
1187                         if (uinfo)
1188                                 camel_message_info_free((CamelMessageInfo *)uinfo);
1189                 }
1190         }
1191 }
1192
1193 struct _folder_changed_msg {
1194         CamelSessionThreadMsg msg;
1195         CamelFolderChangeInfo *changes;
1196         CamelFolder *sub;
1197         CamelVeeFolder *vf;
1198 };
1199
1200 static void
1201 folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg)
1202 {
1203         struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1204         CamelFolder *sub = m->sub;
1205         CamelFolder *folder = (CamelFolder *)m->vf;
1206         CamelVeeFolder *vf = m->vf;
1207         CamelFolderChangeInfo *changes = m->changes;
1208         char *vuid = NULL, hash[8];
1209         const char *uid;
1210         CamelVeeMessageInfo *vinfo;
1211         int i, vuidlen = 0;
1212         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
1213         GPtrArray *matches_added = NULL, /* newly added, that match */
1214                 *matches_changed = NULL, /* newly changed, that now match */
1215                 *newchanged = NULL,
1216                 *changed;
1217         GPtrArray *always_changed = NULL;
1218         GHashTable *matches_hash;
1219         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1220         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1221
1222         /* Check the folder hasn't beem removed while we weren't watching */
1223         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1224         if (g_list_find(_PRIVATE(vf)->folders, sub) == NULL) {
1225                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1226                 return;
1227         }
1228
1229         camel_vee_folder_hash_folder(sub, hash);
1230
1231         /* Lookup anything before we lock anything, to avoid deadlock with build_folder */
1232
1233         /* Find newly added that match */
1234         if (changes->uid_added->len > 0) {
1235                 dd(printf(" Searching for added matches '%s'\n", vf->expression));
1236                 matches_added = camel_folder_search_by_uids(sub, vf->expression, changes->uid_added, NULL);
1237         }
1238
1239         /* TODO:
1240            In this code around here, we can work out if the search will affect the changes
1241            we had, and only re-search against them if they might have */
1242
1243         /* Search for changed items that newly match, but only if we dont have them */
1244         changed = changes->uid_changed;
1245         if (changed->len > 0) {
1246                 dd(printf(" Searching for changed matches '%s'\n", vf->expression));
1247
1248                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
1249                         newchanged = g_ptr_array_new();
1250                         always_changed = g_ptr_array_new();
1251                         for (i=0;i<changed->len;i++) {
1252                                 uid = changed->pdata[i];
1253                                 if (strlen(uid)+9 > vuidlen) {
1254                                         vuidlen = strlen(uid)+64;
1255                                         vuid = g_realloc(vuid, vuidlen);
1256                                 }
1257                                 memcpy(vuid, hash, 8);
1258                                 strcpy(vuid+8, uid);
1259                                 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1260                                 if (vinfo == NULL) {
1261                                         g_ptr_array_add(newchanged, (char *)uid);
1262                                 } else {
1263                                         g_ptr_array_add(always_changed, (char *)uid);
1264                                         camel_message_info_free((CamelMessageInfo *)vinfo);
1265                                 }
1266                         }
1267                         changed = newchanged;
1268                 }
1269
1270                 if (changed->len)
1271                         matches_changed = camel_folder_search_by_uids(sub, vf->expression, changed, NULL);
1272         }
1273
1274         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1275         if (folder_unmatched != NULL)
1276                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1277
1278         dd(printf("Vfolder '%s' subfolder changed '%s'\n", folder->full_name, sub->full_name));
1279         dd(printf(" changed %u added %u removed %u\n", changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len));
1280
1281         /* Always remove removed uid's, in any case */
1282         for (i=0;i<changes->uid_removed->len;i++) {
1283                 dd(printf("  removing uid '%s'\n", (char *)changes->uid_removed->pdata[i]));
1284                 folder_changed_remove_uid(sub, changes->uid_removed->pdata[i], hash, FALSE, vf);
1285         }
1286
1287         /* Add any newly matched or to unmatched folder if they dont */
1288         if (matches_added) {
1289                 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1290                 for (i=0;i<matches_added->len;i++) {
1291                         dd(printf(" %s", (char *)matches_added->pdata[i]));
1292                         g_hash_table_insert(matches_hash, matches_added->pdata[i], matches_added->pdata[i]);
1293                 }
1294                 for (i=0;i<changes->uid_added->len;i++) {
1295                         uid = changes->uid_added->pdata[i];
1296                         if (g_hash_table_lookup(matches_hash, uid)) {
1297                                 dd(printf("  adding uid '%s' [newly matched]\n", (char *)uid));
1298                                 folder_changed_add_uid(sub, uid, hash, vf);
1299                         } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
1300                                 if (strlen(uid)+9 > vuidlen) {
1301                                         vuidlen = strlen(uid)+64;
1302                                         vuid = g_realloc(vuid, vuidlen);
1303                                 }
1304                                 memcpy(vuid, hash, 8);
1305                                 strcpy(vuid+8, uid);
1306                                 
1307                                 if (!CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL && g_hash_table_lookup(unmatched_uids, vuid) == NULL) {
1308                                         dd(printf("  adding uid '%s' to Unmatched [newly unmatched]\n", (char *)uid));
1309                                         vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1310                                         if (vinfo == NULL) {
1311                                                 if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1312                                                         camel_folder_change_info_add_uid(folder_unmatched->changes, vuid);
1313                                         } else {
1314                                                 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1315                                         }
1316                                 }
1317                         }
1318                 }
1319                 g_hash_table_destroy(matches_hash);
1320         }
1321
1322         /* Change any newly changed */
1323         if (always_changed) {
1324                 for (i=0;i<always_changed->len;i++)
1325                         folder_changed_change_uid(sub, always_changed->pdata[i], hash, vf);
1326                 g_ptr_array_free(always_changed, TRUE);
1327         }
1328
1329         /* Change/add/remove any changed */
1330         if (matches_changed) {
1331                 /* If we are auto-updating, then re-check changed uids still match */
1332                 dd(printf(" Vfolder %supdate\nuids match:", (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO)?"auto-":""));
1333                 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1334                 for (i=0;i<matches_changed->len;i++) {
1335                         dd(printf(" %s", (char *)matches_changed->pdata[i]));
1336                         g_hash_table_insert(matches_hash, matches_changed->pdata[i], matches_changed->pdata[i]);
1337                 }
1338                 dd(printf("\n"));
1339                 for (i=0;i<changed->len;i++) {
1340                         uid = changed->pdata[i];
1341                         if (strlen(uid)+9 > vuidlen) {
1342                                 vuidlen = strlen(uid)+64;
1343                                 vuid = g_realloc(vuid, vuidlen);
1344                         }
1345                         memcpy(vuid, hash, 8);
1346                         strcpy(vuid+8, uid);
1347                         vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1348                         if (vinfo == NULL) {
1349                                 if (g_hash_table_lookup(matches_hash, uid)) {
1350                                         /* A uid we dont have, but now it matches, add it */
1351                                         dd(printf("  adding uid '%s' [newly matched]\n", uid));
1352                                         folder_changed_add_uid(sub, uid, hash, vf);
1353                                 } else {
1354                                         /* A uid we still don't have, just change it (for unmatched) */
1355                                         folder_changed_change_uid(sub, uid, hash, vf);
1356                                 }
1357                         } else {
1358                                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
1359                                     || g_hash_table_lookup(matches_hash, uid)) {
1360                                         /* still match, or we're not auto-updating, change event, (if it changed) */
1361                                         dd(printf("  changing uid '%s' [still matches]\n", uid));
1362                                         folder_changed_change_uid(sub, uid, hash, vf);
1363                                 } else {
1364                                         /* No longer matches, remove it, but keep it in unmatched (potentially) */
1365                                         dd(printf("  removing uid '%s' [did match]\n", uid));
1366                                         folder_changed_remove_uid(sub, uid, hash, TRUE, vf);
1367                                 }
1368                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1369                         }
1370                 }
1371                 g_hash_table_destroy(matches_hash);
1372         } else {
1373                 /* stuff didn't match but it changed - check unmatched folder for changes */
1374                 for (i=0;i<changed->len;i++)
1375                         folder_changed_change_uid(sub, changed->pdata[i], hash, vf);
1376         }
1377
1378         if (folder_unmatched != NULL) {
1379                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1380                         unmatched_changes = folder_unmatched->changes;
1381                         folder_unmatched->changes = camel_folder_change_info_new();
1382                 }
1383                 
1384                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1385         }
1386
1387         if (camel_folder_change_info_changed(vf->changes)) {
1388                 vf_changes = vf->changes;
1389                 vf->changes = camel_folder_change_info_new();
1390         }
1391
1392         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1393
1394         /* Cleanup stuff on our folder */
1395         if (matches_added)
1396                 camel_folder_search_free(sub, matches_added);
1397
1398         if (matches_changed)
1399                 camel_folder_search_free(sub, matches_changed);
1400
1401         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1402
1403         /* cleanup the rest */
1404         if (newchanged)
1405                 g_ptr_array_free(newchanged, TRUE);
1406
1407         g_free(vuid);
1408         
1409         if (unmatched_changes) {
1410                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1411                 camel_folder_change_info_free(unmatched_changes);
1412         }
1413         
1414         if (vf_changes) {
1415                 /* If not auto-updating, keep track of changed folders for later re-sync */
1416                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
1417                         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
1418                         if (g_list_find(vf->priv->folders_changed, sub) != NULL)
1419                                 vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub);
1420                         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
1421                 }
1422
1423                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1424                 camel_folder_change_info_free(vf_changes);
1425         }
1426 }
1427
1428 static void
1429 folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg)
1430 {
1431         struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1432
1433         camel_folder_change_info_free(m->changes);
1434         camel_object_unref((CamelObject *)m->vf);
1435         camel_object_unref((CamelObject *)m->sub);
1436 }
1437
1438 static CamelSessionThreadOps folder_changed_ops = {
1439         folder_changed_change,
1440         folder_changed_free,
1441 };
1442
1443 static void
1444 folder_changed_base(CamelVeeFolder *vf, CamelFolder *sub, CamelFolderChangeInfo *changes)
1445 {
1446         struct _folder_changed_msg *m;
1447         CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session;
1448         
1449         m = camel_session_thread_msg_new(session, &folder_changed_ops, sizeof(*m));
1450         m->changes = camel_folder_change_info_new();
1451         camel_folder_change_info_cat(m->changes, changes);
1452         m->sub = sub;
1453         camel_object_ref((CamelObject *)sub);
1454         m->vf = vf;
1455         camel_object_ref((CamelObject *)vf);
1456         camel_session_thread_queue(session, &m->msg, 0);
1457 }
1458
1459 static void
1460 folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf)
1461 {
1462         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_changed(vf, sub, changes);
1463 }
1464
1465 /* track vanishing folders */
1466 static void
1467 subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf)
1468 {
1469         camel_vee_folder_remove_folder(vf, f);
1470 }
1471
1472 static void
1473 subfolder_renamed_update(CamelVeeFolder *vf, CamelFolder *sub, char hash[8])
1474 {
1475         int count, i;
1476         CamelFolderChangeInfo *changes = NULL;
1477         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1478         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1479         CamelFolderSummary *ssummary = sub->summary;
1480
1481         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1482
1483         count = camel_folder_summary_count(((CamelFolder *)vf)->summary);
1484         for (i=0;i<count;i++) {
1485                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)vf)->summary, i);
1486                 CamelVeeMessageInfo *vinfo;
1487
1488                 if (mi == NULL)
1489                         continue;
1490
1491                 if (mi->real->summary == ssummary) {
1492                         char *uid = (char *)camel_message_info_uid(mi);
1493                         char *oldkey;
1494                         void *oldval;
1495
1496                         camel_folder_change_info_remove_uid(vf->changes, uid);
1497                         camel_folder_summary_remove(((CamelFolder *)vf)->summary, (CamelMessageInfo *)mi);
1498
1499                         /* works since we always append on the end */
1500                         i--;
1501                         count--;
1502
1503                         vinfo = vee_folder_add_uid(vf, sub, uid+8, hash);
1504                         if (vinfo)
1505                                 camel_folder_change_info_add_uid(vf->changes, camel_message_info_uid(vinfo));
1506
1507                         /* check unmatched uid's table for any matches */
1508                         if (vf == folder_unmatched
1509                             && g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
1510                                 g_hash_table_remove(unmatched_uids, oldkey);
1511                                 g_hash_table_insert(unmatched_uids, g_strdup(camel_message_info_uid(vinfo)), oldval);
1512                                 g_free(oldkey);
1513                         }
1514                 }
1515
1516                 camel_message_info_free((CamelMessageInfo *)mi);
1517         }
1518
1519         if (camel_folder_change_info_changed(vf->changes)) {
1520                 changes = vf->changes;
1521                 vf->changes = camel_folder_change_info_new();
1522         }
1523
1524         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1525
1526         if (changes) {
1527                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", changes);
1528                 camel_folder_change_info_free(changes);
1529         }
1530 }
1531
1532 static void
1533 folder_renamed_base(CamelVeeFolder *vf, CamelFolder *f, const char *old)
1534 {
1535         char hash[8];
1536         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1537
1538         /* TODO: This could probably be done in another thread, tho it is pretty quick/memory bound */
1539
1540         /* Life just got that little bit harder, if the folder is renamed, it means it breaks all of our uid's.
1541            We need to remove the old uid's, fix them up, then release the new uid's, for the uid's that match this folder */
1542
1543         camel_vee_folder_hash_folder(f, hash);
1544
1545         subfolder_renamed_update(vf, f, hash);
1546         if (folder_unmatched != NULL)
1547                 subfolder_renamed_update(folder_unmatched, f, hash);
1548 }
1549
1550 static void
1551 folder_renamed(CamelFolder *sub, const char *old, CamelVeeFolder *vf)
1552 {
1553         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_renamed(vf, sub, old);
1554 }
1555
1556 static void
1557 vee_freeze (CamelFolder *folder)
1558 {
1559         CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1560         struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1561         GList *node;
1562         
1563         CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1564         
1565         node = p->folders;
1566         while (node) {
1567                 CamelFolder *f = node->data;
1568                 
1569                 camel_folder_freeze(f);
1570                 node = node->next;
1571         }
1572         
1573         CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1574         
1575         /* call parent implementation */
1576         CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->freeze(folder);
1577 }
1578
1579 static void
1580 vee_thaw(CamelFolder *folder)
1581 {
1582         CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1583         struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1584         GList *node;
1585         
1586         CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1587         
1588         node = p->folders;
1589         while (node) {
1590                 CamelFolder *f = node->data;
1591                 
1592                 camel_folder_thaw(f);
1593                 node = node->next;
1594         }
1595         
1596         CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1597         
1598         /* call parent implementation */
1599         CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->thaw(folder);
1600 }
1601
1602 /* vfolder base implementaitons */
1603 static void
1604 vee_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
1605 {
1606         vee_rebuild_folder(vf, sub, NULL);
1607 }
1608
1609 static void
1610 vee_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
1611 {
1612         vee_folder_remove_folder(vf, sub);
1613 }
1614
1615 static void
1616 vee_set_expression(CamelVeeFolder *vf, const char *query)
1617 {
1618         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1619         GList *node;
1620
1621         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1622
1623         /* no change, do nothing */
1624         if ((vf->expression && query && strcmp(vf->expression, query) == 0)
1625             || (vf->expression == NULL && query == NULL)) {
1626                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1627                 return;
1628         }
1629
1630         g_free(vf->expression);
1631         if (query)
1632                 vf->expression = g_strdup(query);
1633
1634         node = p->folders;
1635         while (node) {
1636                 CamelFolder *f = node->data;
1637
1638                 if (camel_vee_folder_rebuild_folder(vf, f, NULL) == -1)
1639                         break;
1640
1641                 node = node->next;
1642         }
1643
1644         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
1645         g_list_free(p->folders_changed);
1646         p->folders_changed = NULL;
1647         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
1648
1649         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1650 }
1651
1652 static void
1653 camel_vee_folder_class_init (CamelVeeFolderClass *klass)
1654 {
1655         CamelFolderClass *folder_class = (CamelFolderClass *) klass;
1656
1657         camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
1658
1659         folder_class->refresh_info = vee_refresh_info;
1660         folder_class->sync = vee_sync;
1661         folder_class->expunge = vee_expunge;
1662
1663         folder_class->get_message = vee_get_message;
1664         folder_class->append_message = vee_append_message;
1665         folder_class->transfer_messages_to = vee_transfer_messages_to;
1666
1667         folder_class->search_by_expression = vee_search_by_expression;
1668         folder_class->search_by_uids = vee_search_by_uids;
1669
1670         folder_class->rename = vee_rename;
1671         folder_class->delete = vee_delete;
1672
1673         folder_class->freeze = vee_freeze;
1674         folder_class->thaw = vee_thaw;
1675
1676         klass->set_expression = vee_set_expression;
1677         klass->add_folder = vee_add_folder;
1678         klass->remove_folder = vee_remove_folder;
1679         klass->rebuild_folder = vee_rebuild_folder;
1680         klass->folder_changed = folder_changed_base;
1681         klass->folder_renamed = folder_renamed_base;
1682 }
1683
1684 static void
1685 camel_vee_folder_init (CamelVeeFolder *obj)
1686 {
1687         struct _CamelVeeFolderPrivate *p;
1688         CamelFolder *folder = (CamelFolder *)obj;
1689
1690         p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
1691
1692         folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
1693                                  CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
1694
1695         /* FIXME: what to do about user flags if the subfolder doesn't support them? */
1696         folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
1697                 CAMEL_MESSAGE_DELETED |
1698                 CAMEL_MESSAGE_DRAFT |
1699                 CAMEL_MESSAGE_FLAGGED |
1700                 CAMEL_MESSAGE_SEEN;
1701
1702         obj->changes = camel_folder_change_info_new();
1703         obj->search = camel_folder_search_new();
1704         
1705         p->summary_lock = g_mutex_new();
1706         p->subfolder_lock = g_mutex_new();
1707         p->changed_lock = g_mutex_new();
1708 }
1709
1710 static void
1711 camel_vee_folder_finalise (CamelObject *obj)
1712 {
1713         CamelVeeFolder *vf = (CamelVeeFolder *)obj;
1714         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1715         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1716         GList *node;
1717
1718         /* TODO: there may be other leaks? */
1719
1720         /* This may invoke sub-classes with partially destroyed state, they must deal with this */
1721         if (vf == folder_unmatched) {
1722                 for (node = p->folders;node;node = g_list_next(node))
1723                         camel_object_unref(node->data);
1724         } else {
1725                 while (p->folders) {
1726                         CamelFolder *f = p->folders->data;
1727                         camel_vee_folder_remove_folder(vf, f);
1728                 }
1729         }
1730
1731         g_free(vf->expression);
1732         
1733         g_list_free(p->folders);
1734         g_list_free(p->folders_changed);
1735
1736         camel_folder_change_info_free(vf->changes);
1737         camel_object_unref((CamelObject *)vf->search);
1738         
1739         g_mutex_free(p->summary_lock);
1740         g_mutex_free(p->subfolder_lock);
1741         g_mutex_free(p->changed_lock);
1742         
1743         g_free(p);
1744 }