mailbox-list-fs-iter.c revision a52e2f737616d4f8bca177e84d381c216560d54f
/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "imap-match.h"
#include "mailbox-tree.h"
#include "mailbox-list-subscriptions.h"
#include "mailbox-list-fs.h"
#include <ctype.h>
#include <dirent.h>
struct list_dir_entry {
const char *fname;
enum mailbox_list_file_type type;
};
struct list_dir_context {
struct list_dir_context *prev;
char *real_path, *virtual_path;
const struct list_dir_entry *next_entry;
struct list_dir_entry entry;
char *entry_fname;
struct mailbox_info info;
unsigned int pattern_pos;
unsigned int delayed_send:1;
};
struct fs_list_iterate_context {
struct mailbox_list_iterate_context ctx;
ARRAY_DEFINE(valid_patterns, char *);
struct imap_match_glob *glob;
struct mailbox_tree_context *subs_tree;
struct mailbox_tree_iterate_context *tree_iter;
char sep;
struct mailbox_info info;
struct list_dir_context *dir;
unsigned int inbox_match:1;
unsigned int inbox_found:1;
unsigned int inbox_listed:1;
};
static const struct mailbox_info *
static const struct mailbox_info *
static int
{
unsigned int i, j;
/* make sure INBOX prefix is matched case-insensitively */
}
for (i = j = 0; path[i] != '\0'; i++) {
if (pattern[j] == '*')
return -1;
if (pattern[j] == '%') {
/* skip until we're at the next hierarchy separator */
/* assume that pattern matches. we can't be
sure, but it'll be checked later. */
for (j++; pattern[j] != '\0'; j++) {
if (pattern[j] == '*')
return -1;
j++;
break;
}
}
}
} else {
/* pattern doesn't match path at all */
return 0;
}
j++;
}
}
*pos_r = j;
return 1;
}
static bool
{
unsigned int pos;
int ret;
return TRUE;
if (ret == 0)
return FALSE;
return TRUE;
return TRUE;
}
return FALSE;
}
{
char *const *patterns;
unsigned int i;
/* if no patterns have wildcards at this point of the path, we don't
have to readdir() the files. instead we can just go through the
mailboxes listed in patterns. */
T_BEGIN {
break;
}
} T_END;
return 1;
}
return 1;
/* root) user gave invalid hiearchy, ignore
sub) probably just race condition with other client
deleting the mailbox. */
return 0;
}
/* ignore permission errors */
return 0;
}
"opendir(%s) failed: %m", path);
return -1;
}
const char **vpath)
{
return NULL;
/* see if there are any absolute paths in patterns */
if (*name == '/') {
*vpath = "/";
return "/";
}
if (*name == '~') {
if (*p == '%' || *p == '*')
break;
if (*p == '/')
last = p;
}
&name)) {
*vpath = p;
return name;
}
}
}
return NULL;
}
struct mailbox_list_iterate_context *
enum mailbox_list_iter_flags flags)
{
struct fs_list_iterate_context *ctx;
char *pattern;
unsigned int prefix_len;
int ret;
/* check that we're not trying to do any "../../" lists */
test_pattern = *patterns;
if ((flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) {
/* skip namespace prefix if possible. this allows using
e.g. ~/mail/ prefix and have it pass the pattern
validation. */
prefix_len) == 0)
}
continue;
}
}
}
/* we've only invalid patterns (or INBOX) */
}
if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) {
/* we want to return MAILBOX_SUBSCRIBED flags, possibly for all
mailboxes. Build a mailbox tree of all the subscriptions. */
}
}
if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
}
} else {
}
if (ret > 0) {
}
}
{
}
{
struct fs_list_iterate_context *ctx =
(struct fs_list_iterate_context *)_ctx;
char **patterns;
unsigned int i, count;
for (i = 0; i < count; i++)
}
return ret;
}
const struct mailbox_info *
{
struct fs_list_iterate_context *ctx =
(struct fs_list_iterate_context *)_ctx;
const struct mailbox_info *info;
return NULL;
T_BEGIN {
} T_END;
return info;
}
static void
{
const char *p;
if (p == NULL) {
*dir_r = "";
} else {
*fname_r = p + 1;
}
}
static enum mailbox_info_flags
const char *mailbox)
{
struct mailbox_node *node;
return 0;
return 0;
}
{
/* INBOX is always selectable */
/* we're listing INBOX for a namespace with a prefix.
if there are children for the INBOX, they're returned under
the mailbox prefix, not under the INBOX itself. */
}
}
{
/* we got here because we didn't see INBOX among other mailboxes,
which means it has no children. */
}
static bool
{
const char *real_path, *inbox_path;
}
static bool
{
if (ctx->inbox_listed) {
/* already listed the INBOX */
return FALSE;
}
/* delay listing in case there's a INBOX/ directory.
do this only when INBOX is a file! otherwise we won't list
INBOX children. */
return FALSE;
}
/* duplicate INBOX, can't show this */
return FALSE;
}
/* INBOX/ directory. show the INBOX list now */
/* this directory is the INBOX */
} else if (!ctx->inbox_found) {
(void)fs_list_inbox(ctx);
} else {
}
return TRUE;
}
static int
const char *fname)
{
struct list_dir_context *dir;
enum imap_match_result match2;
int ret;
if (match == IMAP_MATCH_YES)
else if (match2 == IMAP_MATCH_YES)
else
scan_subdir = TRUE;
delayed_send = TRUE;
}
if (scan_subdir) {
} else {
ret = 0;
}
if (ret > 0) {
dir->virtual_path =
if (delayed_send) {
return 0;
}
} else if (ret < 0)
return -1;
}
static int
const struct list_dir_entry *entry)
{
enum imap_match_result match;
int ret;
/* skip . and .. */
if (fname[0] == '.' &&
return 0;
/* check the pattern */
return 0;
/* mail storage's internal directory */
return 0;
}
/* skip subscriptions file */
return 0;
}
/* get the info.flags using callback */
if (ret <= 0)
return ret;
/* send the parent directory first, then handle this
file again if needed */
if (match == IMAP_MATCH_YES ||
(match & IMAP_MATCH_CHILDREN) != 0)
return 1;
}
/* make sure we give only one correct INBOX */
return 0;
/* This is the INBOX. Don't return it under the mailbox
prefix unless it has children. */
return 0;
}
}
if (match == IMAP_MATCH_YES) {
return 1;
}
return 0;
}
static const struct mailbox_info *
{
struct mailbox_node *node;
enum mailbox_info_flags flags;
unsigned int len;
return NULL;
/* subscription list has real knowledge of only subscription flags */
}
/* if name ends with hierarchy separator, drop the separator */
}
static const struct list_dir_entry *
{
struct dirent *d;
char *const *patterns;
unsigned int pos;
int ret;
return entry;
}
if (d == NULL)
return NULL;
}
for (;;) {
return NULL;
dir->pattern_pos++;
&pos);
if (ret == 0)
continue;
/* get the filename from the pattern */
/* lstat() it to make sure it exists */
errno != ENAMETOOLONG)
continue;
}
else
break;
}
}
static const struct mailbox_info *
{
struct list_dir_context *dir;
const struct list_dir_entry *entry;
int ret;
/* NOTE: list_file() may change ctx->dir */
T_BEGIN {
} T_END;
if (ret > 0)
if (ret < 0) {
return NULL;
}
}
if (dir->delayed_send) {
/* wanted to know if the mailbox had children.
it didn't. */
}
}
if (!ctx->inbox_found &&
ctx->inbox_match)) {
/* INBOX wasn't seen while listing other mailboxes. It might
be located elsewhere. */
return fs_list_inbox(ctx);
}
/* INBOX was found, but we delayed listing it. Show it now. */
}
/* finished */
return NULL;
}