# NOTE: Evolution client breaks with this option when it's trying to APPEND.
#mailbox_check_interval = 0
+# Allow full filesystem access to clients. There's no access checks other than
+# what the operating system does for the active UID/GID. It works with both
+# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/
+# or ~user/.
+#mail_full_filesystem_access = no
+
# Save mails with CR+LF instead of plain LF. This makes sending those mails
# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.
# But it also creates a bit more disk I/O which may just make it slower.
#include "lib.h"
#include "hostpid.h"
+#include "home-expand.h"
#include "unlink-directory.h"
#include "imap-match.h"
#include "subscription-file/subscription-file.h"
struct dirent *d;
struct stat st;
enum mailbox_flags flags;
+ const char *dir, *prefix, *p;
char path[PATH_MAX];
- int failed, found_inbox;
+ int failed, found_inbox, ret;
mail_storage_clear_error(storage);
- dirp = opendir(storage->dir);
+ if (!full_filesystem_access || (p = strrchr(mask, '/')) == NULL) {
+ dir = storage->dir;
+ prefix = "";
+ } else {
+ if (mask == p)
+ dir = prefix = "/";
+ else {
+ dir = t_strdup_until(mask, p);
+ prefix = t_strdup_until(mask, p+1);
+ }
+
+ if (*mask != '/' && *mask != '~')
+ dir = t_strconcat(storage->dir, "/", dir, NULL);
+ dir = home_expand(dir);
+ }
+
+ dirp = opendir(dir);
if (dirp == NULL) {
mail_storage_set_critical(storage, "opendir(%s) failed: %m",
- storage->dir);
+ dir);
return FALSE;
}
/* make sure the mask matches - dirs beginning with ".."
should be deleted and we always want to check those. */
- if (fname[1] == '.' || imap_match(glob, fname+1) <= 0)
+ t_push();
+ ret = imap_match(glob, t_strconcat(prefix, fname+1, NULL));
+ t_pop();
+ if (fname[1] == '.' || ret <= 0)
continue;
- if (str_path(path, sizeof(path), storage->dir, fname) < 0)
+ if (str_path(path, sizeof(path), dir, fname) < 0)
continue;
/* make sure it's a directory */
t_push();
flags = maildir_get_marked_flags(storage, path);
- callback(storage, fname+1, flags, context);
+ callback(storage, t_strconcat(prefix, fname+1, NULL),
+ flags, context);
t_pop();
}
#include "subscription-file/subscription-file.h"
#include "mbox-index.h"
#include "mbox-storage.h"
+#include "home-expand.h"
#include <dirent.h>
#include <sys/stat.h>
void *context;
};
-static int mbox_find_path(struct mail_storage *storage,
- struct imap_match_glob *glob,
- mailbox_list_callback_t callback, void *context,
- const char *relative_dir)
+struct list_context {
+ struct mail_storage *storage;
+ struct imap_match_glob *glob;
+ mailbox_list_callback_t *callback;
+ void *context;
+
+ const char *rootdir;
+};
+
+static int mbox_find_path(struct list_context *ctx, const char *relative_dir)
{
DIR *dirp;
struct dirent *d;
t_push();
if (relative_dir == NULL)
- dir = storage->dir;
+ dir = ctx->rootdir;
+ else if (*ctx->rootdir == '\0' && *relative_dir != '\0')
+ dir = relative_dir;
else {
if (str_path(fulldir, sizeof(fulldir),
- storage->dir, relative_dir) < 0) {
- mail_storage_set_critical(storage, "Path too long: %s",
+ ctx->rootdir, relative_dir) < 0) {
+ mail_storage_set_critical(ctx->storage,
+ "Path too long: %s",
relative_dir);
return FALSE;
}
dir = fulldir;
}
+ dir = home_expand(dir);
dirp = opendir(dir);
if (dirp == NULL) {
t_pop();
return TRUE;
}
- mail_storage_set_critical(storage,
+ if (errno == EACCES) {
+ if (relative_dir != NULL) {
+ /* subfolder, ignore */
+ return TRUE;
+ }
+ mail_storage_set_error(ctx->storage, "Access denied");
+ return FALSE;
+ }
+
+ mail_storage_set_critical(ctx->storage,
"opendir(%s) failed: %m", dir);
return FALSE;
}
else {
if (str_path(path, sizeof(path),
relative_dir, fname) < 0) {
- mail_storage_set_critical(storage,
+ mail_storage_set_critical(ctx->storage,
"Path too long: %s/%s",
relative_dir, fname);
failed = TRUE;
listpath = path;
}
- if ((match = imap_match(glob, listpath)) < 0)
+ if ((match = imap_match(ctx->glob, listpath)) < 0)
continue;
/* see if it's a directory */
if (str_path(fullpath, sizeof(fullpath), dir, fname) < 0) {
- mail_storage_set_critical(storage,
+ mail_storage_set_critical(ctx->storage,
"Path too long: %s/%s",
dir, fname);
failed = TRUE;
if (errno == ENOENT)
continue; /* just deleted, ignore */
- mail_storage_set_critical(storage, "stat(%s) failed: "
- "%m", fullpath);
+ mail_storage_set_critical(ctx->storage,
+ "stat(%s) failed: %m",
+ fullpath);
failed = TRUE;
break;
}
if (S_ISDIR(st.st_mode)) {
/* subdirectory, scan it too */
t_push();
- callback(storage, listpath, MAILBOX_NOSELECT, context);
+ ctx->callback(ctx->storage, listpath, MAILBOX_NOSELECT,
+ ctx->context);
t_pop();
- if (!mbox_find_path(storage, glob, callback,
- context, listpath)) {
+ if (!mbox_find_path(ctx, listpath)) {
failed = TRUE;
break;
}
} else if (match > 0 &&
- strcmp(fullpath, storage->inbox_file) != 0 &&
+ strcmp(fullpath, ctx->storage->inbox_file) != 0 &&
strcasecmp(listpath, "INBOX") != 0) {
/* don't match any INBOX here, it's added later.
we might also have ~/mail/inbox, ~/mail/Inbox etc.
Just ignore them for now. */
t_push();
- callback(storage, listpath, MAILBOX_NOINFERIORS, context);
+ ctx->callback(ctx->storage, listpath,
+ MAILBOX_NOINFERIORS, ctx->context);
t_pop();
}
}
last_dir = p;
}
- return last_dir != NULL ? t_strdup_until(mask, last_dir) : NULL;
+ return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
}
int mbox_find_mailboxes(struct mail_storage *storage, const char *mask,
mailbox_list_callback_t callback, void *context)
{
+ struct list_context ctx;
struct imap_match_glob *glob;
const char *relative_dir;
callback(storage, "INBOX", MAILBOX_NOINFERIORS, context);
}
- if (!mbox_find_path(storage, glob, callback, context, relative_dir))
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.storage = storage;
+ ctx.glob = glob;
+ ctx.callback = callback;
+ ctx.context = context;
+
+ if (!full_filesystem_access || relative_dir == NULL ||
+ (*relative_dir != '/' && *relative_dir != '~' &&
+ *relative_dir != '\0'))
+ ctx.rootdir = storage->dir;
+ else
+ ctx.rootdir = "";
+
+ if (relative_dir != NULL) {
+ const char *matchdir = t_strconcat(relative_dir, "/", NULL);
+
+ if (imap_match(ctx.glob, matchdir) > 0) {
+ t_push();
+ ctx.callback(ctx.storage, matchdir, MAILBOX_NOSELECT,
+ ctx.context);
+ t_pop();
+ }
+ }
+
+ if (!mbox_find_path(&ctx, relative_dir))
return FALSE;
return TRUE;
const char *p;
int newdir;
+ if (full_filesystem_access)
+ return TRUE;
+
+ /* make sure it's not absolute path */
+ if (*mask == '/' || *mask == '\\' || *mask == '~')
+ return FALSE;
+
/* make sure there's no "../" or "..\" stuff */
newdir = TRUE;
for (p = mask; *p != '\0'; p++) {
static struct mail_storage_list *storages = NULL;
enum client_workarounds client_workarounds = 0;
+int full_filesystem_access = FALSE;
void mail_storage_init(void)
{
const char *env;
const char *const *str;
+ full_filesystem_access = getenv("FULL_FILESYSTEM_ACCESS") != NULL;
+
env = getenv("CLIENT_WORKAROUNDS");
if (env == NULL)
return;
};
extern enum client_workarounds client_workarounds;
+extern int full_filesystem_access;
/* Initialize mail storage. */
void mail_storage_init(void);
file-set-size.c \
hash.c \
hex-binary.c \
+ home-expand.c \
hostpid.c \
imem.c \
iostream.c \
file-set-size.h \
hash.h \
hex-binary.h \
+ home-expand.h \
hostpid.h \
imem.h \
iostream-internal.h \
--- /dev/null
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "lib.h"
+#include "home-expand.h"
+
+#include <stdlib.h>
+#include <pwd.h>
+
+/* expand ~/ or ~user/ in beginning of path */
+const char *home_expand(const char *path)
+{
+ const char *home, *p, *orig_path;
+ struct passwd *pw;
+
+ if (*path != '~')
+ return path;
+
+ orig_path = path++;
+ if (*path == '/' || *path == '\0') {
+ home = getenv("HOME");
+ if (*path != '\0') path++;
+ } else {
+ p = strchr(path, '/');
+ if (p == NULL) {
+ pw = getpwnam(path);
+ path = "";
+ } else {
+ pw = getpwnam(t_strdup_until(path, p));
+ path = p+1;
+ }
+
+ home = pw == NULL ? NULL : pw->pw_dir;
+ }
+
+ if (home == NULL)
+ return orig_path;
+ else if (*path == '\0')
+ return t_strdup(home);
+ else
+ return t_strconcat(home, "/", path, NULL);
+}
--- /dev/null
+#ifndef __HOME_EXPAND_H
+#define __HOME_EXPAND_H
+
+/* expand ~/ or ~user/ in beginning of path */
+const char *home_expand(const char *path);
+
+#endif
DEF(SET_STR, mail_cache_fields),
DEF(SET_STR, mail_never_cache_fields),
DEF(SET_STR, client_workarounds),
- DEF(SET_STR, mailbox_check_interval),
- DEF(SET_STR, mail_save_crlf),
- DEF(SET_STR, mail_read_mmaped),
- DEF(SET_STR, maildir_copy_with_hardlinks),
- DEF(SET_STR, maildir_check_content_changes),
+ DEF(SET_INT, mailbox_check_interval),
+ DEF(SET_BOOL, mail_full_filesystem_access),
+ DEF(SET_BOOL, mail_save_crlf),
+ DEF(SET_BOOL, mail_read_mmaped),
+ DEF(SET_BOOL, maildir_copy_with_hardlinks),
+ DEF(SET_BOOL, maildir_check_content_changes),
DEF(SET_STR, mbox_locks),
- DEF(SET_STR, mbox_read_dotlock),
- DEF(SET_STR, mbox_lock_timeout),
- DEF(SET_STR, mbox_dotlock_change_timeout),
- DEF(SET_STR, overwrite_incompatible_index),
- DEF(SET_STR, umask),
+ DEF(SET_BOOL, mbox_read_dotlock),
+ DEF(SET_INT, mbox_lock_timeout),
+ DEF(SET_INT, mbox_dotlock_change_timeout),
+ DEF(SET_BOOL, overwrite_incompatible_index),
+ DEF(SET_INT, umask),
/* imap */
DEF(SET_STR, imap_executable),
MEMBER(mail_never_cache_fields) NULL,
MEMBER(client_workarounds) NULL,
MEMBER(mailbox_check_interval) 0,
+ MEMBER(mail_full_filesystem_access) FALSE,
MEMBER(mail_save_crlf) FALSE,
MEMBER(mail_read_mmaped) FALSE,
MEMBER(maildir_copy_with_hardlinks) FALSE,
const char *mail_never_cache_fields;
const char *client_workarounds;
unsigned int mailbox_check_interval;
+ int mail_full_filesystem_access;
int mail_save_crlf;
int mail_read_mmaped;
int maildir_copy_with_hardlinks;