mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-27 11:42:16 -05:00
Merge branch 'pr/374'
Encoding issues
This commit is contained in:
commit
b2013b6f5e
@ -78,10 +78,11 @@ import com.fsck.k9.mail.internet.MimeMessage;
|
|||||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
||||||
|
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentMessageBody;
|
||||||
import com.fsck.k9.view.MessageWebView;
|
import com.fsck.k9.view.MessageWebView;
|
||||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
import org.htmlcleaner.CleanerProperties;
|
import org.htmlcleaner.CleanerProperties;
|
||||||
import org.htmlcleaner.HtmlCleaner;
|
import org.htmlcleaner.HtmlCleaner;
|
||||||
import org.htmlcleaner.SimpleHtmlSerializer;
|
import org.htmlcleaner.SimpleHtmlSerializer;
|
||||||
@ -1471,11 +1472,17 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
|||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
private void addAttachmentsToMessage(final MimeMultipart mp) throws MessagingException {
|
private void addAttachmentsToMessage(final MimeMultipart mp) throws MessagingException {
|
||||||
|
LocalAttachmentBody body;
|
||||||
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
|
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
|
||||||
Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
|
Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
|
||||||
|
String contentType = attachment.contentType;
|
||||||
MimeBodyPart bp = new MimeBodyPart(
|
if (MimeUtil.isMessage(contentType)) {
|
||||||
new LocalStore.LocalAttachmentBody(attachment.uri, getApplication()));
|
body = new LocalAttachmentMessageBody(attachment.uri,
|
||||||
|
getApplication());
|
||||||
|
} else {
|
||||||
|
body = new LocalAttachmentBody(attachment.uri, getApplication());
|
||||||
|
}
|
||||||
|
MimeBodyPart bp = new MimeBodyPart(body);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Correctly encode the filename here. Otherwise the whole
|
* Correctly encode the filename here. Otherwise the whole
|
||||||
@ -1483,11 +1490,11 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
|||||||
* MimeHeader.writeTo().
|
* MimeHeader.writeTo().
|
||||||
*/
|
*/
|
||||||
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"",
|
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"",
|
||||||
attachment.contentType,
|
contentType,
|
||||||
EncoderUtil.encodeIfNecessary(attachment.name,
|
EncoderUtil.encodeIfNecessary(attachment.name,
|
||||||
EncoderUtil.Usage.WORD_ENTITY, 7)));
|
EncoderUtil.Usage.WORD_ENTITY, 7)));
|
||||||
|
|
||||||
bp.addHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
|
bp.setEncoding(MimeUtility.getEncodingforType(contentType));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: Oh the joys of MIME...
|
* TODO: Oh the joys of MIME...
|
||||||
|
@ -5,7 +5,10 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
|
|
||||||
public interface Body {
|
public interface Body {
|
||||||
public InputStream getInputStream() throws MessagingException;
|
public InputStream getInputStream() throws MessagingException;
|
||||||
|
public void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
|
||||||
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,8 @@ public abstract class BodyPart implements Part {
|
|||||||
public void setParent(Multipart parent) {
|
public void setParent(Multipart parent) {
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||||
|
|
||||||
|
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||||
}
|
}
|
||||||
|
29
src/com/fsck/k9/mail/CompositeBody.java
Normal file
29
src/com/fsck/k9/mail/CompositeBody.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CompositeBody is a {@link Body} extension that can contain subparts that
|
||||||
|
* may require recursing through or iterating over when converting the
|
||||||
|
* CompositeBody from 8bit to 7bit encoding. The {@link Part} to which a
|
||||||
|
* CompositeBody belongs is only permitted to use 8bit or 7bit content transfer
|
||||||
|
* encoding for the CompositeBody.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface CompositeBody extends Body {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called just prior to transmission, once the type of transport is known to
|
||||||
|
* be 7bit.
|
||||||
|
* <p>
|
||||||
|
* All subparts that are 8bit and of type {@link CompositeBody} will be
|
||||||
|
* converted to 7bit and recursed. All supbparts that are 8bit but not
|
||||||
|
* of type CompositeBody will be converted to quoted-printable. Bodies with
|
||||||
|
* encodings other than 8bit remain unchanged.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||||
|
|
||||||
|
}
|
@ -15,7 +15,7 @@ import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
|||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
|
|
||||||
|
|
||||||
public abstract class Message implements Part, Body {
|
public abstract class Message implements Part, CompositeBody {
|
||||||
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
|
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
|
||||||
|
|
||||||
private MessageReference mReference = null;
|
private MessageReference mReference = null;
|
||||||
@ -139,10 +139,6 @@ public abstract class Message implements Part, Body {
|
|||||||
|
|
||||||
public abstract void setBody(Body body) throws MessagingException;
|
public abstract void setBody(Body body) throws MessagingException;
|
||||||
|
|
||||||
public boolean isMimeType(String mimeType) throws MessagingException {
|
|
||||||
return getContentType().startsWith(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract long getId();
|
public abstract long getId();
|
||||||
|
|
||||||
public abstract String getPreview();
|
public abstract String getPreview();
|
||||||
@ -240,7 +236,7 @@ public abstract class Message implements Part, Body {
|
|||||||
|
|
||||||
public void destroy() throws MessagingException {}
|
public void destroy() throws MessagingException {}
|
||||||
|
|
||||||
public abstract void setEncoding(String encoding) throws UnavailableStorageException;
|
public abstract void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
|
||||||
|
|
||||||
public abstract void setCharset(String charset) throws MessagingException;
|
public abstract void setCharset(String charset) throws MessagingException;
|
||||||
|
|
||||||
@ -298,4 +294,5 @@ public abstract class Message implements Part, Body {
|
|||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public abstract Message clone();
|
public abstract Message clone();
|
||||||
|
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,12 @@ package com.fsck.k9.mail;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public abstract class Multipart implements Body {
|
public abstract class Multipart implements CompositeBody {
|
||||||
protected Part mParent;
|
protected Part mParent;
|
||||||
|
|
||||||
protected ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
|
protected ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
|
||||||
@ -54,19 +55,14 @@ public abstract class Multipart implements Body {
|
|||||||
this.mParent = parent;
|
this.mParent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEncoding(String encoding) {
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
for (BodyPart part : mParts) {
|
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||||
try {
|
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
Body body = part.getBody();
|
throw new MessagingException(
|
||||||
if (body instanceof TextBody) {
|
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||||
part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
|
||||||
((TextBody)body).setEncoding(encoding);
|
|
||||||
}
|
|
||||||
} catch (MessagingException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Nothing else to do. Each subpart has its own separate encoding */
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCharset(String charset) throws MessagingException {
|
public void setCharset(String charset) throws MessagingException {
|
||||||
|
@ -30,4 +30,17 @@ public interface Part {
|
|||||||
public void setBody(Body body) throws MessagingException;
|
public void setBody(Body body) throws MessagingException;
|
||||||
|
|
||||||
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called just prior to transmission, once the type of transport is known to
|
||||||
|
* be 7bit.
|
||||||
|
* <p>
|
||||||
|
* All bodies that are 8bit will be converted to 7bit and recursed if of
|
||||||
|
* type {@link CompositeBody}, or will be converted to quoted-printable in all other
|
||||||
|
* cases. Bodies with encodings other than 8bit remain unchanged.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import com.fsck.k9.mail.Body;
|
|||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.filter.Base64OutputStream;
|
import com.fsck.k9.mail.filter.Base64OutputStream;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@ -18,10 +20,16 @@ public class BinaryTempFileBody implements Body {
|
|||||||
|
|
||||||
private File mFile;
|
private File mFile;
|
||||||
|
|
||||||
|
String mEncoding = null;
|
||||||
|
|
||||||
public static void setTempDirectory(File tempDirectory) {
|
public static void setTempDirectory(File tempDirectory) {
|
||||||
mTempDirectory = tempDirectory;
|
mTempDirectory = tempDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
public BinaryTempFileBody() {
|
public BinaryTempFileBody() {
|
||||||
if (mTempDirectory == null) {
|
if (mTempDirectory == null) {
|
||||||
throw new
|
throw new
|
||||||
@ -46,11 +54,21 @@ public class BinaryTempFileBody implements Body {
|
|||||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
InputStream in = getInputStream();
|
InputStream in = getInputStream();
|
||||||
try {
|
try {
|
||||||
Base64OutputStream base64Out = new Base64OutputStream(out);
|
boolean closeStream = false;
|
||||||
|
if (MimeUtil.isBase64Encoding(mEncoding)) {
|
||||||
|
out = new Base64OutputStream(out);
|
||||||
|
closeStream = true;
|
||||||
|
} else if (MimeUtil.isQuotedPrintableEncoded(mEncoding)){
|
||||||
|
out = new QuotedPrintableOutputStream(out, false);
|
||||||
|
closeStream = true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
IOUtils.copy(in, base64Out);
|
IOUtils.copy(in, out);
|
||||||
} finally {
|
} finally {
|
||||||
base64Out.close();
|
if (closeStream) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
in.close();
|
in.close();
|
||||||
|
62
src/com/fsck/k9/mail/internet/BinaryTempFileMessageBody.java
Normal file
62
src/com/fsck/k9/mail/internet/BinaryTempFileMessageBody.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link BinaryTempFileBody} extension containing a body of type
|
||||||
|
* message/rfc822. This relates to a BinaryTempFileBody the same way that a
|
||||||
|
* {@link LocalAttachmentMessageBody} relates to a {@link LocalAttachmentBody}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BinaryTempFileMessageBody extends BinaryTempFileBody implements CompositeBody {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||||
|
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
|
throw new MessagingException(
|
||||||
|
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||||
|
}
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
|
InputStream in = getInputStream();
|
||||||
|
try {
|
||||||
|
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(mEncoding)) {
|
||||||
|
/*
|
||||||
|
* If we knew the message was already 7bit clean, then it
|
||||||
|
* could be sent along without processing. But since we
|
||||||
|
* don't know, we recursively parse it.
|
||||||
|
*/
|
||||||
|
MimeMessage message = new MimeMessage(in, true);
|
||||||
|
message.setUsing7bitTransport();
|
||||||
|
message.writeTo(out);
|
||||||
|
} else {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
/*
|
||||||
|
* There's nothing to recurse into here, so there's nothing to do.
|
||||||
|
* The enclosing BodyPart already called setEncoding(MimeUtil.ENC_7BIT). Once
|
||||||
|
* writeTo() is called, the file with the rfc822 body will be opened
|
||||||
|
* for reading and will then be recursed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,17 @@ package com.fsck.k9.mail.internet;
|
|||||||
|
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Multipart;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO this is a close approximation of Message, need to update along with
|
* TODO this is a close approximation of Message, need to update along with
|
||||||
@ -42,7 +47,7 @@ public class MimeBodyPart extends BodyPart {
|
|||||||
mHeader.addHeader(name, value);
|
mHeader.addHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeader(String name, String value) throws MessagingException {
|
public void setHeader(String name, String value) {
|
||||||
mHeader.setHeader(name, value);
|
mHeader.setHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,21 +65,35 @@ public class MimeBodyPart extends BodyPart {
|
|||||||
|
|
||||||
public void setBody(Body body) throws MessagingException {
|
public void setBody(Body body) throws MessagingException {
|
||||||
this.mBody = body;
|
this.mBody = body;
|
||||||
if (body instanceof com.fsck.k9.mail.Multipart) {
|
if (body instanceof Multipart) {
|
||||||
com.fsck.k9.mail.Multipart multipart = ((com.fsck.k9.mail.Multipart)body);
|
Multipart multipart = ((Multipart)body);
|
||||||
multipart.setParent(this);
|
multipart.setParent(this);
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
|
String type = multipart.getContentType();
|
||||||
|
setHeader(MimeHeader.HEADER_CONTENT_TYPE, type);
|
||||||
|
if ("multipart/signed".equalsIgnoreCase(type)) {
|
||||||
|
setEncoding(MimeUtil.ENC_7BIT);
|
||||||
|
} else {
|
||||||
|
setEncoding(MimeUtil.ENC_8BIT);
|
||||||
|
}
|
||||||
} else if (body instanceof TextBody) {
|
} else if (body instanceof TextBody) {
|
||||||
String contentType = String.format("%s;\n charset=utf-8", getMimeType());
|
String contentType = String.format("%s;\r\n charset=utf-8", getMimeType());
|
||||||
String name = MimeUtility.getHeaderParameter(getContentType(), "name");
|
String name = MimeUtility.getHeaderParameter(getContentType(), "name");
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
contentType += String.format(";\n name=\"%s\"", name);
|
contentType += String.format(";\r\n name=\"%s\"", name);
|
||||||
}
|
}
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
|
setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "quoted-printable");
|
setEncoding(MimeUtil.ENC_8BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
if (mBody != null) {
|
||||||
|
mBody.setEncoding(encoding);
|
||||||
|
}
|
||||||
|
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
public String getContentType() throws MessagingException {
|
public String getContentType() throws MessagingException {
|
||||||
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
||||||
return (contentType == null) ? "text/plain" : contentType;
|
return (contentType == null) ? "text/plain" : contentType;
|
||||||
@ -103,7 +122,7 @@ public class MimeBodyPart extends BodyPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMimeType(String mimeType) throws MessagingException {
|
public boolean isMimeType(String mimeType) throws MessagingException {
|
||||||
return getMimeType().equals(mimeType);
|
return getMimeType().equalsIgnoreCase(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
@ -122,4 +141,46 @@ public class MimeBodyPart extends BodyPart {
|
|||||||
mBody.writeTo(out);
|
mBody.writeTo(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
String type = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
||||||
|
/*
|
||||||
|
* We don't trust that a multipart/* will properly have an 8bit encoding
|
||||||
|
* header if any of its subparts are 8bit, so we automatically recurse
|
||||||
|
* (as long as its not multipart/signed).
|
||||||
|
*/
|
||||||
|
if (mBody instanceof CompositeBody
|
||||||
|
&& !"multipart/signed".equalsIgnoreCase(type)) {
|
||||||
|
setEncoding(MimeUtil.ENC_7BIT);
|
||||||
|
// recurse
|
||||||
|
((CompositeBody) mBody).setUsing7bitTransport();
|
||||||
|
} else if (!MimeUtil.ENC_8BIT
|
||||||
|
.equalsIgnoreCase(getFirstHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING))) {
|
||||||
|
return;
|
||||||
|
} else if (type != null
|
||||||
|
&& (type.equalsIgnoreCase("multipart/signed") || type
|
||||||
|
.toLowerCase(Locale.US).startsWith("message/"))) {
|
||||||
|
/*
|
||||||
|
* This shouldn't happen. In any case, it would be wrong to convert
|
||||||
|
* them to some other encoding for 7bit transport.
|
||||||
|
*
|
||||||
|
* RFC 1847 says multipart/signed must be 7bit. It also says their
|
||||||
|
* bodies must be treated as opaque, so we must not change the
|
||||||
|
* encoding.
|
||||||
|
*
|
||||||
|
* We've dealt with (CompositeBody) type message/rfc822 above. Here
|
||||||
|
* we must deal with all other message/* types. RFC 2045 says
|
||||||
|
* message/* can only be 7bit or 8bit. RFC 2046 says unknown
|
||||||
|
* message/* types must be treated as application/octet-stream,
|
||||||
|
* which means we can't recurse into them. It also says that
|
||||||
|
* existing subtypes message/partial and message/external must only
|
||||||
|
* be 7bit, and that future subtypes "should be" 7bit.
|
||||||
|
*/
|
||||||
|
throw new MessagingException(
|
||||||
|
"Unable to convert 8bit body part to 7bit");
|
||||||
|
} else {
|
||||||
|
setEncoding(MimeUtil.ENC_QUOTED_PRINTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,12 @@ import org.apache.james.mime4j.parser.MimeStreamParser;
|
|||||||
import org.apache.james.mime4j.stream.BodyDescriptor;
|
import org.apache.james.mime4j.stream.BodyDescriptor;
|
||||||
import org.apache.james.mime4j.stream.Field;
|
import org.apache.james.mime4j.stream.Field;
|
||||||
import org.apache.james.mime4j.stream.MimeConfig;
|
import org.apache.james.mime4j.stream.MimeConfig;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
@ -69,7 +71,23 @@ public class MimeMessage extends Message {
|
|||||||
parse(in);
|
parse(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given InputStream using Apache Mime4J to build a MimeMessage.
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* @param recurse A boolean indicating to recurse through all nested MimeMessage subparts.
|
||||||
|
* @throws IOException
|
||||||
|
* @throws MessagingException
|
||||||
|
*/
|
||||||
|
public MimeMessage(InputStream in, boolean recurse) throws IOException, MessagingException {
|
||||||
|
parse(in, true);
|
||||||
|
}
|
||||||
|
|
||||||
protected void parse(InputStream in) throws IOException, MessagingException {
|
protected void parse(InputStream in) throws IOException, MessagingException {
|
||||||
|
parse(in, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parse(InputStream in, boolean recurse) throws IOException, MessagingException {
|
||||||
mHeader.clear();
|
mHeader.clear();
|
||||||
mFrom = null;
|
mFrom = null;
|
||||||
mTo = null;
|
mTo = null;
|
||||||
@ -92,6 +110,9 @@ public class MimeMessage extends Message {
|
|||||||
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
|
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
|
||||||
MimeStreamParser parser = new MimeStreamParser(parserConfig);
|
MimeStreamParser parser = new MimeStreamParser(parserConfig);
|
||||||
parser.setContentHandler(new MimeMessageBuilder());
|
parser.setContentHandler(new MimeMessageBuilder());
|
||||||
|
if (recurse) {
|
||||||
|
parser.setRecurse();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
parser.parse(new EOLConvertingInputStream(in));
|
parser.parse(new EOLConvertingInputStream(in));
|
||||||
} catch (MimeException me) {
|
} catch (MimeException me) {
|
||||||
@ -143,7 +164,7 @@ public class MimeMessage extends Message {
|
|||||||
@Override
|
@Override
|
||||||
public String getContentType() throws MessagingException {
|
public String getContentType() throws MessagingException {
|
||||||
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
||||||
return (contentType == null) ? "text/plain" : contentType.toLowerCase(Locale.US);
|
return (contentType == null) ? "text/plain" : contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisposition() throws MessagingException {
|
public String getDisposition() throws MessagingException {
|
||||||
@ -156,6 +177,10 @@ public class MimeMessage extends Message {
|
|||||||
return MimeUtility.getHeaderParameter(getContentType(), null);
|
return MimeUtility.getHeaderParameter(getContentType(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMimeType(String mimeType) throws MessagingException {
|
||||||
|
return getMimeType().equalsIgnoreCase(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return mSize;
|
return mSize;
|
||||||
}
|
}
|
||||||
@ -355,11 +380,17 @@ public class MimeMessage extends Message {
|
|||||||
if (body instanceof Multipart) {
|
if (body instanceof Multipart) {
|
||||||
Multipart multipart = ((Multipart)body);
|
Multipart multipart = ((Multipart)body);
|
||||||
multipart.setParent(this);
|
multipart.setParent(this);
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
|
String type = multipart.getContentType();
|
||||||
|
setHeader(MimeHeader.HEADER_CONTENT_TYPE, type);
|
||||||
|
if ("multipart/signed".equalsIgnoreCase(type)) {
|
||||||
|
setEncoding(MimeUtil.ENC_7BIT);
|
||||||
|
} else {
|
||||||
|
setEncoding(MimeUtil.ENC_8BIT);
|
||||||
|
}
|
||||||
} else if (body instanceof TextBody) {
|
} else if (body instanceof TextBody) {
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8",
|
setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\r\n charset=utf-8",
|
||||||
getMimeType()));
|
getMimeType()));
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "quoted-printable");
|
setEncoding(MimeUtil.ENC_8BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,13 +439,11 @@ public class MimeMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEncoding(String encoding) throws UnavailableStorageException {
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
if (mBody instanceof Multipart) {
|
if (mBody != null) {
|
||||||
((Multipart)mBody).setEncoding(encoding);
|
mBody.setEncoding(encoding);
|
||||||
} else if (mBody instanceof TextBody) {
|
|
||||||
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
|
||||||
((TextBody)mBody).setEncoding(encoding);
|
|
||||||
}
|
}
|
||||||
|
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -487,8 +516,9 @@ public class MimeMessage extends Message {
|
|||||||
|
|
||||||
public void body(BodyDescriptor bd, InputStream in) throws IOException {
|
public void body(BodyDescriptor bd, InputStream in) throws IOException {
|
||||||
expect(Part.class);
|
expect(Part.class);
|
||||||
Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
|
|
||||||
try {
|
try {
|
||||||
|
Body body = MimeUtility.decodeBody(in,
|
||||||
|
bd.getTransferEncoding(), bd.getMimeType());
|
||||||
((Part)stack.peek()).setBody(body);
|
((Part)stack.peek()).setBody(body);
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
throw new Error(me);
|
throw new Error(me);
|
||||||
@ -597,4 +627,47 @@ public class MimeMessage extends Message {
|
|||||||
public boolean hasAttachments() {
|
public boolean hasAttachments() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
String type = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
||||||
|
/*
|
||||||
|
* We don't trust that a multipart/* will properly have an 8bit encoding
|
||||||
|
* header if any of its subparts are 8bit, so we automatically recurse
|
||||||
|
* (as long as its not multipart/signed).
|
||||||
|
*/
|
||||||
|
if (mBody instanceof CompositeBody
|
||||||
|
&& !"multipart/signed".equalsIgnoreCase(type)) {
|
||||||
|
setEncoding(MimeUtil.ENC_7BIT);
|
||||||
|
// recurse
|
||||||
|
((CompositeBody) mBody).setUsing7bitTransport();
|
||||||
|
} else if (!MimeUtil.ENC_8BIT
|
||||||
|
.equalsIgnoreCase(getFirstHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING))) {
|
||||||
|
return;
|
||||||
|
} else if (type != null
|
||||||
|
&& (type.equalsIgnoreCase("multipart/signed") || type
|
||||||
|
.toLowerCase(Locale.US).startsWith("message/"))) {
|
||||||
|
/*
|
||||||
|
* This shouldn't happen. In any case, it would be wrong to convert
|
||||||
|
* them to some other encoding for 7bit transport.
|
||||||
|
*
|
||||||
|
* RFC 1847 says multipart/signed must be 7bit. It also says their
|
||||||
|
* bodies must be treated as opaque, so we must not change the
|
||||||
|
* encoding.
|
||||||
|
*
|
||||||
|
* We've dealt with (CompositeBody) type message/rfc822 above. Here
|
||||||
|
* we must deal with all other message/* types. RFC 2045 says
|
||||||
|
* message/* can only be 7bit or 8bit. RFC 2046 says unknown
|
||||||
|
* message/* types must be treated as application/octet-stream,
|
||||||
|
* which means we can't recurse into them. It also says that
|
||||||
|
* existing subtypes message/partial and message/external must only
|
||||||
|
* be 7bit, and that future subtypes "should be" 7bit.
|
||||||
|
*/
|
||||||
|
throw new MessagingException(
|
||||||
|
"Unable to convert 8bit body part to 7bit");
|
||||||
|
} else {
|
||||||
|
setEncoding(MimeUtil.ENC_QUOTED_PRINTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,4 +99,11 @@ public class MimeMultipart extends Multipart {
|
|||||||
public InputStream getInputStream() throws MessagingException {
|
public InputStream getInputStream() throws MessagingException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
for (BodyPart part : mParts) {
|
||||||
|
part.setUsing7bitTransport();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStrea
|
|||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -1155,23 +1156,31 @@ public class MimeUtility {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes any content transfer encoding from the stream and returns a Body.
|
* Removes any content transfer encoding from the stream and returns a Body.
|
||||||
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public static Body decodeBody(InputStream in, String contentTransferEncoding)
|
public static Body decodeBody(InputStream in,
|
||||||
throws IOException {
|
String contentTransferEncoding, String contentType)
|
||||||
|
throws IOException, MessagingException {
|
||||||
/*
|
/*
|
||||||
* We'll remove any transfer encoding by wrapping the stream.
|
* We'll remove any transfer encoding by wrapping the stream.
|
||||||
*/
|
*/
|
||||||
if (contentTransferEncoding != null) {
|
if (contentTransferEncoding != null) {
|
||||||
contentTransferEncoding =
|
contentTransferEncoding =
|
||||||
MimeUtility.getHeaderParameter(contentTransferEncoding, null);
|
MimeUtility.getHeaderParameter(contentTransferEncoding, null);
|
||||||
if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) {
|
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(contentTransferEncoding)) {
|
||||||
in = new QuotedPrintableInputStream(in);
|
in = new QuotedPrintableInputStream(in);
|
||||||
} else if ("base64".equalsIgnoreCase(contentTransferEncoding)) {
|
} else if (MimeUtil.ENC_BASE64.equalsIgnoreCase(contentTransferEncoding)) {
|
||||||
in = new Base64InputStream(in);
|
in = new Base64InputStream(in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryTempFileBody tempBody = new BinaryTempFileBody();
|
BinaryTempFileBody tempBody;
|
||||||
|
if (MimeUtil.isMessage(contentType)) {
|
||||||
|
tempBody = new BinaryTempFileMessageBody();
|
||||||
|
} else {
|
||||||
|
tempBody = new BinaryTempFileBody();
|
||||||
|
}
|
||||||
|
tempBody.setEncoding(contentTransferEncoding);
|
||||||
OutputStream out = tempBody.getOutputStream();
|
OutputStream out = tempBody.getOutputStream();
|
||||||
try {
|
try {
|
||||||
IOUtils.copy(in, out);
|
IOUtils.copy(in, out);
|
||||||
@ -2160,6 +2169,38 @@ public class MimeUtility {
|
|||||||
return canonicalizeMimeType(mimeType);
|
return canonicalizeMimeType(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a default content-transfer-encoding for use with a given content-type
|
||||||
|
* when adding an unencoded attachment. It's possible that 8bit encodings
|
||||||
|
* may later be converted to 7bit for 7bit transport.
|
||||||
|
* <ul>
|
||||||
|
* <li>null: base64
|
||||||
|
* <li>message/rfc822: 8bit
|
||||||
|
* <li>message/*: 7bit
|
||||||
|
* <li>multipart/signed: 7bit
|
||||||
|
* <li>multipart/*: 8bit
|
||||||
|
* <li>*/*: base64
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* A String representing a MIME content-type
|
||||||
|
* @return A String representing a MIME content-transfer-encoding
|
||||||
|
*/
|
||||||
|
public static String getEncodingforType(String type) {
|
||||||
|
if (type == null) {
|
||||||
|
return (MimeUtil.ENC_BASE64);
|
||||||
|
} else if (MimeUtil.isMessage(type)) {
|
||||||
|
return (MimeUtil.ENC_8BIT);
|
||||||
|
} else if ("multipart/signed".equalsIgnoreCase(type) || type.toLowerCase(Locale.US).startsWith("message/")) {
|
||||||
|
return (MimeUtil.ENC_7BIT);
|
||||||
|
} else if (type.toLowerCase(Locale.US).startsWith("multipart/")) {
|
||||||
|
return (MimeUtil.ENC_8BIT);
|
||||||
|
} else {
|
||||||
|
return (MimeUtil.ENC_BASE64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Message getMessageFromPart(Part part) {
|
private static Message getMessageFromPart(Part part) {
|
||||||
while (part != null) {
|
while (part != null) {
|
||||||
if (part instanceof Message)
|
if (part instanceof Message)
|
||||||
@ -3326,7 +3367,7 @@ public class MimeUtility {
|
|||||||
|
|
||||||
public static void setCharset(String charset, Part part) throws MessagingException {
|
public static void setCharset(String charset, Part part) throws MessagingException {
|
||||||
part.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
part.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
||||||
part.getMimeType() + ";\n charset=" + getExternalCharset(charset));
|
part.getMimeType() + ";\r\n charset=" + getExternalCharset(charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getExternalCharset(String charset) {
|
public static String getExternalCharset(String charset) {
|
||||||
|
@ -7,6 +7,7 @@ import com.fsck.k9.mail.MessagingException;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
public class TextBody implements Body {
|
public class TextBody implements Body {
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ public class TextBody implements Body {
|
|||||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
if (mBody != null) {
|
if (mBody != null) {
|
||||||
byte[] bytes = mBody.getBytes(mCharset);
|
byte[] bytes = mBody.getBytes(mCharset);
|
||||||
if ("8bit".equals(mEncoding)) {
|
if (MimeUtil.ENC_8BIT.equalsIgnoreCase(mEncoding)) {
|
||||||
out.write(bytes);
|
out.write(bytes);
|
||||||
} else {
|
} else {
|
||||||
QuotedPrintableOutputStream qp = new QuotedPrintableOutputStream(out, false);
|
QuotedPrintableOutputStream qp = new QuotedPrintableOutputStream(out, false);
|
||||||
|
@ -1653,9 +1653,12 @@ public class ImapStore extends Store {
|
|||||||
String bodyString = (String)literal;
|
String bodyString = (String)literal;
|
||||||
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
||||||
|
|
||||||
String contentTransferEncoding = part.getHeader(
|
String contentTransferEncoding = part
|
||||||
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
part.setBody(MimeUtility.decodeBody(bodyStream, contentTransferEncoding));
|
String contentType = part
|
||||||
|
.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
||||||
|
part.setBody(MimeUtility.decodeBody(bodyStream,
|
||||||
|
contentTransferEncoding, contentType));
|
||||||
} else {
|
} else {
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
throw new MessagingException("Got FETCH response with bogus parameters");
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
||||||
@ -3547,10 +3550,13 @@ public class ImapStore extends Store {
|
|||||||
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
||||||
//TODO: check for correct UID
|
//TODO: check for correct UID
|
||||||
|
|
||||||
String contentTransferEncoding = mPart.getHeader(
|
String contentTransferEncoding = mPart
|
||||||
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
|
String contentType = mPart
|
||||||
|
.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
|
||||||
|
|
||||||
return MimeUtility.decodeBody(literal, contentTransferEncoding);
|
return MimeUtility.decodeBody(literal, contentTransferEncoding,
|
||||||
|
contentType);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ import java.util.UUID;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@ -51,6 +53,7 @@ import com.fsck.k9.helper.Utility;
|
|||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
import com.fsck.k9.mail.FetchProfile;
|
import com.fsck.k9.mail.FetchProfile;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
@ -1936,6 +1939,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
String contentUri = cursor.getString(5);
|
String contentUri = cursor.getString(5);
|
||||||
String contentId = cursor.getString(6);
|
String contentId = cursor.getString(6);
|
||||||
String contentDisposition = cursor.getString(7);
|
String contentDisposition = cursor.getString(7);
|
||||||
|
String encoding = MimeUtility.getEncodingforType(type);
|
||||||
Body body = null;
|
Body body = null;
|
||||||
|
|
||||||
if (contentDisposition == null) {
|
if (contentDisposition == null) {
|
||||||
@ -1943,11 +1947,19 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (contentUri != null) {
|
if (contentUri != null) {
|
||||||
body = new LocalAttachmentBody(Uri.parse(contentUri), mApplication);
|
if (MimeUtil.isMessage(type)) {
|
||||||
|
body = new LocalAttachmentMessageBody(
|
||||||
|
Uri.parse(contentUri),
|
||||||
|
mApplication);
|
||||||
|
} else {
|
||||||
|
body = new LocalAttachmentBody(
|
||||||
|
Uri.parse(contentUri),
|
||||||
|
mApplication);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MimeBodyPart bp = new LocalAttachmentBodyPart(body, id);
|
MimeBodyPart bp = new LocalAttachmentBodyPart(body, id);
|
||||||
bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
|
bp.setEncoding(encoding);
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
||||||
String.format("%s;\n name=\"%s\"",
|
String.format("%s;\n name=\"%s\"",
|
||||||
@ -2852,7 +2864,13 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
contentUri = AttachmentProvider.getAttachmentUri(
|
contentUri = AttachmentProvider.getAttachmentUri(
|
||||||
mAccount,
|
mAccount,
|
||||||
attachmentId);
|
attachmentId);
|
||||||
attachment.setBody(new LocalAttachmentBody(contentUri, mApplication));
|
if (MimeUtil.isMessage(attachment.getMimeType())) {
|
||||||
|
attachment.setBody(new LocalAttachmentMessageBody(
|
||||||
|
contentUri, mApplication));
|
||||||
|
} else {
|
||||||
|
attachment.setBody(new LocalAttachmentBody(
|
||||||
|
contentUri, mApplication));
|
||||||
|
}
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put("content_uri", contentUri != null ? contentUri.toString() : null);
|
cv.put("content_uri", contentUri != null ? contentUri.toString() : null);
|
||||||
db.update("attachments", cv, "id = ?", new String[]
|
db.update("attachments", cv, "id = ?", new String[]
|
||||||
@ -3988,6 +4006,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||||
private Application mApplication;
|
private Application mApplication;
|
||||||
private Uri mUri;
|
private Uri mUri;
|
||||||
|
protected String mEncoding;
|
||||||
|
|
||||||
public LocalAttachmentBody(Uri uri, Application application) {
|
public LocalAttachmentBody(Uri uri, Application application) {
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
@ -4011,11 +4030,21 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
InputStream in = getInputStream();
|
InputStream in = getInputStream();
|
||||||
try {
|
try {
|
||||||
Base64OutputStream base64Out = new Base64OutputStream(out);
|
boolean closeStream = false;
|
||||||
|
if (MimeUtil.isBase64Encoding(mEncoding)) {
|
||||||
|
out = new Base64OutputStream(out);
|
||||||
|
closeStream = true;
|
||||||
|
} else if (MimeUtil.isQuotedPrintableEncoded(mEncoding)){
|
||||||
|
out = new QuotedPrintableOutputStream(out, false);
|
||||||
|
closeStream = true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
IOUtils.copy(in, base64Out);
|
IOUtils.copy(in, out);
|
||||||
} finally {
|
} finally {
|
||||||
base64Out.close();
|
if (closeStream) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
in.close();
|
in.close();
|
||||||
@ -4025,6 +4054,64 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
public Uri getContentUri() {
|
public Uri getContentUri() {
|
||||||
return mUri;
|
return mUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LocalAttachmentBody} extension containing a message/rfc822 type body
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class LocalAttachmentMessageBody extends LocalAttachmentBody implements CompositeBody {
|
||||||
|
|
||||||
|
public LocalAttachmentMessageBody(Uri uri, Application application) {
|
||||||
|
super(uri, application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException,
|
||||||
|
MessagingException {
|
||||||
|
InputStream in = getInputStream();
|
||||||
|
try {
|
||||||
|
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(mEncoding)) {
|
||||||
|
/*
|
||||||
|
* If we knew the message was already 7bit clean, then it
|
||||||
|
* could be sent along without processing. But since we
|
||||||
|
* don't know, we recursively parse it.
|
||||||
|
*/
|
||||||
|
MimeMessage message = new MimeMessage(in, true);
|
||||||
|
message.setUsing7bitTransport();
|
||||||
|
message.writeTo(out);
|
||||||
|
} else {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
/*
|
||||||
|
* There's nothing to recurse into here, so there's nothing to do.
|
||||||
|
* The enclosing BodyPart already called setEncoding(MimeUtil.ENC_7BIT). Once
|
||||||
|
* writeTo() is called, the file with the rfc822 body will be opened
|
||||||
|
* for reading and will then be recursed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||||
|
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
|
throw new MessagingException(
|
||||||
|
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||||
|
}
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ThreadInfo {
|
static class ThreadInfo {
|
||||||
|
@ -477,7 +477,9 @@ public class SmtpTransport extends Transport {
|
|||||||
close();
|
close();
|
||||||
open();
|
open();
|
||||||
|
|
||||||
message.setEncoding(m8bitEncodingAllowed ? "8bit" : null);
|
if (!m8bitEncodingAllowed) {
|
||||||
|
message.setUsing7bitTransport();
|
||||||
|
}
|
||||||
// If the message has attachments and our server has told us about a limit on
|
// If the message has attachments and our server has told us about a limit on
|
||||||
// the size of messages, count the message's size before sending it
|
// the size of messages, count the message's size before sending it
|
||||||
if (mLargestAcceptableMessage > 0 && ((LocalMessage)message).hasAttachments()) {
|
if (mLargestAcceptableMessage > 0 && ((LocalMessage)message).hasAttachments()) {
|
||||||
@ -490,8 +492,8 @@ public class SmtpTransport extends Transport {
|
|||||||
|
|
||||||
Address[] from = message.getFrom();
|
Address[] from = message.getFrom();
|
||||||
try {
|
try {
|
||||||
//TODO: Add BODY=8BITMIME parameter if appropriate?
|
executeSimpleCommand("MAIL FROM:" + "<" + from[0].getAddress() + ">"
|
||||||
executeSimpleCommand("MAIL FROM:" + "<" + from[0].getAddress() + ">");
|
+ (m8bitEncodingAllowed ? " BODY=8BITMIME" : ""));
|
||||||
for (String address : addresses) {
|
for (String address : addresses) {
|
||||||
executeSimpleCommand("RCPT TO:" + "<" + address + ">");
|
executeSimpleCommand("RCPT TO:" + "<" + address + ">");
|
||||||
}
|
}
|
||||||
|
380
tests/src/com/fsck/k9/mail/MessageTest.java
Normal file
380
tests/src/com/fsck/k9/mail/MessageTest.java
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
|
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||||
|
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody;
|
||||||
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
|
public class MessageTest extends AndroidTestCase {
|
||||||
|
|
||||||
|
private static final String EIGHT_BIT_RESULT =
|
||||||
|
"From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary103\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing.\r\n"
|
||||||
|
+ "This is a text body with some greek characters.\r\n"
|
||||||
|
+ "αβγδεζηθ\r\n"
|
||||||
|
+ "End of test.\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: message/rfc822\r\n"
|
||||||
|
+ "Content-Disposition: attachment\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary102\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing.\r\n"
|
||||||
|
+ "This is a text body with some greek characters.\r\n"
|
||||||
|
+ "αβγδεζηθ\r\n"
|
||||||
|
+ "End of test.\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: message/rfc822\r\n"
|
||||||
|
+ "Content-Disposition: attachment\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary101\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 8bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing.\r\n"
|
||||||
|
+ "This is a text body with some greek characters.\r\n"
|
||||||
|
+ "αβγδεζηθ\r\n"
|
||||||
|
+ "End of test.\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101--\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102--\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103--\r\n";
|
||||||
|
|
||||||
|
private static final String SEVEN_BIT_RESULT =
|
||||||
|
"From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary103\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 7bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: text/plain;\r\n"
|
||||||
|
+ " charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103\r\n"
|
||||||
|
+ "Content-Type: message/rfc822\r\n"
|
||||||
|
+ "Content-Disposition: attachment\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 7bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary102\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 7bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: text/plain; charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: text/plain; charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102\r\n"
|
||||||
|
+ "Content-Type: message/rfc822\r\n"
|
||||||
|
+ "Content-Disposition: attachment\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 7bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "From: from@example.com\r\n"
|
||||||
|
+ "To: to@example.com\r\n"
|
||||||
|
+ "Subject: Test Message\r\n"
|
||||||
|
+ "Date: Wed, 28 Aug 2013 08:51:09 -0400\r\n"
|
||||||
|
+ "MIME-Version: 1.0\r\n"
|
||||||
|
+ "Content-Type: multipart/mixed; boundary=\"----Boundary101\"\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: 7bit\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: text/plain; charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: text/plain; charset=utf-8\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: quoted-printable\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Testing=2E\r\n"
|
||||||
|
+ "This is a text body with some greek characters=2E\r\n"
|
||||||
|
+ "=CE=B1=CE=B2=CE=B3=CE=B4=CE=B5=CE=B6=CE=B7=CE=B8\r\n"
|
||||||
|
+ "End of test=2E\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101\r\n"
|
||||||
|
+ "Content-Type: application/octet-stream\r\n"
|
||||||
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary101--\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary102--\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "------Boundary103--\r\n";
|
||||||
|
|
||||||
|
private int mMimeBoundary;
|
||||||
|
|
||||||
|
public MessageTest() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMessage() throws MessagingException, IOException {
|
||||||
|
MimeMessage message;
|
||||||
|
ByteArrayOutputStream out;
|
||||||
|
|
||||||
|
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir());
|
||||||
|
|
||||||
|
mMimeBoundary = 101;
|
||||||
|
message = nestedMessage(nestedMessage(sampleMessage()));
|
||||||
|
out = new ByteArrayOutputStream();
|
||||||
|
message.writeTo(out);
|
||||||
|
assertEquals(EIGHT_BIT_RESULT, out.toString());
|
||||||
|
|
||||||
|
mMimeBoundary = 101;
|
||||||
|
message = nestedMessage(nestedMessage(sampleMessage()));
|
||||||
|
message.setUsing7bitTransport();
|
||||||
|
out = new ByteArrayOutputStream();
|
||||||
|
message.writeTo(out);
|
||||||
|
assertEquals(SEVEN_BIT_RESULT, out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MimeMessage nestedMessage(MimeMessage subMessage)
|
||||||
|
throws MessagingException, IOException {
|
||||||
|
BinaryTempFileMessageBody tempMessageBody = new BinaryTempFileMessageBody();
|
||||||
|
tempMessageBody.setEncoding(MimeUtil.ENC_8BIT);
|
||||||
|
|
||||||
|
OutputStream out = tempMessageBody.getOutputStream();
|
||||||
|
try {
|
||||||
|
subMessage.writeTo(out);
|
||||||
|
} finally {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeBodyPart bodyPart = new MimeBodyPart(tempMessageBody, "message/rfc822");
|
||||||
|
bodyPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, "attachment");
|
||||||
|
bodyPart.setEncoding(MimeUtil.ENC_8BIT);
|
||||||
|
|
||||||
|
MimeMessage parentMessage = sampleMessage();
|
||||||
|
((Multipart) parentMessage.getBody()).addBodyPart(bodyPart);
|
||||||
|
|
||||||
|
return parentMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MimeMessage sampleMessage() throws MessagingException, IOException {
|
||||||
|
MimeMessage message = new MimeMessage();
|
||||||
|
message.setFrom(new Address("from@example.com"));
|
||||||
|
message.setRecipient(RecipientType.TO, new Address("to@example.com"));
|
||||||
|
message.setSubject("Test Message");
|
||||||
|
message.setHeader("Date", "Wed, 28 Aug 2013 08:51:09 -0400");
|
||||||
|
message.setEncoding(MimeUtil.ENC_8BIT);
|
||||||
|
|
||||||
|
NonRandomMimeMultipartTest multipartBody = new NonRandomMimeMultipartTest();
|
||||||
|
multipartBody.setSubType("mixed");
|
||||||
|
multipartBody.addBodyPart(textBodyPart(MimeUtil.ENC_8BIT));
|
||||||
|
multipartBody.addBodyPart(textBodyPart(MimeUtil.ENC_QUOTED_PRINTABLE));
|
||||||
|
multipartBody.addBodyPart(binaryBodyPart());
|
||||||
|
message.setBody(multipartBody);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MimeBodyPart binaryBodyPart() throws IOException,
|
||||||
|
MessagingException {
|
||||||
|
String encodedTestString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
+ "abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
BinaryTempFileBody tempFileBody = new BinaryTempFileBody();
|
||||||
|
|
||||||
|
InputStream in = new Base64InputStream(new ByteArrayInputStream(
|
||||||
|
encodedTestString.getBytes("UTF-8")));
|
||||||
|
|
||||||
|
OutputStream out = tempFileBody.getOutputStream();
|
||||||
|
try {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
} finally {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeBodyPart bodyPart = new MimeBodyPart(tempFileBody,
|
||||||
|
"application/octet-stream");
|
||||||
|
bodyPart.setEncoding(MimeUtil.ENC_BASE64);
|
||||||
|
|
||||||
|
return bodyPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MimeBodyPart textBodyPart(String encoding)
|
||||||
|
throws MessagingException {
|
||||||
|
TextBody textBody = new TextBody(
|
||||||
|
"Testing.\r\n"
|
||||||
|
+ "This is a text body with some greek characters.\r\n"
|
||||||
|
+ "αβγδεζηθ\r\n"
|
||||||
|
+ "End of test.\r\n");
|
||||||
|
textBody.setCharset("utf-8");
|
||||||
|
MimeBodyPart bodyPart = new MimeBodyPart(textBody, "text/plain");
|
||||||
|
MimeUtility.setCharset("utf-8", bodyPart);
|
||||||
|
bodyPart.setEncoding(encoding);
|
||||||
|
return bodyPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NonRandomMimeMultipartTest extends MimeMultipart {
|
||||||
|
|
||||||
|
public NonRandomMimeMultipartTest() throws MessagingException {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateBoundary() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("----Boundary");
|
||||||
|
sb.append(Integer.toString(mMimeBoundary++));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user