
2001-11-14 <NotZed@Ximian.com> * camel-vee-folder.c (folder_changed_remove_uid): Use the uid rather than vuid for unmatched. Also add the uid to unmatched if it wasn't in the unmatched_uids table at all. (folder_changed_change): If checking for added, if the new ones dont match, then try to add them to unmatched. Fixes #6893. * camel-folder-summary.c (camel_folder_summary_index): Change lock order, always summary_lock before ref_lock. (camel_folder_summary_array): " (camel_folder_summary_uid): " (camel_folder_summary_remove_uid): " Fixes a deadlock. svn path=/trunk/; revision=14831
1541 lines
48 KiB
C
1541 lines
48 KiB
C
/*
|
|
* Copyright (C) 2000 Ximian Inc.
|
|
*
|
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
|
* Jeffrey Stedfast <fejj@ximian.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "camel-exception.h"
|
|
#include "camel-vee-folder.h"
|
|
#include "camel-store.h"
|
|
#include "camel-folder-summary.h"
|
|
#include "camel-mime-message.h"
|
|
#include "camel-folder-search.h"
|
|
|
|
#include "camel-session.h"
|
|
#include "camel-vee-store.h" /* for open flags */
|
|
#include "camel-private.h"
|
|
|
|
#include "e-util/md5-utils.h"
|
|
|
|
#if defined (DOEPOOLV) || defined (DOESTRV)
|
|
#include "e-util/e-memory.h"
|
|
#endif
|
|
|
|
#define d(x)
|
|
extern int camel_verbose_debug;
|
|
#define dd(x) (camel_verbose_debug?(x):0)
|
|
|
|
#define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv)
|
|
|
|
static void vee_refresh_info(CamelFolder *folder, CamelException *ex);
|
|
|
|
static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
|
|
static void vee_expunge (CamelFolder *folder, CamelException *ex);
|
|
|
|
static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex);
|
|
static void vee_move_messages_to(CamelFolder *source, GPtrArray *uids, CamelFolder *dest, CamelException *ex);
|
|
|
|
static GPtrArray *vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
|
|
static GPtrArray *vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex);
|
|
|
|
static void vee_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
|
|
static void vee_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name, gboolean value);
|
|
static void vee_rename(CamelFolder *folder, const char *new);
|
|
|
|
static void camel_vee_folder_class_init (CamelVeeFolderClass *klass);
|
|
static void camel_vee_folder_init (CamelVeeFolder *obj);
|
|
static void camel_vee_folder_finalise (CamelObject *obj);
|
|
|
|
static int vee_folder_build_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex);
|
|
static void vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source, int killun);
|
|
|
|
static void message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *vf);
|
|
static void folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf);
|
|
static void subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf);
|
|
|
|
static void folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf);
|
|
|
|
static CamelFolderClass *camel_vee_folder_parent;
|
|
|
|
/* a vfolder for unmatched messages */
|
|
/* use folder_unmatched->summary_lock for access to unmatched_uids or appropriate internals, for consistency */
|
|
static CamelVeeFolder *folder_unmatched;
|
|
static GHashTable *unmatched_uids; /* a refcount of uid's that are matched by any rules */
|
|
#ifdef ENABLE_THREADS
|
|
#include <pthread.h>
|
|
static pthread_mutex_t unmatched_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
/* only used to initialise folder_unmatched */
|
|
#define UNMATCHED_LOCK() pthread_mutex_lock(&unmatched_lock)
|
|
#define UNMATCHED_UNLOCK() pthread_mutex_unlock(&unmatched_lock)
|
|
#else
|
|
#define UNMATCHED_LOCK()
|
|
#define UNMATCHED_UNLOCK()
|
|
#endif
|
|
|
|
CamelType
|
|
camel_vee_folder_get_type (void)
|
|
{
|
|
static CamelType type = CAMEL_INVALID_TYPE;
|
|
|
|
if (type == CAMEL_INVALID_TYPE) {
|
|
type = camel_type_register (camel_folder_get_type (), "CamelVeeFolder",
|
|
sizeof (CamelVeeFolder),
|
|
sizeof (CamelVeeFolderClass),
|
|
(CamelObjectClassInitFunc) camel_vee_folder_class_init,
|
|
NULL,
|
|
(CamelObjectInitFunc) camel_vee_folder_init,
|
|
(CamelObjectFinalizeFunc) camel_vee_folder_finalise);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static void
|
|
camel_vee_folder_class_init (CamelVeeFolderClass *klass)
|
|
{
|
|
CamelFolderClass *folder_class = (CamelFolderClass *) klass;
|
|
|
|
camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
|
|
|
|
folder_class->refresh_info = vee_refresh_info;
|
|
folder_class->sync = vee_sync;
|
|
folder_class->expunge = vee_expunge;
|
|
|
|
folder_class->get_message = vee_get_message;
|
|
folder_class->move_messages_to = vee_move_messages_to;
|
|
|
|
folder_class->search_by_expression = vee_search_by_expression;
|
|
folder_class->search_by_uids = vee_search_by_uids;
|
|
|
|
folder_class->set_message_flags = vee_set_message_flags;
|
|
folder_class->set_message_user_flag = vee_set_message_user_flag;
|
|
|
|
folder_class->rename = vee_rename;
|
|
}
|
|
|
|
static void
|
|
camel_vee_folder_init (CamelVeeFolder *obj)
|
|
{
|
|
struct _CamelVeeFolderPrivate *p;
|
|
CamelFolder *folder = (CamelFolder *)obj;
|
|
|
|
p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
|
|
|
|
folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
|
|
CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
|
|
|
|
/* FIXME: what to do about user flags if the subfolder doesn't support them? */
|
|
folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
|
|
CAMEL_MESSAGE_DELETED |
|
|
CAMEL_MESSAGE_DRAFT |
|
|
CAMEL_MESSAGE_FLAGGED |
|
|
CAMEL_MESSAGE_SEEN;
|
|
|
|
obj->changes = camel_folder_change_info_new();
|
|
obj->search = camel_folder_search_new();
|
|
|
|
#ifdef ENABLE_THREADS
|
|
p->summary_lock = g_mutex_new();
|
|
p->subfolder_lock = g_mutex_new();
|
|
p->changed_lock = g_mutex_new();
|
|
#endif
|
|
|
|
}
|
|
|
|
static void
|
|
camel_vee_folder_finalise (CamelObject *obj)
|
|
{
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)obj;
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GList *node;
|
|
|
|
/* FIXME: check leaks */
|
|
node = p->folders;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
|
|
if (vf != folder_unmatched) {
|
|
camel_object_unhook_event((CamelObject *)f, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
|
|
camel_object_unhook_event((CamelObject *)f, "message_changed", (CamelObjectEventHookFunc) message_changed, vf);
|
|
camel_object_unhook_event((CamelObject *)f, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
|
|
/* this updates the vfolder */
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0)
|
|
vee_folder_remove_folder(vf, f, FALSE);
|
|
}
|
|
camel_object_unref((CamelObject *)f);
|
|
|
|
node = g_list_next(node);
|
|
}
|
|
|
|
g_free(vf->expression);
|
|
g_free(vf->vname);
|
|
|
|
g_list_free(p->folders);
|
|
g_list_free(p->folders_changed);
|
|
|
|
camel_folder_change_info_free(vf->changes);
|
|
camel_object_unref((CamelObject *)vf->search);
|
|
|
|
#ifdef ENABLE_THREADS
|
|
g_mutex_free(p->summary_lock);
|
|
g_mutex_free(p->subfolder_lock);
|
|
g_mutex_free(p->changed_lock);
|
|
#endif
|
|
g_free(p);
|
|
}
|
|
|
|
static void
|
|
vee_folder_construct (CamelVeeFolder *vf, CamelStore *parent_store, const char *name, guint32 flags)
|
|
{
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
char *tmp;
|
|
|
|
vf->flags = flags;
|
|
vf->vname = g_strdup(name);
|
|
tmp = strrchr(vf->vname, '/');
|
|
if (tmp)
|
|
tmp++;
|
|
else
|
|
tmp = vf->vname;
|
|
camel_folder_construct(folder, parent_store, vf->vname, tmp);
|
|
|
|
/* should CamelVeeMessageInfo be subclassable ..? */
|
|
folder->summary = camel_folder_summary_new();
|
|
folder->summary->message_info_size = sizeof(CamelVeeMessageInfo);
|
|
}
|
|
|
|
void
|
|
camel_vee_folder_construct(CamelVeeFolder *vf, CamelStore *parent_store, const char *name, guint32 flags)
|
|
{
|
|
UNMATCHED_LOCK();
|
|
|
|
/* setup unmatched folder if we haven't yet */
|
|
if (folder_unmatched == NULL) {
|
|
unmatched_uids = g_hash_table_new (g_str_hash, g_str_equal);
|
|
folder_unmatched = (CamelVeeFolder *)camel_object_new (camel_vee_folder_get_type ());
|
|
d(printf("created foldeer unmatched %p\n", folder_unmatched));
|
|
|
|
vee_folder_construct (folder_unmatched, parent_store, CAMEL_UNMATCHED_NAME, CAMEL_STORE_FOLDER_PRIVATE);
|
|
}
|
|
|
|
UNMATCHED_UNLOCK();
|
|
|
|
vee_folder_construct (vf, parent_store, name, flags);
|
|
}
|
|
|
|
/**
|
|
* camel_vee_folder_new:
|
|
* @parent_store: the parent CamelVeeStore
|
|
* @name: the vfolder name
|
|
* @ex: a CamelException
|
|
*
|
|
* Create a new CamelVeeFolder object.
|
|
*
|
|
* Return value: A new CamelVeeFolder widget.
|
|
**/
|
|
CamelFolder *
|
|
camel_vee_folder_new(CamelStore *parent_store, const char *name, guint32 flags)
|
|
{
|
|
CamelVeeFolder *vf;
|
|
|
|
UNMATCHED_LOCK();
|
|
|
|
/* setup unmatched folder if we haven't yet */
|
|
if (folder_unmatched == NULL) {
|
|
unmatched_uids = g_hash_table_new(g_str_hash, g_str_equal);
|
|
folder_unmatched = vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
|
|
d(printf("created foldeer unmatched %p\n", folder_unmatched));
|
|
vee_folder_construct (vf, parent_store, CAMEL_UNMATCHED_NAME, CAMEL_STORE_FOLDER_PRIVATE);
|
|
}
|
|
|
|
UNMATCHED_UNLOCK();
|
|
|
|
if (strcmp(name, CAMEL_UNMATCHED_NAME) == 0) {
|
|
camel_object_ref((CamelObject *)folder_unmatched);
|
|
d(printf("returning unmatched %p, count = %d\n", folder_unmatched, camel_folder_get_message_count((CamelFolder *)folder_unmatched)));
|
|
return (CamelFolder *)folder_unmatched;
|
|
}
|
|
|
|
vf = (CamelVeeFolder *)camel_object_new(camel_vee_folder_get_type());
|
|
vee_folder_construct(vf, parent_store, name, flags);
|
|
|
|
d(printf("returning folder %s %p, count = %d\n", name, vf, camel_folder_get_message_count((CamelFolder *)vf)));
|
|
|
|
return (CamelFolder *)vf;
|
|
}
|
|
|
|
void
|
|
camel_vee_folder_set_expression(CamelVeeFolder *vf, const char *query)
|
|
{
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GList *node;
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
/* no change, do nothing */
|
|
if ((vf->expression && query && strcmp(vf->expression, query) == 0)
|
|
|| (vf->expression == NULL && query == NULL)) {
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
return;
|
|
}
|
|
|
|
g_free(vf->expression);
|
|
if (query)
|
|
vf->expression = g_strdup(query);
|
|
|
|
node = p->folders;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
|
|
if (vee_folder_build_folder(vf, f, NULL) == -1)
|
|
break;
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
g_list_free(p->folders_changed);
|
|
p->folders_changed = NULL;
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
}
|
|
|
|
/**
|
|
* camel_vee_folder_add_folder:
|
|
* @vf: Virtual Folder object
|
|
* @sub: source CamelFolder to add to @vf
|
|
*
|
|
* Adds @sub as a source folder to @vf.
|
|
**/
|
|
void
|
|
camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub)
|
|
{
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf), *up = _PRIVATE(folder_unmatched);
|
|
|
|
if (vf == (CamelVeeFolder *)sub) {
|
|
g_warning("Adding a virtual folder to itself as source, ignored");
|
|
return;
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
/* for normal vfolders we want only unique ones, for unmatched we want them all recorded */
|
|
if (g_list_find(p->folders, sub) == NULL) {
|
|
camel_object_ref((CamelObject *)sub);
|
|
p->folders = g_list_append(p->folders, sub);
|
|
}
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
camel_object_ref((CamelObject *)sub);
|
|
up->folders = g_list_append(up->folders, sub);
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
d(printf("camel_vee_folder_add_folde(%p, %p)\n", vf, sub));
|
|
|
|
camel_object_hook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc)folder_changed, vf);
|
|
camel_object_hook_event((CamelObject *)sub, "message_changed", (CamelObjectEventHookFunc)message_changed, vf);
|
|
camel_object_hook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc)subfolder_deleted, vf);
|
|
|
|
vee_folder_build_folder(vf, sub, NULL);
|
|
}
|
|
|
|
/**
|
|
* camel_vee_folder_remove_folder:
|
|
* @vf: Virtual Folder object
|
|
* @sub: source CamelFolder to remove from @vf
|
|
*
|
|
* Removed the source folder, @sub, from the virtual folder, @vf.
|
|
**/
|
|
void
|
|
camel_vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *sub)
|
|
{
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf), *up = _PRIVATE(folder_unmatched);
|
|
int killun = FALSE;
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
p->folders_changed = g_list_remove(p->folders_changed, sub);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
|
|
if (g_list_find(p->folders, sub) == NULL) {
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
return;
|
|
}
|
|
|
|
camel_object_unhook_event((CamelObject *)sub, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf);
|
|
camel_object_unhook_event((CamelObject *)sub, "message_changed", (CamelObjectEventHookFunc) message_changed, vf);
|
|
camel_object_unhook_event((CamelObject *)sub, "deleted", (CamelObjectEventHookFunc) subfolder_deleted, vf);
|
|
|
|
p->folders = g_list_remove(p->folders, sub);
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
|
|
/* if folder deleted, then blow it away from unmatched always, and remove all refs to it */
|
|
if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) {
|
|
while (g_list_find(up->folders, sub)) {
|
|
killun = TRUE;
|
|
up->folders = g_list_remove(up->folders, sub);
|
|
camel_object_unref((CamelObject *)sub);
|
|
}
|
|
} else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
if (g_list_find(up->folders, sub) != NULL) {
|
|
up->folders = g_list_remove(up->folders, sub);
|
|
camel_object_unref((CamelObject *)sub);
|
|
}
|
|
if (g_list_find(up->folders, sub) == NULL) {
|
|
killun = TRUE;
|
|
}
|
|
}
|
|
CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
|
|
|
|
vee_folder_remove_folder(vf, sub, killun);
|
|
|
|
camel_object_unref((CamelObject *)sub);
|
|
}
|
|
|
|
static void
|
|
remove_folders(CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf)
|
|
{
|
|
camel_vee_folder_remove_folder(vf, folder);
|
|
camel_object_unref((CamelObject *)folder);
|
|
}
|
|
|
|
/**
|
|
* camel_vee_folder_set_folders:
|
|
* @vf:
|
|
* @folders:
|
|
*
|
|
* Set the whole list of folder sources on a vee folder.
|
|
**/
|
|
void
|
|
camel_vee_folder_set_folders(CamelVeeFolder *vf, GList *folders)
|
|
{
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GHashTable *remove = g_hash_table_new(NULL, NULL);
|
|
GList *l;
|
|
CamelFolder *folder;
|
|
int changed;
|
|
|
|
/* setup a table of all folders we have currently */
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
l = p->folders;
|
|
while (l) {
|
|
g_hash_table_insert(remove, l->data, l->data);
|
|
camel_object_ref((CamelObject *)l->data);
|
|
l = l->next;
|
|
}
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
/* if we already have the folder, ignore it, otherwise add it */
|
|
l = folders;
|
|
while (l) {
|
|
if ((folder = g_hash_table_lookup(remove, l->data))) {
|
|
g_hash_table_remove(remove, folder);
|
|
camel_object_unref((CamelObject *)folder);
|
|
|
|
/* if this was a changed folder, re-update it while we're here */
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
changed = g_list_find(p->folders_changed, folder) != NULL;
|
|
if (changed)
|
|
p->folders_changed = g_list_remove(p->folders_changed, folder);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
if (changed)
|
|
vee_folder_build_folder(vf, folder, NULL);
|
|
} else {
|
|
camel_vee_folder_add_folder(vf, l->data);
|
|
}
|
|
l = l->next;
|
|
}
|
|
|
|
/* then remove any we still have */
|
|
g_hash_table_foreach(remove, (GHFunc)remove_folders, vf);
|
|
g_hash_table_destroy(remove);
|
|
}
|
|
|
|
/**
|
|
* camel_vee_folder_hash_folder:
|
|
* @folder:
|
|
* @:
|
|
*
|
|
* Create a hash string representing the folder name, which should be
|
|
* unique, and remain static for a given folder.
|
|
**/
|
|
void
|
|
camel_vee_folder_hash_folder(CamelFolder *folder, char buffer[8])
|
|
{
|
|
MD5Context ctx;
|
|
unsigned char digest[16];
|
|
unsigned int state = 0, save = 0;
|
|
char *tmp;
|
|
int i;
|
|
|
|
md5_init(&ctx);
|
|
tmp = camel_service_get_url((CamelService *)folder->parent_store);
|
|
md5_update(&ctx, tmp, strlen(tmp));
|
|
g_free(tmp);
|
|
md5_update(&ctx, folder->full_name, strlen(folder->full_name));
|
|
md5_final(&ctx, digest);
|
|
base64_encode_close(digest, 6, FALSE, buffer, &state, &save);
|
|
|
|
for (i=0;i<8;i++) {
|
|
if (buffer[i] == '+')
|
|
buffer[i] = '.';
|
|
if (buffer[i] == '/')
|
|
buffer[i] = '_';
|
|
}
|
|
}
|
|
|
|
static void vee_refresh_info(CamelFolder *folder, CamelException *ex)
|
|
{
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)folder;
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GList *node, *list;
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
list = p->folders_changed;
|
|
p->folders_changed = NULL;
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
|
|
node = list;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
|
|
if (vee_folder_build_folder(vf, f, ex) == -1)
|
|
break;
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
g_list_free(list);
|
|
}
|
|
|
|
static void
|
|
vee_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
|
|
{
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)folder;
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GList *node;
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
node = p->folders;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
|
|
camel_folder_sync(f, expunge, ex);
|
|
if (camel_exception_is_set(ex))
|
|
break;
|
|
|
|
if (expunge && vee_folder_build_folder(vf, f, ex) == -1)
|
|
break;
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
if (expunge && node == NULL) {
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
g_list_free(p->folders_changed);
|
|
p->folders_changed = NULL;
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
}
|
|
|
|
static void
|
|
vee_expunge (CamelFolder *folder, CamelException *ex)
|
|
{
|
|
((CamelFolderClass *)((CamelObject *)folder)->classfuncs)->sync(folder, TRUE, ex);
|
|
}
|
|
|
|
static CamelMimeMessage *
|
|
vee_get_message(CamelFolder *folder, const char *uid, CamelException *ex)
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
CamelMimeMessage *msg = NULL;
|
|
|
|
mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
|
|
if (mi) {
|
|
msg = camel_folder_get_message(mi->folder, camel_message_info_uid(mi)+8, ex);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi);
|
|
} else {
|
|
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
|
_("No such message %s in %s"), uid,
|
|
folder->name);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
static GPtrArray *
|
|
vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
|
|
{
|
|
GList *node;
|
|
GPtrArray *matches, *result = g_ptr_array_new ();
|
|
char *expr;
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)folder;
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GHashTable *searched = g_hash_table_new(NULL, NULL);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
expr = g_strdup_printf("(and %s %s)", vf->expression, expression);
|
|
node = p->folders;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
int i;
|
|
char hash[8];
|
|
|
|
/* make sure we only search each folder once - for unmatched folder to work right */
|
|
if (g_hash_table_lookup(searched, f) == NULL) {
|
|
camel_vee_folder_hash_folder(f, hash);
|
|
matches = camel_folder_search_by_expression(f, expression, ex);
|
|
for (i = 0; i < matches->len; i++) {
|
|
char *uid = matches->pdata[i], *vuid;
|
|
|
|
vuid = g_malloc(strlen(uid)+9);
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
g_ptr_array_add(result, vuid);
|
|
}
|
|
camel_folder_search_free(f, matches);
|
|
g_hash_table_insert(searched, f, f);
|
|
}
|
|
node = g_list_next(node);
|
|
}
|
|
|
|
g_free(expr);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
g_hash_table_destroy(searched);
|
|
|
|
return result;
|
|
}
|
|
|
|
static GPtrArray *
|
|
vee_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex)
|
|
{
|
|
GList *node;
|
|
GPtrArray *matches, *result = g_ptr_array_new ();
|
|
GPtrArray *folder_uids = g_ptr_array_new();
|
|
char *expr;
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)folder;
|
|
struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);
|
|
GHashTable *searched = g_hash_table_new(NULL, NULL);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
|
|
expr = g_strdup_printf("(and %s %s)", vf->expression, expression);
|
|
node = p->folders;
|
|
while (node) {
|
|
CamelFolder *f = node->data;
|
|
int i;
|
|
char hash[8];
|
|
|
|
/* make sure we only search each folder once - for unmatched folder to work right */
|
|
if (g_hash_table_lookup(searched, f) == NULL) {
|
|
camel_vee_folder_hash_folder(f, hash);
|
|
|
|
/* map the vfolder uid's to the source folder uid's first */
|
|
g_ptr_array_set_size(folder_uids, 0);
|
|
for (i=0;i<uids->len;i++) {
|
|
char *uid = uids->pdata[i];
|
|
|
|
if (strlen(uid) >= 8 && strncmp(uid, hash, 8) == 0)
|
|
g_ptr_array_add(folder_uids, uid+8);
|
|
}
|
|
if (folder_uids->len > 0) {
|
|
matches = camel_folder_search_by_uids(f, expression, folder_uids, ex);
|
|
if (matches) {
|
|
for (i = 0; i < matches->len; i++) {
|
|
char *uid = matches->pdata[i], *vuid;
|
|
|
|
vuid = g_malloc(strlen(uid)+9);
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
g_ptr_array_add(result, vuid);
|
|
}
|
|
camel_folder_search_free(f, matches);
|
|
} else {
|
|
g_warning("Search failed: %s", camel_exception_get_description(ex));
|
|
}
|
|
}
|
|
g_hash_table_insert(searched, f, f);
|
|
}
|
|
node = g_list_next(node);
|
|
}
|
|
|
|
g_free(expr);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
g_hash_table_destroy(searched);
|
|
g_ptr_array_free(folder_uids, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
vee_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
|
|
mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
|
|
if (mi) {
|
|
camel_folder_set_message_flags(mi->folder, camel_message_info_uid(mi) + 8, flags, set);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi);
|
|
((CamelFolderClass *)camel_vee_folder_parent)->set_message_flags(folder, uid, flags, set);
|
|
}
|
|
}
|
|
|
|
static void
|
|
vee_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
|
|
mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid);
|
|
if (mi) {
|
|
camel_folder_set_message_user_flag(mi->folder, camel_message_info_uid(mi) + 8, name, value);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi);
|
|
((CamelFolderClass *)camel_vee_folder_parent)->set_message_user_flag(folder, uid, name, value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
vee_move_messages_to (CamelFolder *folder, GPtrArray *uids, CamelFolder *dest, CamelException *ex)
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
int i;
|
|
|
|
for (i = 0; i < uids->len && !camel_exception_is_set (ex); i++) {
|
|
mi = (CamelVeeMessageInfo *) camel_folder_summary_uid (folder->summary, uids->pdata[i]);
|
|
if (mi) {
|
|
/* noop if it we're moving from the same vfolder (uh, which should't happen but who knows) */
|
|
if (folder != mi->folder) {
|
|
GPtrArray *uids;
|
|
|
|
uids = g_ptr_array_new ();
|
|
g_ptr_array_add (uids, (char *) (camel_message_info_uid (mi) + 8));
|
|
camel_folder_move_messages_to (mi->folder, uids, dest, ex);
|
|
g_ptr_array_free (uids, TRUE);
|
|
}
|
|
camel_folder_summary_info_free (folder->summary, (CamelMessageInfo *)mi);
|
|
} else {
|
|
camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
|
_("No such message: %s"), uids->pdata[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void vee_rename(CamelFolder *folder, const char *new)
|
|
{
|
|
CamelVeeFolder *vf = (CamelVeeFolder *)folder;
|
|
|
|
g_free(vf->vname);
|
|
vf->vname = g_strdup(new);
|
|
|
|
((CamelFolderClass *)camel_vee_folder_parent)->rename(folder, new);
|
|
}
|
|
|
|
/* ********************************************************************** *
|
|
utility functions */
|
|
|
|
/* must be called with summary_lock held */
|
|
static CamelVeeMessageInfo *
|
|
vee_folder_add_info(CamelVeeFolder *vf, CamelFolder *f, CamelMessageInfo *info, const char hash[8])
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
char *vuid;
|
|
const char *uid;
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
CamelMessageInfo *dinfo;
|
|
|
|
uid = camel_message_info_uid(info);
|
|
vuid = alloca(strlen(uid)+9);
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
dinfo = camel_folder_summary_uid(folder->summary, vuid);
|
|
if (dinfo) {
|
|
d(printf("w:clash, we already have '%s' in summary\n", vuid));
|
|
camel_folder_summary_info_free(folder->summary, dinfo);
|
|
return NULL;
|
|
}
|
|
|
|
d(printf("adding vuid %s to %s\n", vuid, vf->vname));
|
|
|
|
mi = (CamelVeeMessageInfo *)camel_folder_summary_info_new(folder->summary);
|
|
camel_message_info_dup_to(info, (CamelMessageInfo *)mi);
|
|
#ifdef DOEPOOLV
|
|
mi->info.strings = e_poolv_set(mi->info.strings, CAMEL_MESSAGE_INFO_UID, vuid, FALSE);
|
|
#elif defined (DOESTRV)
|
|
mi->info.strings = e_strv_set_ref(mi->info.strings, CAMEL_MESSAGE_INFO_UID, vuid);
|
|
mi->info.strings = e_strv_pack(mi->info.strings);
|
|
#else
|
|
g_free(mi->info.uid);
|
|
mi->info.uid = g_strdup(vuid);
|
|
#endif
|
|
mi->folder = f;
|
|
camel_folder_summary_add(folder->summary, (CamelMessageInfo *)mi);
|
|
|
|
return mi;
|
|
}
|
|
|
|
/* must be called with summary_lock held */
|
|
static CamelVeeMessageInfo *
|
|
vee_folder_add_uid(CamelVeeFolder *vf, CamelFolder *f, const char *inuid, const char hash[8])
|
|
{
|
|
CamelMessageInfo *info;
|
|
CamelVeeMessageInfo *mi = NULL;
|
|
|
|
info = camel_folder_get_message_info(f, inuid);
|
|
if (info) {
|
|
mi = vee_folder_add_info(vf, f, info, hash);
|
|
camel_folder_free_message_info(f, info);
|
|
}
|
|
return mi;
|
|
}
|
|
|
|
static void
|
|
vee_folder_remove_folder(CamelVeeFolder *vf, CamelFolder *source, int killun)
|
|
{
|
|
int i, count, n, still;
|
|
char *oldkey;
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
char hash[8];
|
|
/*struct _CamelVeeFolderPrivate *p = _PRIVATE(vf);*/
|
|
CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
|
|
|
|
if (vf == folder_unmatched)
|
|
return;
|
|
|
|
/* check if this folder is still to be part of unmatched */
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !killun) {
|
|
CAMEL_VEE_FOLDER_LOCK(folder_unmatched, subfolder_lock);
|
|
still = g_list_find(_PRIVATE(folder_unmatched)->folders, source) != NULL;
|
|
CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, subfolder_lock);
|
|
camel_vee_folder_hash_folder(source, hash);
|
|
} else {
|
|
still = FALSE;
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
|
|
CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
|
|
|
|
/* See if we just blow all uid's from this folder away from unmatched, regardless */
|
|
if (killun) {
|
|
count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
|
|
for (i=0;i<count;i++) {
|
|
CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
|
|
|
|
if (mi) {
|
|
if (mi->folder == source) {
|
|
camel_folder_summary_remove_index(((CamelFolder *)folder_unmatched)->summary, i);
|
|
camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
|
|
i--;
|
|
}
|
|
camel_folder_summary_info_free(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)mi);
|
|
}
|
|
}
|
|
}
|
|
|
|
count = camel_folder_summary_count(folder->summary);
|
|
for (i=0;i<count;i++) {
|
|
CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
|
|
if (mi) {
|
|
if (mi->folder == source) {
|
|
const char *uid = camel_message_info_uid(mi);
|
|
|
|
camel_folder_change_info_remove_uid(vf->changes, uid);
|
|
camel_folder_summary_remove_index(folder->summary, i);
|
|
i--;
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
if (still) {
|
|
if (g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, (void **)&n)) {
|
|
if (n == 1) {
|
|
g_hash_table_remove(unmatched_uids, oldkey);
|
|
if (vee_folder_add_uid(folder_unmatched, source, oldkey+8, hash))
|
|
camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
|
|
g_free(oldkey);
|
|
} else {
|
|
g_hash_table_insert(unmatched_uids, oldkey, (void *)(n-1));
|
|
}
|
|
}
|
|
} else {
|
|
if (g_hash_table_lookup_extended(unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, (void **)&n)) {
|
|
g_hash_table_remove(unmatched_uids, oldkey);
|
|
g_free(oldkey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi);
|
|
}
|
|
}
|
|
|
|
if (camel_folder_change_info_changed(folder_unmatched->changes)) {
|
|
unmatched_changes = folder_unmatched->changes;
|
|
folder_unmatched->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
if (camel_folder_change_info_changed(vf->changes)) {
|
|
vf_changes = vf->changes;
|
|
vf->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
|
|
|
|
if (unmatched_changes) {
|
|
camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
|
|
camel_folder_change_info_free(unmatched_changes);
|
|
}
|
|
|
|
if (vf_changes) {
|
|
camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
|
|
camel_folder_change_info_free(vf_changes);
|
|
}
|
|
}
|
|
|
|
struct _update_data {
|
|
CamelFolder *source;
|
|
CamelVeeFolder *vf;
|
|
char hash[8];
|
|
};
|
|
|
|
static void
|
|
unmatched_check_uid(char *uidin, void *value, struct _update_data *u)
|
|
{
|
|
char *uid;
|
|
int n;
|
|
|
|
uid = alloca(strlen(uidin)+9);
|
|
memcpy(uid, u->hash, 8);
|
|
strcpy(uid+8, uidin);
|
|
n = (int)g_hash_table_lookup(unmatched_uids, uid);
|
|
if (n == 0) {
|
|
if (vee_folder_add_uid(folder_unmatched, u->source, uidin, u->hash))
|
|
camel_folder_change_info_add_uid(folder_unmatched->changes, uid);
|
|
} else {
|
|
CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)folder_unmatched)->summary, uid);
|
|
if (mi) {
|
|
camel_folder_summary_remove(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)mi);
|
|
camel_folder_change_info_remove_uid(folder_unmatched->changes, uid);
|
|
camel_folder_summary_info_free(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)mi);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
folder_added_uid(char *uidin, void *value, struct _update_data *u)
|
|
{
|
|
CamelVeeMessageInfo *mi;
|
|
char *oldkey;
|
|
int n;
|
|
|
|
if ( (mi = vee_folder_add_uid(u->vf, u->source, uidin, u->hash)) ) {
|
|
camel_folder_change_info_add_uid(u->vf->changes, camel_message_info_uid(mi));
|
|
|
|
if (g_hash_table_lookup_extended(unmatched_uids, camel_message_info_uid(mi), (void **)&oldkey, (void **)&n)) {
|
|
g_hash_table_insert(unmatched_uids, oldkey, (void *)(n+1));
|
|
} else {
|
|
g_hash_table_insert(unmatched_uids, g_strdup(camel_message_info_uid(mi)), (void *)1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* build query contents for a single folder */
|
|
static int
|
|
vee_folder_build_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex)
|
|
{
|
|
GPtrArray *match, *all;
|
|
GHashTable *allhash, *matchhash;
|
|
CamelFolder *f = source;
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
int i, n, count;
|
|
struct _update_data u;
|
|
CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
|
|
|
|
if (vf == folder_unmatched)
|
|
return 0;
|
|
|
|
/* if we have no expression, or its been cleared, then act as if no matches */
|
|
if (vf->expression == NULL) {
|
|
match = g_ptr_array_new();
|
|
} else {
|
|
match = camel_folder_search_by_expression(f, vf->expression, ex);
|
|
if (match == NULL)
|
|
return -1;
|
|
}
|
|
|
|
u.source = source;
|
|
u.vf = vf;
|
|
camel_vee_folder_hash_folder(source, u.hash);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
|
|
|
|
/* we build 2 hash tables, one for all uid's not matched, the other for all matched uid's,
|
|
we just ref the real memory */
|
|
matchhash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
for (i=0;i<match->len;i++)
|
|
g_hash_table_insert(matchhash, match->pdata[i], (void *)1);
|
|
|
|
allhash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
all = camel_folder_get_uids(f);
|
|
for (i=0;i<all->len;i++)
|
|
if (g_hash_table_lookup(matchhash, all->pdata[i]) == NULL)
|
|
g_hash_table_insert(allhash, all->pdata[i], (void *)1);
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
|
|
|
|
/* scan, looking for "old" uid's to be removed */
|
|
count = camel_folder_summary_count(folder->summary);
|
|
for (i=0;i<count;i++) {
|
|
CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i);
|
|
|
|
if (mi) {
|
|
if (mi->folder == source) {
|
|
char *uid = (char *)camel_message_info_uid(mi), *oldkey;
|
|
|
|
if (g_hash_table_lookup(matchhash, uid+8) == NULL) {
|
|
camel_folder_summary_remove_index(folder->summary, i);
|
|
camel_folder_change_info_remove_uid(vf->changes, camel_message_info_uid(mi));
|
|
i--;
|
|
if (g_hash_table_lookup_extended(unmatched_uids, uid, (void **)&oldkey, (void **)&n)) {
|
|
if (n == 1) {
|
|
g_hash_table_remove(unmatched_uids, oldkey);
|
|
g_free(oldkey);
|
|
} else {
|
|
g_hash_table_insert(unmatched_uids, oldkey, (void *)(n-1));
|
|
}
|
|
}
|
|
} else {
|
|
g_hash_table_remove(matchhash, uid+8);
|
|
}
|
|
}
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi);
|
|
}
|
|
}
|
|
|
|
/* now matchhash contains any new uid's, add them, etc */
|
|
g_hash_table_foreach(matchhash, (GHFunc)folder_added_uid, &u);
|
|
|
|
/* scan unmatched, remove any that have vanished, etc */
|
|
count = camel_folder_summary_count(((CamelFolder *)folder_unmatched)->summary);
|
|
for (i=0;i<count;i++) {
|
|
CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(((CamelFolder *)folder_unmatched)->summary, i);
|
|
|
|
if (mi) {
|
|
if (mi->folder == source) {
|
|
char *uid = (char *)camel_message_info_uid(mi);
|
|
|
|
if (g_hash_table_lookup(allhash, uid+8) == NULL) {
|
|
/* no longer exists at all, just remove it entirely */
|
|
camel_folder_summary_remove_index(((CamelFolder *)folder_unmatched)->summary, i);
|
|
camel_folder_change_info_remove_uid(folder_unmatched->changes, camel_message_info_uid(mi));
|
|
i--;
|
|
} else {
|
|
g_hash_table_remove(allhash, uid+8);
|
|
}
|
|
}
|
|
camel_folder_summary_info_free(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)mi);
|
|
}
|
|
}
|
|
|
|
/* now allhash contains all potentially new uid's for the unmatched folder, process */
|
|
g_hash_table_foreach(allhash, (GHFunc)unmatched_check_uid, &u);
|
|
|
|
/* copy any changes so we can raise them outside the lock */
|
|
if (camel_folder_change_info_changed(folder_unmatched->changes)) {
|
|
unmatched_changes = folder_unmatched->changes;
|
|
folder_unmatched->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
if (camel_folder_change_info_changed(vf->changes)) {
|
|
vf_changes = vf->changes;
|
|
vf->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
|
|
|
|
g_hash_table_destroy(matchhash);
|
|
g_hash_table_destroy(allhash);
|
|
/* if expression not set, we only had a null list */
|
|
if (vf->expression == NULL)
|
|
g_ptr_array_free(match, TRUE);
|
|
else
|
|
camel_folder_search_free(f, match);
|
|
camel_folder_free_uids(f, all);
|
|
|
|
if (unmatched_changes) {
|
|
camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
|
|
camel_folder_change_info_free(unmatched_changes);
|
|
}
|
|
|
|
if (vf_changes) {
|
|
camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
|
|
camel_folder_change_info_free(vf_changes);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
|
|
(match-folder "folder1" "folder2")
|
|
|
|
*/
|
|
|
|
|
|
/* Hold all these with summary lock and unmatched summary lock held */
|
|
static void
|
|
folder_changed_add_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
|
|
{
|
|
CamelVeeMessageInfo *vinfo;
|
|
const char *vuid;
|
|
char *oldkey;
|
|
int n;
|
|
|
|
vinfo = vee_folder_add_uid(vf, sub, uid, hash);
|
|
if (vinfo == NULL)
|
|
return;
|
|
|
|
vuid = camel_message_info_uid(vinfo);
|
|
camel_folder_change_info_add_uid(vf->changes, vuid);
|
|
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) {
|
|
g_hash_table_insert(unmatched_uids, oldkey, (void *)(n+1));
|
|
} else {
|
|
g_hash_table_insert(unmatched_uids, g_strdup(vuid), (void *)1);
|
|
}
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
|
|
if (vinfo) {
|
|
camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
|
|
camel_folder_summary_remove(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)vinfo);
|
|
camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
folder_changed_remove_uid(CamelFolder *sub, const char *uid, const char hash[8], int keep, CamelVeeFolder *vf)
|
|
{
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
char *vuid, *oldkey;
|
|
int n;
|
|
CamelVeeMessageInfo *vinfo;
|
|
|
|
vuid = alloca(strlen(uid)+9);
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
|
|
if (vinfo) {
|
|
camel_folder_change_info_remove_uid(vf->changes, vuid);
|
|
camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)vinfo);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo);
|
|
}
|
|
|
|
if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
if (keep) {
|
|
if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) {
|
|
if (n == 1) {
|
|
g_hash_table_remove(unmatched_uids, oldkey);
|
|
if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
|
|
camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
|
|
g_free(oldkey);
|
|
} else {
|
|
g_hash_table_insert(unmatched_uids, oldkey, (void *)(n-1));
|
|
}
|
|
} else {
|
|
if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
|
|
camel_folder_change_info_add_uid(folder_unmatched->changes, oldkey);
|
|
}
|
|
} else {
|
|
if (g_hash_table_lookup_extended(unmatched_uids, vuid, (void **)&oldkey, (void **)&n)) {
|
|
g_hash_table_remove(unmatched_uids, oldkey);
|
|
g_free(oldkey);
|
|
}
|
|
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
|
|
if (vinfo) {
|
|
camel_folder_change_info_remove_uid(folder_unmatched->changes, vuid);
|
|
camel_folder_summary_remove_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
|
|
camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
folder_changed_change_uid(CamelFolder *sub, const char *uid, const char hash[8], CamelVeeFolder *vf)
|
|
{
|
|
char *vuid;
|
|
CamelVeeMessageInfo *vinfo, *uinfo;
|
|
CamelMessageInfo *info;
|
|
CamelFolder *folder = (CamelFolder *)vf;
|
|
|
|
vuid = alloca(strlen(uid)+9);
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
|
|
uinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(((CamelFolder *)folder_unmatched)->summary, vuid);
|
|
if (vinfo || uinfo) {
|
|
info = camel_folder_get_message_info(sub, uid);
|
|
if (info) {
|
|
if (vinfo) {
|
|
int changed = FALSE;
|
|
|
|
if (vinfo->info.flags != info->flags){
|
|
vinfo->info.flags = info->flags;
|
|
changed = TRUE;
|
|
}
|
|
|
|
changed |= camel_flag_list_copy(&vinfo->info.user_flags, &info->user_flags);
|
|
changed |= camel_tag_list_copy(&vinfo->info.user_tags, &info->user_tags);
|
|
if (changed)
|
|
camel_folder_change_info_change_uid(vf->changes, vuid);
|
|
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo);
|
|
}
|
|
|
|
if (uinfo) {
|
|
int changed = FALSE;
|
|
|
|
if (uinfo->info.flags != info->flags){
|
|
uinfo->info.flags = info->flags;
|
|
changed = TRUE;
|
|
}
|
|
|
|
changed |= camel_flag_list_copy(&uinfo->info.user_flags, &info->user_flags);
|
|
changed |= camel_tag_list_copy(&uinfo->info.user_tags, &info->user_tags);
|
|
if (changed)
|
|
camel_folder_change_info_change_uid(folder_unmatched->changes, vuid);
|
|
|
|
camel_folder_summary_info_free(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)uinfo);
|
|
}
|
|
|
|
camel_folder_free_message_info(sub, info);
|
|
} else {
|
|
if (vinfo) {
|
|
folder_changed_remove_uid(sub, uid, hash, FALSE, vf);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo);
|
|
}
|
|
if (uinfo)
|
|
camel_folder_summary_info_free(((CamelFolder *)folder_unmatched)->summary, (CamelMessageInfo *)uinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct _folder_changed_msg {
|
|
#ifdef ENABLE_THREADS
|
|
CamelSessionThreadMsg msg;
|
|
#endif
|
|
CamelFolderChangeInfo *changes;
|
|
CamelFolder *sub;
|
|
CamelVeeFolder *vf;
|
|
};
|
|
|
|
static void
|
|
folder_changed_change(CamelSession *session, CamelSessionThreadMsg *msg)
|
|
{
|
|
struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
|
|
CamelFolder *sub = m->sub;
|
|
CamelFolder *folder = (CamelFolder *)m->vf;
|
|
CamelVeeFolder *vf = m->vf;
|
|
CamelFolderChangeInfo *changes = m->changes;
|
|
char *vuid = NULL, hash[8];
|
|
const char *uid;
|
|
CamelVeeMessageInfo *vinfo;
|
|
int i, vuidlen = 0;
|
|
CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL;
|
|
GPtrArray *matches_added = NULL, /* newly added, that match */
|
|
*matches_changed = NULL, /* newly changed, that now match */
|
|
*newchanged = NULL,
|
|
*changed;
|
|
GPtrArray *always_changed = NULL;
|
|
GHashTable *matches_hash;
|
|
|
|
/* Check the folder hasn't beem removed while we weren't watching */
|
|
CAMEL_VEE_FOLDER_LOCK(vf, subfolder_lock);
|
|
if (g_list_find(_PRIVATE(vf)->folders, sub) == NULL) {
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
return;
|
|
}
|
|
|
|
camel_vee_folder_hash_folder(sub, hash);
|
|
|
|
/* Lookup anything before we lock anything, to avoid deadlock with build_folder */
|
|
|
|
/* Find newly added that match */
|
|
if (changes->uid_added->len > 0) {
|
|
dd(printf(" Searching for added matches '%s'\n", vf->expression));
|
|
matches_added = camel_folder_search_by_uids(sub, vf->expression, changes->uid_added, NULL);
|
|
}
|
|
|
|
/* TODO:
|
|
In this code around here, we can work out if the search will affect the changes
|
|
we had, and only re-search against them if they might have */
|
|
|
|
/* Search for changed items that newly match, but only if we dont have them */
|
|
changed = changes->uid_changed;
|
|
if (changed->len > 0) {
|
|
dd(printf(" Searching for changed matches '%s'\n", vf->expression));
|
|
|
|
if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
|
|
newchanged = g_ptr_array_new();
|
|
always_changed = g_ptr_array_new();
|
|
for (i=0;i<changed->len;i++) {
|
|
uid = changed->pdata[i];
|
|
if (strlen(uid)+9 > vuidlen) {
|
|
vuidlen = strlen(uid)+64;
|
|
vuid = g_realloc(vuid, vuidlen);
|
|
}
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
|
|
if (vinfo == NULL) {
|
|
g_ptr_array_add(newchanged, (char *)uid);
|
|
} else {
|
|
g_ptr_array_add(always_changed, (char *)uid);
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo);
|
|
}
|
|
}
|
|
changed = newchanged;
|
|
}
|
|
|
|
if (changed->len)
|
|
matches_changed = camel_folder_search_by_uids(sub, vf->expression, changed, NULL);
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_LOCK(vf, summary_lock);
|
|
CAMEL_VEE_FOLDER_LOCK(folder_unmatched, summary_lock);
|
|
|
|
dd(printf("Vfolder '%s' subfolder changed '%s'\n", folder->full_name, sub->full_name));
|
|
dd(printf(" changed %d added %d removed %d\n", changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len));
|
|
|
|
/* Always remove removed uid's, in any case */
|
|
for (i=0;i<changes->uid_removed->len;i++) {
|
|
dd(printf(" removing uid '%s'\n", (char *)changes->uid_removed->pdata[i]));
|
|
folder_changed_remove_uid(sub, changes->uid_removed->pdata[i], hash, FALSE, vf);
|
|
}
|
|
|
|
/* Add any newly matched or to unmatched folder if they dont */
|
|
if (matches_added) {
|
|
matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
for (i=0;i<matches_added->len;i++) {
|
|
dd(printf(" %s", (char *)matches_added->pdata[i]));
|
|
g_hash_table_insert(matches_hash, matches_added->pdata[i], matches_added->pdata[i]);
|
|
}
|
|
for (i=0;i<changes->uid_added->len;i++) {
|
|
uid = changes->uid_added->pdata[i];
|
|
if (g_hash_table_lookup(matches_hash, uid)) {
|
|
dd(printf(" adding uid '%s' [newly matched]\n", (char *)uid));
|
|
folder_changed_add_uid(sub, uid, hash, vf);
|
|
} else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
|
|
if (strlen(uid)+9 > vuidlen) {
|
|
vuidlen = strlen(uid)+64;
|
|
vuid = g_realloc(vuid, vuidlen);
|
|
}
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
|
|
if (g_hash_table_lookup(unmatched_uids, vuid) == NULL) {
|
|
dd(printf(" adding uid '%s' to Unmatched [newly unmatched]\n", (char *)uid));
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info((CamelFolder *)folder_unmatched, vuid);
|
|
if (vinfo == NULL) {
|
|
if (vee_folder_add_uid(folder_unmatched, sub, uid, hash))
|
|
camel_folder_change_info_add_uid(folder_unmatched->changes, vuid);
|
|
} else {
|
|
camel_folder_free_message_info((CamelFolder *)folder_unmatched, (CamelMessageInfo *)vinfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g_hash_table_destroy(matches_hash);
|
|
}
|
|
|
|
/* Change any newly changed */
|
|
if (always_changed) {
|
|
for (i=0;i<always_changed->len;i++)
|
|
folder_changed_change_uid(sub, always_changed->pdata[i], hash, vf);
|
|
g_ptr_array_free(always_changed, TRUE);
|
|
}
|
|
|
|
/* Change/add/remove any changed */
|
|
if (matches_changed) {
|
|
/* If we are auto-updating, then re-check changed uids still match */
|
|
dd(printf(" Vfolder %supdate\nuids match:", (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO)?"auto-":""));
|
|
matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
for (i=0;i<matches_changed->len;i++) {
|
|
dd(printf(" %s", (char *)matches_changed->pdata[i]));
|
|
g_hash_table_insert(matches_hash, matches_changed->pdata[i], matches_changed->pdata[i]);
|
|
}
|
|
dd(printf("\n"));
|
|
for (i=0;i<changed->len;i++) {
|
|
uid = changed->pdata[i];
|
|
if (strlen(uid)+9 > vuidlen) {
|
|
vuidlen = strlen(uid)+64;
|
|
vuid = g_realloc(vuid, vuidlen);
|
|
}
|
|
memcpy(vuid, hash, 8);
|
|
strcpy(vuid+8, uid);
|
|
vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid);
|
|
if (vinfo == NULL) {
|
|
/* A uid we dont have, but now it matches, add it */
|
|
if (g_hash_table_lookup(matches_hash, uid)) {
|
|
dd(printf(" adding uid '%s' [newly matched]\n", uid));
|
|
folder_changed_add_uid(sub, uid, hash, vf);
|
|
}
|
|
} else {
|
|
if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
|
|
|| g_hash_table_lookup(matches_hash, uid)) {
|
|
/* still match, or we're not auto-updating, change event, (if it changed) */
|
|
dd(printf(" changing uid '%s' [still matches]\n", uid));
|
|
folder_changed_change_uid(sub, uid, hash, vf);
|
|
} else {
|
|
/* No longer matches, remove it, but keep it in unmatched (potentially) */
|
|
dd(printf(" removing uid '%s' [did match]\n", uid));
|
|
folder_changed_remove_uid(sub, uid, hash, TRUE, vf);
|
|
}
|
|
camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo);
|
|
}
|
|
}
|
|
g_hash_table_destroy(matches_hash);
|
|
}
|
|
|
|
if (camel_folder_change_info_changed(folder_unmatched->changes)) {
|
|
unmatched_changes = folder_unmatched->changes;
|
|
folder_unmatched->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
if (camel_folder_change_info_changed(vf->changes)) {
|
|
vf_changes = vf->changes;
|
|
vf->changes = camel_folder_change_info_new();
|
|
}
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(folder_unmatched, summary_lock);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, summary_lock);
|
|
|
|
/* Cleanup stuff on our folder */
|
|
if (matches_added)
|
|
camel_folder_search_free(sub, matches_added);
|
|
|
|
if (matches_changed)
|
|
camel_folder_search_free(sub, matches_changed);
|
|
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, subfolder_lock);
|
|
|
|
/* cleanup the rest */
|
|
if (newchanged)
|
|
g_ptr_array_free(newchanged, TRUE);
|
|
|
|
g_free(vuid);
|
|
|
|
if (unmatched_changes) {
|
|
camel_object_trigger_event((CamelObject *)folder_unmatched, "folder_changed", unmatched_changes);
|
|
camel_folder_change_info_free(unmatched_changes);
|
|
}
|
|
|
|
if (vf_changes) {
|
|
/* If not auto-updating, keep track of changed folders for later re-sync */
|
|
if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) {
|
|
CAMEL_VEE_FOLDER_LOCK(vf, changed_lock);
|
|
if (g_list_find(vf->priv->folders_changed, sub) != NULL)
|
|
vf->priv->folders_changed = g_list_prepend(vf->priv->folders_changed, sub);
|
|
CAMEL_VEE_FOLDER_UNLOCK(vf, changed_lock);
|
|
}
|
|
|
|
camel_object_trigger_event((CamelObject *)vf, "folder_changed", vf_changes);
|
|
camel_folder_change_info_free(vf_changes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
folder_changed_free(CamelSession *session, CamelSessionThreadMsg *msg)
|
|
{
|
|
struct _folder_changed_msg *m = (struct _folder_changed_msg *)msg;
|
|
|
|
camel_folder_change_info_free(m->changes);
|
|
camel_object_unref((CamelObject *)m->vf);
|
|
camel_object_unref((CamelObject *)m->sub);
|
|
}
|
|
|
|
#ifdef ENABLE_THREADS
|
|
static CamelSessionThreadOps folder_changed_ops = {
|
|
folder_changed_change,
|
|
folder_changed_free,
|
|
};
|
|
#endif
|
|
|
|
static void
|
|
folder_changed(CamelFolder *sub, CamelFolderChangeInfo *changes, CamelVeeFolder *vf)
|
|
{
|
|
struct _folder_changed_msg *m;
|
|
CamelSession *session = ((CamelService *)((CamelFolder *)vf)->parent_store)->session;
|
|
|
|
#ifdef ENABLE_THREADS
|
|
m = camel_session_thread_msg_new(session, &folder_changed_ops, sizeof(*m));
|
|
m->changes = camel_folder_change_info_new();
|
|
camel_folder_change_info_cat(m->changes, changes);
|
|
m->sub = sub;
|
|
camel_object_ref((CamelObject *)sub);
|
|
m->vf = vf;
|
|
camel_object_ref((CamelObject *)vf);
|
|
camel_session_thread_queue(session, &m->msg, 0);
|
|
#else
|
|
m = g_malloc(sizeof(*m));
|
|
m->changes = changes;
|
|
m->sub = sub;
|
|
m->vf = vf;
|
|
folder_changed_change(session, &m->msg);
|
|
folder_changed_free(&m->msg);
|
|
g_free(m);
|
|
#endif
|
|
}
|
|
|
|
/* track flag changes in the summary, we just promote it to a folder_changed event */
|
|
static void
|
|
message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *vf)
|
|
{
|
|
CamelFolderChangeInfo *changes;
|
|
|
|
changes = camel_folder_change_info_new();
|
|
camel_folder_change_info_change_uid(changes, uid);
|
|
folder_changed(f, changes, vf);
|
|
camel_folder_change_info_free(changes);
|
|
}
|
|
|
|
/* track vanishing folders */
|
|
static void
|
|
subfolder_deleted(CamelFolder *f, void *event_data, CamelVeeFolder *vf)
|
|
{
|
|
camel_vee_folder_remove_folder(vf, f);
|
|
}
|