mailbox-list.c revision 0910ea0672c0295c442eb686cc41e98656831f37
/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "mkdir-parents.h"
#include "str.h"
#include "sha1.h"
#include "home-expand.h"
#include "close-keep-errno.h"
#include "eacces-error.h"
#include "read-full.h"
#include "write-full.h"
#include "safe-mkstemp.h"
#include "unlink-directory.h"
#include "imap-match.h"
#include "imap-utf7.h"
#include "mailbox-log.h"
#include "mailbox-tree.h"
#include "mail-storage-private.h"
#include "mailbox-list-private.h"
#include <time.h>
#include <unistd.h>
#include <dirent.h>
/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
then start renaming them to larger names from end to beginning, which
eventually would start causing the failures when trying to use too
long mailbox names. */
#define MAILBOX_MAX_HIERARCHY_LEVELS 20
#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
struct ns_list_iterate_context {
struct mailbox_list_iterate_context ctx;
struct mailbox_list_iterate_context *backend_ctx;
struct mail_namespace *namespaces;
const char **patterns, **patterns_ns_match;
enum namespace_type type_mask;
};
struct mailbox_list_module_register mailbox_list_module_register = { 0 };
void mailbox_lists_init(void)
{
}
void mailbox_lists_deinit(void)
{
}
{
const struct mailbox_list *const *drivers;
unsigned int i, count;
for (i = 0; i < count; i++) {
*idx_r = i;
return TRUE;
}
}
return FALSE;
}
{
unsigned int idx;
i_fatal("mailbox_list_register(%s): duplicate driver",
}
}
{
unsigned int idx;
i_fatal("mailbox_list_unregister(%s): unknown driver",
}
}
const struct mailbox_list *
mailbox_list_find_class(const char *driver)
{
const struct mailbox_list *const *class_p;
unsigned int idx;
return NULL;
return *class_p;
}
const struct mailbox_list_settings *set,
{
const struct mailbox_list *const *class_p;
struct mailbox_list *list;
unsigned int idx;
*error_r = "Unknown driver name";
return -1;
}
*error_r = "maildir_name not supported by this driver";
return -1;
}
*error_r = "alt_dir not supported by this driver";
return -1;
}
/* copy settings */
}
} else {
}
i_debug("%s: root=%s, index=%s, control=%s, inbox=%s",
}
return 0;
}
{
if (home_try_expand(&path) < 0) {
*error_r = t_strconcat(
"No home directory for system user. "
" for ", NULL);
return -1;
}
} else {
*error_r = "Home directory not set for user. "
"Can't expand ~/ for ";
return -1;
}
}
return 0;
}
static const char *split_next_arg(const char *const **_args)
{
args++;
args++;
/* string ends with ":", just ignore it. */
break;
}
args++;
}
return str;
}
int mailbox_list_settings_parse(const char *data,
struct mailbox_list_settings *set,
{
if (*data == '\0')
return 0;
/* <root dir> */
return -1;
}
value = "";
} else {
value++;
}
else {
return -1;
}
return -1;
}
}
return 0;
}
{
}
{
}
{
return list->hierarchy_sep;
}
{
}
struct mail_namespace *
{
}
{
/* add the execute bit if either read or write bit is set */
return mode;
}
struct mail_user *
{
}
struct mail_storage **storage_r)
{
else {
return 0;
}
}
struct mail_storage **storage)
{
}
static void
{
const char *path;
/* use safe defaults */
*file_mode_r = 0600;
*dir_mode_r = 0700;
*gid_origin_r = "defaults";
/* no filesystem support in storage */
path);
i_debug("Namespace %s: Permission lookup failed from %s",
}
/* return defaults */
return;
}
} else {
*gid_origin_r = path;
/* we're getting permissions from a file.
apply +x modes as necessary. */
}
/* directory's GID is used automatically for new
files */
/* group has same permissions as world, so don't bother
changing it */
/* using our own gid, no need to change it */
} else {
}
}
}
i_debug("Namespace %s: Using permissions from %s: "
(int)list->dir_create_mode,
(long)list->file_create_gid);
}
}
const char *name,
const char **gid_origin_r)
{
return;
}
}
const char *name,
const char **gid_origin_r)
{
return;
}
}
const char *pattern)
{
bool ret;
T_BEGIN {
} T_END;
return ret;
}
const char *name)
{
bool ret;
/* an ugly way to get to mailbox root (e.g. Maildir/ when
it's not the INBOX) */
return TRUE;
}
T_BEGIN {
} T_END;
return ret;
}
const char *name)
{
const char *p;
int ret;
/* safer to just disallow all control characters */
for (p = name; *p != '\0'; p++) {
if (*p < ' ')
return FALSE;
}
T_BEGIN {
} T_END;
}
enum mailbox_list_path_type type)
{
}
{
}
{
}
{
/* the default implementation: */
if (*ref != '\0') {
/* merge reference and pattern */
}
return pattern;
}
const char *name,
enum mailbox_name_status *status)
{
return 0;
}
}
struct mailbox_list_iterate_context *
enum mailbox_list_iter_flags flags)
{
const char *patterns[2];
}
struct mailbox_list_iterate_context *
const char *const *patterns,
enum mailbox_list_iter_flags flags)
{
}
static bool
{
return FALSE;
return FALSE;
}
return TRUE;
}
static bool
const char *pattern)
{
struct imap_match_glob *glob;
enum imap_match_result result;
const char *prefix_without_sep;
unsigned int len;
len--;
NAMESPACE_FLAG_LIST_CHILDREN)) == 0) {
/* non-listable namespace matches only with exact prefix */
return FALSE;
}
if (*prefix_without_sep == '\0')
else {
}
switch (result) {
case IMAP_MATCH_YES:
case IMAP_MATCH_CHILDREN:
return TRUE;
case IMAP_MATCH_NO:
case IMAP_MATCH_PARENT:
break;
}
return FALSE;
}
switch (result) {
case IMAP_MATCH_YES:
/* allow matching prefix only when it's done without
wildcards */
return TRUE;
break;
case IMAP_MATCH_CHILDREN: {
/* allow this only if there isn't another namespace
with longer prefix that matches this pattern
(namespaces are sorted by prefix length) */
struct mail_namespace *tmp;
T_BEGIN {
break;
}
} T_END;
return TRUE;
break;
}
case IMAP_MATCH_NO:
case IMAP_MATCH_PARENT:
break;
}
return FALSE;
}
static bool
{
unsigned int i;
return FALSE;
/* filter out namespaces whose prefix doesn't match.
this same code handles both with and without
STAR_WITHIN_NS, so the "without" case is slower than
necessary, but this shouldn't matter much */
T_BEGIN {
ctx->patterns_ns_match[i]))
break;
}
} T_END;
return FALSE;
}
return ns;
}
static struct mail_namespace *
{
break;
}
return ns;
}
static const struct mailbox_info *
{
struct ns_list_iterate_context *ctx =
(struct ns_list_iterate_context *)_ctx;
const struct mailbox_info *info;
/* go to the next namespace */
ctx->backend_ctx =
return mailbox_list_ns_iter_next(_ctx);
}
return info;
}
static int
{
struct ns_list_iterate_context *ctx =
(struct ns_list_iterate_context *)_ctx;
int ret;
}
return ret;
}
static const char **
unsigned int count)
{
const char **dup;
unsigned int i;
for (i = 0; i < count; i++) {
dup[i] = p;
for (; *p != '\0'; p++) {
if (*p == '*')
*p = '%';
}
}
return dup;
}
struct mailbox_list_iterate_context *
const char *const *patterns,
enum namespace_type type_mask,
enum mailbox_list_iter_flags flags)
{
struct ns_list_iterate_context *ctx;
unsigned int i, count;
(flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0);
for (i = 0; i < count; i++)
if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
/* create copies of patterns with '*' wildcard changed to '%' */
} else {
}
if (namespaces != NULL) {
ctx->backend_ctx =
}
}
const struct mailbox_info *
{
const struct mailbox_info *info;
return info;
}
{
}
enum mailbox_info_flags *flags_r)
{
unsigned int len;
/* shouldn't happen with anything except shared mailboxes */
return 0;
}
path = "/";
} else {
fname++;
}
} else {
/* a) looking up INBOX that's elsewhere
b) looking up the root dir itself (as INBOX or "") */
fname = "";
}
/* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
access it also via namespace prefix. */
return 0;
}
}
}
{
const char *path;
const char *gid_origin;
return TRUE;
/* don't do this in mailbox_list_create(), because _get_path() might be
overridden by storage (mbox). */
return FALSE;
return TRUE;
}
enum mailbox_log_record_type type,
{
struct mailbox_log_record rec;
if (!mailbox_list_init_changelog(list) ||
return;
}
{
return -1;
/* subscriptions are about names, not about mailboxes. it's possible
to have a subscription to non-existing mailbox. renames also don't
change subscriptions. so instead of using actual GUIDs, we'll use
hash of the name. */
return 0;
}
{
"Invalid mailbox name");
return -1;
}
}
{
unsigned char sha[SHA1_RESULTLEN];
}
{
}
{
}
{
/* If we happened to create any of the parents, we need to mark them
nonexistent. */
}
}
static void
const char *name)
{
struct mailbox_node *node;
enum imap_match_result match;
const char *p;
bool created, add_matched;
if (ctx->update_only ||
add_matched = TRUE;
for (;;) {
if (match == IMAP_MATCH_YES) {
if (created) {
if (create_flags != 0)
}
}
/* We don't want to show the parent mailboxes unless
something else matches them, but if they are matched
we want to show them having child subscriptions */
add_matched = FALSE;
} else {
if ((match & IMAP_MATCH_PARENT) == 0)
break;
/* We've a (possibly) non-subscribed parent mailbox
which has a subscribed child mailbox. Make sure we
return the parent mailbox. */
}
if (!ctx->match_parents)
break;
/* see if parent matches */
if (p == NULL)
break;
}
}
const char *name)
{
T_BEGIN {
} T_END;
}
{
return TRUE;
levels++;
level_len = 0;
} else {
level_len++;
}
}
return TRUE;
return TRUE;
return FALSE;
}
{
enum mailbox_list_file_type type;
#ifdef HAVE_DIRENT_D_TYPE
switch (d->d_type) {
case DT_UNKNOWN:
break;
case DT_REG:
break;
case DT_DIR:
break;
case DT_LNK:
break;
default:
break;
}
#else
#endif
return type;
}
static bool
{
/* ~/dir - use the configured home directory */
return FALSE;
} else {
if (home_try_expand(name) < 0)
return FALSE;
}
return TRUE;
}
const char **name)
{
unsigned int len;
return FALSE;
if (**name == '~') {
/* try to expand home directory */
/* fallback to using actual "~name" mailbox */
return FALSE;
}
} else {
if (**name != '/')
return FALSE;
}
/* okay, we have an absolute path now. but check first if it points to
same directory as one of our regular mailboxes. */
/* yeah, we can replace the full path with mailbox
name. this way we can use indexes. */
*name = mailbox_name;
return FALSE;
}
}
return TRUE;
}
{
if (p == NULL)
return 0;
dir);
return -1;
}
return 0;
}
enum mail_error *error_r)
{
"Unknown internal list error";
}
{
}
{
}
{
char str[256];
list->error_string =
MAIL_ERRSTR_CRITICAL_MSG_STAMP, tm) > 0 ?
}
{
/* critical errors may contain sensitive data, so let user
see only "Internal error" with a timestamp to make it
easier to look from log files the actual error message. */
}
{
const char *error_string;
enum mail_error error;
return FALSE;
return TRUE;
}