1 19 package org.columba.mail.folder; 20 21 import java.io.File ; 22 import java.io.InputStream ; 23 import java.nio.charset.Charset ; 24 import java.nio.charset.UnsupportedCharsetException ; 25 import java.util.logging.Logger ; 26 27 import org.columba.api.command.IStatusObservable; 28 import org.columba.core.command.StatusObservableImpl; 29 import org.columba.core.filter.FilterList; 30 import org.columba.core.filter.IFilter; 31 import org.columba.core.filter.IFilterList; 32 import org.columba.core.io.DiskIO; 33 import org.columba.core.xml.XmlElement; 34 import org.columba.mail.config.FolderItem; 35 import org.columba.mail.config.IFolderItem; 36 import org.columba.mail.folder.command.MarkMessageCommand; 37 import org.columba.mail.folder.event.FolderEvent; 38 import org.columba.mail.folder.event.IFolderListener; 39 import org.columba.mail.folder.search.DefaultSearchEngine; 40 import org.columba.mail.message.ICloseableIterator; 41 import org.columba.mail.message.IColumbaHeader; 42 import org.columba.mail.message.IHeaderList; 43 import org.columba.ristretto.coder.Base64DecoderInputStream; 44 import org.columba.ristretto.coder.CharsetDecoderInputStream; 45 import org.columba.ristretto.coder.QuotedPrintableDecoderInputStream; 46 import org.columba.ristretto.message.Attributes; 47 import org.columba.ristretto.message.Flags; 48 import org.columba.ristretto.message.MailboxInfo; 49 import org.columba.ristretto.message.MimeHeader; 50 51 80 public abstract class AbstractMessageFolder extends AbstractFolder implements 81 IMailbox { 82 83 84 private static final Logger LOG = Logger 85 .getLogger("org.columba.mail.folder"); 86 87 90 protected IMailboxInfo messageFolderInfo = new ColumbaMailboxInfo(); 91 92 95 protected FilterList filterList; 96 97 101 protected boolean changed = false; 102 103 106 protected File directoryFile; 107 108 115 protected Object lastSelection; 116 117 123 protected IStatusObservable observable = new StatusObservableImpl(); 124 125 protected DefaultSearchEngine searchEngine; 127 128 135 public AbstractMessageFolder(FolderItem item, String path) { 136 super(item); 137 138 String dir = path + System.getProperty("file.separator") + getId(); 139 140 if (DiskIO.ensureDirectory(dir)) { 141 directoryFile = new File (dir); 142 } 143 144 loadMessageFolderInfo(); 145 } 146 147 public AbstractMessageFolder(FolderItem item) { 149 super(item); 150 151 loadMessageFolderInfo(); 152 } 153 154 protected AbstractMessageFolder() { 155 super(); 156 } 157 158 161 public AbstractMessageFolder(String name, String type, String path) { 162 super(name, type); 163 164 String dir = path + System.getProperty("file.separator") + getId(); 165 166 if (DiskIO.ensureDirectory(dir)) { 167 directoryFile = new File (dir); 168 } 169 170 loadMessageFolderInfo(); 171 } 172 173 public AbstractMessageFolder(String name, String type) { 175 super(name, type); 176 177 loadMessageFolderInfo(); 178 } 179 180 protected void recreateMessageFolderInfo() { 181 messageFolderInfo.reset(); 182 183 try { 184 ICloseableIterator it = getHeaderList().headerIterator(); 185 186 while (it.hasNext()) { 187 Flags flags = ((IColumbaHeader) it.next()).getFlags(); 188 messageFolderInfo.incExists(); 189 if (flags.getRecent()) { 190 messageFolderInfo.incRecent(); 191 } 192 if (!flags.getSeen()) { 193 messageFolderInfo.incUnseen(); 194 } 195 } 196 197 } catch (Exception e) { 198 LOG.severe(e.getLocalizedMessage()); 199 } 200 201 } 202 203 209 public void fireMessageAdded(Object uid, Flags flags) { 210 try { 211 getMessageFolderInfo().incExists(); 212 213 if (flags.getRecent()) { 214 getMessageFolderInfo().incRecent(); 215 } 216 if (!flags.getSeen()) { 217 getMessageFolderInfo().incUnseen(); 218 } 219 } catch (MailboxInfoInvalidException e) { 220 recreateMessageFolderInfo(); 221 } 222 setChanged(true); 223 224 fireFolderPropertyChanged(); 226 227 FolderEvent e = new FolderEvent(this, uid); 228 Object [] listeners = listenerList.getListenerList(); 230 231 for (int i = listeners.length - 2; i >= 0; i -= 2) { 234 if (listeners[i] == IFolderListener.class) { 235 ((IFolderListener) listeners[i + 1]).messageAdded(e); 236 } 237 } 238 } 239 240 244 public void fireMessageRemoved(Object uid, Flags flags) { 245 246 try { 247 if (flags != null) { 248 if (flags.getRecent()) { 249 getMessageFolderInfo().decRecent(); 250 } 251 if (!flags.getSeen()) { 252 getMessageFolderInfo().decUnseen(); 253 } 254 } 255 getMessageFolderInfo().decExists(); 256 } catch (MailboxInfoInvalidException e) { 257 recreateMessageFolderInfo(); 258 } 259 260 setChanged(true); 261 262 fireFolderPropertyChanged(); 264 265 FolderEvent e = new FolderEvent(this, uid); 266 Object [] listeners = listenerList.getListenerList(); 268 269 for (int i = listeners.length - 2; i >= 0; i -= 2) { 272 if (listeners[i] == IFolderListener.class) { 273 ((IFolderListener) listeners[i + 1]).messageRemoved(e); 274 } 275 } 276 } 277 278 282 public void fireMessageFlagChanged(Object uid, Flags oldFlags, int variant) { 283 284 setChanged(true); 289 290 FolderEvent e = new FolderEvent(this, uid, oldFlags, variant); 291 292 Object [] listeners = listenerList.getListenerList(); 294 295 for (int i = listeners.length - 2; i >= 0; i -= 2) { 298 if (listeners[i] == IFolderListener.class) { 299 ((IFolderListener) listeners[i + 1]).messageFlagChanged(e); 300 } 301 } 302 } 303 304 309 public File getDirectoryFile() { 310 return directoryFile; 311 } 312 313 319 public void setChanged(boolean b) { 320 changed = b; 321 } 322 323 329 public void setMessageFolderInfo(MailboxInfo i) { 330 messageFolderInfo = new ColumbaMailboxInfo(i); 331 } 332 333 338 protected boolean hasChanged() { 339 return changed; 340 } 341 342 347 public IMailboxInfo getMessageFolderInfo() { 348 return messageFolderInfo; 349 } 350 351 356 public IFilterList getFilterList() { 357 return filterList; 358 } 359 360 361 364 public String toString() { 365 return getName(); 366 } 367 368 372 protected void saveMessageFolderInfo() { 373 IMailboxInfo info = getMessageFolderInfo(); 374 375 IFolderItem item = getConfiguration(); 376 377 XmlElement property = item.getElement("property"); 378 379 property.addAttribute("exists", new Integer (info.getExists()) 380 .toString()); 381 property.addAttribute("unseen", new Integer (info.getUnseen()) 382 .toString()); 383 387 property.addAttribute("recent", new Integer (info.getRecent()) 388 .toString()); 389 390 if (info.getUidNext() != -1) { 391 property.addAttribute("uidnext", new Integer (info.getUidNext()) 392 .toString()); 393 property.addAttribute("uidvalidity", new Integer (info 394 .getUidValidity()).toString()); 395 } 396 397 } 398 399 404 protected void loadMessageFolderInfo() { 405 XmlElement property = getConfiguration().getElement("property"); 406 407 if (property == null) { 408 return; 409 } 410 411 IMailboxInfo info = getMessageFolderInfo(); 412 413 String exists = property.getAttribute("exists"); 414 415 if (exists != null) { 416 info.setExists(Integer.parseInt(exists)); 417 } 418 419 String recent = property.getAttribute("recent"); 420 421 if (recent != null) { 422 info.setRecent(Integer.parseInt(recent)); 423 } 424 425 String unseen = property.getAttribute("unseen"); 426 427 if (unseen != null) { 428 info.setUnseen(Integer.parseInt(unseen)); 429 } 430 431 String uidnext = property.getAttribute("uidnext"); 432 433 if (uidnext != null) { 434 info.setUidNext(Integer.parseInt(uidnext)); 435 } 436 437 String uidvalidity = property.getAttribute("uidvalidty"); 438 439 if (uidvalidity != null) { 440 info.setUidValidity(Integer.parseInt(uidvalidity)); 441 } 442 443 if (!info.isSane()) { 445 recreateMessageFolderInfo(); 446 } 447 448 } 449 450 455 public void save() throws Exception { 456 saveMessageFolderInfo(); 457 if (getSearchEngine() != null) { 458 getSearchEngine().save(); 459 } 460 } 461 462 467 public Object getLastSelection() { 468 return lastSelection; 469 } 470 471 475 public void setLastSelection(Object lastSel) { 476 lastSelection = lastSel; 477 } 478 479 482 public IStatusObservable getObservable() { 483 return observable; 484 } 485 486 489 public boolean supportsAddMessage() { 490 return true; 491 } 492 493 498 public boolean isInboxFolder() { 499 return false; 500 } 501 502 507 public boolean isTrashFolder() { 508 return false; 509 } 510 511 protected void updateMailFolderInfo(Flags flags, int variant) 512 throws Exception { 513 boolean updated = false; 514 515 if (flags == null) { 516 return; 517 } 518 519 try { 520 switch (variant) { 521 case MarkMessageCommand.MARK_AS_READ: { 522 if (flags.getRecent()) { 523 getMessageFolderInfo().decRecent(); 524 updated = true; 525 } 526 527 if (!flags.getSeen()) { 528 getMessageFolderInfo().decUnseen(); 529 updated = true; 530 } 531 532 break; 533 } 534 535 case MarkMessageCommand.MARK_AS_UNREAD: { 536 if (flags.getSeen()) { 537 getMessageFolderInfo().incUnseen(); 538 updated = true; 539 } 540 541 break; 542 } 543 544 case MarkMessageCommand.MARK_AS_EXPUNGED: { 545 if (!flags.getSeen()) { 546 getMessageFolderInfo().decUnseen(); 547 updated = true; 548 } 549 550 if (flags.getRecent()) { 551 getMessageFolderInfo().decRecent(); 552 updated = true; 553 } 554 555 break; 556 } 557 558 case MarkMessageCommand.MARK_AS_RECENT: { 559 if (!flags.getRecent()) { 560 getMessageFolderInfo().incRecent(); 561 updated = true; 562 } 563 564 break; 565 } 566 case MarkMessageCommand.MARK_AS_NOTRECENT: { 567 if (flags.getRecent()) { 568 getMessageFolderInfo().decRecent(); 569 updated = true; 570 } 571 572 break; 573 } 574 575 } 576 } catch (MailboxInfoInvalidException e) { 577 recreateMessageFolderInfo(); 578 } 579 580 if (updated) 582 fireFolderPropertyChanged(); 583 } 584 585 591 protected void markMessage(Object uid, int variant) throws Exception { 592 IColumbaHeader header = getHeaderList().get(uid); 593 594 Flags flags = header.getFlags(); 595 596 updateMailFolderInfo(flags, variant); 597 598 if (flags == null) { 599 return; 600 } 601 Flags oldFlags = (Flags) flags.clone(); 602 603 switch (variant) { 604 case MarkMessageCommand.MARK_AS_READ: { 605 606 flags.setSeen(true); 607 flags.setRecent(false); 608 609 break; 610 } 611 612 case MarkMessageCommand.MARK_AS_UNREAD: { 613 614 flags.setSeen(false); 615 616 break; 617 } 618 619 case MarkMessageCommand.MARK_AS_FLAGGED: { 620 flags.setFlagged(true); 621 622 break; 623 } 624 625 case MarkMessageCommand.MARK_AS_UNFLAGGED: { 626 flags.setFlagged(false); 627 628 break; 629 } 630 631 case MarkMessageCommand.MARK_AS_EXPUNGED: { 632 633 flags.setSeen(true); 634 flags.setRecent(false); 635 flags.setDeleted(true); 636 637 break; 638 } 639 640 case MarkMessageCommand.MARK_AS_UNEXPUNGED: { 641 flags.setDeleted(false); 642 643 break; 644 } 645 646 case MarkMessageCommand.MARK_AS_ANSWERED: { 647 flags.setAnswered(true); 648 649 break; 650 } 651 case MarkMessageCommand.MARK_AS_UNANSWERED: { 652 flags.setAnswered(false); 653 654 break; 655 } 656 657 case MarkMessageCommand.MARK_AS_SPAM: { 658 header.getAttributes().put("columba.spam", Boolean.TRUE); 659 660 break; 661 } 662 663 case MarkMessageCommand.MARK_AS_NOTSPAM: { 664 header.getAttributes().put("columba.spam", Boolean.FALSE); 665 666 break; 667 } 668 case MarkMessageCommand.MARK_AS_DRAFT: { 669 flags.setDraft(true); 670 671 break; 672 } 673 case MarkMessageCommand.MARK_AS_NOTDRAFT: { 674 flags.setDraft(false); 675 676 break; 677 } 678 case MarkMessageCommand.MARK_AS_RECENT: { 679 flags.setRecent(true); 680 681 break; 682 } 683 case MarkMessageCommand.MARK_AS_NOTRECENT: { 684 flags.setRecent(false); 685 686 break; 687 } 688 } 689 setChanged(true); 690 691 header.setFlags(flags); 692 getHeaderList().update(uid, header); 693 694 fireMessageFlagChanged(uid, oldFlags, variant); 695 } 696 697 701 public void markMessage(Object [] uids, int variant) throws Exception { 702 for (int i = 0; i < uids.length; i++) { 703 if (exists(uids[i])) { 704 markMessage(uids[i], variant); 705 706 } 707 } 708 } 709 710 711 public void expungeFolder() throws Exception { 712 713 Object [] uids = getUids(); 715 716 for (int i = 0; i < uids.length; i++) { 717 Object uid = uids[i]; 718 719 if (uid == null) { 720 continue; 721 } 722 723 if (!exists(uid)) { 725 LOG.info("uid " + uid + " doesn't exist"); 726 727 continue; 728 } 729 730 if (getFlags(uid).getDeleted()) { 731 LOG.info("removing uid=" + uid); 733 734 removeMessage(uid); 736 } 737 } 738 } 739 740 753 public void removeMessage(Object uid) throws Exception { 754 IColumbaHeader header = getHeaderList().remove(uid); 756 757 fireMessageRemoved(uid, header.getFlags()); 759 } 760 761 762 763 766 770 public boolean exists(Object uid) throws Exception { 771 return getHeaderList().exists(uid); 772 } 773 774 777 public Object [] getUids() throws Exception { 778 return getHeaderList().getUids(); 779 } 780 781 785 public void setAttribute(Object uid, String key, Object value) 786 throws Exception { 787 788 IColumbaHeader header = getHeaderList().get(uid); 789 790 header.getAttributes().put(key, value); 791 792 getHeaderList().update(uid, header); 793 794 setChanged(true); 798 } 799 800 803 public Flags getFlags(Object uid) throws Exception { 804 return getHeaderList().get(uid).getFlags(); 805 } 806 807 810 public Attributes getAttributes(Object uid) throws Exception { 811 return getHeaderList().get(uid).getAttributes(); 812 } 813 814 818 public Object getAttribute(Object uid, String key) throws Exception { 819 return getHeaderList().get(uid).getAttributes().get(key); 820 } 821 822 825 public DefaultSearchEngine getSearchEngine() { 826 return this.searchEngine; 827 } 828 829 835 public Object [] searchMessages(IFilter filter, Object [] uids) 836 throws Exception { 837 if (getSearchEngine() == null) 839 return (new DefaultSearchEngine(this)).searchMessages(filter, uids); 840 return getSearchEngine().searchMessages(filter, uids); 841 } 842 843 848 public Object [] searchMessages(IFilter filter) throws Exception { 849 if (getSearchEngine() == null) 851 return (new DefaultSearchEngine(this)).searchMessages(filter); 852 return getSearchEngine().searchMessages(filter); 853 } 854 855 863 public void setSearchEngine(DefaultSearchEngine engine) { 864 this.searchEngine = engine; 865 } 866 867 874 protected InputStream decodeStream(MimeHeader header, InputStream bodyStream) { 875 String charsetName = header.getContentParameter("charset"); 876 int encoding = header.getContentTransferEncoding(); 877 878 switch (encoding) { 879 case MimeHeader.QUOTED_PRINTABLE: { 880 bodyStream = new QuotedPrintableDecoderInputStream(bodyStream); 881 882 break; 883 } 884 885 case MimeHeader.BASE64: { 886 bodyStream = new Base64DecoderInputStream(bodyStream); 887 888 break; 889 } 890 } 891 892 if (charsetName != null) { 893 Charset charset; 894 895 try { 896 charset = Charset.forName(charsetName); 897 } catch (UnsupportedCharsetException e) { 898 charset = Charset.forName(System.getProperty("file.encoding")); 899 } 900 901 bodyStream = new CharsetDecoderInputStream(bodyStream, charset); 902 } 903 904 return bodyStream; 905 } 906 907 910 public boolean isReadOnly() { 911 return false; 912 } 913 914 917 public abstract IHeaderList getHeaderList() throws Exception ; 918 919 } | Popular Tags |