k-9/k9mail-library/src/main/java/com/fsck/k9/mail/internet/BinaryTempFileBody.java

135 lines
4.0 KiB
Java
Raw Normal View History

package com.fsck.k9.mail.internet;
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.*;
/**
* A Body that is backed by a temp file. The Body exposes a getOutputStream method that allows
* the user to write to the temp file. After the write the body is available via getInputStream
* and writeTo one time. After writeTo is called, or the InputStream returned from
* getInputStream is closed the file is deleted and the Body should be considered disposed of.
*/
public class BinaryTempFileBody implements RawDataBody, SizeAware {
private static File mTempDirectory;
private File mFile;
Recursively convert attachments of type message/rfc822 to 7bit if necessary. The preceding commit resulted in attachments of type message/rfc822 being sent with 8bit encoding even when the SMTP server did not support 8BITMIME. This commit assures that messages will be converted to 7bit when necessary. A new interface CompositeBody was created that extends Body, and classes Message and Multipart were changed from implementing Body to CompositeBody. Additional classes BinaryTempFileMessageBody and LocalAttachmentMessageBody were created (by extending BinaryTempFileBody and LocalAttachmentBody, respectively), and they too implement CompositeBody. A CompositeBody is a Body containing a composite-type that can contain subparts that may require recursive processing when converting from 8bit to 7bit. The Part to which a CompositeBody belongs is only permitted to use 8bit or 7bit encoding for the CompositeBody. Previously, a Message was created so that it was 7bit clean by default (even though that meant base64 encoding all attachments, including messages). Then, if the SMTP server supported 8BITMIME, Message.setEncoding("8bit") was called so that bodies of type TextBody would been transmitted using 8bit encoding rather than quoted-printable. Now, messages are created with 8bit encoding by default. Then, if the SMTP server does not support 8BITMIME, Message.setUsing7bitTransport is called to recursively convert the message and its subparts to 7bit. The method setUsing7bitTransport was added to the interfaces Part and CompositeBody. setEncoding no longer iterates over parts in Multipart. That task belongs to setUsing7bitTransport, which may in turn call setEncoding on the parts. MimeUtility.getEncodingforType was created as a helper function for choosing a default encoding that should be used for a given MIME type when an attachment is added to a message (either while composing or when retrieving from LocalStore). setEncoding was implemented in MimeBodyPart to assure that the encoding set in the Part's headers was the same as set for the Part's Body. (The method already existed in MimeMessage, which has similarities with MimeBodyPart.) MimeMessage.parse(InputStream in, boolean recurse) was implemented so that the parser could be told to recursively process nested messages read from the InputStream, thus giving access to all subparts at any level that may need to be converted from 8bit to 7bit.
2013-09-02 23:49:28 -04:00
String mEncoding = null;
public static void setTempDirectory(File tempDirectory) {
mTempDirectory = tempDirectory;
}
public static File getTempDirectory() {
return mTempDirectory;
}
@Override
public String getEncoding() {
return mEncoding;
}
Recursively convert attachments of type message/rfc822 to 7bit if necessary. The preceding commit resulted in attachments of type message/rfc822 being sent with 8bit encoding even when the SMTP server did not support 8BITMIME. This commit assures that messages will be converted to 7bit when necessary. A new interface CompositeBody was created that extends Body, and classes Message and Multipart were changed from implementing Body to CompositeBody. Additional classes BinaryTempFileMessageBody and LocalAttachmentMessageBody were created (by extending BinaryTempFileBody and LocalAttachmentBody, respectively), and they too implement CompositeBody. A CompositeBody is a Body containing a composite-type that can contain subparts that may require recursive processing when converting from 8bit to 7bit. The Part to which a CompositeBody belongs is only permitted to use 8bit or 7bit encoding for the CompositeBody. Previously, a Message was created so that it was 7bit clean by default (even though that meant base64 encoding all attachments, including messages). Then, if the SMTP server supported 8BITMIME, Message.setEncoding("8bit") was called so that bodies of type TextBody would been transmitted using 8bit encoding rather than quoted-printable. Now, messages are created with 8bit encoding by default. Then, if the SMTP server does not support 8BITMIME, Message.setUsing7bitTransport is called to recursively convert the message and its subparts to 7bit. The method setUsing7bitTransport was added to the interfaces Part and CompositeBody. setEncoding no longer iterates over parts in Multipart. That task belongs to setUsing7bitTransport, which may in turn call setEncoding on the parts. MimeUtility.getEncodingforType was created as a helper function for choosing a default encoding that should be used for a given MIME type when an attachment is added to a message (either while composing or when retrieving from LocalStore). setEncoding was implemented in MimeBodyPart to assure that the encoding set in the Part's headers was the same as set for the Part's Body. (The method already existed in MimeMessage, which has similarities with MimeBodyPart.) MimeMessage.parse(InputStream in, boolean recurse) was implemented so that the parser could be told to recursively process nested messages read from the InputStream, thus giving access to all subparts at any level that may need to be converted from 8bit to 7bit.
2013-09-02 23:49:28 -04:00
public void setEncoding(String encoding) throws MessagingException {
if (mEncoding != null && mEncoding.equalsIgnoreCase(encoding)) {
return;
}
// The encoding changed, so we need to convert the message
if (!MimeUtil.ENC_8BIT.equalsIgnoreCase(mEncoding)) {
throw new RuntimeException("Can't convert from encoding: " + mEncoding);
}
try {
File newFile = File.createTempFile("body", null, mTempDirectory);
OutputStream out = new FileOutputStream(newFile);
try {
if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(encoding)) {
out = new QuotedPrintableOutputStream(out, false);
} else if (MimeUtil.ENC_BASE64.equals(encoding)) {
out = new Base64OutputStream(out);
} else {
throw new RuntimeException("Target encoding not supported: " + encoding);
}
InputStream in = getInputStream();
try {
IOUtils.copy(in, out);
} finally {
in.close();
}
} finally {
out.close();
}
mFile = newFile;
mEncoding = encoding;
} catch (IOException e) {
throw new MessagingException("Unable to convert body", e);
}
}
public BinaryTempFileBody(String encoding) {
if (mTempDirectory == null) {
throw new RuntimeException("setTempDirectory has not been called on BinaryTempFileBody!");
}
mEncoding = encoding;
}
public OutputStream getOutputStream() throws IOException {
mFile = File.createTempFile("body", null, mTempDirectory);
mFile.deleteOnExit();
return new FileOutputStream(mFile);
}
public InputStream getInputStream() throws MessagingException {
try {
return new BinaryTempFileBodyInputStream(new FileInputStream(mFile));
} catch (IOException ioe) {
throw new MessagingException("Unable to open body", ioe);
}
}
public void writeTo(OutputStream out) throws IOException, MessagingException {
InputStream in = getInputStream();
2011-12-31 12:38:41 -05:00
try {
IOUtils.copy(in, out);
2011-12-31 12:38:41 -05:00
} finally {
in.close();
2011-12-31 12:38:41 -05:00
}
}
@Override
public long getSize() {
return mFile.length();
}
public File getFile() {
return mFile;
}
class BinaryTempFileBodyInputStream extends FilterInputStream {
public BinaryTempFileBodyInputStream(InputStream in) {
super(in);
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
mFile.delete();
}
}
public void closeWithoutDeleting() throws IOException {
super.close();
}
}
}