cmd-list.c revision 31a574fda352ef4f71dbff9c30e15e4744e132c0
/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
#include "imap-common.h"
#include "array.h"
#include "str.h"
#include "strescape.h"
#include "imap-utf7.h"
#include "imap-quote.h"
#include "imap-match.h"
#include "imap-status.h"
#include "imap-commands.h"
#include "mail-namespace.h"
struct cmd_list_context {
struct client_command_context *cmd;
const char *ref;
const char *const *patterns;
struct imap_status_items status_items;
struct mail_namespace *ns;
struct mailbox_list_iterate_context *list_iter;
unsigned int lsub:1;
unsigned int lsub_no_unsubscribed:1;
unsigned int inbox_found:1;
unsigned int seen_inbox_namespace:1;
unsigned int cur_ns_match_inbox:1;
unsigned int cur_ns_send_prefix:1;
unsigned int cur_ns_skip_trailing_sep:1;
unsigned int used_listext:1;
unsigned int used_status:1;
};
static void
{
flags &= ~MAILBOX_NONEXISTENT;
}
if ((flags & MAILBOX_SUBSCRIBED) != 0 &&
if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
/* LSUB uses \Noselect for this */
}
if ((flags & MAILBOX_NOSELECT) != 0)
if ((flags & MAILBOX_NONEXISTENT) != 0)
if ((flags & MAILBOX_CHILDREN) != 0)
else if ((flags & MAILBOX_NOINFERIORS) != 0)
else if ((flags & MAILBOX_NOCHILDREN) != 0)
if ((flags & MAILBOX_MARKED) != 0)
if ((flags & MAILBOX_UNMARKED) != 0)
special_use != NULL) {
}
}
static void
enum mailbox_info_flags flags)
{
if (!ctx->used_listext)
return;
if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
}
static bool
{
enum mailbox_list_iter_flags list_flags = 0;
const char *str;
while (!IMAP_ARG_IS_EOL(args)) {
"List options contains non-atoms.");
return FALSE;
}
/* not supported, ignore */
} else {
/* skip also optional list value */
"Unknown select options");
return FALSE;
}
args++;
}
if ((list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
MAILBOX_LIST_ITER_SELECT_SPECIALUSE)) == 0) {
"RECURSIVEMATCH must not be the only selection.");
return FALSE;
}
return TRUE;
}
static bool
{
enum mailbox_list_iter_flags list_flags = 0;
const char *str;
while (!IMAP_ARG_IS_EOL(args)) {
"List options contains non-atoms.");
return FALSE;
}
&ctx->status_items) < 0)
return FALSE;
args++;
} else {
/* skip also optional list value */
"Unknown return options");
return FALSE;
}
args++;
}
return TRUE;
}
static enum mailbox_info_flags
{
struct mail_namespace *ns;
struct mailbox_list_iterate_context *list_iter;
const struct mailbox_info *info;
if (ctx->seen_inbox_namespace &&
/* INBOX doesn't exist. use the default INBOX flags */
return flags;
}
/* find the INBOX flags */
}
(void)mailbox_list_iter_deinit(&list_iter);
return flags;
}
{
struct mailbox_list_iterate_context *list_iter;
const struct mailbox_info *info;
if (mailbox_list_iter_deinit(&list_iter) < 0) {
/* safer to answer TRUE in error conditions */
}
return ret;
}
{
struct imap_match_glob *glob;
enum imap_match_result match;
const char *ns_prefix, *p;
bool inboxcase;
if (match == IMAP_MATCH_YES) {
}
while ((match & IMAP_MATCH_PARENT) != 0) {
}
return ns_prefix;
}
{
if (sep == '\\')
else
}
static void
{
struct mail_namespace *const *listed;
const struct mailbox_settings *mailbox_set;
enum mailbox_existence existence;
unsigned int len;
enum mailbox_info_flags flags;
const char *name;
bool same_ns, ends_with_sep;
/* see if we already listed this as a valid mailbox in another
namespace */
return;
}
/* we may be listing namespace's parent. in such case we always want to
set the name as nonexistent. */
/* INBOX namespace needs to be handled specially. */
if (ctx->inbox_found) {
/* we're just now going to send it */
return;
}
} else if (!same_ns) {
/* parent */
} else {
/* see if namespace prefix is selectable */
else
mailbox_free(&box);
}
if ((flags & MAILBOX_CHILDREN) == 0) {
flags &= ~MAILBOX_NOCHILDREN;
} else {
}
}
if (have_children) {
/* children are going to be listed. */
return;
}
if ((flags & MAILBOX_CHILDREN) == 0) {
/* namespace has no children. don't show it. */
return;
}
/* namespace has children but they don't match the list
pattern. the prefix itself matches though, so show it. */
}
}
enum mailbox_info_flags flags)
{
struct imap_status_result result;
struct mail_namespace *ns;
const char *error;
/* doesn't exist, don't even try to get STATUS */
return;
}
if ((flags & MAILBOX_SUBSCRIBED) == 0 &&
(flags & MAILBOX_CHILD_SUBSCRIBED) != 0) {
/* listing subscriptions, but only child is subscribed */
return;
}
/* if we're listing subscriptions and there are subscriptions=no
namespaces, ctx->ns may not point to correct one */
return;
}
}
{
struct mail_namespace *ns;
NAMESPACE_FLAG_LIST_CHILDREN)) != 0;
}
static int
{
const struct mailbox_info *info;
struct mail_namespace *ns;
enum mailbox_info_flags flags;
const char *name;
int ret = 0;
if (ctx->inbox_found) {
/* we already listed this at the beginning
of handling INBOX/ namespace */
continue;
}
/* INBOX is in non-empty prefix namespace,
and we're now listing prefixless namespace
that contains INBOX. There's no way we can
show this mailbox. */
continue;
}
/* INBOX is in its own namespace, while a
namespace with prefix="" has its children. */
}
}
if (ctx->cur_ns_send_prefix)
/* if there's a namespace with this name, list it as
having children */
flags &= ~MAILBOX_NOCHILDREN;
}
if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
(flags & MAILBOX_SUBSCRIBED) == 0 &&
/* mask doesn't end with %. we don't want to show
any extra mailboxes. */
continue;
}
str_truncate(mutf7_name, 0);
str_truncate(str, 0);
} T_END;
if (ret == 0) {
/* buffer is full, continue later */
return 0;
}
}
ret = -1;
}
static bool list_pattern_has_wildcards(const char *pattern)
{
return TRUE;
}
return FALSE;
}
static void
bool inbox_check, char sep)
{
bool match;
if (pattern_len < prefix_len) {
/* eg. namespace prefix = "INBOX.", pattern = "INBOX" */
return;
}
if (!match && inbox_check) {
/* try INBOX check. */
prefix_len - 5) == 0 &&
}
if (match) {
*prefix += prefix_len;
*pattern += prefix_len;
}
}
static bool
const char **cur_ns_prefix_p,
const char **cur_ref_p)
{
const char *cur_ns_prefix = *cur_ns_prefix_p;
if (*cur_ref != '\0') {
/* reference argument given. skip namespace prefix using it.
cur_ref = foo/
-> cur_ns_prefix=bar/, cur_ref=""
-> cur_ns_prefix="", cur_ref="baz"
*/
/* reference parameter didn't match with
namespace prefix. skip this. */
return FALSE;
}
}
return TRUE;
}
static void
const char **cur_ns_prefix_p,
const char *cur_ref, const char **cur_pattern_p)
{
const char *cur_ns_prefix = *cur_ns_prefix_p;
const char *cur_pattern = *cur_pattern_p;
const char *old_ns_prefix = cur_ns_prefix;
const char *old_pattern = cur_pattern;
if (*cur_ns_prefix == '\0')
return;
/* skip namespace prefix using pattern */
/* trying to list the namespace prefix itself. */
}
}
static enum imap_match_result
{
struct imap_match_glob *inbox_glob;
const char *const *pat;
return IMAP_MATCH_NO;
/* if the original reference and pattern combined produces something
that matches INBOX, the INBOX casing is on. */
ret = IMAP_MATCH_NO;
if (match == IMAP_MATCH_YES)
return IMAP_MATCH_YES;
if ((match & IMAP_MATCH_PARENT) != 0)
}
return ret;
}
static bool
{
/* don't send the prefix if we're listing subscribed mailboxes */
return FALSE;
/* send the prefix if namespace is listable. if children are listable
we may or may not need to send it. */
NAMESPACE_FLAG_LIST_CHILDREN)) != 0)
return TRUE;
/* ..or if pattern is exactly the same as namespace prefix.
some clients (mutt) want to do LIST "" prefix. */
break;
}
return *pattern == '\0';
}
static bool
const char *cur_ref, const char *cur_ns_prefix,
const char *cur_pattern)
{
const char *orig_cur_pattern = cur_pattern;
struct imap_match_glob *pat_glob;
enum imap_match_result match;
const char *p;
cur_ref, &cur_pattern);
if (*cur_ns_prefix == '\0') {
/* no namespace prefix: if list=no we don't want to
show anything, except when the client does e.g.
LIST "" mailbox. prefix="", list=no namespace is
mainly useful for working around client bugs. */
return FALSE;
}
return TRUE;
}
/* namespace prefix still wasn't completely skipped over.
for example cur_ns_prefix=INBOX/, pattern=%/% or pattern=IN%.
Check that pattern matches namespace prefix. */
/* drop the trailing separator in namespace prefix.
don't do it if we're listing only the prefix itself
(LIST "" foo/ needs to return "foo/" entry) */
}
/* hidden and non-listable namespaces are invisible to wildcards */
NAMESPACE_FLAG_LIST_CHILDREN)) == 0 &&
return FALSE;
/* check if this namespace prefix matches the current pattern */
if (match == IMAP_MATCH_YES) {
/* if the pattern contains '*' characters, we'll need to
check our children too */
for (p = orig_cur_pattern; *p != '\0'; p++) {
if (*p == '*')
return TRUE;
}
/* the namespace prefix itself may be subscribed. */
return TRUE;
}
return FALSE;
} else {
while ((match & IMAP_MATCH_PARENT) != 0) {
if (p == NULL)
break;
}
if (match == IMAP_MATCH_YES &&
cur_ns_prefix) == NULL) {
}
return match != IMAP_MATCH_NO;
}
}
{
enum imap_match_result inbox_match;
ARRAY_DEFINE(used_patterns, const char *);
bool inboxcase;
if (*cur_ns_prefix != '\0') {
/* namespace has a prefix. see if we can skip over it. */
return;
}
/* see if pattern even has a chance of matching the
namespace prefix */
cur_ns_prefix, pattern)) {
}
}
if (array_count(&used_patterns) == 0) {
if (!ctx->cur_ns_match_inbox) {
/* it's possible that the namespace prefix matched,
even though its children didn't */
if (ctx->cur_ns_send_prefix)
return;
}
/* we should still list INBOX */
pattern = "INBOX";
}
ctx->list_flags);
}
{
/* INBOX always exists */
}
}
{
int ret;
return TRUE;
}
T_BEGIN {
} T_END;
continue;
}
T_BEGIN {
} T_END;
if (ret < 0) {
return TRUE;
}
if (ret == 0)
return FALSE;
if (ctx->cur_ns_send_prefix) {
/* no mailboxes in this namespace */
}
}
"OK List completed." :
"OK Lsub completed.");
return TRUE;
}
{
struct mail_namespace *ns;
const char *ns_prefix;
char ns_sep;
/* Special request to return the hierarchy delimiter and mailbox root
name. If namespace has a prefix, it's returned as the mailbox root.
Otherwise we'll emulate UW-IMAP behavior. */
} else {
ns_prefix = "";
}
if (*ns_prefix != '\0') {
/* non-hidden namespace, use it as the root name */
} else {
/* Hidden namespace or empty namespace prefix. We could just
return an empty root name, but it's safer to emulate what
UW-IMAP does. With full filesystem access this might even
if (p == NULL)
else {
FALSE);
}
}
}
{
unsigned int arg_count;
struct cmd_list_context *ctx;
const char *pattern, *const *patterns_strarr;
/* [(<selection options>)] <reference> <pattern>|(<pattern list>)
[RETURN (<return options>)] */
return FALSE;
/* LIST-EXTENDED selection options */
return TRUE;
args++;
}
return TRUE;
}
/* convert pattern list to string array */
"Invalid pattern list.");
return TRUE;
}
str_truncate(str, 0);
}
args += 2;
} else {
return TRUE;
}
args += 2;
if (lsub) {
}
}
/* LIST-EXTENDED return options */
return TRUE;
args += 2;
}
if (lsub) {
/* LSUB - we don't care about flags except if
tb-lsub-flags workaround is explicitly set */
WORKAROUND_TB_LSUB_FLAGS) == 0)
} else if (!ctx->used_listext) {
/* non-extended LIST: use default flags */
}
if (!IMAP_ARG_IS_EOL(args)) {
return TRUE;
}
/* Only LIST ref "" gets us here */
} else {
if (!cmd_list_continue(cmd)) {
/* unfinished */
return FALSE;
}
return TRUE;
}
return TRUE;
}
{
}