1 17 package org.columba.mail.pop3; 18 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.text.MessageFormat ; 22 import java.util.ArrayList ; 23 import java.util.Collections ; 24 import java.util.Hashtable ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.Observable ; 29 import java.util.Observer ; 30 import java.util.logging.Logger ; 31 32 import javax.net.ssl.SSLException; 33 import javax.swing.JOptionPane ; 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 66 public class POP3Store implements Observer { 67 68 69 private static final Logger 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 uidMap; 78 79 private List sizeList; 80 81 private String [] capas; 82 83 private int messageCount = -1; 84 85 private boolean usingSSL = false; 86 87 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 observable = new StatusObservableImpl(); 101 } 102 103 public List getUIDList() throws Exception { 104 return new ArrayList (getUidMap().keySet()); 105 } 106 107 110 public int getSize(int index) throws IOException , POP3Exception, 111 CommandCancelledException { 112 try { 113 return ((Integer ) getSizeList().get(index)).intValue(); 114 } catch (IndexOutOfBoundsException e) { 115 throw new MessageNotOnServerException(new Integer (index)); 116 } catch (NullPointerException e) { 117 throw new MessageNotOnServerException(new Integer (index)); 118 } 119 } 120 121 124 private List getSizeList() throws IOException , POP3Exception, 125 CommandCancelledException { 126 if (sizeList == null) { 127 ensureTransaction(); 128 ScanListEntry[] sizes = protocol.list(); 129 130 sizeList = new ArrayList (sizes.length + 1); 131 sizeList.add(null); 134 135 for (int i = 0; i < sizes.length; i++) { 136 if (sizes[i].getIndex() > sizeList.size() - 1) { 137 for (int nextIndex = sizeList.size() - 1; nextIndex < sizes[i] 139 .getIndex(); nextIndex++) { 140 sizeList.add(null); 141 } 142 } 143 144 sizeList.set(sizes[i].getIndex(), new Integer (sizes[i] 146 .getSize())); 147 } 148 } 149 150 return sizeList; 151 } 152 153 156 public int getMessageCount() throws IOException , 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 170 public boolean deleteMessage(Object uid) throws CommandCancelledException, 171 IOException , POP3Exception { 172 ensureTransaction(); 173 return protocol.dele(getIndex(uid)); 174 } 175 176 protected int getIndex(Object uid) throws IOException , POP3Exception, 177 CommandCancelledException { 178 179 if (getUidMap().containsKey(uid)) { 180 return ((Integer ) getUidMap().get(uid)).intValue(); 181 } else { 182 throw new MessageNotOnServerException(uid); 183 } 184 } 185 186 public InputStream fetchMessage(int index) throws IOException , 187 POP3Exception, CommandCancelledException { 188 ensureTransaction(); 189 return protocol.retr(index, getSize(index)); 190 } 191 192 public void logout() throws IOException , POP3Exception { 193 if (protocol.getState() != POP3Protocol.NOT_CONNECTED) { 194 protocol.quit(); 195 } 196 uidMap = null; 197 sizeList = null; 198 messageCount = -1; 199 } 200 201 204 protected boolean isSupported(String command) throws IOException { 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 getCapa(String command) throws IOException { 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 { 225 if (capas == null) { 226 try { 227 capas = protocol.capa(); 228 } catch (POP3Exception e) { 229 capas = new String [] {}; 231 } 232 } 233 } 234 235 250 protected void login() throws IOException , 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 [] { 269 popItem.get("user"), popItem.get("host") }), 270 popItem.get("password"), popItem 271 .getBoolean("save_password")); 272 273 if (dialog.success()) { 274 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 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 } 304 login = true; 305 } else { 306 try { 307 protocol.auth(AuthenticationManager 309 .getSaslName(loginMethod), popItem.get("user"), 310 password); 311 login = true; 312 } catch (AuthenticationException e) { 313 if (e.getCause() instanceof POP3Exception) 317 throw (POP3Exception) e.getCause(); 318 319 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 checkSupportedAuthenticationMethods() 364 throws CommandCancelledException, IOException { 365 ArrayList supportedMechanisms = new ArrayList (); 366 supportedMechanisms.add(new Integer (AuthenticationManager.USER)); 368 369 try { 370 ensureAuthorization(); 371 372 if (protocol.isApopSupported()) { 374 supportedMechanisms 375 .add(new Integer (AuthenticationManager.APOP)); 376 } 377 } catch (POP3Exception e) { 378 } 380 381 try { 382 String serverSaslMechansims = getCapa("SASL"); 383 384 if (serverSaslMechansims != null) { 386 List authMechanisms = AuthenticationFactory.getInstance() 387 .getSupportedMechanisms(serverSaslMechansims); 388 Iterator it = authMechanisms.iterator(); 389 while (it.hasNext()) { 390 supportedMechanisms.add(new Integer (AuthenticationManager 391 .getSaslCode((String ) it.next()))); 392 } 393 } 394 } catch (IOException e) { 395 e.printStackTrace(); 396 } 397 398 return supportedMechanisms; 399 } 400 401 406 private int getLoginMethod() throws CommandCancelledException, IOException { 407 String loginMethod = popItem.get("login_method"); 408 int result = 0; 409 410 try { 411 result = Integer.parseInt(loginMethod); 412 } catch (NumberFormatException e) { 413 } 415 416 if (result == 0) { 417 List supported = checkSupportedAuthenticationMethods(); 418 419 if (usingSSL) { 420 result = ((Integer ) supported.get(0)).intValue(); 423 } else { 424 Collections.sort(supported, 425 new AuthenticationSecurityComparator()); 426 result = ((Integer ) 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 , 436 POP3Exception, CommandCancelledException { 437 POP3Store store = new POP3Store(popItem); 438 store.ensureTransaction(); 439 store.logout(); 440 } 441 442 protected void ensureTransaction() throws IOException , POP3Exception, 443 CommandCancelledException { 444 ensureAuthorization(); 445 if (protocol.getState() < POP3Protocol.TRANSACTION) { 446 login(); 447 } 448 } 449 450 protected void ensureAuthorization() throws IOException , POP3Exception, 451 CommandCancelledException { 452 if (protocol.getState() < POP3Protocol.AUTHORIZATION) { 453 openConnection(); 454 } 455 } 456 457 460 private void openConnection() throws IOException , POP3Exception, 461 CommandCancelledException { 462 int sslType = popItem.getIntegerWithDefault("ssl_type", 463 IncomingItem.TLS); 464 boolean sslEnabled = popItem.getBoolean("enable_ssl"); 465 466 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 popItem.setBoolean("enable_ssl", false); 486 popItem.setInteger("port", POP3Protocol.DEFAULT_PORT); 487 488 protocol.openPort(); 490 } 491 } else { 492 protocol.openPort(); 493 } 494 495 if (!usingSSL && sslEnabled && sslType == IncomingItem.TLS) { 497 if (isSupported("STLS") || capas.length == 0) { 499 try { 500 protocol.startTLS(); 501 502 usingSSL = true; 503 LOG.info("Switched to SSL"); 504 } catch (IOException 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 popItem.setBoolean("enable_ssl", false); 519 520 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 popItem.setBoolean("enable_ssl", false); 535 } 536 } else { 537 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 popItem.setBoolean("enable_ssl", false); 550 } 551 } 552 } 553 554 private int showErrorDialog(String 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 568 private Map getUidMap() throws CommandCancelledException, IOException , 569 POP3Exception { 570 if (uidMap == null) { 571 if (getMessageCount() != 0) { 572 ensureTransaction(); 573 574 UidListEntry[] uidList = protocol.uidl(); 575 uidMap = new Hashtable (); 576 577 for (int i = 0; i < uidList.length; i++) { 578 uidMap.put(uidList[i].getUid(), new Integer (uidList[i] 579 .getIndex())); 580 } 581 } else { 582 uidMap = new Hashtable (0); 583 } 584 } 585 return uidMap; 586 } 587 588 592 public void dropConnection() throws IOException { 593 protocol.dropConnection(); 594 } 595 596 public void update(Observable o, Object arg) { 597 protocol = new POP3Protocol(popItem.get("host"), popItem 598 .getInteger("port")); 599 } 600 601 } 602 | Popular Tags |