Claus Ibsen created CAMEL-26013:
-----------------------------------
Summary: camel-mail - the body of mails: the text of a multipart
mail, the charset of sent text, and more
Key: CAMEL-26013
URL: https://issues.apache.org/jira/browse/CAMEL-26013
Project: Camel
Issue Type: Bug
Components: camel-mail
Affects Versions: 4.18.0, 4.22.0, 4.23.0
Reporter: Claus Ibsen
Fix For: 4.24.0
Found while checking the body types of the mail component for the catalog
(CAMEL-24961).
h4. 1. After a multipart mail with no text part, every multipart mail converts
to the text MimeMultipart@...
When a mail consumer gives a multipart mail, the body is a
{{jakarta.mail.internet.MimeMultipart}}, and
{{MailConverters.toString(Multipart)}} turns it into the text of its first text
part ({{MailConverters}}, main 56aa5f4970a6, lines 85-112). For a multipart
mail with no text part, for example one with only a PDF attachment, it returns
null. The type converter registry then tries the fallback converters, and the
last one, {{ToStringTypeConverter}}, gives
{{jakarta.mail.internet.MimeMultipart@<hash>}}. The registry stores that
fallback for the pair MimeMultipart to String ({{CoreTypeConverterRegistry}},
lines 455-469). From then on, every multipart mail in the CamelContext converts
to {{MimeMultipart@<hash>}}, also the mails that have a text part. That breaks
{{${body}}}, {{convertBodyTo}}, and an smtp producer that forwards the mail.
Checked on 4.24.0-SNAPSHOT with {{camel run}} and GreenMail, with a pop3
consumer that logs {{${bodyAs(String)}}}:
* a multipart/mixed mail with only a PDF attachment:
{{jakarta.mail.internet.MimeMultipart@785eec1b}};
* then a multipart/mixed mail with a text part and an attachment:
{{jakarta.mail.internet.MimeMultipart@1ad1a775}};
* then a multipart/alternative mail:
{{jakarta.mail.internet.MimeMultipart@28dd82c}}.
In a run without the PDF-only mail, the same two mails give their text ("Hello
with attachment").
*Proposed change*: mark the converter {{@Converter(allowNull = true)}}, so a
null result means no text rather than a miss, and the fallback is not used or
stored.
h4. 2. Text without a charset in contentType is sent in the JVM charset with no
charset in the Content-Type
The mail producer sends the body as text with {{new ByteArrayDataSource(body,
contentType)}} ({{MailBinding}}, main 56aa5f4970a6, lines 273-283, and line 301
for the multipart paths). With the default contentType {{text/plain}}, or any
contentType without a charset, the text is encoded in the JVM default charset,
but the Content-Type of the mail has no charset. A reader then decodes it as
US-ASCII, the MIME default, so any text that is not ASCII arrives garbled.
Checked on 4.24.0-SNAPSHOT with {{camel run}} and GreenMail, sending "Grüße aus
Kopenhagen" with {{smtp}} and reading it with {{pop3}}:
* without a charset: Content-Type {{text/plain}}, and the text arrives garbled,
with ü as "ü" and ß as "Ã" and a control character;
* with the exchange property {{CamelCharsetName=UTF-8}}: Content-Type
{{text/plain; charset=UTF-8}}, and the text arrives as sent.
*Proposed change*: when the contentType has no charset, add the charset the
text was encoded in to the Content-Type (UTF-8, or the charset of the exchange).
h4. 3. A first text part without a content handler, such as text/csv, converts
to the name of a stream
{{MailConverters.toString(Multipart)}} gives the text of the first text part of
a multipart mail. For a text part that Jakarta Mail has no content handler for,
such as {{text/csv}} or {{text/calendar}}, the content is an InputStream, and
the converter returns {{content.toString()}} ({{MailConverters}}, main
56aa5f4970a6, line 101). So the text is the name of the stream. The same
{{toString()}} is used for the first part of a {{Message}} (lines 63-76).
Checked on 4.24.0-SNAPSHOT with {{camel run}} and GreenMail: a multipart/mixed
mail whose first part is {{text/csv}} with {{a,b 1,2}}, read with pop3, gives
{{jakarta.mail.util.SharedByteArrayInputStream@5cb748e4}} for
{{${bodyAs(String)}}}.
*Proposed change*: read a stream content as text, in the charset of the part.
h4. 4. A producer on imap or pop3 is accepted and fails every send with invalid
provider
The mail endpoint serves imap, imaps, pop3, pop3s, smtp and smtps. A consumer
on smtp fails at startup with "Protocol smtp cannot be used for a MailConsumer"
({{MailEndpoint}}, main 56aa5f4970a6, lines 139-144). A producer on imap or
pop3 is created without a check (lines 121-129) and asks Jakarta Mail for a
transport of that protocol on every send ({{DefaultJavaMailSender}}, lines 150
and 178-179), which fails.
Checked on 4.24.0-SNAPSHOT with {{camel run}}: {{to:
imap://localhost:3143?username=survey&password=secret&to=survey@localhost}}
fails each exchange with {{jakarta.mail.NoSuchProviderException: invalid
provider}}.
*Proposed change*: fail when the producer is created on imap, imaps, pop3 or
pop3s, with a message that says to use smtp or smtps, as the consumer does for
smtp.
h4. 5. A mail read with mapMailMessage=false cannot be sent with smtp, as it is
read-only
With {{mapMailMessage=false}}, the body of a mail consumer is the
{{jakarta.mail.Message}} of the store, an IMAPMessage or a POP3Message. An smtp
producer sends a MimeMessage body as it is, and calls {{saveChanges()}} on it
first ({{DefaultJavaMailSender}}, main 56aa5f4970a6, line 159). The messages of
a store are read-only, so the send fails.
Checked on 4.24.0-SNAPSHOT with {{camel run}} and GreenMail: {{from:
pop3://...?mapMailMessage=false}}, then {{to: smtp://...}}:
{{jakarta.mail.IllegalWriteException: POP3 messages are read-only}}.
*Proposed change*: copy a message of a store into a new MimeMessage ({{new
MimeMessage((MimeMessage) message)}}) before it is sent.
h4. 6. Setting the body of a mail message to null gives back the content of the
mail
The body of a {{MailMessage}} is created from the mail when it is first read
({{MailMessage.createBody}}, main 56aa5f4970a6, lines 90-93), and
{{MessageSupport.getBody}} creates it again whenever the body is null. So after
a processor, a bean or a script sets the body of the message to null, the next
read gives the content of the mail again. (The setBody EIP is not affected, as
it replaces the message.)
Checked on 4.24.0-SNAPSHOT with {{camel run}} and GreenMail: after a pop3
consumer, a groovy script {{exchange.getIn().setBody(null)}}, then {{log:
${body}}}, logs the text of the mail.
*Proposed change*: remember that the body was set, as JmsMessage does, so a
null body stays null.
_Claude Code on behalf of davsclaus_
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)