[evolution-data-server] Bug 732145 - Breaks existing (not evolution's) maildir folder structure



commit f3094bd99feadd375ad25db7c11b98c5de84f533
Author: Milan Crha <mcrha redhat com>
Date:   Fri Feb 6 12:35:23 2015 +0100

    Bug 732145 - Breaks existing (not evolution's) maildir folder structure

 camel/providers/local/camel-local-store.c   |   11 ++-
 camel/providers/local/camel-maildir-store.c |  116 +++++++++++++++++++-------
 2 files changed, 91 insertions(+), 36 deletions(-)
---
diff --git a/camel/providers/local/camel-local-store.c b/camel/providers/local/camel-local-store.c
index 2b9e3d2..2362381 100644
--- a/camel/providers/local/camel-local-store.c
+++ b/camel/providers/local/camel-local-store.c
@@ -580,11 +580,12 @@ summary_failed:
        } else
                camel_text_index_rename (newibex, oldibex);
 ibex_failed:
-       g_set_error (
-               error, G_IO_ERROR,
-               g_io_error_from_errno (errno),
-               _("Could not rename '%s': %s"),
-               old, g_strerror (errno));
+       if (error && !*error)
+               g_set_error (
+                       error, G_IO_ERROR,
+                       g_io_error_from_errno (errno),
+                       _("Could not rename '%s': %s"),
+                       old, g_strerror (errno));
 
        g_free (newibex);
        g_free (oldibex);
diff --git a/camel/providers/local/camel-maildir-store.c b/camel/providers/local/camel-maildir-store.c
index fc1cbb9..a2a6b04 100644
--- a/camel/providers/local/camel-maildir-store.c
+++ b/camel/providers/local/camel-maildir-store.c
@@ -57,6 +57,7 @@
 
 struct _CamelMaildirStorePrivate {
        gboolean already_migrated;
+       gboolean can_escape_dots;
 };
 
 static CamelFolder * maildir_store_get_folder_sync (CamelStore *store, const gchar *folder_name, 
CamelStoreGetFolderFlags flags,
@@ -65,12 +66,12 @@ static CamelFolderInfo *maildir_store_create_folder_sync (CamelStore *store, con
                                                GCancellable *cancellable, GError **error);
 static gboolean maildir_store_delete_folder_sync (CamelStore * store, const gchar *folder_name, GCancellable 
*cancellable, GError **error);
 
-static gchar *maildir_full_name_to_dir_name (const gchar *full_name);
-static gchar *maildir_dir_name_to_fullname (const gchar *dir_name);
+static gchar *maildir_full_name_to_dir_name (gboolean can_escape_dots, const gchar *full_name);
+static gchar *maildir_dir_name_to_fullname (gboolean can_escape_dots, const gchar *dir_name);
 static gchar *maildir_get_full_path (CamelLocalStore *ls, const gchar *full_name);
 static gchar *maildir_get_meta_path (CamelLocalStore *ls, const gchar *full_name, const gchar *ext);
 static void maildir_migrate_hierarchy (CamelMaildirStore *mstore, gint maildir_version, GCancellable 
*cancellable, GError **error);
-static gboolean maildir_version_requires_migrate (const gchar *meta_filename, gint *maildir_version);
+static gboolean maildir_version_requires_migrate (const gchar *meta_filename, gboolean *file_exists, gint 
*maildir_version);
 
 G_DEFINE_TYPE (CamelMaildirStore, camel_maildir_store, CAMEL_TYPE_LOCAL_STORE)
 
@@ -123,6 +124,14 @@ maildir_store_create_folder_sync (CamelStore *store,
                goto exit;
        }
 
+       if (folder_name && !CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots && strchr (folder_name, 
HIER_SEP_CHAR)) {
+               g_set_error (
+                       error, CAMEL_STORE_ERROR,
+                       CAMEL_STORE_ERROR_INVALID,
+                       _("Cannot create folder containing '%s'"), HIER_SEP);
+               goto exit;
+       }
+
        if ((!parent_name || !*parent_name) && !g_ascii_strcasecmp (folder_name, "Inbox")) {
                g_set_error (
                        error, CAMEL_STORE_ERROR,
@@ -133,10 +142,10 @@ maildir_store_create_folder_sync (CamelStore *store,
 
        if (parent_name && *parent_name) {
                fullname = g_strdup_printf ("%s/%s", parent_name, folder_name);
-               name = maildir_full_name_to_dir_name (fullname);
+               name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
fullname);
                g_free (fullname);
        } else
-               name = maildir_full_name_to_dir_name (folder_name);
+               name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
folder_name);
 
        fullname = g_build_filename (path, name, NULL);
 
@@ -208,7 +217,7 @@ maildir_store_get_folder_sync (CamelStore *store,
        g_object_unref (settings);
 
        folder_name = md_canon_name (folder_name);
-       dir_name = maildir_full_name_to_dir_name (folder_name);
+       dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
folder_name);
 
        /* maildir++ directory names start with a '.' */
        name = g_build_filename (path, dir_name, NULL);
@@ -333,7 +342,7 @@ maildir_store_delete_folder_sync (CamelStore *store,
        g_object_unref (settings);
 
        /* maildir++ directory names start with a '.' */
-       dir_name = maildir_full_name_to_dir_name (folder_name);
+       dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
folder_name);
        name = g_build_filename (path, dir_name, NULL);
        g_free (dir_name);
 
@@ -443,7 +452,7 @@ fill_fi (CamelStore *store,
                g_object_unref (settings);
 
                /* This should be fast enough not to have to test for INFO_FAST */
-               dir_name = maildir_full_name_to_dir_name (fi->full_name);
+               dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
fi->full_name);
 
                if (!strcmp (dir_name, "."))
                        folderpath = g_strdup (root);
@@ -505,7 +514,7 @@ scan_fi (CamelStore *store,
        if (((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) != 0))
                fi->flags = CAMEL_FOLDER_NOCHILDREN;
 
-       dir_name = maildir_full_name_to_dir_name (fi->full_name);
+       dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, 
fi->full_name);
        d (printf ("Adding maildir info: '%s' '%s' '%s'\n", fi->name, dir_name, fi->uri));
 
        tmp = g_build_filename (path, dir_name, "tmp", NULL);
@@ -533,7 +542,8 @@ scan_fi (CamelStore *store,
 
 /* Folder names begin with a dot */
 static gchar *
-maildir_full_name_to_dir_name (const gchar *full_name)
+maildir_full_name_to_dir_name (gboolean can_escape_dots,
+                              const gchar *full_name)
 {
        gchar *path;
 
@@ -545,7 +555,7 @@ maildir_full_name_to_dir_name (const gchar *full_name)
                else
                        path = g_strconcat ("/", full_name, NULL);
 
-               if (strchr (path, HIER_SEP_CHAR) || strchr (path, '_')) {
+               if (can_escape_dots && (strchr (path, HIER_SEP_CHAR) || strchr (path, '_'))) {
                        GString *tmp = g_string_new ("");
                        const gchar *pp;
 
@@ -567,7 +577,8 @@ maildir_full_name_to_dir_name (const gchar *full_name)
 }
 
 static gchar *
-maildir_dir_name_to_fullname (const gchar *dir_name)
+maildir_dir_name_to_fullname (gboolean can_escape_dots,
+                             const gchar *dir_name)
 {
        gchar *full_name;
 
@@ -578,7 +589,7 @@ maildir_dir_name_to_fullname (const gchar *dir_name)
 
        g_strdelimit (full_name, HIER_SEP, '/');
 
-       if (strchr (full_name, '_')) {
+       if (can_escape_dots && strchr (full_name, '_')) {
                gint ii, jj;
 
                for (ii = 0, jj = 0; full_name[ii]; ii++, jj++) {
@@ -605,6 +616,7 @@ scan_dirs (CamelStore *store,
            GError **error)
 {
        CamelLocalSettings *local_settings;
+       CamelMaildirStore *maildir_store;
        CamelSettings *settings;
        CamelService *service;
        CamelFolderInfo *fi;
@@ -615,6 +627,7 @@ scan_dirs (CamelStore *store,
        gchar *path;
 
        service = CAMEL_SERVICE (store);
+       maildir_store = CAMEL_MAILDIR_STORE (store);
 
        settings = camel_service_ref_settings (service);
 
@@ -639,11 +652,12 @@ scan_dirs (CamelStore *store,
                goto exit;
        }
 
-       if (!CAMEL_MAILDIR_STORE (store)->priv->already_migrated) {
+       if (!maildir_store->priv->already_migrated &&
+           maildir_store->priv->can_escape_dots) {
                gchar *meta_path = NULL, *ptr;
                gint maildir_version = 0;
+               gboolean file_exists = FALSE, requires_migrate;
 
-               CAMEL_MAILDIR_STORE (store)->priv->already_migrated = TRUE;
                meta_path = maildir_get_meta_path ((CamelLocalStore *) store, "?", "maildir++");
                ptr = strrchr (meta_path, '?');
                if (!ptr) {
@@ -654,10 +668,22 @@ scan_dirs (CamelStore *store,
                        goto exit;
                }
 
+               maildir_store->priv->already_migrated = TRUE;
+
+               /* Do not migrate folders out of user's data data, which is completely
+                  handled by Camel/Evolution, thus these tweaks can be done there. */
+               maildir_store->priv->can_escape_dots = g_str_has_prefix (meta_path, 
camel_service_get_user_data_dir (service));
+
                /* cannot pass dot inside maildir_get_meta_path(), because it escapes it */
                ptr[0] = '.';
 
-               if (maildir_version_requires_migrate (meta_path, &maildir_version))
+               requires_migrate = maildir_version_requires_migrate (meta_path, &file_exists, 
&maildir_version);
+               if (file_exists) {
+                       /* Users can enable dot escaping by adding ..maildir++ file into the root Maildir 
folder */
+                       maildir_store->priv->can_escape_dots = TRUE;
+               }
+
+               if (requires_migrate && maildir_store->priv->can_escape_dots)
                        maildir_migrate_hierarchy ((CamelMaildirStore *) store, maildir_version, cancellable, 
error);
 
                g_free (meta_path);
@@ -684,7 +710,7 @@ scan_dirs (CamelStore *store,
                        continue;
                }
                g_free (filename);
-               full_name = maildir_dir_name_to_fullname (d->d_name);
+               full_name = maildir_dir_name_to_fullname (maildir_store->priv->can_escape_dots, d->d_name);
                short_name = strrchr (full_name, '/');
                if (!short_name)
                        short_name = full_name;
@@ -833,8 +859,8 @@ rename_traverse_fi (CamelStore *store,
                        gchar *new_full_name, *old_dir, *new_dir;
 
                        new_full_name = g_strconcat (new_full_name_prefix, fi->full_name + old_prefix_len, 
NULL);
-                       old_dir = maildir_full_name_to_dir_name (fi->full_name);
-                       new_dir = maildir_full_name_to_dir_name (new_full_name);
+                       old_dir = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE 
(store)->priv->can_escape_dots, fi->full_name);
+                       new_dir = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE 
(store)->priv->can_escape_dots, new_full_name);
 
                        /* Chain up to parent's rename_folder_sync() method. */
                        ret = store_class->rename_folder_sync (store, old_dir, new_dir, cancellable, error);
@@ -882,14 +908,22 @@ maildir_store_rename_folder_sync (CamelStore *store,
                return FALSE;
        }
 
+       if (new && !CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots && strchr (new, HIER_SEP_CHAR)) {
+               g_set_error (
+                       error, CAMEL_STORE_ERROR,
+                       CAMEL_STORE_ERROR_INVALID,
+                       _("Cannot create folder containing '%s'"), HIER_SEP);
+               return FALSE;
+       }
+
        subfolders = maildir_store_get_folder_info_sync (
                store, old,
                CAMEL_STORE_FOLDER_INFO_RECURSIVE |
                CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL,
                cancellable, NULL);
 
-       old_dir = maildir_full_name_to_dir_name (old);
-       new_dir = maildir_full_name_to_dir_name (new);
+       old_dir = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, old);
+       new_dir = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (store)->priv->can_escape_dots, new);
 
        /* Chain up to parent's rename_folder_sync() method. */
        store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
@@ -941,6 +975,7 @@ camel_maildir_store_init (CamelMaildirStore *maildir_store)
 {
        maildir_store->priv = CAMEL_MAILDIR_STORE_GET_PRIVATE (maildir_store);
        maildir_store->priv->already_migrated = FALSE;
+       maildir_store->priv->can_escape_dots = TRUE;
 }
 
 static gchar *
@@ -963,7 +998,7 @@ maildir_get_full_path (CamelLocalStore *ls,
 
        g_object_unref (settings);
 
-       dir_name = maildir_full_name_to_dir_name (full_name);
+       dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (ls)->priv->can_escape_dots, full_name);
        filename = g_build_filename (path, dir_name, NULL);
        g_free (dir_name);
 
@@ -994,7 +1029,7 @@ maildir_get_meta_path (CamelLocalStore *ls,
 
        g_object_unref (settings);
 
-       dir_name = maildir_full_name_to_dir_name (full_name);
+       dir_name = maildir_full_name_to_dir_name (CAMEL_MAILDIR_STORE (ls)->priv->can_escape_dots, full_name);
        tmp = g_build_filename (path, dir_name, NULL);
        filename = g_strconcat (tmp, ext, NULL);
        g_free (tmp);
@@ -1166,11 +1201,14 @@ maildir_maybe_rename_old_folder (CamelMaildirStore *mstore,
 {
        gchar *new_name = NULL;
 
+       if (g_str_equal (fi->full_name, ".") || g_str_equal (fi->full_name, ".."))
+               return;
+
        if (maildir_version == -1) {
                /* this is when maildir was not converted yet to maildir++ at all,
                 * the '_' and '.'  are still there and the dir separator is slash
                */
-               new_name = maildir_full_name_to_dir_name (fi->full_name);
+               new_name = maildir_full_name_to_dir_name (mstore->priv->can_escape_dots, fi->full_name);
        } else if (maildir_version == 0) {
                /* this is a conversion with maildir folder being already there,
                 * only with no version; there should be escaped only '_', because
@@ -1188,7 +1226,7 @@ maildir_maybe_rename_old_folder (CamelMaildirStore *mstore,
 
                g_strdelimit (full_name, HIER_SEP, '/');
 
-               new_name = maildir_full_name_to_dir_name (full_name);
+               new_name = maildir_full_name_to_dir_name (mstore->priv->can_escape_dots, full_name);
 
                g_free (full_name);
        } else {
@@ -1197,9 +1235,16 @@ maildir_maybe_rename_old_folder (CamelMaildirStore *mstore,
 
        if (!g_str_equal (fi->full_name, new_name)) {
                CamelStoreClass *store_class;
+               GError *local_error = NULL;
 
                store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
-               store_class->rename_folder_sync ((CamelStore *) mstore, fi->full_name, new_name, cancellable, 
error);
+               /* Do not propagate these errors, only warn about them on console */
+               store_class->rename_folder_sync ((CamelStore *) mstore, fi->full_name, new_name, cancellable, 
&local_error);
+
+               if (local_error) {
+                       g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, fi->full_name, 
new_name, local_error->message);
+                       g_error_free (local_error);
+               }
        }
 
        g_free (new_name);
@@ -1224,6 +1269,7 @@ traverse_rename_folder_info (CamelMaildirStore *mstore,
 
 static gboolean
 maildir_version_requires_migrate (const gchar *meta_filename,
+                                 gboolean *file_exists,
                                   gint *maildir_version)
 {
        FILE *metafile;
@@ -1232,16 +1278,19 @@ maildir_version_requires_migrate (const gchar *meta_filename,
        gboolean res = FALSE;
 
        g_return_val_if_fail (meta_filename != NULL, FALSE);
+       g_return_val_if_fail (file_exists != NULL, FALSE);
        g_return_val_if_fail (maildir_version != NULL, FALSE);
 
        /* nonexistent file is -1 */
        *maildir_version = -1;
+       *file_exists = FALSE;
 
        if (!g_file_test (meta_filename, G_FILE_TEST_EXISTS))
                return TRUE;
 
        /* existing file without version is 0 */
        *maildir_version = 0;
+       *file_exists = TRUE;
 
        metafile = fopen (meta_filename, "rb");
        if (!metafile)
@@ -1277,18 +1326,17 @@ maildir_migrate_hierarchy (CamelMaildirStore *mstore,
        CamelFolderInfo *topfi;
        gchar *meta_path = NULL, *ptr;
 
+       g_return_if_fail (mstore->priv->can_escape_dots);
+
        topfi = camel_folder_info_new ();
        topfi->full_name = g_strdup (".");
        topfi->display_name = g_strdup ("Inbox");
 
        if (scan_old_dir_info ((CamelStore *) mstore, topfi, error) == -1) {
-               g_warning ("Failed to scan the old folder info \n");
+               g_warning ("%s: Failed to scan the old folder info", G_STRFUNC);
                goto done;
        }
 
-       if (maildir_version < 1)
-               traverse_rename_folder_info (mstore, topfi, maildir_version, cancellable, error);
-
        meta_path = maildir_get_meta_path ((CamelLocalStore *) mstore, "?", "maildir++");
        ptr = strrchr (meta_path, '?');
        g_return_if_fail (ptr != NULL);
@@ -1296,11 +1344,17 @@ maildir_migrate_hierarchy (CamelMaildirStore *mstore,
        /* cannot pass dot inside maildir_get_meta_path(), because it is escaped */
        ptr[0] = '.';
 
-       if (!g_file_set_contents (meta_path, MAILDIR_CONTENT_VERSION_STR, -1, error)) {
+       /* First create/overwrite the file, only then do the migration, otherwise,
+          if the file creation fails, the folder structure would be migrated repeatedly. */
+       if (!g_file_set_contents (meta_path, MAILDIR_CONTENT_VERSION_STR, -1, error) || (error && *error)) {
                g_warning ("Failed to save the maildir version in ā€˜%sā€™.", meta_path);
                goto done;
        }
 
+       if (maildir_version < 1) {
+               traverse_rename_folder_info (mstore, topfi, maildir_version, cancellable, error);
+       }
+
 done:
        camel_folder_info_free (topfi);
        g_free (meta_path);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]