1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2003 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
6 * Jeffrey Stedfast <fejj@ximian.com>
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.
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.
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.
30 #include <glib/gi18n-lib.h>
32 #include <libedataserver/md5-utils.h>
34 #if defined (DOEPOOLV) || defined (DOESTRV)
35 #include <libedataserver/e-memory.h>
38 #include "camel-debug.h"
39 #include "camel-exception.h"
40 #include "camel-folder-search.h"
41 #include "camel-mime-message.h"
42 #include "camel-private.h"
43 #include "camel-session.h"
44 #include "camel-store.h"
45 #include "camel-vee-folder.h"
46 #include "camel-vee-store.h" /* for open flags */
47 #include "camel-vee-summary.h"
50 #define dd(x) (camel_debug("vfolder")?(x):0)
52 #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv)
55 static void vee_refresh_info(CamelFolder *folder, CamelException *ex);
57 static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
58 static void vee_expunge (CamelFolder *folder, CamelException *ex);
60 static void vee_freeze(CamelFolder *folder);
61 static void vee_thaw(CamelFolder *folder);
63 static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
64 static void vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex);
65 static void vee_transfer_messages_to(CamelFolder *source, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex);
67 static GPtrArray *vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
68 static GPtrArray *vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex);
70 static void vee_rename(CamelFolder *folder, const char *new);
73 static void camel_vee_folder_class_init (CamelVeeFolderClass *klass);
74 static void camel_vee_folder_init (CamelVeeFolder *obj);
75 static void camel_vee_folder_finalise (CamelObject *obj);
77 static int vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex);
78 static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source);
80 static void folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf);
81 static void subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf);
82 static void folder_renamed(CamelFolder *f, const char *old, CamelVeeFolder *vf);
84 static void folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf);
86 static CamelFolderClass *camel_vee_folder_parent;
89 camel_vee_folder_get_type (void)
91 static CamelType type = CAMEL_INVALID_TYPE;
93 if (type == CAMEL_INVALID_TYPE) {
94 type = camel_type_register (camel_folder_get_type (), "CamelVeeFolder",
95 sizeof (CamelVeeFolder),
96 sizeof (CamelVeeFolderClass),
97 (CamelObjectClassInitFunc) camel_vee_folder_class_init,
99 (CamelObjectInitFunc) camel_vee_folder_init,
100 (CamelObjectFinalizeFunc) camel_vee_folder_finalise);
107 camel_vee_folder_construct (CamelVeeFolder *vf, CamelStore *parent_store, const char *full, const char *name, guint32 flags)
109 CamelFolder *folder = (CamelFolder *)vf;
112 camel_folder_construct(folder, parent_store, full, name);
114 folder->summary = camel_vee_summary_new(folder);
116 if (CAMEL_IS_VEE_STORE(parent_store))
117 vf->parent_vee_store = (CamelVeeStore *)parent_store;
121 * camel_vee_folder_new:
122 * @parent_store: the parent CamelVeeStore
123 * @full: the full path to the vfolder.
124 * @ex: a CamelException
126 * Create a new CamelVeeFolder object.
128 * Return value: A new CamelVeeFolder widget.
131 camel_vee_folder_new(CamelStore *parent_store, const char *full, guint32 flags)
136 if (CAMEL_IS_VEE_STORE(parent_store) && strcmp(full, CAMEL_UNMATCHED_NAME) == 0) {
137 vf = ((CamelVeeStore *)parent_store)->folder_unmatched;
138 camel_object_ref(vf);
140 const char *name = strrchr(full, '/');
146 vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
147 camel_vee_folder_construct(vf, parent_store, full, name, flags);
150 d(printf("returning folder %s %p, count = %d\n", name, vf, camel_folder_get_message_count((CamelFolder *)vf)));
152 tmp = g_strdup_printf("%s/%s.cmeta", ((CamelService *)parent_store)->url->path, full);
153 camel_object_set(vf, NULL, CAMEL_OBJECT_STATE_FILE, tmp, NULL);
155 if (camel_object_state_read(vf) == -1) {
156 /* setup defaults: we have none currently */
159 return (CamelFolder *)vf;
163 camel_vee_folder_set_expression(CamelVeeFolder *vf, const char *query)
165 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->set_expression(vf, query);
169 * camel_vee_folder_add_folder:
170 * @vf: Virtual Folder object
171 * @sub: source CamelFolder to add to @vf
173 * Adds @sub as a source folder to @vf.
176 camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
178 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
180 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
182 if (vf == (CamelVeeFolder *)sub) {
183 g_warning("Adding a virtual folder to itself as source, ignored");
187 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
189 /* for normal vfolders we want only unique ones, for unmatched we want them all recorded */
190 if (g_list_find(p->folders, sub) == NULL) {
191 camel_object_ref((CamelObject *)sub);
192 p->folders = g_list_append(p->folders, sub);
194 CAMEL_FOLDER_LOCK(vf, change_lock);
196 /* update the freeze state of 'sub' to match our freeze state */
197 for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
198 camel_folder_freeze(sub);
200 CAMEL_FOLDER_UNLOCK(vf, change_lock);
202 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
203 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
204 camel_object_ref((CamelObject *)sub);
205 up->folders = g_list_append(up->folders, sub);
207 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
209 /* update the freeze state of 'sub' to match Unmatched's freeze state */
210 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
211 camel_folder_freeze(sub);
213 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
216 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
218 d(printf("camel_vee_folder_add_folde(%p, %p)\n", vf, sub));
220 camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc)folder_changed, vf);
221 camel_object_hook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc)subfolder_deleted, vf);
222 camel_object_hook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc)folder_renamed, vf);
224 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->add_folder(vf, sub);
228 * camel_vee_folder_remove_folder:
229 * @vf: Virtual Folder object
230 * @sub: source CamelFolder to remove from @vf
232 * Removed the source folder, @sub, from the virtual folder, @vf.
236 camel_vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
238 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
240 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
242 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
244 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
245 p->folders_changed = g_list_remove(p->folders_changed, sub);
246 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
248 if (g_list_find(p->folders, sub) == NULL) {
249 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
253 camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
254 camel_object_unhook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
255 camel_object_unhook_event((CamelObject *)sub, "renamed", (CamelObjectEventHookFunc) folder_renamed, vf);
257 p->folders = g_list_remove(p->folders, sub);
259 /* undo the freeze state that we have imposed on this source folder */
260 CAMEL_FOLDER_LOCK(vf, change_lock);
261 for (i = 0; i < ((CamelFolder *)vf)->priv->frozen; i++)
262 camel_folder_thaw(sub);
263 CAMEL_FOLDER_UNLOCK(vf, change_lock);
265 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
267 if (folder_unmatched != NULL) {
268 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
270 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
271 /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */
272 if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
273 while (g_list_find(up->folders, sub)) {
274 up->folders = g_list_remove(up->folders, sub);
275 camel_object_unref((CamelObject *)sub);
277 /* undo the freeze state that Unmatched has imposed on this source folder */
278 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
279 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
280 camel_folder_thaw(sub);
281 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
283 } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
284 if (g_list_find(up->folders, sub) != NULL) {
285 up->folders = g_list_remove(up->folders, sub);
286 camel_object_unref((CamelObject *)sub);
288 /* undo the freeze state that Unmatched has imposed on this source folder */
289 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
290 for (i = 0; i < ((CamelFolder *)folder_unmatched)->priv->frozen; i++)
291 camel_folder_thaw(sub);
292 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
295 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
298 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->remove_folder(vf, sub);
300 if (CAMEL_IS_VEE_FOLDER(sub))
303 camel_object_unref((CamelObject *)sub);
307 * camel_vee_folder_rebuild_folder:
308 * @vf: Virtual Folder object
309 * @sub: source CamelFolder to add to @vf
312 * Rebuild the folder @sub, if it should be.
315 camel_vee_folder_rebuild_folder(CamelVeeFolder *vf, CamelFolder *sub, CamelException *ex)
317 return ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->rebuild_folder(vf, sub, ex);
321 remove_folders(CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf)
323 camel_vee_folder_remove_folder(vf, folder);
324 camel_object_unref((CamelObject *)folder);
328 * camel_vee_folder_set_folders:
332 * Set the whole list of folder sources on a vee folder.
335 camel_vee_folder_set_folders(CamelVeeFolder *vf, GList *folders)
337 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
338 GHashTable *remove = g_hash_table_new(NULL, NULL);
342 /* setup a table of all folders we have currently */
343 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
346 g_hash_table_insert(remove, l->data, l->data);
347 camel_object_ref((CamelObject *)l->data);
350 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
352 /* if we already have the folder, ignore it, otherwise add it */
355 if ((folder = g_hash_table_lookup(remove, l->data))) {
356 g_hash_table_remove(remove, folder);
357 camel_object_unref((CamelObject *)folder);
359 camel_vee_folder_add_folder(vf, l->data);
364 /* then remove any we still have */
365 g_hash_table_foreach(remove, (GHFunc)remove_folders, vf);
366 g_hash_table_destroy(remove);
370 * camel_vee_folder_hash_folder:
374 * Create a hash string representing the folder name, which should be
375 * unique, and remain static for a given folder.
378 camel_vee_folder_hash_folder(CamelFolder *folder, char buffer[8])
381 unsigned char digest[16];
382 unsigned int state = 0, save = 0;
387 tmp = camel_service_get_url((CamelService *)folder->parent_store);
388 md5_update(&ctx, tmp, strlen(tmp));
390 md5_update(&ctx, folder->full_name, strlen(folder->full_name));
391 md5_final(&ctx, digest);
392 camel_base64_encode_close(digest, 6, FALSE, buffer, &state, &save);
395 if (buffer[i] == '+')
397 if (buffer[i] == '/')
403 * camel_vee_folder_get_location:
406 * @realuid: if not NULL, set to the uid of the real message, must be
407 * g_free'd by caller.
409 * Find the real folder (and uid)
414 camel_vee_folder_get_location(CamelVeeFolder *vf, const CamelVeeMessageInfo *vinfo, char **realuid)
418 folder = vinfo->real->summary->folder;
420 /* locking? yes? no? although the vfolderinfo is valid when obtained
421 the folder in it might not necessarily be so ...? */
422 if (CAMEL_IS_VEE_FOLDER(folder)) {
424 const CamelVeeMessageInfo *vfinfo;
426 vfinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info(folder, camel_message_info_uid(vinfo)+8);
427 res = camel_vee_folder_get_location((CamelVeeFolder *)folder, vfinfo, realuid);
428 camel_folder_free_message_info(folder, (CamelMessageInfo *)vfinfo);
432 *realuid = g_strdup(camel_message_info_uid(vinfo)+8);
438 static void vee_refresh_info(CamelFolder *folder, CamelException *ex)
440 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
441 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
444 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
445 list = p->folders_changed;
446 p->folders_changed = NULL;
447 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
451 CamelFolder *f = node->data;
453 if (camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
463 vee_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
465 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
466 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
469 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
473 CamelFolder *f = node->data;
475 camel_folder_sync(f, expunge, ex);
476 if (camel_exception_is_set(ex)) {
479 camel_object_get(f, NULL, CAMEL_OBJECT_DESCRIPTION, &desc, NULL);
480 camel_exception_setv(ex, ex->id, _("Error storing `%s': %s"), desc, ex->desc);
484 /* auto update vfolders shouldn't need a rebuild */
485 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
486 && camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
493 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
494 g_list_free(p->folders_changed);
495 p->folders_changed = NULL;
496 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
499 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
501 camel_object_state_write(vf);
505 vee_expunge (CamelFolder *folder, CamelException *ex)
507 ((CamelFolderClass *)((CamelObject *)folder)->klass)->sync(folder, TRUE, ex);
510 static CamelMimeMessage *
511 vee_get_message(CamelFolder *folder, const char *uid, CamelException *ex)
513 CamelVeeMessageInfo *mi;
514 CamelMimeMessage *msg = NULL;
516 mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
518 msg = camel_folder_get_message(mi->real->summary->folder, camel_message_info_uid(mi)+8, ex);
519 camel_message_info_free((CamelMessageInfo *)mi);
521 camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
522 _("No such message %s in %s"), uid,
530 vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
533 GPtrArray *matches, *result = g_ptr_array_new ();
535 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
536 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
537 GHashTable *searched = g_hash_table_new(NULL, NULL);
538 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
540 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
542 if (vf != folder_unmatched)
543 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
545 expr = g_strdup (expression);
549 CamelFolder *f = node->data;
553 /* make sure we only search each folder once - for unmatched folder to work right */
554 if (g_hash_table_lookup(searched, f) == NULL) {
555 camel_vee_folder_hash_folder(f, hash);
556 /* FIXME: shouldn't ignore search exception */
557 matches = camel_folder_search_by_expression(f, expression, NULL);
559 for (i = 0; i < matches->len; i++) {
560 char *uid = matches->pdata[i], *vuid;
562 vuid = g_malloc(strlen(uid)+9);
563 memcpy(vuid, hash, 8);
565 g_ptr_array_add(result, vuid);
567 camel_folder_search_free(f, matches);
569 g_hash_table_insert(searched, f, f);
571 node = g_list_next(node);
575 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
577 g_hash_table_destroy(searched);
583 vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex)
586 GPtrArray *matches, *result = g_ptr_array_new ();
587 GPtrArray *folder_uids = g_ptr_array_new();
589 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
590 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
591 GHashTable *searched = g_hash_table_new(NULL, NULL);
593 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
595 expr = g_strdup_printf("(and %s %s)", vf->expression ? vf->expression : "", expression);
598 CamelFolder *f = node->data;
602 /* make sure we only search each folder once - for unmatched folder to work right */
603 if (g_hash_table_lookup(searched, f) == NULL) {
604 camel_vee_folder_hash_folder(f, hash);
606 /* map the vfolder uid's to the source folder uid's first */
607 g_ptr_array_set_size(folder_uids, 0);
608 for (i=0;i<uids->len;i++) {
609 char *uid = uids->pdata[i];
611 if (strlen(uid) >= 8 && strncmp(uid, hash, 8) == 0)
612 g_ptr_array_add(folder_uids, uid+8);
614 if (folder_uids->len > 0) {
615 matches = camel_folder_search_by_uids(f, expression, folder_uids, ex);
617 for (i = 0; i < matches->len; i++) {
618 char *uid = matches->pdata[i], *vuid;
620 vuid = g_malloc(strlen(uid)+9);
621 memcpy(vuid, hash, 8);
623 g_ptr_array_add(result, vuid);
625 camel_folder_search_free(f, matches);
627 g_warning("Search failed: %s", camel_exception_get_description(ex));
630 g_hash_table_insert(searched, f, f);
632 node = g_list_next(node);
636 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
638 g_hash_table_destroy(searched);
639 g_ptr_array_free(folder_uids, TRUE);
645 vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex)
647 camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
651 vee_transfer_messages_to (CamelFolder *folder, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
653 camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
656 static void vee_rename(CamelFolder *folder, const char *new)
658 /*CamelVeeFolder *vf = (CamelVeeFolder *)folder;*/
660 ((CamelFolderClass *)camel_vee_folder_parent)->rename(folder, new);
663 static void vee_delete(CamelFolder *folder)
665 struct _CamelVeeFolderPrivate *p = _PRIVATE(folder);
667 /* NB: this is never called on UNMTACHED */
669 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
671 CamelFolder *f = p->folders->data;
674 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
676 camel_vee_folder_remove_folder((CamelVeeFolder *)folder, f);
677 camel_object_unref(f);
678 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
680 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
682 ((CamelFolderClass *)camel_vee_folder_parent)->delete(folder);
685 /* ********************************************************************** *
688 /* must be called with summary_lock held */
689 static CamelVeeMessageInfo *
690 vee_folder_add_uid(CamelVeeFolder *vf, CamelFolder *f, const char *inuid, const char hash[8])
692 CamelMessageInfo *info;
693 CamelVeeMessageInfo *mi = NULL;
695 info = camel_folder_get_message_info(f, inuid);
697 mi = camel_vee_summary_add((CamelVeeSummary *)((CamelFolder *)vf)->summary, info, hash);
698 camel_folder_free_message_info(f, info);
704 vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source)
706 int i, count, n, still = FALSE, start, last;
708 CamelFolder *folder = (CamelFolder *)vf;
710 /*struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);*/
711 CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
713 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
714 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
715 CamelFolderSummary *ssummary = source->summary;
718 if (vf == folder_unmatched)
721 if ((source->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
724 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
726 if (folder_unmatched != NULL) {
727 /* check if this folder is still to be part of unmatched */
728 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !killun) {
729 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
730 still = g_list_find(_PRIVATE(folder_unmatched)->folders, source) != NULL;
731 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
732 camel_vee_folder_hash_folder(source, hash);
735 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
737 /* See if we just blow all uid's from this folder away from unmatched, regardless */
741 count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
742 for (i=0;i<count;i++) {
743 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
746 if (mi->real->summary == ssummary) {
747 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
750 } else if (last+1 == i) {
753 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
758 camel_message_info_free((CamelMessageInfo *)mi);
762 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
768 count = camel_folder_summary_count(folder->summary);
769 for (i=0;i<count;i++) {
770 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
772 if (mi->real->summary == ssummary) {
773 const char *uid = camel_message_info_uid(mi);
775 camel_folder_change_info_remove_uid(vf->changes, uid);
779 } else if (last+1 == i) {
782 camel_folder_summary_remove_range(folder->summary, start, last);
786 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && folder_unmatched != NULL) {
788 if (g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
789 n = GPOINTER_TO_INT (oldval);
791 g_hash_table_remove(unmatched_uids, oldkey);
792 if (vee_folder_add_uid(folder_unmatched, source, oldkey+8, hash))
793 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
796 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
800 if (g_hash_table_lookup_extended(unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, &oldval)) {
801 g_hash_table_remove(unmatched_uids, oldkey);
807 camel_message_info_free((CamelMessageInfo *)mi);
812 camel_folder_summary_remove_range(folder->summary, start, last);
814 if (folder_unmatched) {
815 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
816 unmatched_changes = folder_unmatched->changes;
817 folder_unmatched->changes = camel_folder_change_info_new();
820 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
823 if (camel_folder_change_info_changed(vf->changes)) {
824 vf_changes = vf->changes;
825 vf->changes = camel_folder_change_info_new();
828 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
830 if (unmatched_changes) {
831 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
832 camel_folder_change_info_free(unmatched_changes);
836 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
837 camel_folder_change_info_free(vf_changes);
841 struct _update_data {
845 CamelVeeFolder *folder_unmatched;
846 GHashTable *unmatched_uids;
850 unmatched_check_uid(char *uidin, void *value, struct _update_data *u)
855 uid = alloca(strlen(uidin)+9);
856 memcpy(uid, u->hash, 8);
857 strcpy(uid+8, uidin);
858 n = GPOINTER_TO_INT(g_hash_table_lookup(u->unmatched_uids, uid));
860 if (vee_folder_add_uid(u->folder_unmatched, u->source, uidin, u->hash))
861 camel_folder_change_info_add_uid(u->folder_unmatched->changes, uid);
863 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)u->folder_unmatched)->summary, uid);
865 camel_folder_summary_remove(((CamelFolder *)u->folder_unmatched)->summary, (CamelMessageInfo *)mi);
866 camel_folder_change_info_remove_uid(u->folder_unmatched->changes, uid);
867 camel_message_info_free((CamelMessageInfo *)mi);
873 folder_added_uid(char *uidin, void *value, struct _update_data *u)
875 CamelVeeMessageInfo *mi;
880 if ( (mi = vee_folder_add_uid(u->vf, u->source, uidin, u->hash)) ) {
881 camel_folder_change_info_add_uid(u->vf->changes, camel_message_info_uid(mi));
883 if (!CAMEL_IS_VEE_FOLDER(u->source) && u->unmatched_uids != NULL) {
884 if (g_hash_table_lookup_extended(u->unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, &oldval)) {
885 n = GPOINTER_TO_INT (oldval);
886 g_hash_table_insert(u->unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
888 g_hash_table_insert(u->unmatched_uids, g_strdup(camel_message_info_uid(mi)), GINT_TO_POINTER(1));
894 /* build query contents for a single folder */
896 vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex)
898 GPtrArray *match, *all;
899 GHashTable *allhash, *matchhash;
900 CamelFolder *f = source;
901 CamelFolder *folder = (CamelFolder *)vf;
902 int i, n, count, start, last;
903 struct _update_data u;
904 CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
905 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
906 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
907 CamelFolderSummary *ssummary = source->summary;
909 if (vf == folder_unmatched)
912 /* if we have no expression, or its been cleared, then act as if no matches */
913 if (vf->expression == NULL) {
914 match = g_ptr_array_new();
916 match = camel_folder_search_by_expression(f, vf->expression, ex);
923 u.folder_unmatched = folder_unmatched;
924 u.unmatched_uids = unmatched_uids;
925 camel_vee_folder_hash_folder(source, u.hash);
927 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
929 /* we build 2 hash tables, one for all uid's not matched, the other for all matched uid's,
930 we just ref the real memory */
931 matchhash = g_hash_table_new(g_str_hash, g_str_equal);
932 for (i=0;i<match->len;i++)
933 g_hash_table_insert(matchhash, match->pdata[i], GINT_TO_POINTER (1));
935 allhash = g_hash_table_new(g_str_hash, g_str_equal);
936 all = camel_folder_get_uids(f);
937 for (i=0;i<all->len;i++)
938 if (g_hash_table_lookup(matchhash, all->pdata[i]) == NULL)
939 g_hash_table_insert(allhash, all->pdata[i], GINT_TO_POINTER (1));
941 if (folder_unmatched != NULL)
942 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
944 /* scan, looking for "old" uid's to be removed */
947 count = camel_folder_summary_count(folder->summary);
948 for (i=0;i<count;i++) {
949 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
952 if (mi->real->summary == ssummary) {
953 char *uid = (char *)camel_message_info_uid(mi), *oldkey;
956 if (g_hash_table_lookup(matchhash, uid+8) == NULL) {
959 } else if (last+1 == i) {
962 camel_folder_summary_remove_range(folder->summary, start, last);
966 camel_folder_change_info_remove_uid(vf->changes, camel_message_info_uid(mi));
967 if (!CAMEL_IS_VEE_FOLDER(source)
968 && unmatched_uids != NULL
969 && g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
970 n = GPOINTER_TO_INT (oldval);
972 g_hash_table_remove(unmatched_uids, oldkey);
975 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
979 g_hash_table_remove(matchhash, uid+8);
982 camel_message_info_free((CamelMessageInfo *)mi);
986 camel_folder_summary_remove_range(folder->summary, start, last);
988 /* now matchhash contains any new uid's, add them, etc */
989 g_hash_table_foreach(matchhash, (GHFunc)folder_added_uid, &u);
991 if (folder_unmatched != NULL) {
992 /* scan unmatched, remove any that have vanished, etc */
993 count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
994 for (i=0;i<count;i++) {
995 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
998 if (mi->real->summary == ssummary) {
999 char *uid = (char *)camel_message_info_uid(mi);
1001 if (g_hash_table_lookup(allhash, uid+8) == NULL) {
1002 /* no longer exists at all, just remove it entirely */
1003 camel_folder_summary_remove_index(((CamelFolder *)folder_unmatched)->summary, i);
1004 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
1007 g_hash_table_remove(allhash, uid+8);
1010 camel_message_info_free((CamelMessageInfo *)mi);
1014 /* now allhash contains all potentially new uid's for the unmatched folder, process */
1015 if (!CAMEL_IS_VEE_FOLDER(source))
1016 g_hash_table_foreach(allhash, (GHFunc)unmatched_check_uid, &u);
1018 /* copy any changes so we can raise them outside the lock */
1019 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1020 unmatched_changes = folder_unmatched->changes;
1021 folder_unmatched->changes = camel_folder_change_info_new();
1024 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1027 if (camel_folder_change_info_changed(vf->changes)) {
1028 vf_changes = vf->changes;
1029 vf->changes = camel_folder_change_info_new();
1032 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1034 g_hash_table_destroy(matchhash);
1035 g_hash_table_destroy(allhash);
1036 /* if expression not set, we only had a null list */
1037 if (vf->expression == NULL)
1038 g_ptr_array_free(match, TRUE);
1040 camel_folder_search_free(f, match);
1041 camel_folder_free_uids(f, all);
1043 if (unmatched_changes) {
1044 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1045 camel_folder_change_info_free(unmatched_changes);
1049 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1050 camel_folder_change_info_free(vf_changes);
1058 (match-folder "folder1" "folder2")
1063 /* Hold all these with summary lock and unmatched summary lock held */
1065 folder_changed_add_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
1067 CamelVeeMessageInfo *vinfo;
1072 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1073 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1075 vinfo = vee_folder_add_uid(vf, sub, uid, hash);
1079 vuid = camel_message_info_uid(vinfo);
1080 camel_folder_change_info_add_uid(vf->changes, vuid);
1082 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1083 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1084 n = GPOINTER_TO_INT (oldval);
1085 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n+1));
1087 g_hash_table_insert(unmatched_uids, g_strdup(vuid), GINT_TO_POINTER (1));
1089 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1091 camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1092 camel_folder_summary_remove(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)vinfo);
1093 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1099 folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf)
1101 CamelFolder *folder = (CamelFolder *)vf;
1102 char *vuid, *oldkey;
1105 CamelVeeMessageInfo *vinfo;
1106 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1107 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1109 vuid = alloca(strlen(uid)+9);
1110 memcpy(vuid, hash, 8);
1111 strcpy(vuid+8, uid);
1113 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1115 camel_folder_change_info_remove_uid(vf->changes, vuid);
1116 camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)vinfo);
1117 camel_message_info_free((CamelMessageInfo *)vinfo);
1120 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1122 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1123 n = GPOINTER_TO_INT (oldval);
1125 g_hash_table_remove(unmatched_uids, oldkey);
1126 if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1127 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1130 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1133 if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1134 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1137 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, &oldval)) {
1138 g_hash_table_remove(unmatched_uids, oldkey);
1142 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1144 camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
1145 camel_folder_summary_remove_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
1146 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1153 folder_changed_change_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
1156 CamelVeeMessageInfo *vinfo, *uinfo = NULL;
1157 CamelMessageInfo *info;
1158 CamelFolder *folder = (CamelFolder *)vf;
1159 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1161 vuid = alloca(strlen(uid)+9);
1162 memcpy(vuid, hash, 8);
1163 strcpy(vuid+8, uid);
1165 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1166 if (folder_unmatched != NULL)
1167 uinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
1168 if (vinfo || uinfo) {
1169 info = camel_folder_get_message_info(sub, uid);
1172 camel_folder_change_info_change_uid(vf->changes, vuid);
1173 camel_message_info_free((CamelMessageInfo *)vinfo);
1177 camel_folder_change_info_change_uid(folder_unmatched->changes, vuid);
1178 camel_message_info_free((CamelMessageInfo *)uinfo);
1181 camel_folder_free_message_info(sub, info);
1184 folder_changed_remove_uid(sub, uid, hash, FALSE, vf);
1185 camel_message_info_free((CamelMessageInfo *)vinfo);
1188 camel_message_info_free((CamelMessageInfo *)uinfo);
1193 struct _folder_changed_msg {
1194 CamelSessionThreadMsg msg;
1195 CamelFolderChangeInfo *changes;
1201 folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg)
1203 struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1204 CamelFolder *sub = m->sub;
1205 CamelFolder *folder = (CamelFolder *)m->vf;
1206 CamelVeeFolder *vf = m->vf;
1207 CamelFolderChangeInfo *changes = m->changes;
1208 char *vuid = NULL, hash[8];
1210 CamelVeeMessageInfo *vinfo;
1212 CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
1213 GPtrArray *matches_added = NULL, /* newly added, that match */
1214 *matches_changed = NULL, /* newly changed, that now match */
1217 GPtrArray *always_changed = NULL;
1218 GHashTable *matches_hash;
1219 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1220 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1222 /* Check the folder hasn't beem removed while we weren't watching */
1223 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1224 if (g_list_find(_PRIVATE(vf)->folders, sub) == NULL) {
1225 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1229 camel_vee_folder_hash_folder(sub, hash);
1231 /* Lookup anything before we lock anything, to avoid deadlock with build_folder */
1233 /* Find newly added that match */
1234 if (changes->uid_added->len > 0) {
1235 dd(printf(" Searching for added matches '%s'\n", vf->expression));
1236 matches_added = camel_folder_search_by_uids(sub, vf->expression, changes->uid_added, NULL);
1240 In this code around here, we can work out if the search will affect the changes
1241 we had, and only re-search against them if they might have */
1243 /* Search for changed items that newly match, but only if we dont have them */
1244 changed = changes->uid_changed;
1245 if (changed->len > 0) {
1246 dd(printf(" Searching for changed matches '%s'\n", vf->expression));
1248 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
1249 newchanged = g_ptr_array_new();
1250 always_changed = g_ptr_array_new();
1251 for (i=0;i<changed->len;i++) {
1252 uid = changed->pdata[i];
1253 if (strlen(uid)+9 > vuidlen) {
1254 vuidlen = strlen(uid)+64;
1255 vuid = g_realloc(vuid, vuidlen);
1257 memcpy(vuid, hash, 8);
1258 strcpy(vuid+8, uid);
1259 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1260 if (vinfo == NULL) {
1261 g_ptr_array_add(newchanged, (char *)uid);
1263 g_ptr_array_add(always_changed, (char *)uid);
1264 camel_message_info_free((CamelMessageInfo *)vinfo);
1267 changed = newchanged;
1271 matches_changed = camel_folder_search_by_uids(sub, vf->expression, changed, NULL);
1274 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1275 if (folder_unmatched != NULL)
1276 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1278 dd(printf("Vfolder '%s' subfolder changed '%s'\n", folder->full_name, sub->full_name));
1279 dd(printf(" changed %u added %u removed %u\n", changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len));
1281 /* Always remove removed uid's, in any case */
1282 for (i=0;i<changes->uid_removed->len;i++) {
1283 dd(printf(" removing uid '%s'\n", (char *)changes->uid_removed->pdata[i]));
1284 folder_changed_remove_uid(sub, changes->uid_removed->pdata[i], hash, FALSE, vf);
1287 /* Add any newly matched or to unmatched folder if they dont */
1288 if (matches_added) {
1289 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1290 for (i=0;i<matches_added->len;i++) {
1291 dd(printf(" %s", (char *)matches_added->pdata[i]));
1292 g_hash_table_insert(matches_hash, matches_added->pdata[i], matches_added->pdata[i]);
1294 for (i=0;i<changes->uid_added->len;i++) {
1295 uid = changes->uid_added->pdata[i];
1296 if (g_hash_table_lookup(matches_hash, uid)) {
1297 dd(printf(" adding uid '%s' [newly matched]\n", (char *)uid));
1298 folder_changed_add_uid(sub, uid, hash, vf);
1299 } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
1300 if (strlen(uid)+9 > vuidlen) {
1301 vuidlen = strlen(uid)+64;
1302 vuid = g_realloc(vuid, vuidlen);
1304 memcpy(vuid, hash, 8);
1305 strcpy(vuid+8, uid);
1307 if (!CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL && g_hash_table_lookup(unmatched_uids, vuid) == NULL) {
1308 dd(printf(" adding uid '%s' to Unmatched [newly unmatched]\n", (char *)uid));
1309 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
1310 if (vinfo == NULL) {
1311 if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
1312 camel_folder_change_info_add_uid(folder_unmatched->changes, vuid);
1314 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1319 g_hash_table_destroy(matches_hash);
1322 /* Change any newly changed */
1323 if (always_changed) {
1324 for (i=0;i<always_changed->len;i++)
1325 folder_changed_change_uid(sub, always_changed->pdata[i], hash, vf);
1326 g_ptr_array_free(always_changed, TRUE);
1329 /* Change/add/remove any changed */
1330 if (matches_changed) {
1331 /* If we are auto-updating, then re-check changed uids still match */
1332 dd(printf(" Vfolder %supdate\nuids match:", (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO)?"auto-":""));
1333 matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
1334 for (i=0;i<matches_changed->len;i++) {
1335 dd(printf(" %s", (char *)matches_changed->pdata[i]));
1336 g_hash_table_insert(matches_hash, matches_changed->pdata[i], matches_changed->pdata[i]);
1339 for (i=0;i<changed->len;i++) {
1340 uid = changed->pdata[i];
1341 if (strlen(uid)+9 > vuidlen) {
1342 vuidlen = strlen(uid)+64;
1343 vuid = g_realloc(vuid, vuidlen);
1345 memcpy(vuid, hash, 8);
1346 strcpy(vuid+8, uid);
1347 vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
1348 if (vinfo == NULL) {
1349 if (g_hash_table_lookup(matches_hash, uid)) {
1350 /* A uid we dont have, but now it matches, add it */
1351 dd(printf(" adding uid '%s' [newly matched]\n", uid));
1352 folder_changed_add_uid(sub, uid, hash, vf);
1354 /* A uid we still don't have, just change it (for unmatched) */
1355 folder_changed_change_uid(sub, uid, hash, vf);
1358 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
1359 || g_hash_table_lookup(matches_hash, uid)) {
1360 /* still match, or we're not auto-updating, change event, (if it changed) */
1361 dd(printf(" changing uid '%s' [still matches]\n", uid));
1362 folder_changed_change_uid(sub, uid, hash, vf);
1364 /* No longer matches, remove it, but keep it in unmatched (potentially) */
1365 dd(printf(" removing uid '%s' [did match]\n", uid));
1366 folder_changed_remove_uid(sub, uid, hash, TRUE, vf);
1368 camel_message_info_free((CamelMessageInfo *)vinfo);
1371 g_hash_table_destroy(matches_hash);
1373 /* stuff didn't match but it changed - check unmatched folder for changes */
1374 for (i=0;i<changed->len;i++)
1375 folder_changed_change_uid(sub, changed->pdata[i], hash, vf);
1378 if (folder_unmatched != NULL) {
1379 if (camel_folder_change_info_changed(folder_unmatched->changes)) {
1380 unmatched_changes = folder_unmatched->changes;
1381 folder_unmatched->changes = camel_folder_change_info_new();
1384 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1387 if (camel_folder_change_info_changed(vf->changes)) {
1388 vf_changes = vf->changes;
1389 vf->changes = camel_folder_change_info_new();
1392 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1394 /* Cleanup stuff on our folder */
1396 camel_folder_search_free(sub, matches_added);
1398 if (matches_changed)
1399 camel_folder_search_free(sub, matches_changed);
1401 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1403 /* cleanup the rest */
1405 g_ptr_array_free(newchanged, TRUE);
1409 if (unmatched_changes) {
1410 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1411 camel_folder_change_info_free(unmatched_changes);
1415 /* If not auto-updating, keep track of changed folders for later re-sync */
1416 if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
1417 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
1418 if (g_list_find(vf->priv->folders_changed, sub) != NULL)
1419 vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub);
1420 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
1423 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1424 camel_folder_change_info_free(vf_changes);
1429 folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg)
1431 struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1433 camel_folder_change_info_free(m->changes);
1434 camel_object_unref((CamelObject *)m->vf);
1435 camel_object_unref((CamelObject *)m->sub);
1438 static CamelSessionThreadOps folder_changed_ops = {
1439 folder_changed_change,
1440 folder_changed_free,
1444 folder_changed_base(CamelVeeFolder *vf, CamelFolder *sub, CamelFolderChangeInfo *changes)
1446 struct _folder_changed_msg *m;
1447 CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session;
1449 m = camel_session_thread_msg_new(session, &folder_changed_ops, sizeof(*m));
1450 m->changes = camel_folder_change_info_new();
1451 camel_folder_change_info_cat(m->changes, changes);
1453 camel_object_ref((CamelObject *)sub);
1455 camel_object_ref((CamelObject *)vf);
1456 camel_session_thread_queue(session, &m->msg, 0);
1460 folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf)
1462 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_changed(vf, sub, changes);
1465 /* track vanishing folders */
1467 subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf)
1469 camel_vee_folder_remove_folder(vf, f);
1473 subfolder_renamed_update(CamelVeeFolder *vf, CamelFolder *sub, char hash[8])
1476 CamelFolderChangeInfo *changes = NULL;
1477 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1478 GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
1479 CamelFolderSummary *ssummary = sub->summary;
1481 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1483 count = camel_folder_summary_count(((CamelFolder *)vf)->summary);
1484 for (i=0;i<count;i++) {
1485 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)vf)->summary, i);
1486 CamelVeeMessageInfo *vinfo;
1491 if (mi->real->summary == ssummary) {
1492 char *uid = (char *)camel_message_info_uid(mi);
1496 camel_folder_change_info_remove_uid(vf->changes, uid);
1497 camel_folder_summary_remove(((CamelFolder *)vf)->summary, (CamelMessageInfo *)mi);
1499 /* works since we always append on the end */
1503 vinfo = vee_folder_add_uid(vf, sub, uid+8, hash);
1505 camel_folder_change_info_add_uid(vf->changes, camel_message_info_uid(vinfo));
1507 /* check unmatched uid's table for any matches */
1508 if (vf == folder_unmatched
1509 && g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, &oldval)) {
1510 g_hash_table_remove(unmatched_uids, oldkey);
1511 g_hash_table_insert(unmatched_uids, g_strdup(camel_message_info_uid(vinfo)), oldval);
1516 camel_message_info_free((CamelMessageInfo *)mi);
1519 if (camel_folder_change_info_changed(vf->changes)) {
1520 changes = vf->changes;
1521 vf->changes = camel_folder_change_info_new();
1524 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1527 camel_object_trigger_event((CamelObject *)vf, "folder_changed", changes);
1528 camel_folder_change_info_free(changes);
1533 folder_renamed_base(CamelVeeFolder *vf, CamelFolder *f, const char *old)
1536 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1538 /* TODO: This could probably be done in another thread, tho it is pretty quick/memory bound */
1540 /* Life just got that little bit harder, if the folder is renamed, it means it breaks all of our uid's.
1541 We need to remove the old uid's, fix them up, then release the new uid's, for the uid's that match this folder */
1543 camel_vee_folder_hash_folder(f, hash);
1545 subfolder_renamed_update(vf, f, hash);
1546 if (folder_unmatched != NULL)
1547 subfolder_renamed_update(folder_unmatched, f, hash);
1551 folder_renamed(CamelFolder *sub, const char *old, CamelVeeFolder *vf)
1553 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_renamed(vf, sub, old);
1557 vee_freeze (CamelFolder *folder)
1559 CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1560 struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1563 CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1567 CamelFolder *f = node->data;
1569 camel_folder_freeze(f);
1573 CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1575 /* call parent implementation */
1576 CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->freeze(folder);
1580 vee_thaw(CamelFolder *folder)
1582 CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1583 struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1586 CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1590 CamelFolder *f = node->data;
1592 camel_folder_thaw(f);
1596 CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1598 /* call parent implementation */
1599 CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->thaw(folder);
1602 /* vfolder base implementaitons */
1604 vee_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
1606 vee_rebuild_folder(vf, sub, NULL);
1610 vee_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
1612 vee_folder_remove_folder(vf, sub);
1616 vee_set_expression(CamelVeeFolder *vf, const char *query)
1618 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1621 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
1623 /* no change, do nothing */
1624 if ((vf->expression && query && strcmp(vf->expression, query) == 0)
1625 || (vf->expression == NULL && query == NULL)) {
1626 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1630 g_free(vf->expression);
1632 vf->expression = g_strdup(query);
1636 CamelFolder *f = node->data;
1638 if (camel_vee_folder_rebuild_folder(vf, f, NULL) == -1)
1644 CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
1645 g_list_free(p->folders_changed);
1646 p->folders_changed = NULL;
1647 CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
1649 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1653 camel_vee_folder_class_init (CamelVeeFolderClass *klass)
1655 CamelFolderClass *folder_class = (CamelFolderClass *) klass;
1657 camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
1659 folder_class->refresh_info = vee_refresh_info;
1660 folder_class->sync = vee_sync;
1661 folder_class->expunge = vee_expunge;
1663 folder_class->get_message = vee_get_message;
1664 folder_class->append_message = vee_append_message;
1665 folder_class->transfer_messages_to = vee_transfer_messages_to;
1667 folder_class->search_by_expression = vee_search_by_expression;
1668 folder_class->search_by_uids = vee_search_by_uids;
1670 folder_class->rename = vee_rename;
1671 folder_class->delete = vee_delete;
1673 folder_class->freeze = vee_freeze;
1674 folder_class->thaw = vee_thaw;
1676 klass->set_expression = vee_set_expression;
1677 klass->add_folder = vee_add_folder;
1678 klass->remove_folder = vee_remove_folder;
1679 klass->rebuild_folder = vee_rebuild_folder;
1680 klass->folder_changed = folder_changed_base;
1681 klass->folder_renamed = folder_renamed_base;
1685 camel_vee_folder_init (CamelVeeFolder *obj)
1687 struct _CamelVeeFolderPrivate *p;
1688 CamelFolder *folder = (CamelFolder *)obj;
1690 p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
1692 folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
1693 CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
1695 /* FIXME: what to do about user flags if the subfolder doesn't support them? */
1696 folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
1697 CAMEL_MESSAGE_DELETED |
1698 CAMEL_MESSAGE_DRAFT |
1699 CAMEL_MESSAGE_FLAGGED |
1702 obj->changes = camel_folder_change_info_new();
1703 obj->search = camel_folder_search_new();
1705 p->summary_lock = g_mutex_new();
1706 p->subfolder_lock = g_mutex_new();
1707 p->changed_lock = g_mutex_new();
1711 camel_vee_folder_finalise (CamelObject *obj)
1713 CamelVeeFolder *vf = (CamelVeeFolder *)obj;
1714 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1715 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1718 /* TODO: there may be other leaks? */
1720 /* This may invoke sub-classes with partially destroyed state, they must deal with this */
1721 if (vf == folder_unmatched) {
1722 for (node = p->folders;node;node = g_list_next(node))
1723 camel_object_unref(node->data);
1725 while (p->folders) {
1726 CamelFolder *f = p->folders->data;
1727 camel_vee_folder_remove_folder(vf, f);
1731 g_free(vf->expression);
1733 g_list_free(p->folders);
1734 g_list_free(p->folders_changed);
1736 camel_folder_change_info_free(vf->changes);
1737 camel_object_unref((CamelObject *)vf->search);
1739 g_mutex_free(p->summary_lock);
1740 g_mutex_free(p->subfolder_lock);
1741 g_mutex_free(p->changed_lock);