14fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy/**************************************************************** 24fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Licensed to the Apache Software Foundation (ASF) under one * 34fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * or more contributor license agreements. See the NOTICE file * 44fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * distributed with this work for additional information * 54fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * regarding copyright ownership. The ASF licenses this file * 64fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * to you under the Apache License, Version 2.0 (the * 74fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * "License"); you may not use this file except in compliance * 84fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * with the License. You may obtain a copy of the License at * 94fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * * 104fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * http://www.apache.org/licenses/LICENSE-2.0 * 114fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * * 124fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Unless required by applicable law or agreed to in writing, * 134fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * software distributed under the License is distributed on an * 144fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 154fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * KIND, either express or implied. See the License for the * 164fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * specific language governing permissions and limitations * 174fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * under the License. * 184fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy ****************************************************************/ 194fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 204fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedypackage org.apache.james.mime4j.decoder; 214fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 224fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy//BEGIN android-changed: Stubbing out logging 234fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport org.apache.james.mime4j.Log; 244fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport org.apache.james.mime4j.LogFactory; 254fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy//END android-changed 264fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport org.apache.james.mime4j.util.CharsetUtil; 274fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 284fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport java.io.ByteArrayInputStream; 294fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport java.io.ByteArrayOutputStream; 304fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport java.io.IOException; 314fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedyimport java.io.UnsupportedEncodingException; 324fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 334fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy/** 344fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Static methods for decoding strings, byte arrays and encoded words. 354fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 364fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 374fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $ 384fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 394fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedypublic class DecoderUtil { 404fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy private static Log log = LogFactory.getLog(DecoderUtil.class); 414fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 424fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /** 434fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Decodes a string containing quoted-printable encoded data. 444fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 454fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param s the string to decode. 464fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @return the decoded bytes. 474fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 484fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy public static byte[] decodeBaseQuotedPrintable(String s) { 494fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy ByteArrayOutputStream baos = new ByteArrayOutputStream(); 504fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 514fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy try { 524fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy byte[] bytes = s.getBytes("US-ASCII"); 534fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 544fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy QuotedPrintableInputStream is = new QuotedPrintableInputStream( 554fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy new ByteArrayInputStream(bytes)); 564fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 574fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int b = 0; 584fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy while ((b = is.read()) != -1) { 594fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy baos.write(b); 604fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 614fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } catch (IOException e) { 624fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /* 634fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * This should never happen! 644fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 654fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.error(e); 664fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 674fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 684fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return baos.toByteArray(); 694fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 704fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 714fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /** 724fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Decodes a string containing base64 encoded data. 734fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 744fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param s the string to decode. 754fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @return the decoded bytes. 764fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 774fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy public static byte[] decodeBase64(String s) { 784fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy ByteArrayOutputStream baos = new ByteArrayOutputStream(); 794fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 804fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy try { 814fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy byte[] bytes = s.getBytes("US-ASCII"); 824fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 834fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy Base64InputStream is = new Base64InputStream( 844fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy new ByteArrayInputStream(bytes)); 854fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 864fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int b = 0; 874fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy while ((b = is.read()) != -1) { 884fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy baos.write(b); 894fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 904fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } catch (IOException e) { 914fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /* 924fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * This should never happen! 934fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 944fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.error(e); 954fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 964fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 974fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return baos.toByteArray(); 984fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 994fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1004fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /** 1014fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Decodes an encoded word encoded with the 'B' encoding (described in 1024fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * RFC 2047) found in a header field body. 1034fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 1044fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param encodedWord the encoded word to decode. 1054fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param charset the Java charset to use. 1064fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @return the decoded string. 1074fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @throws UnsupportedEncodingException if the given Java charset isn't 1084fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * supported. 1094fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 1104fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy public static String decodeB(String encodedWord, String charset) 1114fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy throws UnsupportedEncodingException { 1124fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1134fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return new String(decodeBase64(encodedWord), charset); 1144fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1154fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1164fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /** 1174fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Decodes an encoded word encoded with the 'Q' encoding (described in 1184fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * RFC 2047) found in a header field body. 1194fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 1204fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param encodedWord the encoded word to decode. 1214fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param charset the Java charset to use. 1224fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @return the decoded string. 1234fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @throws UnsupportedEncodingException if the given Java charset isn't 1244fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * supported. 1254fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 1264fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy public static String decodeQ(String encodedWord, String charset) 1274fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy throws UnsupportedEncodingException { 1284fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1294fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /* 1304fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Replace _ with =20 1314fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 1324fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy StringBuffer sb = new StringBuffer(); 1334fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy for (int i = 0; i < encodedWord.length(); i++) { 1344fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy char c = encodedWord.charAt(i); 1354fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (c == '_') { 1364fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append("=20"); 1374fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } else { 1384fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append(c); 1394fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1404fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1414fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1424fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return new String(decodeBaseQuotedPrintable(sb.toString()), charset); 1434fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1444fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1454fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy /** 1464fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Decodes a string containing encoded words as defined by RFC 2047. 1474fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * Encoded words in have the form 1484fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for 1494fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * quoted-printable and 'B' or 'b' for Base64. 1504fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 1514fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J 1524fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * 1534fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @param body the string to decode. 1544fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy * @return the decoded string. 1554fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy */ 1564fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy public static String decodeEncodedWords(String body) { 1574fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1584fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // ANDROID: Most strings will not include "=?" so a quick test can prevent unneeded 1594fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // object creation. This could also be handled via lazy creation of the StringBuilder. 1604fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (body.indexOf("=?") == -1) { 1614fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return body; 1624fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1639c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner 1644fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int previousEnd = 0; 1654fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy boolean previousWasEncoded = false; 1664fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1674fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy StringBuilder sb = new StringBuilder(); 1684fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1694fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy while (true) { 1704fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int begin = body.indexOf("=?", previousEnd); 1719c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner 1724fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // ANDROID: The mime4j original version has an error here. It gets confused if 1734fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // the encoded string begins with an '=' (just after "?Q?"). This patch seeks forward 1744fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // to find the two '?' in the "header", before looking for the final "?=". 1759c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (begin == -1) { 1769c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner break; 1774fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1789c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner int qm1 = body.indexOf('?', begin + 2); 1799c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (qm1 == -1) { 1809c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner break; 1819c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner } 1829c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner int qm2 = body.indexOf('?', qm1 + 1); 1839c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (qm2 == -1) { 1849c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner break; 1859c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner } 1869c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner int end = body.indexOf("?=", qm2 + 1); 1874fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (end == -1) { 1889c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner break; 1894fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 1904fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy end += 2; 1914fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1924fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String sep = body.substring(previousEnd, begin); 1934fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 1944fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String decoded = decodeEncodedWord(body, begin, end); 1954fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (decoded == null) { 1964fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append(sep); 1974fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append(body.substring(begin, end)); 1984fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } else { 1994fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) { 2004fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append(sep); 2014fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2024fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy sb.append(decoded); 2034fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2044fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2054fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy previousEnd = end; 2064fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy previousWasEncoded = decoded != null; 2074fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2089c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner 2099c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (previousEnd == 0) 2109c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner return body; 2119c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner 2129c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner sb.append(body.substring(previousEnd)); 2139c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner return sb.toString(); 2144fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2154fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2169c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner // return null on error. Begin is index of '=?' in body. 2179c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner public static String decodeEncodedWord(String body, int begin, int end) { 2189c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner // Skip the '?=' chars in body and scan forward from there for next '?' 2194fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int qm1 = body.indexOf('?', begin + 2); 2209c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (qm1 == -1 || qm1 == end - 2) 2214fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2224fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2234fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy int qm2 = body.indexOf('?', qm1 + 1); 2249c4bcbc46a3530c596b6c2bade048f2971a8c69fJay Shrauner if (qm2 == -1 || qm2 == end - 2) 2254fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2264fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2274fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String mimeCharset = body.substring(begin + 2, qm1); 2284fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String encoding = body.substring(qm1 + 1, qm2); 2294fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String encodedText = body.substring(qm2 + 1, end - 2); 2304fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2314fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy String charset = CharsetUtil.toJavaCharset(mimeCharset); 2324fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (charset == null) { 2334fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2344fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("MIME charset '" + mimeCharset + "' in encoded word '" 2354fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + body.substring(begin, end) + "' doesn't have a " 2364fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + "corresponding Java charset"); 2374fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2384fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2394fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } else if (!CharsetUtil.isDecodingSupported(charset)) { 2404fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2414fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("Current JDK doesn't support decoding of charset '" 2424fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + charset + "' (MIME charset '" + mimeCharset 2434fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + "' in encoded word '" + body.substring(begin, end) 2444fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + "')"); 2454fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2464fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2474fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2484fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2494fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (encodedText.length() == 0) { 2504fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2514fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("Missing encoded text in encoded word: '" 2524fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + body.substring(begin, end) + "'"); 2534fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2544fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2554fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2564fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy 2574fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy try { 2584fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (encoding.equalsIgnoreCase("Q")) { 2594fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return DecoderUtil.decodeQ(encodedText, charset); 2604fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } else if (encoding.equalsIgnoreCase("B")) { 2614fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return DecoderUtil.decodeB(encodedText, charset); 2624fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } else { 2634fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2644fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("Warning: Unknown encoding in encoded word '" 2654fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + body.substring(begin, end) + "'"); 2664fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2674fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2684fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2694fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } catch (UnsupportedEncodingException e) { 2704fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy // should not happen because of isDecodingSupported check above 2714fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2724fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("Unsupported encoding in encoded word '" 2734fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + body.substring(begin, end) + "'", e); 2744fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2754fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2764fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } catch (RuntimeException e) { 2774fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy if (log.isWarnEnabled()) { 2784fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy log.warn("Could not decode encoded word '" 2794fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy + body.substring(begin, end) + "'", e); 2804fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2814fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy return null; 2824fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2834fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy } 2844fa0a3295bcacbdcd6a9e7709cf17aa5adb90356Scott Kennedy} 285