struct acl_object *(*object_init)(struct acl_backend *backend,
struct mail_storage *storage,
const char *name);
+ struct acl_object *(*object_init_parent)(struct acl_backend *backend,
+ struct mail_storage *storage,
+ const char *child_name);
void (*object_deinit)(struct acl_object *aclobj);
int (*object_refresh_cache)(struct acl_object *aclobj);
return backend->v.object_init(backend, storage, name);
}
+struct acl_object *acl_object_init_from_parent(struct acl_backend *backend,
+ struct mail_storage *storage,
+ const char *child_name)
+{
+ return backend->v.object_init_parent(backend, storage, child_name);
+}
+
void acl_object_deinit(struct acl_object **_aclobj)
{
struct acl_object *aclobj = *_aclobj;
struct acl_object *acl_object_init_from_name(struct acl_backend *backend,
struct mail_storage *storage,
const char *name);
+struct acl_object *acl_object_init_from_parent(struct acl_backend *backend,
+ struct mail_storage *storage,
+ const char *child_name);
void acl_object_deinit(struct acl_object **aclobj);
/* Returns 1 if we have the requested rights, 0 if not, or -1 if internal
struct acl_backend_vfile_validity {
struct acl_vfile_validity global_validity, local_validity;
+ struct acl_vfile_validity mailbox_validity;
};
struct acl_letter_map {
pool_unref(&backend->backend.pool);
}
+static const char *
+acl_backend_vfile_get_local_dir(struct mail_storage *storage, const char *name)
+{
+ const char *dir;
+ bool is_file;
+
+ dir = mail_storage_get_mailbox_path(storage, name, &is_file);
+ if (is_file) {
+ dir = mailbox_list_get_path(storage->list, name,
+ MAILBOX_LIST_PATH_TYPE_CONTROL);
+ }
+ return dir;
+}
+
static struct acl_object *
acl_backend_vfile_object_init(struct acl_backend *_backend,
struct mail_storage *storage, const char *name)
(struct acl_backend_vfile *)_backend;
struct acl_object_vfile *aclobj;
const char *dir;
- bool is_file;
aclobj = i_new(struct acl_object_vfile, 1);
aclobj->aclobj.backend = _backend;
dir = mailbox_list_get_path(_backend->list, NULL,
MAILBOX_LIST_PATH_TYPE_DIR);
} else {
- dir = mail_storage_get_mailbox_path(storage, name, &is_file);
- if (is_file) {
- dir = mailbox_list_get_path(_backend->list, name,
- MAILBOX_LIST_PATH_TYPE_CONTROL);
- }
+ dir = acl_backend_vfile_get_local_dir(storage, name);
}
aclobj->local_path = i_strconcat(dir, "/"ACL_FILENAME, NULL);
return &aclobj->aclobj;
}
+static const char *
+get_parent_mailbox(struct mail_storage *storage, const char *name)
+{
+ const char *p;
+ char sep;
+
+ sep = mailbox_list_get_hierarchy_sep(storage->list);
+ p = strrchr(name, sep);
+ return p == NULL ? NULL : t_strdup_until(name, p);
+}
+
+static int
+acl_backend_vfile_exists(struct acl_backend_vfile *backend, const char *path,
+ struct acl_vfile_validity *validity)
+{
+ struct stat st;
+
+ if (validity->last_check + (time_t)backend->cache_secs > ioloop_time) {
+ /* use the cached value */
+ return validity->last_mtime != VALIDITY_MTIME_NOTFOUND;
+ }
+
+ validity->last_check = ioloop_time;
+ if (stat(path, &st) < 0) {
+ if (errno == ENOENT) {
+ validity->last_mtime = VALIDITY_MTIME_NOTFOUND;
+ return 0;
+ }
+ if (errno == EACCES) {
+ validity->last_mtime = VALIDITY_MTIME_NOACCESS;
+ return 1;
+ }
+ i_error("stat(%s) failed: %m", path);
+ return -1;
+ }
+ validity->last_mtime = st.st_mtime;
+ validity->last_size = st.st_size;
+ return 1;
+}
+
+static bool
+acl_backend_vfile_has_acl(struct acl_backend *_backend,
+ struct mail_storage *storage, const char *name)
+{
+ struct acl_backend_vfile *backend =
+ (struct acl_backend_vfile *)_backend;
+ struct acl_backend_vfile_validity *old_validity, new_validity;
+ const char *path, *local_path, *global_path, *dir;
+ int ret;
+
+ old_validity = acl_cache_get_validity(_backend->cache, name);
+ if (old_validity != NULL)
+ new_validity = *old_validity;
+ else
+ memset(&new_validity, 0, sizeof(new_validity));
+
+ /* See if the mailbox exists. If we wanted recursive lookups we could
+ skip this, but at least for now we assume that if an existing
+ mailbox has no ACL it's equivalent to default ACLs. */
+ path = mailbox_list_get_path(storage->list, name,
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ ret = acl_backend_vfile_exists(backend, path,
+ &new_validity.mailbox_validity);
+ if (ret == 0) {
+ dir = acl_backend_vfile_get_local_dir(storage, name);
+ local_path = t_strconcat(dir, "/", name, NULL);
+ ret = acl_backend_vfile_exists(backend, local_path,
+ &new_validity.local_validity);
+ }
+ if (ret == 0 && backend->global_dir != NULL) {
+ global_path = t_strconcat(backend->global_dir, "/", name, NULL);
+ ret = acl_backend_vfile_exists(backend, global_path,
+ &new_validity.global_validity);
+ }
+ acl_cache_set_validity(_backend->cache, name, &new_validity);
+ return ret > 0;
+}
+
+static struct acl_object *
+acl_backend_vfile_object_init_parent(struct acl_backend *backend,
+ struct mail_storage *storage,
+ const char *child_name)
+{
+ const char *parent;
+
+ /* stop at the first parent that
+ a) has global ACL file
+ b) has local ACL file
+ c) exists */
+ while ((parent = get_parent_mailbox(storage, child_name)) != NULL) {
+ if (acl_backend_vfile_has_acl(backend, storage, parent))
+ break;
+ child_name = parent;
+ }
+ if (parent == NULL) {
+ /* use the root */
+ parent = "";
+ }
+ return acl_backend_vfile_object_init(backend, storage, parent);
+}
+
static void acl_backend_vfile_object_deinit(struct acl_object *_aclobj)
{
struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
seconds) */
time_t cache_secs = backend->cache_secs;
- if (st.st_mtime < validity->last_read_time - cache_secs ||
- ioloop_time - validity->last_read_time <= cache_secs)
+ if (validity->last_read_time != 0 &&
+ (st.st_mtime < validity->last_read_time - cache_secs ||
+ ioloop_time - validity->last_read_time <= cache_secs))
return 0;
}
acl_backend_vfile_nonowner_iter_next,
acl_backend_vfile_nonowner_iter_deinit,
acl_backend_vfile_object_init,
+ acl_backend_vfile_object_init_parent,
acl_backend_vfile_object_deinit,
acl_backend_vfile_object_refresh_cache,
acl_backend_vfile_object_update,
return alist->rights.backend;
}
-const char *acl_mailbox_list_get_parent_mailbox_name(struct mailbox_list *list,
- const char *name)
-{
- const char *p;
- char sep;
-
- sep = mailbox_list_get_hierarchy_sep(list);
- p = strrchr(name, sep);
- return p == NULL ? "" : t_strdup_until(name, p);
-}
-
static int
-acl_mailbox_list_have_right(struct acl_mailbox_list *alist, const char *name,
+acl_mailbox_list_have_right(struct mailbox_list *list, const char *name,
unsigned int acl_storage_right_idx, bool *can_see_r)
{
- return acl_storage_rights_ctx_have_right(&alist->rights, name,
- acl_storage_right_idx,
- can_see_r);
+ struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+ int ret;
+
+ ret = acl_storage_rights_ctx_have_right(&alist->rights, name, FALSE,
+ acl_storage_right_idx,
+ can_see_r);
+ if (ret < 0)
+ mailbox_list_set_internal_error(list);
+ return ret;
}
static void
acl_mailbox_list_info_is_visible(struct acl_mailbox_list_iterate_context *ctx,
const struct mailbox_info *info)
{
- struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list);
const char *acl_name;
int ret;
}
acl_name = acl_mailbox_list_iter_get_name(&ctx->ctx, info->name);
- ret = acl_mailbox_list_have_right(alist, acl_name,
+ ret = acl_mailbox_list_have_right(ctx->ctx.list, acl_name,
ACL_STORAGE_RIGHT_LOOKUP,
NULL);
if (ret != 0)
return ret;
mailbox_name = acl_mailbox_list_iter_get_name(ctx, mailbox_name);
- return acl_mailbox_list_have_right(alist, mailbox_name,
+ return acl_mailbox_list_have_right(ctx->list, mailbox_name,
ACL_STORAGE_RIGHT_LOOKUP, NULL);
}
struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
int ret;
- ret = acl_mailbox_list_have_right(alist, name, ACL_STORAGE_RIGHT_LOOKUP,
+ ret = acl_mailbox_list_have_right(list, name, ACL_STORAGE_RIGHT_LOOKUP,
NULL);
if (ret < 0)
return -1;
if (ret == 0) {
/* If we have INSERT right for the mailbox, we'll need to
reveal its existence so that APPEND and COPY works. */
- ret = acl_mailbox_list_have_right(alist, name,
+ ret = acl_mailbox_list_have_right(list, name,
ACL_STORAGE_RIGHT_INSERT,
NULL);
if (ret < 0)
case MAILBOX_NAME_NOINFERIORS:
/* have to check if we are allowed to see the parent */
T_BEGIN {
- const char *parent;
-
- parent = acl_mailbox_list_get_parent_mailbox_name(list,
- name);
- ret = acl_mailbox_list_have_right(alist, parent,
- ACL_STORAGE_RIGHT_LOOKUP, NULL);
+ ret = acl_storage_rights_ctx_have_right(&alist->rights, name,
+ TRUE, ACL_STORAGE_RIGHT_LOOKUP, NULL);
} T_END;
- if (ret < 0)
+ if (ret < 0) {
+ mailbox_list_set_internal_error(list);
return -1;
+ }
if (ret == 0) {
/* no permission to see the parent */
*status = MAILBOX_NAME_VALID;
bool can_see;
int ret;
- ret = acl_mailbox_list_have_right(alist, name, ACL_STORAGE_RIGHT_DELETE,
+ ret = acl_mailbox_list_have_right(list, name, ACL_STORAGE_RIGHT_DELETE,
&can_see);
if (ret <= 0) {
if (ret < 0)
int ret;
/* renaming requires rights to delete the old mailbox */
- ret = acl_mailbox_list_have_right(alist, oldname,
+ ret = acl_mailbox_list_have_right(list, oldname,
ACL_STORAGE_RIGHT_DELETE, &can_see);
if (ret <= 0) {
if (ret < 0)
/* and create the new one under the parent mailbox */
T_BEGIN {
- ret = acl_mailbox_list_have_right(alist,
- acl_mailbox_list_get_parent_mailbox_name(list, newname),
- ACL_STORAGE_RIGHT_CREATE, NULL);
+ ret = acl_storage_rights_ctx_have_right(&alist->rights, newname,
+ TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
} T_END;
if (ret <= 0) {
existence. Can't help it. */
mailbox_list_set_error(list, MAIL_ERROR_PERM,
MAIL_ERRSTR_NO_PERMISSION);
+ } else {
+ mailbox_list_set_internal_error(list);
}
return -1;
}
void acl_storage_rights_ctx_init(struct acl_storage_rights_context *ctx,
struct acl_backend *backend);
int acl_storage_rights_ctx_have_right(struct acl_storage_rights_context *ctx,
- const char *name,
+ const char *name, bool parent,
unsigned int acl_storage_right_idx,
bool *can_see_r);
struct acl_backend *acl_mailbox_list_get_backend(struct mailbox_list *list);
-const char *acl_mailbox_list_get_parent_mailbox_name(struct mailbox_list *list,
- const char *name);
void acl_plugin_init(void);
void acl_plugin_deinit(void);
}
int acl_storage_rights_ctx_have_right(struct acl_storage_rights_context *ctx,
- const char *name,
+ const char *name, bool parent,
unsigned int acl_storage_right_idx,
bool *can_see_r)
{
int ret, ret2;
ns = mailbox_list_get_namespace(ctx->backend->list);
- aclobj = acl_object_init_from_name(ctx->backend, ns->storage, name);
+ aclobj = !parent ?
+ acl_object_init_from_name(ctx->backend, ns->storage, name) :
+ acl_object_init_from_parent(ctx->backend, ns->storage, name);
ret = acl_object_have_right(aclobj, idx_arr[acl_storage_right_idx]);
if (can_see_r != NULL) {
struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
int ret;
- ret = acl_storage_rights_ctx_have_right(&astorage->rights, name,
+ ret = acl_storage_rights_ctx_have_right(&astorage->rights, name, FALSE,
acl_storage_right_idx,
can_see_r);
if (ret < 0)
bool directory)
{
struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
- struct mailbox_list *list = mail_storage_get_list(storage);
int ret;
T_BEGIN {
- ret = acl_storage_have_right(storage,
- acl_mailbox_list_get_parent_mailbox_name(list, name),
- ACL_STORAGE_RIGHT_CREATE, NULL);
+ ret = acl_storage_rights_ctx_have_right(&astorage->rights, name,
+ TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
} T_END;
if (ret <= 0) {
existence. Can't help it. */
mail_storage_set_error(storage, MAIL_ERROR_PERM,
MAIL_ERRSTR_NO_PERMISSION);
+ } else {
+ mail_storage_set_internal_error(storage);
}
return -1;
}