KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > transport > mailets > DSNBounce


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.transport.mailets;
19
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.net.ConnectException JavaDoc;
23 import java.net.InetAddress JavaDoc;
24 import java.net.SocketException JavaDoc;
25 import java.net.UnknownHostException JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30
31 import javax.mail.MessagingException JavaDoc;
32 import javax.mail.SendFailedException JavaDoc;
33 import javax.mail.Session JavaDoc;
34 import javax.mail.internet.InternetAddress JavaDoc;
35 import javax.mail.internet.MimeBodyPart JavaDoc;
36 import javax.mail.internet.MimeMessage JavaDoc;
37 import javax.mail.internet.MimeMultipart JavaDoc;
38
39 import javax.activation.CommandMap JavaDoc;
40 import javax.activation.MailcapCommandMap JavaDoc;
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 /**
60  *
61  * <P>Generates a Delivery Status Notification (DSN)
62  * Note that this is different than a mail-client's
63  * reply, which would use the Reply-To or From header.</P>
64  * <P>Bounced messages are attached in their entirety (headers and
65  * content) and the resulting MIME part type is "message/rfc822".<BR>
66  * The reverse-path and the Return-Path header of the response is set to "null" ("<>"),
67  * meaning that no reply should be sent.</P>
68  * <P>A sender of the notification message can optionally be specified.
69  * If one is not specified, the postmaster's address will be used.<BR>
70  * <P>Supports the <CODE>passThrough</CODE> init parameter (true if missing).</P>
71  *
72  * <P>Sample configuration:</P>
73  * <PRE><CODE>
74  * &lt;mailet match="All" class="DSNBounce">
75  * &lt;sender&gt;<I>an address or postmaster or sender or unaltered,
76  default=postmaster</I>&lt;/sender&gt;
77  * &lt;prefix&gt;<I>optional subject prefix prepended to the original
78  message</I>&lt;/prefix&gt;
79  * &lt;attachment&gt;<I>message or none, default=message</I>&lt;/attachment&gt;
80  * &lt;messageString&gt;<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>&lt;/messageString&gt;
81  * &lt;passThrough&gt;<I>true or false, default=true</I>&lt;/passThrough&gt;
82  * &lt;debug&gt;<I>true or false, default=false</I>&lt;/debug&gt;
83  * &lt;/mailet&gt;
84  * </CODE></PRE>
85  *
86  * @see org.apache.james.transport.mailets.AbstractNotify
87  */

88
89
90
91 public class DSNBounce extends AbstractNotify {
92
93
94     /**
95      * Constants and getters for RFC 3463 Enhanced Mail System Status Codes
96      *
97      * I suggest do extract this inner class for future use in the smtp-handler
98      *
99      */

100     public static class DSNStatus {
101         // status code classes
102
/**
103          * Success
104          */

105         public static final int SUCCESS = 2;
106
107         /**
108          * Persistent Transient Failure
109          */

110         public static final int TRANSIENT = 4;
111
112         /**
113          * Permanent Failure
114          */

115         public static final int PERMANENT = 5;
116
117         // subjects and details
118

119         /**
120          * Other or Undefined Status
121          */

122         public static final int UNDEFINED = 0;
123
124         /**
125          * Other undefined status
126          */

127         public static final String JavaDoc UNDEFINED_STATUS = "0.0";
128
129         /**
130          * Addressing Status
131          */

132         public static final int ADDRESS = 1;
133
134         /**
135          * Other address status
136          */

137         public static final String JavaDoc ADDRESS_OTHER = "1.0";
138
139         /**
140          * Bad destination mailbox address
141          */

142         public static final String JavaDoc ADDRESS_MAILBOX = "1.1";
143
144         /**
145          * Bad destination system address
146          */

147         public static final String JavaDoc ADDRESS_SYSTEM = "1.2";
148
149         /**
150          * Bad destination mailbox address syntax
151          */

152         public static final String JavaDoc ADDRESS_SYNTAX = "1.3";
153
154         /**
155          * Destination mailbox address ambiguous
156          */

157         public static final String JavaDoc ADDRESS_AMBIGUOUS = "1.4";
158
159         /**
160          * Destination Address valid
161          */

162         public static final String JavaDoc ADDRESS_VALID = "1.5";
163
164         /**
165          * Destimation mailbox has moved, no forwarding address
166          */

167         public static final String JavaDoc ADDRESS_MOVED = "1.6";
168
169         /**
170          * Bad sender's mailbox address syntax
171          */

172         public static final String JavaDoc ADDRESS_SYNTAX_SENDER = "1.7";
173
174         /**
175          * Bad sender's system address
176          */

177         public static final String JavaDoc ADDRESS_SYSTEM_SENDER = "1.8";
178
179
180         /**
181          * Mailbox Status
182          */

183         public static final int MAILBOX = 2;
184
185         /**
186          * Other or Undefined Mailbox Status
187          */

188         public static final String JavaDoc MAILBOX_OTHER = "2.0";
189
190         /**
191          * Mailbox disabled, not accepting messages
192          */

193         public static final String JavaDoc MAILBOX_DISABLED = "2.1";
194
195         /**
196          * Mailbox full
197          */

198         public static final String JavaDoc MAILBOX_FULL = "2.2";
199
200         /**
201          * Message length exceeds administrative limit
202          */

203         public static final String JavaDoc MAILBOX_MSG_TOO_BIG = "2.3";
204
205         /**
206          * Mailing list expansion problem
207          */

208         public static final String JavaDoc MAILBOX_LIST_EXPANSION = "2.4";
209
210
211         /**
212          * Mail System Status
213          */

214         public static final int SYSTEM = 3;
215
216         /**
217          * Other or undefined mail system status
218          */

219         public static final String JavaDoc SYSTEM_OTHER = "3.0";
220
221         /**
222          * Mail system full
223          */

224         public static final String JavaDoc SYSTEM_FULL = "3.1";
225
226         /**
227          * System not accepting messages
228          */

229         public static final String JavaDoc SYSTEM_NOT_ACCEPTING = "3.2";
230
231         /**
232          * System not capable of selected features
233          */

234         public static final String JavaDoc SYSTEM_NOT_CAPABLE = "3.3";
235
236         /**
237          * Message too big for system
238          */

239         public static final String JavaDoc SYSTEM_MSG_TOO_BIG = "3.4";
240
241         /**
242          * System incorrectly configured
243          */

244         public static final String JavaDoc SYSTEM_CFG_ERROR = "3.5";
245
246
247         /**
248          * Network and Routing Status
249          */

250         public static final int NETWORK = 4;
251
252         /**
253          * Other or undefined network or routing status
254          */

255         public static final String JavaDoc NETWORK_OTHER = "4.0";
256
257         /**
258          * No answer form host
259          */

260         public static final String JavaDoc NETWORK_NO_ANSWER = "4.1";
261
262         /**
263          * Bad Connection
264          */

265         public static final String JavaDoc NETWORK_CONNECTION = "4.2";
266
267         /**
268          * Directory server failure
269          */

270         public static final String JavaDoc NETWORK_DIR_SERVER = "4.3";
271
272         /**
273          * Unable to route
274          */

275         public static final String JavaDoc NETWORK_ROUTE = "4.4";
276
277         /**
278          * Mail system congestion
279          */

280         public static final String JavaDoc NETWORK_CONGESTION = "4.5";
281
282         /**
283          * Routing loop detected
284          */

285         public static final String JavaDoc NETWORK_LOOP = "4.6";
286
287         /**
288          * Delivery time expired
289          */

290         public static final String JavaDoc NETWORK_EXPIRED = "4.7";
291
292
293         /**
294          * Mail Delivery Protocol Status
295          */

296         public static final int DELIVERY = 5;
297
298         /**
299          * Other or undefined (SMTP) protocol status
300          */

301         public static final String JavaDoc DELIVERY_OTHER = "5.0";
302
303         /**
304          * Invalid command
305          */

306         public static final String JavaDoc DELIVERY_INVALID_CMD = "5.1";
307
308         /**
309          * Syntax error
310          */

311         public static final String JavaDoc DELIVERY_SYNTAX = "5.2";
312
313         /**
314          * Too many recipients
315          */

316         public static final String JavaDoc DELIVERY_TOO_MANY_REC = "5.3";
317
318         /**
319          * Invalid command arguments
320          */

321         public static final String JavaDoc DELIVERY_INVALID_ARG = "5.4";
322
323         /**
324          * Wrong protocol version
325          */

326         public static final String JavaDoc DELIVERY_VERSION = "5.5";
327
328
329         /**
330          * Message Content or Media Status
331          */

332         public static final int CONTENT = 6;
333
334         /**
335          * Other or undefined media error
336          */

337         public static final String JavaDoc CONTENT_OTHER = "6.0";
338
339         /**
340          * Media not supported
341          */

342         public static final String JavaDoc CONTENT_UNSUPPORTED = "6.1";
343
344         /**
345          * Conversion required and prohibited
346          */

347         public static final String JavaDoc CONTENT_CONVERSION_NOT_ALLOWED = "6.2";
348
349         /**
350          * Conversion required, but not supported
351          */

352         public static final String JavaDoc CONTENT_CONVERSION_NOT_SUPPORTED = "6.3";
353
354         /**
355          * Conversion with loss performed
356          */

357         public static final String JavaDoc CONTENT_CONVERSION_LOSS = "6.4";
358
359         /**
360          * Conversion failed
361          */

362         public static final String JavaDoc CONTENT_CONVERSION_FAILED = "6.5";
363
364
365         /**
366          * Security or Policy Status
367          */

368         public static final int SECURITY = 7;
369
370         /**
371          * Other or undefined security status
372          */

373         public static final String JavaDoc SECURITY_OTHER = "7.0";
374
375         /**
376          * Delivery not authorized, message refused
377          */

378         public static final String JavaDoc SECURITY_AUTH = "7.1";
379
380         /**
381          * Mailing list expansion prohibited
382          */

383         public static final String JavaDoc SECURITY_LIST_EXP = "7.2";
384
385         /**
386          * Security conversion required, but not possible
387          */

388         public static final String JavaDoc SECURITY_CONVERSION = "7.3";
389
390         /**
391          * Security features not supported
392          */

393         public static final String JavaDoc SECURITY_UNSUPPORTED = "7.4";
394
395         /**
396          * Cryptographic failure
397          */

398         public static final String JavaDoc SECURITY_CRYPT_FAIL = "7.5";
399
400         /**
401          * Cryptographic algorithm not supported
402          */

403         public static final String JavaDoc SECURITY_CRYPT_ALGO = "7.6";
404
405         /**
406          * Message integrity failure
407          */

408         public static final String JavaDoc SECURITY_INTEGRITY = "7.7";
409
410
411         // get methods
412

413         public static String JavaDoc getStatus(int type, String JavaDoc detail) {
414             return type + "." + detail;
415         }
416
417         public static String JavaDoc 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     // Used to generate new mail names
425
private static final java.util.Random JavaDoc random = new java.util.Random JavaDoc();
426
427     // regexp pattern for scaning status code from exception message
428
private static Pattern statusPattern;
429
430     private static Pattern diagPattern;
431
432     private static final String JavaDoc MACHINE_PATTERN = "[machine]";
433
434     private String JavaDoc 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     /*
438      * Static initializer.<p>
439      * Compiles patterns for processing exception messages.<p>
440      */

441     static {
442         Perl5Compiler compiler = new Perl5Compiler();
443         String JavaDoc status_pattern_string = ".*\\s*([245]\\.\\d{1,3}\\.\\d{1,3}).*\\s*";
444         String JavaDoc 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             //this should not happen as the pattern string is hardcoded.
450
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             //this should not happen as the pattern string is hardcoded.
458
System.err.println ("Malformed pattern: " + diag_pattern_string);
459         }
460     }
461
462     /**
463      * Initialize the mailet
464      */

465     public void init() throws MessagingException JavaDoc {
466         super.init();
467         if (getInitParameter("messageString") != null) {
468             messageString = getInitParameter("messageString");
469         }
470         
471         MailcapCommandMap JavaDoc mail_cap =
472             (MailcapCommandMap JavaDoc) 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     /**
479      * Service does the hard work and bounces the originalMail in the format specified by RFC3464.
480      *
481      * @param originalMail the mail to bounce
482      * @throws MessagingException if a problem arises formulating the redirected mail
483      *
484      * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail)
485      */

486     public void service(Mail originalMail) throws MessagingException JavaDoc {
487
488
489         // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
490
Mail newMail = ((MailImpl) originalMail).duplicate(newName((MailImpl) originalMail));
491         // We don't need to use the original Remote Address and Host,
492
// and doing so would likely cause a loop with spam detecting
493
// matchers.
494
try {
495             ((MailImpl)newMail).setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress());
496             ((MailImpl)newMail).setRemoteHost(java.net.InetAddress.getLocalHost().getHostName());
497         } catch (java.net.UnknownHostException JavaDoc _) {
498             ((MailImpl) newMail).setRemoteAddr("127.0.0.1");
499             ((MailImpl) newMail).setRemoteHost("localhost");
500         }
501         MailAddress returnAddress = getExistingReturnPath(originalMail);
502         Collection JavaDoc newRecipients = new HashSet JavaDoc();
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         // create the bounce message
534
MimeMessage JavaDoc newMessage =
535             new MimeMessage JavaDoc(Session.getDefaultInstance(System.getProperties(),
536                                                        null));
537
538         MimeMultipartReport multipart = new MimeMultipartReport ();
539         multipart.setReportType ("delivery-status");
540         
541         // part 1: descripive text message
542
MimeBodyPart JavaDoc part1 = createTextMsg(originalMail);
543         multipart.addBodyPart(part1);
544
545         // part 2: DSN
546
MimeBodyPart JavaDoc part2 = createDSN(originalMail);
547         multipart.addBodyPart(part2);
548
549
550         // part 3: original mail (optional)
551
if (getAttachmentType() != NONE) {
552             MimeBodyPart JavaDoc part3 = createAttachedOriginal(originalMail);
553             multipart.addBodyPart(part3);
554         }
555
556
557         // stuffing all together
558
newMessage.setContent(multipart);
559         newMessage.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
560         newMail.setMessage(newMessage);
561
562         //Set additional headers
563
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 JavaDoc()));
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         // ghosting the original mail
578
if(!getPassThrough(originalMail)) {
579             originalMail.setState(Mail.GHOST);
580         }
581     }
582
583     /**
584      * Create a MimeBodyPart with a textual description for human readers.
585      *
586      * @param originalMail
587      * @return MimeBodyPart
588      * @throws MessagingException
589      */

590     protected MimeBodyPart JavaDoc createTextMsg(Mail originalMail)
591         throws MessagingException JavaDoc {
592         MimeBodyPart JavaDoc part1 = new MimeBodyPart JavaDoc();
593         StringWriter JavaDoc sout = new StringWriter JavaDoc();
594         PrintWriter JavaDoc out = new PrintWriter JavaDoc(sout, true);
595         String JavaDoc machine = "[unknown]";
596         try {
597             InetAddress JavaDoc me = InetAddress.getLocalHost();
598             machine = me.getHostName();
599         } catch(Exception JavaDoc e){
600             machine = "[address unknown]";
601         }
602
603         StringBuffer JavaDoc bounceBuffer =
604             new StringBuffer JavaDoc(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 JavaDoc i = originalMail.getRecipients().iterator(); i.hasNext(); ) {
614             out.println(i.next());
615         }
616         MessagingException JavaDoc ex = (MessagingException JavaDoc)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     /**
627      * creates the DSN-bodypart for automated processing
628      *
629      * @param originalMail
630      * @return MimeBodyPart dsn-bodypart
631      * @throws MessagingException
632      */

633     protected MimeBodyPart JavaDoc createDSN(Mail originalMail) throws MessagingException JavaDoc {
634         MimeBodyPart JavaDoc dsn = new MimeBodyPart JavaDoc();
635         MimeMessage JavaDoc dsnMessage =
636             new MimeMessage JavaDoc(Session.getDefaultInstance(System.getProperties(), null));
637         StringWriter JavaDoc sout = new StringWriter JavaDoc();
638         PrintWriter JavaDoc out = new PrintWriter JavaDoc(sout, true);
639         String JavaDoc errorMsg = null;
640         String JavaDoc nameType = null;
641
642
643         ////////////////////////
644
// per message fields //
645
////////////////////////
646

647         //optional: envelope-id
648
// TODO: Envelope-Id
649
// The Original-Envelope-Id is NOT the same as the Message-Id from the header.
650
// The Message-Id identifies the content of the message, while the Original-Envelope-ID
651
// identifies the transaction in which the message is sent. (see RFC3461)
652
// so do NOT out.println("Original-Envelope-Id:"+originalMail.getMessage().getMessageID());
653

654
655         //required: reporting MTA
656
// this is always us, since we do not translate non-internet-mail
657
// failure reports into DSNs
658
nameType = "dns";
659         try {
660             String JavaDoc myAddress =
661                 (String JavaDoc)getMailetContext().getAttribute(Constants.HELLO_NAME);
662             /*
663             String myAddress = InetAddress.getLocalHost().getCanonicalHostName();
664             */

665             out.println("Reporting-MTA: "+nameType+"; "+myAddress);
666         } catch(Exception JavaDoc e){
667             // we should always know our address, so we shouldn't get here
668
log("WARNING: sending DSN without required Reporting-MTA Address");
669         }
670
671         //only for gateways to non-internet mail systems: dsn-gateway
672

673         //optional: received from
674
out.println("Received-From-MTA: "+nameType+"; "+originalMail.getRemoteHost());
675
676         //optional: Arrival-Date
677

678         //////////////////////////
679
// per recipient fields //
680
//////////////////////////
681

682         Iterator JavaDoc recipients = originalMail.getRecipients().iterator();
683         while (recipients.hasNext())
684             {
685                 MailAddress rec = (MailAddress)recipients.next();
686                 String JavaDoc addressType = "rfc822";
687
688                 //required: blank line
689
out.println();
690
691                 //optional: original recipient (see RFC3461)
692
//out.println("Original-Recipient: "+addressType+"; "+ ??? );
693

694                 //required: final recipient
695
out.println("Final-Recipient: "+addressType+"; "+rec.toString());
696
697                 //required: action
698
// alowed values: failed, delayed, delivered, relayed, expanded
699
// TODO: until now, we do error-bounces only
700
out.println("Action: failed");
701
702                 //required: status
703
// get Exception for getting status information
704
// TODO: it would be nice if the SMTP-handler would set a status attribute we can use here
705
MessagingException JavaDoc ex =
706                     (MessagingException JavaDoc) originalMail.getAttribute("delivery-error");
707                 out.println("Status: "+getStatus(ex));
708
709                 //optional: remote MTA
710
//to which MTA were we talking while the Error occured?
711

712                 //optional: diagnostic-code
713
String JavaDoc diagnosticType = null;
714                 // this typically is the return value received during smtp
715
// (or other transport) communication
716
// and should be stored as attribute by the smtp handler
717
// but until now we only have error-messages.
718
String JavaDoc diagnosticCode = getErrorMsg(ex);
719                 // Sometimes this is the smtp diagnostic code,
720
// but James often gives us other messages
721
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                 //optional: last attempt
732
out.println("Last-Attempt-Date: "+
733                             rfc822DateFormat.format(((MailImpl)originalMail).getLastUpdated()));
734
735                 //optional: retry until
736
//only for 'delayed' reports .. but we don't report this (at least until now)
737

738                 //optional: extension fields
739

740             }
741
742
743         // setting content
744
dsnMessage.setText(sout.toString());
745         dsnMessage.saveChanges();
746
747         
748         //dsn.setContent(sout.toString(), "text/plain");
749

750         dsn.setContent(dsnMessage, "message/delivery-status");
751         dsn.setDescription("Delivery Status Notification");
752         dsn.setFileName("status.dat");
753         return dsn;
754     }
755
756     /**
757      * Create a MimeBodyPart with the original Mail as Attachment
758      *
759      * @param originalMail
760      * @return MimeBodyPart
761      * @throws MessagingException
762      */

763     protected MimeBodyPart JavaDoc createAttachedOriginal(Mail originalMail)
764         throws MessagingException JavaDoc {
765         MimeBodyPart JavaDoc part = new MimeBodyPart JavaDoc();
766         MimeMessage JavaDoc 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     /**
779      * Guessing status code by the exception provided.
780      * This method should use the status attribute when the
781      * SMTP-handler somewhen provides it
782      *
783      * @param MessagingException
784      * @return status code
785      */

786     protected String JavaDoc getStatus(MessagingException JavaDoc me) {
787         if (me.getNextException() == null) {
788             String JavaDoc mess = me.getMessage();
789             Perl5Matcher m = new Perl5Matcher();
790             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
791             if (m.matches(mess, statusPattern)) {
792                 MatchResult res = m.getMatch();
793                 sb.append(res.group(1));
794                 return sb.toString();
795             }
796             // bad destination system adress
797
if (mess.startsWith("There are no DNS entries for the hostname"))
798                 return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.ADDRESS_SYSTEM);
799
800             // no answer from host (4.4.1) or
801
// system not accepting network messages (4.3.2), lets guess ...
802
if (mess.equals("No mail server(s) available at this time."))
803                 return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_NO_ANSWER);
804
805             // other/unknown error
806
return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.UNDEFINED_STATUS);
807         } else {
808             String JavaDoc retVal = null;
809             Exception JavaDoc ex1 = me.getNextException();
810             Perl5Matcher m = new Perl5Matcher ();
811             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
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 JavaDoc) {
817                 // other/undefined protocol status
818

819                 // if we get an smtp returncode starting with 4
820
// it is an persistent transient error, else permanent
821
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 JavaDoc) {
825                 // bad destination system address
826
return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.ADDRESS_SYSTEM);
827             } else if (ex1 instanceof ConnectException JavaDoc) {
828                 // bad connection
829
return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_CONNECTION);
830             } else if (ex1 instanceof SocketException JavaDoc) {
831                 // bad connection
832
return DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_CONNECTION);
833             } else {
834                 // other/undefined/unknown error
835
return DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.UNDEFINED_STATUS);
836             }
837         }
838     }
839
840     /**
841      * Utility method for getting the error message from the (nested) exception.
842      * @param MessagingException
843      * @return error message
844      */

845     protected String JavaDoc getErrorMsg(MessagingException JavaDoc me) {
846         if (me.getNextException() == null) {
847             return me.getMessage().trim();
848         } else {
849             Exception JavaDoc ex1 = me.getNextException();
850             return ex1.getMessage().trim();
851         }
852     }
853
854     /**
855      * Utility method for obtaining a string representation of an array of Objects.
856      */

857     private String JavaDoc arrayToString(Object JavaDoc[] array) {
858         if (array == null) {
859             return "null";
860         }
861         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(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     /**
874      * Create a unique new primary key name.
875      *
876      * @param mail the mail to use as the basis for the new mail name
877      * @return a new name
878      */

879     protected String JavaDoc newName(MailImpl mail) throws MessagingException JavaDoc {
880         String JavaDoc oldName = mail.getName();
881
882         // Checking if the original mail name is too long, perhaps because of a
883
// loop caused by a configuration error.
884
// it could cause a "null pointer exception" in AvalonMailRepository much
885
// harder to understand.
886
if (oldName.length() > 76) {
887             int count = 0;
888             int index = 0;
889             while ((index = oldName.indexOf('!', index + 1)) >= 0) {
890                 count++;
891             }
892             // It looks like a configuration loop. It's better to stop.
893
if (count > 7) {
894                 throw new MessagingException JavaDoc("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 JavaDoc nameBuffer =
903             new StringBuffer JavaDoc(64)
904             .append(oldName)
905             .append("-!")
906             .append(random.nextInt(1048576));
907         return nameBuffer.toString();
908     }
909
910
911
912     public String JavaDoc getMailetInfo() {
913         return "DSNBounce Mailet";
914     }
915     /* ******************************************************************** */
916     /* ****************** Begin of getX and setX methods ****************** */
917     /* ******************************************************************** */
918
919     /** Gets the expected init parameters. */
920     protected String JavaDoc[] getAllowedInitParameters() {
921         String JavaDoc[] allowedArray = {
922             "debug",
923             "passThrough",
924             "messageString",
925             "attachment",
926             "sender",
927             "prefix"
928         };
929         return allowedArray;
930     }
931
932     /**
933      * @return the <CODE>attachment</CODE> init parameter, or <CODE>MESSAGE</CODE> if missing
934      */

935     protected int getAttachmentType() throws MessagingException JavaDoc {
936         if(getInitParameter("attachment") == null) {
937             return MESSAGE;
938         } else {
939             return getTypeCode(getInitParameter("attachment"));
940         }
941     }
942
943
944     /**
945      * @return <CODE>SpecialAddress.REVERSE_PATH</CODE>
946      */

947     protected Collection JavaDoc getRecipients() {
948         Collection JavaDoc newRecipients = new HashSet JavaDoc();
949         newRecipients.add(SpecialAddress.REVERSE_PATH);
950         return newRecipients;
951     }
952
953     /**
954      * @return <CODE>SpecialAddress.REVERSE_PATH</CODE>
955      */

956     protected InternetAddress JavaDoc[] getTo() {
957         InternetAddress JavaDoc[] apparentlyTo = new InternetAddress JavaDoc[1];
958         apparentlyTo[0] = SpecialAddress.REVERSE_PATH.toInternetAddress();
959         return apparentlyTo;
960     }
961
962     /**
963      * @return <CODE>SpecialAddress.NULL</CODE> (the meaning of bounce)
964      */

965     protected MailAddress getReversePath(Mail originalMail) {
966         return SpecialAddress.NULL;
967     }
968
969     /* ******************************************************************** */
970     /* ******************* End of getX and setX methods ******************* */
971     /* ******************************************************************** */
972
973 }
974
Popular Tags