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.GenericMailet;
22 import org.apache.mailet.Mail;
23 import org.apache.mailet.MailAddress;
24 import org.apache.mailet.MailetException;
25
26 import javax.mail.MessagingException;
27 import javax.mail.internet.MimeMessage;
28 import javax.mail.internet.MimeMultipart;
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.Vector;
32
33 /***
34 * An abstract implementation of a listserv. The underlying implementation must define
35 * various settings, and can vary in their individual configuration. Supports restricting
36 * to members only, allowing attachments or not, sending replies back to the list, and an
37 * optional subject prefix.
38 */
39 public abstract class GenericListserv extends GenericMailet {
40
41 /***
42 * Returns a Collection of MailAddress objects of members to receive this email
43 */
44 public abstract Collection getMembers() throws MessagingException;
45
46 /***
47 * Returns whether this list should restrict to senders only
48 */
49 public abstract boolean isMembersOnly() throws MessagingException;
50
51 /***
52 * Returns whether this listserv allow attachments
53 */
54 public abstract boolean isAttachmentsAllowed() throws MessagingException;
55
56 /***
57 * Returns whether listserv should add reply-to header
58 */
59 public abstract boolean isReplyToList() throws MessagingException;
60
61 /***
62 * The email address that this listserv processes on. If returns null, will use the
63 * recipient of the message, which hopefully will be the correct email address assuming
64 * the matcher was properly specified.
65 */
66 public MailAddress getListservAddress() throws MessagingException {
67 return null;
68 }
69
70 /***
71 * An optional subject prefix.
72 */
73 public abstract String getSubjectPrefix() throws MessagingException;
74
75 /***
76 * Should the subject prefix be automatically surrounded by [].
77 *
78 * @return whether the subject prefix will be surrounded by []
79 *
80 * @throws MessagingException never, for this implementation
81 */
82 public boolean isPrefixAutoBracketed() throws MessagingException {
83 return true;
84 }
85
86 /***
87 * <p>This takes the subject string and reduces (normailzes) it.
88 * Multiple "Re:" entries are reduced to one, and capitalized. The
89 * prefix is always moved/placed at the beginning of the line, and
90 * extra blanks are reduced, so that the output is always of the
91 * form:</p>
92 * <code>
93 * <prefix> + <one-optional-"Re:"*gt; + <remaining subject>
94 * </code>
95 * <p>I have done extensive testing of this routine with a standalone
96 * driver, and am leaving the commented out debug messages so that
97 * when someone decides to enhance this method, it can be yanked it
98 * from this file, embedded it with a test driver, and the comments
99 * enabled.</p>
100 */
101 static private String normalizeSubject(final String subj, final String prefix) {
102
103
104
105
106 StringBuffer subject = new StringBuffer(subj);
107 int prefixLength = prefix.length();
108
109
110
111
112 int index = subject.toString().indexOf(prefix);
113 if (index != 0) {
114
115 if (index > 0) {
116 subject.delete(index, index + prefixLength);
117 }
118 subject.insert(0, prefix);
119 }
120
121
122 String match = "Re:";
123 index = subject.toString().indexOf(match, prefixLength);
124
125 while(index > -1) {
126
127 subject.replace(index, index + match.length(), "RE:");
128 index = subject.toString().indexOf(match, prefixLength);
129
130 }
131
132
133 match ="RE:";
134 int indexRE = subject.toString().indexOf(match, prefixLength) + match.length();
135 index = subject.toString().indexOf(match, indexRE);
136 while(index > 0) {
137
138 subject.delete(index, index + match.length());
139 index = subject.toString().indexOf(match, indexRE);
140
141 }
142
143
144 match = " ";
145 index = subject.toString().indexOf(match, prefixLength);
146 while(index > -1) {
147
148 subject.replace(index, index + match.length(), " ");
149 index = subject.toString().indexOf(match, prefixLength);
150
151 }
152
153
154
155
156 return subject.toString();
157 }
158
159 /***
160 * Processes the message. Assumes it is the only recipient of this forked message.
161 */
162 public final void service(Mail mail) throws MessagingException {
163 try {
164 Collection members = getMembers();
165
166
167 if (isMembersOnly() && !members.contains(mail.getSender())) {
168
169 getMailetContext().bounce(mail, "Only members of this listserv are allowed to send a message to this address.");
170 mail.setState(Mail.GHOST);
171 return;
172 }
173
174
175 if (!isAttachmentsAllowed() && mail.getMessage().getContent() instanceof MimeMultipart) {
176 getMailetContext().bounce(mail, "You cannot send attachments to this listserv.");
177 mail.setState(Mail.GHOST);
178 return;
179 }
180
181
182 MimeMessage message = new MimeMessage(mail.getMessage());
183
184 message.removeHeader(RFC2822Headers.RETURN_PATH);
185
186
187 MailAddress listservAddr = getListservAddress();
188 if (listservAddr == null) {
189
190 listservAddr = (MailAddress)mail.getRecipients().iterator().next();
191 }
192
193
194
195
196 if (listservAddr.equals(message.getHeader("X-been-there"))) {
197 mail.setState(Mail.GHOST);
198 return;
199 }
200
201
202 String prefix = getSubjectPrefix();
203 if (prefix != null) {
204 if (isPrefixAutoBracketed()) {
205 StringBuffer prefixBuffer =
206 new StringBuffer(64)
207 .append("[")
208 .append(prefix)
209 .append("] ");
210 prefix = prefixBuffer.toString();
211 }
212 String subj = message.getSubject();
213 if (subj == null) {
214 subj = "";
215 }
216 subj = normalizeSubject(subj, prefix);
217 AbstractRedirect.changeSubject(message, subj);
218 }
219
220
221 if (isReplyToList()) {
222 message.setHeader(RFC2822Headers.REPLY_TO, listservAddr.toString());
223 }
224
225
226 message.setHeader("X-been-there", listservAddr.toString());
227
228
229
230 getMailetContext().sendMail(getMailetContext().getPostmaster(), members, message);
231
232
233 mail.setState(Mail.GHOST);
234 } catch (IOException ioe) {
235 throw new MailetException("Error creating listserv message", ioe);
236 }
237 }
238 }