From: Fred Morcos Date: Tue, 18 Feb 2025 21:40:20 +0000 (+0100) Subject: lib-mail: Refactor and improve attachment detection X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bdff009cb37eb51aa7a00e17e1b346df18f95bec;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: Refactor and improve attachment detection This also adds support for detecting Content-Type->name when Content-Disposition->filename is not available. This only works in the case of Content-Disposition: attachment. --- diff --git a/src/lib-mail/message-part-data.c b/src/lib-mail/message-part-data.c index 0eeede4fc8..4fe4f9c6cc 100644 --- a/src/lib-mail/message-part-data.c +++ b/src/lib-mail/message-part-data.c @@ -68,32 +68,88 @@ bool message_part_data_is_plain_7bit(const struct message_part *part) return TRUE; } -bool message_part_data_get_filename(const struct message_part *part, +/* Returns whether the parameter exists. If a value is requested through + value_r, returns it there and return TRUE, otherwise sets *value_r to NULL + and returns FALSE. */ +static bool get_param_value(const char *parameter_name, + const struct message_part_param *params_list, + unsigned int params_list_count, + const char **value_r) +{ + for (unsigned int i = 0; i < params_list_count; i++) { + const struct message_part_param *param = ¶ms_list[i]; + + if (strcasecmp(param->name, parameter_name) == 0) { + if (value_r != NULL) { + *value_r = NULL; + if (*param->value != '\0') { + *value_r = param->value; + } + return *value_r != NULL; + } + return TRUE; + } + } + return FALSE; +} + +static bool message_part_data_find_attachment_filename( + const struct message_part_data *data, + bool default_return, + const char **filename_r) +{ + /* The filename might be under Content-Disposition->filename or under + Content-Type->name */ + if (get_param_value("filename", data->content_disposition_params, + data->content_disposition_params_count, filename_r) || + get_param_value("name", data->content_type_params, + data->content_type_params_count, filename_r)) + return TRUE; + return default_return; +} + +/* Returns whether the message has an attachment in accordance with the provided + settings. If there is an attachment, the filename is returned in filename_r + if it is not NULL. */ +static bool message_part_get_attachment_filename( + const struct message_part *part, + const struct message_part_attachment_settings *settings, const char **filename_r) { const struct message_part_data *data = part->data; - const struct message_part_param *params; - unsigned int params_count, i; i_assert(data != NULL); - params = data->content_disposition_params; - params_count = data->content_disposition_params_count; - - if (data->content_disposition != NULL && - strcasecmp(data->content_disposition, "attachment") != 0) { + /* Reject the message if the Content-Type is excluded. */ + if (settings && settings->content_type_filter != NULL && + !message_part_has_content_types(part, settings->content_type_filter)) return FALSE; - } - for (i = 0; i < params_count; i++) { - if (strcasecmp(params[i].name, "filename") == 0 && - params[i].value != NULL) { - *filename_r = params[i].value; - return TRUE; - } - } + + /* Accept Content-Disposition: attachment, even when it does not have a + filename or Content-Type->name parameter */ + if (null_strcasecmp(data->content_disposition, "attachment") == 0) + return message_part_data_find_attachment_filename(data, TRUE, filename_r); + + /* Accept Content-Disposition: inline only when a filename or + Content-Type->name is available and if inlined attachments are not + excluded */ + if ((!settings || !settings->exclude_inlined) && + null_strcasecmp(data->content_disposition, "inline") == 0) + return message_part_data_find_attachment_filename(data, FALSE, filename_r); + return FALSE; } +bool message_part_data_get_filename(const struct message_part *part, + const char **filename_r) +{ + const struct message_part_data *data = part->data; + + i_assert(data != NULL); + + return message_part_get_attachment_filename(part, NULL, filename_r); +} + /* * Header parsing */ @@ -532,7 +588,7 @@ void message_part_data_parse_from_header(pool_t pool, } } -bool message_part_has_content_types(struct message_part *part, +bool message_part_has_content_types(const struct message_part *part, const char *const *types) { struct message_part_data *data = part->data; @@ -558,42 +614,9 @@ bool message_part_has_content_types(struct message_part *part, return ret; } -bool message_part_has_parameter(struct message_part *part, const char *parameter, - bool has_value) -{ - struct message_part_data *data = part->data; - - i_assert(data != NULL); - - for (unsigned int i = 0; i < data->content_disposition_params_count; i++) { - const struct message_part_param *param = - &data->content_disposition_params[i]; - if (strcasecmp(param->name, parameter) == 0 && - (!has_value || *param->value != '\0')) { - return TRUE; - } - } - return FALSE; -} - -bool message_part_is_attachment(struct message_part *part, - const struct message_part_attachment_settings *set) +bool message_part_is_attachment( + struct message_part *part, + const struct message_part_attachment_settings *settings) { - struct message_part_data *data = part->data; - - i_assert(data != NULL); - - /* see if the content-type is excluded */ - if (set->content_type_filter != NULL && - !message_part_has_content_types(part, set->content_type_filter)) - return FALSE; - - /* accept any attachment, or any inlined attachment with filename, - unless inlined ones are excluded */ - if (null_strcasecmp(data->content_disposition, "attachment") == 0 || - (!set->exclude_inlined && - null_strcasecmp(data->content_disposition, "inline") == 0 && - message_part_has_parameter(part, "filename", FALSE))) - return TRUE; - return FALSE; + return message_part_get_attachment_filename(part, settings, NULL); } diff --git a/src/lib-mail/message-part-data.h b/src/lib-mail/message-part-data.h index 9513bfc2f0..de740419e5 100644 --- a/src/lib-mail/message-part-data.h +++ b/src/lib-mail/message-part-data.h @@ -74,16 +74,13 @@ bool message_part_data_get_filename(const struct message_part *part, const char **filename_r); /* See message_part_attachment_settings */ -bool message_part_has_content_types(struct message_part *part, const char *const *types); - -/* Returns TRUE if message part has given parameter, and has non-empty - value if has_value is TRUE. */ -bool message_part_has_parameter(struct message_part *part, const char *parameter, - bool has_value); +bool message_part_has_content_types(const struct message_part *part, + const char *const *types); /* Check if part is attachment according to given settings */ -bool message_part_is_attachment(struct message_part *part, - const struct message_part_attachment_settings *set); +bool message_part_is_attachment( + struct message_part *part, + const struct message_part_attachment_settings *settings); /* * Header parsing */