Rfc822Output.java revision f2dded3a2fba83dd3f0d14cce6abe467a4ab66eb
1a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler/* 2a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Copyright (C) 2009 The Android Open Source Project 3a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 4a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Licensed under the Apache License, Version 2.0 (the "License"); 5a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * you may not use this file except in compliance with the License. 6a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * You may obtain a copy of the License at 7a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 8a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * http://www.apache.org/licenses/LICENSE-2.0 9a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 10a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Unless required by applicable law or agreed to in writing, software 11a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * distributed under the License is distributed on an "AS IS" BASIS, 12a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * See the License for the specific language governing permissions and 14a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * limitations under the License. 15a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 16a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 17a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerpackage com.android.email.mail.transport; 18a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 19f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blankimport com.android.email.R; 20a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport com.android.email.codec.binary.Base64; 21c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport com.android.email.codec.binary.Base64OutputStream; 22a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport com.android.email.mail.Address; 23c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport com.android.email.mail.MessagingException; 24a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport com.android.email.mail.internet.MimeUtility; 25c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport com.android.email.provider.EmailContent.Attachment; 26a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport com.android.email.provider.EmailContent.Body; 27a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport com.android.email.provider.EmailContent.Message; 28a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 29c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport org.apache.commons.io.IOUtils; 30c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler 31c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport android.content.ContentUris; 32a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport android.content.Context; 33c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport android.database.Cursor; 34c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport android.net.Uri; 35a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 36a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.io.BufferedOutputStream; 37c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport java.io.FileNotFoundException; 38a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.io.IOException; 39c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadlerimport java.io.InputStream; 40a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.io.OutputStream; 41a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.io.OutputStreamWriter; 42a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.io.Writer; 43a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.text.SimpleDateFormat; 44a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.util.Date; 45a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerimport java.util.Locale; 46f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blankimport java.util.regex.Matcher; 47f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blankimport java.util.regex.Pattern; 48a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 49a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler/** 50a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Utility class to output RFC 822 messages from provider email messages 51a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 52a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadlerpublic class Rfc822Output { 53a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 54f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank private static final Pattern PATTERN_START_OF_LINE = Pattern.compile("(?m)^"); 55f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank private static final Pattern PATTERN_ENDLINE_CRLF = Pattern.compile("\r\n"); 56f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank 57a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to 58a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // "Jan", not the other localized format like "Ene" (meaning January in locale es). 59a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler static final SimpleDateFormat mDateFormat = 60a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); 61a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 62f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank /*package*/ static String buildBodyText(Context context, Message message) { 63f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank int flags = message.mFlags; 64f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank Body body = Body.restoreBodyWithMessageId(context, message.mId); 65f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String text = body.mTextContent; 66f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank 67f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String quotedText = body.mTextReply; 68f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank if (quotedText != null) { 69f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank // fix CR-LF line endings to LF-only needed by EditText. 70f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank Matcher matcher = PATTERN_ENDLINE_CRLF.matcher(quotedText); 71f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank quotedText = matcher.replaceAll("\n"); 72f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } 73f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String fromAsString = Address.unpackToString(message.mFrom); 74f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank if ((flags & Message.FLAG_TYPE_REPLY) != 0) { 75f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank text += context.getString(R.string.message_compose_reply_header_fmt, fromAsString); 76f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank if (quotedText != null) { 77f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText); 78f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank text += matcher.replaceAll(">"); 79f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } 80f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } else if ((flags & Message.FLAG_TYPE_FORWARD) != 0) { 81f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String subject = message.mSubject; 82f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String to = Address.unpackToString(message.mTo); 83f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String cc = Address.unpackToString(message.mCc); 84f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank text += context.getString(R.string.message_compose_fwd_header_fmt, subject, 85f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank fromAsString, to != null ? to : "", cc != null ? cc : ""); 86f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank if (quotedText != null) { 87f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank text += quotedText; 88f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } 89f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } 90f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank return text; 91f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank } 92f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank 93a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 94a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Write the entire message to an output stream. This method provides buffering, so it is 95a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * not necessary to pass in a buffered output stream here. 96a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 97a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param context system context for accessing the provider 98a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param messageId the message to write out 99a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param out the output stream to write the message to 100a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 101a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * TODO is there anything in the flags fields we need to look at? 102a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * TODO alternative parts (e.g. text+html) are not supported here. 103a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 104a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler public static void writeTo(Context context, long messageId, OutputStream out) 105c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler throws IOException, MessagingException { 106a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler Message message = Message.restoreMessageWithId(context, messageId); 107a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler if (message == null) { 108a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // throw something? 109a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler return; 110a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 111a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 112a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler OutputStream stream = new BufferedOutputStream(out, 1024); 113a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler Writer writer = new OutputStreamWriter(stream); 114a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 115a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // Write the fixed headers. Ordering is arbitrary (the legacy code iterated through a 116a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // hashmap here). 117a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 118a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler String date = mDateFormat.format(new Date(message.mTimeStamp)); 119a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeHeader(writer, "Date", date); 120a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 121a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeEncodedHeader(writer, "Subject", message.mSubject); 122a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 123a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeHeader(writer, "Message-ID", message.mMessageId); 124a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 125a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // Address fields. Note, obviously, we skip bcc here 126a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeAddressHeader(writer, "From", message.mFrom); 127a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeAddressHeader(writer, "To", message.mTo); 128a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeAddressHeader(writer, "Cc", message.mCc); 129a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeAddressHeader(writer, "Reply-To", message.mReplyTo); 130a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 131a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler // Analyze message and determine if we have multiparts 132f2dded3a2fba83dd3f0d14cce6abe467a4ab66ebMarc Blank String text = buildBodyText(context, message); 133c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler 134c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId); 135c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Cursor attachmentsCursor = context.getContentResolver().query(uri, 136c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Attachment.CONTENT_PROJECTION, null, null, null); 137c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler 138c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler try { 139c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler boolean mixedParts = attachmentsCursor.getCount() > 0; 140c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler String mixedBoundary = null; 141a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 142c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // Simplified case for no multipart - just emit text and be done. 143c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler if (!mixedParts) { 144c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler if (text != null) { 145c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeTextWithHeaders(writer, stream, text); 146c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } else { 147c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writer.write("\r\n"); // a truly empty message 148c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 149c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } else { 150c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // continue with multipart headers, then into multipart body 151c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "MIME-Version", "1.0"); 152a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 153c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler mixedBoundary = "--_com.android.email_" + System.nanoTime(); 154c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "Content-Type", 155c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler "multipart/mixed; boundary=\"" + mixedBoundary + "\""); 156a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 157c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // Finish headers and prepare for body section(s) 158c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writer.write("\r\n"); 159a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 160c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // first multipart element is the body 161c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler if (text != null) { 162c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeBoundary(writer, mixedBoundary, false); 163c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeTextWithHeaders(writer, stream, text); 164c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 165a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 166c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // Write out the attachments 167c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler while (attachmentsCursor.moveToNext()) { 168c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeBoundary(writer, mixedBoundary, false); 169c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Attachment attachment = 170c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Attachment.getContent(attachmentsCursor, Attachment.class); 171c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeOneAttachment(context, writer, stream, attachment); 172c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writer.write("\r\n"); 173c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 174a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 175c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // end of multipart section 176c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeBoundary(writer, mixedBoundary, true); 177c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 178c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } finally { 179c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler attachmentsCursor.close(); 180a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 181a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 182a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.flush(); 183a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler out.flush(); 184a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 185a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 186a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 187c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler * Write a single attachment and its payload 188c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler */ 189c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler private static void writeOneAttachment(Context context, Writer writer, OutputStream out, 190c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Attachment attachment) throws IOException, MessagingException { 191c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "Content-Type", 192c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler attachment.mMimeType + ";\n name=\"" + attachment.mFileName + "\""); 193c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "Content-Transfer-Encoding", "base64"); 194c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "Content-Disposition", 195c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler "attachment;" 196c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler + "\n filename=\"" + attachment.mFileName + "\";" 197c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler + "\n size=" + Long.toString(attachment.mSize)); 198c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writeHeader(writer, "Content-ID", attachment.mContentId); 199c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writer.append("\r\n"); 200c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler 201c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // Set up input stream and write it out via base64 202c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler InputStream inStream = null; 203c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler try { 204c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // try to open the file 205c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Uri fileUri = Uri.parse(attachment.mContentUri); 206c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler inStream = context.getContentResolver().openInputStream(fileUri); 207c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // switch to output stream for base64 text output 208c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler writer.flush(); 209c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler Base64OutputStream base64Out = new Base64OutputStream(out); 210c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // copy base64 data and close up 211c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler IOUtils.copy(inStream, base64Out); 212c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler base64Out.close(); 213c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 214c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler catch (FileNotFoundException fnfe) { 215c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler // Ignore this - empty file is OK 216c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 217c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler catch (IOException ioe) { 218c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler throw new MessagingException("Invalid attachment.", ioe); 219c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 220c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler } 221c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler 222c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler /** 223a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Write a single header with no wrapping or encoding 224a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 225a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param writer the output writer 226a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param name the header name 227a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param value the header value 228a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 229a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler private static void writeHeader(Writer writer, String name, String value) throws IOException { 230a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler if (value != null && value.length() > 0) { 231a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(name); 232a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(": "); 233a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(value); 234a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("\r\n"); 235a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 236a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 237a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 238a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 239a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Write a single header using appropriate folding & encoding 240a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 241a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param writer the output writer 242a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param name the header name 243a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param value the header value 244a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 245a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler private static void writeEncodedHeader(Writer writer, String name, String value) 246a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler throws IOException { 247a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler if (value != null && value.length() > 0) { 248a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(name); 249a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(": "); 250a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(MimeUtility.foldAndEncode2(value, name.length() + 2)); 251a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("\r\n"); 252a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 253a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 254a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 255a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 256a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Unpack, encode, and fold address(es) into a header 257a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 258a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param writer the output writer 259a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param name the header name 260a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param value the header value (a packed list of addresses) 261a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 262a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler private static void writeAddressHeader(Writer writer, String name, String value) 263a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler throws IOException { 264a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler if (value != null && value.length() > 0) { 265a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(name); 266a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(": "); 267a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(MimeUtility.fold(Address.packedToHeader(value), name.length() + 2)); 268a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("\r\n"); 269a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 270a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 271a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 272a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 273a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Write a multipart boundary 274a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 275a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param writer the output writer 276a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param boundary the boundary string 277a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param end false if inner boundary, true if final boundary 278a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 279a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler private static void writeBoundary(Writer writer, String boundary, boolean end) 280a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler throws IOException { 281a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("--"); 282a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append(boundary); 283a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler if (end) { 284a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("--"); 285a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 286a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.append("\r\n"); 287a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 288a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler 289a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler /** 290a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Write text (either as main body or inside a multipart), preceded by appropriate headers. 291a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 292a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * Note this always uses base64, even when not required. Slightly less efficient for 293a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * US-ASCII text, but handles all formats even when non-ascii chars are involved. A small 294a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * optimization might be to prescan the string for safety and send raw if possible. 295a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * 296a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param writer the output writer 297a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param out the output stream inside the writer (used for byte[] access) 298a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler * @param text The original text of the message 299a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler */ 300a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler private static void writeTextWithHeaders(Writer writer, OutputStream out, String text) 301a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler throws IOException { 302a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeHeader(writer, "Content-Type", "text/plain; charset=utf-8"); 303a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writeHeader(writer, "Content-Transfer-Encoding", "base64"); 304a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.write("\r\n"); 305a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler byte[] bytes = text.getBytes("UTF-8"); 306a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler writer.flush(); 307a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler out.write(Base64.encodeBase64Chunked(bytes)); 308a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler } 309a9a0e57436bce15e186bef216564e996ec20ae82Andrew Stadler} 310