diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
commit | 0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch) | |
tree | 3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib-storage/mail-search-register-imap.c | |
parent | Initial commit. (diff) | |
download | dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip |
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/mail-search-register-imap.c')
-rw-r--r-- | src/lib-storage/mail-search-register-imap.c | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/src/lib-storage/mail-search-register-imap.c b/src/lib-storage/mail-search-register-imap.c new file mode 100644 index 0000000..7df0500 --- /dev/null +++ b/src/lib-storage/mail-search-register-imap.c @@ -0,0 +1,644 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "str.h" +#include "imap-date.h" +#include "imap-seqset.h" +#include "imap-utf7.h" +#include "imap-util.h" +#include "mail-search-register.h" +#include "mail-search-parser.h" +#include "mail-search-build.h" +#include "mail-search-build.h" +#include "mail-search-mime-build.h" + +struct mail_search_register *mail_search_register_imap; + +static struct mail_search_arg * +imap_search_fallback(struct mail_search_build_context *ctx, + const char *key) +{ + struct mail_search_arg *sarg; + + if (*key == '*' || (*key >= '0' && *key <= '9')) { + /* <message-set> */ + sarg = mail_search_build_new(ctx, SEARCH_SEQSET); + p_array_init(&sarg->value.seqset, ctx->pool, 16); + if (imap_seq_set_parse(key, &sarg->value.seqset) < 0) { + ctx->_error = "Invalid messageset"; + return NULL; + } + return sarg; + } + ctx->_error = p_strconcat(ctx->pool, "Unknown argument ", key, NULL); + return NULL; +} + +static struct mail_search_arg * +imap_search_not(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0) + return NULL; + + sarg->match_not = !sarg->match_not; + return sarg; +} + +static struct mail_search_arg * +imap_search_or(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg, **subargs; + + /* <search-key1> <search-key2> */ + sarg = mail_search_build_new(ctx, SEARCH_OR); + + subargs = &sarg->value.subargs; + do { + if (mail_search_build_key(ctx, sarg, subargs) < 0) + return NULL; + subargs = &(*subargs)->next; + + /* <key> OR <key> OR ... <key> - put them all + under one SEARCH_OR list. */ + } while (mail_search_parse_skip_next(ctx->parser, "OR")); + + if (mail_search_build_key(ctx, sarg, subargs) < 0) + return NULL; + return sarg; +} + +#define CALLBACK_STR(_func, _type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx) \ +{ \ + return mail_search_build_str(ctx, _type); \ +} +static struct mail_search_arg * +imap_search_all(struct mail_search_build_context *ctx) +{ + return mail_search_build_new(ctx, SEARCH_ALL); +} +static struct mail_search_arg * +imap_search_nil(struct mail_search_build_context *ctx) +{ + return mail_search_build_new(ctx, SEARCH_NIL); +} + +static struct mail_search_arg * +imap_search_uid(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + /* <message set> */ + sarg = mail_search_build_str(ctx, SEARCH_UIDSET); + if (sarg == NULL) + return NULL; + + p_array_init(&sarg->value.seqset, ctx->pool, 16); + if (strcmp(sarg->value.str, "$") == 0) { + /* SEARCHRES: delay initialization */ + } else { + if (imap_seq_set_parse(sarg->value.str, + &sarg->value.seqset) < 0) { + ctx->_error = "Invalid UID messageset"; + return NULL; + } + } + return sarg; +} + +#define CALLBACK_FLAG(_func, _flag, _not) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx) \ +{ \ + struct mail_search_arg *sarg; \ + sarg = mail_search_build_new(ctx, SEARCH_FLAGS); \ + sarg->value.flags = _flag; \ + sarg->match_not = _not; \ + return sarg; \ +} +CALLBACK_FLAG(answered, MAIL_ANSWERED, FALSE) +CALLBACK_FLAG(unanswered, MAIL_ANSWERED, TRUE) +CALLBACK_FLAG(deleted, MAIL_DELETED, FALSE) +CALLBACK_FLAG(undeleted, MAIL_DELETED, TRUE) +CALLBACK_FLAG(draft, MAIL_DRAFT, FALSE) +CALLBACK_FLAG(undraft, MAIL_DRAFT, TRUE) +CALLBACK_FLAG(flagged, MAIL_FLAGGED, FALSE) +CALLBACK_FLAG(unflagged, MAIL_FLAGGED, TRUE) +CALLBACK_FLAG(seen, MAIL_SEEN, FALSE) +CALLBACK_FLAG(unseen, MAIL_SEEN, TRUE) +CALLBACK_FLAG(recent, MAIL_RECENT, FALSE) +CALLBACK_FLAG(old, MAIL_RECENT, TRUE) + +static struct mail_search_arg * +imap_search_new(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + /* NEW == (RECENT UNSEEN) */ + sarg = mail_search_build_new(ctx, SEARCH_SUB); + sarg->value.subargs = imap_search_recent(ctx); + sarg->value.subargs->next = imap_search_unseen(ctx); + return sarg; +} + +CALLBACK_STR(keyword, SEARCH_KEYWORDS) + +static struct mail_search_arg * +imap_search_unkeyword(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + sarg = imap_search_keyword(ctx); + if (sarg != NULL) + sarg->match_not = TRUE; + return sarg; +} + +static struct mail_search_arg * +arg_new_date(struct mail_search_build_context *ctx, + enum mail_search_arg_type type, + enum mail_search_date_type date_type) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = mail_search_build_new(ctx, type); + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + if (!imap_parse_date(value, &sarg->value.time)) { + ctx->_error = "Invalid search date parameter"; + return NULL; + } + sarg->value.date_type = date_type; + return sarg; +} + +#define CALLBACK_DATE(_func, _type, _date_type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx) \ +{ \ + return arg_new_date(ctx, _type, _date_type); \ +} +CALLBACK_DATE(before, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_RECEIVED) +CALLBACK_DATE(on, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_RECEIVED) +CALLBACK_DATE(since, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_RECEIVED) + +CALLBACK_DATE(sentbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SENT) +CALLBACK_DATE(senton, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SENT) +CALLBACK_DATE(sentsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SENT) + +CALLBACK_DATE(savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED) +CALLBACK_DATE(savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED) +CALLBACK_DATE(savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED) + +CALLBACK_DATE(x_savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED) +CALLBACK_DATE(x_savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED) +CALLBACK_DATE(x_savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED) + +static struct mail_search_arg * +imap_search_savedatesupported(struct mail_search_build_context *ctx) +{ + return mail_search_build_new(ctx, SEARCH_SAVEDATESUPPORTED); +} + +static struct mail_search_arg * +arg_new_size(struct mail_search_build_context *ctx, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = mail_search_build_new(ctx, type); + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + + if (str_to_uoff(value, &sarg->value.size) < 0) { + ctx->_error = "Invalid search size parameter"; + return NULL; + } + return sarg; +} + +static struct mail_search_arg * +imap_search_larger(struct mail_search_build_context *ctx) +{ + return arg_new_size(ctx, SEARCH_LARGER); +} + +static struct mail_search_arg * +imap_search_smaller(struct mail_search_build_context *ctx) +{ + return arg_new_size(ctx, SEARCH_SMALLER); +} + +static struct mail_search_arg * +arg_new_header(struct mail_search_build_context *ctx, + enum mail_search_arg_type type, const char *hdr_name) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = mail_search_build_new(ctx, type); + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + + if (mail_search_build_get_utf8(ctx, value, &sarg->value.str) < 0) + return NULL; + + sarg->hdr_field_name = p_strdup(ctx->pool, hdr_name); + return sarg; +} + +#define CALLBACK_HDR(_name, _type) \ +static struct mail_search_arg *\ +imap_search_##_name(struct mail_search_build_context *ctx) \ +{ \ + return arg_new_header(ctx, _type, #_name); \ +} +CALLBACK_HDR(bcc, SEARCH_HEADER_ADDRESS) +CALLBACK_HDR(cc, SEARCH_HEADER_ADDRESS) +CALLBACK_HDR(from, SEARCH_HEADER_ADDRESS) +CALLBACK_HDR(to, SEARCH_HEADER_ADDRESS) +CALLBACK_HDR(subject, SEARCH_HEADER_COMPRESS_LWSP) + +static struct mail_search_arg * +imap_search_header(struct mail_search_build_context *ctx) +{ + const char *hdr_name; + + /* <hdr-name> <string> */ + if (mail_search_parse_string(ctx->parser, &hdr_name) < 0) + return NULL; + if (mail_search_build_get_utf8(ctx, hdr_name, &hdr_name) < 0) + return NULL; + + return arg_new_header(ctx, SEARCH_HEADER, t_str_ucase(hdr_name)); +} + +static struct mail_search_arg * +arg_new_body(struct mail_search_build_context *ctx, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + + sarg = mail_search_build_str(ctx, type); + if (sarg == NULL) + return NULL; + + if (mail_search_build_get_utf8(ctx, sarg->value.str, + &sarg->value.str) < 0) + return NULL; + return sarg; +} + +#define CALLBACK_BODY(_func, _type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx) \ +{ \ + return arg_new_body(ctx, _type); \ +} +CALLBACK_BODY(body, SEARCH_BODY) +CALLBACK_BODY(text, SEARCH_TEXT) + +static struct mail_search_arg * +arg_new_interval(struct mail_search_build_context *ctx, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + const char *value; + uint32_t interval; + + sarg = mail_search_build_new(ctx, type); + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + + if (str_to_uint32(value, &interval) < 0 || interval == 0) { + ctx->_error = "Invalid search interval parameter"; + return NULL; + } + sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_UTC_TIMES; + sarg->value.time = ioloop_time - interval; + sarg->value.date_type = MAIL_SEARCH_DATE_TYPE_RECEIVED; + return sarg; +} + +static struct mail_search_arg * +imap_search_older(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + sarg = arg_new_interval(ctx, SEARCH_BEFORE); + if (sarg == NULL) + return NULL; + + /* we need to match also equal, but SEARCH_BEFORE compares with "<" */ + sarg->value.time++; + return sarg; +} + +static struct mail_search_arg * +imap_search_younger(struct mail_search_build_context *ctx) +{ + return arg_new_interval(ctx, SEARCH_SINCE); +} + +static int +arg_modseq_set_type(struct mail_search_build_context *ctx, + struct mail_search_modseq *modseq, const char *name) +{ + if (strcasecmp(name, "all") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_ANY; + else if (strcasecmp(name, "priv") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_PRIVATE; + else if (strcasecmp(name, "shared") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_SHARED; + else { + ctx->_error = "Invalid MODSEQ type"; + return -1; + } + return 0; +} + +static int +arg_modseq_set_ext(struct mail_search_build_context *ctx, + struct mail_search_arg *sarg, const char *name) +{ + const char *value; + + name = t_str_lcase(name); + if (!str_begins(name, "/flags/")) + return 0; + name += 7; + + /* set name */ + if (*name == '\\') { + /* system flag */ + sarg->value.flags = imap_parse_system_flag(name); + if (sarg->value.flags == 0 || + sarg->value.flags == MAIL_RECENT) { + ctx->_error = "Invalid MODSEQ system flag"; + return -1; + } + } else { + sarg->value.str = p_strdup(ctx->pool, name); + } + + /* set type */ + if (mail_search_parse_string(ctx->parser, &value) < 0) + return -1; + if (arg_modseq_set_type(ctx, sarg->value.modseq, value) < 0) + return -1; + return 1; +} + +static struct mail_search_arg * +imap_search_modseq(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + const char *value; + int ret; + + /* [<name> <type>] <modseq> */ + sarg = mail_search_build_new(ctx, SEARCH_MODSEQ); + sarg->value.modseq = p_new(ctx->pool, struct mail_search_modseq, 1); + + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + + if ((ret = arg_modseq_set_ext(ctx, sarg, value)) < 0) + return NULL; + if (ret > 0) { + /* extension data used */ + if (mail_search_parse_string(ctx->parser, &value) < 0) + return NULL; + } + + if (str_to_uint64(value, &sarg->value.modseq->modseq) < 0) { + ctx->_error = "Invalid MODSEQ value"; + return NULL; + } + return sarg; +} + +static struct mail_search_arg * +imap_search_last_result(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + /* SEARCHRES: delay initialization */ + sarg = mail_search_build_new(ctx, SEARCH_UIDSET); + sarg->value.str = "$"; + p_array_init(&sarg->value.seqset, ctx->pool, 16); + return sarg; +} + +static void mail_search_arg_set_fuzzy(struct mail_search_arg *sarg) +{ + for (; sarg != NULL; sarg = sarg->next) { + sarg->fuzzy = TRUE; + switch (sarg->type) { + case SEARCH_OR: + case SEARCH_SUB: + case SEARCH_INTHREAD: + mail_search_arg_set_fuzzy(sarg->value.subargs); + break; + default: + break; + } + } +} + +static struct mail_search_arg * +imap_search_fuzzy(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0) + return NULL; + i_assert(sarg->next == NULL); + + mail_search_arg_set_fuzzy(sarg); + return sarg; +} + +static struct mail_search_arg * +imap_search_mimepart(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + sarg = mail_search_build_new(ctx, SEARCH_MIMEPART); + if (mail_search_mime_build(ctx, &sarg->value.mime_part) < 0) + return NULL; + return sarg; +} + +static struct mail_search_arg * +imap_search_inthread(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + /* <algorithm> <search key> */ + enum mail_thread_type thread_type; + const char *algorithm; + + if (mail_search_parse_string(ctx->parser, &algorithm) < 0) + return NULL; + if (!mail_thread_type_parse(algorithm, &thread_type)) { + ctx->_error = "Unknown thread algorithm"; + return NULL; + } + + sarg = mail_search_build_new(ctx, SEARCH_INTHREAD); + sarg->value.thread_type = thread_type; + if (mail_search_build_key(ctx, sarg, &sarg->value.subargs) < 0) + return NULL; + return sarg; +} + +CALLBACK_STR(x_guid, SEARCH_GUID) + +static struct mail_search_arg * +imap_search_x_mailbox(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + string_t *utf8_name; + + sarg = mail_search_build_str(ctx, SEARCH_MAILBOX_GLOB); + if (sarg == NULL) + return NULL; + + utf8_name = t_str_new(strlen(sarg->value.str)); + if (imap_utf7_to_utf8(sarg->value.str, utf8_name) < 0) { + ctx->_error = "X-MAILBOX name not mUTF-7"; + return NULL; + } + sarg->value.str = p_strdup(ctx->pool, str_c(utf8_name)); + return sarg; +} + +static struct mail_search_arg * +imap_search_x_real_uid(struct mail_search_build_context *ctx) +{ + struct mail_search_arg *sarg; + + /* <message set> */ + sarg = mail_search_build_str(ctx, SEARCH_REAL_UID); + if (sarg == NULL) + return NULL; + + p_array_init(&sarg->value.seqset, ctx->pool, 16); + if (imap_seq_set_parse(sarg->value.str, + &sarg->value.seqset) < 0) { + ctx->_error = "Invalid X-REAL-UID messageset"; + return NULL; + } + return sarg; +} + +static const struct mail_search_register_arg imap_register_args[] = { + /* argument set operations */ + { "NOT", imap_search_not }, + { "OR", imap_search_or }, + { "NIL", imap_search_nil }, + + /* message sets */ + { "ALL", imap_search_all }, + { "UID", imap_search_uid }, + + /* flags */ + { "ANSWERED", imap_search_answered }, + { "UNANSWERED", imap_search_unanswered }, + { "DELETED", imap_search_deleted }, + { "UNDELETED", imap_search_undeleted }, + { "DRAFT", imap_search_draft }, + { "UNDRAFT", imap_search_undraft }, + { "FLAGGED", imap_search_flagged }, + { "UNFLAGGED", imap_search_unflagged }, + { "SEEN", imap_search_seen }, + { "UNSEEN", imap_search_unseen }, + { "RECENT", imap_search_recent }, + { "OLD", imap_search_old }, + { "NEW", imap_search_new }, + + /* keywords */ + { "KEYWORD", imap_search_keyword }, + { "UNKEYWORD", imap_search_unkeyword }, + + /* dates */ + { "BEFORE", imap_search_before }, + { "ON", imap_search_on }, + { "SINCE", imap_search_since }, + { "SENTBEFORE", imap_search_sentbefore }, + { "SENTON", imap_search_senton }, + { "SENTSINCE", imap_search_sentsince }, + { "SAVEDBEFORE", imap_search_savedbefore }, + { "SAVEDON", imap_search_savedon }, + { "SAVEDSINCE", imap_search_savedsince }, + { "SAVEDATESUPPORTED", imap_search_savedatesupported }, + /* FIXME: remove these in v2.4: */ + { "X-SAVEDBEFORE", imap_search_x_savedbefore }, + { "X-SAVEDON", imap_search_x_savedon }, + { "X-SAVEDSINCE", imap_search_x_savedsince }, + + /* sizes */ + { "LARGER", imap_search_larger }, + { "SMALLER", imap_search_smaller }, + + /* headers */ + { "BCC", imap_search_bcc }, + { "CC", imap_search_cc }, + { "FROM", imap_search_from }, + { "TO", imap_search_to }, + { "SUBJECT", imap_search_subject }, + { "HEADER", imap_search_header }, + + /* body */ + { "BODY", imap_search_body }, + { "TEXT", imap_search_text }, + + /* WITHIN extension: */ + { "OLDER", imap_search_older }, + { "YOUNGER", imap_search_younger }, + + /* CONDSTORE extension: */ + { "MODSEQ", imap_search_modseq }, + + /* SEARCHRES extension: */ + { "$", imap_search_last_result }, + + /* FUZZY extension: */ + { "FUZZY", imap_search_fuzzy }, + + /* SEARCH=MIMEPART extension: */ + { "MIMEPART", imap_search_mimepart }, + + /* Other Dovecot extensions: */ + { "INTHREAD", imap_search_inthread }, + { "X-GUID", imap_search_x_guid }, + { "X-MAILBOX", imap_search_x_mailbox }, + { "X-REAL-UID", imap_search_x_real_uid } +}; + +static struct mail_search_register *mail_search_register_init_imap(void) +{ + struct mail_search_register *reg; + + reg = mail_search_register_init(); + mail_search_register_add(reg, imap_register_args, + N_ELEMENTS(imap_register_args)); + mail_search_register_fallback(reg, imap_search_fallback); + return reg; +} + +struct mail_search_register * +mail_search_register_get_imap(void) +{ + if (mail_search_register_imap == NULL) + mail_search_register_imap = mail_search_register_init_imap(); + return mail_search_register_imap; +} |