KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > mail > pop3 > POP3Store


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

17 package org.columba.mail.pop3;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.text.MessageFormat JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.Hashtable JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Observable JavaDoc;
29 import java.util.Observer JavaDoc;
30 import java.util.logging.Logger JavaDoc;
31
32 import javax.net.ssl.SSLException;
33 import javax.swing.JOptionPane JavaDoc;
34
35 import org.columba.api.command.IStatusObservable;
36 import org.columba.core.base.Blowfish;
37 import org.columba.core.command.CommandCancelledException;
38 import org.columba.core.command.StatusObservableImpl;
39 import org.columba.core.gui.base.MultiLineLabel;
40 import org.columba.core.gui.frame.FrameManager;
41 import org.columba.mail.config.IncomingItem;
42 import org.columba.mail.config.PopItem;
43 import org.columba.mail.gui.util.PasswordDialog;
44 import org.columba.mail.util.AuthenticationManager;
45 import org.columba.mail.util.AuthenticationSecurityComparator;
46 import org.columba.mail.util.MailResourceLoader;
47 import org.columba.ristretto.auth.AuthenticationException;
48 import org.columba.ristretto.auth.AuthenticationFactory;
49 import org.columba.ristretto.pop3.MessageNotOnServerException;
50 import org.columba.ristretto.pop3.POP3Exception;
51 import org.columba.ristretto.pop3.POP3Protocol;
52 import org.columba.ristretto.pop3.ScanListEntry;
53 import org.columba.ristretto.pop3.UidListEntry;
54
55 /**
56  * First abstractionlayer of the POP3 Protocol. Its task is to manage the state
57  * of the server and handle the low level connection stuff. This means open the
58  * connection using SSL or not, login via the most secure or selected
59  * authentication method. It also provides the capabilites to convert an POP3
60  * uid to the index.
61  *
62  * @see POP3Server
63  *
64  * @author freddy, tstich
65  */

66 public class POP3Store implements Observer JavaDoc {
67
68     /** JDK 1.4+ logging framework logger, used for logging. */
69     private static final Logger JavaDoc LOG = Logger.getLogger("org.columba.mail.pop3");
70
71     private POP3Protocol protocol;
72
73     private PopItem popItem;
74
75     private StatusObservableImpl observable;
76
77     private Map JavaDoc uidMap;
78
79     private List JavaDoc sizeList;
80
81     private String JavaDoc[] capas;
82
83     private int messageCount = -1;
84
85     private boolean usingSSL = false;
86
87     /**
88      * Constructor for POP3Store.
89      */

90     public POP3Store(PopItem popItem) {
91         super();
92         this.popItem = popItem;
93
94         popItem.getRoot().addObserver(this);
95
96         protocol = new POP3Protocol(popItem.get("host"), popItem
97                 .getInteger("port"));
98
99         // add status information observable
100
observable = new StatusObservableImpl();
101     }
102
103     public List JavaDoc getUIDList() throws Exception JavaDoc {
104         return new ArrayList JavaDoc(getUidMap().keySet());
105     }
106
107     /**
108      * Returns the size of the message with the given index.
109      */

110     public int getSize(int index) throws IOException JavaDoc, POP3Exception,
111             CommandCancelledException {
112         try {
113             return ((Integer JavaDoc) getSizeList().get(index)).intValue();
114         } catch (IndexOutOfBoundsException JavaDoc e) {
115             throw new MessageNotOnServerException(new Integer JavaDoc(index));
116         } catch (NullPointerException JavaDoc e) {
117             throw new MessageNotOnServerException(new Integer JavaDoc(index));
118         }
119     }
120
121     /**
122      * @return
123      */

124     private List JavaDoc getSizeList() throws IOException JavaDoc, POP3Exception,
125             CommandCancelledException {
126         if (sizeList == null) {
127             ensureTransaction();
128             ScanListEntry[] sizes = protocol.list();
129
130             sizeList = new ArrayList JavaDoc(sizes.length + 1);
131             // since the indices on the pop server start with 1 we add
132
// a dummy null for the 0 element in the list
133
sizeList.add(null);
134
135             for (int i = 0; i < sizes.length; i++) {
136                 if (sizes[i].getIndex() > sizeList.size() - 1) {
137                     // fill with nulls
138
for (int nextIndex = sizeList.size() - 1; nextIndex < sizes[i]
139                             .getIndex(); nextIndex++) {
140                         sizeList.add(null);
141                     }
142                 }
143
144                 // put size at the specified place
145
sizeList.set(sizes[i].getIndex(), new Integer JavaDoc(sizes[i]
146                         .getSize()));
147             }
148         }
149
150         return sizeList;
151     }
152
153     /**
154      * Returns the number of messages on the server.
155      */

156     public int getMessageCount() throws IOException JavaDoc, POP3Exception,
157             CommandCancelledException {
158         if (messageCount == -1) {
159             ensureTransaction();
160             int[] stat = protocol.stat();
161             messageCount = stat[0];
162         }
163
164         return messageCount;
165     }
166
167     /**
168      * Deletes the message with the given UID from the server.
169      */

170     public boolean deleteMessage(Object JavaDoc uid) throws CommandCancelledException,
171             IOException JavaDoc, POP3Exception {
172         ensureTransaction();
173         return protocol.dele(getIndex(uid));
174     }
175
176     protected int getIndex(Object JavaDoc uid) throws IOException JavaDoc, POP3Exception,
177             CommandCancelledException {
178
179         if (getUidMap().containsKey(uid)) {
180             return ((Integer JavaDoc) getUidMap().get(uid)).intValue();
181         } else {
182             throw new MessageNotOnServerException(uid);
183         }
184     }
185
186     public InputStream JavaDoc fetchMessage(int index) throws IOException JavaDoc,
187             POP3Exception, CommandCancelledException {
188         ensureTransaction();
189         return protocol.retr(index, getSize(index));
190     }
191
192     public void logout() throws IOException JavaDoc, POP3Exception {
193         if (protocol.getState() != POP3Protocol.NOT_CONNECTED) {
194             protocol.quit();
195         }
196         uidMap = null;
197         sizeList = null;
198         messageCount = -1;
199     }
200
201     /**
202      * Returns whether a given POP3 command is supported by the server.
203      */

204     protected boolean isSupported(String JavaDoc command) throws IOException JavaDoc {
205         fetchCapas();
206         for (int i = 0; i < capas.length; i++) {
207             if (capas[i].startsWith(command)) {
208                 return true;
209             }
210         }
211         return false;
212     }
213
214     protected String JavaDoc getCapa(String JavaDoc command) throws IOException JavaDoc {
215         fetchCapas();
216         for (int i = 0; i < capas.length; i++) {
217             if (capas[i].startsWith(command)) {
218                 return capas[i];
219             }
220         }
221         return null;
222     }
223
224     private void fetchCapas() throws IOException JavaDoc {
225         if (capas == null) {
226             try {
227                 capas = protocol.capa();
228             } catch (POP3Exception e) {
229                 // CAPA not supported
230
capas = new String JavaDoc[] {};
231             }
232         }
233     }
234
235     /**
236      * Try to login a user to a given pop3 server. While the login is not
237      * succeed, the connection to the server is opened, then a dialog box is
238      * coming up, then the user should fill in his password. The username and
239      * password is sended to the pop3 server. Then we recive a answer. Is the
240      * answer is false, we try to logout from the server and closing the
241      * connection. The we begin again with open connection, showing dialog and
242      * so on.
243      *
244      * @param worker
245      * used for cancel button
246      * @throws Exception
247      *
248      * Bug number 619290 fixed.
249      */

250     protected void login() throws IOException JavaDoc, POP3Exception,
251             CommandCancelledException {
252         PasswordDialog dialog;
253         boolean login = false;
254
255         char[] password = new char[0];
256
257         boolean save = false;
258
259         int loginMethod = getLoginMethod();
260
261         while (!login) {
262             if ((password = Blowfish.decrypt(popItem.getRoot().getAttribute(
263                     "password", ""))).length == 0) {
264                 dialog = new PasswordDialog();
265
266                 dialog.showDialog(MessageFormat
267                         .format(MailResourceLoader.getString("dialog",
268                                 "password", "enter_password"), new Object JavaDoc[] {
269                                 popItem.get("user"), popItem.get("host") }),
270                         popItem.get("password"), popItem
271                                 .getBoolean("save_password"));
272
273                 if (dialog.success()) {
274                     // ok pressed
275
password = dialog.getPassword();
276                     save = dialog.getSave();
277                 } else {
278                     throw new CommandCancelledException();
279                 }
280             } else {
281                 save = popItem.getBoolean("save_password");
282             }
283
284             try {
285                 if (loginMethod == AuthenticationManager.USER) {
286                     protocol.userPass(popItem.get("user"), password);
287                     login = true;
288                 } else if (loginMethod == AuthenticationManager.APOP) {
289                     try {
290                         protocol.apop(popItem.get("user"), password);
291                     } catch (POP3Exception e1) {
292                         // some server have a bogus apop
293
// try user/pass to check if the password is
294
// correct
295
protocol.userPass(popItem.get("user"), password);
296
297                         LOG
298                                 .warning(popItem.get("host")
299                                         + " : bogus APOP implementation -> falling back to USER/PASS.");
300
301                         // user/pass worked -> this is indeed
302
// a bogus server.
303
}
304                     login = true;
305                 } else {
306                     try {
307                         // AUTH
308
protocol.auth(AuthenticationManager
309                                 .getSaslName(loginMethod), popItem.get("user"),
310                                 password);
311                         login = true;
312                     } catch (AuthenticationException e) {
313                         // If the cause is a IMAPExcpetion then only password
314
// wrong
315
// else bogus authentication mechanism
316
if (e.getCause() instanceof POP3Exception)
317                             throw (POP3Exception) e.getCause();
318
319                         // Some error in the client/server communication
320
// --> fall back to default login process
321
int result = JOptionPane
322                                 .showConfirmDialog(
323                                         FrameManager.getInstance()
324                                                 .getActiveFrame(),
325                                         new MultiLineLabel(
326                                                 e.getMessage()
327                                                         + "\n"
328                                                         + MailResourceLoader
329                                                                 .getString(
330                                                                         "dialog",
331                                                                         "error",
332                                                                         "authentication_fallback_to_default")),
333                                         MailResourceLoader.getString("dialog",
334                                                 "error",
335                                                 "authentication_process_error"),
336                                         JOptionPane.OK_CANCEL_OPTION);
337                         if (result == JOptionPane.OK_OPTION) {
338                             loginMethod = AuthenticationManager.USER;
339                             popItem.setString("login_method", Integer
340                                     .toString(loginMethod));
341                         } else {
342                             throw new CommandCancelledException();
343                         }
344                     }
345                 }
346             } catch (POP3Exception e) {
347                 JOptionPane.showMessageDialog(null, e.getMessage(),
348                         "Authorization failed!", JOptionPane.ERROR_MESSAGE);
349
350                 popItem.setString("password", "");
351             }
352
353             LOG.info("login=" + login);
354         }
355
356         popItem.setBoolean("save_password", save);
357
358         if (save) {
359             popItem.setString("password", Blowfish.encrypt(password));
360         }
361     }
362
363     public List JavaDoc checkSupportedAuthenticationMethods()
364             throws CommandCancelledException, IOException JavaDoc {
365         ArrayList JavaDoc supportedMechanisms = new ArrayList JavaDoc();
366         // USER/PASS is always supported
367
supportedMechanisms.add(new Integer JavaDoc(AuthenticationManager.USER));
368
369         try {
370             ensureAuthorization();
371
372             // APOP?
373
if (protocol.isApopSupported()) {
374                 supportedMechanisms
375                         .add(new Integer JavaDoc(AuthenticationManager.APOP));
376             }
377         } catch (POP3Exception e) {
378             // APOP not supported
379
}
380
381         try {
382             String JavaDoc serverSaslMechansims = getCapa("SASL");
383
384             // AUTH?
385
if (serverSaslMechansims != null) {
386                 List JavaDoc authMechanisms = AuthenticationFactory.getInstance()
387                         .getSupportedMechanisms(serverSaslMechansims);
388                 Iterator JavaDoc it = authMechanisms.iterator();
389                 while (it.hasNext()) {
390                     supportedMechanisms.add(new Integer JavaDoc(AuthenticationManager
391                             .getSaslCode((String JavaDoc) it.next())));
392                 }
393             }
394         } catch (IOException JavaDoc e) {
395             e.printStackTrace();
396         }
397
398         return supportedMechanisms;
399     }
400
401     /**
402      * Gets the selected Authentication method or else the most secure.
403      *
404      * @return the authentication method
405      */

406     private int getLoginMethod() throws CommandCancelledException, IOException JavaDoc {
407         String JavaDoc loginMethod = popItem.get("login_method");
408         int result = 0;
409
410         try {
411             result = Integer.parseInt(loginMethod);
412         } catch (NumberFormatException JavaDoc e) {
413             // Just use the default as fallback
414
}
415
416         if (result == 0) {
417             List JavaDoc supported = checkSupportedAuthenticationMethods();
418
419             if (usingSSL) {
420                 // NOTE if SSL is possible we just need the plain login
421
// since SSL does the encryption for us.
422
result = ((Integer JavaDoc) supported.get(0)).intValue();
423             } else {
424                 Collections.sort(supported,
425                         new AuthenticationSecurityComparator());
426                 result = ((Integer JavaDoc) supported.get(supported.size() - 1))
427                         .intValue();
428             }
429
430         }
431
432         return result;
433     }
434
435     public static void doPOPbeforeSMTP(PopItem popItem) throws IOException JavaDoc,
436             POP3Exception, CommandCancelledException {
437         POP3Store store = new POP3Store(popItem);
438         store.ensureTransaction();
439         store.logout();
440     }
441
442     protected void ensureTransaction() throws IOException JavaDoc, POP3Exception,
443             CommandCancelledException {
444         ensureAuthorization();
445         if (protocol.getState() < POP3Protocol.TRANSACTION) {
446             login();
447         }
448     }
449
450     protected void ensureAuthorization() throws IOException JavaDoc, POP3Exception,
451             CommandCancelledException {
452         if (protocol.getState() < POP3Protocol.AUTHORIZATION) {
453             openConnection();
454         }
455     }
456
457     /**
458      *
459      */

460     private void openConnection() throws IOException JavaDoc, POP3Exception,
461             CommandCancelledException {
462         int sslType = popItem.getIntegerWithDefault("ssl_type",
463                 IncomingItem.TLS);
464         boolean sslEnabled = popItem.getBoolean("enable_ssl");
465
466         // open a port to the server
467
if (sslEnabled && sslType == IncomingItem.IMAPS_POP3S) {
468             try {
469                 protocol.openSSLPort();
470                 usingSSL = true;
471             } catch (SSLException e) {
472                 int result = showErrorDialog(MailResourceLoader.getString(
473                         "dialog", "error", "ssl_handshake_error")
474                         + ": "
475                         + e.getLocalizedMessage()
476                         + "\n"
477                         + MailResourceLoader.getString("dialog", "error",
478                                 "ssl_turn_off"));
479
480                 if (result == JOptionPane.CANCEL_OPTION) {
481                     throw new CommandCancelledException();
482                 }
483
484                 // turn off SSL for the future
485
popItem.setBoolean("enable_ssl", false);
486                 popItem.setInteger("port", POP3Protocol.DEFAULT_PORT);
487
488                 // reopen the port
489
protocol.openPort();
490             }
491         } else {
492             protocol.openPort();
493         }
494
495         // shall we switch to SSL?
496
if (!usingSSL && sslEnabled && sslType == IncomingItem.TLS) {
497             // if CAPA was not support just give it a try...
498
if (isSupported("STLS") || capas.length == 0) {
499                 try {
500                     protocol.startTLS();
501
502                     usingSSL = true;
503                     LOG.info("Switched to SSL");
504                 } catch (IOException JavaDoc e) {
505                     int result = showErrorDialog(MailResourceLoader.getString(
506                             "dialog", "error", "ssl_handshake_error")
507                             + ": "
508                             + e.getLocalizedMessage()
509                             + "\n"
510                             + MailResourceLoader.getString("dialog", "error",
511                                     "ssl_turn_off"));
512
513                     if (result == JOptionPane.CANCEL_OPTION) {
514                         throw new CommandCancelledException();
515                     }
516
517                     // turn off SSL for the future
518
popItem.setBoolean("enable_ssl", false);
519
520                     // reopen the port
521
protocol.openPort();
522                 } catch (POP3Exception e) {
523                     int result = showErrorDialog(MailResourceLoader.getString(
524                             "dialog", "error", "ssl_not_supported")
525                             + "\n"
526                             + MailResourceLoader.getString("dialog", "error",
527                                     "ssl_turn_off"));
528
529                     if (result == JOptionPane.CANCEL_OPTION) {
530                         throw new CommandCancelledException();
531                     }
532
533                     // turn off SSL for the future
534
popItem.setBoolean("enable_ssl", false);
535                 }
536             } else {
537                 // CAPAs say that SSL is not supported
538
int result = showErrorDialog(MailResourceLoader.getString(
539                         "dialog", "error", "ssl_not_supported")
540                         + "\n"
541                         + MailResourceLoader.getString("dialog", "error",
542                                 "ssl_turn_off"));
543
544                 if (result == JOptionPane.CANCEL_OPTION) {
545                     throw new CommandCancelledException();
546                 }
547
548                 // turn off SSL for the future
549
popItem.setBoolean("enable_ssl", false);
550             }
551         }
552     }
553
554     private int showErrorDialog(String JavaDoc message) {
555         int result = JOptionPane.showConfirmDialog(FrameManager.getInstance()
556                 .getActiveFrame(), message, "Warning",
557                 JOptionPane.WARNING_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
558         return result;
559     }
560
561     public IStatusObservable getObservable() {
562         return observable;
563     }
564
565     /**
566      * @return Returns the uidMap.
567      */

568     private Map JavaDoc getUidMap() throws CommandCancelledException, IOException JavaDoc,
569             POP3Exception {
570         if (uidMap == null) {
571             if (getMessageCount() != 0) {
572                 ensureTransaction();
573
574                 UidListEntry[] uidList = protocol.uidl();
575                 uidMap = new Hashtable JavaDoc();
576
577                 for (int i = 0; i < uidList.length; i++) {
578                     uidMap.put(uidList[i].getUid(), new Integer JavaDoc(uidList[i]
579                             .getIndex()));
580                 }
581             } else {
582                 uidMap = new Hashtable JavaDoc(0);
583             }
584         }
585         return uidMap;
586     }
587
588     /**
589      * @throws IOException
590      *
591      */

592     public void dropConnection() throws IOException JavaDoc {
593         protocol.dropConnection();
594     }
595
596     public void update(Observable JavaDoc o, Object JavaDoc arg) {
597         protocol = new POP3Protocol(popItem.get("host"), popItem
598                 .getInteger("port"));
599     }
600
601 }
602
Popular Tags