61
acl_mailbox_try_list_fast(struct acl_mailbox_list_iterate_context *ctx,
62
const char *const *patterns)
64
acl_mailbox_try_list_fast(struct acl_mailbox_list_iterate_context *ctx)
64
66
struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list);
65
67
struct acl_backend *backend = alist->rights.backend;
66
const unsigned int *idxp =
67
alist->rights.acl_storage_right_idx + ACL_STORAGE_RIGHT_LOOKUP;
68
const unsigned int *idxp;
68
69
const struct acl_mask *acl_mask;
69
70
struct acl_mailbox_list_context *nonowner_list_ctx;
70
71
struct mail_namespace *ns = ctx->ctx.list->ns;
71
72
struct mailbox_list_iter_update_context update_ctx;
74
string_t *vname = NULL;
77
77
if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RAW_LIST |
78
78
MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0)
81
if (ns->type == NAMESPACE_PUBLIC) {
82
/* mailboxes in public namespace should all be listable to
83
someone. we don't benefit from fast listing. */
87
/* if this namespace's default rights contain LOOKUP, we'll need to
88
go through all mailboxes in any case. */
89
idxp = alist->rights.acl_storage_right_idx + ACL_STORAGE_RIGHT_LOOKUP;
81
90
if (acl_backend_get_default_rights(backend, &acl_mask) < 0 ||
82
91
acl_cache_mask_isset(acl_mask, *idxp))
85
/* default is to not list mailboxes. we can optimize this. */
86
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) {
94
/* no LOOKUP right by default, we can optimize this */
95
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0)
88
96
vname = t_str_new(256);
94
98
memset(&update_ctx, 0, sizeof(update_ctx));
95
99
update_ctx.iter_ctx = &ctx->ctx;
97
imap_match_init_multiple(pool_datastack_create(), patterns,
101
imap_match_init(pool_datastack_create(), "*",
102
(ns->flags & NAMESPACE_FLAG_INBOX) != 0,
99
104
update_ctx.match_parents = TRUE;
101
for (try = 0; try < 2; try++) {
103
acl_backend_nonowner_lookups_iter_init(backend);
104
ctx->tree = mailbox_tree_init(sep);
105
update_ctx.tree_ctx = ctx->tree;
107
while ((ret = acl_backend_nonowner_lookups_iter_next(
108
nonowner_list_ctx, &name)) > 0) {
110
name = mail_namespace_get_vname(ns, vname,
113
mailbox_list_iter_update(&update_ctx, name);
115
acl_backend_nonowner_lookups_iter_deinit(&nonowner_list_ctx);
121
mailbox_tree_deinit(&ctx->tree);
105
update_ctx.tree_ctx = mailbox_tree_init(ctx->sep);
107
nonowner_list_ctx = acl_backend_nonowner_lookups_iter_init(backend);
108
while ((ret = acl_backend_nonowner_lookups_iter_next(nonowner_list_ctx,
111
name = mail_namespace_get_vname(ns, vname, name);
112
mailbox_list_iter_update(&update_ctx, name);
114
acl_backend_nonowner_lookups_iter_deinit(&nonowner_list_ctx);
117
ctx->lookup_boxes = update_ctx.tree_ctx;
119
mailbox_tree_deinit(&update_ctx.tree_ctx);
122
static struct mailbox_list_iterate_context *
123
acl_mailbox_list_iter_init_shared(struct mailbox_list *list,
124
const char *const *patterns,
125
enum mailbox_list_iter_flags flags)
127
struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
128
struct mailbox_list_iterate_context *ctx;
131
/* before listing anything add namespaces for all users
132
who may have visible mailboxes */
133
ret = acl_shared_namespaces_add(list->ns);
135
ctx = alist->module_ctx.super.iter_init(list, patterns, flags);
125
141
static struct mailbox_list_iterate_context *
130
146
struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
131
147
struct acl_mailbox_list_iterate_context *ctx;
133
152
ctx = i_new(struct acl_mailbox_list_iterate_context, 1);
134
153
ctx->ctx.list = list;
135
154
ctx->ctx.flags = flags;
156
inboxcase = (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0;
157
ctx->sep = (ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0 ?
158
list->ns->sep : list->ns->real_sep;
159
ctx->glob = imap_match_init_multiple(default_pool, patterns,
160
inboxcase, ctx->sep);
161
/* see if all patterns have only a single '*' and it's at the end.
162
we can use it to do some optimizations. */
163
ctx->simple_star_glob = TRUE;
164
for (i = 0; patterns[i] != NULL; i++) {
165
p = strchr(patterns[i], '*');
166
if (p == NULL || p[1] != '\0') {
167
ctx->simple_star_glob = FALSE;
172
/* Try to avoid reading ACLs from all mailboxes by getting a smaller
173
list of mailboxes that have even potential to be visible. If we
174
couldn't get such a list, we'll go through all mailboxes. */
138
acl_mailbox_try_list_fast(ctx, patterns);
176
acl_mailbox_try_list_fast(ctx);
140
178
ctx->super_ctx = alist->module_ctx.super.
141
179
iter_init(list, patterns, flags);
176
215
return mail_namespace_fix_sep(ns, name);
219
iter_is_listing_all_children(struct acl_mailbox_list_iterate_context *ctx)
223
/* If all patterns (with '.' separator) are in "name*", "name.*" or
224
"%.*" style format, simple_star_glob=TRUE and we can easily test
225
this by simply checking if name/child mailbox matches. */
226
child = t_strdup_printf("%s%cx", ctx->info.name, ctx->sep);
227
return ctx->simple_star_glob &&
228
imap_match(ctx->glob, child) == IMAP_MATCH_YES;
232
iter_mailbox_has_visible_children(struct acl_mailbox_list_iterate_context *ctx,
233
bool only_nonpatterns)
235
struct mailbox_list_iterate_context *iter;
236
const struct mailbox_info *info;
237
enum mailbox_list_iter_flags flags;
240
unsigned int i, prefix_len;
241
bool stars = FALSE, ret = FALSE;
243
/* do we have child mailboxes with LOOKUP right that don't match
245
if (ctx->lookup_boxes != NULL) {
246
/* we have a list of mailboxes with LOOKUP rights. before
247
starting the slow list iteration, check check first
248
if there even are any children with LOOKUP rights. */
249
struct mailbox_node *node;
251
node = mailbox_tree_lookup(ctx->lookup_boxes, ctx->info.name);
252
i_assert(node != NULL);
253
if (node->children == NULL)
257
/* if mailbox name has '*' characters in it, they'll conflict with the
258
LIST wildcard. replace then with '%' and verify later that all
259
results have the correct prefix. */
260
pattern = t_str_new(128);
261
for (i = 0; ctx->info.name[i] != '\0'; i++) {
262
if (ctx->info.name[i] != '*')
263
str_append_c(pattern, ctx->info.name[i]);
266
str_append_c(pattern, '%');
269
str_append_c(pattern, ctx->sep);
270
str_append_c(pattern, '*');
271
prefix = str_c(pattern);
272
prefix_len = str_len(pattern) - 1;
274
flags = (ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) |
275
MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
276
iter = mailbox_list_iter_init(ctx->ctx.list, str_c(pattern), flags);
277
while ((info = mailbox_list_iter_next(iter)) != NULL) {
278
if (only_nonpatterns &&
279
imap_match(ctx->glob, info->name) == IMAP_MATCH_YES) {
280
/* at least one child matches also the original list
281
patterns. we don't need to show this mailbox. */
285
if (!stars || strncmp(info->name, prefix, prefix_len) == 0)
288
(void)mailbox_list_iter_deinit(&iter);
180
293
acl_mailbox_list_info_is_visible(struct acl_mailbox_list_iterate_context *ctx)
295
#define PRESERVE_MAILBOX_FLAGS (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED)
182
296
struct mailbox_info *info = &ctx->info;
183
297
const char *acl_name;
192
306
ret = acl_mailbox_list_have_right(ctx->ctx.list, acl_name,
193
307
ACL_STORAGE_RIGHT_LOOKUP,
310
if ((info->flags & MAILBOX_CHILDREN) != 0 &&
311
!iter_mailbox_has_visible_children(ctx, FALSE)) {
312
info->flags &= ~MAILBOX_CHILDREN;
313
info->flags |= MAILBOX_NOCHILDREN;
198
318
/* no permission to see this mailbox */
199
if ((info->flags & MAILBOX_SUBSCRIBED) != 0) {
200
/* it's subscribed, show it as non-existent */
201
info->flags = MAILBOX_NONEXISTENT | MAILBOX_SUBSCRIBED;
319
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
320
/* we're listing subscribed mailboxes. this one or its child
321
is subscribed, so we'll need to list it. but since we don't
322
have LOOKUP right, we'll need to show it as nonexistent. */
323
i_assert((info->flags & PRESERVE_MAILBOX_FLAGS) != 0);
324
info->flags = MAILBOX_NONEXISTENT |
325
(info->flags & PRESERVE_MAILBOX_FLAGS);
329
if (!iter_is_listing_all_children(ctx) &&
330
iter_mailbox_has_visible_children(ctx, TRUE)) {
331
/* no child mailboxes match the list pattern(s), but mailbox
332
has visible children. we'll need to show this as
334
info->flags = MAILBOX_NONEXISTENT | MAILBOX_CHILDREN |
335
(info->flags & PRESERVE_MAILBOX_FLAGS);
232
acl_mailbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
233
const char *dir, const char *fname,
234
const char *mailbox_name,
235
enum mailbox_list_file_type type,
236
enum mailbox_info_flags *flags_r)
238
struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->list);
241
ret = alist->module_ctx.super.iter_is_mailbox(ctx, dir, fname,
244
if (ret <= 0 || (ctx->flags & MAILBOX_LIST_ITER_RAW_LIST) != 0)
247
mailbox_name = acl_mailbox_list_iter_get_name(ctx, mailbox_name);
248
return acl_mailbox_list_have_right(ctx->list, mailbox_name,
249
ACL_STORAGE_RIGHT_LOOKUP, NULL);
253
366
acl_mailbox_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
255
368
struct acl_mailbox_list_iterate_context *ctx =
392
506
return alist->module_ctx.super.rename_mailbox(list, oldname, newname);
395
void acl_mailbox_list_created(struct mailbox_list *list)
509
static void acl_mailbox_list_init_shared(struct mailbox_list *list)
511
struct acl_mailbox_list *alist;
513
alist = p_new(list->pool, struct acl_mailbox_list, 1);
514
alist->module_ctx.super = list->v;
515
list->v.iter_init = acl_mailbox_list_iter_init_shared;
517
MODULE_CONTEXT_SET(list, acl_mailbox_list_module, alist);
520
static void acl_mailbox_list_init_default(struct mailbox_list *list)
522
struct acl_user *auser = ACL_USER_CONTEXT(list->ns->user);
397
523
struct acl_mailbox_list *alist;
398
524
struct acl_backend *backend;
399
525
struct mail_namespace *ns;
400
526
enum mailbox_list_flags flags;
401
const char *acl_env, *current_username, *owner_username;
527
const char *current_username, *owner_username;
402
528
bool owner = TRUE;
404
acl_env = getenv("ACL");
405
i_assert(acl_env != NULL);
407
owner_username = getenv("USER");
408
if (owner_username == NULL)
409
i_fatal("ACL: USER environment not set");
411
current_username = getenv("MASTER_USER");
530
owner_username = list->ns->user->username;
531
current_username = auser->master_user;
412
532
if (current_username == NULL)
413
533
current_username = owner_username;
421
541
if (ns->type != NAMESPACE_PRIVATE)
424
/* FIXME: set groups */
425
backend = acl_backend_init(acl_env, list, current_username,
426
getenv("ACL_GROUPS") == NULL ? NULL :
427
t_strsplit(getenv("ACL_GROUPS"), ","),
544
backend = acl_backend_init(auser->acl_env, list, current_username,
545
auser->groups, owner);
429
546
if (backend == NULL)
430
547
i_fatal("ACL backend initialization failed");
432
549
flags = mailbox_list_get_flags(list);
433
550
if ((flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) {
434
/* not necessarily, but safer to do this for now.. */
551
/* not necessarily, but safer to do this for now. */
435
552
i_fatal("mail_full_filesystem_access=yes is "
436
553
"incompatible with ACLs");
441
558
list->v.iter_init = acl_mailbox_list_iter_init;
442
559
list->v.iter_next = acl_mailbox_list_iter_next;
443
560
list->v.iter_deinit = acl_mailbox_list_iter_deinit;
444
list->v.iter_is_mailbox = acl_mailbox_list_iter_is_mailbox;
445
561
list->v.get_mailbox_name_status = acl_get_mailbox_name_status;
446
562
list->v.delete_mailbox = acl_mailbox_list_delete;
447
563
list->v.rename_mailbox = acl_mailbox_list_rename;
449
565
acl_storage_rights_ctx_init(&alist->rights, backend);
451
566
MODULE_CONTEXT_SET(list, acl_mailbox_list_module, alist);
569
void acl_mailbox_list_created(struct mailbox_list *list)
571
if ((list->ns->flags & NAMESPACE_FLAG_NOACL) != 0) {
572
/* no ACL checks for internal namespaces (deliver, shared) */
573
if (list->ns->type == NAMESPACE_SHARED)
574
acl_mailbox_list_init_shared(list);
576
acl_mailbox_list_init_default(list);
453
579
if (acl_next_hook_mailbox_list_created != NULL)
454
580
acl_next_hook_mailbox_list_created(list);