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