package com.zegoggles.smssync.mail; import android.content.ContentResolver; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.filter.Base64OutputStream; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.TextBody; import org.apache.commons.io.IOUtils; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import static com.zegoggles.smssync.App.TAG; class Attachment { // RFC2231 encoding from geronimo-javamail private static final String MIME_SPECIALS = "()<>@,;:\\\"/[]?=" + "\t "; private static final String RFC2231_SPECIALS = "*'%" + MIME_SPECIALS; private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); private Attachment() {} public static MimeBodyPart createTextPart(String text) throws MessagingException { return new MimeBodyPart(new TextBody(text)); } public static MimeBodyPart createPartFromUri(ContentResolver resolver, Uri uri, String filename, String contentType) throws MessagingException { return createPart(new ResolverBody(resolver, uri), filename, contentType); } public static MimeBodyPart createPartFromFile(File file, String contentType) throws MessagingException { return createPart(new FileBody(file), file.getName(), contentType); } private static MimeBodyPart createPart(Body body, final String filename, final String contentType) throws MessagingException { MimeBodyPart part = new MimeBodyPart(body, contentType); String contentTypeHeader = TextUtils.isEmpty(contentType) ? "application/octet-stream" : contentType; String disposition = "attachment"; if (!TextUtils.isEmpty(filename)) { // should set both name and filename parameters // http://www.imc.org/ietf-smtp/mail-archive/msg05023.html disposition += encodeRFC2231("filename", filename); contentTypeHeader += encodeRFC2231("name", filename); } part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentTypeHeader); part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, disposition); part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); return part; } private static abstract class Base64Body implements Body { @Override public void writeTo(OutputStream outputStream) throws IOException, MessagingException { InputStream in = getInputStream(); if (in != null) { Base64OutputStream base64Out = new Base64OutputStream(outputStream); IOUtils.copy(in, base64Out); base64Out.close(); } else { Log.w(TAG, "input stream is null"); } } } private static class ResolverBody extends Base64Body { private ContentResolver mResolver; private Uri mUri; public ResolverBody(ContentResolver contentResolver, Uri uri) { mResolver = contentResolver; mUri = uri; } public InputStream getInputStream() throws MessagingException { try { return mResolver.openInputStream(mUri); } catch (FileNotFoundException fnfe) { /* * Since it's completely normal for us to try to serve up attachments that * have been blown away, we just return an empty stream. */ return new ByteArrayInputStream(new byte[0]); } } @Override public void setEncoding(String s) throws MessagingException { } } private static class FileBody extends Base64Body { private final File file; public FileBody(File file) { this.file = file; } @Override public InputStream getInputStream() throws MessagingException { try { return new FileInputStream(file); } catch (FileNotFoundException e) { return new ByteArrayInputStream(new byte[0]); } } @Override public void setEncoding(String s) throws MessagingException { } } protected static String encodeRFC2231(String key, String value) { StringBuilder buf = new StringBuilder(); boolean encoded = encodeRFC2231value(value, buf); if (encoded) { return "; " + key + "*=" + buf.toString(); } else { return "; " + key + "=" + value; } } private static boolean encodeRFC2231value(String value, StringBuilder buf) { String charset = "UTF-8"; buf.append(charset); buf.append("''"); // no language byte[] bytes; try { bytes = value.getBytes(charset); } catch (UnsupportedEncodingException e) { return true; } boolean encoded = false; for (byte aByte : bytes) { int ch = aByte & 0xff; if (ch <= 32 || ch >= 127 || RFC2231_SPECIALS.indexOf(ch) != -1) { buf.append('%'); buf.append(HEX_DIGITS[ch >> 4]); buf.append(HEX_DIGITS[ch & 0xf]); encoded = true; } else { buf.append((char) ch); } } return encoded; } }