Files
evolution/mail/mail-folder-cache.c
9 3e6cd9a7e5 Free folders_uri. (real_folder_deleted): If folder is deleted, remove it
2001-10-19    <NotZed@Ximian.com>

	* mail-folder-cache.c (store_finalised): Free folders_uri.
	(real_folder_deleted): If folder is deleted, remove it from the
	hashtables.

	* subscribe-dialog.c (get_short_folderinfo_get): Remove the
	register/unregister, they're already done above us.

	* mail-vfolder.c
	(mail_vfolder_delete_uri): Dont do any work to remove the actual
	folder from the vfolder (we'd have to look it up first), let the
	vfolder remove it itself.  Just update the rules.

svn path=/trunk/; revision=13787
2001-10-19 05:40:42 +00:00

556 lines
16 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Authors: Peter Williams <peterw@ximian.com>
* Michael Zucchi <notzed@ximian.com>
*
* Copyright 2000,2001 Ximian, Inc. (www.ximian.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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
#ifdef G_LOG_DOMAIN
#undef G_LOG_DOMAIN
#endif
#define G_LOG_DOMAIN "folder tree"
#include <pthread.h>
#include <bonobo/bonobo-exception.h>
#include <camel/camel-store.h>
#include <camel/camel-folder.h>
#include <camel/camel-vtrash-folder.h>
#include "mail-mt.h"
#include "mail-folder-cache.h"
#include "mail-ops.h"
#include "mail-vfolder.h"
#define d(x)
/* note that many things are effectively serialised by having them run in
the main loop thread which they need to do because of corba/gtk calls */
#define LOCK(x) pthread_mutex_lock(&x)
#define UNLOCK(x) pthread_mutex_unlock(&x)
static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER;
struct _folder_info {
struct _store_info *store_info; /* 'parent' link */
char *path; /* shell path */
char *full_name; /* full name of folder/folderinfo */
char *uri; /* uri of folder */
CamelFolder *folder; /* if known */
};
struct _store_info {
GHashTable *folders; /* by full_name */
GHashTable *folders_uri; /* by uri */
CamelStore *store; /* the store for these folders */
/* only 1 should be set */
EvolutionStorage *storage;
GNOME_Evolution_Storage corba_storage;
MailAsyncEvent *async_event;
};
static GHashTable *stores;
static void free_folder_info(char *path, struct _folder_info *mfi, void *data);
static void unset_folder_info(struct _folder_info *mfi, int delete);
/* This is how unread counts work (and don't work):
*
* camel_folder_unread_message_count() only gives a correct answer if
* the store is paying attention to the folder. (Some stores always
* pay attention to all folders, but IMAP can only pay attention to
* one folder at a time.) But it doesn't have any way to know when
* it's lying, so it's only safe to call it when you know for sure
* that the store is paying attention to the folder, such as when it's
* just been created, or you get a folder_changed or message_changed
* signal on it.
*
* camel_store_get_folder_info() always gives correct answers for the
* folders it checks, but it can also return -1 for a folder, meaning
* it didn't check, and so you should stick with your previous answer.
*
* update_1folder is called from three places: with info != NULL when
* the folder is created (or get_folder_info), with info == NULL when
* a folder changed event is emitted.
*
* So if info is NULL, camel_folder_unread_message_count is correct,
* and if it's not NULL and its unread_message_count isn't -1, then
* it's correct. */
static void
update_1folder(struct _folder_info *mfi, CamelFolderInfo *info)
{
struct _store_info *si;
CamelFolder *folder;
int unread = -1;
CORBA_Environment ev;
extern CamelFolder *outbox_folder, *sent_folder;
si = mfi->store_info;
LOCK(info_lock);
folder = mfi->folder;
if (folder) {
if (CAMEL_IS_VTRASH_FOLDER (folder) || folder == outbox_folder || folder == sent_folder) {
unread = camel_folder_get_message_count(folder);
} else {
if (info)
unread = info->unread_message_count;
else
unread = camel_folder_get_unread_message_count (folder);
}
} else if (info)
unread = info->unread_message_count;
UNLOCK(info_lock);
if (unread == -1)
return;
if (si->storage == NULL) {
d(printf("Updating existing (local) folder: %s (%d unread) folder=%p\n", mfi->path, unread, folder));
CORBA_exception_init(&ev);
GNOME_Evolution_Storage_updateFolder(si->corba_storage, mfi->path, unread, &ev);
CORBA_exception_free(&ev);
} else {
d(printf("Updating existing folder: %s (%d unread)\n", mfi->path, unread));
evolution_storage_update_folder(si->storage, mfi->path, unread);
}
}
static void
setup_folder(CamelFolderInfo *fi, struct _store_info *si)
{
struct _folder_info *mfi;
char *type;
CamelStore *store;
LOCK(info_lock);
mfi = g_hash_table_lookup(si->folders, fi->full_name);
if (mfi) {
UNLOCK(info_lock);
update_1folder(mfi, fi);
} else {
/* always 'add it', but only 'add it' to non-local stores */
d(printf("Adding new folder: %s (%s) %d unread\n", fi->path, fi->url, fi->unread_message_count));
mfi = g_malloc0(sizeof(*mfi));
mfi->path = g_strdup(fi->path);
mfi->full_name = g_strdup(fi->full_name);
mfi->uri = g_strdup(fi->url);
mfi->store_info = si;
g_hash_table_insert(si->folders, mfi->full_name, mfi);
g_hash_table_insert(si->folders_uri, mfi->uri, mfi);
store = si->store;
camel_object_ref((CamelObject *)store);
UNLOCK(info_lock);
if (si->storage != NULL) {
int unread = (fi->unread_message_count==-1)?0:fi->unread_message_count;
type = (strncmp(fi->url, "vtrash:", 7)==0)?"vtrash":"mail";
evolution_storage_new_folder(si->storage, mfi->path, fi->name, type,
fi->url, fi->name, unread);
}
if (strstr(fi->url, ";noselect") == NULL)
mail_vfolder_add_uri(store, fi->url, FALSE);
camel_object_unref((CamelObject *)store);
}
}
static void
real_folder_changed(CamelFolder *folder, void *event_data, void *data)
{
struct _folder_info *mfi = data;
update_1folder(mfi, NULL);
camel_object_unref((CamelObject *)folder);
}
static void
folder_changed(CamelObject *o, gpointer event_data, gpointer user_data)
{
struct _folder_info *mfi = user_data;
if (mfi->folder != CAMEL_FOLDER(o))
return;
d(printf("Fodler changed!\n"));
camel_object_ref((CamelObject *)o);
mail_async_event_emit(mfi->store_info->async_event, (CamelObjectEventHookFunc)real_folder_changed, o, NULL, mfi);
}
static void
folder_finalised(CamelObject *o, gpointer event_data, gpointer user_data)
{
struct _folder_info *mfi = user_data;
d(printf("Folder finalised '%s'!\n", ((CamelFolder *)o)->full_name));
mfi->folder = NULL;
}
static void
folder_deleted(CamelObject *o, gpointer event_data, gpointer user_data)
{
struct _folder_info *mfi = user_data;
d(printf("Folder deleted '%s'!\n", ((CamelFolder *)o)->full_name));
mfi->folder = NULL;
}
static void
real_note_folder(CamelFolder *folder, void *event_data, void *data)
{
struct _folder_info *mfi = event_data;
update_1folder(mfi, NULL);
camel_object_unref((CamelObject *)folder);
}
void mail_note_folder(CamelFolder *folder)
{
CamelStore *store = folder->parent_store;
struct _store_info *si;
struct _folder_info *mfi;
if (stores == NULL) {
g_warning("Adding a folder `%s' to a store which hasn't been added yet?\n", folder->full_name);
return;
}
LOCK(info_lock);
si = g_hash_table_lookup(stores, store);
if (si == NULL) {
/*g_warning("Adding a folder `%s' to a store %p which hasn't been added yet?", folder->full_name, store);*/
UNLOCK(info_lock);
return;
}
mfi = g_hash_table_lookup(si->folders, folder->full_name);
if (mfi == NULL) {
g_warning("Adding a folder `%s' that I dont know about yet?", folder->full_name);
UNLOCK(info_lock);
return;
}
/* dont do anything if we already have this */
if (mfi->folder == folder) {
UNLOCK(info_lock);
return;
}
mfi->folder = folder;
camel_object_hook_event((CamelObject *)folder, "folder_changed", folder_changed, mfi);
camel_object_hook_event((CamelObject *)folder, "message_changed", folder_changed, mfi);
camel_object_hook_event((CamelObject *)folder, "deleted", folder_deleted, mfi);
camel_object_hook_event((CamelObject *)folder, "finalize", folder_finalised, mfi);
camel_object_ref((CamelObject *)folder);
UNLOCK(info_lock);
mail_async_event_emit(si->async_event, (CamelObjectEventHookFunc)real_note_folder, (CamelObject *)folder, (void *)mfi, NULL);
}
static void
real_folder_created(CamelStore *store, struct _store_info *si, CamelFolderInfo *fi)
{
setup_folder(fi, si);
camel_object_unref((CamelObject *)store);
camel_folder_info_free(fi);
}
static void
store_folder_subscribed(CamelObject *o, void *event_data, void *data)
{
struct _store_info *si;
LOCK(info_lock);
si = g_hash_table_lookup(stores, o);
if (si)
camel_object_ref(o);
UNLOCK(info_lock);
if (si)
mail_async_event_emit(si->async_event,
(CamelObjectEventHookFunc)real_folder_created, o, si,
camel_folder_info_clone(event_data));
}
static void
store_folder_created(CamelObject *o, void *event_data, void *data)
{
/* we only want created events to do more work if we dont support subscriptions */
if (!camel_store_supports_subscriptions(CAMEL_STORE(o)))
store_folder_subscribed(o, event_data, data);
}
static void
real_folder_deleted(CamelStore *store, struct _store_info *si, CamelFolderInfo *fi)
{
struct _folder_info *mfi;
d(printf("real_folder_deleted: %s (%s)\n", fi->full_name, fi->url));
LOCK(info_lock);
mfi = g_hash_table_lookup(si->folders, fi->full_name);
if (mfi) {
g_hash_table_remove(si->folders, mfi->full_name);
g_hash_table_remove(si->folders_uri, mfi->uri);
unset_folder_info(mfi, TRUE);
free_folder_info(NULL, mfi, NULL);
}
UNLOCK(info_lock);
camel_object_unref((CamelObject *)store);
camel_folder_info_free(fi);
}
static void
store_folder_unsubscribed(CamelObject *o, void *event_data, void *data)
{
struct _store_info *si;
LOCK(info_lock);
si = g_hash_table_lookup(stores, o);
if (si)
camel_object_ref(o);
UNLOCK(info_lock);
if (si)
mail_async_event_emit(si->async_event,
(CamelObjectEventHookFunc)real_folder_deleted, o, si,
camel_folder_info_clone(event_data));
}
static void
store_folder_deleted(CamelObject *o, void *event_data, void *data)
{
/* we only want deleted events to do more work if we dont support subscriptions */
if (!camel_store_supports_subscriptions(CAMEL_STORE(o)))
store_folder_unsubscribed(o, event_data, data);
}
static void
unset_folder_info(struct _folder_info *mfi, int delete)
{
if (mfi->folder) {
CamelFolder *folder = mfi->folder;
camel_object_unhook_event((CamelObject *)folder, "folder_changed", folder_changed, mfi);
camel_object_unhook_event((CamelObject *)folder, "message_changed", folder_changed, mfi);
camel_object_unhook_event((CamelObject *)folder, "deleted", folder_deleted, mfi);
camel_object_unhook_event((CamelObject *)folder, "finalize", folder_finalised, mfi);
}
if (strstr(mfi->uri, ";noselect") == NULL) {
if (delete)
mail_vfolder_delete_uri(mfi->store_info->store, mfi->uri);
else
mail_vfolder_add_uri(mfi->store_info->store, mfi->uri, TRUE);
}
}
static void
unset_folder_info_hash(char *path, struct _folder_info *mfi, void *data)
{
unset_folder_info(mfi, FALSE);
}
static void
free_folder_info(char *path, struct _folder_info *mfi, void *data)
{
g_free(mfi->path);
g_free(mfi->full_name);
g_free(mfi->uri);
}
static void
store_finalised(CamelObject *o, void *event_data, void *data)
{
CamelStore *store = (CamelStore *)o;
struct _store_info *si;
d(printf("store finalised!!\n"));
LOCK(info_lock);
si = g_hash_table_lookup(stores, store);
if (si) {
g_hash_table_remove(stores, store);
camel_object_unhook_event((CamelObject *)store, "folder_created", store_folder_created, NULL);
camel_object_unhook_event((CamelObject *)store, "folder_deleted", store_folder_deleted, NULL);
camel_object_unhook_event((CamelObject *)store, "folder_subscribed", store_folder_subscribed, NULL);
camel_object_unhook_event((CamelObject *)store, "folder_unsubscribed", store_folder_unsubscribed, NULL);
camel_object_unhook_event((CamelObject *)store, "finalize", store_finalised, NULL);
g_hash_table_foreach(si->folders, (GHFunc)unset_folder_info_hash, NULL);
UNLOCK(info_lock);
mail_async_event_destroy(si->async_event);
LOCK(info_lock);
g_hash_table_foreach(si->folders, (GHFunc)free_folder_info, NULL);
g_hash_table_destroy(si->folders);
g_hash_table_destroy(si->folders_uri);
g_free(si);
}
UNLOCK(info_lock);
}
static void
create_folders(CamelFolderInfo *fi, struct _store_info *si)
{
d(printf("Setup new folder: %s\n", fi->url));
setup_folder(fi, si);
if (fi->child)
create_folders(fi->child, si);
if (fi->sibling)
create_folders(fi->sibling, si);
}
struct _update_data {
struct _store_info *si;
void (*done)(CamelStore *store, CamelFolderInfo *info, void *data);
void *data;
};
static void
update_folders(CamelStore *store, CamelFolderInfo *info, void *data)
{
struct _update_data *ud = data;
if (info) {
if (ud->si->storage)
gtk_object_set_data (GTK_OBJECT (ud->si->storage), "connected", GINT_TO_POINTER (TRUE));
create_folders(info, ud->si);
}
if (ud->done)
ud->done(store, info, ud->data);
g_free(ud);
}
void
mail_note_store_remove(CamelStore *store)
{
g_assert(CAMEL_IS_STORE(store));
if (stores == NULL)
return;
/* same action */
store_finalised((CamelObject *)store, NULL, NULL);
}
void
mail_note_store(CamelStore *store, EvolutionStorage *storage, GNOME_Evolution_Storage corba_storage,
void (*done)(CamelStore *store, CamelFolderInfo *info, void *data), void *data)
{
struct _store_info *si;
struct _update_data *ud;
g_assert(CAMEL_IS_STORE(store));
g_assert(pthread_self() == mail_gui_thread);
g_assert(storage != NULL || corba_storage != CORBA_OBJECT_NIL);
LOCK(info_lock);
if (stores == NULL)
stores = g_hash_table_new(NULL, NULL);
si = g_hash_table_lookup(stores, store);
if (si == NULL) {
d(printf("Noting a new store: %p: %s\n", store, camel_url_to_string(((CamelService *)store)->url, 0)));
/* FIXME: Need to ref the storages & store or something?? */
si = g_malloc0(sizeof(*si));
si->folders = g_hash_table_new(g_str_hash, g_str_equal);
si->folders_uri = g_hash_table_new(CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->hash_folder_name,
CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name);
si->storage = storage;
si->corba_storage = corba_storage;
si->store = store;
g_hash_table_insert(stores, store, si);
si->async_event = mail_async_event_new();
camel_object_hook_event((CamelObject *)store, "folder_created", store_folder_created, NULL);
camel_object_hook_event((CamelObject *)store, "folder_deleted", store_folder_deleted, NULL);
camel_object_hook_event((CamelObject *)store, "folder_subscribed", store_folder_subscribed, NULL);
camel_object_hook_event((CamelObject *)store, "folder_unsubscribed", store_folder_unsubscribed, NULL);
camel_object_hook_event((CamelObject *)store, "finalize", store_finalised, NULL);
}
UNLOCK(info_lock);
ud = g_malloc(sizeof(*ud));
ud->si = si;
ud->done = done;
ud->data = data;
mail_get_folderinfo(store, update_folders, ud);
}
struct _find_info {
const char *uri;
struct _folder_info *fi;
};
/* look up on each storeinfo using proper hash function for that stores uri's */
static void storeinfo_find_folder_info(CamelStore *store, struct _store_info *si, struct _find_info *fi)
{
if (fi->fi == NULL)
fi->fi = g_hash_table_lookup(si->folders_uri, fi->uri);
}
/* returns TRUE if the uri is available, folderp is set to a
reffed folder if the folder has also already been opened */
int mail_note_get_folder_from_uri(const char *uri, CamelFolder **folderp)
{
struct _find_info fi = { uri, NULL };
if (stores == NULL)
return FALSE;
LOCK(info_lock);
g_hash_table_foreach(stores, (GHFunc)storeinfo_find_folder_info, &fi);
if (folderp) {
if (fi.fi && fi.fi->folder) {
*folderp = fi.fi->folder;
camel_object_ref((CamelObject *)*folderp);
} else {
*folderp = NULL;
}
}
UNLOCK(info_lock);
return fi.fi != NULL;
}