[dovecot-cvs] dovecot/src/lib-storage/list .cvsignore, NONE, 1.1 mailbox-list-fs-iter.c, NONE, 1.1 mailbox-list-fs.c, NONE, 1.1 mailbox-list-fs.h, NONE, 1.1 mailbox-list-maildir-iter.c, NONE, 1.1 mailbox-list-maildir.c, NONE, 1.1 mailbox-list-maildir.h, NONE, 1.1 subscription-file.c, NONE, 1.1 subscription-file.h, NONE, 1.1
tss at dovecot.org
tss at dovecot.org
Thu Nov 16 00:16:42 UTC 2006
- Previous message: [dovecot-cvs] dovecot/src/lib-storage/index/dbox Makefile.am, 1.4, 1.5 dbox-list.c, 1.8, NONE dbox-storage.c, 1.22, 1.23 dbox-storage.h, 1.14, 1.15
- Next message: [dovecot-cvs] dovecot/src/lib-storage/register .cvsignore, 1.1, 1.2 Makefile.am, 1.3, 1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /var/lib/cvs/dovecot/src/lib-storage/list
In directory talvi:/tmp/cvs-serv25364/src/lib-storage/list
Added Files:
.cvsignore mailbox-list-fs-iter.c mailbox-list-fs.c
mailbox-list-fs.h mailbox-list-maildir-iter.c
mailbox-list-maildir.c mailbox-list-maildir.h
subscription-file.c subscription-file.h
Log Message:
Initial code for separation of mailbox accessing and directory layout
handling. It's not yet possible to change the default layouts though.
--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations
--- NEW FILE: mailbox-list-fs-iter.c ---
/* Copyright (C) 2002-2006 Timo Sirainen */
#include "lib.h"
#include "home-expand.h"
#include "unlink-directory.h"
#include "imap-match.h"
#include "subscription-file.h"
#include "mailbox-list-fs.h"
#include <dirent.h>
struct list_dir_context {
struct list_dir_context *prev;
DIR *dirp;
char *real_path, *virtual_path;
};
struct fs_list_iterate_context {
struct mailbox_list_iterate_context ctx;
struct imap_match_glob *glob;
struct subsfile_list_context *subsfile_ctx;
bool inbox_found;
struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx);
pool_t info_pool;
struct mailbox_info info;
struct list_dir_context *dir;
};
static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx);
static struct mailbox_info *fs_list_path(struct fs_list_iterate_context *ctx);
static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx);
static const char *mask_get_dir(const char *mask)
{
const char *p, *last_dir;
last_dir = NULL;
for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
if (*p == '/')
last_dir = p;
}
return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
}
static int list_opendir(struct mailbox_list *list,
const char *path, bool root, DIR **dirp)
{
*dirp = opendir(*path == '\0' ? "/" : path);
if (*dirp != NULL)
return 1;
if (ENOTFOUND(errno)) {
/* root) user gave invalid hiearchy, ignore
sub) probably just race condition with other client
deleting the mailbox. */
return 0;
}
if (errno == EACCES) {
if (!root) {
/* subfolder, ignore */
return 0;
}
mailbox_list_set_error(list, "Access denied");
return -1;
}
mailbox_list_set_critical(list, "opendir(%s) failed: %m", path);
return -1;
}
struct mailbox_list_iterate_context *
fs_list_iter_init(struct mailbox_list *_list,
const char *ref, const char *mask,
enum mailbox_list_iter_flags flags)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
struct fs_list_iterate_context *ctx;
const char *path, *virtual_path;
DIR *dirp;
mailbox_list_clear_error(&list->list);
ctx = i_new(struct fs_list_iterate_context, 1);
ctx->ctx.list = _list;
ctx->ctx.flags = flags;
ctx->info_pool = pool_alloconly_create("fs list", 1024);
ctx->next = fs_list_next;
/* check that we're not trying to do any "../../" lists */
if (!mailbox_list_is_valid_mask(_list, ref) ||
!mailbox_list_is_valid_mask(_list, mask)) {
mailbox_list_set_error(_list, "Invalid mask");
ctx->ctx.failed = TRUE;
return &ctx->ctx;
}
if (*mask == '/' || *mask == '~') {
/* mask overrides reference */
} else if (*ref != '\0') {
/* merge reference and mask */
mask = t_strconcat(ref, mask, NULL);
}
if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0) {
ctx->next = fs_list_subs;
path = t_strconcat(_list->set.control_dir != NULL ?
_list->set.control_dir : _list->set.root_dir,
"/", _list->set.subscription_fname, NULL);
ctx->subsfile_ctx = subsfile_list_init(_list, path);
if (ctx->subsfile_ctx == NULL) {
ctx->next = fs_list_next;
ctx->ctx.failed = TRUE;
return &ctx->ctx;
}
ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
return &ctx->ctx;
}
/* if we're matching only subdirectories, don't bother scanning the
parent directories */
virtual_path = mask_get_dir(mask);
path = mailbox_list_get_path(_list, virtual_path,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (list_opendir(_list, path, TRUE, &dirp) < 0)
return &ctx->ctx;
/* if user gave invalid directory, we just don't show any results. */
ctx->ctx.flags = flags;
ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
if (virtual_path != NULL && dirp != NULL)
ctx->next = fs_list_path;
if (dirp != NULL) {
ctx->dir = i_new(struct list_dir_context, 1);
ctx->dir->dirp = dirp;
ctx->dir->real_path = i_strdup(path);
ctx->dir->virtual_path = i_strdup(virtual_path);
}
return &ctx->ctx;
}
static void list_dir_context_free(struct list_dir_context *dir)
{
(void)closedir(dir->dirp);
i_free(dir->real_path);
i_free(dir->virtual_path);
i_free(dir);
}
int fs_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
{
struct fs_list_iterate_context *ctx =
(struct fs_list_iterate_context *)_ctx;
int ret = ctx->ctx.failed ? -1 : 0;
if (ctx->subsfile_ctx != NULL) {
if (subsfile_list_deinit(ctx->subsfile_ctx) < 0)
ret = -1;
}
while (ctx->dir != NULL) {
struct list_dir_context *dir = ctx->dir;
ctx->dir = dir->prev;
list_dir_context_free(dir);
}
if (ctx->info_pool != NULL)
pool_unref(ctx->info_pool);
if (ctx->glob != NULL)
imap_match_deinit(&ctx->glob);
i_free(ctx);
return ret;
}
struct mailbox_info *
fs_list_iter_next(struct mailbox_list_iterate_context *_ctx)
{
struct fs_list_iterate_context *ctx =
(struct fs_list_iterate_context *)_ctx;
if (ctx->ctx.failed)
return NULL;
return ctx->next(ctx);
}
static int
list_file(struct fs_list_iterate_context *ctx, const struct dirent *d)
{
const char *fname = d->d_name;
struct list_dir_context *dir;
const char *list_path, *real_path, *path, *inbox_path;
DIR *dirp;
enum imap_match_result match, match2;
int ret;
/* skip . and .. */
if (fname[0] == '.' &&
(fname[1] == '\0' ||
(fname[1] == '.' && fname[2] == '\0')))
return 0;
/* check the mask */
if (ctx->dir->virtual_path == NULL)
list_path = fname;
else {
list_path = t_strconcat(ctx->dir->virtual_path,
"/", fname, NULL);
}
if ((match = imap_match(ctx->glob, list_path)) < 0)
return 0;
/* get the info.flags using callback */
ctx->info.flags = 0;
ret = ctx->ctx.list->callback(ctx->dir->real_path, fname,
mailbox_list_get_file_type(d),
ctx->ctx.flags, &ctx->info.flags,
ctx->ctx.list->context);
if (ret <= 0)
return ret;
/* make sure we give only one correct INBOX */
real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
if (strcasecmp(list_path, "INBOX") == 0 &&
(ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0) {
inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (ctx->inbox_found || strcmp(real_path, inbox_path) != 0)
return 0;
ctx->inbox_found = TRUE;
}
if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) {
/* subdirectory. scan inside it. */
path = t_strconcat(list_path, "/", NULL);
match2 = imap_match(ctx->glob, path);
if (match > 0)
ctx->info.name = p_strdup(ctx->info_pool, list_path);
else if (match2 > 0)
ctx->info.name = p_strdup(ctx->info_pool, path);
else
ctx->info.name = NULL;
ret = match2 < 0 ? 0 :
list_opendir(ctx->ctx.list, real_path, FALSE, &dirp);
if (ret > 0) {
dir = i_new(struct list_dir_context, 1);
dir->dirp = dirp;
dir->real_path = i_strdup(real_path);
dir->virtual_path = i_strdup(list_path);
dir->prev = ctx->dir;
ctx->dir = dir;
} else if (ret < 0)
return -1;
return match > 0 || match2 > 0;
} else if (match > 0) {
ctx->info.name = p_strdup(ctx->info_pool, list_path);
return 1;
}
return 0;
}
static void
path_split(const char *path, const char **dir_r, const char **fname_r)
{
const char *p;
p = strrchr(path, '/');
if (p == NULL) {
*dir_r = "";
*fname_r = path;
} else {
*dir_r = t_strdup_until(path, p);
*fname_r = p + 1;
}
}
static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx)
{
const char *name, *path, *p, *dir, *fname;
enum imap_match_result match = IMAP_MATCH_NO;
while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
match = imap_match(ctx->glob, name);
if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
break;
}
if (name == NULL)
return NULL;
ctx->info.flags = 0;
ctx->info.name = name;
if (match == IMAP_MATCH_PARENT) {
/* placeholder */
ctx->info.flags = MAILBOX_PLACEHOLDER;
while ((p = strrchr(name, '/')) != NULL) {
name = t_strdup_until(name, p);
if (imap_match(ctx->glob, name) > 0) {
p_clear(ctx->info_pool);
ctx->info.name = p_strdup(ctx->info_pool, name);
return &ctx->info;
}
}
i_unreached();
}
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0)
return &ctx->info;
t_push();
path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
path_split(path, &dir, &fname);
if (ctx->ctx.list->callback(dir, fname,
MAILBOX_LIST_FILE_TYPE_UNKNOWN,
ctx->ctx.flags, &ctx->info.flags,
ctx->ctx.list->context) < 0)
ctx->ctx.failed = TRUE;
t_pop();
return &ctx->info;
}
static struct mailbox_info *fs_list_path(struct fs_list_iterate_context *ctx)
{
ctx->next = fs_list_next;
ctx->info.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
ctx->info.name =
p_strconcat(ctx->info_pool, ctx->dir->virtual_path, "/", NULL);
if (imap_match(ctx->glob, ctx->info.name) > 0)
return &ctx->info;
else
return ctx->next(ctx);
}
static struct mailbox_info *fs_list_inbox(struct fs_list_iterate_context *ctx)
{
const char *inbox_path, *dir, *fname;
ctx->info.flags = 0;
ctx->info.name = "INBOX";
t_push();
inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
MAILBOX_LIST_PATH_TYPE_MAILBOX);
path_split(inbox_path, &dir, &fname);
if (ctx->ctx.list->callback(dir, fname,
MAILBOX_LIST_FILE_TYPE_UNKNOWN,
ctx->ctx.flags, &ctx->info.flags,
ctx->ctx.list->context) < 0)
ctx->ctx.failed = TRUE;
t_pop();
return &ctx->info;
}
static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx)
{
struct list_dir_context *dir;
struct dirent *d;
int ret;
p_clear(ctx->info_pool);
while (ctx->dir != NULL) {
/* NOTE: list_file() may change ctx->dir */
while ((d = readdir(ctx->dir->dirp)) != NULL) {
t_push();
ret = list_file(ctx, d);
t_pop();
if (ret > 0)
return &ctx->info;
if (ret < 0) {
ctx->ctx.failed = TRUE;
return NULL;
}
}
dir = ctx->dir;
ctx->dir = dir->prev;
list_dir_context_free(dir);
}
if (!ctx->inbox_found &&
(ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) {
/* show inbox */
ctx->inbox_found = TRUE;
return fs_list_inbox(ctx);
}
/* finished */
return NULL;
}
--- NEW FILE: mailbox-list-fs.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "hostpid.h"
#include "home-expand.h"
#include "subscription-file.h"
#include "mailbox-list-fs.h"
#include <sys/stat.h>
extern struct mailbox_list fs_mailbox_list;
static struct mailbox_list *fs_list_alloc(void)
{
struct fs_mailbox_list *list;
pool_t pool;
pool = pool_alloconly_create("fs list", 512);
list = p_new(pool, struct fs_mailbox_list, 1);
list->list = fs_mailbox_list;
list->list.pool = pool;
list->temp_prefix = p_strconcat(pool, ".temp.", my_hostname, ".",
my_pid, ".", NULL);
return &list->list;
}
static void fs_list_deinit(struct mailbox_list *_list)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
pool_unref(list->list.pool);
}
static bool fs_list_is_valid_common(const char *name, size_t *len_r)
{
*len_r = strlen(name);
if (name[0] == '\0' || name[*len_r-1] == '/')
return FALSE;
return TRUE;
}
static bool
fs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name)
{
const char *p;
bool newdir;
size_t maildir_len;
/* make sure it's not absolute path */
if (*name == '/' || *name == '~')
return FALSE;
/* make sure the mailbox name doesn't contain any foolishness:
"../" could give access outside the mailbox directory.
"./" and "//" could fool ACL checks. */
newdir = TRUE;
maildir_len = strlen(list->set.maildir_name);
for (p = name; *p != '\0'; p++) {
if (newdir) {
if (p[0] == '/')
return FALSE; /* // */
if (p[0] == '.') {
if (p[1] == '/')
return FALSE; /* ./ */
if (p[1] == '.' && p[2] == '/')
return FALSE; /* ../ */
}
if (maildir_len > 0 &&
strncmp(p, list->set.maildir_name,
maildir_len) == 0 &&
(p[maildir_len-1] == '\0' ||
p[maildir_len-1] == '/')) {
/* don't allow maildir_name to be used as part
of the mailbox name */
return FALSE;
}
}
newdir = p[0] == '/';
}
if (name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'))) {
/* "." and ".." aren't allowed. */
return FALSE;
}
return TRUE;
}
static bool
fs_is_valid_mask(struct mailbox_list *list, const char *mask)
{
if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
return TRUE;
return fs_list_is_valid_common_nonfs(list, mask);
}
static bool
fs_is_valid_existing_name(struct mailbox_list *list, const char *name)
{
size_t len;
if (!fs_list_is_valid_common(name, &len))
return FALSE;
if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
return TRUE;
return fs_list_is_valid_common_nonfs(list, name);
}
static bool
fs_is_valid_create_name(struct mailbox_list *list, const char *name)
{
size_t len;
if (!fs_list_is_valid_common(name, &len))
return FALSE;
if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH)
return FALSE;
if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
return TRUE;
if (mailbox_list_name_is_too_large(name, '/'))
return FALSE;
return fs_list_is_valid_common_nonfs(list, name);
}
static const char *
fs_list_get_path(struct mailbox_list *_list, const char *name,
enum mailbox_list_path_type type)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
const struct mailbox_list_settings *set = &_list->set;
mailbox_list_clear_error(&list->list);
if (name == NULL) {
/* return root directories */
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
return set->root_dir;
case MAILBOX_LIST_PATH_TYPE_CONTROL:
return set->control_dir != NULL ?
set->control_dir : set->root_dir;
case MAILBOX_LIST_PATH_TYPE_INDEX:
return set->index_dir != NULL ?
set->index_dir : set->root_dir;
}
i_unreached();
}
i_assert(mailbox_list_is_valid_existing_name(_list, name));
if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
(*name == '/' || *name == '~'))
return name;
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
if (*set->maildir_name != '\0')
return t_strdup_printf("%s/%s", set->root_dir, name);
break;
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
break;
case MAILBOX_LIST_PATH_TYPE_CONTROL:
if (set->control_dir != NULL)
return t_strdup_printf("%s/%s", set->control_dir,
name);
break;
case MAILBOX_LIST_PATH_TYPE_INDEX:
if (set->index_dir != NULL) {
if (*set->index_dir == '\0')
return "";
return t_strdup_printf("%s/%s", set->index_dir, name);
}
break;
}
if (strcmp(name, "INBOX") == 0) {
return set->inbox_path != NULL ?
set->inbox_path : set->root_dir;
}
if (*set->maildir_name == '\0')
return t_strdup_printf("%s/%s", set->root_dir, name);
else {
return t_strdup_printf("%s/%s/%s", set->root_dir, name,
set->maildir_name);
}
}
static int
fs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name,
enum mailbox_name_status *status)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
struct stat st;
const char *path;
mailbox_list_clear_error(&list->list);
if (!mailbox_list_is_valid_existing_name(_list, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
path = mailbox_list_get_path(_list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
*status = MAILBOX_NAME_EXISTS;
return 0;
}
if (!mailbox_list_is_valid_create_name(_list, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
if (ENOTFOUND(errno) || errno == EACCES) {
*status = MAILBOX_NAME_VALID;
return 0;
} else if (errno == ENOTDIR) {
*status = MAILBOX_NAME_NOINFERIORS;
return 0;
} else {
mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
return -1;
}
}
static const char *
fs_list_get_temp_prefix(struct mailbox_list *_list)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
return list->temp_prefix;
}
static int fs_list_set_subscribed(struct mailbox_list *_list,
const char *name, bool set)
{
struct fs_mailbox_list *list =
(struct fs_mailbox_list *)_list;
const char *path;
mailbox_list_clear_error(&list->list);
path = t_strconcat(_list->set.control_dir != NULL ?
_list->set.control_dir : _list->set.root_dir,
"/", _list->set.subscription_fname, NULL);
return subsfile_set_subscribed(_list, path, list->temp_prefix,
name, set);
}
struct mailbox_list fs_mailbox_list = {
MEMBER(name) "fs",
MEMBER(hierarchy_sep) '/',
MEMBER(mailbox_name_max_length) PATH_MAX,
{
fs_list_alloc,
fs_list_deinit,
fs_is_valid_mask,
fs_is_valid_existing_name,
fs_is_valid_create_name,
fs_list_get_path,
fs_list_get_mailbox_name_status,
fs_list_get_temp_prefix,
fs_list_iter_init,
fs_list_iter_next,
fs_list_iter_deinit,
fs_list_set_subscribed
}
};
--- NEW FILE: mailbox-list-fs.h ---
#ifndef __MAILBOX_LIST_FS_H
#define __MAILBOX_LIST_FS_H
#include "mailbox-list-private.h"
/* Don't allow creating too long mailbox names. They could start causing
problems when they reach the limit. */
#define FS_MAX_CREATE_MAILBOX_NAME_LENGTH (PATH_MAX/2)
struct fs_mailbox_list {
struct mailbox_list list;
const char *temp_prefix;
};
struct mailbox_list_iterate_context *
fs_list_iter_init(struct mailbox_list *_list,
const char *ref, const char *mask,
enum mailbox_list_iter_flags flags);
int fs_list_iter_deinit(struct mailbox_list_iterate_context *ctx);
struct mailbox_info *
fs_list_iter_next(struct mailbox_list_iterate_context *ctx);
#endif
--- NEW FILE: mailbox-list-maildir-iter.c ---
/* Copyright (C) 2002-2006 Timo Sirainen */
#include "lib.h"
#include "str.h"
#include "home-expand.h"
#include "imap-match.h"
#include "subscription-file.h"
#include "mailbox-tree.h"
#include "mailbox-list-maildir.h"
#include <dirent.h>
#define MAILBOX_FLAG_MATCHED 0x40000000
struct maildir_list_iterate_context {
struct mailbox_list_iterate_context ctx;
pool_t pool;
const char *dir, *prefix;
struct mailbox_tree_context *tree_ctx;
string_t *node_path;
size_t parent_pos;
struct mailbox_node *root, *next_node;
struct mailbox_info info;
};
static void maildir_nodes_fix(struct mailbox_node *node, bool is_subs)
{
while (node != NULL) {
if (node->children != NULL) {
node->flags |= MAILBOX_CHILDREN;
node->flags &= ~MAILBOX_NOCHILDREN;
maildir_nodes_fix(node->children, is_subs);
} else if ((node->flags & MAILBOX_PLACEHOLDER) != 0) {
if (!is_subs) {
node->flags &= ~MAILBOX_PLACEHOLDER;
node->flags |= MAILBOX_NOSELECT;
}
}
node = node->next;
}
}
static int
maildir_fill_readdir(struct maildir_list_iterate_context *ctx,
struct imap_match_glob *glob, bool update_only)
{
DIR *dirp;
struct dirent *d;
const char *p, *mailbox_c;
string_t *mailbox;
enum mailbox_info_flags flags;
enum imap_match_result match;
struct mailbox_node *node;
bool created;
char hierarchy_sep;
int ret;
dirp = opendir(ctx->dir);
if (dirp == NULL) {
if (errno != ENOENT) {
mailbox_list_set_critical(ctx->ctx.list,
"opendir(%s) failed: %m", ctx->dir);
return -1;
}
return 0;
}
hierarchy_sep = ctx->ctx.list->hierarchy_sep;
t_push();
mailbox = t_str_new(PATH_MAX);
while ((d = readdir(dirp)) != NULL) {
const char *fname = d->d_name;
if (fname[0] != hierarchy_sep)
continue;
/* skip . and .. */
if (fname[0] == '.' &&
(fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0')))
continue;
/* make sure the mask matches */
str_truncate(mailbox, 0);
str_append(mailbox, ctx->prefix);
str_append(mailbox, fname + 1);
mailbox_c = str_c(mailbox);
match = imap_match(glob, mailbox_c);
if (match != IMAP_MATCH_YES &&
match != IMAP_MATCH_PARENT)
continue;
/* check if this is an actual mailbox */
flags = 0;
ret = ctx->ctx.list->callback(ctx->dir, fname,
mailbox_list_get_file_type(d),
ctx->ctx.flags, &flags,
ctx->ctx.list->context);
if (ret < 0) {
t_pop();
return -1;
}
if (ret == 0)
continue;
if (match == IMAP_MATCH_PARENT) {
t_push();
while ((p = strrchr(mailbox_c,
hierarchy_sep)) != NULL) {
str_truncate(mailbox, (size_t) (p-mailbox_c));
mailbox_c = str_c(mailbox);
if (imap_match(glob, mailbox_c) > 0)
break;
}
i_assert(p != NULL);
created = FALSE;
node = update_only ?
mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
mailbox_tree_get(ctx->tree_ctx,
mailbox_c, &created);
if (node != NULL) {
if (created)
node->flags = MAILBOX_PLACEHOLDER;
node->flags |= MAILBOX_CHILDREN |
MAILBOX_FLAG_MATCHED;
node->flags &= ~MAILBOX_NOCHILDREN;
}
t_pop();
} else {
created = FALSE;
node = update_only ?
mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
mailbox_tree_get(ctx->tree_ctx,
mailbox_c, &created);
if (node != NULL) {
if (created)
node->flags = MAILBOX_NOCHILDREN;
node->flags &= ~(MAILBOX_PLACEHOLDER |
MAILBOX_NONEXISTENT);
node->flags |= MAILBOX_FLAG_MATCHED;
}
}
}
t_pop();
if (closedir(dirp) < 0) {
mailbox_list_set_critical(ctx->ctx.list,
"readdir(%s) failed: %m", ctx->dir);
return -1;
}
if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
(ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0) {
/* make sure INBOX is there */
node = mailbox_tree_get(ctx->tree_ctx, "INBOX", &created);
if (created)
node->flags = MAILBOX_NOCHILDREN;
else
node->flags &= ~MAILBOX_PLACEHOLDER;
switch (imap_match(glob, "INBOX")) {
case IMAP_MATCH_YES:
case IMAP_MATCH_PARENT:
node->flags |= MAILBOX_FLAG_MATCHED;
break;
default:
break;
}
}
maildir_nodes_fix(mailbox_tree_get(ctx->tree_ctx, NULL, NULL),
(ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0);
return 0;
}
static int maildir_fill_subscribed(struct maildir_list_iterate_context *ctx,
struct imap_match_glob *glob)
{
struct subsfile_list_context *subsfile_ctx;
const char *path, *name, *p;
struct mailbox_node *node;
char hierarchy_sep;
bool created;
path = t_strconcat(ctx->ctx.list->set.control_dir != NULL ?
ctx->ctx.list->set.control_dir :
ctx->ctx.list->set.root_dir,
"/", ctx->ctx.list->set.subscription_fname, NULL);
subsfile_ctx = subsfile_list_init(ctx->ctx.list, path);
hierarchy_sep = ctx->ctx.list->hierarchy_sep;
while ((name = subsfile_list_next(subsfile_ctx)) != NULL) {
switch (imap_match(glob, name)) {
case IMAP_MATCH_YES:
node = mailbox_tree_get(ctx->tree_ctx, name, NULL);
node->flags = MAILBOX_FLAG_MATCHED;
if ((ctx->ctx.flags &
MAILBOX_LIST_ITER_FAST_FLAGS) == 0) {
node->flags |= MAILBOX_NONEXISTENT |
MAILBOX_NOCHILDREN;
}
break;
case IMAP_MATCH_PARENT:
/* placeholder */
while ((p = strrchr(name, hierarchy_sep)) != NULL) {
name = t_strdup_until(name, p);
if (imap_match(glob, name) > 0)
break;
}
i_assert(p != NULL);
node = mailbox_tree_get(ctx->tree_ctx, name, &created);
if (created) node->flags = MAILBOX_PLACEHOLDER;
node->flags |= MAILBOX_FLAG_MATCHED | MAILBOX_CHILDREN;
node->flags &= ~MAILBOX_NOCHILDREN;
break;
default:
break;
}
}
return subsfile_list_deinit(subsfile_ctx);
}
struct mailbox_list_iterate_context *
maildir_list_iter_init(struct mailbox_list *_list,
const char *ref, const char *mask,
enum mailbox_list_iter_flags flags)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
struct maildir_list_iterate_context *ctx;
struct imap_match_glob *glob;
const char *dir, *p;
pool_t pool;
mailbox_list_clear_error(&list->list);
pool = pool_alloconly_create("maildir_list", 1024);
ctx = p_new(pool, struct maildir_list_iterate_context, 1);
ctx->ctx.list = _list;
ctx->ctx.flags = flags;
ctx->pool = pool;
ctx->tree_ctx = mailbox_tree_init(_list->hierarchy_sep);
if (*ref != '\0') {
/* join reference + mask */
mask = t_strconcat(ref, mask, NULL);
}
glob = imap_match_init(pool, mask, TRUE, _list->hierarchy_sep);
ctx->dir = _list->set.root_dir;
ctx->prefix = "";
if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0) {
if (maildir_fill_subscribed(ctx, glob) < 0) {
ctx->ctx.failed = TRUE;
return &ctx->ctx;
}
} else if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
(p = strrchr(mask, '/')) != NULL) {
dir = t_strdup_until(mask, p);
ctx->prefix = p_strdup_until(pool, mask, p+1);
if (*mask != '/' && *mask != '~')
dir = t_strconcat(_list->set.root_dir, "/", dir, NULL);
ctx->dir = p_strdup(pool, home_expand(dir));
}
if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0 ||
(ctx->ctx.flags & MAILBOX_LIST_ITER_FAST_FLAGS) == 0) {
bool update_only = (flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0;
if (maildir_fill_readdir(ctx, glob, update_only) < 0) {
ctx->ctx.failed = TRUE;
return &ctx->ctx;
}
}
ctx->node_path = str_new(pool, 256);
ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL);
return &ctx->ctx;
}
int maildir_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
{
struct maildir_list_iterate_context *ctx =
(struct maildir_list_iterate_context *)_ctx;
int ret = ctx->ctx.failed ? -1 : 0;
mailbox_tree_deinit(ctx->tree_ctx);
pool_unref(ctx->pool);
return ret;
}
static struct mailbox_node *find_next(struct mailbox_node **node,
string_t *path, char hierarchy_sep)
{
struct mailbox_node *child;
size_t len;
while (*node != NULL) {
if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0)
return *node;
if ((*node)->children != NULL) {
len = str_len(path);
if (len != 0)
str_append_c(path, hierarchy_sep);
str_append(path, (*node)->name);
child = find_next(&(*node)->children, path,
hierarchy_sep);
if (child != NULL)
return child;
str_truncate(path, len);
}
*node = (*node)->next;
}
return NULL;
}
struct mailbox_info *
maildir_list_iter_next(struct mailbox_list_iterate_context *_ctx)
{
struct maildir_list_iterate_context *ctx =
(struct maildir_list_iterate_context *)_ctx;
struct mailbox_node *node;
for (node = ctx->next_node; node != NULL; node = node->next) {
if ((node->flags & MAILBOX_FLAG_MATCHED) != 0)
break;
}
if (node == NULL) {
if (ctx->root == NULL)
return NULL;
str_truncate(ctx->node_path, 0);
node = find_next(&ctx->root, ctx->node_path,
ctx->ctx.list->hierarchy_sep);
ctx->parent_pos = str_len(ctx->node_path);
if (node == NULL)
return NULL;
}
ctx->next_node = node->next;
i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0);
node->flags &= ~MAILBOX_FLAG_MATCHED;
str_truncate(ctx->node_path, ctx->parent_pos);
if (ctx->parent_pos != 0)
str_append_c(ctx->node_path, ctx->ctx.list->hierarchy_sep);
str_append(ctx->node_path, node->name);
ctx->info.name = str_c(ctx->node_path);
ctx->info.flags = node->flags;
return &ctx->info;
}
--- NEW FILE: mailbox-list-maildir.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "hostpid.h"
#include "home-expand.h"
#include "subscription-file.h"
#include "mailbox-list-maildir.h"
#include <sys/stat.h>
extern struct mailbox_list maildir_mailbox_list;
static struct mailbox_list *maildir_list_alloc(void)
{
struct maildir_mailbox_list *list;
pool_t pool;
pool = pool_alloconly_create("maildir++ list", 512);
list = p_new(pool, struct maildir_mailbox_list, 1);
list->list = maildir_mailbox_list;
list->list.pool = pool;
list->temp_prefix =
p_strconcat(pool, "temp.", my_hostname, ".", my_pid, ".", NULL);
return &list->list;
}
static void maildir_list_deinit(struct mailbox_list *_list)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
pool_unref(list->list.pool);
}
static const char *
maildir_list_get_absolute_path(struct mailbox_list *list, const char *name)
{
const char *p;
name = home_expand(name);
p = strrchr(name, '/');
if (p == NULL)
return name;
return t_strdup_printf("%s/%c%s", t_strdup_until(name, p),
list->hierarchy_sep, p+1);
}
static bool
maildir_list_is_valid_common(struct mailbox_list *list, const char *name,
size_t *len_r)
{
size_t len;
/* check that there are no adjacent hierarchy separators */
for (len = 0; name[len] != '\0'; len++) {
if (name[len] == list->hierarchy_sep &&
name[len+1] == list->hierarchy_sep)
return FALSE;
}
if (len == 0 || name[len-1] == '/')
return FALSE;
if (name[0] == list->hierarchy_sep ||
name[len-1] == list->hierarchy_sep)
return FALSE;
*len_r = len;
return TRUE;
}
static bool maildir_list_is_valid_common_nonfs(const char *name)
{
if (*name == '~' || strchr(name, '/') != NULL)
return FALSE;
if (name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'))) {
/* "." and ".." aren't allowed. */
return FALSE;
}
return TRUE;
}
static bool
maildir_is_valid_mask(struct mailbox_list *list __attr_unused__,
const char *mask __attr_unused__)
{
i_unreached();
return FALSE;
}
static bool
maildir_is_valid_existing_name(struct mailbox_list *list, const char *name)
{
size_t len;
if (!maildir_list_is_valid_common(list, name, &len))
return FALSE;
if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
return TRUE;
return maildir_list_is_valid_common_nonfs(name);
}
static bool
maildir_is_valid_create_name(struct mailbox_list *list, const char *name)
{
size_t len;
if (!maildir_list_is_valid_common(list, name, &len))
return FALSE;
if (len > MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH)
return FALSE;
if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
return TRUE;
if (!maildir_list_is_valid_common_nonfs(name))
return FALSE;
if (mailbox_list_name_is_too_large(name, list->hierarchy_sep))
return FALSE;
return TRUE;
}
static const char *
maildir_list_get_path(struct mailbox_list *_list, const char *name,
enum mailbox_list_path_type type)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
mailbox_list_clear_error(&list->list);
if (name == NULL) {
/* return root directories */
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
return _list->set.root_dir;
case MAILBOX_LIST_PATH_TYPE_CONTROL:
return _list->set.control_dir != NULL ?
_list->set.control_dir : _list->set.root_dir;
case MAILBOX_LIST_PATH_TYPE_INDEX:
return _list->set.index_dir != NULL ?
_list->set.index_dir : _list->set.root_dir;
}
i_unreached();
}
i_assert(mailbox_list_is_valid_existing_name(_list, name));
if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
(*name == '/' || *name == '~'))
return maildir_list_get_absolute_path(_list, name);
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
break;
case MAILBOX_LIST_PATH_TYPE_CONTROL:
if (_list->set.control_dir != NULL) {
return t_strdup_printf("%s/%c%s",
_list->set.control_dir,
_list->hierarchy_sep, name);
}
break;
case MAILBOX_LIST_PATH_TYPE_INDEX:
if (_list->set.index_dir != NULL) {
if (*_list->set.index_dir == '\0')
return "";
return t_strdup_printf("%s/%c%s", _list->set.index_dir,
_list->hierarchy_sep, name);
}
break;
}
if (strcmp(name, "INBOX") == 0) {
return _list->set.inbox_path != NULL ?
_list->set.inbox_path : _list->set.root_dir;
}
return t_strdup_printf("%s/%c%s", _list->set.root_dir,
_list->hierarchy_sep, name);
}
static int
maildir_list_get_mailbox_name_status(struct mailbox_list *_list,
const char *name,
enum mailbox_name_status *status)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
struct stat st;
const char *path;
mailbox_list_clear_error(&list->list);
if (!mailbox_list_is_valid_existing_name(_list, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
path = mailbox_list_get_path(_list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
*status = MAILBOX_NAME_EXISTS;
return 0;
}
if (!mailbox_list_is_valid_create_name(_list, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
if (ENOTFOUND(errno) || errno == EACCES) {
*status = MAILBOX_NAME_VALID;
return 0;
} else {
mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
return -1;
}
}
static const char *
maildir_list_get_temp_prefix(struct mailbox_list *_list)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
return list->temp_prefix;
}
static int maildir_list_set_subscribed(struct mailbox_list *_list,
const char *name, bool set)
{
struct maildir_mailbox_list *list =
(struct maildir_mailbox_list *)_list;
const char *path;
mailbox_list_clear_error(&list->list);
path = t_strconcat(_list->set.control_dir != NULL ?
_list->set.control_dir : _list->set.root_dir,
"/", _list->set.subscription_fname, NULL);
return subsfile_set_subscribed(_list, path, list->temp_prefix,
name, set);
}
struct mailbox_list maildir_mailbox_list = {
MEMBER(name) "maildir++",
MEMBER(hierarchy_sep) '.',
MEMBER(mailbox_name_max_length) PATH_MAX,
{
maildir_list_alloc,
maildir_list_deinit,
maildir_is_valid_mask,
maildir_is_valid_existing_name,
maildir_is_valid_create_name,
maildir_list_get_path,
maildir_list_get_mailbox_name_status,
maildir_list_get_temp_prefix,
maildir_list_iter_init,
maildir_list_iter_next,
maildir_list_iter_deinit,
maildir_list_set_subscribed
}
};
--- NEW FILE: mailbox-list-maildir.h ---
#ifndef __MAILBOX_LIST_MAILDIR_H
#define __MAILBOX_LIST_MAILDIR_H
#include "mailbox-list-private.h"
/* Don't allow creating too long mailbox names. They could start causing
problems when they reach the limit. */
#define MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH (PATH_MAX/2)
struct maildir_mailbox_list {
struct mailbox_list list;
const char *temp_prefix;
};
struct mailbox_list_iterate_context *
maildir_list_iter_init(struct mailbox_list *_list,
const char *ref, const char *mask,
enum mailbox_list_iter_flags flags);
int maildir_list_iter_deinit(struct mailbox_list_iterate_context *ctx);
struct mailbox_info *
maildir_list_iter_next(struct mailbox_list_iterate_context *ctx);
#endif
--- NEW FILE: subscription-file.c ---
/* Copyright (C) 2002-2003 Timo Sirainen */
#include "lib.h"
#include "istream.h"
#include "ostream.h"
#include "nfs-workarounds.h"
#include "file-dotlock.h"
#include "mailbox-list-private.h"
#include "subscription-file.h"
#include <unistd.h>
#include <fcntl.h>
#define SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
#define SUBSCRIPTION_FILE_LOCK_TIMEOUT 120
#define SUBSCRIPTION_FILE_CHANGE_TIMEOUT 30
struct subsfile_list_context {
pool_t pool;
struct mailbox_list *list;
struct istream *input;
const char *path;
bool failed;
};
static void subsfile_set_syscall_error(struct mailbox_list *list,
const char *function, const char *path)
{
i_assert(function != NULL);
if (errno == EACCES)
mailbox_list_set_error(list, "Permission denied");
else {
mailbox_list_set_critical(list,
"%s failed with subscription file %s: %m",
function, path);
}
}
static const char *next_line(struct mailbox_list *list, const char *path,
struct istream *input, bool *failed_r,
bool ignore_estale)
{
const char *line;
*failed_r = FALSE;
if (input == NULL)
return NULL;
while ((line = i_stream_next_line(input)) == NULL) {
switch (i_stream_read(input)) {
case -1:
if (input->stream_errno != 0 &&
(input->stream_errno != ESTALE || !ignore_estale)) {
subsfile_set_syscall_error(list,
"read()", path);
*failed_r = TRUE;
}
return NULL;
case -2:
/* mailbox name too large */
mailbox_list_set_critical(list,
"Subscription file %s contains lines longer "
"than %u characters", path,
list->mailbox_name_max_length);
*failed_r = TRUE;
return NULL;
}
}
return line;
}
int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
const char *temp_prefix, const char *name, bool set)
{
struct dotlock_settings dotlock_set;
struct dotlock *dotlock;
const char *line;
struct istream *input;
struct ostream *output;
int fd_in, fd_out;
bool found, failed = FALSE;
if (strcasecmp(name, "INBOX") == 0)
name = "INBOX";
memset(&dotlock_set, 0, sizeof(dotlock_set));
dotlock_set.temp_prefix = temp_prefix;
dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
fd_out = file_dotlock_open(&dotlock_set, path, 0, &dotlock);
if (fd_out == -1) {
if (errno == EAGAIN) {
mailbox_list_set_error(list,
"Timeout waiting for subscription file lock");
} else {
subsfile_set_syscall_error(list,
"file_dotlock_open()", path);
}
return -1;
}
fd_in = nfs_safe_open(path, O_RDONLY);
if (fd_in == -1 && errno != ENOENT) {
subsfile_set_syscall_error(list, "open()", path);
(void)file_dotlock_delete(&dotlock);
return -1;
}
input = fd_in == -1 ? NULL :
i_stream_create_file(fd_in, default_pool,
list->mailbox_name_max_length+1, TRUE);
output = o_stream_create_file(fd_out, default_pool,
list->mailbox_name_max_length+1, FALSE);
found = FALSE;
while ((line = next_line(list, path, input,
&failed, FALSE)) != NULL) {
if (strcmp(line, name) == 0) {
found = TRUE;
if (!set)
continue;
}
if (o_stream_send_str(output, line) < 0 ||
o_stream_send(output, "\n", 1) < 0) {
subsfile_set_syscall_error(list, "write()", path);
failed = TRUE;
break;
}
}
if (!failed && set && !found) {
/* append subscription */
line = t_strconcat(name, "\n", NULL);
if (o_stream_send_str(output, line) < 0) {
subsfile_set_syscall_error(list, "write()", path);
failed = TRUE;
}
}
if (input != NULL)
i_stream_destroy(&input);
o_stream_destroy(&output);
if (failed || (set && found) || (!set && !found)) {
if (file_dotlock_delete(&dotlock) < 0) {
subsfile_set_syscall_error(list,
"file_dotlock_delete()", path);
failed = TRUE;
}
} else {
enum dotlock_replace_flags flags =
DOTLOCK_REPLACE_FLAG_VERIFY_OWNER;
if (file_dotlock_replace(&dotlock, flags) < 0) {
subsfile_set_syscall_error(list,
"file_dotlock_replace()", path);
failed = TRUE;
}
}
return failed ? -1 : 0;
}
struct subsfile_list_context *
subsfile_list_init(struct mailbox_list *list, const char *path)
{
struct subsfile_list_context *ctx;
pool_t pool;
int fd;
pool = pool_alloconly_create("subsfile_list",
list->mailbox_name_max_length + 1024);
ctx = p_new(pool, struct subsfile_list_context, 1);
ctx->pool = pool;
ctx->list = list;
fd = nfs_safe_open(path, O_RDONLY);
if (fd == -1) {
if (errno != ENOENT) {
subsfile_set_syscall_error(list, "open()", path);
ctx->failed = TRUE;
}
} else {
ctx->input =
i_stream_create_file(fd, pool,
list->mailbox_name_max_length+1,
TRUE);
}
ctx->path = p_strdup(pool, path);
return ctx;
}
int subsfile_list_deinit(struct subsfile_list_context *ctx)
{
int ret = ctx->failed ? -1 : 0;
if (ctx->input != NULL)
i_stream_destroy(&ctx->input);
pool_unref(ctx->pool);
return ret;
}
const char *subsfile_list_next(struct subsfile_list_context *ctx)
{
const char *line;
unsigned int i;
int fd;
if (ctx->failed || ctx->input == NULL)
return NULL;
for (i = 0;; i++) {
line = next_line(ctx->list, ctx->path, ctx->input, &ctx->failed,
i < SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT);
if (ctx->input->stream_errno != ESTALE ||
i == SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT)
break;
/* Reopen the subscription file and re-send everything.
this isn't the optimal behavior, but it's allowed by
IMAP and this way we don't have to read everything into
memory or try to play any guessing games. */
i_stream_destroy(&ctx->input);
fd = nfs_safe_open(ctx->path, O_RDONLY);
if (fd == -1) {
/* In case of ENOENT all the subscriptions got lost.
Just return end of subscriptions list in that
case. */
if (errno != ENOENT) {
subsfile_set_syscall_error(ctx->list, "open()",
ctx->path);
ctx->failed = TRUE;
}
return NULL;
}
ctx->input = i_stream_create_file(fd, ctx->pool,
ctx->list->mailbox_name_max_length+1,
TRUE);
}
return line;
}
--- NEW FILE: subscription-file.h ---
#ifndef __SUBSCRIPTION_FILE_H
#define __SUBSCRIPTION_FILE_H
struct mailbox_list;
/* Initialize new subscription file listing. */
struct subsfile_list_context *
subsfile_list_init(struct mailbox_list *list, const char *path);
/* Deinitialize subscription file listing. Returns 0 if ok, or -1 if some
error occurred while listing. */
int subsfile_list_deinit(struct subsfile_list_context *ctx);
/* Returns the next subscribed mailbox, or NULL. */
const char *subsfile_list_next(struct subsfile_list_context *ctx);
int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
const char *temp_prefix, const char *name,
bool set);
#endif
- Previous message: [dovecot-cvs] dovecot/src/lib-storage/index/dbox Makefile.am, 1.4, 1.5 dbox-list.c, 1.8, NONE dbox-storage.c, 1.22, 1.23 dbox-storage.h, 1.14, 1.15
- Next message: [dovecot-cvs] dovecot/src/lib-storage/register .cvsignore, 1.1, 1.2 Makefile.am, 1.3, 1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the dovecot-cvs
mailing list