Mercurial > dovecot > core-2.2
view src/lib-storage/index/imapc/imapc-list.c @ 12620:b9baa9db8950
imapc: Fixed LIST/LSUB handling.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 31 Jan 2011 19:01:22 +0200 |
parents | 5d97dfa9d86c |
children | aa131065b53d |
line wrap: on
line source
/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "imap-arg.h" #include "imap-match.h" #include "mailbox-tree.h" #include "imapc-client.h" #include "imapc-storage.h" #include "imapc-list.h" struct imapc_mailbox_list_iterate_context { struct mailbox_list_iterate_context ctx; struct mailbox_tree_iterate_context *iter; struct imap_match_glob *glob; struct mailbox_info info; bool failed; }; extern struct mailbox_list imapc_mailbox_list; static struct mailbox_list *imapc_list_alloc(void) { struct imapc_mailbox_list *list; pool_t pool; pool = pool_alloconly_create("imapc mailbox list", 1024); list = p_new(pool, struct imapc_mailbox_list, 1); list->list = imapc_mailbox_list; list->list.pool = pool; return &list->list; } static void imapc_list_deinit(struct mailbox_list *_list) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; if (list->mailboxes != NULL) mailbox_tree_deinit(&list->mailboxes); if (list->subscriptions != NULL) mailbox_tree_deinit(&list->subscriptions); pool_unref(&list->list.pool); } static void imapc_list_simple_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_simple_context *ctx = context; const char *str; enum mail_error error; imapc_simple_callback(reply, context); if (ctx->ret < 0) { str = mail_storage_get_last_error(&ctx->storage->storage, &error); mailbox_list_set_error(&ctx->storage->list->list, error, str); } } static struct mailbox_node * imapc_list_update_tree(struct mailbox_tree_context *tree, const struct imap_arg *args) { struct mailbox_node *node; const struct imap_arg *flags; const char *name, *flag; enum mailbox_info_flags info_flags = 0; bool created; if (!imap_arg_get_list(&args[0], &flags) || args[1].type == IMAP_ARG_EOL || !imap_arg_get_astring(&args[2], &name)) return NULL; while (imap_arg_get_atom(flags, &flag)) { if (strcasecmp(flag, "\\NoSelect") == 0) info_flags |= MAILBOX_NOSELECT; else if (strcasecmp(flag, "\\NonExistent") == 0) info_flags |= MAILBOX_NONEXISTENT; else if (strcasecmp(flag, "\\NoInferiors") == 0) info_flags |= MAILBOX_NOINFERIORS; else if (strcasecmp(flag, "\\Subscribed") == 0) info_flags |= MAILBOX_SUBSCRIBED; flags++; } if ((info_flags & MAILBOX_NONEXISTENT) != 0) node = mailbox_tree_lookup(tree, name); else node = mailbox_tree_get(tree, name, &created); if (node != NULL) node->flags = info_flags; return node; } static void imapc_untagged_list(const struct imapc_untagged_reply *reply, struct imapc_storage *storage) { struct imapc_mailbox_list *list = storage->list; const struct imap_arg *args = reply->args; const char *sep, *name; if (list->sep == '\0') { /* we haven't asked for the separator yet. lets see if this is the reply for its request. */ if (args[0].type == IMAP_ARG_EOL || !imap_arg_get_nstring(&args[1], &sep) || !imap_arg_get_astring(&args[2], &name)) return; /* we can't handle NIL separator yet */ list->sep = sep == NULL ? '/' : sep[0]; return; } (void)imapc_list_update_tree(list->mailboxes, args); } static void imapc_untagged_lsub(const struct imapc_untagged_reply *reply, struct imapc_storage *storage) { struct imapc_mailbox_list *list = storage->list; const struct imap_arg *args = reply->args; struct mailbox_node *node; if (list->sep == '\0') { /* we haven't asked for the separator yet */ return; } node = imapc_list_update_tree(list->subscriptions, args); if (node != NULL) node->flags |= MAILBOX_SUBSCRIBED; } void imapc_list_register_callbacks(struct imapc_mailbox_list *list) { imapc_storage_register_untagged(list->storage, "LIST", imapc_untagged_list); imapc_storage_register_untagged(list->storage, "LSUB", imapc_untagged_lsub); } static int imapc_list_refresh(struct imapc_mailbox_list *list, enum mailbox_list_iter_flags flags) { struct imapc_simple_context ctx; ctx.storage = list->storage; if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) { imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, "LIST \"\" *"); if (list->mailboxes != NULL) mailbox_tree_deinit(&list->mailboxes); list->mailboxes = mailbox_tree_init(list->sep); } else { imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, "LSUB \"\" *"); if (list->subscriptions != NULL) mailbox_tree_deinit(&list->subscriptions); list->subscriptions = mailbox_tree_init(list->sep); } imapc_client_run(list->storage->client); return ctx.ret; } static bool imapc_is_valid_pattern(struct mailbox_list *list ATTR_UNUSED, const char *pattern ATTR_UNUSED) { return TRUE; } static bool imapc_is_valid_existing_name(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED) { return TRUE; } static bool imapc_is_valid_create_name(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED) { return TRUE; } static char imapc_list_get_hierarchy_sep(struct mailbox_list *_list) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct imapc_simple_context ctx; if (list->sep == '\0') { ctx.storage = list->storage; imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, "LIST \"\" \"\""); imapc_client_run(list->storage->client); if (ctx.ret < 0) { list->broken = TRUE; return '/'; } } return list->sep; } static const char * imapc_list_get_path(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED, enum mailbox_list_path_type type) { if (type == MAILBOX_LIST_PATH_TYPE_INDEX) return ""; return NULL; } static const char * imapc_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED) { i_panic("imapc: Can't return a temp prefix for '%s'", list->ns->prefix); return NULL; } static const char * imapc_list_join_refpattern(struct mailbox_list *list ATTR_UNUSED, const char *ref, const char *pattern) { return t_strconcat(ref, pattern, NULL); } static struct mailbox_list_iterate_context * imapc_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct imapc_mailbox_list_iterate_context *ctx; struct mailbox_tree_context *tree; char sep; sep = mailbox_list_get_hierarchy_sep(_list); ctx = i_new(struct imapc_mailbox_list_iterate_context, 1); ctx->ctx.list = _list; ctx->ctx.flags = flags; ctx->info.ns = _list->ns; ctx->glob = imap_match_init_multiple(default_pool, patterns, FALSE, sep); if (imapc_list_refresh(list, flags) < 0) ctx->failed = TRUE; else { tree = (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 ? list->subscriptions : list->mailboxes; ctx->iter = mailbox_tree_iterate_init(tree, NULL, 0); } return &ctx->ctx; } static const struct mailbox_info * imapc_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct imapc_mailbox_list_iterate_context *ctx = (struct imapc_mailbox_list_iterate_context *)_ctx; struct mailbox_node *node; const char *name; if (ctx->failed) return NULL; while ((node = mailbox_tree_iterate_next(ctx->iter, &name)) != NULL) { if (imap_match(ctx->glob, name) == IMAP_MATCH_YES) { ctx->info.flags &= ~(MAILBOX_CHILDREN | MAILBOX_NOCHILDREN); if (node->children == NULL) ctx->info.flags |= MAILBOX_NOCHILDREN; else ctx->info.flags |= MAILBOX_CHILDREN; ctx->info.name = name; return &ctx->info; } } return NULL; } static int imapc_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct imapc_mailbox_list_iterate_context *ctx = (struct imapc_mailbox_list_iterate_context *)_ctx; int ret = ctx->failed ? -1 : 0; if (ctx->iter != NULL) mailbox_tree_iterate_deinit(&ctx->iter); imap_match_deinit(&ctx->glob); i_free(ctx); return ret; } static int imapc_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct imapc_simple_context ctx; ctx.storage = list->storage; imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, set ? "SUBSCRIBE %s" : "UNSUBSCRIBE %s", name); imapc_client_run(list->storage->client); return ctx.ret; } static int imapc_list_create_mailbox_dir(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED, enum mailbox_dir_create_type type ATTR_UNUSED) { /* this gets called just before mailbox.create(). we don't need to do anything. */ return 0; } static int imapc_list_delete_mailbox(struct mailbox_list *_list, const char *name) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; struct imapc_simple_context ctx; ctx.storage = list->storage; imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, "DELETE %s", name); imapc_client_run(list->storage->client); return ctx.ret; } static int imapc_list_delete_dir(struct mailbox_list *list ATTR_UNUSED, const char *name ATTR_UNUSED) { return 0; } static int imapc_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist; struct imapc_simple_context ctx; if (!rename_children) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming without children not supported."); return -1; } if (oldlist != newlist) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailboxes across storages."); return -1; } ctx.storage = list->storage; imapc_client_cmdf(list->storage->client, imapc_list_simple_callback, &ctx, "RENAME %s %s", oldname, newname); imapc_client_run(list->storage->client); return ctx.ret; } struct mailbox_list imapc_mailbox_list = { .name = MAILBOX_LIST_NAME_IMAPC, .props = 0, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, { imapc_list_alloc, imapc_list_deinit, NULL, imapc_is_valid_pattern, imapc_is_valid_existing_name, imapc_is_valid_create_name, imapc_list_get_hierarchy_sep, mailbox_list_default_get_vname, mailbox_list_default_get_storage_name, imapc_list_get_path, imapc_list_get_temp_prefix, imapc_list_join_refpattern, imapc_list_iter_init, imapc_list_iter_next, imapc_list_iter_deinit, NULL, NULL, imapc_list_set_subscribed, imapc_list_create_mailbox_dir, imapc_list_delete_mailbox, imapc_list_delete_dir, imapc_list_rename_mailbox } };