[evolution-data-server] Bug #651399 - [maildir] Escape dots in folder display names
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug #651399 - [maildir] Escape dots in folder display names
- Date: Mon, 28 Jan 2013 18:10:53 +0000 (UTC)
commit d398ddefae08e60053e3374581b432b707d9df21
Author: Milan Crha <mcrha redhat com>
Date: Mon Jan 28 19:09:46 2013 +0100
Bug #651399 - [maildir] Escape dots in folder display names
camel/providers/local/camel-maildir-store.c | 258 ++++++++++++++++++++-------
camel/providers/local/camel-maildir-store.h | 3 +
2 files changed, 196 insertions(+), 65 deletions(-)
---
diff --git a/camel/providers/local/camel-maildir-store.c b/camel/providers/local/camel-maildir-store.c
index 0e757f9..9afec8e 100644
--- a/camel/providers/local/camel-maildir-store.c
+++ b/camel/providers/local/camel-maildir-store.c
@@ -41,6 +41,14 @@
G_DEFINE_TYPE (CamelMaildirStore, camel_maildir_store, CAMEL_TYPE_LOCAL_STORE)
+struct _CamelMaildirStorePrivate {
+ gboolean already_migrated;
+};
+
+/* after the space is a version of the folder structure */
+#define MAILDIR_CONTENT_VERSION_STR "maildir++ 1"
+#define MAILDIR_CONTENT_VERSION 1
+
#define HIER_SEP "."
#define HIER_SEP_CHAR '.'
@@ -54,7 +62,8 @@ 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_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, GCancellable *cancellable, GError **error);
+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);
/* This fixes up some historical cruft of names starting with "./" */
static const gchar *
@@ -82,7 +91,7 @@ maildir_store_create_folder_sync (CamelStore *store,
CamelService *service;
CamelFolder *folder;
CamelFolderInfo *info = NULL;
- gchar *name = NULL;
+ gchar *name = NULL, *fullname = NULL;
gchar *path;
struct stat st;
@@ -105,16 +114,6 @@ maildir_store_create_folder_sync (CamelStore *store,
goto exit;
}
- if (g_strstr_len (folder_name, -1, ".")) {
- g_set_error (
- error, CAMEL_STORE_ERROR,
- CAMEL_STORE_ERROR_NO_FOLDER,
- _("Cannot create folder: %s: "
- "Folder name cannot contain a dot"),
- folder_name);
- goto exit;
- }
-
if (!g_ascii_strcasecmp (folder_name, "Inbox")) {
g_set_error (
error, CAMEL_STORE_ERROR,
@@ -124,13 +123,18 @@ maildir_store_create_folder_sync (CamelStore *store,
}
if (parent_name && *parent_name) {
- gchar *dir_name = maildir_full_name_to_dir_name (parent_name);
- name = g_strdup_printf ("%s/%s.%s", path, dir_name, folder_name);
- g_free (dir_name);
+ fullname = g_strdup_printf ("%s/%s", parent_name, folder_name);
+ name = maildir_full_name_to_dir_name (fullname);
+ g_free (fullname);
} else
name = maildir_full_name_to_dir_name (folder_name);
- if (g_stat (name, &st) == 0 || errno != ENOENT) {
+ fullname = g_build_filename (path, name, NULL);
+
+ g_free (name);
+ name = NULL;
+
+ if (g_stat (fullname, &st) == 0 || errno != ENOENT) {
g_set_error (
error, G_IO_ERROR,
g_io_error_from_errno (errno),
@@ -139,8 +143,8 @@ maildir_store_create_folder_sync (CamelStore *store,
goto exit;
}
- g_free (name);
- name = NULL;
+ g_free (fullname);
+ fullname = NULL;
if (parent_name && *parent_name)
name = g_strdup_printf ("%s/%s", parent_name, folder_name);
@@ -156,6 +160,7 @@ maildir_store_create_folder_sync (CamelStore *store,
}
exit:
+ g_free (fullname);
g_free (name);
g_free (path);
@@ -520,15 +525,31 @@ maildir_full_name_to_dir_name (const gchar *full_name)
{
gchar *path;
- if (g_ascii_strcasecmp (full_name, "Inbox")) {
- if (!g_ascii_strncasecmp (full_name, "Inbox/", 6))
- path = g_strconcat (".", full_name + 5, NULL);
+ if (g_ascii_strcasecmp (full_name, "Inbox") == 0) {
+ path = g_strdup (".");
+ } else {
+ if (g_ascii_strncasecmp (full_name, "Inbox/", 6) == 0)
+ path = g_strconcat ("/", full_name + 5, NULL);
else
- path = g_strconcat (".", full_name, NULL);
+ path = g_strconcat ("/", full_name, NULL);
- g_strdelimit (path + 1, "/", HIER_SEP_CHAR);
- } else
- path = g_strdup (".");
+ if (strchr (path, HIER_SEP_CHAR) || strchr (path, '_')) {
+ GString *tmp = g_string_new ("");
+ const gchar *pp;
+
+ for (pp = path; *pp; pp++) {
+ if (*pp == HIER_SEP_CHAR || *pp == '_')
+ g_string_append_printf (tmp, "_%02X", *pp);
+ else
+ g_string_append_c (tmp, *pp);
+ }
+
+ g_free (path);
+ path = g_string_free (tmp, FALSE);
+ }
+
+ g_strdelimit (path, "/", HIER_SEP_CHAR);
+ }
return path;
}
@@ -545,6 +566,21 @@ maildir_dir_name_to_fullname (const gchar *dir_name)
g_strdelimit (full_name, HIER_SEP, '/');
+ if (strchr (full_name, '_')) {
+ gint ii, jj;
+
+ for (ii = 0, jj = 0; full_name[ii]; ii++, jj++) {
+ if (full_name[ii] == '_' && g_ascii_isxdigit (full_name[ii + 1]) && g_ascii_isxdigit (full_name[ii + 2])) {
+ full_name[jj] = 16 * g_ascii_xdigit_value (full_name[ii + 1]) + g_ascii_xdigit_value (full_name[ii + 2]);
+ ii += 2;
+ } else if (ii != jj) {
+ full_name[jj] = full_name[ii];
+ }
+ }
+
+ full_name[jj] = '\0';
+ }
+
return full_name;
}
@@ -563,7 +599,6 @@ scan_dirs (CamelStore *store,
gint res = -1;
DIR *dir;
struct dirent *d;
- gchar *meta_path = NULL;
gchar *path;
service = CAMEL_SERVICE (store);
@@ -591,11 +626,23 @@ scan_dirs (CamelStore *store,
goto exit;
}
- meta_path = maildir_get_meta_path ((CamelLocalStore *) store, ".", "maildir++");
- if (!g_file_test (meta_path, G_FILE_TEST_EXISTS))
- maildir_migrate_hierarchy ((CamelMaildirStore *) store, cancellable, error);
+ if (!CAMEL_MAILDIR_STORE (store)->priv->already_migrated) {
+ gchar *meta_path = NULL, *ptr;
+ gint maildir_version = 0;
- g_free (meta_path);
+ CAMEL_MAILDIR_STORE (store)->priv->already_migrated = TRUE;
+ meta_path = maildir_get_meta_path ((CamelLocalStore *) store, "?", "maildir++");
+ ptr = strrchr (meta_path, '?');
+ g_return_val_if_fail (ptr != NULL, -1);
+
+ /* cannot pass dot inside maildir_get_meta_path(), because it escapes it */
+ ptr[0] = '.';
+
+ if (maildir_version_requires_migrate (meta_path, &maildir_version))
+ maildir_migrate_hierarchy ((CamelMaildirStore *) store, maildir_version, cancellable, error);
+
+ g_free (meta_path);
+ }
while ((d = readdir (dir))) {
gchar *full_name, *filename;
@@ -790,15 +837,6 @@ maildir_store_rename_folder_sync (CamelStore *store,
return FALSE;
}
- if (g_strstr_len (new, -1, ".")) {
- g_set_error (
- error, CAMEL_STORE_ERROR,
- CAMEL_STORE_ERROR_NO_FOLDER,
- _("Cannot rename the folder: %s: Folder name cannot contain a dot"), new);
- return FALSE;
-
- }
-
if (!g_ascii_strcasecmp (new, "Inbox")) {
g_set_error (
error, CAMEL_STORE_ERROR,
@@ -836,6 +874,8 @@ camel_maildir_store_class_init (CamelMaildirStoreClass *class)
CamelStoreClass *store_class;
CamelLocalStoreClass *local_class;
+ g_type_class_add_private (class, sizeof (CamelMaildirStorePrivate));
+
store_class = CAMEL_STORE_CLASS (class);
store_class->hash_folder_name = maildir_store_hash_folder_name;
store_class->equal_folder_name = maildir_store_equal_folder_name;
@@ -855,6 +895,8 @@ camel_maildir_store_class_init (CamelMaildirStoreClass *class)
static void
camel_maildir_store_init (CamelMaildirStore *maildir_store)
{
+ maildir_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (maildir_store, CAMEL_TYPE_MAILDIR_STORE, CamelMaildirStorePrivate);
+ maildir_store->priv->already_migrated = FALSE;
}
static gchar *
@@ -928,21 +970,27 @@ struct _scan_node {
ino_t inode;
};
-static guint scan_hash (gconstpointer d)
+static guint
+scan_hash (gconstpointer d)
{
const struct _scan_node *v = d;
return v->inode ^ v->dnode;
}
-static gboolean scan_equal (gconstpointer a, gconstpointer b)
+static gboolean
+scan_equal (gconstpointer a,
+ gconstpointer b)
{
const struct _scan_node *v1 = a, *v2 = b;
return v1->inode == v2->inode && v1->dnode == v2->dnode;
}
-static void scan_free (gpointer k, gpointer v, gpointer d)
+static void
+scan_free (gpointer k,
+ gpointer v,
+ gpointer d)
{
g_free (k);
}
@@ -1066,51 +1114,124 @@ exit:
}
static void
-maildir_rename_old_folder (CamelMaildirStore *mstore,
- CamelFolderInfo *fi,
- GCancellable *cancellable,
- GError **error)
+maildir_maybe_rename_old_folder (CamelMaildirStore *mstore,
+ CamelFolderInfo *fi,
+ gint maildir_version,
+ GCancellable *cancellable,
+ GError **error)
{
- gchar *new_name = NULL, *old_name;
- CamelStoreClass *store_class;
+ gchar *new_name = NULL;
+
+ 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);
+ } 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
+ the '.' is already garbled;
+ fi->full_name is a dir name here
+ */
+ gchar *full_name;
+
+ if (!g_ascii_strncasecmp (fi->full_name, "..", 2))
+ full_name = g_strconcat ("Inbox/", fi->full_name + 2, NULL);
+ else if (fi->full_name[0] == '.')
+ full_name = g_strdup (fi->full_name + 1);
+ else
+ full_name = g_strdup (fi->full_name);
- old_name = g_strdup (fi->full_name);
- g_strdelimit (old_name, ".", '_');
- new_name = maildir_full_name_to_dir_name (old_name);
+ g_strdelimit (full_name, HIER_SEP, '/');
- store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
- store_class->rename_folder_sync (
- (CamelStore *) mstore, fi->full_name, new_name, cancellable, error);
+ new_name = maildir_full_name_to_dir_name (full_name);
+
+ g_free (full_name);
+ } else {
+ return;
+ }
+
+ if (!g_str_equal (fi->full_name, new_name)) {
+ CamelStoreClass *store_class;
+
+ store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
+ store_class->rename_folder_sync ((CamelStore *) mstore, fi->full_name, new_name, cancellable, error);
+ }
- g_free (old_name);
g_free (new_name);
}
static void
traverse_rename_folder_info (CamelMaildirStore *mstore,
CamelFolderInfo *fi,
+ gint maildir_version,
GCancellable *cancellable,
GError **error)
{
- if (fi != NULL) {
+ while (fi != NULL) {
if (fi->child)
- traverse_rename_folder_info (mstore, fi->child, cancellable, error);
+ traverse_rename_folder_info (mstore, fi->child, maildir_version, cancellable, error);
- if (strcmp (fi->full_name, ".") && ((!g_str_has_prefix (fi->full_name, ".") && (!fi->parent || !strcmp (fi->parent->full_name, "."))) ||
- (fi->parent && strcmp (fi->parent->full_name, "."))))
- maildir_rename_old_folder (mstore, fi, cancellable, error);
+ maildir_maybe_rename_old_folder (mstore, fi, maildir_version, cancellable, error);
- traverse_rename_folder_info (mstore, fi->next, cancellable, error);
+ fi = fi->next;
}
}
+static gboolean
+maildir_version_requires_migrate (const gchar *meta_filename,
+ gint *maildir_version)
+{
+ FILE *metafile;
+ gchar cc;
+ gint verpos = 0;
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (meta_filename != NULL, FALSE);
+ g_return_val_if_fail (maildir_version != NULL, FALSE);
+
+ /* nonexistent file is -1 */
+ *maildir_version = -1;
+
+ if (!g_file_test (meta_filename, G_FILE_TEST_EXISTS))
+ return TRUE;
+
+ /* existing file without version is 0 */
+ *maildir_version = 0;
+
+ metafile = fopen (meta_filename, "rb");
+ if (!metafile)
+ return FALSE;
+
+ while (cc = fgetc (metafile), !res && !feof (metafile)) {
+ if (verpos > 1 && MAILDIR_CONTENT_VERSION_STR[verpos - 1] == ' ') {
+ if (cc >= '0' && cc <= '9') {
+ (*maildir_version) = (*maildir_version) * 10 + cc - '0';
+ } else if (cc == ' ' || cc == '\n' || cc == '\r' || cc == '\t') {
+ break;
+ } else {
+ res = TRUE;
+ }
+ } else if (cc == MAILDIR_CONTENT_VERSION_STR[verpos]) {
+ verpos++;
+ } else {
+ res = TRUE;
+ }
+ }
+
+ fclose (metafile);
+
+ return res || (*maildir_version) < MAILDIR_CONTENT_VERSION;
+}
+
static void
maildir_migrate_hierarchy (CamelMaildirStore *mstore,
+ gint maildir_version,
GCancellable *cancellable,
GError **error)
{
CamelFolderInfo *topfi;
- gchar *meta_path;
+ gchar *meta_path, *ptr;
topfi = camel_folder_info_new ();
topfi->full_name = g_strdup (".");
@@ -1122,10 +1243,17 @@ maildir_migrate_hierarchy (CamelMaildirStore *mstore,
return;
}
- traverse_rename_folder_info (mstore, topfi, cancellable, error);
+ 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);
+
+ /* cannot pass dot inside maildir_get_meta_path(), because it is escaped */
+ ptr[0] = '.';
- meta_path = maildir_get_meta_path ((CamelLocalStore *) mstore, ".", "maildir++");
- g_file_set_contents (meta_path, "maildir++", -1, NULL);
+ g_file_set_contents (meta_path, MAILDIR_CONTENT_VERSION_STR, -1, NULL);
camel_folder_info_free (topfi);
g_free (meta_path);
diff --git a/camel/providers/local/camel-maildir-store.h b/camel/providers/local/camel-maildir-store.h
index 50183e9..6b954ab 100644
--- a/camel/providers/local/camel-maildir-store.h
+++ b/camel/providers/local/camel-maildir-store.h
@@ -47,9 +47,12 @@ G_BEGIN_DECLS
typedef struct _CamelMaildirStore CamelMaildirStore;
typedef struct _CamelMaildirStoreClass CamelMaildirStoreClass;
+typedef struct _CamelMaildirStorePrivate CamelMaildirStorePrivate;
struct _CamelMaildirStore {
CamelLocalStore parent;
+
+ CamelMaildirStorePrivate *priv;
};
struct _CamelMaildirStoreClass {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]