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