001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.transport.mailets;
019:
020: import org.apache.james.Constants;
021: import org.apache.james.core.MailImpl;
022: import org.apache.james.util.mail.MimeMultipartReport;
023: import org.apache.james.util.mail.dsn.DSNStatus;
024: import org.apache.mailet.Mail;
025: import org.apache.mailet.MailAddress;
026: import org.apache.mailet.RFC2822Headers;
027: import org.apache.mailet.dates.RFC822DateFormat;
028: import org.apache.oro.text.regex.MalformedPatternException;
029: import org.apache.oro.text.regex.MatchResult;
030: import org.apache.oro.text.regex.Pattern;
031: import org.apache.oro.text.regex.Perl5Compiler;
032: import org.apache.oro.text.regex.Perl5Matcher;
033:
034: import javax.mail.MessagingException;
035: import javax.mail.SendFailedException;
036: import javax.mail.Session;
037: import javax.mail.internet.InternetAddress;
038: import javax.mail.internet.MimeBodyPart;
039: import javax.mail.internet.MimeMessage;
040:
041: import java.io.PrintWriter;
042: import java.io.StringWriter;
043: import java.net.ConnectException;
044: import java.net.InetAddress;
045: import java.net.SocketException;
046: import java.net.UnknownHostException;
047: import java.util.Collection;
048: import java.util.Date;
049: import java.util.HashSet;
050: import java.util.Iterator;
051:
052: /**
053: *
054: * <P>Generates a Delivery Status Notification (DSN)
055: * Note that this is different than a mail-client's
056: * reply, which would use the Reply-To or From header.</P>
057: * <P>Bounced messages are attached in their entirety (headers and
058: * content) and the resulting MIME part type is "message/rfc822".<BR>
059: * The reverse-path and the Return-Path header of the response is set to "null" ("<>"),
060: * meaning that no reply should be sent.</P>
061: * <P>A sender of the notification message can optionally be specified.
062: * If one is not specified, the postmaster's address will be used.<BR>
063: * <P>Supports the <CODE>passThrough</CODE> init parameter (true if missing).</P>
064: *
065: * <P>Sample configuration:</P>
066: * <PRE><CODE>
067: * <mailet match="All" class="DSNBounce">
068: * <sender><I>an address or postmaster or sender or unaltered,
069: default=postmaster</I></sender>
070: * <prefix><I>optional subject prefix prepended to the original
071: message</I></prefix>
072: * <attachment><I>message, heads or none, default=message</I></attachment>
073: * <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>
074: * <passThrough><I>true or false, default=true</I></passThrough>
075: * <debug><I>true or false, default=false</I></debug>
076: * </mailet>
077: * </CODE></PRE>
078: *
079: * @see org.apache.james.transport.mailets.AbstractNotify
080: */
081:
082: public class DSNBounce extends AbstractNotify {
083:
084: private static final RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
085:
086: // Used to generate new mail names
087: private static final java.util.Random random = new java.util.Random();
088:
089: // regexp pattern for scaning status code from exception message
090: private static Pattern statusPattern;
091:
092: private static Pattern diagPattern;
093:
094: private static final String MACHINE_PATTERN = "[machine]";
095:
096: private String messageString = null;
097:
098: /*
099: * Static initializer.<p>
100: * Compiles patterns for processing exception messages.<p>
101: */
102: static {
103: Perl5Compiler compiler = new Perl5Compiler();
104: String status_pattern_string = ".*\\s*([245]\\.\\d{1,3}\\.\\d{1,3}).*\\s*";
105: String diag_pattern_string = "^\\d{3}\\s.*$";
106: try {
107: statusPattern = compiler.compile(status_pattern_string,
108: Perl5Compiler.READ_ONLY_MASK);
109: } catch (MalformedPatternException mpe) {
110: //this should not happen as the pattern string is hardcoded.
111: System.err.println("Malformed pattern: "
112: + status_pattern_string);
113: mpe.printStackTrace(System.err);
114: }
115: try {
116: diagPattern = compiler.compile(diag_pattern_string,
117: Perl5Compiler.READ_ONLY_MASK);
118: } catch (MalformedPatternException mpe) {
119: //this should not happen as the pattern string is hardcoded.
120: System.err.println("Malformed pattern: "
121: + diag_pattern_string);
122: }
123: }
124:
125: /**
126: * Initialize the mailet
127: */
128: public void init() throws MessagingException {
129: super .init();
130: messageString = getInitParameter(
131: "messageString",
132: "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");
133: }
134:
135: /**
136: * Service does the hard work and bounces the originalMail in the format specified by RFC3464.
137: *
138: * @param originalMail the mail to bounce
139: * @throws MessagingException if a problem arises formulating the redirected mail
140: *
141: * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail)
142: */
143: public void service(Mail originalMail) throws MessagingException {
144:
145: // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
146: MailImpl newMail = new MailImpl(originalMail,
147: newName(originalMail));
148: try {
149: // We don't need to use the original Remote Address and Host,
150: // and doing so would likely cause a loop with spam detecting
151: // matchers.
152: try {
153: newMail.setRemoteAddr(java.net.InetAddress
154: .getLocalHost().getHostAddress());
155: newMail.setRemoteHost(java.net.InetAddress
156: .getLocalHost().getHostName());
157: } catch (java.net.UnknownHostException _) {
158: newMail.setRemoteAddr("127.0.0.1");
159: newMail.setRemoteHost("localhost");
160: }
161:
162: if (originalMail.getSender() == null) {
163: if (isDebug)
164: log("Processing a bounce request for a message with an empty reverse-path. No bounce will be sent.");
165: if (!getPassThrough(originalMail)) {
166: originalMail.setState(Mail.GHOST);
167: }
168: return;
169: }
170:
171: MailAddress reversePath = originalMail.getSender();
172: if (isDebug)
173: log("Processing a bounce request for a message with a reverse path. The bounce will be sent to "
174: + reversePath);
175:
176: Collection newRecipients = new HashSet();
177: newRecipients.add(reversePath);
178: newMail.setRecipients(newRecipients);
179:
180: if (isDebug) {
181: log("New mail - sender: "
182: + newMail.getSender()
183: + ", recipients: "
184: + arrayToString(newMail.getRecipients()
185: .toArray()) + ", name: "
186: + newMail.getName() + ", remoteHost: "
187: + newMail.getRemoteHost() + ", remoteAddr: "
188: + newMail.getRemoteAddr() + ", state: "
189: + newMail.getState() + ", lastUpdated: "
190: + newMail.getLastUpdated() + ", errorMessage: "
191: + newMail.getErrorMessage());
192: }
193:
194: // create the bounce message
195: MimeMessage newMessage = new MimeMessage(Session
196: .getDefaultInstance(System.getProperties(), null));
197:
198: MimeMultipartReport multipart = new MimeMultipartReport();
199: multipart.setReportType("delivery-status");
200:
201: // part 1: descripive text message
202: MimeBodyPart part1 = createTextMsg(originalMail);
203: multipart.addBodyPart(part1);
204:
205: // part 2: DSN
206: MimeBodyPart part2 = createDSN(originalMail);
207: multipart.addBodyPart(part2);
208:
209: // part 3: original mail (optional)
210: if (getAttachmentType() != NONE) {
211: MimeBodyPart part3 = createAttachedOriginal(
212: originalMail, getAttachmentType());
213: multipart.addBodyPart(part3);
214: }
215:
216: // stuffing all together
217: newMessage.setContent(multipart);
218: newMessage.setHeader(RFC2822Headers.CONTENT_TYPE, multipart
219: .getContentType());
220: newMail.setMessage(newMessage);
221:
222: //Set additional headers
223: setRecipients(newMail, getRecipients(originalMail),
224: originalMail);
225: setTo(newMail, getTo(originalMail), originalMail);
226: setSubjectPrefix(newMail, getSubjectPrefix(originalMail),
227: originalMail);
228: if (newMail.getMessage().getHeader(RFC2822Headers.DATE) == null) {
229: newMail.getMessage().setHeader(RFC2822Headers.DATE,
230: rfc822DateFormat.format(new Date()));
231: }
232: setReplyTo(newMail, getReplyTo(originalMail), originalMail);
233: setReversePath(newMail, getReversePath(originalMail),
234: originalMail);
235: setSender(newMail, getSender(originalMail), originalMail);
236: setIsReply(newMail, isReply(originalMail), originalMail);
237:
238: newMail.getMessage().saveChanges();
239: getMailetContext().sendMail(newMail);
240: } finally {
241: newMail.dispose();
242: }
243:
244: // ghosting the original mail
245: if (!getPassThrough(originalMail)) {
246: originalMail.setState(Mail.GHOST);
247: }
248: }
249:
250: /**
251: * Create a MimeBodyPart with a textual description for human readers.
252: *
253: * @param originalMail
254: * @return MimeBodyPart
255: * @throws MessagingException
256: */
257: protected MimeBodyPart createTextMsg(Mail originalMail)
258: throws MessagingException {
259: MimeBodyPart part1 = new MimeBodyPart();
260: StringWriter sout = new StringWriter();
261: PrintWriter out = new PrintWriter(sout, true);
262: String machine = "[unknown]";
263: try {
264: InetAddress me = InetAddress.getLocalHost();
265: machine = me.getHostName();
266: } catch (Exception e) {
267: machine = "[address unknown]";
268: }
269:
270: StringBuffer bounceBuffer = new StringBuffer(128)
271: .append(messageString);
272: int m_idx_begin = messageString.indexOf(MACHINE_PATTERN);
273: if (m_idx_begin != -1) {
274: bounceBuffer.replace(m_idx_begin, m_idx_begin
275: + MACHINE_PATTERN.length(), machine);
276: }
277: out.println(bounceBuffer.toString());
278: out.println("Failed recipient(s):");
279: for (Iterator i = originalMail.getRecipients().iterator(); i
280: .hasNext();) {
281: out.println(i.next());
282: }
283: MessagingException ex = (MessagingException) originalMail
284: .getAttribute("delivery-error");
285: out.println();
286: out.println("Error message:");
287: out.println(getErrorMsg(ex));
288: out.println();
289:
290: part1.setText(sout.toString());
291: return part1;
292: }
293:
294: /**
295: * creates the DSN-bodypart for automated processing
296: *
297: * @param originalMail
298: * @return MimeBodyPart dsn-bodypart
299: * @throws MessagingException
300: */
301: protected MimeBodyPart createDSN(Mail originalMail)
302: throws MessagingException {
303: MimeBodyPart dsn = new MimeBodyPart();
304: StringWriter sout = new StringWriter();
305: PrintWriter out = new PrintWriter(sout, true);
306: String nameType = null;
307:
308: ////////////////////////
309: // per message fields //
310: ////////////////////////
311:
312: //optional: envelope-id
313: // TODO: Envelope-Id
314: // The Original-Envelope-Id is NOT the same as the Message-Id from the header.
315: // The Message-Id identifies the content of the message, while the Original-Envelope-ID
316: // identifies the transaction in which the message is sent. (see RFC3461)
317: // so do NOT out.println("Original-Envelope-Id:"+originalMail.getMessage().getMessageID());
318:
319: //required: reporting MTA
320: // this is always us, since we do not translate non-internet-mail
321: // failure reports into DSNs
322: nameType = "dns";
323: try {
324: String myAddress = (String) getMailetContext()
325: .getAttribute(Constants.HELLO_NAME);
326: /*
327: String myAddress = InetAddress.getLocalHost().getCanonicalHostName();
328: */
329: out
330: .println("Reporting-MTA: " + nameType + "; "
331: + myAddress);
332: } catch (Exception e) {
333: // we should always know our address, so we shouldn't get here
334: log("WARNING: sending DSN without required Reporting-MTA Address");
335: }
336:
337: //only for gateways to non-internet mail systems: dsn-gateway
338:
339: //optional: received from
340: out.println("Received-From-MTA: " + nameType + "; "
341: + originalMail.getRemoteHost());
342:
343: //optional: Arrival-Date
344:
345: //////////////////////////
346: // per recipient fields //
347: //////////////////////////
348:
349: Iterator recipients = originalMail.getRecipients().iterator();
350: while (recipients.hasNext()) {
351: MailAddress rec = (MailAddress) recipients.next();
352: String addressType = "rfc822";
353:
354: //required: blank line
355: out.println();
356:
357: //optional: original recipient (see RFC3461)
358: //out.println("Original-Recipient: "+addressType+"; "+ ??? );
359:
360: //required: final recipient
361: out.println("Final-Recipient: " + addressType + "; "
362: + rec.toString());
363:
364: //required: action
365: // alowed values: failed, delayed, delivered, relayed, expanded
366: // TODO: until now, we do error-bounces only
367: out.println("Action: failed");
368:
369: //required: status
370: // get Exception for getting status information
371: // TODO: it would be nice if the SMTP-handler would set a status attribute we can use here
372: MessagingException ex = (MessagingException) originalMail
373: .getAttribute("delivery-error");
374: out.println("Status: " + getStatus(ex));
375:
376: //optional: remote MTA
377: //to which MTA were we talking while the Error occured?
378:
379: //optional: diagnostic-code
380: String diagnosticType = null;
381: // this typically is the return value received during smtp
382: // (or other transport) communication
383: // and should be stored as attribute by the smtp handler
384: // but until now we only have error-messages.
385: String diagnosticCode = getErrorMsg(ex);
386: // Sometimes this is the smtp diagnostic code,
387: // but James often gives us other messages
388: Perl5Matcher diagMatcher = new Perl5Matcher();
389: boolean smtpDiagCodeAvailable = diagMatcher.matches(
390: diagnosticCode, diagPattern);
391: if (smtpDiagCodeAvailable) {
392: diagnosticType = "smtp";
393: } else {
394: diagnosticType = "X-James";
395: }
396: out.println("Diagnostic-Code: " + diagnosticType + "; "
397: + diagnosticCode);
398:
399: //optional: last attempt
400: out.println("Last-Attempt-Date: "
401: + rfc822DateFormat.format(originalMail
402: .getLastUpdated()));
403:
404: //optional: retry until
405: //only for 'delayed' reports .. but we don't report this (at least until now)
406:
407: //optional: extension fields
408:
409: }
410:
411: // Changed this from rfc822 handling to text/plain
412: // It should be handled correctly as delivery-status but it
413: // is better text/plain than rfc822 (rfc822 add message headers not
414: // allowed in the delivery-status.
415: // text/plain does not support rfc822 header encoding that we
416: // should support here.
417: dsn.setContent(sout.toString(), "text/plain");
418: dsn.setHeader("Content-Type", "message/delivery-status");
419: dsn.setDescription("Delivery Status Notification");
420: dsn.setFileName("status.dat");
421: return dsn;
422: }
423:
424: /**
425: * Create a MimeBodyPart with the original Mail as Attachment
426: *
427: * @param originalMail
428: * @return MimeBodyPart
429: * @throws MessagingException
430: */
431: protected MimeBodyPart createAttachedOriginal(Mail originalMail,
432: int attachmentType) throws MessagingException {
433: MimeBodyPart part = new MimeBodyPart();
434: MimeMessage originalMessage = originalMail.getMessage();
435:
436: if (attachmentType == HEADS) {
437: part.setContent(getMessageHeaders(originalMessage),
438: "text/plain");
439: part.setHeader("Content-Type", "text/rfc822-headers");
440: } else {
441: part.setContent(originalMessage, "message/rfc822");
442: }
443:
444: if ((originalMessage.getSubject() != null)
445: && (originalMessage.getSubject().trim().length() > 0)) {
446: part.setFileName(originalMessage.getSubject().trim());
447: } else {
448: part.setFileName("No Subject");
449: }
450: part.setDisposition("Attachment");
451: return part;
452: }
453:
454: /**
455: * Guessing status code by the exception provided.
456: * This method should use the status attribute when the
457: * SMTP-handler somewhen provides it
458: *
459: * @param MessagingException
460: * @return status code
461: */
462: protected String getStatus(MessagingException me) {
463: if (me.getNextException() == null) {
464: String mess = me.getMessage();
465: Perl5Matcher m = new Perl5Matcher();
466: StringBuffer sb = new StringBuffer();
467: if (m.matches(mess, statusPattern)) {
468: MatchResult res = m.getMatch();
469: sb.append(res.group(1));
470: return sb.toString();
471: }
472: // bad destination system adress
473: if (mess
474: .startsWith("There are no DNS entries for the hostname"))
475: return DSNStatus.getStatus(DSNStatus.PERMANENT,
476: DSNStatus.ADDRESS_SYSTEM);
477:
478: // no answer from host (4.4.1) or
479: // system not accepting network messages (4.3.2), lets guess ...
480: if (mess
481: .equals("No mail server(s) available at this time."))
482: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
483: DSNStatus.NETWORK_NO_ANSWER);
484:
485: // other/unknown error
486: return DSNStatus.getStatus(DSNStatus.PERMANENT,
487: DSNStatus.UNDEFINED_STATUS);
488: } else {
489: Exception ex1 = me.getNextException();
490: Perl5Matcher m = new Perl5Matcher();
491: StringBuffer sb = new StringBuffer();
492: if (m.matches(ex1.getMessage(), statusPattern)) {
493: MatchResult res = m.getMatch();
494: sb.append(res.group(1));
495: return sb.toString();
496: } else if (ex1 instanceof SendFailedException) {
497: // other/undefined protocol status
498: int smtpCode = 0;
499: try {
500: smtpCode = Integer.parseInt(ex1.getMessage()
501: .substring(0, 3));
502: } catch (NumberFormatException e) {
503: }
504:
505: switch (smtpCode) {
506:
507: // Req mail action not taken: mailbox unavailable
508: case 450:
509: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
510: DSNStatus.MAILBOX_OTHER);
511: // Req action aborted: local error in processing
512: case 451:
513: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
514: DSNStatus.SYSTEM_OTHER);
515: // Req action not taken: insufficient sys storage
516: case 452:
517: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
518: DSNStatus.SYSTEM_FULL);
519: // Syntax error, command unrecognized
520: case 500:
521: return DSNStatus.getStatus(DSNStatus.PERMANENT,
522: DSNStatus.DELIVERY_SYNTAX);
523: // Syntax error in parameters or arguments
524: case 501:
525: return DSNStatus.getStatus(DSNStatus.PERMANENT,
526: DSNStatus.DELIVERY_INVALID_ARG);
527: // Command not implemented
528: case 502:
529: return DSNStatus.getStatus(DSNStatus.PERMANENT,
530: DSNStatus.DELIVERY_INVALID_CMD);
531: // Bad sequence of commands
532: case 503:
533: return DSNStatus.getStatus(DSNStatus.PERMANENT,
534: DSNStatus.DELIVERY_INVALID_CMD);
535: // Command parameter not implemented
536: case 504:
537: return DSNStatus.getStatus(DSNStatus.PERMANENT,
538: DSNStatus.DELIVERY_INVALID_ARG);
539: // Req mail action not taken: mailbox unavailable
540: case 550:
541: return DSNStatus.getStatus(DSNStatus.PERMANENT,
542: DSNStatus.MAILBOX_OTHER);
543: // User not local; please try <...>
544: // 5.7.1 Select another host to act as your forwarder
545: case 551:
546: return DSNStatus.getStatus(DSNStatus.PERMANENT,
547: DSNStatus.SECURITY_AUTH);
548: // Req mail action aborted: exceeded storage alloc
549: case 552:
550: return DSNStatus.getStatus(DSNStatus.PERMANENT,
551: DSNStatus.MAILBOX_FULL);
552: // Req action not taken: mailbox name not allowed
553: case 553:
554: return DSNStatus.getStatus(DSNStatus.PERMANENT,
555: DSNStatus.ADDRESS_SYNTAX);
556: // Transaction failed
557: case 554:
558: return DSNStatus.getStatus(DSNStatus.PERMANENT,
559: DSNStatus.UNDEFINED_STATUS);
560: // Not authorized. This is not an SMTP code, but many server use it.
561: case 571:
562: return DSNStatus.getStatus(DSNStatus.PERMANENT,
563: DSNStatus.SECURITY_AUTH);
564:
565: default:
566: // if we get an smtp returncode starting with 4
567: // it is an persistent transient error, else permanent
568: if (ex1.getMessage().startsWith("4")) {
569: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
570: DSNStatus.DELIVERY_OTHER);
571: } else
572: return DSNStatus.getStatus(DSNStatus.PERMANENT,
573: DSNStatus.DELIVERY_OTHER);
574: }
575:
576: } else if (ex1 instanceof UnknownHostException) {
577: // bad destination system address
578: return DSNStatus.getStatus(DSNStatus.PERMANENT,
579: DSNStatus.ADDRESS_SYSTEM);
580: } else if (ex1 instanceof ConnectException) {
581: // bad connection
582: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
583: DSNStatus.NETWORK_CONNECTION);
584: } else if (ex1 instanceof SocketException) {
585: // bad connection
586: return DSNStatus.getStatus(DSNStatus.TRANSIENT,
587: DSNStatus.NETWORK_CONNECTION);
588: } else {
589: // other/undefined/unknown error
590: return DSNStatus.getStatus(DSNStatus.PERMANENT,
591: DSNStatus.UNDEFINED_STATUS);
592: }
593: }
594: }
595:
596: /**
597: * Utility method for getting the error message from the (nested) exception.
598: * @param MessagingException
599: * @return error message
600: */
601: protected String getErrorMsg(MessagingException me) {
602: if (me.getNextException() == null) {
603: return me.getMessage().trim();
604: } else {
605: Exception ex1 = me.getNextException();
606: return ex1.getMessage().trim();
607: }
608: }
609:
610: /**
611: * Utility method for obtaining a string representation of an array of Objects.
612: */
613: private String arrayToString(Object[] array) {
614: if (array == null) {
615: return "null";
616: }
617: StringBuffer sb = new StringBuffer(1024);
618: sb.append("[");
619: for (int i = 0; i < array.length; i++) {
620: if (i > 0) {
621: sb.append(",");
622: }
623: sb.append(array[i]);
624: }
625: sb.append("]");
626: return sb.toString();
627: }
628:
629: /**
630: * Create a unique new primary key name.
631: *
632: * @param mail the mail to use as the basis for the new mail name
633: * @return a new name
634: */
635: protected String newName(Mail mail) throws MessagingException {
636: String oldName = mail.getName();
637:
638: // Checking if the original mail name is too long, perhaps because of a
639: // loop caused by a configuration error.
640: // it could cause a "null pointer exception" in AvalonMailRepository much
641: // harder to understand.
642: if (oldName.length() > 76) {
643: int count = 0;
644: int index = 0;
645: while ((index = oldName.indexOf('!', index + 1)) >= 0) {
646: count++;
647: }
648: // It looks like a configuration loop. It's better to stop.
649: if (count > 7) {
650: throw new MessagingException(
651: "Unable to create a new message name: too long."
652: + " Possible loop in config.xml.");
653: } else {
654: oldName = oldName.substring(0, 76);
655: }
656: }
657:
658: StringBuffer nameBuffer = new StringBuffer(64).append(oldName)
659: .append("-!").append(random.nextInt(1048576));
660: return nameBuffer.toString();
661: }
662:
663: public String getMailetInfo() {
664: return "DSNBounce Mailet";
665: }
666:
667: /* ******************************************************************** */
668: /* ****************** Begin of getX and setX methods ****************** */
669: /* ******************************************************************** */
670:
671: /** Gets the expected init parameters. */
672: protected String[] getAllowedInitParameters() {
673: String[] allowedArray = { "debug", "passThrough",
674: "messageString", "attachment", "sender", "prefix" };
675: return allowedArray;
676: }
677:
678: /**
679: * @return the <CODE>attachment</CODE> init parameter, or <CODE>MESSAGE</CODE> if missing
680: */
681: protected int getAttachmentType() throws MessagingException {
682: return getTypeCode(getInitParameter("attachment", "message"));
683: }
684:
685: /**
686: * @return <CODE>SpecialAddress.REVERSE_PATH</CODE>
687: */
688: protected Collection getRecipients() {
689: Collection newRecipients = new HashSet();
690: newRecipients.add(SpecialAddress.REVERSE_PATH);
691: return newRecipients;
692: }
693:
694: /**
695: * @return <CODE>SpecialAddress.REVERSE_PATH</CODE>
696: */
697: protected InternetAddress[] getTo() {
698: InternetAddress[] apparentlyTo = new InternetAddress[1];
699: apparentlyTo[0] = SpecialAddress.REVERSE_PATH
700: .toInternetAddress();
701: return apparentlyTo;
702: }
703:
704: /**
705: * @return <CODE>SpecialAddress.NULL</CODE> (the meaning of bounce)
706: */
707: protected MailAddress getReversePath(Mail originalMail) {
708: return SpecialAddress.NULL;
709: }
710:
711: /* ******************************************************************** */
712: /* ******************* End of getX and setX methods ******************* */
713: /* ******************************************************************** */
714:
715: }
|