KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > transport > mailets > CommandListservProcessor


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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.avalon.framework.component.ComponentManager;
21 import org.apache.james.Constants;
22 import org.apache.james.services.UsersRepository;
23 import org.apache.james.services.UsersStore;
24 import org.apache.james.util.RFC2822Headers;
25 import org.apache.james.util.XMLResources;
26 import org.apache.mailet.GenericMailet;
27 import org.apache.mailet.Mail;
28 import org.apache.mailet.MailAddress;
29 import org.apache.mailet.MailetException;
30
31 import javax.mail.MessagingException JavaDoc;
32 import javax.mail.internet.MimeMessage JavaDoc;
33 import javax.mail.internet.MimeMultipart JavaDoc;
34 import javax.mail.internet.ParseException JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.util.*;
37
38
39 /**
40  * CommandListservProcessor processes messages intended for the list serv mailing list.
41  * For command handling, see {@link CommandListservManager} <br />
42  *
43  * This class is based on the existing list serv processor shipped with James.
44  * <br />
45  * <br />
46  *
47  * To configure the CommandListservProcessor place this configuratin in the root processor:
48  * <pre>
49  * &lt;mailet match="RecipientIs=announce@localhost" class="CommandListservProcessor"&gt;
50  * &lt;membersonly&gt;false&lt;/membersonly&gt;
51  * &lt;attachmentsallowed&gt;true&lt;/attachmentsallowed&gt;
52  * &lt;replytolist&gt;true&lt;/replytolist&gt;
53  * &lt;repositoryName&gt;list-announce&lt;/repositoryName&gt;
54  * &lt;subjectprefix&gt;Announce&lt;/subjectprefix&gt;
55  * &lt;autobracket&gt;true&lt;/autobracket&gt;
56  * &lt;listOwner&gt;owner@localhost&lt;/listOwner&gt;
57  * &lt;listName&gt;announce&lt;/listName&gt;
58  * &lt;/mailet&gt;
59  *
60  * </pre>
61  *
62  * @version CVS $Revision: 1.1.2.6 $ $Date: 2004/04/16 20:59:14 $
63  * @since 2.2.0
64  */

65 public class CommandListservProcessor extends GenericMailet {
66
67     /**
68      * Whether only members can post to the list specified by the config param: 'membersonly'.
69      * <br />
70      * eg: <pre>&lt;membersonly&gt;false&lt;/membersonly&gt;</pre>
71      *
72      * Defaults to false
73      */

74     protected boolean membersOnly;
75
76     /**
77      * Whether attachments can be sent to the list specified by the config param: 'attachmentsallowed'.
78      * <br />
79      * eg: <pre>&lt;attachmentsallowed&gt;true&lt;/attachmentsallowed&gt;</pre>
80      *
81      * Defaults to true
82      */

83     protected boolean attachmentsAllowed;
84
85     /**
86      * Whether the reply-to header should be set to the list address
87      * specified by the config param: 'replytolist'.
88      * <br />
89      * eg: <pre>&lt;replytolist&gt;true&lt;/replytolist&gt;</pre>
90      *
91      * Defaults to true
92      */

93     protected boolean replyToList;
94
95     /**
96      * A String to prepend to the subject of the message when it is sent to the list
97      * specified by the config param: 'subjectPrefix'.
98      * <br />
99      * eg: <pre>&lt;subjectPrefix&gt;MyList&lt;/subjectPrefix&gt;</pre>
100      *
101      * For example: MyList
102      */

103     protected String JavaDoc subjectPrefix;
104
105     /**
106      * Whether the subject prefix should be bracketed with '[' and ']'
107      * specified by the config param: 'autoBracket'.
108      * <br />
109      * eg: <pre>&lt;autoBracket&gt;true&lt;/autoBracket&gt;</pre>
110      *
111      * Defaults to true
112      */

113     protected boolean autoBracket;
114
115     /**
116      * The repository containing the users on this list
117      * specified by the config param: 'repositoryName'.
118      * <br />
119      * eg: <pre>&lt;repositoryName&gt;list-announce&lt;/repositoryName&gt;</pre>
120      */

121     protected UsersRepository usersRepository;
122
123     /**
124      * The list owner
125      * specified by the config param: 'listOwner'.
126      * <br />
127      * eg: <pre>&lt;listOwner&gt;owner@localhost&lt;/listOwner&gt;</pre>
128      */

129     protected MailAddress listOwner;
130
131     /**
132      * Name of the mailing list
133      * specified by the config param: 'listName'.
134      * <br />
135      * eg: <pre>&lt;listName&gt;announce&lt;/listName&gt;</pre>
136      *
137      */

138     protected String JavaDoc listName;
139
140     /**
141      * The list serv manager
142      */

143     protected ICommandListservManager commandListservManager;
144
145     /**
146      * Mailet that will add the footer to the message
147      */

148     protected CommandListservFooter commandListservFooter;
149
150     /**
151      * @see XMLResources
152      */

153     protected XMLResources xmlResources;
154
155     /**
156      * Initialize the mailet
157      */

158     public void init() throws MessagingException JavaDoc {
159         membersOnly = getBoolean("membersonly", false);
160         attachmentsAllowed = getBoolean("attachmentsallowed", true);
161         replyToList = getBoolean("replytolist", true);
162         subjectPrefix = getString("subjectprefix", null);
163         listName = getString("listName", null);
164         autoBracket = getBoolean("autobracket", true);
165         try {
166             listOwner = new MailAddress(getString("listOwner", null));
167             //initialize resources
168
initializeResources();
169             //init user repos
170
initUsersRepository();
171         } catch (Exception JavaDoc e) {
172             throw new MessagingException JavaDoc(e.getMessage(), e);
173         }
174     }
175
176     /**
177      * A message was sent to the list serv. Broadcast if appropriate...
178      * @param mail
179      * @throws MessagingException
180      */

181     public void service(Mail mail) throws MessagingException JavaDoc {
182         try {
183             Collection members = new ArrayList();
184             members.addAll(getMembers());
185             MailAddress listservAddr = (MailAddress) mail.getRecipients().iterator().next();
186
187             //Check for members only flag....
188
if (!checkMembers(members, mail)) {
189                 return;
190             }
191
192             //Check for no attachments
193
if (!checkAnnouncements(mail)) {
194                 return;
195             }
196
197             //check been there
198
if (!checkBeenThere(listservAddr, mail)) {
199                 return;
200             }
201
202             //addfooter
203
addFooter(mail);
204
205             //prepare the new message
206
MimeMessage JavaDoc message = prepareListMessage(mail, listservAddr);
207
208             //Set the subject if set
209
setSubject(message);
210
211             //Send the message to the list members
212
//We set the list owner as the sender for now so bounces go to him/her
213
getMailetContext().sendMail(listOwner, members, message);
214         } catch (IOException JavaDoc ioe) {
215             throw new MailetException("Error creating listserv message", ioe);
216         } finally {
217             //Kill the old message
218
mail.setState(Mail.GHOST);
219         }
220     }
221
222     /**
223      * Add the footer using {@link CommandListservFooter}
224      * @param mail
225      * @throws MessagingException
226      */

227     protected void addFooter(Mail mail) throws MessagingException JavaDoc {
228         getCommandListservFooter().service(mail);
229     }
230
231     protected void setSubject(MimeMessage JavaDoc message) throws MessagingException JavaDoc {
232         String JavaDoc prefix = subjectPrefix;
233         if (prefix != null) {
234             if (autoBracket) {
235                 StringBuffer JavaDoc prefixBuffer =
236                         new StringBuffer JavaDoc(64)
237                         .append("[")
238                         .append(prefix)
239                         .append("]");
240                 prefix = prefixBuffer.toString();
241             }
242             String JavaDoc subj = message.getSubject();
243             if (subj == null) {
244                 subj = "";
245             }
246             subj = normalizeSubject(subj, prefix);
247             AbstractRedirect.changeSubject(message, subj);
248         }
249     }
250
251     /**
252      * Create a new message with some set headers
253      * @param mail
254      * @param listservAddr
255      * @return a prepared List Message
256      * @throws MessagingException
257      */

258     protected MimeMessage JavaDoc prepareListMessage(Mail mail, MailAddress listservAddr) throws MessagingException JavaDoc {
259         //Create a copy of this message to send out
260
MimeMessage JavaDoc message = new MimeMessage JavaDoc(mail.getMessage());
261
262         //We need tao remove this header from the copy we're sending around
263
message.removeHeader(RFC2822Headers.RETURN_PATH);
264
265         //We're going to set this special header to avoid bounces
266
// getting sent back out to the list
267
message.setHeader("X-been-there", listservAddr.toString());
268
269         //If replies should go to this list, we need to set the header
270
if (replyToList) {
271             message.setHeader(RFC2822Headers.REPLY_TO, listservAddr.toString());
272         }
273
274         return message;
275     }
276
277     /**
278      * return true if this is ok, false otherwise
279      * Check if the X-been-there header is set to the listserv's name
280      * (the address). If it has, this means it's a message from this
281      * listserv that's getting bounced back, so we need to swallow it
282      *
283      * @param listservAddr
284      * @param mail
285      * @return true if this message has already bounced, false otherwse
286      * @throws MessagingException
287      */

288     protected boolean checkBeenThere(MailAddress listservAddr, Mail mail) throws MessagingException JavaDoc {
289         if (listservAddr.equals(mail.getMessage().getHeader("X-been-there"))) {
290             return false;
291         }
292         return true;
293     }
294
295     /**
296      * Returns true if this is ok to send to the list
297      * @param mail
298      * @return true if this message is ok, false otherwise
299      * @throws IOException
300      * @throws MessagingException
301      */

302     protected boolean checkAnnouncements(Mail mail) throws IOException JavaDoc, MessagingException JavaDoc {
303         if (!attachmentsAllowed && mail.getMessage().getContent() instanceof MimeMultipart JavaDoc) {
304             Properties standardProperties = getCommandListservManager().getStandardProperties();
305
306             getCommandListservManager().onError(mail,
307                     xmlResources.getString("invalid.mail.subject", standardProperties),
308                     xmlResources.getString("error.attachments", standardProperties));
309             return false;
310         }
311         return true;
312     }
313
314     /**
315      * Returns true if this user is ok to send to the list
316      *
317      * @param members
318      * @param mail
319      * @return true if this message is ok, false otherwise
320      * @throws MessagingException
321      */

322     protected boolean checkMembers(Collection members, Mail mail) throws MessagingException JavaDoc {
323         if (membersOnly && !members.contains(mail.getSender())) {
324             Properties standardProperties = getCommandListservManager().getStandardProperties();
325             getCommandListservManager().onError(mail,
326                     xmlResources.getString("invalid.mail.subject", standardProperties),
327                     xmlResources.getString("error.membersonly", standardProperties));
328
329             return false;
330         }
331         return true;
332     }
333
334     public Collection getMembers() throws ParseException JavaDoc {
335         Collection reply = new Vector();
336         for (Iterator it = usersRepository.list(); it.hasNext();) {
337             String JavaDoc member = it.next().toString();
338             try {
339                 reply.add(new MailAddress(member));
340             } catch (Exception JavaDoc e) {
341                 // Handle an invalid subscriber address by logging it and
342
// proceeding to the next member.
343
StringBuffer JavaDoc logBuffer =
344                         new StringBuffer JavaDoc(1024)
345                         .append("Invalid subscriber address: ")
346                         .append(member)
347                         .append(" caused: ")
348                         .append(e.getMessage());
349                 log(logBuffer.toString());
350             }
351         }
352         return reply;
353     }
354
355     /**
356      * Get a configuration value
357      * @param attrName
358      * @param defValue
359      * @return the value if found, defValue otherwise
360      */

361     protected boolean getBoolean(String JavaDoc attrName, boolean defValue) {
362         boolean value = defValue;
363         try {
364             value = new Boolean JavaDoc(getInitParameter(attrName)).booleanValue();
365         } catch (Exception JavaDoc e) {
366             // Ignore any exceptions, default to false
367
}
368         return value;
369     }
370
371     /**
372      * Get a configuration value
373      * @param attrName
374      * @param defValue
375      * @return the attrValue if found, defValue otherwise
376      */

377     protected String JavaDoc getString(String JavaDoc attrName, String JavaDoc defValue) {
378         String JavaDoc value = defValue;
379         try {
380             value = getInitParameter(attrName);
381         } catch (Exception JavaDoc e) {
382             // Ignore any exceptions, default to false
383
}
384         return value;
385     }
386
387     /**
388      * initialize the resources
389      * @throws Exception
390      */

391     protected void initializeResources() throws Exception JavaDoc {
392         xmlResources = getCommandListservManager().initXMLResources(new String JavaDoc[]{"List Manager"})[0];
393     }
394
395     /**
396      * Fetch the repository of users
397      */

398     protected void initUsersRepository() throws Exception JavaDoc {
399         ComponentManager compMgr = (ComponentManager) getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
400         UsersStore usersStore = (UsersStore) compMgr.lookup("org.apache.james.services.UsersStore");
401         String JavaDoc repName = getInitParameter("repositoryName");
402
403         usersRepository = usersStore.getRepository(repName);
404         if (usersRepository == null) throw new Exception JavaDoc("Invalid user repository: " + repName);
405     }
406
407     /**
408      * <p>This takes the subject string and reduces (normailzes) it.
409      * Multiple "Re:" entries are reduced to one, and capitalized. The
410      * prefix is always moved/placed at the beginning of the line, and
411      * extra blanks are reduced, so that the output is always of the
412      * form:</p>
413      * <code>
414      * &lt;prefix&gt; + &lt;one-optional-"Re:"*gt; + &lt;remaining subject&gt;
415      * </code>
416      * <p>I have done extensive testing of this routine with a standalone
417      * driver, and am leaving the commented out debug messages so that
418      * when someone decides to enhance this method, it can be yanked it
419      * from this file, embedded it with a test driver, and the comments
420      * enabled.</p>
421      */

422     static private String JavaDoc normalizeSubject(final String JavaDoc subj, final String JavaDoc prefix) {
423         // JDK IMPLEMENTATION NOTE! When we require JDK 1.4+, all
424
// occurrences of subject.toString.().indexOf(...) can be
425
// replaced by subject.indexOf(...).
426

427         StringBuffer JavaDoc subject = new StringBuffer JavaDoc(subj);
428         int prefixLength = prefix.length();
429
430         // System.err.println("In: " + subject);
431

432         // If the "prefix" is not at the beginning the subject line, remove it
433
int index = subject.toString().indexOf(prefix);
434         if (index != 0) {
435             // System.err.println("(p) index: " + index + ", subject: " + subject);
436
if (index > 0) {
437                 subject.delete(index, index + prefixLength);
438             }
439             subject.insert(0, prefix); // insert prefix at the front
440
}
441
442         // Replace Re: with RE:
443
String JavaDoc match = "Re:";
444         index = subject.toString().indexOf(match, prefixLength);
445
446         while(index > -1) {
447             // System.err.println("(a) index: " + index + ", subject: " + subject);
448
subject.replace(index, index + match.length(), "RE:");
449             index = subject.toString().indexOf(match, prefixLength);
450             // System.err.println("(b) index: " + index + ", subject: " + subject);
451
}
452
453         // Reduce them to one at the beginning
454
match ="RE:";
455         int indexRE = subject.toString().indexOf(match, prefixLength) + match.length();
456         index = subject.toString().indexOf(match, indexRE);
457         while(index > 0) {
458             // System.err.println("(c) index: " + index + ", subject: " + subject);
459
subject.delete(index, index + match.length());
460             index = subject.toString().indexOf(match, indexRE);
461             // System.err.println("(d) index: " + index + ", subject: " + subject);
462
}
463
464         // Reduce blanks
465
match = " ";
466         index = subject.toString().indexOf(match, prefixLength);
467         while(index > -1) {
468             // System.err.println("(e) index: " + index + ", subject: " + subject);
469
subject.replace(index, index + match.length(), " ");
470             index = subject.toString().indexOf(match, prefixLength);
471             // System.err.println("(f) index: " + index + ", subject: " + subject);
472
}
473
474
475         // System.err.println("Out: " + subject);
476

477         return subject.toString();
478     }
479
480     /**
481      * lazy retrieval
482      * @return ICommandListservManager
483      */

484     protected ICommandListservManager getCommandListservManager() {
485         if (commandListservManager == null) {
486             commandListservManager = (ICommandListservManager) getMailetContext().getAttribute(ICommandListservManager.ID + listName);
487             if (commandListservManager == null) {
488                 throw new IllegalStateException JavaDoc("Unable to find command list manager named: " + listName);
489             }
490         }
491
492         return commandListservManager;
493     }
494
495     /**
496      * Lazy init
497      * @throws MessagingException
498      */

499     protected CommandListservFooter getCommandListservFooter() throws MessagingException JavaDoc {
500         if (commandListservFooter == null) {
501             commandListservFooter = new CommandListservFooter(getCommandListservManager());
502             commandListservFooter.init(getMailetConfig());
503         }
504         return commandListservFooter;
505     }
506 }
507
Popular Tags