    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 }