1 21 22 package org.armedbear.j.mail; 23 24 import java.io.UnsupportedEncodingException ; 25 import java.net.MalformedURLException ; 26 import java.util.ArrayList ; 27 import java.util.Collections ; 28 import java.util.HashMap ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import javax.swing.SwingUtilities ; 32 import org.armedbear.j.BackgroundProcess; 33 import org.armedbear.j.Buffer; 34 import org.armedbear.j.BufferIterator; 35 import org.armedbear.j.Debug; 36 import org.armedbear.j.Editor; 37 import org.armedbear.j.EditorIterator; 38 import org.armedbear.j.File; 39 import org.armedbear.j.FastStringBuffer; 40 import org.armedbear.j.Frame; 41 import org.armedbear.j.Headers; 42 import org.armedbear.j.InputDialog; 43 import org.armedbear.j.Line; 44 import org.armedbear.j.Log; 45 import org.armedbear.j.MessageDialog; 46 import org.armedbear.j.ProgressNotifier; 47 import org.armedbear.j.Property; 48 import org.armedbear.j.Sidebar; 49 import org.armedbear.j.StatusBar; 50 import org.armedbear.j.StatusBarProgressNotifier; 51 import org.armedbear.j.Utilities; 52 import org.armedbear.j.View; 53 54 public final class ImapMailbox extends Mailbox 55 { 56 private static final int DEFAULT_PORT = 143; 57 58 private final ImapSession session; 59 private final String folderName; 60 61 private int messageCount = -1; 62 private int uidValidity; 63 private int uidLast; 64 private ImapMailboxCache mailboxCache; 65 private boolean cancelled; 66 private Thread backgroundThread; 67 68 public ImapMailbox(ImapURL url, ImapSession session) 69 { 70 super(url); 71 this.session = session; 72 session.setMailbox(this); 73 String tunnel = getStringProperty(Property.TUNNEL); 74 if (tunnel != null) { 75 Log.debug("tunnel = |" + tunnel + "|"); 76 session.setTunnel(tunnel); 77 } 78 folderName = session.getFolderName(); 79 Debug.assertTrue(folderName.equals(url.getFolderName())); 80 supportsUndo = false; 81 type = TYPE_MAILBOX; 82 mode = MailboxMode.getMode(); 83 formatter = mode.getFormatter(this); 84 readOnly = true; 85 setInitialized(true); 86 Log.debug("ImapMailbox constructor ".concat(url.getCanonicalName())); 87 } 88 89 public String getFileNameForDisplay() 90 { 91 FastStringBuffer sb = new FastStringBuffer(64); 92 sb.append(url.toString()); 93 String limitPattern = getLimitPattern(); 94 if (limitPattern != null) { 95 sb.append(' '); 96 sb.append(limitPattern); 97 } 98 return sb.toString(); 99 } 100 101 public String getName() 102 { 103 FastStringBuffer sb = new FastStringBuffer(); 104 sb.append('{'); 105 sb.append(session.getUser()); 106 sb.append('@'); 107 sb.append(session.getHost()); 108 sb.append(':'); 109 sb.append(session.getPort()); 110 sb.append('}'); 111 sb.append(folderName); 112 return sb.toString(); 113 } 114 115 public final ImapSession getSession() 116 { 117 return session; 118 } 119 120 public final String getFolderName() 121 { 122 return folderName; 123 } 124 125 public final int getUidValidity() 126 { 127 return uidValidity; 128 } 129 130 public final int getMessageCount() 131 { 132 return messageCount; 133 } 134 135 public synchronized long getLastErrorMillis() 136 { 137 return session.getLastErrorMillis(); 138 } 139 140 public void setAlertText(final String s) 141 { 142 Log.debug("alert = " + s); 143 Runnable r = new Runnable () { 144 public void run() 145 { 146 Editor.currentEditor().status(s); 147 } 148 }; 149 SwingUtilities.invokeLater(r); 150 } 151 152 public void setStatusText(final String s) 153 { 154 Log.debug("status = " + s); 155 Runnable r = new Runnable () { 156 public void run() 157 { 158 Editor.currentEditor().status(s); 159 } 160 }; 161 SwingUtilities.invokeLater(r); 162 } 163 164 public void messageExpunged(int messageNumber) 165 { 166 messageCount = -1; 168 } 169 170 public void expunge() 171 { 172 Runnable expungeRunnable = new Runnable () { 173 public void run() 174 { 175 try { 176 if (session.verifyConnected() && session.verifySelected(folderName)) { 177 if (session.isReadOnly()) { 178 Log.debug("expunge - read-only - reselecting..."); 179 session.reselect(folderName); 180 if (session.isReadOnly()) { 181 Log.error("expunge - mailbox is read-only"); 182 readOnlyError(); 183 return; 184 } 185 } 186 if (messageCache != null) 187 messageCache.removeDeletedEntries(Collections.unmodifiableList(entries)); 188 if (session.close()) { 189 Log.debug("expunge back from close(), calling reselect()"); 190 if (session.reselect(folderName)) { 191 Log.debug("expunge back from reselect()"); 192 getAllMessageHeaders(); 193 refreshBuffer(); 194 setBusy(false); 195 for (int i = 0; i < Editor.getFrameCount(); i++) { 196 Frame frame = Editor.getFrame(i); 197 if (frame != null) { 198 StatusBar statusBar = frame.getStatusBar(); 199 if (statusBar != null) 200 statusBar.setText(null); 201 } 202 } 203 updateDisplay(); 204 return; 205 } 206 } 207 error("Operation failed", "Expunge"); 209 } 210 } 211 finally { 212 setBusy(false); 213 unlock(); 214 } 215 } 216 }; 217 if (lock()) { 218 setBusy(true); 219 saveDisplayState(); 220 new Thread (expungeRunnable).start(); 221 } 222 } 223 224 public synchronized int load() 225 { 226 if (isLoaded()) { 227 return LOAD_COMPLETED; 228 } else if (lock()) { 229 Debug.assertTrue(backgroundThread == null); 230 backgroundThread = new Thread (loadProcess); 231 backgroundThread.start(); 232 setLoaded(true); 233 return LOAD_PENDING; 234 } else { 235 Debug.bug("ImapMailbox.load can't lock mailbox"); 237 return LOAD_FAILED; 238 } 239 } 240 241 private BackgroundProcess loadProcess = new BackgroundProcess() { 242 public void run() 243 { 244 Runnable completionRunnable = null; 245 try { 246 cancelled = false; 247 setBusy(true); 248 setBackgroundProcess(this); 249 if (getAllMessageHeaders()) { 250 refreshBuffer(); 251 completionRunnable = new Runnable () { 252 public void run() 253 { 254 setBusy(false); 255 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 256 Editor ed = it.nextEditor(); 257 View view = new View(); 258 view.setDotEntry(getInitialEntry()); 259 ed.setView(ImapMailbox.this, view); 260 if (ed.getBuffer() == ImapMailbox.this) { 261 ed.bufferActivated(true); 262 ed.updateDisplay(); 263 } 264 } 265 } 266 }; 267 } else { 268 completionRunnable = new Runnable () { 270 public void run() 271 { 272 if (Editor.getBufferList().contains(ImapMailbox.this)) 273 kill(); 274 for (EditorIterator it = new EditorIterator(); it.hasNext();) 275 it.nextEditor().updateDisplay(); 276 } 277 }; 278 } 279 } 280 finally { 281 setBackgroundProcess(null); 282 backgroundThread = null; 283 unlock(); 284 if (completionRunnable != null) 285 SwingUtilities.invokeLater(completionRunnable); 286 } 287 } 288 289 public void cancel() 290 { 291 abort(); 292 } 293 }; 294 295 private void abort() 296 { 297 Log.debug("ImapMailbox.abort"); 298 cancelled = true; 299 if (backgroundThread != null && backgroundThread.isAlive()) 300 backgroundThread.interrupt(); 301 session.disconnect(); 302 } 303 304 public final void getNewMessages() 305 { 306 if (lock()) 307 getNewMessages(true); 308 else 309 Editor.currentEditor().status("Mailbox is locked"); 310 } 311 312 public void getNewMessages(boolean interactive) 313 { 314 Debug.assertTrue(isLocked()); 315 setBusy(true); 316 if (interactive) 317 saveDisplayState(); 318 Debug.assertTrue(backgroundThread == null); 319 backgroundThread = new Thread (new GetNewMessagesProcess(interactive)); 320 backgroundThread.start(); 321 } 322 323 private class GetNewMessagesProcess implements BackgroundProcess 324 { 325 private final boolean interactive; 326 327 public GetNewMessagesProcess(boolean interactive) 328 { 329 this.interactive = interactive; 330 } 331 332 public void run() 333 { 334 cancelled = false; 335 setBackgroundProcess(this); 336 boolean changed = false; 337 if (interactive) 338 changed = clearRecent(); 339 try { 340 if (!session.verifyConnected()) { 343 Log.error("GetNewMessagesProcess.run can't connect"); 344 return; 345 } 346 if (cancelled) { 347 Log.debug("cancelled, disconnecting..."); 348 session.disconnect(); 349 return; 350 } 351 if (!session.verifySelected(folderName)) { 352 Log.error("GetNewMessagesProcess.run can't select " + 353 folderName); 354 return; 355 } 356 if (cancelled) { 357 Log.debug("cancelled, disconnecting..."); 358 session.disconnect(); 359 return; 360 } 361 if (session.isReadOnly()) { 362 Log.warn("GetNewMessagesProcess.run " + folderName + 363 " is read-only, returning..."); 364 return; 365 } 366 if (uidValidity != session.getUidValidity()) { 367 getAllMessageHeaders(); 368 changed = true; 369 } else if (interactive) { 370 changed = getNewMessageHeaders() || changed; 371 } else { 372 if (session.getMessageCount() != messageCount) { 374 Log.debug("session.getMessageCount() = " + 375 session.getMessageCount() + 376 " mailbox message count = " + 377 messageCount); 378 changed = getNewMessageHeaders(); 379 } 380 } 381 if (cancelled) { 382 Log.debug("cancelled, disconnecting..."); 383 session.disconnect(); 384 } 385 } 386 finally { 387 setBackgroundProcess(null); 388 backgroundThread = null; 389 if (changed) { 390 refreshBuffer(); 391 setBusy(false); 392 updateDisplay(); 393 newMessagesStatus(); 394 } else { 395 setBusy(false); 396 for (EditorIterator it = new EditorIterator(); it.hasNext();) { 397 Editor ed = it.nextEditor(); 398 if (ed != null && ed.getBuffer() == ImapMailbox.this) 399 ed.setDefaultCursor(); 400 } 401 } 402 setLastCheckMillis(System.currentTimeMillis()); 403 unlock(); 404 } 405 } 406 407 public void cancel() 408 { 409 abort(); 410 } 411 } 412 413 public void createFolder() 414 { 415 final Editor editor = Editor.currentEditor(); 416 final String input = 417 InputDialog.showInputDialog(editor, "Folder:", "Create Folder"); 418 if (input == null || input.length() == 0) 419 return; 420 final String name = extractFolderName(input); 421 if (name == null) 422 return; 423 Runnable createRunnable = new Runnable () { 424 public void run() 425 { 426 boolean succeeded = false; 427 try { 428 if (session.verifyConnected() && session.verifySelected(folderName)) { 429 session.setEcho(true); 430 session.writeTagged("create " + name); 431 succeeded = session.getResponse() == ImapSession.OK; 432 session.setEcho(false); 433 } 434 } 435 finally { 436 unlock(); 437 setBusy(false); 438 Editor.updateDisplayLater(ImapMailbox.this); 439 if (succeeded) 440 success("Folder created"); 441 else 442 error("Unable to create folder", "Error"); 443 } 444 } 445 }; 446 if (lock()) { 449 setBusy(true); 450 new Thread (createRunnable).start(); 451 } 452 } 453 454 public void deleteFolder() 455 { 456 final Editor editor = Editor.currentEditor(); 457 final String input = 458 InputDialog.showInputDialog(editor, "Folder:", "Delete Folder"); 459 if (input == null || input.length() == 0) 460 return; 461 final String name = extractFolderName(input); 462 if (name == null) 463 return; 464 String message = "Delete folder \"" + name + "\" on " + session.getHost() + "?"; 465 if (!editor.confirm("Delete Folder", message)) 466 return; 467 Runnable deleteFolderRunnable = new Runnable () { 468 public void run() 469 { 470 boolean succeeded = false; 471 try { 472 if (session.verifyConnected() && session.verifySelected(folderName)) { 473 session.setEcho(true); 474 session.writeTagged("delete " + name); 475 succeeded = session.getResponse() == ImapSession.OK; 476 session.setEcho(false); 477 } 478 } 479 finally { 480 unlock(); 481 setBusy(false); 482 Editor.updateDisplayLater(ImapMailbox.this); 483 if (succeeded) 484 success("Folder deleted"); 485 else 486 error("Unable to delete folder", "Error"); 487 } 488 } 489 }; 490 if (lock()) { 493 setBusy(true); 494 new Thread (deleteFolderRunnable).start(); 495 } 496 } 497 498 public void saveToFolder() 499 { 500 final Editor editor = Editor.currentEditor(); 501 boolean advanceDot = false; 502 List list = getTaggedEntries(); 503 if (list == null) { 504 Line line = editor.getDotLine(); 505 if (!(line instanceof MailboxLine)) 506 return; 507 advanceDot = true; 508 list = new ArrayList (); 509 list.add(((MailboxLine)line).getMailboxEntry()); 510 } 511 final List toBeCopied = list; 512 final int count = toBeCopied.size(); 513 String title; 514 if (count > 1) 515 title = "Save Tagged Messages to Folder"; 516 else 517 title = "Save Message to Folder"; 518 FastStringBuffer sb = new FastStringBuffer("Save "); 519 sb.append(count); 520 sb.append(" message"); 521 if (count > 1) 522 sb.append('s'); 523 sb.append(" to:"); 524 final String destination = ChooseFolderDialog.chooseFolder(editor, 525 sb.toString(), title); 526 if (destination == null) 527 return; 528 final Line dotLine = advanceDot ? editor.getDotLine() : null; 529 Runnable saveRunnable = new Runnable () { 530 public void run() 531 { 532 boolean succeeded = false; 533 try { 534 if (session.verifyConnected() && session.verifySelected(folderName)) { 535 if (destination.startsWith("mailbox:")) { 536 succeeded = saveLocal(toBeCopied, destination, false); 538 } else { 539 session.setEcho(true); 540 final String messageSet = getMessageSet(toBeCopied); 541 FastStringBuffer sbuf = new FastStringBuffer("uid copy "); 542 sbuf.append(messageSet); 543 sbuf.append(' '); 544 sbuf.append(destination); 545 if (session.writeTagged(sbuf.toString())) 546 if (session.getResponse() == ImapSession.OK) 547 succeeded = true; 548 session.setEcho(false); 549 } 550 } 551 } 552 finally { 553 if (succeeded && dotLine != null) 554 advanceDot(dotLine); 555 setBusy(false); 556 unlock(); 557 editor.updateDisplayLater(); 558 if (succeeded) { 559 FastStringBuffer sbuf = new FastStringBuffer("Saved "); 560 sbuf.append(toBeCopied.size()); 561 sbuf.append(" message"); 562 if (toBeCopied.size() != 1) 563 sbuf.append('s'); 564 success(sbuf.toString()); 565 } else 566 error("Save failed", "Error"); 567 } 568 } 569 }; 570 if (lock()) { 571 setBusy(true); 572 new Thread (saveRunnable).start(); 573 } 574 } 575 576 public void moveToFolder() 577 { 578 final Editor editor = Editor.currentEditor(); 579 boolean advanceDot = false; 580 List list = getTaggedEntries(); 581 if (list == null) { 582 Line line = editor.getDotLine(); 583 if (!(line instanceof MailboxLine)) 584 return; 585 advanceDot = true; 586 list = new ArrayList (); 587 list.add(((MailboxLine)line).getMailboxEntry()); 588 } 589 final List toBeMoved = list; 590 final int count = toBeMoved.size(); 591 String title; 592 if (count > 1) 593 title = "Move Tagged Messages to Folder"; 594 else 595 title = "Move Message to Folder"; 596 FastStringBuffer sb = new FastStringBuffer("Move "); 597 sb.append(count); 598 sb.append(" message"); 599 if (count > 1) 600 sb.append('s'); 601 sb.append(" to:"); 602 final String destination = ChooseFolderDialog.chooseFolder(editor, 603 sb.toString(), title); 604 if (destination == null) 605 return; 606 final Line dotLine = advanceDot ? editor.getDotLine() : null; 607 Runnable moveRunnable = new Runnable () { 608 public void run() 609 { 610 boolean succeeded = false; 611 String errorText = "Move failed"; 612 try { 613 succeeded = moveToFolder(toBeMoved, destination); 614 } 615 catch (MailException e) { 616 errorText = e.getMessage(); 617 } 618 finally { 619 countMessages(); 623 Sidebar.repaintBufferListInAllFrames(); 624 if (succeeded && dotLine != null) 625 advanceDot(dotLine); 626 setBusy(false); 627 unlock(); 628 editor.updateDisplayLater(); 629 if (succeeded) { 630 FastStringBuffer sbuf = new FastStringBuffer("Moved "); 631 sbuf.append(toBeMoved.size()); 632 sbuf.append(" message"); 633 if (toBeMoved.size() != 1) 634 sbuf.append('s'); 635 success(sbuf.toString()); 636 } else 637 error(errorText, "Error"); 638 } 639 } 640 }; 641 if (lock()) { 642 setBusy(true); 643 new Thread (moveRunnable).start(); 644 } 645 } 646 647 private boolean moveToFolder(List toBeMoved, String destination) throws MailException 648 { 649 if (destination == null) 650 return false; 651 if (!destination.startsWith("mailbox:")) { 652 destination = extractFolderName(destination); 654 if (destination == null) 655 return false; 656 } 657 boolean succeeded = false; 658 if (session.verifyConnected() && session.verifySelected(folderName)) { 659 if (session.isReadOnly()) { 660 Log.debug("moveToFolder " + folderName + " is read-only - reselecting..."); 661 session.reselect(folderName); 662 if (session.isReadOnly()) 663 throw new MailException("Mailbox " + folderName + " is read-only"); 664 } 665 if (destination.startsWith("mailbox:")) { 666 succeeded = saveLocal(toBeMoved, destination, true); 668 } else { 669 session.setEcho(true); 671 final String messageSet = getMessageSet(toBeMoved); 672 FastStringBuffer sb = new FastStringBuffer("uid copy "); 673 sb.append(messageSet); 674 sb.append(' '); 675 sb.append(destination); 676 session.writeTagged(sb.toString()); 677 if (session.getResponse() == ImapSession.OK) { 678 sb.setLength(0); 679 sb.append("uid store "); 680 sb.append(messageSet); 681 sb.append(" +flags.silent (\\deleted)"); 682 session.writeTagged(sb.toString()); 683 if (session.getResponse() == ImapSession.OK) { 684 succeeded = true; 685 for (int i = 0; i < toBeMoved.size(); i++) { 686 ImapMailboxEntry entry = (ImapMailboxEntry) toBeMoved.get(i); 687 entry.setFlags(entry.getFlags() | MailboxEntry.DELETED); 688 updateEntry(entry); 689 } 690 } 691 } 692 session.setEcho(false); 693 } 694 } 695 return succeeded; 696 } 697 698 private boolean saveLocal(List toBeSaved, String destination, boolean delete) 699 { 700 File file = File.getInstance(destination.substring(8)); 701 if (file == null) 702 return false; 703 if (!file.isLocal()) 704 return false; 705 Mbox mbox = Mbox.getInstance(file); 706 if (!mbox.lock()) 707 return false; 708 boolean error = false; 709 for (int i = 0; i < toBeSaved.size(); i++) { 710 ImapMailboxEntry entry = (ImapMailboxEntry) toBeSaved.get(i); 711 Message message = getMessage(entry, null); 712 if (message != null && 713 mbox.appendMessage(message, entry.getFlags() & ~MailboxEntry.TAGGED)) { 714 if (delete) { 715 session.writeTagged("uid store " + entry.getUid() + " +flags.silent (\\deleted)"); 716 if (session.getResponse() == ImapSession.OK) { 717 entry.setFlags(entry.getFlags() | MailboxEntry.DELETED); 718 updateEntry(entry); 719 } else { 720 error = true; 721 break; 722 } 723 } 724 } else { 725 error = true; 726 break; 727 } 728 } 729 mbox.unlock(); 730 mbox.updateViews(); 731 return !error; 732 } 733 734 public String extractFolderName(String input) 735 { 736 if (input.startsWith("{")) { 737 try { 738 ImapURL targetUrl = new ImapURL(input); 739 if (!url.getHost().equals(targetUrl.getHost())) { 740 return null; 743 } 744 String name = targetUrl.getFolderName(); 745 Log.debug("folder name = |" + name + "|"); 746 return name; 747 } 748 catch (MalformedURLException e) { 749 Log.error(e); 750 return null; 751 } 752 } else { 753 return input; 755 } 756 } 757 758 public void delete() 759 { 760 final Editor editor = Editor.currentEditor(); 761 boolean advanceDot = false; 762 List list = getTaggedEntries(); 763 if (list == null) { 764 Line line = editor.getDotLine(); 765 if (!(line instanceof MailboxLine)) 766 return; 767 advanceDot = true; 768 list = new ArrayList (); 769 list.add(((MailboxLine)line).getMailboxEntry()); 770 } 771 final List toBeDeleted = list; 772 final Line dotLine = advanceDot ? editor.getDotLine() : null; 773 Runnable deleteRunnable = new Runnable () { 774 public void run() 775 { 776 boolean succeeded = false; 777 String errorText = "Delete failed"; 778 try { 779 succeeded = delete(toBeDeleted); 780 } 781 catch (MailException e) { 782 errorText = e.getMessage(); 783 } 784 finally { 785 if (succeeded) { 786 countMessages(); 788 Sidebar.repaintBufferListInAllFrames(); 789 if (dotLine != null) 790 advanceDot(dotLine); 791 } 792 setBusy(false); 793 unlock(); 794 editor.updateDisplayLater(); 795 if (!succeeded) 796 error(errorText, "Error"); 797 } 798 } 799 }; 800 if (lock()) { 801 setBusy(true); 802 new Thread (deleteRunnable).start(); 803 } 804 } 805 806 private boolean delete(List toBeDeleted) throws MailException 807 { 808 boolean succeeded = false; 809 if (session.verifyConnected() && session.verifySelected(folderName)) { 810 if (session.isReadOnly()) { 811 Log.debug("delete " + folderName + " is read-only - reselecting..."); 812 session.reselect(folderName); 813 if (session.isReadOnly()) 814 throw new MailException("Mailbox " + folderName + " is read-only"); 815 } 816 session.setEcho(true); 817 session.uidStore(getMessageSet(toBeDeleted), "+flags.silent (\\deleted)"); 818 if (session.getResponse() == ImapSession.OK) { 819 succeeded = true; 820 for (int i = 0; i < toBeDeleted.size(); i++) { 821 ImapMailboxEntry entry = (ImapMailboxEntry) toBeDeleted.get(i); 822 entry.setFlags(entry.getFlags() | MailboxEntry.DELETED); 823 updateEntry(entry); 824 } 825 } 826 session.setEcho(false); 827 } 828 return succeeded; 829 } 830 831 public void undelete() 832 { 833 storeFlagsInternal(ACTION_UNDELETE); 834 } 835 836 public void markRead() 837 { 838 storeFlagsInternal(ACTION_MARK_READ); 839 } 840 841 public void markUnread() 842 { 843 storeFlagsInternal(ACTION_MARK_UNREAD); 844 } 845 846 private static final int ACTION_UNDELETE = 0; 847 private static final int ACTION_MARK_UNREAD = 1; 848 private static final int ACTION_MARK_READ = 2; 849 850 private void storeFlagsInternal(final int action) 851 { 852 final Editor editor = Editor.currentEditor(); 853 boolean advanceDot = false; 854 List list = getTaggedEntries(); 855 if (list == null) { 856 Line line = editor.getDotLine(); 857 if (!(line instanceof MailboxLine)) 858 return; 859 if (action == ACTION_UNDELETE) 860 advanceDot = getBooleanProperty(Property.UNDELETE_ADVANCE_DOT); 861 else 862 advanceDot = true; 863 list = new ArrayList (); 864 list.add(((MailboxLine)line).getMailboxEntry()); 865 } 866 final List entriesToBeProcessed = list; 867 final Line dotLine = advanceDot ? editor.getDotLine() : null; 868 Runnable storeFlagsRunnable = new Runnable () { 869 public void run() 870 { 871 try { 872 if (session.verifyConnected() && session.verifySelected(folderName)) { 873 if (session.isReadOnly()) { 874 Log.debug("storeFlagsInternal - read-only - reselecting..."); 875 session.reselect(folderName); 876 if (session.isReadOnly()) { 877 readOnlyError(); 878 return; 879 } 880 } 881 session.setEcho(true); 882 final String messageSet = getMessageSet(entriesToBeProcessed); 883 switch (action) { 884 case ACTION_UNDELETE: 885 session.uidStore(messageSet, "-flags.silent (\\deleted)"); 886 break; 887 case ACTION_MARK_READ: 888 session.uidStore(messageSet, "+flags.silent (\\seen)"); 889 break; 890 case ACTION_MARK_UNREAD: 891 session.uidStore(messageSet, "-flags.silent (\\seen)"); 892 break; 893 default: 894 Debug.assertTrue(false); 895 break; 896 } 897 if (session.getResponse() == ImapSession.OK) { 898 for (int i = 0; i < entriesToBeProcessed.size(); i++) { 899 ImapMailboxEntry entry = (ImapMailboxEntry) entriesToBeProcessed.get(i); 900 switch (action) { 901 case ACTION_UNDELETE: 902 entry.setFlags(entry.getFlags() & ~MailboxEntry.DELETED); 903 break; 904 case ACTION_MARK_READ: 905 entry.setFlags(entry.getFlags() | MailboxEntry.SEEN); 906 break; 907 case ACTION_MARK_UNREAD: 908 entry.setFlags(entry.getFlags() & ~MailboxEntry.SEEN); 909 break; 910 default: 911 Debug.assertTrue(false); 912 break; 913 } 914 updateEntry(entry); 915 } 916 if (dotLine != null) 917 advanceDot(dotLine); 918 } 919 session.setEcho(false); 920 } 921 countMessages(); 922 } 923 finally { 924 setBusy(false); 925 unlock(); 926 editor.updateDisplayLater(); 927 Sidebar.repaintBufferListInAllFrames(); 929 } 930 } 931 }; 932 if (lock()) { 933 setBusy(true); 934 new Thread (storeFlagsRunnable).start(); 935 } 936 } 937 938 public void flag() 939 { 940 final Editor editor = Editor.currentEditor(); 941 boolean advanceDot = false; 942 List list = getTaggedEntries(); 943 if (list == null) { 944 Line line = editor.getDotLine(); 945 if (!(line instanceof MailboxLine)) 946 return; 947 advanceDot = true; 948 list = new ArrayList (); 949 list.add(((MailboxLine)line).getMailboxEntry()); 950 } 951 final List entriesToBeSet = new ArrayList (); 952 final List entriesToBeCleared = new ArrayList (); 953 for (int i = 0; i < list.size(); i++) { 954 MailboxEntry entry = (MailboxEntry) list.get(i); 955 if (entry.isFlagged()) 956 entriesToBeCleared.add(entry); 957 else 958 entriesToBeSet.add(entry); 959 } 960 final Line dotLine = advanceDot ? editor.getDotLine() : null; 961 Runnable flagRunnable = new Runnable () { 962 public void run() 963 { 964 try { 965 if (session.verifyConnected() && session.verifySelected(folderName)) { 966 if (session.isReadOnly()) { 967 Log.debug("storeFlagsInternal - read-only - reselecting..."); 968 session.reselect(folderName); 969 if (session.isReadOnly()) { 970 readOnlyError(); 971 return; 972 } 973 } 974 boolean error = false; 975 session.setEcho(true); 976 if (entriesToBeSet.size() > 0) { 977 session.uidStore(getMessageSet(entriesToBeSet), "+flags.silent (\\flagged)"); 978 if (session.getResponse() == ImapSession.OK) { 979 for (int i = 0; i < entriesToBeSet.size(); i++) { 980 MailboxEntry entry = (MailboxEntry) entriesToBeSet.get(i); 981 entry.flag(); 982 updateEntry(entry); 983 } 984 } else 985 error = true; 986 } 987 if (!error && entriesToBeCleared.size() > 0) { 988 session.uidStore(getMessageSet(entriesToBeCleared), "-flags.silent (\\flagged)"); 989 if (session.getResponse() == ImapSession.OK) { 990 for (int i = 0; i < entriesToBeCleared.size(); i++) { 991 MailboxEntry entry = (MailboxEntry) entriesToBeCleared.get(i); 992 entry.unflag(); 993 updateEntry(entry); 994 } 995 } else 996 error = true; 997 } 998 session.setEcho(false); 999 if (!error && dotLine != null) 1000 advanceDot(dotLine); 1001 } 1002 } 1003 finally { 1004 setBusy(false); 1005 unlock(); 1006 editor.updateDisplayLater(); 1007 Sidebar.repaintBufferListInAllFrames(); 1009 } 1010 } 1011 }; 1012 if (lock()) { 1013 setBusy(true); 1014 new Thread (flagRunnable).start(); 1015 } 1016 } 1017 1018 public void setAnsweredFlag(final MailboxEntry entry) 1019 { 1020 Runnable setAnsweredFlagRunnable = new Runnable () { 1021 public void run() 1022 { 1023 try { 1024 if (session.verifyConnected() && session.verifySelected(folderName)) { 1025 session.setEcho(true); 1026 session.uidStore(((ImapMailboxEntry)entry).getUid(), "+flags (\\answered)"); 1027 if (session.getResponse() == ImapSession.OK) { 1028 entry.setFlags(entry.getFlags() | MailboxEntry.ANSWERED); 1029 updateEntry(entry); 1030 } 1031 session.setEcho(false); 1032 } 1033 } 1034 finally { 1035 setBusy(false); 1036 unlock(); 1037 Editor.updateDisplayLater(ImapMailbox.this); 1038 } 1039 } 1040 }; 1041 if (lock()) { 1042 setBusy(true); 1043 new Thread (setAnsweredFlagRunnable).start(); 1044 } 1045 } 1046 1047 private List retrieveMessageHeaders(int uidBegin, int uidEnd, 1048 boolean recent) 1049 { 1050 Log.debug("retrieveMessageHeaders " + folderName + " " + uidBegin + 1051 " " + uidEnd); 1052 long start = System.currentTimeMillis(); 1053 uidValidity = session.getUidValidity(); 1054 messageCount = session.getMessageCount(); 1055 ArrayList list = new ArrayList (); 1056 FastStringBuffer sbCommand = new FastStringBuffer("uid fetch "); 1057 sbCommand.append(uidBegin); 1058 sbCommand.append(':'); 1059 if (uidEnd < 0) 1060 sbCommand.append('*'); 1061 else 1062 sbCommand.append(uidEnd); 1063 sbCommand.append(" (uid flags internaldate rfc822.size envelope"); 1064 sbCommand.append(" body.peek[header.fields (references)]"); 1065 sbCommand.append(')'); 1066 Log.debug("command = |" + sbCommand.toString() + "|"); 1067 session.writeTagged(sbCommand.toString()); 1068 StatusBarProgressNotifier progressNotifier = 1069 new StatusBarProgressNotifier(this); 1070 progressNotifier.progressStart(); 1071 try { 1072 FastStringBuffer sb = new FastStringBuffer(); 1073 final String endPrefix = session.lastTag() + " "; 1074 while (true) { 1075 String s = session.readLine(); 1076 if (s == null) { 1077 Log.debug("retrieveMessageHeaders s == null"); 1078 break; 1079 } 1080 if (s.startsWith(endPrefix)) 1081 break; 1082 if (s.indexOf("ENVELOPE (") >= 0) { 1083 if (sb.length() > 0) { 1085 addEntry(list, sb.toString(), uidBegin, recent); 1087 progressNotifier.progress(getProgressText(list.size())); 1088 if (cancelled) { 1089 Log.debug("retrieveMessageHeaders cancelled, disconnecting..."); 1090 session.disconnect(); 1091 return list; 1092 } 1093 sb.setLength(0); 1094 } 1095 sb.append(s); 1096 } else { 1097 sb.append("\r\n"); 1099 sb.append(s); 1100 } 1101 } 1102 if (sb.length() > 0) { 1103 addEntry(list, sb.toString(), uidBegin, recent); 1105 progressNotifier.progress(getProgressText(list.size())); 1106 } 1107 } 1108 catch (Exception e) { 1109 Log.error(e); 1110 } 1111 finally { 1112 progressNotifier.progressStop(); 1113 long elapsed = System.currentTimeMillis() - start; 1114 Log.debug("retrieveMessageHeaders " + list.size() + " messages in " + elapsed + " ms"); 1115 } 1116 return list; 1117 } 1118 1119 private void addEntry(List list, String s, int uidBegin, boolean recent) 1120 { 1121 ImapMailboxEntry entry = ImapMailboxEntry.parseEntry(this, s); 1122 if (entry != null) { 1123 if (entry.getUid() >= uidBegin) { 1124 if (recent) 1125 entry.setFlags(entry.getFlags() | MailboxEntry.RECENT); 1126 list.add(entry); 1127 } else { 1128 Log.debug("not adding message " + entry.getMessageNumber() + 1129 " uid " + entry.getUid() + " uidBegin = " + uidBegin); 1130 if (messageCount < 0) { 1131 messageCount = entry.getMessageNumber(); 1133 } else if (entry.getMessageNumber() != messageCount) 1134 Debug.bug(); 1135 } 1136 } else 1137 Log.error("can't parse envelope |" + s + "|"); 1138 } 1139 1140 private void updateLastUid() 1142 { 1143 uidLast = 0; 1144 if (entries != null) { 1145 for (int i = entries.size()-1; i >= 0; i--) { 1146 ImapMailboxEntry entry = (ImapMailboxEntry) entries.get(i); 1147 if (entry.getUid() > uidLast) 1148 uidLast = entry.getUid(); 1149 } 1150 } 1151 } 1152 1153 public void readOnlyError() 1154 { 1155 Runnable r = new Runnable () { 1156 public void run() 1157 { 1158 MessageDialog.showMessageDialog("Mailbox is read-only", 1159 "Error"); 1160 } 1161 }; 1162 SwingUtilities.invokeLater(r); 1163 } 1164 1165 private void fatal(final String text, final String title) 1166 { 1167 Runnable r = new Runnable () { 1168 public void run() 1169 { 1170 MessageDialog.showMessageDialog(Editor.currentEditor(), 1171 text, title); 1172 if (Editor.getBufferList().contains(ImapMailbox.this)) 1173 kill(); 1174 } 1175 }; 1176 SwingUtilities.invokeLater(r); 1177 } 1178 1179 private boolean getAllMessageHeaders() 1180 { 1181 Thread t = new Thread () { 1182 public void run() 1183 { 1184 mailboxCache = ImapMailboxCache.readCache(ImapMailbox.this); 1185 } 1186 }; 1187 t.start(); 1188 if (!session.verifyConnected()){ 1189 Log.error("getAllMessageHeaders can't connect to " + 1190 session.getHost() + " on port " + session.getPort()); 1191 setBusy(false); 1192 if (!cancelled) 1193 fatal(session.getErrorText(), "Error"); 1194 return false; 1195 } 1196 if (!session.verifySelected(folderName)) { 1197 Log.error("getAllMessageHeaders can't SELECT " + folderName + 1198 " on " + session.getHost()); 1199 setBusy(false); 1200 if (!cancelled) 1201 fatal(session.getErrorText(), "Error"); 1202 return false; 1203 } 1204 if (t != null) { 1205 try { 1206 t.join(); 1207 } 1208 catch (InterruptedException e) { 1209 Log.error(e); 1210 } 1211 } 1212 entries = null; 1213 uidLast = 0; 1214 if (mailboxCache != null && mailboxCache.isValid()) { 1215 Log.debug("mailboxCache is valid"); 1216 List cachedEntries = mailboxCache.getEntries(); 1217 Log.debug("cachedEntries.size() = " + cachedEntries.size()); 1218 updateCachedEntries(cachedEntries); 1219 int size = cachedEntries.size(); 1220 entries = new ArrayList (size); 1221 for (int i = 0; i < size; i++) { 1223 Object o = cachedEntries.get(i); 1224 if (o != null) 1225 entries.add(o); 1226 } 1227 Log.debug("entries.size() = " + entries.size()); 1228 mailboxCache = null; 1230 updateLastUid(); 1231 } 1232 if (cancelled) { 1233 session.disconnect(); 1234 return false; 1235 } 1236 boolean recent = uidLast > 0; 1239 final List newEntries = retrieveMessageHeaders(uidLast+1, -1, recent); 1240 if (newEntries != null && newEntries.size() > 0) { 1241 addEntriesToAddressBook(newEntries); 1242 processIncomingFilters(newEntries); 1243 if (entries != null) 1244 entries.addAll(newEntries); 1245 else 1246 entries = new ArrayList (newEntries); 1247 } 1248 uidValidity = session.getUidValidity(); 1249 if (entries == null) 1250 entries = new ArrayList (); 1251 else if (entries instanceof ArrayList ) 1252 ((ArrayList )entries).trimToSize(); 1253 new ImapMailboxCache(this).writeCache(); 1254 updateLastUid(); 1255 return true; 1256 } 1257 1258 private boolean getNewMessageHeaders() 1259 { 1260 List newEntries = retrieveMessageHeaders(uidLast+1, -1, true); 1261 if (newEntries == null || newEntries.size() == 0) 1262 return false; 1263 addEntriesToAddressBook(newEntries); 1264 processIncomingFilters(newEntries); 1265 entries.addAll(newEntries); 1266 if (entries instanceof ArrayList ) 1267 ((ArrayList )entries).trimToSize(); 1268 new ImapMailboxCache(this).writeCache(); 1269 updateLastUid(); 1270 return true; 1271 } 1272 1273 private void processIncomingFilters(List entryList) 1274 { 1275 Log.debug("processIncomingFilters"); 1276 final List filterList = IncomingFilter.getFilterList(); 1277 if (filterList == null || filterList.size() == 0) 1278 return; 1279 String inbox = Editor.preferences().getStringProperty(Property.INBOX); 1281 if (inbox == null) 1282 return; 1283 MailboxURL inboxUrl = MailboxURL.parse(inbox); 1284 if (!(inboxUrl instanceof ImapURL)) { 1285 Log.debug("processIncomingFilters not inbox " + url.toString()); 1286 return; 1287 } 1288 if (!url.getHost().equals(inboxUrl.getHost())) { 1289 Log.debug("processIncomingFilters not inbox " + url.toString()); 1290 return; 1291 } 1292 if (!folderName.equals(((ImapURL)inboxUrl).getFolderName())) { 1293 Log.debug("processIncomingFilters not inbox " + url.toString()); 1294 return; 1295 } 1296 SmtpSession smtp = null; 1297 for (int i = 0; i < entryList.size(); i++) { 1298 ImapMailboxEntry entry = (ImapMailboxEntry) entryList.get(i); 1299 if (entry.isDeleted()) 1301 continue; 1302 for (int j = 0; j < filterList.size(); j++) { 1303 IncomingFilter incomingFilter = (IncomingFilter) filterList.get(j); 1304 if (incomingFilter != null) { 1305 MailboxFilter mf = incomingFilter.getFilter(); 1306 if (mf != null && mf.accept(entry)) { 1307 switch (incomingFilter.getAction()) { 1308 case IncomingFilter.MOVE: { 1309 processMove(entry, incomingFilter.getParameter()); 1310 break; 1311 } 1312 case IncomingFilter.BOUNCE: { 1313 boolean succeeded = false; 1314 if (smtp == null) 1315 smtp = SmtpSession.getDefaultSession(); 1316 if (smtp != null) 1317 succeeded = processBounce(entry, incomingFilter.getParameter(), smtp); 1318 if (!succeeded) 1319 Log.error("processBounce failed"); 1320 break; 1321 } 1322 case IncomingFilter.BOUNCE_AND_DELETE: { 1323 boolean succeeded = false; 1324 if (smtp == null) 1325 smtp = SmtpSession.getDefaultSession(); 1326 if (smtp != null) 1327 succeeded = processBounce(entry, incomingFilter.getParameter(), smtp); 1328 if (succeeded) 1329 processDelete(entry); 1330 else 1331 Log.error("processBounce failed"); 1332 break; 1333 } 1334 default: 1335 break; 1336 } 1337 break; 1338 } 1339 } 1340 } 1341 } 1342 if (smtp != null) 1344 smtp.quit(); 1345 } 1346 1347 private void processMove(ImapMailboxEntry entry, String destination) 1348 { 1349 if (destination != null) { 1350 Log.debug("destination = |" + destination + "|"); 1351 ArrayList list = new ArrayList (1); 1352 list.add(entry); 1353 try { 1354 Log.debug("processMove calling moveToFolder"); 1355 moveToFolder(list, destination); 1356 Log.debug("processMove back from moveToFolder"); 1357 } 1358 catch (Exception e) { 1359 Log.error(e); 1360 } 1361 } 1362 } 1363 1364 private boolean processBounce(ImapMailboxEntry entry, String bounceTo, SmtpSession smtp) 1365 { 1366 if (bounceTo == null) 1367 return false; 1368 Log.debug("bounceTo = |" + bounceTo + "|"); 1369 MailAddress[] to = MailAddress.parseAddresses(bounceTo); 1370 if (to == null) 1371 return false; 1372 if (to.length == 0) 1373 return false; 1374 Message message = getMessage(entry, null); 1375 if (message == null) { 1376 Log.error("processBounce getMessage() failed"); 1377 return false; 1378 } 1379 return Mail.bounceMessage(message, to, smtp); 1380 } 1381 1382 private void processDelete(ImapMailboxEntry entry) 1383 { 1384 ArrayList list = new ArrayList (1); 1385 list.add(entry); 1386 try { 1387 delete(list); 1388 } 1389 catch (Exception e) { 1390 Log.error(e); 1391 } 1392 } 1393 1394 private void updateCachedEntries(List cachedEntries) 1395 { 1396 if (cachedEntries == null) 1397 return; 1398 final int size = cachedEntries.size(); 1399 if (size == 0) 1400 return; 1401 long start = System.currentTimeMillis(); 1402 session.writeTagged("uid fetch 1:* (uid flags)"); 1403 HashMap map = new HashMap (size); 1404 Iterator iter = cachedEntries.iterator(); 1405 while (iter.hasNext()) { 1406 ImapMailboxEntry entry = (ImapMailboxEntry) iter.next(); 1407 map.put(new Integer (entry.getUid()), entry); 1408 } 1409 Log.debug("built map " + (System.currentTimeMillis() - start) + " ms"); 1410 iter = null; 1411 final String endPrefix = session.lastTag() + " "; 1412 while (true) { 1414 final String s = session.readLine(); 1415 if (s == null) { 1416 Log.debug("updateCachedEntries s is null"); 1417 break; 1418 } 1419 if (s.startsWith(endPrefix)) 1420 break; 1421 int uid = ImapMailboxEntry.parseUid(s); 1422 if (uid == 0) { 1423 Log.debug("uid = 0 s = |" + s + "|"); 1424 continue; 1425 } 1426 ImapMailboxEntry entry = 1427 (ImapMailboxEntry) map.get(new Integer (uid)); 1428 if (entry != null) { 1429 Debug.assertTrue(entry.getMailbox() == null); 1430 entry.setMailbox(this); 1431 entry.setFlags(ImapMailboxEntry.parseFlags(s)); 1432 } 1433 } 1434 map.clear(); 1435 for (int i = 0; i < size; i++) { 1438 ImapMailboxEntry entry = (ImapMailboxEntry) cachedEntries.get(i); 1439 if (entry.getMailbox() == null) 1440 cachedEntries.set(i, null); 1441 } 1442 long elapsed = System.currentTimeMillis() - start; 1443 Log.debug("updateCachedEntries " + elapsed + " ms " + 1444 (float)elapsed/cachedEntries.size() + " ms per entry"); 1445 } 1446 1447 public void readMessage(Line line) 1448 { 1449 readMessage(line, false); 1450 } 1451 1452 public void readMessageOtherWindow(Line line) 1453 { 1454 readMessage(line, true); 1455 } 1456 1457 private void readMessage(Line line, boolean useOtherWindow) 1458 { 1459 Editor editor = Editor.currentEditor(); 1460 ImapMailboxEntry entry = 1461 (ImapMailboxEntry) ((MailboxLine)line).getMailboxEntry(); 1462 Buffer buf = null; 1463 for (BufferIterator it = new BufferIterator(); it.hasNext();) { 1464 Buffer b = it.nextBuffer(); 1465 if (b instanceof ImapMessageBuffer) { 1466 ImapMessageBuffer mb = (ImapMessageBuffer) b; 1467 if (mb.getMailboxEntry() == entry) { 1468 buf = b; 1469 break; 1470 } 1471 } 1472 } 1473 if (buf != null) { 1474 activateMessageBuffer(editor, (ImapMessageBuffer) buf, 1476 useOtherWindow); 1477 return; 1478 } 1479 if (getBooleanProperty(Property.IMAP_USE_LOCAL_CACHE)) { 1480 String rawText = getMessageTextFromCache(entry.getUid()); 1481 if (rawText != null) { 1482 ImapMessageBuffer mb = 1483 new ImapMessageBuffer(this, entry, rawText); 1484 activateMessageBuffer(editor, mb, useOtherWindow); 1485 if ((entry.getFlags() & MailboxEntry.SEEN) == 0) { 1486 if (session.isReadOnly()) 1487 markReadLocal(entry); 1488 else 1489 markRead(entry); 1490 } 1491 return; 1492 } 1493 } 1494 if (lock()) { 1497 setBusy(true); 1498 ImapMessageBuffer mb = new ImapMessageBuffer(this, entry); 1499 activateMessageBuffer(editor, mb, useOtherWindow); 1500 } else 1501 editor.status("Mailbox is locked"); 1502 } 1503 1504 private void markRead(final ImapMailboxEntry entry) 1505 { 1506 Runnable markReadRunnable = new Runnable () { 1507 public void run() 1508 { 1509 try { 1510 if (session.verifyConnected() && session.verifySelected(folderName)) { 1511 session.uidStore(entry.getUid(), "+flags.silent (\\seen)"); 1512 if (session.getResponse() == ImapSession.OK) 1513 markReadLocal(entry); 1514 } 1515 } 1516 finally { 1517 setBusy(false); 1518 unlock(); 1519 Editor.updateDisplayLater(ImapMailbox.this); 1520 } 1521 } 1522 }; 1523 if (lock()) { 1524 setBusy(true); 1525 new Thread (markReadRunnable).start(); 1526 } 1527 } 1528 1529 private void markReadLocal(ImapMailboxEntry entry) 1530 { 1531 entry.setFlags(entry.getFlags() | MailboxEntry.SEEN); 1532 updateEntry(entry); 1533 countMessages(); 1534 } 1535 1536 public ImapMailboxEntry getMailboxEntry(String messageId) 1537 { 1538 for (int i = entries.size()-1; i >= 0; i--) { 1539 ImapMailboxEntry entry = (ImapMailboxEntry) entries.get(i); 1540 if (messageId.equals(entry.getMessageId())) 1541 return entry; 1542 } 1543 return null; 1544 } 1545 1546 public Message getMessage(MailboxEntry entry, 1547 ProgressNotifier progressNotifier) 1548 { 1549 if (!(entry instanceof ImapMailboxEntry)) 1550 return null; 1551 final int uid = ((ImapMailboxEntry)entry).getUid(); 1552 if (getBooleanProperty(Property.IMAP_USE_LOCAL_CACHE)) { 1553 String rawText = getMessageTextFromCache(uid); 1554 if (rawText != null) 1555 return new Message(rawText); 1556 } 1557 if (!isLocked()) 1558 Debug.bug("ImapMailbox.getMessage mailbox is not locked!"); 1559 if (!session.verifyConnected()) 1560 return null; 1561 if (progressNotifier != null && progressNotifier.cancelled()) 1562 return null; 1563 if (!session.verifySelected(folderName)) 1564 return null; 1565 if (progressNotifier != null && progressNotifier.cancelled()) 1566 return null; 1567 String header = fetchPart(uid, "header", null, progressNotifier); 1568 if (header == null) 1569 return null; 1570 Headers headers = Headers.parse(header); 1571 String charset = null; 1572 String contentType = headers.getValue(Headers.CONTENT_TYPE); 1573 if (contentType != null) 1574 charset = Utilities.getCharsetFromContentType(contentType); 1575 Log.debug("charset = " + charset); 1576 String encoding = Utilities.getEncodingFromCharset(charset); 1577 if (encoding.equalsIgnoreCase("us-ascii")) 1578 encoding = null; 1579 else if (encoding.equalsIgnoreCase("iso-8859-1")) 1580 encoding = null; 1581 String body = fetchPart(uid, "text", encoding, progressNotifier); 1582 if (body == null) 1583 return null; 1584 if (getBooleanProperty(Property.IMAP_USE_LOCAL_CACHE)) 1585 cacheMessage(uid, header + body, encoding); 1586 return new Message(header.concat(body), headers); 1587 } 1588 1589 private String fetchPart(int uid, String part, String encoding, 1590 ProgressNotifier progressNotifier) 1591 { 1592 FastStringBuffer sb = new FastStringBuffer("uid fetch "); 1593 sb.append(uid); 1594 sb.append(" body["); 1595 sb.append(part); 1596 sb.append(']'); 1597 session.writeTagged(sb.toString()); 1598 sb = null; 1599 int length = -1; 1600 try { 1601 if (progressNotifier != null && progressNotifier.cancelled()) { 1602 session.disconnect(); 1603 return null; 1604 } 1605 String s = session.readLine(); 1606 if (s == null) 1607 return null; 1608 if (s.startsWith("* ")) { 1609 int index = s.indexOf('('); 1610 if (index < 0) { 1611 Log.error("can't parse response s = |" + s + "|"); 1612 return null; 1613 } 1614 String before = s.substring(0, index).trim(); 1615 if (!before.endsWith(" FETCH")) { 1616 Log.error("no FETCH"); 1617 return null; 1618 } 1619 String after = s.substring(index); 1620 int begin = after.indexOf('{'); 1621 if (begin < 0) { 1622 Log.error("no '{'"); 1623 Log.error("s = |" + s + "|"); 1624 begin = after.indexOf('"'); 1625 if (begin < 0) 1626 return null; 1627 else 1628 return parseQuotedString(after.substring(begin)); 1629 } 1630 int end = after.indexOf('}', begin + 1); 1631 if (end < 0) { 1632 Log.error("no '}'"); 1633 return null; 1634 } 1635 try { 1636 length = Integer.parseInt(after.substring(begin + 1, end)); 1637 } 1638 catch (NumberFormatException e) { 1639 Log.error(e); 1640 } 1641 } 1642 if (length < 0) { 1643 Log.error("can't determine length"); 1644 Log.error("s = |" + s + "|"); 1645 return null; 1646 } 1647 sb = new FastStringBuffer(length + 64); 1648 while (true) { 1649 if (progressNotifier != null && progressNotifier.cancelled()) { 1650 session.disconnect(); 1651 return null; 1652 } 1653 s = session.readLine(); 1654 if (s == null) 1655 break; 1656 if (s.startsWith(session.lastTag() + " OK")) 1657 break; 1658 if (encoding != null) { 1660 int len = s.length(); 1662 byte[] bytes = new byte[len]; 1663 for (int i = 0; i < len; i++) 1664 bytes[i] = (byte) s.charAt(i); 1665 try { 1666 s = new String (bytes, encoding); 1667 } 1668 catch (UnsupportedEncodingException e) { 1669 Log.debug(e); 1670 encoding = null; 1672 } 1673 } 1674 sb.append(s); 1675 sb.append("\r\n"); 1676 if (progressNotifier != null) 1677 progressNotifier.progress("Received ", sb.length(), length); 1678 } 1679 } 1680 catch (Exception e) { 1681 Log.error(e); 1682 } 1683 if (sb != null) { 1684 Log.debug("advertised length = " + length); 1685 Log.debug("actual length = " + sb.length()); 1686 sb.setLength(length); 1687 return sb.toString(); 1688 } else 1689 return null; 1690 } 1691 1692 private String parseQuotedString(final String s) 1693 { 1694 Debug.assertTrue(s.length() > 0); 1695 Debug.assertTrue(s.charAt(0) == '"'); 1696 FastStringBuffer sb = new FastStringBuffer(); 1697 final int length = s.length(); 1698 for (int i = 1; i < length; i++) { 1699 char c = s.charAt(i); 1700 if (c == '\\' && i+1 < length) 1701 sb.append(s.charAt(++i)); 1702 else if (c == '"') 1703 break; 1704 else 1705 sb.append(c); 1706 } 1707 return sb.toString(); 1708 } 1709 1710 public void dispose() 1711 { 1712 Log.debug("ImapMailbox.dispose " + folderName + " on " + 1713 session.getHost()); 1714 Runnable r = new Runnable () { 1715 public void run() 1716 { 1717 session.logout(); 1718 } 1719 }; 1720 new Thread (r).start(); 1721 MailboxProperties.saveProperties(this); 1722 } 1723 1724 protected void finalize() throws Throwable 1725 { 1726 Log.debug("ImapMailbox.finalize " + folderName + " on " + 1727 session.getHost()); 1728 super.finalize(); 1729 } 1730 1731 private String getProgressText(int n) 1732 { 1733 FastStringBuffer sb = new FastStringBuffer(32); 1734 sb.append("Retrieved "); 1735 sb.append(n); 1736 sb.append(" message header"); 1737 if (n > 1) 1738 sb.append('s'); 1739 return sb.toString(); 1740 } 1741 1742 static String getMessageSet(List list) 1744 { 1745 FastStringBuffer sb = new FastStringBuffer(); 1746 int limit = list.size(); 1747 int begin = -1; 1748 int end = -1; 1749 for (int i = 0; i < limit; i++) { 1750 ImapMailboxEntry entry = (ImapMailboxEntry) list.get(i); 1751 if (begin < 0) { 1752 begin = entry.getUid(); 1753 end = entry.getUid(); 1754 } else if (entry.getUid() == end + 1) { 1755 end = entry.getUid(); 1756 } else { 1757 if (sb.length() > 0) 1758 sb.append(','); 1759 if (begin != end) { 1760 Debug.assertTrue(end > begin); 1761 sb.append(begin); 1762 sb.append(':'); 1763 sb.append(end); 1764 begin = end = entry.getUid(); 1765 } else { 1766 sb.append(begin); 1767 begin = end = entry.getUid(); 1768 } 1769 } 1770 } 1771 if (sb.length() > 0) 1772 sb.append(','); 1773 if (begin != end) { 1774 Debug.assertTrue(end > begin); 1775 sb.append(begin); 1776 sb.append(':'); 1777 sb.append(end); 1778 } else 1779 sb.append(begin); 1780 return sb.toString(); 1781 } 1782 1783 public String toString() 1784 { 1785 int newMessageCount = getNewMessageCount(); 1786 if (newMessageCount > 0) { 1787 FastStringBuffer sb = new FastStringBuffer(url.toString()); 1788 sb.append(" ("); 1789 sb.append(newMessageCount); 1790 sb.append(" new)"); 1791 return sb.toString(); 1792 } else 1793 return url.toString(); 1794 } 1795 1796 public String getTitle() 1797 { 1798 return toString(); 1799 } 1800 1801 private ImapMessageCache messageCache; 1802 1803 private void cacheMessage(int uid, String message, String encoding) 1804 { 1805 if (messageCache != null) { 1806 if (messageCache.getUidValidity() != session.getUidValidity()) 1807 messageCache = null; 1808 } 1809 if (messageCache == null) { 1810 messageCache = ImapMessageCache.getMessageCache(this); 1811 if (messageCache == null) 1812 return; 1813 } 1814 messageCache.store(uid, message, encoding); 1815 } 1816 1817 private String getMessageTextFromCache(int uid) 1818 { 1819 if (messageCache != null) { 1820 if (messageCache.getUidValidity() != session.getUidValidity()) 1821 messageCache = null; 1822 } 1823 if (messageCache == null) { 1824 messageCache = ImapMessageCache.getMessageCache(this); 1825 if (messageCache == null) 1826 return null; 1827 } 1828 return messageCache.getMessageText(uid); 1829 } 1830} 1831 | Popular Tags |