From 08ef3d5ce57be80c5a74af4da004c81d8420d736 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 8 Aug 2016 15:12:57 +0200 Subject: [PATCH] clean up TextBody, make it slightly more failfast Previously, TextBody supported only 8bit or quoted printable encodings, defaulting to quoted printable if the encoding was not equal to "8bit". This behavior is now changed to throw a runtime exception when an unsupported encoding has been selected. The `setEncoding` method also throws if an unsupported encoding is selected. --- .../k9/mail/internet/MessageExtractor.java | 2 +- .../com/fsck/k9/mail/internet/TextBody.java | 85 ++++++++++--------- .../k9/message/IdentityHeaderBuilder.java | 4 +- .../fsck/k9/message/TextBodyBuilderTest.java | 6 +- 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MessageExtractor.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MessageExtractor.java index cec032b84..d89098505 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MessageExtractor.java @@ -47,7 +47,7 @@ public class MessageExtractor { if ((part != null) && (part.getBody() != null)) { final Body body = part.getBody(); if (body instanceof TextBody) { - return ((TextBody)body).getText(); + return ((TextBody)body).getRawText(); } final String mimeType = part.getMimeType(); if (mimeType != null && MimeUtility.mimeTypeMatches(mimeType, "text/*") || diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/TextBody.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/TextBody.java index dbfd4d736..453c07f8d 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/TextBody.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/TextBody.java @@ -2,6 +2,7 @@ package com.fsck.k9.mail.internet; import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.K9MailLib; import com.fsck.k9.mail.MessagingException; import java.io.ByteArrayInputStream; @@ -10,6 +11,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import android.support.annotation.Nullable; +import android.util.Log; + import com.fsck.k9.mail.filter.CountingOutputStream; import com.fsck.k9.mail.filter.SignSafeOutputStream; @@ -17,102 +21,107 @@ import org.apache.james.mime4j.codec.QuotedPrintableOutputStream; import org.apache.james.mime4j.util.MimeUtil; public class TextBody implements Body, SizeAware { - - /** - * Immutable empty byte array - */ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private final String mBody; - private String mEncoding; - private String mCharset = "UTF-8"; + + private final String text; + private String encoding; + private String charset = "UTF-8"; // Length of the message composed (as opposed to quoted). I don't like the name of this variable and am open to // suggestions as to what it should otherwise be. -achen 20101207 - private Integer mComposedMessageLength; + @Nullable + private Integer composedMessageLength; // Offset from position 0 where the composed message begins. - private Integer mComposedMessageOffset; + @Nullable + private Integer composedMessageOffset; public TextBody(String body) { - this.mBody = body; + this.text = body; } @Override public void writeTo(OutputStream out) throws IOException, MessagingException { - if (mBody != null) { - byte[] bytes = mBody.getBytes(mCharset); - if (MimeUtil.ENC_8BIT.equalsIgnoreCase(mEncoding)) { - out.write(bytes); - } else { + if (text != null) { + byte[] bytes = text.getBytes(charset); + if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) { SignSafeOutputStream signSafeOutputStream = new SignSafeOutputStream(out); QuotedPrintableOutputStream signSafeQuotedPrintableOutputStream = new QuotedPrintableOutputStream(signSafeOutputStream, false); signSafeQuotedPrintableOutputStream.write(bytes); signSafeQuotedPrintableOutputStream.flush(); signSafeOutputStream.flush(); + } else if (MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) { + out.write(bytes); + } else { + throw new IllegalStateException("Cannot get size for encoding!"); } } } - /** - * Get the text of the body in it's unencoded format. - * @return - */ - public String getText() { - return mBody; + public String getRawText() { + return text; } - /** - * Returns an InputStream that reads this body's text. - */ @Override public InputStream getInputStream() throws MessagingException { try { byte[] b; - if (mBody != null) { - b = mBody.getBytes(mCharset); + if (text != null) { + b = text.getBytes(charset); } else { b = EMPTY_BYTE_ARRAY; } return new ByteArrayInputStream(b); } catch (UnsupportedEncodingException uee) { + Log.e(K9MailLib.LOG_TAG, "Unsupported charset: " + charset, uee); return null; } } @Override public void setEncoding(String encoding) { - mEncoding = encoding; + boolean isSupportedEncoding = MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding) || + MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding); + if (!isSupportedEncoding) { + throw new IllegalArgumentException("Cannot encode to " + encoding); + } + + this.encoding = encoding; } public void setCharset(String charset) { - mCharset = charset; + this.charset = charset; } + @Nullable public Integer getComposedMessageLength() { - return mComposedMessageLength; + return composedMessageLength; } - public void setComposedMessageLength(Integer composedMessageLength) { - this.mComposedMessageLength = composedMessageLength; + public void setComposedMessageLength(@Nullable Integer composedMessageLength) { + this.composedMessageLength = composedMessageLength; } + @Nullable public Integer getComposedMessageOffset() { - return mComposedMessageOffset; + return composedMessageOffset; } - public void setComposedMessageOffset(Integer composedMessageOffset) { - this.mComposedMessageOffset = composedMessageOffset; + public void setComposedMessageOffset(@Nullable Integer composedMessageOffset) { + this.composedMessageOffset = composedMessageOffset; } @Override public long getSize() { try { - byte[] bytes = mBody.getBytes(mCharset); + byte[] bytes = text.getBytes(charset); - if (MimeUtil.ENC_8BIT.equalsIgnoreCase(mEncoding)) { + if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) { + return getLengthWhenQuotedPrintableEncoded(bytes); + } else if (MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) { return bytes.length; } else { - return getLengthWhenQuotedPrintableEncoded(bytes); + throw new IllegalStateException("Cannot get size for encoding!"); } } catch (IOException e) { throw new RuntimeException("Couldn't get body size", e); @@ -134,6 +143,6 @@ public class TextBody implements Body, SizeAware { } public String getEncoding() { - return mEncoding; + return encoding; } } diff --git a/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java b/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java index e754dba36..8b587e237 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java +++ b/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java @@ -49,7 +49,7 @@ public class IdentityHeaderBuilder { appendValue(IdentityField.OFFSET, body.getComposedMessageOffset()); } else { // If not, calculate it now. - appendValue(IdentityField.LENGTH, body.getText().length()); + appendValue(IdentityField.LENGTH, body.getRawText().length()); appendValue(IdentityField.OFFSET, 0); } @@ -66,7 +66,7 @@ public class IdentityHeaderBuilder { appendValue(IdentityField.PLAIN_OFFSET, composedMessageOffset); } else { // If not, calculate it now. - appendValue(IdentityField.PLAIN_LENGTH, body.getText().length()); + appendValue(IdentityField.PLAIN_LENGTH, body.getRawText().length()); appendValue(IdentityField.PLAIN_OFFSET, 0); } } diff --git a/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java b/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java index cf6b03fcb..2c1c5d64b 100644 --- a/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java @@ -109,10 +109,10 @@ public class TextBodyBuilderTest { TextBody textBody = textBodyBuilder.buildTextPlain(); assertThat(textBody, instanceOf(TextBody.class)); - assertThat(textBody.getText(), is(expectedText)); + assertThat(textBody.getRawText(), is(expectedText)); assertThat(textBody.getComposedMessageLength(), is(expectedMessageLength)); assertThat(textBody.getComposedMessageOffset(), is(expectedMessagePosition)); - assertThat(textBody.getText().substring(expectedMessagePosition, expectedMessagePosition + expectedMessageLength), + assertThat(textBody.getRawText().substring(expectedMessagePosition, expectedMessagePosition + expectedMessageLength), is("message content")); } @@ -285,7 +285,7 @@ public class TextBodyBuilderTest { TextBody textBody = textBodyBuilder.buildTextHtml(); assertThat(textBody, instanceOf(TextBody.class)); - assertThat(textBody.getText(), is(expectedText)); + assertThat(textBody.getRawText(), is(expectedText)); assertThat(textBody.getComposedMessageLength(), is(expectedMessageLength)); assertThat(textBody.getComposedMessageOffset(), is(expectedMessagePosition)); assertThat(insertableHtmlContent.toDebugString(), is(expectedHtmlContent));