View Javadoc

1   /*
2    * Copyright (c) 2004-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.mercury.smtp.command;
14  
15  import java.io.IOException;
16  import java.io.InterruptedIOException;
17  import java.io.OutputStream;
18  import java.io.PrintStream;
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.Map.Entry;
22  
23  import org.abstracthorizon.danube.connection.Connection;
24  import org.abstracthorizon.danube.service.server.ServerConnectionHandler;
25  import org.abstracthorizon.mercury.common.command.CommandException;
26  import org.abstracthorizon.mercury.smtp.SMTPResponses;
27  import org.abstracthorizon.mercury.smtp.SMTPScanner;
28  import org.abstracthorizon.mercury.smtp.SMTPSession;
29  import org.abstracthorizon.mercury.smtp.exception.ParserException;
30  import org.abstracthorizon.mercury.smtp.filter.MailSessionData;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  /**
35   * SMTP command factory. This class is implementing parsing commands and invoking them.
36   *
37   *
38   * @author Daniel Sendula
39   */
40  public class SMTPCommandFactory extends ServerConnectionHandler {
41  
42      /** HELO commmand */
43      public static String HELO = "HELO";
44  
45      /** EHLO command */
46      public static String EHLO = "EHLO";
47  
48      /** MAIL command */
49      public static String MAIL = "MAIL";
50  
51      /** RCPT command */
52      public static String RCPT = "RCPT";
53  
54      /** DATA command */
55      public static String DATA = "DATA";
56  
57      /** NOOP commmand */
58      public static String NOOP = "NOOP";
59  
60      /** RSET (reset) command */
61      public static String RSET = "RSET";
62  
63      /** QUIT command */
64      public static String QUIT = "QUIT";
65  
66      /** VRFY command */
67      public static String VRFY = "VRFY";
68  
69      /** EXPN command */
70      public static String EXPN = "EXPN";
71  
72      /** Logger */
73      protected static final Logger logger = LoggerFactory.getLogger(SMTPCommandFactory.class);
74  
75      /** Map of commands */
76      protected Map<String, SMTPCommand> commands = new HashMap<String, SMTPCommand>();
77  
78      /** Inactivity timeout */
79      protected int inactivityTimeout = 120000;
80  
81      /**
82       * Constructor. It updates the map.
83       */
84      public SMTPCommandFactory() {
85  
86          commands.put(EHLO, new EhloCommand());
87          commands.put(HELO, new EhloCommand());
88          commands.put(MAIL, new MailCommand());
89          commands.put(RCPT, new RcptCommand());
90          commands.put(DATA, new DataCommand());
91  
92          commands.put(NOOP, new NoopCommand());
93          commands.put(RSET, new ResetCommand());
94          commands.put(QUIT, new QuitCommand());
95          commands.put(VRFY, new NotImplementedCommand());
96          commands.put(EXPN, new NotImplementedCommand());
97      }
98  
99      /**
100      * Returns commands
101      * @return commands
102      */
103     public Map<String, SMTPCommand> getCommands() {
104         return commands;
105     }
106 
107     /**
108      * Sets map of commands
109      * @param commands commands
110      */
111     public void setCommands(Map<String, SMTPCommand> commands) {
112         this.commands = commands;
113     }
114 
115     /**
116      * Sets inactivity timeout
117      * @param timeout timeout
118      */
119     public void setInactivityTimeout(int timeout) {
120         this.inactivityTimeout = timeout;
121     }
122 
123     /**
124      * Returns inactivity timeout
125      * @return inactivity timeout
126      */
127     public int getInactivityTimeout() {
128         return inactivityTimeout;
129     }
130 
131     /**
132      * Processes keywords
133      * @param connection connection
134      */
135     protected void processConnection(Connection connection) {
136         SMTPSession smtpConnection = (SMTPSession)connection.adapt(SMTPSession.class);
137         try {
138             processKeywords(smtpConnection);
139         } catch (IOException e) {
140             throw new RuntimeException(e);
141         }
142     }
143 
144     /**
145      * Sets state to {@link SMTPSession#STATE_READY}
146      *
147      * @param connection connection
148      */
149     protected Connection decorateConnection(Connection connection) {
150         SMTPSession smtpConnection = (SMTPSession)connection.adapt(SMTPSession.class);
151         smtpConnection.setCommandFactory(this);
152         smtpConnection.setState(SMTPSession.STATE_READY);
153         return smtpConnection;
154     }
155 
156     /**
157      * Returns <code>false</code> only if state of connection is {@link SMTPSession#STATE_CONNECTED}
158      * @param connection connection
159      */
160     protected boolean postProcessing(Connection connection) {
161         boolean persistConnection = super.postProcessing(connection);
162         SMTPSession session = (SMTPSession)connection.adapt(SMTPSession.class);
163         if ((System.currentTimeMillis() - session.getSessionAccessed()) > getInactivityTimeout()) {
164             return false;
165         }
166 
167 //        SMTPSession smtpConnection = (SMTPSession)connection.adapt(SMTPSession.class);
168 //        if (smtpConnection.getState() == SMTPSession.STATE_CONNECTED) {
169 //            persistConnection = false;
170 //        }
171         MailSessionData data = session.getMailSessionData();
172         boolean dropConnection = "true".equals(data.getAttribute("dropconnection"));
173         persistConnection = !dropConnection && persistConnection && (session.getState() != SMTPSession.STATE_READY);
174         
175         return persistConnection;
176     }
177 
178     /**
179      * Does nothing
180      * @param connection connection
181      * @param closedConnection is connection already closed.
182      */
183     protected void finishProcessingConnection(Connection connection, boolean closedConnection) {
184         // DO nothing!
185     }
186 
187     /**
188      * Returns a command from the map of commands
189      * @param mnemonic command name
190      * @return command or <code>null</code>
191      * @throws CommandException
192      */
193     public SMTPCommand getCommand(String mnemonic) throws CommandException {
194         SMTPCommand command = commands.get(mnemonic);
195         return command;
196     }
197 
198     /**
199      * Checks all defined commands in map of commands. As soon as command is
200      * found it is executed.
201      *
202      * @param smtpConnection smtp connection
203      * @throws IOException
204      */
205     public void processKeywords(SMTPSession smtpConnection) throws IOException {
206         SMTPScanner scanner = smtpConnection.getScanner();
207         for (Entry<String, SMTPCommand> entry : commands.entrySet()) {
208             if (scanner.keyword(entry.getKey())) {
209                 invokeCommand(smtpConnection, entry.getKey(), entry.getValue());
210                 return;
211             }
212         }
213 
214         scanner.skip_line();
215         scanner.resetEOL();
216         smtpConnection.sendResponse(SMTPResponses.COMMAND_NOT_RECOGNISED_RESPONSE);
217     }
218 
219     /**
220      * Invokes command
221      *
222      * @param smtpConnection smtp connection
223      * @param commandName command name
224      * @param command command itself
225      * @throws IOException
226      */
227     public void invokeCommand(SMTPSession smtpConnection, String commandName, SMTPCommand command) throws IOException {
228         SMTPScanner scanner = smtpConnection.getScanner();
229         if (scanner.peek_char(' ') || scanner.peek_char('\r')) {
230             try {
231                 long started = System.currentTimeMillis();
232                 try {
233                     if (command != null) {
234                         command.handleConnection(smtpConnection);
235                     } else {
236                         smtpConnection.sendResponse(SMTPResponses.SYNTAX_ERROR_RESPONSE);
237                     }
238                 } finally {
239                     if (logger.isDebugEnabled()) {
240                         logger.debug("Command " + commandName + " completed in " + (System.currentTimeMillis() - started) + "ms");
241                     }
242                 }
243             } catch (InterruptedIOException e) {
244                 throw e;
245             } catch (ParserException e) {
246                 smtpConnection.setKeepLog(true);
247                 if (logger.isDebugEnabled()) {
248                     logger.debug("Problem", e);
249                 }
250                 OutputStream debugStream = smtpConnection.getDebugStream();
251                 if (debugStream != null) {
252                     e.printStackTrace(new PrintStream(debugStream));
253                 }
254                 smtpConnection.sendResponse(SMTPResponses.SYNTAX_ERROR_RESPONSE);
255             } catch (CommandException e) {
256                 smtpConnection.setKeepLog(true);
257                 logger.error("Problem", e);
258                 OutputStream debugStream = smtpConnection.getDebugStream();
259                 if (debugStream != null) {
260                     e.printStackTrace(new PrintStream(debugStream));
261                 }
262                 smtpConnection.sendResponse(SMTPResponses.GENERIC_ERROR_RESPONSE);
263             } catch (Throwable e) {
264                 smtpConnection.setKeepLog(true);
265                 logger.error("Problem", e);
266                 OutputStream debugStream = smtpConnection.getDebugStream();
267                 if (debugStream != null) {
268                     e.printStackTrace(new PrintStream(debugStream));
269                 }
270                 smtpConnection.sendResponse(SMTPResponses.GENERIC_ERROR_RESPONSE);
271             }
272         } else {
273             smtpConnection.sendResponse(SMTPResponses.SYNTAX_ERROR_RESPONSE);
274         }
275         scanner.skip_line();
276         scanner.resetEOL();
277     }
278 }