1d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/* 2d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Copyright (C) 2015 The Android Open Source Project 3d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 4d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * you may not use this file except in compliance with the License. 6d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * You may obtain a copy of the License at 7d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 8d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 10d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * See the License for the specific language governing permissions and 14d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * limitations under the License. 15d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 16d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpackage com.android.voicemail.impl.mail.internet; 17d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 18d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.text.TextUtils; 19d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.util.Base64; 20d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.util.Base64DataException; 21d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.util.Base64InputStream; 22d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.VvmLog; 23d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.Body; 24d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.BodyPart; 25d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.Message; 26d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.MessagingException; 27d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.Multipart; 28d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.mail.Part; 29d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.ByteArrayOutputStream; 30d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.IOException; 31d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.InputStream; 32d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.OutputStream; 33d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.ArrayList; 34d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.regex.Matcher; 35d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.regex.Pattern; 36d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport org.apache.commons.io.IOUtils; 378369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanianimport org.apache.james.mime4j.codec.DecodeMonitor; 388369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanianimport org.apache.james.mime4j.codec.DecoderUtil; 39d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport org.apache.james.mime4j.codec.EncoderUtil; 408369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanianimport org.apache.james.mime4j.codec.QuotedPrintableInputStream; 41d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport org.apache.james.mime4j.util.CharsetUtil; 42d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 43d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpublic class MimeUtility { 44d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String LOG_TAG = "Email"; 45d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 46d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static final String MIME_TYPE_RFC822 = "message/rfc822"; 47d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final Pattern PATTERN_CR_OR_LF = Pattern.compile("\r|\n"); 48d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 49d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 50d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Replace sequences of CRLF+WSP with WSP. Tries to preserve original string object whenever 51d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * possible. 52d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 53d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String unfold(String s) { 54d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (s == null) { 55d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 56d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 57d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Matcher patternMatcher = PATTERN_CR_OR_LF.matcher(s); 58d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (patternMatcher.find()) { 59d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian patternMatcher.reset(); 60d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian s = patternMatcher.replaceAll(""); 61d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 62d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return s; 63d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 64d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 65d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String decode(String s) { 66d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (s == null) { 67d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 68d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 698369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian return DecoderUtil.decodeEncodedWords(s, DecodeMonitor.STRICT); 70d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 71d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 72d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String unfoldAndDecode(String s) { 73d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return decode(unfold(s)); 74d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 75d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 76d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // TODO implement proper foldAndEncode 77d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // NOTE: When this really works, we *must* remove all calls to foldAndEncode2() to prevent 78d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // duplication of encoding. 79d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String foldAndEncode(String s) { 80d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return s; 81d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 82d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 83d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 84d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * INTERIM version of foldAndEncode that will be used only by Subject: headers. This is safer than 85d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * implementing foldAndEncode() (see above) and risking unknown damage to other headers. 86d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 87d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>TODO: Copy this code to foldAndEncode(), get rid of this function, confirm all working OK. 88d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 89d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param s original string to encode and fold 90d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param usedCharacters number of characters already used up by header name 91d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return the String ready to be transmitted 92d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 93d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String foldAndEncode2(String s, int usedCharacters) { 94d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // james.mime4j.codec.EncoderUtil.java 95d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // encode: encodeIfNecessary(text, usage, numUsedInHeaderName) 96d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Usage.TEXT_TOKENlooks like the right thing for subjects 97d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // use WORD_ENTITY for address/names 98d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 99d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String encoded = EncoderUtil.encodeIfNecessary(s, EncoderUtil.Usage.TEXT_TOKEN, usedCharacters); 100d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 101d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return fold(encoded, usedCharacters); 102d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 103d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 104d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 105d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * INTERIM: From newer version of org.apache.james (but we don't want to import the entire 106d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * MimeUtil class). 107d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 108d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>Splits the specified string into a multiple-line representation with lines no longer than 76 109d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * characters (because the line might contain encoded words; see <a 110d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a> section 2). If the string contains 111d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * non-whitespace sequences longer than 76 characters a line break is inserted at the whitespace 112d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * character following the sequence resulting in a line longer than 76 characters. 113d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 114d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param s string to split. 115d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param usedCharacters number of characters already used up. Usually the number of characters 116d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * for header field name plus colon and one space. 117d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return a multiple-line representation of the given string. 118d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 119d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String fold(String s, int usedCharacters) { 120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final int maxCharacters = 76; 121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 122d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final int length = s.length(); 123d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (usedCharacters + length <= maxCharacters) { 124d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return s; 125d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 126d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 127d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian StringBuilder sb = new StringBuilder(); 128d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 129d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int lastLineBreak = -usedCharacters; 130d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int wspIdx = indexOfWsp(s, 0); 131d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian while (true) { 132d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (wspIdx == length) { 133d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian sb.append(s.substring(Math.max(0, lastLineBreak))); 134d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return sb.toString(); 135d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 136d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 137d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int nextWspIdx = indexOfWsp(s, wspIdx + 1); 138d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 139d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (nextWspIdx - lastLineBreak > maxCharacters) { 140d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian sb.append(s.substring(Math.max(0, lastLineBreak), wspIdx)); 141d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian sb.append("\r\n"); 142d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian lastLineBreak = wspIdx; 143d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 144d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 145d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian wspIdx = nextWspIdx; 146d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 147d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 148d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 149d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 150d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * INTERIM: From newer version of org.apache.james (but we don't want to import the entire 151d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * MimeUtil class). 152d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 153d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>Search for whitespace. 154d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 155d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static int indexOfWsp(String s, int fromIndex) { 156d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final int len = s.length(); 157d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (int index = fromIndex; index < len; index++) { 158d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian char c = s.charAt(index); 159d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (c == ' ' || c == '\t') { 160d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return index; 161d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 162d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 163d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return len; 164d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 165d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 166d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 167d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Returns the named parameter of a header field. If name is null the first parameter is returned, 168d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * or if there are no additional parameters in the field the entire field is returned. Otherwise 169d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * the named parameter is searched for in a case insensitive fashion and returned. If the 170d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * parameter cannot be found the method returns null. 171d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 172d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>TODO: quite inefficient with the inner trimming & splitting. TODO: Also has a latent bug: 173d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * uses "startsWith" to match the name, which can false-positive. TODO: The doc says that for a 174d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * null name you get the first param, but you get the header. Should probably just fix the doc, 175d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * but if other code assumes that behavior, fix the code. TODO: Need to decode %-escaped strings, 176d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * as in: filename="ab%22d". ('+' -> ' ' conversion too? check RFC) 177d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 178d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param header 179d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param name 180d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return the entire header (if name=null), the found parameter, or null 181d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 182d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String getHeaderParameter(String header, String name) { 183d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (header == null) { 184d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 185d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 186d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String[] parts = unfold(header).split(";"); 187d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (name == null) { 188d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return parts[0].trim(); 189d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 190d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String lowerCaseName = name.toLowerCase(); 191d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (String part : parts) { 192d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (part.trim().toLowerCase().startsWith(lowerCaseName)) { 193d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String[] parameterParts = part.split("=", 2); 194d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (parameterParts.length < 2) { 195d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 196d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 197d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String parameter = parameterParts[1].trim(); 198d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (parameter.startsWith("\"") && parameter.endsWith("\"")) { 199d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return parameter.substring(1, parameter.length() - 1); 200d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else { 201d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return parameter; 202d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 203d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 204d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 205d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 206d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 207d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 208d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 209d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Reads the Part's body and returns a String based on any charset conversion that needed to be 210d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * done. 211d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 212d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param part The part containing a body 213d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return a String containing the converted text in the body, or null if there was no text or an 214d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * error during conversion. 215d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 216d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String getTextFromPart(Part part) { 217d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 218d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (part != null && part.getBody() != null) { 219d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian InputStream in = part.getBody().getInputStream(); 220d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String mimeType = part.getMimeType(); 221d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (mimeType != null && MimeUtility.mimeTypeMatches(mimeType, "text/*")) { 222d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 223d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Now we read the part into a buffer for further processing. Because 224d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * the stream is now wrapped we'll remove any transfer encoding at this point. 225d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 226d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian ByteArrayOutputStream out = new ByteArrayOutputStream(); 227d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian IOUtils.copy(in, out); 228d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian in.close(); 229d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian in = null; // we want all of our memory back, and close might not release 230d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 231d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 232d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * We've got a text part, so let's see if it needs to be processed further. 233d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 234d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String charset = getHeaderParameter(part.getContentType(), "charset"); 235d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (charset != null) { 236d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 237d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * See if there is conversion from the MIME charset to the Java one. 238d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 2398369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian charset = CharsetUtil.lookup(charset).name(); 240d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 241d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 242d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * No encoding, so use us-ascii, which is the standard. 243d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 244d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (charset == null) { 245d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian charset = "ASCII"; 246d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 247d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 248d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Convert and return as new String 249d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 250d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String result = out.toString(charset); 251d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian out.close(); 252d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return result; 253d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 254d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 255d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 256d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (OutOfMemoryError oom) { 257d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 258d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * If we are not able to process the body there's nothing we can do about it. Return 259d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * null and let the upper layers handle the missing content. 260d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 261d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString()); 262d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (Exception e) { 263d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 264d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * If we are not able to process the body there's nothing we can do about it. Return 265d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * null and let the upper layers handle the missing content. 266d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 267d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + e.toString()); 268d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 269d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 270d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 271d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 272d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 273d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Returns true if the given mimeType matches the matchAgainst specification. The comparison 274d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * ignores case and the matchAgainst string may include "*" for a wildcard (e.g. "image/*"). 275d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 276d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param mimeType A MIME type to check. 277d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param matchAgainst A MIME type to check against. May include wildcards. 278d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return true if the mimeType matches 279d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 280d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static boolean mimeTypeMatches(String mimeType, String matchAgainst) { 281d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Pattern p = Pattern.compile(matchAgainst.replaceAll("\\*", "\\.\\*"), Pattern.CASE_INSENSITIVE); 282d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return p.matcher(mimeType).matches(); 283d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 284d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 285d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 286d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Returns true if the given mimeType matches any of the matchAgainst specifications. The 287d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * comparison ignores case and the matchAgainst strings may include "*" for a wildcard (e.g. 288d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * "image/*"). 289d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 290d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param mimeType A MIME type to check. 291d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param matchAgainst An array of MIME types to check against. May include wildcards. 292d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return true if the mimeType matches any of the matchAgainst strings 293d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 294d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static boolean mimeTypeMatches(String mimeType, String[] matchAgainst) { 295d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (String matchType : matchAgainst) { 296d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (mimeTypeMatches(mimeType, matchType)) { 297d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return true; 298d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 299d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 300d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return false; 301d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 302d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 303d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 304d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Given an input stream and a transfer encoding, return a wrapped input stream for that encoding 305d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * (or the original if none is required) 306d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 307d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param in the input stream 308d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param contentTransferEncoding the content transfer encoding 309d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return a properly wrapped stream 310d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 311d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static InputStream getInputStreamForContentTransferEncoding( 312d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian InputStream in, String contentTransferEncoding) { 313d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (contentTransferEncoding != null) { 314d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian contentTransferEncoding = MimeUtility.getHeaderParameter(contentTransferEncoding, null); 315d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) { 316d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian in = new QuotedPrintableInputStream(in); 317d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else if ("base64".equalsIgnoreCase(contentTransferEncoding)) { 318d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian in = new Base64InputStream(in, Base64.DEFAULT); 319d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 320d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 321d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return in; 322d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 323d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 324d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** Removes any content transfer encoding from the stream and returns a Body. */ 325d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static Body decodeBody(InputStream in, String contentTransferEncoding) throws IOException { 326d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /* 327d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * We'll remove any transfer encoding by wrapping the stream. 328d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 329d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian in = getInputStreamForContentTransferEncoding(in, contentTransferEncoding); 330d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian BinaryTempFileBody tempBody = new BinaryTempFileBody(); 331d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian OutputStream out = tempBody.getOutputStream(); 332d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 333d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian IOUtils.copy(in, out); 334d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (Base64DataException bde) { 335d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // TODO Need to fix this somehow 336d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian //String warning = "\n\n" + Email.getMessageDecodeErrorString(); 337d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian //out.write(warning.getBytes()); 338d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } finally { 339d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian out.close(); 340d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 341d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return tempBody; 342d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 343d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 344d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 345d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Recursively scan a Part (usually a Message) and sort out which of its children will be 346d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * "viewable" and which will be attachments. 347d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 348d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param part The part to be broken down 349d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param viewables This arraylist will be populated with all parts that appear to be the 350d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * "message" (e.g. text/plain & text/html) 351d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param attachments This arraylist will be populated with all parts that appear to be 352d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * attachments (including inlines) 353d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @throws MessagingException 354d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 355d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static void collectParts(Part part, ArrayList<Part> viewables, ArrayList<Part> attachments) 356d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws MessagingException { 357d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String disposition = part.getDisposition(); 358d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String dispositionType = MimeUtility.getHeaderParameter(disposition, null); 359d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // If a disposition is not specified, default to "inline" 360d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian boolean inline = 361d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType); 362d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // The lower-case mime type 363d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String mimeType = part.getMimeType().toLowerCase(); 364d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 365d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (part.getBody() instanceof Multipart) { 366d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // If the part is Multipart but not alternative it's either mixed or 367d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // something we don't know about, which means we treat it as mixed 368d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // per the spec. We just process its pieces recursively. 369d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian MimeMultipart mp = (MimeMultipart) part.getBody(); 370d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian boolean foundHtml = false; 371d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (mp.getSubTypeForTest().equals("alternative")) { 372d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (int i = 0; i < mp.getCount(); i++) { 373d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (mp.getBodyPart(i).isMimeType("text/html")) { 374d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian foundHtml = true; 375d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian break; 376d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 377d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 378d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 379d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (int i = 0; i < mp.getCount(); i++) { 380d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // See if we have text and html 381d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian BodyPart bp = mp.getBodyPart(i); 382d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // If there's html, don't bother loading text 383d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (foundHtml && bp.isMimeType("text/plain")) { 384d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian continue; 385d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 386d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian collectParts(bp, viewables, attachments); 387d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 388d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else if (part.getBody() instanceof Message) { 389d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // If the part is an embedded message we just continue to process 390d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // it, pulling any viewables or attachments into the running list. 391d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Message message = (Message) part.getBody(); 392d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian collectParts(message, viewables, attachments); 393d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) { 394d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // We'll treat text and images as viewables 395d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian viewables.add(part); 396d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else { 397d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Everything else is an attachment. 398d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian attachments.add(part); 399d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 400d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 401d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian} 402