6647c17557e9466b3e38146107039712ca038133
[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) 1999-2008 Novell, Inc. (www.novell.com)
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 "camel-db.h"
33 #include "camel-debug.h"
34 #include "camel-exception.h"
35 #include "camel-folder-search.h"
36 #include "camel-mime-message.h"
37 #include "camel-private.h"
38 #include "camel-session.h"
39 #include "camel-store.h"
40 #include "camel-vee-folder.h"
41 #include "camel-vee-store.h"    /* for open flags */
42 #include "camel-vee-summary.h"
43 #include "camel-string-utils.h"
44 #include "camel-vee-folder.h"
45 #include "camel-vtrash-folder.h"
46
47 #define d(x)
48 #define dd(x) (camel_debug("vfolder")?(x):0)
49
50 #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv)
51
52 #if 0
53 static void vee_refresh_info(CamelFolder *folder, CamelException *ex);
54
55 static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
56 static void vee_expunge (CamelFolder *folder, CamelException *ex);
57
58 static void vee_freeze(CamelFolder *folder);
59 static void vee_thaw(CamelFolder *folder);
60
61 static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
62 static void vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, gchar **appended_uid, CamelException *ex);
63 static void vee_transfer_messages_to(CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
64
65 static guint32 vee_count_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex);
66 static GPtrArray *vee_search_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex);
67 static GPtrArray *vee_search_by_uids(CamelFolder *folder, const gchar *expression, GPtrArray *uids, CamelException *ex);
68
69 static void vee_rename(CamelFolder *folder, const gchar *new);
70 #endif
71
72 static void camel_vee_folder_class_init (CamelVeeFolderClass *klass);
73 static void camel_vee_folder_init       (CamelVeeFolder *obj);
74 static void camel_vee_folder_finalise   (CamelObject *obj);
75
76 static gint vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex);
77 static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source);
78
79 static void folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf);
80 static void subfolder_deleted(CamelFolder *f, gpointer event_data, CamelVeeFolder *vf);
81 static void folder_renamed(CamelFolder *f, const gchar *old, CamelVeeFolder *vf);
82
83 static CamelFolderClass *camel_vee_folder_parent;
84
85 CamelType
86 camel_vee_folder_get_type (void)
87 {
88         static CamelType type = CAMEL_INVALID_TYPE;
89
90         if (type == CAMEL_INVALID_TYPE) {
91                 type = camel_type_register (camel_folder_get_type (), "CamelVeeFolder",
92                                             sizeof (CamelVeeFolder),
93                                             sizeof (CamelVeeFolderClass),
94                                             (CamelObjectClassInitFunc) camel_vee_folder_class_init,
95                                             NULL,
96                                             (CamelObjectInitFunc) camel_vee_folder_init,
97                                             (CamelObjectFinalizeFunc) camel_vee_folder_finalise);
98         }
99
100         return type;
101 }
102
103 void
104 camel_vee_folder_construct (CamelVeeFolder *vf, CamelStore *parent_store, const gchar *full, const gchar *name, guint32 flags)
105 {
106         CamelFolder *folder = (CamelFolder *)vf;
107
108         vf->flags = flags;
109         camel_folder_construct(folder, parent_store, full, name);
110
111         folder->summary = camel_vee_summary_new(folder);
112
113         if (CAMEL_IS_VEE_STORE(parent_store))
114                 vf->parent_vee_store = (CamelVeeStore *)parent_store;
115 }
116
117 /**
118  * camel_vee_folder_new:
119  * @parent_store: the parent CamelVeeStore
120  * @full: the full path to the vfolder.
121  * @flags: flags of some kind
122  *
123  * Create a new CamelVeeFolder object.
124  *
125  * Returns: A new CamelVeeFolder widget.
126  **/
127 CamelFolder *
128 camel_vee_folder_new(CamelStore *parent_store, const gchar *full, guint32 flags)
129 {
130         CamelVeeFolder *vf;
131         gchar *tmp;
132
133         if (CAMEL_IS_VEE_STORE(parent_store) && strcmp(full, CAMEL_UNMATCHED_NAME) == 0) {
134                 vf = ((CamelVeeStore *)parent_store)->folder_unmatched;
135                 camel_object_ref(vf);
136         } else {
137                 const gchar *name = strrchr(full, '/');
138
139                 if (name == NULL)
140                         name = full;
141                 else
142                         name++;
143                 vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
144                 camel_vee_folder_construct(vf, parent_store, full, name, flags);
145         }
146
147         d(printf("returning folder %s %p, count = %d\n", full, vf, camel_folder_get_message_count((CamelFolder *)vf)));
148
149         if (vf) {
150                 tmp = g_strdup_printf("%s/%s.cmeta", ((CamelService *)parent_store)->url->path, full);
151                 camel_object_set(vf, NULL, CAMEL_OBJECT_STATE_FILE, tmp, NULL);
152                 g_free(tmp);
153                 if (camel_object_state_read(vf) == -1) {
154                         /* setup defaults: we have none currently */
155                 }
156         }
157         return (CamelFolder *)vf;
158 }
159
160 void
161 camel_vee_folder_set_expression(CamelVeeFolder *vf, const gchar *query)
162 {
163         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->set_expression(vf, query);
164 }
165
166 /**
167  * camel_vee_folder_add_folder:
168  * @vf: Virtual Folder object
169  * @sub: source CamelFolder to add to @vf
170  *
171  * Adds @sub as a source folder to @vf.
172  **/
173 void
174 camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
175 {
176         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
177         gint i;
178         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
179
180         if (vf == (CamelVeeFolder *)sub) {
181                 g_warning("Adding a virtual folder to itself as source, ignored");
182                 return;
183         }
184
185         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
186
187         /* for normal vfolders we want only unique ones, for unmatched we want them all recorded */
188         if (g_list_find(p->folders, sub) == NULL) {
189                 camel_object_ref((CamelObject *)sub);
190                 p->folders = g_list_append(p->folders, sub);
191
192                 CAMEL_FOLDER_LOCK(vf, change_lock);
193
194                 /* update the freeze state of 'sub' to match our freeze state */
195                 for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
196                         camel_folder_freeze(sub);
197
198                 CAMEL_FOLDER_UNLOCK(vf, change_lock);
199         }
200         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
201                 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
202                 camel_object_ref((CamelObject *)sub);
203                 up->folders = g_list_append(up->folders, sub);
204
205                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
206
207                 /* update the freeze state of 'sub' to match Unmatched's freeze state */
208                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
209                         camel_folder_freeze(sub);
210
211                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
212         }
213
214         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
215
216         d(printf("camel_vee_folder_add_folder(%s, %s)\n", ((CamelFolder *)vf)->full_name, sub->full_name));
217
218         camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc)folder_changed, vf);
219         camel_object_hook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc)subfolder_deleted, vf);
220         camel_object_hook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc)folder_renamed, vf);
221
222         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->add_folder(vf, sub);
223
224 }
225
226 /**
227  * camel_vee_folder_remove_folder:
228  * @vf: Virtual Folder object
229  * @sub: source CamelFolder to remove from @vf
230  *
231  * Removed the source folder, @sub, from the virtual folder, @vf.
232  **/
233 void
234
235 camel_vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
236 {
237         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
238         gint i;
239         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
240
241         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
242
243         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
244         p->folders_changed = g_list_remove(p->folders_changed, sub);
245         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
246
247         if (g_list_find(p->folders, sub) == NULL) {
248                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
249                 return;
250         }
251
252         camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
253         camel_object_unhook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
254         camel_object_unhook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc) folder_renamed, vf);
255
256         p->folders = g_list_remove(p->folders, sub);
257
258         /* undo the freeze state that we have imposed on this source folder */
259         CAMEL_FOLDER_LOCK(vf, change_lock);
260         for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
261                 camel_folder_thaw(sub);
262         CAMEL_FOLDER_UNLOCK(vf, change_lock);
263
264         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
265
266         if (folder_unmatched != NULL) {
267                 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
268
269                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
270                 /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */
271                 if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
272                         while (g_list_find(up->folders, sub)) {
273                                 up->folders = g_list_remove(up->folders, sub);
274                                 camel_object_unref((CamelObject *)sub);
275
276                                 /* undo the freeze state that Unmatched has imposed on this source folder */
277                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
278                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
279                                         camel_folder_thaw(sub);
280                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
281                         }
282                 } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
283                         if (g_list_find(up->folders, sub) != NULL) {
284                                 up->folders = g_list_remove(up->folders, sub);
285                                 camel_object_unref((CamelObject *)sub);
286
287                                 /* undo the freeze state that Unmatched has imposed on this source folder */
288                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
289                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
290                                         camel_folder_thaw(sub);
291                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
292                         }
293                 }
294                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
295         }
296
297         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->remove_folder(vf, sub);
298
299         if (CAMEL_IS_VEE_FOLDER(sub))
300                 return;
301
302         camel_object_unref((CamelObject *)sub);
303 }
304
305 /**
306  * camel_vee_folder_rebuild_folder:
307  * @vf: Virtual Folder object
308  * @sub: source CamelFolder to add to @vf
309  * @ex: Exception.
310  *
311  * Rebuild the folder @sub, if it should be.
312  **/
313 gint
314 camel_vee_folder_rebuild_folder(CamelVeeFolder *vf, CamelFolder *sub, CamelException *ex)
315 {
316         return ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->rebuild_folder(vf, sub, ex);
317 }
318
319 static void
320 remove_folders(CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf)
321 {
322         camel_vee_folder_remove_folder(vf, folder);
323         camel_object_unref((CamelObject *)folder);
324 }
325
326 /**
327  * camel_vee_folder_set_folders:
328  * @vf:
329  * @folders:
330  *
331  * Set the whole list of folder sources on a vee folder.
332  **/
333 void
334 camel_vee_folder_set_folders(CamelVeeFolder *vf, GList *folders)
335 {
336         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
337         GHashTable *remove = g_hash_table_new(NULL, NULL);
338         GList *l;
339         CamelFolder *folder;
340
341         /* setup a table of all folders we have currently */
342         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
343         l = p->folders;
344         while (l) {
345                 g_hash_table_insert(remove, l->data, l->data);
346                 camel_object_ref((CamelObject *)l->data);
347                 l = l->next;
348         }
349         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
350
351         /* if we already have the folder, ignore it, otherwise add it */
352         l = folders;
353         while (l) {
354                 if ((folder = g_hash_table_lookup(remove, l->data))) {
355                         g_hash_table_remove(remove, folder);
356                         camel_object_unref((CamelObject *)folder);
357                 } else {
358                         camel_vee_folder_add_folder(vf, l->data);
359                 }
360                 l = l->next;
361         }
362
363         /* then remove any we still have */
364         g_hash_table_foreach(remove, (GHFunc)remove_folders, vf);
365         g_hash_table_destroy(remove);
366 }
367
368 /**
369  * camel_vee_folder_hash_folder:
370  * @folder:
371  * @:
372  *
373  * Create a hash string representing the folder name, which should be
374  * unique, and remain static for a given folder.
375  **/
376 void
377 camel_vee_folder_hash_folder(CamelFolder *folder, gchar buffer[8])
378 {
379         GChecksum *checksum;
380         guint8 *digest;
381         gsize length;
382         gint state = 0, save = 0;
383         gchar *tmp;
384         gint i;
385
386         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
387         digest = g_alloca (length);
388
389         checksum = g_checksum_new (G_CHECKSUM_MD5);
390         tmp = camel_service_get_url((CamelService *)folder->parent_store);
391         g_checksum_update (checksum, (guchar *) tmp, -1);
392         g_free (tmp);
393         tmp = folder->full_name;
394         g_checksum_update (checksum, (guchar *) tmp, -1);
395         g_checksum_get_digest (checksum, digest, &length);
396         g_checksum_free (checksum);
397
398         g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save);
399         g_base64_encode_close (FALSE, buffer, &state, &save);
400
401         for (i=0;i<8;i++) {
402                 if (buffer[i] == '+')
403                         buffer[i] = '.';
404                 if (buffer[i] == '/')
405                         buffer[i] = '_';
406         }
407 }
408
409 /**
410  * camel_vee_folder_get_location:
411  * @vf:
412  * @vinfo:
413  * @realuid: if not NULL, set to the uid of the real message, must be
414  * g_free'd by caller.
415  *
416  * Find the real folder (and uid)
417  *
418  * Returns:
419  **/
420 CamelFolder *
421 camel_vee_folder_get_location(CamelVeeFolder *vf, const CamelVeeMessageInfo *vinfo, gchar **realuid)
422 {
423         CamelFolder *folder;
424
425         folder = vinfo->summary->folder;
426
427         /* locking?  yes?  no?  although the vfolderinfo is valid when obtained
428            the folder in it might not necessarily be so ...? */
429         if (CAMEL_IS_VEE_FOLDER(folder)) {
430                 CamelFolder *res;
431                 const CamelVeeMessageInfo *vfinfo;
432
433                 vfinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info(folder, camel_message_info_uid(vinfo)+8);
434                 res = camel_vee_folder_get_location((CamelVeeFolder *)folder, vfinfo, realuid);
435                 camel_folder_free_message_info(folder, (CamelMessageInfo *)vfinfo);
436                 return res;
437         } else {
438                 if (realuid)
439                         *realuid = g_strdup(camel_message_info_uid(vinfo)+8);
440
441                 return folder;
442         }
443 }
444
445 static void vee_refresh_info(CamelFolder *folder, CamelException *ex)
446 {
447         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
448         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
449         GList *node, *list;
450
451         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
452         list = p->folders_changed;
453         p->folders_changed = NULL;
454         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
455
456         node = list;
457         while (node) {
458                 CamelFolder *f = node->data;
459
460                 if (camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
461                         break;
462
463                 node = node->next;
464         }
465
466         g_list_free(list);
467 }
468
469 static guint32
470 count_folder (CamelFolder *f, gchar *expr, CamelException *ex)
471 {
472         return camel_folder_count_by_expression(f, expr, ex);
473 }
474 static gint
475 count_result (CamelFolderSummary *summary, const gchar *query, CamelException *ex)
476 {
477         CamelFolder *folder = summary->folder;
478         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
479         guint32 count=0;
480         gchar *expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", query);
481         GList *node;
482         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
483
484         node = p->folders;
485         while (node) {
486                 CamelFolder *f = node->data;
487                 count += count_folder(f, expr, ex);
488                 node = node->next;
489         }
490
491         g_free(expr);
492         return count;
493 }
494
495 static  CamelFIRecord *
496 summary_header_to_db (CamelFolderSummary *s, CamelException *ex)
497 {
498         CamelFIRecord * record = g_new0 (CamelFIRecord, 1);
499         CamelDB *db;
500         gchar *table_name;
501         guint32 visible, unread, deleted, junked, junked_not_deleted;
502
503         /* We do this during write, so lets use write handle, though we gonna read */
504         db = s->folder->parent_store->cdb_w;
505         table_name = s->folder->full_name;
506
507         record->folder_name = table_name;
508
509         /* we always write out the current version */
510         record->version = 13;  /* FIXME: CAMEL_FOLDER_SUMMARY_VERSION; */
511         record->flags  = s->flags;
512         record->nextuid = s->nextuid;
513         record->time = s->time;
514
515         record->saved_count = s->uids->len;
516         camel_object_get(s->folder, NULL,
517                                  CAMEL_FOLDER_DELETED, &deleted,
518                                  CAMEL_FOLDER_VISIBLE, &visible,
519                                  CAMEL_FOLDER_JUNKED, &junked,
520                                  CAMEL_FOLDER_JUNKED_NOT_DELETED, &junked_not_deleted,
521                                  CAMEL_FOLDER_UNREAD, &unread, NULL);
522         if (1) { /* We always would do this. Just refactor the code again. */
523                 /*!(((CamelVeeSummary *) s)->force_counts) && !g_getenv("FORCE_VFOLDER_COUNT")) {*/
524                 /* We should be in sync always. so use the count. Don't search.*/
525                 record->junk_count = s->junk_count;
526                 record->deleted_count = s->deleted_count;
527                 record->unread_count = s->unread_count;
528
529                 if (((CamelVeeSummary *)s)->fake_visible_count)
530                         record->visible_count = ((CamelVeeSummary *)s)->fake_visible_count;
531                 else
532                         record->visible_count = s->visible_count;
533                 ((CamelVeeSummary *)s)->fake_visible_count = 0;
534
535                 record->jnd_count = s->junk_not_deleted_count;
536         } else {
537                 /* Either first time, or by force we search the count */
538                 s->junk_count = count_result (s, "(match-all (system-flag  \"junk\"))", ex);
539                 s->deleted_count = count_result (s, "(match-all (system-flag  \"deleted\"))", ex);
540                 s->unread_count = count_result (s, "(match-all (not (system-flag  \"Seen\")))", ex);
541                 s->visible_count = count_result (s, "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))", ex);
542                 s->junk_not_deleted_count = count_result (s, "(match-all (and (not (system-flag \"deleted\")) (system-flag \"junk\")))", ex);
543
544                 record->junk_count = s->junk_count;
545                 record->deleted_count = s->deleted_count;
546                 record->unread_count = s->unread_count;
547                 record->visible_count = s->visible_count;
548                 record->jnd_count = s->junk_not_deleted_count;
549         }
550
551         d(printf("%s %d %d %d %d %d\n", s->folder->full_name, record->junk_count, record->deleted_count, record->unread_count, record->visible_count, record->jnd_count));
552         return record;
553 }
554
555 static void
556 vee_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
557 {
558         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
559         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
560         GList *node;
561
562         if (((CamelVeeSummary *)folder->summary)->fake_visible_count)
563                 folder->summary->visible_count = ((CamelVeeSummary *)folder->summary)->fake_visible_count;
564         ((CamelVeeSummary *)folder->summary)->fake_visible_count = 0;
565
566         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
567
568         node = p->folders;
569         while (node) {
570                 CamelFolder *f = node->data;
571
572                 camel_folder_sync(f, expunge, ex);
573                 if (camel_exception_is_set(ex) && strncmp(camel_exception_get_description(ex), "no such table", 13)) {
574                         const gchar *desc;
575
576                         camel_object_get(f, NULL, CAMEL_OBJECT_DESCRIPTION, &desc, NULL);
577                         camel_exception_setv(ex, ex->id, _("Error storing '%s': %s"), desc, ex->desc);
578                         g_warning ("%s", camel_exception_get_description(ex));
579                 } else
580                         camel_exception_clear (ex);
581
582                 /* auto update vfolders shouldn't need a rebuild */
583 /*              if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0 */
584 /*                  && camel_vee_folder_rebuild_folder(vf, f, ex) == -1) */
585 /*                      break; */
586
587                 node = node->next;
588         }
589
590 #if 0
591         /* Seems like we are doing something wrong with this, as folder_changed happens after this, the counts are misleading.
592          * Anyways we do a force sync on exit, it should be all fine.
593           */
594         record = summary_header_to_db (folder->summary, ex);
595         camel_db_write_folder_info_record (folder->parent_store->cdb, record, ex);
596         g_free (record);
597 #endif
598         /* It makes no sense to clear the folders_changed list without
599          * actually rebuilding. */
600 #if 0
601         if (node == NULL) {
602                 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
603                 g_list_free(p->folders_changed);
604                 p->folders_changed = NULL;
605                 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
606         }
607 #endif
608         if (vf->priv->unread_vfolder == 1) {
609                 /* Cleanup Junk/Trash uids */
610                 GSList *del = NULL;
611                 gint i, count;
612                 count = folder->summary->uids->len;
613
614                 for (i=0; i < count; i++) {
615                         CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index (folder->summary, i);
616                         if (mi->old_flags & CAMEL_MESSAGE_DELETED) {
617                                 del = g_slist_prepend (del, (gpointer) camel_pstring_strdup(((CamelMessageInfo *)mi)->uid));
618                                 camel_folder_summary_remove_index_fast (folder->summary, i);
619                                 count--;
620                                 i--;
621
622                         }
623                         camel_message_info_free (mi);
624                 }
625                 camel_db_delete_vuids (folder->parent_store->cdb_w, folder->full_name, "", del, ex);
626                 g_slist_foreach (del, (GFunc) camel_pstring_free, NULL);
627                 g_slist_free (del);
628         }
629         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
630
631         camel_object_state_write(vf);
632 }
633
634 static void
635 vee_expunge (CamelFolder *folder, CamelException *ex)
636 {
637         /* Force it to rebuild the counts, when some folders were expunged. */
638         ((CamelVeeSummary *) folder->summary)->force_counts = TRUE;
639         ((CamelFolderClass *)((CamelObject *)folder)->klass)->sync(folder, TRUE, ex);
640 }
641
642 static CamelMimeMessage *
643 vee_get_message(CamelFolder *folder, const gchar *uid, CamelException *ex)
644 {
645         CamelVeeMessageInfo *mi;
646         CamelMimeMessage *msg = NULL;
647
648         mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
649         if (mi) {
650                 msg =  camel_folder_get_message(mi->summary->folder, camel_message_info_uid(mi)+8, ex);
651                 camel_message_info_free((CamelMessageInfo *)mi);
652         } else {
653                 camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
654                                      _("No such message %s in %s"), uid,
655                                      folder->name);
656         }
657
658         return msg;
659 }
660
661 static guint32
662 vee_count_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex)
663 {
664         GList *node;
665         gchar *expr;
666         guint32 count = 0;
667         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
668         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
669         GHashTable *searched = g_hash_table_new(NULL, NULL);
670         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
671
672         if (vf != folder_unmatched)
673                 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
674         else
675                 expr = g_strdup (expression);
676
677         node = p->folders;
678         while (node) {
679                 CamelFolder *f = node->data;
680
681                 /* make sure we only search each folder once - for unmatched folder to work right */
682                 if (g_hash_table_lookup(searched, f) == NULL) {
683                         count += camel_folder_count_by_expression(f, expr, ex);
684                         g_hash_table_insert(searched, f, f);
685                 }
686                 node = g_list_next(node);
687         }
688
689         g_free(expr);
690
691         g_hash_table_destroy(searched);
692         return count;
693 }
694 static GPtrArray *
695 vee_search_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex)
696 {
697         GList *node;
698         GPtrArray *matches, *result = g_ptr_array_new ();
699         gchar *expr;
700         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
701         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
702         GHashTable *searched = g_hash_table_new(NULL, NULL);
703         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
704
705         if (vf != folder_unmatched)
706                 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
707         else
708                 expr = g_strdup (expression);
709
710         node = p->folders;
711         while (node) {
712                 CamelFolder *f = node->data;
713                 gint i;
714                 gchar hash[8];
715
716                 /* make sure we only search each folder once - for unmatched folder to work right */
717                 if (g_hash_table_lookup(searched, f) == NULL) {
718                         camel_vee_folder_hash_folder(f, hash);
719                         matches = camel_folder_search_by_expression(f, expr, ex);
720                         if (camel_exception_is_set(ex) && strncmp(camel_exception_get_description(ex), "no such table", 13)) {
721                                 camel_exception_clear(ex);
722                         }
723                         if (matches) {
724                                 for (i = 0; i < matches->len; i++) {
725                                         gchar *uid = matches->pdata[i], *vuid;
726
727                                         vuid = g_malloc(strlen(uid)+9);
728                                         memcpy(vuid, hash, 8);
729                                         strcpy(vuid+8, uid);
730                                         g_ptr_array_add(result, (gpointer) camel_pstring_strdup(vuid));
731                                         g_free (vuid);
732                                 }
733                                 camel_folder_search_free(f, matches);
734                         }
735                         g_hash_table_insert(searched, f, f);
736                 }
737                 node = g_list_next(node);
738         }
739
740         g_free(expr);
741
742         g_hash_table_destroy(searched);
743         d(printf("returning %d\n", result->len));
744         return result;
745 }
746
747 static GPtrArray *
748 vee_search_by_uids(CamelFolder *folder, const gchar *expression, GPtrArray *uids, CamelException *ex)
749 {
750         GList *node;
751         GPtrArray *matches, *result = g_ptr_array_new ();
752         GPtrArray *folder_uids = g_ptr_array_new();
753         gchar *expr;
754         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
755         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
756         GHashTable *searched = g_hash_table_new(NULL, NULL);
757
758         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
759
760         expr = g_strdup_printf("(and %s %s)", vf->expression ? vf->expression : "", expression);
761         node = p->folders;
762         while (node) {
763                 CamelFolder *f = node->data;
764                 gint i;
765                 gchar hash[8];
766
767                 /* make sure we only search each folder once - for unmatched folder to work right */
768                 if (g_hash_table_lookup(searched, f) == NULL) {
769                         camel_vee_folder_hash_folder(f, hash);
770
771                         /* map the vfolder uid's to the source folder uid's first */
772                         g_ptr_array_set_size(folder_uids, 0);
773                         for (i=0;i<uids->len;i++) {
774                                 gchar *uid = uids->pdata[i];
775
776                                 if (strlen(uid) >= 8 && strncmp(uid, hash, 8) == 0)
777                                         g_ptr_array_add(folder_uids, uid+8);
778                         }
779                         if (folder_uids->len > 0) {
780                                 matches = camel_folder_search_by_uids(f, expr, folder_uids, ex);
781                                 if (matches) {
782                                         for (i = 0; i < matches->len; i++) {
783                                                 gchar *uid = matches->pdata[i], *vuid;
784
785                                                 vuid = g_malloc(strlen(uid)+9);
786                                                 memcpy(vuid, hash, 8);
787                                                 strcpy(vuid+8, uid);
788                                                 g_ptr_array_add(result, (gpointer) camel_pstring_strdup(vuid));
789                                                 g_free(vuid);
790                                         }
791                                         camel_folder_search_free(f, matches);
792                                 } else {
793                                         g_warning("Search failed: %s", camel_exception_get_description(ex));
794                                 }
795                         }
796                         g_hash_table_insert(searched, f, f);
797                 }
798                 node = g_list_next(node);
799         }
800
801         g_free(expr);
802         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
803
804         g_hash_table_destroy(searched);
805         g_ptr_array_free(folder_uids, TRUE);
806
807         return result;
808 }
809
810 static void
811 vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, gchar **appended_uid, CamelException *ex)
812 {
813         camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
814 }
815
816 static void
817 vee_transfer_messages_to (CamelFolder *folder, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
818 {
819         camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
820 }
821
822 static void vee_rename(CamelFolder *folder, const gchar *new)
823 {
824         /*CamelVeeFolder *vf = (CamelVeeFolder *)folder;*/
825
826         ((CamelFolderClass *)camel_vee_folder_parent)->rename(folder, new);
827 }
828
829 static void vee_delete(CamelFolder *folder)
830 {
831         struct _CamelVeeFolderPrivate *p = _PRIVATE(folder);
832
833         /* NB: this is never called on UNMTACHED */
834
835         CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
836         while (p->folders) {
837                 CamelFolder *f = p->folders->data;
838
839                 camel_object_ref(f);
840                 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
841
842                 camel_vee_folder_remove_folder((CamelVeeFolder *)folder, f);
843                 camel_object_unref(f);
844                 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
845         }
846         CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
847
848         ((CamelFolderClass *)camel_vee_folder_parent)->delete(folder);
849         ((CamelVeeFolder *)folder)->deleted = TRUE;
850 }
851
852 /* ********************************************************************** *
853    utility functions */
854
855 /* A "correlating" expression has the property that whether a message matches
856  * depends on the other messages being searched.  folder_changed_change on a
857  * vfolder with a correlating expression may not make all the necessary updates,
858  * so the query is redone on the entire changed source folder the next time
859  * the vfolder is opened.
860  *
861  * The only current example of a correlating expression is one that uses
862  * "match-threads". */
863 static gboolean
864 expression_is_correlating(const gchar *expr)
865 {
866         /* XXX: Actually parse the expression to avoid triggering on
867          * "match-threads" in the text the user is searching for! */
868         return (strstr(expr, "match-threads") != NULL);
869 }
870
871 /* must be called with summary_lock held */
872 static CamelVeeMessageInfo *
873 vee_folder_add_uid(CamelVeeFolder *vf, CamelFolder *f, const gchar *inuid, const gchar hash[8])
874 {
875         CamelVeeMessageInfo *mi = NULL;
876
877         mi = camel_vee_summary_add((CamelVeeSummary *)((CamelFolder *)vf)->summary, f->summary, (gchar *)inuid, hash);
878         return mi;
879 }
880
881 /* same as vee_folder_add_uid, only returns whether uid was added or not */
882 static gboolean
883 vee_folder_add_uid_test (CamelVeeFolder *vf, CamelFolder *f, const gchar *inuid, const gchar hash[8])
884 {
885         CamelVeeMessageInfo *mi;
886
887         mi = vee_folder_add_uid (vf, f, inuid, hash);
888
889         if (mi != NULL)
890                 camel_message_info_free ((CamelMessageInfo *) mi);
891
892         return mi != NULL;
893 }
894
895 static void
896 vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source)
897 {
898         gint i, count, n, still = FALSE, start, last;
899         gchar *oldkey;
900         CamelFolder *folder = (CamelFolder *)vf;
901         gchar hash[8];
902         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
903         gpointer oldval;
904         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
905         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
906         CamelFolderSummary *ssummary = source->summary;
907         gint killun = FALSE;
908
909         if (vf == folder_unmatched)
910                 return;
911
912         if ((source->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
913                 killun = TRUE;
914
915         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
916
917         if (folder_unmatched != NULL) {
918                 /* check if this folder is still to be part of unmatched */
919                 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !killun) {
920                         CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
921                         still = g_list_find(_PRIVATE(folder_unmatched)->folders, source) != NULL;
922                         CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
923                         camel_vee_folder_hash_folder(source, hash);
924                 }
925
926                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
927
928                 /* See if we just blow all uid's from this folder away from unmatched, regardless */
929                 if (killun) {
930                         start = -1;
931                         last = -1;
932                         count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
933                         for (i=0;i<count;i++) {
934                                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
935
936                                 if (mi) {
937                                         if (mi->summary == ssummary) {
938                                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
939                                                 if (last == -1) {
940                                                         last = start = i;
941                                                 } else if (last+1 == i) {
942                                                         last = i;
943                                                 } else {
944                                                         camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
945                                                         i -= (last-start)+1;
946                                                         start = last = i;
947                                                 }
948                                         }
949                                         camel_message_info_free((CamelMessageInfo *)mi);
950                                 }
951                         }
952                         if (last != -1)
953                                 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
954                 }
955         }
956
957         /*FIXME: This can be optimized a lot like, searching for UID in the summary uids */
958         start = -1;
959         last = -1;
960         count = camel_folder_summary_count(folder->summary);
961         for (i=0;i<count;i++) {
962                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
963                 if (mi) {
964                         if (mi->summary == ssummary) {
965                                 const gchar *uid = camel_message_info_uid(mi);
966
967                                 camel_folder_change_info_remove_uid(vf->changes, uid);
968
969                                 if (last == -1) {
970                                         last = start = i;
971                                 } else if (last+1 == i) {
972                                         last = i;
973                                 } else {
974                                         camel_folder_summary_remove_range(folder->summary, start, last);
975                                         i -= (last-start)+1;
976                                         start = last = i;
977                                 }
978                                 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && folder_unmatched != NULL) {
979                                         if (still) {
980                                                 if (g_hash_table_lookup_extended(unmatched_uids, uid, (gpointer *)&oldkey, &oldval)) {
981                                                         n = GPOINTER_TO_INT (oldval);
982                                                         if (n == 1) {
983                                                                 g_hash_table_remove(unmatched_uids, oldkey);
984                                                                 if (vee_folder_add_uid_test (folder_unmatched, source, oldkey+8, hash)) {
985                                                                         camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
986                                                                 }
987                                                                 g_free(oldkey);
988                                                         } else {
989                                                                 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
990                                                         }
991                                                 }
992                                         } else {
993                                                 if (g_hash_table_lookup_extended(unmatched_uids, camel_message_info_uid(mi), (gpointer *)&oldkey, &oldval)) {
994                                                         g_hash_table_remove(unmatched_uids, oldkey);
995                                                         g_free(oldkey);
996                                                 }
997                                         }
998                                 }
999                         }
1000                         camel_message_info_free((CamelMessageInfo *)mi);
1001                 }
1002         }
1003
1004         if (last != -1)
1005                 camel_folder_summary_remove_range(folder->summary, start, last);
1006
1007         if (folder_unmatched) {
1008                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1009                         unmatched_changes = folder_unmatched->changes;
1010                         folder_unmatched->changes = camel_folder_change_info_new();
1011                 }
1012
1013                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1014         }
1015
1016         if (camel_folder_change_info_changed(vf->changes)) {
1017                 vf_changes = vf->changes;
1018                 vf->changes = camel_folder_change_info_new();
1019         }
1020
1021         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1022
1023         if (unmatched_changes) {
1024                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1025                 camel_folder_change_info_free(unmatched_changes);
1026         }
1027
1028         if (vf_changes) {
1029                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1030                 camel_folder_change_info_free(vf_changes);
1031         }
1032 }
1033
1034 struct _update_data {
1035         CamelFolder *source;
1036         CamelVeeFolder *vf;
1037         gchar hash[8];
1038         CamelVeeFolder *folder_unmatched;
1039         GHashTable *unmatched_uids;
1040         gboolean rebuilt, correlating;
1041 };
1042
1043 static void
1044 unmatched_check_uid(gchar *uidin, gpointer value, struct _update_data *u)
1045 {
1046         gchar *uid;
1047         gint n;
1048
1049         uid = alloca(strlen(uidin)+9);
1050         memcpy(uid, u->hash, 8);
1051         strcpy(uid+8, uidin);
1052         n = GPOINTER_TO_INT(g_hash_table_lookup(u->unmatched_uids, uid));
1053         if (n == 0) {
1054                 if (vee_folder_add_uid_test (u->folder_unmatched, u->source, uidin, u->hash))
1055                         camel_folder_change_info_add_uid(u->folder_unmatched->changes, uid);
1056         } else {
1057                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)u->folder_unmatched)->summary, uid);
1058                 if (mi) {
1059                         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1060                         camel_db_delete_uid_from_vfolder_transaction (((CamelFolder *)u->folder_unmatched)->parent_store->cdb_w, ((CamelFolder *)u->folder_unmatched)->full_name, uid, &ex);
1061                         camel_folder_summary_remove_uid_fast (((CamelFolder *)u->folder_unmatched)->summary, uid);
1062                         camel_folder_change_info_remove_uid(u->folder_unmatched->changes, uid);
1063                         camel_message_info_free((CamelMessageInfo *)mi);
1064                         camel_exception_clear (&ex);
1065                 }
1066         }
1067 }
1068
1069 static void
1070 folder_added_uid(gchar *uidin, gpointer value, struct _update_data *u)
1071 {
1072         CamelVeeMessageInfo *mi;
1073         gchar *oldkey;
1074         gpointer oldval;
1075         gint n;
1076
1077         if ((mi = vee_folder_add_uid (u->vf, u->source, uidin, u->hash)) != NULL) {
1078                 camel_folder_change_info_add_uid(u->vf->changes, camel_message_info_uid(mi));
1079                 /* FIXME[disk-summary] Handle exceptions */
1080                 /* FIXME[disk-summary] Make all these as transactions, just
1081                  * testing atm */
1082                 if (u->rebuilt && !u->correlating) {
1083                         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1084                         camel_db_add_to_vfolder_transaction (((CamelFolder *) u->vf)->parent_store->cdb_w, ((CamelFolder *) u->vf)->full_name, camel_message_info_uid(mi), &ex);
1085                         camel_exception_clear (&ex);
1086                 }
1087                 if (!CAMEL_IS_VEE_FOLDER(u->source) && u->unmatched_uids != NULL) {
1088                         if (g_hash_table_lookup_extended(u->unmatched_uids, camel_message_info_uid(mi), (gpointer *)&oldkey, &oldval)) {
1089                                 n = GPOINTER_TO_INT (oldval);
1090                                 g_hash_table_insert(u->unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
1091                         } else {
1092                                 g_hash_table_insert(u->unmatched_uids, g_strdup(camel_message_info_uid(mi)), GINT_TO_POINTER(1));
1093                         }
1094                 }
1095
1096                 camel_message_info_free ((CamelMessageInfo *) mi);
1097         }
1098 }
1099
1100 /* build query contents for a single folder */
1101 static gint
1102 vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex)
1103 {
1104         GPtrArray *match, *all;
1105         GHashTable *allhash, *matchhash, *fullhash;
1106         GSList *del_list = NULL;
1107         CamelFolder *folder = (CamelFolder *)vf;
1108         gint i, n, count, start, last;
1109         struct _update_data u;
1110         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
1111         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1112         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1113         CamelFolderSummary *ssummary = source->summary;
1114         gboolean rebuilded = FALSE;
1115         gchar *shash;
1116
1117         /* Since the source of a correlating vfolder has to be requeried in
1118          * full every time it changes, caching the results in the db is not
1119          * worth the effort.  Thus, DB use is conditioned on !correlating. */
1120         gboolean correlating = expression_is_correlating(vf->expression);
1121
1122         if (vf == folder_unmatched)
1123                 return 0;
1124
1125         camel_vee_folder_hash_folder(source, u.hash);
1126         shash = g_strdup_printf("%c%c%c%c%c%c%c%c", u.hash[0], u.hash[1], u.hash[2], u.hash[3], u.hash[4], u.hash[5], u.hash[6], u.hash[7]);
1127         if (!g_hash_table_lookup (vf->hashes, shash)) {
1128                 g_hash_table_insert (vf->hashes, g_strdup(shash), source->summary);
1129         }
1130         if (folder_unmatched && !g_hash_table_lookup (folder_unmatched->hashes, shash)) {
1131                 g_hash_table_insert (folder_unmatched->hashes, g_strdup (shash), source->summary);
1132         }
1133
1134         /* if we have no expression, or its been cleared, then act as if no matches */
1135         if (vf->expression == NULL) {
1136                 match = g_ptr_array_new();
1137         } else {
1138                 if (!correlating) {
1139                         /* Load the folder results from the DB. */
1140                         match = camel_vee_summary_get_ids ((CamelVeeSummary *)folder->summary, u.hash);
1141                 }
1142                 if (correlating ||
1143                         /* We take this to mean the results have not been cached.
1144                          * XXX: It will also trigger if the result set is empty. */
1145                         match == NULL) {
1146                         match = camel_folder_search_by_expression(source, vf->expression, ex);
1147                         if (match == NULL) /* Search failed */
1148                                 return 0;
1149                         rebuilded = TRUE;
1150                 }
1151
1152         }
1153         dd(printf("vee_rebuild_folder(%s <- %s %s): match %d, correlating %d, rebuilded %d\n",
1154                 folder->full_name, source->full_name, shash, match->len, correlating, rebuilded));
1155
1156         u.source = source;
1157         u.vf = vf;
1158         u.folder_unmatched = folder_unmatched;
1159         u.unmatched_uids = unmatched_uids;
1160         u.rebuilt = rebuilded;
1161         u.correlating = correlating;
1162
1163         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1164
1165         /* we build 2 hash tables, one for all uid's not matched, the
1166            other for all matched uid's, we just ref the real memory */
1167         matchhash = g_hash_table_new(g_str_hash, g_str_equal);
1168         for (i=0;i<match->len;i++)
1169                 g_hash_table_insert(matchhash, match->pdata[i], GINT_TO_POINTER (1));
1170
1171         allhash = g_hash_table_new(g_str_hash, g_str_equal);
1172         fullhash = g_hash_table_new(g_str_hash, g_str_equal);
1173         all = camel_folder_summary_array(source->summary);
1174         for (i=0;i<all->len;i++) {
1175                 if (g_hash_table_lookup(matchhash, all->pdata[i]) == NULL)
1176                         g_hash_table_insert(allhash, all->pdata[i], GINT_TO_POINTER (1));
1177                 g_hash_table_insert(fullhash, all->pdata[i], GINT_TO_POINTER (1));
1178
1179         }
1180         /* remove uids that can't be found in the source folder */
1181         count = match->len;
1182         for (i=0; i<count; i++) {
1183                 if (!g_hash_table_lookup(fullhash, match->pdata[i])) {
1184                         g_hash_table_remove (matchhash, match->pdata[i]);
1185                         del_list = g_slist_prepend (del_list, match->pdata[i]); /* Free the original */
1186                         g_ptr_array_remove_index_fast (match, i);
1187                         i--;
1188                         count--;
1189                         continue;
1190                 }
1191         }
1192
1193         if (folder_unmatched != NULL)
1194                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1195
1196         /* scan, looking for "old" uid's to be removed. "old" uid's
1197            are those that are from previous added sources (not in
1198            current source) */
1199         start = -1;
1200         last = -1;
1201         count = camel_folder_summary_count(folder->summary);
1202         for (i=0;i<count;i++) {
1203                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
1204
1205                 if (mi) {
1206                         if (mi->summary == ssummary) {
1207                                 gchar *uid = (gchar *)camel_message_info_uid(mi), *oldkey;
1208                                 gpointer oldval;
1209
1210                                 if (g_hash_table_lookup(matchhash, uid+8) == NULL) {
1211                                         if (last == -1) {
1212                                                 last = start = i;
1213                                         } else if (last+1 == i) {
1214                                                 last = i;
1215                                         } else {
1216                                                 camel_folder_summary_remove_range(folder->summary, start, last);
1217                                                 i -= (last-start)+1;
1218                                                 start = last = i;
1219                                         }
1220                                         camel_folder_change_info_remove_uid(vf->changes, camel_message_info_uid(mi));
1221                                         if (!CAMEL_IS_VEE_FOLDER(source)
1222                                             && unmatched_uids != NULL
1223                                             && g_hash_table_lookup_extended(unmatched_uids, uid, (gpointer *)&oldkey, &oldval)) {
1224                                                 n = GPOINTER_TO_INT (oldval);
1225                                                 if (n == 1) {
1226                                                         g_hash_table_remove(unmatched_uids, oldkey);
1227                                                         g_free(oldkey);
1228                                                 } else {
1229                                                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1230                                                 }
1231                                         }
1232                                 } else {
1233                                         g_hash_table_remove(matchhash, uid+8);
1234                                 }
1235                         }
1236                         camel_message_info_free((CamelMessageInfo *)mi);
1237                 }
1238         }
1239         if (last != -1)
1240                 camel_folder_summary_remove_range(folder->summary, start, last);
1241
1242         /* now matchhash contains any new uid's, add them, etc */
1243         if (rebuilded && !correlating) {
1244                 camel_db_begin_transaction (folder->parent_store->cdb_w, NULL);
1245
1246         }
1247         g_hash_table_foreach(matchhash, (GHFunc)folder_added_uid, &u);
1248
1249         if (rebuilded && !correlating)
1250                 camel_db_end_transaction (folder->parent_store->cdb_w, NULL);
1251
1252         if (folder_unmatched != NULL) {
1253                 /* scan unmatched, remove any that have vanished, etc */
1254                 count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
1255                 for (i=0;i<count;i++) {
1256                         gchar *uid = camel_folder_summary_uid_from_index (((CamelFolder *)folder_unmatched)->summary, i);
1257
1258                         if (uid) {
1259                                 if (strncmp (uid, u.hash, 8) == 0) {
1260                                         if (g_hash_table_lookup(allhash, uid+8) == NULL) {
1261                                                 /* no longer exists at all, just remove it entirely */
1262                                                 camel_folder_summary_remove_index_fast(((CamelFolder *)folder_unmatched)->summary, i);
1263                                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, uid);
1264                                                 i--;
1265                                         } else {
1266                                                 g_hash_table_remove(allhash, uid+8);
1267                                         }
1268                                 }
1269                                 g_free (uid);
1270                         }
1271                 }
1272
1273                 /* now allhash contains all potentially new uid's for the unmatched folder, process */
1274                 if (!CAMEL_IS_VEE_FOLDER(source))
1275                         g_hash_table_foreach(allhash, (GHFunc)unmatched_check_uid, &u);
1276
1277                 /* copy any changes so we can raise them outside the lock */
1278                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1279                         unmatched_changes = folder_unmatched->changes;
1280                         folder_unmatched->changes = camel_folder_change_info_new();
1281                 }
1282
1283                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1284         }
1285
1286         if (camel_folder_change_info_changed(vf->changes)) {
1287                 vf_changes = vf->changes;
1288                 vf->changes = camel_folder_change_info_new();
1289         }
1290
1291         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1292
1293         /* Del the unwanted things from the summary, we don't hold any locks now. */
1294         if (del_list) {
1295                 if (!correlating) {
1296                         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1297                         camel_db_delete_vuids(folder->parent_store->cdb_w, folder->full_name, shash, del_list, &ex);
1298                         camel_exception_clear (&ex);
1299                 }
1300                 ((CamelVeeSummary *)folder->summary)->force_counts = TRUE;
1301                 g_slist_foreach (del_list, (GFunc) camel_pstring_free, NULL);
1302                 g_slist_free (del_list);
1303         };
1304
1305         g_hash_table_destroy(matchhash);
1306         g_hash_table_destroy(allhash);
1307         g_hash_table_destroy(fullhash);
1308
1309         g_free(shash);
1310         /* if expression not set, we only had a null list */
1311         if (vf->expression == NULL || !rebuilded) {
1312                 g_ptr_array_foreach (match, (GFunc) camel_pstring_free, NULL);
1313                 g_ptr_array_free(match, TRUE);
1314         } else
1315                 camel_folder_search_free(source, match);
1316         camel_folder_free_summary (source, all);
1317
1318         if (unmatched_changes) {
1319                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1320                 camel_folder_change_info_free(unmatched_changes);
1321         }
1322
1323         if (vf_changes) {
1324                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1325                 camel_folder_change_info_free(vf_changes);
1326         }
1327
1328         return 0;
1329 }
1330
1331 /* Hold all these with summary lock and unmatched summary lock held */
1332 static void
1333 folder_changed_add_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
1334 {
1335         CamelFolder *folder = (CamelFolder *)vf;
1336         CamelVeeMessageInfo *vinfo;
1337         const gchar *vuid;
1338         gchar *oldkey;
1339         gpointer oldval;
1340         gint n;
1341         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1342         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1343
1344         vinfo = vee_folder_add_uid(vf, sub, uid, hash);
1345         if (vinfo == NULL)
1346                 return;
1347
1348         vuid = camel_pstring_strdup (camel_message_info_uid (vinfo));
1349         camel_message_info_free ((CamelMessageInfo *) vinfo);
1350         if (use_db) {
1351                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1352                 camel_db_add_to_vfolder_transaction (folder->parent_store->cdb_w, folder->full_name, vuid, &ex);
1353                 camel_exception_clear (&ex);
1354         }
1355         camel_folder_change_info_add_uid(vf->changes,  vuid);
1356         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1357                 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
1358                         n = GPOINTER_TO_INT (oldval);
1359                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
1360                 } else {
1361                         g_hash_table_insert(unmatched_uids, g_strdup(vuid), GINT_TO_POINTER (1));
1362                 }
1363                 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1364                 if (vinfo) {
1365                         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1366                         camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1367                         camel_db_delete_uid_from_vfolder_transaction (folder->parent_store->cdb_w, ((CamelFolder *)folder_unmatched)->full_name, vuid, &ex);
1368                         camel_folder_summary_remove_uid_fast(((CamelFolder *)folder_unmatched)->summary, vuid);
1369                         camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1370                         camel_exception_clear (&ex);
1371                 }
1372         }
1373
1374         camel_pstring_free (vuid);
1375 }
1376
1377 static void
1378 folder_changed_remove_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], gint keep, CamelVeeFolder *vf, gboolean use_db)
1379 {
1380         CamelFolder *folder = (CamelFolder *)vf;
1381         gchar *vuid, *oldkey;
1382         gpointer oldval;
1383         gint n;
1384         CamelVeeMessageInfo *vinfo;
1385         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1386         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1387
1388         vuid = alloca(strlen(uid)+9);
1389         memcpy(vuid, hash, 8);
1390         strcpy(vuid+8, uid);
1391
1392         camel_folder_change_info_remove_uid(vf->changes, vuid);
1393         if (use_db) {
1394                 /* FIXME[disk-summary] Handle exception */
1395                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1396                 camel_db_delete_uid_from_vfolder_transaction (folder->parent_store->cdb_w, folder->full_name, vuid, &ex);
1397                 camel_exception_clear (&ex);
1398         }
1399         camel_folder_summary_remove_uid_fast(folder->summary, vuid);
1400
1401         if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1402                 if (keep) {
1403                         if (g_hash_table_lookup_extended(unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
1404                                 n = GPOINTER_TO_INT (oldval);
1405                                 if (n == 1) {
1406                                         g_hash_table_remove(unmatched_uids, oldkey);
1407                                         if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash))
1408                                                 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1409                                         g_free(oldkey);
1410                                 } else {
1411                                         g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1412                                 }
1413                         } else {
1414                                 if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash))
1415                                         camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1416                         }
1417                 } else {
1418                         if (g_hash_table_lookup_extended(unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
1419                                 g_hash_table_remove(unmatched_uids, oldkey);
1420                                 g_free(oldkey);
1421                         }
1422
1423                         vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1424                         if (vinfo) {
1425                                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1426                                 camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1427                                 camel_db_delete_uid_from_vfolder_transaction (folder->parent_store->cdb_w, ((CamelFolder *)folder_unmatched)->full_name, vuid, &ex);
1428                                 camel_folder_summary_remove_uid_fast(((CamelFolder *)folder_unmatched)->summary, vuid);
1429                                 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1430                                 camel_exception_clear (&ex);
1431                         }
1432                 }
1433         }
1434 }
1435
1436 static void
1437 folder_changed_change_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
1438 {
1439         gchar *vuid;
1440         CamelVeeMessageInfo *vinfo, *uinfo = NULL;
1441         CamelMessageInfo *info;
1442         CamelFolder *folder = (CamelFolder *)vf;
1443         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1444
1445         vuid = alloca(strlen(uid)+9);
1446         memcpy(vuid, hash, 8);
1447         strcpy(vuid+8, uid);
1448
1449         vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1450         if (folder_unmatched != NULL)
1451                 uinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
1452         if (vinfo || uinfo) {
1453                 info = camel_folder_get_message_info(sub, uid);
1454                 if (info) {
1455                         if (vinfo) {
1456                                 camel_folder_change_info_change_uid(vf->changes, vuid);
1457                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1458                         }
1459
1460                         if (uinfo) {
1461                                 camel_folder_change_info_change_uid(folder_unmatched->changes, vuid);
1462                                 camel_message_info_free((CamelMessageInfo *)uinfo);
1463                         }
1464
1465                         camel_folder_free_message_info(sub, info);
1466                 } else {
1467                         if (vinfo) {
1468                                 folder_changed_remove_uid(sub, uid, hash, FALSE, vf, use_db);
1469                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1470                         }
1471                         if (uinfo)
1472                                 camel_message_info_free((CamelMessageInfo *)uinfo);
1473                 }
1474         }
1475 }
1476
1477 struct _folder_changed_msg {
1478         CamelSessionThreadMsg msg;
1479         CamelFolderChangeInfo *changes;
1480         CamelFolder *sub;
1481         CamelVeeFolder *vf;
1482 };
1483
1484 static void
1485 folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg)
1486 {
1487         struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1488         CamelFolder *sub = m->sub;
1489         CamelFolder *folder = (CamelFolder *)m->vf;
1490         CamelVeeFolder *vf = m->vf;
1491         CamelFolderChangeInfo *changes = m->changes;
1492         gchar *vuid = NULL, hash[8];
1493         const gchar *uid;
1494         CamelVeeMessageInfo *vinfo;
1495         gint i, vuidlen = 0;
1496         CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
1497         GPtrArray *matches_added = NULL, /* newly added, that match */
1498                 *matches_changed = NULL, /* newly changed, that now match */
1499                 *newchanged = NULL,
1500                 *changed;
1501         GPtrArray *always_changed = NULL;
1502         GHashTable *matches_hash;
1503         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1504         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1505         GPtrArray *present = NULL;
1506
1507         /* See vee_rebuild_folder. */
1508         gboolean correlating = expression_is_correlating(vf->expression);
1509
1510         /* Check the folder hasn't beem removed while we weren't watching */
1511         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1512         if (g_list_find(_PRIVATE(vf)->folders, sub) == NULL) {
1513                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1514                 return;
1515         }
1516
1517         camel_vee_folder_hash_folder(sub, hash);
1518
1519         /* Lookup anything before we lock anything, to avoid deadlock with build_folder */
1520
1521         /* Find newly added that match */
1522         if (changes->uid_added->len > 0) {
1523                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1524                 dd(printf(" Searching for added matches '%s'\n", vf->expression));
1525                 matches_added = camel_folder_search_by_uids(sub, vf->expression, changes->uid_added, &ex);
1526                 camel_exception_clear (&ex);
1527         }
1528
1529         /* TODO:
1530            In this code around here, we can work out if the search will affect the changes
1531            we had, and only re-search against them if they might have */
1532
1533         /* Search for changed items that newly match, but only if we dont have them */
1534         changed = changes->uid_changed;
1535         if (changed->len > 0) {
1536                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1537                 dd(printf(" Searching for changed matches '%s'\n", vf->expression));
1538
1539                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
1540                         newchanged = g_ptr_array_new();
1541                         always_changed = g_ptr_array_new();
1542                         for (i=0;i<changed->len;i++) {
1543                                 uid = changed->pdata[i];
1544                                 if (strlen(uid)+9 > vuidlen) {
1545                                         vuidlen = strlen(uid)+64;
1546                                         vuid = g_realloc(vuid, vuidlen);
1547                                 }
1548                                 memcpy(vuid, hash, 8);
1549                                 strcpy(vuid+8, uid);
1550                                 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1551                                 if (vinfo == NULL) {
1552                                         g_ptr_array_add(newchanged, (gchar *)uid);
1553                                 } else {
1554                                         g_ptr_array_add(always_changed, (gchar *)uid);
1555                                         camel_message_info_free((CamelMessageInfo *)vinfo);
1556                                 }
1557                         }
1558                         changed = newchanged;
1559                 }
1560
1561                 if (changed->len)
1562                         matches_changed = camel_folder_search_by_uids(sub, vf->expression, changed, &ex);
1563                 camel_exception_clear (&ex);
1564                 if (always_changed && always_changed->len)
1565                         present = camel_folder_search_by_uids(sub, vf->expression, always_changed, &ex);
1566                 camel_exception_clear (&ex);
1567         }
1568
1569         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1570
1571         if (folder_unmatched != NULL)
1572                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1573
1574         if (matches_changed || matches_added || changes->uid_removed->len||present)
1575                 camel_db_begin_transaction (folder->parent_store->cdb_w, NULL);
1576
1577         dd(printf("Vfolder '%s' subfolder changed '%s'\n", folder->full_name, sub->full_name));
1578         dd(printf(" changed %u added %u removed %u\n", changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len));
1579
1580         /* Always remove removed uid's, in any case */
1581         for (i=0;i<changes->uid_removed->len;i++) {
1582                 dd(printf("  removing uid '%s'\n", (gchar *)changes->uid_removed->pdata[i]));
1583                 folder_changed_remove_uid(sub, changes->uid_removed->pdata[i], hash, FALSE, vf, !correlating);
1584         }
1585
1586         /* Add any newly matched or to unmatched folder if they dont */
1587         if (matches_added) {
1588                 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1589                 for (i=0;i<matches_added->len;i++) {
1590                         dd(printf(" %s", (gchar *)matches_added->pdata[i]));
1591                         g_hash_table_insert(matches_hash, matches_added->pdata[i], matches_added->pdata[i]);
1592                 }
1593                 for (i=0;i<changes->uid_added->len;i++) {
1594                         uid = changes->uid_added->pdata[i];
1595                         if (g_hash_table_lookup(matches_hash, uid)) {
1596                                 dd(printf("  adding uid '%s' [newly matched]\n", (gchar *)uid));
1597                                 folder_changed_add_uid(sub, uid, hash, vf, !correlating);
1598                         } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
1599                                 if (strlen(uid)+9 > vuidlen) {
1600                                         vuidlen = strlen(uid)+64;
1601                                         vuid = g_realloc(vuid, vuidlen);
1602                                 }
1603                                 memcpy(vuid, hash, 8);
1604                                 strcpy(vuid+8, uid);
1605
1606                                 if (!CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL && g_hash_table_lookup(unmatched_uids, vuid) == NULL) {
1607                                         dd(printf("  adding uid '%s' to Unmatched [newly unmatched]\n", (gchar *)uid));
1608                                         vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1609                                         if (vinfo == NULL) {
1610                                                 if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash))
1611                                                         camel_folder_change_info_add_uid(folder_unmatched->changes, vuid);
1612                                         } else {
1613                                                 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1614                                         }
1615                                 }
1616                         }
1617                 }
1618                 g_hash_table_destroy(matches_hash);
1619         }
1620
1621         /* Change any newly changed */
1622         if (always_changed) {
1623                 if (correlating) {
1624                         /* Messages may be pulled in by the correlation even if
1625                          * they do not match the expression individually, so it
1626                          * would be wrong to preemptively remove anything here.
1627                          * vee_rebuild_folder will make any necessary removals
1628                          * when it re-queries the entire source folder. */
1629                         for (i=0;i<always_changed->len;i++)
1630                                 folder_changed_change_uid(sub, always_changed->pdata[i], hash, vf, !correlating);
1631                 } else {
1632                         GHashTable *ht_present = g_hash_table_new (g_str_hash, g_str_equal);
1633
1634                         for (i=0;present && i<present->len;i++) {
1635                                 folder_changed_change_uid(sub, present->pdata[i], hash, vf, !correlating);
1636                                 g_hash_table_insert (ht_present, present->pdata[i], present->pdata[i]);
1637                         }
1638
1639                         for (i=0; i<always_changed->len; i++) {
1640                                 if (!present || !g_hash_table_lookup(ht_present, always_changed->pdata[i]))
1641                                         /* XXX: IIUC, these messages haven't been deleted from the
1642                                          * source folder, so shouldn't "keep" be set to TRUE? */
1643                                         folder_changed_remove_uid(sub, always_changed->pdata[i], hash, TRUE, vf, !correlating);
1644                         }
1645
1646                         g_hash_table_destroy (ht_present);
1647                 }
1648                 g_ptr_array_free(always_changed, TRUE);
1649         }
1650
1651         /* Change/add/remove any changed */
1652         if (changes->uid_changed->len) {
1653                 /* If we are auto-updating, then re-check changed uids still match */
1654                 dd(printf(" Vfolder %supdate\nuids match:", (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO)?"auto-":""));
1655                 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1656                 for (i=0;matches_changed && i<matches_changed->len;i++) {
1657                         dd(printf(" %s", (gchar *)matches_changed->pdata[i]));
1658                         g_hash_table_insert(matches_hash, matches_changed->pdata[i], matches_changed->pdata[i]);
1659                 }
1660                 dd(printf("\n"));
1661
1662                 for (i=0;i<changed->len;i++) {
1663                         uid = changed->pdata[i];
1664                         if (strlen(uid)+9 > vuidlen) {
1665                                 vuidlen = strlen(uid)+64;
1666                                 vuid = g_realloc(vuid, vuidlen);
1667                         }
1668                         memcpy(vuid, hash, 8);
1669                         strcpy(vuid+8, uid);
1670                         vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1671                         if (vinfo == NULL) {
1672                                 if (g_hash_table_lookup(matches_hash, uid)) {
1673                                         /* A uid we dont have, but now it matches, add it */
1674                                         dd(printf("  adding uid '%s' [newly matched]\n", uid));
1675                                         folder_changed_add_uid(sub, uid, hash, vf, !correlating);
1676                                 } else {
1677                                         /* A uid we still don't have, just change it (for unmatched) */
1678                                         folder_changed_change_uid(sub, uid, hash, vf, !correlating);
1679                                 }
1680                         } else {
1681                                 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
1682                                     || g_hash_table_lookup(matches_hash, uid)) {
1683                                         /* still match, or we're not auto-updating, change event, (if it changed) */
1684                                         dd(printf("  changing uid '%s' [still matches]\n", uid));
1685                                         folder_changed_change_uid(sub, uid, hash, vf, !correlating);
1686                                 } else {
1687                                         /* No longer matches, remove it, but keep it in unmatched (potentially) */
1688                                         dd(printf("  removing uid '%s' [did match]\n", uid));
1689                                         folder_changed_remove_uid(sub, uid, hash, TRUE, vf, !correlating);
1690                                 }
1691                                 camel_message_info_free((CamelMessageInfo *)vinfo);
1692                         }
1693                 }
1694                 g_hash_table_destroy(matches_hash);
1695         } else {
1696                 /* stuff didn't match but it changed - check unmatched folder for changes */
1697                 for (i=0;i<changed->len;i++)
1698                         folder_changed_change_uid(sub, changed->pdata[i], hash, vf, !correlating);
1699         }
1700
1701         if (folder_unmatched != NULL) {
1702                 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1703                         unmatched_changes = folder_unmatched->changes;
1704                         folder_unmatched->changes = camel_folder_change_info_new();
1705                 }
1706
1707                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1708         }
1709
1710         if (camel_folder_change_info_changed(vf->changes)) {
1711                 vf_changes = vf->changes;
1712                 vf->changes = camel_folder_change_info_new();
1713         }
1714
1715         if (matches_changed || matches_added || changes->uid_removed->len || present)
1716                 camel_db_end_transaction (folder->parent_store->cdb_w, NULL);
1717         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1718
1719         /* Cleanup stuff on our folder */
1720         if (matches_added)
1721                 camel_folder_search_free(sub, matches_added);
1722         if (present)
1723                 camel_folder_search_free (sub, present);
1724
1725         if (matches_changed)
1726                 camel_folder_search_free(sub, matches_changed);
1727
1728         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1729
1730         /* cleanup the rest */
1731         if (newchanged)
1732                 g_ptr_array_free(newchanged, TRUE);
1733
1734         g_free(vuid);
1735
1736         if (unmatched_changes) {
1737                 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1738                 camel_folder_change_info_free(unmatched_changes);
1739         }
1740
1741         /* Add to folders_changed if we need to call vee_rebuild_folder, which
1742          * could be the case for two reasons:
1743          * - We changed the vfolder and it is not auto-updating.  Need to re-sync.
1744          * - Vfolder is correlating.  Changes to non-matching source messages
1745          *   won't be processed here and won't show up in vf_changes but may
1746          *   still affect the vfolder contents (e.g., non-matching messages
1747          *   added to a matching thread), so we re-run the query on the whole
1748          *   source folder.  (For match-threads, it may be enough to do this if
1749          *   changes->uid_added->len > 0, but I'm not completely sure and I'd
1750          *   rather be safe than sorry.)
1751          */
1752         if ((vf_changes && (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) || correlating) {
1753                 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
1754                 if (g_list_find(vf->priv->folders_changed, sub) == NULL)
1755                         vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub);
1756                 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
1757         }
1758
1759         if (vf_changes) {
1760                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1761                 camel_folder_change_info_free(vf_changes);
1762         }
1763 }
1764
1765 static void
1766 folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg)
1767 {
1768         struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1769
1770         camel_folder_change_info_free(m->changes);
1771         camel_object_unref((CamelObject *)m->vf);
1772         camel_object_unref((CamelObject *)m->sub);
1773 }
1774
1775 static CamelSessionThreadOps folder_changed_ops = {
1776         folder_changed_change,
1777         folder_changed_free,
1778 };
1779
1780 static void
1781 folder_changed_base(CamelVeeFolder *vf, CamelFolder *sub, CamelFolderChangeInfo *changes)
1782 {
1783         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1784         struct _folder_changed_msg *m;
1785         CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session;
1786
1787         if (p->destroyed)
1788                 return;
1789
1790         m = camel_session_thread_msg_new(session, &folder_changed_ops, sizeof(*m));
1791         m->changes = camel_folder_change_info_new();
1792         camel_folder_change_info_cat(m->changes, changes);
1793         m->sub = sub;
1794         camel_object_ref((CamelObject *)sub);
1795         m->vf = vf;
1796         camel_object_ref((CamelObject *)vf);
1797         camel_session_thread_queue(session, &m->msg, 0);
1798 }
1799
1800 static void
1801 folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf)
1802 {
1803         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_changed(vf, sub, changes);
1804 }
1805
1806 /* track vanishing folders */
1807 static void
1808 subfolder_deleted(CamelFolder *f, gpointer event_data, CamelVeeFolder *vf)
1809 {
1810         camel_vee_folder_remove_folder(vf, f);
1811 }
1812
1813 static void
1814 subfolder_renamed_update(CamelVeeFolder *vf, CamelFolder *sub, gchar hash[8])
1815 {
1816         gint count, i;
1817         CamelFolderChangeInfo *changes = NULL;
1818         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1819         GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1820         CamelFolderSummary *ssummary = sub->summary;
1821
1822         CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1823
1824         count = camel_folder_summary_count(((CamelFolder *)vf)->summary);
1825         for (i=0;i<count;i++) {
1826                 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)vf)->summary, i);
1827                 CamelVeeMessageInfo *vinfo;
1828
1829                 if (mi == NULL)
1830                         continue;
1831
1832                 if (mi->summary == ssummary) {
1833                         gchar *uid = (gchar *)camel_message_info_uid(mi);
1834                         gchar *oldkey;
1835                         gpointer oldval;
1836
1837                         camel_folder_change_info_remove_uid(vf->changes, uid);
1838                         camel_folder_summary_remove(((CamelFolder *)vf)->summary, (CamelMessageInfo *)mi);
1839
1840                         /* works since we always append on the end */
1841                         i--;
1842                         count--;
1843
1844                         vinfo = vee_folder_add_uid(vf, sub, uid+8, hash);
1845                         if (vinfo) {
1846                                 camel_folder_change_info_add_uid(vf->changes, camel_message_info_uid(vinfo));
1847
1848                                 /* check unmatched uid's table for any matches */
1849                                 if (vf == folder_unmatched
1850                                     && g_hash_table_lookup_extended(unmatched_uids, uid, (gpointer *)&oldkey, &oldval)) {
1851                                         g_hash_table_remove(unmatched_uids, oldkey);
1852                                         g_hash_table_insert(unmatched_uids, g_strdup(camel_message_info_uid(vinfo)), oldval);
1853                                         g_free(oldkey);
1854                                 }
1855
1856                                 camel_message_info_free ((CamelMessageInfo *) vinfo);
1857                         }
1858                 }
1859
1860                 camel_message_info_free((CamelMessageInfo *)mi);
1861         }
1862
1863         if (camel_folder_change_info_changed(vf->changes)) {
1864                 changes = vf->changes;
1865                 vf->changes = camel_folder_change_info_new();
1866         }
1867
1868         CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1869
1870         if (changes) {
1871                 camel_object_trigger_event((CamelObject *)vf, "folder_changed", changes);
1872                 camel_folder_change_info_free(changes);
1873         }
1874 }
1875
1876 static void
1877 folder_renamed_base(CamelVeeFolder *vf, CamelFolder *f, const gchar *old)
1878 {
1879         gchar hash[8];
1880         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1881
1882         /* TODO: This could probably be done in another thread, tho it is pretty quick/memory bound */
1883
1884         /* Life just got that little bit harder, if the folder is renamed, it means it breaks all of our uid's.
1885            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 */
1886
1887         camel_vee_folder_hash_folder(f, hash);
1888
1889         subfolder_renamed_update(vf, f, hash);
1890         if (folder_unmatched != NULL)
1891                 subfolder_renamed_update(folder_unmatched, f, hash);
1892 }
1893
1894 static void
1895 folder_renamed(CamelFolder *sub, const gchar *old, CamelVeeFolder *vf)
1896 {
1897         ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_renamed(vf, sub, old);
1898 }
1899
1900 static void
1901 vee_freeze (CamelFolder *folder)
1902 {
1903         CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1904         struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1905         GList *node;
1906
1907         CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1908
1909         node = p->folders;
1910         while (node) {
1911                 CamelFolder *f = node->data;
1912
1913                 camel_folder_freeze(f);
1914                 node = node->next;
1915         }
1916
1917         CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1918
1919         /* call parent implementation */
1920         CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->freeze(folder);
1921 }
1922
1923 static void
1924 vee_thaw(CamelFolder *folder)
1925 {
1926         CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1927         struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1928         GList *node;
1929
1930         CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1931
1932         node = p->folders;
1933         while (node) {
1934                 CamelFolder *f = node->data;
1935
1936                 camel_folder_thaw(f);
1937                 node = node->next;
1938         }
1939
1940         CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1941
1942         /* call parent implementation */
1943         CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->thaw(folder);
1944 }
1945
1946 /* vfolder base implementaitons */
1947 static void
1948 vee_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
1949 {
1950         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1951
1952         vee_rebuild_folder (vf, sub, &ex);
1953
1954         camel_exception_clear (&ex);
1955 }
1956
1957 static void
1958 vee_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
1959 {
1960         gchar *shash, hash[8];
1961         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1962
1963         camel_vee_folder_hash_folder(sub, hash);
1964         vee_folder_remove_folder(vf, sub);
1965         shash = g_strdup_printf("%c%c%c%c%c%c%c%c", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]);
1966         if (g_hash_table_lookup (vf->hashes, shash)) {
1967                 g_hash_table_remove (vf->hashes, shash);
1968         }
1969
1970         if (folder_unmatched && g_hash_table_lookup (folder_unmatched->hashes, shash)) {
1971                 g_hash_table_remove (folder_unmatched->hashes, shash);
1972         }
1973
1974         g_free(shash);
1975
1976 }
1977
1978 static void
1979 vee_set_expression(CamelVeeFolder *vf, const gchar *query)
1980 {
1981         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1982         GList *node;
1983         CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1984
1985         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1986
1987         /* no change, do nothing */
1988         if ((vf->expression && query && strcmp(vf->expression, query) == 0)
1989             || (vf->expression == NULL && query == NULL)) {
1990                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1991                 return;
1992         }
1993
1994         /* Recreate the table when the query changes, only if we are not setting it first */
1995         if (vf->expression) {
1996                 CamelFolderSummary *s = ((CamelFolder *)vf)->summary;
1997                 camel_folder_summary_clear (s);
1998                 camel_db_recreate_vfolder (((CamelFolder *) vf)->parent_store->cdb_w, ((CamelFolder *) vf)->full_name, &ex);
1999                 camel_exception_clear (&ex);
2000                 s->junk_count = 0;
2001                 s->deleted_count = 0;
2002                 s->unread_count = 0;
2003                 s->visible_count = 0;
2004                 s->junk_not_deleted_count = 0;
2005         }
2006
2007         g_free(vf->expression);
2008         if (query)
2009                 vf->expression = g_strdup(query);
2010
2011         node = p->folders;
2012         while (node) {
2013                 CamelFolder *f = node->data;
2014
2015                 if (camel_vee_folder_rebuild_folder (vf, f, &ex) == -1)
2016                         break;
2017
2018                 camel_exception_clear (&ex);
2019
2020                 node = node->next;
2021         }
2022
2023         camel_exception_clear (&ex);
2024
2025         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
2026         g_list_free(p->folders_changed);
2027         p->folders_changed = NULL;
2028         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
2029
2030         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
2031 }
2032
2033 /* This entire code will be useless, since we sync the counts always. */
2034 static gint
2035 vf_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
2036 {
2037         CamelFolder *folder = (CamelFolder *)object;
2038         CamelVeeFolder *vf = (CamelVeeFolder *)folder;
2039         gint i;
2040         guint32 tag;
2041         gint unread = -1, deleted = 0, junked = 0, visible = 0, count = -1, junked_not_deleted = -1;
2042
2043         for (i=0;i<args->argc;i++) {
2044                 CamelArgGet *arg = &args->argv[i];
2045
2046                 tag = arg->tag;
2047
2048                 /* NB: this is a copy of camel-folder.c with the unread count logic altered.
2049                    makes sure its still atomically calculated */
2050                 switch (tag & CAMEL_ARG_TAG) {
2051                 case CAMEL_FOLDER_ARG_UNREAD:
2052                 case CAMEL_FOLDER_ARG_DELETED:
2053                 case CAMEL_FOLDER_ARG_JUNKED:
2054                 case CAMEL_FOLDER_ARG_JUNKED_NOT_DELETED:
2055                 case CAMEL_FOLDER_ARG_VISIBLE:
2056
2057                         if (vf->expression && vf->priv->unread_vfolder == -1)
2058                                 camel_vee_summary_load_check_unread_vfolder ((CamelVeeSummary *)folder->summary);
2059
2060                         /* This is so we can get the values atomically, and also so we can calculate them only once */
2061                         if (unread == -1) {
2062                                 gint j;
2063                                 CamelMessageInfoBase *info;
2064                                 CamelVeeMessageInfo *vinfo;
2065
2066                                 unread = deleted = visible = junked = junked_not_deleted = 0;
2067                                 count = camel_folder_summary_count(folder->summary);
2068                                 for (j=0; j<count; j++) {
2069                                         if ((info = (CamelMessageInfoBase *) camel_folder_summary_index(folder->summary, j))) {
2070                                                 guint32 flags;
2071
2072                                                 vinfo = (CamelVeeMessageInfo *) info;
2073                                                 flags = vinfo->old_flags; /* ? vinfo->old_flags : camel_message_info_flags(info); */
2074
2075                                                 if ((flags & (CAMEL_MESSAGE_SEEN)) == 0)
2076                                                         unread++;
2077                                                 if (flags & CAMEL_MESSAGE_DELETED)
2078                                                         deleted++;
2079                                                 if (flags & CAMEL_MESSAGE_JUNK) {
2080                                                         junked++;
2081                                                                 if (!(flags & CAMEL_MESSAGE_DELETED))
2082                                                                         junked_not_deleted++;
2083                                                 }
2084                                                 if ((flags & (CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
2085                                                         visible++;
2086                                                 camel_message_info_free(info);
2087                                         }
2088                                 }
2089                         }
2090
2091                         switch (tag & CAMEL_ARG_TAG) {
2092                         case CAMEL_FOLDER_ARG_UNREAD:
2093                                 if (vf->priv->unread_vfolder == 1)
2094                                         count = unread == -1 ? 0 : unread - junked_not_deleted;
2095                                 else
2096                                         count = unread == -1 ? 0 : unread;
2097                                 break;
2098                         case CAMEL_FOLDER_ARG_DELETED:
2099                                 count = deleted == -1 ? 0 : deleted;
2100                                 break;
2101                         case CAMEL_FOLDER_ARG_JUNKED:
2102                                 count = junked == -1 ? 0 : junked;
2103                                 break;
2104                         case CAMEL_FOLDER_ARG_JUNKED_NOT_DELETED:
2105                                 count = junked_not_deleted == -1 ? 0 : junked_not_deleted;
2106                                 break;
2107                         case CAMEL_FOLDER_ARG_VISIBLE:
2108                                 if (vf->priv->unread_vfolder == 1)
2109                                         count = unread == -1 ? 0 : unread - junked_not_deleted;
2110                                 else
2111                                         count = visible == -1 ? 0 : visible;
2112
2113                                 break;
2114                         }
2115                         folder->summary->unread_count = unread == -1 ? 0 : unread;
2116                         folder->summary->deleted_count = deleted == -1 ? 0 : deleted;
2117                         junked = folder->summary->junk_count = junked == -1 ? 0 : junked;
2118                         folder->summary->junk_not_deleted_count = junked_not_deleted == -1 ? 0 : junked_not_deleted;
2119                         folder->summary->visible_count = visible == -1 ? 0 : visible;
2120                         *arg->ca_int = count;
2121                         break;
2122                 default:
2123                         continue;
2124                 }
2125
2126                 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
2127         }
2128
2129         return ((CamelObjectClass *)camel_vee_folder_parent)->getv(object, ex, args);
2130 }
2131
2132 static void
2133 camel_vee_folder_class_init (CamelVeeFolderClass *klass)
2134 {
2135         CamelFolderClass *folder_class = (CamelFolderClass *) klass;
2136
2137         camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
2138
2139         ((CamelObjectClass *)klass)->getv = vf_getv;
2140
2141         folder_class->refresh_info = vee_refresh_info;
2142         folder_class->sync = vee_sync;
2143         folder_class->expunge = vee_expunge;
2144
2145         folder_class->get_message = vee_get_message;
2146         folder_class->append_message = vee_append_message;
2147         folder_class->transfer_messages_to = vee_transfer_messages_to;
2148
2149         folder_class->search_by_expression = vee_search_by_expression;
2150         folder_class->search_by_uids = vee_search_by_uids;
2151         folder_class->count_by_expression = vee_count_by_expression;
2152
2153         folder_class->rename = vee_rename;
2154         folder_class->delete = vee_delete;
2155
2156         folder_class->freeze = vee_freeze;
2157         folder_class->thaw = vee_thaw;
2158
2159         klass->set_expression = vee_set_expression;
2160         klass->add_folder = vee_add_folder;
2161         klass->remove_folder = vee_remove_folder;
2162         klass->rebuild_folder = vee_rebuild_folder;
2163         klass->folder_changed = folder_changed_base;
2164         klass->folder_renamed = folder_renamed_base;
2165 }
2166
2167 static void
2168 camel_vee_folder_init (CamelVeeFolder *obj)
2169 {
2170         struct _CamelVeeFolderPrivate *p;
2171         CamelFolder *folder = (CamelFolder *)obj;
2172
2173         p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
2174
2175         folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
2176                                  CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
2177
2178         /* FIXME: what to do about user flags if the subfolder doesn't support them? */
2179         folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
2180                 CAMEL_MESSAGE_DELETED |
2181                 CAMEL_MESSAGE_DRAFT |
2182                 CAMEL_MESSAGE_FLAGGED |
2183                 CAMEL_MESSAGE_SEEN;
2184
2185         obj->changes = camel_folder_change_info_new();
2186         obj->search = camel_folder_search_new();
2187         obj->hashes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2188         /* Loaded is no longer used.*/
2189         obj->loaded = NULL;
2190         obj->deleted = FALSE;
2191         p->summary_lock = g_mutex_new();
2192         p->subfolder_lock = g_mutex_new();
2193         p->changed_lock = g_mutex_new();
2194         p->unread_vfolder = -1;
2195 }
2196
2197 /**
2198  * camel_vee_folder_mask_event_folder_changed:
2199  *
2200  * Since: 2.26
2201  **/
2202 void
2203 camel_vee_folder_mask_event_folder_changed (CamelVeeFolder *vf, CamelFolder *sub)
2204 {
2205         camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
2206
2207 }
2208
2209 /**
2210  * camel_vee_folder_unmask_event_folder_changed:
2211  *
2212  * Since: 2.26
2213  **/
2214 void
2215 camel_vee_folder_unmask_event_folder_changed (CamelVeeFolder *vf, CamelFolder *sub)
2216 {
2217         camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
2218 }
2219
2220 static void
2221 vee_folder_stop_folder(CamelVeeFolder *vf, CamelFolder *sub)
2222 {
2223         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
2224         gint i;
2225         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
2226
2227         CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
2228
2229         CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
2230         p->folders_changed = g_list_remove(p->folders_changed, sub);
2231         CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
2232
2233         if (g_list_find(p->folders, sub) == NULL) {
2234                 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
2235                 return;
2236         }
2237
2238         camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
2239         camel_object_unhook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
2240         camel_object_unhook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc) folder_renamed, vf);
2241
2242         p->folders = g_list_remove(p->folders, sub);
2243
2244         /* undo the freeze state that we have imposed on this source folder */
2245         CAMEL_FOLDER_LOCK(vf, change_lock);
2246         for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
2247                 camel_folder_thaw(sub);
2248         CAMEL_FOLDER_UNLOCK(vf, change_lock);
2249
2250         CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
2251
2252         if (folder_unmatched != NULL) {
2253                 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
2254
2255                 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
2256                 /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */
2257                 if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
2258                         while (g_list_find(up->folders, sub)) {
2259                                 up->folders = g_list_remove(up->folders, sub);
2260                                 camel_object_unref((CamelObject *)sub);
2261
2262                                 /* undo the freeze state that Unmatched has imposed on this source folder */
2263                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
2264                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
2265                                         camel_folder_thaw(sub);
2266                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
2267                         }
2268                 } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
2269                         if (g_list_find(up->folders, sub) != NULL) {
2270                                 up->folders = g_list_remove(up->folders, sub);
2271                                 camel_object_unref((CamelObject *)sub);
2272
2273                                 /* undo the freeze state that Unmatched has imposed on this source folder */
2274                                 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
2275                                 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
2276                                         camel_folder_thaw(sub);
2277                                 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
2278                         }
2279                 }
2280                 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
2281         }
2282
2283         if (CAMEL_IS_VEE_FOLDER(sub))
2284                 return;
2285
2286         camel_object_unref((CamelObject *)sub);
2287 }
2288
2289 /**
2290  * camel_vee_folder_sync_headers:
2291  *
2292  * Since: 2.24
2293  **/
2294 void
2295 camel_vee_folder_sync_headers (CamelFolder *vf, CamelException *ex)
2296 {
2297         CamelFIRecord * record;
2298         time_t start, end;
2299
2300         /* Save the counts to DB */
2301         start = time(NULL);
2302         record = summary_header_to_db (vf->summary, ex);
2303         camel_db_write_folder_info_record (vf->parent_store->cdb_w, record, ex);
2304         end = time(NULL);
2305         dd(printf("Sync for vfolder '%s': %ld secs\n", vf->full_name, end-start));
2306
2307         g_free (record);
2308 }
2309
2310 static void
2311 camel_vee_folder_finalise (CamelObject *obj)
2312 {
2313         CamelVeeFolder *vf = (CamelVeeFolder *)obj;
2314         struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
2315         CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
2316         GList *node;
2317         CamelFIRecord * record;
2318
2319         p->destroyed = TRUE;
2320
2321         /* Save the counts to DB */
2322         if (!vf->deleted) {
2323                 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
2324                 record = summary_header_to_db (((CamelFolder *)vf)->summary, NULL);
2325                 camel_db_write_folder_info_record (((CamelFolder *) vf)->parent_store->cdb_w, record, &ex);
2326                 g_free (record);
2327                 camel_exception_clear (&ex);
2328         }
2329
2330         /* This may invoke sub-classes with partially destroyed state, they must deal with this */
2331         if (vf == folder_unmatched) {
2332                 for (node = p->folders;node;node = g_list_next(node))
2333                         camel_object_unref(node->data);
2334         } else {
2335                 /* FIXME[disk-summary] See if it is really reqd */
2336                 camel_folder_freeze ((CamelFolder *)vf);
2337                 while (p->folders) {
2338                         CamelFolder *f = p->folders->data;
2339                         vee_folder_stop_folder(vf, f);
2340                 }
2341                 camel_folder_thaw ((CamelFolder *)vf);
2342         }
2343
2344         g_free(vf->expression);
2345
2346         g_list_free(p->folders);
2347         g_list_free(p->folders_changed);
2348
2349         camel_folder_change_info_free(vf->changes);
2350         camel_object_unref((CamelObject *)vf->search);
2351
2352         g_mutex_free(p->summary_lock);
2353         g_mutex_free(p->subfolder_lock);
2354         g_mutex_free(p->changed_lock);
2355         g_hash_table_destroy (vf->hashes);
2356         g_free(p);
2357 }