Merge branch 'pr/374'

Encoding issues
This commit is contained in:
cketti 2013-09-06 22:21:15 +02:00
commit b2013b6f5e
18 changed files with 857 additions and 70 deletions

View File

@ -78,10 +78,11 @@ 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;
import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentMessageBody;
import com.fsck.k9.view.MessageWebView;
import org.apache.james.mime4j.codec.EncoderUtil;
import org.apache.james.mime4j.util.MimeUtil;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleHtmlSerializer;
@ -1471,11 +1472,17 @@ public class MessageCompose extends K9Activity implements OnClickListener {
* @throws MessagingException
*/
private void addAttachmentsToMessage(final MimeMultipart mp) throws MessagingException {
LocalAttachmentBody body;
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
MimeBodyPart bp = new MimeBodyPart(
new LocalStore.LocalAttachmentBody(attachment.uri, getApplication()));
String contentType = attachment.contentType;
if (MimeUtil.isMessage(contentType)) {
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
@ -1483,11 +1490,11 @@ public class MessageCompose extends K9Activity implements OnClickListener {
* MimeHeader.writeTo().
*/
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"",
attachment.contentType,
contentType,
EncoderUtil.encodeIfNecessary(attachment.name,
EncoderUtil.Usage.WORD_ENTITY, 7)));
bp.addHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
bp.setEncoding(MimeUtility.getEncodingforType(contentType));
/*
* TODO: Oh the joys of MIME...

View File

@ -5,7 +5,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.fsck.k9.mail.store.UnavailableStorageException;
public interface Body {
public InputStream getInputStream() throws MessagingException;
public void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
public void writeTo(OutputStream out) throws IOException, MessagingException;
}

View File

@ -11,4 +11,8 @@ public abstract class BodyPart implements Part {
public void setParent(Multipart parent) {
mParent = parent;
}
public abstract void setEncoding(String encoding) throws MessagingException;
public abstract void setUsing7bitTransport() throws MessagingException;
}

View 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;
}

View File

@ -15,7 +15,7 @@ import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
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 MessageReference mReference = null;
@ -139,10 +139,6 @@ public abstract class Message implements Part, Body {
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 String getPreview();
@ -240,7 +236,7 @@ public abstract class Message implements Part, Body {
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;
@ -298,4 +294,5 @@ public abstract class Message implements Part, Body {
* </p>
*/
public abstract Message clone();
public abstract void setUsing7bitTransport() throws MessagingException;
}

View File

@ -3,11 +3,12 @@ package com.fsck.k9.mail;
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.TextBody;
public abstract class Multipart implements Body {
public abstract class Multipart implements CompositeBody {
protected Part mParent;
protected ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
@ -54,19 +55,14 @@ public abstract class Multipart implements Body {
this.mParent = parent;
}
public void setEncoding(String encoding) {
for (BodyPart part : mParts) {
try {
Body body = part.getBody();
if (body instanceof TextBody) {
part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
((TextBody)body).setEncoding(encoding);
}
} catch (MessagingException e) {
// Ignore
}
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");
}
/* Nothing else to do. Each subpart has its own separate encoding */
}
public void setCharset(String charset) throws MessagingException {

View File

@ -30,4 +30,17 @@ public interface Part {
public void setBody(Body body) throws 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;
}

View File

@ -4,6 +4,8 @@ import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.filter.Base64OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
import org.apache.james.mime4j.util.MimeUtil;
import java.io.*;
@ -18,10 +20,16 @@ public class BinaryTempFileBody implements Body {
private File mFile;
String mEncoding = null;
public static void setTempDirectory(File tempDirectory) {
mTempDirectory = tempDirectory;
}
public void setEncoding(String encoding) throws MessagingException {
mEncoding = encoding;
}
public BinaryTempFileBody() {
if (mTempDirectory == null) {
throw new
@ -46,11 +54,21 @@ public class BinaryTempFileBody implements Body {
public void writeTo(OutputStream out) throws IOException, MessagingException {
InputStream in = getInputStream();
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 {
IOUtils.copy(in, base64Out);
IOUtils.copy(in, out);
} finally {
base64Out.close();
if (closeStream) {
out.close();
}
}
} finally {
in.close();

View 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.
*/
}
}

View File

@ -3,12 +3,17 @@ package com.fsck.k9.mail.internet;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.CompositeBody;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
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
@ -42,7 +47,7 @@ public class MimeBodyPart extends BodyPart {
mHeader.addHeader(name, value);
}
public void setHeader(String name, String value) throws MessagingException {
public void setHeader(String name, String value) {
mHeader.setHeader(name, value);
}
@ -60,21 +65,35 @@ public class MimeBodyPart extends BodyPart {
public void setBody(Body body) throws MessagingException {
this.mBody = body;
if (body instanceof com.fsck.k9.mail.Multipart) {
com.fsck.k9.mail.Multipart multipart = ((com.fsck.k9.mail.Multipart)body);
if (body instanceof Multipart) {
Multipart multipart = ((Multipart)body);
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) {
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");
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_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 {
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
return (contentType == null) ? "text/plain" : contentType;
@ -103,7 +122,7 @@ public class MimeBodyPart extends BodyPart {
}
public boolean isMimeType(String mimeType) throws MessagingException {
return getMimeType().equals(mimeType);
return getMimeType().equalsIgnoreCase(mimeType);
}
public int getSize() {
@ -122,4 +141,46 @@ public class MimeBodyPart extends BodyPart {
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);
}
}
}

View File

@ -22,10 +22,12 @@ import org.apache.james.mime4j.parser.MimeStreamParser;
import org.apache.james.mime4j.stream.BodyDescriptor;
import org.apache.james.mime4j.stream.Field;
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.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.CompositeBody;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
@ -69,7 +71,23 @@ public class MimeMessage extends Message {
parse(in);
}
protected void parse(InputStream in) throws IOException, MessagingException {
/**
* 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 {
parse(in, false);
}
protected void parse(InputStream in, boolean recurse) throws IOException, MessagingException {
mHeader.clear();
mFrom = null;
mTo = null;
@ -92,6 +110,9 @@ public class MimeMessage extends Message {
parserConfig.setMaxHeaderCount(-1); // Disable the check for header count.
MimeStreamParser parser = new MimeStreamParser(parserConfig);
parser.setContentHandler(new MimeMessageBuilder());
if (recurse) {
parser.setRecurse();
}
try {
parser.parse(new EOLConvertingInputStream(in));
} catch (MimeException me) {
@ -143,7 +164,7 @@ public class MimeMessage extends Message {
@Override
public String getContentType() throws MessagingException {
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 {
@ -156,6 +177,10 @@ public class MimeMessage extends Message {
return MimeUtility.getHeaderParameter(getContentType(), null);
}
public boolean isMimeType(String mimeType) throws MessagingException {
return getMimeType().equalsIgnoreCase(mimeType);
}
public int getSize() {
return mSize;
}
@ -355,11 +380,17 @@ public class MimeMessage extends Message {
if (body instanceof Multipart) {
Multipart multipart = ((Multipart)body);
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) {
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()));
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "quoted-printable");
setEncoding(MimeUtil.ENC_8BIT);
}
}
@ -408,13 +439,11 @@ public class MimeMessage extends Message {
}
@Override
public void setEncoding(String encoding) throws UnavailableStorageException {
if (mBody instanceof Multipart) {
((Multipart)mBody).setEncoding(encoding);
} else if (mBody instanceof TextBody) {
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
((TextBody)mBody).setEncoding(encoding);
public void setEncoding(String encoding) throws MessagingException {
if (mBody != null) {
mBody.setEncoding(encoding);
}
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
}
@Override
@ -487,8 +516,9 @@ public class MimeMessage extends Message {
public void body(BodyDescriptor bd, InputStream in) throws IOException {
expect(Part.class);
Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
try {
Body body = MimeUtility.decodeBody(in,
bd.getTransferEncoding(), bd.getMimeType());
((Part)stack.peek()).setBody(body);
} catch (MessagingException me) {
throw new Error(me);
@ -597,4 +627,47 @@ public class MimeMessage extends Message {
public boolean hasAttachments() {
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);
}
}
}

View File

@ -99,4 +99,11 @@ public class MimeMultipart extends Multipart {
public InputStream getInputStream() throws MessagingException {
return null;
}
@Override
public void setUsing7bitTransport() throws MessagingException {
for (BodyPart part : mParts) {
part.setUsing7bitTransport();
}
}
}

View File

@ -13,6 +13,7 @@ import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStrea
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.Base64InputStream;
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
import org.apache.james.mime4j.util.MimeUtil;
import java.io.IOException;
import java.io.InputStream;
@ -1155,23 +1156,31 @@ public class MimeUtility {
/**
* Removes any content transfer encoding from the stream and returns a Body.
* @throws MessagingException
*/
public static Body decodeBody(InputStream in, String contentTransferEncoding)
throws IOException {
public static Body decodeBody(InputStream in,
String contentTransferEncoding, String contentType)
throws IOException, MessagingException {
/*
* We'll remove any transfer encoding by wrapping the stream.
*/
if (contentTransferEncoding != null) {
contentTransferEncoding =
MimeUtility.getHeaderParameter(contentTransferEncoding, null);
if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) {
if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(contentTransferEncoding)) {
in = new QuotedPrintableInputStream(in);
} else if ("base64".equalsIgnoreCase(contentTransferEncoding)) {
} else if (MimeUtil.ENC_BASE64.equalsIgnoreCase(contentTransferEncoding)) {
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();
try {
IOUtils.copy(in, out);
@ -2160,6 +2169,38 @@ public class MimeUtility {
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>*&#47;*: 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) {
while (part != null) {
if (part instanceof Message)
@ -3326,7 +3367,7 @@ public class MimeUtility {
public static void setCharset(String charset, Part part) throws MessagingException {
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) {

View File

@ -7,6 +7,7 @@ import com.fsck.k9.mail.MessagingException;
import java.io.*;
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
import org.apache.james.mime4j.util.MimeUtil;
public class TextBody implements Body {
@ -31,7 +32,7 @@ public class TextBody implements Body {
public void writeTo(OutputStream out) throws IOException, MessagingException {
if (mBody != null) {
byte[] bytes = mBody.getBytes(mCharset);
if ("8bit".equals(mEncoding)) {
if (MimeUtil.ENC_8BIT.equalsIgnoreCase(mEncoding)) {
out.write(bytes);
} else {
QuotedPrintableOutputStream qp = new QuotedPrintableOutputStream(out, false);

View File

@ -1653,9 +1653,12 @@ public class ImapStore extends Store {
String bodyString = (String)literal;
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
String contentTransferEncoding = part.getHeader(
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
part.setBody(MimeUtility.decodeBody(bodyStream, contentTransferEncoding));
String contentTransferEncoding = part
.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
String contentType = part
.getHeader(MimeHeader.HEADER_CONTENT_TYPE)[0];
part.setBody(MimeUtility.decodeBody(bodyStream,
contentTransferEncoding, contentType));
} else {
// This shouldn't happen
throw new MessagingException("Got FETCH response with bogus parameters");
@ -3547,10 +3550,13 @@ public class ImapStore extends Store {
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
//TODO: check for correct UID
String contentTransferEncoding = mPart.getHeader(
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
String contentTransferEncoding = mPart
.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;
}

View File

@ -25,6 +25,8 @@ import java.util.UUID;
import java.util.regex.Pattern;
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.content.ContentResolver;
@ -51,6 +53,7 @@ import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.CompositeBody;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
@ -1936,6 +1939,7 @@ public class LocalStore extends Store implements Serializable {
String contentUri = cursor.getString(5);
String contentId = cursor.getString(6);
String contentDisposition = cursor.getString(7);
String encoding = MimeUtility.getEncodingforType(type);
Body body = null;
if (contentDisposition == null) {
@ -1943,11 +1947,19 @@ public class LocalStore extends Store implements Serializable {
}
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);
bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
bp.setEncoding(encoding);
if (name != null) {
bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
String.format("%s;\n name=\"%s\"",
@ -2852,7 +2864,13 @@ public class LocalStore extends Store implements Serializable {
contentUri = AttachmentProvider.getAttachmentUri(
mAccount,
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();
cv.put("content_uri", contentUri != null ? contentUri.toString() : null);
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 Application mApplication;
private Uri mUri;
protected String mEncoding;
public LocalAttachmentBody(Uri uri, Application application) {
mApplication = application;
@ -4011,11 +4030,21 @@ public class LocalStore extends Store implements Serializable {
public void writeTo(OutputStream out) throws IOException, MessagingException {
InputStream in = getInputStream();
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 {
IOUtils.copy(in, base64Out);
IOUtils.copy(in, out);
} finally {
base64Out.close();
if (closeStream) {
out.close();
}
}
} finally {
in.close();
@ -4025,6 +4054,64 @@ public class LocalStore extends Store implements Serializable {
public Uri getContentUri() {
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 {

View File

@ -477,7 +477,9 @@ public class SmtpTransport extends Transport {
close();
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
// the size of messages, count the message's size before sending it
if (mLargestAcceptableMessage > 0 && ((LocalMessage)message).hasAttachments()) {
@ -490,8 +492,8 @@ public class SmtpTransport extends Transport {
Address[] from = message.getFrom();
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) {
executeSimpleCommand("RCPT TO:" + "<" + address + ">");
}

View 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();
}
}
}