Sender.java
package org.flasby.net.mail;
import static org.flasby.net.mail.SmtpEvent.CONNECT;
import static org.flasby.net.mail.SmtpEvent.DISCONNECT;
import static org.flasby.net.mail.SmtpEvent.PROTOCOL_FAULT;
import static org.flasby.net.mail.SmtpEvent.RESP_220;
import static org.flasby.net.mail.SmtpEvent.RESP_250;
import static org.flasby.net.mail.SmtpEvent.RESP_250_HYPHON;
import static org.flasby.net.mail.SmtpEvent.RESP_5xx;
import static org.flasby.net.mail.SmtpEvent.SOCKET_FAULT;
import static org.flasby.net.mail.SmtpEvent.TIME_OUT;
import static org.flasby.net.mail.SmtpState.COMPLETED;
import static org.flasby.net.mail.SmtpState.SENDING_MAILFROM;
import static org.flasby.net.mail.SmtpState.START;
import static org.flasby.net.mail.SmtpState.WAITING_FOR_GREETING;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.function.BiConsumer;
import org.flasby.net.SocketConnector;
import org.flasby.util.statemachine.StateMachine;
/**
* sends mail using SMTP. Potentially another implementation could send using
* another protocol, but this would require more abstraction than I can be
* bothered with just now.
*
* Protocol is:
* <ul>
* <li>Open socket</li>
* <li>Wait for greeting from server</li>
* <li>Send EHLO with</li>
* </ul>
*
* @author steve
*
*/
public class Sender {
private String smtpHost;
private int smtpPort;
private Message msg;
private StateMachine<SmtpState, SmtpEvent> sm = new StateMachine<>(START);
interface SmtpAction extends BiConsumer<SmtpState, SmtpEvent> {
}
private SocketConnector sock;
private OutputStream writer;
// private DataInputStream reader;
private Parser parser;
public Sender(String smtpHost, int smtpPort, Message msg) {
this.smtpHost = smtpHost;
this.smtpPort = smtpPort;
this.msg = msg;
sm.addTransition(START, WAITING_FOR_GREETING, CONNECT, openSocket);
sm.addTransition(WAITING_FOR_GREETING, START, RESP_220, sendHello);
sm.addTransition(START, SENDING_MAILFROM, RESP_250, registerExtension);
sm.addTransition(START, START, RESP_250_HYPHON, registerExtension);
sm.addTransition(SENDING_MAILFROM, COMPLETED, RESP_250, sendMailFrom);
// We're done - this will return from the SM
sm.addTransition(COMPLETED, COMPLETED, DISCONNECT);
sm.addGlobalTransition(DISCONNECT, COMPLETED, closeSocket);
sm.addGlobalTransition(SOCKET_FAULT, COMPLETED, end);
sm.addGlobalTransition(TIME_OUT, COMPLETED, end);
sm.addGlobalTransition(PROTOCOL_FAULT, COMPLETED, end);
sm.addGlobalTransition(RESP_5xx, COMPLETED, end);
}
protected SmtpAction openSocket = (state, event) -> {
System.out.println("Opening " + Sender.this.smtpHost + ":" + Sender.this.smtpPort);
sock = new SocketConnector() {
@Override
protected void disconnected() {
System.out.println("SocketConnector said closed");
sm.process(DISCONNECT);
}
@Override
protected void connected(BufferedInputStream inputStream, OutputStream outputStream) throws IOException {
System.out.println("SocketConnector said: Connected");
// Sender.this.reader = dataInputStream;
Sender.this.writer = outputStream;
// Once connected then invoke the parser
// to drive the state machine
parser = new Parser(inputStream);
while (sm.getState() != COMPLETED) {
sm.process(parser.getNextEvent());
}
}
};
sock.setSenderHost(Sender.this.smtpHost);
sock.setSenderPort(Sender.this.smtpPort);
System.out.println("Starting...");
sock.start(false);
System.out.println("Start Returned...");
};
protected SmtpAction closeSocket = (state, event) -> {
System.out.println("Close Socket");
sock.stop();
System.out.println("Stopped");
};
protected SmtpAction end = (state, event) -> {
sock.stop();
};
protected SmtpAction registerExtension = (state, event) -> {
try {
System.out.println(" Extension: " + parser.consumeToEol());
} catch (IOException e) {
processSocketFault(e);
}
};
protected SmtpAction sendMailFrom = (state, event) -> {
String ehlo = "MAIL FROM: " + Sender.this.msg.getFrom() + "\n";
try {
System.out.println("Consuming...");
parser.consumeToEol();
System.out.println("Sending: " + ehlo);
writeln(ehlo);
} catch (IOException e) {
processSocketFault(e);
}
};
protected SmtpAction sendHello = (state, event) -> {
String ehlo = "EHLO " + Sender.this.msg.getFrom().substring(Sender.this.msg.getFrom().indexOf("@") + 1) + "\n";
try {
parser.consumeToEol();
System.out.println("Sending: " + ehlo);
writeln(ehlo);
} catch (IOException e) {
processSocketFault(e);
}
};
protected void writeln(String text) throws IOException {
writer.write(text.getBytes(Charset.forName("US-ASCII")));
System.out.println("Sent: " + text);
}
public void processSocketFault(Exception ex) {
sm.process(SOCKET_FAULT);
}
public SmtpState getState() {
return sm.getState();
}
public void send() {
sm.process(CONNECT);
}
}