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 * <mailet match="All" class="<I>a notification mailet</I>">
57 * <sender><I>an address or postmaster or sender or unaltered, default=postmaster</I></sender>
58 * <attachError><I>true or false, default=false</I></attachError>
59 * <message><I>notice attached to the original message text (optional)</I></message>
60 * <prefix><I>optional subject prefix prepended to the original message</I></prefix>
61 * <inline><I>see {@link Redirect}, default=none</I></inline>
62 * <attachment><I>see {@link Redirect}, default=message</I></attachment>
63 * <passThrough><I>true or false, default=true</I></passThrough>
64 * <fakeDomainCheck><I>true or false, default=true</I></fakeDomainCheck>
65 * <debug><I>true or false, default=false</I></debug>
66 * </mailet>
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 }