cmd-list.c revision ce555be496d79e08ca1e0230bf08b502fbf7c56e
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen#include "common.h"
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen#include "strescape.h"
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen#include "imap-match.h"
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen#include "commands.h"
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainenstruct list_node {
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen struct list_node *next;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen struct list_node *children;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen char *name; /* escaped */
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen enum mailbox_flags flags;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen};
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainenstruct list_context {
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen pool_t pool;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen struct list_node *nodes;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen struct mail_storage *storage;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen};
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainenstatic const char *mailbox_flags2str(enum mailbox_flags flags)
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen{
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen const char *str;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen if (flags == MAILBOX_PLACEHOLDER)
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen flags = MAILBOX_NOSELECT;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen str = t_strconcat((flags & MAILBOX_NOSELECT) ? " \\Noselect" : "",
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen (flags & MAILBOX_NOINFERIORS) ? " \\NoInferiors" : "",
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen (flags & MAILBOX_MARKED) ? " \\Marked" : "",
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen (flags & MAILBOX_UNMARKED) ? " \\UnMarked" : "",
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen NULL);
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen return *str == '\0' ? "" : str+1;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen}
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainenstatic struct list_node *list_node_get(pool_t pool, struct list_node **node,
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen const char *path, char separator)
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen{
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen const char *name, *parent;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen parent = NULL;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen for (name = path;; path++) {
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen if (*path != separator && *path != '\0')
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen continue;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen t_push();
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen /* escaping is done here to make sure we don't try to escape
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen the separator char */
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen name = str_escape(t_strdup_until(name, path));
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen /* find the node */
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen while (*node != NULL) {
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen if (strcmp((*node)->name, name) == 0)
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen break;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen node = &(*node)->next;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen }
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen if (*node == NULL) {
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen /* not found, create it */
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen *node = p_new(pool, struct list_node, 1);
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen (*node)->name = p_strdup(pool, name);
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen (*node)->flags = MAILBOX_PLACEHOLDER;
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen }
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen t_pop();
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen if (*path == '\0')
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen break;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen name = path+1;
4cb2599c5cdf27362a66ac475ce295409c093c92Timo Sirainen 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)
{
const char *name, *send_name, *str;
enum imap_match_result match;
for (; node != NULL; node = node->next) {
t_push();
/* Send INBOX always uppercased */
if (path != NULL)
name = t_strconcat(path, sep, node->name, NULL);
else if (strcasecmp(node->name, "INBOX") == 0)
name = "INBOX";
else
name = node->name;
send_name = name;
if (node->flags != MAILBOX_PLACEHOLDER)
match = IMAP_MATCH_YES;
else {
/* make sure the placeholder matches. */
const char *buf;
buf = str_unescape(t_strdup_noconst(name));
match = imap_match(glob, buf);
if (match == IMAP_MATCH_CHILDREN) {
send_name = t_strconcat(name, sep, NULL);
buf = str_unescape(t_strdup_noconst(send_name));
match = imap_match(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);
}
if (node->children != NULL)
list_send(client, node->children, cmd, name, sep, glob);
t_pop();
}
}
static void list_and_sort(struct client *client,
struct mailbox_list_context *ctx,
const char *cmd, const char *sep, const char *mask)
{
struct mailbox_list *list;
struct list_node *nodes, *node;
struct imap_match_glob *glob;
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;
}
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);
pool_unref(pool);
}
static void list_unsorted(struct client *client,
struct mailbox_list_context *ctx,
const char *cmd, const char *sep)
{
struct mailbox_list *list;
const char *name, *str;
while ((list = client->storage->list_mailbox_next(ctx)) != NULL) {
t_push();
if (strcasecmp(list->name, "INBOX") == 0)
name = "INBOX";
else
name = str_escape(list->name);
str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
mailbox_flags2str(list->flags),
sep, name);
client_send_line(client, str);
t_pop();
}
}
static int list_mailboxes(struct client *client, const char *mask,
int subscribed, const char *sep)
{
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;
cmd = subscribed ? "LSUB" : "LIST";
if (sorted)
list_unsorted(client, ctx, cmd, sep);
else
list_and_sort(client, ctx, cmd, sep, mask);
return client->storage->list_mailbox_deinit(ctx);
}
int _cmd_list_full(struct client *client, int subscribed)
{
const char *ref, *mask;
char sep_chr, sep[3];
int failed;
sep_chr = client->storage->hierarchy_sep;
if (IS_ESCAPED_CHAR(sep_chr)) {
sep[0] = '\\';
sep[1] = sep_chr;
sep[2] = '\0';
} else {
sep[0] = sep_chr;
sep[1] = '\0';
}
/* <reference> <mailbox wildcards> */
if (!client_read_string_args(client, 2, &ref, &mask))
return FALSE;
if (*mask == '\0' && !subscribed) {
/* special request to return the hierarchy delimiter */
client_send_line(client, t_strconcat(
"* LIST (\\Noselect) \"", sep, "\" \"\"", NULL));
failed = FALSE;
} else {
if (*ref != '\0') {
/* join reference + mask */
if (*mask == sep_chr &&
ref[strlen(ref)-1] == sep_chr) {
/* LIST A. .B -> A.B */
mask++;
}
if (*mask != sep_chr &&
ref[strlen(ref)-1] != sep_chr) {
/* LIST A B -> A.B */
mask = t_strconcat(ref, sep, mask, NULL);
} else {
mask = t_strconcat(ref, mask, NULL);
}
}
failed = !list_mailboxes(client, mask, subscribed, sep);
}
if (failed)
client_send_storage_error(client);
else {
client_send_tagline(client, subscribed ?
"OK Lsub completed." :
"OK List completed.");
}
return TRUE;
}
int cmd_list(struct client *client)
{
return _cmd_list_full(client, FALSE);
}