KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > mail > smtp > SMTPServer


1 // The contents of this file are subject to the Mozilla Public License Version
2
// 1.1
3
//(the "License"); you may not use this file except in compliance with the
4
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
5
//
6
//Software distributed under the License is distributed on an "AS IS" basis,
7
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8
//for the specific language governing rights and
9
//limitations under the License.
10
//
11
//The Original Code is "The Columba Project"
12
//
13
//The Initial Developers of the Original Code are Frederik Dietz and Timo
14
// Stich.
15
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
16
//
17
//All Rights Reserved.
18

19 package org.columba.mail.smtp;
20
21 import java.io.IOException JavaDoc;
22 import java.net.InetAddress JavaDoc;
23 import java.text.MessageFormat JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Observable JavaDoc;
29 import java.util.Observer JavaDoc;
30
31 import javax.swing.JOptionPane JavaDoc;
32
33 import org.columba.api.command.IWorkerStatusController;
34 import org.columba.core.base.Blowfish;
35 import org.columba.core.command.CommandCancelledException;
36 import org.columba.core.command.ProgressObservedInputStream;
37 import org.columba.core.gui.base.MultiLineLabel;
38 import org.columba.core.gui.frame.FrameManager;
39 import org.columba.mail.composer.SendableMessage;
40 import org.columba.mail.config.AccountItem;
41 import org.columba.mail.config.Identity;
42 import org.columba.mail.config.ImapItem;
43 import org.columba.mail.config.OutgoingItem;
44 import org.columba.mail.config.PopItem;
45 import org.columba.mail.gui.util.PasswordDialog;
46 import org.columba.mail.pop3.POP3Store;
47 import org.columba.mail.util.AuthenticationManager;
48 import org.columba.mail.util.AuthenticationSecurityComparator;
49 import org.columba.mail.util.MailResourceLoader;
50 import org.columba.ristretto.auth.AuthenticationException;
51 import org.columba.ristretto.auth.AuthenticationFactory;
52 import org.columba.ristretto.message.Address;
53 import org.columba.ristretto.parser.ParserException;
54 import org.columba.ristretto.pop3.POP3Exception;
55 import org.columba.ristretto.smtp.SMTPException;
56 import org.columba.ristretto.smtp.SMTPProtocol;
57
58 /**
59  *
60  * SMTPServer makes use of <class>SMTPProtocol </class> to add a higher
61  * abstraction layer for sending messages.
62  *
63  * It takes care of authentication all the details.
64  *
65  * To send a message just create a <class>SendableMessage </class> object and
66  * use <method>sendMessage </method>.
67  *
68  * @author fdietz, Timo Stich <tstich@users.sourceforge.net>
69  *
70  */

71 public class SMTPServer implements Observer JavaDoc {
72
73     private String JavaDoc[] capas;
74
75     protected SMTPProtocol protocol;
76
77     protected OutgoingItem smtpItem;
78
79     protected Identity identity;
80
81     protected String JavaDoc fromAddress;
82
83     private boolean usingSSL;
84
85     private AccountItem accountItem;
86
87     /**
88      * Constructor for SMTPServer.
89      */

90     public SMTPServer(AccountItem accountItem) {
91         super();
92
93         this.accountItem = accountItem;
94         identity = accountItem.getIdentity();
95
96         // initialise protocol layer
97
smtpItem = accountItem.getSmtpItem();
98
99         smtpItem.getRoot().addObserver(this);
100         protocol = new SMTPProtocol(smtpItem.get("host"), smtpItem.getInteger("port"));
101     }
102
103     private void ensureConnected() throws IOException JavaDoc, SMTPException, CommandCancelledException {
104         if (protocol.getState() == SMTPProtocol.NOT_CONNECTED) {
105             // Start login procedure
106
protocol.openPort();
107
108             initialize();
109
110             doSSL();
111         }
112     }
113
114     /**
115      * Open connection to SMTP server and login if needed.
116      *
117      * @return true if connection was successful, false otherwise
118      */

119     private void ensureAuthenticated() throws IOException JavaDoc, SMTPException,
120             CommandCancelledException {
121         String JavaDoc username;
122         char[] password = new char[0];
123         boolean savePassword;
124
125         // Init Values
126
// user's email address
127
fromAddress = identity.getAddress().getMailAddress();
128
129
130
131         usingSSL = smtpItem.getBoolean("enable_ssl");
132         int authMethod = getLoginMethod();
133
134         boolean authenticated = (authMethod == AuthenticationManager.NONE);
135
136         if (authMethod == AuthenticationManager.POP_BEFORE_SMTP) {
137             // no esmtp - use POP3-before-SMTP instead
138
try {
139                 pop3Authentification();
140             } catch (POP3Exception e) {
141                 throw new SMTPException(e);
142             }
143
144             authenticated = true;
145         }
146
147         ensureConnected();
148
149
150         if (!authenticated) {
151             username = smtpItem.get("user");
152             password = Blowfish.decrypt(smtpItem.getRoot().getAttribute("password", ""));
153             savePassword = smtpItem.getBoolean("save_password");
154
155             if (username.length() == 0) {
156                 // there seems to be no username set in the smtp-options
157
// -> use username from pop3 or imap options
158
if (accountItem.isPopAccount()) {
159                     PopItem pop3Item = accountItem.getPopItem();
160                     username = pop3Item.get("user");
161                 } else {
162                     ImapItem imapItem = accountItem.getImapItem();
163                     username = imapItem.get("user");
164                 }
165             }
166
167             PasswordDialog passDialog = new PasswordDialog();
168
169             // ask password from user
170
if (password.length == 0) {
171                 passDialog.showDialog(MessageFormat.format(MailResourceLoader
172                         .getString("dialog", "password", "enter_password"),
173                         new Object JavaDoc[] { username,
174                                 smtpItem.get("host") }),
175                         new String JavaDoc(password), savePassword);
176
177                 if (passDialog.success()) {
178                     password = passDialog.getPassword();
179                     savePassword = passDialog.getSave();
180                 } else {
181                     throw new CommandCancelledException();
182                 }
183             }
184
185             // try to authenticate
186
while (!authenticated) {
187                 try {
188                     try {
189                         protocol.auth(AuthenticationManager
190                                 .getSaslName(authMethod), username, password);
191                         authenticated = true;
192                     } catch (AuthenticationException e) {
193                         // If the cause is a SMTPExcpetion then only password
194
// wrong
195
// else bogus authentication mechanism
196
if (e.getCause() instanceof SMTPException) {
197                             int errorCode = ((SMTPException) e.getCause()).getCode();
198
199                             // Authentication is not supported
200
if( errorCode == 504 ) {
201                                 //TODO: Add dialog to inform user that the smtp server
202
// does not support authentication
203
JOptionPane
204                                 .showMessageDialog(
205                                         FrameManager.getInstance()
206                                                 .getActiveFrame(),
207                                         new MultiLineLabel(
208                                                 MailResourceLoader
209                                                                 .getString(
210                                                                         "dialog",
211                                                                         "error",
212                                                                         "authentication_not_supported")),
213                                         MailResourceLoader.getString("dialog",
214                                                 "error",
215                                                 "authentication_process_error"),
216                                         JOptionPane.INFORMATION_MESSAGE);
217
218                                 //Turn off authentication for the future
219
smtpItem.setString("login_method", Integer
220                                         .toString(AuthenticationManager.NONE));
221
222                                 return;
223                             }
224
225                         } else {
226                             throw (SMTPException) e.getCause();
227                         }
228
229                         // Some error in the client/server communication
230
// --> fall back to default login process
231
int result = JOptionPane
232                                 .showConfirmDialog(
233                                         FrameManager.getInstance()
234                                                 .getActiveFrame(),
235                                         new MultiLineLabel(
236                                                 e.getMessage()
237                                                         + "\n"
238                                                         + MailResourceLoader
239                                                                 .getString(
240                                                                         "dialog",
241                                                                         "error",
242                                                                         "authentication_fallback_to_default")),
243                                         MailResourceLoader.getString("dialog",
244                                                 "error",
245                                                 "authentication_process_error"),
246                                         JOptionPane.OK_CANCEL_OPTION);
247
248                         if (result == JOptionPane.OK_OPTION) {
249                             authMethod = AuthenticationManager.SASL_PLAIN;
250                             smtpItem.setString("login_method", Integer
251                                     .toString(authMethod));
252                         } else {
253                             throw new CommandCancelledException();
254                         }
255
256                     }
257                 } catch (SMTPException e) {
258                     passDialog.showDialog(MessageFormat.format(
259                             MailResourceLoader.getString("dialog", "password",
260                                     "enter_password"), new Object JavaDoc[] { username,
261                                     smtpItem.get("host") }), new String JavaDoc(
262                             password), savePassword);
263
264                     if (!passDialog.success()) {
265                         throw new CommandCancelledException();
266                     } else {
267                         password = passDialog.getPassword();
268                         savePassword = passDialog.getSave();
269                     }
270                 }
271             }
272
273             // authentication was successful
274
// -> save name/password
275
smtpItem.setString("user", username);
276             smtpItem.setBoolean("save_password", savePassword);
277             if (savePassword) {
278                 smtpItem.setString("password", Blowfish.encrypt(password));
279             }
280         }
281     }
282
283     /**
284      * @param smtpItem
285      * @throws CommandCancelledException
286      * @throws IOException
287      * @throws SMTPException
288      */

289     private void doSSL() throws CommandCancelledException, IOException JavaDoc, SMTPException {
290         if (smtpItem.getBoolean("enable_ssl")) {
291             if (isSupported("STARTTLS")) {
292                 try {
293                     protocol.startTLS();
294                     usingSSL = true;
295                 } catch (Exception JavaDoc e) {
296                     Object JavaDoc[] options = new String JavaDoc[] {
297                             MailResourceLoader.getString("", "global", "ok")
298                                     .replaceAll("&", ""),
299                             MailResourceLoader
300                                     .getString("", "global", "cancel")
301                                     .replaceAll("&", "") };
302
303                     int result = JOptionPane.showOptionDialog(FrameManager.getInstance()
304                             .getActiveFrame(),
305                             MailResourceLoader.getString("dialog", "error",
306                                     "ssl_handshake_error")
307                                     + ": "
308                                     + e.getLocalizedMessage()
309                                     + "\n"
310                                     + MailResourceLoader.getString("dialog",
311                                             "error", "ssl_turn_off"),
312                             "Warning", JOptionPane.DEFAULT_OPTION,
313                             JOptionPane.WARNING_MESSAGE, null, options,
314                             options[0]);
315
316                     if (result == 1) {
317                         throw new CommandCancelledException();
318                     }
319
320                     // turn off SSL for the future
321
smtpItem.setBoolean("enable_ssl", false);
322
323                     protocol.openPort();
324
325                     initialize();
326                 }
327             } else {
328                 Object JavaDoc[] options = new String JavaDoc[] {
329                         MailResourceLoader.getString("", "global", "ok")
330                                 .replaceAll("&", ""),
331                         MailResourceLoader.getString("", "global", "cancel")
332                                 .replaceAll("&", "") };
333                 int result = JOptionPane.showOptionDialog(null,
334                         MailResourceLoader.getString("dialog", "error",
335                                 "ssl_not_supported")
336                                 + "\n"
337                                 + MailResourceLoader.getString("dialog",
338                                         "error", "ssl_turn_off"), "Warning",
339                         JOptionPane.DEFAULT_OPTION,
340                         JOptionPane.WARNING_MESSAGE, null, options, options[0]);
341
342                 if (result == 1) {
343                     throw new CommandCancelledException();
344                 }
345
346                 // turn off SSL for the future
347
smtpItem.setBoolean("enable_ssl", false);
348             }
349         }
350     }
351
352     /**
353      * @param string
354      * @return
355      */

356     private boolean isSupported(String JavaDoc string) {
357         for (int i = 0; i < capas.length; i++) {
358             if (capas[i].startsWith(string)) {
359                 return true;
360             }
361         }
362
363         return false;
364     }
365
366     /**
367      * @return
368      * @throws CommandCancelledException
369      */

370     public List JavaDoc checkSupportedAuthenticationMethods() throws IOException JavaDoc,
371             SMTPException, CommandCancelledException {
372         ensureConnected();
373
374         List JavaDoc supportedMechanisms = new ArrayList JavaDoc();
375
376         for (int i = 0; i < capas.length; i++) {
377             if (capas[i].startsWith("AUTH")) {
378                 List JavaDoc authMechanisms = AuthenticationFactory.getInstance()
379                         .getSupportedMechanisms(capas[i]);
380                 Iterator JavaDoc it = authMechanisms.iterator();
381                 while (it.hasNext()) {
382                     supportedMechanisms.add(new Integer JavaDoc(AuthenticationManager
383                             .getSaslCode((String JavaDoc) it.next())));
384                 }
385
386                 break;
387             }
388         }
389
390         if( supportedMechanisms.size() == 0) {
391             // Add a default PLAIN login as fallback
392
supportedMechanisms.add(new Integer JavaDoc(AuthenticationManager.SASL_PLAIN));
393         }
394
395         return supportedMechanisms;
396     }
397
398     private void initialize() throws IOException JavaDoc, SMTPException {
399         try {
400             capas = protocol.ehlo(InetAddress.getLocalHost());
401         } catch (SMTPException e1) {
402             // EHLO not supported -> AUTH not supported
403
if( protocol.getState() < SMTPProtocol.PLAIN ) {
404                 protocol.openPort();
405             }
406             protocol.helo(InetAddress.getLocalHost());
407             capas = new String JavaDoc[] {};
408         }
409     }
410
411     /**
412      * @param authType
413      * @return
414      * @throws CommandCancelledException
415      */

416     private int getLoginMethod() throws IOException JavaDoc, SMTPException, CommandCancelledException {
417         String JavaDoc authType = accountItem.getSmtpItem().get("login_method");
418         int method = 0;
419
420         try {
421             method = Integer.parseInt(authType);
422         } catch (NumberFormatException JavaDoc e) {
423             //Fallback to Securest Login method
424
}
425
426         if (method == 0) {
427             List JavaDoc supported = checkSupportedAuthenticationMethods();
428
429             if (accountItem.isPopAccount()) {
430                 supported
431                         .add(new Integer JavaDoc(AuthenticationManager.POP_BEFORE_SMTP));
432             }
433
434             if (supported.size() == 0) {
435                 // No Authentication available
436
return AuthenticationManager.NONE;
437             }
438
439             if (usingSSL) {
440                 // NOTE if SSL is possible we just need the plain login
441
// since SSL does the encryption for us.
442
method = ((Integer JavaDoc) supported.get(0)).intValue();
443             } else {
444                 Collections.sort(supported,
445                         new AuthenticationSecurityComparator());
446                 method = ((Integer JavaDoc) supported.get(supported.size() - 1))
447                         .intValue();
448             }
449         }
450
451         return method;
452     }
453
454     /**
455      *
456      * close the connection to the SMTP server
457      *
458      */

459     public void closeConnection() {
460         // Close Port
461
try {
462             protocol.quit();
463         } catch (Exception JavaDoc e) {
464             e.printStackTrace();
465         }
466     }
467
468     /**
469      *
470      * POP-before-SMTP authentication makes use of the POP3 authentication
471      * mechanism, before sending mail.
472      *
473      * Basically you authenticate with the POP3 server, which allows you to use
474      * the SMTP server for sending mail for a specific amount of time.
475      *
476      * @throws Exception
477      */

478     protected void pop3Authentification() throws IOException JavaDoc, POP3Exception,
479             CommandCancelledException {
480         POP3Store.doPOPbeforeSMTP(accountItem.getPopItem());
481     }
482
483     /**
484      * Send a message
485      *
486      * For an complete example of creating a <class>SendableMessage </class>
487      * object see <class>MessageComposer </class>
488      *
489      * @param message
490      * @param workerStatusController
491      * @throws Exception
492      */

493     public void sendMessage(SendableMessage message,
494             IWorkerStatusController workerStatusController)
495             throws SMTPException, IOException JavaDoc, CommandCancelledException {
496         ensureAuthenticated();
497
498         // send from address and recipient list to SMTP server
499
// ->all addresses have to be normalized
500
protocol.mail(identity.getAddress());
501
502         Iterator JavaDoc recipients = message.getRecipients().iterator();
503
504         while (recipients.hasNext()) {
505             try {
506                 protocol.rcpt(Address.parse((String JavaDoc) recipients.next()));
507             } catch (ParserException e1) {
508                 throw new SMTPException(e1);
509             }
510         }
511
512         // now send message source
513
protocol.data(new ProgressObservedInputStream(
514                 message.getSourceStream(), workerStatusController));
515     }
516
517     public String JavaDoc getName() {
518         OutgoingItem smtpItem = accountItem.getSmtpItem();
519         String JavaDoc host = smtpItem.get("host");
520
521         return host;
522     }
523
524     public void dropConnection() throws IOException JavaDoc {
525         protocol.dropConnection();
526     }
527
528     public void update(Observable JavaDoc o, Object JavaDoc arg) {
529         protocol = new SMTPProtocol(smtpItem.get("host"), smtpItem.getInteger("port"));
530     }
531 }
Popular Tags