1   package org.masukomi.aspirin.core;
2   
3   
4   /*
5    * Portions of this file are Copyright (C) The Apache Software Foundation. All
6    * rights reserved.
7    *
8    * This software is published under the terms of the Apache Software License
9    * version 1.1, a copy of which has been included with this distribution in
10   * the LICENSE file.
11   */
12  
13  
14  import org.apache.james.core.MailHeaders;
15  
16  import org.apache.james.util.*;
17  import org.apache.james.util.watchdog.BytesReadResetInputStream;
18  import org.apache.james.util.watchdog.Watchdog;
19  import org.apache.james.util.watchdog.WatchdogTarget;
20  import org.apache.james.smtpserver.*;
21  import org.apache.mailet.MailAddress;
22  
23  import org.masukomi.aspirin.filters.*;
24  
25  import org.masukomi.prefs.XMLClassPreferences;
26  
27  import java.io.*;
28  
29  import java.net.Socket;
30  import java.net.SocketException;
31  import java.net.InetAddress;
32  import java.net.UnknownHostException;
33  
34  import java.util.*;
35  
36  import javax.mail.MessagingException;
37  import javax.mail.*;
38  import javax.mail.internet.*;
39  
40  
41  /***
42   * Provides SMTP functionality by carrying out the server side of the SMTP
43   * interaction.
44   *
45   * @author Serge Knystautas <sergek@lokitech.com>
46   * @author Federico Barbieri <scoobie@systemy.it>
47   * @author Jason Borden <jborden@javasense.com>
48   * @author Matthew Pangaro <mattp@lokitech.com>
49   * @author Danny Angus <danny@thought.co.uk>
50   * @author Peter M. Goldstein <farsight@alum.mit.edu>
51   * @author Kate Rhodes <masukomi@masukomi.org>
52   *
53   * @todo enable authentication (roughly lines 840, 1099, 10266)
54   */
55  public class SMTPHandler extends Thread{ //implements SMTPHandler, Poolable {
56  	
57      //~ Static fields/initializers ---------------------------------------------
58  
59      /***
60       * SMTP Server identification string used in SMTP headers
61       */
62      private final static String SOFTWARE_TYPE = "Aspirin SMTP Server ";
63          //+ Constants.SOFTWARE_VERSION;
64  
65      // Keys used to store/lookup data in the internal state hash map
66  	
67  	private static String DEFAULT_HELO_NAME =  "UNKNOWN Aspirin Server";
68  	
69  	private final static boolean DEFAULT_AUTH_REQUIRED = false;
70  	
71  	private final long DEFAULT_MAX_MESSAGE_SIZE =0;
72  	
73  	private final int DEFAULT_RESET_LENGTH =20*1024;
74  	
75  	
76      /*** DOCUMENT ME! */
77      private final static String CURRENT_HELO_MODE = "CURRENT_HELO_MODE"; // HELO or EHLO
78  
79      /*** DOCUMENT ME! */
80      private final static String SENDER = "SENDER_ADDRESS"; // Sender's email address 
81  
82      /*** DOCUMENT ME! */
83      private final static String MESG_FAILED = "MESG_FAILED"; // Message failed flag
84  
85      /*** DOCUMENT ME! */
86      private final static String MESG_SIZE = "MESG_SIZE"; // The size of the message
87  
88      /*** DOCUMENT ME! */
89      private final static String RCPT_LIST = "RCPT_LIST"; // The message recipients
90  
91      /***
92       * The character array that indicates termination of an SMTP connection
93       */
94      private final static char[] SMTPTerminator = { '\r', '\n', '.', '\r', '\n' };
95  
96      /***
97       * Static Random instance used to generate SMTP ids
98       */
99      private final static Random random = new Random();
100 
101     /***
102      * Static RFC822DateFormat used to generate date headers
103      */
104     private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
105 
106     /***
107      * The text string for the SMTP HELO command.
108      */
109     private final static String COMMAND_HELO = "HELO";
110 
111     /***
112      * The text string for the SMTP EHLO command.
113      */
114     private final static String COMMAND_EHLO = "EHLO";
115 
116     /***
117      * The text string for the SMTP AUTH command.
118      */
119     private final static String COMMAND_AUTH = "AUTH";
120 
121     /***
122      * The text string for the SMTP MAIL command.
123      */
124     private final static String COMMAND_MAIL = "MAIL";
125 
126     /***
127      * The text string for the SMTP RCPT command.
128      */
129     private final static String COMMAND_RCPT = "RCPT";
130 
131     /***
132      * The text string for the SMTP NOOP command.
133      */
134     private final static String COMMAND_NOOP = "NOOP";
135 
136     /***
137      * The text string for the SMTP RSET command.
138      */
139     private final static String COMMAND_RSET = "RSET";
140 
141     /***
142      * The text string for the SMTP DATA command.
143      */
144     private final static String COMMAND_DATA = "DATA";
145 
146     /***
147      * The text string for the SMTP QUIT command.
148      */
149     private final static String COMMAND_QUIT = "QUIT";
150 
151     /***
152      * The text string for the SMTP HELP command.
153      */
154     private final static String COMMAND_HELP = "HELP";
155 
156     /***
157      * The text string for the SMTP VRFY command.
158      */
159     private final static String COMMAND_VRFY = "VRFY";
160 
161     /***
162      * The text string for the SMTP EXPN command.
163      */
164     private final static String COMMAND_EXPN = "EXPN";
165 
166     /***
167      * The text string for the SMTP AUTH type PLAIN.
168      */
169     private final static String AUTH_TYPE_PLAIN = "PLAIN";
170 
171     /***
172      * The text string for the SMTP AUTH type LOGIN.
173      */
174     private final static String AUTH_TYPE_LOGIN = "LOGIN";
175 
176     /***
177      * The text string for the SMTP MAIL command SIZE option.
178      */
179     private final static String MAIL_OPTION_SIZE = "SIZE";
180 
181     //~ Instance fields --------------------------------------------------------
182 
183     private Session currentSession;
184     
185     private MimeMessage message;
186     
187     /***
188      * The per-handler response buffer used to marshal responses.
189      */
190     StringBuffer responseBuffer = new StringBuffer(256);
191 
192     /***
193      * The watchdog being used by this handler to deal with idle timeouts.
194      */
195     Watchdog theWatchdog;
196 
197     /***
198      * The watchdog target that idles out this handler.
199      */
200     WatchdogTarget theWatchdogTarget = new SMTPWatchdogTarget();
201 
202     /***
203      * A Reader wrapper for the incoming stream of bytes coming from the socket.
204      */
205     private BufferedReader inReader;
206 
207     /***
208      * The hash map that holds variables for the SMTP message transfer in progress.
209      *
210      * This hash map should only be used to store variable set in a particular
211      * set of sequential MAIL-RCPT-DATA commands, as described in RFC 2821.  Per
212      * connection values should be stored as member variables in this class.
213      */
214     private HashMap state = new HashMap();
215 
216     /***
217      * The incoming stream of bytes coming from the socket.
218      */
219     private InputStream in;
220 
221     /***
222      * The writer to which outgoing messages are written.
223      */
224     private PrintWriter out;
225 
226     /***
227      * The per-service configuration data that applies to all handlers
228      */
229 
230     //private SMTPHandlerConfigurationData theConfigData;
231 
232     /***
233      * The TCP/IP socket over which the SMTP
234      * dialogue is occurring.
235      */
236     private Socket socket;
237 
238     /***
239      * The user name of the authenticated user associated with this SMTP transaction.
240      */
241     private String authenticatedUser;
242 
243     /***
244      * The remote host name obtained by lookup on the socket.
245      */
246     private String remoteHost;
247 
248     /***
249      * The remote IP address of the socket.
250      */
251     private String remoteIP;
252 
253     /***
254      * The id associated with this particular SMTP interaction.
255      */
256     private String smtpID;
257 
258     /***
259      * The thread executing this handler
260      */
261     private Thread handlerThread;
262 
263     /*** should contain the following keys
264      * <ul>
265      * <li>maxLineLength - the maximum length of a line to read from incoming
266      * sockets.</li>
267      * <li>heloName</li>
268      * <li>resetLength</li>
269      * <li>maxMessageSize</li>
270      * <li>authRequired</li>
271      * </ul>
272      */
273     private XMLClassPreferences preferences;
274     
275     
276     private String rawMessage;
277 
278     //~ Constructors -----------------------------------------------------------
279 	
280 	
281 	static {
282 		try {
283 			DEFAULT_HELO_NAME=InetAddress.getLocalHost().getHostName();
284 		} catch (UnknownHostException e) {
285 		}
286 	}
287     /***
288      * Creates a new SMTPHandler object.
289      */
290     public SMTPHandler() {
291         preferences = XMLClassPreferences.systemNodeForClass(this.getClass());
292 		currentSession = Session.getInstance(System.getProperties(), null);
293     }
294 	/***
295 	 * Creates a new SMTPHandler object.
296 	 */
297 	public SMTPHandler(Socket socket) {
298 		preferences = XMLClassPreferences.systemNodeForClass(this.getClass());
299 		currentSession = Session.getInstance(System.getProperties(), null);
300 		setSocket(socket);
301 	}
302 
303     //~ Methods ----------------------------------------------------------------
304 	
305 	public void setSocket(Socket socket){
306 		this.socket=socket;
307 		remoteIP = socket.getInetAddress().getHostAddress();
308 		remoteHost = socket.getInetAddress().getHostName();
309 		StringBuffer threadName = new StringBuffer("SMTPHandler connection from ");
310 		threadName.append(remoteHost);
311 		threadName.append("(");
312 		threadName.append(remoteIP);
313 		threadName.append(")");
314 		setName(threadName.toString());
315 		
316 	}
317 	
318     /***
319      * @see org.apache.avalon.cornerstone.services.connection.SMTPHandler#handleConnection(Socket)
320      */
321     public void handleConnection() throws IOException {
322 		
323         try {
324             //this.socket = connection;
325 
326             //synchronized (this) {
327             //    handlerThread = Thread.currentThread();
328             //}
329 
330             in = new BufferedInputStream(socket.getInputStream(), 1024);
331 
332             // An ASCII encoding can be used because all transmissions other
333             // that those in the DATA command are guaranteed
334             // to be ASCII
335             inReader = new BufferedReader(new InputStreamReader(in, "ASCII"),
336                     512);
337             
338             smtpID = random.nextInt(1024) + "";
339             resetState();
340             
341 			if (! RelayHostsFilter.isRelayableHost(remoteHost)){
342 				writeLoggedFlushedResponse("550 Relaying denied.");
343 				 return;
344 			}
345             
346         } catch (Exception e) {
347             StringBuffer exceptionBuffer = new StringBuffer(256).append(
348                     "Cannot open connection from ").append(remoteHost)
349                                                                 .append(" (")
350                                                                 .append(remoteIP)
351                                                                 .append("): ")
352                                                                 .append(e
353                     .getMessage());
354             String exceptionString = exceptionBuffer.toString();
355             //getLogger().error(exceptionString, e);
356             throw new RuntimeException(exceptionString);
357         }
358 
359         //if (getLogger().isInfoEnabled()) {
360             StringBuffer infoBuffer = new StringBuffer(128).append(
361                     "Connection from ").append(remoteHost).append(" (")
362                                                            .append(remoteIP)
363                                                            .append(")");
364             //getLogger().info(infoBuffer.toString());
365             System.out.println(infoBuffer.toString());
366         //}
367 
368         try {
369             out = new InternetPrintWriter(new BufferedWriter(
370                         new OutputStreamWriter(socket.getOutputStream()), 1024),
371                     false);
372 
373             // Initially greet the connector
374             // Format is:  Sat, 24 Jan 1998 13:16:09 -0500
375             responseBuffer.append("220 ").append(preferences.get("heloName", DEFAULT_HELO_NAME))
376                           .append(" SMTP Server (").append(SOFTWARE_TYPE)
377                           .append(") ready ").append(rfc822DateFormat.format(
378                     new Date()));
379             
380 			System.out.println(responseBuffer.toString());
381             String responseString = clearResponseBuffer();
382             writeLoggedFlushedResponse(responseString);
383 
384             //theWatchdog.start();
385 			
386             while (parseCommand(readCommandLine())) {
387                 //theWatchdog.reset();
388             }
389 
390             //theWatchdog.stop();
391             //getLogger().debug("Closing socket.");
392             
393         } catch (SocketException se) {
394             //if (getLogger().isDebugEnabled()) {
395                 StringBuffer errorBuffer = new StringBuffer(64).append(
396                         "Socket to ").append(remoteHost).append(" (")
397                                                                .append(remoteIP)
398                                                                .append(") closed remotely.");
399                 //getLogger().debug(errorBuffer.toString(), se);
400                 System.out.println(errorBuffer.toString());
401             //}
402         } catch (InterruptedIOException iioe) {
403             //if (getLogger().isDebugEnabled()) {
404                 StringBuffer errorBuffer = new StringBuffer(64).append(
405                         "Socket to ").append(remoteHost).append(" (")
406                                                                .append(remoteIP)
407                                                                .append(") timeout.");
408                 //getLogger().debug(errorBuffer.toString(), iioe);
409 				System.out.println(errorBuffer.toString());
410             //}
411         } catch (IOException ioe) {
412             //if (getLogger().isDebugEnabled()) {
413                 StringBuffer errorBuffer = new StringBuffer(256).append(
414                         "Exception handling socket to ").append(remoteHost)
415                                                                 .append(" (")
416                                                                 .append(remoteIP)
417                                                                 .append(") : ")
418                                                                 .append(ioe
419                         .getMessage());
420                 //getLogger().debug(errorBuffer.toString(), ioe);
421 				System.out.println(errorBuffer.toString());
422             //}
423         } catch (Exception e) {
424             //if (getLogger().isDebugEnabled()) {
425                // getLogger().debug("Exception opening socket: " + e.getMessage(),
426                //     e);
427 				System.out.println("Exception opening socket: " + e);
428             //}
429         } finally {
430             resetHandler();
431         }
432     }
433 
434     /***
435      * Set the configuration data for the handler
436      *
437      * @param theData the per-service configuration data for this handler
438      */
439     /*void setConfigurationData(SMTPHandlerConfigurationData theData) {
440         theConfigData = theData;
441     }*/
442 
443     /***
444      * Set the Watchdog for use by this handler.
445      *
446      * @param theWatchdog the watchdog
447      */
448     void setWatchdog(Watchdog theWatchdog) {
449         this.theWatchdog = theWatchdog;
450     }
451 
452     /***
453      * Gets the Watchdog Target that should be used by Watchdogs managing
454      * this connection.
455      *
456      * @return the WatchdogTarget
457      */
458     WatchdogTarget getWatchdogTarget() {
459         return theWatchdogTarget;
460     }
461 
462     /***
463      * Idle out this connection
464      */
465     void idleClose() {
466         /*if (getLogger() != null) {
467             getLogger().error("SMTP Connection has idled out.");
468         }*/
469 		System.out.println("SMTP Connection has idled out.");
470 
471         try {
472             if (socket != null) {
473                 socket.close();
474             }
475         } catch (Exception e) {
476             // ignored
477         }
478 
479         synchronized (this) {
480             // Interrupt the thread to recover from internal hangs
481             if (handlerThread != null) {
482                 handlerThread.interrupt();
483             }
484         }
485     }
486 
487     /***
488      * Reads a line of characters off the command line.
489      *
490      * @return the trimmed input line
491      * @throws IOException if an exception is generated reading in the input characters
492      */
493     final String readCommandLine() throws IOException {
494         return inReader.readLine().trim();
495     }
496 
497     /***
498      * Write and flush a response string.  The response is also logged.
499      * Should be used for the last line of a multi-line response or
500      * for a single line response.
501      *
502      * @param responseString the response string sent to the client
503      */
504     final void writeLoggedFlushedResponse(String responseString) {
505         out.println(responseString);
506         out.flush();
507         logResponseString(responseString);
508     }
509 
510     /***
511      * Write a response string.  The response is also logged.
512      * Used for multi-line responses.
513      *
514      * @param responseString the response string sent to the client
515      */
516     final void writeLoggedResponse(String responseString) {
517         out.println(responseString);
518         logResponseString(responseString);
519     }
520 
521     /***
522      * Sets the user name associated with this SMTP interaction.
523      *
524      * @param userID the user name
525      */
526     private void setUser(String userID) {
527         authenticatedUser = userID;
528     }
529 
530     /***
531      * Returns the user name associated with this SMTP interaction.
532      *
533      * @return the user name
534      */
535     private String getUser() {
536         return authenticatedUser;
537     }
538 
539     /***
540      * Clears the response buffer, returning the String of characters in the buffer.
541      *
542      * @return the data in the response buffer
543      */
544     private String clearResponseBuffer() {
545         String responseString = responseBuffer.toString();
546         responseBuffer.delete(0, responseBuffer.length());
547 
548         return responseString;
549     }
550 
551     /***
552      * This method logs at a "DEBUG" level the response string that
553      * was sent to the SMTP client.  The method is provided largely
554      * as syntactic sugar to neaten up the code base.  It is declared
555      * private and final to encourage compiler inlining.
556      *
557      * @param responseString the response string sent to the client
558      */
559     private final void logResponseString(String responseString) {
560         /*if (getLogger().isDebugEnabled()) {
561             getLogger().debug("Sent: " + responseString);
562         }*/
563         //System.out.println("Sent: " + responseString);
564     }
565 
566     /***
567      * Handler method called upon receipt of a AUTH command.
568      * Handles client authentication to the SMTP server.
569      *
570      * @param argument the argument passed in with the command by the SMTP client
571      */
572     private void doAUTH(String argument) throws Exception {
573         String responseString = null;
574 
575         if (getUser() != null) {
576             responseString = "503 User has previously authenticated. "
577                 + " Further authentication is not required!";
578             writeLoggedFlushedResponse(responseString);
579         } else if (argument == null) {
580             responseString = "501 Usage: AUTH (authentication type) <challenge>";
581             writeLoggedFlushedResponse(responseString);
582         } else {
583             String initialResponse = null;
584 
585             if ((argument != null) && (argument.indexOf(" ") > 0)) {
586                 initialResponse = argument.substring(argument.indexOf(" ") + 1);
587                 argument = argument.substring(0, argument.indexOf(" "));
588             }
589 
590             String authType = argument.toUpperCase(Locale.US);
591 
592             if (authType.equals(AUTH_TYPE_PLAIN)) {
593                 doPlainAuth(initialResponse);
594 
595                 return;
596             } else if (authType.equals(AUTH_TYPE_LOGIN)) {
597                 doLoginAuth(initialResponse);
598 
599                 return;
600             } else {
601                 doUnknownAuth(authType, initialResponse);
602 
603                 return;
604             }
605         }
606     }
607 
608     /***
609      * Handler method called upon receipt of a DATA command.
610      * Reads in message data, creates header, and delivers to
611      * mail server service for delivery.
612      *
613      * @param argument the argument passed in with the command by the SMTP client
614      */
615     private void doDATA(String argument) {
616         String responseString = null;
617 
618         if ((argument != null) && (argument.length() > 0)) {
619             responseString = "500 Unexpected argument provided with DATA command";
620             writeLoggedFlushedResponse(responseString);
621         }
622 
623         if (!state.containsKey(SENDER)) {
624             responseString = "503 No sender specified";
625             writeLoggedFlushedResponse(responseString);
626         } else if (!state.containsKey(RCPT_LIST)) {
627             responseString = "503 No recipients specified";
628             writeLoggedFlushedResponse(responseString);
629         } else {
630             responseString = "354 Ok Send data ending with <CRLF>.<CRLF>";
631             writeLoggedFlushedResponse(responseString);
632 
633             InputStream msgIn = new CharTerminatedInputStream(in, SMTPTerminator);
634 
635             try {
636                 //msgIn = new BytesReadResetInputStream(msgIn, theWatchdog,
637                 //        theConfigData.getResetLength());
638 				msgIn = new BytesReadResetInputStream(msgIn, theWatchdog,
639 										preferences.getInt("resetLength", DEFAULT_RESET_LENGTH));
640 
641                 // if the message size limit has been set, we'll
642                 // wrap msgIn with a SizeLimitedInputStream
643                 //long maxMessageSize = theConfigData.getMaxMessageSize();
644                 long maxMessageSize = preferences.getLong("maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE); // zero means no limit
645 
646                 if (maxMessageSize > 0) {
647                     /*if (getLogger().isDebugEnabled()) {
648                         StringBuffer logBuffer = new StringBuffer(128).append(
649                                 "Using SizeLimitedInputStream ")
650                                                                       .append(" with max message size: ")
651                                                                       .append(maxMessageSize);
652                         getLogger().debug(logBuffer.toString());
653                     }*/
654 
655                     msgIn = new SizeLimitedInputStream(msgIn, maxMessageSize);
656                 }
657 
658                 // Removes the dot stuffing
659 				msgIn = new SMTPInputStream(msgIn);
660                 
661                 
662                 // Parse out the message headers
663                 //MailHeaders headers = new MailHeaders(msgIn);
664                 //headers = processMailHeaders(headers);
665                 //processMail(headers, msgIn);
666                 //headers = null;
667                 processFullMail(msgIn);
668             } catch (MessagingException me) {
669                 // Grab any exception attached to this one.
670                 Exception e = me.getNextException();
671 
672                 // If there was an attached exception, and it's a
673                 // MessageSizeException
674                 if ((e != null) && e instanceof MessageSizeException) {
675                     // Add an item to the state to suppress
676                     // logging of extra lines of data
677                     // that are sent after the size limit has
678                     // been hit.
679                     state.put(MESG_FAILED, Boolean.TRUE);
680 
681                     // then let the client know that the size
682                     // limit has been hit.
683                     responseString = "552 Error processing message: "
684                         + e.getMessage();
685 
686                     /*StringBuffer errorBuffer = new StringBuffer(256).append(
687                             "Rejected message from ")
688                                                                     .append(state.get(
689                                 SENDER).toString()).append(" from host ")
690                                                                     .append(remoteHost)
691                                                                     .append(" (")
692                                                                     .append(remoteIP)
693                                                                     .append(") exceeding system maximum message size of ")
694                                                                     .append(theConfigData
695                             .getMaxMessageSize());
696                             
697                     */
698 					StringBuffer errorBuffer = new StringBuffer(256).append(
699 												"Rejected message from ")
700 																						.append(state.get(
701 													SENDER).toString()).append(" from host ")
702 																						.append(remoteHost)
703 																						.append(" (")
704 																						.append(remoteIP)
705 																						.append(") exceeding system maximum message size of ")
706 																						.append(preferences.getLong("maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE));
707                     //getLogger().error(errorBuffer.toString());
708                 } else {
709                     responseString = "451 Error processing message: "
710                         + me.getMessage();
711                     //getLogger().error("Unknown error occurred while processing DATA.",
712                     //    me);
713                 }
714 
715                 writeLoggedFlushedResponse(responseString);
716 
717                 return;
718             } finally {
719                 if (msgIn != null) {
720                     try {
721                         msgIn.close();
722                     } catch (Exception e) {
723                         // Ignore close exception
724                     }
725 
726                     msgIn = null;
727                 }
728             }
729 
730             resetState();
731             responseString = "250 Message received";
732             writeLoggedFlushedResponse(responseString);
733         }
734     }
735 
736     /***
737      * Handler method called upon receipt of a EHLO command.
738      * Responds with a greeting and informs the client whether
739      * client authentication is required.
740      *
741      * @param argument the argument passed in with the command by the SMTP client
742      */
743     private void doEHLO(String argument) {
744         String responseString = null;
745 
746         if (argument == null) {
747             responseString = "501 Domain address required: " + COMMAND_EHLO;
748             writeLoggedFlushedResponse(responseString);
749         } else {
750             resetState();
751             state.put(CURRENT_HELO_MODE, COMMAND_EHLO);
752 
753             // Extension defined in RFC 1870
754             //long maxMessageSize = theConfigData.getMaxMessageSize();
755 			long maxMessageSize = preferences.getLong("maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE);
756             if (maxMessageSize > 0) {
757                 responseString = "250-SIZE " + maxMessageSize;
758                 writeLoggedResponse(responseString);
759             }
760 
761             //if (theConfigData.isAuthRequired()) {
762             if (preferences.getBoolean("authRequired", DEFAULT_AUTH_REQUIRED)){
763             
764                 //This is necessary because we're going to do a multiline response
765                 responseBuffer.append("250-");
766             } else {
767                 responseBuffer.append("250 ");
768             }
769 
770             //responseBuffer.append(theConfigData.getHelloName()).append(" Hello ")
771 			responseBuffer.append(preferences.get("heloName", DEFAULT_HELO_NAME)).append(" Hello ")
772                           .append(argument).append(" (").append(remoteHost)
773                           .append(" [").append(remoteIP).append("])");
774             responseString = clearResponseBuffer();
775 
776             //if (theConfigData.isAuthRequired()) {
777 			if (preferences.getBoolean("authRequired", DEFAULT_AUTH_REQUIRED)) {
778                 writeLoggedResponse(responseString);
779                 responseString = "250 AUTH LOGIN PLAIN";
780             }
781 
782             writeLoggedFlushedResponse(responseString);
783         }
784     }
785 
786     /***
787      * Handler method called upon receipt of a EXPN command.
788      * This method informs the client that the command is
789      * not implemented.
790      *
791      * @param argument the argument passed in with the command by the SMTP client
792      */
793     private void doEXPN(String argument) {
794         String responseString = "502 EXPN is not supported";
795         writeLoggedFlushedResponse(responseString);
796     }
797 
798     /***
799      * Handler method called upon receipt of a HELO command.
800      * Responds with a greeting and informs the client whether
801      * client authentication is required.
802      *
803      * @param argument the argument passed in with the command by the SMTP client
804      */
805     private void doHELO(String argument) {
806         String responseString = null;
807 
808         if (argument == null) {
809             responseString = "501 Domain address required: " + COMMAND_HELO;
810             writeLoggedFlushedResponse(responseString);
811         } else {
812             resetState();
813             state.put(CURRENT_HELO_MODE, COMMAND_HELO);
814 
815             //if (theConfigData.isAuthRequired()) {
816 			if (preferences.getBoolean("authRequired", DEFAULT_AUTH_REQUIRED)) {
817                 //This is necessary because we're going to do a multiline response
818                 responseBuffer.append("250-");
819             } else {
820                 responseBuffer.append("250 ");
821             }
822 
823             //responseBuffer.append(theConfigData.getHelloName()).append(" Hello ")
824 			responseBuffer.append(preferences.get("heloName", DEFAULT_HELO_NAME)).append(" Hello ")
825                           .append(argument).append(" (").append(remoteHost)
826                           .append(" [").append(remoteIP).append("])");
827             responseString = clearResponseBuffer();
828 
829             //if (theConfigData.isAuthRequired()) {
830 			if (preferences.getBoolean("authRequired", DEFAULT_AUTH_REQUIRED)) {
831                 writeLoggedResponse(responseString);
832                 responseString = "250 AUTH LOGIN PLAIN";
833             }
834         }
835 
836         writeLoggedFlushedResponse(responseString);
837     }
838 
839     /***
840      * Handler method called upon receipt of a HELP command.
841      * This method informs the client that the command is
842      * not implemented.
843      *
844      * @param argument the argument passed in with the command by the SMTP client
845      */
846     private void doHELP(String argument) {
847         String responseString = "502 HELP is not supported";
848         writeLoggedFlushedResponse(responseString);
849     }
850 
851     /***
852      * Carries out the Login AUTH SASL exchange.
853      *
854      * @param initialResponse the initial response line passed in with the AUTH command
855      */
856     private void doLoginAuth(String initialResponse) throws IOException {
857         String user = null;
858         String pass = null;
859         String responseString = null;
860 
861         if (initialResponse == null) {
862             responseString = "334 VXNlcm5hbWU6"; // base64 encoded "Username:"
863             writeLoggedFlushedResponse(responseString);
864             user = readCommandLine();
865         } else {
866             user = initialResponse.trim();
867         }
868 
869         if (user != null) {
870             try {
871                 user = Base64.decodeAsString(user);
872             } catch (Exception e) {
873                 // Ignored - this parse error will be
874                 // addressed in the if clause below
875                 user = null;
876             }
877         }
878 
879         responseString = "334 UGFzc3dvcmQ6"; // base64 encoded "Password:"
880         writeLoggedFlushedResponse(responseString);
881         pass = readCommandLine();
882 
883         if (pass != null) {
884             try {
885                 pass = Base64.decodeAsString(pass);
886             } catch (Exception e) {
887                 // Ignored - this parse error will be
888                 // addressed in the if clause below
889                 pass = null;
890             }
891         }
892 
893         // Authenticate user
894         /*if ((user == null) || (pass == null)) {
895             responseString = "501 Could not decode parameters for AUTH LOGIN";
896         } else if (theConfigData.getUsersRepository().test(user, pass)) {
897             setUser(user);
898         */    responseString    = "235 Authentication Successful";
899 
900             /*if (getLogger().isDebugEnabled()) {
901                 // TODO: Make this string a more useful debug message
902                 getLogger().debug("AUTH method LOGIN succeeded");
903             }*/
904         /*} else {
905             responseString = "535 Authentication Failed";
906 
907             // TODO: Make this string a more useful error message
908             //getLogger().error("AUTH method LOGIN failed");
909         }*/
910 
911         writeLoggedFlushedResponse(responseString);
912 
913         return;
914     }
915 
916     /***
917      * Handler method called upon receipt of a MAIL command.
918      * Sets up handler to deliver mail as the stated sender.
919      *
920      * @param argument the argument passed in with the command by the SMTP client
921      */
922     private void doMAIL(String argument) {
923         String responseString = null;
924 
925         String sender = null;
926 
927         if ((argument != null) && (argument.indexOf(":") > 0)) {
928             int colonIndex = argument.indexOf(":");
929             sender = argument.substring(colonIndex + 1);
930             argument = argument.substring(0, colonIndex);
931         }
932 
933         if (state.containsKey(SENDER)) {
934             responseString = "503 Sender already specified";
935             writeLoggedFlushedResponse(responseString);
936         } else if ((argument == null)
937                 || !argument.toUpperCase(Locale.US).equals("FROM")
938                 || (sender == null)) {
939             responseString = "501 Usage: MAIL FROM:<sender>";
940             writeLoggedFlushedResponse(responseString);
941         } else {
942             sender = sender.trim();
943 
944             int lastChar = sender.lastIndexOf('>');
945 
946             // Check to see if any options are present and, if so, whether they are correctly formatted
947             // (separated from the closing angle bracket by a ' ').
948             if ((lastChar > 0) && (sender.length() > (lastChar + 2))
949                     && (sender.charAt(lastChar + 1) == ' ')) {
950                 String mailOptionString = sender.substring(lastChar + 2);
951 
952                 // Remove the options from the sender
953                 sender = sender.substring(0, lastChar + 1);
954 
955                 StringTokenizer optionTokenizer = new StringTokenizer(mailOptionString,
956                         " ");
957 
958                 while (optionTokenizer.hasMoreElements()) {
959                     String mailOption = optionTokenizer.nextToken();
960                     int equalIndex = mailOptionString.indexOf('=');
961                     String mailOptionName = mailOption;
962                     String mailOptionValue = "";
963 
964                     if (equalIndex > 0) {
965                         mailOptionName = mailOption.substring(0, equalIndex)
966                                                    .toUpperCase(Locale.US);
967                         mailOptionValue = mailOption.substring(equalIndex + 1);
968                     }
969 
970                     // Handle the SIZE extension keyword
971                     if (mailOptionName.startsWith(MAIL_OPTION_SIZE)) {
972                         if (!(doMailSize(mailOptionValue))) {
973                             return;
974                         }
975                     } else {
976                         // Unexpected option attached to the Mail command
977                         /*if (getLogger().isDebugEnabled()) {
978                             StringBuffer debugBuffer = new StringBuffer(128).append(
979                                     "MAIL command had unrecognized/unexpected option ")
980                                                                             .append(mailOptionName)
981                                                                             .append(" with value ")
982                                                                             .append(mailOptionValue);
983                             getLogger().debug(debugBuffer.toString());
984                         }*/
985                     }
986                 }
987             }
988 
989             if (!sender.startsWith("<") || !sender.endsWith(">")) {
990                 responseString = "501 Syntax error in MAIL command";
991                 writeLoggedFlushedResponse(responseString);
992 
993                 /*if (getLogger().isErrorEnabled()) {
994                     StringBuffer errorBuffer = new StringBuffer(128).append(
995                             "Error parsing sender address: ").append(sender)
996                                                                     .append(": did not start and end with < >");
997                     getLogger().error(errorBuffer.toString());
998                 }*/
999 
1000                 return;
1001             }
1002 
1003             MailAddress senderAddress = null;
1004 
1005             //Remove < and >
1006             //sender = sender.substring(1, sender.length() - 1);
1007 			sender = extractEmailAddress(sender);
1008             if (sender.length() == 0) {
1009                 //This is the <> case.  Let senderAddress == null
1010             } else {
1011                 if (sender.indexOf("@") < 0) {
1012                     sender = sender + "@localhost";
1013                 }
1014                 
1015 
1016                 try {
1017                     senderAddress = new MailAddress(sender);
1018                 } catch (Exception pe) {
1019                     responseString = "501 Syntax error in sender address. address read as " + sender;
1020                     writeLoggedFlushedResponse(responseString);
1021 
1022                     /*if (getLogger().isErrorEnabled()) {
1023                         StringBuffer errorBuffer = new StringBuffer(256).append(
1024                                 "Error parsing sender address: ").append(sender)
1025                                                                         .append(": ")
1026                                                                         .append(pe
1027                                 .getMessage());
1028                         getLogger().error(errorBuffer.toString());
1029                     }*/
1030 
1031                     return;
1032                 }
1033             }
1034 
1035             state.put(SENDER, senderAddress);
1036             responseBuffer.append("250 Sender <").append(sender).append("> OK");
1037             responseString = clearResponseBuffer();
1038             writeLoggedFlushedResponse(responseString);
1039         }
1040     }
1041 
1042     /***
1043      * Handles the SIZE MAIL option.
1044      *
1045      * @param mailOptionValue the option string passed in with the SIZE option
1046      * @returns true if further options should be processed, false otherwise
1047      */
1048     private boolean doMailSize(String mailOptionValue) {
1049         int size = 0;
1050 
1051         try {
1052             size = Integer.parseInt(mailOptionValue);
1053         } catch (NumberFormatException pe) {
1054             // This is a malformed option value.  We return an error
1055             String responseString = "501 Syntactically incorrect value for SIZE parameter";
1056             writeLoggedFlushedResponse(responseString);
1057             //getLogger().error("Rejected syntactically incorrect value for SIZE parameter.");
1058 
1059             return false;
1060         }
1061 
1062         /*if (getLogger().isDebugEnabled()) {
1063             StringBuffer debugBuffer = new StringBuffer(128).append(
1064                     "MAIL command option SIZE received with value ").append(size)
1065                                                             .append(".");
1066             getLogger().debug(debugBuffer.toString());
1067         }*/
1068 
1069         //long maxMessageSize = theConfigData.getMaxMessageSize();
1070 		long maxMessageSize = preferences.getLong("maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE);
1071 		
1072         if ((maxMessageSize > 0) && (size > maxMessageSize)) {
1073             // Let the client know that the size limit has been hit.
1074             String responseString = "552 Message size exceeds fixed maximum message size";
1075             writeLoggedFlushedResponse(responseString);
1076 
1077             StringBuffer errorBuffer = new StringBuffer(256).append(
1078                     "Rejected message from ")
1079                                                             .append(state.get(
1080                         SENDER).toString()).append(" from host ")
1081                                                             .append(remoteHost)
1082                                                             .append(" (")
1083                                                             .append(remoteIP)
1084                                                             .append(") of size ")
1085                                                             .append(size)
1086                                                             .append(" exceeding system maximum message size of ")
1087                                                             .append(maxMessageSize)
1088                                                             .append("based on SIZE option.");
1089             //getLogger().error(errorBuffer.toString());
1090 
1091             return false;
1092         } else {
1093             // put the message size in the message state so it can be used
1094             // later to restrict messages for user quotas, etc.
1095             state.put(MESG_SIZE, new Integer(size));
1096         }
1097 
1098         return true;
1099     }
1100 
1101     /***
1102      * Handler method called upon receipt of a NOOP command.
1103      * Just sends back an OK and logs the command.
1104      *
1105      * @param argument the argument passed in with the command by the SMTP client
1106      */
1107     private void doNOOP(String argument) {
1108         String responseString = "250 OK";
1109         writeLoggedFlushedResponse(responseString);
1110     }
1111 
1112     /***
1113      * Carries out the Plain AUTH SASL exchange.
1114      *
1115      * @param initialResponse the initial response line passed in with the AUTH command
1116      */
1117     private void doPlainAuth(String initialResponse) throws IOException {
1118         String userpass = null;
1119         String user = null;
1120         String pass = null;
1121         String responseString = null;
1122 
1123         if (initialResponse == null) {
1124             responseString = "334 OK. Continue authentication";
1125             writeLoggedFlushedResponse(responseString);
1126             userpass = readCommandLine();
1127         } else {
1128             userpass = initialResponse.trim();
1129         }
1130 
1131         try {
1132             if (userpass != null) {
1133                 userpass = Base64.decodeAsString(userpass);
1134             }
1135 
1136             if (userpass != null) {
1137                 StringTokenizer authTokenizer = new StringTokenizer(userpass,
1138                         "\0");
1139                 user = authTokenizer.nextToken();
1140                 pass = authTokenizer.nextToken();
1141                 authTokenizer = null;
1142             }
1143         } catch (Exception e) {
1144             // Ignored - this exception in parsing will be dealt
1145             // with in the if clause below
1146         }
1147 
1148         // Authenticate user
1149         /*if ((user == null) || (pass == null)) {
1150             responseString = "501 Could not decode parameters for AUTH PLAIN";
1151             writeLoggedFlushedResponse(responseString);
1152         } else if (theConfigData.getUsersRepository().test(user, pass)) {
1153             setUser(user);
1154         */    responseString    = "235 Authentication Successful";
1155             writeLoggedFlushedResponse(responseString);
1156         /*    getLogger().info("AUTH method PLAIN succeeded");
1157         } else {
1158             responseString = "535 Authentication Failed";
1159             writeLoggedFlushedResponse(responseString);
1160             getLogger().error("AUTH method PLAIN failed");
1161         }*/
1162 
1163         return;
1164     }
1165 
1166     /***
1167      * Handler method called upon receipt of a QUIT command.
1168      * This method informs the client that the connection is
1169      * closing.
1170      *
1171      * @param argument the argument passed in with the command by the SMTP client
1172      */
1173     private void doQUIT(String argument) {
1174         String responseString = "";
1175 
1176         if ((argument == null) || (argument.length() == 0)) {
1177             responseBuffer.append("221 ").append(preferences.get("heloName", DEFAULT_HELO_NAME))
1178                           .append(" Service closing transmission channel");
1179             responseString = clearResponseBuffer();
1180         } else {
1181             responseString = "500 Unexpected argument provided with QUIT command";
1182         }
1183 
1184         writeLoggedFlushedResponse(responseString);
1185     }
1186 
1187     /***
1188      * Handler method called upon receipt of a RCPT command.
1189      * Reads recipient.  Does some connection validation.
1190      *
1191      * @param argument the argument passed in with the command by the SMTP client
1192      */
1193     private void doRCPT(String argument) {
1194         String responseString = null;
1195 
1196         String recipient = null;
1197 
1198         if ((argument != null) && (argument.indexOf(":") > 0)) {
1199             int colonIndex = argument.indexOf(":");
1200             recipient = argument.substring(colonIndex + 1);
1201             argument = argument.substring(0, colonIndex);
1202         }
1203 
1204         if (!state.containsKey(SENDER)) {
1205             responseString = "503 Need MAIL before RCPT";
1206             writeLoggedFlushedResponse(responseString);
1207         } else if ((argument == null)
1208                 || !argument.toUpperCase(Locale.US).equals("TO")
1209                 || (recipient == null)) {
1210             responseString = "501 Usage: RCPT TO:<recipient>";
1211             writeLoggedFlushedResponse(responseString);
1212         } else {
1213             Collection rcptColl = (Collection) state.get(RCPT_LIST);
1214 
1215             if (rcptColl == null) {
1216                 rcptColl = new ArrayList();
1217             }
1218 
1219             recipient = recipient.trim();
1220 
1221             int lastChar = recipient.lastIndexOf('>');
1222 
1223             // Check to see if any options are present and, if so, whether they are correctly formatted
1224             // (separated from the closing angle bracket by a ' ').
1225             if ((lastChar > 0) && (recipient.length() > (lastChar + 2))
1226                     && (recipient.charAt(lastChar + 1) == ' ')) {
1227                 String rcptOptionString = recipient.substring(lastChar + 2);
1228 
1229                 // Remove the options from the recipient
1230                 recipient = recipient.substring(0, lastChar + 1);
1231 
1232                 StringTokenizer optionTokenizer = new StringTokenizer(rcptOptionString,
1233                         " ");
1234 
1235                 while (optionTokenizer.hasMoreElements()) {
1236                     String rcptOption = optionTokenizer.nextToken();
1237                     int equalIndex = rcptOptionString.indexOf('=');
1238                     String rcptOptionName = rcptOption;
1239                     String rcptOptionValue = "";
1240 
1241                     if (equalIndex > 0) {
1242                         rcptOptionName = rcptOption.substring(0, equalIndex)
1243                                                    .toUpperCase(Locale.US);
1244                         rcptOptionValue = rcptOption.substring(equalIndex + 1);
1245                     }
1246 
1247                     // Unexpected option attached to the RCPT command
1248                     /*if (getLogger().isDebugEnabled()) {
1249                         StringBuffer debugBuffer = new StringBuffer(128).append(
1250                                 "RCPT command had unrecognized/unexpected option ")
1251                                                                         .append(rcptOptionName)
1252                                                                         .append(" with value ")
1253                                                                         .append(rcptOptionValue);
1254                         getLogger().debug(debugBuffer.toString());
1255                     }*/
1256                 }
1257 
1258                 optionTokenizer = null;
1259             }
1260 
1261             if (!recipient.startsWith("<") || !recipient.endsWith(">")) {
1262                 responseString = "501 Syntax error in parameters or arguments";
1263                 writeLoggedFlushedResponse(responseString);
1264 
1265                 /*if (getLogger().isErrorEnabled()) {
1266                     StringBuffer errorBuffer = new StringBuffer(192).append(
1267                             "Error parsing recipient address: ")
1268                                                                     .append(recipient)
1269                                                                     .append(": did not start and end with < >");
1270                     getLogger().error(errorBuffer.toString());
1271                 }*/
1272 
1273                 return;
1274             }
1275 
1276             MailAddress recipientAddress = null;
1277 
1278             //Remove < and >
1279             //recipient = recipient.substring(1, recipient.length() - 1);
1280 			recipient = extractEmailAddress(recipient);
1281             if (recipient.indexOf("@") < 0) {
1282                 recipient = recipient + "@localhost";
1283             } 
1284 
1285             try {
1286                 recipientAddress = new MailAddress(recipient);
1287             } catch (Exception pe) {
1288                 responseString = "501 Syntax error in recipient address";
1289                 writeLoggedFlushedResponse(responseString);
1290 
1291                 /*if (getLogger().isErrorEnabled()) {
1292                     StringBuffer errorBuffer = new StringBuffer(192).append(
1293                             "Error parsing recipient address: ")
1294                                                                     .append(recipient)
1295                                                                     .append(": ")
1296                                                                     .append(pe
1297                             .getMessage());
1298                     getLogger().error(errorBuffer.toString());
1299                 }*/
1300 
1301                 return;
1302             }
1303 
1304             //if (theConfigData.isAuthRequired()) {
1305 			if (preferences.getBoolean("authRequired", DEFAULT_AUTH_REQUIRED)) {
1306                 // Make sure the mail is being sent locally if not
1307                 // authenticated else reject.
1308                 if (getUser() == null) {
1309                     String toDomain = recipientAddress.getHost();
1310 
1311                     //if (!theConfigData.getMailServer().isLocalServer(toDomain)) {
1312                     if (RelayHostsFilter.isRelayableHost(toDomain)){
1313                         responseString = "530 Authentication Required";
1314                     
1315                         writeLoggedFlushedResponse(responseString);
1316                         //getLogger().error("Rejected message - authentication is required for mail request");
1317 
1318                         return;
1319                     }
1320                 } else {
1321                     // Identity verification checking
1322                     /*
1323                      * 
1324                      if (theConfigData.isVerifyIdentity()) {
1325                         String authUser = (getUser()).toLowerCase(Locale.US);
1326                         MailAddress senderAddress = (MailAddress) state.get(SENDER);
1327                         boolean domainExists = false;
1328 
1329                         if ((!authUser.equals(senderAddress.getUser()))
1330                                 || (!theConfigData.getMailServer()
1331                                                       .isLocalServer(senderAddress
1332                                     .getHost()))) {
1333                             responseString = "503 Incorrect Authentication for Specified Email Address";
1334                             writeLoggedFlushedResponse(responseString);
1335 
1336                             if (getLogger().isErrorEnabled()) {
1337                                 StringBuffer errorBuffer = new StringBuffer(128).append(
1338                                         "User ").append(authUser)
1339                                                                                 .append(" authenticated, however tried sending email as ")
1340                                                                                 .append(senderAddress);
1341                                 getLogger().error(errorBuffer.toString());
1342                             }
1343 
1344                             return;
1345                         }
1346                     }
1347                     */
1348                 }
1349             }
1350 			
1351 			// shouldn't this be in the above bracket set?
1352             rcptColl.add(recipientAddress);
1353             state.put(RCPT_LIST, rcptColl);
1354             responseBuffer.append("250 Recipient <").append(recipient).append("> OK");
1355             responseString = clearResponseBuffer();
1356             writeLoggedFlushedResponse(responseString);
1357         }
1358     }
1359 
1360     /***
1361      * Handler method called upon receipt of a RSET command.
1362      * Resets message-specific, but not authenticated user, state.
1363      *
1364      * @param argument the argument passed in with the command by the SMTP client
1365      */
1366     private void doRSET(String argument) {
1367         String responseString = "";
1368 
1369         if ((argument == null) || (argument.length() == 0)) {
1370             responseString = "250 OK";
1371             resetState();
1372         } else {
1373             responseString = "500 Unexpected argument provided with RSET command";
1374         }
1375 
1376         writeLoggedFlushedResponse(responseString);
1377     }
1378 
1379     /***
1380      * Handles the case of an unrecognized auth type.
1381      *
1382      * @param authType the unknown auth type
1383      * @param initialResponse the initial response line passed in with the AUTH command
1384      */
1385     private void doUnknownAuth(String authType, String initialResponse) {
1386         String responseString = "504 Unrecognized Authentication Type";
1387         writeLoggedFlushedResponse(responseString);
1388 
1389         /*if (getLogger().isErrorEnabled()) {
1390             StringBuffer errorBuffer = new StringBuffer(128).append(
1391                     "AUTH method ").append(authType).append(" is an unrecognized authentication type");
1392             getLogger().error(errorBuffer.toString());
1393         }*/
1394 
1395         return;
1396     }
1397 
1398     /***
1399      * Handler method called upon receipt of an unrecognized command.
1400      * Returns an error response and logs the command.
1401      *
1402      * @param command the command parsed by the SMTP client
1403      * @param argument the argument passed in with the command by the SMTP client
1404      */
1405     private void doUnknownCmd(String command, String argument) {
1406         //responseBuffer.append("500 ").append(theConfigData.getHelloName())
1407 		responseBuffer.append("500 ").append(preferences.get("heloName", DEFAULT_HELO_NAME))
1408                       .append(" Syntax error, command unrecognized: ").append(command);
1409 
1410         String responseString = clearResponseBuffer();
1411         writeLoggedFlushedResponse(responseString);
1412     }
1413 
1414     /***
1415      * Handler method called upon receipt of a VRFY command.
1416      * This method informs the client that the command is
1417      * not implemented.
1418      *
1419      * @param argument the argument passed in with the command by the SMTP client
1420      */
1421     private void doVRFY(String argument) {
1422         String responseString = "502 VRFY is not supported";
1423         writeLoggedFlushedResponse(responseString);
1424     }
1425 
1426     /***
1427      * This method parses SMTP commands read off the wire in handleConnection.
1428      * Actual processing of the command (possibly including additional back and
1429      * forth communication with the client) is delegated to one of a number of
1430      * command specific handler methods.  The primary purpose of this method is
1431      * to parse the raw command string to determine exactly which handler should
1432      * be called.  It returns true if expecting additional commands, false otherwise.
1433      *
1434      * @param rawCommand the raw command string passed in over the socket
1435      *
1436      * @return whether additional commands are expected.
1437      */
1438     private boolean parseCommand(String rawCommand) throws Exception {
1439         
1440 		
1441         
1442         String argument = null;
1443         boolean returnValue = true;
1444         String command = rawCommand;
1445 
1446 		//System.out.println("Command received: " + command);
1447 
1448         if (rawCommand == null) {
1449             return false;
1450         }
1451 
1452         /*if ((state.get(MESG_FAILED) == null) && (getLogger().isDebugEnabled())) {
1453             getLogger().debug("Command received: " + command);
1454         }*/
1455         
1456 
1457         int spaceIndex = command.indexOf(" ");
1458 
1459         if (spaceIndex > 0) {
1460             argument = command.substring(spaceIndex + 1);
1461             command = command.substring(0, spaceIndex);
1462         }
1463 
1464         command = command.toUpperCase(Locale.US);
1465 
1466         if (command.equals(COMMAND_HELO)) {
1467             doHELO(argument);
1468         } else if (command.equals(COMMAND_EHLO)) {
1469             doEHLO(argument);
1470         } else if (command.equals(COMMAND_AUTH)) {
1471             doAUTH(argument);
1472         } else if (command.equals(COMMAND_MAIL)) {
1473             doMAIL(argument);
1474         } else if (command.equals(COMMAND_RCPT)) {
1475             doRCPT(argument);
1476         } else if (command.equals(COMMAND_NOOP)) {
1477             doNOOP(argument);
1478         } else if (command.equals(COMMAND_RSET)) {
1479             doRSET(argument);
1480         } else if (command.equals(COMMAND_DATA)) {
1481             doDATA(argument);
1482         } else if (command.equals(COMMAND_QUIT)) {
1483             doQUIT(argument);
1484             returnValue = false;
1485         } else if (command.equals(COMMAND_VRFY)) {
1486             doVRFY(argument);
1487         } else if (command.equals(COMMAND_EXPN)) {
1488             doEXPN(argument);
1489         } else if (command.equals(COMMAND_HELP)) {
1490             doHELP(argument);
1491         } else {
1492             if (state.get(MESG_FAILED) == null) {
1493                 doUnknownCmd(command, argument);
1494             }
1495         }
1496 
1497         return returnValue;
1498     }
1499 	
1500 	/*** processes the full mail including the headers */
1501 	private void processFullMail(InputStream msgIn) throws MessagingException{
1502 		setMessage(new MimeMessage(currentSession, msgIn));
1503 
1504 		//setRawMessage(new String(fullMessage));
1505 		//if ((getRawMessage() != null) && (getMessage() != null)){
1506 			FilterManager manager = new FilterManager(getRawMessage(), getMessage());
1507 			manager.start();
1508 		//}
1509 	}
1510 	
1511     /***
1512      * Processes the mail message coming in off the wire.  Reads the
1513      * content and delivers to the spool.
1514      *
1515      * @param headers the headers of the mail being read
1516      * @param msgIn the stream containing the message content
1517      */
1518     private void processMail(MailHeaders headers, InputStream msgIn)
1519         throws MessagingException {
1520         ByteArrayInputStream headersIn = null;
1521         //MailImpl mail = null;
1522         List recipientCollection = null;
1523         
1524 		
1525 	
1526 		
1527 		/*
1528         try {
1529 			headersIn = new ByteArrayInputStream(headers.toByteArray());
1530             recipientCollection = (List) state.get(RCPT_LIST);
1531             mail = new MailImpl(theConfigData.getMailServer().getId(),
1532                     (MailAddress) state.get(SENDER), recipientCollection,
1533                     new SequenceInputStream(headersIn, msgIn)); 
1534 			
1535 			
1536 
1537             // Call mail.getSize() to force the message to be
1538             // loaded. Need to do this to enforce the size limit
1539             //if (theConfigData.getMaxMessageSize() > 0) {
1540 			if (preferences.getLong("maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE) > 0) {
1541                 mail.getMessageSize();
1542             }
1543 
1544             mail.setRemoteHost(remoteHost);
1545             mail.setRemoteAddr(remoteIP);
1546             
1547             
1548             theConfigData.getMailServer().sendMail(mail);
1549 
1550             Collection theRecipients = mail.getRecipients();
1551             String recipientString = "";
1552 
1553             if (theRecipients != null) {
1554                 recipientString = theRecipients.toString();
1555             }
1556             
1557             if (getLogger().isDebugEnabled()) {
1558                 StringBuffer debugBuffer = new StringBuffer(256).append(
1559                         "Successfully spooled mail )").append(mail.getName())
1560                                                                 .append(" from ")
1561                                                                 .append(mail
1562                         .getSender()).append(" for ").append(recipientString);
1563                 getLogger().debug(debugBuffer.toString());
1564             } else if (getLogger().isInfoEnabled()) {
1565                 StringBuffer infoBuffer = new StringBuffer(256).append(
1566                         "Successfully spooled mail from ")
1567                                                                .append(mail
1568                         .getSender()).append(" for ").append(recipientString);
1569                 getLogger().info(infoBuffer.toString());
1570             } 
1571         } finally {
1572             
1573             if (recipientCollection != null) {
1574                 recipientCollection.clear();
1575             }
1576 
1577             recipientCollection = null;
1578 
1579             if (mail != null) {
1580                 mail.dispose();
1581             }
1582 
1583             mail = null;
1584 
1585             if (headersIn != null) {
1586                 try {
1587                     headersIn.close();
1588                 } catch (IOException ioe) {
1589                     // Ignore exception on close.
1590                 }
1591             }
1592 
1593             headersIn = null;
1594             
1595         }
1596         */
1597         
1598         
1599     }
1600 
1601     /***
1602      * DOCUMENT ME!
1603      *
1604      * @param headers DOCUMENT ME!
1605      *
1606      * @return DOCUMENT ME!
1607      *
1608      * @throws MessagingException DOCUMENT ME!
1609      */
1610     private MailHeaders processMailHeaders(MailHeaders headers)
1611         throws MessagingException {
1612         	
1613         if (message == null){
1614         	message = new MimeMessage(currentSession);	
1615         }
1616         	
1617         // If headers do not contains minimum REQUIRED headers fields,
1618         // add them
1619         if (!headers.isSet(RFC2822Headers.DATE)) {
1620             headers.setHeader(RFC2822Headers.DATE,
1621                 rfc822DateFormat.format(new Date()));
1622                 
1623 
1624         }
1625 
1626         if (!headers.isSet(RFC2822Headers.FROM) && (state.get(SENDER) != null)) {
1627             headers.setHeader(RFC2822Headers.FROM, state.get(SENDER).toString());
1628         }
1629 
1630         // Determine the Return-Path
1631         String returnPath = headers.getHeader(RFC2822Headers.RETURN_PATH, "\r\n");
1632         headers.removeHeader(RFC2822Headers.RETURN_PATH);
1633 
1634         StringBuffer headerLineBuffer = new StringBuffer(512);
1635 
1636         if (returnPath == null) {
1637             if (state.get(SENDER) == null) {
1638                 returnPath = "<>";
1639             } else {
1640                 headerLineBuffer.append("<").append(state.get(SENDER)).append(">");
1641                 returnPath = headerLineBuffer.toString();
1642                 headerLineBuffer.delete(0, headerLineBuffer.length());
1643             }
1644         }
1645 
1646         // We will rebuild the header object to put Return-Path and our
1647         // Received header at the top
1648         Enumeration headerLines = headers.getAllHeaderLines();
1649         MailHeaders newHeaders = new MailHeaders();
1650 
1651         // Put the Return-Path first
1652         newHeaders.addHeaderLine(RFC2822Headers.RETURN_PATH + ": " + returnPath);
1653 
1654         // Put our Received header next
1655         headerLineBuffer.append(RFC2822Headers.RECEIVED + ": from ")
1656                         .append(remoteHost).append(" ([").append(remoteIP)
1657                         .append("])");
1658 
1659         newHeaders.addHeaderLine(headerLineBuffer.toString());
1660 		message.addHeaderLine(headerLineBuffer.toString());
1661         headerLineBuffer.delete(0, headerLineBuffer.length());
1662 
1663         headerLineBuffer.append("          by ")
1664                         //.append(theConfigData.getHelloName()).append(" (")
1665 						.append(preferences.get("heloName", DEFAULT_HELO_NAME)).append(" (")
1666                         .append(SOFTWARE_TYPE).append(") with SMTP ID ").append(smtpID);
1667 
1668         if (((Collection) state.get(RCPT_LIST)).size() == 1) {
1669             // Only indicate a recipient if they're the only recipient
1670             // (prevents email address harvesting and large headers in
1671             //  bulk email)
1672             newHeaders.addHeaderLine(headerLineBuffer.toString());
1673 			message.addHeaderLine(headerLineBuffer.toString());
1674             headerLineBuffer.delete(0, headerLineBuffer.length());
1675             headerLineBuffer.append("          for <")
1676                             .append(((List) state.get(RCPT_LIST)).get(0)
1677                                      .toString()).append(">;");
1678             newHeaders.addHeaderLine(headerLineBuffer.toString());
1679 			message.addHeaderLine(headerLineBuffer.toString());
1680             headerLineBuffer.delete(0, headerLineBuffer.length());
1681         } else {
1682             // Put the ; on the end of the 'by' line
1683             headerLineBuffer.append(";");
1684             newHeaders.addHeaderLine(headerLineBuffer.toString());
1685 			message.addHeaderLine(headerLineBuffer.toString());
1686             headerLineBuffer.delete(0, headerLineBuffer.length());
1687         }
1688 
1689         headerLineBuffer = null;
1690         newHeaders.addHeaderLine("          "
1691             + rfc822DateFormat.format(new Date()));
1692 		message.addHeaderLine("          "
1693 			+ rfc822DateFormat.format(new Date()));
1694 
1695         // Add all the original message headers back in next
1696         while (headerLines.hasMoreElements()) {
1697             newHeaders.addHeaderLine((String) headerLines.nextElement());
1698 			message.addHeaderLine((String) headerLines.nextElement());
1699         }
1700 
1701         return newHeaders;
1702     }
1703 
1704     /***
1705      * Resets the handler data to a basic state.
1706      */
1707     private void resetHandler() {
1708         resetState();
1709 
1710         clearResponseBuffer();
1711         in = null;
1712         inReader = null;
1713         out = null;
1714         remoteHost = null;
1715         remoteIP = null;
1716         authenticatedUser = null;
1717         smtpID = null;
1718 
1719         if (theWatchdog != null) {
1720             /*if (theWatchdog instanceof Disposable) {
1721                 ((Disposable) theWatchdog).dispose();
1722             }*/
1723 
1724             theWatchdog = null;
1725         }
1726 
1727         try {
1728             if (socket != null) {
1729                 socket.close();
1730             }
1731         } catch (IOException e) {
1732             /*if (getLogger().isErrorEnabled()) {
1733                 getLogger().error("Exception closing socket: " + e.getMessage());
1734             }*/
1735         } finally {
1736             socket = null;
1737         }
1738 
1739         synchronized (this) {
1740             handlerThread = null;
1741         }
1742     }
1743 
1744     /***
1745      * Resets message-specific, but not authenticated user, state.
1746      *
1747      */
1748     private void resetState() {
1749         ArrayList recipients = (ArrayList) state.get(RCPT_LIST);
1750 
1751         if (recipients != null) {
1752             recipients.clear();
1753         }
1754 
1755         state.clear();
1756     }
1757     
1758     
1759     
1760     
1761 	/*** Reads a single message from the given reader, halting when the
1762 	   sequence CRLF-dot-CRLF is received, or the maximum message length
1763 	   is reached.
1764 	   @param reader      the reader
1765 	   @return            the message
1766 	   @exception IOException if any other IOException occurs
1767 	 */
1768 	private String readMessage(BufferedReader reader) throws IOException {
1769 		StringBuffer buffer = new StringBuffer();
1770 	
1771 		boolean done = false;
1772 	
1773 		while (!done) {
1774 			// Read a line and add it to the buffer
1775 			String line = readLine(reader,
1776 					preferences.getInt("maxLineLength", 1000));
1777 	
1778 			if (line.equals(".\r\n")
1779 					|| (buffer.length() > preferences.getInt("maxLineLength",
1780 						1000))) {
1781 				// end of buffer
1782 				done = true;
1783 			} else if (line.startsWith(".")) {
1784 				buffer.append(line.substring(1));
1785 			} else {
1786 				buffer.append(line);
1787 			}
1788 		}
1789 	
1790 		return (buffer.toString());
1791 	}
1792     
1793     
1794 	/*** Reads a single line from the given reader, including EOL
1795 	   (\r\n); truncates the line at maxLength characters.
1796 	   @param reader        the reader
1797 	   @param maxLength     maximum length of the line
1798 	   @return              the line
1799 	   @exception EOFException if an EOF is read
1800 	   @exception IOException if any other IOException occurs
1801 	 */
1802 	private String readLine(Reader reader, int maxLength)
1803 		throws IOException {
1804 		StringBuffer buffer = new StringBuffer();
1805 		short prev;
1806 		short curr = 0;
1807 
1808 		do {
1809 			prev = curr;
1810 			curr = (short) reader.read();
1811 
1812 			if (curr == -1) {
1813 				throw new EOFException();
1814 			}
1815 
1816 			if (buffer.length() <= maxLength) {
1817 				buffer.append((char) curr);
1818 			}
1819 		} while (!((prev == '\r') && (curr == '\n')));
1820 
1821 		//System.out.println("ConnectionHandler.readLine()_read this: " + buffer.toString());
1822 		return (buffer.toString());
1823 	}
1824     
1825     
1826     
1827     
1828     
1829     
1830 
1831     //~ Inner Classes ----------------------------------------------------------
1832 
1833     /***
1834      * A private inner class which serves as an adaptor
1835      * between the WatchdogTarget interface and this
1836      * handler class.
1837      */
1838     private class SMTPWatchdogTarget implements WatchdogTarget {
1839         //~ Methods ------------------------------------------------------------
1840 
1841         /***
1842          * @see org.apache.james.util.watchdog.WatchdogTarget#execute()
1843          */
1844         public void execute() {
1845             SMTPHandler.this.idleClose();
1846         }
1847     }
1848     
1849     public void run(){
1850     	
1851     	try {
1852 			handleConnection();
1853 		} catch (IOException e) {
1854 			// log error
1855 			System.out.println("SMTPHandler.run(): " + e);
1856 		}
1857     }
1858     
1859 	
1860 	/***
1861 	 * Returns the rawMessage.
1862 	 * @return String
1863 	 */
1864 	public String getRawMessage() {
1865 		return rawMessage;
1866 	}
1867 
1868 	/***
1869 	 * Sets the rawMessage.
1870 	 * @param rawMessage The rawMessage to set
1871 	 */
1872 	public void setRawMessage(String rawMessage) {
1873 		this.rawMessage = rawMessage;
1874 	}
1875 
1876 	/***
1877 	 * Returns the message.
1878 	 * @return MimeMessage
1879 	 */
1880 	public MimeMessage getMessage() {
1881 		return message;
1882 	}
1883 
1884 	/***
1885 	 * Sets the message.
1886 	 * @param message The message to set
1887 	 */
1888 	public void setMessage(MimeMessage message) {
1889 		this.message = message;
1890 	}
1891 	
1892 	/*** extracts an e-mail address from a String 
1893 	 * 
1894 	 * @param addressString the string you wish to extract the e-mail address
1895 	 * from
1896 	 * @return String
1897 	 */
1898 	public static String extractEmailAddress(String addressString){
1899 		if (addressString.indexOf("<") > -1){
1900 			addressString = addressString.substring(addressString.indexOf("<")+1, addressString.lastIndexOf(">"));
1901 			if (addressString.indexOf("<") > -1){
1902 				addressString = extractEmailAddress(addressString);
1903 			}
1904 		}
1905 		
1906 		return addressString;
1907 	}
1908 	
1909 	
1910 
1911 	
1912 	
1913 
1914 }
1915 
This page was automatically generated by Maven