blob: 82718ce362cd77fe1d23505d6671ce6f97cae330 [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.geronimo.javamail.testserver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import javax.mail.Flags;
import javax.mail.internet.MimeMessage;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.configuration.DefaultConfigurationBuilder;
import org.apache.james.dnsservice.api.DNSService;
import org.apache.james.domainlist.api.DomainListException;
import org.apache.james.domainlist.api.mock.SimpleDomainList;
import org.apache.james.filesystem.api.mock.MockFileSystem;
import org.apache.james.imap.encode.main.DefaultImapEncoderFactory;
import org.apache.james.imap.encode.main.DefaultLocalizer;
import org.apache.james.imap.main.DefaultImapDecoderFactory;
import org.apache.james.imap.processor.main.DefaultImapProcessorFactory;
import org.apache.james.imapserver.netty.IMAPServer;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.acl.GroupMembershipResolver;
import org.apache.james.mailbox.acl.MailboxACLResolver;
import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory;
import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.Authenticator;
import org.apache.james.mailbox.store.StoreMailboxManager;
import org.apache.james.mailrepository.mock.MockMailRepositoryStore;
import org.apache.james.pop3server.netty.POP3Server;
import org.apache.james.protocols.lib.PortUtil;
import org.apache.james.protocols.lib.mock.MockProtocolHandlerLoader;
import org.apache.james.queue.api.MailQueue;
import org.apache.james.queue.api.MailQueue.MailQueueItem;
import org.apache.james.queue.api.MailQueueFactory;
import org.apache.james.queue.file.FileMailQueueFactory;
import org.apache.james.rrt.api.RecipientRewriteTable;
import org.apache.james.rrt.api.RecipientRewriteTableException;
import org.apache.james.smtpserver.netty.SMTPServer;
import org.apache.james.user.api.UsersRepositoryException;
import org.apache.james.user.lib.mock.MockUsersRepository;
import org.apache.mailet.HostAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//James based POP3 or IMAP or SMTP server (for unittesting only)
public class MailServer {
private POP3Server pop3Server;
private IMAPServer imapServer;
private SMTPServer smtpServer;
private AlterableDNSServer dnsServer;
private final MockUsersRepository usersRepository = new MockUsersRepository();
private final MockFileSystem fileSystem = new MockFileSystem();
private MockProtocolHandlerLoader protocolHandlerChain;
private StoreMailboxManager<Long> mailboxManager;
private MockMailRepositoryStore store;
private DNSService dnsService;
private MailQueueFactory queueFactory;
private MailQueue queue;
private final Semaphore sem = new Semaphore(0);
private final Logger log = LoggerFactory.getLogger("Mock");
public void ensureMsgCount(final int count) throws InterruptedException {
sem.acquire(count);
}
private class Fetcher extends Thread {
private final MailQueue queue;
private final MessageManager mailbox;
private final MailboxSession session;
Fetcher(final MailQueue queue, final MessageManager mailbox, final MailboxSession session) {
super();
this.queue = queue;
this.mailbox = mailbox;
this.session = session;
}
@Override
public void run() {
while (true) {
try {
System.out.println("Await new mail ...");
final MailQueueItem item = queue.deQueue();
System.out.println("got it");
final MimeMessage msg = item.getMail().getMessage();
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
msg.writeTo(bout);
mailbox.appendMessage(new ByteArrayInputStream(bout.toByteArray()), new Date(), session, true, new Flags());
item.done(true);
sem.release();
System.out.println("mail copied over");
} catch (final Exception e) {
e.printStackTrace();
return;
}
}
}
}
public MailServer() {
super();
try {
usersRepository.addUser("serveruser", "serverpass");
} catch (final UsersRepositoryException e) {
throw new RuntimeException(e);
}
}
public void start(final SmtpTestConfiguration smtpConfig, final Pop3TestConfiguration pop3Config, final ImapTestConfiguration imapConfig)
throws Exception {
setUpServiceManager();
imapServer = new IMAPServer();
imapServer.setImapEncoder(DefaultImapEncoderFactory.createDefaultEncoder(new DefaultLocalizer(), false));
imapServer.setImapDecoder(DefaultImapDecoderFactory.createDecoder());
pop3Server = new POP3Server();
pop3Server.setProtocolHandlerLoader(protocolHandlerChain);
smtpServer = new SMTPServer() {
@Override
protected java.lang.Class<? extends org.apache.james.protocols.lib.handler.HandlersPackage> getJMXHandlersPackage() {
return RefinedJMXHandlersLoader.class;
};
};
smtpServer.setProtocolHandlerLoader(protocolHandlerChain);
smtpServer.setDNSService(dnsServer);
imapServer.setFileSystem(fileSystem);
pop3Server.setFileSystem(fileSystem);
smtpServer.setFileSystem(fileSystem);
imapServer.setLog(log);
pop3Server.setLog(log);
smtpServer.setLog(log);
final MailboxPath mailboxPath = new MailboxPath(MailboxConstants.USER_NAMESPACE, "serveruser", "INBOX");
final MailboxSession session = mailboxManager.login("serveruser", "serverpass", LoggerFactory.getLogger("Test"));
if (!mailboxManager.mailboxExists(mailboxPath, session)) {
mailboxManager.createMailbox(mailboxPath, session);
}
imapServer.setImapProcessor(DefaultImapProcessorFactory.createXListSupportingProcessor(mailboxManager, null, null));//new StoreSubscriptionManager(new InMemoryMailboxSessionMapperFactory()), null));
//setupTestMails(session, mailboxManager.getMailbox(mailboxPath, session));
new Fetcher(queue, mailboxManager.getMailbox(mailboxPath, session), session).start();
smtpConfig.init();
pop3Config.init();
imapConfig.init();
smtpServer.configure(smtpConfig);
pop3Server.configure(pop3Config);
imapServer.configure(imapConfig);
smtpServer.init();
pop3Server.init();
imapServer.init();
}
public void stop() throws Exception {
if (protocolHandlerChain != null) {
protocolHandlerChain.dispose();
}
if (imapServer != null) {
imapServer.destroy();
}
if (pop3Server != null) {
pop3Server.destroy();
}
if (smtpServer != null) {
smtpServer.destroy();
}
}
/* protected void setupTestMailsx(MailboxSession session, MessageManager mailbox) throws MailboxException {
mailbox.appendMessage(new ByteArrayInputStream(content), new Date(), session, true, new Flags());
byte[] content2 = ("EMPTY").getBytes();
mailbox.appendMessage(new ByteArrayInputStream(content2), new Date(), session, true, new Flags());
}*/
protected void setUpServiceManager() throws Exception {
protocolHandlerChain = new MockProtocolHandlerLoader();
protocolHandlerChain.put("usersrepository", usersRepository);
final InMemoryMailboxSessionMapperFactory factory = new InMemoryMailboxSessionMapperFactory();
final MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
final GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();
mailboxManager = new StoreMailboxManager<Long>(factory, new Authenticator() {
public boolean isAuthentic(final String userid, final CharSequence passwd) {
try {
return usersRepository.test(userid, passwd.toString());
} catch (final UsersRepositoryException e) {
e.printStackTrace();
return false;
}
}
}, aclResolver, groupMembershipResolver);
mailboxManager.init();
protocolHandlerChain.put("mailboxmanager", mailboxManager);
protocolHandlerChain.put("fileSystem", fileSystem);
//smtp
dnsServer = new AlterableDNSServer();
store = new MockMailRepositoryStore();
protocolHandlerChain.put("mailStore", store);
protocolHandlerChain.put("dnsservice", dnsServer);
protocolHandlerChain.put("org.apache.james.smtpserver.protocol.DNSService", dnsService);
protocolHandlerChain.put("recipientrewritetable", new RecipientRewriteTable() {
public void addRegexMapping(final String user, final String domain, final String regex) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void removeRegexMapping(final String user, final String domain, final String regex)
throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void addAddressMapping(final String user, final String domain, final String address)
throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void removeAddressMapping(final String user, final String domain, final String address)
throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void addErrorMapping(final String user, final String domain, final String error) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void removeErrorMapping(final String user, final String domain, final String error)
throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public Collection<String> getUserDomainMappings(final String user, final String domain) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void addMapping(final String user, final String domain, final String mapping) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void removeMapping(final String user, final String domain, final String mapping) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public Map<String, Collection<String>> getAllMappings() throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void addAliasDomainMapping(final String aliasDomain, final String realDomain) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public void removeAliasDomainMapping(final String aliasDomain, final String realDomain) throws RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
public Collection<String> getMappings(final String user, final String domain) throws ErrorMappingException,
RecipientRewriteTableException {
throw new UnsupportedOperationException("Not implemented");
}
});
protocolHandlerChain.put("org.apache.james.smtpserver.protocol.DNSService", dnsService);
final FileMailQueueFactory ff = new FileMailQueueFactory();// MockMailQueueFactory();
ff.setLog(log);
ff.setFileSystem(fileSystem);
queueFactory = ff;
queue = queueFactory.getQueue(MailQueueFactory.SPOOL);
protocolHandlerChain.put("mailqueuefactory", queueFactory);
protocolHandlerChain.put("domainlist", new SimpleDomainList() {
@Override
public String getDefaultDomain() {
return "localhost";
}
@Override
public String[] getDomains() throws DomainListException {
return new String[] { "localhost" };
}
@Override
public boolean containsDomain(final String serverName) {
return "localhost".equals(serverName);
}
});
}
/**
* @return the queue
*/
public MailQueue getQueue() {
return queue;
}
public static File getAbsoluteFilePathFromClassPath(final String fileNameFromClasspath) throws FileNotFoundException {
File configFile = null;
final URL configURL = MailServer.class.getClassLoader().getResource(fileNameFromClasspath);
if (configURL != null) {
try {
configFile = new File(configURL.toURI());
} catch (URISyntaxException e) {
configFile = new File(configURL.getPath());
}
//Java 7 only
/*if(!configFile.exists()) {
try {
configFile = Paths.get(configURL.toURI()).toFile();
} catch (URISyntaxException e) {
throw new FileNotFoundException("Failed to load " + fileNameFromClasspath+ " due to "+e);
}
}*/
if (configFile.exists()) {
return configFile;
} else {
throw new FileNotFoundException("Cannot read from "+configFile.getAbsolutePath()+" (original resource was "+fileNameFromClasspath+", URL: "+configURL+"), because the file does not exist");
}
} else {
throw new FileNotFoundException("Failed to load " + fileNameFromClasspath+", because resource cannot be found within the classpath");
}
}
public static abstract class AbstractTestConfiguration extends DefaultConfigurationBuilder {
private final int listenerPort = PortUtil.getNonPrivilegedPort();
/**
* @return the listenerPort
*/
public int getListenerPort() {
return listenerPort;
}
public AbstractTestConfiguration enableSSL(final boolean enableStartTLS, final boolean enableSSL) throws FileNotFoundException {
addProperty("tls.[@startTLS]", enableStartTLS);
addProperty("tls.[@socketTLS]", enableSSL);
addProperty("tls.keystore", "file://" + getAbsoluteFilePathFromClassPath("dummykeystore.jks").getAbsolutePath());
addProperty("tls.secret", "123456");
addProperty("tls.provider", "org.bouncycastle.jce.provider.BouncyCastleProvider");
return this;
}
public void init() {
addProperty("[@enabled]", true);
addProperty("bind", "127.0.0.1:" + this.listenerPort);
addProperty("connectiontimeout", "360000");
//addProperty("jmxName", getServertype().name()+"on"+this.listenerPort);
addProperty("helloName", "jamesserver");
addProperty("helloName.[@autodetect]", false);
}
}
public static class Pop3TestConfiguration extends AbstractTestConfiguration {
@Override
public void init() {
super.init();
addProperty("helloName", "pop3 on port " + getListenerPort());
addProperty("handlerchain.[@coreHandlersPackage]", RefinedCoreCmdHandlerLoader.class.getName());
}
}
public static class ImapTestConfiguration extends AbstractTestConfiguration {
@Override
public void init() {
super.init();
addProperty("helloName", "imap on port " + getListenerPort());
}
}
public static class SmtpTestConfiguration extends AbstractTestConfiguration {
@Override
public void init() {
super.init();
addProperty("handlerchain.handler[@class]", RefinedSmtpCoreCmdHandlerLoader.class.getName());
}
public SmtpTestConfiguration setRequireAuth(final boolean requireAuth) {
addProperty("authRequired", requireAuth);
return this;
}
public SmtpTestConfiguration setHeloEhloEnforcement(final boolean heloEhloEnforcement) {
addProperty("heloEhloEnforcement", heloEhloEnforcement);
return this;
}
}
public static class DummySocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(final String host, final int port) throws IOException, UnknownHostException {
throw new IOException("dummy socket factory");
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
throw new IOException("dummy socket factory");
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort) throws IOException,
UnknownHostException {
throw new IOException("dummy socket factory");
}
@Override
public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort)
throws IOException {
throw new IOException("dummy socket factory");
}
@Override
public Socket createSocket(final Socket arg0, final String arg1, final int arg2, final boolean arg3) throws IOException {
throw new IOException("dummy socket factory");
}
@Override
public String[] getDefaultCipherSuites() {
return new String[0];
}
@Override
public String[] getSupportedCipherSuites() {
return new String[0];
}
}
private final class AlterableDNSServer implements DNSService {
private InetAddress localhostByName = null;
public Collection<String> findMXRecords(final String hostname) {
final List<String> res = new ArrayList<String>();
if (hostname == null) {
return res;
}
if ("james.apache.org".equals(hostname)) {
res.add("nagoya.apache.org");
}
return res;
}
public Iterator<HostAddress> getSMTPHostAddresses(final String domainName) {
throw new UnsupportedOperationException("Unimplemented mock service");
}
public InetAddress[] getAllByName(final String host) throws UnknownHostException {
return new InetAddress[] { getByName(host) };
}
public InetAddress getByName(final String host) throws UnknownHostException {
if (getLocalhostByName() != null) {
if ("127.0.0.1".equals(host)) {
return getLocalhostByName();
}
}
if ("0.0.0.0".equals(host)) {
return InetAddress.getByName("0.0.0.0");
}
if ("james.apache.org".equals(host)) {
return InetAddress.getByName("james.apache.org");
}
if ("abgsfe3rsf.de".equals(host)) {
throw new UnknownHostException();
}
if ("128.0.0.1".equals(host) || "192.168.0.1".equals(host) || "127.0.0.1".equals(host) || "127.0.0.0".equals(host)
|| "255.0.0.0".equals(host) || "255.255.255.255".equals(host)) {
return InetAddress.getByName(host);
}
throw new UnsupportedOperationException("getByName not implemented in mock for host: " + host);
}
public Collection<String> findTXTRecords(final String hostname) {
final List<String> res = new ArrayList<String>();
if (hostname == null) {
return res;
}
if ("2.0.0.127.bl.spamcop.net.".equals(hostname)) {
res.add("Blocked - see http://www.spamcop.net/bl.shtml?127.0.0.2");
}
return res;
}
public InetAddress getLocalhostByName() {
return localhostByName;
}
public void setLocalhostByName(final InetAddress localhostByName) {
this.localhostByName = localhostByName;
}
public String getHostName(final InetAddress addr) {
return addr.getHostName();
}
public InetAddress getLocalHost() throws UnknownHostException {
return InetAddress.getLocalHost();
}
}
}