Folders can leave notifications in the main loop which would be delivered on idle, but these can be left in the main loop longer than the temporary CamelSession object is alive, which leads to a crash, because of the CamelStore's descendant being freed too early.
343 lines
9.0 KiB
C
343 lines
9.0 KiB
C
/*
|
|
* e-convert-local-mail.c
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser 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 Lesser General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <glib/gstdio.h>
|
|
#include <camel/camel.h>
|
|
|
|
#include <shell/e-shell.h>
|
|
|
|
/* Forward Declarations */
|
|
void e_convert_local_mail (EShell *shell);
|
|
|
|
static gboolean
|
|
mail_to_maildir_migration_needed (const gchar *mail_data_dir)
|
|
{
|
|
gchar *local_store;
|
|
gchar *local_outbox;
|
|
gboolean migration_needed = FALSE;
|
|
|
|
local_store = g_build_filename (mail_data_dir, "local", NULL);
|
|
local_outbox = g_build_filename (local_store, ".Outbox", NULL);
|
|
|
|
/* If this is a fresh install (no local store exists yet)
|
|
* then obviously there's nothing to migrate to Maildir. */
|
|
if (!g_file_test (local_store, G_FILE_TEST_IS_DIR))
|
|
migration_needed = FALSE;
|
|
|
|
/* Look for a Maildir Outbox folder. */
|
|
else if (!g_file_test (local_outbox, G_FILE_TEST_IS_DIR))
|
|
migration_needed = TRUE;
|
|
|
|
g_free (local_store);
|
|
g_free (local_outbox);
|
|
|
|
return migration_needed;
|
|
}
|
|
|
|
/* Folder names with '.' are converted to '_' */
|
|
static gchar *
|
|
sanitize_maildir_folder_name (gchar *folder_name)
|
|
{
|
|
gchar *maildir_folder_name;
|
|
|
|
maildir_folder_name = g_strdup (folder_name);
|
|
g_strdelimit (maildir_folder_name, ".", '_');
|
|
|
|
return maildir_folder_name;
|
|
}
|
|
|
|
static void
|
|
copy_folder (CamelStore *mail_store,
|
|
CamelStore *maildir_store,
|
|
const gchar *mail_fname,
|
|
const gchar *maildir_fname)
|
|
{
|
|
CamelFolder *fromfolder, *tofolder;
|
|
GPtrArray *uids;
|
|
|
|
fromfolder = camel_store_get_folder_sync (
|
|
mail_store, mail_fname, 0, NULL, NULL);
|
|
if (fromfolder == NULL) {
|
|
g_warning ("Cannot find mail folder %s \n", mail_fname);
|
|
return;
|
|
}
|
|
|
|
tofolder = camel_store_get_folder_sync (
|
|
maildir_store, maildir_fname,
|
|
CAMEL_STORE_FOLDER_CREATE, NULL, NULL);
|
|
if (tofolder == NULL) {
|
|
g_warning ("Cannot create maildir folder %s \n", maildir_fname);
|
|
g_object_unref (fromfolder);
|
|
return;
|
|
}
|
|
|
|
uids = camel_folder_get_uids (fromfolder);
|
|
camel_folder_transfer_messages_to_sync (
|
|
fromfolder, uids, tofolder, FALSE, NULL, NULL, NULL);
|
|
camel_folder_free_uids (fromfolder, uids);
|
|
|
|
g_object_unref (fromfolder);
|
|
g_object_unref (tofolder);
|
|
}
|
|
|
|
static void
|
|
copy_folders (CamelStore *mail_store,
|
|
CamelStore *maildir_store,
|
|
CamelFolderInfo *fi,
|
|
CamelSession *session)
|
|
{
|
|
if (fi) {
|
|
if (!g_str_has_prefix (fi->full_name, ".#evolution")) {
|
|
gchar *maildir_folder_name;
|
|
|
|
/* sanitize folder names and copy folders */
|
|
maildir_folder_name =
|
|
sanitize_maildir_folder_name (fi->full_name);
|
|
copy_folder (
|
|
mail_store, maildir_store,
|
|
fi->full_name, maildir_folder_name);
|
|
g_free (maildir_folder_name);
|
|
}
|
|
|
|
if (fi->child)
|
|
copy_folders (mail_store, maildir_store, fi->child, session);
|
|
|
|
copy_folders (mail_store, maildir_store, fi->next, session);
|
|
}
|
|
}
|
|
|
|
struct MigrateStore {
|
|
CamelSession *session;
|
|
CamelStore *mail_store;
|
|
CamelStore *maildir_store;
|
|
gboolean complete;
|
|
};
|
|
|
|
static void
|
|
migrate_stores (struct MigrateStore *ms)
|
|
{
|
|
CamelFolderInfo *mail_fi;
|
|
CamelStore *mail_store = ms->mail_store;
|
|
CamelStore *maildir_store = ms->maildir_store;
|
|
|
|
mail_fi = camel_store_get_folder_info_sync (
|
|
mail_store, NULL,
|
|
CAMEL_STORE_FOLDER_INFO_RECURSIVE |
|
|
CAMEL_STORE_FOLDER_INFO_FAST |
|
|
CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
|
|
NULL, NULL);
|
|
|
|
/* FIXME progres dialog */
|
|
copy_folders (mail_store, maildir_store, mail_fi, ms->session);
|
|
ms->complete = TRUE;
|
|
}
|
|
|
|
static void
|
|
rename_mbox_dir (ESource *mbox_source,
|
|
const gchar *mail_data_dir)
|
|
{
|
|
gchar *old_mail_dir;
|
|
gchar *new_mail_dir;
|
|
gboolean need_rename;
|
|
const gchar *mbox_uid;
|
|
|
|
mbox_uid = e_source_get_uid (mbox_source);
|
|
|
|
old_mail_dir = g_build_filename (mail_data_dir, "local", NULL);
|
|
new_mail_dir = g_build_filename (mail_data_dir, mbox_uid, NULL);
|
|
|
|
/* Rename if old directory exists and new directory does not. */
|
|
need_rename =
|
|
g_file_test (old_mail_dir, G_FILE_TEST_EXISTS) &&
|
|
!g_file_test (new_mail_dir, G_FILE_TEST_EXISTS);
|
|
|
|
if (need_rename) {
|
|
if (g_rename (old_mail_dir, new_mail_dir) == -1)
|
|
g_warning (
|
|
"%s: Failed to rename '%s' to '%s': %s",
|
|
G_STRFUNC, old_mail_dir, new_mail_dir, g_strerror (errno));
|
|
}
|
|
|
|
g_free (old_mail_dir);
|
|
g_free (new_mail_dir);
|
|
}
|
|
|
|
static gboolean
|
|
migrate_mbox_to_maildir (EShell *shell,
|
|
CamelSession *session,
|
|
ESource *mbox_source)
|
|
{
|
|
ESourceRegistry *registry;
|
|
ESourceExtension *extension;
|
|
const gchar *extension_name;
|
|
CamelService *mbox_service = NULL;
|
|
CamelService *maildir_service = NULL;
|
|
CamelSettings *settings;
|
|
const gchar *data_dir;
|
|
const gchar *mbox_uid;
|
|
gchar *path;
|
|
struct MigrateStore ms;
|
|
GThread *thread;
|
|
GError *error = NULL;
|
|
|
|
registry = e_shell_get_registry (shell);
|
|
|
|
data_dir = camel_session_get_user_data_dir (session);
|
|
|
|
mbox_uid = e_source_get_uid (mbox_source);
|
|
e_source_set_display_name (mbox_source, "local_mbox");
|
|
|
|
extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
|
|
extension = e_source_get_extension (mbox_source, extension_name);
|
|
|
|
e_source_backend_set_backend_name (
|
|
E_SOURCE_BACKEND (extension), "mbox");
|
|
|
|
extension_name = e_source_camel_get_extension_name ("mbox");
|
|
extension = e_source_get_extension (mbox_source, extension_name);
|
|
settings = e_source_camel_get_settings (E_SOURCE_CAMEL (extension));
|
|
|
|
path = g_build_filename (data_dir, mbox_uid, NULL);
|
|
g_object_set (settings, "path", path, NULL);
|
|
g_free (path);
|
|
|
|
e_source_registry_commit_source_sync (
|
|
registry, mbox_source, NULL, &error);
|
|
|
|
if (error == NULL)
|
|
mbox_service = camel_session_add_service (
|
|
session, mbox_uid, "mbox",
|
|
CAMEL_PROVIDER_STORE, &error);
|
|
|
|
if (error == NULL)
|
|
maildir_service = camel_session_add_service (
|
|
session, "local", "maildir",
|
|
CAMEL_PROVIDER_STORE, &error);
|
|
|
|
if (error != NULL) {
|
|
if (mbox_service != NULL)
|
|
g_object_unref (mbox_service);
|
|
if (maildir_service != NULL)
|
|
g_object_unref (maildir_service);
|
|
g_warning ("%s: %s", G_STRFUNC, error->message);
|
|
g_error_free (error);
|
|
return FALSE;
|
|
}
|
|
|
|
g_return_val_if_fail (CAMEL_IS_STORE (mbox_service), FALSE);
|
|
g_return_val_if_fail (CAMEL_IS_STORE (maildir_service), FALSE);
|
|
|
|
camel_service_set_settings (mbox_service, settings);
|
|
|
|
settings = camel_service_ref_settings (maildir_service);
|
|
|
|
path = g_build_filename (data_dir, "local", NULL);
|
|
g_object_set (settings, "path", path, NULL);
|
|
if (g_mkdir (path, 0700) == -1)
|
|
g_warning (
|
|
"%s: Failed to make directory '%s': %s",
|
|
G_STRFUNC, path, g_strerror (errno));
|
|
g_free (path);
|
|
|
|
g_object_unref (settings);
|
|
|
|
ms.mail_store = CAMEL_STORE (mbox_service);
|
|
ms.maildir_store = CAMEL_STORE (maildir_service);
|
|
ms.session = session;
|
|
ms.complete = FALSE;
|
|
|
|
thread = g_thread_new (NULL, (GThreadFunc) migrate_stores, &ms);
|
|
/* coverity[loop_condition] */
|
|
while (!ms.complete)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
g_object_unref (mbox_service);
|
|
g_object_unref (maildir_service);
|
|
g_thread_unref (thread);
|
|
|
|
/* Folders can leave notifications in the main loop which would be delivered
|
|
on idle, but these can be left in the main loop longer than the temporary
|
|
CamelSession object is alive, which leads to a crash, because of
|
|
the CamelStore's descendant being freed too early. */
|
|
while (g_main_context_pending (NULL))
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
e_convert_local_mail (EShell *shell)
|
|
{
|
|
CamelSession *session;
|
|
ESource *mbox_source;
|
|
const gchar *user_data_dir;
|
|
const gchar *user_cache_dir;
|
|
gchar *mail_data_dir;
|
|
gchar *mail_cache_dir;
|
|
gchar *local_store;
|
|
gint response;
|
|
|
|
user_data_dir = e_get_user_data_dir ();
|
|
user_cache_dir = e_get_user_cache_dir ();
|
|
|
|
mail_data_dir = g_build_filename (user_data_dir, "mail", NULL);
|
|
mail_cache_dir = g_build_filename (user_cache_dir, "mail", NULL);
|
|
|
|
if (!mail_to_maildir_migration_needed (mail_data_dir))
|
|
goto exit;
|
|
|
|
response = e_alert_run_dialog_for_args (
|
|
e_shell_get_active_window (NULL),
|
|
"mail:ask-migrate-store", NULL);
|
|
|
|
if (response == GTK_RESPONSE_CANCEL)
|
|
exit (EXIT_SUCCESS);
|
|
|
|
mbox_source = e_source_new (NULL, NULL, NULL);
|
|
|
|
rename_mbox_dir (mbox_source, mail_data_dir);
|
|
|
|
local_store = g_build_filename (mail_data_dir, "local", NULL);
|
|
|
|
if (!g_file_test (local_store, G_FILE_TEST_EXISTS))
|
|
g_mkdir_with_parents (local_store, 0700);
|
|
|
|
g_free (local_store);
|
|
|
|
session = g_object_new (
|
|
CAMEL_TYPE_SESSION,
|
|
"online", FALSE,
|
|
"user-data-dir", mail_data_dir,
|
|
"user-cache-dir", mail_cache_dir,
|
|
NULL);
|
|
|
|
migrate_mbox_to_maildir (shell, session, mbox_source);
|
|
|
|
g_object_unref (session);
|
|
|
|
g_object_unref (mbox_source);
|
|
|
|
exit:
|
|
g_free (mail_data_dir);
|
|
g_free (mail_cache_dir);
|
|
}
|