{{error}}
{{(quickSearchResults.length>10)?'10+':(quickSearchResults.length)}} {{(quickSearchResults.length==1)?'result':'results'}}
{{result.title}} {{result.timeStamp | mysql2ymd }}
I am sorry, no such article was written yet.
My too-simple-to-be-true SMTP web server
My too-simple-to-be-true SMTP web server
My minimal SMTP server in JAVA - I needed it during email functionality tests because:
  • does not require mailbox creation in order to receive an email on any email address;
  • integrated with a minimal HTML page, I can instantly query for the message once it was sent, without waiting for virus scanning, etc.;
  • the server is domain-agnostic (it does not matter on what domain I send the email, as long as it reaches my machine);
  • I have full access to the email source immediately it reached the server, so the automated software can analyse subtle details under the hood of the email representation (alternate representations, email hidden headers, etc.).
All the code is invoked in SMTPServer.
SMTPThread.java
package eu.sorescu.smtp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;

import eu.sorescu.heap.RawHeap;

public class SmtpThread extends Thread {
	private String serverDomain;
	private Socket _socket;
	private PrintWriter out;
	private BufferedReader in;

	SmtpThread(Socket socket) throws IOException {
		this._socket = socket;
		this.out = new PrintWriter(this._socket.getOutputStream());
		this.in = new BufferedReader(new InputStreamReader(
				this._socket.getInputStream()));
		serverDomain = socket.getLocalAddress().getHostName();
	}

	private static String extractArguments(String commandLine,
			String... commandNames) {
		for (int i = 0; i < commandNames.length; i++)
			if (commandLine.toUpperCase().startsWith(
					commandNames[i].toUpperCase()))
				return commandLine.substring(commandNames[i].length());
		return null;
	}

	@Override
	public void run() {
		try {
			System.out.println("SMTP received: " + new Date());
			writeLine("220 " + serverDomain
					+ " DMS SMTP MAIL Service, Version: 1.0 1410261259");
			for (;;) {
				String commandLine = in.readLine();
				if (commandLine == null)
					break;
				if (commandLine.length() == 0)
					break;
				System.out.println(commandLine);
				String commandArguments = "";
				commandArguments = extractArguments(commandLine, "HELO ",
						"EHLO ");
				if (commandArguments != null) {
					HELO_EHLO(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "MAIL FROM:");
				if (commandArguments != null) {
					MAIL_FROM(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "RCPT TO:");
				if (commandArguments != null) {
					RCPT_TO(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "DATA");
				if (commandArguments != null) {
					DATA(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "RSET");
				if (commandArguments != null) {
					RSET(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "NOOP");
				if (commandArguments != null) {
					NOOP(commandArguments);
					continue;
				}
				commandArguments = extractArguments(commandLine, "QUIT");
				if (commandArguments != null) {
					QUIT(commandArguments);
					break;
				}
				// commandArguments = extractArguments(commandLine,
				// "SEND FROM:",
				// "SOML FROM:", "SAML FROM:", "VRFY", "EXPN", "HELP");
				// if (commandArguments != null)
				NA(commandArguments);
			}
			_socket.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	private void writeLine(String string) throws IOException {
		out.println(string);
		out.flush();
	}

	public void HELO_EHLO(String domain) throws IOException {
		writeLine("250 " + serverDomain);
	}

	private ArrayList<String> to = new ArrayList<String>();

	public void MAIL_FROM(String reversePath) throws IOException {
		try {
			writeLine("250 OK");
		} catch (Throwable t) {
			t.printStackTrace();
			writeLine("451 Requested action aborted: local error in processing");
		}
	}

	public void RCPT_TO(String forwardPath) throws IOException {
		try {
			this.to.add(forwardPath);
			writeLine("250 OK");
		} catch (Throwable t) {
			t.printStackTrace();
			writeLine("451 Requested action aborted: local error in processing");
		}
	}

	public void DATA(String empty) throws IOException {
		writeLine("354 Start mail input; end with <CRLF>.<CRLF>");
		String raw = "";
		for (;;) {
			String line = in.readLine();
			if (line == null)
				break;
			if (line.equals("."))
				break;
			raw += line + "\r\n";
		}
		try {
			for (int i = 0; i < to.size(); i++) {
				String too = to.get(i);
				if (too.indexOf('<') >= 0)
					too = too.split("\\<")[1].split("\\>")[0];
				String[] toos = too.split("\\@");
				new RawHeap("smtp", toos[toos.length - 1], too).put(raw
						.getBytes());
			}
			writeLine("250 OK");
		} catch (Throwable t) {
			t.printStackTrace();
			writeLine("451 Requested action aborted: local error in processing");
		}
	}

	public void RSET(String empty) throws IOException {
		// from = null;
		to = new ArrayList<String>();
		writeLine("250 OK");
	}

	public void NA(String reversePath) throws IOException {
		writeLine("502 Command not implemented");
	}

	// public void SEND_FROM(String reversePath) throws IOException {
	// writeLine("502 Command not implemented");
	// }
	//
	// public void SOML_FROM(String reversePath) throws IOException {
	// writeLine("502 Command not implemented");
	// }
	//
	// public void SAML_FROM(String reversePath) throws IOException {
	// writeLine("502 Command not implemented");
	// }
	//
	// public void VRFY(String string) throws IOException {
	// writeLine("502 Command not implemented");
	// }
	//
	// public void EXPN(String string) throws IOException {
	// writeLine("502 Command not implemented");
	// }
	//
	// public void HELP(String string) throws IOException {
	// writeLine("502 Command not implemented");
	// }

	public void NOOP(String arguments) throws IOException {
		writeLine("250 OK");
	}

	public void QUIT(String arguments) throws IOException {
		writeLine("221 " + serverDomain
				+ " Service closing transmission channel");
	}
}
SMTPServer.java
package eu.sorescu.smtp;

import java.io.IOException;
import java.net.ServerSocket;

public class SmtpServer extends Thread {
	public static void main(String[] args) throws IOException {
		new SmtpServer(25).start();
	}

	private ServerSocket serverSocket;

	public SmtpServer() throws IOException {
		this(25);
	}

	SmtpServer(int port) throws IOException {
		serverSocket = new ServerSocket(port);
	}

	@Override
	public void run() {
		System.out.println("SMTP server up...");
		for (;;) {
			try {
				new SmtpThread(serverSocket.accept()).start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
/*
 * HELO <SP> <domain> <CRLF> MAIL <SP> FROM:<reverse-path> <CRLF> RCPT <SP>
 * TO:<forward-path> <CRLF> DATA <CRLF> RSET <CRLF> SEND <SP>
 * FROM:<reverse-path> <CRLF> SOML <SP> FROM:<reverse-path> <CRLF> SAML <SP>
 * FROM:<reverse-path> <CRLF> VRFY <SP> <string> <CRLF> EXPN <SP> <string>
 * <CRLF> HELP [<SP> <string>] <CRLF> NOOP <CRLF> QUIT <CRLF> TURN <CRLF>
 */