1   /*
2    * Dumbster: a dummy SMTP server.
3    * Copyright (C) 2003, Jason Paul Kitchen
4    * lilnottsman@yahoo.com
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19   */
20  package com.dumbster.smtp;
21  
22  /***
23   * Contains an SMTP client request.
24   */
25  public class SmtpRequest {
26  
27  	/*** SMTP action received from client. */
28  	private SmtpActionType action;
29  
30  	/*** Current state of the SMTP state table. */
31  	private SmtpState state;
32  
33  	/*** Additional information passed from the client with the SMTP action. */
34  	private String params;
35  
36  	/***
37  	 * Create a new SMTP client request.
38  	 * @param actionType type of action/command
39  	 * @param params remainder of command line once command is removed
40  	 * @param state current SMTP server state
41  	 */
42  	public SmtpRequest(SmtpActionType actionType, String params, SmtpState state) {
43  		this.action = actionType;
44  		this.state = state;
45  		this.params = params;
46  	}
47  
48  	/***
49  	 * Execute the SMTP request returning a response. This method models the state transition table for the SMTP server.
50  	 * @return reponse to the request
51  	 */
52  	public SmtpResponse execute() {
53  		SmtpResponse response = null;
54  		if (action.isStateless()) {
55  			if (SmtpActionType.EXPN == action || SmtpActionType.VRFY == action) {
56  				response = new SmtpResponse(252, "Not supported", this.state);
57  			} else if (SmtpActionType.HELP == action) {
58  				response = new SmtpResponse(211, "No help available", this.state);
59  			} else if (SmtpActionType.NOOP == action) {
60  				response = new SmtpResponse(250, "OK", this.state);
61  			} else if (SmtpActionType.VRFY == action) {
62  				response = new SmtpResponse(252, "Not supported", this.state);
63  			} else if (SmtpActionType.RSET == action) {
64  				response = new SmtpResponse(250, "OK", SmtpState.GREET);
65  			} else {
66  				response = new SmtpResponse(500, "Command not recognized", this.state);
67  			}
68  		} else { // Stateful commands
69  			if (SmtpActionType.CONNECT == action) {
70  				if (SmtpState.CONNECT == state) {
71  					response = new SmtpResponse(220, "localhost Dumbster SMTP service ready",
72  							SmtpState.GREET);
73  				} else {
74  					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
75  							this.state);
76  				}
77  			} else if (SmtpActionType.EHLO == action) {
78  				if (SmtpState.GREET == state) {
79  					response = new SmtpResponse(250, "OK", SmtpState.MAIL);
80  				} else {
81  					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
82  							this.state);
83  				}
84  			} else if (SmtpActionType.MAIL == action) {
85  				if (SmtpState.MAIL == state) {
86  					response = new SmtpResponse(250, "OK", SmtpState.RCPT);
87  				} else {
88  					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
89  							this.state);
90  				}
91  			} else if (SmtpActionType.RCPT == action) {
92  				if (SmtpState.RCPT == state) {
93  					response = new SmtpResponse(250, "OK", this.state);
94  				} else {
95  					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
96  							this.state);
97  				}
98  			} else if (SmtpActionType.DATA == action) {
99  				if (SmtpState.RCPT == state) {
100 					response = new SmtpResponse(354, "Start mail input; end with <CRLF>.<CRLF>",
101 							SmtpState.DATA_HDR);
102 				} else {
103 					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
104 							this.state);
105 				}
106 			} else if (SmtpActionType.UNRECOG == action) {
107 				if (SmtpState.DATA_HDR == state || SmtpState.DATA_BODY == state) {
108 					response = new SmtpResponse(-1, "", this.state);
109 				} else {
110 					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
111 							this.state);
112 				}
113 			} else if (SmtpActionType.DATA_END == action) {
114 				if (SmtpState.DATA_HDR == state || SmtpState.DATA_BODY == state) {
115 					response = new SmtpResponse(250, "OK", SmtpState.QUIT);
116 				} else {
117 					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
118 							this.state);
119 				}
120 			} else if (SmtpActionType.BLANK_LINE == action) {
121 				if (SmtpState.DATA_HDR == state) {
122 					response = new SmtpResponse(-1, "", SmtpState.DATA_BODY);
123 				} else if (SmtpState.DATA_BODY == state) {
124 					response = new SmtpResponse(-1, "", this.state);
125 				} else {
126 					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
127 							this.state);
128 				}
129 			} else if (SmtpActionType.QUIT == action) {
130 				if (SmtpState.QUIT == state) {
131 					response = new SmtpResponse(250, "OK", SmtpState.CONNECT);
132 				} else {
133 					response = new SmtpResponse(503, "Bad sequence of commands: " + action,
134 							this.state);
135 				}
136 			} else {
137 				response = new SmtpResponse(500, "Command not recognized", this.state);
138 			}
139 		}
140 		return response;
141 	}
142 
143 	/***
144 	 * Create an SMTP request object given a line of the input stream from the client and the current internal state.
145 	 * @param s line of input
146 	 * @param state current state
147 	 * @return a populated SmtpRequest object
148 	 */
149 	public static SmtpRequest createRequest(String s, SmtpState state) {
150 		SmtpActionType action = null;
151 		String params = null;
152 
153 		if (state == SmtpState.DATA_HDR) {
154 			if (s.equals(".")) {
155 				action = SmtpActionType.DATA_END;
156 			} else if (s.length() < 1) {
157 				action = SmtpActionType.BLANK_LINE;
158 			} else {
159 				action = SmtpActionType.UNRECOG;
160 				params = s;
161 			}
162 		} else if (state == SmtpState.DATA_BODY) {
163 			if (s.equals(".")) {
164 				action = SmtpActionType.DATA_END;
165 			} else {
166 				action = SmtpActionType.UNRECOG;
167 				if (s.length() < 1) {
168 					params = "\n";
169 				} else {
170 					params = s;
171 				}
172 			}
173 		} else {
174 			String su = s.toUpperCase();
175 			if (su.startsWith("EHLO ") || su.startsWith("HELO")) {
176 				action = SmtpActionType.EHLO;
177 				params = s.substring(5);
178 			} else if (su.startsWith("MAIL FROM:")) {
179 				action = SmtpActionType.MAIL;
180 				params = s.substring(10);
181 			} else if (su.startsWith("RCPT TO:")) {
182 				action = SmtpActionType.RCPT;
183 				params = s.substring(8);
184 			} else if (su.startsWith("DATA")) {
185 				action = SmtpActionType.DATA;
186 			} else if (su.startsWith("QUIT")) {
187 				action = SmtpActionType.QUIT;
188 			} else if (su.startsWith("RSET")) {
189 				action = SmtpActionType.RSET;
190 			} else if (su.startsWith("NOOP")) {
191 				action = SmtpActionType.NOOP;
192 			} else if (su.startsWith("EXPN")) {
193 				action = SmtpActionType.EXPN;
194 			} else if (su.startsWith("VRFY")) {
195 				action = SmtpActionType.VRFY;
196 			} else if (su.startsWith("HELP")) {
197 				action = SmtpActionType.HELP;
198 			}
199 		}
200 
201 		SmtpRequest req = new SmtpRequest(action, params, state);
202 		return req;
203 	}
204 
205 	/***
206 	 * Get the parameters of this request (remainder of command line once the command is removed.
207 	 * @return parameters
208 	 */
209 	public String getParams() {
210 		return params;
211 	}
212 }