1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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>
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"
48 #define dd(x) (camel_debug("vfolder")?(x):0)
50 #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv)
53 static void vee_refresh_info(CamelFolder *folder, CamelException *ex);
55 static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
56 static void vee_expunge (CamelFolder *folder, CamelException *ex);
58 static void vee_freeze(CamelFolder *folder);
59 static void vee_thaw(CamelFolder *folder);
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);
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);
69 static void vee_rename(CamelFolder *folder, const gchar *new);
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);
76 static gint vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex);
77 static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source);
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);
83 static CamelFolderClass *camel_vee_folder_parent;
86 camel_vee_folder_get_type (void)
88 static CamelType type = CAMEL_INVALID_TYPE;
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,
96 (CamelObjectInitFunc) camel_vee_folder_init,
97 (CamelObjectFinalizeFunc) camel_vee_folder_finalise);
104 camel_vee_folder_construct (CamelVeeFolder *vf, CamelStore *parent_store, const gchar *full, const gchar *name, guint32 flags)
106 CamelFolder *folder = (CamelFolder *)vf;
109 camel_folder_construct(folder, parent_store, full, name);
111 folder->summary = camel_vee_summary_new(folder);
113 if (CAMEL_IS_VEE_STORE(parent_store))
114 vf->parent_vee_store = (CamelVeeStore *)parent_store;
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
123 * Create a new CamelVeeFolder object.
125 * Returns: A new CamelVeeFolder widget.
128 camel_vee_folder_new(CamelStore *parent_store, const gchar *full, guint32 flags)
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);
137 const gchar *name = strrchr(full, '/');
143 vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
144 camel_vee_folder_construct(vf, parent_store, full, name, flags);
147 d(printf("returning folder %s %p, count = %d\n", full, vf, camel_folder_get_message_count((CamelFolder *)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);
153 if (camel_object_state_read(vf) == -1) {
154 /* setup defaults: we have none currently */
157 return (CamelFolder *)vf;
161 camel_vee_folder_set_expression(CamelVeeFolder *vf, const gchar *query)
163 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->set_expression(vf, query);
167 * camel_vee_folder_add_folder:
168 * @vf: Virtual Folder object
169 * @sub: source CamelFolder to add to @vf
171 * Adds @sub as a source folder to @vf.
174 camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
176 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
178 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
180 if (vf == (CamelVeeFolder *)sub) {
181 g_warning("Adding a virtual folder to itself as source, ignored");
185 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
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);
192 CAMEL_FOLDER_LOCK(vf, change_lock);
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);
198 CAMEL_FOLDER_UNLOCK(vf, change_lock);
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);
205 CAMEL_FOLDER_LOCK(folder_unmatched, change_lock);
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);
211 CAMEL_FOLDER_UNLOCK(folder_unmatched, change_lock);
214 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
216 d(printf("camel_vee_folder_add_folder(%s, %s)\n", ((CamelFolder *)vf)->full_name, sub->full_name));
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);
222 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->add_folder(vf, sub);
227 * camel_vee_folder_remove_folder:
228 * @vf: Virtual Folder object
229 * @sub: source CamelFolder to remove from @vf
231 * Removed the source folder, @sub, from the virtual folder, @vf.
235 camel_vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
237 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
239 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
241 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
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);
247 if (g_list_find(p->folders, sub) == NULL) {
248 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
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);
256 p->folders = g_list_remove(p->folders, sub);
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);
264 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
266 if (folder_unmatched != NULL) {
267 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
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);
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);
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);
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);
294 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
297 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->remove_folder(vf, sub);
299 if (CAMEL_IS_VEE_FOLDER(sub))
302 camel_object_unref((CamelObject *)sub);
306 * camel_vee_folder_rebuild_folder:
307 * @vf: Virtual Folder object
308 * @sub: source CamelFolder to add to @vf
311 * Rebuild the folder @sub, if it should be.
314 camel_vee_folder_rebuild_folder(CamelVeeFolder *vf, CamelFolder *sub, CamelException *ex)
316 return ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->rebuild_folder(vf, sub, ex);
320 remove_folders(CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf)
322 camel_vee_folder_remove_folder(vf, folder);
323 camel_object_unref((CamelObject *)folder);
327 * camel_vee_folder_set_folders:
331 * Set the whole list of folder sources on a vee folder.
334 camel_vee_folder_set_folders(CamelVeeFolder *vf, GList *folders)
336 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
337 GHashTable *remove = g_hash_table_new(NULL, NULL);
341 /* setup a table of all folders we have currently */
342 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
345 g_hash_table_insert(remove, l->data, l->data);
346 camel_object_ref((CamelObject *)l->data);
349 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
351 /* if we already have the folder, ignore it, otherwise add it */
354 if ((folder = g_hash_table_lookup(remove, l->data))) {
355 g_hash_table_remove(remove, folder);
356 camel_object_unref((CamelObject *)folder);
358 camel_vee_folder_add_folder(vf, l->data);
363 /* then remove any we still have */
364 g_hash_table_foreach(remove, (GHFunc)remove_folders, vf);
365 g_hash_table_destroy(remove);
369 * camel_vee_folder_hash_folder:
373 * Create a hash string representing the folder name, which should be
374 * unique, and remain static for a given folder.
377 camel_vee_folder_hash_folder(CamelFolder *folder, gchar buffer[8])
382 gint state = 0, save = 0;
386 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
387 digest = g_alloca (length);
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);
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);
398 g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save);
399 g_base64_encode_close (FALSE, buffer, &state, &save);
402 if (buffer[i] == '+')
404 if (buffer[i] == '/')
410 * camel_vee_folder_get_location:
413 * @realuid: if not NULL, set to the uid of the real message, must be
414 * g_free'd by caller.
416 * Find the real folder (and uid)
421 camel_vee_folder_get_location(CamelVeeFolder *vf, const CamelVeeMessageInfo *vinfo, gchar **realuid)
425 folder = vinfo->summary->folder;
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)) {
431 const CamelVeeMessageInfo *vfinfo;
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);
439 *realuid = g_strdup(camel_message_info_uid(vinfo)+8);
445 static void vee_refresh_info(CamelFolder *folder, CamelException *ex)
447 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
448 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
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);
458 CamelFolder *f = node->data;
460 if (camel_vee_folder_rebuild_folder(vf, f, ex) == -1)
470 count_folder (CamelFolder *f, gchar *expr, CamelException *ex)
472 return camel_folder_count_by_expression(f, expr, ex);
475 count_result (CamelFolderSummary *summary, const gchar *query, CamelException *ex)
477 CamelFolder *folder = summary->folder;
478 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
480 gchar *expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", query);
482 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
486 CamelFolder *f = node->data;
487 count += count_folder(f, expr, ex);
495 static CamelFIRecord *
496 summary_header_to_db (CamelFolderSummary *s, CamelException *ex)
498 CamelFIRecord * record = g_new0 (CamelFIRecord, 1);
501 guint32 visible, unread, deleted, junked, junked_not_deleted;
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;
507 record->folder_name = table_name;
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;
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;
529 if (((CamelVeeSummary *)s)->fake_visible_count)
530 record->visible_count = ((CamelVeeSummary *)s)->fake_visible_count;
532 record->visible_count = s->visible_count;
533 ((CamelVeeSummary *)s)->fake_visible_count = 0;
535 record->jnd_count = s->junk_not_deleted_count;
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);
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;
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));
556 vee_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
558 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
559 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
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;
566 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
570 CamelFolder *f = node->data;
572 camel_folder_sync(f, expunge, ex);
573 if (camel_exception_is_set(ex) && strncmp(camel_exception_get_description(ex), "no such table", 13)) {
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));
580 camel_exception_clear (ex);
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) */
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.
594 record = summary_header_to_db (folder->summary, ex);
595 camel_db_write_folder_info_record (folder->parent_store->cdb, record, ex);
598 /* It makes no sense to clear the folders_changed list without
599 * actually rebuilding. */
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);
608 if (vf->priv->unread_vfolder == 1) {
609 /* Cleanup Junk/Trash uids */
612 count = folder->summary->uids->len;
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);
623 camel_message_info_free (mi);
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);
629 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
631 camel_object_state_write(vf);
635 vee_expunge (CamelFolder *folder, CamelException *ex)
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);
642 static CamelMimeMessage *
643 vee_get_message(CamelFolder *folder, const gchar *uid, CamelException *ex)
645 CamelVeeMessageInfo *mi;
646 CamelMimeMessage *msg = NULL;
648 mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
650 msg = camel_folder_get_message(mi->summary->folder, camel_message_info_uid(mi)+8, ex);
651 camel_message_info_free((CamelMessageInfo *)mi);
653 camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
654 _("No such message %s in %s"), uid,
662 vee_count_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex)
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;
672 if (vf != folder_unmatched)
673 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
675 expr = g_strdup (expression);
679 CamelFolder *f = node->data;
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);
686 node = g_list_next(node);
691 g_hash_table_destroy(searched);
695 vee_search_by_expression(CamelFolder *folder, const gchar *expression, CamelException *ex)
698 GPtrArray *matches, *result = g_ptr_array_new ();
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;
705 if (vf != folder_unmatched)
706 expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression);
708 expr = g_strdup (expression);
712 CamelFolder *f = node->data;
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);
724 for (i = 0; i < matches->len; i++) {
725 gchar *uid = matches->pdata[i], *vuid;
727 vuid = g_malloc(strlen(uid)+9);
728 memcpy(vuid, hash, 8);
730 g_ptr_array_add(result, (gpointer) camel_pstring_strdup(vuid));
733 camel_folder_search_free(f, matches);
735 g_hash_table_insert(searched, f, f);
737 node = g_list_next(node);
742 g_hash_table_destroy(searched);
743 d(printf("returning %d\n", result->len));
748 vee_search_by_uids(CamelFolder *folder, const gchar *expression, GPtrArray *uids, CamelException *ex)
751 GPtrArray *matches, *result = g_ptr_array_new ();
752 GPtrArray *folder_uids = g_ptr_array_new();
754 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
755 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
756 GHashTable *searched = g_hash_table_new(NULL, NULL);
758 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
760 expr = g_strdup_printf("(and %s %s)", vf->expression ? vf->expression : "", expression);
763 CamelFolder *f = node->data;
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);
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];
776 if (strlen(uid) >= 8 && strncmp(uid, hash, 8) == 0)
777 g_ptr_array_add(folder_uids, uid+8);
779 if (folder_uids->len > 0) {
780 matches = camel_folder_search_by_uids(f, expr, folder_uids, ex);
782 for (i = 0; i < matches->len; i++) {
783 gchar *uid = matches->pdata[i], *vuid;
785 vuid = g_malloc(strlen(uid)+9);
786 memcpy(vuid, hash, 8);
788 g_ptr_array_add(result, (gpointer) camel_pstring_strdup(vuid));
791 camel_folder_search_free(f, matches);
793 g_warning("Search failed: %s", camel_exception_get_description(ex));
796 g_hash_table_insert(searched, f, f);
798 node = g_list_next(node);
802 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
804 g_hash_table_destroy(searched);
805 g_ptr_array_free(folder_uids, TRUE);
811 vee_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, gchar **appended_uid, CamelException *ex)
813 camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
817 vee_transfer_messages_to (CamelFolder *folder, GPtrArray *uids, CamelFolder *dest, GPtrArray **transferred_uids, gboolean delete_originals, CamelException *ex)
819 camel_exception_set(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot copy or move messages into a Virtual Folder"));
822 static void vee_rename(CamelFolder *folder, const gchar *new)
824 /*CamelVeeFolder *vf = (CamelVeeFolder *)folder;*/
826 ((CamelFolderClass *)camel_vee_folder_parent)->rename(folder, new);
829 static void vee_delete(CamelFolder *folder)
831 struct _CamelVeeFolderPrivate *p = _PRIVATE(folder);
833 /* NB: this is never called on UNMTACHED */
835 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
837 CamelFolder *f = p->folders->data;
840 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
842 camel_vee_folder_remove_folder((CamelVeeFolder *)folder, f);
843 camel_object_unref(f);
844 CAMEL_VEE_FOLDER_LOCK(folder, subfolder_lock);
846 CAMEL_VEE_FOLDER_UNLOCK(folder, subfolder_lock);
848 ((CamelFolderClass *)camel_vee_folder_parent)->delete(folder);
849 ((CamelVeeFolder *)folder)->deleted = TRUE;
852 /* ********************************************************************** *
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.
861 * The only current example of a correlating expression is one that uses
862 * "match-threads". */
864 expression_is_correlating(const gchar *expr)
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);
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])
875 CamelVeeMessageInfo *mi = NULL;
877 mi = camel_vee_summary_add((CamelVeeSummary *)((CamelFolder *)vf)->summary, f->summary, (gchar *)inuid, hash);
881 /* same as vee_folder_add_uid, only returns whether uid was added or not */
883 vee_folder_add_uid_test (CamelVeeFolder *vf, CamelFolder *f, const gchar *inuid, const gchar hash[8])
885 CamelVeeMessageInfo *mi;
887 mi = vee_folder_add_uid (vf, f, inuid, hash);
890 camel_message_info_free ((CamelMessageInfo *) mi);
896 vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source)
898 gint i, count, n, still = FALSE, start, last;
900 CamelFolder *folder = (CamelFolder *)vf;
902 CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
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;
909 if (vf == folder_unmatched)
912 if ((source->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED))
915 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
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);
926 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
928 /* See if we just blow all uid's from this folder away from unmatched, regardless */
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);
937 if (mi->summary == ssummary) {
938 camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
941 } else if (last+1 == i) {
944 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
949 camel_message_info_free((CamelMessageInfo *)mi);
953 camel_folder_summary_remove_range(((CamelFolder *)folder_unmatched)->summary, start, last);
957 /*FIXME: This can be optimized a lot like, searching for UID in the summary uids */
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);
964 if (mi->summary == ssummary) {
965 const gchar *uid = camel_message_info_uid(mi);
967 camel_folder_change_info_remove_uid(vf->changes, uid);
971 } else if (last+1 == i) {
974 camel_folder_summary_remove_range(folder->summary, start, last);
978 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && folder_unmatched != NULL) {
980 if (g_hash_table_lookup_extended(unmatched_uids, uid, (gpointer *)&oldkey, &oldval)) {
981 n = GPOINTER_TO_INT (oldval);
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);
989 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
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);
1000 camel_message_info_free((CamelMessageInfo *)mi);
1005 camel_folder_summary_remove_range(folder->summary, start, last);
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();
1013 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1016 if (camel_folder_change_info_changed(vf->changes)) {
1017 vf_changes = vf->changes;
1018 vf->changes = camel_folder_change_info_new();
1021 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1023 if (unmatched_changes) {
1024 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1025 camel_folder_change_info_free(unmatched_changes);
1029 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1030 camel_folder_change_info_free(vf_changes);
1034 struct _update_data {
1035 CamelFolder *source;
1038 CamelVeeFolder *folder_unmatched;
1039 GHashTable *unmatched_uids;
1040 gboolean rebuilt, correlating;
1044 unmatched_check_uid(gchar *uidin, gpointer value, struct _update_data *u)
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));
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);
1057 CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)u->folder_unmatched)->summary, uid);
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);
1070 folder_added_uid(gchar *uidin, gpointer value, struct _update_data *u)
1072 CamelVeeMessageInfo *mi;
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
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);
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));
1092 g_hash_table_insert(u->unmatched_uids, g_strdup(camel_message_info_uid(mi)), GINT_TO_POINTER(1));
1096 camel_message_info_free ((CamelMessageInfo *) mi);
1100 /* build query contents for a single folder */
1102 vee_rebuild_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex)
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;
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);
1122 if (vf == folder_unmatched)
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);
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);
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();
1139 /* Load the folder results from the DB. */
1140 match = camel_vee_summary_get_ids ((CamelVeeSummary *)folder->summary, u.hash);
1143 /* We take this to mean the results have not been cached.
1144 * XXX: It will also trigger if the result set is empty. */
1146 match = camel_folder_search_by_expression(source, vf->expression, ex);
1147 if (match == NULL) /* Search failed */
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));
1158 u.folder_unmatched = folder_unmatched;
1159 u.unmatched_uids = unmatched_uids;
1160 u.rebuilt = rebuilded;
1161 u.correlating = correlating;
1163 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
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));
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));
1180 /* remove uids that can't be found in the source folder */
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);
1193 if (folder_unmatched != NULL)
1194 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1196 /* scan, looking for "old" uid's to be removed. "old" uid's
1197 are those that are from previous added sources (not in
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);
1206 if (mi->summary == ssummary) {
1207 gchar *uid = (gchar *)camel_message_info_uid(mi), *oldkey;
1210 if (g_hash_table_lookup(matchhash, uid+8) == NULL) {
1213 } else if (last+1 == i) {
1216 camel_folder_summary_remove_range(folder->summary, start, last);
1217 i -= (last-start)+1;
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);
1226 g_hash_table_remove(unmatched_uids, oldkey);
1229 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1233 g_hash_table_remove(matchhash, uid+8);
1236 camel_message_info_free((CamelMessageInfo *)mi);
1240 camel_folder_summary_remove_range(folder->summary, start, last);
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);
1247 g_hash_table_foreach(matchhash, (GHFunc)folder_added_uid, &u);
1249 if (rebuilded && !correlating)
1250 camel_db_end_transaction (folder->parent_store->cdb_w, NULL);
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);
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);
1266 g_hash_table_remove(allhash, uid+8);
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);
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();
1283 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1286 if (camel_folder_change_info_changed(vf->changes)) {
1287 vf_changes = vf->changes;
1288 vf->changes = camel_folder_change_info_new();
1291 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1293 /* Del the unwanted things from the summary, we don't hold any locks now. */
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);
1300 ((CamelVeeSummary *)folder->summary)->force_counts = TRUE;
1301 g_slist_foreach (del_list, (GFunc) camel_pstring_free, NULL);
1302 g_slist_free (del_list);
1305 g_hash_table_destroy(matchhash);
1306 g_hash_table_destroy(allhash);
1307 g_hash_table_destroy(fullhash);
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);
1315 camel_folder_search_free(source, match);
1316 camel_folder_free_summary (source, all);
1318 if (unmatched_changes) {
1319 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1320 camel_folder_change_info_free(unmatched_changes);
1324 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1325 camel_folder_change_info_free(vf_changes);
1331 /* Hold all these with summary lock and unmatched summary lock held */
1333 folder_changed_add_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
1335 CamelFolder *folder = (CamelFolder *)vf;
1336 CamelVeeMessageInfo *vinfo;
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;
1344 vinfo = vee_folder_add_uid(vf, sub, uid, hash);
1348 vuid = camel_pstring_strdup (camel_message_info_uid (vinfo));
1349 camel_message_info_free ((CamelMessageInfo *) vinfo);
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);
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));
1361 g_hash_table_insert(unmatched_uids, g_strdup(vuid), GINT_TO_POINTER (1));
1363 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
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);
1374 camel_pstring_free (vuid);
1378 folder_changed_remove_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], gint keep, CamelVeeFolder *vf, gboolean use_db)
1380 CamelFolder *folder = (CamelFolder *)vf;
1381 gchar *vuid, *oldkey;
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;
1388 vuid = alloca(strlen(uid)+9);
1389 memcpy(vuid, hash, 8);
1390 strcpy(vuid+8, uid);
1392 camel_folder_change_info_remove_uid(vf->changes, vuid);
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);
1399 camel_folder_summary_remove_uid_fast(folder->summary, vuid);
1401 if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER(sub) && folder_unmatched != NULL) {
1403 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
1404 n = GPOINTER_TO_INT (oldval);
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);
1411 g_hash_table_insert(unmatched_uids, oldkey, GINT_TO_POINTER(n-1));
1414 if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash))
1415 camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
1418 if (g_hash_table_lookup_extended(unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
1419 g_hash_table_remove(unmatched_uids, oldkey);
1423 vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
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);
1437 folder_changed_change_uid(CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
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;
1445 vuid = alloca(strlen(uid)+9);
1446 memcpy(vuid, hash, 8);
1447 strcpy(vuid+8, uid);
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);
1456 camel_folder_change_info_change_uid(vf->changes, vuid);
1457 camel_message_info_free((CamelMessageInfo *)vinfo);
1461 camel_folder_change_info_change_uid(folder_unmatched->changes, vuid);
1462 camel_message_info_free((CamelMessageInfo *)uinfo);
1465 camel_folder_free_message_info(sub, info);
1468 folder_changed_remove_uid(sub, uid, hash, FALSE, vf, use_db);
1469 camel_message_info_free((CamelMessageInfo *)vinfo);
1472 camel_message_info_free((CamelMessageInfo *)uinfo);
1477 struct _folder_changed_msg {
1478 CamelSessionThreadMsg msg;
1479 CamelFolderChangeInfo *changes;
1485 folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg)
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];
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 */
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;
1507 /* See vee_rebuild_folder. */
1508 gboolean correlating = expression_is_correlating(vf->expression);
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);
1517 camel_vee_folder_hash_folder(sub, hash);
1519 /* Lookup anything before we lock anything, to avoid deadlock with build_folder */
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);
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 */
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));
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);
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);
1554 g_ptr_array_add(always_changed, (gchar *)uid);
1555 camel_message_info_free((CamelMessageInfo *)vinfo);
1558 changed = newchanged;
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);
1569 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
1571 if (folder_unmatched != NULL)
1572 CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
1574 if (matches_changed || matches_added || changes->uid_removed->len||present)
1575 camel_db_begin_transaction (folder->parent_store->cdb_w, NULL);
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));
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);
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]);
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);
1603 memcpy(vuid, hash, 8);
1604 strcpy(vuid+8, uid);
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);
1613 camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
1618 g_hash_table_destroy(matches_hash);
1621 /* Change any newly changed */
1622 if (always_changed) {
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);
1632 GHashTable *ht_present = g_hash_table_new (g_str_hash, g_str_equal);
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]);
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);
1646 g_hash_table_destroy (ht_present);
1648 g_ptr_array_free(always_changed, TRUE);
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]);
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);
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);
1677 /* A uid we still don't have, just change it (for unmatched) */
1678 folder_changed_change_uid(sub, uid, hash, vf, !correlating);
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);
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);
1691 camel_message_info_free((CamelMessageInfo *)vinfo);
1694 g_hash_table_destroy(matches_hash);
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);
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();
1707 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
1710 if (camel_folder_change_info_changed(vf->changes)) {
1711 vf_changes = vf->changes;
1712 vf->changes = camel_folder_change_info_new();
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);
1719 /* Cleanup stuff on our folder */
1721 camel_folder_search_free(sub, matches_added);
1723 camel_folder_search_free (sub, present);
1725 if (matches_changed)
1726 camel_folder_search_free(sub, matches_changed);
1728 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
1730 /* cleanup the rest */
1732 g_ptr_array_free(newchanged, TRUE);
1736 if (unmatched_changes) {
1737 camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
1738 camel_folder_change_info_free(unmatched_changes);
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.)
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);
1760 camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
1761 camel_folder_change_info_free(vf_changes);
1766 folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg)
1768 struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
1770 camel_folder_change_info_free(m->changes);
1771 camel_object_unref((CamelObject *)m->vf);
1772 camel_object_unref((CamelObject *)m->sub);
1775 static CamelSessionThreadOps folder_changed_ops = {
1776 folder_changed_change,
1777 folder_changed_free,
1781 folder_changed_base(CamelVeeFolder *vf, CamelFolder *sub, CamelFolderChangeInfo *changes)
1783 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1784 struct _folder_changed_msg *m;
1785 CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session;
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);
1794 camel_object_ref((CamelObject *)sub);
1796 camel_object_ref((CamelObject *)vf);
1797 camel_session_thread_queue(session, &m->msg, 0);
1801 folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf)
1803 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_changed(vf, sub, changes);
1806 /* track vanishing folders */
1808 subfolder_deleted(CamelFolder *f, gpointer event_data, CamelVeeFolder *vf)
1810 camel_vee_folder_remove_folder(vf, f);
1814 subfolder_renamed_update(CamelVeeFolder *vf, CamelFolder *sub, gchar hash[8])
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;
1822 CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
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;
1832 if (mi->summary == ssummary) {
1833 gchar *uid = (gchar *)camel_message_info_uid(mi);
1837 camel_folder_change_info_remove_uid(vf->changes, uid);
1838 camel_folder_summary_remove(((CamelFolder *)vf)->summary, (CamelMessageInfo *)mi);
1840 /* works since we always append on the end */
1844 vinfo = vee_folder_add_uid(vf, sub, uid+8, hash);
1846 camel_folder_change_info_add_uid(vf->changes, camel_message_info_uid(vinfo));
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);
1856 camel_message_info_free ((CamelMessageInfo *) vinfo);
1860 camel_message_info_free((CamelMessageInfo *)mi);
1863 if (camel_folder_change_info_changed(vf->changes)) {
1864 changes = vf->changes;
1865 vf->changes = camel_folder_change_info_new();
1868 CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
1871 camel_object_trigger_event((CamelObject *)vf, "folder_changed", changes);
1872 camel_folder_change_info_free(changes);
1877 folder_renamed_base(CamelVeeFolder *vf, CamelFolder *f, const gchar *old)
1880 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
1882 /* TODO: This could probably be done in another thread, tho it is pretty quick/memory bound */
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 */
1887 camel_vee_folder_hash_folder(f, hash);
1889 subfolder_renamed_update(vf, f, hash);
1890 if (folder_unmatched != NULL)
1891 subfolder_renamed_update(folder_unmatched, f, hash);
1895 folder_renamed(CamelFolder *sub, const gchar *old, CamelVeeFolder *vf)
1897 ((CamelVeeFolderClass *)((CamelObject *)vf)->klass)->folder_renamed(vf, sub, old);
1901 vee_freeze (CamelFolder *folder)
1903 CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1904 struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1907 CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1911 CamelFolder *f = node->data;
1913 camel_folder_freeze(f);
1917 CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1919 /* call parent implementation */
1920 CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->freeze(folder);
1924 vee_thaw(CamelFolder *folder)
1926 CamelVeeFolder *vfolder = (CamelVeeFolder *)folder;
1927 struct _CamelVeeFolderPrivate *p = _PRIVATE(vfolder);
1930 CAMEL_VEE_FOLDER_LOCK(vfolder, subfolder_lock);
1934 CamelFolder *f = node->data;
1936 camel_folder_thaw(f);
1940 CAMEL_VEE_FOLDER_UNLOCK(vfolder, subfolder_lock);
1942 /* call parent implementation */
1943 CAMEL_FOLDER_CLASS (camel_vee_folder_parent)->thaw(folder);
1946 /* vfolder base implementaitons */
1948 vee_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
1950 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1952 vee_rebuild_folder (vf, sub, &ex);
1954 camel_exception_clear (&ex);
1958 vee_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
1960 gchar *shash, hash[8];
1961 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
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);
1970 if (folder_unmatched && g_hash_table_lookup (folder_unmatched->hashes, shash)) {
1971 g_hash_table_remove (folder_unmatched->hashes, shash);
1979 vee_set_expression(CamelVeeFolder *vf, const gchar *query)
1981 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
1983 CamelException ex = CAMEL_EXCEPTION_INITIALISER;
1985 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
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);
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);
2001 s->deleted_count = 0;
2002 s->unread_count = 0;
2003 s->visible_count = 0;
2004 s->junk_not_deleted_count = 0;
2007 g_free(vf->expression);
2009 vf->expression = g_strdup(query);
2013 CamelFolder *f = node->data;
2015 if (camel_vee_folder_rebuild_folder (vf, f, &ex) == -1)
2018 camel_exception_clear (&ex);
2023 camel_exception_clear (&ex);
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);
2030 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
2033 /* This entire code will be useless, since we sync the counts always. */
2035 vf_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
2037 CamelFolder *folder = (CamelFolder *)object;
2038 CamelVeeFolder *vf = (CamelVeeFolder *)folder;
2041 gint unread = -1, deleted = 0, junked = 0, visible = 0, count = -1, junked_not_deleted = -1;
2043 for (i=0;i<args->argc;i++) {
2044 CamelArgGet *arg = &args->argv[i];
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:
2057 if (vf->expression && vf->priv->unread_vfolder == -1)
2058 camel_vee_summary_load_check_unread_vfolder ((CamelVeeSummary *)folder->summary);
2060 /* This is so we can get the values atomically, and also so we can calculate them only once */
2063 CamelMessageInfoBase *info;
2064 CamelVeeMessageInfo *vinfo;
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))) {
2072 vinfo = (CamelVeeMessageInfo *) info;
2073 flags = vinfo->old_flags; /* ? vinfo->old_flags : camel_message_info_flags(info); */
2075 if ((flags & (CAMEL_MESSAGE_SEEN)) == 0)
2077 if (flags & CAMEL_MESSAGE_DELETED)
2079 if (flags & CAMEL_MESSAGE_JUNK) {
2081 if (!(flags & CAMEL_MESSAGE_DELETED))
2082 junked_not_deleted++;
2084 if ((flags & (CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)) == 0)
2086 camel_message_info_free(info);
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;
2096 count = unread == -1 ? 0 : unread;
2098 case CAMEL_FOLDER_ARG_DELETED:
2099 count = deleted == -1 ? 0 : deleted;
2101 case CAMEL_FOLDER_ARG_JUNKED:
2102 count = junked == -1 ? 0 : junked;
2104 case CAMEL_FOLDER_ARG_JUNKED_NOT_DELETED:
2105 count = junked_not_deleted == -1 ? 0 : junked_not_deleted;
2107 case CAMEL_FOLDER_ARG_VISIBLE:
2108 if (vf->priv->unread_vfolder == 1)
2109 count = unread == -1 ? 0 : unread - junked_not_deleted;
2111 count = visible == -1 ? 0 : visible;
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;
2126 arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE;
2129 return ((CamelObjectClass *)camel_vee_folder_parent)->getv(object, ex, args);
2133 camel_vee_folder_class_init (CamelVeeFolderClass *klass)
2135 CamelFolderClass *folder_class = (CamelFolderClass *) klass;
2137 camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
2139 ((CamelObjectClass *)klass)->getv = vf_getv;
2141 folder_class->refresh_info = vee_refresh_info;
2142 folder_class->sync = vee_sync;
2143 folder_class->expunge = vee_expunge;
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;
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;
2153 folder_class->rename = vee_rename;
2154 folder_class->delete = vee_delete;
2156 folder_class->freeze = vee_freeze;
2157 folder_class->thaw = vee_thaw;
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;
2168 camel_vee_folder_init (CamelVeeFolder *obj)
2170 struct _CamelVeeFolderPrivate *p;
2171 CamelFolder *folder = (CamelFolder *)obj;
2173 p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
2175 folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
2176 CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
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 |
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.*/
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;
2198 * camel_vee_folder_mask_event_folder_changed:
2203 camel_vee_folder_mask_event_folder_changed (CamelVeeFolder *vf, CamelFolder *sub)
2205 camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
2210 * camel_vee_folder_unmask_event_folder_changed:
2215 camel_vee_folder_unmask_event_folder_changed (CamelVeeFolder *vf, CamelFolder *sub)
2217 camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
2221 vee_folder_stop_folder(CamelVeeFolder *vf, CamelFolder *sub)
2223 struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
2225 CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
2227 CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
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);
2233 if (g_list_find(p->folders, sub) == NULL) {
2234 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
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);
2242 p->folders = g_list_remove(p->folders, sub);
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);
2250 CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
2252 if (folder_unmatched != NULL) {
2253 struct _CamelVeeFolderPrivate *up = _PRIVATE(folder_unmatched);
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);
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);
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);
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);
2280 CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
2283 if (CAMEL_IS_VEE_FOLDER(sub))
2286 camel_object_unref((CamelObject *)sub);
2290 * camel_vee_folder_sync_headers:
2295 camel_vee_folder_sync_headers (CamelFolder *vf, CamelException *ex)
2297 CamelFIRecord * record;
2300 /* Save the counts to DB */
2302 record = summary_header_to_db (vf->summary, ex);
2303 camel_db_write_folder_info_record (vf->parent_store->cdb_w, record, ex);
2305 dd(printf("Sync for vfolder '%s': %ld secs\n", vf->full_name, end-start));
2311 camel_vee_folder_finalise (CamelObject *obj)
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;
2317 CamelFIRecord * record;
2319 p->destroyed = TRUE;
2321 /* Save the counts to DB */
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);
2327 camel_exception_clear (&ex);
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);
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);
2341 camel_folder_thaw ((CamelFolder *)vf);
2344 g_free(vf->expression);
2346 g_list_free(p->folders);
2347 g_list_free(p->folders_changed);
2349 camel_folder_change_info_free(vf->changes);
2350 camel_object_unref((CamelObject *)vf->search);
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);