static void test_imap_match_globs_equal(void) { struct imap_match_glob *glob; pool_t pool; pool = pool_alloconly_create("imap match globs equal", 1024); test_begin("imap match globs equal"); glob = imap_match_init(pool, "1", FALSE, '/'); test_assert(imap_match_globs_equal(glob, imap_match_init(pool, "1", FALSE, '/'))); test_assert(imap_match_globs_equal(glob, imap_match_init(pool, "1", TRUE, '/'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "1", FALSE, '.'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "11", FALSE, '/'))); glob = imap_match_init(pool, "in%", TRUE, '/'); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "in%", FALSE, '/'))); test_assert(!imap_match_globs_equal(glob, imap_match_init(pool, "In%", TRUE, '/'))); pool_unref(&pool); test_end(); }
static void acl_mailbox_try_list_fast(struct acl_mailbox_list_iterate_context *ctx) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list); struct acl_backend *backend = alist->rights.backend; const unsigned int *idxp; const struct acl_mask *acl_mask; struct acl_mailbox_list_context *nonowner_list_ctx; struct mail_namespace *ns = ctx->ctx.list->ns; struct mailbox_list_iter_update_context update_ctx; const char *name; int ret; if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) return; if (ns->type == MAIL_NAMESPACE_TYPE_PUBLIC) { /* mailboxes in public namespace should all be listable to someone. we don't benefit from fast listing. */ return; } /* if this namespace's default rights contain LOOKUP, we'll need to go through all mailboxes in any case. */ idxp = alist->rights.acl_storage_right_idx + ACL_STORAGE_RIGHT_LOOKUP; if (acl_backend_get_default_rights(backend, &acl_mask) < 0 || acl_cache_mask_isset(acl_mask, *idxp)) return; /* no LOOKUP right by default, we can optimize this */ memset(&update_ctx, 0, sizeof(update_ctx)); update_ctx.iter_ctx = &ctx->ctx; update_ctx.glob = imap_match_init(pool_datastack_create(), "*", (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0, ctx->sep); update_ctx.match_parents = TRUE; update_ctx.tree_ctx = mailbox_tree_init(ctx->sep); nonowner_list_ctx = acl_backend_nonowner_lookups_iter_init(backend); while ((ret = acl_backend_nonowner_lookups_iter_next(nonowner_list_ctx, &name)) > 0) { T_BEGIN { const char *vname = mailbox_list_get_vname(ns->list, name); mailbox_list_iter_update(&update_ctx, vname); } T_END; } acl_backend_nonowner_lookups_iter_deinit(&nonowner_list_ctx); if (ret == 0) ctx->lookup_boxes = update_ctx.tree_ctx; else mailbox_tree_deinit(&update_ctx.tree_ctx); }
static int virtual_config_parse_line(struct virtual_parse_context *ctx, const char *line, const char **error_r) { struct mail_user *user = ctx->mbox->storage->storage.user; struct virtual_backend_box *bbox; const char *p; bool no_wildcards = FALSE; if (*line == ' ' || *line == '\t') { /* continues the previous search rule */ if (ctx->rule_idx == array_count(&ctx->mbox->backend_boxes)) { *error_r = "Search rule without a mailbox"; return -1; } while (*line == ' ' || *line == '\t') line++; str_append_c(ctx->rule, ' '); str_append(ctx->rule, line); return 0; } /* if there is no rule yet, it means we want the previous mailboxes to use the rule that comes later */ if (str_len(ctx->rule) > 0) { if (virtual_config_add_rule(ctx, error_r) < 0) return -1; } if (!uni_utf8_str_is_valid(line)) { *error_r = t_strdup_printf("Mailbox name not UTF-8: %s", line); return -1; } /* new mailbox. the search args are added to it later. */ bbox = p_new(ctx->pool, struct virtual_backend_box, 1); bbox->virtual_mbox = ctx->mbox; if (strcasecmp(line, "INBOX") == 0) line = "INBOX"; bbox->name = p_strdup(ctx->pool, line); switch (bbox->name[0]) { case '+': bbox->name++; bbox->clear_recent = TRUE; break; case '-': bbox->name++; bbox->negative_match = TRUE; break; case '!': /* save messages here */ if (ctx->mbox->save_bbox != NULL) { *error_r = "Multiple save mailboxes defined"; return -1; } bbox->name++; ctx->mbox->save_bbox = bbox; no_wildcards = TRUE; break; } if (bbox->name[0] == '/') { /* [+-!]/metadata entry:value */ if ((p = strchr(bbox->name, ':')) == NULL) { *error_r = "':' separator missing between metadata entry name and value"; return -1; } bbox->metadata_entry = p_strdup_until(ctx->pool, bbox->name, p++); bbox->metadata_value = p; if (!imap_metadata_verify_entry_name(bbox->metadata_entry, error_r)) return -1; no_wildcards = TRUE; } if (!no_wildcards && (strchr(bbox->name, '*') != NULL || strchr(bbox->name, '%') != NULL)) { bbox->glob = imap_match_init(ctx->pool, bbox->name, TRUE, ctx->sep); ctx->have_wildcards = TRUE; } if (bbox->metadata_entry == NULL) { /* now that the prefix characters have been processed, find the namespace */ bbox->ns = strcasecmp(bbox->name, "INBOX") == 0 ? mail_namespace_find_inbox(user->namespaces) : mail_namespace_find(user->namespaces, bbox->name); if (bbox->ns == NULL) { *error_r = t_strdup_printf("Namespace not found for %s", bbox->name); return -1; } if (strcmp(bbox->name, ctx->mbox->box.vname) == 0) { *error_r = "Virtual mailbox can't point to itself"; return -1; } ctx->have_mailbox_defines = TRUE; } array_append(&ctx->mbox->backend_boxes, &bbox, 1); return 0; }
static int virtual_config_parse_line(struct virtual_parse_context *ctx, const char *line, const char **error_r) { struct mail_user *user = ctx->mbox->storage->storage.user; struct virtual_backend_box *bbox; const char *name; if (*line == ' ' || *line == '\t') { /* continues the previous search rule */ if (ctx->rule_idx == array_count(&ctx->mbox->backend_boxes)) { *error_r = "Search rule without a mailbox"; return -1; } while (*line == ' ' || *line == '\t') line++; str_append_c(ctx->rule, ' '); str_append(ctx->rule, line); return 0; } /* if there is no rule yet, it means we want the previous mailboxes to use the rule that comes later */ if (str_len(ctx->rule) > 0) { if (virtual_config_add_rule(ctx, error_r) < 0) return -1; } /* new mailbox. the search args are added to it later. */ bbox = p_new(ctx->pool, struct virtual_backend_box, 1); if (strcasecmp(line, "INBOX") == 0) line = "INBOX"; bbox->name = p_strdup(ctx->pool, line); if (*line == '-' || *line == '+' || *line == '!') line++; bbox->ns = strcasecmp(line, "INBOX") == 0 ? mail_namespace_find_inbox(user->namespaces) : mail_namespace_find(user->namespaces, line); if (!uni_utf8_str_is_valid(bbox->name)) { *error_r = t_strdup_printf("Mailbox name not UTF-8: %s", bbox->name); return -1; } if (bbox->ns == NULL) { *error_r = t_strdup_printf("Namespace not found for %s", bbox->name); return -1; } if (bbox->name[0] == '+') { bbox->name++; bbox->clear_recent = TRUE; } if (strchr(bbox->name, '*') != NULL || strchr(bbox->name, '%') != NULL) { name = bbox->name[0] == '-' ? bbox->name + 1 : bbox->name; bbox->glob = imap_match_init(ctx->pool, name, TRUE, ctx->sep); ctx->have_wildcards = TRUE; } else if (bbox->name[0] == '!') { /* save messages here */ if (ctx->mbox->save_bbox != NULL) { *error_r = "Multiple save mailboxes defined"; return -1; } bbox->name++; ctx->mbox->save_bbox = bbox; } ctx->have_mailbox_defines = TRUE; array_append(&ctx->mbox->backend_boxes, &bbox, 1); return 0; }
static void test_imap_match(void) { struct test_imap_match test[] = { { "", "", IMAP_MATCH_YES }, { "a", "b", IMAP_MATCH_NO }, { "foo", "foo", IMAP_MATCH_YES }, { "foo", "foo/", IMAP_MATCH_PARENT }, { "%", "", IMAP_MATCH_YES }, { "%", "foo", IMAP_MATCH_YES }, { "%", "foo/", IMAP_MATCH_PARENT }, { "%/", "foo/", IMAP_MATCH_YES }, { "%", "foo/bar", IMAP_MATCH_PARENT }, { "%/%", "foo", IMAP_MATCH_CHILDREN }, { "%/%", "foo/", IMAP_MATCH_YES }, { "foo/bar/%", "foo", IMAP_MATCH_CHILDREN }, { "foo/bar/%", "foo/", IMAP_MATCH_CHILDREN }, { "foo*", "foo", IMAP_MATCH_YES }, { "foo*", "foo/", IMAP_MATCH_YES }, { "foo*", "fobo", IMAP_MATCH_NO }, { "*foo*", "bar/foo/", IMAP_MATCH_YES }, { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, { "foo*bar", "foobar/baz", IMAP_MATCH_CHILDREN | IMAP_MATCH_PARENT }, { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, { "%/%/%", "foo/", IMAP_MATCH_CHILDREN }, { "%/%o/%", "foo/", IMAP_MATCH_CHILDREN }, { "%/%o/%", "foo", IMAP_MATCH_CHILDREN }, { "inbox", "inbox", IMAP_MATCH_YES }, { "inbox", "INBOX", IMAP_MATCH_NO } }; struct test_imap_match inbox_test[] = { { "inbox", "inbox", IMAP_MATCH_YES }, { "inbox", "iNbOx", IMAP_MATCH_YES }, { "i%X", "iNbOx", IMAP_MATCH_YES }, { "%I%N%B%O%X%", "inbox", IMAP_MATCH_YES }, { "i%X/foo", "iNbOx/foo", IMAP_MATCH_YES }, { "%I%N%B%O%X%/foo", "inbox/foo", IMAP_MATCH_YES }, { "i%X/foo", "inbx/foo", IMAP_MATCH_NO } }; struct imap_match_glob *glob, *glob2; unsigned int i; pool_t pool; pool = pool_alloconly_create("imap match", 1024); /* first try tests without inboxcasing */ test_begin("imap match"); for (i = 0; i < N_ELEMENTS(test); i++) { glob = imap_match_init(pool, test[i].pattern, FALSE, '/'); test_assert(imap_match(glob, test[i].input) == test[i].result); glob2 = imap_match_dup(default_pool, glob); test_assert(imap_match_globs_equal(glob, glob2)); p_clear(pool); /* test the dup after clearing first one's memory */ test_assert(imap_match(glob2, test[i].input) == test[i].result); imap_match_deinit(&glob2); } /* inboxcasing tests */ for (i = 0; i < N_ELEMENTS(inbox_test); i++) { glob = imap_match_init(pool, inbox_test[i].pattern, TRUE, '/'); test_assert(imap_match(glob, inbox_test[i].input) == inbox_test[i].result); glob2 = imap_match_dup(default_pool, glob); test_assert(imap_match_globs_equal(glob, glob2)); p_clear(pool); /* test the dup after clearing first one's memory */ test_assert(imap_match(glob2, inbox_test[i].input) == inbox_test[i].result); imap_match_deinit(&glob2); } pool_unref(&pool); test_end(); }