| 1 |  |  | 
| 2 |  |  | 
| 3 |  |  | 
| 4 |  |  | 
| 5 |  |  | 
| 6 |  |  | 
| 7 |  |  | 
| 8 |  |  | 
| 9 |  |  | 
| 10 |  |  | 
| 11 |  |  | 
| 12 |  |  | 
| 13 |  |  | 
| 14 |  |  | 
| 15 |  |  | 
| 16 |  |  | 
| 17 |  |  | 
| 18 |  | package javax.mail.internet; | 
| 19 |  |  | 
| 20 |  | import java.io.BufferedInputStream; | 
| 21 |  | import java.io.BufferedReader; | 
| 22 |  | import java.io.ByteArrayInputStream; | 
| 23 |  | import java.io.ByteArrayOutputStream; | 
| 24 |  | import java.io.IOException; | 
| 25 |  | import java.io.InputStream; | 
| 26 |  | import java.io.InputStreamReader; | 
| 27 |  | import java.io.OutputStream; | 
| 28 |  | import java.io.UnsupportedEncodingException; | 
| 29 |  | import java.util.HashMap; | 
| 30 |  | import java.util.Map; | 
| 31 |  | import java.util.NoSuchElementException; | 
| 32 |  | import java.util.StringTokenizer; | 
| 33 |  |  | 
| 34 |  | import javax.activation.DataHandler; | 
| 35 |  | import javax.activation.DataSource; | 
| 36 |  | import javax.mail.MessagingException; | 
| 37 |  |  | 
| 38 |  | import org.apache.geronimo.mail.util.ASCIIUtil; | 
| 39 |  | import org.apache.geronimo.mail.util.Base64; | 
| 40 |  | import org.apache.geronimo.mail.util.Base64DecoderStream; | 
| 41 |  | import org.apache.geronimo.mail.util.Base64Encoder; | 
| 42 |  | import org.apache.geronimo.mail.util.Base64EncoderStream; | 
| 43 |  | import org.apache.geronimo.mail.util.QuotedPrintableDecoderStream; | 
| 44 |  | import org.apache.geronimo.mail.util.QuotedPrintableEncoderStream; | 
| 45 |  | import org.apache.geronimo.mail.util.QuotedPrintableEncoder; | 
| 46 |  | import org.apache.geronimo.mail.util.QuotedPrintable; | 
| 47 |  | import org.apache.geronimo.mail.util.SessionUtil; | 
| 48 |  | import org.apache.geronimo.mail.util.UUDecoderStream; | 
| 49 |  | import org.apache.geronimo.mail.util.UUEncoderStream; | 
| 50 |  |  | 
| 51 |  |  | 
| 52 |  |  | 
| 53 |  |  | 
| 54 |  |  | 
| 55 |  |  | 
| 56 |  |  | 
| 57 |  | public class MimeUtility { | 
| 58 |  |  | 
| 59 |  | private static final String MIME_FOLDENCODEDWORDS = "mail.mime.foldencodedwords"; | 
| 60 |  | private static final String MIME_DECODE_TEXT_STRICT = "mail.mime.decodetext.strict"; | 
| 61 |  |  | 
| 62 | 0 | private MimeUtility() { | 
| 63 |  | } | 
| 64 |  |  | 
| 65 |  | public static final int ALL = -1; | 
| 66 |  |  | 
| 67 |  | private static String defaultJavaCharset; | 
| 68 |  | private static String escapedChars = "\"\\\r\n"; | 
| 69 |  | private static String linearWhiteSpace = " \t\r\n"; | 
| 70 |  |  | 
| 71 |  | private static String QP_WORD_SPECIALS = "=_?\"#$%&'(),.:;<>@[\\]^`{|}~"; | 
| 72 |  | private static String QP_TEXT_SPECIALS = "=_?"; | 
| 73 |  |  | 
| 74 |  |  | 
| 75 |  |  | 
| 76 |  | private static Map java2mime; | 
| 77 |  | private static Map mime2java; | 
| 78 |  |  | 
| 79 |  | static { | 
| 80 |  |  | 
| 81 | 1 | loadCharacterSetMappings(); | 
| 82 |  | } | 
| 83 |  |  | 
| 84 | 16 | public static InputStream decode(InputStream in, String encoding) throws MessagingException { | 
| 85 | 16 | encoding = encoding.toLowerCase(); | 
| 86 |  |  | 
| 87 |  |  | 
| 88 | 16 | if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { | 
| 89 | 4 | return in; | 
| 90 |  | } | 
| 91 | 12 | else if (encoding.equals("base64")) { | 
| 92 | 4 | return new Base64DecoderStream(in); | 
| 93 |  | } | 
| 94 |  |  | 
| 95 | 8 | else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { | 
| 96 | 5 | return new UUDecoderStream(in); | 
| 97 |  | } | 
| 98 | 3 | else if (encoding.equals("quoted-printable")) { | 
| 99 | 3 | return new QuotedPrintableDecoderStream(in); | 
| 100 |  | } | 
| 101 |  | else { | 
| 102 | 0 | throw new MessagingException("Unknown encoding " + encoding); | 
| 103 |  | } | 
| 104 |  | } | 
| 105 |  |  | 
| 106 |  |  | 
| 107 |  |  | 
| 108 |  |  | 
| 109 |  |  | 
| 110 |  |  | 
| 111 |  |  | 
| 112 |  |  | 
| 113 |  |  | 
| 114 |  |  | 
| 115 |  |  | 
| 116 |  |  | 
| 117 | 9 | public static String decodeText(String text) throws UnsupportedEncodingException { | 
| 118 |  |  | 
| 119 |  |  | 
| 120 | 9 | if (text.indexOf("=?") < 0) { | 
| 121 | 3 | return text; | 
| 122 |  | } | 
| 123 |  |  | 
| 124 |  |  | 
| 125 | 6 | if (!SessionUtil.getBooleanProperty(MIME_DECODE_TEXT_STRICT, true)) { | 
| 126 | 0 | return decodeTextNonStrict(text); | 
| 127 |  | } | 
| 128 |  |  | 
| 129 | 6 | int offset = 0; | 
| 130 | 6 | int endOffset = text.length(); | 
| 131 |  |  | 
| 132 | 6 | int startWhiteSpace = -1; | 
| 133 | 6 | int endWhiteSpace = -1; | 
| 134 |  |  | 
| 135 | 6 | StringBuffer decodedText = new StringBuffer(text.length()); | 
| 136 |  |  | 
| 137 | 6 | boolean previousTokenEncoded = false; | 
| 138 |  |  | 
| 139 | 6 | while (offset < endOffset) { | 
| 140 | 6 | char ch = text.charAt(offset); | 
| 141 |  |  | 
| 142 |  |  | 
| 143 | 6 | if (linearWhiteSpace.indexOf(ch) != -1) { | 
| 144 | 0 | startWhiteSpace = offset; | 
| 145 | 0 | while (offset < endOffset) { | 
| 146 |  |  | 
| 147 | 0 | ch = text.charAt(offset); | 
| 148 | 0 | if (linearWhiteSpace.indexOf(ch) != -1) { | 
| 149 | 0 | offset++; | 
| 150 |  | } | 
| 151 |  | else { | 
| 152 |  |  | 
| 153 |  |  | 
| 154 | 0 | endWhiteSpace = offset; | 
| 155 | 0 | break; | 
| 156 |  | } | 
| 157 |  | } | 
| 158 |  | } | 
| 159 |  | else { | 
| 160 |  |  | 
| 161 | 6 | int wordStart = offset; | 
| 162 |  |  | 
| 163 | 6 | while (offset < endOffset) { | 
| 164 |  |  | 
| 165 | 201 | ch = text.charAt(offset); | 
| 166 | 201 | if (linearWhiteSpace.indexOf(ch) == -1) { | 
| 167 | 201 | offset++; | 
| 168 |  | } | 
| 169 |  | else { | 
| 170 | 0 | break; | 
| 171 |  | } | 
| 172 |  |  | 
| 173 |  |  | 
| 174 |  | } | 
| 175 |  |  | 
| 176 | 6 | String word = text.substring(wordStart, offset); | 
| 177 |  |  | 
| 178 | 6 | if (word.startsWith("=?")) { | 
| 179 | 6 | try { | 
| 180 |  |  | 
| 181 | 6 | String decodedWord = decodeWord(word); | 
| 182 |  |  | 
| 183 |  |  | 
| 184 | 6 | if (!previousTokenEncoded) { | 
| 185 | 6 | if (startWhiteSpace != -1) { | 
| 186 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 187 | 0 | startWhiteSpace = -1; | 
| 188 |  | } | 
| 189 |  | } | 
| 190 |  |  | 
| 191 | 6 | previousTokenEncoded = true; | 
| 192 |  |  | 
| 193 | 6 | decodedText.append(decodedWord); | 
| 194 |  |  | 
| 195 |  |  | 
| 196 | 6 | continue; | 
| 197 |  |  | 
| 198 |  | } catch (ParseException e) { | 
| 199 |  | } | 
| 200 |  | } | 
| 201 |  |  | 
| 202 |  |  | 
| 203 | 0 | if (startWhiteSpace != -1) { | 
| 204 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 205 | 0 | startWhiteSpace = -1; | 
| 206 |  | } | 
| 207 |  |  | 
| 208 | 0 | previousTokenEncoded = false; | 
| 209 | 0 | decodedText.append(word); | 
| 210 |  | } | 
| 211 |  | } | 
| 212 |  |  | 
| 213 | 6 | return decodedText.toString(); | 
| 214 |  | } | 
| 215 |  |  | 
| 216 |  |  | 
| 217 |  |  | 
| 218 |  |  | 
| 219 |  |  | 
| 220 |  |  | 
| 221 |  |  | 
| 222 |  |  | 
| 223 |  |  | 
| 224 |  |  | 
| 225 |  |  | 
| 226 |  |  | 
| 227 |  |  | 
| 228 |  |  | 
| 229 |  |  | 
| 230 |  |  | 
| 231 | 0 | private static String decodeTextNonStrict(String text) throws UnsupportedEncodingException { | 
| 232 | 0 | int offset = 0; | 
| 233 | 0 | int endOffset = text.length(); | 
| 234 |  |  | 
| 235 | 0 | int startWhiteSpace = -1; | 
| 236 | 0 | int endWhiteSpace = -1; | 
| 237 |  |  | 
| 238 | 0 | StringBuffer decodedText = new StringBuffer(text.length()); | 
| 239 |  |  | 
| 240 | 0 | boolean previousTokenEncoded = false; | 
| 241 |  |  | 
| 242 | 0 | while (offset < endOffset) { | 
| 243 | 0 | char ch = text.charAt(offset); | 
| 244 |  |  | 
| 245 |  |  | 
| 246 | 0 | if (linearWhiteSpace.indexOf(ch) != -1) { | 
| 247 | 0 | startWhiteSpace = offset; | 
| 248 | 0 | while (offset < endOffset) { | 
| 249 |  |  | 
| 250 | 0 | ch = text.charAt(offset); | 
| 251 | 0 | if (linearWhiteSpace.indexOf(ch) != -1) { | 
| 252 | 0 | offset++; | 
| 253 |  | } | 
| 254 |  | else { | 
| 255 |  |  | 
| 256 |  |  | 
| 257 | 0 | endWhiteSpace = offset; | 
| 258 | 0 | break; | 
| 259 |  | } | 
| 260 |  | } | 
| 261 |  | } | 
| 262 |  | else { | 
| 263 |  |  | 
| 264 | 0 | int wordStart = offset; | 
| 265 |  |  | 
| 266 | 0 | while (offset < endOffset) { | 
| 267 |  |  | 
| 268 | 0 | ch = text.charAt(offset); | 
| 269 | 0 | if (linearWhiteSpace.indexOf(ch) == -1) { | 
| 270 | 0 | offset++; | 
| 271 |  | } | 
| 272 |  | else { | 
| 273 | 0 | break; | 
| 274 |  | } | 
| 275 |  |  | 
| 276 |  |  | 
| 277 |  | } | 
| 278 |  |  | 
| 279 | 0 | String word = text.substring(wordStart, offset); | 
| 280 |  |  | 
| 281 | 0 | int decodeStart = 0; | 
| 282 |  |  | 
| 283 |  |  | 
| 284 | 0 | while (decodeStart < word.length()) { | 
| 285 | 0 | int tokenStart = word.indexOf("=?", decodeStart); | 
| 286 | 0 | if (tokenStart == -1) { | 
| 287 |  |  | 
| 288 |  |  | 
| 289 | 0 | if (startWhiteSpace != -1) { | 
| 290 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 291 | 0 | startWhiteSpace = -1; | 
| 292 |  | } | 
| 293 |  |  | 
| 294 | 0 | previousTokenEncoded = false; | 
| 295 | 0 | decodedText.append(word.substring(decodeStart)); | 
| 296 |  |  | 
| 297 | 0 | break; | 
| 298 |  | } | 
| 299 |  |  | 
| 300 |  | else { | 
| 301 |  |  | 
| 302 | 0 | if (tokenStart != decodeStart) { | 
| 303 |  |  | 
| 304 |  |  | 
| 305 | 0 | if (startWhiteSpace != -1) { | 
| 306 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 307 | 0 | startWhiteSpace = -1; | 
| 308 |  | } | 
| 309 |  |  | 
| 310 | 0 | previousTokenEncoded = false; | 
| 311 | 0 | decodedText.append(word.substring(decodeStart, tokenStart)); | 
| 312 |  | } | 
| 313 |  |  | 
| 314 |  |  | 
| 315 | 0 | int tokenEnd = word.indexOf("?=", tokenStart); | 
| 316 |  |  | 
| 317 | 0 | if (tokenEnd == -1) { | 
| 318 |  |  | 
| 319 |  |  | 
| 320 | 0 | if (startWhiteSpace != -1) { | 
| 321 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 322 | 0 | startWhiteSpace = -1; | 
| 323 |  | } | 
| 324 |  |  | 
| 325 | 0 | previousTokenEncoded = false; | 
| 326 | 0 | decodedText.append(word.substring(tokenStart)); | 
| 327 |  |  | 
| 328 | 0 | break; | 
| 329 |  | } | 
| 330 |  | else { | 
| 331 |  |  | 
| 332 | 0 | decodeStart = tokenEnd + 2; | 
| 333 |  |  | 
| 334 | 0 | String token = word.substring(tokenStart, tokenEnd); | 
| 335 | 0 | try { | 
| 336 |  |  | 
| 337 | 0 | String decodedWord = decodeWord(token); | 
| 338 |  |  | 
| 339 |  |  | 
| 340 | 0 | if (!previousTokenEncoded) { | 
| 341 | 0 | if (startWhiteSpace != -1) { | 
| 342 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 343 | 0 | startWhiteSpace = -1; | 
| 344 |  | } | 
| 345 |  | } | 
| 346 |  |  | 
| 347 | 0 | previousTokenEncoded = true; | 
| 348 |  |  | 
| 349 | 0 | decodedText.append(decodedWord); | 
| 350 |  |  | 
| 351 |  |  | 
| 352 | 0 | continue; | 
| 353 |  |  | 
| 354 |  | } catch (ParseException e) { | 
| 355 |  | } | 
| 356 |  |  | 
| 357 |  |  | 
| 358 | 0 | if (startWhiteSpace != -1) { | 
| 359 | 0 | decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); | 
| 360 | 0 | startWhiteSpace = -1; | 
| 361 |  | } | 
| 362 |  |  | 
| 363 | 0 | previousTokenEncoded = false; | 
| 364 | 0 | decodedText.append(token); | 
| 365 |  | } | 
| 366 |  | } | 
| 367 |  | } | 
| 368 |  | } | 
| 369 |  | } | 
| 370 |  |  | 
| 371 | 0 | return decodedText.toString(); | 
| 372 |  | } | 
| 373 |  |  | 
| 374 |  |  | 
| 375 |  |  | 
| 376 |  |  | 
| 377 |  |  | 
| 378 |  |  | 
| 379 |  |  | 
| 380 |  |  | 
| 381 |  |  | 
| 382 |  |  | 
| 383 |  |  | 
| 384 |  |  | 
| 385 |  |  | 
| 386 | 9 | public static String decodeWord(String word) throws ParseException, UnsupportedEncodingException { | 
| 387 |  |  | 
| 388 |  |  | 
| 389 |  |  | 
| 390 | 9 | if (!word.startsWith("=?")) { | 
| 391 | 0 | throw new ParseException("Invalid RFC 2047 encoded-word: " + word); | 
| 392 |  | } | 
| 393 |  |  | 
| 394 | 9 | int charsetPos = word.indexOf('?', 2); | 
| 395 | 9 | if (charsetPos == -1) { | 
| 396 | 0 | throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word); | 
| 397 |  | } | 
| 398 |  |  | 
| 399 |  |  | 
| 400 | 9 | String charset = word.substring(2, charsetPos).toLowerCase(); | 
| 401 |  |  | 
| 402 |  |  | 
| 403 | 9 | int encodingPos = word.indexOf('?', charsetPos + 1); | 
| 404 | 9 | if (encodingPos == -1) { | 
| 405 | 0 | throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word); | 
| 406 |  | } | 
| 407 |  |  | 
| 408 | 9 | String encoding = word.substring(charsetPos + 1, encodingPos); | 
| 409 |  |  | 
| 410 |  |  | 
| 411 | 9 | int encodedTextPos = word.indexOf("?=", encodingPos + 1); | 
| 412 | 9 | if (encodedTextPos == -1) { | 
| 413 | 0 | throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word); | 
| 414 |  | } | 
| 415 |  |  | 
| 416 | 9 | String encodedText = word.substring(encodingPos + 1, encodedTextPos); | 
| 417 |  |  | 
| 418 |  |  | 
| 419 | 9 | if (encodedText.length() == 0) { | 
| 420 | 0 | return ""; | 
| 421 |  | } | 
| 422 |  |  | 
| 423 | 9 | try { | 
| 424 |  |  | 
| 425 | 9 | ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length()); | 
| 426 |  |  | 
| 427 | 9 | byte[] encodedData = encodedText.getBytes("US-ASCII"); | 
| 428 |  |  | 
| 429 |  |  | 
| 430 | 9 | if (encoding.equals("B")) { | 
| 431 | 2 | Base64.decode(encodedData, out); | 
| 432 |  | } | 
| 433 |  |  | 
| 434 | 7 | else if (encoding.equals("Q")) { | 
| 435 | 7 | QuotedPrintableEncoder dataEncoder = new QuotedPrintableEncoder(); | 
| 436 | 7 | dataEncoder.decodeWord(encodedData, out); | 
| 437 |  | } | 
| 438 |  | else { | 
| 439 | 0 | throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding); | 
| 440 |  | } | 
| 441 |  |  | 
| 442 | 9 | byte[] decodedData = out.toByteArray(); | 
| 443 | 9 | return new String(decodedData, javaCharset(charset)); | 
| 444 |  | } catch (IOException e) { | 
| 445 | 0 | throw new UnsupportedEncodingException("Invalid RFC 2047 encoding"); | 
| 446 |  | } | 
| 447 |  |  | 
| 448 |  | } | 
| 449 |  |  | 
| 450 |  |  | 
| 451 |  |  | 
| 452 |  |  | 
| 453 |  |  | 
| 454 |  |  | 
| 455 |  |  | 
| 456 |  |  | 
| 457 |  |  | 
| 458 |  |  | 
| 459 |  |  | 
| 460 | 25 | public static OutputStream encode(OutputStream out, String encoding) throws MessagingException { | 
| 461 |  |  | 
| 462 | 25 | if (encoding == null) { | 
| 463 | 5 | return out; | 
| 464 |  | } | 
| 465 |  |  | 
| 466 | 20 | encoding = encoding.toLowerCase(); | 
| 467 |  |  | 
| 468 |  |  | 
| 469 | 20 | if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { | 
| 470 | 8 | return out; | 
| 471 |  | } | 
| 472 | 12 | else if (encoding.equals("base64")) { | 
| 473 | 4 | return new Base64EncoderStream(out); | 
| 474 |  | } | 
| 475 |  |  | 
| 476 | 8 | else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { | 
| 477 | 5 | return new UUEncoderStream(out); | 
| 478 |  | } | 
| 479 | 3 | else if (encoding.equals("quoted-printable")) { | 
| 480 | 3 | return new QuotedPrintableEncoderStream(out); | 
| 481 |  | } | 
| 482 |  | else { | 
| 483 | 0 | throw new MessagingException("Unknown encoding " + encoding); | 
| 484 |  | } | 
| 485 |  | } | 
| 486 |  |  | 
| 487 |  |  | 
| 488 |  |  | 
| 489 |  |  | 
| 490 |  |  | 
| 491 |  |  | 
| 492 |  |  | 
| 493 |  |  | 
| 494 |  |  | 
| 495 |  |  | 
| 496 |  |  | 
| 497 |  |  | 
| 498 | 0 | public static OutputStream encode(OutputStream out, String encoding, String filename) throws MessagingException { | 
| 499 | 0 | encoding = encoding.toLowerCase(); | 
| 500 |  |  | 
| 501 |  |  | 
| 502 | 0 | if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { | 
| 503 | 0 | return out; | 
| 504 |  | } | 
| 505 | 0 | else if (encoding.equals("base64")) { | 
| 506 | 0 | return new Base64EncoderStream(out); | 
| 507 |  | } | 
| 508 |  |  | 
| 509 | 0 | else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { | 
| 510 | 0 | return new UUEncoderStream(out, filename); | 
| 511 |  | } | 
| 512 | 0 | else if (encoding.equals("quoted-printable")) { | 
| 513 | 0 | return new QuotedPrintableEncoderStream(out); | 
| 514 |  | } | 
| 515 |  | else { | 
| 516 | 0 | throw new MessagingException("Unknown encoding " + encoding); | 
| 517 |  | } | 
| 518 |  | } | 
| 519 |  |  | 
| 520 |  |  | 
| 521 | 1 | public static String encodeText(String word) throws UnsupportedEncodingException { | 
| 522 | 1 | return encodeText(word, null, null); | 
| 523 |  | } | 
| 524 |  |  | 
| 525 | 12 | public static String encodeText(String word, String charset, String encoding) throws UnsupportedEncodingException { | 
| 526 | 12 | return encodeWord(word, charset, encoding, false); | 
| 527 |  | } | 
| 528 |  |  | 
| 529 | 66 | public static String encodeWord(String word) throws UnsupportedEncodingException { | 
| 530 | 66 | return encodeWord(word, null, null); | 
| 531 |  | } | 
| 532 |  |  | 
| 533 | 68 | public static String encodeWord(String word, String charset, String encoding) throws UnsupportedEncodingException { | 
| 534 | 68 | return encodeWord(word, charset, encoding, true); | 
| 535 |  | } | 
| 536 |  |  | 
| 537 |  |  | 
| 538 | 80 | private static String encodeWord(String word, String charset, String encoding, boolean encodingWord) throws UnsupportedEncodingException { | 
| 539 |  |  | 
| 540 |  |  | 
| 541 | 80 | String encoder = ASCIIUtil.getTextTransferEncoding(word); | 
| 542 |  |  | 
| 543 | 80 | if (encoder.equals("7bit")) { | 
| 544 | 69 | return word; | 
| 545 |  | } | 
| 546 |  |  | 
| 547 |  |  | 
| 548 | 11 | if (charset == null) { | 
| 549 | 2 | charset = getDefaultMIMECharset(); | 
| 550 |  | } | 
| 551 |  |  | 
| 552 |  |  | 
| 553 | 11 | if (encoding != null) { | 
| 554 | 4 | if (encoding.equalsIgnoreCase("B")) { | 
| 555 | 2 | encoder = "base64"; | 
| 556 |  | } | 
| 557 | 2 | else if (encoding.equalsIgnoreCase("Q")) { | 
| 558 | 2 | encoder = "quoted-printable"; | 
| 559 |  | } | 
| 560 |  | else { | 
| 561 | 0 | throw new UnsupportedEncodingException("Unknown transfer encoding: " + encoding); | 
| 562 |  | } | 
| 563 |  | } | 
| 564 |  |  | 
| 565 | 11 | try { | 
| 566 |  |  | 
| 567 | 11 | InputStream in = new ByteArrayInputStream(word.getBytes( javaCharset(charset))); | 
| 568 | 11 | ByteArrayOutputStream out = new ByteArrayOutputStream(); | 
| 569 |  |  | 
| 570 | 11 | if (encoder.equals("base64")) { | 
| 571 | 2 | Base64Encoder dataEncoder = new Base64Encoder(); | 
| 572 | 2 | dataEncoder.encodeWord(in, charset, out, SessionUtil.getBooleanProperty(MIME_FOLDENCODEDWORDS, false)); | 
| 573 |  | } | 
| 574 |  | else { | 
| 575 | 9 | QuotedPrintableEncoder dataEncoder = new QuotedPrintableEncoder(); | 
| 576 | 9 | dataEncoder.encodeWord(in, charset, encodingWord ? QP_WORD_SPECIALS : QP_TEXT_SPECIALS, out, SessionUtil.getBooleanProperty(MIME_FOLDENCODEDWORDS, false)); | 
| 577 |  | } | 
| 578 |  |  | 
| 579 | 11 | byte[] bytes = out.toByteArray(); | 
| 580 | 11 | return new String(bytes); | 
| 581 |  | } catch (IOException e) { | 
| 582 | 0 | throw new UnsupportedEncodingException("Invalid encoding"); | 
| 583 |  | } | 
| 584 |  | } | 
| 585 |  |  | 
| 586 |  |  | 
| 587 |  |  | 
| 588 |  |  | 
| 589 |  |  | 
| 590 |  |  | 
| 591 |  |  | 
| 592 |  |  | 
| 593 |  |  | 
| 594 |  |  | 
| 595 |  |  | 
| 596 |  |  | 
| 597 | 4 | public static String getEncoding(DataHandler handler) { | 
| 598 |  |  | 
| 599 |  |  | 
| 600 |  |  | 
| 601 |  |  | 
| 602 |  |  | 
| 603 | 4 | DataSource ds = handler.getDataSource(); | 
| 604 | 4 | if (ds != null) { | 
| 605 | 4 | return getEncoding(ds); | 
| 606 |  | } | 
| 607 |  |  | 
| 608 | 0 | try { | 
| 609 |  |  | 
| 610 | 0 | ContentType content = new ContentType(ds.getContentType()); | 
| 611 |  |  | 
| 612 |  |  | 
| 613 |  |  | 
| 614 |  |  | 
| 615 | 0 | ContentCheckingOutputStream checker = new ContentCheckingOutputStream(); | 
| 616 |  |  | 
| 617 | 0 | handler.writeTo(checker); | 
| 618 |  |  | 
| 619 |  |  | 
| 620 | 0 | if (content.match("text/*")) { | 
| 621 | 0 | return checker.getTextTransferEncoding(); | 
| 622 |  | } | 
| 623 |  | else { | 
| 624 | 0 | return checker.getBinaryTransferEncoding(); | 
| 625 |  | } | 
| 626 |  |  | 
| 627 |  | } catch (Exception e) { | 
| 628 |  |  | 
| 629 | 0 | return "base64"; | 
| 630 |  | } | 
| 631 |  | } | 
| 632 |  |  | 
| 633 |  |  | 
| 634 |  |  | 
| 635 |  |  | 
| 636 |  |  | 
| 637 |  |  | 
| 638 |  |  | 
| 639 |  |  | 
| 640 |  |  | 
| 641 |  |  | 
| 642 |  |  | 
| 643 | 4 | public static String getEncoding(DataSource source) { | 
| 644 | 4 | InputStream in = null; | 
| 645 |  |  | 
| 646 | 4 | try { | 
| 647 |  |  | 
| 648 | 4 | ContentType content = new ContentType(source.getContentType()); | 
| 649 |  |  | 
| 650 |  |  | 
| 651 | 4 | in = source.getInputStream(); | 
| 652 |  |  | 
| 653 | 4 | if (!content.match("text/*")) { | 
| 654 |  |  | 
| 655 |  |  | 
| 656 | 1 | return ASCIIUtil.getBinaryTransferEncoding(in); | 
| 657 |  | } | 
| 658 |  | else { | 
| 659 | 3 | return ASCIIUtil.getTextTransferEncoding(in); | 
| 660 |  | } | 
| 661 |  | } catch (Exception e) { | 
| 662 |  |  | 
| 663 |  |  | 
| 664 | 0 | return "base64"; | 
| 665 |  | } finally { | 
| 666 |  |  | 
| 667 | 4 | try { | 
| 668 | 4 | if (in != null) { | 
| 669 | 4 | in.close(); | 
| 670 |  | } | 
| 671 |  | } catch (IOException e) { | 
| 672 |  | } | 
| 673 |  | } | 
| 674 |  | } | 
| 675 |  |  | 
| 676 |  |  | 
| 677 |  |  | 
| 678 |  |  | 
| 679 |  |  | 
| 680 |  |  | 
| 681 |  |  | 
| 682 |  |  | 
| 683 |  |  | 
| 684 |  |  | 
| 685 |  |  | 
| 686 |  |  | 
| 687 |  |  | 
| 688 |  |  | 
| 689 | 29 | public static String quote(String word, String specials) { | 
| 690 | 29 | int wordLength = word.length(); | 
| 691 | 29 | boolean requiresQuoting = false; | 
| 692 |  |  | 
| 693 | 29 | for (int i =0; i < wordLength; i++) { | 
| 694 | 154 | char ch = word.charAt(i); | 
| 695 |  |  | 
| 696 | 154 | if (escapedChars.indexOf(ch) >= 0) { | 
| 697 | 4 | return quoteAndEscapeString(word); | 
| 698 |  | } | 
| 699 |  |  | 
| 700 | 150 | if (ch < 32 || ch >= 127 || specials.indexOf(ch) >= 0) { | 
| 701 |  |  | 
| 702 |  |  | 
| 703 | 12 | return quoteAndEscapeString(word); | 
| 704 |  | } | 
| 705 |  | } | 
| 706 | 13 | return word; | 
| 707 |  | } | 
| 708 |  |  | 
| 709 |  |  | 
| 710 |  |  | 
| 711 |  |  | 
| 712 |  |  | 
| 713 |  |  | 
| 714 |  |  | 
| 715 |  |  | 
| 716 |  |  | 
| 717 | 16 | private static String quoteAndEscapeString(String word) { | 
| 718 | 16 | int wordLength = word.length(); | 
| 719 |  |  | 
| 720 | 16 | StringBuffer buffer = new StringBuffer(wordLength + 10); | 
| 721 |  |  | 
| 722 | 16 | buffer.append('"'); | 
| 723 |  |  | 
| 724 | 16 | for (int i = 0; i < wordLength; i++) { | 
| 725 | 266 | char ch = word.charAt(i); | 
| 726 |  |  | 
| 727 | 266 | if (escapedChars.indexOf(ch) >= 0) { | 
| 728 |  |  | 
| 729 | 4 | buffer.append('\\'); | 
| 730 |  | } | 
| 731 | 266 | buffer.append(ch); | 
| 732 |  | } | 
| 733 |  |  | 
| 734 | 16 | buffer.append('"'); | 
| 735 | 16 | return buffer.toString(); | 
| 736 |  | } | 
| 737 |  |  | 
| 738 |  |  | 
| 739 |  |  | 
| 740 |  |  | 
| 741 |  |  | 
| 742 |  |  | 
| 743 |  |  | 
| 744 |  |  | 
| 745 |  |  | 
| 746 | 20 | public static String javaCharset(String charset) { | 
| 747 |  |  | 
| 748 | 20 | if (charset == null) { | 
| 749 | 0 | return null; | 
| 750 |  | } | 
| 751 |  |  | 
| 752 | 20 | String mappedCharset = (String)mime2java.get(charset.toLowerCase()); | 
| 753 |  |  | 
| 754 |  |  | 
| 755 | 20 | return mappedCharset == null ? charset : mappedCharset; | 
| 756 |  | } | 
| 757 |  |  | 
| 758 |  |  | 
| 759 |  |  | 
| 760 |  |  | 
| 761 |  |  | 
| 762 |  |  | 
| 763 |  |  | 
| 764 |  |  | 
| 765 | 2 | public static String mimeCharset(String charset) { | 
| 766 |  |  | 
| 767 | 2 | if (charset == null) { | 
| 768 | 0 | return null; | 
| 769 |  | } | 
| 770 |  |  | 
| 771 | 2 | String mappedCharset = (String)java2mime.get(charset.toLowerCase()); | 
| 772 |  |  | 
| 773 |  |  | 
| 774 | 2 | return mappedCharset == null ? charset : mappedCharset; | 
| 775 |  | } | 
| 776 |  |  | 
| 777 |  |  | 
| 778 |  |  | 
| 779 |  |  | 
| 780 |  |  | 
| 781 |  |  | 
| 782 |  |  | 
| 783 |  |  | 
| 784 |  |  | 
| 785 |  |  | 
| 786 |  |  | 
| 787 | 0 | public static String getDefaultJavaCharset() { | 
| 788 | 0 | String charset = SessionUtil.getProperty("mail.mime.charset"); | 
| 789 | 0 | if (charset != null) { | 
| 790 | 0 | return javaCharset(charset); | 
| 791 |  | } | 
| 792 | 0 | return SessionUtil.getProperty("file.encoding", "8859_1"); | 
| 793 |  | } | 
| 794 |  |  | 
| 795 |  |  | 
| 796 |  |  | 
| 797 |  |  | 
| 798 |  |  | 
| 799 |  |  | 
| 800 |  |  | 
| 801 |  |  | 
| 802 |  |  | 
| 803 |  |  | 
| 804 | 2 | static String getDefaultMIMECharset() { | 
| 805 |  |  | 
| 806 | 2 | String charset = SessionUtil.getProperty("mail.mime.charset"); | 
| 807 | 2 | if (charset != null) { | 
| 808 | 0 | return charset; | 
| 809 |  | } | 
| 810 |  |  | 
| 811 |  |  | 
| 812 | 2 | return mimeCharset(SessionUtil.getProperty("file.encoding", "8859_1")); | 
| 813 |  | } | 
| 814 |  |  | 
| 815 |  |  | 
| 816 |  |  | 
| 817 |  |  | 
| 818 |  |  | 
| 819 |  |  | 
| 820 |  |  | 
| 821 |  |  | 
| 822 |  |  | 
| 823 |  |  | 
| 824 | 1 | static private void loadCharacterSetMappings() { | 
| 825 | 1 | java2mime = new HashMap(); | 
| 826 | 1 | mime2java = new HashMap(); | 
| 827 |  |  | 
| 828 |  |  | 
| 829 |  |  | 
| 830 | 1 | try { | 
| 831 | 1 | InputStream map = javax.mail.internet.MimeUtility.class.getResourceAsStream("/META-INF/javamail.charset.map"); | 
| 832 |  |  | 
| 833 | 1 | if (map != null) { | 
| 834 |  |  | 
| 835 | 1 | BufferedReader reader = new BufferedReader(new InputStreamReader(map)); | 
| 836 |  |  | 
| 837 | 1 | readMappings(reader, java2mime); | 
| 838 | 1 | readMappings(reader, mime2java); | 
| 839 |  | } | 
| 840 |  | } catch (Exception e) { | 
| 841 |  | } | 
| 842 |  |  | 
| 843 |  |  | 
| 844 |  |  | 
| 845 |  |  | 
| 846 |  |  | 
| 847 |  |  | 
| 848 | 1 | if (java2mime.isEmpty()) { | 
| 849 | 0 | java2mime.put("8859_1", "ISO-8859-1"); | 
| 850 | 0 | java2mime.put("iso8859_1", "ISO-8859-1"); | 
| 851 | 0 | java2mime.put("iso8859-1", "ISO-8859-1"); | 
| 852 |  |  | 
| 853 | 0 | java2mime.put("8859_2", "ISO-8859-2"); | 
| 854 | 0 | java2mime.put("iso8859_2", "ISO-8859-2"); | 
| 855 | 0 | java2mime.put("iso8859-2", "ISO-8859-2"); | 
| 856 |  |  | 
| 857 | 0 | java2mime.put("8859_3", "ISO-8859-3"); | 
| 858 | 0 | java2mime.put("iso8859_3", "ISO-8859-3"); | 
| 859 | 0 | java2mime.put("iso8859-3", "ISO-8859-3"); | 
| 860 |  |  | 
| 861 | 0 | java2mime.put("8859_4", "ISO-8859-4"); | 
| 862 | 0 | java2mime.put("iso8859_4", "ISO-8859-4"); | 
| 863 | 0 | java2mime.put("iso8859-4", "ISO-8859-4"); | 
| 864 |  |  | 
| 865 | 0 | java2mime.put("8859_5", "ISO-8859-5"); | 
| 866 | 0 | java2mime.put("iso8859_5", "ISO-8859-5"); | 
| 867 | 0 | java2mime.put("iso8859-5", "ISO-8859-5"); | 
| 868 |  |  | 
| 869 | 0 | java2mime.put ("8859_6", "ISO-8859-6"); | 
| 870 | 0 | java2mime.put("iso8859_6", "ISO-8859-6"); | 
| 871 | 0 | java2mime.put("iso8859-6", "ISO-8859-6"); | 
| 872 |  |  | 
| 873 | 0 | java2mime.put("8859_7", "ISO-8859-7"); | 
| 874 | 0 | java2mime.put("iso8859_7", "ISO-8859-7"); | 
| 875 | 0 | java2mime.put("iso8859-7", "ISO-8859-7"); | 
| 876 |  |  | 
| 877 | 0 | java2mime.put("8859_8", "ISO-8859-8"); | 
| 878 | 0 | java2mime.put("iso8859_8", "ISO-8859-8"); | 
| 879 | 0 | java2mime.put("iso8859-8", "ISO-8859-8"); | 
| 880 |  |  | 
| 881 | 0 | java2mime.put("8859_9", "ISO-8859-9"); | 
| 882 | 0 | java2mime.put("iso8859_9", "ISO-8859-9"); | 
| 883 | 0 | java2mime.put("iso8859-9", "ISO-8859-9"); | 
| 884 |  |  | 
| 885 | 0 | java2mime.put("sjis", "Shift_JIS"); | 
| 886 | 0 | java2mime.put ("jis", "ISO-2022-JP"); | 
| 887 | 0 | java2mime.put("iso2022jp", "ISO-2022-JP"); | 
| 888 | 0 | java2mime.put("euc_jp", "euc-jp"); | 
| 889 | 0 | java2mime.put("koi8_r", "koi8-r"); | 
| 890 | 0 | java2mime.put("euc_cn", "euc-cn"); | 
| 891 | 0 | java2mime.put("euc_tw", "euc-tw"); | 
| 892 | 0 | java2mime.put("euc_kr", "euc-kr"); | 
| 893 |  | } | 
| 894 |  |  | 
| 895 | 1 | if (mime2java.isEmpty ()) { | 
| 896 | 0 | mime2java.put("iso-2022-cn", "ISO2022CN"); | 
| 897 | 0 | mime2java.put("iso-2022-kr", "ISO2022KR"); | 
| 898 | 0 | mime2java.put("utf-8", "UTF8"); | 
| 899 | 0 | mime2java.put("utf8", "UTF8"); | 
| 900 | 0 | mime2java.put("ja_jp.iso2022-7", "ISO2022JP"); | 
| 901 | 0 | mime2java.put("ja_jp.eucjp", "EUCJIS"); | 
| 902 | 0 | mime2java.put ("euc-kr", "KSC5601"); | 
| 903 | 0 | mime2java.put("euckr", "KSC5601"); | 
| 904 | 0 | mime2java.put("us-ascii", "ISO-8859-1"); | 
| 905 | 0 | mime2java.put("x-us-ascii", "ISO-8859-1"); | 
| 906 |  | } | 
| 907 |  | } | 
| 908 |  |  | 
| 909 |  |  | 
| 910 |  |  | 
| 911 |  |  | 
| 912 |  |  | 
| 913 |  |  | 
| 914 |  |  | 
| 915 |  |  | 
| 916 |  |  | 
| 917 |  |  | 
| 918 |  |  | 
| 919 |  |  | 
| 920 | 2 | static private void readMappings(BufferedReader reader, Map table) throws IOException { | 
| 921 |  |  | 
| 922 | 2 | while (true) { | 
| 923 | 56 | String line = reader.readLine(); | 
| 924 |  |  | 
| 925 | 56 | if (line == null) { | 
| 926 | 1 | return; | 
| 927 |  | } | 
| 928 |  |  | 
| 929 |  |  | 
| 930 | 55 | line = line.trim(); | 
| 931 |  |  | 
| 932 | 55 | if (line.length() == 0 || line.startsWith("#")) { | 
| 933 | 17 | continue; | 
| 934 |  | } | 
| 935 |  |  | 
| 936 |  |  | 
| 937 | 38 | if (line.startsWith("--") && line.endsWith("--")) { | 
| 938 | 1 | return; | 
| 939 |  | } | 
| 940 |  |  | 
| 941 |  |  | 
| 942 | 37 | StringTokenizer tokenizer = new StringTokenizer(line, " \t"); | 
| 943 |  |  | 
| 944 | 37 | try { | 
| 945 | 37 | String from = tokenizer.nextToken().toLowerCase(); | 
| 946 | 37 | String to = tokenizer.nextToken(); | 
| 947 |  |  | 
| 948 | 37 | table.put(from, to); | 
| 949 |  | } catch (NoSuchElementException e) { | 
| 950 |  |  | 
| 951 |  | } | 
| 952 |  | } | 
| 953 |  | } | 
| 954 |  |  | 
| 955 |  |  | 
| 956 |  | } | 
| 957 |  |  | 
| 958 |  |  | 
| 959 |  |  | 
| 960 |  |  | 
| 961 |  |  | 
| 962 |  |  | 
| 963 |  |  | 
| 964 |  | class ContentCheckingOutputStream extends OutputStream { | 
| 965 |  | private int asciiChars = 0; | 
| 966 |  | private int nonAsciiChars = 0; | 
| 967 |  | private boolean containsLongLines = false; | 
| 968 |  | private boolean containsMalformedEOL = false; | 
| 969 |  | private int previousChar = 0; | 
| 970 |  | private int span = 0; | 
| 971 |  |  | 
| 972 | 0 | ContentCheckingOutputStream() { | 
| 973 |  | } | 
| 974 |  |  | 
| 975 | 0 | public void write(byte[] data) throws IOException { | 
| 976 | 0 | write(data, 0, data.length); | 
| 977 |  | } | 
| 978 |  |  | 
| 979 | 0 | public void write(byte[] data, int offset, int length) throws IOException { | 
| 980 | 0 | for (int i = 0; i < length; i++) { | 
| 981 | 0 | write(data[offset + i]); | 
| 982 |  | } | 
| 983 |  | } | 
| 984 |  |  | 
| 985 | 0 | public void write(int ch) { | 
| 986 |  |  | 
| 987 |  |  | 
| 988 | 0 | if (ch == '\n' || ch == '\r') { | 
| 989 |  |  | 
| 990 | 0 | if (ch == '\n') { | 
| 991 |  |  | 
| 992 | 0 | if (previousChar != '\r') { | 
| 993 | 0 | containsMalformedEOL = true; | 
| 994 |  | } | 
| 995 |  | } | 
| 996 |  |  | 
| 997 | 0 | span = 0; | 
| 998 |  | } | 
| 999 |  | else { | 
| 1000 | 0 | span++; | 
| 1001 |  |  | 
| 1002 | 0 | if (span > 998) { | 
| 1003 | 0 | containsLongLines = true; | 
| 1004 |  | } | 
| 1005 |  |  | 
| 1006 |  |  | 
| 1007 | 0 | if (!ASCIIUtil.isAscii(ch)) { | 
| 1008 | 0 | nonAsciiChars++; | 
| 1009 |  | } | 
| 1010 |  | else { | 
| 1011 | 0 | asciiChars++; | 
| 1012 |  | } | 
| 1013 |  | } | 
| 1014 | 0 | previousChar = ch; | 
| 1015 |  | } | 
| 1016 |  |  | 
| 1017 |  |  | 
| 1018 | 0 | public String getBinaryTransferEncoding() { | 
| 1019 | 0 | if (nonAsciiChars != 0 || containsLongLines || containsMalformedEOL) { | 
| 1020 | 0 | return "base64"; | 
| 1021 |  | } | 
| 1022 |  | else { | 
| 1023 | 0 | return "7bit"; | 
| 1024 |  | } | 
| 1025 |  | } | 
| 1026 |  |  | 
| 1027 | 0 | public String getTextTransferEncoding() { | 
| 1028 |  |  | 
| 1029 | 0 | if (nonAsciiChars == 0) { | 
| 1030 |  |  | 
| 1031 |  |  | 
| 1032 | 0 | if (containsLongLines) { | 
| 1033 | 0 | return "quoted-printable"; | 
| 1034 |  | } | 
| 1035 |  | else { | 
| 1036 |  |  | 
| 1037 | 0 | return "7bit"; | 
| 1038 |  | } | 
| 1039 |  | } | 
| 1040 |  | else { | 
| 1041 |  |  | 
| 1042 | 0 | if (nonAsciiChars > asciiChars) { | 
| 1043 | 0 | return "base64"; | 
| 1044 |  | } | 
| 1045 |  | else { | 
| 1046 |  |  | 
| 1047 | 0 | return "quoted-printable"; | 
| 1048 |  | } | 
| 1049 |  | } | 
| 1050 |  | } | 
| 1051 |  | } |