<commit_before>/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "imap-match.h"
#include "mail-storage.h"
#include "mailbox-tree.h"
#include "mailbox-list-subscriptions.h"
#include "mailbox-list-fs.h"

#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>

struct list_dir_entry {
	const char *fname;
	enum mailbox_info_flags info_flags;
};

struct list_dir_context {
	struct list_dir_context *parent;

	pool_t pool;
	const char *storage_name;
	/* this directory's info flags. */
	enum mailbox_info_flags info_flags;

	/* all files in this directory */
	ARRAY_DEFINE(entries, struct list_dir_entry);
	unsigned int entry_idx;
};

struct fs_list_iterate_context {
	struct mailbox_list_iterate_context ctx;

	const char *const *valid_patterns;
	/* roots can be either /foo, ~user/bar or baz */
	ARRAY_DEFINE(roots, const char *);
	unsigned int root_idx;
	char sep;

	pool_t info_pool;
	struct mailbox_info info;
	/* current directory we're handling */
	struct list_dir_context *dir;

	unsigned int inbox_found:1;
};

static int
fs_get_existence_info_flag(struct fs_list_iterate_context *ctx,
			   const char *vname,
			   enum mailbox_info_flags *info_flags)
{
	struct mailbox *box;
	enum mailbox_existence existence;
	bool auto_boxes;
	int ret;

	auto_boxes = (ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0;
	box = mailbox_alloc(ctx->ctx.list, vname, 0);
	ret = mailbox_exists(box, auto_boxes, &existence);
	mailbox_free(&box);

	if (ret < 0) {
		/* this can only be an internal error */
		mailbox_list_set_internal_error(ctx->ctx.list);
		return -1;
	}
	switch (existence) {
	case MAILBOX_EXISTENCE_NONE:
		*info_flags |= MAILBOX_NONEXISTENT;
		break;
	case MAILBOX_EXISTENCE_NOSELECT:
		*info_flags |= MAILBOX_NOSELECT;
		break;
	case MAILBOX_EXISTENCE_SELECT:
		*info_flags |= MAILBOX_SELECT;
		break;
	}
	return 0;
}

static int
dir_entry_get(struct fs_list_iterate_context *ctx, const char *dir_path,
	      struct list_dir_context *dir, const struct dirent *d)
{
	const char *storage_name, *vname, *root_dir;
	struct list_dir_entry *entry;
	enum imap_match_result match;
	enum mailbox_info_flags info_flags;
	int ret;

	/* skip . and .. */
	if (d->d_name[0] == '.' &&
	    (d->d_name[1] == '\0' ||
	     (d->d_name[1] == '.' && d->d_name[2] == '\0')))
		return 0;

	if (strcmp(d->d_name, ctx->ctx.list->set.maildir_name) == 0) {
		/* mail storage's internal directory (e.g. dbox-Mails).
		   this also means that the parent is selectable */
		dir->info_flags &= ~MAILBOX_NOSELECT;
		dir->info_flags |= MAILBOX_SELECT;
		return 0;
	}
	if (strcmp(d->d_name, ctx->ctx.list->set.subscription_fname) == 0) {
		/* if this is the subscriptions file, skip it */
		root_dir = mailbox_list_get_path(ctx->ctx.list, NULL,
						 MAILBOX_LIST_PATH_TYPE_DIR);
		if (strcmp(root_dir, dir_path) == 0)
			return 0;
	}

	/* check the pattern */
	storage_name = *dir->storage_name == '\0' ? d->d_name :
		t_strconcat(dir->storage_name, "/", d->d_name, NULL);
	vname = mailbox_list_get_vname(ctx->ctx.list, storage_name);
	match = imap_match(ctx->ctx.glob, vname);

	if ((dir->info_flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN |
				MAILBOX_NOINFERIORS)) == 0 &&
	    (ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0) {
		/* we don't know yet if the parent has children. need to figure
		   out if this file is actually a visible mailbox */
	} else if (match != IMAP_MATCH_YES &&
		   (match & IMAP_MATCH_CHILDREN) == 0) {
		/* mailbox doesn't match any patterns, we don't care about it */
		return 0;
	}
	ret = ctx->ctx.list->v.
		get_mailbox_flags(ctx->ctx.list, dir_path, d->d_name,
				  mailbox_list_get_file_type(d), &info_flags);
	if (ret <= 0)
		return ret;
	if (!MAILBOX_INFO_FLAGS_FINISHED(info_flags)) {
		/* mailbox existence isn't known yet. need to figure it out
		   the hard way. */
		if (fs_get_existence_info_flag(ctx, vname, &info_flags) < 0)
			return -1;
	}
	if ((info_flags & MAILBOX_NONEXISTENT) != 0)
		return 0;

	/* mailbox exists - make sure parent knows it has children */
	dir->info_flags &= ~(MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS);
	dir->info_flags |= MAILBOX_CHILDREN;

	if (match != IMAP_MATCH_YES && (match & IMAP_MATCH_CHILDREN) == 0) {
		/* this mailbox didn't actually match any pattern,
		   we just needed to know the children state */
		return 0;
	}

	/* entry matched a pattern. we're going to return this. */
	entry = array_append_space(&dir->entries);
	entry->fname = p_strdup(dir->pool, d->d_name);
	entry->info_flags = info_flags;
	return 0;
}

static bool
fs_list_get_storage_path(struct fs_list_iterate_context *ctx,
			 const char *storage_name, const char **path_r)
{
	const char *root, *path = storage_name;

	if (*path == '~') {
		if (!mailbox_list_try_get_absolute_path(ctx->ctx.list, &path)) {
			/* couldn't expand ~user/ */
			return FALSE;
		}
		/* NOTE: the path may have been translated to a storage_name
		   instead of path */
	}
	if (*path != '/') {
		/* non-absolute path. add the mailbox root dir as prefix. */
		root = mailbox_list_get_path(ctx->ctx.list, NULL,
					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
		path = t_strconcat(root, "/", path, NULL);
	}
	*path_r = path;
	return TRUE;
}

static int
fs_list_dir_read(struct fs_list_iterate_context *ctx,
		 struct list_dir_context *dir)<commit_msg>fs layout: Don't list subscriptions file as a mailbox.<commit_after>/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "imap-match.h"
#include "mail-storage.h"
#include "mailbox-tree.h"
#include "mailbox-list-subscriptions.h"
#include "mailbox-list-fs.h"

#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>

struct list_dir_entry {
	const char *fname;
	enum mailbox_info_flags info_flags;
};

struct list_dir_context {
	struct list_dir_context *parent;

	pool_t pool;
	const char *storage_name;
	/* this directory's info flags. */
	enum mailbox_info_flags info_flags;

	/* all files in this directory */
	ARRAY_DEFINE(entries, struct list_dir_entry);
	unsigned int entry_idx;
};

struct fs_list_iterate_context {
	struct mailbox_list_iterate_context ctx;

	const char *const *valid_patterns;
	/* roots can be either /foo, ~user/bar or baz */
	ARRAY_DEFINE(roots, const char *);
	unsigned int root_idx;
	char sep;

	pool_t info_pool;
	struct mailbox_info info;
	/* current directory we're handling */
	struct list_dir_context *dir;

	unsigned int inbox_found:1;
};

static int
fs_get_existence_info_flag(struct fs_list_iterate_context *ctx,
			   const char *vname,
			   enum mailbox_info_flags *info_flags)
{
	struct mailbox *box;
	enum mailbox_existence existence;
	bool auto_boxes;
	int ret;

	auto_boxes = (ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0;
	box = mailbox_alloc(ctx->ctx.list, vname, 0);
	ret = mailbox_exists(box, auto_boxes, &existence);
	mailbox_free(&box);

	if (ret < 0) {
		/* this can only be an internal error */
		mailbox_list_set_internal_error(ctx->ctx.list);
		return -1;
	}
	switch (existence) {
	case MAILBOX_EXISTENCE_NONE:
		*info_flags |= MAILBOX_NONEXISTENT;
		break;
	case MAILBOX_EXISTENCE_NOSELECT:
		*info_flags |= MAILBOX_NOSELECT;
		break;
	case MAILBOX_EXISTENCE_SELECT:
		*info_flags |= MAILBOX_SELECT;
		break;
	}
	return 0;
}

static int
dir_entry_get(struct fs_list_iterate_context *ctx, const char *dir_path,
	      struct list_dir_context *dir, const struct dirent *d)
{
	const char *storage_name, *vname, *root_dir;
	struct list_dir_entry *entry;
	enum imap_match_result match;
	enum mailbox_info_flags info_flags;
	int ret;

	/* skip . and .. */
	if (d->d_name[0] == '.' &&
	    (d->d_name[1] == '\0' ||
	     (d->d_name[1] == '.' && d->d_name[2] == '\0')))
		return 0;

	if (strcmp(d->d_name, ctx->ctx.list->set.maildir_name) == 0) {
		/* mail storage's internal directory (e.g. dbox-Mails).
		   this also means that the parent is selectable */
		dir->info_flags &= ~MAILBOX_NOSELECT;
		dir->info_flags |= MAILBOX_SELECT;
		return 0;
	}
	if (strcmp(d->d_name, ctx->ctx.list->set.subscription_fname) == 0) {
		/* if this is the subscriptions file, skip it */
		root_dir = mailbox_list_get_path(ctx->ctx.list, NULL,
						 MAILBOX_LIST_PATH_TYPE_DIR);
		if (strcmp(root_dir, dir_path) == 0)
			return 0;
	}

	/* check the pattern */
	storage_name = *dir->storage_name == '\0' ? d->d_name :
		t_strconcat(dir->storage_name, "/", d->d_name, NULL);
	vname = mailbox_list_get_vname(ctx->ctx.list, storage_name);
	match = imap_match(ctx->ctx.glob, vname);

	if ((dir->info_flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN |
				MAILBOX_NOINFERIORS)) == 0 &&
	    (ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0) {
		/* we don't know yet if the parent has children. need to figure
		   out if this file is actually a visible mailbox */
	} else if (match != IMAP_MATCH_YES &&
		   (match & IMAP_MATCH_CHILDREN) == 0) {
		/* mailbox doesn't match any patterns, we don't care about it */
		return 0;
	}
	ret = ctx->ctx.list->v.
		get_mailbox_flags(ctx->ctx.list, dir_path, d->d_name,
				  mailbox_list_get_file_type(d), &info_flags);
	if (ret <= 0)
		return ret;
	if (!MAILBOX_INFO_FLAGS_FINISHED(info_flags)) {
		/* mailbox existence isn't known yet. need to figure it out
		   the hard way. */
		if (fs_get_existence_info_flag(ctx, vname, &info_flags) < 0)
			return -1;
	}
	if ((info_flags & MAILBOX_NONEXISTENT) != 0)
		return 0;

	/* mailbox exists - make sure parent knows it has children */
	dir->info_flags &= ~(MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS);
	dir->info_flags |= MAILBOX_CHILDREN;

	if (match != IMAP_MATCH_YES && (match & IMAP_MATCH_CHILDREN) == 0) {
		/* this mailbox didn't actually match any pattern,
		   we just needed to know the children state */
		return 0;
	}

	/* entry matched a pattern. we're going to return this. */
	entry = array_append_space(&dir->entries);
	entry->fname = p_strdup(dir->pool, d->d_name);
	entry->info_flags = info_flags;
	return 0;
}

static bool
fs_list_get_storage_path(struct fs_list_iterate_context *ctx,
			 const char *storage_name, const char **path_r)
{
	const char *root, *path = storage_name;

	if (*path == '~') {
		if (!mailbox_list_try_get_absolute_path(ctx->ctx.list, &path)) {
			/* couldn't expand ~user/ */
			return FALSE;
		}
		/* NOTE: the path may have been translated to a storage_name
		   instead of path */
	}
	if (*path != '/') {
		/* non-absolute path. add the mailbox root dir as prefix. */
		root = mailbox_list_get_path(ctx->ctx.list, NULL,
					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
		path = *path == '\0' ? root :
			t_strconcat(root, "/", path, NULL);
	}
	*path_r = path;
	return TRUE;
}

static int
fs_list_dir_read(struct fs_list_iterate_context *ctx,
		 struct list_dir_context *dir)
{'fraction_non_alphanumeric': 0.12812412896032704, 'fraction_numerical': 0.007339960977422652, 'mean_word_length': 4.072573044297832, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7579002', 'n_tokens_mistral': 4311, 'n_tokens_neox': 3998, 'n_words': 992}