You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by Daniel Perry <d....@netcase.co.uk> on 2005/03/14 18:25:58 UTC
added allowed posters to commandlistserver
I have now added some code to commandlistserv to allow you to limit posting
to certain addresses specified in the config block. Note if the person is
not a member you must turn off membersonly aswell or that will block them!
Example config:
<mailet match="RecipientIs=testlist@..." class="CommandListservProcessor">
<membersonly>false</membersonly>
...
<specifiedpostersonly>true</specifiedpostersonly>
<allowedposters>
<address>a@b.com</address>
<address>d@e.com</address>
</allowedposters>
</mailet>
Obviously there is the flaw that anyone can spoof any address they want...
but thats a different story :)
Daniel.
All changes are in CommandListservProcessor.java which is below:
-------CommandListservProcessor.java-------
/***********************************************************************
* Copyright (c) 2000-2004 The Apache Software Foundation. *
* All rights reserved. *
* ------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); you *
* may not use this file except in compliance with the License. You *
* may obtain a copy of the License at: *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* permissions and limitations under the License. *
***********************************************************************/
package org.apache.james.transport.mailets;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.james.Constants;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
import org.apache.james.util.RFC2822Headers;
import org.apache.james.util.XMLResources;
import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.MailetException;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.ParseException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
/**
* CommandListservProcessor processes messages intended for the list serv
mailing list.
* For command handling, see {@link CommandListservManager} <br />
*
* This class is based on the existing list serv processor shipped with
James.
* <br />
* <br />
*
* To configure the CommandListservProcessor place this configuratin in the
root processor:
* <pre>
* <mailet match="RecipientIs=announce@localhost"
class="CommandListservProcessor">
* <membersonly>false</membersonly>
* <attachmentsallowed>true</attachmentsallowed>
* <replytolist>true</replytolist>
* <repositoryName>list-announce</repositoryName>
* <subjectprefix>Announce</subjectprefix>
* <autobracket>true</autobracket>
* <listOwner>owner@localhost</listOwner>
* <listName>announce</listName>
* </mailet>
*
* </pre>
*
* @version CVS $Revision: 1.1.2.6 $ $Date: 2004/04/16 20:59:14 $
* @since 2.2.0
*/
public class CommandListservProcessor extends GenericMailet {
/**
* Whether only members can post to the list specified by the config
param: 'membersonly'.
* <br />
* eg: <pre><membersonly>false</membersonly></pre>
*
* Defaults to false
*/
protected boolean membersOnly;
/**
* Whether attachments can be sent to the list specified by the config
param: 'attachmentsallowed'.
* <br />
* eg:
<pre><attachmentsallowed>true</attachmentsallowed></pre>
*
* Defaults to true
*/
protected boolean attachmentsAllowed;
/**
* Whether the reply-to header should be set to the list address
* specified by the config param: 'replytolist'.
* <br />
* eg: <pre><replytolist>true</replytolist></pre>
*
* Defaults to true
*/
protected boolean replyToList;
/**
* A String to prepend to the subject of the message when it is sent to
the list
* specified by the config param: 'subjectPrefix'.
* <br />
* eg: <pre><subjectPrefix>MyList</subjectPrefix></pre>
*
* For example: MyList
*/
protected String subjectPrefix;
/**
* Whether the subject prefix should be bracketed with '[' and ']'
* specified by the config param: 'autoBracket'.
* <br />
* eg: <pre><autoBracket>true</autoBracket></pre>
*
* Defaults to true
*/
protected boolean autoBracket;
/**
* The repository containing the users on this list
* specified by the config param: 'repositoryName'.
* <br />
* eg:
<pre><repositoryName>list-announce</repositoryName></pre>
*/
protected UsersRepository usersRepository;
/**
* The list owner
* specified by the config param: 'listOwner'.
* <br />
* eg: <pre><listOwner>owner@localhost</listOwner></pre>
*/
protected MailAddress listOwner;
/**
* Name of the mailing list
* specified by the config param: 'listName'.
* <br />
* eg: <pre><listName>announce</listName></pre>
*
*/
protected String listName;
/**
* The list serv manager
*/
protected ICommandListservManager commandListservManager;
/**
* Mailet that will add the footer to the message
*/
protected CommandListservFooter commandListservFooter;
/**
* @see XMLResources
*/
protected XMLResources xmlResources;
protected boolean specificPostersOnly;
protected Collection allowedPosters;
/**
* Initialize the mailet
*/
public void init() throws MessagingException {
try {
Configuration configuration = (Configuration)
getField(getMailetConfig(), "configuration");
membersOnly = getBoolean("membersonly", false);
attachmentsAllowed = getBoolean("attachmentsallowed", true);
replyToList = getBoolean("replytolist", true);
subjectPrefix = getString("subjectprefix", null);
listName = getString("listName", null);
autoBracket = getBoolean("autobracket", true);
listOwner = new MailAddress(getString("listOwner", null));
specificPostersOnly=getBoolean("specifiedpostersonly", false);
//initialize resources
initializeResources();
//init user repos
initUsersRepository();
initAllowedPosters(configuration);
} catch (Exception e) {
throw new MessagingException(e.getMessage(), e);
}
}
/**
* A message was sent to the list serv. Broadcast if appropriate...
* @param mail
* @throws MessagingException
*/
public void service(Mail mail) throws MessagingException {
try {
Collection members = new ArrayList();
members.addAll(getMembers());
MailAddress listservAddr = (MailAddress)
mail.getRecipients().iterator().next();
//Check for members only flag....
if (!checkMembers(members, mail)) {
return;
}
//Check for specified posters only flag....
if (!checkAllowedPoster(mail)) {
return;
}
//Check for no attachments
if (!checkAnnouncements(mail)) {
return;
}
//check been there
if (!checkBeenThere(listservAddr, mail)) {
return;
}
//addfooter
addFooter(mail);
//prepare the new message
MimeMessage message = prepareListMessage(mail, listservAddr);
//Set the subject if set
setSubject(message);
//Send the message to the list members
//We set the list owner as the sender for now so bounces go to
him/her
getMailetContext().sendMail(listOwner, members, message);
} catch (IOException ioe) {
throw new MailetException("Error creating listserv message",
ioe);
} finally {
//Kill the old message
mail.setState(Mail.GHOST);
}
}
/**
* Add the footer using {@link CommandListservFooter}
* @param mail
* @throws MessagingException
*/
protected void addFooter(Mail mail) throws MessagingException {
getCommandListservFooter().service(mail);
}
protected void setSubject(MimeMessage message) throws MessagingException
{
String prefix = subjectPrefix;
if (prefix != null) {
if (autoBracket) {
StringBuffer prefixBuffer =
new StringBuffer(64)
.append("[")
.append(prefix)
.append("]");
prefix = prefixBuffer.toString();
}
String subj = message.getSubject();
if (subj == null) {
subj = "";
}
subj = normalizeSubject(subj, prefix);
AbstractRedirect.changeSubject(message, subj);
}
}
/**
* Create a new message with some set headers
* @param mail
* @param listservAddr
* @return a prepared List Message
* @throws MessagingException
*/
protected MimeMessage prepareListMessage(Mail mail, MailAddress
listservAddr) throws MessagingException {
//Create a copy of this message to send out
MimeMessage message = new MimeMessage(mail.getMessage());
//We need tao remove this header from the copy we're sending around
message.removeHeader(RFC2822Headers.RETURN_PATH);
//We're going to set this special header to avoid bounces
// getting sent back out to the list
message.setHeader("X-been-there", listservAddr.toString());
//If replies should go to this list, we need to set the header
if (replyToList) {
message.setHeader(RFC2822Headers.REPLY_TO,
listservAddr.toString());
}
return message;
}
/**
* return true if this is ok, false otherwise
* Check if the X-been-there header is set to the listserv's name
* (the address). If it has, this means it's a message from this
* listserv that's getting bounced back, so we need to swallow it
*
* @param listservAddr
* @param mail
* @return true if this message has already bounced, false otherwse
* @throws MessagingException
*/
protected boolean checkBeenThere(MailAddress listservAddr, Mail mail)
throws MessagingException {
if
(listservAddr.equals(mail.getMessage().getHeader("X-been-there"))) {
return false;
}
return true;
}
/**
* Returns true if this is ok to send to the list
* @param mail
* @return true if this message is ok, false otherwise
* @throws IOException
* @throws MessagingException
*/
protected boolean checkAnnouncements(Mail mail) throws IOException,
MessagingException {
if (!attachmentsAllowed && mail.getMessage().getContent() instanceof
MimeMultipart) {
Properties standardProperties =
getCommandListservManager().getStandardProperties();
getCommandListservManager().onError(mail,
xmlResources.getString("invalid.mail.subject",
standardProperties),
xmlResources.getString("error.attachments",
standardProperties));
return false;
}
return true;
}
/**
* Returns true if this user is ok to send to the list
*
* @param members
* @param mail
* @return true if this message is ok, false otherwise
* @throws MessagingException
*/
protected boolean checkMembers(Collection members, Mail mail) throws
MessagingException {
if (membersOnly && !members.contains(mail.getSender())) {
Properties standardProperties =
getCommandListservManager().getStandardProperties();
getCommandListservManager().onError(mail,
xmlResources.getString("invalid.mail.subject",
standardProperties),
xmlResources.getString("error.membersonly",
standardProperties));
return false;
}
return true;
}
public Collection getMembers() throws ParseException {
Collection reply = new Vector();
for (Iterator it = usersRepository.list(); it.hasNext();) {
String member = it.next().toString();
try {
reply.add(new MailAddress(member));
} catch (Exception e) {
// Handle an invalid subscriber address by logging it and
// proceeding to the next member.
StringBuffer logBuffer =
new StringBuffer(1024)
.append("Invalid subscriber address: ")
.append(member)
.append(" caused: ")
.append(e.getMessage());
log(logBuffer.toString());
}
}
return reply;
}
/**
* Get a configuration value
* @param attrName
* @param defValue
* @return the value if found, defValue otherwise
*/
protected boolean getBoolean(String attrName, boolean defValue) {
boolean value = defValue;
try {
value = new Boolean(getInitParameter(attrName)).booleanValue();
} catch (Exception e) {
// Ignore any exceptions, default to false
}
return value;
}
/**
* Get a configuration value
* @param attrName
* @param defValue
* @return the attrValue if found, defValue otherwise
*/
protected String getString(String attrName, String defValue) {
String value = defValue;
try {
value = getInitParameter(attrName);
} catch (Exception e) {
// Ignore any exceptions, default to false
}
return value;
}
/**
* initialize the resources
* @throws Exception
*/
protected void initializeResources() throws Exception {
xmlResources = getCommandListservManager().initXMLResources(new
String[]{"List Manager"})[0];
}
/**
* Fetch the repository of users
*/
protected void initUsersRepository() throws Exception {
ComponentManager compMgr = (ComponentManager)
getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
UsersStore usersStore = (UsersStore)
compMgr.lookup("org.apache.james.services.UsersStore");
String repName = getInitParameter("repositoryName");
usersRepository = usersStore.getRepository(repName);
if (usersRepository == null) throw new Exception("Invalid user
repository: " + repName);
}
/**
* <p>This takes the subject string and reduces (normailzes) it.
* Multiple "Re:" entries are reduced to one, and capitalized. The
* prefix is always moved/placed at the beginning of the line, and
* extra blanks are reduced, so that the output is always of the
* form:</p>
* <code>
* <prefix> + <one-optional-"Re:"*gt; + <remaining
subject>
* </code>
* <p>I have done extensive testing of this routine with a standalone
* driver, and am leaving the commented out debug messages so that
* when someone decides to enhance this method, it can be yanked it
* from this file, embedded it with a test driver, and the comments
* enabled.</p>
*/
static private String normalizeSubject(final String subj, final String
prefix) {
// JDK IMPLEMENTATION NOTE! When we require JDK 1.4+, all
// occurrences of subject.toString.().indexOf(...) can be
// replaced by subject.indexOf(...).
StringBuffer subject = new StringBuffer(subj);
int prefixLength = prefix.length();
// System.err.println("In: " + subject);
// If the "prefix" is not at the beginning the subject line, remove
it
int index = subject.toString().indexOf(prefix);
if (index != 0) {
// System.err.println("(p) index: " + index + ", subject: " +
subject);
if (index > 0) {
subject.delete(index, index + prefixLength);
}
subject.insert(0, prefix); // insert prefix at the front
}
// Replace Re: with RE:
String match = "Re:";
index = subject.toString().indexOf(match, prefixLength);
while(index > -1) {
// System.err.println("(a) index: " + index + ", subject: " +
subject);
subject.replace(index, index + match.length(), "RE:");
index = subject.toString().indexOf(match, prefixLength);
// System.err.println("(b) index: " + index + ", subject: " +
subject);
}
// Reduce them to one at the beginning
match ="RE:";
int indexRE = subject.toString().indexOf(match, prefixLength) +
match.length();
index = subject.toString().indexOf(match, indexRE);
while(index > 0) {
// System.err.println("(c) index: " + index + ", subject: " +
subject);
subject.delete(index, index + match.length());
index = subject.toString().indexOf(match, indexRE);
// System.err.println("(d) index: " + index + ", subject: " +
subject);
}
// Reduce blanks
match = " ";
index = subject.toString().indexOf(match, prefixLength);
while(index > -1) {
// System.err.println("(e) index: " + index + ", subject: " +
subject);
subject.replace(index, index + match.length(), " ");
index = subject.toString().indexOf(match, prefixLength);
// System.err.println("(f) index: " + index + ", subject: " +
subject);
}
// System.err.println("Out: " + subject);
return subject.toString();
}
/**
* lazy retrieval
* @return ICommandListservManager
*/
protected ICommandListservManager getCommandListservManager() {
if (commandListservManager == null) {
commandListservManager = (ICommandListservManager)
getMailetContext().getAttribute(ICommandListservManager.ID + listName);
if (commandListservManager == null) {
throw new IllegalStateException("Unable to find command list
manager named: " + listName);
}
}
return commandListservManager;
}
/**
* Lazy init
* @throws MessagingException
*/
protected CommandListservFooter getCommandListservFooter() throws
MessagingException {
if (commandListservFooter == null) {
commandListservFooter = new
CommandListservFooter(getCommandListservManager());
commandListservFooter.init(getMailetConfig());
}
return commandListservFooter;
}
/**
* Retrieves a data field, potentially defined by a super class.
* @return null if not found, the object otherwise
*/
protected static Object getField(Object instance, String name) throws
IllegalAccessException {
Class clazz = instance.getClass();
Field[] fields;
while (clazz != null) {
fields = clazz.getDeclaredFields();
for (int index = 0; index < fields.length; index++) {
Field field = fields[index];
if (field.getName().equals(name)) {
field.setAccessible(true);
return field.get(instance);
}
}
clazz = clazz.getSuperclass();
}
return null;
}
protected void initAllowedPosters(Configuration configuration) throws
Exception{
final Configuration allowedPostersElement =
configuration.getChild("allowedposters");
if (allowedPostersElement==null){
allowedPosters=new ArrayList();
}
else {
allowedPosters=new ArrayList();
final Configuration[] addresses =
allowedPostersElement.getChildren("address");
for (int index = 0; index < addresses.length; index++) {
Configuration address = addresses[index];
String emailAddress = address.getValue();
allowedPosters.add(new MailAddress(emailAddress));
}
}
}
/**
* Returns true if this user is ok to send to the list
*
* @param mail
* @return true if this message is ok, false otherwise
* @throws MessagingException
*/
protected boolean checkAllowedPoster(Mail mail) throws
MessagingException {
if (specificPostersOnly &&
!allowedPosters.contains(mail.getSender())) {
Properties standardProperties =
getCommandListservManager().getStandardProperties();
getCommandListservManager().onError(mail,
xmlResources.getString("invalid.mail.subject",
standardProperties),
xmlResources.getString("error.membersonly",
standardProperties));
return false;
}
return true;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org
RE: added allowed posters to commandlistserver
Posted by "Noel J. Bergman" <no...@devtech.com>.
I like the concept. I do something similar in the pipeline with matchers,
but your solution would streamline things. Would you please post a patch?
--- Noel
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org
RE: added allowed posters to commandlistserver
Posted by "Noel J. Bergman" <no...@devtech.com>.
> Note if the person is not a member you must turn off membersonly
> as well or that will block them!
That's fairly easy to change, and would be more in keeping with
"traditional" uses of allowed posters. We could allow messages from the
allowed list OR the subscriber list, or from the allowed list only.
Thoughts?
--- Noel
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org