#include "eacces-error.h"
#include "unlink-directory.h"
#include "unlink-old-files.h"
+#include "mailbox-log.h"
#include "mailbox-uidvalidity.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
return 0;
}
+static int
+maildir_delete_with_trash(struct mailbox_list *list, const char *src,
+ const char *dest, const char *name)
+{
+ unsigned int count;
+
+ /* rename the .maildir into ..DOVECOT-TRASH which atomically
+ marks it as being deleted. If we die before deleting the
+ ..DOVECOT-TRASH directory, it gets deleted the next time
+ mailbox listing sees it. */
+ count = 0;
+ while (rename(src, dest) < 0) {
+ if (errno == ENOENT) {
+ /* it was just deleted under us by
+ another process */
+ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+ return -1;
+ }
+ if (!EDESTDIREXISTS(errno)) {
+ mailbox_list_set_critical(list,
+ "rename(%s, %s) failed: %m", src, dest);
+ return -1;
+ }
+
+ /* already existed, delete it and try again */
+ if (unlink_directory(dest, TRUE) < 0 &&
+ (errno != ENOTEMPTY || count >= 5)) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m", dest);
+ return -1;
+ }
+ count++;
+ }
+
+ if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m", dest);
+
+ /* it's already renamed to ..dir, which means it's
+ deleted as far as the client is concerned. Report
+ success. */
+ }
+ return 0;
+}
+
+static void mailbox_get_guid(struct mailbox_list *list, const char *name,
+ uint8_t mailbox_guid[MAIL_GUID_128_SIZE])
+{
+ struct mailbox *box;
+ struct mailbox_status status;
+
+ box = mailbox_alloc(list, name, NULL, MAILBOX_FLAG_KEEP_RECENT);
+ if (mailbox_open(box) < 0)
+ memset(mailbox_guid, 0, MAIL_GUID_128_SIZE);
+ else {
+ mailbox_get_status(box, STATUS_GUID, &status);
+ memcpy(mailbox_guid, status.mailbox_guid, MAIL_GUID_128_SIZE);
+ }
+ mailbox_close(&box);
+}
+
static int
maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
union mailbox_list_module_context *mlist = MAILDIR_LIST_CONTEXT(list);
+ uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+ uint8_t dir_guid[MAIL_GUID_128_SIZE];
struct stat st;
const char *src, *dest, *base;
- int count;
+ int ret;
+
+ mailbox_get_guid(list, name, mailbox_guid);
+ (void)mailbox_list_get_guid(list, name, dir_guid);
/* Make sure the indexes are closed before trying to delete the
directory that contains them. It can still fail with some NFS
dest = maildir_get_unlink_dest(list, name);
if (dest == NULL) {
/* delete the directory directly without any renaming */
- return maildir_delete_nonrecursive(list, src, name);
- }
-
- /* rename the .maildir into ..DOVECOT-TRASH which atomically
- marks it as being deleted. If we die before deleting the
- ..DOVECOT-TRASH directory, it gets deleted the next time
- mailbox listing sees it. */
- count = 0;
- while (rename(src, dest) < 0) {
- if (errno == ENOENT) {
- /* it was just deleted under us by
- another process */
- mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
- T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
- return -1;
- }
- if (!EDESTDIREXISTS(errno)) {
- mailbox_list_set_critical(list,
- "rename(%s, %s) failed: %m", src, dest);
- return -1;
- }
-
- /* already existed, delete it and try again */
- if (unlink_directory(dest, TRUE) < 0 &&
- (errno != ENOTEMPTY || count >= 5)) {
- mailbox_list_set_critical(list,
- "unlink_directory(%s) failed: %m", dest);
- return -1;
- }
- count++;
+ ret = maildir_delete_nonrecursive(list, src, name);
+ } else {
+ ret = maildir_delete_with_trash(list, src, dest, name);
}
- if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
- mailbox_list_set_critical(list,
- "unlink_directory(%s) failed: %m", dest);
-
- /* it's already renamed to ..dir, which means it's
- deleted as far as the client is concerned. Report
- success. */
+ if (ret == 0) {
+ mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_MAILBOX,
+ mailbox_guid);
+ mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR,
+ dir_guid);
}
return 0;
}
#include "ioloop.h"
#include "mkdir-parents.h"
#include "str.h"
+#include "sha1.h"
#include "home-expand.h"
+#include "close-keep-errno.h"
+#include "eacces-error.h"
+#include "read-full.h"
+#include "write-full.h"
+#include "safe-mkstemp.h"
#include "unlink-directory.h"
#include "imap-match.h"
#include "imap-utf7.h"
+#include "mailbox-log.h"
#include "mailbox-tree.h"
#include "mail-storage-private.h"
#include "mailbox-list-private.h"
{
const struct mailbox_list *const *class_p;
struct mailbox_list *list;
+ const char *path;
unsigned int idx;
i_assert(ns->list == NULL);
mail_namespace_finish_list_init(ns, list);
+ path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX);
+ if (path != NULL) {
+ path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
+ list->changelog = mailbox_log_alloc(path);
+ }
+
if (hook_mailbox_list_created != NULL)
hook_mailbox_list_created(list);
return 0;
*_list = NULL;
i_free_and_null(list->error_string);
+ if (list->changelog != NULL)
+ mailbox_log_free(&list->changelog);
list->v.deinit(list);
}
flags_r);
}
+void mailbox_list_add_change(struct mailbox_list *list,
+ enum mailbox_log_record_type type,
+ const uint8_t mailbox_guid[MAIL_GUID_128_SIZE])
+{
+ struct mailbox_log_record rec;
+
+ if (list->changelog == NULL || mail_guid_128_is_empty(mailbox_guid))
+ return;
+
+ memset(&rec, 0, sizeof(rec));
+ rec.type = type;
+ memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid));
+ mailbox_log_record_set_timestamp(&rec, ioloop_time);
+ (void)mailbox_log_append(list->changelog, &rec);
+}
+
int mailbox_list_set_subscribed(struct mailbox_list *list,
const char *name, bool set)
{
+ uint8_t guid[MAIL_GUID_128_SIZE];
+ unsigned char sha[SHA1_RESULTLEN];
+
mailbox_list_clear_error(list);
- return list->v.set_subscribed(list, name, set);
+ if (list->v.set_subscribed(list, name, set) < 0)
+ return -1;
+
+ /* subscriptions are about names, not about mailboxes. it's possible
+ to have a subscription to non-existing mailbox. renames also don't
+ change subscriptions. so instead of using actual GUIDs, we'll use
+ hash of the name. */
+ sha1_get_digest(name, strlen(name), sha);
+ memcpy(guid, sha, I_MIN(sizeof(guid), sizeof(sha)));
+ mailbox_list_add_change(list, set ? MAILBOX_LOG_RECORD_SUBSCRIBE :
+ MAILBOX_LOG_RECORD_UNSUBSCRIBE, guid);
+ return 0;
}
int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
struct mail_storage *oldstorage;
struct mail_storage *newstorage;
+ uint8_t guid[MAIL_GUID_128_SIZE];
if (mailbox_list_get_storage(&oldlist, &oldname, &oldstorage) < 0)
return -1;
return -1;
}
- return oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname,
- rename_children);
+ (void)mailbox_list_get_guid(oldlist, oldname, guid);
+ if (oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children) < 0)
+ return -1;
+ mailbox_list_add_change(oldlist, MAILBOX_LOG_RECORD_RENAME, guid);
+ return 0;
}
static int mailbox_list_read_guid(struct mailbox_list *list, const char *path,
return ret;
}
+struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list)
+{
+ return list->changelog;
+}
+
static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir)
{
if (unlink_directory(dir, TRUE) == 0 || errno == ENOENT)