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);
93 settings = camel_service_ref_settings (service);
95 local_settings = CAMEL_LOCAL_SETTINGS (settings);
96 path = camel_local_settings_dup_path (local_settings);
98 g_object_unref (settings);
100 if (!g_path_is_absolute (path)) {
102 error, CAMEL_STORE_ERROR,
103 CAMEL_STORE_ERROR_NO_FOLDER,
104 _("Store root %s is not an absolute path"), path);
108 if (g_strstr_len (folder_name, -1, ".")) {
110 error, CAMEL_STORE_ERROR,
111 CAMEL_STORE_ERROR_NO_FOLDER,
112 _("Cannot create folder: %s: "
113 "Folder name cannot contain a dot"),
118 if (!g_ascii_strcasecmp (folder_name, "Inbox")) {
120 error, CAMEL_STORE_ERROR,
121 CAMEL_STORE_ERROR_NO_FOLDER,
122 _("Folder %s already exists"), folder_name);
126 if (parent_name && *parent_name) {
127 gchar *dir_name = maildir_full_name_to_dir_name (parent_name);
128 name = g_strdup_printf("%s/%s.%s", path, dir_name, folder_name);
131 name = maildir_full_name_to_dir_name (folder_name);
133 if (g_stat (name, &st) == 0 || errno != ENOENT) {
136 g_io_error_from_errno (errno),
137 _("Cannot get folder: %s: %s"),
138 name, g_strerror (errno));
145 if (parent_name && *parent_name)
146 name = g_strdup_printf("%s/%s", parent_name, folder_name);
148 name = g_strdup_printf("%s", folder_name);
150 folder = maildir_store_get_folder_sync (
151 store, name, CAMEL_STORE_FOLDER_CREATE, cancellable, error);
153 g_object_unref (folder);
154 info = CAMEL_STORE_GET_CLASS (store)->get_folder_info_sync (
155 store, name, 0, cancellable, error);
166 maildir_store_get_folder_sync (CamelStore *store,
167 const gchar *folder_name,
168 CamelStoreGetFolderFlags flags,
169 GCancellable *cancellable,
172 CamelStoreClass *store_class;
173 CamelLocalSettings *local_settings;
174 CamelSettings *settings;
175 CamelService *service;
176 gchar *name, *tmp, *cur, *new, *dir_name;
179 CamelFolder *folder = NULL;
181 service = CAMEL_SERVICE (store);
183 settings = camel_service_ref_settings (service);
185 local_settings = CAMEL_LOCAL_SETTINGS (settings);
186 path = camel_local_settings_dup_path (local_settings);
188 g_object_unref (settings);
190 folder_name = md_canon_name (folder_name);
191 dir_name = maildir_full_name_to_dir_name (folder_name);
193 /* maildir++ directory names start with a '.' */
194 name = g_build_filename (path, dir_name, NULL);
199 /* Chain up to parent's get_folder() method. */
200 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
201 if (!store_class->get_folder_sync (store, dir_name, flags, cancellable, error)) {
206 tmp = g_strdup_printf ("%s/tmp", name);
207 cur = g_strdup_printf ("%s/cur", name);
208 new = g_strdup_printf ("%s/new", name);
210 if (!g_ascii_strcasecmp (folder_name, "Inbox")) {
211 /* special case "." (aka inbox), may need to be created */
212 if (g_stat (tmp, &st) != 0 || !S_ISDIR (st.st_mode)
213 || g_stat (cur, &st) != 0 || !S_ISDIR (st.st_mode)
214 || g_stat (new, &st) != 0 || !S_ISDIR (st.st_mode)) {
215 if (g_mkdir (tmp, 0700) != 0
216 || g_mkdir (cur, 0700) != 0
217 || g_mkdir (new, 0700) != 0) {
220 g_io_error_from_errno (errno),
221 _("Cannot create folder '%s': %s"),
222 folder_name, g_strerror (errno));
229 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
230 } else if (g_stat (name, &st) == -1) {
231 /* folder doesn't exist, see if we should create it */
232 if (errno != ENOENT) {
235 g_io_error_from_errno (errno),
236 _("Cannot get folder '%s': %s"),
237 folder_name, g_strerror (errno));
238 } else if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
240 error, CAMEL_STORE_ERROR,
241 CAMEL_STORE_ERROR_NO_FOLDER,
242 _("Cannot get folder '%s': folder does not exist."),
245 if (g_mkdir (name, 0700) != 0
246 || g_mkdir (tmp, 0700) != 0
247 || g_mkdir (cur, 0700) != 0
248 || g_mkdir (new, 0700) != 0) {
251 g_io_error_from_errno (errno),
252 _("Cannot create folder '%s': %s"),
253 folder_name, g_strerror (errno));
259 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
262 } else if (!S_ISDIR (st.st_mode)
263 || g_stat (tmp, &st) != 0 || !S_ISDIR (st.st_mode)
264 || g_stat (cur, &st) != 0 || !S_ISDIR (st.st_mode)
265 || g_stat (new, &st) != 0 || !S_ISDIR (st.st_mode)) {
266 /* folder exists, but not maildir */
268 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
269 _("Cannot get folder '%s': not a maildir directory."),
271 } else if (flags & CAMEL_STORE_FOLDER_EXCL) {
273 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
274 _("Cannot create folder '%s': folder exists."),
277 folder = camel_maildir_folder_new (store, folder_name, flags, cancellable, error);
289 maildir_store_delete_folder_sync (CamelStore *store,
290 const gchar *folder_name,
291 GCancellable *cancellable,
294 CamelLocalSettings *local_settings;
295 CamelSettings *settings;
296 CamelService *service;
297 gchar *name, *tmp, *cur, *new, *dir_name;
300 gboolean success = TRUE;
302 if (g_ascii_strcasecmp (folder_name, "Inbox") == 0) {
304 error, CAMEL_STORE_ERROR,
305 CAMEL_STORE_ERROR_NO_FOLDER,
306 _("Cannot delete folder: %s: Invalid operation"),
311 service = CAMEL_SERVICE (store);
313 settings = camel_service_ref_settings (service);
315 local_settings = CAMEL_LOCAL_SETTINGS (settings);
316 path = camel_local_settings_dup_path (local_settings);
318 g_object_unref (settings);
320 /* maildir++ directory names start with a '.' */
321 dir_name = maildir_full_name_to_dir_name (folder_name);
322 name = g_build_filename (path, dir_name, NULL);
327 tmp = g_strdup_printf ("%s/tmp", name);
328 cur = g_strdup_printf ("%s/cur", name);
329 new = g_strdup_printf ("%s/new", name);
331 if (g_stat (name, &st) == -1 || !S_ISDIR (st.st_mode)
332 || g_stat (tmp, &st) == -1 || !S_ISDIR (st.st_mode)
333 || g_stat (cur, &st) == -1 || !S_ISDIR (st.st_mode)
334 || g_stat (new, &st) == -1 || !S_ISDIR (st.st_mode)) {
337 g_io_error_from_errno (errno),
338 _("Could not delete folder '%s': %s"),
339 folder_name, errno ? g_strerror (errno) :
340 _("not a maildir directory"));
344 /* remove subdirs first - will fail if not empty */
345 if (rmdir (cur) == -1 || rmdir (new) == -1) {
351 /* for tmp (only), its contents is irrelevant */
354 while ((d = readdir (dir))) {
355 gchar *name = d->d_name, *file;
357 if (!strcmp(name, ".") || !strcmp(name, ".."))
359 file = g_strdup_printf("%s/%s", tmp, name);
365 if (rmdir (tmp) == -1 || rmdir (name) == -1)
370 /* easier just to mkdir all (and let them fail), than remember what we got to */
371 g_mkdir (name, 0700);
377 g_io_error_from_errno (err),
378 _("Could not delete folder '%s': %s"),
379 folder_name, g_strerror (err));
381 CamelStoreClass *store_class;
383 /* Chain up to parent's delete_folder() method. */
384 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
385 success = store_class->delete_folder_sync (
386 store, folder_name, cancellable, error);
399 fill_fi (CamelStore *store,
402 GCancellable *cancellable)
406 folder = camel_object_bag_peek (store->folders, fi->full_name);
408 if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0)
409 camel_folder_refresh_info_sync (folder, cancellable, NULL);
410 fi->unread = camel_folder_get_unread_message_count (folder);
411 fi->total = camel_folder_get_message_count (folder);
412 g_object_unref (folder);
414 CamelLocalSettings *local_settings;
415 CamelSettings *settings;
416 CamelService *service;
417 gchar *folderpath, *dir_name;
418 CamelFolderSummary *s;
421 service = CAMEL_SERVICE (store);
423 settings = camel_service_ref_settings (service);
425 local_settings = CAMEL_LOCAL_SETTINGS (settings);
426 root = camel_local_settings_dup_path (local_settings);
428 g_object_unref (settings);
430 /* This should be fast enough not to have to test for INFO_FAST */
431 dir_name = maildir_full_name_to_dir_name (fi->full_name);
433 if (!strcmp (dir_name, "."))
434 folderpath = g_strdup (root);
436 folderpath = g_build_filename (root, dir_name, NULL);
440 s = (CamelFolderSummary *) camel_maildir_summary_new (NULL, folderpath, NULL);
441 if (camel_folder_summary_header_load_from_db (s, store, fi->full_name, NULL)) {
442 fi->unread = camel_folder_summary_get_unread_count (s);
443 fi->total = camel_folder_summary_get_saved_count (s);
450 if (camel_local_store_is_main_store (CAMEL_LOCAL_STORE (store)) && fi->full_name
451 && (fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_NORMAL)
452 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK)
453 | camel_local_store_get_folder_type_by_full_name (CAMEL_LOCAL_STORE (store), fi->full_name);
456 static CamelFolderInfo *
457 scan_fi (CamelStore *store,
461 GCancellable *cancellable)
463 CamelLocalSettings *local_settings;
464 CamelSettings *settings;
465 CamelService *service;
467 gchar *tmp, *cur, *new, *dir_name;
471 service = CAMEL_SERVICE (store);
473 settings = camel_service_ref_settings (service);
475 local_settings = CAMEL_LOCAL_SETTINGS (settings);
476 path = camel_local_settings_dup_path (local_settings);
478 g_object_unref (settings);
480 g_return_val_if_fail (path != NULL, NULL);
482 fi = camel_folder_info_new ();
483 fi->full_name = g_strdup (full);
484 fi->display_name = g_strdup (name);
489 /* we only calculate nochildren properly if we're recursive */
490 if (((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) != 0))
491 fi->flags = CAMEL_FOLDER_NOCHILDREN;
493 dir_name = maildir_full_name_to_dir_name (fi->full_name);
494 d(printf("Adding maildir info: '%s' '%s' '%s'\n", fi->name, dir_name, fi->uri));
496 tmp = g_build_filename (path, dir_name, "tmp", NULL);
497 cur = g_build_filename (path, dir_name, "cur", NULL);
498 new = g_build_filename (path, dir_name, "new", NULL);
500 if (!(g_stat (tmp, &st) == 0 && S_ISDIR (st.st_mode)
501 && g_stat (cur, &st) == 0 && S_ISDIR (st.st_mode)
502 && g_stat (new, &st) == 0 && S_ISDIR (st.st_mode)))
503 fi->flags |= CAMEL_FOLDER_NOSELECT;
510 fill_fi (store, fi, flags, cancellable);
517 /* Folder names begin with a dot */
519 maildir_full_name_to_dir_name (const gchar *full_name)
523 if (g_ascii_strcasecmp (full_name, "Inbox")) {
524 if (!g_ascii_strncasecmp (full_name, "Inbox/", 6))
525 path = g_strconcat (".", full_name + 5, NULL);
527 path = g_strconcat (".", full_name, NULL);
529 g_strdelimit (path + 1, "/", HIER_SEP_CHAR);
531 path = g_strdup (".");
537 maildir_dir_name_to_fullname (const gchar *dir_name)
541 if (!g_ascii_strncasecmp (dir_name, "..", 2))
542 full_name = g_strconcat ("Inbox/", dir_name + 2, NULL);
544 full_name = g_strdup (dir_name + 1);
546 g_strdelimit (full_name, HIER_SEP, '/');
552 scan_dirs (CamelStore *store,
554 gboolean can_inbox_sibling,
555 CamelFolderInfo **topfi,
556 GCancellable *cancellable,
559 CamelLocalSettings *local_settings;
560 CamelSettings *settings;
561 CamelService *service;
566 gchar *meta_path = NULL;
569 service = CAMEL_SERVICE (store);
571 settings = camel_service_ref_settings (service);
573 local_settings = CAMEL_LOCAL_SETTINGS (settings);
574 path = camel_local_settings_dup_path (local_settings);
576 g_object_unref (settings);
578 g_return_val_if_fail (path != NULL, -1);
580 folders = g_ptr_array_new ();
581 if (!g_ascii_strcasecmp ((*topfi)->full_name, "Inbox"))
582 g_ptr_array_add (folders, (*topfi));
584 dir = opendir (path);
588 g_io_error_from_errno (errno),
589 _("Could not scan folder '%s': %s"),
590 path, g_strerror (errno));
594 meta_path = maildir_get_meta_path ((CamelLocalStore *) store, ".", "maildir++");
595 if (!g_file_test (meta_path, G_FILE_TEST_EXISTS))
596 maildir_migrate_hierarchy ((CamelMaildirStore *) store, cancellable, error);
600 while ((d = readdir (dir))) {
601 gchar *full_name, *filename;
602 const gchar *short_name;
606 if (strcmp(d->d_name, "tmp") == 0
607 || strcmp(d->d_name, "cur") == 0
608 || strcmp(d->d_name, "new") == 0
609 || strcmp(d->d_name, ".#evolution") == 0
610 || strcmp(d->d_name, ".") == 0
611 || strcmp(d->d_name, "..") == 0
612 || !g_str_has_prefix (d->d_name, "."))
616 filename = g_build_filename (path, d->d_name, NULL);
617 if (!(g_stat (filename, &st) == 0 && S_ISDIR (st.st_mode))) {
622 full_name = maildir_dir_name_to_fullname (d->d_name);
623 short_name = strrchr (full_name, '/');
625 short_name = full_name;
629 if ((g_ascii_strcasecmp ((*topfi)->full_name, "Inbox") != 0
630 && (!g_str_has_prefix (full_name, (*topfi)->full_name) ||
631 (full_name[strlen ((*topfi)->full_name)] != '\0' &&
632 full_name[strlen ((*topfi)->full_name)] != '/')))
633 || (!can_inbox_sibling
634 && g_ascii_strcasecmp ((*topfi)->full_name, "Inbox") == 0
635 && (!g_str_has_prefix (full_name, (*topfi)->full_name) ||
636 (full_name[strlen ((*topfi)->full_name)] != '\0' &&
637 full_name[strlen ((*topfi)->full_name)] != '/')))) {
642 fi = scan_fi (store, flags, full_name, short_name, cancellable);
645 fi->flags &= ~CAMEL_FOLDER_NOCHILDREN;
646 fi->flags |= CAMEL_FOLDER_CHILDREN;
648 g_ptr_array_add (folders, fi);
653 if (folders->len != 0) {
654 if (!g_ascii_strcasecmp ((*topfi)->full_name, "Inbox")) {
655 *topfi = camel_folder_info_build (folders, "", '/', TRUE);
657 CamelFolderInfo *old_topfi = *topfi;
659 *topfi = camel_folder_info_build (folders, (*topfi)->full_name, '/', TRUE);
660 camel_store_free_folder_info (store, old_topfi);
668 g_ptr_array_free (folders, TRUE);
676 maildir_store_hash_folder_name (gconstpointer a)
678 return g_str_hash (md_canon_name (a));
682 maildir_store_equal_folder_name (gconstpointer a,
685 return g_str_equal (md_canon_name (a), md_canon_name (b));
688 static CamelFolderInfo *
689 maildir_store_get_folder_info_sync (CamelStore *store,
691 CamelStoreGetFolderInfoFlags flags,
692 GCancellable *cancellable,
695 CamelFolderInfo *fi = NULL;
697 if (top == NULL || top[0] == 0) {
698 /* create a dummy "." parent inbox, use to scan, then put back at the top level */
699 fi = scan_fi(store, flags, "Inbox", _("Inbox"), cancellable);
700 if (scan_dirs (store, flags, TRUE, &fi, cancellable, error) == -1)
703 fi->flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
704 } else if (!strcmp(top, ".")) {
705 fi = scan_fi(store, flags, "Inbox", _("Inbox"), cancellable);
706 fi->flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
708 const gchar *name = strrchr (top, '/');
710 fi = scan_fi (store, flags, top, name ? name + 1 : top, cancellable);
711 if (g_strcmp0 (fi->full_name, CAMEL_VTRASH_NAME) != 0 &&
712 g_strcmp0 (fi->full_name, CAMEL_VJUNK_NAME) != 0 &&
713 scan_dirs (store, flags, FALSE, &fi, cancellable, error) == -1)
721 camel_store_free_folder_info_full (store, fi);
727 maildir_store_get_inbox_sync (CamelStore *store,
728 GCancellable *cancellable,
731 return camel_store_get_folder_sync (
732 store, "Inbox", CAMEL_STORE_FOLDER_CREATE, cancellable, error);
736 rename_traverse_fi (CamelStore *store,
737 CamelStoreClass *store_class,
739 const gchar *old_full_name_prefix,
740 const gchar *new_full_name_prefix,
741 GCancellable *cancellable,
744 gint old_prefix_len = strlen (old_full_name_prefix);
748 if (fi->full_name && g_str_has_prefix (fi->full_name, old_full_name_prefix)) {
749 gchar *new_full_name, *old_dir, *new_dir;
751 new_full_name = g_strconcat (new_full_name_prefix, fi->full_name + old_prefix_len, NULL);
752 old_dir = maildir_full_name_to_dir_name (fi->full_name);
753 new_dir = maildir_full_name_to_dir_name (new_full_name);
755 /* Chain up to parent's rename_folder_sync() method. */
756 ret = store_class->rename_folder_sync (store, old_dir, new_dir, cancellable, error);
760 g_free (new_full_name);
763 if (fi->child && !rename_traverse_fi (store, store_class, fi->child, old_full_name_prefix, new_full_name_prefix, cancellable, error))
773 maildir_store_rename_folder_sync (CamelStore *store,
776 GCancellable *cancellable,
779 CamelStoreClass *store_class;
781 gchar *old_dir, *new_dir;
782 CamelFolderInfo *subfolders;
784 if (strcmp(old, ".") == 0) {
786 error, CAMEL_STORE_ERROR,
787 CAMEL_STORE_ERROR_NO_FOLDER,
788 _("Cannot rename folder: %s: Invalid operation"),
793 if (g_strstr_len (new, -1, ".")) {
795 error, CAMEL_STORE_ERROR,
796 CAMEL_STORE_ERROR_NO_FOLDER,
797 _("Cannot rename the folder: %s: Folder name cannot contain a dot"), new);
802 if (!g_ascii_strcasecmp (new, "Inbox")) {
804 error, CAMEL_STORE_ERROR,
805 CAMEL_STORE_ERROR_NO_FOLDER,
806 _("Folder %s already exists"), new);
810 subfolders = maildir_store_get_folder_info_sync (store, old, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, cancellable, NULL);
812 old_dir = maildir_full_name_to_dir_name (old);
813 new_dir = maildir_full_name_to_dir_name (new);
815 /* Chain up to parent's rename_folder_sync() method. */
816 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
817 ret = store_class->rename_folder_sync (
818 store, old_dir, new_dir, cancellable, error);
822 ret = rename_traverse_fi (store, store_class, subfolders->child, old, new, cancellable, error);
824 camel_store_free_folder_info (store, subfolders);
834 camel_maildir_store_class_init (CamelMaildirStoreClass *class)
836 CamelStoreClass *store_class;
837 CamelLocalStoreClass *local_class;
839 store_class = CAMEL_STORE_CLASS (class);
840 store_class->hash_folder_name = maildir_store_hash_folder_name;
841 store_class->equal_folder_name = maildir_store_equal_folder_name;
842 store_class->create_folder_sync = maildir_store_create_folder_sync;
843 store_class->free_folder_info = camel_store_free_folder_info_full;
844 store_class->get_folder_sync = maildir_store_get_folder_sync;
845 store_class->get_folder_info_sync = maildir_store_get_folder_info_sync;
846 store_class->get_inbox_folder_sync = maildir_store_get_inbox_sync;
847 store_class->delete_folder_sync = maildir_store_delete_folder_sync;
848 store_class->rename_folder_sync = maildir_store_rename_folder_sync;
850 local_class = CAMEL_LOCAL_STORE_CLASS (class);
851 local_class->get_full_path = maildir_get_full_path;
852 local_class->get_meta_path = maildir_get_meta_path;
856 camel_maildir_store_init (CamelMaildirStore *maildir_store)
861 maildir_get_full_path (CamelLocalStore *ls,
862 const gchar *full_name)
864 CamelLocalSettings *local_settings;
865 CamelSettings *settings;
866 CamelService *service;
871 service = CAMEL_SERVICE (ls);
873 settings = camel_service_ref_settings (service);
875 local_settings = CAMEL_LOCAL_SETTINGS (settings);
876 path = camel_local_settings_dup_path (local_settings);
878 g_object_unref (settings);
880 dir_name = maildir_full_name_to_dir_name (full_name);
881 filename = g_build_filename (path, dir_name, NULL);
890 maildir_get_meta_path (CamelLocalStore *ls,
891 const gchar *full_name,
894 CamelLocalSettings *local_settings;
895 CamelSettings *settings;
896 CamelService *service;
902 service = CAMEL_SERVICE (ls);
904 settings = camel_service_ref_settings (service);
906 local_settings = CAMEL_LOCAL_SETTINGS (settings);
907 path = camel_local_settings_dup_path (local_settings);
909 g_object_unref (settings);
911 dir_name = maildir_full_name_to_dir_name (full_name);
912 tmp = g_build_filename (path, dir_name, NULL);
913 filename = g_strconcat (tmp, ext, NULL);
922 /* Migration from old to maildir++ hierarchy */
931 static guint scan_hash (gconstpointer d)
933 const struct _scan_node *v = d;
935 return v->inode ^ v->dnode;
938 static gboolean scan_equal (gconstpointer a, gconstpointer b)
940 const struct _scan_node *v1 = a, *v2 = b;
942 return v1->inode == v2->inode && v1->dnode == v2->dnode;
945 static void scan_free (gpointer k, gpointer v, gpointer d)
951 scan_old_dir_info (CamelStore *store,
952 CamelFolderInfo *topfi,
955 CamelLocalSettings *local_settings;
956 CamelSettings *settings;
957 CamelService *service;
958 GQueue queue = G_QUEUE_INIT;
959 struct _scan_node *sn;
966 service = CAMEL_SERVICE (store);
968 settings = camel_service_ref_settings (service);
970 local_settings = CAMEL_LOCAL_SETTINGS (settings);
971 path = camel_local_settings_dup_path (local_settings);
973 g_object_unref (settings);
975 visited = g_hash_table_new (scan_hash, scan_equal);
977 sn = g_malloc0 (sizeof (*sn));
979 g_queue_push_tail (&queue, sn);
980 g_hash_table_insert (visited, sn, sn);
982 while (!g_queue_is_empty (&queue)) {
986 CamelFolderInfo *last;
988 sn = g_queue_pop_head (&queue);
990 last = (CamelFolderInfo *) &sn->fi->child;
992 if (!strcmp(sn->fi->full_name, "."))
993 name = g_strdup (path);
995 name = g_build_filename (path, sn->fi->full_name, NULL);
997 dir = opendir (name);
1002 g_io_error_from_errno (errno),
1003 _("Could not scan folder '%s': %s"),
1004 path, g_strerror (errno));
1008 while ((d = readdir (dir))) {
1009 if (strcmp(d->d_name, "tmp") == 0
1010 || strcmp(d->d_name, "cur") == 0
1011 || strcmp(d->d_name, "new") == 0
1012 || strcmp(d->d_name, ".#evolution") == 0
1013 || strcmp(d->d_name, ".") == 0
1014 || strcmp(d->d_name, "..") == 0)
1017 tmp = g_build_filename (name, d->d_name, NULL);
1018 if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode)) {
1019 struct _scan_node in;
1021 in.dnode = st.st_dev;
1022 in.inode = st.st_ino;
1024 /* see if we've visited already */
1025 if (g_hash_table_lookup (visited, &in) == NULL) {
1026 struct _scan_node *snew = g_malloc (sizeof (*snew));
1028 CamelFolderInfo *fi = NULL;
1030 snew->dnode = in.dnode;
1031 snew->inode = in.inode;
1033 if (!strcmp(sn->fi->full_name, "."))
1034 full = g_strdup (d->d_name);
1036 full = g_strdup_printf("%s/%s", sn->fi->full_name, d->d_name);
1038 fi = camel_folder_info_new ();
1039 fi->full_name = full;
1040 fi->display_name = g_strdup (d->d_name);
1043 last->next = snew->fi;
1045 snew->fi->parent = sn->fi;
1047 g_hash_table_insert (visited, snew, snew);
1048 g_queue_push_tail (&queue, snew);
1060 g_hash_table_foreach (visited, scan_free, NULL);
1061 g_hash_table_destroy (visited);
1069 maildir_rename_old_folder (CamelMaildirStore *mstore,
1070 CamelFolderInfo *fi,
1071 GCancellable *cancellable,
1074 gchar *new_name = NULL, *old_name;
1075 CamelStoreClass *store_class;
1077 old_name = g_strdup (fi->full_name);
1078 g_strdelimit (old_name, ".", '_');
1079 new_name = maildir_full_name_to_dir_name (old_name);
1081 store_class = CAMEL_STORE_CLASS (camel_maildir_store_parent_class);
1082 store_class->rename_folder_sync (
1083 (CamelStore *) mstore, fi->full_name, new_name, cancellable, error);
1090 traverse_rename_folder_info (CamelMaildirStore *mstore,
1091 CamelFolderInfo *fi,
1092 GCancellable *cancellable,
1097 traverse_rename_folder_info (mstore, fi->child, cancellable, error);
1099 if (strcmp (fi->full_name, ".") && ((!g_str_has_prefix (fi->full_name, ".") && (!fi->parent || !strcmp(fi->parent->full_name, "."))) ||
1100 (fi->parent && strcmp (fi->parent->full_name, "."))))
1101 maildir_rename_old_folder (mstore, fi, cancellable, error);
1103 traverse_rename_folder_info (mstore, fi->next, cancellable, error);
1108 maildir_migrate_hierarchy (CamelMaildirStore *mstore,
1109 GCancellable *cancellable,
1112 CamelFolderInfo *topfi;
1115 topfi = camel_folder_info_new ();
1116 topfi->full_name = g_strdup (".");
1117 topfi->display_name = g_strdup ("Inbox");
1119 if (scan_old_dir_info ((CamelStore *) mstore, topfi, error) == -1) {
1120 g_warning ("Failed to scan the old folder info \n");
1121 camel_folder_info_free (topfi);
1125 traverse_rename_folder_info (mstore, topfi, cancellable, error);
1127 meta_path = maildir_get_meta_path ((CamelLocalStore *) mstore, ".", "maildir++");
1128 g_file_set_contents (meta_path, "maildir++", -1, NULL);
1130 camel_folder_info_free (topfi);