From ed354926406e28254b581f821bb052f38d9c14e8 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 2 Dec 2011 17:05:31 +0200 Subject: [PATCH] Implemented IMAP SPECIAL-USE extension. --- configure.in | 2 +- src/imap/cmd-list.c | 22 +++++++--- src/lib-storage/mail-storage-settings.c | 56 ++++++++++++++++++++++++- src/lib-storage/mail-storage-settings.h | 1 + src/lib-storage/mail-storage.c | 2 +- src/lib-storage/mail-storage.h | 3 ++ src/lib-storage/mailbox-list-iter.c | 51 ++++++++++++++++------ src/lib-storage/mailbox-list-private.h | 1 + src/lib-storage/mailbox-list.h | 6 ++- 9 files changed, 121 insertions(+), 23 deletions(-) diff --git a/configure.in b/configure.in index a8502ef1b5..1bb6f0ec27 100644 --- a/configure.in +++ b/configure.in @@ -2671,7 +2671,7 @@ dnl ** dnl IDLE doesn't really belong to banner. It's there just to make Blackberries dnl happy, because otherwise BIS server disables push email. capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE" -capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS FUZZY" +capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS FUZZY SPECIAL-USE" AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities) AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", IMAP capabilities advertised in banner) diff --git a/src/imap/cmd-list.c b/src/imap/cmd-list.c index 2e20aea8db..2ea786f226 100644 --- a/src/imap/cmd-list.c +++ b/src/imap/cmd-list.c @@ -37,7 +37,7 @@ struct cmd_list_context { static void mailbox_flags2str(struct cmd_list_context *ctx, string_t *str, - enum mailbox_info_flags flags) + const char *special_use, enum mailbox_info_flags flags) { unsigned int orig_len = str_len(str); @@ -76,6 +76,11 @@ mailbox_flags2str(struct cmd_list_context *ctx, string_t *str, if ((flags & MAILBOX_UNMARKED) != 0) str_append(str, "\\UnMarked "); + if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0 && + special_use != NULL) { + str_append(str, special_use); + str_append_c(str, ' '); + } if (str_len(str) != orig_len) str_truncate(str, str_len(str)-1); } @@ -150,6 +155,8 @@ parse_return_flags(struct cmd_list_context *ctx, const struct imap_arg *args) list_flags |= MAILBOX_LIST_ITER_RETURN_SUBSCRIBED; else if (strcasecmp(str, "CHILDREN") == 0) list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN; + else if (strcasecmp(str, "SPECIAL-USE") == 0) + list_flags |= MAILBOX_LIST_ITER_RETURN_SPECIALUSE; else if (strcasecmp(str, "STATUS") == 0 && imap_arg_get_list(&args[1], &list_args)) { if (imap_status_parse_items(ctx->cmd, list_args, @@ -262,6 +269,7 @@ static void list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children) { struct mail_namespace *const *listed; + const struct mailbox_settings *mailbox_set; unsigned int len; enum mailbox_info_flags flags; const char *name; @@ -330,7 +338,10 @@ list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children) str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST"); if (ctx->lsub) flags |= MAILBOX_NONEXISTENT; - mailbox_flags2str(ctx, str, flags); + mailbox_set = (ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) == 0 ? NULL : + mailbox_settings_find(ctx->cmd->client->user, name); + mailbox_flags2str(ctx, str, mailbox_set == NULL ? NULL : + mailbox_set->special_use, flags); str_append(str, ") "); list_reply_append_ns_sep_param(str, ns_sep); str_append_c(str, ' '); @@ -447,7 +458,7 @@ list_namespace_mailboxes(struct cmd_list_context *ctx) str_truncate(str, 0); str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST"); - mailbox_flags2str(ctx, str, flags); + mailbox_flags2str(ctx, str, info->special_use, flags); str_append(str, ") "); list_reply_append_ns_sep_param(str, mail_namespace_get_sep(ctx->ns)); @@ -954,8 +965,9 @@ bool cmd_list_full(struct client_command_context *cmd, bool lsub) WORKAROUND_TB_LSUB_FLAGS) == 0) ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_NO_FLAGS; } else if (!ctx->used_listext) { - /* non-extended LIST - return children flags always */ - ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN; + /* non-extended LIST: use default flags */ + ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN | + MAILBOX_LIST_ITER_RETURN_SPECIALUSE; } ctx->list_flags |= MAILBOX_LIST_ITER_SHOW_EXISTING_PARENT; diff --git a/src/lib-storage/mail-storage-settings.c b/src/lib-storage/mail-storage-settings.c index 1957ad9d7d..18cbe8a9ee 100644 --- a/src/lib-storage/mail-storage-settings.c +++ b/src/lib-storage/mail-storage-settings.c @@ -150,6 +150,7 @@ const struct setting_parser_info mail_namespace_setting_parser_info = { static const struct setting_define mailbox_setting_defines[] = { DEF(SET_STR, name), { SET_ENUM, "auto", offsetof(struct mailbox_settings, autocreate), NULL } , + DEF(SET_STR, special_use), SETTING_DEFINE_LIST_END }; @@ -158,7 +159,8 @@ const struct mailbox_settings mailbox_default_settings = { .name = "", .autocreate = MAILBOX_SET_AUTO_NO":" MAILBOX_SET_AUTO_CREATE":" - MAILBOX_SET_AUTO_SUBSCRIBE + MAILBOX_SET_AUTO_SUBSCRIBE, + .special_use = "" }; const struct setting_parser_info mailbox_setting_parser_info = { @@ -460,7 +462,53 @@ static bool namespace_settings_check(void *_set, pool_t pool ATTR_UNUSED, return TRUE; } -static bool mailbox_settings_check(void *_set, pool_t pool ATTR_UNUSED, +static bool mailbox_special_use_exists(const char *name) +{ + if (name[0] != '\\') + return FALSE; + name++; + + if (strcasecmp(name, "All") == 0) + return TRUE; + if (strcasecmp(name, "Archive") == 0) + return TRUE; + if (strcasecmp(name, "Drafts") == 0) + return TRUE; + if (strcasecmp(name, "Flagged") == 0) + return TRUE; + if (strcasecmp(name, "Junk") == 0) + return TRUE; + if (strcasecmp(name, "Sent") == 0) + return TRUE; + if (strcasecmp(name, "Trash") == 0) + return TRUE; + return FALSE; +} + +static bool +mailbox_special_use_check(struct mailbox_settings *set, pool_t pool, + const char **error_r) +{ + const char *const *uses, *str; + unsigned int i; + + uses = t_strsplit_spaces(set->special_use, " "); + for (i = 0; uses[i] != NULL; i++) { + if (!mailbox_special_use_exists(uses[i])) { + *error_r = t_strdup_printf( + "mailbox %s: unknown special_use: %s", + set->name, uses[i]); + return FALSE; + } + } + /* make sure there are no extra spaces */ + str = t_strarray_join(uses, " "); + if (strcmp(str, set->special_use) != 0) + set->special_use = p_strdup(pool, str); + return TRUE; +} + +static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r) { struct mailbox_settings *set = _set; @@ -470,6 +518,10 @@ static bool mailbox_settings_check(void *_set, pool_t pool ATTR_UNUSED, set->name); return FALSE; } + if (*set->special_use != '\0') { + if (!mailbox_special_use_check(set, pool, error_r)) + return FALSE; + } return TRUE; } diff --git a/src/lib-storage/mail-storage-settings.h b/src/lib-storage/mail-storage-settings.h index 77c5768ec4..8d19855d93 100644 --- a/src/lib-storage/mail-storage-settings.h +++ b/src/lib-storage/mail-storage-settings.h @@ -63,6 +63,7 @@ struct mail_namespace_settings { struct mailbox_settings { const char *name; const char *autocreate; + const char *special_use; }; struct mail_user_settings { diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 380a4227cd..bdf387a32b 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -602,7 +602,7 @@ bool mail_storage_set_error_from_errno(struct mail_storage *storage) return TRUE; } -static struct mailbox_settings * +const struct mailbox_settings * mailbox_settings_find(struct mail_user *user, const char *vname) { struct mailbox_settings *const *box_set; diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index 2f802bba94..ae42d19914 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -429,6 +429,9 @@ mailbox_get_namespace(const struct mailbox *box) ATTR_PURE; /* Returns the storage's settings. */ const struct mail_storage_settings * mailbox_get_settings(struct mailbox *box) ATTR_PURE; +/* Returns the mailbox's settings, or NULL if there are none. */ +const struct mailbox_settings * +mailbox_settings_find(struct mail_user *user, const char *vname); /* Returns name of given mailbox */ const char *mailbox_get_name(const struct mailbox *box) ATTR_PURE; diff --git a/src/lib-storage/mailbox-list-iter.c b/src/lib-storage/mailbox-list-iter.c index d17f4be356..50e4970d96 100644 --- a/src/lib-storage/mailbox-list-iter.c +++ b/src/lib-storage/mailbox-list-iter.c @@ -3,6 +3,7 @@ #include "lib.h" #include "array.h" #include "imap-match.h" +#include "mail-storage.h" #include "mailbox-tree.h" #include "mailbox-list-private.h" @@ -17,6 +18,7 @@ enum autocreate_match_result { struct autocreate_box { const char *name; + const struct mailbox_settings *set; enum mailbox_info_flags flags; bool child_listed; }; @@ -103,6 +105,7 @@ mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx) array_append(&actx->box_sets, &box_sets[i], 1); autobox = array_append_space(&actx->boxes); autobox->name = box_sets[i]->name; + autobox->set = box_sets[i]; } } } @@ -529,19 +532,42 @@ static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx, return FALSE; } +static const struct mailbox_info * +mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx) +{ + const struct mailbox_info *info; + const struct mailbox_settings *set; + + info = ctx->list->v.iter_next(ctx); + if (info == NULL) + return NULL; + + ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE; + if ((ctx->list->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) { + set = mailbox_settings_find(ctx->list->ns->user, info->name); + if (set != NULL && *set->special_use != '\0') { + ctx->specialuse_info = *info; + ctx->specialuse_info.special_use = + *set->special_use == '\0' ? NULL : + set->special_use; + info = &ctx->specialuse_info; + } + } + return info; +} + static const struct mailbox_info * autocreate_iter_next(struct mailbox_list_iterate_context *ctx) { struct mailbox_list_autocreate_iterate_context *actx = ctx->autocreate_ctx; const struct mailbox_info *info; - const struct autocreate_box *autoboxes; + const struct autocreate_box *autoboxes, *autobox; unsigned int count; if (actx->idx == 0) { - info = ctx->list->v.iter_next(ctx); + info = mailbox_list_iter_next_call(ctx); if (info != NULL) { - ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE; actx->new_info = *info; return autocreate_iter_existing(ctx); } @@ -550,8 +576,13 @@ autocreate_iter_next(struct mailbox_list_iterate_context *ctx) /* list missing mailboxes */ autoboxes = array_get(&actx->boxes, &count); while (actx->idx < count) { - if (autocreate_iter_autobox(ctx, &autoboxes[actx->idx++])) + autobox = &autoboxes[actx->idx++]; + if (autocreate_iter_autobox(ctx, autobox)) { + actx->new_info.special_use = + *autobox->set->special_use == '\0' ? NULL : + autobox->set->special_use; return &actx->new_info; + } } i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets)); return NULL; @@ -560,16 +591,10 @@ autocreate_iter_next(struct mailbox_list_iterate_context *ctx) const struct mailbox_info * mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx) { - const struct mailbox_info *info; - if (ctx->autocreate_ctx != NULL) - info = autocreate_iter_next(ctx); - else { - info = ctx->list->v.iter_next(ctx); - if (info != NULL) - ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE; - } - return info; + return autocreate_iter_next(ctx); + else + return mailbox_list_iter_next_call(ctx); } int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx) diff --git a/src/lib-storage/mailbox-list-private.h b/src/lib-storage/mailbox-list-private.h index 8aada9faf4..a0a69f2326 100644 --- a/src/lib-storage/mailbox-list-private.h +++ b/src/lib-storage/mailbox-list-private.h @@ -147,6 +147,7 @@ struct mailbox_list_iterate_context { struct imap_match_glob *glob; struct mailbox_list_autocreate_iterate_context *autocreate_ctx; + struct mailbox_info specialuse_info; ARRAY_DEFINE(module_contexts, union mailbox_list_iterate_module_context *); diff --git a/src/lib-storage/mailbox-list.h b/src/lib-storage/mailbox-list.h index 8ad9d012c1..17180427fa 100644 --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@ -85,7 +85,9 @@ enum mailbox_list_iter_flags { /* Return MAILBOX_SUBSCRIBED flag */ MAILBOX_LIST_ITER_RETURN_SUBSCRIBED = 0x002000, /* Return children flags */ - MAILBOX_LIST_ITER_RETURN_CHILDREN = 0x004000 + MAILBOX_LIST_ITER_RETURN_CHILDREN = 0x004000, + /* Return IMAP special use flags */ + MAILBOX_LIST_ITER_RETURN_SPECIALUSE = 0x008000 }; enum mailbox_list_path_type { @@ -143,7 +145,9 @@ struct mailbox_list_settings { struct mailbox_info { const char *name; + const char *special_use; enum mailbox_info_flags flags; + struct mail_namespace *ns; }; -- 2.47.3