📄 dsnbounce.java
字号:
/**************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * * or more contributor license agreements. See the NOTICE file * * distributed with this work for additional information * * regarding copyright ownership. The ASF licenses this file * * to you under the Apache License, Version 2.0 (the * * "License"); you may not use this file except in compliance * * with the License. You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, * * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * ****************************************************************/package org.apache.james.transport.mailets;import org.apache.james.Constants;import org.apache.james.core.MailImpl;import org.apache.james.util.mail.MimeMultipartReport;import org.apache.james.util.mail.dsn.DSNStatus;import org.apache.mailet.Mail;import org.apache.mailet.MailAddress;import org.apache.mailet.RFC2822Headers;import org.apache.mailet.dates.RFC822DateFormat;import org.apache.oro.text.regex.MalformedPatternException;import org.apache.oro.text.regex.MatchResult;import org.apache.oro.text.regex.Pattern;import org.apache.oro.text.regex.Perl5Compiler;import org.apache.oro.text.regex.Perl5Matcher;import javax.mail.MessagingException;import javax.mail.SendFailedException;import javax.mail.Session;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeBodyPart;import javax.mail.internet.MimeMessage;import java.io.PrintWriter;import java.io.StringWriter;import java.net.ConnectException;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;import java.util.Collection;import java.util.Date;import java.util.HashSet;import java.util.Iterator;/** * * <P>Generates a Delivery Status Notification (DSN) * Note that this is different than a mail-client's * reply, which would use the Reply-To or From header.</P> * <P>Bounced messages are attached in their entirety (headers and * content) and the resulting MIME part type is "message/rfc822".<BR> * The reverse-path and the Return-Path header of the response is set to "null" ("<>"), * meaning that no reply should be sent.</P> * <P>A sender of the notification message can optionally be specified. * If one is not specified, the postmaster's address will be used.<BR> * <P>Supports the <CODE>passThrough</CODE> init parameter (true if missing).</P> * * <P>Sample configuration:</P> * <PRE><CODE> * <mailet match="All" class="DSNBounce"> * <sender><I>an address or postmaster or sender or unaltered, default=postmaster</I></sender> * <prefix><I>optional subject prefix prepended to the original message</I></prefix> * <attachment><I>message, heads or none, default=message</I></attachment> * <messageString><I>the message sent in the bounce, the first occurrence of the pattern [machine] is replaced with the name of the executing machine, default=Hi. This is the James mail server at [machine] ... </I></messageString> * <passThrough><I>true or false, default=true</I></passThrough> * <debug><I>true or false, default=false</I></debug> * </mailet> * </CODE></PRE> * * @see org.apache.james.transport.mailets.AbstractNotify */public class DSNBounce extends AbstractNotify { private static final RFC822DateFormat rfc822DateFormat = new RFC822DateFormat(); // Used to generate new mail names private static final java.util.Random random = new java.util.Random(); // regexp pattern for scaning status code from exception message private static Pattern statusPattern; private static Pattern diagPattern; private static final String MACHINE_PATTERN = "[machine]"; private String messageString = null; /* * Static initializer.<p> * Compiles patterns for processing exception messages.<p> */ static { Perl5Compiler compiler = new Perl5Compiler(); String status_pattern_string = ".*\\s*([245]\\.\\d{1,3}\\.\\d{1,3}).*\\s*"; String diag_pattern_string = "^\\d{3}\\s.*$"; try { statusPattern = compiler. compile(status_pattern_string, Perl5Compiler.READ_ONLY_MASK); } catch(MalformedPatternException mpe) { //this should not happen as the pattern string is hardcoded. System.err.println ("Malformed pattern: " + status_pattern_string); mpe.printStackTrace (System.err); } try { diagPattern = compiler. compile(diag_pattern_string, Perl5Compiler.READ_ONLY_MASK); } catch(MalformedPatternException mpe) { //this should not happen as the pattern string is hardcoded. System.err.println ("Malformed pattern: " + diag_pattern_string); } } /** * Initialize the mailet */ public void init() throws MessagingException { super.init(); messageString = getInitParameter("messageString","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"); } /** * Service does the hard work and bounces the originalMail in the format specified by RFC3464. * * @param originalMail the mail to bounce * @throws MessagingException if a problem arises formulating the redirected mail * * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail) */ public void service(Mail originalMail) throws MessagingException { // duplicates the Mail object, to be able to modify the new mail keeping the original untouched MailImpl newMail = new MailImpl(originalMail,newName(originalMail)); try { // We don't need to use the original Remote Address and Host, // and doing so would likely cause a loop with spam detecting // matchers. try { newMail.setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress()); newMail.setRemoteHost(java.net.InetAddress.getLocalHost().getHostName()); } catch (java.net.UnknownHostException _) { newMail.setRemoteAddr("127.0.0.1"); newMail.setRemoteHost("localhost"); } if (originalMail.getSender() == null) { if (isDebug) log("Processing a bounce request for a message with an empty reverse-path. No bounce will be sent."); if(!getPassThrough(originalMail)) { originalMail.setState(Mail.GHOST); } return; } MailAddress reversePath = originalMail.getSender(); if (isDebug) log("Processing a bounce request for a message with a reverse path. The bounce will be sent to " + reversePath); Collection newRecipients = new HashSet(); newRecipients.add(reversePath); newMail.setRecipients(newRecipients); if (isDebug) { log("New mail - sender: " + newMail.getSender() + ", recipients: " + arrayToString(newMail.getRecipients().toArray()) + ", name: " + newMail.getName() + ", remoteHost: " + newMail.getRemoteHost() + ", remoteAddr: " + newMail.getRemoteAddr() + ", state: " + newMail.getState() + ", lastUpdated: " + newMail.getLastUpdated() + ", errorMessage: " + newMail.getErrorMessage()); } // create the bounce message MimeMessage newMessage = new MimeMessage(Session.getDefaultInstance(System.getProperties(), null)); MimeMultipartReport multipart = new MimeMultipartReport (); multipart.setReportType ("delivery-status"); // part 1: descripive text message MimeBodyPart part1 = createTextMsg(originalMail); multipart.addBodyPart(part1); // part 2: DSN MimeBodyPart part2 = createDSN(originalMail); multipart.addBodyPart(part2); // part 3: original mail (optional) if (getAttachmentType() != NONE) { MimeBodyPart part3 = createAttachedOriginal(originalMail,getAttachmentType()); multipart.addBodyPart(part3); } // stuffing all together newMessage.setContent(multipart); newMessage.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType()); newMail.setMessage(newMessage); //Set additional headers setRecipients(newMail, getRecipients(originalMail), originalMail); setTo(newMail, getTo(originalMail), originalMail); setSubjectPrefix(newMail, getSubjectPrefix(originalMail), originalMail); if(newMail.getMessage().getHeader(RFC2822Headers.DATE) == null) { newMail.getMessage().setHeader(RFC2822Headers.DATE,rfc822DateFormat.format(new Date())); } setReplyTo(newMail, getReplyTo(originalMail), originalMail); setReversePath(newMail, getReversePath(originalMail), originalMail); setSender(newMail, getSender(originalMail), originalMail); setIsReply(newMail, isReply(originalMail), originalMail); newMail.getMessage().saveChanges(); getMailetContext().sendMail(newMail); } finally { newMail.dispose(); } // ghosting the original mail if(!getPassThrough(originalMail)) { originalMail.setState(Mail.GHOST); } } /** * Create a MimeBodyPart with a textual description for human readers. * * @param originalMail * @return MimeBodyPart * @throws MessagingException */ protected MimeBodyPart createTextMsg(Mail originalMail) throws MessagingException { MimeBodyPart part1 = new MimeBodyPart(); StringWriter sout = new StringWriter(); PrintWriter out = new PrintWriter(sout, true); String machine = "[unknown]"; try { InetAddress me = InetAddress.getLocalHost(); machine = me.getHostName(); } catch(Exception e){ machine = "[address unknown]"; } StringBuffer bounceBuffer = new StringBuffer(128).append (messageString); int m_idx_begin = messageString.indexOf(MACHINE_PATTERN); if (m_idx_begin != -1) { bounceBuffer.replace (m_idx_begin, m_idx_begin+MACHINE_PATTERN.length(), machine); } out.println(bounceBuffer.toString()); out.println("Failed recipient(s):"); for (Iterator i = originalMail.getRecipients().iterator(); i.hasNext(); ) { out.println(i.next()); } MessagingException ex = (MessagingException)originalMail.getAttribute("delivery-error"); out.println(); out.println("Error message:"); out.println(getErrorMsg(ex)); out.println(); part1.setText(sout.toString()); return part1; } /** * creates the DSN-bodypart for automated processing * * @param originalMail * @return MimeBodyPart dsn-bodypart * @throws MessagingException */ protected MimeBodyPart createDSN(Mail originalMail) throws MessagingException { MimeBodyPart dsn = new MimeBodyPart(); StringWriter sout = new StringWriter(); PrintWriter out = new PrintWriter(sout, true); String nameType = null; //////////////////////// // per message fields // //////////////////////// //optional: envelope-id // TODO: Envelope-Id // The Original-Envelope-Id is NOT the same as the Message-Id from the header. // The Message-Id identifies the content of the message, while the Original-Envelope-ID // identifies the transaction in which the message is sent. (see RFC3461) // so do NOT out.println("Original-Envelope-Id:"+originalMail.getMessage().getMessageID()); //required: reporting MTA // this is always us, since we do not translate non-internet-mail // failure reports into DSNs nameType = "dns"; try { String myAddress = (String)getMailetContext().getAttribute(Constants.HELLO_NAME); /* String myAddress = InetAddress.getLocalHost().getCanonicalHostName(); */ out.println("Reporting-MTA: "+nameType+"; "+myAddress); } catch(Exception e){ // we should always know our address, so we shouldn't get here log("WARNING: sending DSN without required Reporting-MTA Address"); } //only for gateways to non-internet mail systems: dsn-gateway //optional: received from out.println("Received-From-MTA: "+nameType+"; "+originalMail.getRemoteHost()); //optional: Arrival-Date //////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -