View Javadoc

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