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)

Reply via email to