1 21 22 package org.armedbear.j.mail; 23 24 import java.io.BufferedOutputStream ; 25 import java.io.ByteArrayOutputStream ; 26 import java.io.FileOutputStream ; 27 import java.io.IOException ; 28 import java.io.UnsupportedEncodingException ; 29 import java.util.ArrayList ; 30 import java.util.List ; 31 import java.util.Vector ; 32 import org.armedbear.j.Directories; 33 import org.armedbear.j.Editor; 34 import org.armedbear.j.File; 35 import org.armedbear.j.FastStringBuffer; 36 import org.armedbear.j.Headers; 37 import org.armedbear.j.Log; 38 import org.armedbear.j.StringPair; 39 import org.armedbear.j.Utilities; 40 41 public class MimePart 42 { 43 protected String raw; 44 protected Headers headers; 45 46 private Vector parts; 47 48 public MimePart(String raw) 49 { 50 this.raw = raw; 51 } 52 53 public MimePart(String raw, Headers headers) 54 { 55 this.raw = raw; 56 this.headers = headers; 57 } 58 59 public final String getRawText() 60 { 61 return raw; 62 } 63 64 public String getAllHeaders() 65 { 66 if (raw.startsWith("\r\n")) 67 return "\r\n"; 68 if (raw.startsWith("\n")) 69 return "\n"; 70 int index = raw.indexOf("\r\n\r\n"); 71 if (index >= 0) 72 return RFC2047.decode(raw.substring(0, index + 2)); 73 index = raw.indexOf("\n\n"); 74 if (index >= 0) 75 return RFC2047.decode(raw.substring(0, index + 1)); 76 return raw; 77 } 78 79 public String getRawHeaders() 80 { 81 if (raw.startsWith("\r\n")) 82 return "\r\n"; 83 if (raw.startsWith("\n")) 84 return "\n"; 85 int index = raw.indexOf("\r\n\r\n"); 86 if (index >= 0) 87 return raw.substring(0, index + 2); 88 index = raw.indexOf("\n\n"); 89 if (index >= 0) 90 return raw.substring(0, index + 1); 91 return raw; 92 } 93 94 public String getRawBody() 95 { 96 if (raw.startsWith("\r\n")) 97 return raw.substring(2); 98 else if (raw.startsWith("\n")) 99 return raw.substring(1); 100 else { 101 int index = raw.indexOf("\r\n\r\n"); 102 if (index >= 0) 103 return raw.substring(index + 4); 104 else { 105 index = raw.indexOf("\n\n"); 106 if (index >= 0) 107 return raw.substring(index + 2); 108 else 109 return raw; 110 } 111 } 112 } 113 114 public String getDecodedBody() 115 { 116 final String rawBody = getRawBody(); 117 final String contentType = getContentType(); 118 final String transferEncoding = getTransferEncoding(); 119 final String charset = 120 Utilities.getCharsetFromContentType(getHeaderValue(Headers.CONTENT_TYPE)); 121 final String characterEncoding = Utilities.getEncodingFromCharset(charset); 122 if (contentType == null || contentType.toLowerCase().startsWith("text/")) { 123 if (transferEncoding == null || 124 transferEncoding.equals("7bit") || 125 transferEncoding.equals("8bit") || 126 transferEncoding.equals("binary")) 127 return rawBody; 128 else if (transferEncoding.equals("quoted-printable")) { 129 byte[] bytes = QuotedPrintableDecoder.decode(rawBody); 130 try { 131 return new String (bytes, characterEncoding); 132 } 133 catch (UnsupportedEncodingException e) { 134 Log.error(e); 135 return new String (bytes); 136 } 137 } else if (transferEncoding.equals("base64")) { 138 try { 139 ByteArrayOutputStream out = new ByteArrayOutputStream (); 140 Base64Decoder.decode(rawBody, out); byte[] bytes = out.toByteArray(); 142 if (bytes != null) { 143 try { 144 return new String (bytes, 0, bytes.length, 145 characterEncoding); 146 } 147 catch (UnsupportedEncodingException e) { 148 Log.error(e); 149 return new String (bytes, 0, bytes.length); 150 } 151 } 152 } 153 catch (IOException e) { 154 Log.error(e); 155 } 156 return null; 157 } else 158 return null; 159 } else if (contentType.startsWith("multipart/")) 160 return rawBody; 161 else if (transferEncoding == null) 162 return rawBody; 163 else 164 return null; 165 } 166 167 private byte[] getDecodedBodyAsByteArray() 168 { 169 final String rawBody = getRawBody(); 170 final String encoding = getTransferEncoding(); 171 if (encoding == null || encoding.equals("7bit") || 172 encoding.equals("8bit") || encoding.equals("binary")) { 173 byte[] bytes = null; 174 try { 175 bytes = rawBody.getBytes("ISO8859_1"); 176 } 177 catch (UnsupportedEncodingException e) { 178 Log.error(e); 179 } 180 return bytes; 181 } 182 if (encoding.equals("quoted-printable")) 183 return QuotedPrintableDecoder.decode(rawBody); 184 if (encoding.equals("base64")) { 185 try { 186 ByteArrayOutputStream out = new ByteArrayOutputStream (); 187 if (Base64Decoder.decode(rawBody, out)) 188 return out.toByteArray(); 189 } 190 catch (IOException e) { 191 Log.error(e); 192 } 193 } 195 return null; 196 } 197 198 public Vector getParts() 199 { 200 return parts; 201 } 202 203 public MimePart getPart(int i) 204 { 205 if (parts == null) 206 return null; 207 if (i < 0) 208 return null; 209 if (i >= parts.size()) 210 return null; 211 return (MimePart) parts.get(i); 212 } 213 214 protected void addParts(Vector v) 215 { 216 v.add(this); 217 if (parts != null) { 219 for (int i = 0; i < parts.size(); i++) { 220 MimePart part = (MimePart) parts.get(i); 221 String contentType = part.getContentType(); 222 if (contentType != null && contentType.startsWith("multipart/")) 223 part.addParts(v); 224 else 225 v.add(part); 226 } 227 } 228 } 229 230 public final Headers getHeaders() 231 { 232 if (headers == null) 233 headers = Headers.parse(raw); 234 return headers; 235 } 236 237 public final String getHeaderValue(int index) 238 { 239 if (headers == null) 240 headers = Headers.parse(raw); 241 return headers.getValue(index); 242 } 243 244 public final String getContentType() 245 { 246 String s = getHeaderValue(Headers.CONTENT_TYPE); 247 if (s == null ) 248 return null; 249 s = s.trim(); 250 if (s.length() == 0) 251 return null; 252 int index = s.indexOf(';'); 253 if (index >= 0) 254 s = s.substring(0, index); 255 return s.toLowerCase(); 256 } 257 258 public final String getTransferEncoding() 259 { 260 String s = getHeaderValue(Headers.CONTENT_TRANSFER_ENCODING); 261 if (s == null ) 262 return null; 263 return s.toLowerCase(); 264 } 265 266 public final String getDisposition() 267 { 268 String s = getHeaderValue(Headers.CONTENT_DISPOSITION); 269 if (s == null) 270 return null; 271 s = s.trim(); 272 int index = s.indexOf(';'); 273 if (index >= 0 ) 274 s = s.substring(0, index); 275 return s; 276 } 277 278 public final int getSize() 279 { 280 String s = getRawBody(); 281 return s == null ? 0 : s.length(); 282 } 283 284 public final boolean isAttachment() 285 { 286 String s = getHeaderValue(Headers.CONTENT_DISPOSITION); 287 if (s != null && s.trim().startsWith("attachment")) 288 return true; 289 else 290 return false; 291 } 292 293 public final boolean isInline() 294 { 295 String s = getHeaderValue(Headers.CONTENT_DISPOSITION); 296 if (s != null && s.trim().startsWith("inline")) 297 return true; 298 return false; 299 } 300 301 public String getAttachmentFileName() 302 { 303 String filename = 304 getHeaderParameter(Headers.CONTENT_DISPOSITION, "filename"); 305 if (filename == null) 306 filename = getHeaderParameter(Headers.CONTENT_TYPE, "name"); 307 if (filename != null) { 308 filename = filename.trim(); 309 int length = filename.length(); 310 if (length >= 2 && filename.charAt(0) == '"' && 312 filename.charAt(length-1) == '"') 313 filename = filename.substring(1, length-1); 314 filename = RFC2047.decode(filename); 316 int index = filename.lastIndexOf('/'); 318 if (index < 0) 319 index = filename.lastIndexOf('\\'); 320 if (index >= 0) 321 filename = filename.substring(index+1); 322 } 323 return filename; 324 } 325 326 private String getHeaderParameter(int header, String parameterName) 327 { 328 String s = getHeaderValue(header); 329 if (s != null) { 330 s = s.trim(); 331 String lower = s.toLowerCase(); 332 int index = lower.indexOf(parameterName.concat("=")); 333 if (index >= 0) { 334 int begin = index + parameterName.length() + 1; 335 int end = s.indexOf(';', begin); 336 if (end >= 0) 337 return s.substring(begin, end); 338 else 339 return s.substring(begin); 340 } 341 } 342 return null; 343 } 344 345 public File cacheDecoded() 346 { 347 String filename = getAttachmentFileName(); 348 String extension = null; 349 if (filename != null && filename.length() > 0) { 350 extension = Utilities.getExtension(filename); 351 } else { 352 String contentType = getContentType(); 354 if (contentType != null) { 355 if (contentType.equals("image/jpeg")) 356 extension = ".jpg"; 357 else if (contentType.equals("image/gif")) 358 extension = ".gif"; 359 else if (contentType.equals("text/html")) 360 extension = ".html"; 361 } 362 } 363 File cache = 364 Utilities.getTempFile(Directories.getTempDirectory(), extension); 365 if (cache != null && saveDecoded(cache)) 366 return cache; 367 else 368 return null; 369 } 370 371 public boolean saveDecoded(File file) 372 { 373 String encoding = getTransferEncoding(); 374 if (encoding == null || encoding.equals("7bit") || 375 encoding.equals("8bit") || encoding.equals("binary") || 376 encoding.equals("quoted-printable")) { 377 try { 378 byte[] bytes = getDecodedBodyAsByteArray(); 379 if (bytes == null) 380 return false; 381 FileOutputStream out = file.getOutputStream(); 382 out.write(bytes); 383 out.flush(); 384 out.close(); 385 return true; 386 } 387 catch (IOException e) { 388 Log.error(e); 389 return false; 390 } 391 } else if (encoding.equals("base64")) { 392 return saveDecodedBase64(file); 393 } else if (encoding.equals("x-uuencode")) { 394 Log.error("saveDecoded x-uuencode not supported"); 395 return false; 396 } else { 397 Log.error("saveDecoded unrecognized encoding " + encoding); 398 return false; 399 } 400 } 401 402 private boolean saveDecodedBase64(File file) 403 { 404 boolean success = false; 405 try { 406 BufferedOutputStream out = 407 new BufferedOutputStream (file.getOutputStream()); 408 String rawBody = getRawBody(); 409 success = Base64Decoder.decode(rawBody, out); 410 out.flush(); 411 out.close(); 412 } 413 catch (IOException e) { 414 Log.error(e); 415 success = false; 416 } 417 if (!success) 418 file.delete(); 419 return success; 420 } 421 422 private static final String BOUNDARY_START = "boundary="; 423 424 public void parse() 425 { 426 final String contentType = getHeaderValue(Headers.CONTENT_TYPE); 427 if (contentType == null) 428 return; 429 if (contentType.toLowerCase().startsWith("multipart/")) { 430 int index = contentType.toLowerCase().indexOf(BOUNDARY_START); 431 if (index < 0) { 432 Log.error("can't find boundary parameter"); 433 return; 434 } 435 String boundary = 436 contentType.substring(index + BOUNDARY_START.length()).trim(); 437 if (boundary.length() >= 2 && boundary.charAt(0) == '"') { 438 int end = boundary.indexOf('"', 1); 439 if (end >= 0) 440 boundary = boundary.substring(1, end); 441 } 442 parts = parseParts(boundary); 443 if (parts != null) { 444 for (int i = 0; i < parts.size(); i++) { 445 MimePart part = (MimePart) parts.get(i); 446 part.parse(); 447 } 448 } 449 return; 450 } 451 final String disposition = getDisposition(); 452 if (disposition != null && disposition.equalsIgnoreCase("attachment")) { 453 parts = new Vector (); 454 MimePart part = new MimePart(raw); 455 parts.add(part); 456 } 457 } 458 459 private Vector parseParts(String boundary) 460 { 461 final String marker = "--" + boundary; 462 final String endMarker = marker + "--"; 463 final int veryEnd = raw.indexOf(endMarker); 464 int start = raw.indexOf(marker); 465 if (start < 0) 466 return null; 467 Vector v = new Vector (); 468 while (true) { 469 start += marker.length(); 470 if (raw.charAt(start) == '\r') 471 ++start; 472 if (raw.charAt(start) == '\n') 473 ++start; 474 int end = raw.indexOf(marker, start); 475 if (end < 0) 476 end = raw.length(); 477 MimePart part = new MimePart(raw.substring(start, end)); 478 v.add(part); 479 if (end == veryEnd || end == raw.length()) 480 break; 481 start = end; 482 } 483 return v; 484 } 485 } 486 | Popular Tags |