blob: 4f40506bbffada86d35698769f0e43b4632b4434 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.managesieve.mailet;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import javax.inject.Inject;
import javax.mail.MessagingException;
import org.apache.james.managesieve.api.SieveParser;
import org.apache.james.managesieve.core.CoreProcessor;
import org.apache.james.managesieve.mailet.transcode.MessageToCoreToMessage;
import org.apache.james.managesieve.mailet.transcode.MessageToCoreToMessage.HelpProvider;
import org.apache.james.managesieve.transcode.LineToCore;
import org.apache.james.managesieve.transcode.LineToCoreToLine;
import org.apache.james.managesieve.util.SettableSession;
import org.apache.james.sieverepository.api.SieveRepository;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.base.GenericMailet;
/**
* <code>ManageSieveMailet</code> interprets mail from a local sender as
* commands to manage Sieve scripts stored on the mail server. The commands are
* a subset of those defined by <a
* href=http://tools.ietf.org/html/rfc5804#section
* -2>http://tools.ietf.org/html/rfc5804#section-2 "MessageToCoreToMessage"</a>.
*
* <p>
* For each supported command and associated response, the format is the same as
* defined by RFC 5804, with the exception that when Sieve scripts are involved
* in the exchange they are attached to the mail with the MIME type of
* 'application/sieve' rather than being embedded in the command.
*
* <p>
* The command is written in the subject header of a mail received by this
* mailet. Responses from this mailet are sent to the sender as mail with the
* message body containing the response.
*
* <p>
* The following commands are supported:
* <ul>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.4>CAPABILITY</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.5>HAVESPACE</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.6>PUTSCRIPT</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.7>LISTSCRIPTS</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.8>SETACTIVE</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.9>GETSCRIPT</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.10>DELETESCRIPT</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.11>RENAMESCRIPT</a>
* <li><a href=http://tools.ietf.org/html/rfc5804#section-2.12>CHECKSCRIPT</a>
* </ul>
*
* <h2>An Important Note About Security</h2>
* <p>
* The mail server on which this mailet is deployed MUST robustly authenticate
* the sender, who MUST be local.
* <p>
* Sieve provides powerful email processing capabilities that if hijacked can
* expose the mail of individuals and organisations to intruders.
*/
public class ManageSieveMailet extends GenericMailet implements HelpProvider {
private class MailSession extends SettableSession {
public MailSession() {
super();
}
/**
* @param mail
* the mail to set
*/
public void setMail(Mail mail) {
setUser(getUser(mail.getSender()));
setAuthentication(null != mail.getAttribute(SMTP_AUTH_USER_ATTRIBUTE_NAME));
}
protected String getUser(MailAddress addr) {
return addr.getLocalPart() + '@' + (null == addr.getDomain() ? "localhost" : addr.getDomain());
}
}
public final static String SMTP_AUTH_USER_ATTRIBUTE_NAME = "org.apache.james.SMTPAuthUser";
private MailSession _session = null;
// Injected
private SieveRepository _sieveRepository = null;
// Injected
private SieveParser _sieveParser = null;
private MessageToCoreToMessage _transcoder = null;
private URL _helpURL = null;
private String _help = null;
private boolean _cache = true;
/**
* Creates a new instance of ManageSieveMailet.
*
*/
public ManageSieveMailet() {
super();
}
/**
* Creates a new instance of ManageSieveMailet.
*
* @param sieveRepository
* @param sieveParser
*/
public ManageSieveMailet(SieveRepository sieveRepository, SieveParser sieveParser)
{
this();
setSieveRepository(sieveRepository);
setSieveParser(sieveParser);
}
/**
* @see org.apache.mailet.base.GenericMailet#init()
*/
@Override
public void init() throws MessagingException {
super.init();
// Validate resources
if (null == _sieveParser)
{
throw new MessagingException("Missing resource \"sieveparser\"");
}
if (null == _sieveRepository)
{
throw new MessagingException("Missing resource \"sieverepository\"");
}
setHelpURL(getInitParameter("helpURL"));
_cache = getInitParameter("cache", true);
_session = new MailSession();
_transcoder = new MessageToCoreToMessage(new LineToCoreToLine(new LineToCore(new CoreProcessor(_session,
_sieveRepository, _sieveParser))), this);
}
protected void setHelpURL(String helpURL) throws MessagingException
{
try
{
_helpURL = new URL(helpURL);
}
catch (MalformedURLException ex)
{
throw new MessagingException("Invalid helpURL", ex);
}
}
/**
* @see org.apache.mailet.base.GenericMailet#service(org.apache.mailet.Mail)
*/
@Override
public void service(Mail mail) throws MessagingException {
// Sanity checks
if (null == mail.getSender()) {
getMailetContext().log("ERROR: Sender is null");
return;
}
if (!getMailetContext().isLocalServer(mail.getSender().getDomain().toLowerCase())) {
getMailetContext().log("ERROR: Sender not local");
return;
}
// Update the Session for the current mail and execute
_session.setMail(mail);
getMailetContext().sendMail(_transcoder.execute(mail.getMessage()));
mail.setState(Mail.GHOST);
// And tidy up
clearCaches();
}
protected void clearCaches()
{
if (!_cache)
{
_help = null;
}
}
public String getHelp() throws MessagingException {
if (null == _help)
{
_help = computeHelp();
}
return _help;
}
protected String computeHelp() throws MessagingException
{
InputStream stream = null;
String help = null;
try {
stream = _helpURL.openStream();
help = new Scanner(stream, "UTF-8").useDelimiter("\\A").next();
} catch (IOException ex) {
throw new MessagingException("Unable to access help URL: " + _helpURL.toExternalForm(), ex);
}
finally
{
if (null != stream)
{
try {
stream.close();
} catch (IOException ex) {
// no op
}
}
}
return help;
}
/**
*/
@Inject
public void setSieveRepository(SieveRepository repository) {
_sieveRepository = repository;
}
/**
* @param sieveParser
* the sieveParser to set
*/
@Inject
public void setSieveParser(SieveParser sieveParser) {
_sieveParser = sieveParser;
}
/**
* @see org.apache.mailet.base.GenericMailet#getMailetInfo()
*/
@Override
public String getMailetInfo() {
return getClass().getName();
}
}