void index_storage_mailbox_alloc(struct mailbox *box, const char *vname, enum mailbox_flags flags, const char *index_prefix) { static unsigned int mailbox_generation_sequence = 0; struct index_mailbox_context *ibox; i_assert(vname != NULL); box->generation_sequence = ++mailbox_generation_sequence; box->vname = p_strdup(box->pool, vname); box->name = p_strdup(box->pool, mailbox_list_get_storage_name(box->list, vname)); box->flags = flags; box->index_prefix = p_strdup(box->pool, index_prefix); p_array_init(&box->search_results, box->pool, 16); array_create(&box->module_contexts, box->pool, sizeof(void *), 5); ibox = p_new(box->pool, struct index_mailbox_context, 1); ibox->list_index_sync_ext_id = (uint32_t)-1; ibox->index_flags = MAIL_INDEX_OPEN_FLAG_CREATE | mail_storage_settings_to_index_flags(box->storage->set); if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) ibox->index_flags |= MAIL_INDEX_OPEN_FLAG_SAVEONLY; ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL; MODULE_CONTEXT_SET(box, index_storage_module, ibox); box->inbox_user = strcmp(box->name, "INBOX") == 0 && (box->list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0; box->inbox_any = strcmp(box->name, "INBOX") == 0 && (box->list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0; }
const struct mailbox_info * mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *_ctx) { struct subscriptions_mailbox_list_iterate_context *ctx = (struct subscriptions_mailbox_list_iterate_context *)_ctx; struct mailbox_list *list = _ctx->list; struct mailbox_node *node; enum mailbox_info_flags subs_flags; const char *vname, *storage_name, *error; int ret; node = mailbox_tree_iterate_next(ctx->iter, &vname); if (node == NULL) return NULL; ctx->info.vname = vname; subs_flags = node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && (_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) { /* don't care about flags, just return it */ ctx->info.flags = subs_flags; return &ctx->info; } storage_name = mailbox_list_get_storage_name(list, vname); if (!mailbox_list_is_valid_name(list, storage_name, &error)) { /* broken entry in subscriptions file */ ctx->info.flags = MAILBOX_NONEXISTENT; } else if (mailbox_list_mailbox(list, storage_name, &ctx->info.flags) < 0) { ctx->info.flags = 0; _ctx->failed = TRUE; } else if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 && (ctx->info.flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN)) == 0) { ret = mailbox_has_children(list, storage_name); if (ret < 0) _ctx->failed = TRUE; else if (ret == 0) ctx->info.flags |= MAILBOX_NOCHILDREN; else ctx->info.flags |= MAILBOX_CHILDREN; } ctx->info.flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); ctx->info.flags |= node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); return &ctx->info; }
static const char * acl_mailbox_list_iter_get_name(struct mailbox_list_iterate_context *ctx, const char *vname) { struct mail_namespace *ns = ctx->list->ns; const char *name; unsigned int len; name = mailbox_list_get_storage_name(ns->list, vname); len = strlen(name); if (len > 0 && name[len-1] == mailbox_list_get_hierarchy_sep(ns->list)) { /* name ends with separator. this can happen if doing e.g. LIST "" foo/% and it lists "foo/". */ name = t_strndup(name, len-1); } return name; }
static int shared_get_storage(struct mailbox_list **list, const char *vname, struct mail_storage **storage_r) { struct mail_namespace *ns = (*list)->ns; const char *name; name = mailbox_list_get_storage_name(*list, vname); if (*name == '\0' && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) { /* trying to access the shared/ prefix itself */ *storage_r = ns->storage; return 0; } if (shared_storage_get_namespace(&ns, &name) < 0) return -1; *list = ns->list; return mailbox_list_get_storage(list, vname, storage_r); }
static const char * imapc_list_get_fs_name(struct imapc_mailbox_list *list, const char *name) { struct mailbox_list *fs_list = imapc_list_get_fs(list); struct mail_namespace *ns = list->list.ns; const char *vname; char ns_sep = mail_namespace_get_sep(ns); if (name == NULL) return NULL; vname = mailbox_list_get_vname(&list->list, name); if (list->set->imapc_list_prefix[0] != '\0') { /* put back the prefix, so it gets included in the filesystem. */ unsigned int vname_len = strlen(vname); if (ns->prefix_len > 0) { /* skip over the namespace prefix */ i_assert(strncmp(vname, ns->prefix, ns->prefix_len-1) == 0); if (vname_len == ns->prefix_len-1) vname = ""; else { i_assert(vname[ns->prefix_len-1] == ns_sep); vname += ns->prefix_len; } } if (vname[0] == '\0') { vname = t_strconcat(ns->prefix, list->set->imapc_list_prefix, NULL); } else { vname = t_strdup_printf("%s%s%c%s", ns->prefix, list->set->imapc_list_prefix, ns_sep, vname); } } return mailbox_list_get_storage_name(fs_list, vname); }
static int acllist_append(struct acl_backend_vfile *backend, struct ostream *output, const char *vname) { struct acl_object *aclobj; struct acl_object_list_iter *iter; struct acl_rights rights; struct acl_backend_vfile_acllist acllist; const char *name; int ret; name = mailbox_list_get_storage_name(backend->backend.list, vname); acl_cache_flush(backend->backend.cache, name); aclobj = acl_object_init_from_name(&backend->backend, name); iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { if (acl_rights_has_nonowner_lookup_changes(&rights)) break; } acl_object_list_deinit(&iter); if (acl_backend_vfile_object_get_mtime(aclobj, &acllist.mtime) < 0) ret = -1; if (ret > 0) { acllist.name = p_strdup(backend->acllist_pool, name); array_append(&backend->acllist, &acllist, 1); T_BEGIN { const char *line; line = t_strdup_printf("%s %s\n", dec2str(acllist.mtime), name); o_stream_nsend_str(output, line); } T_END; }
int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain, const struct dsync_mailbox_tree_sync_change *change, enum mail_error *error_r) { struct mailbox *box = NULL, *destbox; const char *errstr, *func_name = NULL, *storage_name; enum mail_error error; int ret = -1; if (brain->backup_send) { i_assert(brain->no_backup_overwrite); return 0; } switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: /* make sure we're deleting the correct mailbox */ ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid, &box, &errstr, error_r); if (ret < 0) { i_error("Mailbox sync: Couldn't allocate mailbox GUID %s: %s", guid_128_to_string(change->mailbox_guid), errstr); return -1; } if (ret == 0) { if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox GUID %s deletion conflict: %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(change->mailbox_guid), errstr); } brain->changes_during_sync = TRUE; return 0; } break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: storage_name = mailbox_list_get_storage_name(change->ns->list, change->full_name); if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0) return 0; errstr = mailbox_list_get_last_error(change->ns->list, &error); if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_EXISTS) { if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox %s mailbox_list_delete_dir conflict: %s", brain->master_brain ? 'M' : 'S', change->full_name, errstr); } brain->changes_during_sync = TRUE; return 0; } else { i_error("Mailbox sync: mailbox_list_delete_dir failed: %s", errstr); *error_r = error; return -1; } default: box = mailbox_alloc(change->ns->list, change->full_name, 0); break; } switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX: ret = sync_create_box(brain, box, change->mailbox_guid, change->uid_validity, error_r); mailbox_free(&box); return ret; case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR: ret = mailbox_create(box, NULL, TRUE); if (ret < 0 && mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) { /* it doesn't matter if somebody else created this directory or we automatically did while creating its child mailbox. it's there now anyway and we don't gain anything by treating this failure any differently from success. */ ret = 0; } func_name = "mailbox_create"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: ret = mailbox_delete(box); func_name = "mailbox_delete"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: i_unreached(); case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME: destbox = mailbox_alloc(change->ns->list, change->rename_dest_name, 0); ret = mailbox_rename(box, destbox); func_name = "mailbox_rename"; mailbox_free(&destbox); break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE: ret = mailbox_set_subscribed(box, TRUE); func_name = "mailbox_set_subscribed"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE: ret = mailbox_set_subscribed(box, FALSE); func_name = "mailbox_set_subscribed"; break; } if (ret < 0) { errstr = mailbox_get_last_error(box, &error); if (error == MAIL_ERROR_EXISTS || error == MAIL_ERROR_NOTFOUND) { /* mailbox was already created or was already deleted. let the next sync figure out what to do */ if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox %s %s conflict: %s", brain->master_brain ? 'M' : 'S', mailbox_get_vname(box), func_name, errstr); } brain->changes_during_sync = TRUE; ret = 0; } else { i_error("Mailbox %s sync: %s failed: %s", mailbox_get_vname(box), func_name, errstr); *error_r = error; } } mailbox_free(&box); 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; }