1bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/**************************************************************** 2bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Licensed to the Apache Software Foundation (ASF) under one * 3bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * or more contributor license agreements. See the NOTICE file * 4bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * distributed with this work for additional information * 5bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * regarding copyright ownership. The ASF licenses this file * 6bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * to you under the Apache License, Version 2.0 (the * 7bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * "License"); you may not use this file except in compliance * 8bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * with the License. You may obtain a copy of the License at * 9bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * * 10bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * http://www.apache.org/licenses/LICENSE-2.0 * 11bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * * 12bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Unless required by applicable law or agreed to in writing, * 13bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * software distributed under the License is distributed on an * 14bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * KIND, either express or implied. See the License for the * 16bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * specific language governing permissions and limitations * 17bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * under the License. * 18bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ****************************************************************/ 19bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 20bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpackage org.apache.james.mime4j.decoder; 21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook//BEGIN android-changed: Stubbing out logging 23bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport org.apache.james.mime4j.Log; 24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport org.apache.james.mime4j.LogFactory; 25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook//END android-changed 26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport org.apache.james.mime4j.util.CharsetUtil; 27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.ByteArrayInputStream; 29bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.ByteArrayOutputStream; 30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.IOException; 31bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.io.UnsupportedEncodingException; 32bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/** 34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Static methods for decoding strings, byte arrays and encoded words. 35bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 36bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $ 38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic class DecoderUtil { 40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static Log log = LogFactory.getLog(DecoderUtil.class); 41bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Decodes a string containing quoted-printable encoded data. 44bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param s the string to decode. 46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the decoded bytes. 47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static byte[] decodeBaseQuotedPrintable(String s) { 49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ByteArrayOutputStream baos = new ByteArrayOutputStream(); 50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 51bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook try { 52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook byte[] bytes = s.getBytes("US-ASCII"); 53bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 54bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook QuotedPrintableInputStream is = new QuotedPrintableInputStream( 55bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new ByteArrayInputStream(bytes)); 56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int b = 0; 58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while ((b = is.read()) != -1) { 59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook baos.write(b); 60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } catch (IOException e) { 62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* 63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * This should never happen! 64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.error(e); 66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return baos.toByteArray(); 69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Decodes a string containing base64 encoded data. 73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param s the string to decode. 75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the decoded bytes. 76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static byte[] decodeBase64(String s) { 78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ByteArrayOutputStream baos = new ByteArrayOutputStream(); 79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook try { 81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook byte[] bytes = s.getBytes("US-ASCII"); 82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Base64InputStream is = new Base64InputStream( 84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new ByteArrayInputStream(bytes)); 85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int b = 0; 87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while ((b = is.read()) != -1) { 88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook baos.write(b); 89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } catch (IOException e) { 91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* 92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * This should never happen! 93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.error(e); 95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return baos.toByteArray(); 98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Decodes an encoded word encoded with the 'B' encoding (described in 102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * RFC 2047) found in a header field body. 103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param encodedWord the encoded word to decode. 105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param charset the Java charset to use. 106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the decoded string. 107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @throws UnsupportedEncodingException if the given Java charset isn't 108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * supported. 109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static String decodeB(String encodedWord, String charset) 111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook throws UnsupportedEncodingException { 112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return new String(decodeBase64(encodedWord), charset); 114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Decodes an encoded word encoded with the 'Q' encoding (described in 118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * RFC 2047) found in a header field body. 119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param encodedWord the encoded word to decode. 121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param charset the Java charset to use. 122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the decoded string. 123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @throws UnsupportedEncodingException if the given Java charset isn't 124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * supported. 125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static String decodeQ(String encodedWord, String charset) 127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook throws UnsupportedEncodingException { 128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* 130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Replace _ with =20 131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook StringBuffer sb = new StringBuffer(); 133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook for (int i = 0; i < encodedWord.length(); i++) { 134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook char c = encodedWord.charAt(i); 135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (c == '_') { 136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append("=20"); 137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(c); 139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return new String(decodeBaseQuotedPrintable(sb.toString()), charset); 143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Decodes a string containing encoded words as defined by RFC 2047. 147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Encoded words in have the form 148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for 149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * quoted-printable and 'B' or 'b' for Base64. 150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J 152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param body the string to decode. 154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the decoded string. 155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static String decodeEncodedWords(String body) { 157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // ANDROID: Most strings will not include "=?" so a quick test can prevent unneeded 159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // object creation. This could also be handled via lazy creation of the StringBuilder. 160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (body.indexOf("=?") == -1) { 161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return body; 162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int previousEnd = 0; 165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook boolean previousWasEncoded = false; 166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook StringBuilder sb = new StringBuilder(); 168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while (true) { 170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int begin = body.indexOf("=?", previousEnd); 171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // ANDROID: The mime4j original version has an error here. It gets confused if 173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // the encoded string begins with an '=' (just after "?Q?"). This patch seeks forward 174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // to find the two '?' in the "header", before looking for the final "?=". 175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int endScan = begin + 2; 176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (begin != -1) { 177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int qm1 = body.indexOf('?', endScan + 2); 178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int qm2 = body.indexOf('?', qm1 + 1); 179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (qm2 != -1) { 180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook endScan = qm2 + 1; 181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int end = begin == -1 ? -1 : body.indexOf("?=", endScan); 185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (end == -1) { 186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (previousEnd == 0) 187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return body; 188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(body.substring(previousEnd)); 190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return sb.toString(); 191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook end += 2; 193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String sep = body.substring(previousEnd, begin); 195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String decoded = decodeEncodedWord(body, begin, end); 197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (decoded == null) { 198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(sep); 199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(body.substring(begin, end)); 200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) { 202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(sep); 203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sb.append(decoded); 205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook previousEnd = end; 208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook previousWasEncoded = decoded != null; 209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // return null on error 213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static String decodeEncodedWord(String body, int begin, int end) { 214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int qm1 = body.indexOf('?', begin + 2); 215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (qm1 == end - 2) 216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int qm2 = body.indexOf('?', qm1 + 1); 219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (qm2 == end - 2) 220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String mimeCharset = body.substring(begin + 2, qm1); 223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String encoding = body.substring(qm1 + 1, qm2); 224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String encodedText = body.substring(qm2 + 1, end - 2); 225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String charset = CharsetUtil.toJavaCharset(mimeCharset); 227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (charset == null) { 228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("MIME charset '" + mimeCharset + "' in encoded word '" 230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + body.substring(begin, end) + "' doesn't have a " 231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + "corresponding Java charset"); 232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else if (!CharsetUtil.isDecodingSupported(charset)) { 235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("Current JDK doesn't support decoding of charset '" 237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + charset + "' (MIME charset '" + mimeCharset 238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + "' in encoded word '" + body.substring(begin, end) 239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + "')"); 240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (encodedText.length() == 0) { 245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("Missing encoded text in encoded word: '" 247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + body.substring(begin, end) + "'"); 248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook try { 253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (encoding.equalsIgnoreCase("Q")) { 254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return DecoderUtil.decodeQ(encodedText, charset); 255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else if (encoding.equalsIgnoreCase("B")) { 256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return DecoderUtil.decodeB(encodedText, charset); 257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("Warning: Unknown encoding in encoded word '" 260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + body.substring(begin, end) + "'"); 261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } catch (UnsupportedEncodingException e) { 265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // should not happen because of isDecodingSupported check above 266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("Unsupported encoding in encoded word '" 268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + body.substring(begin, end) + "'", e); 269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } catch (RuntimeException e) { 272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (log.isWarnEnabled()) { 273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook log.warn("Could not decode encoded word '" 274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + body.substring(begin, end) + "'", e); 275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook} 280