]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added support for CHILDREN and LISTEXT extensions.
authorTimo Sirainen <tss@iki.fi>
Mon, 24 Feb 2003 17:39:31 +0000 (19:39 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 24 Feb 2003 17:39:31 +0000 (19:39 +0200)
--HG--
branch : HEAD

configure.in
src/imap/cmd-list.c
src/imap/commands.h
src/lib-storage/index/maildir/maildir-list.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/mbox/mbox-list.c
src/lib-storage/mail-storage.h

index 560c9f0b1e23be87f278690503ac7f91f306a3b9..a40d8449b5e0c1a10e8198ea654e7996c9723c4b 100644 (file)
@@ -871,7 +871,7 @@ dnl **
 dnl ** capabilities
 dnl **
 
-capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE"
+capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN LISTEXT LIST-SUBSCRIBED"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 
 CFLAGS="$CFLAGS $EXTRA_CFLAGS"
index 76d8288156ac715b94a36478f41d0fe61417dd01..5e34e6a78b938e3fa557154ae0cd4623dbb29b49 100644 (file)
@@ -19,14 +19,36 @@ struct list_context {
        struct mail_storage *storage;
 };
 
-static const char *mailbox_flags2str(enum mailbox_flags flags)
+struct list_send_context {
+       struct client *client;
+       const char *response_name;
+       const char *sep;
+       struct imap_match_glob *glob;
+       int listext;
+};
+
+static const char *mailbox_flags2str(enum mailbox_flags flags, int listext)
 {
        const char *str;
 
-       if (flags == MAILBOX_PLACEHOLDER)
-               flags = MAILBOX_NOSELECT;
+       if (flags & MAILBOX_PLACEHOLDER) {
+               if (flags == MAILBOX_PLACEHOLDER) {
+                       if (!listext)
+                               flags = MAILBOX_NOSELECT;
+               } else {
+                       /* it was at one point, but then we got better specs */
+                       flags &= ~MAILBOX_PLACEHOLDER;
+               }
+               flags |= MAILBOX_CHILDREN;
+       }
+       if ((flags & MAILBOX_NONEXISTENT) != 0 && !listext)
+               flags |= MAILBOX_NOSELECT;
 
        str = t_strconcat((flags & MAILBOX_NOSELECT) ? " \\Noselect" : "",
+                         (flags & MAILBOX_NONEXISTENT) ? " \\NonExistent" : "",
+                         (flags & MAILBOX_PLACEHOLDER) ? " \\PlaceHolder" : "",
+                         (flags & MAILBOX_CHILDREN) ? " \\Children" : "",
+                         (flags & MAILBOX_NOCHILDREN) ? " \\NoChildren" : "",
                          (flags & MAILBOX_NOINFERIORS) ? " \\NoInferiors" : "",
                          (flags & MAILBOX_MARKED) ? " \\Marked" : "",
                          (flags & MAILBOX_UNMARKED) ? " \\UnMarked" : "",
@@ -35,8 +57,9 @@ static const char *mailbox_flags2str(enum mailbox_flags flags)
        return *str == '\0' ? "" : str+1;
 }
 
-static struct list_node *list_node_get(pool_t pool, struct list_node **node,
-                                      const char *path, char separator)
+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;
 
@@ -64,7 +87,11 @@ static struct list_node *list_node_get(pool_t pool, struct list_node **node,
                        /* not found, create it */
                        *node = p_new(pool, struct list_node, 1);
                        (*node)->name = p_strdup(pool, name);
-                       (*node)->flags = MAILBOX_PLACEHOLDER;
+                       (*node)->flags = *path == '\0' ?
+                               flags : MAILBOX_PLACEHOLDER;
+               } else {
+                       if (*path == '\0')
+                               (*node)->flags |= flags;
                }
 
                t_pop();
@@ -76,15 +103,12 @@ static struct list_node *list_node_get(pool_t pool, struct list_node **node,
                parent = (*node)->name;
                node = &(*node)->children;
        }
-
-       return *node;
 }
 
-static void list_send(struct client *client, struct list_node *node,
-                     const char *cmd, const char *path, const char *sep,
-                     struct imap_match_glob *glob)
+static void list_send(struct list_send_context *ctx, struct list_node *node,
+                     const char *path)
 {
-       const char *name, *send_name, *str;
+       const char *name, *send_name, *str, *flagstr;
        enum imap_match_result match;
 
        for (; node != NULL; node = node->next) {
@@ -92,7 +116,7 @@ static void list_send(struct client *client, struct list_node *node,
 
                /* Send INBOX always uppercased */
                if (path != NULL)
-                       name = t_strconcat(path, sep, node->name, NULL);
+                       name = t_strconcat(path, ctx->sep, node->name, NULL);
                else if (strcasecmp(node->name, "INBOX") == 0)
                        name = "INBOX";
                else
@@ -106,24 +130,25 @@ static void list_send(struct client *client, struct list_node *node,
                        const char *buf;
 
                        buf = str_unescape(t_strdup_noconst(name));
-                       match = imap_match(glob, buf);
+                       match = imap_match(ctx->glob, buf);
                        if (match == IMAP_MATCH_CHILDREN) {
-                               send_name = t_strconcat(name, sep, NULL);
+                               send_name = t_strconcat(name, ctx->sep, NULL);
                                buf = str_unescape(t_strdup_noconst(send_name));
-                               match = imap_match(glob, buf);
+                               match = imap_match(ctx->glob, buf);
                        }
                }
 
                if (match == IMAP_MATCH_YES) {
                        /* node->name should already be escaped */
-                       str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
-                                             mailbox_flags2str(node->flags),
-                                             sep, send_name);
-                       client_send_line(client, str);
+                       flagstr = mailbox_flags2str(node->flags, ctx->listext);
+                       str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"",
+                                             ctx->response_name, flagstr,
+                                             ctx->sep, send_name);
+                       client_send_line(ctx->client, str);
                }
 
                if (node->children != NULL)
-                       list_send(client, node->children, cmd, name, sep, glob);
+                       list_send(ctx, node->children,  name);
 
                t_pop();
        }
@@ -131,32 +156,38 @@ static void list_send(struct client *client, struct list_node *node,
 
 static void list_and_sort(struct client *client,
                          struct mailbox_list_context *ctx,
-                         const char *cmd, const char *sep, const char *mask)
+                         const char *response_name,
+                         const char *sep, const char *mask, int listext)
 {
        struct mailbox_list *list;
-       struct list_node *nodes, *node;
-       struct imap_match_glob *glob;
+       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) {
-               node = list_node_get(pool, &nodes, list->name,
-                                    client->storage->hierarchy_sep);
-               node->flags |= list->flags;
+               list_node_update(pool, &nodes, list->name,
+                                client->storage->hierarchy_sep,
+                                list->flags);
        }
 
-       glob = imap_match_init(data_stack_pool, mask, TRUE,
-                              client->storage->hierarchy_sep);
-       list_send(client, nodes, cmd, NULL, sep, glob);
-       imap_match_deinit(glob);
+       send_ctx.client = client;
+       send_ctx.response_name = response_name;
+       send_ctx.sep = sep;
+       send_ctx.glob = imap_match_init(data_stack_pool, mask, TRUE,
+                                       client->storage->hierarchy_sep);
+       send_ctx.listext = listext;
+
+       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 *cmd, const char *sep)
+                         const char *reply, const char *sep, int listext)
 {
        struct mailbox_list *list;
        const char *name, *str;
@@ -167,42 +198,50 @@ static void list_unsorted(struct client *client,
                        name = "INBOX";
                else
                        name = str_escape(list->name);
-               str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
-                                     mailbox_flags2str(list->flags),
+               str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", reply,
+                                     mailbox_flags2str(list->flags, listext),
                                      sep, name);
                client_send_line(client, str);
                t_pop();
        }
 }
 
-static int list_mailboxes(struct client *client, const char *mask,
-                         int subscribed, const char *sep)
+static int parse_list_flags(struct client *client, struct imap_arg *args,
+                           enum mailbox_list_flags *list_flags)
 {
-       struct mailbox_list_context *ctx;
-       const char *cmd;
-       int sorted;
-
-       ctx = client->storage->
-               list_mailbox_init(client->storage, mask,
-                                 subscribed ? MAILBOX_LIST_SUBSCRIBED : 0,
-                                 &sorted);
-       if (ctx == NULL)
-               return FALSE;
+       const char *atom;
+
+       while (args->type != IMAP_ARG_EOL) {
+               if (args->type != IMAP_ARG_ATOM) {
+                       client_send_command_error(client,
+                               "List options contains non-atoms.");
+                       return FALSE;
+               }
 
-        cmd = subscribed ? "LSUB" : "LIST";
-       if (sorted)
-               list_unsorted(client, ctx, cmd, sep);
-       else
-               list_and_sort(client, ctx, cmd, sep, mask);
+               atom = IMAP_ARG_STR(args);
 
-       return client->storage->list_mailbox_deinit(ctx);
+               if (strcasecmp(atom, "SUBSCRIBED") == 0)
+                       *list_flags |= MAILBOX_LIST_SUBSCRIBED;
+               else if (strcasecmp(atom, "CHILDREN") == 0)
+                       *list_flags |= MAILBOX_LIST_CHILDREN;
+               else {
+                       client_send_tagline(client, t_strconcat(
+                               "BAD Invalid list option ", atom, NULL));
+                       return FALSE;
+               }
+               args++;
+       }
+       return TRUE;
 }
 
-int _cmd_list_full(struct client *client, int subscribed)
+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;
+       int failed, sorted, listext;
 
        sep_chr = client->storage->hierarchy_sep;
        if (IS_ESCAPED_CHAR(sep_chr)) {
@@ -214,11 +253,34 @@ int _cmd_list_full(struct client *client, int subscribed)
                sep[1] = '\0';
        }
 
-       /* <reference> <mailbox wildcards> */
-       if (!client_read_string_args(client, 2, &ref, &mask))
+       /* [(<options>)] <reference> <mailbox wildcards> */
+       if (!client_read_args(client, 0, 0, &args))
                return FALSE;
 
-       if (*mask == '\0' && !subscribed) {
+       listext = FALSE;
+       if (lsub)
+               list_flags = MAILBOX_LIST_SUBSCRIBED | MAILBOX_LIST_FAST_FLAGS;
+       else {
+               list_flags = 0;
+               if (args[0].type == IMAP_ARG_LIST) {
+                       listext = TRUE;
+                       if (!parse_list_flags(client,
+                                             IMAP_ARG_LIST(&args[0])->args,
+                                             &list_flags))
+                               return TRUE;
+                       args++;
+               }
+       }
+
+       ref = imap_arg_string(&args[0]);
+       mask = imap_arg_string(&args[1]);
+
+       if (ref == NULL || mask == NULL) {
+               client_send_command_error(client, "Invalid FETCH arguments.");
+               return TRUE;
+       }
+
+       if (*mask == '\0' && !lsub) {
                /* special request to return the hierarchy delimiter */
                client_send_line(client, t_strconcat(
                        "* LIST (\\Noselect) \"", sep, "\" \"\"", NULL));
@@ -240,13 +302,29 @@ int _cmd_list_full(struct client *client, int subscribed)
                        }
                }
 
-               failed = !list_mailboxes(client, mask, subscribed, sep);
+               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, sep,
+                                             mask, listext);
+                       }
+
+                       failed = !client->storage->list_mailbox_deinit(ctx);
+               }
        }
 
        if (failed)
                client_send_storage_error(client);
        else {
-               client_send_tagline(client, subscribed ?
+               client_send_tagline(client, lsub ?
                                    "OK Lsub completed." :
                                    "OK List completed.");
        }
index 2c178941e030d30083ac3b2605c7583ed469fd4b..89aa8228b7ec907e55e62c1a4c827b686a72e671 100644 (file)
@@ -47,7 +47,7 @@ int cmd_unselect(struct client *client);
 int cmd_idle(struct client *client);
 
 /* private: */
-int _cmd_list_full(struct client *client, int subscribed);
+int _cmd_list_full(struct client *client, int lsub);
 int _cmd_select_full(struct client *client, int readonly);
 int _cmd_subscribe_full(struct client *client, int subscribe);
 
index 2383ac1936202b724307663c5aa41faa0b7274eb..0f8df4e28e7559957b9d24ee104f5f2565364e55 100644 (file)
@@ -147,9 +147,6 @@ static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx)
        ctx->list.flags = 0;
        ctx->list.name = name;
 
-       if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
-               return &ctx->list;
-
        if (match == IMAP_MATCH_PARENT) {
                /* placeholder */
                ctx->list.flags = MAILBOX_PLACEHOLDER;
@@ -163,6 +160,9 @@ static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx)
                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))
@@ -171,7 +171,7 @@ static struct mailbox_list *maildir_list_subs(struct mailbox_list_context *ctx)
                if (strcasecmp(ctx->list.name, "INBOX") == 0)
                        ctx->list.flags = 0;
                else
-                       ctx->list.flags = MAILBOX_NOSELECT;
+                       ctx->list.flags = MAILBOX_NONEXISTENT;
        }
        t_pop();
        return &ctx->list;
@@ -253,8 +253,8 @@ static struct mailbox_list *maildir_list_next(struct mailbox_list_context *ctx)
                }
 
                p_clear(ctx->list_pool);
-               if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0)
-                       ctx->list.flags = maildir_get_marked_flags(path);
+                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;
@@ -270,7 +270,8 @@ static struct mailbox_list *maildir_list_next(struct mailbox_list_context *ctx)
        if (imap_match(ctx->glob, "INBOX") > 0) {
                const char *path = maildir_get_path(ctx->storage, "INBOX");
 
-               ctx->list.flags = maildir_get_marked_flags(path);
+               ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ?
+                       maildir_get_marked_flags(path) : 0;
                ctx->list.name = "INBOX";
                return &ctx->list;
        }
index 14cba88894987bd544958f6b8f7b2fafa438d6b5..e14d5702904f4932cecabfab73eb326a5fb93d19 100644 (file)
@@ -416,7 +416,7 @@ static int rename_subfolders(struct mail_storage *storage,
 
        ctx = storage->list_mailbox_init(storage,
                                         t_strconcat(oldname, ".*", NULL),
-                                        MAILBOX_LIST_NO_FLAGS, &sorted);
+                                        MAILBOX_LIST_FAST_FLAGS, &sorted);
        while ((list = maildir_list_mailbox_next(ctx)) != NULL) {
                i_assert(oldnamelen <= strlen(list->name));
 
index 999367765064742c8833a7bb45b5c85870230262..87b1e90b08ce17672888a613caf3f7beebd44df9 100644 (file)
@@ -233,13 +233,13 @@ static int list_file(struct mailbox_list_context *ctx, const char *fname)
                path = t_strconcat(list_path, "/", NULL);
                match2 = imap_match(ctx->glob, path);
 
-               if (match > 0) {
-                       ctx->list.flags = MAILBOX_NOSELECT;
+               ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+               if (match > 0)
                        ctx->list.name = p_strdup(ctx->list_pool, list_path);
-               } else if (match2 > 0) {
-                       ctx->list.flags = MAILBOX_NOSELECT;
+               else if (match2 > 0)
                        ctx->list.name = p_strdup(ctx->list_pool, path);
-               }
+               else
+                       ctx->list.name = NULL;
 
                ret = match2 < 0 ? 0 :
                        list_opendir(ctx->storage, real_path, FALSE, &dirp);
@@ -286,9 +286,6 @@ static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
        ctx->list.flags = 0;
        ctx->list.name = name;
 
-       if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
-               return &ctx->list;
-
        if (match == IMAP_MATCH_PARENT) {
                /* placeholder */
                ctx->list.flags = MAILBOX_PLACEHOLDER;
@@ -302,11 +299,14 @@ static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
                i_unreached();
        }
 
+       if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) != 0)
+               return &ctx->list;
+
        t_push();
        path = mbox_get_path(ctx->storage, ctx->list.name);
        if (stat(path, &st) == 0) {
                if (S_ISDIR(st.st_mode))
-                       ctx->list.flags = MAILBOX_NOSELECT;
+                       ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
                else {
                        ctx->list.flags = MAILBOX_NOINFERIORS |
                                STAT_GET_MARKED(st);
@@ -315,7 +315,7 @@ static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
                if (strcasecmp(ctx->list.name, "INBOX") == 0)
                        ctx->list.flags = MAILBOX_UNMARKED;
                else
-                       ctx->list.flags = MAILBOX_NOSELECT;
+                       ctx->list.flags = MAILBOX_NONEXISTENT;
        }
        t_pop();
        return &ctx->list;
@@ -332,7 +332,7 @@ static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx)
 
        /* INBOX exists always, even if the file doesn't. */
        ctx->list.flags = MAILBOX_NOINFERIORS;
-       if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0) {
+       if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0) {
                if (stat(ctx->storage->inbox_file, &st) < 0)
                        ctx->list.flags |= MAILBOX_UNMARKED;
                else
@@ -347,7 +347,7 @@ static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx)
 {
        ctx->next = mbox_list_next;
 
-       ctx->list.flags = MAILBOX_NOSELECT;
+       ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
        ctx->list.name = p_strconcat(ctx->list_pool,
                                     ctx->dir->virtual_path, "/", NULL);
 
index f5c44bae2bf152c856733f77450e36d7a3bb7170..a6653252811ee41771d256fc7254d6515dd13533 100644 (file)
@@ -6,20 +6,22 @@ struct message_size;
 #include "imap-util.h"
 
 enum mailbox_list_flags {
-       MAILBOX_LIST_SUBSCRIBED = 0x01, /* show only subscribed */
-       MAILBOX_LIST_NO_FLAGS   = 0x02  /* don't set mailbox_flags */
+       MAILBOX_LIST_SUBSCRIBED = 0x01,
+       MAILBOX_LIST_FAST_FLAGS = 0x02,
+       MAILBOX_LIST_CHILDREN   = 0x04
 };
 
 enum mailbox_flags {
-       MAILBOX_NOSELECT        = 0x01,
-       MAILBOX_CHILDREN        = 0x02,
-       MAILBOX_NOCHILDREN      = 0x04,
-       MAILBOX_NOINFERIORS     = 0x08,
-       MAILBOX_MARKED          = 0x10,
-       MAILBOX_UNMARKED        = 0x20,
-       MAILBOX_PLACEHOLDER     = 0x40,
-
-       MAILBOX_READONLY        = 0x40
+       MAILBOX_NOSELECT        = 0x001,
+       MAILBOX_NONEXISTENT     = 0x002,
+       MAILBOX_PLACEHOLDER     = 0x004,
+       MAILBOX_CHILDREN        = 0x008,
+       MAILBOX_NOCHILDREN      = 0x010,
+       MAILBOX_NOINFERIORS     = 0x020,
+       MAILBOX_MARKED          = 0x040,
+       MAILBOX_UNMARKED        = 0x080,
+
+       MAILBOX_READONLY        = 0x100
 };
 
 enum mailbox_status_items {