]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Fill mailbox log with mailbox delete/rename/(un)subscribes.
authorTimo Sirainen <tss@iki.fi>
Thu, 6 Aug 2009 00:29:24 +0000 (20:29 -0400)
committerTimo Sirainen <tss@iki.fi>
Thu, 6 Aug 2009 00:29:24 +0000 (20:29 -0400)
--HG--
branch : HEAD

src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c
src/lib-storage/mailbox-list.h

index d79a9963cde482bfb711b197bc189ff3f5f587cf..35d81ef9d4023d6d90f8e2c0b1ad6f313e2913f2 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -709,13 +710,80 @@ maildir_delete_nonrecursive(struct mailbox_list *list, const char *path,
        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
@@ -762,45 +830,16 @@ maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
        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;
 }
index d7aae18bb21eda237fb4286500c292d3164f2ccb..887064ceb2f8f812e7056de8040a8903838a5e72 100644 (file)
@@ -9,6 +9,9 @@
 #define MAILBOX_LIST_NAME_IMAPDIR "imapdir"
 #define MAILBOX_LIST_NAME_FS "fs"
 
+#define MAILBOX_LOG_FILE_NAME "dovecot.mailbox.log"
+
+enum mailbox_log_record_type;
 struct dirent;
 struct imap_match_glob;
 struct mailbox_tree_context;
@@ -95,6 +98,8 @@ struct mailbox_list {
        /* origin (e.g. path) where the file_create_gid was got from */
        const char *file_create_gid_origin;
 
+       struct mailbox_log *changelog;
+
        char *error_string;
        enum mail_error error;
        bool temporary_error;
@@ -144,6 +149,10 @@ enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d);
 bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
                                        const char **name);
 
+void mailbox_list_add_change(struct mailbox_list *list,
+                            enum mailbox_log_record_type type,
+                            const uint8_t mailbox_guid[MAIL_GUID_128_SIZE]);
+
 void mailbox_list_clear_error(struct mailbox_list *list);
 void mailbox_list_set_error(struct mailbox_list *list,
                            enum mail_error error, const char *string);
index e625255fe668219c49be32cff3cfe3878f7dd22e..ac7bbe31e907d52d8ee1e28196bc845e5a94f467 100644 (file)
@@ -5,10 +5,17 @@
 #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"
@@ -94,6 +101,7 @@ int mailbox_list_create(const char *driver, struct mail_namespace *ns,
 {
        const struct mailbox_list *const *class_p;
        struct mailbox_list *list;
+       const char *path;
        unsigned int idx;
 
        i_assert(ns->list == NULL);
@@ -162,6 +170,12 @@ int mailbox_list_create(const char *driver, struct mail_namespace *ns,
 
        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;
@@ -281,6 +295,8 @@ void mailbox_list_destroy(struct mailbox_list **_list)
        *_list = NULL;
        i_free_and_null(list->error_string);
 
+       if (list->changelog != NULL)
+               mailbox_log_free(&list->changelog);
        list->v.deinit(list);
 }
 
@@ -661,12 +677,42 @@ int mailbox_list_mailbox(struct mailbox_list *list, const char *name,
                                       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)
@@ -697,6 +743,7 @@ int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
 {
        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;
@@ -729,8 +776,12 @@ int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
                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,
@@ -836,6 +887,11 @@ int mailbox_list_get_guid(struct mailbox_list *list, const char *name,
        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)
index 6cf2fff22c1fa256db348eb0eba96d71647de03e..c20a0e8e3fa8099657b684bb1b4d09d66339bb27 100644 (file)
@@ -186,6 +186,9 @@ int mailbox_list_get_mailbox_name_status(struct mailbox_list *list,
 /* Get 128bit mailbox directory GUID, creating it if necessary. */
 int mailbox_list_get_guid(struct mailbox_list *list, const char *name,
                          uint8_t mailbox_guid[MAIL_GUID_128_SIZE]);
+/* Returns mailbox's change log, or NULL if it doesn't have one. */
+struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list);
+
 
 /* Returns a prefix that temporary files should use without conflicting
    with the namespace. */