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