Merge pull request #4980 from k9mail/remove_message_charset
Clean up creating messages
This commit is contained in:
commit
b2e8e4b8b0
9 changed files with 27 additions and 103 deletions
|
@ -157,8 +157,6 @@ public abstract class Message implements Part, Body {
|
||||||
@Override
|
@Override
|
||||||
public abstract void setEncoding(String encoding) throws MessagingException;
|
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||||
|
|
||||||
public abstract void setCharset(String charset) throws MessagingException;
|
|
||||||
|
|
||||||
public long calculateSize() {
|
public long calculateSize() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,6 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.CharsetSupport;
|
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
|
||||||
|
|
||||||
public abstract class Multipart implements Body {
|
public abstract class Multipart implements Body {
|
||||||
private Part mParent;
|
private Part mParent;
|
||||||
|
|
||||||
|
@ -54,18 +51,6 @@ public abstract class Multipart implements Body {
|
||||||
/* Nothing else to do. Each subpart has its own separate encoding */
|
/* Nothing else to do. Each subpart has its own separate encoding */
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCharset(String charset) throws MessagingException {
|
|
||||||
if (mParts.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
BodyPart part = mParts.get(0);
|
|
||||||
Body body = part.getBody();
|
|
||||||
if (body instanceof TextBody) {
|
|
||||||
CharsetSupport.setCharset(charset, part);
|
|
||||||
((TextBody)body).setCharset(charset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract byte[] getPreamble();
|
public abstract byte[] getPreamble();
|
||||||
public abstract byte[] getEpilogue();
|
public abstract byte[] getEpilogue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,19 +30,6 @@ public class CharsetSupport {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public static void setCharset(String charset, Part part) {
|
|
||||||
part.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
|
||||||
part.getMimeType() + ";\r\n charset=" + getExternalCharset(charset));
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getExternalCharset(String charset) {
|
|
||||||
if (JisSupport.isShiftJis(charset)) {
|
|
||||||
return SHIFT_JIS;
|
|
||||||
} else {
|
|
||||||
return charset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String fixupCharset(String charset, Message message) throws MessagingException {
|
static String fixupCharset(String charset, Message message) throws MessagingException {
|
||||||
if (charset == null || "0".equals(charset))
|
if (charset == null || "0".equals(charset))
|
||||||
charset = "US-ASCII"; // No encoding, so use us-ascii, which is the standard.
|
charset = "US-ASCII"; // No encoding, so use us-ascii, which is the standard.
|
||||||
|
|
|
@ -12,9 +12,7 @@ import org.apache.james.mime4j.Charsets;
|
||||||
* as defined in <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a>
|
* as defined in <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a>
|
||||||
* or display-names of an e-mail address, for example.
|
* or display-names of an e-mail address, for example.
|
||||||
*
|
*
|
||||||
* This class is copied from the org.apache.james.mime4j.decoder.EncoderUtil class. It's modified here in order to
|
* This class is copied from the org.apache.james.mime4j.decoder.EncoderUtil class.
|
||||||
* encode emoji characters in the Subject headers. The method to decode emoji depends on the MimeMessage class because
|
|
||||||
* it has to be determined with the sender address.
|
|
||||||
*/
|
*/
|
||||||
class EncoderUtil {
|
class EncoderUtil {
|
||||||
private static final BitSet Q_RESTRICTED_CHARS = initChars("=_?\"#$%&'(),.:;<>@[\\]^`{|}~");
|
private static final BitSet Q_RESTRICTED_CHARS = initChars("=_?\"#$%&'(),.:;<>@[\\]^`{|}~");
|
||||||
|
@ -54,21 +52,15 @@ class EncoderUtil {
|
||||||
*
|
*
|
||||||
* @param text
|
* @param text
|
||||||
* text to encode.
|
* text to encode.
|
||||||
* @param charset
|
|
||||||
* the Java charset that should be used to encode the specified
|
|
||||||
* string into a byte array. A suitable charset is detected
|
|
||||||
* automatically if this parameter is <code>null</code>.
|
|
||||||
* @return the encoded word (or sequence of encoded words if the given text
|
* @return the encoded word (or sequence of encoded words if the given text
|
||||||
* does not fit in a single encoded word).
|
* does not fit in a single encoded word).
|
||||||
*/
|
*/
|
||||||
public static String encodeEncodedWord(String text, Charset charset) {
|
public static String encodeEncodedWord(String text) {
|
||||||
if (text == null)
|
if (text == null)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
if (charset == null)
|
Charset charset = determineCharset(text);
|
||||||
charset = determineCharset(text);
|
String mimeCharset = charset.name();
|
||||||
|
|
||||||
String mimeCharset = CharsetSupport.getExternalCharset(charset.name());
|
|
||||||
|
|
||||||
byte[] bytes = encode(text, charset);
|
byte[] bytes = encode(text, charset);
|
||||||
|
|
||||||
|
@ -164,20 +156,14 @@ class EncoderUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Charset determineCharset(String text) {
|
private static Charset determineCharset(String text) {
|
||||||
// it is an important property of iso-8859-1 that it directly maps
|
|
||||||
// unicode code points 0000 to 00ff to byte values 00 to ff.
|
|
||||||
boolean ascii = true;
|
|
||||||
final int len = text.length();
|
final int len = text.length();
|
||||||
for (int index = 0; index < len; index++) {
|
for (int index = 0; index < len; index++) {
|
||||||
char ch = text.charAt(index);
|
char ch = text.charAt(index);
|
||||||
if (ch > 0xff) {
|
if (ch > 0x7f) {
|
||||||
return Charsets.UTF_8;
|
return Charsets.UTF_8;
|
||||||
}
|
}
|
||||||
if (ch > 0x7f) {
|
|
||||||
ascii = false;
|
|
||||||
}
|
}
|
||||||
}
|
return Charsets.US_ASCII;
|
||||||
return ascii ? Charsets.US_ASCII : Charsets.ISO_8859_1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Encoding determineEncoding(byte[] bytes) {
|
private static Encoding determineEncoding(byte[] bytes) {
|
||||||
|
|
|
@ -4,13 +4,11 @@ import com.fsck.k9.mail.internet.MimeHeader.Field.NameValueField
|
||||||
import com.fsck.k9.mail.internet.MimeHeader.Field.RawField
|
import com.fsck.k9.mail.internet.MimeHeader.Field.RawField
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.LinkedHashSet
|
import java.util.LinkedHashSet
|
||||||
|
|
||||||
class MimeHeader {
|
class MimeHeader {
|
||||||
private val fields: MutableList<Field> = ArrayList()
|
private val fields: MutableList<Field> = ArrayList()
|
||||||
private var charset: String? = null
|
|
||||||
|
|
||||||
val headerNames: Set<String>
|
val headerNames: Set<String>
|
||||||
get() = fields.mapTo(LinkedHashSet()) { it.name }
|
get() = fields.mapTo(LinkedHashSet()) { it.name }
|
||||||
|
@ -76,8 +74,7 @@ class MimeHeader {
|
||||||
private fun Appendable.appendNameValueField(field: Field) {
|
private fun Appendable.appendNameValueField(field: Field) {
|
||||||
val value = field.value
|
val value = field.value
|
||||||
val encodedValue = if (hasToBeEncoded(value)) {
|
val encodedValue = if (hasToBeEncoded(value)) {
|
||||||
val charset = this@MimeHeader.charset?.let { Charset.forName(it) }
|
EncoderUtil.encodeEncodedWord(value)
|
||||||
EncoderUtil.encodeEncodedWord(value, charset)
|
|
||||||
} else {
|
} else {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
@ -92,10 +89,6 @@ class MimeHeader {
|
||||||
return text.any { !it.isVChar() && !it.isWspOrCrlf() }
|
return text.any { !it.isVChar() && !it.isWspOrCrlf() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCharset(charset: String?) {
|
|
||||||
this.charset = charset
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SUBJECT = "Subject"
|
const val SUBJECT = "Subject"
|
||||||
const val HEADER_CONTENT_TYPE = "Content-Type"
|
const val HEADER_CONTENT_TYPE = "Content-Type"
|
||||||
|
|
|
@ -466,17 +466,6 @@ public class MimeMessage extends Message {
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCharset(String charset) throws MessagingException {
|
|
||||||
mHeader.setCharset(charset);
|
|
||||||
if (mBody instanceof Multipart) {
|
|
||||||
((Multipart)mBody).setCharset(charset);
|
|
||||||
} else if (mBody instanceof TextBody) {
|
|
||||||
CharsetSupport.setCharset(charset, this);
|
|
||||||
((TextBody)mBody).setCharset(charset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MimeMessageBuilder implements ContentHandler {
|
private class MimeMessageBuilder implements ContentHandler {
|
||||||
private final LinkedList<Object> stack = new LinkedList<>();
|
private final LinkedList<Object> stack = new LinkedList<>();
|
||||||
private final BodyFactory bodyFactory;
|
private final BodyFactory bodyFactory;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
@ -14,9 +13,10 @@ import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.filter.CountingOutputStream;
|
import com.fsck.k9.mail.filter.CountingOutputStream;
|
||||||
import com.fsck.k9.mail.filter.SignSafeOutputStream;
|
import com.fsck.k9.mail.filter.SignSafeOutputStream;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.Charsets;
|
||||||
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
|
|
||||||
public class TextBody implements Body, SizeAware {
|
public class TextBody implements Body, SizeAware {
|
||||||
|
@ -25,7 +25,6 @@ public class TextBody implements Body, SizeAware {
|
||||||
|
|
||||||
private final String text;
|
private final String text;
|
||||||
private String encoding;
|
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
|
// 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
|
// suggestions as to what it should otherwise be. -achen 20101207
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -41,7 +40,7 @@ public class TextBody implements Body, SizeAware {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
byte[] bytes = text.getBytes(charset);
|
byte[] bytes = text.getBytes(Charsets.UTF_8);
|
||||||
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) {
|
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) {
|
||||||
writeSignSafeQuotedPrintable(out, bytes);
|
writeSignSafeQuotedPrintable(out, bytes);
|
||||||
} else if (MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
} else if (MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
|
@ -58,18 +57,13 @@ public class TextBody implements Body, SizeAware {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws MessagingException {
|
public InputStream getInputStream() throws MessagingException {
|
||||||
try {
|
|
||||||
byte[] b;
|
byte[] b;
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
b = text.getBytes(charset);
|
b = text.getBytes(Charsets.UTF_8);
|
||||||
} else {
|
} else {
|
||||||
b = EMPTY_BYTE_ARRAY;
|
b = EMPTY_BYTE_ARRAY;
|
||||||
}
|
}
|
||||||
return new ByteArrayInputStream(b);
|
return new ByteArrayInputStream(b);
|
||||||
} catch (UnsupportedEncodingException uee) {
|
|
||||||
Timber.e(uee, "Unsupported charset: %s", charset);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -83,10 +77,6 @@ public class TextBody implements Body, SizeAware {
|
||||||
this.encoding = encoding;
|
this.encoding = encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCharset(String charset) {
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Integer getComposedMessageLength() {
|
public Integer getComposedMessageLength() {
|
||||||
return composedMessageLength;
|
return composedMessageLength;
|
||||||
|
@ -108,7 +98,7 @@ public class TextBody implements Body, SizeAware {
|
||||||
@Override
|
@Override
|
||||||
public long getSize() {
|
public long getSize() {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = text.getBytes(charset);
|
byte[] bytes = text.getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) {
|
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) {
|
||||||
return getLengthWhenQuotedPrintableEncoded(bytes);
|
return getLengthWhenQuotedPrintableEncoded(bytes);
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.fsck.k9.mail
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileBody
|
import com.fsck.k9.mail.internet.BinaryTempFileBody
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody
|
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody
|
||||||
import com.fsck.k9.mail.internet.CharsetSupport
|
|
||||||
import com.fsck.k9.mail.internet.MimeBodyPart
|
import com.fsck.k9.mail.internet.MimeBodyPart
|
||||||
import com.fsck.k9.mail.internet.MimeHeader
|
import com.fsck.k9.mail.internet.MimeHeader
|
||||||
import com.fsck.k9.mail.internet.MimeMessage
|
import com.fsck.k9.mail.internet.MimeMessage
|
||||||
|
@ -78,9 +77,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary103
|
------Boundary103
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -108,9 +107,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary102
|
------Boundary102
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -138,9 +137,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary101
|
------Boundary101
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -177,9 +176,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary103
|
------Boundary103
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -207,9 +206,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary102
|
------Boundary102
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -237,9 +236,9 @@ class MessageTest {
|
||||||
Content-Transfer-Encoding: 7bit
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
------Boundary101
|
------Boundary101
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Type: text/plain;
|
Content-Type: text/plain;
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
Testing=2E
|
Testing=2E
|
||||||
This is a text body with some greek characters=2E
|
This is a text body with some greek characters=2E
|
||||||
|
@ -315,13 +314,10 @@ class MessageTest {
|
||||||
End of test.
|
End of test.
|
||||||
|
|
||||||
""".trimIndent().crlf()
|
""".trimIndent().crlf()
|
||||||
).apply {
|
)
|
||||||
setCharset("utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
return MimeBodyPart().apply {
|
return MimeBodyPart().apply {
|
||||||
MimeMessageHelper.setBody(this, textBody)
|
MimeMessageHelper.setBody(this, textBody)
|
||||||
CharsetSupport.setCharset("utf-8", this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ import org.junit.Test
|
||||||
class EncoderUtilTest {
|
class EncoderUtilTest {
|
||||||
@Test
|
@Test
|
||||||
fun singleNonAsciiCharacter() {
|
fun singleNonAsciiCharacter() {
|
||||||
assertInputEncodesToExpected("123456789Ä", "=?ISO-8859-1?Q?123456789=C4?=")
|
assertInputEncodesToExpected("123456789Ä", "=?UTF-8?Q?123456789=C3=84?=")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onlyNonAsciiCharacters() {
|
fun onlyNonAsciiCharacters() {
|
||||||
assertInputEncodesToExpected("ÄÖÜÄÖÜÄÖÜÄ", "=?ISO-8859-1?B?xNbcxNbcxNbcxA==?=")
|
assertInputEncodesToExpected("ÄÖÜÄÖÜÄÖÜÄ", "=?UTF-8?B?w4TDlsOcw4TDlsOcw4TDlsOcw4Q=?=")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -27,7 +27,7 @@ class EncoderUtilTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertInputEncodesToExpected(input: String, expected: String) {
|
private fun assertInputEncodesToExpected(input: String, expected: String) {
|
||||||
val encodedText = EncoderUtil.encodeEncodedWord(input, null)
|
val encodedText = EncoderUtil.encodeEncodedWord(input)
|
||||||
assertEquals(expected, encodedText)
|
assertEquals(expected, encodedText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue