View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 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 org.apache.mailet.RFC2822Headers;
21  import org.apache.mailet.Mail;
22  import org.apache.mailet.MailAddress;
23  
24  import javax.mail.MessagingException;
25  import javax.mail.internet.InternetAddress;
26  import javax.mail.internet.MimeMessage;
27  import java.io.PrintWriter;
28  import java.io.StringWriter;
29  import java.util.Collection;
30  import java.util.Iterator;
31  
32  /***
33   * <P>Abstract mailet providing configurable notification services.<BR>
34   * This mailet can be subclassed to make authoring notification mailets simple.<BR>
35   * <P>Provides the following functionalities to all notification subclasses:</P>
36   * <UL>
37   * <LI>A common notification message layout.</LI>
38   * <LI>A sender of the notification message can optionally be specified.
39   * If one is not specified, the postmaster's address will be used.</LI>
40   * <LI>A notice text can be specified, and in such case will be inserted into the 
41   * notification inline text.</LI>
42   * <LI>If the notified message has an "error message" set, it will be inserted into the 
43   * notification inline text. If the <CODE>attachStackTrace</CODE> init parameter
44   * is set to true, such error message will be attached to the notification message.</LI>
45   * <LI>The notified messages are attached in their entirety (headers and
46   * content) and the resulting MIME part type is "message/rfc822".</LI>
47   * <LI>Supports by default the <CODE>passThrough</CODE> init parameter (true if missing).</LI>
48   * </UL>
49   *
50   * <P>Sample configuration common to all notification mailet subclasses:</P>
51   * <PRE><CODE>
52   * &lt;mailet match="All" class="<I>a notification mailet</I>">
53   *   &lt;sender&gt;<I>an address or postmaster or sender or unaltered, default=postmaster</I>&lt;/sender&gt;
54   *   &lt;attachError&gt;<I>true or false, default=false</I>&lt;/attachError&gt;
55   *   &lt;message&gt;<I>notice attached to the original message text (optional)</I>&lt;/message&gt;
56   *   &lt;prefix&gt;<I>optional subject prefix prepended to the original message</I>&lt;/prefix&gt;
57   *   &lt;inline&gt;<I>see {@link Redirect}, default=none</I>&lt;/inline&gt;
58   *   &lt;attachment&gt;<I>see {@link Redirect}, default=message</I>&lt;/attachment&gt;
59   *   &lt;passThrough&gt;<I>true or false, default=true</I>&lt;/passThrough&gt;
60   *   &lt;fakeDomainCheck&gt;<I>true or false, default=true</I>&lt;/fakeDomainCheck&gt;
61   *   &lt;debug&gt;<I>true or false, default=false</I>&lt;/debug&gt;
62   * &lt;/mailet&gt;
63   * </CODE></PRE>
64   * <P><I>notice</I> and <I>senderAddress</I> can be used instead of
65   * <I>message</I> and <I>sender</I>; such names are kept for backward compatibility.</P>
66   *
67   * @version CVS $Revision: 382444 $ $Date: 2006-03-02 16:56:32 +0000 (gio, 02 mar 2006) $
68   * @since 2.2.0
69   */
70  public abstract class AbstractNotify extends AbstractRedirect {
71  
72      /* ******************************************************************** */
73      /* ****************** Begin of getX and setX methods ****************** */
74      /* ******************************************************************** */
75  
76      /***
77       * @return the <CODE>passThrough</CODE> init parameter, or true if missing
78       */
79      protected boolean getPassThrough() throws MessagingException {
80          return new Boolean(getInitParameter("passThrough","true")).booleanValue();
81      }
82  
83      /***
84       * @return the <CODE>inline</CODE> init parameter, or <CODE>NONE</CODE> if missing
85       */
86      protected int getInLineType() throws MessagingException {
87          return getTypeCode(getInitParameter("inline","none"));
88      }
89  
90      /***
91       * @return the <CODE>attachment</CODE> init parameter, or <CODE>MESSAGE</CODE> if missing
92       */
93      protected int getAttachmentType() throws MessagingException {
94          return getTypeCode(getInitParameter("attachment","message"));
95      }
96  
97      /***
98       * @return the <CODE>notice</CODE> init parameter,
99       * or the <CODE>message</CODE> init parameter if missing,
100      * or a default string if both are missing
101      */
102     protected String getMessage() {
103         return getInitParameter("notice",
104                 getInitParameter("message",
105                 "We were unable to deliver the attached message because of an error in the mail server."));
106     }
107 
108     /***
109      * @return the full message to append, built from the Mail object
110      */
111     protected String getMessage(Mail originalMail) throws MessagingException {
112         MimeMessage message = originalMail.getMessage();
113         StringWriter sout = new StringWriter();
114         PrintWriter out = new PrintWriter(sout, true);
115 
116         // First add the "local" notice
117         // (either from conf or generic error message)
118         out.println(getMessage());
119         // And then the message from other mailets
120         if (originalMail.getErrorMessage() != null) {
121             out.println();
122             out.println("Error message below:");
123             out.println(originalMail.getErrorMessage());
124         }
125         out.println();
126         out.println("Message details:");
127 
128         if (message.getSubject() != null) {
129             out.println("  Subject: " + message.getSubject());
130         }
131         if (message.getSentDate() != null) {
132             out.println("  Sent date: " + message.getSentDate());
133         }
134         out.println("  MAIL FROM: " + originalMail.getSender());
135         Iterator rcptTo = originalMail.getRecipients().iterator();
136         out.println("  RCPT TO: " + rcptTo.next());
137         while (rcptTo.hasNext()) {
138             out.println("           " + rcptTo.next());
139         }
140         String[] addresses = null;
141         addresses = message.getHeader(RFC2822Headers.FROM);
142         if (addresses != null) {
143             out.print("  From: ");
144             for (int i = 0; i < addresses.length; i++) {
145                 out.print(addresses[i] + " ");
146             }
147             out.println();
148         }
149         addresses = message.getHeader(RFC2822Headers.TO);
150         if (addresses != null) {
151             out.print("  To: ");
152             for (int i = 0; i < addresses.length; i++) {
153                 out.print(addresses[i] + " ");
154             }
155             out.println();
156         }
157         addresses = message.getHeader(RFC2822Headers.CC);
158         if (addresses != null) {
159             out.print("  CC: ");
160             for (int i = 0; i < addresses.length; i++) {
161                 out.print(addresses[i] + " ");
162             }
163             out.println();
164         }
165         out.println("  Size (in bytes): " + message.getSize());
166         if (message.getLineCount() >= 0) {
167             out.println("  Number of lines: " + message.getLineCount());
168         }
169 
170         return sout.toString();
171     }
172 
173     // All subclasses of AbstractNotify are expected to establish their own recipients
174     abstract protected Collection getRecipients() throws MessagingException;
175 
176     /***
177      * @return null
178      */
179     protected InternetAddress[] getTo() throws MessagingException {
180         return null;
181     }
182 
183     /***
184      * @return <CODE>SpecialAddress.NULL</CODE>, that will remove the "ReplyTo:" header
185      */
186     protected MailAddress getReplyTo() throws MessagingException {
187         return SpecialAddress.NULL;
188     }
189 
190     /***
191      * @return {@link AbstractRedirect#getSender(Mail)}, meaning the new requested sender if any
192      */
193     protected MailAddress getReversePath(Mail originalMail) throws MessagingException {
194         return getSender(originalMail);
195     }
196 
197     /***
198      * @return the value of the <CODE>sendingAddress</CODE> init parameter,
199      * or the value of the <CODE>sender</CODE> init parameter if missing,
200      * or the postmaster address if both are missing
201      * @return the <CODE>sendingAddress</CODE> init parameter
202      * or the <CODE>sender</CODE> init parameter
203      * or the postmaster address if both are missing;
204      * possible special addresses returned are
205      * <CODE>SpecialAddress.SENDER</CODE>
206      * and <CODE>SpecialAddress.UNALTERED</CODE>
207      */
208     protected MailAddress getSender() throws MessagingException {
209         String addressString = getInitParameter("sendingAddress",getInitParameter("sender"));
210         
211         if (addressString == null) {
212             return getMailetContext().getPostmaster();
213         }
214         
215         MailAddress specialAddress = getSpecialAddress(addressString,
216                                         new String[] {"postmaster", "sender", "unaltered"});
217         if (specialAddress != null) {
218             return specialAddress;
219         }
220 
221         try {
222             return new MailAddress(addressString);
223         } catch(Exception e) {
224             throw new MessagingException("Exception thrown in getSender() parsing: " + addressString, e);
225         }
226     }
227 
228     /***
229      * @return null
230      */
231     protected String getSubject() throws MessagingException {
232         return null;
233     }
234 
235     /***
236      * @return the <CODE>prefix</CODE> init parameter or "Re:" if missing
237      */
238     protected String getSubjectPrefix() {
239         return getInitParameter("prefix","Re:");
240     }
241 
242     /***
243      * Builds the subject of <I>newMail</I> appending the subject
244      * of <I>originalMail</I> to <I>subjectPrefix</I>, but avoiding a duplicate.
245      */
246     protected void setSubjectPrefix(Mail newMail, String subjectPrefix, Mail originalMail) throws MessagingException {
247         String subject = originalMail.getMessage().getSubject();
248         if (subject == null) {
249             subject = "";
250         }
251         if (subjectPrefix==null || subject.indexOf(subjectPrefix) == 0) {
252             newMail.getMessage().setSubject(subject);
253         } else {
254             newMail.getMessage().setSubject(subjectPrefix + subject);
255         }
256     }
257 
258     /***
259      * @return true
260      */
261     protected boolean isReply() {
262         return true;
263     }
264 
265     /* ******************************************************************** */
266     /* ******************* End of getX and setX methods ******************* */
267     /* ******************************************************************** */
268 
269 }