1 17 18 package org.apache.james.transport.mailets; 19 20 import java.io.PrintWriter ; 21 import java.io.StringWriter ; 22 import java.net.ConnectException ; 23 import java.net.InetAddress ; 24 import java.net.SocketException ; 25 import java.net.UnknownHostException ; 26 import java.util.Collection ; 27 import java.util.Date ; 28 import java.util.HashSet ; 29 import java.util.Iterator ; 30 31 import javax.mail.MessagingException ; 32 import javax.mail.SendFailedException ; 33 import javax.mail.Session ; 34 import javax.mail.internet.InternetAddress ; 35 import javax.mail.internet.MimeBodyPart ; 36 import javax.mail.internet.MimeMessage ; 37 import javax.mail.internet.MimeMultipart ; 38 39 import javax.activation.CommandMap ; 40 import javax.activation.MailcapCommandMap ; 41 42 import org.apache.james.core.MailImpl; 43 import org.apache.mailet.Mail; 44 import org.apache.mailet.MailAddress; 45 import org.apache.james.util.RFC2822Headers; 46 import org.apache.james.util.RFC822DateFormat; 47 import org.apache.james.Constants; 48 import org.apache.james.util.mail.MimeMultipartReport; 49 50 import org.apache.oro.text.regex.MalformedPatternException; 51 import org.apache.oro.text.regex.Pattern; 52 import org.apache.oro.text.regex.Perl5Compiler; 53 import org.apache.oro.text.regex.Perl5Matcher; 54 import org.apache.oro.text.regex.MatchResult; 55 56 57 58 59 88 89 90 91 public class DSNBounce extends AbstractNotify { 92 93 94 100 public static class DSNStatus { 101 105 public static final int SUCCESS = 2; 106 107 110 public static final int TRANSIENT = 4; 111 112 115 public static final int PERMANENT = 5; 116 117 119 122 public static final int UNDEFINED = 0; 123 124 127 public static final String UNDEFINED_STATUS = "0.0"; 128 129 132 public static final int ADDRESS = 1; 133 134 137 public static final String ADDRESS_OTHER = "1.0"; 138 139 142 public static final String ADDRESS_MAILBOX = "1.1"; 143 144 147 public static final String ADDRESS_SYSTEM = "1.2"; 148 149 152 public static final String ADDRESS_SYNTAX = "1.3"; 153 154 157 public static final String ADDRESS_AMBIGUOUS = "1.4"; 158 159 162 public static final String ADDRESS_VALID = "1.5"; 163 164 167 public static final String ADDRESS_MOVED = "1.6"; 168 169 172 public static final String ADDRESS_SYNTAX_SENDER = "1.7"; 173 174 177 public static final String ADDRESS_SYSTEM_SENDER = "1.8"; 178 179 180 183 public static final int MAILBOX = 2; 184 185 188 public static final String MAILBOX_OTHER = "2.0"; 189 190 193 public static final String MAILBOX_DISABLED = "2.1"; 194 195 198 public static final String MAILBOX_FULL = "2.2"; 199 200 203 public static final String MAILBOX_MSG_TOO_BIG = "2.3"; 204 205 208 public static final String MAILBOX_LIST_EXPANSION = "2.4"; 209 210 211 214 public static final int SYSTEM = 3; 215 216 219 public static final String SYSTEM_OTHER = "3.0"; 220 221 224 public static final String SYSTEM_FULL = "3.1"; 225 226 229 public static final String SYSTEM_NOT_ACCEPTING = "3.2"; 230 231 234 public static final String SYSTEM_NOT_CAPABLE = "3.3"; 235 236 239 public static final String SYSTEM_MSG_TOO_BIG = "3.4"; 240 241 244 public static final String SYSTEM_CFG_ERROR = "3.5"; 245 246 247 250 public static final int NETWORK = 4; 251 252 255 public static final String NETWORK_OTHER = "4.0"; 256 257 260 public static final String NETWORK_NO_ANSWER = "4.1"; 261 262 265 public static final String NETWORK_CONNECTION = "4.2"; 266 267 270 public static final String NETWORK_DIR_SERVER = "4.3"; 271 272 275 public static final String NETWORK_ROUTE = "4.4"; 276 277 280 public static final String NETWORK_CONGESTION = "4.5"; 281 282 285 public static final String NETWORK_LOOP = "4.6"; 286 287 290 public static final String NETWORK_EXPIRED = "4.7"; 291 292 293 296 public static final int DELIVERY = 5; 297 298 301 public static final String DELIVERY_OTHER = "5.0"; 302 303 306 public static final String DELIVERY_INVALID_CMD = "5.1"; 307 308 311 public static final String DELIVERY_SYNTAX = "5.2"; 312 313 316 public static final String DELIVERY_TOO_MANY_REC = "5.3"; 317 318 321 public static final String DELIVERY_INVALID_ARG = "5.4"; 322 323 326 public static final String DELIVERY_VERSION = "5.5"; 327 328 329 332 public static final int CONTENT = 6; 333 334 337 public static final String CONTENT_OTHER = "6.0"; 338 339 342 public static final String CONTENT_UNSUPPORTED = "6.1"; 343 344 347 public static final String CONTENT_CONVERSION_NOT_ALLOWED = "6.2"; 348 349 352 public static final String CONTENT_CONVERSION_NOT_SUPPORTED = "6.3"; 353 354 357 public static final String CONTENT_CONVERSION_LOSS = "6.4"; 358 359 362 public static final String CONTENT_CONVERSION_FAILED = "6.5"; 363 364 365 368 public static final int SECURITY = 7; 369 370 373 public static final String SECURITY_OTHER = "7.0"; 374 375 378 public static final String SECURITY_AUTH = "7.1"; 379 380 383 public static final String SECURITY_LIST_EXP = "7.2"; 384 385 388 public static final String SECURITY_CONVERSION = "7.3"; 389 390 393 public static final String SECURITY_UNSUPPORTED = "7.4"; 394 395 398 public static final String SECURITY_CRYPT_FAIL = "7.5"; 399 400 403 public static final String SECURITY_CRYPT_ALGO = "7.6"; 404 405 408 public static final String SECURITY_INTEGRITY = "7.7"; 409 410 411 413 public static String getStatus(int type, String detail) { 414 return type + "." + detail; 415 } 416 417 public static String getStatus(int type, int subject, int detail) { 418 return type + "." + subject + "." + detail; 419 } 420 } 421 422 private static final RFC822DateFormat rfc822DateFormat = new RFC822DateFormat(); 423 424 private static final java.util.Random random = new java.util.Random (); 426 427 private static Pattern statusPattern; 429 430 private static Pattern diagPattern; 431 432 private static final String MACHINE_PATTERN = "[machine]"; 433 434 private String messageString = 435 "Hi. This is the James mail server at [machine].\nI'm afraid I wasn't able to deliver your message to the following addresses.\nThis is a permanent error; I've given up. Sorry it didn't work out. Below\nI include the list of recipients and the reason why I was unable to deliver\nyour message.\n"; 436 437 441 static { 442 Perl5Compiler compiler = new Perl5Compiler(); 443 String status_pattern_string = ".*\\s*([245]\\.\\d{1,3}\\.\\d{1,3}).*\\s*"; 444 String diag_pattern_string = "^\\d{3}\\s.*$"; 445 try { 446 statusPattern = compiler. 447 compile(status_pattern_string, Perl5Compiler.READ_ONLY_MASK); 448 } catch(MalformedPatternException mpe) { 449 System.err.println ("Malformed pattern: " + status_pattern_string); 451 mpe.printStackTrace (System.err); 452 } 453 try { 454 diagPattern = compiler. 455 compile(diag_pattern_string, Perl5Compiler.READ_ONLY_MASK); 456 } catch(MalformedPatternException mpe) { 457 System.err.println ("Malformed pattern: " + diag_pattern_string); 459 } 460 } 461 462 465 public void init() throws MessagingException { 466 super.init(); 467 if (getInitParameter("messageString") != null) { 468 messageString = getInitParameter("messageString"); 469 } 470 471 MailcapCommandMap mail_cap = 472 (MailcapCommandMap ) CommandMap.getDefaultCommandMap(); 473 474 mail_cap.addMailcap ("message/delivery-status;; x-java-content-handler=com.sun.mail.handlers.message_rfc822"); 475 CommandMap.setDefaultCommandMap (mail_cap); 476 } 477 478 486 public void service(Mail originalMail) throws MessagingException { 487 488 489 Mail newMail = ((MailImpl) originalMail).duplicate(newName((MailImpl) originalMail)); 491 try { 495 ((MailImpl)newMail).setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress()); 496 ((MailImpl)newMail).setRemoteHost(java.net.InetAddress.getLocalHost().getHostName()); 497 } catch (java.net.UnknownHostException _) { 498 ((MailImpl) newMail).setRemoteAddr("127.0.0.1"); 499 ((MailImpl) newMail).setRemoteHost("localhost"); 500 } 501 MailAddress returnAddress = getExistingReturnPath(originalMail); 502 Collection newRecipients = new HashSet (); 503 if (returnAddress == SpecialAddress.NULL) { 504 if (isDebug) 505 log("Processing a bounce request for a message with an empty reverse-path. No bounce will be sent."); 506 if(!getPassThrough(originalMail)) { 507 originalMail.setState(Mail.GHOST); 508 } 509 return; 510 } else if (returnAddress == null) { 511 log("WARNING: Mail to be bounced does not contain a reverse-path."); 512 } else { 513 if (isDebug) 514 log("Processing a bounce request for a message with a return path header. The bounce will be sent to " + returnAddress); 515 } 516 517 newRecipients.add(returnAddress); 518 ((MailImpl)newMail).setRecipients(newRecipients); 519 520 if (isDebug) { 521 MailImpl newMailImpl = (MailImpl) newMail; 522 log("New mail - sender: " + newMailImpl.getSender() 523 + ", recipients: " + 524 arrayToString(newMailImpl.getRecipients().toArray()) 525 + ", name: " + newMailImpl.getName() 526 + ", remoteHost: " + newMailImpl.getRemoteHost() 527 + ", remoteAddr: " + newMailImpl.getRemoteAddr() 528 + ", state: " + newMailImpl.getState() 529 + ", lastUpdated: " + newMailImpl.getLastUpdated() 530 + ", errorMessage: " + newMailImpl.getErrorMessage()); 531 } 532 533 MimeMessage newMessage = 535 new MimeMessage (Session.getDefaultInstance(System.getProperties(), 536 null)); 537 538 MimeMultipartReport multipart = new MimeMultipartReport (); 539 multipart.setReportType ("delivery-status"); 540 541 MimeBodyPart part1 = createTextMsg(originalMail); 543 multipart.addBodyPart(part1); 544 545 MimeBodyPart part2 = createDSN(originalMail); 547 multipart.addBodyPart(part2); 548 549 550 if (getAttachmentType() != NONE) { 552 MimeBodyPart part3 = createAttachedOriginal(originalMail); 553 multipart.addBodyPart(part3); 554 } 555 556 557 newMessage.setContent(multipart); 559 newMessage.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType()); 560 newMail.setMessage(newMessage); 561 562 setRecipients(newMail, getRecipients(originalMail), originalMail); 564 setTo(newMail, getTo(originalMail), originalMail); 565 setSubjectPrefix(newMail, getSubjectPrefix(originalMail), originalMail); 566 if(newMail.getMessage().getHeader(RFC2822Headers.DATE) == null) { 567 newMail.getMessage().setHeader(RFC2822Headers.DATE,rfc822DateFormat.format(new Date ())); 568 } 569 setReplyTo(newMail, getReplyTo(originalMail), originalMail); 570 setReversePath(newMail, getReversePath(originalMail), originalMail); 571 setSender(newMail, getSender(originalMail), originalMail); 572 setIsReply(newMail, isReply(originalMail), originalMail); 573 574 newMail.getMessage().saveChanges(); 575 getMailetContext().sendMail(newMail); 576 577 if(!getPassThrough(originalMail)) { 579 originalMail.setState(Mail.GHOST); 580 } 581 } 582 583 590 protected MimeBodyPart createTextMsg(Mail originalMail) 591 throws MessagingException { 592 MimeBodyPart part1 = new MimeBodyPart (); 593 StringWriter sout = new StringWriter (); 594 PrintWriter out = new PrintWriter (sout, true); 595 String machine = "[unknown]"; 596 try { 597 InetAddress me = InetAddress.getLocalHost(); 598 machine = me.getHostName(); 599 } catch(Exception e){ 600 machine = "[address unknown]"; 601 } 602 603 StringBuffer bounceBuffer = 604 new StringBuffer (128).append (messageString); 605 int m_idx_begin = messageString.indexOf(MACHINE_PATTERN); 606 if (m_idx_begin != -1) { 607 bounceBuffer.replace (m_idx_begin, 608 m_idx_begin+MACHINE_PATTERN.length(), 609 machine); 610 } 611 out.println(bounceBuffer.toString()); 612 out.println("Failed recipient(s):"); 613 for (Iterator i = originalMail.getRecipients().iterator(); i.hasNext(); ) { 614 out.println(i.next()); 615 } 616 MessagingException ex = (MessagingException )originalMail.getAttribute("delivery-error"); 617 out.println(); 618 out.println("Error message:"); 619 out.println(getErrorMsg(ex)); 620 out.println(); 621 622 part1.setText(sout.toString()); 623 return part1; 624 } 625 626 633 protected MimeBodyPart createDSN(Mail originalMail) throws MessagingException { 634 MimeBodyPart dsn = new MimeBodyPart (); 635 MimeMessage dsnMessage = 636 new MimeMessage (Session.getDefaultInstance(System.getProperties(), null)); 637 StringWriter sout = new StringWriter (); 638 PrintWriter out = new PrintWriter (sout, true); 639 String errorMsg = null; 640 String nameType = null; 641 642 643 647 654 655 nameType = "dns"; 659 try { 660 String myAddress = 661 (String )getMailetContext().getAttribute(Constants.HELLO_NAME); 662 665 out.println("Reporting-MTA: "+nameType+"; "+myAddress); 666 } catch(Exception e){ 667 log("WARNING: sending DSN without required Reporting-MTA Address"); 669 } 670 671 673 out.println("Received-From-MTA: "+nameType+"; "+originalMail.getRemoteHost()); 675 676 678 682 Iterator recipients = originalMail.getRecipients().iterator(); 683 while (recipients.hasNext()) 684 { 685 MailAddress rec = (MailAddress)recipients.next(); 686 String addressType = "rfc822"; 687 688 out.println(); 690 691 694 out.println("Final-Recipient: "+addressType+"; "+rec.toString()); 696 697 out.println("Action: failed"); 701 702 MessagingException ex = 706 (MessagingException ) originalMail.getAttribute("delivery-error"); 707 out.println("Status: "+getStatus(ex)); 708 709 712 String diagnosticType = null; 714 String diagnosticCode = getErrorMsg(ex); 719 Perl5Matcher diagMatcher = new Perl5Matcher(); 722 boolean smtpDiagCodeAvailable = 723 diagMatcher.matches(diagnosticCode, diagPattern); 724 if (smtpDiagCodeAvailable){ 725 diagnosticType = "smtp"; 726 } else { 727 diagnosticType = "X-James"; 728 } 729 out.println("Diagnostic-Code: "+diagnosticType+"; "+diagnosticCode); 730 731 out.println("Last-Attempt-Date: "+ 733 rfc822DateFormat.format(((MailImpl)originalMail).getLastUpdated())); 734 735 738 740 } 741 742 743 dsnMessage.setText(sout.toString()); 745 dsnMessage.saveChanges(); 746 747 748 750 dsn.setContent(dsnMessage, "message/delivery-status"); 751 dsn.setDescription("Delivery Status Notification"); 752 dsn.setFileName("status.dat"); 753 return dsn; 754 } 755 756 763 protected MimeBodyPart createAttachedOriginal(Mail originalMail) 764 throws MessagingException { 765 MimeBodyPart part = new MimeBodyPart (); 766 MimeMessage originalMessage = originalMail.getMessage(); 767 part.setContent(originalMessage, "message/rfc822"); 768 if ((originalMessage.getSubject() != null) && 769 (originalMessage.getSubject().trim().length() > 0)) { 770 part.setFileName(originalMessage.getSubject().trim()); 771 } else { 772 part.setFileName("No Subject"); 773 } 774 part.setDisposition("Attachment"); 775 return part; 776 } 777 778 786 protected String getStatus(MessagingException me) { 787 if (me.getNextException() == null) { 788 String mess = me.getMessage(); 789 Perl5Matcher m = new Perl5Matcher(); 790 StringBuffer sb = new StringBuffer (); 791 if (m.matches(mess, statusPattern)) { 792 MatchResult res = m.getMatch(); 793 sb.append(res.group(1)); 794 return sb.toString(); 795 } 796 if (mess.startsWith("There are no DNS entries for the hostname")) 798 return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.ADDRESS_SYSTEM); 799 800 if (mess.equals("No mail server(s) available at this time.")) 803 return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_NO_ANSWER); 804 805 return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.UNDEFINED_STATUS); 807 } else { 808 String retVal = null; 809 Exception ex1 = me.getNextException(); 810 Perl5Matcher m = new Perl5Matcher (); 811 StringBuffer sb = new StringBuffer (); 812 if (m.matches(ex1.getMessage(), statusPattern)) { 813 MatchResult res = m.getMatch(); 814 sb.append(res.group(1)); 815 return sb.toString(); 816 } else if (ex1 instanceof SendFailedException ) { 817 819 if (ex1.getMessage().startsWith("4")) { 822 return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.DELIVERY_OTHER); 823 } else return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_OTHER); 824 } else if (ex1 instanceof UnknownHostException ) { 825 return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.ADDRESS_SYSTEM); 827 } else if (ex1 instanceof ConnectException ) { 828 return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_CONNECTION); 830 } else if (ex1 instanceof SocketException ) { 831 return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_CONNECTION); 833 } else { 834 return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.UNDEFINED_STATUS); 836 } 837 } 838 } 839 840 845 protected String getErrorMsg(MessagingException me) { 846 if (me.getNextException() == null) { 847 return me.getMessage().trim(); 848 } else { 849 Exception ex1 = me.getNextException(); 850 return ex1.getMessage().trim(); 851 } 852 } 853 854 857 private String arrayToString(Object [] array) { 858 if (array == null) { 859 return "null"; 860 } 861 StringBuffer sb = new StringBuffer (1024); 862 sb.append("["); 863 for (int i = 0; i < array.length; i++) { 864 if (i > 0) { 865 sb.append(","); 866 } 867 sb.append(array[i]); 868 } 869 sb.append("]"); 870 return sb.toString(); 871 } 872 873 879 protected String newName(MailImpl mail) throws MessagingException { 880 String oldName = mail.getName(); 881 882 if (oldName.length() > 76) { 887 int count = 0; 888 int index = 0; 889 while ((index = oldName.indexOf('!', index + 1)) >= 0) { 890 count++; 891 } 892 if (count > 7) { 894 throw new MessagingException ("Unable to create a new message name: too long." 895 + " Possible loop in config.xml."); 896 } 897 else { 898 oldName = oldName.substring(0, 76); 899 } 900 } 901 902 StringBuffer nameBuffer = 903 new StringBuffer (64) 904 .append(oldName) 905 .append("-!") 906 .append(random.nextInt(1048576)); 907 return nameBuffer.toString(); 908 } 909 910 911 912 public String getMailetInfo() { 913 return "DSNBounce Mailet"; 914 } 915 916 917 918 919 920 protected String [] getAllowedInitParameters() { 921 String [] allowedArray = { 922 "debug", 923 "passThrough", 924 "messageString", 925 "attachment", 926 "sender", 927 "prefix" 928 }; 929 return allowedArray; 930 } 931 932 935 protected int getAttachmentType() throws MessagingException { 936 if(getInitParameter("attachment") == null) { 937 return MESSAGE; 938 } else { 939 return getTypeCode(getInitParameter("attachment")); 940 } 941 } 942 943 944 947 protected Collection getRecipients() { 948 Collection newRecipients = new HashSet (); 949 newRecipients.add(SpecialAddress.REVERSE_PATH); 950 return newRecipients; 951 } 952 953 956 protected InternetAddress [] getTo() { 957 InternetAddress [] apparentlyTo = new InternetAddress [1]; 958 apparentlyTo[0] = SpecialAddress.REVERSE_PATH.toInternetAddress(); 959 return apparentlyTo; 960 } 961 962 965 protected MailAddress getReversePath(Mail originalMail) { 966 return SpecialAddress.NULL; 967 } 968 969 970 971 972 973 } 974 | Popular Tags |