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

org.subethamail.smtp.client.SMTPClient Maven / Gradle / Ivy

There is a newer version: 3.1.7
Show newest version
package org.subethamail.smtp.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.subethamail.smtp.io.ExtraDotOutputStream;


/**
 * A very low level abstraction of the STMP stream which knows how to handle
 * the raw protocol for lines, whitespace, etc.
 *
 * @author Jeff Schnitzer
 */
public class SMTPClient
{
	/** */
	private static Logger log = LoggerFactory.getLogger(SMTPClient.class);

	/** Just for display purposes */
	String hostPort;

	/** The raw socket */
	Socket socket;

	/** */
	BufferedReader reader;

	/** Output streams used for data */
	OutputStream rawOutput;
	ExtraDotOutputStream dataOutput;

	/** Note we bypass this during DATA */
	PrintWriter writer;

	/**
	 * Result of an SMTP exchange.
	 */
	public static class Response
	{
		int code;
		String message;

		public Response(int code, String text)
		{
			this.code = code;
			this.message = text;
		}

		public int getCode() { return this.code; }
		public String getMessage() { return this.message; }

		public boolean isSuccess()
		{
			return this.code >= 100 && this.code < 400;
		}

		@Override
		public String toString() { return this.code + " " + this.message; }
	}

	/**
	 * Establishes a connection to host and port and negotiate the initial EHLO
	 * exchange.
	 *
	 * @throws UnknownHostException if the hostname cannot be resolved
	 * @throws IOException if there is a problem connecting to the port
	 */
	public SMTPClient(String host, int port) throws UnknownHostException, IOException
	{
		this(host, port, null);
	}

	/**
	 * Establishes a connection to host and port from the specified local socket
	 * address and negotiate the initial EHLO exchange.
	 *
	 * @param bindpoint the local socket address. If null, the system will pick
	 *            up an ephemeral port and a valid local address.
	 *
	 * @throws UnknownHostException if the hostname cannot be resolved
	 * @throws IOException if there is a problem connecting to the port
	 */
	public SMTPClient(String host, int port, SocketAddress bindpoint) throws UnknownHostException, IOException
	{
		this.hostPort = host + ":" + port;

		if (log.isDebugEnabled())
			log.debug("Connecting to " + this.hostPort);

		this.socket = new Socket();
		this.socket.bind(bindpoint);
		this.socket.connect(new InetSocketAddress(host, port));
		this.reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

		this.rawOutput = this.socket.getOutputStream();
		this.dataOutput = new ExtraDotOutputStream(this.rawOutput);
		this.writer = new PrintWriter(this.rawOutput, true);
	}

	/**
	 * @return a nice pretty description of who we are connected to
	 */
	public String getHostPort()
	{
		return this.hostPort;
	}

	/**
	 * Sends a message to the server, ie "HELO foo.example.com". A newline will
	 * be appended to the message.
	 *
	 * @param msg should not have any newlines
	 */
	protected void send(String msg) throws IOException
	{
		if (log.isDebugEnabled())
			log.debug("Client: " + msg);

		// Force \r\n since println() behaves differently on different platforms
		this.writer.print(msg + "\r\n");
		this.writer.flush();
	}

	/**
	 * Note that the response text comes back without trailing newlines.
	 */
	protected Response receive() throws IOException
	{
		StringBuilder builder = new StringBuilder();
		String line = null;

		boolean done = false;
		while (!done)
		{
			line = this.reader.readLine();

			if (log.isDebugEnabled())
				log.debug("Server: " + line);

			builder.append(line.substring(4));

			if (line.charAt(3) == '-')
				builder.append('\n');
			else
				done = true;
		}

		String code = line.substring(0, 3);

		return new Response(Integer.parseInt(code), builder.toString());
	}

	/**
	 * Sends a message to the server, ie "HELO foo.example.com". A newline will
	 * be appended to the message.
	 *
	 * @param msg should not have any newlines
	 * @return the response from the server
	 */
	public Response sendReceive(String msg) throws IOException
	{
		this.send(msg);
		return this.receive();
	}

	/** If response is not success, throw an exception */
	public void receiveAndCheck() throws IOException, SMTPException
	{
		Response resp = this.receive();
		if (!resp.isSuccess())
			throw new SMTPException(resp);
	}

	/** If response is not success, throw an exception */
	public void sendAndCheck(String msg) throws IOException, SMTPException
	{
		this.send(msg);
		this.receiveAndCheck();
	}

	/** Logs but otherwise ignores errors */
	public void close()
	{
		if (!this.socket.isClosed())
		{
			try
			{
				this.socket.close();

				if (log.isDebugEnabled())
					log.debug("Closed connection to " + this.hostPort);
			}
			catch (IOException ex)
			{
				log.error("Problem closing connection to " + this.hostPort, ex);
			}
		}
	}

	/** */
	@Override
	public String toString()
	{
		return this.getClass().getSimpleName() + " { " + this.hostPort + "}";
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy