[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


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



More information about the dovecot-cvs mailing list