int fts_backend_reset_last_uids(struct fts_backend *backend) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; struct mailbox *box; int ret = 0; iter = mailbox_list_iter_init(backend->ns->list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) continue; box = mailbox_alloc(info->ns->list, info->vname, 0); if (mailbox_open(box) == 0) { if (fts_index_set_last_uid(box, 0) < 0) ret = -1; } mailbox_free(&box); } if (mailbox_list_iter_deinit(&iter) < 0) ret = -1; return ret; }
static int quota_count_namespace(struct quota_root *root, struct mail_namespace *ns, uint64_t *bytes, uint64_t *count) { struct mailbox_list_iterate_context *ctx; const struct mailbox_info *info; int ret = 0; ctx = mailbox_list_iter_init(ns->list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(ctx)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) == 0) { ret = quota_count_mailbox(root, ns, info->vname, bytes, count); if (ret < 0) break; } } if (mailbox_list_iter_deinit(&ctx) < 0) ret = -1; if (ns->prefix_len > 0 && ret == 0 && (ns->prefix_len != 6 || strncasecmp(ns->prefix, "INBOX", 5) != 0)) { /* if the namespace prefix itself exists, count it also */ const char *name = t_strndup(ns->prefix, ns->prefix_len-1); ret = quota_count_mailbox(root, ns, name, bytes, count); } return ret; }
void mailbox_guid_cache_refresh(struct mailbox_list *list) { struct mailbox_list_iterate_context *ctx; const struct mailbox_info *info; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_guid_cache_rec *rec; uint8_t *guid_p; if (!hash_table_is_created(list->guid_cache)) { list->guid_cache_pool = pool_alloconly_create("guid cache", 1024*16); hash_table_create(&list->guid_cache, list->guid_cache_pool, 0, guid_128_hash, guid_128_cmp); } else { hash_table_clear(list->guid_cache, TRUE); p_clear(list->guid_cache_pool); } list->guid_cache_invalidated = FALSE; list->guid_cache_updated = FALSE; list->guid_cache_errors = FALSE; ctx = mailbox_list_iter_init(list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(ctx)) != NULL) { if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) continue; box = mailbox_alloc(list, info->vname, 0); if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) { i_error("Couldn't get mailbox %s GUID: %s", info->vname, mailbox_get_last_internal_error(box, NULL)); list->guid_cache_errors = TRUE; } else if ((rec = hash_table_lookup(list->guid_cache, (const uint8_t *)metadata.guid)) != NULL) { i_warning("Mailbox %s has duplicate GUID with %s: %s", info->vname, rec->vname, guid_128_to_string(metadata.guid)); } else { rec = p_new(list->guid_cache_pool, struct mailbox_guid_cache_rec, 1); memcpy(rec->guid, metadata.guid, sizeof(rec->guid)); rec->vname = p_strdup(list->guid_cache_pool, info->vname); guid_p = rec->guid; hash_table_insert(list->guid_cache, guid_p, rec); } mailbox_free(&box); } if (mailbox_list_iter_deinit(&ctx) < 0) list->guid_cache_errors = TRUE; }
static void acl_shared_namespace_add(struct mail_namespace *ns, struct mail_storage *storage, const char *userdomain) { static struct var_expand_table static_tab[] = { { 'u', NULL, "user" }, { 'n', NULL, "username" }, { 'd', NULL, "domain" }, { '\0', NULL, NULL } }; struct shared_storage *sstorage = (struct shared_storage *)storage; struct mail_namespace *new_ns = ns; struct var_expand_table *tab; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; const char *p, *mailbox; string_t *str; if (strcmp(ns->user->username, userdomain) == 0) { /* skip ourself */ return; } p = strchr(userdomain, '@'); tab = t_malloc_no0(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = userdomain; tab[1].value = p == NULL ? userdomain : t_strdup_until(userdomain, p); tab[2].value = p == NULL ? "" : p + 1; str = t_str_new(128); var_expand(str, sstorage->ns_prefix_pattern, tab); mailbox = str_c(str); if (shared_storage_get_namespace(&new_ns, &mailbox) < 0) return; /* check if there are any mailboxes really visible to us */ iter = mailbox_list_iter_init(new_ns->list, "*", MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) break; (void)mailbox_list_iter_deinit(&iter); if (info == NULL && !acl_ns_prefix_exists(new_ns)) { /* no visible mailboxes, remove the namespace */ mail_namespace_destroy(new_ns); } }
static int quota_mailbox_iter_deinit(struct quota_mailbox_iter **_iter) { struct quota_mailbox_iter *iter = *_iter; int ret = iter->failed ? -1 : 0; *_iter = NULL; if (iter->iter != NULL) { if (mailbox_list_iter_deinit(&iter->iter) < 0) { i_error("quota: Listing namespace '%s' failed: %s", iter->ns->prefix, mailbox_list_get_last_error(iter->ns->list, NULL)); ret = -1; } } i_free(iter); return ret; }
static const struct mailbox_info * quota_mailbox_iter_next(struct quota_mailbox_iter *iter) { struct mail_namespace *const *namespaces; const struct mailbox_info *info; unsigned int count; if (iter->iter == NULL) { namespaces = array_get(&iter->root->quota->namespaces, &count); if (iter->ns_idx >= count) return NULL; iter->ns = namespaces[iter->ns_idx++]; iter->iter = mailbox_list_iter_init(iter->ns->list, "*", MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_NO_AUTO_BOXES); } while ((info = mailbox_list_iter_next(iter->iter)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) == 0) return info; } if (mailbox_list_iter_deinit(&iter->iter) < 0) { i_error("quota: Listing namespace '%s' failed: %s", iter->ns->prefix, mailbox_list_get_last_error(iter->ns->list, NULL)); iter->failed = TRUE; } if (iter->ns->prefix_len > 0 && (iter->ns->prefix_len != 6 || strncasecmp(iter->ns->prefix, "INBOX", 5) != 0)) { /* if the namespace prefix itself exists, count it also */ iter->info.ns = iter->ns; iter->info.vname = t_strndup(iter->ns->prefix, iter->ns->prefix_len-1); return &iter->info; } /* try the next namespace */ return quota_mailbox_iter_next(iter); }
int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree, struct mail_namespace *ns, const char *box_name, const guid_128_t box_guid, const char *const *exclude_mailboxes, enum mail_error *error_r) { const enum mailbox_list_iter_flags list_flags = /* FIXME: we'll skip symlinks, because we can't handle them currently. in future we could detect them and create them by creating the symlink. */ MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES; const enum mailbox_list_iter_flags subs_list_flags = MAILBOX_LIST_ITER_NO_AUTO_BOXES | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct mailbox_list_iterate_context *iter; struct dsync_mailbox_node *node, *dup_node1, *dup_node2; const struct mailbox_info *info; const char *list_pattern = box_name != NULL && box_name[0] != '\\' ? box_name : "*"; int ret = 0; i_assert(mail_namespace_get_sep(ns) == tree->sep); /* assign namespace to its root, so it gets copied to children */ if (ns->prefix_len > 0) { node = dsync_mailbox_tree_get(tree, t_strndup(ns->prefix, ns->prefix_len-1)); node->ns = ns; } else { tree->root.ns = ns; } /* first add all of the existing mailboxes */ iter = mailbox_list_iter_init(ns->list, list_pattern, list_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN { if (dsync_mailbox_info_is_wanted(info, box_name, exclude_mailboxes)) { if (dsync_mailbox_tree_add(tree, info, box_guid, error_r) < 0) ret = -1; } } T_END; if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Mailbox listing for namespace '%s' failed: %s", ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r)); ret = -1; } /* add subscriptions */ iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (dsync_mailbox_tree_add_node(tree, info, &node) == 0) node->subscribed = TRUE; else { *error_r = MAIL_ERROR_TEMP; ret = -1; } } if (mailbox_list_iter_deinit(&iter) < 0) { i_error("Mailbox listing for namespace '%s' failed: %s", ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r)); ret = -1; } if (ret < 0) return -1; while (dsync_mailbox_tree_build_guid_hash(tree, &dup_node1, &dup_node2) < 0) { if (dsync_mailbox_tree_fix_guid_duplicate(tree, dup_node1, dup_node2) < 0) return -1; } /* add timestamps */ if (dsync_mailbox_tree_add_change_timestamps(tree, ns) < 0) return -1; return 0; }
static bool iter_mailbox_has_visible_children(struct acl_mailbox_list_iterate_context *ctx, bool only_nonpatterns, bool subscribed) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; string_t *pattern; const char *prefix; unsigned int i, prefix_len; bool stars = FALSE, ret = FALSE; /* do we have child mailboxes with LOOKUP right that don't match the list pattern? */ if (ctx->lookup_boxes != NULL) { /* we have a list of mailboxes with LOOKUP rights. before starting the slow list iteration, check check first if there even are any children with LOOKUP rights. */ struct mailbox_node *node; node = mailbox_tree_lookup(ctx->lookup_boxes, ctx->info.vname); i_assert(node != NULL); if (node->children == NULL) return FALSE; } /* if mailbox name has '*' characters in it, they'll conflict with the LIST wildcard. replace then with '%' and verify later that all results have the correct prefix. */ pattern = t_str_new(128); for (i = 0; ctx->info.vname[i] != '\0'; i++) { if (ctx->info.vname[i] != '*') str_append_c(pattern, ctx->info.vname[i]); else { stars = TRUE; str_append_c(pattern, '%'); } } if (i > 0 && ctx->info.vname[i-1] != ctx->sep) str_append_c(pattern, ctx->sep); str_append_c(pattern, '*'); prefix = str_c(pattern); prefix_len = str_len(pattern) - 1; iter = mailbox_list_iter_init(ctx->ctx.list, str_c(pattern), (!subscribed ? 0 : MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (only_nonpatterns && imap_match(ctx->ctx.glob, info->vname) == IMAP_MATCH_YES) { /* at least one child matches also the original list patterns. we don't need to show this mailbox. */ ret = FALSE; break; } if (!stars || strncmp(info->vname, prefix, prefix_len) == 0) ret = TRUE; } (void)mailbox_list_iter_deinit(&iter); return ret; }
static int maildir_rename_children(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; ARRAY(const char *) names_arr; const char *pattern, *oldpath, *newpath, *old_childname, *new_childname; const char *const *names, *old_vname, *new_vname; unsigned int i, count, old_vnamelen; pool_t pool; char old_ns_sep; int ret; ret = 0; /* first get the list of the children and save them to memory, because we can't rely on readdir() not skipping files while the directory is being modified. this doesn't protect against modifications by other processes though. */ pool = pool_alloconly_create("Maildir++ children list", 1024); i_array_init(&names_arr, 64); old_vname = mailbox_list_get_vname(oldlist, oldname); old_vnamelen = strlen(old_vname); new_vname = mailbox_list_get_vname(newlist, newname); old_ns_sep = mail_namespace_get_sep(oldlist->ns); pattern = t_strdup_printf("%s%c*", old_vname, old_ns_sep); iter = mailbox_list_iter_init(oldlist, pattern, MAILBOX_LIST_ITER_RETURN_NO_FLAGS | MAILBOX_LIST_ITER_RAW_LIST); while ((info = mailbox_list_iter_next(iter)) != NULL) { const char *name; /* verify that the prefix matches, otherwise we could have problems with mailbox names containing '%' and '*' chars */ if (strncmp(info->vname, old_vname, old_vnamelen) == 0 && info->vname[old_vnamelen] == old_ns_sep) { name = p_strdup(pool, info->vname + old_vnamelen); array_append(&names_arr, &name, 1); } } if (mailbox_list_iter_deinit(&iter) < 0) { ret = -1; names = NULL; count = 0; } else { names = array_get(&names_arr, &count); } for (i = 0; i < count; i++) { old_childname = mailbox_list_get_storage_name(oldlist, t_strconcat(old_vname, names[i], NULL)); if (strcmp(old_childname, new_vname) == 0) { /* When doing RENAME "a" "a.b" we see "a.b" here. We don't want to rename it anymore to "a.b.b". */ continue; } new_childname = mailbox_list_get_storage_name(newlist, t_strconcat(new_vname, names[i], NULL)); if (mailbox_list_get_path(oldlist, old_childname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 || mailbox_list_get_path(newlist, new_childname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0) i_unreached(); /* FIXME: it's possible to merge two mailboxes if either one of them doesn't have existing root mailbox. We could check this but I'm not sure if it's worth it. It could be even considered as a feature. Anyway, the bug with merging is that if both mailboxes have identically named child mailbox they conflict. Just ignore those and leave them under the old mailbox. */ if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno)) ret = 1; else { mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", oldpath, newpath); ret = -1; break; } (void)rename_dir(oldlist, old_childname, newlist, new_childname, MAILBOX_LIST_PATH_TYPE_CONTROL); (void)rename_dir(oldlist, old_childname, newlist, new_childname, MAILBOX_LIST_PATH_TYPE_INDEX); } array_free(&names_arr); pool_unref(&pool); return ret; }