cmd-list.c revision 2b619d427addefb7d1660b23a528259d162a5f67
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen#include "common.h"
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen#include "commands.h"
b87761f9bbef949f31dae297e619ac3f5e9c2b2eTimo Sirainen#include "imap-match.h"
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainentypedef struct _ListNode ListNode;
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstruct _ListNode {
de5f478d9e7ae7b8e58082e0b30b6ce1f034236aTimo Sirainen ListNode *next;
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen ListNode *children;
f7d018e7e0980044e3d537958126e44ef4c45056Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen char *name; /* escaped */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen MailboxFlags flags;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen};
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainentypedef struct {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen Pool pool;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ListNode *nodes;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen MailStorage *storage;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen} ListContext;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstatic const char *mailbox_flags2str(MailboxFlags flags)
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen{
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen const char *str;
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen str = t_strconcat((flags & MAILBOX_NOSELECT) ? " \\Noselect" : "",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (flags & MAILBOX_NOINFERIORS) ? " \\NoInferiors" : "",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (flags & MAILBOX_MARKED) ? " \\Marked" : "",
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen (flags & MAILBOX_UNMARKED) ? " \\UnMarked" : "",
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen NULL);
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen return *str == '\0' ? "" : str+1;
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen}
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenstatic ListNode *list_node_get(Pool pool, ListNode **node,
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen const char *path, char separator)
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen{
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen const char *name, *parent;
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen parent = NULL;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen for (name = path;; path++) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (*path != separator && *path != '\0')
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen continue;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_push();
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* escaping is done here to make sure we don't try to escape
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen the separator char */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen name = imap_escape(t_strdup_until(name, path));
c72cfe4a2bda39fff3b8a8bd64b31a7cc14d7d11Timo Sirainen
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen /* find the node */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen while (*node != NULL) {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (strcmp((*node)->name, name) == 0)
f9eee365367f37b1692c07db6c23d30243844aaaTimo Sirainen break;
f7d018e7e0980044e3d537958126e44ef4c45056Timo Sirainen
7cd055a212d44067e2d94452c05691d696c9f699Timo Sirainen node = &(*node)->next;
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (*node == NULL) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* not found, create it */
f0e416aa42058e7ccc0dc6deec0d4f4a19ee6ebeTimo Sirainen *node = p_new(pool, ListNode, 1);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (*node)->name = p_strdup(pool, name);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (*node)->flags = MAILBOX_NOSELECT;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen t_pop();
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (*path == '\0')
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen break;
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen name = path+1;
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen parent = (*node)->name;
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen node = &(*node)->children;
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen }
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen return *node;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen}
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstatic void list_func(MailStorage *storage __attr_unused__, const char *name,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen MailboxFlags flags, void *context)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen{
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ListContext *ctx = context;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ListNode *node;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen node = list_node_get(ctx->pool, &ctx->nodes, name,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ctx->storage->hierarchy_sep);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* set the flags, this also nicely overrides the NOSELECT flag
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen set by list_node_get() */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen node->flags = flags;
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen}
2ccb478c35972517721ce415d81fcbd11a73fad3Timo Sirainen
37e8420b32a0fa3442c405616980e45beb494104Timo Sirainenstatic void list_send(Client *client, ListNode *node, const char *cmd,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char *path, const char *sep, ImapMatchGlob *glob)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen{
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char *name, *str;
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen for (; node != NULL; node = node->next) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_push();
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* Send INBOX always uppercased */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (path != NULL)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen name = t_strconcat(path, sep, node->name, NULL);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen else if (strcasecmp(node->name, "INBOX") == 0)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen name = "INBOX";
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen else
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen name = node->name;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (node->children != NULL)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen list_send(client, node->children, cmd, name, sep, glob);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if ((node->flags & MAILBOX_NOSELECT) == 0 ||
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen imap_match(glob, name) > 0) {
e22ec7998afd426c53c658483ce66b6e404e27c6Timo Sirainen /* node->name should already be escaped */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen mailbox_flags2str(node->flags),
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen sep, name);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen client_send_line(client, str);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_pop();
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen}
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenint _cmd_list_full(Client *client, int subscribed)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen{
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ListContext ctx;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char *ref, *pattern;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen char sep_chr, sep[3];
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen int failed;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep_chr = client->storage->hierarchy_sep;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (IS_ESCAPED_CHAR(sep_chr)) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep[0] = '\\';
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep[1] = sep_chr;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep[2] = '\0';
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen } else {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep[0] = sep_chr;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen sep[1] = '\0';
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* <reference> <mailbox wildcards> */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (!client_read_string_args(client, 2, &ref, &pattern))
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen return FALSE;
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (*pattern == '\0' && !subscribed) {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen /* special request to return the hierarchy delimiter */
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen client_send_line(client, t_strconcat(
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen "* LIST (\\Noselect) \"", sep, "\" \"\"", NULL));
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen failed = FALSE;
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen } else {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (*ref != '\0') {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen /* join reference + pattern */
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (*pattern == sep_chr &&
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen ref[strlen(ref)-1] == sep_chr) {
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* LIST A. .B -> A.B */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen pattern++;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen pattern = t_strconcat(ref, pattern, NULL);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ctx.pool = pool_alloconly_create("ListContext", 10240);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ctx.nodes = NULL;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ctx.storage = client->storage;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (!subscribed) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen failed = !client->storage->
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen find_mailboxes(client->storage,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen pattern, list_func, &ctx);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen } else {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen failed = !client->storage->
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen find_subscribed(client->storage,
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen pattern, list_func, &ctx);
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen }
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (!failed) {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen list_send(client, ctx.nodes,
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen subscribed ? "LSUB" : "LIST", NULL, sep,
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen imap_match_init(pattern, TRUE, sep_chr));
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen }
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen pool_unref(ctx.pool);
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen }
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (failed)
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen client_send_storage_error(client);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen else {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen client_send_tagline(client, subscribed ?
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "OK Lsub completed." :
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "OK List completed.");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen }
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen return TRUE;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen}
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenint cmd_list(Client *client)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen{
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen return _cmd_list_full(client, FALSE);
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen}
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen