git clone of logicmail with some fixes/features added

Fixed attachment filename encoding and decoding issues, added ISO-8859-2 to the StringFactory, and increased scope of the MIMEInputStream bug workaround preprocessing to cover message part headers and local message file loading (refs #352)

git-svn-id: https://logicmail.svn.sourceforge.net/svnroot/logicmail/trunk@925 5c734088-3d25-0410-9155-b3c3832efda5

octorian d42489b6 acf62197

+319 -88
+3 -2
LogicMail/src/org/logicprobe/LogicMail/mail/LocalMessageRequest.java
··· 31 31 32 32 package org.logicprobe.LogicMail.mail; 33 33 34 - import java.io.ByteArrayInputStream; 35 34 import java.io.IOException; 35 + import java.io.InputStream; 36 36 import java.util.Enumeration; 37 37 import java.util.Hashtable; 38 38 ··· 92 92 93 93 // Parse the message source 94 94 Hashtable contentMap = new Hashtable(); 95 - MimeMessagePart rootPart = MailMessageParser.parseRawMessage(contentMap, new ByteArrayInputStream(messageSource.getBytes())); 95 + InputStream messageStream = MailMessageParser.convertMessageResultToStream(messageSource); 96 + MimeMessagePart rootPart = MailMessageParser.parseRawMessage(contentMap, messageStream); 96 97 message = new Message(rootPart); 97 98 Enumeration e = contentMap.keys(); 98 99 while(e.hasMoreElements()) {
+1 -1
LogicMail/src/org/logicprobe/LogicMail/mail/imap/ImapParser.java
··· 458 458 if (key.equalsIgnoreCase(CHARSET)) { 459 459 sec.charset = value; 460 460 } else if (key.equalsIgnoreCase(NAME)) { 461 - sec.name = value; 461 + sec.name = StringParser.parseEncodedHeader(value); 462 462 } 463 463 } 464 464 }
+1 -58
LogicMail/src/org/logicprobe/LogicMail/mail/pop/PopClient.java
··· 31 31 32 32 package org.logicprobe.LogicMail.mail.pop; 33 33 34 - import java.io.ByteArrayInputStream; 35 34 import java.io.IOException; 36 35 import java.io.InputStream; 37 36 import java.util.Enumeration; 38 37 import java.util.Hashtable; 39 38 40 39 import net.rim.device.api.util.Arrays; 41 - import net.rim.device.api.util.DataBuffer; 42 40 import net.rim.device.api.util.ToIntHashtable; 43 41 44 42 import org.logicprobe.LogicMail.conf.AccountConfig; ··· 83 81 private String password; 84 82 private boolean openStarted; 85 83 86 - private static final byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' }; 87 84 private static String CAPA_STLS = "STLS"; 88 85 89 86 /** ··· 521 518 // the special case where maxLines == retrievedLines and we are 522 519 // downloading a single-part message with no separators. 523 520 524 - InputStream inputStream = convertMessageResultToStream(retrResult); 521 + InputStream inputStream = MailMessageParser.convertMessageResultToStream(retrResult); 525 522 526 523 Hashtable contentMap = new Hashtable(); 527 524 MimeMessagePart rootPart = MailMessageParser.parseRawMessage(contentMap, inputStream); ··· 554 551 } 555 552 } 556 553 557 - /** 558 - * Convert the server result to an InputStream wrapping a byte[] buffer 559 - * that includes the CRLF markers that were stripped out by the socket 560 - * reading code, and has any other necessary pre-processing applied. 561 - * 562 - * @param resultLines the lines of message data returned by the server 563 - * @return the input stream to be passed to the parser code 564 - */ 565 - private static InputStream convertMessageResultToStream(byte[][] resultLines) { 566 - DataBuffer buf = new DataBuffer(); 567 - 568 - boolean inHeaders = true; 569 - for(int i=0; i<resultLines.length; i++) { 570 - if(inHeaders) { 571 - // Special logic to unfold message headers and replace HTAB 572 - // indentations in folded headers with spaces. 573 - // This is a workaround for a bug in MIMEInputStream that 574 - // causes it to fail to parse certain messages with folded 575 - // headers. (The bug appears to be fixed in OS 6.0, but the 576 - // workaround is always invoked because it should not have 577 - // any harmful side-effects.) 578 - if(resultLines[i].length == 0) { 579 - inHeaders = false; 580 - if(i > 0) { 581 - buf.write(CRLF); 582 - } 583 - buf.write(CRLF); 584 - } 585 - else if(resultLines[i][0] == (byte)'\t') { 586 - resultLines[i][0] = (byte)' '; 587 - buf.write(resultLines[i]); 588 - } 589 - else if(resultLines[i][0] == (byte)' ') { 590 - buf.write(resultLines[i]); 591 - } 592 - else { 593 - if(i > 0) { 594 - buf.write(CRLF); 595 - } 596 - buf.write(resultLines[i]); 597 - } 598 - } 599 - else { 600 - buf.write(resultLines[i]); 601 - buf.write(CRLF); 602 - } 603 - } 604 - 605 - ByteArrayInputStream inputStream = new ByteArrayInputStream( 606 - buf.getArray(), buf.getArrayStart(), buf.getArrayLength()); 607 - 608 - return inputStream; 609 - } 610 - 611 554 /* (non-Javadoc) 612 555 * @see org.logicprobe.LogicMail.mail.IncomingMailClient#deleteMessage(org.logicprobe.LogicMail.mail.MessageToken) 613 556 */
+3 -2
LogicMail/src/org/logicprobe/LogicMail/message/MessageMimeConverter.java
··· 351 351 ContentPart contentPart = (ContentPart)part; 352 352 String name = contentPart.getName(); 353 353 if(name.length() > 0) { 354 - mimeStream.addContentTypeParameter("name", name); 354 + mimeStream.addContentTypeParameter("name", "\"" + StringParser.createEncodedHeader(10, name) + "\""); 355 355 if(contentPart.getDisposition().equalsIgnoreCase("attachment")) { 356 - mimeStream.addHeaderField("Content-Disposition: attachment; filename=\"" + name + "\""); 356 + mimeStream.addHeaderField("Content-Disposition: attachment; filename=\"" 357 + + StringParser.createEncodedHeader(44, name) + "\""); 357 358 } 358 359 } 359 360 }
+1 -4
LogicMail/src/org/logicprobe/LogicMail/ui/CompositionScreen.java
··· 1086 1086 String mimeSubtype = mimeType.substring(p + 1); 1087 1087 mimeType = mimeType.substring(0, p); 1088 1088 1089 - p = fileUrl.lastIndexOf('/'); 1090 - String fileName = fileUrl.substring(p + 1); 1091 - 1092 1089 MimeMessagePart part = 1093 1090 MimeMessagePartFactory.createMimeMessagePart( 1094 1091 mimeType, 1095 1092 mimeSubtype, 1096 - fileName, 1093 + fileConnection.getName(), 1097 1094 null, // encoding 1098 1095 null, // param 1099 1096 "attachment", // disposition
+130 -1
LogicMail/src/org/logicprobe/LogicMail/util/MailMessageParser.java
··· 31 31 package org.logicprobe.LogicMail.util; 32 32 33 33 import net.rim.device.api.io.IOUtilities; 34 + import net.rim.device.api.io.LineReader; 34 35 import net.rim.device.api.io.SharedInputStream; 35 36 import net.rim.device.api.mime.MIMEInputStream; 36 37 import net.rim.device.api.mime.MIMEParsingException; 37 38 import net.rim.device.api.system.EventLogger; 38 39 import net.rim.device.api.util.Arrays; 40 + import net.rim.device.api.util.DataBuffer; 39 41 40 42 import org.logicprobe.LogicMail.AppInfo; 41 43 import org.logicprobe.LogicMail.message.MimeMessageContent; ··· 47 49 import org.logicprobe.LogicMail.message.TextPart; 48 50 import org.logicprobe.LogicMail.message.UnsupportedContentException; 49 51 52 + import java.io.ByteArrayInputStream; 50 53 import java.io.IOException; 51 54 import java.io.InputStream; 52 55 53 56 import java.util.Calendar; 54 57 import java.util.Hashtable; 58 + import java.util.Vector; 55 59 56 60 57 61 /** ··· 60 64 */ 61 65 public class MailMessageParser { 62 66 private static String strCRLF = "\r\n"; 67 + private static final byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' }; 68 + private static final byte[] CONTENT_TYPE_KEY = "Content-Type:".getBytes(); 69 + private static final byte[] BOUNDARY_EQ = "boundary=".getBytes(); 63 70 64 71 private MailMessageParser() { 65 72 } ··· 182 189 183 190 return addresses; 184 191 } 192 + 193 + /** 194 + * Convert string-format raw message source to an InputStream that is 195 + * compatible with {@link #parseRawMessage(Hashtable, InputStream)}. 196 + * 197 + * @param messageSource the message source 198 + * @return the input stream to be passed to the parser code 199 + */ 200 + public static InputStream convertMessageResultToStream(String messageSource) { 201 + ByteArrayInputStream inputStream = new ByteArrayInputStream(messageSource.getBytes()); 202 + 203 + Vector lines = new Vector(); 204 + try { 205 + LineReader reader = new LineReader(inputStream); 206 + byte[] line; 207 + while((line = reader.readLine()) != null) { 208 + lines.addElement(line); 209 + } 210 + } catch (IOException e) { 211 + EventLogger.logEvent(AppInfo.GUID, 212 + ("Error converting message to stream: " + e.getMessage()).getBytes(), 213 + EventLogger.WARNING); 214 + } 215 + 216 + byte[][] resultLines = new byte[lines.size()][]; 217 + lines.copyInto(resultLines); 218 + return convertMessageResultToStream(resultLines); 219 + } 220 + 221 + /** 222 + * Convert the server result to an InputStream wrapping a byte[] buffer 223 + * that includes the CRLF markers that were stripped out by the socket 224 + * reading code, and has any other necessary pre-processing applied. 225 + * 226 + * @param resultLines the lines of message data returned by the server 227 + * @return the input stream to be passed to the parser code 228 + */ 229 + public static InputStream convertMessageResultToStream(byte[][] resultLines) { 230 + DataBuffer buf = new DataBuffer(); 231 + 232 + boolean inHeaders = true; 233 + boolean inInitialHeaders = true; 234 + boolean firstHeaderLine = true; 235 + byte[] boundary = null; 236 + for(int i=0; i<resultLines.length; i++) { 237 + if(inHeaders) { 238 + // Special logic to unfold message headers and replace HTAB 239 + // indentations in folded headers with spaces. 240 + // This is a workaround for a bug in MIMEInputStream that 241 + // causes it to fail to parse certain messages with folded 242 + // headers. (The bug appears to be fixed in OS 6.0, but the 243 + // workaround is always invoked because it should not have 244 + // any harmful side-effects.) 245 + if(resultLines[i].length == 0) { 246 + inHeaders = false; 247 + if(!firstHeaderLine) { 248 + buf.write(CRLF); 249 + } 250 + buf.write(CRLF); 251 + 252 + if(inInitialHeaders) { 253 + boundary = getContentBoundary(buf.getArray(), buf.getArrayStart(), buf.getArrayLength()); 254 + inInitialHeaders = false; 255 + } 256 + } 257 + else if(resultLines[i][0] == (byte)'\t' || resultLines[i][0] == (byte)' ') { 258 + for(int j=1; j<resultLines[i].length; j++) { 259 + if(resultLines[i][j] != (byte)'\t' && resultLines[i][j] != (byte)' ') { 260 + buf.write((byte)' '); 261 + buf.write(resultLines[i], j, resultLines[i].length - j); 262 + break; 263 + } 264 + } 265 + } 266 + else { 267 + if(!firstHeaderLine) { 268 + buf.write(CRLF); 269 + } 270 + buf.write(resultLines[i]); 271 + } 272 + if(firstHeaderLine) { firstHeaderLine = false; } 273 + } 274 + else { 275 + buf.write(resultLines[i]); 276 + buf.write(CRLF); 277 + if(Arrays.equals(resultLines[i], boundary)) { 278 + inHeaders = true; 279 + firstHeaderLine = true; 280 + } 281 + } 282 + } 283 + 284 + ByteArrayInputStream inputStream = new ByteArrayInputStream( 285 + buf.getArray(), buf.getArrayStart(), buf.getArrayLength()); 286 + 287 + return inputStream; 288 + } 289 + 290 + private static byte[] getContentBoundary(byte[] buf, int offset, int length) { 291 + int p = StringArrays.indexOf(buf, CONTENT_TYPE_KEY, offset, length, true); 292 + if(p == -1) { return null; } else { p += CONTENT_TYPE_KEY.length; } 293 + 294 + int q = StringArrays.indexOf(buf, CRLF, p, length, false); 295 + if(q == -1) { return null; } 296 + 297 + int r = StringArrays.indexOf(buf, BOUNDARY_EQ, p, q, true); 298 + if(r == -1) { return null; } else { r += BOUNDARY_EQ.length; } 299 + 300 + int s = StringArrays.indexOf(buf, (byte)' ', r); 301 + if(s == -1 || s > q) { s = q; } 302 + 303 + if(buf[r] == (byte)'\"') { r++; } 304 + if(buf[s - 1] == (byte)'\"') { s--; } 305 + if((s - r) <= 0) { return null; } 306 + 307 + byte[] result = new byte[(s - r) + 2]; 308 + result[0] = (byte)'-'; 309 + result[1] = (byte)'-'; 310 + System.arraycopy(buf, r, result, 2, s - r); 311 + 312 + return result; 313 + } 185 314 186 315 /** 187 316 * Parses the raw message body. ··· 236 365 String subtype = mimeType.substring(mimeType.indexOf('/') + 1); 237 366 String encoding = mimeInputStream.getHeader("Content-Transfer-Encoding"); 238 367 String charset = mimeInputStream.getContentTypeParameter("charset"); 239 - String name = mimeInputStream.getContentTypeParameter("name"); 368 + String name = StringParser.parseEncodedHeader(mimeInputStream.getContentTypeParameter("name"), false); 240 369 String disposition = mimeInputStream.getHeader("Content-Disposition"); 241 370 String contentId = mimeInputStream.getHeader("Content-ID"); 242 371
+62 -11
LogicMail/src/org/logicprobe/LogicMail/util/StringArrays.java
··· 30 30 */ 31 31 package org.logicprobe.LogicMail.util; 32 32 33 + import net.rim.device.api.util.CharacterUtilities; 34 + 33 35 /** 34 36 * Provides utility methods that operate on <code>byte</code> arrays, with 35 37 * similar functionality to common <code>String</code> methods. ··· 92 94 * @throws NullPointerException if <code>str</code> is <code>null</code> 93 95 */ 94 96 public static int indexOf(byte[] array, byte[] str, int fromIndex) { 97 + return indexOf(array, str, fromIndex, array.length, false); 98 + } 99 + 100 + /** 101 + * Returns the index within this string of the first occurrence of the 102 + * specified substring, starting at the specified index. 103 + * 104 + * @param array the array representing the string. 105 + * @param str the substring to search for. 106 + * @param fromIndex the index to start the search from. 107 + * @param arrayLength the length of the data in the array that is valid for 108 + * the search 109 + * @param ignoreCase true, if the search should be case insensitive 110 + * @return If the string argument occurs as a substring within this object 111 + * at a starting index no smaller than <code>fromIndex</code>, then the 112 + * index of the first character of the first such substring is returned. 113 + * If it does not occur as a substring starting at <code>fromIndex</code> 114 + * or beyond, <code>-1</code> is returned. 115 + * @throws NullPointerException if <code>str</code> is <code>null</code> 116 + */ 117 + public static int indexOf(byte[] array, byte[] str, int fromIndex, int arrayLength, boolean ignoreCase) { 95 118 if(str == null) { 96 119 throw new NullPointerException(); 97 120 } 98 - if(fromIndex + str.length > array.length) { 121 + if(fromIndex + str.length > arrayLength) { 99 122 return -1; 100 123 } 101 124 102 - int max = array.length - str.length; 125 + int max = arrayLength - str.length; 126 + 127 + if(ignoreCase) { 128 + for (int i = fromIndex; i <= max; i++) { 129 + if (!equalsIgnoreCase(array[i], str[0])) { 130 + while (++i <= max && !equalsIgnoreCase(array[i], str[0])); 131 + } 132 + 133 + if (i <= max) { 134 + int j = i + 1; 135 + int end = j + str.length - 1; 136 + for (int k = 1; j < end && equalsIgnoreCase(array[j], str[k]); j++, k++); 103 137 104 - for (int i = fromIndex; i <= max; i++) { 105 - if (array[i] != str[0]) { 106 - while (++i <= max && array[i] != str[0]); 138 + if (j == end) { 139 + return i; 140 + } 141 + } 107 142 } 143 + } 144 + else { 145 + for (int i = fromIndex; i <= max; i++) { 146 + if (array[i] != str[0]) { 147 + while (++i <= max && array[i] != str[0]); 148 + } 108 149 109 - if (i <= max) { 110 - int j = i + 1; 111 - int end = j + str.length - 1; 112 - for (int k = 1; j < end && array[j] == str[k]; j++, k++); 150 + if (i <= max) { 151 + int j = i + 1; 152 + int end = j + str.length - 1; 153 + for (int k = 1; j < end && array[j] == str[k]; j++, k++); 113 154 114 - if (j == end) { 115 - return i; 155 + if (j == end) { 156 + return i; 157 + } 116 158 } 117 159 } 118 160 } 119 161 return -1; 162 + } 163 + 164 + private static boolean equalsIgnoreCase(byte ch1, byte ch2) { 165 + if(CharacterUtilities.isLetter((char)ch1) && CharacterUtilities.isLetter((char)ch2)) { 166 + return CharacterUtilities.toUpperCase((char)ch1) == CharacterUtilities.toUpperCase((char)ch2); 167 + } 168 + else { 169 + return ch1 == ch2; 170 + } 120 171 } 121 172 122 173 /**
+84 -4
LogicMail/src/org/logicprobe/LogicMail/util/StringFactory.java
··· 54 54 * <li><b>&quot;Windows-1256&quot;</b> - Arabic</li> 55 55 * <li><b>&quot;Windows-1257&quot;</b> - Estonian, Latvian and Lithuanian</li> 56 56 * <li><b>&quot;Windows-1258&quot;</b> - Vietnamese</li> 57 + * <li><b>&quot;ISO-8859-2&quot;</b> - Central European and Eastern European languages that use Latin script</li> 57 58 * <li><b>&quot;KOI8-R&quot;</b> - Russian</li> 58 59 * <li><b>&quot;KOI8-U&quot;</b> - Ukrainian</li> 59 60 * </ul> ··· 85 86 charsetMappingTables.put("WINDOWS-1257", mappingTableCP1257); 86 87 charsetMappingTables.put("CP1258", mappingTableCP1258); 87 88 charsetMappingTables.put("WINDOWS-1258", mappingTableCP1258); 89 + charsetMappingTables.put("ISO-8859-2", mappingTable8859_2); 88 90 charsetMappingTables.put("KOI8-R", mappingTableKOI8R); 89 91 charsetMappingTables.put("KOI8-U", mappingTableKOI8U); 90 92 } ··· 117 119 * @see String#String(byte[], String) 118 120 */ 119 121 public static String create(byte[] bytes, String charset) throws UnsupportedEncodingException { 122 + return create(bytes, 0, bytes.length, charset); 123 + } 124 + 125 + /** 126 + * Construct a new <tt>String</tt> by converting the specified array of 127 + * bytes using the specified character encoding. This method will first 128 + * attempt to use the constructor of the <tt>String</tt> class that takes 129 + * a character encoding parameter. If <tt>String</tt> does not directly 130 + * recognize the encoding, this method will then attempt to use a variety 131 + * of Unicode mapping tables. 132 + * 133 + * @param bytes The bytes to be converted into characters 134 + * @param off Index of the first byte to convert 135 + * @param len Number of bytes to convert 136 + * @param charset The name of a supported character encoding 137 + * @return Converted string representation 138 + * @throws UnsupportedEncodingException If the named character encoding is not supported 139 + * 140 + * @see String#String(byte[], String) 141 + */ 142 + public static String create(byte[] bytes, int off, int len, String charset) throws UnsupportedEncodingException { 120 143 if(charset == null || charset.length() == 0) { 121 144 throw new UnsupportedEncodingException(); 122 145 } 123 146 124 147 String result; 125 148 try { 126 - result = new String(bytes, charset); 149 + result = new String(bytes, off, len, charset); 127 150 } catch (UnsupportedEncodingException exp) { 128 - result = getInstance().createFromMappingTable(bytes, charset); 151 + result = getInstance().createFromMappingTable(bytes, off, len, charset); 129 152 } 130 153 return result; 131 154 } ··· 135 158 * bytes using the specified character encoding. 136 159 * 137 160 * @param bytes The bytes to be converted into characters 161 + * @param off Index of the first byte to convert 162 + * @param len Number of bytes to convert 138 163 * @param charset The name of a supported character encoding 139 164 * @return Converted string representation 140 165 * @throws UnsupportedEncodingException If the named character encoding is not supported 141 166 */ 142 - private String createFromMappingTable(byte[] bytes, String charset) throws UnsupportedEncodingException { 167 + private String createFromMappingTable(byte[] bytes, int off, int len, String charset) throws UnsupportedEncodingException { 143 168 char[] mappingTable = (char[])charsetMappingTables.get(charset.toUpperCase()); 144 169 if(mappingTable == null) { 145 170 throw new UnsupportedEncodingException(); 146 171 } 147 172 148 173 StringBuffer buf = new StringBuffer(); 149 - for(int i=0; i<bytes.length; i++) { 174 + for(int i=off; i<len; i++) { 150 175 buf.append(mappingTable[(int) bytes[i] & 0xFF]); 151 176 } 152 177 return buf.toString(); ··· 647 672 '\u00FC', '\u01B0', '\u20AB', '\u00FF' 648 673 }; 649 674 675 + /** 676 + * ISO-8859-2 to Unicode table 677 + * <pre> 678 + * Unicode version: 3.0 679 + * Table version: 1.0 680 + * Date: 07/27/1999 681 + * URL: http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-2.TXT 682 + * </pre> 683 + */ 684 + private static char[] mappingTable8859_2 = { 685 + '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', 686 + '\u0006', '\u0007', '\u0008', '\u0009', '\n', '\u000B', 687 + '\u000C', '\r', '\u000E', '\u000F', '\u0010', '\u0011', 688 + '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017', 689 + '\u0018', '\u0019', '\u001A', '\u001B', '\u001C', '\u001D', 690 + '\u001E', '\u001F', '\u0020', '\u0021', '\u0022', '\u0023', 691 + '\u0024', '\u0025', '\u0026', '\'', '\u0028', '\u0029', 692 + '\u002A', '\u002B', '\u002C', '\u002D', '\u002E', '\u002F', 693 + '\u0030', '\u0031', '\u0032', '\u0033', '\u0034', '\u0035', 694 + '\u0036', '\u0037', '\u0038', '\u0039', '\u003A', '\u003B', 695 + '\u003C', '\u003D', '\u003E', '\u003F', '\u0040', '\u0041', 696 + '\u0042', '\u0043', '\u0044', '\u0045', '\u0046', '\u0047', 697 + '\u0048', '\u0049', '\u004A', '\u004B', '\u004C', '\u004D', 698 + '\u004E', '\u004F', '\u0050', '\u0051', '\u0052', '\u0053', 699 + '\u0054', '\u0055', '\u0056', '\u0057', '\u0058', '\u0059', 700 + '\u005A', '\u005B', '\\', '\u005D', '\u005E', '\u005F', 701 + '\u0060', '\u0061', '\u0062', '\u0063', '\u0064', '\u0065', 702 + '\u0066', '\u0067', '\u0068', '\u0069', '\u006A', '\u006B', 703 + '\u006C', '\u006D', '\u006E', '\u006F', '\u0070', '\u0071', 704 + '\u0072', '\u0073', '\u0074', '\u0075', '\u0076', '\u0077', 705 + '\u0078', '\u0079', '\u007A', '\u007B', '\u007C', '\u007D', 706 + '\u007E', '\u007F', '\u0080', '\u0081', '\u0082', '\u0083', 707 + '\u0084', '\u0085', '\u0086', '\u0087', '\u0088', '\u0089', 708 + '\u008A', '\u008B', '\u008C', '\u008D', '\u008E', '\u008F', 709 + '\u0090', '\u0091', '\u0092', '\u0093', '\u0094', '\u0095', 710 + '\u0096', '\u0097', '\u0098', '\u0099', '\u009A', '\u009B', 711 + '\u009C', '\u009D', '\u009E', '\u009F', '\u00A0', '\u0104', 712 + '\u02D8', '\u0141', '\u00A4', '\u013D', '\u015A', '\u00A7', 713 + '\u00A8', '\u0160', '\u015E', '\u0164', '\u0179', '\u00AD', 714 + '\u017D', '\u017B', '\u00B0', '\u0105', '\u02DB', '\u0142', 715 + '\u00B4', '\u013E', '\u015B', '\u02C7', '\u00B8', '\u0161', 716 + '\u015F', '\u0165', '\u017A', '\u02DD', '\u017E', '\u017C', 717 + '\u0154', '\u00C1', '\u00C2', '\u0102', '\u00C4', '\u0139', 718 + '\u0106', '\u00C7', '\u010C', '\u00C9', '\u0118', '\u00CB', 719 + '\u011A', '\u00CD', '\u00CE', '\u010E', '\u0110', '\u0143', 720 + '\u0147', '\u00D3', '\u00D4', '\u0150', '\u00D6', '\u00D7', 721 + '\u0158', '\u016E', '\u00DA', '\u0170', '\u00DC', '\u00DD', 722 + '\u0162', '\u00DF', '\u0155', '\u00E1', '\u00E2', '\u0103', 723 + '\u00E4', '\u013A', '\u0107', '\u00E7', '\u010D', '\u00E9', 724 + '\u0119', '\u00EB', '\u011B', '\u00ED', '\u00EE', '\u010F', 725 + '\u0111', '\u0144', '\u0148', '\u00F3', '\u00F4', '\u0151', 726 + '\u00F6', '\u00F7', '\u0159', '\u016F', '\u00FA', '\u0171', 727 + '\u00FC', '\u00FD', '\u0163', '\u02D9' 728 + }; 729 + 650 730 /** 651 731 * KOI8-R (RFC1489) to Unicode table 652 732 * <pre>
+34 -5
LogicMail/src/org/logicprobe/LogicMail/util/StringParser.java
··· 504 504 * @return Processed unicode string 505 505 */ 506 506 public static String parseEncodedHeader(String text) { 507 + return parseEncodedHeader(text, true); 508 + } 509 + 510 + /** 511 + * Scans the provided string for blocks of text encoded according 512 + * to RFC2047, decodes them accordingly, and returns a new Unicode 513 + * string usable in the rest of the application. If the charset for 514 + * a block of text is not supported, then that block is not included 515 + * in the result. 516 + * 517 + * @param text The text to scan. 518 + * @param reencode true, if the text should be reencoded if it contains 519 + * characters outside of the US-ASCII 7bit range 520 + * @return Processed unicode string 521 + */ 522 + public static String parseEncodedHeader(String text, boolean reencode) { 507 523 // Quick check for null input 508 524 if (text == null) { 509 525 return null; 510 526 } 511 527 512 - text = reencodeStringIfNecessary(text); 528 + if(reencode) { 529 + text = reencodeStringIfNecessary(text); 530 + } 513 531 514 532 int size = text.length(); 515 533 ··· 756 774 } else if (encoding.charAt(0) == 'B') { 757 775 // Base64 758 776 try { 759 - result = new String( 760 - Base64InputStream.decode(encodedText), 761 - charset); 777 + byte[] decodedData = Base64InputStream.decode(encodedText); 778 + try { 779 + result = StringFactory.create(decodedData, charset); 780 + } catch (UnsupportedEncodingException e) { 781 + result = new String(decodedData); 782 + } 762 783 } catch (IOException e) { 763 784 result = ""; 764 785 } ··· 784 805 buf.append(' '); 785 806 786 807 appendEncodedHeaderSegment(buf, key.length() + 1, text); 808 + 809 + return buf.toString(); 810 + } 811 + 812 + public static String createEncodedHeader(int indent, String text) { 813 + StringBuffer buf = new StringBuffer(); 814 + 815 + appendEncodedHeaderSegment(buf, indent, text); 787 816 788 817 return buf.toString(); 789 818 } ··· 1225 1254 decodeQuotedPrintableDataImpl(buf, text, header); 1226 1255 String result; 1227 1256 try { 1228 - result = new String(buf.getArray(), buf.getArrayStart(), buf.getArrayLength(), charset); 1257 + result = StringFactory.create(buf.getArray(), buf.getArrayStart(), buf.getArrayLength(), charset); 1229 1258 } catch (UnsupportedEncodingException e) { 1230 1259 result = new String(buf.getArray(), buf.getArrayStart(), buf.getArrayLength()); 1231 1260 }