1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
31 #include <sys/types.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n-lib.h>
36 #include "camel-maildir-folder.h"
37 #include "camel-maildir-store.h"
38 #include "camel-maildir-summary.h"
42 G_DEFINE_TYPE (CamelMaildirStore, camel_maildir_store, CAMEL_TYPE_LOCAL_STORE)
45 #define HIER_SEP_CHAR '.'
47 static CamelFolder * maildir_store_get_folder_sync (CamelStore *store, const gchar *folder_name, CamelStoreGetFolderFlags flags,
48 GCancellable *cancellable, GError **error);
49 static CamelFolderInfo *maildir_store_create_folder_sync (CamelStore *store, const gchar *parent_name, const gchar *folder_name,
50 GCancellable *cancellable, GError **error);
51 static gboolean maildir_store_delete_folder_sync (CamelStore * store, const gchar *folder_name, GCancellable *cancellable, GError **error);
53 static gchar *maildir_full_name_to_dir_name (const gchar *full_name);
54 static gchar *maildir_dir_name_to_fullname (const gchar *dir_name);
55 static gchar *maildir_get_full_path (CamelLocalStore *ls, const gchar *full_name);
56 static gchar *maildir_get_meta_path (CamelLocalStore *ls, const gchar *full_name, const gchar *ext);
57 static void maildir_migrate_hierarchy (CamelMaildirStore *mstore, GCancellable *cancellable, GError **error);
59 /* This fixes up some historical cruft of names starting with "./" */
61 md_canon_name (const gchar *a)
66 if (a[0] == '.' && a[1] == '/')
73 static CamelFolderInfo *
74 maildir_store_create_folder_sync (CamelStore *store,
75 const gchar *parent_name,
76 const gchar *folder_name,
77 GCancellable *cancellable,
80 CamelLocalSettings *local_settings;
81 CamelSettings *settings;
82 CamelService *service;
84 CamelFolderInfo *info = NULL;
89 /* This is a pretty hacky version of create folder, but should basically work */
91 service = CAMEL_SERVICE (store);
92 settings = camel_service_get_settings (service);
94 local_settings = CAMEL_LOCAL_SETTINGS (settings);
95 path = camel_local_settings_dup_path (local_settings);
97 if (!g_path_is_absolute (path)) {
99 error, CAMEL_STORE_ERROR,
100 CAMEL_STORE_ERROR_NO_FOLDER,
101 _("Store root %s is not an absolute path"), path);
105 if (g_strstr_len (folder_name, -1, ".")) {
107 error, CAMEL_STORE_ERROR,
108 CAMEL_STORE_ERROR_NO_FOLDER,
109 _("Cannot create folder: %s: "
110 "Folder name cannot contain a dot"),
115 if (!g_ascii_strcasecmp (folder_name, "Inbox")) {
117 error, CAMEL_STORE_ERROR,
118 CAMEL_STORE_ERROR_NO_FOLDER,
119 _("Folder %s already exists"), folder_name);
123 if (parent_name && *parent_name) {
124 gchar *dir_name = maildir_full_name_to_dir_name (parent_name);
125 name = g_strdup_printf("%s/%s.%s", path, dir_name, folder_name);
128 name = maildir_full_name_to_dir_name (folder_name);
130 if (g_stat (name, &st) == 0 || errno != ENOENT) {
133 g_io_error_from_errno (errno),
134 _("Cannot get folder: %s: %s"),
135 name, g_strerror (errno));
142 if (parent_name && *parent_name)
143 name = g_strdup_printf("%s/%s", parent_name, folder_name);
145 name = g_strdup_printf("%s", folder_name);
147 folder = maildir_store_get_folder_sync (
148 store, name, CAMEL_STORE_FOLDER_CREATE, cancellable, error);
150 g_object_unref (folder);
151 info = CAMEL_STORE_GET_CLASS (store)->get_folder_info_sync (
152 store, name, 0, cancellable, error);
163 maildir_store_get_folder_sync (CamelStore *store,
164 const gchar *folder_name,
165 CamelStoreGetFolderFlags flags,
166 GCancellable *cancellable,
169 CamelStoreClass *store_class;
170 CamelLocalSettings *local_settings;
171 CamelSettings *settings;
172 CamelService *service;
173 gchar *name, *tmp, *cur, *new, *dir_name;
176 CamelFolder *folder = NULL;
178 service = CAMEL_SERVICE (store);
179 settings = camel_service_get_settings (service);
181 local_settings = CAMEL_LOCAL_SETTINGS (settings);
182 path = camel_local_settings_dup_path (local_settings);
184 folder_name = md_canon_name (folder_name);
185 dir_name = maildir_full_name_to_dir_name (folder_name);
187 /* maildir++ directory names start with a '.' */
188 name = g_build_filename (path, dir_name, NULL);
193 /* Chain up to parent's get_folder() method. */
194 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
195 if (!store_class->get_folder_sync (store, dir_name, flags, cancellable, error)) {
200 tmp = g_strdup_printf ("%s/tmp", name);
201 cur = g_strdup_printf ("%s/cur", name);
202 new = g_strdup_printf ("%s/new", name);
204 if (!g_ascii_strcasecmp (folder_name, "Inbox")) {
205 /* special case "." (aka inbox), may need to be created */
206 if (g_stat (tmp, &st) != 0 || !S_ISDIR (st.st_mode)
207 || g_stat (cur, &st) != 0 || !S_ISDIR (st.st_mode)
208 || g_stat (new, &st) != 0 || !S_ISDIR (st.st_mode)) {
209 if (g_mkdir (tmp, 0700) != 0
210 || g_mkdir (cur, 0700) != 0
211 || g_mkdir (new, 0700) != 0) {
214 g_io_error_from_errno (errno),
215 _("Cannot create folder '%s': %s"),
216 folder_name, g_strerror (errno));
223 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
224 } else if (g_stat (name, &st) == -1) {
225 /* folder doesn't exist, see if we should create it */
226 if (errno != ENOENT) {
229 g_io_error_from_errno (errno),
230 _("Cannot get folder '%s': %s"),
231 folder_name, g_strerror (errno));
232 } else if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
234 error, CAMEL_STORE_ERROR,
235 CAMEL_STORE_ERROR_NO_FOLDER,
236 _("Cannot get folder '%s': folder does not exist."),
239 if (g_mkdir (name, 0700) != 0
240 || g_mkdir (tmp, 0700) != 0
241 || g_mkdir (cur, 0700) != 0
242 || g_mkdir (new, 0700) != 0) {
245 g_io_error_from_errno (errno),
246 _("Cannot create folder '%s': %s"),
247 folder_name, g_strerror (errno));
253 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
256 } else if (!S_ISDIR (st.st_mode)
257 || g_stat (tmp, &st) != 0 || !S_ISDIR (st.st_mode)
258 || g_stat (cur, &st) != 0 || !S_ISDIR (st.st_mode)
259 || g_stat (new, &st) != 0 || !S_ISDIR (st.st_mode)) {
260 /* folder exists, but not maildir */
262 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
263 _("Cannot get folder '%s': not a maildir directory."),
265 } else if (flags & CAMEL_STORE_FOLDER_EXCL) {
267 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
268 _("Cannot create folder '%s': folder exists."),
271 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
283 maildir_store_delete_folder_sync (CamelStore *store,
284 const gchar *folder_name,
285 GCancellable *cancellable,
288 CamelLocalSettings *local_settings;
289 CamelSettings *settings;
290 CamelService *service;
291 gchar *name, *tmp, *cur, *new, *dir_name;
294 gboolean success = TRUE;
296 if (g_ascii_strcasecmp (folder_name, "Inbox") == 0) {
298 error, CAMEL_STORE_ERROR,
299 CAMEL_STORE_ERROR_NO_FOLDER,
300 _("Cannot delete folder: %s: Invalid operation"),
305 service = CAMEL_SERVICE (store);
306 settings = camel_service_get_settings (service);
308 local_settings = CAMEL_LOCAL_SETTINGS (settings);
309 path = camel_local_settings_dup_path (local_settings);
311 /* maildir++ directory names start with a '.' */
312 dir_name = maildir_full_name_to_dir_name (folder_name);
313 name = g_build_filename (path, dir_name, NULL);
318 tmp = g_strdup_printf ("%s/tmp", name);
319 cur = g_strdup_printf ("%s/cur", name);
320 new = g_strdup_printf ("%s/new", name);
322 if (g_stat (name, &st) == -1 || !S_ISDIR (st.st_mode)
323 || g_stat (tmp, &st) == -1 || !S_ISDIR (st.st_mode)
324 || g_stat (cur, &st) == -1 || !S_ISDIR (st.st_mode)
325 || g_stat (new, &st) == -1 || !S_ISDIR (st.st_mode)) {
328 g_io_error_from_errno (errno),
329 _("Could not delete folder '%s': %s"),
330 folder_name, errno ? g_strerror (errno) :
331 _("not a maildir directory"));
335 /* remove subdirs first - will fail if not empty */
336 if (rmdir (cur) == -1 || rmdir (new) == -1) {
342 /* for tmp (only), its contents is irrelevant */
345 while ((d = readdir (dir))) {
346 gchar *name = d->d_name, *file;
348 if (!strcmp(name, ".") || !strcmp(name, ".."))
350 file = g_strdup_printf("%s/%s", tmp, name);
356 if (rmdir (tmp) == -1 || rmdir (name) == -1)
361 /* easier just to mkdir all (and let them fail), than remember what we got to */
362 g_mkdir (name, 0700);
368 g_io_error_from_errno (err),
369 _("Could not delete folder '%s': %s"),
370 folder_name, g_strerror (err));
372 CamelStoreClass *store_class;
374 /* Chain up to parent's delete_folder() method. */
375 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
376 success = store_class->delete_folder_sync (
377 store, folder_name, cancellable, error);
390 fill_fi (CamelStore *store,
393 GCancellable *cancellable)
397 folder = camel_object_bag_peek (store->folders, fi->full_name);
399 if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0)
400 camel_folder_refresh_info_sync (folder, cancellable, NULL);
401 fi->unread = camel_folder_get_unread_message_count (folder);
402 fi->total = camel_folder_get_message_count (folder);
403 g_object_unref (folder);
405 CamelLocalSettings *local_settings;
406 CamelSettings *settings;
407 CamelService *service;
408 gchar *folderpath, *dir_name;
409 CamelFolderSummary *s;
412 service = CAMEL_SERVICE (store);
413 settings = camel_service_get_settings (service);
415 local_settings = CAMEL_LOCAL_SETTINGS (settings);
416 root = camel_local_settings_dup_path (local_settings);
418 /* This should be fast enough not to have to test for INFO_FAST */
419 dir_name = maildir_full_name_to_dir_name (fi->full_name);
421 if (!strcmp (dir_name, "."))
422 folderpath = g_strdup (root);
424 folderpath = g_build_filename (root, dir_name, NULL);
428 s = (CamelFolderSummary *) camel_maildir_summary_new (NULL, folderpath, NULL);
429 if (camel_folder_summary_header_load_from_db (s, store, fi->full_name, NULL)) {
430 fi->unread = camel_folder_summary_get_unread_count (s);
431 fi->total = camel_folder_summary_get_saved_count (s);
438 if (camel_local_store_is_main_store (CAMEL_LOCAL_STORE (store)) && fi->full_name
439 && (fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_NORMAL)
440 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK)
441 | camel_local_store_get_folder_type_by_full_name (CAMEL_LOCAL_STORE (store), fi->full_name);
444 static CamelFolderInfo *
445 scan_fi (CamelStore *store,
449 GCancellable *cancellable)
451 CamelLocalSettings *local_settings;
452 CamelSettings *settings;
453 CamelService *service;
455 gchar *tmp, *cur, *new, *dir_name;
459 service = CAMEL_SERVICE (store);
460 settings = camel_service_get_settings (service);
462 local_settings = CAMEL_LOCAL_SETTINGS (settings);
463 path = camel_local_settings_dup_path (local_settings);
464 g_return_val_if_fail (path != NULL, NULL);
466 fi = camel_folder_info_new ();
467 fi->full_name = g_strdup (full);
468 fi->display_name = g_strdup (name);
473 /* we only calculate nochildren properly if we're recursive */
474 if (((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) != 0))
475 fi->flags = CAMEL_FOLDER_NOCHILDREN;
477 dir_name = maildir_full_name_to_dir_name (fi->full_name);
478 d(printf("Adding maildir info: '%s' '%s' '%s'\n", fi->name, dir_name, fi->uri));
480 tmp = g_build_filename (path, dir_name, "tmp", NULL);
481 cur = g_build_filename (path, dir_name, "cur", NULL);
482 new = g_build_filename (path, dir_name, "new", NULL);
484 if (!(g_stat (tmp, &st) == 0 && S_ISDIR (st.st_mode)
485 && g_stat (cur, &st) == 0 && S_ISDIR (st.st_mode)
486 && g_stat (new, &st) == 0 && S_ISDIR (st.st_mode)))
487 fi->flags |= CAMEL_FOLDER_NOSELECT;
494 fill_fi (store, fi, flags, cancellable);
501 /* Folder names begin with a dot */
503 maildir_full_name_to_dir_name (const gchar *full_name)
507 if (g_ascii_strcasecmp (full_name, "Inbox")) {
508 if (!g_ascii_strncasecmp (full_name, "Inbox/", 6))
509 path = g_strconcat (".", full_name + 5, NULL);
511 path = g_strconcat (".", full_name, NULL);
513 g_strdelimit (path + 1, "/", HIER_SEP_CHAR);
515 path = g_strdup (".");
521 maildir_dir_name_to_fullname (const gchar *dir_name)
525 if (!g_ascii_strncasecmp (dir_name, "..", 2))
526 full_name = g_strconcat ("Inbox/", dir_name + 2, NULL);
528 full_name = g_strdup (dir_name + 1);
530 g_strdelimit (full_name, HIER_SEP, '/');
536 scan_dirs (CamelStore *store,
538 gboolean can_inbox_sibling,
539 CamelFolderInfo **topfi,
540 GCancellable *cancellable,
543 CamelLocalSettings *local_settings;
544 CamelSettings *settings;
545 CamelService *service;
550 gchar *meta_path = NULL;
553 service = CAMEL_SERVICE (store);
554 settings = camel_service_get_settings (service);
556 local_settings = CAMEL_LOCAL_SETTINGS (settings);
557 path = camel_local_settings_dup_path (local_settings);
558 g_return_val_if_fail (path != NULL, -1);
560 folders = g_ptr_array_new ();
561 if (!g_ascii_strcasecmp ((*topfi)->full_name, "Inbox"))
562 g_ptr_array_add (folders, (*topfi));
564 dir = opendir (path);
568 g_io_error_from_errno (errno),
569 _("Could not scan folder '%s': %s"),
570 path, g_strerror (errno));
574 meta_path = maildir_get_meta_path ((CamelLocalStore *) store, ".", "maildir++");
575 if (!g_file_test (meta_path, G_FILE_TEST_EXISTS))
576 maildir_migrate_hierarchy ((CamelMaildirStore *) store, cancellable, error);
580 while ((d = readdir (dir))) {
581 gchar *full_name, *filename;
582 const gchar *short_name;
586 if (strcmp(d->d_name, "tmp") == 0
587 || strcmp(d->d_name, "cur") == 0
588 || strcmp(d->d_name, "new") == 0
589 || strcmp(d->d_name, ".#evolution") == 0
590 || strcmp(d->d_name, ".") == 0
591 || strcmp(d->d_name, "..") == 0
592 || !g_str_has_prefix (d->d_name, "."))
596 filename = g_build_filename (path, d->d_name, NULL);
597 if (!(g_stat (filename, &st) == 0 && S_ISDIR (st.st_mode))) {
602 full_name = maildir_dir_name_to_fullname (d->d_name);
603 short_name = strrchr (full_name, '/');
605 short_name = full_name;
609 if ((g_ascii_strcasecmp ((*topfi)->full_name, "Inbox") != 0
610 && (!g_str_has_prefix (full_name, (*topfi)->full_name) ||
611 (full_name[strlen ((*topfi)->full_name)] != '\0' &&
612 full_name[strlen ((*topfi)->full_name)] != '/')))
613 || (!can_inbox_sibling
614 && g_ascii_strcasecmp ((*topfi)->full_name, "Inbox") == 0
615 && (!g_str_has_prefix (full_name, (*topfi)->full_name) ||
616 (full_name[strlen ((*topfi)->full_name)] != '\0' &&
617 full_name[strlen ((*topfi)->full_name)] != '/')))) {
622 fi = scan_fi (store, flags, full_name, short_name, cancellable);
625 fi->flags &= ~CAMEL_FOLDER_NOCHILDREN;
626 fi->flags |= CAMEL_FOLDER_CHILDREN;
628 g_ptr_array_add (folders, fi);
633 if (folders->len != 0) {
634 if (!g_ascii_strcasecmp ((*topfi)->full_name, "Inbox")) {
635 *topfi = camel_folder_info_build (folders, "", '/', TRUE);
637 CamelFolderInfo *old_topfi = *topfi;
639 *topfi = camel_folder_info_build (folders, (*topfi)->full_name, '/', TRUE);
640 camel_store_free_folder_info (store, old_topfi);
648 g_ptr_array_free (folders, TRUE);
656 maildir_store_hash_folder_name (gconstpointer a)
658 return g_str_hash (md_canon_name (a));
662 maildir_store_compare_folder_name (gconstpointer a,
665 return g_str_equal (md_canon_name (a), md_canon_name (b));
668 static CamelFolderInfo *
669 maildir_store_get_folder_info_sync (CamelStore *store,
671 CamelStoreGetFolderInfoFlags flags,
672 GCancellable *cancellable,
675 CamelFolderInfo *fi = NULL;
677 if (top == NULL || top[0] == 0) {
678 /* create a dummy "." parent inbox, use to scan, then put back at the top level */
679 fi = scan_fi(store, flags, "Inbox", _("Inbox"), cancellable);
680 if (scan_dirs (store, flags, TRUE, &fi, cancellable, error) == -1)
683 fi->flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
684 } else if (!strcmp(top, ".")) {
685 fi = scan_fi(store, flags, "Inbox", _("Inbox"), cancellable);
686 fi->flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
688 const gchar *name = strrchr (top, '/');
690 fi = scan_fi (store, flags, top, name ? name + 1 : top, cancellable);
691 if (g_strcmp0 (fi->full_name, CAMEL_VTRASH_NAME) != 0 &&
692 g_strcmp0 (fi->full_name, CAMEL_VJUNK_NAME) != 0 &&
693 scan_dirs (store, flags, FALSE, &fi, cancellable, error) == -1)
701 camel_store_free_folder_info_full (store, fi);
707 maildir_store_get_inbox_sync (CamelStore *store,
708 GCancellable *cancellable,
711 return camel_store_get_folder_sync (
712 store, "Inbox", CAMEL_STORE_FOLDER_CREATE, cancellable, error);
716 rename_traverse_fi (CamelStore *store,
717 CamelStoreClass *store_class,
719 const gchar *old_full_name_prefix,
720 const gchar *new_full_name_prefix,
721 GCancellable *cancellable,
724 gint old_prefix_len = strlen (old_full_name_prefix);
728 if (fi->full_name && g_str_has_prefix (fi->full_name, old_full_name_prefix)) {
729 gchar *new_full_name, *old_dir, *new_dir;
731 new_full_name = g_strconcat (new_full_name_prefix, fi->full_name + old_prefix_len, NULL);
732 old_dir = maildir_full_name_to_dir_name (fi->full_name);
733 new_dir = maildir_full_name_to_dir_name (new_full_name);
735 /* Chain up to parent's rename_folder_sync() method. */
736 ret = store_class->rename_folder_sync (store, old_dir, new_dir, cancellable, error);
740 g_free (new_full_name);
743 if (fi->child && !rename_traverse_fi (store, store_class, fi->child, old_full_name_prefix, new_full_name_prefix, cancellable, error))
753 maildir_store_rename_folder_sync (CamelStore *store,
756 GCancellable *cancellable,
759 CamelStoreClass *store_class;
761 gchar *old_dir, *new_dir;
762 CamelFolderInfo *subfolders;
764 if (strcmp(old, ".") == 0) {
766 error, CAMEL_STORE_ERROR,
767 CAMEL_STORE_ERROR_NO_FOLDER,
768 _("Cannot rename folder: %s: Invalid operation"),
773 if (g_strstr_len (new, -1, ".")) {
775 error, CAMEL_STORE_ERROR,
776 CAMEL_STORE_ERROR_NO_FOLDER,
777 _("Cannot rename the folder: %s: Folder name cannot contain a dot"), new);
782 if (!g_ascii_strcasecmp (new, "Inbox")) {
784 error, CAMEL_STORE_ERROR,
785 CAMEL_STORE_ERROR_NO_FOLDER,
786 _("Folder %s already exists"), new);
790 subfolders = maildir_store_get_folder_info_sync (store, old, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, cancellable, NULL);
792 old_dir = maildir_full_name_to_dir_name (old);
793 new_dir = maildir_full_name_to_dir_name (new);
795 /* Chain up to parent's rename_folder_sync() method. */
796 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
797 ret = store_class->rename_folder_sync (
798 store, old_dir, new_dir, cancellable, error);
802 ret = rename_traverse_fi (store, store_class, subfolders->child, old, new, cancellable, error);
804 camel_store_free_folder_info (store, subfolders);
814 camel_maildir_store_class_init (CamelMaildirStoreClass *class)
816 CamelStoreClass *store_class;
817 CamelLocalStoreClass *local_class;
819 store_class = CAMEL_STORE_CLASS (class);
820 store_class->hash_folder_name = maildir_store_hash_folder_name;
821 store_class->compare_folder_name = maildir_store_compare_folder_name;
822 store_class->create_folder_sync = maildir_store_create_folder_sync;
823 store_class->free_folder_info = camel_store_free_folder_info_full;
824 store_class->get_folder_sync = maildir_store_get_folder_sync;
825 store_class->get_folder_info_sync = maildir_store_get_folder_info_sync;
826 store_class->get_inbox_folder_sync = maildir_store_get_inbox_sync;
827 store_class->delete_folder_sync = maildir_store_delete_folder_sync;
828 store_class->rename_folder_sync = maildir_store_rename_folder_sync;
830 local_class = CAMEL_LOCAL_STORE_CLASS (class);
831 local_class->get_full_path = maildir_get_full_path;
832 local_class->get_meta_path = maildir_get_meta_path;
836 camel_maildir_store_init (CamelMaildirStore *maildir_store)
841 maildir_get_full_path (CamelLocalStore *ls,
842 const gchar *full_name)
844 CamelLocalSettings *local_settings;
845 CamelSettings *settings;
846 CamelService *service;
851 service = CAMEL_SERVICE (ls);
852 settings = camel_service_get_settings (service);
854 local_settings = CAMEL_LOCAL_SETTINGS (settings);
855 path = camel_local_settings_dup_path (local_settings);
857 dir_name = maildir_full_name_to_dir_name (full_name);
858 filename = g_build_filename (path, dir_name, NULL);
867 maildir_get_meta_path (CamelLocalStore *ls,
868 const gchar *full_name,
871 CamelLocalSettings *local_settings;
872 CamelSettings *settings;
873 CamelService *service;
879 service = CAMEL_SERVICE (ls);
880 settings = camel_service_get_settings (service);
882 local_settings = CAMEL_LOCAL_SETTINGS (settings);
883 path = camel_local_settings_dup_path (local_settings);
885 dir_name = maildir_full_name_to_dir_name (full_name);
886 tmp = g_build_filename (path, dir_name, NULL);
887 filename = g_strconcat (tmp, ext, NULL);
896 /* Migration from old to maildir++ hierarchy */
905 static guint scan_hash (gconstpointer d)
907 const struct _scan_node *v = d;
909 return v->inode ^ v->dnode;
912 static gboolean scan_equal (gconstpointer a, gconstpointer b)
914 const struct _scan_node *v1 = a, *v2 = b;
916 return v1->inode == v2->inode && v1->dnode == v2->dnode;
919 static void scan_free (gpointer k, gpointer v, gpointer d)
925 scan_old_dir_info (CamelStore *store,
926 CamelFolderInfo *topfi,
929 CamelLocalSettings *local_settings;
930 CamelSettings *settings;
931 CamelService *service;
932 GQueue queue = G_QUEUE_INIT;
933 struct _scan_node *sn;
940 service = CAMEL_SERVICE (store);
941 settings = camel_service_get_settings (service);
943 local_settings = CAMEL_LOCAL_SETTINGS (settings);
944 path = camel_local_settings_dup_path (local_settings);
946 visited = g_hash_table_new (scan_hash, scan_equal);
948 sn = g_malloc0 (sizeof (*sn));
950 g_queue_push_tail (&queue, sn);
951 g_hash_table_insert (visited, sn, sn);
953 while (!g_queue_is_empty (&queue)) {
957 CamelFolderInfo *last;
959 sn = g_queue_pop_head (&queue);
961 last = (CamelFolderInfo *) &sn->fi->child;
963 if (!strcmp(sn->fi->full_name, "."))
964 name = g_strdup (path);
966 name = g_build_filename (path, sn->fi->full_name, NULL);
968 dir = opendir (name);
973 g_io_error_from_errno (errno),
974 _("Could not scan folder '%s': %s"),
975 path, g_strerror (errno));
979 while ((d = readdir (dir))) {
980 if (strcmp(d->d_name, "tmp") == 0
981 || strcmp(d->d_name, "cur") == 0
982 || strcmp(d->d_name, "new") == 0
983 || strcmp(d->d_name, ".#evolution") == 0
984 || strcmp(d->d_name, ".") == 0
985 || strcmp(d->d_name, "..") == 0)
988 tmp = g_build_filename (name, d->d_name, NULL);
989 if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode)) {
990 struct _scan_node in;
992 in.dnode = st.st_dev;
993 in.inode = st.st_ino;
995 /* see if we've visited already */
996 if (g_hash_table_lookup (visited, &in) == NULL) {
997 struct _scan_node *snew = g_malloc (sizeof (*snew));
999 CamelFolderInfo *fi = NULL;
1001 snew->dnode = in.dnode;
1002 snew->inode = in.inode;
1004 if (!strcmp(sn->fi->full_name, "."))
1005 full = g_strdup (d->d_name);
1007 full = g_strdup_printf("%s/%s", sn->fi->full_name, d->d_name);
1009 fi = camel_folder_info_new ();
1010 fi->full_name = full;
1011 fi->display_name = g_strdup (d->d_name);
1014 last->next = snew->fi;
1016 snew->fi->parent = sn->fi;
1018 g_hash_table_insert (visited, snew, snew);
1019 g_queue_push_tail (&queue, snew);
1031 g_hash_table_foreach (visited, scan_free, NULL);
1032 g_hash_table_destroy (visited);
1040 maildir_rename_old_folder (CamelMaildirStore *mstore,
1041 CamelFolderInfo *fi,
1042 GCancellable *cancellable,
1045 gchar *new_name = NULL, *old_name;
1046 CamelStoreClass *store_class;
1048 old_name = g_strdup (fi->full_name);
1049 g_strdelimit (old_name, ".", '_');
1050 new_name = maildir_full_name_to_dir_name (old_name);
1052 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
1053 store_class->rename_folder_sync (
1054 (CamelStore *) mstore, fi->full_name, new_name, cancellable, error);
1061 traverse_rename_folder_info (CamelMaildirStore *mstore,
1062 CamelFolderInfo *fi,
1063 GCancellable *cancellable,
1068 traverse_rename_folder_info (mstore, fi->child, cancellable, error);
1070 if (strcmp (fi->full_name, ".") && ((!g_str_has_prefix (fi->full_name, ".") && (!fi->parent || !strcmp(fi->parent->full_name, "."))) ||
1071 (fi->parent && strcmp (fi->parent->full_name, "."))))
1072 maildir_rename_old_folder (mstore, fi, cancellable, error);
1074 traverse_rename_folder_info (mstore, fi->next, cancellable, error);
1079 maildir_migrate_hierarchy (CamelMaildirStore *mstore,
1080 GCancellable *cancellable,
1083 CamelFolderInfo *topfi;
1086 topfi = camel_folder_info_new ();
1087 topfi->full_name = g_strdup (".");
1088 topfi->display_name = g_strdup ("Inbox");
1090 if (scan_old_dir_info ((CamelStore *) mstore, topfi, error) == -1) {
1091 g_warning ("Failed to scan the old folder info \n");
1092 camel_folder_info_free (topfi);
1096 traverse_rename_folder_info (mstore, topfi, cancellable, error);
1098 meta_path = maildir_get_meta_path ((CamelLocalStore *) mstore, ".", "maildir++");
1099 g_file_set_contents (meta_path, "maildir++", -1, NULL);
1101 camel_folder_info_free (topfi);