All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.subethamail.smtp.server.ConnectionHandler Maven / Gradle / Ivy

package org.subethamail.smtp.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketAddress;

import org.apache.mina.common.BufferDataException;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.TransportType;
import org.apache.mina.filter.SSLFilter;
import org.apache.mina.filter.SSLFilter.SSLFilterMessage;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.auth.Credential;
import org.subethamail.smtp.command.AuthCommand;
import org.subethamail.smtp.command.DataEndCommand;
import org.subethamail.smtp.command.EhloCommand;
import org.subethamail.smtp.command.HelloCommand;
import org.subethamail.smtp.command.NoopCommand;
import org.subethamail.smtp.command.QuitCommand;
import org.subethamail.smtp.command.ResetCommand;

/**
 * The IoHandler that handles a connection. This class
 * passes most of it's responsibilities off to the
 * CommandHandler.
 * 
 * @author Jon Stevens
 * 
 * This file has been used and differs from the original
 * by the use of MINA NIO framework.
 * 
 * @author De Oliveira Edouard <[email protected]>
 */
public class ConnectionHandler extends IoHandlerAdapter
{
	public class Context implements ConnectionContext, MessageContext
	{
		private SMTPServer server;

		private Session sessionCtx;

		private IoSession ioSession;
		
		private Credential credential;
		
		private InputStream inputStream;

		public Context(SMTPServer server, IoSession ioSession)
		{
			this.server = server;
			this.ioSession = ioSession;
			this.sessionCtx = new Session(this.server.getMessageHandlerFactory().create(this));
		}

		public InputStream getInputStream() 
		{
			return inputStream;
		}

		public void setInputStream(InputStream inputStream) 
		{
			this.inputStream = inputStream;
		}

		public Session getSession()
		{
			return sessionCtx;
		}

		public void sendResponse(String response) throws IOException
		{
			ConnectionHandler.sendResponse(ioSession, response);
		}

		public SocketAddress getRemoteAddress()
		{
			return ioSession.getRemoteAddress();
		}

		public SMTPServer getSMTPServer()
		{
			return server;
		}

		public IoSession getIOSession()
		{
			return ioSession;
		}

		public Credential getCredential() 
		{
			return credential;
		}

		public void setCredential(Credential credential) 
		{
			this.credential = credential;
		}
	}

	// Session objects
	protected final static String CONTEXT_ATTRIBUTE = ConnectionHandler.class.getName() + ".ctx";

	private static Logger log = LoggerFactory.getLogger(ConnectionHandler.class);

	private SMTPServer server;

	private int numberOfConnections;
	
	private int receiveBufferSize = 128;

	public ConnectionHandler(SMTPServer server)
	{
		this.server = server;
	}

	/** */
	private synchronized void updateNumberOfConnections(int newValue)
	{
		numberOfConnections = newValue;
		if (log.isDebugEnabled())
			log.debug("Active connections = " + numberOfConnections);
	}

	/**
	 * @return The number of open connections
	 */
	public int getNumberOfConnections()
	{
		return numberOfConnections;
	}

	/**
	 * Sets the receive buffer size.
	 */
	public void setReceiveBufferSize(int receiveBufferSize)
	{
		this.receiveBufferSize=receiveBufferSize;
	}
	
	/** */
	public void sessionCreated(IoSession session)
	{
		updateNumberOfConnections(numberOfConnections + 1);

		if (session.getTransportType() == TransportType.SOCKET)
		{
			((SocketSessionConfig)session.getConfig()).setReceiveBufferSize(receiveBufferSize);
			((SocketSessionConfig)session.getConfig()).setSendBufferSize(64);
		}

		session.setIdleTime(IdleStatus.READER_IDLE, server.getConnectionTimeout() / 1000);

		// We're going to use SSL negotiation notification.
		session.setAttribute(SSLFilter.USE_NOTIFICATION);

		// Init protocol internals
		if (log.isDebugEnabled())
			log.debug("SMTP connection count: " + this.server.getNumberOfConnections());

		Context minaCtx = new Context(server, session);
		session.setAttribute(CONTEXT_ATTRIBUTE, minaCtx);

		try
		{
			if (this.server.hasTooManyConnections())
			{
				log.debug("SMTP Too many connections!");

				sendResponse(session, "554 Transaction failed. Too many connections.");
			}

			sendResponse(session, "220 " + this.server.getHostName() + " ESMTP " + this.server.getName());
		}
		catch (IOException e1)
		{
			try
			{
				// primarily if things fail during the MessageListener.deliver(), then try
				// to send a temporary failure back so that the server will try to resend 
				// the message later.
				sendResponse(session, "450 Problem when connecting. Please try again later.");
			}
			catch (IOException e)
			{
			}
			if (log.isDebugEnabled())
				log.debug("Error on session creation", e1);

			session.close();
		}
	}

	/**
	 * Session closed.
	 */
	public void sessionClosed(IoSession session) throws Exception
	{
		updateNumberOfConnections(numberOfConnections - 1);
	}

	/**
	 * Sends a response telling that the session is idle and closes it.
	 */
	public void sessionIdle(IoSession session, IdleStatus status)
	{
		try
		{
			sendResponse(session, "421 Timeout waiting for data from client.");
		}
		catch (IOException ioex)
		{
		}
		finally
		{
			session.close();
		}
	}

	/** */
	public void exceptionCaught(IoSession session, Throwable cause)
	{
		if (log.isDebugEnabled())
			log.debug("Exception occured :", cause);

		try
		{
			if (cause instanceof BufferDataException)
			{
				sendResponse(session, "501 " + cause.getMessage());
			}
			else
			{
				// primarily if things fail during the MessageListener.deliver(), then try
				// to send a temporary failure back so that the server will try to resend 
				// the message later.
				sendResponse(session, "450 Problem attempting to execute commands. Please try again later.");
			}
		}
		catch (IOException e)
		{
		}
		finally
		{
			session.close();
		}
	}

	/** */
	public void messageReceived(IoSession session, Object message) throws Exception
	{
		if (message == null)
		{
			if (log.isDebugEnabled())
				log.debug("no more lines from client");
			return;
		}
		
		if (message instanceof SSLFilterMessage)
		{
			if (log.isDebugEnabled())
				log.debug("SSL FILTER message -> " + message);
			return;
		}

		Context minaCtx = (Context) session.getAttribute(CONTEXT_ATTRIBUTE);

		if (message instanceof InputStream)
		{
			minaCtx.setInputStream((InputStream) message);
			new DataEndCommand().execute(null, minaCtx);
		}
		else
		{
			String line = (String) message;
			
			if (log.isDebugEnabled())
				log.debug("C: " + line);
			
            if (minaCtx.getSession().isAuthenticating())
            	this.server.getCommandHandler().handleAuthChallenge(minaCtx, line);
            else
            if (!minaCtx.getSession().isAuthenticated() 
            		&& !minaCtx.getSession().getMessageHandler().getAuthenticationMechanisms().isEmpty())
            {
            	// Per RFC 2554
            	Command cmd = this.server.getCommandHandler().getCommandFromString(line);
            	
            	if (cmd != null && (cmd instanceof AuthCommand || cmd instanceof EhloCommand || cmd instanceof HelloCommand ||
            			   cmd instanceof NoopCommand || cmd instanceof ResetCommand || cmd instanceof QuitCommand))
            		this.server.getCommandHandler().handleCommand(minaCtx, line);
            	else
            		sendResponse(session, "530 Authentication required");
            }
            else
            	this.server.getCommandHandler().handleCommand(minaCtx, line);
		}
	}

	/** */
	public static void sendResponse(IoSession session, String response) throws IOException
	{
		if (log.isDebugEnabled())
			log.debug("S: " + response);

		if (response != null)
			session.write(response);
		
		Context minaCtx = (Context) session.getAttribute(CONTEXT_ATTRIBUTE);
		if (!minaCtx.getSession().isActive())
			session.close();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy