0001: // The contents of this file are subject to the Mozilla Public License Version
0002: // 1.1
0003: //(the "License"); you may not use this file except in compliance with the
0004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
0005: //
0006: //Software distributed under the License is distributed on an "AS IS" basis,
0007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0008: //for the specific language governing rights and
0009: //limitations under the License.
0010: //
0011: //The Original Code is "The Columba Project"
0012: //
0013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
0014: // Stich.
0015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
0016: //
0017: //All Rights Reserved.
0018:
0019: package org.columba.mail.folder.imap;
0020:
0021: import java.io.File;
0022: import java.io.IOException;
0023: import java.io.InputStream;
0024: import java.util.ArrayList;
0025: import java.util.Arrays;
0026: import java.util.Collections;
0027: import java.util.LinkedList;
0028: import java.util.List;
0029: import java.util.logging.Logger;
0030:
0031: import org.columba.api.command.IStatusObservable;
0032: import org.columba.core.base.ListTools;
0033: import org.columba.core.command.CommandCancelledException;
0034: import org.columba.core.command.CommandProcessor;
0035: import org.columba.core.connectionstate.ConnectionStateImpl;
0036: import org.columba.core.xml.XmlElement;
0037: import org.columba.mail.command.MailFolderCommandReference;
0038: import org.columba.mail.config.FolderItem;
0039: import org.columba.mail.config.IFolderItem;
0040: import org.columba.mail.config.ImapItem;
0041: import org.columba.mail.folder.AbstractRemoteFolder;
0042: import org.columba.mail.folder.IHeaderListCorruptedListener;
0043: import org.columba.mail.folder.IMailFolder;
0044: import org.columba.mail.folder.IMailbox;
0045: import org.columba.mail.folder.RootFolder;
0046: import org.columba.mail.folder.command.ApplyFilterCommand;
0047: import org.columba.mail.folder.headercache.BerkeleyDBHeaderList;
0048: import org.columba.mail.folder.headercache.CachedHeaderfields;
0049: import org.columba.mail.folder.search.DefaultSearchEngine;
0050: import org.columba.mail.folder.search.IMAPQueryEngine;
0051: import org.columba.mail.imap.IImapServer;
0052: import org.columba.mail.message.ColumbaHeader;
0053: import org.columba.mail.message.ICloseableIterator;
0054: import org.columba.mail.message.IColumbaHeader;
0055: import org.columba.mail.message.IHeaderList;
0056: import org.columba.mail.message.IPersistantHeaderList;
0057: import org.columba.mail.parser.PassiveHeaderParserInputStream;
0058: import org.columba.mail.util.MailResourceLoader;
0059: import org.columba.ristretto.imap.IMAPException;
0060: import org.columba.ristretto.imap.IMAPFlags;
0061: import org.columba.ristretto.imap.MailboxStatus;
0062: import org.columba.ristretto.imap.SearchKey;
0063: import org.columba.ristretto.imap.SequenceEntry;
0064: import org.columba.ristretto.imap.SequenceSet;
0065: import org.columba.ristretto.message.Attributes;
0066: import org.columba.ristretto.message.Flags;
0067: import org.columba.ristretto.message.Header;
0068: import org.columba.ristretto.message.MimeTree;
0069:
0070: public class IMAPFolder extends AbstractRemoteFolder {
0071:
0072: /** JDK 1.4+ logging framework logger, used for logging. */
0073: private static final Logger LOG = Logger
0074: .getLogger("org.columba.mail.folder.imap");
0075:
0076: /**
0077: *
0078: */
0079: private IImapServer store;
0080:
0081: private IStatusObservable observable;
0082:
0083: /**
0084: *
0085: */
0086: protected boolean existsOnServer = true;
0087:
0088: private boolean readOnly;
0089:
0090: private IPersistantHeaderList headerList;
0091:
0092: private boolean firstSync = true;
0093:
0094: private boolean mailboxSyncEnabled = true;
0095:
0096: /**
0097: * @see org.columba.mail.folder.IMailbox#isReadOnly()
0098: */
0099: public boolean isReadOnly() {
0100: return readOnly;
0101: }
0102:
0103: /**
0104: * Constructor for testing purposes only!
0105: *
0106: */
0107: public IMAPFolder(IPersistantHeaderList headerList,
0108: IImapServer server) {
0109: super ("test", "IMAPFolder", "/tmp/");
0110: this .headerList = headerList;
0111: this .observable = new DummyObservable();
0112:
0113: store = server;
0114: }
0115:
0116: /**
0117: * @see org.columba.mail.folder.FolderTreeNode#FolderTreeNode(org.columba.mail.config.FolderItem)
0118: */
0119: public IMAPFolder(FolderItem folderItem, String path) {
0120: super (folderItem, path);
0121:
0122: DefaultSearchEngine engine = new DefaultSearchEngine(this );
0123: engine.setNonDefaultEngine(new IMAPQueryEngine(this ));
0124: setSearchEngine(engine);
0125:
0126: }
0127:
0128: /**
0129: * @param type
0130: */
0131: // only called by FolderFactory
0132: public IMAPFolder(String name, String type, String path)
0133: throws Exception {
0134: super (name, type, path);
0135:
0136: IFolderItem item = getConfiguration();
0137: item.setString("property", "accessrights", "user");
0138: item.setString("property", "subfolder", "true");
0139: }
0140:
0141: /**
0142: * @see org.columba.mail.folder.FolderTreeNode#removeFolder()
0143: */
0144: public void removeFolder() throws Exception {
0145: try {
0146: if (existsOnServer) {
0147: String path = getImapPath();
0148:
0149: getServer().deleteFolder(path);
0150: }
0151:
0152: super .removeFolder();
0153: } catch (Exception e) {
0154: throw e;
0155: }
0156: }
0157:
0158: public void setName(String name) throws Exception {
0159: if (getName() == null) { // if creating new folder
0160: super .setName(name);
0161: return;
0162: }
0163:
0164: String oldPath = getImapPath();
0165: LOG.info("old path=" + oldPath);
0166:
0167: String newPath = null;
0168:
0169: if (getParent() instanceof IMAPFolder) {
0170: newPath = ((IMAPFolder) getParent()).getImapPath();
0171: }
0172:
0173: newPath += (getServer().getDelimiter() + name);
0174: LOG.info("new path=" + newPath);
0175:
0176: getServer().renameFolder(oldPath, newPath);
0177: super .setName(name);
0178: }
0179:
0180: /**
0181: * Method getStore.
0182: *
0183: * @return IMAPStore
0184: */
0185: public IImapServer getServer() {
0186: if (store == null) {
0187: store = ((IMAPRootFolder) getRootFolder()).getServer();
0188: }
0189:
0190: return store;
0191: }
0192:
0193: /**
0194: * @see org.columba.mail.folder.Folder#getHeaderList(org.columba.api.command.IWorkerStatusController)
0195: */
0196: public synchronized IHeaderList getHeaderList() throws Exception {
0197: if (headerList == null) {
0198: // header cache is stored in "headerlist" subfolder
0199: File headercacheDirectory = new File(getDirectoryFile(),
0200: "headerlist");
0201: headerList = new BerkeleyDBHeaderList(headercacheDirectory,
0202: getId());
0203:
0204: headerList
0205: .addHeaderListCorruptedListener(new IHeaderListCorruptedListener() {
0206:
0207: public void headerListCorrupted(
0208: IHeaderList headerList) {
0209: headerList.clear();
0210: getMessageFolderInfo().reset();
0211: fireFolderPropertyChanged();
0212: }
0213: });
0214:
0215: }
0216:
0217: if (mailboxSyncEnabled
0218: && ConnectionStateImpl.getInstance().isOnline()
0219: && !getServer().isSelected(this )) {
0220: // Trigger Synchronization
0221: CommandProcessor.getInstance().addOp(
0222: new CheckForNewMessagesCommand(
0223: new MailFolderCommandReference(this )));
0224: }
0225:
0226: return headerList;
0227: }
0228:
0229: protected ImapItem getImapItem() {
0230: return getServer().getItem();
0231: }
0232:
0233: protected void printStatusMessage(String message) {
0234: if (getObservable() != null) {
0235: getObservable().setMessage(
0236: getImapItem().get("host") + ": " + message);
0237: }
0238: }
0239:
0240: protected void setProgress(int progress) {
0241: if (getObservable() != null) {
0242: getObservable().setCurrent(progress);
0243: }
0244: }
0245:
0246: protected void setMaximum(int progress) {
0247: if (getObservable() != null) {
0248: getObservable().setMax(progress);
0249: }
0250: }
0251:
0252: /**
0253: * @throws Exception
0254: * @throws IOException
0255: * @throws CommandCancelledException
0256: * @throws IMAPException
0257: */
0258: Object[] synchronizeHeaderlist() throws Exception, IOException,
0259: CommandCancelledException, IMAPException {
0260:
0261: Object[] result = new Object[0];
0262:
0263: // Check if the mailbox has changed
0264: MailboxStatus status = getServer().getStatus(this );
0265:
0266: if (status.getMessages() == 0) {
0267: purgeHeaderList();
0268:
0269: syncMailboxInfo(status);
0270:
0271: return result;
0272: }
0273:
0274: if (!firstSync
0275: && status.getMessages() == this .getMessageFolderInfo()
0276: .getExists()) {
0277: return result;
0278: }
0279:
0280: List localUids = new LinkedList(Arrays.asList(getHeaderList()
0281: .getUids()));
0282: // Sort the uid list
0283: Collections.sort(localUids);
0284:
0285: int largestLocalUid = localUids.size() > 0 ? ((Integer) localUids
0286: .get(localUids.size() - 1)).intValue()
0287: : -1;
0288:
0289: int largestRemoteUid = getServer().getLargestRemoteUid(this );
0290:
0291: if (localUids.size() == status.getMessages()
0292: && largestRemoteUid == largestLocalUid) {
0293: // Seems to be no change!
0294: if (firstSync) {
0295: firstSync = false;
0296: synchronizeFlags(status);
0297: } else {
0298: syncMailboxInfo(status);
0299: }
0300:
0301: return result;
0302: }
0303:
0304: printStatusMessage(MailResourceLoader.getString("statusbar",
0305: "message", "sync_messages"));
0306:
0307: largestRemoteUid = getServer().fetchUid(
0308: new SequenceSet(SequenceEntry.STAR), this );
0309:
0310: if (largestRemoteUid == -1) {
0311: largestRemoteUid = getServer().fetchUid(
0312: new SequenceSet(status.getMessages()), this );
0313: }
0314:
0315: int largestLocalUidIndex = findLargestLocalUidIndex(localUids);
0316:
0317: int newMessages = status.getMessages() - largestLocalUidIndex;
0318:
0319: /*
0320: * // Somehow there are new messages that // have a lower index -> out
0321: * of sync if (localUids.size() - status.getMessages() + newMessages <
0322: * 0) {
0323: *
0324: * LOG.severe("Folder " + getName() + " is out of sync -> recreating the
0325: * cache!"); purgeHeaderList(); // all messages are new newMessages =
0326: * status.getMessages(); // Set the index of the largest Uid to 0 // ->
0327: * ensure it works with the fetch of new // messages part below
0328: * largestLocalUidIndex = 0; largestLocalUid = -1;
0329: *
0330: * localUids.clear(); }
0331: */
0332: LOG.fine("Found " + newMessages + " new Messages");
0333:
0334: // If we have new messages add them to the headerlist
0335: if (newMessages > 0) {
0336: printStatusMessage(MailResourceLoader.getString(
0337: "statusbar", "message", "fetch_new_headers"));
0338:
0339: // Ensure sizes are correct
0340: getMessageFolderInfo().setExists(localUids.size());
0341: getMessageFolderInfo().setUnseen(
0342: Math.min(getMessageFolderInfo().getUnseen(),
0343: localUids.size()));
0344: getMessageFolderInfo().setRecent(0);
0345:
0346: List newUids = fetchNewMessages(largestLocalUidIndex);
0347:
0348: localUids.addAll(newUids);
0349:
0350: if (newUids.size() < newMessages) {
0351: // There are still more messages to update
0352: // -> issue another fetch messages command
0353: CommandProcessor.getInstance().addOp(
0354: new FetchMessagesCommand(
0355: new MailFolderCommandReference(this ),
0356: newMessages, largestLocalUidIndex,
0357: newUids.size()));
0358:
0359: } else {
0360: fetchDone();
0361: }
0362:
0363: result = newUids.toArray(new Object[0]);
0364: } else {
0365:
0366: // Number of deleted messages is computed from exists on imap and
0367: // local
0368: // newMessages
0369: findRemovedMessages(status, localUids);
0370:
0371: if (firstSync) {
0372: firstSync = false;
0373: synchronizeFlags(status);
0374: } else {
0375: syncMailboxInfo(status);
0376: }
0377: }
0378:
0379: return result;
0380: }
0381:
0382: private int findLargestLocalUidIndex(List localUids)
0383: throws IOException, IMAPException,
0384: CommandCancelledException {
0385: int largestLocalUidIndex = -1;
0386:
0387: printStatusMessage(MailResourceLoader.getString("statusbar",
0388: "message", "sync_messages"));
0389:
0390: // Compute the number of new messages
0391: if (localUids.size() > 0) {
0392: // Find the index of the largest local Uid
0393: int position = localUids.size() - 1;
0394: while (largestLocalUidIndex == -1
0395: && position >= localUids.size() - 10
0396: && position >= 0) {
0397: largestLocalUidIndex = getServer().getIndex(
0398: (Integer) localUids.get(position--), this );
0399: }
0400:
0401: // Still not found -> do a binary search
0402: if (largestLocalUidIndex == -1) {
0403: int a, b, c;
0404: int index;
0405: a = 0;
0406: b = position;
0407: while (b > a && b - a > 1) {
0408: c = Math.round((b - a) * 0.5f) + a;
0409:
0410: index = getServer().getIndex(
0411: (Integer) localUids.get(c), this );
0412: if (index == -1) {
0413: b = c;
0414: } else {
0415: a = c;
0416: largestLocalUidIndex = index;
0417: }
0418: }
0419:
0420: // removedLocalUids = localUids.size() - 1 - position;
0421: } else {
0422: // -2 because of the decrement in line 317
0423: // removedLocalUids = localUids.size() - 2 - position;
0424: }
0425:
0426: // Check if all local uids have been deleted
0427: if (largestLocalUidIndex == -1) {
0428: // all messages are new
0429: largestLocalUidIndex = 0;
0430: }
0431:
0432: } else {
0433: // all messages are new
0434: largestLocalUidIndex = 0;
0435: }
0436: return largestLocalUidIndex;
0437: }
0438:
0439: private void findRemovedMessages(MailboxStatus status,
0440: List localUids) throws Exception, IOException,
0441: IMAPException, CommandCancelledException {
0442: int largestRemoteUid = getServer().getLargestRemoteUid(this );
0443:
0444: int deletedMessages = localUids.size() - status.getMessages();
0445:
0446: LOG.fine("Found " + deletedMessages + " deleted Messages");
0447:
0448: // Find the messages that have been deleted
0449: if (deletedMessages > 0) {
0450: int found = 0;
0451: // First deleted all local uids that
0452: // are larger than the largest remote uid
0453: while (localUids.size() > 0
0454: && found != deletedMessages
0455: && ((Integer) localUids.get(localUids.size() - 1))
0456: .intValue() > largestRemoteUid) {
0457: Flags flags = getHeaderList().remove(
0458: localUids.get(localUids.size() - 1)).getFlags();
0459: fireMessageRemoved(localUids
0460: .remove(localUids.size() - 1), flags);
0461:
0462: found++;
0463: }
0464:
0465: // Search in packs beginning from newest to oldest
0466: // -> in most cases this should save us a lot of uid fetchings to
0467: // find the deleted messages
0468:
0469: // Pack size is min 10, max 200 else mailboxsize / 10
0470: int packSize = Math.min(Math.max(deletedMessages, status
0471: .getMessages() / 10), 200);
0472:
0473: int upper = status.getMessages();
0474:
0475: int localPointer = localUids.size() - 1;
0476:
0477: // Fetch Pack outer loop
0478: while (upper >= 1 && found != deletedMessages) {
0479: SequenceSet set = new SequenceSet();
0480: set.add(Math.max(upper - packSize + 1, 1), upper);
0481:
0482: // Fetch these uids and compare them to the
0483: // local list
0484: Integer[] actUids = getServer().fetchUids(set, this );
0485:
0486: // Compare inner loop
0487: for (int i = actUids.length - 1; i >= 0
0488: && found != deletedMessages; i--) {
0489:
0490: // Find missing uids loop
0491: while (found != deletedMessages
0492: && localPointer >= 0
0493: && !localUids.get(localPointer).equals(
0494: actUids[i])) {
0495: // We found the uid of a deleted message
0496: // -> remove it from the headerlist
0497: getHeaderList().remove(
0498: localUids.get(localPointer));
0499:
0500: found++;
0501: localPointer--;
0502: }
0503:
0504: // next position in the local uid list
0505: localPointer--;
0506: }
0507:
0508: upper = upper - packSize;
0509: }
0510:
0511: // All the other local mails are deleted
0512: while (found < deletedMessages && localPointer >= 0) {
0513: getHeaderList().remove(localUids.get(localPointer--));
0514: found++;
0515: }
0516:
0517: if (found != deletedMessages) {
0518: LOG.severe("Assertion failed : found only " + found
0519: + " of " + deletedMessages);
0520: }
0521:
0522: }
0523: }
0524:
0525: private void purgeHeaderList() throws Exception {
0526: ICloseableIterator it = getHeaderList().keyIterator();
0527: while (it.hasNext()) {
0528: Object uid = it.next();
0529: fireMessageRemoved(uid, getHeaderList().get(uid).getFlags());
0530: }
0531: it.close();
0532: getHeaderList().clear();
0533:
0534: getMessageFolderInfo().reset();
0535: fireFolderPropertyChanged();
0536: }
0537:
0538: List fetchNewMessages(int startIndex) throws IOException,
0539: IMAPException, CommandCancelledException, Exception {
0540: IMAPFlags[] newFlags = getServer().fetchFlagsListStartFrom2(
0541: startIndex + 1, this );
0542:
0543: List newUids = new ArrayList(newFlags.length);
0544:
0545: // Build a list of the new uids
0546: for (int i = 0; i < newFlags.length; i++) {
0547: // Update the list of new and local uids
0548: newUids.add(newFlags[i].getUid());
0549: }
0550:
0551: // Fetch the headers of the new messages ...
0552: getServer().fetchHeaderList(getHeaderList(), newUids, this );
0553:
0554: // .. and set the flags
0555: setFlags(newFlags);
0556:
0557: // fire message added updates
0558: for (int i = 0; i < newFlags.length; i++) {
0559: fireMessageAdded(newFlags[i].getUid(), newFlags[i]);
0560: }
0561:
0562: return newUids;
0563: }
0564:
0565: private void syncMailboxInfo(MailboxStatus status) {
0566: boolean updated = false;
0567: if (status.getMessages() != -1
0568: && messageFolderInfo.getExists() != status
0569: .getMessages()) {
0570: messageFolderInfo.setExists(status.getMessages());
0571: updated = true;
0572: }
0573:
0574: if (status.getMessages() == 0) {
0575: messageFolderInfo.setRecent(0);
0576: messageFolderInfo.setUnseen(0);
0577: updated = true;
0578: } else {
0579:
0580: if (status.getUnseen() != -1
0581: && messageFolderInfo.getUnseen() != status
0582: .getUnseen()) {
0583: messageFolderInfo.setUnseen(status.getUnseen());
0584: updated = true;
0585: }
0586: }
0587:
0588: // Sanity tests
0589: if (messageFolderInfo.getRecent() < 0) {
0590: messageFolderInfo.setRecent(0);
0591: updated = true;
0592: }
0593: if (messageFolderInfo.getRecent() > messageFolderInfo
0594: .getExists()) {
0595: messageFolderInfo.setRecent(messageFolderInfo.getExists());
0596: updated = true;
0597: }
0598:
0599: if (messageFolderInfo.getUnseen() < 0) {
0600: messageFolderInfo.setUnseen(0);
0601: updated = true;
0602: }
0603: if (messageFolderInfo.getUnseen() > messageFolderInfo
0604: .getExists()) {
0605: messageFolderInfo.setUnseen(messageFolderInfo.getExists());
0606: updated = true;
0607: }
0608:
0609: if (updated) {
0610: fireFolderPropertyChanged();
0611: }
0612:
0613: }
0614:
0615: private void synchronizeFlags(MailboxStatus status)
0616: throws Exception, IOException, CommandCancelledException,
0617: IMAPException {
0618: printStatusMessage(MailResourceLoader.getString("statusbar",
0619: "message", "sync_flags"));
0620:
0621: MailboxStatus flagStatus = new MailboxStatus();
0622: flagStatus.setMessages(status.getMessages());
0623:
0624: // Build the remote lists of messages that are UNSEEN, FLAGGED, DELETED,
0625: // JUNK
0626: SearchKey unseenKey = new SearchKey(SearchKey.UNSEEN);
0627: List remoteUnseenUids = Arrays.asList(getServer().search(
0628: unseenKey, this ));
0629: flagStatus.setUnseen(remoteUnseenUids.size());
0630:
0631: SearchKey flaggedKey = new SearchKey(SearchKey.FLAGGED);
0632: List remoteFlaggedUids = Arrays.asList(getServer().search(
0633: flaggedKey, this ));
0634:
0635: SearchKey deletedKey = new SearchKey(SearchKey.DELETED);
0636: List remoteDeletedUids = Arrays.asList(getServer().search(
0637: deletedKey, this ));
0638:
0639: SearchKey recentKey = new SearchKey(SearchKey.RECENT);
0640: List remoteRecentUids = Arrays.asList(getServer().search(
0641: recentKey, this ));
0642: flagStatus.setRecent(remoteRecentUids.size());
0643:
0644: SearchKey junkKey = new SearchKey(SearchKey.KEYWORD, "JUNK");
0645: List remoteJunkUids = Arrays.asList(getServer().search(junkKey,
0646: this ));
0647:
0648: // update the local flags and ensure that the MailboxInfo is correct
0649: ICloseableIterator headerIterator = getHeaderList()
0650: .headerIterator();
0651:
0652: int unseen = 0;
0653: int flagged = 0;
0654: int recent = 0;
0655: int deleted = 0;
0656: int junk = 0;
0657:
0658: while (headerIterator.hasNext()) {
0659: IColumbaHeader header = (IColumbaHeader) headerIterator
0660: .next();
0661: Object uid = header.get("columba.uid");
0662:
0663: Flags flag = header.getFlags();
0664: Flags oldFlag = (Flags) flag.clone();
0665:
0666: int index;
0667:
0668: index = Collections.binarySearch(remoteUnseenUids, uid);
0669: flag.setSeen(index < 0);
0670: if (!flag.getSeen()) {
0671: unseen++;
0672: }
0673:
0674: index = Collections.binarySearch(remoteDeletedUids, uid);
0675: flag.setDeleted(index >= 0);
0676: if (flag.getDeleted()) {
0677: deleted++;
0678: }
0679:
0680: index = Collections.binarySearch(remoteFlaggedUids, uid);
0681: flag.setFlagged(index >= 0);
0682: if (flag.getFlagged()) {
0683: flagged++;
0684: }
0685:
0686: index = Collections.binarySearch(remoteRecentUids, uid);
0687: flag.setRecent(index >= 0);
0688: if (flag.getRecent()) {
0689: recent++;
0690: }
0691:
0692: index = Collections.binarySearch(remoteJunkUids, uid);
0693: header.getAttributes().put("columba.spam",
0694: new Boolean(index >= 0));
0695: if (index >= 0) {
0696: junk++;
0697: }
0698:
0699: if (!flag.equals(oldFlag)) {
0700: getHeaderList().update(uid, header);
0701:
0702: fireMessageFlagChanged(uid, oldFlag, 0);
0703: }
0704: }
0705: headerIterator.close();
0706:
0707: if (remoteJunkUids.size() != junk
0708: || remoteRecentUids.size() != recent
0709: || remoteFlaggedUids.size() != flagged
0710: || remoteDeletedUids.size() != deleted
0711: || remoteUnseenUids.size() != unseen) {
0712: // Something is wrong
0713: // Sync again
0714:
0715: synchronizeHeaderlist();
0716: return;
0717: }
0718:
0719: syncMailboxInfo(flagStatus);
0720: }
0721:
0722: /**
0723: * Method updateFlags.
0724: *
0725: * @param flagsList
0726: */
0727: protected void setFlags(Flags[] flagsList) throws Exception {
0728: for (int i = 0; i < flagsList.length; i++) {
0729: IMAPFlags flags = (IMAPFlags) flagsList[i];
0730:
0731: Integer uid = (Integer) flags.getUid();
0732:
0733: IColumbaHeader header = getHeaderList().get(uid);
0734:
0735: Flags localFlags = header.getFlags();
0736:
0737: localFlags.setFlags(flags.getFlags());
0738:
0739: // Junk flag
0740: header.getAttributes().put("columba.spam",
0741: Boolean.valueOf(flags.get(IMAPFlags.JUNK)));
0742:
0743: getHeaderList().update(uid, header);
0744: }
0745: }
0746:
0747: /**
0748: * @see org.columba.mail.folder.Folder#getMimeTree(java.lang.Object,
0749: * IMAPFolder)
0750: */
0751: public MimeTree getMimePartTree(Object uid) throws Exception {
0752: MimeTree tree = IMAPCache.getInstance().getMimeTree(this , uid);
0753: if (tree == null) {
0754: tree = getServer().getMimeTree(uid, this );
0755: IMAPCache.getInstance().addMimeTree(this , uid, tree);
0756: }
0757:
0758: return tree;
0759: }
0760:
0761: /**
0762: * Copies a set of messages from this folder to a destination folder.
0763: * <p>
0764: * The IMAP copy command also keeps the flags intact. So, there's no need to
0765: * change these manually.
0766: *
0767: * @see org.columba.mail.folder.Folder#innerCopy(org.columba.mail.folder.IMailbox,
0768: * java.lang.Object, org.columba.api.command.IWorkerStatusController)
0769: */
0770: public void innerCopy(IMailbox destiny, Object[] uids)
0771: throws Exception {
0772: IMAPFolder destFolder = (IMAPFolder) destiny;
0773: IHeaderList srcHeaderList = getHeaderList();
0774: IPersistantHeaderList destHeaderList = (IPersistantHeaderList) destFolder
0775: .getHeaderList();
0776:
0777: Object[] destUids = getServer().copy(destFolder, uids, this );
0778:
0779: if (destUids.length < uids.length) {
0780: LOG
0781: .warning("Some messages could not be copied because they do not exist anymore!");
0782: }
0783:
0784: // Check if maybe no message at all got copied
0785: // In this case we are finished here
0786: if (destUids.length == 0)
0787: return;
0788:
0789: // update headerlist of destination-folder
0790: // -> this is necessary to reflect the changes visually
0791: // but only do it if the target folder is still in sync!
0792:
0793: Integer largestDestUid = new Integer(-1);
0794: ICloseableIterator it = destHeaderList.keyIterator();
0795: while (it.hasNext()) {
0796: Integer uid = (Integer) it.next();
0797: if (largestDestUid.compareTo(uid) < 0) {
0798: largestDestUid = uid;
0799: }
0800: }
0801: it.close();
0802:
0803: if (((Integer) destUids[0]).intValue() == largestDestUid
0804: .intValue() + 1) {
0805: int j = 0;
0806: for (int i = 0; i < uids.length; i++) {
0807: IColumbaHeader destHeader = srcHeaderList.get(uids[i]);
0808: // Was this message actually copied?
0809: if (destHeader != null) {
0810: // Copy the header
0811: destHeader = (IColumbaHeader) destHeader.clone();
0812:
0813: destHeader.set("columba.uid", destUids[j]);
0814: destHeaderList.add(destHeader, destUids[j]);
0815:
0816: // We need IMAPFlags
0817: IMAPFlags flags = new IMAPFlags(destHeader
0818: .getFlags().getFlags());
0819: flags.setUid(destUids[j]);
0820:
0821: destFolder.fireMessageAdded(flags.getUid(), flags);
0822: j++;
0823: }
0824: }
0825: }
0826:
0827: }
0828:
0829: /**
0830: * @see org.columba.mail.folder.Folder#markMessage(java.lang.Object, int,
0831: * IMAPFolder)
0832: */
0833: public void markMessage(Object[] uids, int variant)
0834: throws Exception {
0835: getServer().markMessage(uids, variant, this );
0836:
0837: super .markMessage(uids, variant);
0838: }
0839:
0840: /**
0841: * @see org.columba.mail.folder.Folder#expungeFolder(java.lang.Object,
0842: * org.columba.api.command.IWorkerStatusController)
0843: */
0844: public void expungeFolder() throws Exception {
0845: try {
0846: getServer().expunge(this );
0847: super .expungeFolder();
0848: } catch (Exception e) {
0849: throw e;
0850: }
0851: }
0852:
0853: /**
0854: * @see org.columba.mail.folder.Folder#getMessageHeader(java.lang.Object,
0855: * org.columba.api.command.IWorkerStatusController)
0856: * @TODO dont use deprecated method
0857: */
0858: public IColumbaHeader getMessageHeader(Object uid) throws Exception {
0859: return getHeaderList().get(uid);
0860: }
0861:
0862: /**
0863: * Method getImapPath.
0864: *
0865: * @return String
0866: */
0867: public String getImapPath() throws IOException, IMAPException,
0868: CommandCancelledException {
0869: StringBuffer path = new StringBuffer();
0870: path.append(getName());
0871:
0872: IMailFolder child = this ;
0873:
0874: while (true) {
0875: child = (IMailFolder) child.getParent();
0876:
0877: if (child instanceof IMAPRootFolder) {
0878: break;
0879: }
0880:
0881: String n = ((IMAPFolder) child).getName();
0882:
0883: path.insert(0, n + getServer().getDelimiter());
0884: }
0885:
0886: return path.toString();
0887: }
0888:
0889: /**
0890: * @see org.columba.mail.folder.FolderTreeNode#getDefaultProperties()
0891: */
0892: public static XmlElement getDefaultProperties() {
0893: XmlElement props = new XmlElement("property");
0894:
0895: props.addAttribute("accessrights", "user");
0896: props.addAttribute("subfolder", "true");
0897:
0898: return props;
0899: }
0900:
0901: /**
0902: * @see org.columba.mail.folder.FolderTreeNode#tryToGetLock(java.lang.Object)
0903: */
0904: public boolean tryToGetLock(Object locker) {
0905: // IMAP Folders have no own lock ,but share the lock from the Root
0906: // to ensure that only one operation can be processed simultanous
0907: IMailFolder root = getRootFolder();
0908: if (root == null)
0909: throw new IllegalArgumentException("IMAPRoot is null");
0910:
0911: return root.tryToGetLock(locker);
0912: }
0913:
0914: /**
0915: * @see org.columba.mail.folder.FolderTreeNode#releaseLock()
0916: */
0917: public void releaseLock(Object locker) {
0918: IMailFolder root = getRootFolder();
0919: if (root == null)
0920: throw new IllegalArgumentException("IMAPRoot is null");
0921:
0922: root.releaseLock(locker);
0923: }
0924:
0925: /*
0926: * (non-Javadoc)
0927: *
0928: * @see org.columba.mail.folder.FolderTreeNode#addSubfolder(org.columba.mail.folder.FolderTreeNode)
0929: */
0930: public void addSubfolder(IMailFolder child) throws Exception {
0931: if (child instanceof IMAPFolder) {
0932:
0933: getServer().createMailbox(child.getName(), this );
0934: }
0935:
0936: super .addSubfolder(child);
0937: }
0938:
0939: /*
0940: * (non-Javadoc)
0941: *
0942: * @see org.columba.mail.folder.Folder#getObservable()
0943: */
0944: public IStatusObservable getObservable() {
0945: if (observable == null) {
0946: observable = ((IMAPRootFolder) getRootFolder())
0947: .getObservable();
0948: }
0949:
0950: return observable;
0951: }
0952:
0953: /*
0954: * (non-Javadoc)
0955: *
0956: * @see org.columba.mail.folder.IMailbox#addMessage(java.io.InputStream)
0957: */
0958: public Object addMessage(InputStream in, Attributes attributes,
0959: Flags flags) throws Exception {
0960: PassiveHeaderParserInputStream withHeaderInputStream = new PassiveHeaderParserInputStream(
0961: in);
0962:
0963: IMAPFlags imapFlags = new IMAPFlags(flags.getFlags());
0964:
0965: Integer uid = getServer().append(withHeaderInputStream,
0966: imapFlags, this );
0967:
0968: // Since JUNK is a non-system Flag we have to set it with
0969: // an addtitional STORE command
0970: if (((Boolean) attributes.get("columba.spam")).booleanValue()) {
0971: imapFlags.set(IMAPFlags.JUNK, true);
0972:
0973: getServer().setFlags(new Object[] { uid }, imapFlags, this );
0974: }
0975:
0976: // Parser the header
0977: Header header = withHeaderInputStream.getHeader();
0978:
0979: // update the HeaderList
0980: IColumbaHeader cHeader = new ColumbaHeader(header,
0981: (Attributes) attributes.clone(), imapFlags);
0982:
0983: getHeaderList().add(cHeader, uid);
0984:
0985: fireMessageAdded(uid, cHeader.getFlags());
0986:
0987: return uid;
0988: }
0989:
0990: /*
0991: * (non-Javadoc)
0992: *
0993: * @see org.columba.mail.folder.IMailbox#getHeaderFields(java.lang.Object,
0994: * java.lang.String[])
0995: */
0996: public Header getHeaderFields(Object uid, String[] keys)
0997: throws Exception {
0998: // get header with UID
0999: IColumbaHeader header = getHeaderList().get(uid);
1000:
1001: if (header == null)
1002: return new Header();
1003:
1004: // cached headerfield list
1005: List cachedList = Arrays.asList(CachedHeaderfields
1006: .getDefaultHeaderfields());
1007:
1008: List keyList = new ArrayList(Arrays.asList(keys));
1009: ListTools.substract(keyList, cachedList);
1010:
1011: if (keyList.size() > 0) {
1012: return getServer().getHeaders(uid, keys, this );
1013: } else {
1014: return (Header) header.getHeader().clone();
1015: }
1016: }
1017:
1018: /*
1019: * (non-Javadoc)
1020: *
1021: * @see org.columba.mail.folder.IMailbox#getMessageSourceStream(java.lang.Object)
1022: */
1023: public InputStream getMessageSourceStream(Object uid)
1024: throws Exception {
1025: return getServer().getMessageSourceStream(uid, this );
1026: }
1027:
1028: /*
1029: * (non-Javadoc)
1030: *
1031: * @see org.columba.mail.folder.IMailbox#getMimePartSourceStream(java.lang.Object,
1032: * java.lang.Integer[])
1033: */
1034: public InputStream getMimePartSourceStream(Object uid,
1035: Integer[] address) throws Exception {
1036: return getServer().getMimePartSourceStream(uid, address, this );
1037: }
1038:
1039: /**
1040: *
1041: *
1042: * @see org.columba.mail.folder.IMailbox#getMimePartBodyStream(java.lang.Object,
1043: * java.lang.Integer[])
1044: */
1045: public InputStream getMimePartBodyStream(Object uid,
1046: Integer[] address) throws Exception {
1047: InputStream result = IMAPCache.getInstance().getMimeBody(this ,
1048: uid, address);
1049: if (result == null) {
1050: LOG.fine("Cache miss - fetching from server");
1051: result = IMAPCache.getInstance().addMimeBody(
1052: this ,
1053: uid,
1054: address,
1055: getServer().getMimePartBodyStream(uid, address,
1056: this ));
1057: }
1058: return result;
1059: }
1060:
1061: /*
1062: * (non-Javadoc)
1063: *
1064: * @see org.columba.mail.folder.Folder#isInboxFolder()
1065: */
1066: public boolean isInboxFolder() {
1067: RootFolder root = (RootFolder) getRootFolder();
1068:
1069: if (root != null) {
1070: return root.getInboxFolder() == this ;
1071: } else {
1072: return false;
1073: }
1074: }
1075:
1076: /*
1077: * (non-Javadoc)
1078: *
1079: * @see org.columba.mail.folder.Folder#isTrashFolder()
1080: */
1081: public boolean isTrashFolder() {
1082: RootFolder root = (RootFolder) getRootFolder();
1083:
1084: if (root != null) {
1085: return root.getTrashFolder() == this ;
1086: } else {
1087: return false;
1088: }
1089: }
1090:
1091: /*
1092: * (non-Javadoc)
1093: *
1094: * @see org.columba.mail.folder.IMailbox#addMessage(java.io.InputStream,
1095: * org.columba.ristretto.message.Attributes)
1096: */
1097: public Object addMessage(InputStream in) throws Exception {
1098: PassiveHeaderParserInputStream withHeaderInputStream = new PassiveHeaderParserInputStream(
1099: in);
1100:
1101: Integer uid = getServer().append(withHeaderInputStream, this );
1102:
1103: if (getServer().isSelected(this )) {
1104: // update the HeaderList
1105: Header header = withHeaderInputStream.getHeader();
1106: ColumbaHeader h = new ColumbaHeader(header);
1107: getHeaderList().add(h, uid);
1108:
1109: fireMessageAdded(uid, h.getFlags());
1110: } else {
1111: if (mailboxSyncEnabled) {
1112: // Trigger Synchronization
1113: CommandProcessor.getInstance().addOp(
1114: new CheckForNewMessagesCommand(
1115: new MailFolderCommandReference(this )));
1116: }
1117: }
1118:
1119: return uid;
1120: }
1121:
1122: /**
1123: * @see org.columba.mail.folder.Folder#getHeaderListStorage()
1124: */
1125: /*
1126: * public IHeaderListStorage getHeaderListStorage() { if (attributeStorage ==
1127: * null) attributeStorage = new RemoteHeaderListStorage(this);
1128: *
1129: * return attributeStorage; }
1130: */
1131:
1132: /**
1133: * @see org.columba.mail.folder.Folder#getSearchEngineInstance()
1134: */
1135: public DefaultSearchEngine getSearchEngine() {
1136: if (searchEngine == null) {
1137: searchEngine = new DefaultSearchEngine(this );
1138: searchEngine.setNonDefaultEngine(new IMAPQueryEngine(this ));
1139: }
1140:
1141: return searchEngine;
1142: }
1143:
1144: /**
1145: * @see org.columba.mail.folder.IMailbox#getAllHeaderFields(java.lang.Object)
1146: */
1147: public Header getAllHeaderFields(Object uid) throws Exception {
1148: return getServer().getAllHeaders(uid, this );
1149:
1150: }
1151:
1152: /**
1153: * @see org.columba.mail.folder.AbstractFolder#supportsAddFolder()
1154: */
1155: public boolean supportsAddFolder(String folder) {
1156: return true;
1157: }
1158:
1159: /**
1160: * This is called from the UpdateFlagCommand which gets triggered from an
1161: * unexpected flags updated.
1162: *
1163: * @param flag
1164: * @throws IOException
1165: * @throws CommandCancelledException
1166: * @throws IMAPException
1167: */
1168: public void updateFlag(IMAPFlags flag) throws Exception,
1169: CommandCancelledException {
1170: if (getServer().isSelected(this )) {
1171: Integer uid = new Integer(getServer().fetchUid(
1172: new SequenceSet(flag.getIndex()), this ));
1173: flag.setUid(uid);
1174: setFlags(new Flags[] { flag });
1175: }
1176: }
1177:
1178: /**
1179: * @param readOnly
1180: * The readOnly to set.
1181: */
1182: public void setReadOnly(boolean readOnly) {
1183: this .readOnly = readOnly;
1184: }
1185:
1186: /*
1187: * (non-Javadoc)
1188: *
1189: * @see org.columba.mail.folder.AbstractMessageFolder#save()
1190: */
1191: public void save() throws Exception {
1192: super .save();
1193:
1194: if (headerList != null)
1195: headerList.persist();
1196: }
1197:
1198: void fetchDone() throws IOException, CommandCancelledException,
1199: IMAPException, Exception {
1200: MailboxStatus status = getServer().getStatus(this );
1201:
1202: List localUids = new LinkedList(Arrays.asList(getHeaderList()
1203: .getUids()));
1204: // Sort the uid list
1205: Collections.sort(localUids);
1206:
1207: // Number of deleted messages is computed from exists on imap and local
1208: // newMessages
1209: findRemovedMessages(status, localUids);
1210:
1211: if (firstSync) {
1212: firstSync = false;
1213: synchronizeFlags(status);
1214: } else {
1215: syncMailboxInfo(status);
1216: }
1217:
1218: // Apply filter if enabled
1219: ImapItem item = getServer().getItem();
1220:
1221: boolean applyFilter = item.getBooleanWithDefault(
1222: "automatically_apply_filter", false);
1223:
1224: // if "automatically apply filter" is selected & there
1225: // are
1226: // new
1227: // messages
1228: if (applyFilter) {
1229: CommandProcessor.getInstance().addOp(
1230: new ApplyFilterCommand(
1231: new MailFolderCommandReference(this )));
1232: }
1233:
1234: // Reenable Updating the mailbox
1235: mailboxSyncEnabled = true;
1236: }
1237:
1238: /**
1239: * @return Returns the mailboxSyncEnabled.
1240: */
1241: public boolean isMailboxSyncEnabled() {
1242: return mailboxSyncEnabled;
1243: }
1244:
1245: /**
1246: * @param mailboxSyncEnabled
1247: * The mailboxSyncEnabled to set.
1248: */
1249: public void setMailboxSyncEnabled(boolean mailboxSyncEnabled) {
1250: this.mailboxSyncEnabled = mailboxSyncEnabled;
1251: }
1252:
1253: }
|