From: Timo Sirainen Date: Sat, 26 Jul 2003 23:53:05 +0000 (+0300) Subject: API change: Mailbox list sorting must now always done by storage itself if X-Git-Tag: 1.1.alpha1~4456 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05ea76547573aec80f1b58654bce7b850dd99501;p=thirdparty%2Fdovecot%2Fcore.git API change: Mailbox list sorting must now always done by storage itself if it's needed. Maildir listing rewritten. --HG-- branch : HEAD --- diff --git a/src/imap/cmd-list.c b/src/imap/cmd-list.c index e5c70051ef..0a73449657 100644 --- a/src/imap/cmd-list.c +++ b/src/imap/cmd-list.c @@ -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) diff --git a/src/lib-storage/Makefile.am b/src/lib-storage/Makefile.am index 01fe71e3c7..429bb15ddc 100644 --- a/src/lib-storage/Makefile.am +++ b/src/lib-storage/Makefile.am @@ -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 diff --git a/src/lib-storage/index/maildir/maildir-list.c b/src/lib-storage/index/maildir/maildir-list.c index ab2bfac333..5a0b9a449b 100644 --- a/src/lib-storage/index/maildir/maildir-list.c +++ b/src/lib-storage/index/maildir/maildir-list.c @@ -1,62 +1,229 @@ /* 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 #include +#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; } diff --git a/src/lib-storage/index/maildir/maildir-storage.c b/src/lib-storage/index/maildir/maildir-storage.c index 7cf0796184..d7c627c0de 100644 --- a/src/lib-storage/index/maildir/maildir-storage.c +++ b/src/lib-storage/index/maildir/maildir-storage.c @@ -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)); diff --git a/src/lib-storage/index/maildir/maildir-storage.h b/src/lib-storage/index/maildir/maildir-storage.h index 9129333208..f1bdd3f9f6 100644 --- a/src/lib-storage/index/maildir/maildir-storage.h +++ b/src/lib-storage/index/maildir/maildir-storage.h @@ -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); diff --git a/src/lib-storage/index/mbox/mbox-list.c b/src/lib-storage/index/mbox/mbox-list.c index 93fa55d3f1..c700f7b95d 100644 --- a/src/lib-storage/index/mbox/mbox-list.c +++ b/src/lib-storage/index/mbox/mbox-list.c @@ -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"); diff --git a/src/lib-storage/index/mbox/mbox-storage.h b/src/lib-storage/index/mbox/mbox-storage.h index 7580dbeaf1..1fed72efe1 100644 --- a/src/lib-storage/index/mbox/mbox-storage.h +++ b/src/lib-storage/index/mbox/mbox-storage.h @@ -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); diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index d1ca820d19..a7552bb841 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -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 index 0000000000..3a7d9164d6 --- /dev/null +++ b/src/lib-storage/mailbox-tree.c @@ -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 index 0000000000..af4e639f13 --- /dev/null +++ b/src/lib-storage/mailbox-tree.h @@ -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 diff --git a/src/lib-storage/proxy-mail-storage.c b/src/lib-storage/proxy-mail-storage.c index b5e1027765..287560f84f 100644 --- a/src/lib-storage/proxy-mail-storage.c +++ b/src/lib-storage/proxy-mail-storage.c @@ -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,