FileDocCategorySizeDatePackage
CommandListservProcessor.javaAPI DocApache James 2.3.121394Fri Jan 12 12:56:28 GMT 2007org.apache.james.transport.mailets

CommandListservProcessor

public class CommandListservProcessor extends org.apache.mailet.GenericMailet
CommandListservProcessor processes messages intended for the list serv mailing list. For command handling, see {@link CommandListservManager}
This class is based on the existing list serv processor shipped with James.

To configure the CommandListservProcessor place this configuratin in the root processor:
<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>

version
CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $
since
2.2.0

Fields Summary
protected boolean
membersOnly
Whether only members can post to the list specified by the config param: 'membersonly'.
eg:
<membersonly>false</membersonly>
Defaults to false
protected boolean
attachmentsAllowed
Whether attachments can be sent to the list specified by the config param: 'attachmentsallowed'.
eg:
<attachmentsallowed>true</attachmentsallowed>
Defaults to true
protected boolean
replyToList
Whether the reply-to header should be set to the list address specified by the config param: 'replytolist'.
eg:
<replytolist>true</replytolist>
Defaults to true
protected String
subjectPrefix
A String to prepend to the subject of the message when it is sent to the list specified by the config param: 'subjectPrefix'.
eg:
<subjectPrefix>MyList</subjectPrefix>
For example: MyList
protected boolean
autoBracket
Whether the subject prefix should be bracketed with '[' and ']' specified by the config param: 'autoBracket'.
eg:
<autoBracket>true</autoBracket>
Defaults to true
protected org.apache.james.services.UsersRepository
usersRepository
The repository containing the users on this list specified by the config param: 'repositoryName'.
eg:
<repositoryName>list-announce</repositoryName>
protected org.apache.mailet.MailAddress
listOwner
The list owner specified by the config param: 'listOwner'.
eg:
<listOwner>owner@localhost</listOwner>
protected String
listName
Name of the mailing list specified by the config param: 'listName'.
eg:
<listName>announce</listName>
protected ICommandListservManager
commandListservManager
The list serv manager
protected CommandListservFooter
commandListservFooter
Mailet that will add the footer to the message
protected org.apache.james.util.XMLResources
xmlResources
protected boolean
specificPostersOnly
protected Collection
allowedPosters
Constructors Summary
Methods Summary
protected voidaddFooter(org.apache.mailet.Mail mail)
Add the footer using {@link CommandListservFooter}

param
mail
throws
MessagingException

        getCommandListservFooter().service(mail);
    
protected booleancheckAllowedPoster(org.apache.mailet.Mail mail, java.util.Collection members)
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

        /*
        if we don't require someone to be an allowed poster, then allow post if we don't require require them to be a subscriber, or they are one.
        if the sender is in the allowed list, post
        */
        if ((!specificPostersOnly && (!membersOnly || members.contains(mail.getSender()))) || allowedPosters.contains(mail.getSender())) {
            return true;
        } else {
            Properties standardProperties = getCommandListservManager().getStandardProperties();
            getCommandListservManager().onError(mail,
                                                xmlResources.getString("invalid.mail.subject", standardProperties),
                                                xmlResources.getString("error.membersonly", standardProperties));
            return false;
        }
    
protected booleancheckAnnouncements(org.apache.mailet.Mail mail)
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

        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;
    
protected booleancheckBeenThere(org.apache.mailet.MailAddress listservAddr, org.apache.mailet.Mail mail)
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

        if (listservAddr.equals(mail.getMessage().getHeader("X-been-there"))) {
            return false;
        }
        return true;
    
protected booleancheckMembers(java.util.Collection members, org.apache.mailet.Mail mail)
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

        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;
    
protected booleangetBoolean(java.lang.String attrName, boolean defValue)
Get a configuration value

param
attrName
param
defValue
return
the value if found, defValue otherwise

        boolean value = defValue;
        try {
            value = new Boolean(getInitParameter(attrName)).booleanValue();
        } catch (Exception e) {
            // Ignore any exceptions, default to false
        }
        return value;
    
protected CommandListservFootergetCommandListservFooter()
Lazy init

throws
MessagingException

        if (commandListservFooter == null) {
            commandListservFooter = new CommandListservFooter(getCommandListservManager());
            commandListservFooter.init(getMailetConfig());
        }
        return commandListservFooter;
    
protected ICommandListservManagergetCommandListservManager()
lazy retrieval

return
ICommandListservManager

        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;
    
protected static java.lang.ObjectgetField(java.lang.Object instance, java.lang.String name)
Retrieves a data field, potentially defined by a super class.

return
null if not found, the object otherwise

        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;
    
public java.util.CollectiongetMembers()

        Collection reply = new ArrayList();
        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;
    
protected java.lang.StringgetString(java.lang.String attrName, java.lang.String defValue)
Get a configuration value

param
attrName
param
defValue
return
the attrValue if found, defValue otherwise

        String value = defValue;
        try {
            value = getInitParameter(attrName);
        } catch (Exception e) {
            // Ignore any exceptions, default to false
        }
        return value;
    
public voidinit()
Initialize the mailet

        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);
        }
    
protected voidinitAllowedPosters(org.apache.avalon.framework.configuration.Configuration configuration)

        final Configuration allowedPostersElement = configuration.getChild("allowedposters");
        allowedPosters = new ArrayList();
        if (allowedPostersElement != null) {
            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));
            }
        }
    
protected voidinitUsersRepository()
Fetch the repository of users

        ServiceManager compMgr = (ServiceManager) getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
        UsersStore usersStore = (UsersStore) compMgr.lookup(UsersStore.ROLE);
        String repName = getInitParameter("repositoryName");

        usersRepository = usersStore.getRepository(repName);
        if (usersRepository == null) throw new Exception("Invalid user repository: " + repName);
    
protected voidinitializeResources()
initialize the resources

throws
Exception

        xmlResources = getCommandListservManager().initXMLResources(new String[]{"List Manager"})[0];
    
private static java.lang.StringnormalizeSubject(java.lang.String subj, java.lang.String prefix)

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:

<prefix> + <one-optional-"Re:"*gt; + <remaining subject>

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.

        // 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();
    
protected javax.mail.internet.MimeMessageprepareListMessage(org.apache.mailet.Mail mail, org.apache.mailet.MailAddress listservAddr)
Create a new message with some set headers

param
mail
param
listservAddr
return
a prepared List Message
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;
    
public voidservice(org.apache.mailet.Mail mail)
A message was sent to the list serv. Broadcast if appropriate...

param
mail
throws
MessagingException

        try {
            Collection members = getMembers();
            MailAddress listservAddr = (MailAddress) mail.getRecipients().iterator().next();

            // Check if allowed to post
            if (!checkAllowedPoster(mail, members)) {
                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);
        }
    
protected voidsetSubject(javax.mail.internet.MimeMessage message)

        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);
        }