]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
API change: Mailbox list sorting must now always done by storage itself if
authorTimo Sirainen <tss@iki.fi>
Sat, 26 Jul 2003 23:53:05 +0000 (02:53 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 26 Jul 2003 23:53:05 +0000 (02:53 +0300)
it's needed.

Maildir listing rewritten.

--HG--
branch : HEAD

src/imap/cmd-list.c
src/lib-storage/Makefile.am
src/lib-storage/index/maildir/maildir-list.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/maildir/maildir-storage.h
src/lib-storage/index/mbox/mbox-list.c
src/lib-storage/index/mbox/mbox-storage.h
src/lib-storage/mail-storage.h
src/lib-storage/mailbox-tree.c [new file with mode: 0644]
src/lib-storage/mailbox-tree.h [new file with mode: 0644]
src/lib-storage/proxy-mail-storage.c

index e5c70051ef52b254c015cb6e925a1159cb26ede2..0a7344965713237298e60b43f392ae1a0642a96d 100644 (file)
@@ -7,42 +7,15 @@
 #include "imap-match.h"
 #include "commands.h"
 
-struct list_node {
-       struct list_node *next;
-       struct list_node *children;
-
-       char *name; /* escaped */
-       enum mailbox_flags flags;
-};
-
-struct list_context {
-       pool_t pool;
-       struct list_node *nodes;
-       struct mail_storage *storage;
-};
-
-struct list_send_context {
-       struct client *client;
-       const char *response_name;
-       const char *sep;
-       char sep_chr;
-       struct imap_match_glob *glob;
-       int listext, no_placeholder;
-};
-
-static const char *mailbox_flags2str(enum mailbox_flags flags,
-                                    int listext, int no_placeholder)
+static const char *mailbox_flags2str(enum mailbox_flags flags, int listext)
 {
        const char *str;
 
        if (flags & MAILBOX_PLACEHOLDER) {
-               if ((flags & ~MAILBOX_CHILDREN) == MAILBOX_PLACEHOLDER) {
-                       if (!listext || no_placeholder)
-                               flags = MAILBOX_NOSELECT;
-               } else {
-                       /* it was at one point, but then we got better specs */
-                       flags &= ~MAILBOX_PLACEHOLDER;
-               }
+               i_assert((flags & ~MAILBOX_CHILDREN) == MAILBOX_PLACEHOLDER);
+
+               if (!listext)
+                       flags = MAILBOX_NOSELECT;
                flags |= MAILBOX_CHILDREN;
        }
        if ((flags & MAILBOX_NONEXISTENT) != 0 && !listext)
@@ -61,177 +34,33 @@ static const char *mailbox_flags2str(enum mailbox_flags flags,
        return *str == '\0' ? "" : str+1;
 }
 
-static void list_node_update(pool_t pool, struct list_node **node,
-                            const char *path, char separator,
-                            enum mailbox_flags flags)
-{
-       const char *name, *parent;
-
-       parent = NULL;
-
-       for (name = path;; path++) {
-               if (*path != separator && *path != '\0')
-                       continue;
-
-               t_push();
-
-               name = t_strdup_until(name, path);
-
-               /* find the node */
-               while (*node != NULL) {
-                       if (strcmp((*node)->name, name) == 0)
-                               break;
-
-                       node = &(*node)->next;
-               }
-
-               if (*node == NULL) {
-                       /* not found, create it */
-                       *node = p_new(pool, struct list_node, 1);
-                       (*node)->name = p_strdup(pool, name);
-                       (*node)->flags = *path == '\0' ? flags :
-                               MAILBOX_PLACEHOLDER;
-               } else {
-                       if (*path == '\0') {
-                               if (((*node)->flags & MAILBOX_NOSELECT) != 0 &&
-                                   (flags & MAILBOX_NOSELECT) == 0) {
-                                       /* overrides previous flag */
-                                       (*node)->flags &= ~MAILBOX_NOSELECT;
-                               }
-
-                               (*node)->flags |= flags;
-                       }
-               }
-
-               t_pop();
-
-               if (*path == '\0')
-                       break;
-
-               name = path+1;
-               parent = (*node)->name;
-               node = &(*node)->children;
-       }
-}
-
-static void list_send(struct list_send_context *ctx, struct list_node *node,
-                     const char *path)
-{
-       const char *name, *send_name, *flagstr;
-       enum imap_match_result match;
-       string_t *str;
-
-       for (; node != NULL; node = node->next) {
-               t_push();
-
-               /* Send INBOX always uppercased */
-               if (path != NULL) {
-                       name = t_strdup_printf("%s%c%s", path, ctx->sep_chr,
-                                              node->name);
-               } else if (strcasecmp(node->name, "INBOX") == 0)
-                       name = "INBOX";
-               else
-                       name = node->name;
-               send_name = name;
-
-               if ((node->flags & MAILBOX_PLACEHOLDER) == 0 &&
-                   (node->flags & MAILBOX_NOSELECT) == 0)
-                       match = IMAP_MATCH_YES;
-               else {
-                       /* make sure the placeholder matches. */
-                       const char *buf;
-
-                       buf = str_unescape(t_strdup_noconst(name));
-                       match = imap_match(ctx->glob, buf);
-                       /* FIXME: IMAP spec says this should be done, but
-                          a) this is broken, we shouldn't give \NoSelect for
-                             this folder if it actually works.
-                          b) at least mozilla's subscriptions list breaks if
-                             this is sent
-                          c) cyrus and courier doesn't do this either..
-
-                       if (match == IMAP_MATCH_CHILDREN) {
-                               send_name = t_strdup_printf("%s%c", name,
-                                                           ctx->sep);
-                               buf = str_unescape(t_strdup_noconst(send_name));
-                               match = imap_match(ctx->glob, buf);
-                       }*/
-               }
-
-               if (match == IMAP_MATCH_YES) {
-                       /* node->name should already be escaped */
-                       flagstr = mailbox_flags2str(node->flags, ctx->listext,
-                                                   ctx->no_placeholder);
-                       t_push();
-                       str = t_str_new(256);
-                       str_printfa(str, "* %s (%s) \"%s\" ",
-                                   ctx->response_name, flagstr, ctx->sep);
-                       imap_quote_append_string(str, send_name, FALSE);
-                       client_send_line(ctx->client, str_c(str));
-                       t_pop();
-               }
-
-               if (node->children != NULL)
-                       list_send(ctx, node->children,  name);
-
-               t_pop();
-       }
-}
-
-static void list_and_sort(struct client *client,
-                         struct mailbox_list_context *ctx,
-                         const char *response_name, const char *mask,
-                         const char *sep, char sep_chr,
-                         enum mailbox_list_flags list_flags, int listext)
-{
-       struct mailbox_list *list;
-       struct list_node *nodes;
-       struct list_send_context send_ctx;
-       pool_t pool;
-
-       pool = pool_alloconly_create("list_mailboxes", 10240);
-       nodes = NULL;
-
-       while ((list = client->storage->list_mailbox_next(ctx)) != NULL) {
-               list_node_update(pool, &nodes, list->name,
-                                client->storage->hierarchy_sep,
-                                list->flags);
-       }
-
-       send_ctx.client = client;
-       send_ctx.response_name = response_name;
-       send_ctx.sep = sep;
-       send_ctx.sep_chr = sep_chr;
-       send_ctx.glob = imap_match_init(data_stack_pool, mask, TRUE,
-                                       client->storage->hierarchy_sep);
-       send_ctx.listext = listext;
-       send_ctx.no_placeholder = (list_flags & MAILBOX_LIST_SUBSCRIBED) == 0;
-
-       list_send(&send_ctx, nodes, NULL);
-       imap_match_deinit(send_ctx.glob);
-       pool_unref(pool);
-}
-
-static void list_unsorted(struct client *client,
-                         struct mailbox_list_context *ctx,
-                         const char *reply, const char *sep, int listext)
+static int mailbox_list(struct client *client, const char *mask,
+                       const char *sep, const char *reply,
+                       enum mailbox_list_flags list_flags, int listext)
 {
+       struct mailbox_list_context *ctx;
        struct mailbox_list *list;
        string_t *str;
 
+       ctx = client->storage->list_mailbox_init(client->storage, mask,
+                                                list_flags);
+       if (ctx == NULL)
+               return FALSE;
+
+       str = t_str_new(256);
        while ((list = client->storage->list_mailbox_next(ctx)) != NULL) {
-               t_push();
-               str = t_str_new(256);
+               str_truncate(str, 0);
                str_printfa(str, "* %s (%s) \"%s\" ", reply,
-                           mailbox_flags2str(list->flags, listext, FALSE),
+                           mailbox_flags2str(list->flags, listext),
                            sep);
                if (strcasecmp(list->name, "INBOX") == 0)
                        str_append(str, "INBOX");
                else
                        imap_quote_append_string(str, list->name, FALSE);
                client_send_line(client, str_c(str));
-               t_pop();
        }
+
+       return client->storage->list_mailbox_deinit(ctx);
 }
 
 static int parse_list_flags(struct client *client, struct imap_arg *args,
@@ -266,10 +95,9 @@ int _cmd_list_full(struct client *client, int lsub)
 {
        struct imap_arg *args;
         enum mailbox_list_flags list_flags;
-       struct mailbox_list_context *ctx;
        const char *ref, *mask;
        char sep_chr, sep[3];
-       int failed, sorted, listext;
+       int failed, listext;
 
        sep_chr = client->storage->hierarchy_sep;
        if (IS_ESCAPED_CHAR(sep_chr)) {
@@ -330,24 +158,9 @@ int _cmd_list_full(struct client *client, int lsub)
                        }
                }
 
-               ctx = client->storage->list_mailbox_init(client->storage, mask,
-                                                        list_flags, &sorted);
-               if (ctx == NULL)
-                       failed = TRUE;
-               else {
-                       const char *response_name = lsub ? "LSUB" : "LIST";
-
-                       if (sorted) {
-                               list_unsorted(client, ctx, response_name, sep,
-                                             listext);
-                       } else {
-                               list_and_sort(client, ctx, response_name, mask,
-                                             sep, sep_chr, list_flags,
-                                             listext);
-                       }
-
-                       failed = !client->storage->list_mailbox_deinit(ctx);
-               }
+               failed = !mailbox_list(client, mask, sep,
+                                      lsub ? "LSUB" : "LIST",
+                                      list_flags, listext);
        }
 
        if (failed)
index 01fe71e3c7a76b1041d81f33df1cad3966d98b4e..429bb15ddc7289daec5a47dc8e53851c999fd1ec 100644 (file)
@@ -11,6 +11,7 @@ libstorage_a_SOURCES = \
        mail-save.c \
        mail-search.c \
        mail-storage.c \
+       mailbox-tree.c \
        proxy-mail.c \
        proxy-mail-storage.c \
        proxy-mailbox.c
@@ -19,6 +20,7 @@ noinst_HEADERS = \
        mail-save.h \
        mail-search.h \
        mail-storage.h \
+       mailbox-tree.h \
        proxy-mail.h \
        proxy-mail-storage.h \
        proxy-mailbox.h
index ab2bfac33382810d578c8fcdeaaf0e7dbb3ac600..5a0b9a449bde682940f88c144cd8842e4b082899 100644 (file)
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "hostpid.h"
+#include "ioloop.h"
+#include "str.h"
 #include "home-expand.h"
 #include "unlink-directory.h"
 #include "imap-match.h"
 #include "subscription-file/subscription-file.h"
-#include "maildir-index.h"
 #include "maildir-storage.h"
+#include "mailbox-tree.h"
 
 #include <dirent.h>
 #include <sys/stat.h>
 
+#define MAILBOX_FLAG_MATCHED 0x40000000
+
 struct mailbox_list_context {
-       pool_t pool, list_pool;
+       pool_t pool;
 
        struct mail_storage *storage;
        const char *dir, *prefix;
         enum mailbox_list_flags flags;
 
-       DIR *dirp;
-       struct imap_match_glob *glob;
-       struct subsfile_list_context *subsfile_ctx;
-
-       struct mailbox_list *(*next)(struct mailbox_list_context *ctx);
+        struct mailbox_tree_context *tree_ctx;
 
+       string_t *node_path;
+       size_t parent_pos;
+       struct mailbox_node *root, *next_node;
        struct mailbox_list list;
        int failed;
 };
 
-static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx);
-static struct mailbox_list *maildir_list_next(struct mailbox_list_context *ctx);
+static void maildir_nodes_fix(struct mailbox_node *node, int is_subs)
+{
+       while (node != NULL) {
+               if (node->children != NULL) {
+                       node->flags |= MAILBOX_CHILDREN;
+                       maildir_nodes_fix(node->children, is_subs);
+               } else if ((node->flags & MAILBOX_PLACEHOLDER) != 0) {
+                       if (!is_subs) {
+                               node->flags &= ~MAILBOX_PLACEHOLDER;
+                               node->flags |= MAILBOX_NOSELECT;
+                       }
+               } else {
+                       if ((node->flags & MAILBOX_CHILDREN) == 0)
+                               node->flags |= MAILBOX_NOCHILDREN;
+               }
+               node = node->next;
+       }
+}
 
-static enum mailbox_flags maildir_get_marked_flags(const char *dir)
+static int maildir_fill_readdir(struct mailbox_list_context *ctx,
+                               struct imap_match_glob *glob, int update_only)
 {
-       struct stat st_new, st_cur;
+       DIR *dirp;
+       struct dirent *d;
+       const char *path, *p;
+       string_t *mailbox;
+       enum imap_match_result match;
+       struct mailbox_node *node;
+       int created;
+
+       dirp = opendir(ctx->dir);
+       if (dirp == NULL) {
+               if (errno != ENOENT) {
+                       mail_storage_set_critical(ctx->storage,
+                               "opendir(%s) failed: %m", ctx->dir);
+                       return FALSE;
+               }
+       }
 
-       /* assume marked if new/ has been modified later than cur/ */
-       if (stat(t_strconcat(dir, "/new", NULL), &st_new) < 0)
-               return MAILBOX_UNMARKED;
+       /* INBOX exists always */
+       if (imap_match(glob, "INBOX") > 0 && !update_only) {
+               node = mailbox_tree_get(ctx->tree_ctx, "INBOX", NULL);
+               node->flags |= MAILBOX_FLAG_MATCHED;
+               node->flags &= ~(MAILBOX_PLACEHOLDER | MAILBOX_NONEXISTENT);
+       }
 
-       if (stat(t_strconcat(dir, "/cur", NULL), &st_cur) < 0)
-               return MAILBOX_UNMARKED;
+       mailbox = t_str_new(PATH_MAX);
+       while ((d = readdir(dirp)) != NULL) {
+               const char *fname = d->d_name;
+
+               if (fname[0] != '.')
+                       continue;
+
+               /* skip . and .. */
+               if (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))
+                       continue;
+
+               /* FIXME: kludges. these files must be renamed later */
+               if (strcmp(fname, ".customflags") == 0 ||
+                   strcmp(fname, ".subscriptions") == 0)
+                       continue;
+
+               fname++;
+               if (*fname == '.') {
+                       /* this mailbox is in the middle of being deleted,
+                          or the process trying to delete it had died.
+
+                          delete it ourself if it's been there longer than
+                          one hour. don't touch it if it's outside our
+                          mail root dir. */
+                       struct stat st;
+
+                       if (*ctx->prefix == '\0')
+                               continue;
+
+                       t_push();
+                       path = t_strdup_printf("%s/%s", ctx->dir, fname);
+                       if (stat(path, &st) == 0 &&
+                           st.st_mtime < ioloop_time - 3600)
+                               (void)unlink_directory(path, TRUE);
+                       t_pop();
+                       continue;
+               }
+
+               /* make sure the mask matches */
+               str_truncate(mailbox, 0);
+               str_append(mailbox, ctx->prefix);
+               str_append(mailbox, fname);
+
+               match = imap_match(glob, str_c(mailbox));
+
+               if (match != IMAP_MATCH_YES &&
+                   (match != IMAP_MATCH_PARENT || update_only))
+                       continue;
+
+               if (strcasecmp(str_c(mailbox), "INBOX") == 0)
+                       continue; /* ignore inboxes */
+
+               if (match == IMAP_MATCH_PARENT) {
+                       t_push();
+                       while ((p = strrchr(fname, '.')) != NULL) {
+                               fname = t_strdup_until(fname, p);
+                               p = t_strconcat(ctx->prefix, fname, NULL);
+                               if (imap_match(glob, p) > 0)
+                                       break;
+                       }
+                       i_assert(p != NULL);
+
+                       node = mailbox_tree_get(ctx->tree_ctx, p, &created);
+                       if (created)
+                               node->flags = MAILBOX_PLACEHOLDER;
+                       node->flags |= MAILBOX_CHILDREN | MAILBOX_FLAG_MATCHED;
+
+                       t_pop();
+               } else {
+                       p = str_c(mailbox);
+                       if (update_only)
+                               node = mailbox_tree_update(ctx->tree_ctx, p);
+                       else
+                               node = mailbox_tree_get(ctx->tree_ctx, p, NULL);
+
+                       if (node != NULL) {
+                               node->flags &= ~(MAILBOX_PLACEHOLDER |
+                                                MAILBOX_NONEXISTENT);
+                               node->flags |= MAILBOX_FLAG_MATCHED;
+                       }
+               }
+       }
+
+       if (closedir(dirp) < 0) {
+               mail_storage_set_critical(ctx->storage,
+                                         "readdir(%s) failed: %m", ctx->dir);
+               return FALSE;
+       }
+
+       maildir_nodes_fix(mailbox_tree_get(ctx->tree_ctx, NULL, NULL),
+                         (ctx->flags & MAILBOX_LIST_SUBSCRIBED) != 0);
+       return TRUE;
+}
+
+static int maildir_fill_subscribed(struct mailbox_list_context *ctx,
+                                  struct imap_match_glob *glob,
+                                  int nonexistent)
+{
+       struct subsfile_list_context *subsfile_ctx;
+       const char *name, *p;
+       struct mailbox_node *node;
+       int created;
+
+       subsfile_ctx = subsfile_list_init(ctx->storage);
+       if (subsfile_ctx == NULL)
+               return FALSE;
+
+       while ((name = subsfile_list_next(subsfile_ctx)) != NULL) {
+               switch (imap_match(glob, name)) {
+               case IMAP_MATCH_YES:
+                       node = mailbox_tree_get(ctx->tree_ctx, name, NULL);
+                       node->flags = MAILBOX_FLAG_MATCHED;
+                       if (nonexistent && strcasecmp(name, "INBOX") != 0)
+                               node->flags |= MAILBOX_NONEXISTENT;
+                       break;
+               case IMAP_MATCH_PARENT:
+                       /* placeholder */
+                       while ((p = strrchr(name, '.')) != NULL) {
+                               name = t_strdup_until(name, p);
+                               if (imap_match(glob, name) > 0)
+                                       break;
+                       }
+                       i_assert(p != NULL);
+
+                       node = mailbox_tree_get(ctx->tree_ctx, name, &created);
+                       if (created) node->flags = MAILBOX_PLACEHOLDER;
+                       node->flags |= MAILBOX_FLAG_MATCHED;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return subsfile_list_deinit(subsfile_ctx);
 
-       return st_new.st_mtime <= st_cur.st_mtime ?
-               MAILBOX_UNMARKED : MAILBOX_MARKED;
 }
 
 struct mailbox_list_context *
 maildir_list_mailbox_init(struct mail_storage *storage,
-                         const char *mask, enum mailbox_list_flags flags,
-                         int *sorted)
+                         const char *mask, enum mailbox_list_flags flags)
 {
         struct mailbox_list_context *ctx;
-       pool_t pool;
+        struct imap_match_glob *glob;
        const char *dir, *p;
+       int nonexistent;
+       pool_t pool;
 
-       *sorted = FALSE;
        mail_storage_clear_error(storage);
 
        pool = pool_alloconly_create("maildir_list", 1024);
@@ -64,219 +231,115 @@ maildir_list_mailbox_init(struct mail_storage *storage,
        ctx->pool = pool;
        ctx->storage = storage;
        ctx->flags = flags;
+       ctx->tree_ctx = mailbox_tree_init('.');
+
+       glob = imap_match_init(pool, mask, TRUE, '.');
 
        if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-               ctx->glob = imap_match_init(pool, mask, TRUE, '.');
-               ctx->subsfile_ctx = subsfile_list_init(storage);
-               ctx->next = maildir_list_subs;
-               if (ctx->subsfile_ctx == NULL) {
+               ctx->dir = storage->dir;
+               ctx->prefix = "";
+
+               nonexistent = (flags & MAILBOX_LIST_FAST_FLAGS) == 0;
+               if (!maildir_fill_subscribed(ctx, glob, nonexistent)) {
+                        mailbox_tree_deinit(ctx->tree_ctx);
                        pool_unref(pool);
                        return NULL;
                }
-               return ctx;
-       }
-
-       if (!full_filesystem_access || (p = strrchr(mask, '/')) == NULL) {
-               ctx->dir = storage->dir;
-               ctx->prefix = "";
        } else {
-               dir = t_strdup_until(mask, p);
-               ctx->prefix = t_strdup_until(mask, p+1);
-
-               if (*mask != '/' && *mask != '~')
-                       dir = t_strconcat(storage->dir, "/", dir, NULL);
-               ctx->dir = p_strdup(pool, home_expand(dir));
+               if (!full_filesystem_access ||
+                   (p = strrchr(mask, '/')) == NULL) {
+                       ctx->dir = storage->dir;
+                       ctx->prefix = "";
+               } else {
+                       dir = t_strdup_until(mask, p);
+                       ctx->prefix = t_strdup_until(mask, p+1);
+
+                       if (*mask != '/' && *mask != '~')
+                               dir = t_strconcat(storage->dir, "/", dir, NULL);
+                       ctx->dir = p_strdup(pool, home_expand(dir));
+               }
        }
 
-       ctx->dirp = opendir(ctx->dir);
-       if (ctx->dirp == NULL && errno != ENOENT) {
-               mail_storage_set_critical(storage, "opendir(%s) failed: %m",
-                                         ctx->dir);
-               pool_unref(pool);
-               return NULL;
+       if ((flags & MAILBOX_LIST_SUBSCRIBED) == 0 ||
+           (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0) {
+               int update_only = (flags & MAILBOX_LIST_SUBSCRIBED) != 0;
+               if (!maildir_fill_readdir(ctx, glob, update_only)) {
+                       mailbox_tree_deinit(ctx->tree_ctx);
+                       pool_unref(pool);
+                       return NULL;
+               }
        }
 
-       ctx->list_pool = pool_alloconly_create("maildir_list.list", 4096);
-       ctx->glob = imap_match_init(pool, mask, TRUE, '.');
-       ctx->next = maildir_list_next;
+       ctx->node_path = str_new(pool, 256);
+       ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL);
        return ctx;
 }
 
 int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx)
 {
-       int failed;
-
-       if (ctx->subsfile_ctx != NULL)
-               failed = !subsfile_list_deinit(ctx->subsfile_ctx);
-       else
-               failed = ctx->failed;
-
-       if (ctx->dirp != NULL)
-               (void)closedir(ctx->dirp);
-       if (ctx->list_pool != NULL)
-               pool_unref(ctx->list_pool);
-       imap_match_deinit(ctx->glob);
+       mailbox_tree_deinit(ctx->tree_ctx);
        pool_unref(ctx->pool);
-
-       return !failed;
+       return TRUE;
 }
 
-static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx)
+static struct mailbox_node *find_next(struct mailbox_node **node,
+                                     string_t *path)
 {
-       struct stat st;
-       const char *name, *path, *p;
-       enum imap_match_result match = IMAP_MATCH_NO;
+       struct mailbox_node *child;
+       size_t len;
 
-       while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
-               match = imap_match(ctx->glob, name);
-               if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
-                       break;
-       }
+       while (*node != NULL) {
+               if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0)
+                       return *node;
 
-       if (name == NULL)
-               return NULL;
+               if ((*node)->children != NULL) {
+                       len = str_len(path);
+                       if (len != 0)
+                               str_append_c(path, '.');
+                       str_append(path, (*node)->name);
 
-       ctx->list.flags = 0;
-       ctx->list.name = name;
+                       child = find_next(&(*node)->children, path);
+                       if (child != NULL)
+                               return child;
 
-       if (match == IMAP_MATCH_PARENT) {
-               /* placeholder */
-               ctx->list.flags = MAILBOX_PLACEHOLDER;
-               while ((p = strrchr(name, '.')) != NULL) {
-                       name = t_strdup_until(name, p);
-                       if (imap_match(ctx->glob, name) > 0) {
-                               ctx->list.name = name;
-                               return &ctx->list;
-                       }
+                       str_truncate(path, len);
                }
-               i_unreached();
-       }
 
-       if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) != 0)
-               return &ctx->list;
-
-       t_push();
-       path = maildir_get_path(ctx->storage, ctx->list.name);
-       if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
-               ctx->list.flags = maildir_get_marked_flags(path);
-       else {
-               if (strcasecmp(ctx->list.name, "INBOX") == 0)
-                       ctx->list.flags = 0;
-               else
-                       ctx->list.flags = MAILBOX_NONEXISTENT;
+               *node = (*node)->next;
        }
-       t_pop();
-       return &ctx->list;
+
+       return NULL;
 }
 
-static struct mailbox_list *maildir_list_next(struct mailbox_list_context *ctx)
+struct mailbox_list *
+maildir_list_mailbox_next(struct mailbox_list_context *ctx)
 {
-       struct dirent *d;
-       struct stat st;
-       const char *fname, *p;
-       char path[PATH_MAX];
-       enum imap_match_result match;
-
-       if (ctx->dirp == NULL)
-               return NULL;
-
-       while ((d = readdir(ctx->dirp)) != NULL) {
-               fname = d->d_name;
+       struct mailbox_node *node;
 
-               if (fname[0] != '.')
-                       continue;
-
-               /* skip . and .. */
-               if (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))
-                       continue;
-
-               /* make sure the mask matches - dirs beginning with ".."
-                  should be deleted and we always want to check those. */
-               t_push();
-               match = imap_match(ctx->glob,
-                                  t_strconcat(ctx->prefix, fname+1, NULL));
-               t_pop();
-               if (fname[1] != '.' && match != IMAP_MATCH_YES &&
-                   match != IMAP_MATCH_PARENT)
-                       continue;
-
-               if (str_path(path, sizeof(path), ctx->dir, fname) < 0)
-                       continue;
+       for (node = ctx->next_node; node != NULL; node = node->next) {
+               if ((node->flags & MAILBOX_FLAG_MATCHED) != 0)
+                       break;
+       }
 
-               /* make sure it's a directory */
-               if (stat(path, &st) < 0) {
-                       if (errno == ENOENT)
-                               continue; /* just deleted, ignore */
+       if (node == NULL) {
+               str_truncate(ctx->node_path, 0);
+               node = find_next(&ctx->root, ctx->node_path);
+                ctx->parent_pos = str_len(ctx->node_path);
 
-                       mail_storage_set_critical(ctx->storage,
-                                                 "stat(%s) failed: %m", path);
-                       ctx->failed = TRUE;
+               if (node == NULL)
                        return NULL;
-               }
-
-               if (!S_ISDIR(st.st_mode))
-                       continue;
-
-               fname++;
-               if (*fname == '.') {
-                       /* this mailbox is in the middle of being deleted,
-                          or the process trying to delete it had died.
-
-                          delete it ourself if it's been there longer than
-                          one hour. don't touch it if it's outside our
-                          mail root dir. */
-                       if (st.st_mtime < 3600 && *ctx->prefix == '\0')
-                               (void)unlink_directory(path, TRUE);
-                       continue;
-               }
-
-               if (strcasecmp(fname, "INBOX") == 0)
-                       continue; /* ignore inboxes */
-
-               if (match == IMAP_MATCH_PARENT) {
-                       ctx->list.flags =
-                               MAILBOX_PLACEHOLDER | MAILBOX_CHILDREN;
-                       while ((p = strrchr(fname, '.')) != NULL) {
-                               fname = t_strdup_until(fname, p);
-                               p = t_strconcat(ctx->prefix, fname, NULL);
-                               if (imap_match(ctx->glob, p) > 0) {
-                                       ctx->list.name = p;
-                                       return &ctx->list;
-                               }
-                       }
-                       i_unreached();
-               }
-
-               p_clear(ctx->list_pool);
-                ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ?
-                       maildir_get_marked_flags(path) : 0;
-               ctx->list.name = p_strconcat(ctx->list_pool,
-                                            ctx->prefix, fname, NULL);
-               return &ctx->list;
        }
+       ctx->next_node = node->next;
 
-       if (closedir(ctx->dirp) < 0) {
-               mail_storage_set_critical(ctx->storage,
-                                         "closedir(%s) failed: %m", ctx->dir);
-               ctx->failed = TRUE;
-       }
-       ctx->dirp = NULL;
+       i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0);
+       node->flags &= ~MAILBOX_FLAG_MATCHED;
 
-       if (imap_match(ctx->glob, "INBOX") > 0) {
-               const char *path = maildir_get_path(ctx->storage, "INBOX");
+       str_truncate(ctx->node_path, ctx->parent_pos);
+       if (ctx->parent_pos != 0)
+               str_append_c(ctx->node_path, '.');
+       str_append(ctx->node_path, node->name);
 
-               ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ?
-                       maildir_get_marked_flags(path) : 0;
-               ctx->list.name = "INBOX";
-               return &ctx->list;
-       }
-
-       /* we're finished */
-       return NULL;
-}
-
-struct mailbox_list *
-maildir_list_mailbox_next(struct mailbox_list_context *ctx)
-{
-       return ctx->next(ctx);
+       ctx->list.name = str_c(ctx->node_path);
+       ctx->list.flags = node->flags;
+       return &ctx->list;
 }
index 7cf0796184dc04e952efba10ca2b0aecdf3ac9d7..d7c627c0defffcc9ea764ab5034aaed4e84c89b7 100644 (file)
@@ -536,14 +536,14 @@ static int rename_subfolders(struct mail_storage *storage,
         struct mailbox_list *list;
        const char *oldpath, *newpath, *new_listname;
        size_t oldnamelen;
-       int sorted, ret;
+       int ret;
 
        ret = 0;
        oldnamelen = strlen(oldname);
 
        ctx = storage->list_mailbox_init(storage,
                                         t_strconcat(oldname, ".*", NULL),
-                                        MAILBOX_LIST_FAST_FLAGS, &sorted);
+                                        MAILBOX_LIST_FAST_FLAGS);
        while ((list = maildir_list_mailbox_next(ctx)) != NULL) {
                i_assert(oldnamelen <= strlen(list->name));
 
index 9129333208becf682a34272de6af3c0dd6f1c876..f1bdd3f9f60e5a6f971de493a1222d34f6878a3a 100644 (file)
@@ -17,8 +17,7 @@ int maildir_storage_save_next(struct mail_save_context *ctx,
 
 struct mailbox_list_context *
 maildir_list_mailbox_init(struct mail_storage *storage,
-                         const char *mask, enum mailbox_list_flags flags,
-                         int *sorted);
+                         const char *mask, enum mailbox_list_flags flags);
 int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx);
 struct mailbox_list *
 maildir_list_mailbox_next(struct mailbox_list_context *ctx);
index 93fa55d3f1005010e9c6a211cec6987dedcb2483..c700f7b95d937d075ca47dad51eabb8d1a59b938 100644 (file)
@@ -98,14 +98,12 @@ static int list_opendir(struct mail_storage *storage,
 
 struct mailbox_list_context *
 mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
-                      enum mailbox_list_flags flags, int *sorted)
+                      enum mailbox_list_flags flags)
 {
        struct mailbox_list_context *ctx;
        const char *path, *virtual_path;
        DIR *dirp;
 
-       *sorted = (flags & MAILBOX_LIST_SUBSCRIBED) == 0;
-
        /* check that we're not trying to do any "../../" lists */
        if (!mbox_is_valid_mask(mask)) {
                mail_storage_set_error(storage, "Invalid mask");
index 7580dbeaf1cdb68d59fcbd642f8275089f6dae49..1fed72efe19cee271b17a84f0f180cd45531c167 100644 (file)
@@ -16,7 +16,7 @@ int mbox_storage_save_next(struct mail_save_context *ctx,
 
 struct mailbox_list_context *
 mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
-                      enum mailbox_list_flags flags, int *sorted);
+                      enum mailbox_list_flags flags);
 int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx);
 struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx);
 
index d1ca820d19faa2c830604c10770be8619ba89a0e..a7552bb841a457d60ca99b3c31db68258dc041bc 100644 (file)
@@ -179,14 +179,11 @@ struct mail_storage {
 
        /* Initialize new mailbox list request. mask may contain '%' and '*'
           wildcards as defined in RFC2060. Matching against "INBOX" is
-          case-insensitive, but anything else is not. *sorted is set to TRUE
-          if the output will contain parent mailboxes always before their
-          children. */
+          case-insensitive, but anything else is not. */
        struct mailbox_list_context *
                (*list_mailbox_init)(struct mail_storage *storage,
                                     const char *mask,
-                                    enum mailbox_list_flags flags,
-                                    int *sorted);
+                                    enum mailbox_list_flags flags);
        /* Deinitialize mailbox list request. Returns FALSE if some error
           occured while listing. */
        int (*list_mailbox_deinit)(struct mailbox_list_context *ctx);
diff --git a/src/lib-storage/mailbox-tree.c b/src/lib-storage/mailbox-tree.c
new file mode 100644 (file)
index 0000000..3a7d916
--- /dev/null
@@ -0,0 +1,108 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "lib.h"
+#include "str.h"
+#include "mailbox-tree.h"
+
+struct mailbox_tree_context {
+       pool_t pool;
+       char separator;
+       struct mailbox_node *nodes;
+};
+
+struct mailbox_tree_context *mailbox_tree_init(char separator)
+{
+       struct mailbox_tree_context *ctx;
+       pool_t pool;
+
+       pool = pool_alloconly_create("mailbox_tree", 10240);
+
+       ctx = p_new(pool, struct mailbox_tree_context, 1);
+       ctx->pool = pool;
+       ctx->separator = separator;
+       return ctx;
+}
+
+void mailbox_tree_deinit(struct mailbox_tree_context *ctx)
+{
+       pool_unref(ctx->pool);
+}
+
+static struct mailbox_node *
+mailbox_tree_traverse(struct mailbox_tree_context *ctx, const char *path,
+                     int create, int *created)
+{
+       struct mailbox_node **node;
+       const char *name;
+       string_t *str;
+
+       if (created != NULL)
+               *created = FALSE;
+
+       if (path == NULL)
+               return ctx->nodes;
+
+       t_push();
+
+       if (strncasecmp(path, "INBOX", 5) == 0 &&
+           (path[5] == '\0' || path[5] == ctx->separator))
+               path = t_strdup_printf("INBOX%s", path+5);
+
+       node = &ctx->nodes;
+
+       str = t_str_new(strlen(path)+1);
+       for (name = path;; path++) {
+               if (*path != ctx->separator && *path != '\0')
+                       continue;
+
+               str_truncate(str, 0);
+               str_append_n(str, name, (size_t) (path - name));
+               name = str_c(str);
+
+               /* find the node */
+               while (*node != NULL) {
+                       if (strcmp((*node)->name, name) == 0)
+                               break;
+
+                       node = &(*node)->next;
+               }
+
+               if (*node == NULL) {
+                       /* not found, create it */
+                       if (!create)
+                               break;
+
+                       *node = p_new(ctx->pool, struct mailbox_node, 1);
+                       (*node)->name = p_strdup(ctx->pool, name);
+
+                       if (*path != '\0')
+                               (*node)->flags = MAILBOX_PLACEHOLDER;
+                       else {
+                               if (created != NULL)
+                                       *created = TRUE;
+                       }
+               }
+
+               if (*path == '\0')
+                       break;
+
+               name = path+1;
+               node = &(*node)->children;
+       }
+       t_pop();
+
+       return *node;
+}
+
+struct mailbox_node *
+mailbox_tree_get(struct mailbox_tree_context *ctx, const char *path,
+                int *created)
+{
+       return mailbox_tree_traverse(ctx, path, TRUE, created);
+}
+
+struct mailbox_node *
+mailbox_tree_update(struct mailbox_tree_context *ctx, const char *path)
+{
+       return mailbox_tree_traverse(ctx, path, FALSE, NULL);
+}
diff --git a/src/lib-storage/mailbox-tree.h b/src/lib-storage/mailbox-tree.h
new file mode 100644 (file)
index 0000000..af4e639
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __MAILBOX_TREE_H
+#define __MAILBOX_TREE_H
+
+#include "mail-storage.h"
+
+struct mailbox_node {
+       struct mailbox_node *next;
+       struct mailbox_node *children;
+
+       char *name;
+       enum mailbox_flags flags;
+};
+
+struct mailbox_tree_context *mailbox_tree_init(char separator);
+void mailbox_tree_deinit(struct mailbox_tree_context *ctx);
+
+struct mailbox_node *
+mailbox_tree_get(struct mailbox_tree_context *ctx, const char *path,
+                int *created);
+
+struct mailbox_node *
+mailbox_tree_update(struct mailbox_tree_context *ctx, const char *path);
+
+#endif
index b5e102776526efc599b4f6aa5e09f747a0f6416d..287560f84f81c0c8b92387e3d87f6766afb865c4 100644 (file)
@@ -53,11 +53,11 @@ static int _rename_mailbox(struct mail_storage *storage, const char *oldname,
 
 static struct mailbox_list_context *
 _list_mailbox_init(struct mail_storage *storage, const char *mask,
-                  enum mailbox_list_flags flags, int *sorted)
+                  enum mailbox_list_flags flags)
 {
        struct proxy_mail_storage *s = (struct proxy_mail_storage *) storage;
 
-       return s->storage->list_mailbox_init(s->storage, mask, flags, sorted);
+       return s->storage->list_mailbox_init(s->storage, mask, flags);
 }
 
 static int _set_subscribed(struct mail_storage *storage,