diff options
Diffstat (limited to 'src/lib-storage/mail-search-mime.c')
-rw-r--r-- | src/lib-storage/mail-search-mime.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/src/lib-storage/mail-search-mime.c b/src/lib-storage/mail-search-mime.c new file mode 100644 index 0000000..39b1f41 --- /dev/null +++ b/src/lib-storage/mail-search-mime.c @@ -0,0 +1,609 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "utc-offset.h" +#include "imap-date.h" +#include "imap-util.h" +#include "imap-quote.h" +#include "mail-search.h" +#include "mail-search-mime.h" + +/* + * + */ + +static struct mail_search_mime_arg * +mail_search_mime_arg_dup_one(pool_t pool, + const struct mail_search_mime_arg *arg) +{ + struct mail_search_mime_arg *new_arg; + + new_arg = p_new(pool, struct mail_search_mime_arg, 1); + new_arg->type = arg->type; + new_arg->match_not = arg->match_not; + new_arg->match_always = arg->match_always; + new_arg->nonmatch_always = arg->nonmatch_always; + + switch (arg->type) { + case SEARCH_MIME_OR: + case SEARCH_MIME_SUB: + new_arg->value.subargs = + mail_search_mime_arg_dup(pool, arg->value.subargs); + break; + case SEARCH_MIME_SIZE_EQUAL: + case SEARCH_MIME_SIZE_LARGER: + case SEARCH_MIME_SIZE_SMALLER: + new_arg->value.size = arg->value.size; + break; + case SEARCH_MIME_HEADER: + new_arg->field_name = p_strdup(pool, arg->field_name); + /* fall through */ + case SEARCH_MIME_DESCRIPTION: + case SEARCH_MIME_DISPOSITION_TYPE: + case SEARCH_MIME_DISPOSITION_PARAM: + case SEARCH_MIME_ENCODING: + case SEARCH_MIME_ID: + case SEARCH_MIME_LANGUAGE: + case SEARCH_MIME_LOCATION: + case SEARCH_MIME_MD5: + case SEARCH_MIME_TYPE: + case SEARCH_MIME_SUBTYPE: + case SEARCH_MIME_PARAM: + case SEARCH_MIME_BODY: + case SEARCH_MIME_TEXT: + case SEARCH_MIME_CC: + case SEARCH_MIME_BCC: + case SEARCH_MIME_FROM: + case SEARCH_MIME_IN_REPLY_TO: + case SEARCH_MIME_MESSAGE_ID: + case SEARCH_MIME_REPLY_TO: + case SEARCH_MIME_SENDER: + case SEARCH_MIME_SUBJECT: + case SEARCH_MIME_TO: + case SEARCH_MIME_FILENAME_IS: + case SEARCH_MIME_FILENAME_CONTAINS: + case SEARCH_MIME_FILENAME_BEGINS: + case SEARCH_MIME_FILENAME_ENDS: + new_arg->value.str = + p_strdup(pool, arg->value.str); + break; + case SEARCH_MIME_SENTBEFORE: + case SEARCH_MIME_SENTON: + case SEARCH_MIME_SENTSINCE: + new_arg->value.time = arg->value.time; + break; + case SEARCH_MIME_PARENT: + case SEARCH_MIME_CHILD: + if (new_arg->value.subargs != NULL) { + new_arg->value.subargs = + mail_search_mime_arg_dup(pool, arg->value.subargs); + } + break; + case SEARCH_MIME_DEPTH_EQUAL: + case SEARCH_MIME_DEPTH_MIN: + case SEARCH_MIME_DEPTH_MAX: + case SEARCH_MIME_INDEX: + new_arg->value.number = arg->value.number; + break; + } + return new_arg; +} + +struct mail_search_mime_arg * +mail_search_mime_arg_dup(pool_t pool, + const struct mail_search_mime_arg *arg) +{ + struct mail_search_mime_arg *new_arg = NULL, **dest = &new_arg; + + for (; arg != NULL; arg = arg->next) { + *dest = mail_search_mime_arg_dup_one(pool, arg); + dest = &(*dest)->next; + } + return new_arg; +} + +struct mail_search_mime_part * +mail_search_mime_part_dup(pool_t pool, + const struct mail_search_mime_part *mpart) +{ + struct mail_search_mime_part *new_mpart; + + new_mpart = p_new(pool, struct mail_search_mime_part, 1); + new_mpart->simplified = mpart->simplified; + new_mpart->args = mail_search_mime_arg_dup(pool, mpart->args); + return new_mpart; +} + +/* + * + */ + +void mail_search_mime_args_reset(struct mail_search_mime_arg *args, + bool full_reset) +{ + while (args != NULL) { + if (args->type == SEARCH_MIME_OR || args->type == SEARCH_MIME_SUB) + mail_search_mime_args_reset(args->value.subargs, full_reset); + + if (args->match_always) { + if (!full_reset) + args->result = 1; + else { + args->match_always = FALSE; + args->result = -1; + } + } else if (args->nonmatch_always) { + if (!full_reset) + args->result = 0; + else { + args->nonmatch_always = FALSE; + args->result = -1; + } + } else { + args->result = -1; + } + + args = args->next; + } +} + +static void search_mime_arg_foreach(struct mail_search_mime_arg *arg, + mail_search_mime_foreach_callback_t *callback, + void *context) +{ + struct mail_search_mime_arg *subarg; + + if (arg->result != -1) + return; + + if (arg->type == SEARCH_MIME_SUB) { + /* sublist of conditions */ + i_assert(arg->value.subargs != NULL); + + arg->result = 1; + subarg = arg->value.subargs; + while (subarg != NULL) { + if (subarg->result == -1) + search_mime_arg_foreach(subarg, callback, context); + + if (subarg->result == -1) + arg->result = -1; + else if (subarg->result == 0) { + /* didn't match */ + arg->result = 0; + break; + } + + subarg = subarg->next; + } + if (arg->match_not && arg->result != -1) + arg->result = arg->result > 0 ? 0 : 1; + } else if (arg->type == SEARCH_MIME_OR) { + /* OR-list of conditions */ + i_assert(arg->value.subargs != NULL); + + subarg = arg->value.subargs; + arg->result = 0; + while (subarg != NULL) { + if (subarg->result == -1) + search_mime_arg_foreach(subarg, callback, context); + + if (subarg->result == -1) + arg->result = -1; + else if (subarg->result > 0) { + /* matched */ + arg->result = 1; + break; + } + + subarg = subarg->next; + } + if (arg->match_not && arg->result != -1) + arg->result = arg->result > 0 ? 0 : 1; + } else { + /* just a single condition */ + callback(arg, context); + } +} + +#undef mail_search_mime_args_foreach +int mail_search_mime_args_foreach(struct mail_search_mime_arg *args, + mail_search_mime_foreach_callback_t *callback, + void *context) +{ + int result; + + result = 1; + for (; args != NULL; args = args->next) { + search_mime_arg_foreach(args, callback, context); + + if (args->result == 0) { + /* didn't match */ + return 0; + } + + if (args->result == -1) + result = -1; + } + + return result; +} + +/* + * + */ + +bool mail_search_mime_arg_one_equals(const struct mail_search_mime_arg *arg1, + const struct mail_search_mime_arg *arg2) +{ + if (arg1->type != arg2->type || + arg1->match_not != arg2->match_not) + return FALSE; + + switch (arg1->type) { + case SEARCH_MIME_OR: + case SEARCH_MIME_SUB: + return mail_search_mime_arg_equals(arg1->value.subargs, + arg2->value.subargs); + + case SEARCH_MIME_SIZE_EQUAL: + case SEARCH_MIME_SIZE_LARGER: + case SEARCH_MIME_SIZE_SMALLER: + return arg1->value.size == arg2->value.size; + + case SEARCH_MIME_HEADER: + case SEARCH_MIME_DISPOSITION_PARAM: + case SEARCH_MIME_PARAM: + if (strcasecmp(arg1->field_name, arg2->field_name) != 0) + return FALSE; + /* fall through */ + case SEARCH_MIME_DESCRIPTION: + case SEARCH_MIME_DISPOSITION_TYPE: + case SEARCH_MIME_ENCODING: + case SEARCH_MIME_ID: + case SEARCH_MIME_LANGUAGE: + case SEARCH_MIME_LOCATION: + case SEARCH_MIME_MD5: + case SEARCH_MIME_TYPE: + case SEARCH_MIME_SUBTYPE: + case SEARCH_MIME_BODY: + case SEARCH_MIME_TEXT: + case SEARCH_MIME_CC: + case SEARCH_MIME_BCC: + case SEARCH_MIME_FROM: + case SEARCH_MIME_IN_REPLY_TO: + case SEARCH_MIME_MESSAGE_ID: + case SEARCH_MIME_REPLY_TO: + case SEARCH_MIME_SENDER: + case SEARCH_MIME_SUBJECT: + case SEARCH_MIME_TO: + case SEARCH_MIME_FILENAME_IS: + case SEARCH_MIME_FILENAME_CONTAINS: + case SEARCH_MIME_FILENAME_BEGINS: + case SEARCH_MIME_FILENAME_ENDS: + /* don't bother doing case-insensitive comparison. we should support + full i18n case-insensitivity (or the active comparator + in future). */ + return strcmp(arg1->value.str, arg2->value.str) == 0; + + case SEARCH_MIME_SENTBEFORE: + case SEARCH_MIME_SENTON: + case SEARCH_MIME_SENTSINCE: + return arg1->value.time == arg2->value.time; + + case SEARCH_MIME_PARENT: + case SEARCH_MIME_CHILD: + if (arg1->value.subargs == NULL) + return arg2->value.subargs == NULL; + if (arg2->value.subargs == NULL) + return FALSE; + return mail_search_mime_arg_equals(arg1->value.subargs, + arg2->value.subargs); + + case SEARCH_MIME_DEPTH_EQUAL: + case SEARCH_MIME_DEPTH_MIN: + case SEARCH_MIME_DEPTH_MAX: + case SEARCH_MIME_INDEX: + return arg1->value.number == arg2->value.number; + } + i_unreached(); +} + +bool mail_search_mime_arg_equals(const struct mail_search_mime_arg *arg1, + const struct mail_search_mime_arg *arg2) +{ + while (arg1 != NULL && arg2 != NULL) { + if (!mail_search_mime_arg_one_equals(arg1, arg2)) + return FALSE; + arg1 = arg1->next; + arg2 = arg2->next; + } + return arg1 == NULL && arg2 == NULL; +} + +bool mail_search_mime_parts_equal(const struct mail_search_mime_part *mpart1, + const struct mail_search_mime_part *mpart2) +{ + i_assert(mpart1->simplified == mpart2->simplified); + + return mail_search_mime_arg_equals(mpart1->args, mpart2->args); +} + +/* + * + */ + +void mail_search_mime_simplify(struct mail_search_mime_part *mpart) +{ + mpart->simplified = TRUE; + + // FIXME: implement and use +} + +/* + * + */ + +static bool +mail_search_mime_subargs_to_imap(string_t *dest, + const struct mail_search_mime_arg *args, + const char *prefix, const char **error_r) +{ + const struct mail_search_mime_arg *arg; + + if (prefix[0] == '\0') + str_append_c(dest, '('); + for (arg = args; arg != NULL; arg = arg->next) { + if (arg->next != NULL) + str_append(dest, prefix); + if (!mail_search_mime_arg_to_imap(dest, arg, error_r)) + return FALSE; + if (arg->next != NULL) + str_append_c(dest, ' '); + } + if (prefix[0] == '\0') + str_append_c(dest, ')'); + return TRUE; +} + +static bool +mail_search_mime_arg_to_imap_date(string_t *dest, + const struct mail_search_mime_arg *arg) +{ + time_t timestamp = arg->value.time; + const char *str; + struct tm *tm; + int tz_offset; + + tm = localtime(×tamp); + tz_offset = utc_offset(tm, timestamp); + timestamp -= tz_offset * 60; + + if (!imap_to_date(timestamp, &str)) + return FALSE; + str_printfa(dest, " \"%s\"", str); + return TRUE; +} + +bool mail_search_mime_arg_to_imap(string_t *dest, + const struct mail_search_mime_arg *arg, const char **error_r) +{ + if (arg->match_not) + str_append(dest, "NOT "); + switch (arg->type) { + case SEARCH_MIME_OR: + if (!mail_search_mime_subargs_to_imap + (dest, arg->value.subargs, "OR ", error_r)) + return FALSE; + break; + case SEARCH_MIME_SUB: + if (!mail_search_mime_subargs_to_imap + (dest, arg->value.subargs, "", error_r)) + return FALSE; + break; + case SEARCH_MIME_SIZE_EQUAL: + str_printfa(dest, "SIZE %"PRIuUOFF_T, arg->value.size); + break; + case SEARCH_MIME_SIZE_LARGER: + str_printfa(dest, "SIZE LARGER %"PRIuUOFF_T, arg->value.size); + break; + case SEARCH_MIME_SIZE_SMALLER: + str_printfa(dest, "SIZE SMALLER %"PRIuUOFF_T, arg->value.size); + break; + case SEARCH_MIME_DESCRIPTION: + str_append(dest, "DESCRIPTION "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_DISPOSITION_TYPE: + str_append(dest, "DISPOSITION TYPE "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_DISPOSITION_PARAM: + str_append(dest, "DISPOSITION PARAM "); + imap_append_astring(dest, arg->field_name); + str_append_c(dest, ' '); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_ENCODING: + str_append(dest, "ENCODING "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_ID: + str_append(dest, "ID "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_LANGUAGE: + str_append(dest, "LANGUAGE "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_LOCATION: + str_append(dest, "LOCATION "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_MD5: + str_append(dest, "MD5 "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_TYPE: + str_append(dest, "TYPE "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_SUBTYPE: + str_append(dest, "SUBTYPE "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_PARAM: + str_append(dest, "PARAM "); + imap_append_astring(dest, arg->field_name); + str_append_c(dest, ' '); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_HEADER: + str_append(dest, "HEADER "); + imap_append_astring(dest, arg->field_name); + str_append_c(dest, ' '); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_BODY: + str_append(dest, "BODY "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_TEXT: + str_append(dest, "TEXT "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_CC: + str_append(dest, "CC "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_BCC: + str_append(dest, "BCC "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_FROM: + str_append(dest, "FROM "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_IN_REPLY_TO: + str_append(dest, "IN-REPLY-TO "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_MESSAGE_ID: + str_append(dest, "MESSAGE-ID "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_REPLY_TO: + str_append(dest, "REPLY-TO "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_SENDER: + str_append(dest, "SENDER "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_SENTBEFORE: + str_append(dest, "SENTBEFORE"); + if (!mail_search_mime_arg_to_imap_date(dest, arg)) { + *error_r = t_strdup_printf( + "SENTBEFORE can't be written as IMAP MIMEPART key " + "for timestamp %"PRIdTIME_T, arg->value.time); + return FALSE; + } + break; + case SEARCH_MIME_SENTON: + str_append(dest, "SENTON"); + if (!mail_search_mime_arg_to_imap_date(dest, arg)) { + *error_r = t_strdup_printf( + "SENTON can't be written as IMAP MIMEPART key " + "for timestamp %"PRIdTIME_T, arg->value.time); + return FALSE; + } + break; + case SEARCH_MIME_SENTSINCE: + str_append(dest, "SENTSINCE"); + if (!mail_search_mime_arg_to_imap_date(dest, arg)) { + *error_r = t_strdup_printf( + "SENTSINCE can't be written as IMAP MIMEPART key " + "for timestamp %"PRIdTIME_T, arg->value.time); + return FALSE; + } + break; + case SEARCH_MIME_SUBJECT: + str_append(dest, "SUBJECT "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_TO: + str_append(dest, "TO "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_DEPTH_EQUAL: + str_printfa(dest, "DEPTH %u", arg->value.number); + break; + case SEARCH_MIME_DEPTH_MIN: + str_printfa(dest, "DEPTH MIN %u", arg->value.number); + break; + case SEARCH_MIME_DEPTH_MAX: + str_printfa(dest, "DEPTH MAX %u", arg->value.number); + break; + case SEARCH_MIME_INDEX: + str_printfa(dest, "INDEX %u", arg->value.number); + break; + case SEARCH_MIME_PARENT: + str_append(dest, "PARENT "); + if (arg->value.subargs == NULL) + str_append(dest, "EXISTS"); + else if (!mail_search_mime_subargs_to_imap + (dest, arg->value.subargs, "", error_r)) + return FALSE; + break; + case SEARCH_MIME_CHILD: + str_append(dest, "CHILD "); + if (arg->value.subargs == NULL) + str_append(dest, "EXISTS"); + else if (!mail_search_mime_subargs_to_imap + (dest, arg->value.subargs, "", error_r)) + return FALSE; + break; + case SEARCH_MIME_FILENAME_IS: + str_append(dest, "FILENAME IS "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_FILENAME_CONTAINS: + str_append(dest, "FILENAME CONTAINS "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_FILENAME_BEGINS: + str_append(dest, "FILENAME BEGINS "); + imap_append_astring(dest, arg->value.str); + break; + case SEARCH_MIME_FILENAME_ENDS: + str_append(dest, "FILENAME ENDS "); + imap_append_astring(dest, arg->value.str); + break; + } + return TRUE; +} + +bool mail_search_mime_part_to_imap(string_t *dest, + const struct mail_search_mime_part *mpart, const char **error_r) +{ + const struct mail_search_mime_arg *arg; + + i_assert(mpart->args != NULL); + if (mpart->args->next == NULL) { + if (!mail_search_mime_arg_to_imap(dest, mpart->args, error_r)) + return FALSE; + } else { + str_append_c(dest, '('); + for (arg = mpart->args; arg != NULL; arg = arg->next) { + if (!mail_search_mime_arg_to_imap(dest, arg, error_r)) + return FALSE; + if (arg->next != NULL) + str_append_c(dest, ' '); + } + str_append_c(dest, ')'); + } + return TRUE; +} + |