Mercurial > dovecot > original-hg > dovecot-1.2
view src/plugins/convert/convert-storage.c @ 9490:fd84592e817b HEAD
dovecot-example.conf: Updated dict comments.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 23 Nov 2009 13:08:47 -0500 |
parents | a9f719f9a509 |
children | 00cd9aacd03c |
line wrap: on
line source
/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "file-lock.h" #include "file-dotlock.h" #include "mail-storage-private.h" #include "mail-namespace.h" #include "mail-search-build.h" #include "convert-storage.h" #include <stdio.h> #include <dirent.h> #define CONVERT_LOCK_FILENAME ".dovecot.convert" static struct dotlock_settings dotlock_settings = { MEMBER(temp_prefix) NULL, MEMBER(lock_suffix) NULL, MEMBER(timeout) 60*5, MEMBER(stale_timeout) 60*5 }; static const char *storage_error(struct mail_storage *storage) { enum mail_error error; return mail_storage_get_last_error(storage, &error); } static int mailbox_copy_mails(struct mailbox *srcbox, struct mailbox *destbox, struct dotlock *dotlock, const char **error_r) { struct mail_search_context *ctx; struct mailbox_transaction_context *src_trans, *dest_trans; struct mail_save_context *save_ctx; struct mail *mail; struct mail_search_args *search_args; int ret = 0; if (mailbox_sync(srcbox, MAILBOX_SYNC_FLAG_FULL_READ, 0, NULL) < 0) { *error_r = storage_error(srcbox->storage); return -1; } *error_r = NULL; search_args = mail_search_build_init(); mail_search_build_add_all(search_args); src_trans = mailbox_transaction_begin(srcbox, 0); dest_trans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); ctx = mailbox_search_init(src_trans, search_args, NULL); mail_search_args_unref(&search_args); mail = mail_alloc(src_trans, MAIL_FETCH_FLAGS | MAIL_FETCH_RECEIVED_DATE | MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY | MAIL_FETCH_FROM_ENVELOPE, NULL); while (mailbox_search_next(ctx, mail) > 0) { struct mail_keywords *keywords; const char *const *keywords_list; if ((mail->seq % 100) == 0) { /* touch the lock file so that if there are tons of mails another process won't override our lock. */ (void)file_dotlock_touch(dotlock); } keywords_list = mail_get_keywords(mail); keywords = str_array_length(keywords_list) == 0 ? NULL : mailbox_keywords_create_valid(destbox, keywords_list); save_ctx = mailbox_save_alloc(dest_trans); mailbox_save_set_flags(save_ctx, mail_get_flags(mail), keywords); ret = mailbox_copy(&save_ctx, mail); mailbox_keywords_free(destbox, &keywords); if (ret < 0) { *error_r = storage_error(destbox->storage); break; } } mail_free(&mail); if (mailbox_search_deinit(&ctx) < 0) { ret = -1; *error_r = storage_error(srcbox->storage); } if (ret < 0) mailbox_transaction_rollback(&dest_trans); else { ret = mailbox_transaction_commit(&dest_trans); if (ret < 0) *error_r = storage_error(destbox->storage); } /* source transaction committing isn't all that important. ignore if it fails. */ if (ret < 0) mailbox_transaction_rollback(&src_trans); else (void)mailbox_transaction_commit(&src_trans); i_assert(ret == 0 || *error_r != NULL); return ret; } static const char * mailbox_name_convert(struct mail_storage *dest_storage, struct mail_storage *source_storage, const struct convert_settings *set, const char *name) { char *dest_name, *p, src_sep, dest_sep; src_sep = mail_storage_get_hierarchy_sep(source_storage); dest_sep = mail_storage_get_hierarchy_sep(dest_storage); if (src_sep == dest_sep) return name; dest_name = t_strdup_noconst(name); for (p = dest_name; *p != '\0'; p++) { if (*p == dest_sep && set->alt_hierarchy_char != '\0') *p = set->alt_hierarchy_char; else if (*p == src_sep) *p = dest_sep; } return dest_name; } static int mailbox_convert_maildir_to_dbox(struct mail_storage *src_storage, struct mail_storage *dest_storage, const char *src_name, const char *dest_name) { static const char *maildir_files[] = { "dovecot-uidlist", "dovecot-keywords", "dovecot.index", "dovecot.index.log", "dovecot.index.cache" }; string_t *src, *dest; DIR *dir; struct dirent *dp; const char *src_path, *dest_path, *new_path, *cur_path; unsigned int i, src_dir_len, dest_dir_len; bool t; int ret; /* create as non-selectable mailbox so the dbox-Mails directory isn't created yet */ if (mail_storage_mailbox_create(dest_storage, dest_name, TRUE) < 0) { i_error("Mailbox conversion: " "Couldn't create mailbox %s: %s", dest_name, storage_error(dest_storage)); return -1; } src_path = mail_storage_get_mailbox_path(src_storage, src_name, &t); dest_path = mail_storage_get_mailbox_path(dest_storage, dest_name, &t); /* rename cur/ directory as the destination directory */ cur_path = t_strconcat(src_path, "/cur", NULL); if (rename(cur_path, dest_path) < 0) { i_error("rename(%s, %s) failed: %m", cur_path, dest_path); return -1; } /* move metadata files */ src = t_str_new(256); str_printfa(src, "%s/", src_path); src_dir_len = str_len(src); dest = t_str_new(256); str_printfa(dest, "%s/", dest_path); dest_dir_len = str_len(dest); for (i = 0; i < N_ELEMENTS(maildir_files); i++) { str_truncate(src, src_dir_len); str_truncate(dest, dest_dir_len); str_append(src, maildir_files[i]); str_append(dest, maildir_files[i]); if (rename(str_c(src), str_c(dest)) < 0 && errno != ENOENT) { i_error("rename(%s, %s) failed: %m", str_c(src), str_c(dest)); } } /* move files in new/ */ new_path = t_strconcat(src_path, "/new", NULL); str_truncate(src, src_dir_len); str_append(src, "new/"); src_dir_len = str_len(src); dir = opendir(new_path); if (dir == NULL) { if (errno == ENOENT) return 0; i_error("opendir(%s) failed: %m", new_path); return -1; } ret = 0; errno = 0; while ((dp = readdir(dir)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; str_truncate(src, src_dir_len); str_truncate(dest, dest_dir_len); str_append(src, dp->d_name); str_append(dest, dp->d_name); if (strstr(dp->d_name, ":2,") == NULL) str_append(dest, ":2,"); if (rename(str_c(src), str_c(dest)) < 0) { i_error("rename(%s, %s) failed: %m", str_c(src), str_c(dest)); ret = -1; errno = 0; } } if (errno != 0) { i_error("readdir(%s) failed: %m", new_path); ret = -1; } if (closedir(dir) < 0) { i_error("closedir(%s) failed: %m", new_path); ret = -1; } return ret; } static int mailbox_convert_list_item(struct mail_storage *source_storage, struct mail_storage *dest_storage, const struct mailbox_info *info, struct dotlock *dotlock, const struct convert_settings *set) { const char *name, *dest_name, *error; struct mailbox *srcbox, *destbox; int ret = 0; if ((info->flags & MAILBOX_NONEXISTENT) != 0) return 0; name = strcasecmp(info->name, "INBOX") == 0 ? "INBOX" : info->name; dest_name = mailbox_name_convert(dest_storage, source_storage, set, name); if ((info->flags & MAILBOX_NOSELECT) != 0) { /* \NoSelect mailbox, so it's probably a "directory" */ if (*info->name == '.' && set->skip_dotdirs) return 0; if (mail_storage_mailbox_create(dest_storage, dest_name, TRUE) < 0) { i_error("Mailbox conversion: Couldn't create mailbox " "directory %s", dest_name); return -1; } return 0; } if (strcmp(source_storage->name, "maildir") == 0 && strcmp(dest_storage->name, "dbox") == 0) { if (mailbox_convert_maildir_to_dbox(source_storage, dest_storage, name, dest_name) < 0) { i_error("Mailbox conversion failed for mailbox %s", name); return -1; } return 0; } /* First open the source mailbox. If we can't open it, don't create the destination mailbox either. */ srcbox = mailbox_open(&source_storage, name, NULL, MAILBOX_OPEN_READONLY | MAILBOX_OPEN_KEEP_RECENT); if (srcbox == NULL) { if (set->skip_broken_mailboxes) return 0; i_error("Mailbox conversion: " "Couldn't open source mailbox %s: %s", name, storage_error(source_storage)); return -1; } /* Create and open the destination mailbox. */ if (strcmp(dest_name, "INBOX") != 0) { if (mail_storage_mailbox_create(dest_storage, dest_name, FALSE) < 0) { i_error("Mailbox conversion: " "Couldn't create mailbox %s: %s", dest_name, storage_error(dest_storage)); mailbox_close(&srcbox); return -1; } } destbox = mailbox_open(&dest_storage, dest_name, NULL, MAILBOX_OPEN_KEEP_RECENT); if (destbox == NULL) { i_error("Mailbox conversion: Couldn't open dest mailbox %s: %s", dest_name, storage_error(dest_storage)); mailbox_close(&srcbox); return -1; } if (mailbox_copy_mails(srcbox, destbox, dotlock, &error) < 0) { i_error("Mailbox conversion: Couldn't copy mailbox %s: %s", mailbox_get_name(srcbox), error); } mailbox_close(&srcbox); mailbox_close(&destbox); return ret; } static int mailbox_list_copy(struct mail_storage *source_storage, struct mail_namespace *dest_namespaces, struct dotlock *dotlock, const struct convert_settings *set) { struct mailbox_list_iterate_context *iter; struct mail_namespace *dest_ns; const struct mailbox_info *info; int ret = 0; dest_ns = mail_namespace_find_inbox(dest_namespaces); iter = mailbox_list_iter_init(mail_storage_get_list(source_storage), "*", MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { T_BEGIN { ret = mailbox_convert_list_item(source_storage, dest_ns->storage, info, dotlock, set); } T_END; if (ret < 0) break; /* In case there are lots of mailboxes. Also the other touch is done only after 100 mails. */ (void)file_dotlock_touch(dotlock); } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } static int mailbox_list_copy_subscriptions(struct mail_storage *source_storage, struct mail_namespace *dest_namespaces, const struct convert_settings *set) { struct mailbox_list_iterate_context *iter; struct mail_namespace *dest_ns; const struct mailbox_info *info; struct mailbox_list *dest_list; const char *dest_name; int ret = 0; dest_ns = mail_namespace_find_inbox(dest_namespaces); dest_list = mail_storage_get_list(dest_ns->storage); iter = mailbox_list_iter_init(mail_storage_get_list(source_storage), "*", MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { dest_name = mailbox_name_convert(dest_ns->storage, source_storage, set, info->name); if (mailbox_list_set_subscribed(dest_list, dest_name, TRUE) < 0) { ret = -1; break; } } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; } int convert_storage(const char *source_data, struct mail_namespace *dest_namespaces, const struct convert_settings *set) { struct mail_user *user = dest_namespaces->user; struct mail_namespace *source_ns, *dest_inbox_ns; struct dotlock *dotlock; enum mail_storage_flags src_flags; enum file_lock_method lock_method; const char *home, *path, *error; int ret; i_assert(user->namespaces == dest_namespaces); source_ns = mail_namespaces_init_empty(user); user->namespaces = dest_namespaces; dest_inbox_ns = mail_namespace_find_inbox(dest_namespaces); src_flags = dest_inbox_ns->storage->flags; lock_method = dest_inbox_ns->storage->lock_method; src_flags |= MAIL_STORAGE_FLAG_NO_AUTOCREATE; if (mail_storage_create(source_ns, NULL, source_data, src_flags, lock_method, &error) < 0) { /* No need for conversion. */ return 0; } if (mail_user_get_home(user, &home) <= 0) i_unreached(); path = t_strconcat(home, "/"CONVERT_LOCK_FILENAME, NULL); dotlock_settings.use_excl_lock = (source_ns->storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0; dotlock_settings.nfs_flush = (source_ns->storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0; ret = file_dotlock_create(&dotlock_settings, path, 0, &dotlock); if (ret <= 0) { if (ret == 0) i_error("Mailbox conversion: Lock creation timeouted"); else i_error("file_dotlock_create(%s) failed: %m", path); return -1; } /* just in case if another process just had converted the mailbox, reopen the source storage */ mail_storage_destroy(&source_ns->storage); if (mail_storage_create(source_ns, NULL, source_data, src_flags, lock_method, &error) < 0) { /* No need for conversion anymore. */ file_dotlock_delete(&dotlock); return 0; } ret = mailbox_list_copy(source_ns->storage, dest_namespaces, dotlock, set); if (ret == 0) { ret = mailbox_list_copy_subscriptions(source_ns->storage, dest_namespaces, set); } if (ret == 0) { /* all finished. rename the source directory to mark the move as finished. */ const char *src, *dest; bool is_file; src = mail_storage_get_mailbox_path(source_ns->storage, "", &is_file); if (src != NULL) { dest = t_strconcat(src, "-converted", NULL); if (rename(src, dest) < 0) { i_error("Mailbox conversion: " "rename(%s, %s) failed: %m", src, dest); /* return success anyway */ } } ret = 1; } file_dotlock_delete(&dotlock); mail_namespaces_deinit(&source_ns); return ret; }