Author: norman Date: Thu Jan 12 14:17:35 2012 New Revision: 1230560 URL: http://svn.apache.org/viewvc?rev=1230560&view=rev Log: Add support for APOP. See PROTOCOL-87
Added: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractApopCmdHandler.java Modified: james/protocols/trunk/api/src/main/java/org/apache/james/protocols/api/ProtocolConfigurationImpl.java james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3ProtocolHandlerChain.java james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3Session.java james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractPassCmdHandler.java james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/WelcomeMessageHandler.java james/protocols/trunk/pop3/src/test/java/org/apache/james/protocols/pop3/POP3ServerTest.java Modified: james/protocols/trunk/api/src/main/java/org/apache/james/protocols/api/ProtocolConfigurationImpl.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/api/src/main/java/org/apache/james/protocols/api/ProtocolConfigurationImpl.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/api/src/main/java/org/apache/james/protocols/api/ProtocolConfigurationImpl.java (original) +++ james/protocols/trunk/api/src/main/java/org/apache/james/protocols/api/ProtocolConfigurationImpl.java Thu Jan 12 14:17:35 2012 @@ -19,6 +19,9 @@ package org.apache.james.protocols.api; +import java.net.InetAddress; +import java.net.UnknownHostException; + /** * Default implementation of a {@link ProtocolConfiguration} which allows to easily set the different configurations. @@ -31,14 +34,29 @@ public class ProtocolConfigurationImpl i private String greeting; private String softwareName = "JAMES Protocols Server"; - private String helloName = "localhost"; + private String helloName = null; + private static final String DEFAULT_HELLO_NAME; + + static { + String hName; + try { + hName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hName = "localhost"; + } + DEFAULT_HELLO_NAME = hName; + } /* + * * (non-Javadoc) * * @see org.apache.james.protocols.pop3.POP3Configuration#getHelloName() */ public String getHelloName() { + if (helloName == null) { + return DEFAULT_HELLO_NAME; + } return helloName; } Modified: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3ProtocolHandlerChain.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3ProtocolHandlerChain.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3ProtocolHandlerChain.java (original) +++ james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3ProtocolHandlerChain.java Thu Jan 12 14:17:35 2012 @@ -54,24 +54,28 @@ public class POP3ProtocolHandlerChain ex } /** - * The {@link AbstractPassCmdHandler} to use. If a <code>not null</code> {@link AbstractPassCmdHandler} is given, the {@link POP3ProtocolHandlerChain} + * The {@link AbstractPassCmdHandler}'s to use. If at least one {@link AbstractPassCmdHandler} is given, the {@link POP3ProtocolHandlerChain} * will add all default handlers * - * @param passHandler + * @param authHandlers * @throws WiringException */ - public POP3ProtocolHandlerChain(AbstractPassCmdHandler passHandler) throws WiringException { - if (passHandler != null) { - addAll(initDefaultHandlers(passHandler)); + public POP3ProtocolHandlerChain(AbstractPassCmdHandler... authHandlers) throws WiringException { + if (authHandlers != null && authHandlers.length > 0) { + addAll(initDefaultHandlers(authHandlers)); wireExtensibleHandlers(); } } - protected List<ProtocolHandler> initDefaultHandlers(AbstractPassCmdHandler passHandler) { + protected List<ProtocolHandler> initDefaultHandlers(AbstractPassCmdHandler... authHandlers) { List<ProtocolHandler> handlers = new ArrayList<ProtocolHandler>(); + // add all pass handlers + for (AbstractPassCmdHandler handler: authHandlers) { + handlers.add(handler); + } + handlers.add(new CapaCmdHandler()); handlers.add(new UserCmdHandler()); - handlers.add(passHandler); handlers.add(new ListCmdHandler()); handlers.add(new UidlCmdHandler()); handlers.add(new RsetCmdHandler()); Modified: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3Session.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3Session.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3Session.java (original) +++ james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/POP3Session.java Thu Jan 12 14:17:35 2012 @@ -30,6 +30,7 @@ public interface POP3Session extends Pro final static String UID_LIST = "UID_LIST"; final static String DELETED_UID_LIST = "DELETED_UID_LIST"; + final static String APOP_TIMESTAMP = "APOP_TIMESTAMP"; // Authentication states for the POP3 interaction /** Waiting for user id */ Added: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractApopCmdHandler.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractApopCmdHandler.java?rev=1230560&view=auto ============================================================================== --- james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractApopCmdHandler.java (added) +++ james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractApopCmdHandler.java Thu Jan 12 14:17:35 2012 @@ -0,0 +1,103 @@ +/**************************************************************** + * 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.protocols.pop3.core; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.apache.james.protocols.api.ProtocolSession.State; +import org.apache.james.protocols.api.Request; +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.pop3.POP3Response; +import org.apache.james.protocols.pop3.POP3Session; +import org.apache.james.protocols.pop3.mailbox.Mailbox; + +/** + * Handles the APOP command + * + * @author Maurer + * + */ +public abstract class AbstractApopCmdHandler extends AbstractPassCmdHandler{ + + private static final Collection<String> COMMANDS = Collections.unmodifiableCollection(Arrays.asList("APOP")); + + @Override + public Response onCommand(POP3Session session, Request request) { + if (session.getAttachment(POP3Session.APOP_TIMESTAMP, State.Connection) == null) { + // APOP timestamp was not found in the session so APOP is not supported + return POP3Response.ERR; + } + + String parameters = request.getArgument(); + String parts[] = null; + boolean syntaxError = false; + if (parameters != null) { + parts = parameters.split(" "); + if (parts.length != 2) { + syntaxError = true; + } + } else { + syntaxError = true; + } + if (!syntaxError && session.getHandlerState() == POP3Session.AUTHENTICATION_READY) { + + Response response = doAuth(session, parts[0], parts[1]); + + if (POP3Response.OK_RESPONSE.equals(response.getRetCode())) { + // the auth was successful so set the user + session.setUser(parts[0]); + } + return response; + } else { + session.setHandlerState(POP3Session.AUTHENTICATION_READY); + return AUTH_FAILED; + } + + } + + @Override + public Collection<String> getImplCommands() { + return COMMANDS; + } + + + /* + * (non-Javadoc) + * @see org.apache.james.protocols.pop3.core.AbstractPassCmdHandler#auth(org.apache.james.protocols.pop3.POP3Session, java.lang.String, java.lang.String) + */ + protected final Mailbox auth(POP3Session session, String username, String password) throws Exception { + return auth(session, (String)session.getAttachment(POP3Session.APOP_TIMESTAMP, State.Connection), username, password); + } + + + /** + * Authenticate a {@link POP3Session} and returns the {@link Mailbox} for it. If it can not get authenticated it will return <code>null</code>. + * + * @param session + * @param apopTimestamp + * @param user + * @param digest + * @return mailbox + * @throws Exception + */ + protected abstract Mailbox auth(POP3Session session, String apopTimestamp, String user, String digest) throws Exception; +} Modified: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractPassCmdHandler.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractPassCmdHandler.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractPassCmdHandler.java (original) +++ james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/AbstractPassCmdHandler.java Thu Jan 12 14:17:35 2012 @@ -36,7 +36,7 @@ import org.apache.james.protocols.pop3.m public abstract class AbstractPassCmdHandler extends RsetCmdHandler { private static final Collection<String> COMMANDS = Collections.unmodifiableCollection(Arrays.asList("PASS")); private static final Response UNEXPECTED_ERROR = new POP3Response(POP3Response.ERR_RESPONSE, "Unexpected error accessing mailbox").immutable(); - private static final Response AUTH_FAILED = new POP3Response(POP3Response.ERR_RESPONSE, "Authentication failed.").immutable(); + protected static final Response AUTH_FAILED = new POP3Response(POP3Response.ERR_RESPONSE, "Authentication failed.").immutable(); /** * Handler method called upon receipt of a PASS command. Reads in and @@ -44,33 +44,45 @@ public abstract class AbstractPassCmdHan */ public Response onCommand(POP3Session session, Request request) { String parameters = request.getArgument(); - POP3Response response = null; if (session.getHandlerState() == POP3Session.AUTHENTICATION_USERSET && parameters != null) { - String passArg = parameters; - try { - Mailbox mailbox = auth(session, passArg); - if (mailbox != null) { - session.setUserMailbox(mailbox); - stat(session); - - StringBuilder responseBuffer = new StringBuilder(64).append("Welcome ").append(session.getUser()); - response = new POP3Response(POP3Response.OK_RESPONSE, responseBuffer.toString()); - session.setHandlerState(POP3Session.TRANSACTION); - } else { - session.setHandlerState(POP3Session.AUTHENTICATION_READY); - return AUTH_FAILED; - } - } catch (Exception e) { - session.getLogger().error("Unexpected error accessing mailbox for " + session.getUser(), e); - session.setHandlerState(POP3Session.AUTHENTICATION_READY); - return UNEXPECTED_ERROR; - } + return doAuth(session, session.getUser(), parameters); } else { session.setHandlerState(POP3Session.AUTHENTICATION_READY); return AUTH_FAILED; } + } + + + /** + * Authenticate a user and return the {@link Response} + * + * @param session + * @param user + * @param pass + * @return response + */ + protected final Response doAuth(POP3Session session, String user, String pass) { + try { + Mailbox mailbox = auth(session, user, pass); - return response; + if (mailbox != null) { + session.setUserMailbox(mailbox); + stat(session); + + session.setHandlerState(POP3Session.TRANSACTION); + + + StringBuilder responseBuffer = new StringBuilder(64).append("Welcome ").append(session.getUser()); + return new POP3Response(POP3Response.OK_RESPONSE, responseBuffer.toString()); + } else { + session.setHandlerState(POP3Session.AUTHENTICATION_READY); + return AUTH_FAILED; + } + } catch (Exception e) { + session.getLogger().error("Unexpected error accessing mailbox for " + session.getUser(), e); + session.setHandlerState(POP3Session.AUTHENTICATION_READY); + return UNEXPECTED_ERROR; + } } /** @@ -84,9 +96,10 @@ public abstract class AbstractPassCmdHan * Authenticate a {@link POP3Session} and returns the {@link Mailbox} for it. If it can not get authenticated it will return <code>null</code>. * * @param session + * @param user * @param password * @return mailbox * */ - protected abstract Mailbox auth(POP3Session session, String password) throws Exception; + protected abstract Mailbox auth(POP3Session session, String username, String password) throws Exception; } Modified: james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/WelcomeMessageHandler.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/WelcomeMessageHandler.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/WelcomeMessageHandler.java (original) +++ james/protocols/trunk/pop3/src/main/java/org/apache/james/protocols/pop3/core/WelcomeMessageHandler.java Thu Jan 12 14:17:35 2012 @@ -19,7 +19,7 @@ package org.apache.james.protocols.pop3.core; - +import org.apache.james.protocols.api.ProtocolSession.State; import org.apache.james.protocols.api.Response; import org.apache.james.protocols.api.handler.ConnectHandler; import org.apache.james.protocols.pop3.POP3Response; @@ -33,11 +33,19 @@ public class WelcomeMessageHandler imple */ public Response onConnect(POP3Session session) { StringBuilder responseBuffer = new StringBuilder(); - // Initially greet the connector - // Format is: Sat, 24 Jan 1998 13:16:09 -0500 - responseBuffer.append(session.getConfiguration().getHelloName()).append(" POP3 server (").append(session.getConfiguration().getSoftwareName()).append(") ready "); + + // Generate the timestamp which can be also used with APOP. See RFC1939 APOP + responseBuffer.append("<").append(session.getSessionID()).append(".").append(System.currentTimeMillis()).append("@").append(session.getConfiguration().getHelloName()).append("> "); + + // store the timestamp for later usage + session.setAttachment(POP3Session.APOP_TIMESTAMP, responseBuffer.toString(), State.Connection); + + // complete the response banner and send it back to the client + responseBuffer.append("POP3 server (").append(session.getConfiguration().getSoftwareName()).append(") ready "); POP3Response response = new POP3Response(POP3Response.OK_RESPONSE, responseBuffer.toString()); return response; } + + } Modified: james/protocols/trunk/pop3/src/test/java/org/apache/james/protocols/pop3/POP3ServerTest.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/pop3/src/test/java/org/apache/james/protocols/pop3/POP3ServerTest.java?rev=1230560&r1=1230559&r2=1230560&view=diff ============================================================================== --- james/protocols/trunk/pop3/src/test/java/org/apache/james/protocols/pop3/POP3ServerTest.java (original) +++ james/protocols/trunk/pop3/src/test/java/org/apache/james/protocols/pop3/POP3ServerTest.java Thu Jan 12 14:17:35 2012 @@ -39,8 +39,10 @@ import java.util.concurrent.atomic.Atomi import org.apache.commons.net.pop3.POP3Client; import org.apache.commons.net.pop3.POP3MessageInfo; +import org.apache.commons.net.pop3.POP3Reply; import org.apache.james.protocols.api.handler.WiringException; import org.apache.james.protocols.netty.NettyServer; +import org.apache.james.protocols.pop3.core.AbstractApopCmdHandler; import org.apache.james.protocols.pop3.core.AbstractPassCmdHandler; import org.apache.james.protocols.pop3.mailbox.Mailbox; import org.apache.james.protocols.pop3.mailbox.MessageMetaData; @@ -462,6 +464,42 @@ public class POP3ServerTest { } } + + + @Test + public void testAPop() throws Exception { + InetSocketAddress address = new InetSocketAddress("127.0.0.1", TestUtils.getFreePort()); + + NettyServer server = null; + try { + TestApopCmdHandler handler = new TestApopCmdHandler(); + server = new NettyServer(createProtocol(handler)); + server.setListenAddresses(address); + server.bind(); + + POP3Client client = new POP3Client(); + client.connect(address.getAddress().getHostAddress(), address.getPort()); + String welcomeMessage = client.getReplyString(); + + // check for valid syntax that include all info needed for APOP + assertTrue(welcomeMessage.trim().matches("\\+OK \\<\\d+\\.\\d+@.+\\> .+")); + + int reply = client.sendCommand("APOP invalid invalid"); + assertEquals(POP3Reply.ERROR, reply); + + handler.add("valid", new MockMailbox("id")); + reply = client.sendCommand("APOP valid valid"); + assertEquals(POP3Reply.OK, reply); + + assertTrue(client.logout()); + + } finally { + if (server != null) { + server.unbind(); + } + } + + } private void checkMessage(Message message, Reader reader) throws IOException { int read = 0; int i = -1; @@ -503,12 +541,26 @@ public class POP3ServerTest { public void add(String username, Mailbox mailbox) { mailboxes.put(username, mailbox); } + + protected Mailbox auth(POP3Session session, String username, String password) throws Exception{ + return mailboxes.get(username); + } - @Override - protected Mailbox auth(POP3Session session, String password) { - return mailboxes.get(session.getUser()); + + } + + private final class TestApopCmdHandler extends AbstractApopCmdHandler { + private final Map<String, Mailbox> mailboxes = new HashMap<String, Mailbox>(); + + public void add(String username, Mailbox mailbox) { + mailboxes.put(username, mailbox); } + @Override + protected Mailbox auth(POP3Session session, String apopTimestamp, String user, String digest) throws Exception { + return mailboxes.get(user); + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org