Test encode/decode of attachments

b/15089448

Change-Id: Ifd2afcfb36c9820c5d64ebbcc3230603b2a27089
This commit is contained in:
Tony Mantler 2014-05-21 10:25:08 -07:00
parent 2fbd8c269c
commit 5a93b8f7c6
2 changed files with 145 additions and 34 deletions

View File

@ -45,6 +45,7 @@ import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.AttachmentUtilities; import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.mail.providers.UIProvider; import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogUtils; import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -183,6 +184,59 @@ public class LegacyConversions {
} }
} }
/**
* Convert a MIME Part object into an Attachment object. Separated for unit testing.
*
* @param part MIME part object to convert
* @return Populated Account object
* @throws MessagingException
*/
@VisibleForTesting
protected static Attachment mimePartToAttachment(final Part part) throws MessagingException {
// Transfer fields from mime format to provider format
final String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
String name = MimeUtility.getHeaderParameter(contentType, "name");
if (TextUtils.isEmpty(name)) {
final String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
}
// Incoming attachment: Try to pull size from disposition (if not downloaded yet)
long size = 0;
final String disposition = part.getDisposition();
if (!TextUtils.isEmpty(disposition)) {
String s = MimeUtility.getHeaderParameter(disposition, "size");
if (!TextUtils.isEmpty(s)) {
try {
size = Long.parseLong(s);
} catch (final NumberFormatException e) {
LogUtils.d(LogUtils.TAG, e, "Could not decode size \"%s\" from attachment part",
size);
}
}
}
// Get partId for unloaded IMAP attachments (if any)
// This is only provided (and used) when we have structure but not the actual attachment
final String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
final String partId = partIds != null ? partIds[0] : null;
final Attachment localAttachment = new Attachment();
// Run the mime type through inferMimeType in case we have something generic and can do
// better using the filename extension
localAttachment.mMimeType = AttachmentUtilities.inferMimeType(name, part.getMimeType());
localAttachment.mFileName = name;
localAttachment.mSize = size;
localAttachment.mContentId = part.getContentId();
localAttachment.setContentUri(null); // Will be rewritten by saveAttachmentBody
localAttachment.mLocation = partId;
localAttachment.mEncoding = "B"; // TODO - convert other known encodings
return localAttachment;
}
/** /**
* Add a single attachment part to the message * Add a single attachment part to the message
* *
@ -203,40 +257,8 @@ public class LegacyConversions {
public static void addOneAttachment(final Context context, public static void addOneAttachment(final Context context,
final EmailContent.Message localMessage, final Part part) final EmailContent.Message localMessage, final Part part)
throws MessagingException, IOException { throws MessagingException, IOException {
// Transfer fields from mime format to provider format final Attachment localAttachment = mimePartToAttachment(part);
final String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
String name = MimeUtility.getHeaderParameter(contentType, "name");
if (name == null) {
final String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
}
// Incoming attachment: Try to pull size from disposition (if not downloaded yet)
long size = 0;
final String disposition = part.getDisposition();
if (disposition != null) {
String s = MimeUtility.getHeaderParameter(disposition, "size");
if (s != null) {
size = Long.parseLong(s);
}
}
// Get partId for unloaded IMAP attachments (if any)
// This is only provided (and used) when we have structure but not the actual attachment
final String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
final String partId = partIds != null ? partIds[0] : null;
// Run the mime type through inferMimeType in case we have something generic and can do
// better using the filename extension
final Attachment localAttachment = new Attachment();
localAttachment.mMimeType = AttachmentUtilities.inferMimeType(name, part.getMimeType());
localAttachment.mFileName = name;
localAttachment.mSize = size; // May be reset below if file handled
localAttachment.mContentId = part.getContentId();
localAttachment.setContentUri(null); // Will be rewritten by saveAttachmentBody
localAttachment.mMessageKey = localMessage.mId; localAttachment.mMessageKey = localMessage.mId;
localAttachment.mLocation = partId;
localAttachment.mEncoding = "B"; // TODO - convert other known encodings
localAttachment.mAccountKey = localMessage.mAccountKey; localAttachment.mAccountKey = localMessage.mAccountKey;
if (DEBUG_ATTACHMENTS) { if (DEBUG_ATTACHMENTS) {
@ -469,7 +491,8 @@ public class LegacyConversions {
* @param contentId as referenced from cid: uris in the message body (if applicable) * @param contentId as referenced from cid: uris in the message body (if applicable)
* @param content unencoded bytes * @param content unencoded bytes
*/ */
private static void addAttachmentPart(final Multipart mp, final String contentType, @VisibleForTesting
protected static void addAttachmentPart(final Multipart mp, final String contentType,
final Long contentSize, final String filename, final String contentId, final Long contentSize, final String filename, final String contentId,
final InputStream content) throws MessagingException { final InputStream content) throws MessagingException {
final Base64Body body = new Base64Body(content); final Base64Body body = new Base64Body(content);

View File

@ -16,23 +16,41 @@
package com.android.email; package com.android.email;
import android.content.Context;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.emailcommon.TempDirectory;
import com.android.emailcommon.internet.MimeBodyPart; import com.android.emailcommon.internet.MimeBodyPart;
import com.android.emailcommon.internet.MimeMessage; import com.android.emailcommon.internet.MimeMessage;
import com.android.emailcommon.internet.MimeMultipart;
import com.android.emailcommon.mail.Address; import com.android.emailcommon.mail.Address;
import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.Message.RecipientType;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.mail.Multipart;
import com.android.emailcommon.mail.Part; import com.android.emailcommon.mail.Part;
import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.utility.ConversionUtilities; import com.android.emailcommon.utility.ConversionUtilities;
import com.android.emailcommon.utility.ConversionUtilities.BodyFieldData; import com.android.emailcommon.utility.ConversionUtilities.BodyFieldData;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@SmallTest
public class LegacyConversionsTest extends AndroidTestCase { public class LegacyConversionsTest extends AndroidTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
TempDirectory.setTempDirectory(getContext());
}
/** /**
* Test basic fields conversion from Store message to Provider message. * Test basic fields conversion from Store message to Provider message.
*/ */
@ -172,4 +190,74 @@ public class LegacyConversionsTest extends AndroidTestCase {
final BodyFieldData data = ConversionUtilities.parseBodyFields(viewables); final BodyFieldData data = ConversionUtilities.parseBodyFields(viewables);
assertNull(data.textContent); assertNull(data.textContent);
} }
/**
* Test adding an attachment to a message, and then parsing it back out.
* @throws MessagingException
*/
public void testAttachmentRoundTrip() throws Exception {
final Context context = getContext();
final MimeMultipart mp = new MimeMultipart();
mp.setSubType("mixed");
final long size;
final File tempDir = context.getCacheDir();
if (!tempDir.isDirectory() && !tempDir.mkdirs()) {
fail("Could not create temporary directory");
}
final File tempAttachmentFile = File.createTempFile("testAttachmentRoundTrip", ".txt",
tempDir);
try {
final OutputStream attOut = new FileOutputStream(tempAttachmentFile);
try {
attOut.write("TestData".getBytes());
} finally {
attOut.close();
}
size = tempAttachmentFile.length();
final InputStream attIn = new FileInputStream(tempAttachmentFile);
LegacyConversions.addAttachmentPart(mp, "text/plain", size, "test.txt",
"testContentId", attIn);
} finally {
if (!tempAttachmentFile.delete()) {
fail("Setup failure: Could not clean up temp file");
}
}
final MimeMessage outMessage = new MimeMessage();
outMessage.setBody(mp);
final MimeMessage inMessage;
final File tempBodyFile = File.createTempFile("testAttachmentRoundTrip", ".eml",
context.getCacheDir());
try {
final OutputStream bodyOut = new FileOutputStream(tempBodyFile);
try {
outMessage.writeTo(bodyOut);
} finally {
bodyOut.close();
}
final InputStream bodyIn = new FileInputStream(tempBodyFile);
try {
inMessage = new MimeMessage(bodyIn);
} finally {
bodyIn.close();
}
} finally {
if (!tempBodyFile.delete()) {
fail("Setup failure: Could not clean up temp file");
}
}
final Multipart inBody = (Multipart) inMessage.getBody();
final Part attPart = inBody.getBodyPart(0);
final Attachment att = LegacyConversions.mimePartToAttachment(attPart);
assertEquals(att.mFileName, "test.txt");
assertEquals(att.mMimeType, "text/plain");
assertEquals(att.mSize, size);
assertEquals(att.mContentId, "testContentId");
}
} }