bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
042a58438dc324c6d4de18e4c8a68044d6328e66Stephan Boschconst char *message_part_envelope_headers[] = {
042a58438dc324c6d4de18e4c8a68044d6328e66Stephan Bosch "Date", "Subject", "From", "Sender", "Reply-To",
042a58438dc324c6d4de18e4c8a68044d6328e66Stephan Bosch "To", "Cc", "Bcc", "In-Reply-To", "Message-ID",
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschbool message_part_data_is_plain_7bit(const struct message_part *part)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const struct message_part_data *data = part->data;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* if content-type is text/xxx we don't have to check any
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch multipart stuff */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if ((part->flags & MESSAGE_PART_FLAG_TEXT) == 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (part->next != NULL || part->children != NULL)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch return FALSE; /* shouldn't happen normally.. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* must be text/plain */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch strcasecmp(data->content_subtype, "plain") != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* only allowed parameter is charset=us-ascii, which is also default */
89bc31f70e51f4a69e0444c4f893831f6e96456eTimo Sirainen /* charset defaults to us-ascii */
89bc31f70e51f4a69e0444c4f893831f6e96456eTimo Sirainen } else if (data->content_type_params_count != 1 ||
89bc31f70e51f4a69e0444c4f893831f6e96456eTimo Sirainen strcasecmp(data->content_type_params[0].name, "charset") != 0 ||
89bc31f70e51f4a69e0444c4f893831f6e96456eTimo Sirainen strcasecmp(data->content_type_params[0].value,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (data->content_transfer_encoding != NULL &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch strcasecmp(data->content_transfer_encoding, "7bit") != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* BODYSTRUCTURE checks: */
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Boschbool message_part_data_get_filename(const struct message_part *part,
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Bosch const struct message_part_data *data = part->data;
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Bosch params_count = data->content_disposition_params_count;
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Bosch strcasecmp(data->content_disposition, "attachment") != 0) {
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Bosch for (i = 0; i < params_count; i++) {
6407243c0b7340dd7b53b96bcb8e6b0e1acdb104Stephan Bosch if (strcasecmp(params[i].name, "filename") == 0 &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch * Header parsing
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch/* Message part envelope */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschvoid message_part_envelope_parse_from_header(pool_t pool,
b674bd911aaab7e8b1a77c106a0b5bccb603439fStephan Bosch *data = p_new(pool, struct message_part_envelope, 1);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* wait for full value */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch *addr_p = message_address_parse(pool, hdr->full_value,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch/* Message part data */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_mime_parameters(struct rfc822_parser_context *parser,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch pool_t pool, const struct message_part_param **params_r,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch params = p_new(pool, struct message_part_param, params_count);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch for (i = 0; i < params_count; i++) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch params[i].name = p_strdup(pool, results[i*2+0]);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch params[i].value = p_strdup(pool, results[i*2+1]);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_content_type(struct message_part_data *data,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch unsigned int i;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch ret = rfc822_parse_content_type(&parser, str);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* Save content type and subtype */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch data->content_subtype = p_strdup(pool, value + i+1);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch data->content_type = p_strdup(pool, str_c(str));
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* Content-Type is broken, but we wanted to get it as well as
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch we could. Don't try to read the parameters anymore though.
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch We don't completely ignore a broken Content-Type, because
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch then it would be written as text/plain. This would cause a
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch mismatch with the message_part's MESSAGE_PART_FLAG_TEXT. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_content_transfer_encoding(struct message_part_data *data,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (rfc822_parse_mime_token(&parser, str) >= 0 &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch rfc822_skip_lwsp(&parser) == 0 && str_len(str) > 0) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_content_disposition(struct message_part_data *data,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (rfc822_parse_mime_token(&parser, str) < 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch data->content_disposition = p_strdup(pool, str_c(str));
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_content_language(struct message_part_data *data,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch pool_t pool, const unsigned char *value, size_t value_len)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* Language-Header = "Content-Language" ":" 1#Language-tag
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch Language-Tag = Primary-tag *( "-" Subtag )
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch Primary-tag = 1*8ALPHA
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch Subtag = 1*8ALPHA */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch rfc822_parser_init(&parser, value, value_len, NULL);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch while (rfc822_parse_atom(&parser, str) >= 0) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const char *lang = p_strdup(pool, str_c(str));
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (parser.data == parser.end || *parser.data != ',')
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschparse_content_header(struct message_part_data *data,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const char *name = hdr->name + strlen("Content-");
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch value = t_strndup(hdr->full_value, hdr->full_value_len);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (strcasecmp(name, "ID") == 0 && data->content_id == NULL)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (strcasecmp(name, "MD5") == 0 && data->content_md5 == NULL)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (strcasecmp(name, "Type") == 0 && data->content_type == NULL)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch else if (strcasecmp(name, "Transfer-Encoding") == 0 &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch parse_content_transfer_encoding(data, pool, hdr);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch } else if (strcasecmp(name, "Location") == 0 &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch data->content_location = p_strdup(pool, value);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch data->content_description = p_strdup(pool, value);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch else if (strcasecmp(name, "Disposition") == 0 &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschvoid message_part_data_parse_from_header(pool_t pool,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* no Content-* headers. add an empty context
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch structure anyway. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch } else if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* If there was no Mime-Version, forget all
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch the Content-stuff */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* initialize message part data */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (strncasecmp(hdr->name, "Content-", 8) == 0) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* message/rfc822, we need the envelope */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch message_part_envelope_parse_from_header(pool, &part_data->envelope, hdr);
bc41d811809ca144922697333b78a857b97546c9Aki Tuomibool message_part_has_content_types(struct message_part *part,
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi const char *const *types)
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi content_type = t_strdup_printf("%s/", data->content_type);
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi content_type = t_strdup_printf("%s/%s", data->content_type,
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi if (wildcard_match_icase(content_type, (*ptr)+(exclude?1:0)))
bc41d811809ca144922697333b78a857b97546c9Aki Tuomibool message_part_has_parameter(struct message_part *part, const char *parameter,
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi for (unsigned int i = 0; i < data->content_disposition_params_count; i++) {
bc41d811809ca144922697333b78a857b97546c9Aki Tuomibool message_part_is_attachment(struct message_part *part,
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi const struct message_part_attachment_settings *set)
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi /* see if the content-type is excluded */
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi !message_part_has_content_types(part, set->content_type_filter))
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi /* accept any attachment, or any inlined attachment with filename,
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi unless inlined ones are excluded */
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi if (null_strcasecmp(data->content_disposition, "attachment") == 0 ||
bc41d811809ca144922697333b78a857b97546c9Aki Tuomi null_strcasecmp(data->content_disposition, "inline") == 0 &&