OSDN Git Service

Support: [ #25090 ] TLS/SSL SMTP server connection suuport. Ph.1
[mdc/BetaProject.git] / src / org / jent / checksmtp / Processer.java
1 package org.jent.checksmtp;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.io.OutputStream;
8
9 /*
10  * Socket accept proceed
11  */
12 import java.io.OutputStreamWriter;
13 import java.io.PrintWriter;
14
15 import java.net.Socket;
16 import java.security.KeyManagementException;
17 import java.security.NoSuchAlgorithmException;
18
19 import java.util.ArrayList;
20 import java.util.ListIterator;
21 import java.util.regex.Pattern;
22 import javax.net.SocketFactory;
23 import javax.net.ssl.SSLContext;
24 import javax.net.ssl.TrustManager;
25 import org.jent.checksmtp.ssl.RespondingX509TrustManager;
26
27
28 public class Processer implements Runnable, ResultNotify {
29   private Socket client;
30
31   //Conform status.
32   private final int RESULT_UNKNOWN = 0;
33   private final int RESULT_OK = 1;
34   private final int RESULT_NG = 2;
35   private int result = RESULT_UNKNOWN;
36
37   //SMTP result code
38   private final int R354StartInput = 354;
39   private final int R221ServiceClosing = 221;
40   private final int R451RequestedActionAbort = 451;
41   private final int R502CommandNotImplemented = 502;
42   
43   //private final int R500SyntexErrorInCommand = 500;
44   //private final int R501SyntaxErrorInArguments = 501;
45   //private final int R503BadSequenceOfCommands = 503:
46   //private final int R504CommandParameterNotImplemented = 504:
47   //private final int R211SystemStatusRepl = 211;
48   //private final int R214HelpMessage = 214;
49   //private final int R220ServiceReady = 220;
50   //private final int R421ClosingTransmissionChannel = 421;
51   //private final int R250RequestedActionCompleted = 250;
52   //private final int R251UserNotLocal = 251;
53   //private final int R252CannotVRFYuser = 252;
54   //private final int R450RequestedMailActionNotTaken = 450;
55   //private final int R550RequestedActionNotTaken = 550;
56   //private final int R551UserNotLocal = 551;
57   //private final int R452RequestedActionNotTaken = 452;
58   //private final int R552RequestedMailActionAborted = 552;
59   //private final int R553RequestedActionNotTaken = 553;
60   //private final int R554TransactionFailed = 554;
61   
62   //SMTP command
63   private final String COMMAND_RCPT_TO = "RCPT TO:"; // NOI18N
64   private final String COMMAND_DATA = "DATA"; // NOI18N
65   private final String COMMAND_RESET = "RSET"; // NOI18N
66   private final String COMMAND_TURN = "TURN"; // NOI18N
67
68   private final String COMMAND_NOOP = "NOOP";
69   //private final String COMAMND_QUIT = "QUIT";
70   
71   //private final String COMMAND_EXHELLO = "EHLO";
72   //private final String COMMAND_HELLO = "HELO";
73   //private final String COMMAND_MAIL = "MAIL FROM:";
74   //private final String COMMAND_VERIFY = "VRFY";
75   //private final String COMMAND_EXPAND = "EXPN";
76   //private final String COMMAND_HELP = "HELP";
77   public Processer(Socket client) {
78       this.client = client;
79   }
80
81   public void run() {
82     InputStream serverInput;
83     OutputStream serverOutput;
84     InputStream clientInput;
85     OutputStream clientOutput;
86
87     BufferedReader serverReader;
88     PrintWriter serverWriter;
89     BufferedReader clientReader;
90     PrintWriter clientWriter;
91
92     SocketFactory socketFactory = null;
93     Socket server = null;
94       
95     try {
96       //Connect to SMTP Server host 
97       String servername = ApplicationProperties.getSmtpServerHost();
98
99       //SMTP Server port 
100       int serverport = ApplicationProperties.getSmtpServerPort();
101
102       if ( ApplicationProperties.getSmtpServerSSL() ) {
103         //SMTP Server use SSL
104         System.out.println("SMTP Server use SSL.");
105         //socketFactory = SSLSocketFactory.getDefault();
106         //OreOre server connection support TrustManager setting.
107         try {
108           SSLContext sslContext = SSLContext.getInstance("TLS"); //SSL,TLS,TLSv1.1
109           sslContext.init(null,
110                 new TrustManager[]{ new RespondingX509TrustManager()  }, null);
111           socketFactory = sslContext.getSocketFactory();
112         } catch (NoSuchAlgorithmException nsaEx ) {//from SSLContext.getDefault()
113           //TODO: Error dialog
114           nsaEx.printStackTrace(System.err);
115         } catch (KeyManagementException kmEx) {//from SSLContext.init()
116           //TODO: Error dialog
117           kmEx.printStackTrace(System.err);
118         } catch (IllegalStateException isEx) {//from SSLContext.getSocketFactory()
119           //TODO: Error dialog
120           isEx.printStackTrace(System.err);
121         } catch (Exception ex) { //for new RespondingX506TrustManager()
122           //TODO: Error dialog
123           ex.printStackTrace(System.err);
124         }
125       } else {
126         //SMTP Server is normal Socket.
127         socketFactory = SocketFactory.getDefault();
128       }
129
130       //Connection to true SMTP server.
131       server = socketFactory.createSocket(servername, serverport);
132       serverInput = server.getInputStream();
133       serverOutput = server.getOutputStream();
134       clientInput = client.getInputStream();
135       clientOutput = client.getOutputStream();
136
137       serverReader = new BufferedReader(new InputStreamReader(
138                   server.getInputStream()));
139       serverWriter = new PrintWriter(new SmtpBufferedWriter(
140                   new OutputStreamWriter(server.getOutputStream(),
141                       "ISO-8859-1")), true); // NOI18N
142       clientReader = new BufferedReader(new InputStreamReader(
143                   client.getInputStream()));
144       clientWriter = new PrintWriter(new SmtpBufferedWriter(
145                   new OutputStreamWriter(client.getOutputStream(),
146                       "ISO-8859-1")), true); // NOI18N
147
148       smtpStart(serverReader, clientWriter, clientReader, serverWriter);
149
150       //for SMTP Server connection test
151       //SocketProxy forServer = new SocketProxy(clientInput, serverOutput, 0);
152       //SocketProxy forClient = new SocketProxy(serverInput, clientOutput, 1);
153       //Thread forServerThread = new Thread(forServer);
154       //Thread forClientThread = new Thread(forClient);
155       //forServerThread.start();
156       ////Use this ThreadforClientThread.run();
157       server.close();
158       server = null;
159       client.close();
160       client = null;
161     } catch (IOException e) {
162       String errorMessage = java.util.ResourceBundle.getBundle("org/jent/checksmtp/Bundle").getString("Processer.error.Execption_occurred_at_starting_SMTP_thread.");
163       System.err.println(errorMessage);
164       new MessageDialogUI(errorMessage, e, MessageDialogUI.ERROR_MODE);
165     } catch (RuntimeException runEx) {
166       new MessageDialogUI(java.util.ResourceBundle.getBundle("org/jent/checksmtp/Bundle").getString("Processer.error.Runtime_Exception_occurred_in_SMTP_parse"),
167               runEx, MessageDialogUI.ERROR_MODE);
168     } finally {
169       //for failsafe Socket close.
170       if (server != null) { 
171         try { 
172           server.close();
173         } catch (IOException ioEx ) {
174           //IGNORE close Exception
175         }
176       }
177       if (client != null) { 
178         try {
179           client.close();
180         } catch (IOException ioEx ) {
181           //IGNORE close Exception
182         }
183       }
184       //LDAP Connection close.
185       LDAPSearch.close();
186     }
187   }
188
189   private int getCode(String s) {
190     int code = 0;
191     try {
192       code = Integer.parseInt(s.substring(0, 3));
193     } catch (NumberFormatException nfEx) {
194       code = -1;  //Unexpected: SMTP CODE is allway 3digits.
195     }
196     return code;
197   }
198
199   private boolean isCompliteCode(int code) {
200     return code >=200 && code<=299 ;
201   }
202   
203   private boolean isErrorCode(int code) {
204     return code>=400 && code<=599;
205   }  
206   
207   private boolean isContinue(String s) {
208       if ('-' == s.charAt(3)) {
209           return true;
210       }
211
212       return false;
213   }
214
215   private boolean isRcptTo(String s) {
216       if (s.startsWith(COMMAND_RCPT_TO)) {
217           return true;
218       }
219
220       return false;
221   }
222
223   private boolean isData(String s) {
224       if (s.startsWith(COMMAND_DATA)) {
225           return true;
226       }
227
228       return false;
229   }
230
231   private boolean isTurn(String s) {
232       if (s.startsWith(COMMAND_TURN)) {
233           return true;
234       }
235
236       return false;
237   }
238
239   public void sayOK() {
240       result = RESULT_OK;
241       notifyResult();
242   }
243
244   public void sayNG() {
245       result = RESULT_NG;
246       notifyResult();
247   }
248
249   private synchronized void notifyResult() {
250       notify();
251   }
252
253   private void smtpStart(BufferedReader serverReader,
254     PrintWriter clientWriter, BufferedReader clientReader,
255     PrintWriter serverWriter) {
256     String line;
257
258     try {
259       ArrayList toList = new ArrayList();
260
261       while (true) {
262         //Server responce
263         line = serverReader.readLine();
264         clientWriter.println(line);
265         System.out.println(line);
266
267         if (isContinue(line)) {
268           continue; //Server responce continue
269         }
270
271         //Client request
272         if (R221ServiceClosing == getCode(line)) {
273           break; //end of session.
274         } else if (R354StartInput == getCode(line)) {
275           System.out.println("Send mail data.");
276
277           while (true) {
278             line = clientReader.readLine();
279             serverWriter.println(line);
280
281             if (line.equals(".")) { // NOI18N
282               break; //end of mail dara.
283             }
284           }
285         } else {
286           while (true) {
287             line = clientReader.readLine();
288
289             if (line == null) {
290               //Finish client stream.
291               System.err.println("Client disconnected.");
292               break; //force disconnect.
293             }
294
295             if (isRcptTo(line)) {
296               //stored To: address.
297               toList.add(formatToAddress(line));
298             } else if (isTurn(line)) {
299               System.err.println("'TURN' is unsupported command.");
300               clientWriter.println(R502CommandNotImplemented);
301
302               continue; //read next client request.
303             } else if (isData(line)) {
304               //Client want to send data. Check out toList.
305               ListIterator iterater = toList.listIterator();
306
307               while (iterater.hasNext()) {
308                 System.out.println("ADDRESS CHECK:" + iterater.next());
309               }
310
311               //checkout toList
312               result = RESULT_UNKNOWN; //refresh RESULT flag.
313               new ToListUI(this, toList);
314
315               while (result == RESULT_UNKNOWN) {
316                 try {
317                     synchronized (this) {
318                         wait(4000); //TODO: Configurable NOOP time
319                         //Dummy SMTP server access while ToListUI dialog wait.
320                         serverWriter.println(COMMAND_NOOP);
321                         String serverReply = serverReader.readLine();
322                         System.out.println(COMMAND_NOOP + ":ANSER is " + serverReply);
323                         //Server MUST retrun "250 OK" by NOOP command.
324                         if ( isErrorCode(getCode(serverReply)) ) {
325                           System.err.println("Fatal Error. Server return error code by NOOP command.");
326                           //Ignore Fatal Error.
327                         }
328                     }
329                 } catch (InterruptedException e) {
330                     System.err.println("Confirm dialog wait interrupted");
331                     e.printStackTrace();
332                 }
333               }
334               toList = new ArrayList(); //refresh TO address list.
335               
336               if (result != RESULT_OK ) {
337                 System.out.println("CANCEL sending mail.");
338                 serverWriter.println(COMMAND_RESET);
339                 line = serverReader.readLine(); //Server MUST retrun "250 OK"
340                 System.out.println(COMMAND_RESET + ":ANSER is " + line);
341                 if ( isErrorCode(getCode(line)) ) {
342                   System.err.println("Fatal Error. Server return error code by RESET command.");
343                   //Ignore Fatal Error.
344                 }
345                 clientWriter.println(R451RequestedActionAbort);
346
347                 continue; //I think Client QUIT or Retry. 
348               }
349             }
350
351             break; // client read while(true);
352           }
353
354           if (line == null ) {
355             break;
356           }
357           serverWriter.println(line);
358           System.out.println(line);
359         }
360       }
361     } catch (IOException e) {
362       String errorMessage = java.util.ResourceBundle.getBundle("org/jent/checksmtp/Bundle").getString("Processer.error.Error_occurred_in_SMTP_parse.");
363       System.err.println(errorMessage);
364       new MessageDialogUI(errorMessage, e, MessageDialogUI.ERROR_MODE);
365     } catch (RuntimeException runEx) {
366       new MessageDialogUI(java.util.ResourceBundle.getBundle("org/jent/checksmtp/Bundle").getString("Processer.error.Runtime_Exception_occurred_in_SMTP_parse"),
367               runEx, MessageDialogUI.ERROR_MODE);
368     }
369
370     System.out.println("End of Session.");
371   }
372
373   private String formatToAddress(String line) {
374     System.out.println("FIND To: " + line);
375
376     //Splitting Mail address.
377     String str = line.substring(COMMAND_RCPT_TO.length() + 1);
378     Pattern pattern = Pattern.compile("[< >]+"); // NOI18N
379     String[] address = pattern.split(str); // 0<1>2 3
380     int i;
381
382     for (i = 0; i < address.length; i++) {
383       if (!address[i].equals("")) { // NOI18N
384         break;
385       }
386     }
387
388     //Search LDAP
389     str = address[i] + " " + LDAPSearch.search(address[i]); // NOI18N
390     return str;
391   }
392 }