Mercurial > dovecot > original-hg > dovecot-1.2
changeset 1650:bc76e08a9c9d HEAD
API change: Mailbox list sorting must now always done by storage itself if
it's needed.
Maildir listing rewritten.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 27 Jul 2003 02:53:05 +0300 |
parents | 27f68eecfb35 |
children | 43fdcf8d9a0d |
files | 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 src/lib-storage/mailbox-tree.h src/lib-storage/proxy-mail-storage.c |
diffstat | 11 files changed, 433 insertions(+), 429 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap/cmd-list.c Sun Jul 27 02:28:05 2003 +0300 +++ b/src/imap/cmd-list.c Sun Jul 27 02:53:05 2003 +0300 @@ -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 @@ 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) +static int mailbox_list(struct client *client, const char *mask, + const char *sep, const char *reply, + enum mailbox_list_flags list_flags, int listext) { - 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) -{ + 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 @@ { 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 @@ } } - 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)
--- a/src/lib-storage/Makefile.am Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/Makefile.am Sun Jul 27 02:53:05 2003 +0300 @@ -11,6 +11,7 @@ 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 @@ mail-save.h \ mail-search.h \ mail-storage.h \ + mailbox-tree.h \ proxy-mail.h \ proxy-mail-storage.h \ proxy-mailbox.h
--- a/src/lib-storage/index/maildir/maildir-list.c Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-list.c Sun Jul 27 02:53:05 2003 +0300 @@ -1,187 +1,85 @@ /* 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_tree_context *tree_ctx; - struct mailbox_list *(*next)(struct mailbox_list_context *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 enum mailbox_flags maildir_get_marked_flags(const char *dir) -{ - struct stat st_new, st_cur; - - /* assume marked if new/ has been modified later than cur/ */ - if (stat(t_strconcat(dir, "/new", NULL), &st_new) < 0) - return MAILBOX_UNMARKED; - - if (stat(t_strconcat(dir, "/cur", NULL), &st_cur) < 0) - return MAILBOX_UNMARKED; - - 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) +static void maildir_nodes_fix(struct mailbox_node *node, int is_subs) { - struct mailbox_list_context *ctx; - pool_t pool; - const char *dir, *p; - - *sorted = FALSE; - mail_storage_clear_error(storage); - - pool = pool_alloconly_create("maildir_list", 1024); - ctx = p_new(pool, struct mailbox_list_context, 1); - ctx->pool = pool; - ctx->storage = storage; - ctx->flags = flags; - - 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) { - pool_unref(pool); - return NULL; + 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; } - return ctx; + node = node->next; } - - 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; - } - - ctx->list_pool = pool_alloconly_create("maildir_list.list", 4096); - ctx->glob = imap_match_init(pool, mask, TRUE, '.'); - ctx->next = maildir_list_next; - return ctx; } -int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx) +static int maildir_fill_readdir(struct mailbox_list_context *ctx, + struct imap_match_glob *glob, int update_only) { - int failed; - - if (ctx->subsfile_ctx != NULL) - failed = !subsfile_list_deinit(ctx->subsfile_ctx); - else - failed = ctx->failed; + DIR *dirp; + struct dirent *d; + const char *path, *p; + string_t *mailbox; + enum imap_match_result match; + struct mailbox_node *node; + int created; - if (ctx->dirp != NULL) - (void)closedir(ctx->dirp); - if (ctx->list_pool != NULL) - pool_unref(ctx->list_pool); - imap_match_deinit(ctx->glob); - pool_unref(ctx->pool); - - return !failed; -} - -static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx) -{ - struct stat st; - const char *name, *path, *p; - enum imap_match_result match = IMAP_MATCH_NO; - - 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; + dirp = opendir(ctx->dir); + if (dirp == NULL) { + if (errno != ENOENT) { + mail_storage_set_critical(ctx->storage, + "opendir(%s) failed: %m", ctx->dir); + return FALSE; + } } - if (name == NULL) - return NULL; - - ctx->list.flags = 0; - ctx->list.name = name; - - 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; - } - } - i_unreached(); + /* 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 ((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; - } - t_pop(); - return &ctx->list; -} - -static struct mailbox_list *maildir_list_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; + mailbox = t_str_new(PATH_MAX); + while ((d = readdir(dirp)) != NULL) { + const char *fname = d->d_name; if (fname[0] != '.') continue; @@ -190,31 +88,9 @@ 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; - - /* make sure it's a directory */ - if (stat(path, &st) < 0) { - if (errno == ENOENT) - continue; /* just deleted, ignore */ - - mail_storage_set_critical(ctx->storage, - "stat(%s) failed: %m", path); - ctx->failed = TRUE; - return NULL; - } - - if (!S_ISDIR(st.st_mode)) + /* FIXME: kludges. these files must be renamed later */ + if (strcmp(fname, ".customflags") == 0 || + strcmp(fname, ".subscriptions") == 0) continue; fname++; @@ -225,58 +101,245 @@ 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') + 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; } - if (strcasecmp(fname, "INBOX") == 0) + /* 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) { - ctx->list.flags = - MAILBOX_PLACEHOLDER | MAILBOX_CHILDREN; + t_push(); 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; - } + if (imap_match(glob, p) > 0) + break; } - i_unreached(); + 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; + } } + } - 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; + 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; + } } - if (closedir(ctx->dirp) < 0) { - mail_storage_set_critical(ctx->storage, - "closedir(%s) failed: %m", ctx->dir); - ctx->failed = TRUE; - } - ctx->dirp = NULL; + return subsfile_list_deinit(subsfile_ctx); + +} + +struct mailbox_list_context * +maildir_list_mailbox_init(struct mail_storage *storage, + const char *mask, enum mailbox_list_flags flags) +{ + struct mailbox_list_context *ctx; + struct imap_match_glob *glob; + const char *dir, *p; + int nonexistent; + pool_t pool; + + mail_storage_clear_error(storage); + + pool = pool_alloconly_create("maildir_list", 1024); + ctx = p_new(pool, struct mailbox_list_context, 1); + ctx->pool = pool; + ctx->storage = storage; + ctx->flags = flags; + ctx->tree_ctx = mailbox_tree_init('.'); - if (imap_match(ctx->glob, "INBOX") > 0) { - const char *path = maildir_get_path(ctx->storage, "INBOX"); + glob = imap_match_init(pool, mask, TRUE, '.'); + + if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) { + ctx->dir = storage->dir; + ctx->prefix = ""; - ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ? - maildir_get_marked_flags(path) : 0; - ctx->list.name = "INBOX"; - return &ctx->list; + 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; + } + } else { + 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)); + } } - /* we're finished */ + 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->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) +{ + mailbox_tree_deinit(ctx->tree_ctx); + pool_unref(ctx->pool); + return TRUE; +} + +static struct mailbox_node *find_next(struct mailbox_node **node, + string_t *path) +{ + struct mailbox_node *child; + size_t len; + + while (*node != NULL) { + if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0) + return *node; + + if ((*node)->children != NULL) { + len = str_len(path); + if (len != 0) + str_append_c(path, '.'); + str_append(path, (*node)->name); + + child = find_next(&(*node)->children, path); + if (child != NULL) + return child; + + str_truncate(path, len); + } + + *node = (*node)->next; + } + return NULL; } struct mailbox_list * maildir_list_mailbox_next(struct mailbox_list_context *ctx) { - return ctx->next(ctx); + struct mailbox_node *node; + + for (node = ctx->next_node; node != NULL; node = node->next) { + if ((node->flags & MAILBOX_FLAG_MATCHED) != 0) + break; + } + + 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); + + if (node == NULL) + return NULL; + } + ctx->next_node = node->next; + + i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0); + node->flags &= ~MAILBOX_FLAG_MATCHED; + + 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.name = str_c(ctx->node_path); + ctx->list.flags = node->flags; + return &ctx->list; }
--- a/src/lib-storage/index/maildir/maildir-storage.c Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.c Sun Jul 27 02:53:05 2003 +0300 @@ -536,14 +536,14 @@ 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));
--- a/src/lib-storage/index/maildir/maildir-storage.h Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.h Sun Jul 27 02:53:05 2003 +0300 @@ -17,8 +17,7 @@ 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);
--- a/src/lib-storage/index/mbox/mbox-list.c Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/index/mbox/mbox-list.c Sun Jul 27 02:53:05 2003 +0300 @@ -98,14 +98,12 @@ 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");
--- a/src/lib-storage/index/mbox/mbox-storage.h Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/index/mbox/mbox-storage.h Sun Jul 27 02:53:05 2003 +0300 @@ -16,7 +16,7 @@ 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);
--- a/src/lib-storage/mail-storage.h Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/mail-storage.h Sun Jul 27 02:53:05 2003 +0300 @@ -179,14 +179,11 @@ /* 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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mailbox-tree.c Sun Jul 27 02:53:05 2003 +0300 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mailbox-tree.h Sun Jul 27 02:53:05 2003 +0300 @@ -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
--- a/src/lib-storage/proxy-mail-storage.c Sun Jul 27 02:28:05 2003 +0300 +++ b/src/lib-storage/proxy-mail-storage.c Sun Jul 27 02:53:05 2003 +0300 @@ -53,11 +53,11 @@ 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,