/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* mail-local.c: Local mailbox support. */
/*
* Authors:
* Michael Zucchi <NotZed@ximian.com>
* Peter Williams <peterw@ximian.com>
* Ettore Perazzoli <ettore@ximian.com>
* Dan Winship <danw@ximian.com>
*
* Copyright 2000 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
*/
/*
TODO:
If we are going to have all this LocalStore stuff, then the LocalStore
should have a reconfigure_folder method on it, as, in reality, it is
the maintainer of this information.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gnome-xml/xmlmemory.h>
#include <libgnomeui/gnome-dialog.h>
#include <libgnomeui/gnome-dialog-util.h>
#include <glade/glade.h>
#include "Evolution.h"
#include "evolution-storage.h"
#include "evolution-shell-component.h"
#include "evolution-storage-listener.h"
#include "gal/widgets/e-gui-utils.h"
#include "e-util/e-path.h"
#include "camel/camel.h"
#include "camel/camel-vee-store.h"
#include "camel/camel-vee-folder.h"
#include "camel/camel-vtrash-folder.h"
#include "filter/vfolder-context.h"
#include "filter/vfolder-rule.h"
#include "filter/vfolder-editor.h"
#include "mail.h"
#include "mail-local.h"
#include "mail-tools.h"
#include "folder-browser.h"
#include "mail-mt.h"
#include "mail-folder-cache.h"
#include "mail-vfolder.h"
#define d(x)
/* Local folder metainfo */
struct _local_meta {
char *path; /* path of metainfo file */
char *format; /* format of mailbox */
char *name; /* name of mbox itself */
int indexed; /* do we index the body? */
};
static struct _local_meta *
load_metainfo (const char *path)
{
xmlDocPtr doc;
xmlNodePtr node;
struct _local_meta *meta;
meta = g_malloc0 (sizeof (*meta));
meta->path = g_strdup (path);
d(printf("Loading folder metainfo from : %s\n", meta->path));
doc = xmlParseFile (meta->path);
if (doc == NULL) {
goto dodefault;
}
node = doc->root;
if (strcmp (node->name, "folderinfo")) {
goto dodefault;
}
node = node->childs;
while (node) {
if (!strcmp (node->name, "folder")) {
char *index, *txt;
txt = xmlGetProp (node, "type");
meta->format = g_strdup (txt ? txt : "mbox");
xmlFree (txt);
txt = xmlGetProp (node, "name");
meta->name = g_strdup (txt ? txt : "mbox");
xmlFree (txt);
index = xmlGetProp (node, "index");
if (index) {
meta->indexed = atoi (index);
xmlFree (index);
} else
meta->indexed = TRUE;
}
node = node->next;
}
xmlFreeDoc (doc);
return meta;
dodefault:
meta->format = g_strdup ("mbox"); /* defaults */
meta->name = g_strdup ("mbox");
meta->indexed = TRUE;
xmlFreeDoc (doc);
return meta;
}
static void
free_metainfo (struct _local_meta *meta)
{
g_free (meta->path);
g_free (meta->format);
g_free (meta->name);
g_free (meta);
}
static int
save_metainfo(struct _local_meta *meta)
{
xmlDocPtr doc;
xmlNodePtr root, node;
int ret;
d(printf("Saving folder metainfo to : %s\n", meta->path));
doc = xmlNewDoc("1.0");
root = xmlNewDocNode(doc, NULL, "folderinfo", NULL);
xmlDocSetRootElement(doc, root);
node = xmlNewChild(root, NULL, "folder", NULL);
xmlSetProp(node, "type", meta->format);
xmlSetProp(node, "name", meta->name);
xmlSetProp(node, "index", meta->indexed?"1":"0");
ret = xmlSaveFile(meta->path, doc);
xmlFreeDoc(doc);
return ret;
}
/* MailLocalStore implementation */
#define MAIL_LOCAL_STORE_TYPE (mail_local_store_get_type ())
#define MAIL_LOCAL_STORE(obj) (CAMEL_CHECK_CAST((obj), MAIL_LOCAL_STORE_TYPE, MailLocalStore))
#define MAIL_LOCAL_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_LOCAL_STORE_TYPE, MailLocalStoreClass))
#define MAIL_IS_LOCAL_STORE(o) (CAMEL_CHECK_TYPE((o), MAIL_LOCAL_STORE_TYPE))
typedef struct {
CamelStore parent_object;
EvolutionStorage *storage;
GNOME_Evolution_Storage corba_storage;
EvolutionStorageListener *local_storage_listener;
char *local_path;
int local_pathlen;
GHashTable *folders; /* points to MailLocalFolder */
} MailLocalStore;
typedef struct {
CamelStoreClass parent_class;
} MailLocalStoreClass;
typedef struct {
CamelFolder *folder;
MailLocalStore *local_store;
char *path, *name, *uri;
} MailLocalFolder;
static MailLocalStore *local_store;
CamelType mail_local_store_get_type (void);
static char *get_name (CamelService *service, gboolean brief);
static CamelFolder *get_folder (CamelStore *store, const char *folder_name,
guint32 flags, CamelException *ex);
static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top,
guint32 flags, CamelException *ex);
static void delete_folder (CamelStore *store, const char *folder_name,
CamelException *ex);
static void rename_folder (CamelStore *store, const char *old_name,
const char *new_name, CamelException *ex);
static void init_trash (CamelStore *store);
static CamelStoreClass *local_parent_class;
static void
mail_local_store_class_init (MailLocalStoreClass *mail_local_store_class)
{
CamelStoreClass *camel_store_class =
CAMEL_STORE_CLASS (mail_local_store_class);
CamelServiceClass *camel_service_class =
CAMEL_SERVICE_CLASS (mail_local_store_class);
/* virtual method overload */
camel_service_class->get_name = get_name;
/* Don't cache folders */
camel_store_class->hash_folder_name = NULL;
camel_store_class->compare_folder_name = NULL;
camel_store_class->init_trash = init_trash;
camel_store_class->get_folder = get_folder;
camel_store_class->get_folder_info = get_folder_info;
camel_store_class->free_folder_info = camel_store_free_folder_info_full;
camel_store_class->delete_folder = delete_folder;
camel_store_class->rename_folder = rename_folder;
local_parent_class = (CamelStoreClass *)camel_type_get_global_classfuncs (camel_store_get_type ());
}
static void
mail_local_store_init (gpointer object, gpointer klass)
{
MailLocalStore *local_store = MAIL_LOCAL_STORE (object);
local_store->corba_storage = CORBA_OBJECT_NIL;
}
static void
free_local_folder (MailLocalFolder *lf)
{
if (lf->folder)
camel_object_unref ((CamelObject *)lf->folder);
g_free (lf->path);
g_free (lf->name);
g_free (lf->uri);
camel_object_unref ((CamelObject *)lf->local_store);
}
static void
free_folder (gpointer key, gpointer data, gpointer user_data)
{
MailLocalFolder *lf = data;
g_free (key);
free_local_folder (lf);
}
static void
mail_local_store_finalize (gpointer object)
{
MailLocalStore *local_store = MAIL_LOCAL_STORE (object);
CORBA_Environment ev;
CORBA_exception_init (&ev);
if (!CORBA_Object_is_nil (local_store->corba_storage, &ev))
bonobo_object_release_unref (local_store->corba_storage, &ev);
CORBA_exception_free (&ev);
if (local_store->local_storage_listener)
gtk_object_unref (GTK_OBJECT (local_store->local_storage_listener));
g_hash_table_foreach (local_store->folders, free_folder, NULL);
g_hash_table_destroy (local_store->folders);
g_free (local_store->local_path);
}
CamelType
mail_local_store_get_type (void)
{
static CamelType mail_local_store_type = CAMEL_INVALID_TYPE;
if (mail_local_store_type == CAMEL_INVALID_TYPE) {
mail_local_store_type = camel_type_register (
CAMEL_STORE_TYPE, "MailLocalStore",
sizeof (MailLocalStore),
sizeof (MailLocalStoreClass),
(CamelObjectClassInitFunc) mail_local_store_class_init,
NULL,
(CamelObjectInitFunc) mail_local_store_init,
(CamelObjectFinalizeFunc) mail_local_store_finalize);
}
return mail_local_store_type;
}
static CamelFolder *
get_folder (CamelStore *store,
const char *folder_name,
guint32 flags,
CamelException *ex)
{
MailLocalStore *local_store = (MailLocalStore *)store;
MailLocalFolder *local_folder;
CamelFolder *folder;
local_folder = g_hash_table_lookup (local_store->folders, folder_name);
if (local_folder) {
folder = local_folder->folder;
camel_object_ref (CAMEL_OBJECT (folder));
} else {
folder = NULL;
camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
_("No such folder /%s"), folder_name);
}
return folder;
}
static void
trash_add_folder (gpointer key, gpointer value, gpointer data)
{
MailLocalFolder *local_folder = (MailLocalFolder *) value;
CamelFolder *folder = local_folder->folder;
CamelStore *store = CAMEL_STORE (data);
camel_vee_folder_add_folder (CAMEL_VEE_FOLDER (store->vtrash), folder);
}
static void
trash_finalize (CamelObject *trash, gpointer event_data, gpointer user_data)
{
CamelStore *store = CAMEL_STORE (user_data);
store->vtrash = NULL;
}
static void
init_trash (CamelStore *store)
{
MailLocalStore *local_store = MAIL_LOCAL_STORE (store);
store->vtrash = camel_vtrash_folder_new (store, CAMEL_VTRASH_NAME);
if (store->vtrash) {
/* attach to the finalize event of the vtrash */
camel_object_hook_event (CAMEL_OBJECT (store->vtrash), "finalize",
trash_finalize, store);
/* add all the pre-opened folders to the vtrash */
if (local_store->folders) {
/* lock? */
g_hash_table_foreach (local_store->folders, trash_add_folder, store);
/* unlock? */
}
/* would prefer not to special-case this, but... */
mail_folder_cache_note_folder ("vtrash:file:/", store->vtrash);
mail_folder_cache_set_update_lstorage ("vtrash:file:/",
local_store->corba_storage,
"/Trash");
}
}
static void
populate_folders (gpointer key, gpointer data, gpointer user_data)
{
GPtrArray *folders = user_data;
MailLocalFolder *folder;
CamelFolderInfo *fi;
folder = data;
fi = g_new0 (CamelFolderInfo, 1);
fi->full_name = g_strdup (folder->path);
fi->name = g_strdup (folder->name);
fi->url = g_strdup (folder->uri);
fi->unread_message_count = -1;
g_ptr_array_add (folders, fi);
}
static CamelFolderInfo *
get_folder_info (CamelStore *store, const char *top,
guint32 flags, CamelException *ex)
{
MailLocalStore *local_store = MAIL_LOCAL_STORE (store);
CamelFolderInfo *fi = NULL;
GPtrArray *folders;
folders = g_ptr_array_new ();
g_hash_table_foreach (local_store->folders, populate_folders, folders);
fi = camel_folder_info_build (folders, top, '/', TRUE);
g_ptr_array_free (folders, TRUE);
return fi;
}
static void
delete_folder (CamelStore *store, const char *folder_name, CamelException *ex)
{
/* No-op. The shell local storage deals with this. */
}
static void
rename_folder (CamelStore *store, const char *old, const char *new,
CamelException *ex)
{
/* Probable no-op... */
}
static char *
get_name (CamelService *service, gboolean brief)
{
return g_strdup ("Local mail folders");
}
/* Callbacks for the EvolutionStorageListner signals. */
static void
local_storage_destroyed_cb (EvolutionStorageListener *storage_listener,
void *data)
{
/* FIXME: Dunno how to handle this yet. */
g_warning ("%s -- The LocalStorage has gone?!", __FILE__);
}
/* ********************************************************************** */
/* Register folder */
struct _register_msg {
struct _mail_msg msg;
MailLocalFolder *local_folder;
};
static char *
register_folder_desc (struct _mail_msg *mm, int done)
{
struct _register_msg *m = (struct _register_msg *)mm;
return g_strdup_printf (_("Registering '%s'"), m->local_folder->uri);
}
static void
register_folder_register (struct _mail_msg *mm)
{
struct _register_msg *m = (struct _register_msg *)mm;
MailLocalFolder *local_folder = m->local_folder;
char *name, *path = local_folder->uri + 7;
struct _local_meta *meta;
CamelStore *store;
guint32 flags;
name = g_strdup_printf ("%s/local-metadata.xml", path);
meta = load_metainfo (name);
g_free (name);
name = g_strdup_printf ("%s:%s", meta->format, path);
store = camel_session_get_store (session, name, &mm->ex);
g_free (name);
if (!store) {
free_metainfo (meta);
return;
}
flags = CAMEL_STORE_FOLDER_CREATE;
if (meta->indexed)
flags |= CAMEL_STORE_FOLDER_BODY_INDEX;
local_folder->folder = camel_store_get_folder (store, meta->name, flags, &mm->ex);
camel_object_unref (CAMEL_OBJECT (store));
free_metainfo (meta);
}
static void
register_folder_registered (struct _mail_msg *mm)
{
struct _register_msg *m = (struct _register_msg *)mm;
MailLocalFolder *local_folder = m->local_folder;
if (local_folder->folder) {
gchar *name;
g_hash_table_insert (local_folder->local_store->folders,
local_folder->uri + 8,
local_folder);
/* Remove the circular ref once the local store knows about the folder */
camel_object_unref ((CamelObject *)local_folder->local_store);
/* add the folder to the vfolder lists FIXME: merge stuff above with this */
vfolder_register_source (local_folder->folder);
mail_folder_cache_set_update_lstorage (local_folder->uri,
local_folder->local_store->corba_storage,
local_folder->path);
name = strrchr (local_folder->path, '/');
if (name) /* should always be true... */ {
name += 1; /* skip the slash */
mail_folder_cache_note_name (local_folder->uri, name);
}
/* Do this after specifying the name so it isn't 'mbox' */
mail_folder_cache_note_folder (local_folder->uri, local_folder->folder);
m->local_folder = NULL;
}
}
static void
register_folder_free (struct _mail_msg *mm)
{
struct _register_msg *m = (struct _register_msg *)mm;
if (m->local_folder)
free_local_folder (m->local_folder);
}
static struct _mail_msg_op register_folder_op = {
register_folder_desc,
register_folder_register,
register_folder_registered,
register_folder_free,
};
static void
local_storage_new_folder_cb (EvolutionStorageListener *storage_listener,
const char *path,
const GNOME_Evolution_Folder *folder,
void *data)
{
MailLocalStore *local_store = data;
MailLocalFolder *local_folder;
struct _register_msg *m;
int id;
if (strcmp (folder->type, "mail") != 0 ||
strncmp (folder->physicalUri, "file://", 7) != 0 ||
strncmp (folder->physicalUri + 7, local_store->local_path,
local_store->local_pathlen) != 0)
return;
local_folder = g_new0 (MailLocalFolder, 1);
local_folder->name = g_strdup (strrchr (path, '/') + 1);
local_folder->path = g_strdup (path);
local_folder->uri = g_strdup (folder->physicalUri);
local_folder->local_store = local_store;
camel_object_ref ((CamelObject *)local_store);
m = mail_msg_new (®ister_folder_op, NULL, sizeof (*m));
m->local_folder = local_folder;
/* run synchronous, the shell expects it (I think) */
id = m->msg.seq;
e_thread_put (mail_thread_queued, (EMsg *)m);
mail_msg_wait (id);
}
static void
local_storage_removed_folder_cb (EvolutionStorageListener *storage_listener,
const char *path,
void *data)
{
MailLocalStore *local_store = data;
MailLocalFolder *local_folder;
char *physical_path;
char *tmpname;
physical_path = e_path_to_physical (local_store->local_path, path);
if (strncmp (physical_path, local_store->local_path,
local_store->local_pathlen) != 0)
return;
tmpname = strchr (physical_path, '/');
if (tmpname) {
while (*tmpname == '/')
tmpname++;
local_folder = g_hash_table_lookup (local_store->folders, tmpname);
camel_object_ref ((CamelObject *)local_store); /* When we go to free_local_folder() the
local_store will be unref'd */
}
else
local_folder = NULL;
if (local_folder) {
g_hash_table_remove (local_store->folders, tmpname);
free_local_folder (local_folder);
}
g_free (physical_path);
}
static CamelProvider local_provider = {
"file", "Local mail", NULL, "mail",
CAMEL_PROVIDER_IS_STORAGE, CAMEL_URL_NEED_PATH,
/* ... */
};
/* There's only one "file:" store. */
static guint
non_hash (gconstpointer key)
{
return 0;
}
static gint
non_equal (gconstpointer a, gconstpointer b)
{
return TRUE;
}
void
mail_local_storage_startup (EvolutionShellClient *shellclient,
const char *evolution_path)
{
EvolutionStorageListener *local_storage_listener;
GNOME_Evolution_StorageListener corba_local_storage_listener;
CORBA_Environment ev;
/* Register with Camel to handle file: URLs */
local_provider.object_types[CAMEL_PROVIDER_STORE] =
mail_local_store_get_type();
local_provider.service_cache = g_hash_table_new (non_hash, non_equal);
camel_session_register_provider (session, &local_provider);
/* Now build the storage. */
local_store = (MailLocalStore *)camel_session_get_service (
session, "file:/", CAMEL_PROVIDER_STORE, NULL);
if (!local_store) {
g_warning ("No local store!");
return;
}
local_store->corba_storage = evolution_shell_client_get_local_storage (shellclient);
if (local_store->corba_storage == CORBA_OBJECT_NIL) {
g_warning ("No local storage!");
camel_object_unref (CAMEL_OBJECT (local_store));
return;
}
local_storage_listener = evolution_storage_listener_new ();
corba_local_storage_listener = evolution_storage_listener_corba_objref (
local_storage_listener);
gtk_signal_connect (GTK_OBJECT (local_storage_listener),
"destroyed",
GTK_SIGNAL_FUNC (local_storage_destroyed_cb),
local_store);
gtk_signal_connect (GTK_OBJECT (local_storage_listener),
"new_folder",
GTK_SIGNAL_FUNC (local_storage_new_folder_cb),
local_store);
gtk_signal_connect (GTK_OBJECT (local_storage_listener),
"removed_folder",
GTK_SIGNAL_FUNC (local_storage_removed_folder_cb),
local_store);
local_store->local_storage_listener = local_storage_listener;
local_store->local_path = g_strdup_printf ("%s/local",
evolution_path);
local_store->local_pathlen = strlen (local_store->local_path);
local_store->folders = g_hash_table_new (g_str_hash, g_str_equal);
CORBA_exception_init (&ev);
GNOME_Evolution_Storage_addListener (local_store->corba_storage,
corba_local_storage_listener, &ev);
if (ev._major != CORBA_NO_EXCEPTION) {
g_warning ("Cannot add a listener to the Local Storage.");
camel_object_unref (CAMEL_OBJECT (local_store));
CORBA_exception_free (&ev);
return;
}
CORBA_exception_free (&ev);
}
/*----------------------------------------------------------------------
* Local folder reconfiguration stuff
*----------------------------------------------------------------------*/
/*
open new
copy old->new
close old
rename old oldsave
rename new old
open oldsave
delete oldsave
close old
rename oldtmp
open new
open oldtmp
copy oldtmp new
close oldtmp
close oldnew
*/
/* ******************** */
/* we should have our own progress bar for this */
struct _reconfigure_msg {
struct _mail_msg msg;
FolderBrowser *fb;
char *newtype;
GtkWidget *frame;
GtkWidget *apply;
GtkWidget *cancel;
GtkOptionMenu *optionlist;
};
static char *
reconfigure_folder_describe (struct _mail_msg *mm, int done)
{
struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm;
return g_strdup_printf (_("Changing folder \"%s\" to \"%s\" format"),
m->fb->uri,
m->newtype);
}
static void
reconfigure_folder_reconfigure (struct _mail_msg *mm)
{
struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm;
MailLocalFolder *local_folder = NULL;
CamelStore *fromstore = NULL, *tostore = NULL;
char *fromurl = NULL, *tourl = NULL;
CamelFolder *fromfolder = NULL, *tofolder = NULL;
GPtrArray *uids;
char *metapath;
char *tmpname;
CamelURL *url = NULL;
struct _local_meta *meta = NULL;
guint32 flags;
d(printf("reconfiguring folder: %s to type %s\n", m->fb->uri, m->newtype));
/* NOTE: This var is cleared by the folder_browser via the set_uri method */
m->fb->reconfigure = TRUE;
/* get the actual location of the mailbox */
url = camel_url_new (m->fb->uri, &mm->ex);
if (camel_exception_is_set (&mm->ex)) {
g_warning ("%s is not a workable url!", m->fb->uri);
goto cleanup;
}
tmpname = strchr (m->fb->uri, '/');
if (tmpname) {
while (*tmpname == '/')
tmpname++;
local_folder = g_hash_table_lookup (local_store->folders, tmpname);
} else
local_folder = NULL;
if (!local_folder) {
g_warning ("%s is not a registered local folder!", m->fb->uri);
goto cleanup;
}
metapath = g_strdup_printf ("%s/local-metadata.xml", url->path);
meta = load_metainfo (metapath);
g_free (metapath);
/* first, 'close' the old folder */
camel_folder_sync (local_folder->folder, FALSE, &mm->ex);
/* Once for the FolderBrowser, once for the local store */
camel_object_unref (CAMEL_OBJECT (local_folder->folder));
camel_object_unref (CAMEL_OBJECT (local_folder->folder));
local_folder->folder = m->fb->folder = NULL;
camel_url_set_protocol (url, meta->format);
fromurl = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
camel_url_set_protocol (url, m->newtype);
tourl = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
d(printf("opening stores %s and %s\n", fromurl, tourl));
fromstore = camel_session_get_store (session, fromurl, &mm->ex);
if (camel_exception_is_set (&mm->ex))
goto cleanup;
tostore = camel_session_get_store (session, tourl, &mm->ex);
if (camel_exception_is_set (&mm->ex))
goto cleanup;
/* rename the old mbox and open it again, without indexing */
tmpname = g_strdup_printf ("%s_reconfig", meta->name);
d(printf("renaming %s to %s, and opening it\n", meta->name, tmpname));
camel_store_rename_folder (fromstore, meta->name, tmpname, &mm->ex);
if (camel_exception_is_set (&mm->ex)) {
goto cleanup;
}
/* we dont need to set the create flag ... or need an index if it has one */
fromfolder = camel_store_get_folder (fromstore, tmpname, 0, &mm->ex);
if (fromfolder == NULL || camel_exception_is_set (&mm->ex)) {
/* try and recover ... */
camel_exception_clear (&mm->ex);
camel_store_rename_folder (fromstore, tmpname, meta->name, &mm->ex);
goto cleanup;
}
/* create a new mbox */
d(printf("Creating the destination mbox\n"));
flags = CAMEL_STORE_FOLDER_CREATE;
if (meta->indexed)
flags |= CAMEL_STORE_FOLDER_BODY_INDEX;
tofolder = camel_store_get_folder (tostore, meta->name, flags, &mm->ex);
if (tofolder == NULL || camel_exception_is_set (&mm->ex)) {
d(printf("cannot open destination folder\n"));
/* try and recover ... */
camel_exception_clear (&mm->ex);
camel_store_rename_folder (fromstore, tmpname, meta->name, &mm->ex);
goto cleanup;
}
uids = camel_folder_get_uids (fromfolder);
camel_folder_move_messages_to (fromfolder, uids, tofolder, &mm->ex);
camel_folder_free_uids (fromfolder, uids);
if (camel_exception_is_set (&mm->ex))
goto cleanup;
camel_folder_expunge (fromfolder, &mm->ex);
d(printf("delete old mbox ...\n"));
camel_store_delete_folder (fromstore, tmpname, &mm->ex);
/* switch format */
g_free (meta->format);
meta->format = g_strdup (m->newtype);
if (save_metainfo (meta) == -1) {
camel_exception_setv (&mm->ex, CAMEL_EXCEPTION_SYSTEM,
_("Cannot save folder metainfo; "
"you'll probably find you can't\n"
"open this folder anymore: %s"),
tourl);
}
cleanup:
if (local_folder && !local_folder->folder) {
struct _register_msg *rm = mail_msg_new (®ister_folder_op, NULL, sizeof (*m));
/* fake the internal part of this operation, nasty hackish thing */
rm->local_folder = local_folder;
register_folder_register ((struct _mail_msg *)rm);
rm->local_folder = NULL;
mail_msg_free ((struct _mail_msg *)rm);
}
if (tofolder)
camel_object_unref (CAMEL_OBJECT (tofolder));
if (fromfolder)
camel_object_unref (CAMEL_OBJECT (fromfolder));
if (fromstore)
camel_object_unref (CAMEL_OBJECT (fromstore));
if (tostore)
camel_object_unref (CAMEL_OBJECT (tostore));
if (meta)
free_metainfo (meta);
g_free (fromurl);
g_free (tourl);
if (url)
camel_url_free (url);
}
static void
reconfigure_folder_reconfigured (struct _mail_msg *mm)
{
struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm;
char *uri;
if (camel_exception_is_set (&mm->ex)) {
gnome_error_dialog (_("If you can no longer open this mailbox, then\n"
"you may need to repair it manually."));
}
/* force a reload of the newly formatted folder */
d(printf("opening new source\n"));
uri = g_strdup (m->fb->uri);
folder_browser_set_uri (m->fb, uri);
g_free (uri);
}
static void
reconfigure_folder_free (struct _mail_msg *mm)
{
struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm;
gtk_object_unref (GTK_OBJECT (m->fb));
g_free (m->newtype);
}
static struct _mail_msg_op reconfigure_folder_op = {
reconfigure_folder_describe,
reconfigure_folder_reconfigure,
reconfigure_folder_reconfigured,
reconfigure_folder_free,
};
/* hash table of folders that the user has a reconfig-folder dialog for */
static GHashTable *reconfigure_folder_hash = NULL;
static void
reconfigure_clicked (GnomeDialog *dialog, int button, struct _reconfigure_msg *m)
{
if (button == 0) {
GtkWidget *menu;
int type;
char *types[] = { "mbox", "maildir", "mh" };
/* hack to clear the message list during update */
message_list_set_folder (m->fb->message_list, NULL, FALSE);
menu = gtk_option_menu_get_menu (m->optionlist);
type = g_list_index (GTK_MENU_SHELL (menu)->children,
gtk_menu_get_active (GTK_MENU (menu)));
if (type < 0 || type > 2)
type = 0;
gtk_widget_set_sensitive (m->frame, FALSE);
gtk_widget_set_sensitive (m->apply, FALSE);
gtk_widget_set_sensitive (m->cancel, FALSE);
m->newtype = g_strdup (types[type]);
e_thread_put (mail_thread_queued, (EMsg *)m);
} else
mail_msg_free ((struct _mail_msg *)m);
if (button != -1)
gnome_dialog_close (dialog);
}
void
mail_local_reconfigure_folder (FolderBrowser *fb)
{
CamelStore *store;
GladeXML *gui;
GnomeDialog *gd;
struct _reconfigure_msg *m;
char *name, *title;
if (fb->folder == NULL) {
g_warning ("Trying to reconfigure nonexistant folder");
return;
}
if (!reconfigure_folder_hash)
reconfigure_folder_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
if ((gd = g_hash_table_lookup (reconfigure_folder_hash, fb->folder))) {
gdk_window_raise (GTK_WIDGET (gd)->window);
return;
}
/* check if we can work on this folder */
name = strchr (fb->uri, '/');
if (name) {
while (*name == '/')
name++;
/* we just want to see if it's NULL or not */
name = (char *) g_hash_table_lookup (local_store->folders, name);
}
if (name == NULL) {
e_notice (NULL, GNOME_MESSAGE_BOX_WARNING,
_("You cannot change the format of a non-local folder."));
return;
}
m = mail_msg_new (&reconfigure_folder_op, NULL, sizeof (*m));
store = camel_folder_get_parent_store (fb->folder);
gui = glade_xml_new (EVOLUTION_GLADEDIR "/local-config.glade", "dialog_format");
gd = (GnomeDialog *)glade_xml_get_widget (gui, "dialog_format");
name = mail_tool_get_folder_name (fb->folder);
title = g_strdup_printf (_("Reconfigure %s"), name);
gtk_window_set_title (GTK_WINDOW (gd), title);
g_free (title);
g_free (name);
m->frame = glade_xml_get_widget (gui, "frame_format");
m->apply = glade_xml_get_widget (gui, "apply_format");
m->cancel = glade_xml_get_widget (gui, "cancel_format");
m->optionlist = (GtkOptionMenu *)glade_xml_get_widget (gui, "option_format");
m->newtype = NULL;
m->fb = fb;
gtk_object_ref (GTK_OBJECT (fb));
gtk_label_set_text ((GtkLabel *)glade_xml_get_widget (gui, "label_format"),
((CamelService *)store)->url->protocol);
gtk_signal_connect (GTK_OBJECT (gd), "clicked", reconfigure_clicked, m);
gtk_object_unref (GTK_OBJECT (gui));
g_hash_table_insert (reconfigure_folder_hash, (gpointer) fb->folder, (gpointer) gd);
gnome_dialog_run_and_close (GNOME_DIALOG (gd));
/* remove this folder from our hash since we are done with it */
g_hash_table_remove (reconfigure_folder_hash, fb->folder);
if (g_hash_table_size (reconfigure_folder_hash) == 0) {
/* additional cleanup */
g_hash_table_destroy (reconfigure_folder_hash);
reconfigure_folder_hash = NULL;
}
}