Merge pull request #3352 from k9mail/LimitMimeEncodeOnlyFilename
RFC 2184 parameter value encoding & ignored test for wrapping headers
This commit is contained in:
commit
9b00cc431b
2 changed files with 158 additions and 55 deletions
|
@ -226,43 +226,60 @@ public abstract class MessageBuilder {
|
|||
Body body = new TempFileBody(attachment.filename);
|
||||
MimeBodyPart bp = new MimeBodyPart(body);
|
||||
|
||||
/*
|
||||
* Correctly encode the filename here. Otherwise the whole
|
||||
* header value (all parameters at once) will be encoded by
|
||||
* MimeHeader.writeTo().
|
||||
*/
|
||||
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\r\n name=\"%s\"",
|
||||
attachment.contentType,
|
||||
EncoderUtil.encodeIfNecessary(attachment.name,
|
||||
EncoderUtil.Usage.WORD_ENTITY, 7)));
|
||||
|
||||
if (!MimeUtil.isMessage(attachment.contentType)) {
|
||||
bp.setEncoding(MimeUtility.getEncodingforType(attachment.contentType));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Oh the joys of MIME...
|
||||
*
|
||||
* From RFC 2183 (The Content-Disposition Header Field):
|
||||
* "Parameter values longer than 78 characters, or which
|
||||
* contain non-ASCII characters, MUST be encoded as specified
|
||||
* in [RFC 2184]."
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Content-Type: application/x-stuff
|
||||
* title*1*=us-ascii'en'This%20is%20even%20more%20
|
||||
* title*2*=%2A%2A%2Afun%2A%2A%2A%20
|
||||
* title*3="isn't it!"
|
||||
*/
|
||||
bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format(Locale.US,
|
||||
"attachment;\r\n filename=\"%s\";\r\n size=%d",
|
||||
attachment.name, attachment.size));
|
||||
addContentType(bp, attachment.contentType, attachment.name);
|
||||
addContentDisposition(bp, attachment.name, attachment.size);
|
||||
|
||||
mp.addBodyPart(bp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Content-Type is defined in RFC 2045
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Content-Type: text/plain; charset=us-ascii (Plain text)
|
||||
*
|
||||
* TODO: RFC 2231/2184 long parameter encoding
|
||||
* Example:
|
||||
*
|
||||
* Content-Type: application/x-stuff
|
||||
* title*1*=us-ascii'en'This%20is%20even%20more%20
|
||||
* title*2*=%2A%2A%2Afun%2A%2A%2A%20
|
||||
* title*3="isn't it!"
|
||||
*/
|
||||
private void addContentType(MimeBodyPart bp, String contentType, String name) throws MessagingException {
|
||||
/*
|
||||
* Correctly encode the filename here. Otherwise the whole
|
||||
* header value (all parameters at once) will be encoded by
|
||||
* MimeHeader.writeTo().
|
||||
*/
|
||||
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\r\n name=\"%s\"",
|
||||
contentType,
|
||||
EncoderUtil.encodeIfNecessary(name,
|
||||
EncoderUtil.Usage.WORD_ENTITY, 7)));
|
||||
|
||||
if (!MimeUtil.isMessage(contentType)) {
|
||||
bp.setEncoding(MimeUtility.getEncodingforType(contentType));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: RFC 2231/2184 long parameter encoding
|
||||
*
|
||||
* From RFC 2183 (The Content-Disposition Header Field):
|
||||
* "Parameter values longer than 78 characters, or which
|
||||
* contain non-ASCII characters, MUST be encoded as specified
|
||||
* in [RFC 2184]."
|
||||
*
|
||||
*/
|
||||
private void addContentDisposition(MimeBodyPart bp, String name, Long size) {
|
||||
bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format(Locale.US,
|
||||
"attachment;\r\n filename=\"%s\";\r\n size=%d",
|
||||
EncoderUtil.encodeIfNecessary(name, EncoderUtil.Usage.WORD_ENTITY, 7),
|
||||
size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Body that will contain the text of the message. We'll decide where to
|
||||
* include it later. Draft messages are treated somewhat differently in that signatures are not
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
|||
import com.fsck.k9.message.MessageBuilder.Callback;
|
||||
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.robolectric.Robolectric;
|
||||
|
@ -45,23 +46,26 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
|
||||
public class MessageBuilderTest extends RobolectricTest {
|
||||
public static final String TEST_MESSAGE_TEXT = "soviet message\r\ntext ☭";
|
||||
public static final String TEST_ATTACHMENT_TEXT = "text data in attachment";
|
||||
public static final String TEST_SUBJECT = "test_subject";
|
||||
public static final Address TEST_IDENTITY_ADDRESS = new Address("test@example.org", "tester");
|
||||
public static final Address[] TEST_TO = new Address[] {
|
||||
new Address("to1@example.org", "recip 1"), new Address("to2@example.org", "recip 2")
|
||||
private static final String TEST_MESSAGE_TEXT = "soviet message\r\ntext ☭";
|
||||
private static final String TEST_ATTACHMENT_TEXT = "text data in attachment";
|
||||
private static final String TEST_SUBJECT = "test_subject";
|
||||
private static final Address TEST_IDENTITY_ADDRESS = new Address("test@example.org", "tester");
|
||||
private static final Address[] TEST_TO = new Address[] {
|
||||
new Address("to1@example.org", "recip 1"),
|
||||
new Address("to2@example.org", "recip 2")
|
||||
};
|
||||
public static final Address[] TEST_CC = new Address[] { new Address("cc@example.org", "cc recip") };
|
||||
public static final Address[] TEST_BCC = new Address[] { new Address("bcc@example.org", "bcc recip") };
|
||||
public static final String TEST_MESSAGE_ID = "<00000000-0000-007B-0000-0000000000EA@example.org>";
|
||||
public static final Date SENT_DATE = new Date(10000000000L);
|
||||
private static final Address[] TEST_CC = new Address[] {
|
||||
new Address("cc@example.org", "cc recip") };
|
||||
private static final Address[] TEST_BCC = new Address[] {
|
||||
new Address("bcc@example.org", "bcc recip") };
|
||||
private static final String TEST_MESSAGE_ID = "<00000000-0000-007B-0000-0000000000EA@example.org>";
|
||||
private static final Date SENT_DATE = new Date(10000000000L);
|
||||
|
||||
public static final String BOUNDARY_1 = "----boundary1";
|
||||
public static final String BOUNDARY_2 = "----boundary2";
|
||||
public static final String BOUNDARY_3 = "----boundary3";
|
||||
private static final String BOUNDARY_1 = "----boundary1";
|
||||
private static final String BOUNDARY_2 = "----boundary2";
|
||||
private static final String BOUNDARY_3 = "----boundary3";
|
||||
|
||||
public static final String MESSAGE_HEADERS = "" +
|
||||
private static final String MESSAGE_HEADERS = "" +
|
||||
"Date: Sun, 26 Apr 1970 17:46:40 +0000\r\n" +
|
||||
"From: tester <test@example.org>\r\n" +
|
||||
"To: recip 1 <to1@example.org>,recip 2 <to2@example.org>\r\n" +
|
||||
|
@ -73,16 +77,16 @@ public class MessageBuilderTest extends RobolectricTest {
|
|||
"References: references\r\n" +
|
||||
"Message-ID: " + TEST_MESSAGE_ID + "\r\n" +
|
||||
"MIME-Version: 1.0\r\n";
|
||||
|
||||
public static final String MESSAGE_CONTENT = "" +
|
||||
|
||||
private static final String MESSAGE_CONTENT = "" +
|
||||
"Content-Type: text/plain;\r\n" +
|
||||
" charset=utf-8\r\n" +
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||
"\r\n" +
|
||||
"soviet message\r\n" +
|
||||
"text =E2=98=AD";
|
||||
|
||||
public static final String MESSAGE_CONTENT_WITH_ATTACH = "" +
|
||||
|
||||
private static final String MESSAGE_CONTENT_WITH_ATTACH = "" +
|
||||
"Content-Type: multipart/mixed; boundary=\"" + BOUNDARY_1 + "\"\r\n" +
|
||||
"Content-Transfer-Encoding: 7bit\r\n" +
|
||||
"\r\n" +
|
||||
|
@ -104,8 +108,57 @@ public class MessageBuilderTest extends RobolectricTest {
|
|||
"dGV4dCBkYXRhIGluIGF0dGFjaG1lbnQ=\r\n" +
|
||||
"\r\n" +
|
||||
"--" + BOUNDARY_1 + "--\r\n";
|
||||
|
||||
public static final String MESSAGE_CONTENT_WITH_MESSAGE_ATTACH = "" +
|
||||
|
||||
private static final String MESSAGE_CONTENT_WITH_LONG_CONTENT_TYPE =
|
||||
"Content-Type: multipart/mixed; boundary=\"" + BOUNDARY_1 + "\"\r\n" +
|
||||
"Content-Transfer-Encoding: 7bit\r\n" +
|
||||
"\r\n" +
|
||||
"--" + BOUNDARY_1 + "\r\n" +
|
||||
"Content-Type: text/plain;\r\n" +
|
||||
" title*1*=1234567891123456789212345678931234567894123456789\r\n" +
|
||||
" title*2*=5123456789612345678971234567898123456789091234567890;\r\n" +
|
||||
" charset=utf-8\r\n" +
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||
"\r\n" +
|
||||
"soviet message\r\n" +
|
||||
"text =E2=98=AD\r\n" +
|
||||
"--" + BOUNDARY_1 + "\r\n" +
|
||||
"Content-Type: text/plain;\r\n" +
|
||||
" name=\"attach.txt\"\r\n" +
|
||||
"Content-Transfer-Encoding: base64\r\n" +
|
||||
"Content-Disposition: attachment;\r\n" +
|
||||
" filename=\"attach.txt\";\r\n" +
|
||||
" size=23\r\n" +
|
||||
"\r\n" +
|
||||
"dGV4dCBkYXRhIGluIGF0dGFjaG1lbnQ=\r\n" +
|
||||
"\r\n" +
|
||||
"--" + BOUNDARY_1 + "--\r\n";
|
||||
|
||||
private static final String ATTACHMENT_FILENAME_NON_ASCII = "テスト文書.txt";
|
||||
private static final String MESSAGE_CONTENT_WITH_ATTACH_NON_ASCII_FILENAME = "" +
|
||||
"Content-Type: multipart/mixed; boundary=\"" + BOUNDARY_1 + "\"\r\n" +
|
||||
"Content-Transfer-Encoding: 7bit\r\n" +
|
||||
"\r\n" +
|
||||
"--" + BOUNDARY_1 + "\r\n" +
|
||||
"Content-Type: text/plain;\r\n" +
|
||||
" charset=utf-8\r\n" +
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||
"\r\n" +
|
||||
"soviet message\r\n" +
|
||||
"text =E2=98=AD\r\n" +
|
||||
"--" + BOUNDARY_1 + "\r\n" +
|
||||
"Content-Type: text/plain;\r\n" +
|
||||
" name=\"=?UTF-8?B?44OG44K544OI5paH5pu4LnR4dA==?=\"\r\n" +
|
||||
"Content-Transfer-Encoding: base64\r\n" +
|
||||
"Content-Disposition: attachment;\r\n" +
|
||||
" filename=\"=?UTF-8?B?44OG44K544OI5paH5pu4LnR4dA==?=\";\r\n" +
|
||||
" size=23\r\n" +
|
||||
"\r\n" +
|
||||
"dGV4dCBkYXRhIGluIGF0dGFjaG1lbnQ=\r\n" +
|
||||
"\r\n" +
|
||||
"--" + BOUNDARY_1 + "--\r\n";
|
||||
|
||||
private static final String MESSAGE_CONTENT_WITH_MESSAGE_ATTACH = "" +
|
||||
"Content-Type: multipart/mixed; boundary=\"" + BOUNDARY_1 + "\"\r\n" +
|
||||
"Content-Transfer-Encoding: 7bit\r\n" +
|
||||
"\r\n" +
|
||||
|
@ -165,7 +218,8 @@ public class MessageBuilderTest extends RobolectricTest {
|
|||
@Test
|
||||
public void build_withAttachment_shouldSucceed() throws Exception {
|
||||
MessageBuilder messageBuilder = createSimpleMessageBuilder();
|
||||
Attachment attachment = createAttachmentWithContent("text/plain", "attach.txt", TEST_ATTACHMENT_TEXT);
|
||||
Attachment attachment = createAttachmentWithContent(
|
||||
"text/plain", "attach.txt", TEST_ATTACHMENT_TEXT);
|
||||
messageBuilder.setAttachments(Collections.singletonList(attachment));
|
||||
|
||||
messageBuilder.buildAsync(callback);
|
||||
|
@ -174,6 +228,36 @@ public class MessageBuilderTest extends RobolectricTest {
|
|||
assertEquals(MESSAGE_HEADERS + MESSAGE_CONTENT_WITH_ATTACH, getMessageContents(message));
|
||||
}
|
||||
|
||||
@Ignore("RFC2231/2184 not implemented") @Test
|
||||
public void build_withAttachment_longContentType_shouldSucceed() throws Exception {
|
||||
MessageBuilder messageBuilder = createSimpleMessageBuilder();
|
||||
Attachment attachment = createAttachmentWithContent(
|
||||
"text/plain;title=1234567891123456789212345678931234567894123456789" +
|
||||
"5123456789612345678971234567898123456789091234567890",
|
||||
"attach.txt", TEST_ATTACHMENT_TEXT);
|
||||
messageBuilder.setAttachments(Collections.singletonList(attachment));
|
||||
|
||||
messageBuilder.buildAsync(callback);
|
||||
|
||||
MimeMessage message = getMessageFromCallback();
|
||||
assertEquals(MESSAGE_HEADERS + MESSAGE_CONTENT_WITH_LONG_CONTENT_TYPE,
|
||||
getMessageContents(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_withAttachment_nonAscii_shouldSucceed() throws Exception {
|
||||
MessageBuilder messageBuilder = createSimpleMessageBuilder();
|
||||
Attachment attachment = createAttachmentWithContent(
|
||||
"text/plain", ATTACHMENT_FILENAME_NON_ASCII, TEST_ATTACHMENT_TEXT);
|
||||
messageBuilder.setAttachments(Collections.singletonList(attachment));
|
||||
|
||||
messageBuilder.buildAsync(callback);
|
||||
|
||||
MimeMessage message = getMessageFromCallback();
|
||||
assertEquals(MESSAGE_HEADERS + MESSAGE_CONTENT_WITH_ATTACH_NON_ASCII_FILENAME,
|
||||
getMessageContents(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_usingHtmlFormat_shouldUseMultipartAlternativeInCorrectOrder() {
|
||||
MessageBuilder messageBuilder = createHtmlMessageBuilder();
|
||||
|
@ -192,13 +276,15 @@ public class MessageBuilderTest extends RobolectricTest {
|
|||
@Test
|
||||
public void build_withMessageAttachment_shouldAttachAsMessageRfc822() throws Exception {
|
||||
MessageBuilder messageBuilder = createSimpleMessageBuilder();
|
||||
Attachment attachment = createAttachmentWithContent("message/rfc822", "attach.txt", TEST_ATTACHMENT_TEXT);
|
||||
Attachment attachment = createAttachmentWithContent(
|
||||
"message/rfc822", "attach.txt", TEST_ATTACHMENT_TEXT);
|
||||
messageBuilder.setAttachments(Collections.singletonList(attachment));
|
||||
|
||||
messageBuilder.buildAsync(callback);
|
||||
|
||||
MimeMessage message = getMessageFromCallback();
|
||||
assertEquals(MESSAGE_HEADERS + MESSAGE_CONTENT_WITH_MESSAGE_ATTACH, getMessageContents(message));
|
||||
assertEquals(MESSAGE_HEADERS + MESSAGE_CONTENT_WITH_MESSAGE_ATTACH,
|
||||
getMessageContents(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue