SMTP Server Help! need to write commands for Helo, Mail, RCPT, Data and Quit
I have 3 classes: FSM, ServerThread and smtpd. The code is posted below. My question is how do I Implement commands HELO, MAIL, RCPT, DATA, QUIT in such a way so SMTP server will be capable of treating sequence of incoming SMTP commands to compile a list of messages. I then need each message to be presented as a member of some collection for later processing. The collection of messages can be associated with particular SMTP session (timestamp and/or IP:port of the client can be used for identification purposes) so collections of messages from different sessions will not affect each other.
Optionally I need to implement RSET command, which resets the ongoing session to the state where MAIL command is expected.
Upon completion of an SMTP session, corresponding queue (collection) of messages must be dumped into a log file (plaintext format) for debuging purposes. Each message must be represented by all required components (sender, recipient(s), body). Messages must be separated by a single empty line. Incomplete messages must not be included in the log.
Any ideas, suggestions or code would be greatly appreciated.
First class
import java.util.HashMap;
enum smtpdStates {
wfHELO,
wfMAIL,
wfRCPT,
wfDATA,
wfPeriod,
wfRSET;
};
class mapData {
private smtpdStates state;
private String text;
public mapData(smtpdStates state, String text) {
super();
this.state = state;
this.text = text;
}
public static mapData of(smtpdStates state, String text) {
return new mapData(state,text);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((state == null) ? 0 : state.hashCode());
result = prime * result + ((text == null) ? 0 : text.toLowerCase().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
mapData other = (mapData) obj;
if (state != other.state)
return false;
if (text == null) {
if (other.text != null)
return false;
} else if (!text.equalsIgnoreCase(other.text))
return false;
return true;
}
public String toString() {
return this.text;
}
public smtpdStates getState() {
return state;
}
public String getText() {
return text;
}
}
public class FSM {
public static void main(String[] args) {
HashMap<mapData,mapData> myMap = new HashMap<mapData,mapData>();
myMap.put(new mapData(smtpdStates.wfHELO,"helo"),new mapData(smtpdStates.wfMAIL,"250 OK"));
myMap.put(mapData.of(smtpdStates.wfHELO,"help"),ma pData.of(smtpdStates.wfHELO,"Help message"));
myMap.put(mapData.of(smtpdStates.wfMAIL,"mail"),ma pData.of(smtpdStates.wfRCPT,"250 OK RCPT received"));
myMap.put(mapData.of(smtpdStates.wfRSET,"mail"),ma pData.of(smtpdStates.wfRCPT,"Seesion has been reset"));
System.out.println(myMap.get(mapData.of(smtpdState s.wfMAIL, "MaIl")));
}
}
SECOND CLASS
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
public class ServerThread extends Thread {
private BufferedReader br;
private PrintWriter pw;;
private Socket client;
private HashMap FSM;
private boolean TerminationFlag;
public boolean isTerminationFlag() {
return TerminationFlag;
}
private String help() {
return "This is a help message";
}
public ServerThread(Socket client, HashMap FSM) {
this.client = client;
this.FSM = FSM;
this.TerminationFlag = false;
}
public void run() {
String temp = null;
smtpdStates s = smtpdStates.wfHELO;
try {
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
pw = new PrintWriter(client.getOutputStream(),true);
}
catch (IOException e) {
System.err.println("I/O stream cannot be extracted");
//-- to add additional processing
}
while(true) {
try {
temp = br.readLine();
}
catch (IOException e) {
System.err.println("Readline() failed");
//-- to add additional processing
}
if(temp.equalsIgnoreCase("help")) {
pw.println(help());
continue;
}
else if(temp.equalsIgnoreCase("quit")) {
try {
pw.println("250 OK. Bye-bye");
br.close();
pw.close();
client.close();
}
catch (IOException e) {
System.err.println("Clean up procedure failed");
//-- to add additional processing
}
this.TerminationFlag = true;
break;
}
String cmd = temp.split(" ",2)[0];
System.out.println("Was in state: " + s + "'");
System.out.println("Received: '" + cmd + "'");
mapData fsmResponse = (mapData)FSM.get(mapData.of(s,cmd));
if(fsmResponse == null) {
pw.println("Command cannot be recognized");
continue;
}
s = fsmResponse.getState();
System.out.println("New state: '" + s + "'");
System.out.println("Response: '" + fsmResponse.getText() + "'\n");
pw.println(fsmResponse.getText());
}
}
}
THIRD CLASS
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
public class smtpd {
public static void main(String[] args) {
int port = 0;
ArrayList<ServerThread> listOfThreads = new ArrayList<ServerThread>();
HashMap<mapData,mapData> myMap = new HashMap<mapData,mapData>();
myMap.put(new mapData(smtpdStates.wfHELO,"helo"),new mapData(smtpdStates.wfMAIL,"250 OK"));
myMap.put(new mapData(smtpdStates.wfHELO,"mail"),new mapData(smtpdStates.wfMAIL,"777 Received MAIL, but expected HELO"));
myMap.put(new mapData(smtpdStates.wfHELO,"rcpt"),new mapData(smtpdStates.wfMAIL,"777 Received RCPT, but expected HELO"));
myMap.put(new mapData(smtpdStates.wfHELO,"data"),new mapData(smtpdStates.wfMAIL,"777 Received DATA, but expected HELO"));
myMap.put(mapData.of(smtpdStates.wfMAIL,"helo"),ma pData.of(smtpdStates.wfRCPT,"250 OK RCPT received"));
myMap.put(mapData.of(smtpdStates.wfMAIL,"mail"),ma pData.of(smtpdStates.wfRCPT,"250 OK RCPT received"));
myMap.put(new mapData(smtpdStates.wfMAIL,"rcpt"),new mapData(smtpdStates.wfRCPT,"777 Received RCPT, but expected HELO"));
myMap.put(new mapData(smtpdStates.wfMAIL,"data"),new mapData(smtpdStates.wfRCPT,"777 Received DATA, but expected HELO"));
myMap.put(mapData.of(smtpdStates.wfRCPT,"helo"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfRCPT,"mail"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfRCPT,"rcpt"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfRCPT,"data"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfDATA,"helo"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfDATA,"mail"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfDATA,"rcpt"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfDATA,"data"),ma pData.of(smtpdStates.wfRCPT,"888 Waiting for RCPT"));
myMap.put(mapData.of(smtpdStates.wfRSET,"mail"),ma pData.of(smtpdStates.wfRCPT,"Seesion has been reset"));
myMap.put(mapData.of(smtpdStates.wfRSET,"helo"),ma pData.of(smtpdStates.wfRCPT,"Seesion has been reset"));
myMap.put(mapData.of(smtpdStates.wfRSET,"rcpt"),ma pData.of(smtpdStates.wfRCPT,"Seesion has been reset"));
myMap.put(mapData.of(smtpdStates.wfRSET,"data"),ma pData.of(smtpdStates.wfRCPT,"Seesion has been reset"));
if(args.length < 1) {
System.err.println("Need at least on command line argument");
System.exit(1);
}
try {
port = Integer.parseInt(args[0]);
}
catch(NumberFormatException ex) {
System.err.println("First command-line argument must be an integer");
System.exit(2);
}
ServerSocket server;
Socket client;
while(true) {
try {
server = new ServerSocket(port);
break;
}
catch (IOException e) {
port++;
}
}
System.out.println("Port number is " + port);
try {
server.setSoTimeout(5000);
}
catch (SocketException e) {
System.err.println("ServerSocket timeout setting failed: " + e);
}
while(true) {
try {
client = server.accept();
//-- create new thread to serve new connection request
System.out.println("New client from '" + client.getRemoteSocketAddress() + "' requested connection");
ServerThread tempThread = new ServerThread(client,myMap);
listOfThreads.add(tempThread);
tempThread.start();
}
catch (java.net.SocketTimeoutException e1) {
/*
for(ServerThread T : listOfThreads) {
//-- Try this approach to implement iteration, it should throw an exception
}
*/
for(int i=listOfThreads.size()-1; i>= 0; i--) {
ServerThread termThread = listOfThreads.get(i);
if(termThread.isTerminationFlag() == true) {
try {
termThread.join();
}
catch (InterruptedException e2) {
System.err.println("Joining of a thread failed: " + e2);
}
listOfThreads.remove(termThread);
System.out.println("Thread had been successfully removed.");
}
}
}
catch (IOException e3) {
System.err.println("Accept() method returned error: " + e3);
//-- to add additional processing
}
}
}
}
Re: SMTP Server Help! need to write commands for Helo, Mail, RCPT, Data and Quit
Please edit your post and wrap your code with code tags:
[code=java]
YOUR CODE HERE
[/code]
to get highlighting and preserve formatting.
Is this the same topic: http://www.javaprogrammingforums.com...rver-java.html
Re: SMTP Server Help! need to write commands for Helo, Mail, RCPT, Data and Quit
Yes, but I am new to the forum and wasn't sure if I posted correctly
--- Update ---
I wasn't sure the correct "syntax" for posting code in the forum. Can you explain what you are referring too?
Thanks
scapres