mailbox-list.c revision 318ef3683d67683173f1b552cf5f9af4375b3017
/* Copyright (c) 2006-2011 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 "hash.h"
#include "home-expand.h"
#include "time-util.h"
#include "unichar.h"
#include "settings-parser.h"
#include "imap-utf7.h"
#include "mailbox-log.h"
#include "mailbox-tree.h"
#include "mail-storage.h"
#include "mail-storage-hooks.h"
#include "mailbox-list-private.h"
#include <time.h>
#include <ctype.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 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,
enum mailbox_list_flags flags,
{
const struct mailbox_list *const *class_p;
struct mailbox_list *list;
unsigned int idx;
(flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
*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, alt=%s",
}
if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
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;
}
struct mailbox_list_settings *set_r,
const char **error_r)
{
if (*data == '\0')
return 0;
/* <root dir> */
return -1;
}
/* probably mbox user trying to avoid root_dir */
return -1;
}
continue;
}
value = "";
} else {
value++;
}
else {
return -1;
}
return -1;
}
}
return 0;
}
enum mailbox_list_path_type type)
{
const struct mail_storage_settings *mail_set;
struct mailbox_list_settings set;
const char *p, *error;
if (*location == SETTING_STRVAR_EXPANDED[0]) {
/* set using -o or userdb lookup. */
return "";
}
location++;
if (*location == '\0') {
if (*location == SETTING_STRVAR_EXPANDED[0])
return "";
location++;
}
/* type:settings */
if (p == NULL)
return "";
return "";
}
static const char *
{
/* no escaping of namespace prefix */
}
/* escape the mailbox name */
if (*vname == '~') {
vname++;
}
*vname == '/') {
} else {
}
}
return str_c(escaped_name);
}
const char *vname)
{
const char *storage_name = vname;
storage_name = "INBOX";
/* skip namespace prefix, except if this is INBOX */
/* trying to access the namespace prefix itself */
storage_name = "";
} else {
/* we're converting a nonexistent mailbox name,
such as a LIST pattern. */
}
}
/* UTF-8 -> mUTF-7 conversion */
}
return storage_name;
/* shared namespace root. the backend storage's hierarchy
separator isn't known yet, so do nothing. */
return storage_name;
}
for (p = ret; *p != '\0'; p++) {
if (*p == ns_sep)
*p = list_sep;
}
return ret;
}
const char *vname)
{
}
static const char *
{
unsigned int num;
else
num *= 16;
else
src += 2;
else
}
}
const char *storage_name)
{
unsigned int i, prefix_len, name_len;
const char *vname = storage_name;
/* user's INBOX - use as-is. NOTE: don't do case-insensitive
comparison, otherwise we can't differentiate between INBOX
and <ns prefix>/inBox. */
return vname;
}
if (*vname == '\0') {
/* return namespace prefix without the separator */
else {
}
/* mUTF-7 -> UTF-8 conversion */
}
/* @UNSAFE */
for (i = 0; i < name_len; i++) {
ret[i + prefix_len] =
}
}
return vname;
}
{
}
{
}
}
{
}
{
}
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)
{
}
{
}
struct mailbox_permissions *permissions_r)
{
const char *path, *parent_name, *p;
/* use safe defaults */
/* no filesystem support in storage */
path);
i_debug("Namespace %s: %s doesn't exist yet, "
"using default permissions",
}
/* return parent mailbox */
if (p == NULL) {
/* return root defaults */
parent_name = NULL;
} else {
}
return;
}
/* assume current defaults for mailboxes that don't exist or
can't be looked up for some other reason */
} else {
/* 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);
}
}
{
struct mailbox_permissions perm;
} else {
}
}
static int
{
const char *p;
return -1;
}
if (p == NULL)
path = "/";
else
}
*root_dir_r = path;
return 0;
}
static const char *
const char *expanded_full)
{
const char *ret;
unsigned int i, slash_count = 0, slash2_count = 0;
/* get the expanded path up to the same amount of '/' characters.
if there isn't the same amount of '/' characters, it means %variable
expansion added more of them and we can't handle this. */
for (i = 0; unexpanded_start+i != unexpanded_stop; i++) {
if (unexpanded_start[i] == '/')
slash_count++;
}
for (; unexpanded_start[i] != '\0'; i++) {
if (unexpanded_start[i] == '/')
slash2_count++;
}
for (i = 0; expanded_full[i] != '\0'; i++) {
if (expanded_full[i] == '/') {
if (slash_count == 0)
break;
slash_count--;
}
}
if (slash_count != 0)
return "";
for (; expanded_full[i] != '\0'; i++) {
if (expanded_full[i] == '/') {
if (slash2_count == 0)
return "";
slash2_count--;
}
}
if (slash2_count != 0)
return "";
return ret;
}
enum mailbox_list_path_type type,
const char **error_r)
{
/* looks like it already exists, don't bother checking
further. */
return 0;
}
return -1;
}
/* get the directory path up to last %variable. for example
if (p == NULL)
expanded = "";
else {
while (p != unexpanded && *p != '/') p--;
if (p == unexpanded)
expanded = "";
else {
}
}
if (*expanded != '\0') {
/* up to this directory get the permissions from the first
parent directory that exists, if it has setgid bit
enabled. */
error_r) < 0)
return -1;
"mkdir(%s) failed: %m", expanded);
return -1;
}
}
/* change the group for user directories */
}
}
/* the rest of the directories exist only for one user. create them
with default directory permissions */
else
return -1;
}
return 0;
}
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 ((unsigned char)*p < ' ')
return FALSE;
}
else T_BEGIN {
} T_END;
}
enum mailbox_list_path_type type)
{
}
const char *
enum mailbox_list_path_type type)
{
const char *path;
switch (type) {
}
i_unreached();
}
{
}
{
}
{
/* the default implementation: */
if (*ref != '\0') {
/* merge reference and pattern */
}
return pattern;
}
{
struct mailbox_list_iterate_context *iter;
const char *pattern;
int ret;
if (mailbox_list_iter_deinit(&iter) < 0)
ret = -1;
return ret;
}
enum mailbox_info_flags *flags_r)
{
unsigned int len;
*flags_r = 0;
/* special handling for INBOX, mainly because with Maildir++
layout it needs to check if the cur/ directory exists,
which the Maildir++ layout backend itself can't do.. */
enum mailbox_existence existence;
int ret;
/* kludge: with imapc backend we can get here with
list=Maildir++ (for indexes), but list->ns->list=imapc */
mailbox_free(&box);
if (ret < 0) {
/* this can only be an internal error */
return -1;
}
switch (existence) {
case MAILBOX_EXISTENCE_NONE:
return 0;
case MAILBOX_EXISTENCE_SELECT:
break;
}
return 1;
}
/* can't do this optimized. do it the slow way. */
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
const char *vname;
else
return mailbox_list_iter_deinit(&iter);
}
dir = "/";
} else {
fname++;
}
/* looking up a regular mailbox under mail root dir */
/* looking up INBOX that's elsewhere */
} else {
/* looking up the root dir itself */
fname = "";
}
/* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
access it also via namespace prefix. */
return 0;
}
}
flags_r);
}
{
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). */
if (*path == '\0')
return FALSE;
&gid, &gid_origin);
return TRUE;
}
enum mailbox_log_record_type type,
const guid_128_t mailbox_guid)
{
struct mailbox_log_record rec;
if (!mailbox_list_init_changelog(list) ||
return;
if (!list->index_root_dir_created) {
return;
}
}
{
int ret;
/* make sure we'll refresh the file on next list */
return ret;
/* subscriptions are about names, not about mailboxes. it's possible
to have a subscription to nonexistent 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;
}
}
{
"Invalid mailbox name");
return -1;
}
}
{
"Invalid mailbox name");
return -1;
}
}
{
unsigned char sha[SHA1_RESULTLEN];
}
{
}
{
}
{
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;
}
{
struct mailbox_permissions perm;
perm.file_create_gid_origin) < 0 &&
path);
return -1;
}
return 0;
}
{
const char *p;
if (p == NULL)
return 0;
}
const char *name)
{
struct mailbox_permissions perm;
unsigned int n = 0;
if (*index_dir == '\0')
return 0;
return 0;
/* the directory might not have been created yet */
}
&error) < 0) {
"Couldn't create index root dir %s: %s",
return -1;
}
return 0;
}
perm.file_create_gid_origin) < 0) {
break;
"mkdir(%s) failed: %m", index_dir);
return -1;
}
/* create the parent directory first */
&error) < 0) {
"Couldn't create index dir %s: %s",
parent_dir, error);
return -1;
}
}
return 0;
}
enum mail_error *error_r)
{
"Unknown internal list error";
}
{
}
{
}
{
const char *str;
}
{
/* 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;
}