add IMAP Condstore support, RFC 4551, bug 336151, r=standard8, sr=neil
authorDavid Bienvenu <bienvenu@nventure.com>
Fri, 25 Jul 2008 11:32:27 -0700
changeset 12 f1cac7bc0e1defff68c7cb7b6febe939bd26aa02
parent 4 201855946b54aa8dd10e5a289b12c622f44d372d
child 13 0b3d633a47f7986c14269d1e7c4747d187811a9f
push id9
push userbienvenu@nventure.com
push dateFri, 25 Jul 2008 18:57:49 +0000
treeherdercomm-central@0b3d633a47f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstandard8, neil
bugs336151
add IMAP Condstore support, RFC 4551, bug 336151, r=standard8, sr=neil
mailnews/imap/public/nsIImapFlagAndUidState.idl
mailnews/imap/public/nsIImapIncomingServer.idl
mailnews/imap/public/nsIImapMessageSink.idl
mailnews/imap/public/nsIMailboxSpec.idl
mailnews/imap/src/nsImapCore.h
mailnews/imap/src/nsImapFlagAndUidState.cpp
mailnews/imap/src/nsImapFlagAndUidState.h
mailnews/imap/src/nsImapIncomingServer.cpp
mailnews/imap/src/nsImapMailFolder.cpp
mailnews/imap/src/nsImapMailFolder.h
mailnews/imap/src/nsImapProtocol.cpp
mailnews/imap/src/nsImapProtocol.h
mailnews/imap/src/nsImapServerResponseParser.cpp
mailnews/imap/src/nsImapServerResponseParser.h
mailnews/imap/src/nsImapService.cpp
mailnews/imap/src/nsImapUtils.cpp
mailnews/imap/src/nsImapUtils.h
mailnews/mailnews.js
--- a/mailnews/imap/public/nsIImapFlagAndUidState.idl
+++ b/mailnews/imap/public/nsIImapFlagAndUidState.idl
@@ -32,21 +32,38 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(d4ba3414-fe9e-436b-a01b-bd446e737d04)]
+[scriptable, uuid(fe57609e-f90b-46a2-b78c-9c13aded30ee)]
 interface nsIImapFlagAndUidState : nsISupports 
 {
   readonly attribute long numberOfMessages;
   readonly attribute long numberOfRecentMessages;
+
+  /**
+   * If a full update, the total number of deleted messages
+   * in the folder; if a partial update, the number of deleted
+   * messages in the partial update
+   **/
+  readonly attribute long numberOfDeletedMessages;
+
+  /**
+   * If this is true, instead of fetching 1:* (FLAGS), and putting all
+   * UIDs and flags in the array, we only fetched the uids and flags
+   * that changed since the last time we were selected on this folder.
+   * This means we have a sparse array, and should not assume missing
+   * UIDs have been deleted.
+   **/
+  readonly attribute boolean partialUIDFetch;
+
   void getUidOfMessage(in long zeroBasedIndex, out unsigned long result);
   void getMessageFlags(in long zeroBasedIndex, out unsigned short result);
   void setMessageFlags(in long zeroBasedIndex, in unsigned short flags);
   void expungeByIndex(in unsigned long zeroBasedIndex);
   void addUidFlagPair(in unsigned long uid, in unsigned short flags, in unsigned long zeroBasedIndex);
   void addUidCustomFlagPair(in unsigned long uid, in string customFlag);
   string getCustomFlags(in unsigned long uid); // returns space-separated keywords
   void reset(in unsigned long howManyLeft);
--- a/mailnews/imap/public/nsIImapIncomingServer.idl
+++ b/mailnews/imap/public/nsIImapIncomingServer.idl
@@ -50,17 +50,17 @@ typedef long nsMsgImapDeleteModel;
 [scriptable, uuid(bbfc33de-fe89-11d3-a564-0060b0fc04b7)]
 interface nsMsgImapDeleteModels
 {
   const long IMAPDelete = 0;    /* delete with a big red x */
   const long MoveToTrash = 1;   /* delete moves message to the trash */
   const long DeleteNoTrash = 2; /* delete is shift delete - don't create or use trash */
 };
 
-[scriptable, uuid(ceae707a-51c8-4a2f-b4f6-e4c816f779bd)]
+[scriptable, uuid(fb6595b7-3e51-4a28-b4a6-30a2ac1c9aef)]
 interface nsIImapIncomingServer : nsISupports {
 
   attribute long maximumConnectionsNumber;
   attribute long timeOutLimits;
   attribute ACString adminUrl;
   attribute ACString serverDirectory;
   attribute long capabilityPref;
   attribute boolean cleanupInboxOnExit;
@@ -76,16 +76,22 @@ interface nsIImapIncomingServer : nsISup
   attribute ACString manageMailAccountUrl;
   attribute boolean fetchByChunks;
   attribute boolean mimePartsOnDemand;
   attribute boolean isAOLServer;       
   attribute boolean aOLMailboxView;
   attribute boolean storeReadMailInPFC;
   attribute boolean storeSentMailInPFC;
   attribute boolean useIdle;
+
+  /**
+   * See IMAP RFC 4551
+   **/
+  attribute boolean useCondStore;
+
   attribute AString trashFolderName;
 
   boolean getIsPFC(in ACString folderName);
   nsIMsgFolder getPFC(in boolean createIfMissing);
   attribute boolean downloadBodiesOnGetNewMail;
   attribute boolean autoSyncOfflineStores;
   void GetImapConnectionAndLoadUrl(in nsIEventTarget aClientEventTarget,
                                            in nsIImapUrl aImapUrl,
--- a/mailnews/imap/public/nsIImapMessageSink.idl
+++ b/mailnews/imap/public/nsIImapMessageSink.idl
@@ -36,41 +36,47 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "MailNewsTypes2.idl"
 #include "nsIImapUrl.idl"
 
 interface nsIMsgMailNewsUrl;
 
-[scriptable, uuid(AD0F50C3-8C0E-461F-B8DC-DF07FB72E0E0)]
+[scriptable, uuid(271e0cb1-003b-4714-8fca-16a549a7c224)]
 
 interface nsIImapMessageSink : nsISupports {
-    // set up messge download output stream
-	void SetupMsgWriteStream(in nsIFile aFile, in boolean appendDummyEnvelope);
+  // set up messge download output stream
+  void setupMsgWriteStream(in nsIFile aFile, in boolean aAppendDummyEnvelope);
 
-    void ParseAdoptedMsgLine(in string adoptedMsgLine, in nsMsgKey uidOfMsg);
+  void parseAdoptedMsgLine(in string aAdoptedMsgLine, in nsMsgKey aUidOfMsg);
     
-    void normalEndMsgWriteStream(in nsMsgKey uidOfMessage, in boolean markMsgRead, in nsIImapUrl imapUrl);
+  void normalEndMsgWriteStream(in nsMsgKey aUidOfMessage, in boolean aMarkMsgRead, in nsIImapUrl aImapUrl);
     
-    void AbortMsgWriteStream();
+  void abortMsgWriteStream();
 
   attribute boolean notifyDownloadedLines;  // imap protocol doesn't notify message sink of downloaded
                                   // lines when it has a channelListener. This forces it to,
                                   // even if there is a channel listener.
-    // message move/copy related methods
-//    void OnlineCopyReport(in ImapOnlineCopyState aCopyState);
-    void BeginMessageUpload();
-//    void UploadMessageFile(UploadMessageInfo* aMsgInfo);
 
-    // message flags operation
-    void NotifyMessageFlags(in unsigned long flags, in nsMsgKey messageKey);
+  void beginMessageUpload();
 
-    void NotifyMessageDeleted(in string onlineFolderName,in boolean deleteAllMsgs,in string msgIdString);
-
-    void GetMessageSizeFromDB(in string id, in boolean idIsUid, out unsigned long size);
+  /**
+   *  Notify the message sink that one or more flags have changed
+   *  For Condstore servers, also update the highestMod Sequence
+   *  @param   aFlags         - The new flags for the message
+   *  @param   aMessageKey    - The UID of the message that changed
+   *  @param   aHighestModSeq - The highest mod seq the parser has seen
+   *                            for this folder
+  **/
+  void notifyMessageFlags(in unsigned long aFlags, in nsMsgKey aMessageKey,
+                          in unsigned long long aHighestModSeq);
 
-    void SetContentModified(in nsIImapUrl aImapUrl, in nsImapContentModifiedType modified);
-    void SetImageCacheSessionForUrl(in nsIMsgMailNewsUrl aMailUrl);
-    
-    unsigned long getCurMoveCopyMessageInfo(in nsIImapUrl runningUrl, 
-                                            out PRTime date, out string keywords);
+  void notifyMessageDeleted(in string aOnlineFolderName,in boolean aDeleteAllMsgs,in string aMsgIdString);
+
+  void getMessageSizeFromDB(in string aId, out unsigned long aSize);
+
+  void setContentModified(in nsIImapUrl aImapUrl, in nsImapContentModifiedType aModified);
+  void setImageCacheSessionForUrl(in nsIMsgMailNewsUrl aMailUrl);
+  
+  unsigned long getCurMoveCopyMessageInfo(in nsIImapUrl aRunningUrl, 
+                                          out PRTime aDate, out string aKeywords);
 };
--- a/mailnews/imap/public/nsIMailboxSpec.idl
+++ b/mailnews/imap/public/nsIMailboxSpec.idl
@@ -35,20 +35,25 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIImapFlagAndUidState.idl"
 
 interface nsIMAPNamespace;
 
-[scriptable, uuid(35a2d114-2801-431c-bba5-be8cafa0461b)]
+[scriptable, uuid(94AE833C-3E53-481A-B588-89B28096F5D0)]
 interface nsIMailboxSpec : nsISupports
 {
   attribute long folder_UIDVALIDITY;
+  /**
+   * The highest modification sequence number the parser has seen 
+   * for this mailbox. See IMAP RFC 4551
+   **/
+  attribute unsigned long long highestModSeq;
   attribute long numMessages;
   attribute long numUnseenMessages;
   attribute long numRecentMessages;
 	
   attribute unsigned long	box_flags;
   attribute unsigned long supportedUserFlags;
 	
   attribute ACString allocatedPathName;
--- a/mailnews/imap/src/nsImapCore.h
+++ b/mailnews/imap/src/nsImapCore.h
@@ -131,17 +131,19 @@ typedef enum {
     kHasLanguageCapability = 0x00010000, /* language extensions */
     kHasCRAMCapability     = 0x00020000, /* CRAM auth extension */
     kQuotaCapability       = 0x00040000, /* RFC 2087 quota extension */
     kHasIdleCapability       = 0x00080000,  /* RFC 2177 idle extension */
     kHasAuthNTLMCapability = 0x00100000,  /* AUTH NTLM extension */
     kHasAuthMSNCapability = 0x00200000,   /* AUTH MSN extension */
     kHasStartTLSCapability = 0x00400000,   /* STARTTLS support */
     kLoginDisabled = 0x00800000,        /* login disabled */
-    kHasAuthGssApiCapability = 0x01000000
+    kHasAuthGssApiCapability = 0x01000000, /* GSSAPI AUTH */ 
+    kHasCondStoreCapability =  0x02000000, /* RFC 3551 CondStore extension */
+    kHasEnableCapability    =  0x04000000  /* RFC 5161 ENABLE extension */
 } eIMAPCapabilityFlag;
 
 // this used to be part of the connection object class - maybe we should move it into 
 // something similar
 typedef enum {
     kEveryThingRFC822,
     kEveryThingRFC822Peek,
     kHeadersRFC822andUid,
--- a/mailnews/imap/src/nsImapFlagAndUidState.cpp
+++ b/mailnews/imap/src/nsImapFlagAndUidState.cpp
@@ -105,45 +105,54 @@ NS_IMETHODIMP nsImapFlagAndUidState::Get
   }
   PR_CExitMonitor(this);
   
   *result = numUnseenMessages;
   
   return NS_OK;
 }
 
+NS_IMETHODIMP nsImapFlagAndUidState::GetPartialUIDFetch(PRBool *aPartialUIDFetch)
+{
+  NS_ENSURE_ARG_POINTER(aPartialUIDFetch);
+  *aPartialUIDFetch = fPartialUIDFetch;
+  return NS_OK;
+}
+
 /* amount to expand for imap entry flags when we need more */
 
 nsImapFlagAndUidState::nsImapFlagAndUidState(PRInt32 numberOfMessages, PRUint16 flags)
 {
   fNumberOfMessagesAdded = 0;
   fNumberOfMessageSlotsAllocated = numberOfMessages;
   if (!fNumberOfMessageSlotsAllocated)
 	  fNumberOfMessageSlotsAllocated = kImapFlagAndUidStateSize;
   fFlags = (imapMessageFlagsType*) PR_Malloc(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
   
   fUids.InsertElementsAt(0, fNumberOfMessageSlotsAllocated, 0);
   memset(fFlags, 0, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated);
   fSupportedUserFlags = flags;
   fNumberDeleted = 0;
+  fPartialUIDFetch = PR_TRUE;
   m_customFlagsHash.Init(10);
 }
 
 nsImapFlagAndUidState::nsImapFlagAndUidState(const nsImapFlagAndUidState& state, 
                                              uint16 flags)
 {
   fNumberOfMessagesAdded = state.fNumberOfMessagesAdded;
   
   fNumberOfMessageSlotsAllocated = state.fNumberOfMessageSlotsAllocated;
   fFlags = (imapMessageFlagsType*) PR_Malloc(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
   fUids = state.fUids;
 
   memcpy(fFlags, state.fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated);
   fSupportedUserFlags = flags;
   fNumberDeleted = 0;
+  fPartialUIDFetch = state.fPartialUIDFetch;
   m_customFlagsHash.Init(10);
 }
 
 /* static */PLDHashOperator nsImapFlagAndUidState::FreeCustomFlags(const PRUint32 &aKey, char *aData,
                                         void *closure)
 {
   PR_Free(aData);
   return PL_DHASH_NEXT;
@@ -218,33 +227,42 @@ NS_IMETHODIMP nsImapFlagAndUidState::Add
   if (zeroBasedIndex > 0x3FFFFFFF)
     return NS_ERROR_INVALID_ARG;
   PR_CEnterMonitor(this);
   if (zeroBasedIndex + 1 > fNumberOfMessagesAdded)
     fNumberOfMessagesAdded = zeroBasedIndex + 1;
   // make sure there is room for this pair
   if (fNumberOfMessagesAdded >= fNumberOfMessageSlotsAllocated)
   {
-    fNumberOfMessageSlotsAllocated += kImapFlagAndUidStateSize;
-    fUids.InsertElementsAt(fUids.Length(), kImapFlagAndUidStateSize, 0);
+    PRInt32 sizeToGrowBy = PR_MAX(kImapFlagAndUidStateSize, 
+                      fNumberOfMessagesAdded - fNumberOfMessageSlotsAllocated);
+    fNumberOfMessageSlotsAllocated += sizeToGrowBy;
+    fUids.InsertElementsAt(fUids.Length(), sizeToGrowBy, 0);
     fFlags = (imapMessageFlagsType*) PR_REALLOC(fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
     if (!fFlags)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   fUids[zeroBasedIndex] = uid;
   fFlags[zeroBasedIndex] = flags;
   if (flags & kImapMsgDeletedFlag)
     fNumberDeleted++;
   PR_CExitMonitor(this);
   return NS_OK;
 }
 
-	
-PRInt32 nsImapFlagAndUidState::GetNumberOfDeletedMessages()
+
+NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfDeletedMessages(PRInt32 *numDeletedMessages)
+{
+  NS_ENSURE_ARG_POINTER(numDeletedMessages);
+  *numDeletedMessages = NumberOfDeletedMessages();
+  return NS_OK;
+}
+
+PRInt32 nsImapFlagAndUidState::NumberOfDeletedMessages()
 {
   return fNumberDeleted;
 }
 	
 // since the uids are sorted, start from the back (rb)
 
 PRUint32  nsImapFlagAndUidState::GetHighestNonDeletedUID()
 {
--- a/mailnews/imap/src/nsImapFlagAndUidState.h
+++ b/mailnews/imap/src/nsImapFlagAndUidState.h
@@ -49,36 +49,38 @@ const PRInt32 kImapFlagAndUidStateSize =
 
 class nsImapFlagAndUidState : public nsIImapFlagAndUidState
 {
 public:
     NS_DECL_ISUPPORTS
     nsImapFlagAndUidState(int numberOfMessages, PRUint16 flags = 0);
     nsImapFlagAndUidState(const nsImapFlagAndUidState& state, PRUint16 flags = 0);
     virtual ~nsImapFlagAndUidState();
-    
 
     NS_DECL_NSIIMAPFLAGANDUIDSTATE
 
-    PRInt32               GetNumberOfDeletedMessages();
+    PRInt32               NumberOfDeletedMessages();
     
     imapMessageFlagsType  GetMessageFlagsFromUID(PRUint32 uid, PRBool *foundIt, PRInt32 *ndx);
-    PRBool                IsLastMessageUnseen(void);
-    
-    PRUint32              GetHighestNonDeletedUID();
-    PRUint16              GetSupportedUserFlags() { return fSupportedUserFlags; }
+
+    PRBool       IsLastMessageUnseen(void);
+    PRBool       GetPartialUIDFetch() {return fPartialUIDFetch;}
+    void         SetPartialUIDFetch(PRBool isPartial) {fPartialUIDFetch = isPartial;}
+    PRUint32     GetHighestNonDeletedUID();
+    PRUint16     GetSupportedUserFlags() { return fSupportedUserFlags; }
 
 private:
     
   static PLDHashOperator FreeCustomFlags(const PRUint32 &aKey, char *aData, void *closure);
     PRInt32                 fNumberOfMessagesAdded;
     PRInt32                 fNumberOfMessageSlotsAllocated;
-    PRInt32                 fNumberDeleted;
     nsTArray<nsMsgKey>      fUids;
     imapMessageFlagsType    *fFlags;
     nsDataHashtable<nsUint32HashKey, char *> m_customFlagsHash;	// Hash table, mapping uids to extra flags
     PRUint16                fSupportedUserFlags;
+    PRInt32                 fNumberDeleted;
+    PRBool                  fPartialUIDFetch;
 };
 
 
 
 
 #endif
--- a/mailnews/imap/src/nsImapIncomingServer.cpp
+++ b/mailnews/imap/src/nsImapIncomingServer.cpp
@@ -319,16 +319,19 @@ NS_IMPL_SERVERPREF_BOOL(nsImapIncomingSe
                         "download_bodies_on_get_new_mail")
 
 NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, AutoSyncOfflineStores,
                         "autosync_offline_stores")
 
 NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseIdle,
                         "use_idle")
 
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseCondStore,
+                        "use_condstore")
+
 NS_IMETHODIMP
 nsImapIncomingServer::GetShuttingDown(PRBool *retval)
 {
   NS_ENSURE_ARG_POINTER(retval);
   *retval = m_shuttingDown;
   return NS_OK;
 }
 
--- a/mailnews/imap/src/nsImapMailFolder.cpp
+++ b/mailnews/imap/src/nsImapMailFolder.cpp
@@ -226,16 +226,17 @@ nsImapMailFolder::nsImapMailFolder() :
     mImapHdrDownloadedAtom = NS_NewAtom("ImapHdrDownloaded");
   m_appendMsgMonitor = nsnull;  // since we're not using this (yet?) make it null.
   // if we do start using it, it should be created lazily
 
   m_thread = do_GetCurrentThread();
   m_moveCoalescer = nsnull;
   m_boxFlags = 0;
   m_uidValidity = kUidUnknown;
+  m_highestModSeq = 0;
   m_numStatusRecentMessages = 0;
   m_numStatusUnseenMessages = 0;
   m_hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
   m_folderACL = nsnull;
   m_aclFlags = 0;
   m_supportedUserFlags = 0;
   m_namespace = nsnull;
   m_numFilterClassifyRequests = 0;
@@ -2373,22 +2374,28 @@ NS_IMETHODIMP nsImapMailFolder::UpdateIm
   rv = aSpec->GetFolderSelected(&folderSelected);
   NS_ENSURE_SUCCESS(rv, rv);
   nsTArray<nsMsgKey> existingKeys;
   nsTArray<nsMsgKey> keysToDelete;
   nsTArray<nsMsgKey> keysToFetch;
   PRUint32 numNewUnread;
   nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
   PRInt32 imapUIDValidity = 0;
-
   if (mDatabase)
   {
     rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
     if (NS_SUCCEEDED(rv) && dbFolderInfo)
+    {
       dbFolderInfo->GetImapUidValidity(&imapUIDValidity);
+      PRUint64 mailboxHighestModSeq;
+      aSpec->GetHighestModSeq(&mailboxHighestModSeq);
+      char intStrBuf[40];
+      PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu",  mailboxHighestModSeq);
+      dbFolderInfo->SetCharProperty(kModSeqPropertyName, nsDependentCString(intStrBuf));
+    }
     mDatabase->ListAllKeys(existingKeys);
     PRInt32 keyCount = existingKeys.Length();
     mDatabase->ListAllOfflineDeletes(&existingKeys);
     if (keyCount < existingKeys.Length())
       existingKeys.Sort();
   }
   PRInt32 folderValidity;
   aSpec->GetFolder_UIDVALIDITY(&folderValidity);
@@ -2400,17 +2407,17 @@ NS_IMETHODIMP nsImapMailFolder::UpdateIm
   aSpec->GetSupportedUserFlags(&supportedUserFlags);
   SetSupportedUserFlags(supportedUserFlags);
 
   m_uidValidity = folderValidity;
 
   if ((imapUIDValidity != folderValidity) /* && // if UIDVALIDITY Changed
     !NET_IsOffline() */)
   {
-    NS_ASSERTION(PR_FALSE, "uid validity seems to have changed, blowing away db");
+    NS_ASSERTION(!imapUIDValidity, "non-zero uid validity seems to have changed, blowing away db");
     nsCOMPtr<nsILocalFile> pathFile;
     rv = GetFilePath(getter_AddRefs(pathFile));
     if (NS_FAILED(rv)) return rv;
 
     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr <nsIDBFolderInfo> transferInfo;
@@ -2453,17 +2460,22 @@ NS_IMETHODIMP nsImapMailFolder::UpdateIm
         if(mAddListener)
           mDatabase->AddListener(this);
         rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
       }
     }
     // store the new UIDVALIDITY value
 
     if (NS_SUCCEEDED(rv) && dbFolderInfo)
+    {
       dbFolderInfo->SetImapUidValidity(folderValidity);
+      // need to forget highest mod seq when uid validity rolls.
+      dbFolderInfo->SetCharProperty(kModSeqPropertyName, EmptyCString());
+      dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName, 0);
+    }
     // delete all my msgs, the keys are bogus now
     // add every message in this folder
     existingKeys.Clear();
     //      keysToDelete.CopyArray(&existingKeys);
 
     if (flagState)
     {
       nsTArray<nsMsgKey> no_existingKeys;
@@ -2782,16 +2794,27 @@ nsresult nsImapMailFolder::NormalEndHead
   // here we need to tweak flags from uid state..
   if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages()))
   {
     mDatabase->AddNewHdrToDB(newMsgHdr, PR_TRUE);
     nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
     if (notifier)
       notifier->NotifyMsgAdded(newMsgHdr);
   }
+  // adjust highestRecordedUID
+  if (mDatabase)
+  {
+    nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
+    nsMsgKey highestUID;
+    mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
+    dbFolderInfo->GetUint32Property(kHighestRecordedUIDPropertyName, 0, &highestUID);
+    if (m_curMsgUid > highestUID)
+      dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName, m_curMsgUid);
+
+  }
   m_msgParser->Clear(); // clear out parser, because it holds onto a msg hdr.
   m_msgParser->SetMailDB(nsnull); // tell it to let go of the db too.
   // I don't think we want to do this - it does bad things like set the size incorrectly.
   //    m_msgParser->FinishHeader();
     return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMailFolder::AbortHeaderParseStream(nsIImapProtocol* aProtocol)
@@ -3549,34 +3572,61 @@ nsresult nsImapMailFolder::MoveIncorpora
   // it is done async and that can fail
   return rv;
 }
 
 // both of these algorithms assume that key arrays and flag states are sorted by increasing key.
 void nsImapMailFolder::FindKeysToDelete(const nsTArray<nsMsgKey> &existingKeys, nsTArray<nsMsgKey> &keysToDelete, nsIImapFlagAndUidState *flagState)
 {
   PRBool showDeletedMessages = ShowDeletedMessages();
+  PRInt32 numMessageInFlagState;
+  PRBool partialUIDFetch;
+  PRUint32 uidOfMessage;
+  imapMessageFlagsType flags;
+
+  flagState->GetNumberOfMessages(&numMessageInFlagState);
+  flagState->GetPartialUIDFetch(&partialUIDFetch);
+
+  // if we're doing a partialUIDFetch, just delete the keys from the db
+  // that have the deleted flag set (if not using imap delete model)
+  // and return.
+  if (partialUIDFetch)
+  {
+    if (!showDeletedMessages)
+    {
+      for (PRUint32 i = 0; i < numMessageInFlagState; i++)
+      {
+        flagState->GetUidOfMessage(i, &uidOfMessage);
+        // flag state will be zero filled up to first real uid, so ignore those.
+        if (uidOfMessage)
+        {
+          flagState->GetMessageFlags(i, &flags);
+          if (flags & kImapMsgDeletedFlag)
+            keysToDelete.AppendElement(uidOfMessage);
+        }
+      }
+    }
+    return;
+  }
+  // otherwise, we have a complete set of uid's and flags, so we delete
+  // anything thats in existingKeys but not in the flag state, as well
+  // as messages with the deleted flag set.
   PRUint32 total = existingKeys.Length();
-  PRInt32 messageIndex;
-
-  int onlineIndex=0; // current index into flagState
-  for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++)
-  {
-    PRUint32 uidOfMessage;
-
-    flagState->GetNumberOfMessages(&messageIndex);
-    while ((onlineIndex < messageIndex) &&
+  int onlineIndex = 0; // current index into flagState
+  for (PRUint32 keyIndex = 0; keyIndex < total; keyIndex++)
+  {
+
+    while ((onlineIndex < numMessageInFlagState) &&
          (flagState->GetUidOfMessage(onlineIndex, &uidOfMessage), (existingKeys[keyIndex] > uidOfMessage) ))
       onlineIndex++;
 
-    imapMessageFlagsType flags;
     flagState->GetUidOfMessage(onlineIndex, &uidOfMessage);
     flagState->GetMessageFlags(onlineIndex, &flags);
     // delete this key if it is not there or marked deleted
-    if ( (onlineIndex >= messageIndex ) ||
+    if ( (onlineIndex >= numMessageInFlagState ) ||
        (existingKeys[keyIndex] != uidOfMessage) ||
        ((flags & kImapMsgDeletedFlag) && !showDeletedMessages) )
     {
       nsMsgKey doomedKey = existingKeys[keyIndex];
       if ((PRInt32) doomedKey <= 0 && doomedKey != nsMsgKey_None)
         continue;
       else
         keysToDelete.AppendElement(existingKeys[keyIndex]);
@@ -4073,34 +4123,55 @@ nsresult nsImapMailFolder::NotifyMessage
       mDatabase->SetLabel(msgKey, 0);
   }
   if (flags & kImapMsgMDNSentFlag)
     mDatabase->MarkMDNSent(msgKey, PR_TRUE, nsnull);
 
   return NS_OK;
 }
 
-// message flags operation - this is called (rarely) from the imap protocol,
-// proxied over from the imap thread to the ui thread
+// message flags operation - this is called from the imap protocol,
+// proxied over from the imap thread to the ui thread, when a flag changes
 NS_IMETHODIMP
-nsImapMailFolder::NotifyMessageFlags(PRUint32 flags, nsMsgKey msgKey)
+nsImapMailFolder::NotifyMessageFlags(PRUint32 aFlags, nsMsgKey aMsgKey, PRUint64 aHighestModSeq)
 {
   if (NS_SUCCEEDED(GetDatabase(nsnull)) && mDatabase)
   {
+    PRBool msgDeleted = aFlags & kImapMsgDeletedFlag;
+    if (aHighestModSeq || msgDeleted)
+    {
+      nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
+      mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
+      if (dbFolderInfo)
+      {
+        if (aHighestModSeq)
+        {
+          char intStrBuf[40];
+          PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu",  aHighestModSeq);
+          dbFolderInfo->SetCharProperty(kModSeqPropertyName, nsDependentCString(intStrBuf));
+        }
+        if (msgDeleted)
+        {
+          PRUint32 oldDeletedCount;
+          dbFolderInfo->GetUint32Property(kDeletedHdrCountPropertyName, 0, &oldDeletedCount);
+          dbFolderInfo->SetUint32Property(kDeletedHdrCountPropertyName, oldDeletedCount + 1);
+        }
+      }
+    }
     nsCOMPtr<nsIMsgDBHdr> dbHdr;
-    nsresult rv;
     PRBool containsKey;
-    rv = mDatabase->ContainsKey(msgKey , &containsKey);
+    nsresult rv = mDatabase->ContainsKey(aMsgKey , &containsKey);
     // if we don't have the header, don't diddle the flags.
     // GetMsgHdrForKey will create the header if it doesn't exist.
     if (NS_FAILED(rv) || !containsKey)
       return rv;
-    rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(dbHdr));
+    rv = mDatabase->GetMsgHdrForKey(aMsgKey, getter_AddRefs(dbHdr));
     if(NS_SUCCEEDED(rv) && dbHdr)
-      NotifyMessageFlagsFromHdr(dbHdr, msgKey, flags);
+      NotifyMessageFlagsFromHdr(dbHdr, aMsgKey, aFlags);
+
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImapMailFolder::NotifyMessageDeleted(const char * onlineFolderName, PRBool deleteAllMsgs, const char * msgIdString)
 {
   if (deleteAllMsgs)
@@ -4172,29 +4243,27 @@ void nsImapMailFolder::SetIMAPDeletedFla
   nsresult markStatus = 0;
   PRUint32 total = msgids.Length();
 
   for (PRUint32 msgIndex=0; !markStatus && (msgIndex < total); msgIndex++)
     markStatus = mailDB->MarkImapDeleted(msgids[msgIndex], markDeleted, nsnull);
 }
 
 NS_IMETHODIMP
-nsImapMailFolder::GetMessageSizeFromDB(const char * id, PRBool idIsUid, PRUint32 *size)
+nsImapMailFolder::GetMessageSizeFromDB(const char * id, PRUint32 *size)
 {
   NS_ENSURE_ARG_POINTER(size);
   nsresult rv;
   *size = 0;
   (void) GetDatabase(nsnull);
   if (id && mDatabase)
   {
     PRUint32 key = atoi(id);
     nsCOMPtr<nsIMsgDBHdr> mailHdr;
-    NS_ASSERTION(idIsUid, "ids must be uids to get message size");
-    if (idIsUid)
-      rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(mailHdr));
+    rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(mailHdr));
     if (NS_SUCCEEDED(rv) && mailHdr)
       rv = mailHdr->GetMessageSize(size);
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsImapMailFolder::SetContentModified(nsIImapUrl *aImapUrl, nsImapContentModifiedType modified)
--- a/mailnews/imap/src/nsImapMailFolder.h
+++ b/mailnews/imap/src/nsImapMailFolder.h
@@ -441,16 +441,19 @@ protected:
   nsCOMPtr<nsIMsgFilterPlugin> m_filterPlugin;  // XXX should be a list
   // used with filter plugins to know when we've finished classifying and can playback moves
   PRInt32 m_numFilterClassifyRequests;
   PRBool m_msgMovedByFilter;
   nsImapMoveCoalescer *m_moveCoalescer; // strictly owned by the nsImapMailFolder
   nsCOMPtr<nsIMutableArray> m_junkMessagesToMarkAsRead;
   nsMsgKey m_curMsgUid;
   PRUint32 m_uidValidity;
+  // used for condstore support;
+  PRUint64 m_highestModSeq;
+
   PRInt32 m_numStatusRecentMessages; // used to store counts from Status command
   PRInt32 m_numStatusUnseenMessages;
   PRInt32  m_nextMessageByteLength;
   nsCOMPtr<nsIThread> m_thread;
   nsCOMPtr<nsIUrlListener> m_urlListener;
   PRBool m_urlRunning;
 
   // *** jt - undo move/copy trasaction support
--- a/mailnews/imap/src/nsImapProtocol.cpp
+++ b/mailnews/imap/src/nsImapProtocol.cpp
@@ -67,16 +67,17 @@
 #include "nspr.h"
 #include "plbase64.h"
 #include "nsIImapService.h"
 #include "nsISocketTransportService.h"
 #include "nsIStreamListenerTee.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
+#include "nsIDBFolderInfo.h"
 #include "nsIPipe.h"
 #include "nsIMsgFolder.h"
 #include "nsMsgMessageFlags.h"
 #include "nsImapStringBundle.h"
 #include "nsICopyMsgStreamListener.h"
 #include "nsTextFormatter.h"
 #include "nsAutoLock.h"
 #include "nsIMsgHdr.h"
@@ -352,16 +353,17 @@ nsresult nsImapProtocol::GlobalInitializ
 
 nsImapProtocol::nsImapProtocol() : nsMsgProtocol(nsnull),
     m_parser(*this)
 {
   m_urlInProgress = PR_FALSE;
   m_idle = PR_FALSE;
   m_retryUrlOnError = PR_FALSE;
   m_useIdle = PR_TRUE; // by default, use it
+  m_useCondStore = PR_TRUE;
   m_ignoreExpunges = PR_FALSE;
   m_useSecAuth = PR_FALSE;
   m_socketType = nsIMsgIncomingServer::tryTLS;
   m_connectionStatus = 0;
   m_hostSessionList = nsnull;
   m_flagState = nsnull;
   m_fetchBodyIdList = nsnull;
 
@@ -450,16 +452,18 @@ nsImapProtocol::nsImapProtocol() : nsMsg
   m_downloadLineCache = new nsMsgImapLineDownloadCache();
 
   // subscription
   m_autoSubscribe = PR_TRUE;
   m_autoUnsubscribe = PR_TRUE;
   m_autoSubscribeOnOpen = PR_TRUE;
   m_deletableChildren = nsnull;
 
+  mFolderLastModSeq = 0;
+  
   Configure(gTooFastTime, gIdealTime, gChunkAddSize, gChunkSize,
                     gChunkThreshold, gFetchByChunks);
 
   // where should we do this? Perhaps in the factory object?
   if (!IMAP)
     IMAP = PR_NewLogModule("IMAP");
 }
 
@@ -489,16 +493,17 @@ nsresult nsImapProtocol::Initialize(nsII
    nsresult rv = m_downloadLineCache->GrowBuffer(kDownLoadCacheSize);
    NS_ENSURE_SUCCESS(rv, rv);
 
    m_flagState = new nsImapFlagAndUidState(kImapFlagAndUidStateSize, PR_FALSE);
    if (!m_flagState)
      return NS_ERROR_OUT_OF_MEMORY;
 
    aServer->GetUseIdle(&m_useIdle);
+   aServer->GetUseCondStore(&m_useCondStore);
    NS_ADDREF(m_flagState);
 
     m_sinkEventTarget = aSinkEventTarget;
     m_hostSessionList = aHostSessionList; // no ref count...host session list has life time > connection
     m_parser.SetHostSessionList(aHostSessionList);
     m_parser.SetFlagState(m_flagState);
 
   // Now initialize the thread for the connection and create appropriate monitors
@@ -663,31 +668,52 @@ static void SetSecurityCallbacksFromChan
                                          getter_AddRefs(securityCallbacks));
   if (securityCallbacks)
     aTrans->SetSecurityCallbacks(securityCallbacks);
 }
 
 // Setup With Url is intended to set up data which is held on a PER URL basis and not
 // a per connection basis. If you have data which is independent of the url we are currently
 // running, then you should put it in Initialize().
+// This is only ever called from the UI thread. It is called from LoadUrl, right
+// before the url gets run - i.e., the url is next in line to run.
 nsresult nsImapProtocol::SetupWithUrl(nsIURI * aURL, nsISupports* aConsumer)
 {
   nsresult rv = NS_ERROR_FAILURE;
   NS_PRECONDITION(aURL, "null URL passed into Imap Protocol");
   if (aURL)
   {
     m_runningUrl = do_QueryInterface(aURL, &rv);
     if (NS_FAILED(rv)) return rv;
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
     if (!server)
     {
       rv = mailnewsUrl->GetServer(getter_AddRefs(server));
       m_server = do_GetWeakReference(server);
     }
+    nsCOMPtr <nsIMsgFolder> folder;
+    mailnewsUrl->GetFolder(getter_AddRefs(folder));
+    mFolderLastModSeq = 0;
+    mFolderTotalMsgCount = 0;
+    mFolderHighestUID = 0;
+    if (folder)
+    {
+      nsCOMPtr<nsIMsgDatabase> folderDB;
+      nsCOMPtr<nsIDBFolderInfo> folderInfo;
+      folder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(folderDB));
+      if (folderInfo)
+      {
+        nsCString modSeqStr;
+        folderInfo->GetCharProperty(kModSeqPropertyName, modSeqStr);
+        mFolderLastModSeq = ParseUint64Str(modSeqStr.get());
+        folderInfo->GetNumMessages(&mFolderTotalMsgCount);
+        folderInfo->GetUint32Property(kHighestRecordedUIDPropertyName, 0, &mFolderHighestUID);
+      } 
+    }
     nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server);
     nsCOMPtr<nsIStreamListener> aRealStreamListener = do_QueryInterface(aConsumer);
     m_runningUrl->GetMockChannel(getter_AddRefs(m_mockChannel));
     if (!m_mockChannel)
     {
       // there are several imap operations that aren't initiated via a nsIChannel::AsyncOpen call on the mock channel.
       // such as selecting a folder. nsImapProtocol now insists on a mock channel when processing a url.
       nsCOMPtr<nsIChannel> channel;
@@ -1073,17 +1099,18 @@ NS_IMETHODIMP nsImapProtocol::OnInputStr
 }
 
 
 NS_IMETHODIMP
 nsImapProtocol::TellThreadToDie(PRBool isSafeToClose)
 {
   nsresult rv = NS_OK;
   // ** This routine is called from the ui thread and the imap protocol thread.
-  // The UI thread always passes in FALSE for isSafeToClose.
+  // The UI thread  passes in FALSE if it's dropping a timed out connection,
+  // true when closing a cached connection.
   {
     nsAutoCMonitor mon(this);
 
     m_urlInProgress = PR_TRUE;  // let's say it's busy so no one tries to use
                                 // this about to die connection.
     PRBool urlWritingData = PR_FALSE;
     PRBool connectionIdle = !m_runningUrl;
 
@@ -2163,18 +2190,17 @@ void nsImapProtocol::ProcessSelectedStat
             m_progressIndex = 0;
             m_progressCount = CountMessagesInIdString(messageIdString.get());
 
             // we need to set this so we'll get the msg from the memory cache.
             if (m_imapAction == nsIImapUrl::nsImapMsgFetchPeek)
               SetContentModified(IMAP_CONTENT_NOT_MODIFIED);
             FetchMessage(messageIdString,
               (m_imapAction == nsIImapUrl::nsImapMsgPreview)
-              ? kBodyStart : kEveryThingRFC822Peek,
-              bMessageIdsAreUids);
+              ? kBodyStart : kEveryThingRFC822Peek);
             if (m_imapAction == nsIImapUrl::nsImapMsgPreview)
               HeaderFetchCompleted();
             SetProgressString(0);
           }
           else
           {
             // A single message ID
 		    nsIMAPeFetchFields whatToFetch = kEveryThingRFC822;
@@ -2327,18 +2353,17 @@ void nsImapProtocol::ProcessSelectedStat
       case nsIImapUrl::nsImapMsgHeader:
         {
           nsCString messageIds;
           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIds));
 
           // we don't want to send the flags back in a group
           //        GetServerStateParser().ResetFlagInfo(0);
           FetchMessage(messageIds,
-            kHeadersRFC822andUid,
-            bMessageIdsAreUids);
+            kHeadersRFC822andUid);
           // if we explicitly ask for headers, as opposed to getting them as a result
           // of selecting the folder, or biff, send the headerFetchCompleted notification
           // to flush out the header cache.
           HeaderFetchCompleted();
         }
         break;
       case nsIImapUrl::nsImapSearch:
         {
@@ -2570,17 +2595,17 @@ void nsImapProtocol::ProcessSelectedStat
           nsCString messageIdString;
           nsresult rv = m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
           if (NS_SUCCEEDED(rv))
           {
             SetProgressString(IMAP_FOLDER_RECEIVING_MESSAGE_OF);
             m_progressIndex = 0;
             m_progressCount = CountMessagesInIdString(messageIdString.get());
             
-            FetchMessage(messageIdString, kEveryThingRFC822Peek, bMessageIdsAreUids);
+            FetchMessage(messageIdString, kEveryThingRFC822Peek);
 
             SetProgressString(0);
             if (m_imapMailFolderSink)
             {
               ImapOnlineCopyState copyStatus;
               copyStatus = GetServerStateParser().LastCommandSuccessful() ? 
                 ImapOnlineCopyStateType::kSuccessfulCopy : ImapOnlineCopyStateType::kFailedCopy;
 
@@ -2813,17 +2838,20 @@ void nsImapProtocol::SelectMailbox(const
 
   m_closeNeededBeforeSelect = PR_FALSE;   // initial value
   GetServerStateParser().ResetFlagInfo(0);
   nsCString escapedName;
   CreateEscapedMailboxName(mailboxName, escapedName);
   nsCString commandBuffer(GetServerCommandTag());
   commandBuffer.Append(" select \"");
   commandBuffer.Append(escapedName.get());
-  commandBuffer.Append("\"" CRLF);
+  commandBuffer.Append("\"");
+  if (UseCondStore())
+    commandBuffer.Append(" (CONDSTORE)");
+  commandBuffer.Append(CRLF);
 
   nsresult res;
   res = SendData(commandBuffer.get());
   if (NS_FAILED(res)) return;
   ParseIMAPandCheckForNewMail();
 
   PRInt32 numOfMessagesInFlagState = 0;
   nsImapAction imapAction;
@@ -2956,27 +2984,24 @@ void nsImapProtocol::FallbackToFetchWhol
       m_imapMessageSink->SetNotifyDownloadedLines(PR_TRUE);
   }
   FetchTryChunking(messageId, kEveryThingRFC822, PR_TRUE, NULL, messageSize, PR_TRUE);
 }
 
 void
 nsImapProtocol::FetchMessage(const nsCString &messageIds, 
                              nsIMAPeFetchFields whatToFetch,
-                             PRBool idIsUid,
+                             const char *fetchModifier,
                              PRUint32 startByte, PRUint32 numBytes,
                              char *part)
 {
   IncrementCommandTagNumber();
 
   nsCString commandString;
-  if (idIsUid)
-    commandString = "%s UID fetch";
-  else
-    commandString = "%s fetch";
+  commandString = "%s UID fetch";
 
   switch (whatToFetch) {
   case kEveryThingRFC822:
     m_flagChangeCount++;
     GetServerStateParser().SetFetchingEverythingRFC822(PR_TRUE);
     if (m_trackingTime)
       AdjustChunkSize();      // we started another segment
     m_startTime = PR_Now();     // save start of download time
@@ -3163,16 +3188,19 @@ nsImapProtocol::FetchMessage(const nsCSt
     }
     commandString.Append(")");
     break;
   case kMIMEHeader:
     commandString.Append(" %s (BODY[%s.MIME])");
     break;
   };
 
+  if (fetchModifier)
+    commandString.Append(fetchModifier);
+
   commandString.Append(CRLF);
 
   // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one
   const char *commandTag = GetServerCommandTag();
   int protocolStringSize = commandString.Length() + messageIds.Length() + PL_strlen(commandTag) + 1 +
     (part ? PL_strlen(part) : 0);
   char *protocolString = (char *) PR_CALLOC( protocolStringSize );
 
@@ -3231,17 +3259,17 @@ void nsImapProtocol::FetchTryChunking(co
     while (!DeathSignalReceived() && !GetPseudoInterrupted() &&
       !GetServerStateParser().GetLastFetchChunkReceived() &&
       GetServerStateParser().ContinueParse())
     {
       PRUint32 sizeToFetch = startByte + m_chunkSize > downloadSize ?
         downloadSize - startByte : m_chunkSize;
       FetchMessage(messageIds,
              whatToFetch,
-             idIsUid,
+             nsnull,
              startByte, sizeToFetch,
              part);
       startByte += sizeToFetch;
       // adjust the message size based on rfc822 size, if we're fetching
       // the whole message, and not just a mime part.
       if (whatToFetch != kMIMEPart)
       {
         PRUint32 newMsgSize = GetServerStateParser().SizeOfMostRecentMessage();
@@ -3262,17 +3290,17 @@ void nsImapProtocol::FetchTryChunking(co
       PseudoInterrupt(PR_FALSE);
     }
   }
   else
   {
     // small message, or (we're not chunking and not doing bodystructure),
     // or the server is not rev1.
     // Just fetch the whole thing.
-    FetchMessage(messageIds, whatToFetch,idIsUid, 0, 0, part);
+    FetchMessage(messageIds, whatToFetch, nsnull, 0, 0, part);
   }
 }
 
 
 void nsImapProtocol::PipelinedFetchMessageParts(nsCString &uid, nsIMAPMessagePartIDArray *parts)
 {
   // assumes no chunking
 
@@ -3695,40 +3723,76 @@ void nsImapProtocol::ProcessMailboxUpdat
       }
     }
 
     // make the parser record these flags
     nsCString fetchStr;
     PRInt32 added = 0, deleted = 0;
 
     m_flagState->GetNumberOfMessages(&added);
-    deleted = m_flagState->GetNumberOfDeletedMessages();
-
-    if (!added || (added == deleted))
+    deleted = m_flagState->NumberOfDeletedMessages();
+    PRBool flagStateEmpty = !added;
+    // Figure out if we need to do any kind of sync.
+    PRBool needFolderSync = (flagStateEmpty || added == deleted) && (!UseCondStore() || 
+      (GetServerStateParser().fHighestModSeq != mFolderLastModSeq) ||
+      (GetShowDeletedMessages() && 
+         GetServerStateParser().NumberOfMessages() != mFolderTotalMsgCount));
+
+    // Figure out if we need to do a full sync (UID Fetch Flags 1:*),
+    // a partial sync using CHANGEDSINCE, or a sync from the previous
+    // highwater mark.
+
+    // if the folder doesn't know about a the highest uid, or the flag state
+    // is empty, and we're not using CondStore, we need a full sync.
+    PRBool needFullFolderSync = !mFolderHighestUID || (flagStateEmpty && !UseCondStore());
+
+    if (needFullFolderSync || needFolderSync)
     {
       nsCString idsToFetch("1:*");
-      FetchMessage(idsToFetch, kFlags, PR_TRUE);  // id string shows uids
+      char fetchModifier[40] = "";
+      if (!needFullFolderSync && !GetShowDeletedMessages())
+        PR_snprintf(fetchModifier, sizeof(fetchModifier), " (CHANGEDSINCE %llu)",
+                    mFolderLastModSeq);
+
+      FetchMessage(idsToFetch, kFlags, fetchModifier);
       // lets see if we should expunge during a full sync of flags.
-      if (!DeathSignalReceived()) // only expunge if not reading messages manually and before fetching new
+      if (!DeathSignalReceived()) 
       {
+        // if we did a CHANGEDSINCE fetch, do a sanity check on the msg counts
+        // to see if some other client may have done an expunge.
+        if (m_flagState->GetPartialUIDFetch())
+        {
+          if (m_flagState->NumberOfDeletedMessages() + 
+              mFolderTotalMsgCount != GetServerStateParser().NumberOfMessages())
+          {
+            // sanity check failed - fall back to full flag sync
+            m_flagState->Reset(0);
+            m_flagState->SetPartialUIDFetch(PR_FALSE);
+            FetchMessage(NS_LITERAL_CSTRING("1:*"), kFlags);  
+          }
+        }
+        PRInt32 numDeleted = m_flagState->NumberOfDeletedMessages();
         // Don't do expunge when we are lite selecting folder because we could be doing undo
-        if ((m_flagState->GetNumberOfDeletedMessages() >= gExpungeThreshold) &&
+        if ((numDeleted >= gExpungeThreshold) &&
                  !GetShowDeletedMessages() && 
                  m_imapAction != nsIImapUrl::nsImapLiteSelectFolder)
           Expunge();
       }
-
     }
     else
     {
-      AppendUid(fetchStr, GetServerStateParser().HighestRecordedUID() + 1);
+      PRUint32 highestRecordedUID = GetServerStateParser().HighestRecordedUID();
+      // if we're using CONDSTORE, and the parser hasn't seen any UIDs, use
+      // the highest UID we've seen from the folder.
+      if (UseCondStore() && !highestRecordedUID)
+        highestRecordedUID = mFolderHighestUID;
+
+      AppendUid(fetchStr, highestRecordedUID + 1);
       fetchStr.Append(":*");
-
-      // sprintf(fetchStr, "%ld:*", GetServerStateParser().HighestRecordedUID() + 1);
-      FetchMessage(fetchStr, kFlags, PR_TRUE);      // only new messages please
+      FetchMessage(fetchStr, kFlags);      // only new messages please
     }
   }
   else if (!DeathSignalReceived())
     GetServerStateParser().ResetFlagInfo(0);
 
   if (!DeathSignalReceived())
   {
     nsImapAction imapAction;
@@ -3906,36 +3970,30 @@ NS_IMETHODIMP nsImapProtocol::GetSupport
   if (!supportedFlags)
     return NS_ERROR_NULL_POINTER;
 
   *supportedFlags = m_flagState->GetSupportedUserFlags();
   return NS_OK;
 }
 void nsImapProtocol::FolderMsgDumpLoop(PRUint32 *msgUids, PRUint32 msgCount, nsIMAPeFetchFields fields)
 {
-
   PRInt32 msgCountLeft = msgCount;
   PRUint32 msgsDownloaded = 0;
   do
   {
     nsCString idString;
-
     PRUint32 msgsToDownload = msgCountLeft;
     AllocateImapUidString(msgUids + msgsDownloaded, msgsToDownload, m_flagState, idString);  // 20 * 200
-
-    FetchMessage(idString,  fields, PR_TRUE);                  // msg ids are uids                 
-
+    FetchMessage(idString, fields);
     msgsDownloaded += msgsToDownload;
     msgCountLeft -= msgsToDownload;
-
   }
   while (msgCountLeft > 0 && !DeathSignalReceived());
 }
 
-
 void nsImapProtocol::HeaderFetchCompleted()
 {
   if (m_imapMailFolderSink)
     m_imapMailFolderSink->ParseMsgHdrs(this, m_hdrDownloadCache);
   m_hdrDownloadCache->ReleaseAll();
 
   if (m_imapMailFolderSink)
     m_imapMailFolderSink->HeaderFetchCompleted(this);
@@ -3959,25 +4017,25 @@ void nsImapProtocol::PeriodicBiff()
     PRInt32 numMessages = 0;
     m_flagState->GetNumberOfMessages(&numMessages);
     if (GetServerStateParser().NumberOfMessages() != numMessages)
     {
       PRUint32 id = GetServerStateParser().HighestRecordedUID() + 1;
       nsCString fetchStr;           // only update flags
       PRUint32 added = 0, deleted = 0;
 
-      deleted = m_flagState->GetNumberOfDeletedMessages();
+      deleted = m_flagState->NumberOfDeletedMessages();
       added = numMessages;
       if (!added || (added == deleted)) // empty keys, get them all
         id = 1;
 
       //sprintf(fetchStr, "%ld:%ld", id, id + GetServerStateParser().NumberOfMessages() - fFlagState->GetNumberOfMessages());
       AppendUid(fetchStr, id);
       fetchStr.Append(":*"); 
-      FetchMessage(fetchStr, kFlags, PR_TRUE);
+      FetchMessage(fetchStr, kFlags);
       if (((PRUint32) m_flagState->GetHighestNonDeletedUID() >= id) && m_flagState->IsLastMessageUnseen())
         m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NewMail;
       else
         m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NoMail;
     }
     else
       m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NoMail;
   }
@@ -4127,17 +4185,17 @@ PRUint32 nsImapProtocol::GetMessageSize(
     else
        m_runningUrl->AllocateCanonicalPath(
           folderFromParser,kOnlineHierarchySeparatorUnknown,
           &folderName);
 
     if (id && folderName)
     {
       if (m_imapMessageSink)
-          m_imapMessageSink->GetMessageSizeFromDB(id, idsAreUids, &size);
+          m_imapMessageSink->GetMessageSizeFromDB(id, &size);
     }
     PR_FREEIF(id);
     PR_FREEIF(folderName);
 
     PRUint32 rv = 0;
     if (!DeathSignalReceived())
       rv = size;
     return rv;
@@ -4440,23 +4498,24 @@ void
 nsImapProtocol::SetConnectionStatus(PRInt32 status)
 {
 //    PR_CEnterMonitor(this);
     m_connectionStatus = status;
 //    PR_CExitMonitor(this);
 }
 
 void
-nsImapProtocol::NotifyMessageFlags(imapMessageFlagsType flags, nsMsgKey key)
+nsImapProtocol::NotifyMessageFlags(imapMessageFlagsType flags, 
+                                   nsMsgKey key, PRUint64 highestModSeq)
 {
     if (m_imapMessageSink)
     {
       // if we're selecting the folder, don't need to report the flags; we've already fetched them.
       if (m_imapAction != nsIImapUrl::nsImapSelectFolder && (m_imapAction != nsIImapUrl::nsImapMsgFetch || (flags & ~kImapMsgRecentFlag) != kImapMsgSeenFlag))
-        m_imapMessageSink->NotifyMessageFlags(flags, key);
+        m_imapMessageSink->NotifyMessageFlags(flags, key, highestModSeq);
     }
 }
 
 void
 nsImapProtocol::NotifySearchHit(const char * hitLine)
 {
     nsresult rv;
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl, &rv);
@@ -5000,16 +5059,28 @@ void nsImapProtocol::Capability()
       if (capabilityFlag & kLiteralPlusCapability)
       {
         GetServerStateParser().SetCapabilityFlag(capabilityFlag & ~kLiteralPlusCapability);
         m_hostSessionList->SetCapabilityForHost(GetImapServerKey(), capabilityFlag & ~kLiteralPlusCapability);
       }
     }
 }
 
+void nsImapProtocol::EnableCondStore()
+{
+  IncrementCommandTagNumber();
+  nsCString command(GetServerCommandTag());
+  
+  command.Append(" ENABLE CONDSTORE" CRLF);
+  
+  nsresult rv = SendData(command.get());
+  if (NS_SUCCEEDED(rv))
+    ParseIMAPandCheckForNewMail();  
+}
+
 void nsImapProtocol::Language()
 {
   // only issue the language request if we haven't done so already...
   if (!TestFlag(IMAP_ISSUED_LANGUAGE_REQUEST))
   {
     SetFlag(IMAP_ISSUED_LANGUAGE_REQUEST);
     ProgressEventFunctionUsingId (IMAP_STATUS_CHECK_COMPAT);
     IncrementCommandTagNumber();
@@ -7400,16 +7471,19 @@ void nsImapProtocol::ProcessAfterAuthent
     PRBool haveNameSpacesForHost = PR_FALSE;
     m_hostSessionList->GetNamespacesOverridableForHost(GetImapServerKey(), nameSpacesOverridable);
     m_hostSessionList->GetGotNamespacesForHost(GetImapServerKey(), haveNameSpacesForHost);
 
   // mscott: VERIFY THIS CLAUSE!!!!!!!
     if (nameSpacesOverridable && !haveNameSpacesForHost)
       Namespace();
   }
+  if ((GetServerStateParser().GetCapabilityFlag() & kHasEnableCapability) &&
+       UseCondStore())
+    EnableCondStore();
 }
 
 void nsImapProtocol::SetupMessageFlagsString(nsCString& flagString,
                                              imapMessageFlagsType flags,
                                              PRUint16 userFlags)
 {
     if (flags & kImapMsgSeenFlag)
         flagString.Append("\\Seen ");
@@ -7823,16 +7897,25 @@ PRBool nsImapProtocol::CheckNeeded()
   PRInt32 deltaInSeconds;
 
   LL_SUB(deltaTime, PR_Now(), m_lastCheckTime);
   PRTime2Seconds(deltaTime, &deltaInSeconds);
 
   return (deltaInSeconds >= kMaxSecondsBeforeCheck);
 }
 
+PRBool nsImapProtocol::UseCondStore()
+{
+  // Check that the server is capable of cond store, and the user
+  // hasn't disabled the use of constore for this server.
+  return m_useCondStore &&
+         GetServerStateParser().GetCapabilityFlag() & kHasCondStoreCapability &&
+         GetServerStateParser().fUseModSeq;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 // The following is the implementation of nsImapMockChannel and an intermediary
 // imap steam listener. The stream listener is used to make a clean binding between the
 // imap mock channel and the memory cache channel (if we are reading from the cache)
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 // WARNING: the cache stream listener is intended to be accessed from the UI thread!
 // it will NOT create another proxy for the stream listener that gets passed in...
--- a/mailnews/imap/src/nsImapProtocol.h
+++ b/mailnews/imap/src/nsImapProtocol.h
@@ -184,17 +184,17 @@ public:
   static void EscapeUserNamePasswordString(const char *strToEscape, nsCString *resultStr);
 
   // used to start fetching a message.
   void GetShouldDownloadAllHeaders(PRBool *aResult);
   void GetArbitraryHeadersToDownload(nsCString &aResult);
   virtual void AdjustChunkSize();
   virtual void FetchMessage(const nsCString &messageIds, 
     nsIMAPeFetchFields whatToFetch,
-    PRBool idAreUid,
+    const char *fetchModifier = nsnull,
     PRUint32 startByte = 0, PRUint32 numBytes = 0,
     char *part = 0);
   void FetchTryChunking(const nsCString &messageIds,
     nsIMAPeFetchFields whatToFetch,
     PRBool idIsUid,
     char *part,
     PRUint32 downloadSize,
     PRBool tryChunking);
@@ -241,17 +241,18 @@ public:
   void SetConnectionStatus(PRInt32 status);
 
   const nsCString& GetImapHostName(); // return the host name from the url for the
   // current connection
   const nsCString& GetImapUserName(); // return the user name from the identity;
   const char* GetImapServerKey(); // return the user name from the incoming server;
 
   // state set by the imap parser...
-  void NotifyMessageFlags(imapMessageFlagsType flags, nsMsgKey key);
+  void NotifyMessageFlags(imapMessageFlagsType flags, nsMsgKey key,
+                          PRUint64 highestModSeq);
   void NotifySearchHit(const char * hitLine);
 
   // Event handlers for the imap parser.
   void DiscoverMailboxSpec(nsImapMailboxSpec * adoptedBoxSpec);
   void AlertUserEventUsingId(PRUint32 aMessageId);
   void AlertUserEvent(const char * message);
   void AlertUserEventFromServer(const char * aServerEvent);
 
@@ -456,16 +457,17 @@ private:
 
   void StartTLS();
 
   // login related methods.
   nsresult GetPassword(nsCString &password);
 
   // All of these methods actually issue protocol
   void Capability(); // query host for capabilities.
+  void EnableCondStore(); 
   void Language(); // set the language on the server if it supports it
   void Namespace();
   void InsecureLogin(const char *userName, const nsCString &password);
   nsresult AuthLogin(const char *userName, const nsCString &password, eIMAPCapabilityFlag flag);
   void ProcessAuthenticatedStateURL();
   void ProcessAfterAuthenticated();
   void ProcessSelectedStateURL();
   PRBool TryToLogon();
@@ -542,16 +544,27 @@ private:
     PRInt32 ChunkAddSize, PRInt32 ChunkSize, PRInt32 ChunkThreshold,
     PRBool FetchByChunks);
   nsresult GetMsgWindow(nsIMsgWindow ** aMsgWindow);
   // End Process AuthenticatedState Url helper methods
 
   // Quota support
   void GetQuotaDataIfSupported(const char *aBoxName);
 
+  // CondStore support - true if server supports it, and the user hasn't disabled it.
+  PRBool UseCondStore();
+  // false if pref "mail.server.serverxxx.use_condstore" is false;
+  PRBool m_useCondStore; 
+  // these come from the nsIDBFolderInfo in the msgDatabase and
+  // are initialized in nsImapProtocol::SetupWithUrl.
+  PRUint64 mFolderLastModSeq;
+  PRInt32 mFolderTotalMsgCount;
+  PRUint32 mFolderHighestUID;
+  PRUint32 mFolderNumDeleted;
+
   nsCStringArray mCustomDBHeaders;
   PRBool  m_trackingTime;
   PRTime  m_startTime;
   PRTime  m_endTime;
   PRTime  m_lastActiveTime;
   PRInt32 m_tooFastTime;
   PRInt32 m_idealTime;
   PRInt32 m_chunkAddSize;
--- a/mailnews/imap/src/nsImapServerResponseParser.cpp
+++ b/mailnews/imap/src/nsImapServerResponseParser.cpp
@@ -84,22 +84,24 @@ nsImapServerResponseParser::nsImapServer
   fXSenderInfo = nsnull;
   fSupportsUserDefinedFlags = 0;
   fSettablePermanentFlags = 0;
   fCapabilityFlag = kCapabilityUndefined; 
   fLastAlert = nsnull;
   fDownloadingHeaders = PR_FALSE;
   fGotPermanentFlags = PR_FALSE;
   fFolderUIDValidity = 0;
+  fHighestModSeq = 0;
   fAuthChallenge = nsnull;
   fStatusUnseenMessages = 0;
   fStatusRecentMessages = 0;
   fStatusNextUID = nsMsgKey_None;
   fStatusExistingMessages = 0;
   fReceivedHeaderOrSizeForUID = nsMsgKey_None;
+  fCondStoreEnabled = PR_FALSE;
 }
 
 nsImapServerResponseParser::~nsImapServerResponseParser()
 {
   PR_Free( fCurrentCommandTag );
   delete fSearchResults; 
   PR_Free( fFolderAdminUrl );
   PR_Free( fNetscapeServerVersionString );
@@ -690,16 +692,20 @@ void nsImapServerResponseParser::respons
         else
         {
           fMailAccountUrl.Adopt(CreateAstring());
           AdvanceToNextToken();
         }
       } 
       else SetSyntaxError(PR_TRUE);
       break;
+    case 'E':
+      if (!PL_strcasecmp(fNextToken, "ENABLED"))
+        enable_data();
+        break;
     case 'X':
       if (!PL_strcasecmp(fNextToken, "XSERVERINFO"))
         xserverinfo_data();
       else if (!PL_strcasecmp(fNextToken, "XMAILBOXINFO"))
         xmailboxinfo_data();
       else if (!PL_strcasecmp(fNextToken, "XAOL-OPTION"))
         skip_to_CRLF();
       else 
@@ -748,17 +754,17 @@ void nsImapServerResponseParser::PostPro
   // a fetch response to a 'uid store' command might return the flags
   // before it returns the uid of the message.  So we need both before
   // we report the new flag info to the front end
   
   // also check and be sure that there was a UID in the current response
   if (fCurrentLineContainedFlagInfo && CurrentResponseUID())
   {
     fCurrentLineContainedFlagInfo = PR_FALSE;
-    fServerConnection.NotifyMessageFlags(fSavedFlagInfo, CurrentResponseUID());
+    fServerConnection.NotifyMessageFlags(fSavedFlagInfo, CurrentResponseUID(), fHighestModSeq);
   }
 }
 
 
 /*
  mailbox_data    ::=  "FLAGS" SPACE flag_list /
                                "LIST" SPACE mailbox_list /
                                "LSUB" SPACE mailbox_list /
@@ -1010,16 +1016,17 @@ void nsImapServerResponseParser::numeric
 
 /*
 msg_fetch       ::= "(" 1#("BODY" SPACE body /
 "BODYSTRUCTURE" SPACE body /
 "BODY[" section "]" SPACE nstring /
 "ENVELOPE" SPACE envelope /
 "FLAGS" SPACE "(" #(flag / "\Recent") ")" /
 "INTERNALDATE" SPACE date_time /
+"MODSEQ" SPACE "(" nz_number ")" /
 "RFC822" [".HEADER" / ".TEXT"] SPACE nstring /
 "RFC822.SIZE" SPACE number /
 "UID" SPACE uniqueid) ")"
 
 */
 
 void nsImapServerResponseParser::msg_fetch()
 {
@@ -1064,22 +1071,61 @@ void nsImapServerResponseParser::msg_fet
         fCurrentResponseUID = atoi(fNextToken);
         if (fCurrentResponseUID > fHighestRecordedUID)
           fHighestRecordedUID = fCurrentResponseUID;
         // size came before UID
         if (fSizeOfMostRecentMessage)
           fReceivedHeaderOrSizeForUID = CurrentResponseUID();
         // if this token ends in ')', then it is the last token
         // else we advance
-        if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
+        char lastTokenChar = *(fNextToken + strlen(fNextToken) - 1);
+        if (lastTokenChar == ')')
           fNextToken += strlen(fNextToken) - 1;
+        else if (lastTokenChar < '0' || lastTokenChar > '9')
+        {
+          // GIANT HACK
+          // this is a corrupt uid - see if it's pre 5.08 Zimbra omitting
+          // a space between the UID and MODSEQ
+          if (strlen(fNextToken) > 6 && 
+              !strcmp("MODSEQ", fNextToken + strlen(fNextToken) - 6))
+            fNextToken += strlen(fNextToken) - 6;
+        }
         else
           AdvanceToNextToken();
       }
     }
+    else if (!PL_strcasecmp(fNextToken, "MODSEQ"))
+    {
+      AdvanceToNextToken();
+      if (ContinueParse())
+      {
+        fNextToken++; // eat '('
+        PRUint64 modSeq =  ParseUint64Str(fNextToken);
+        if (modSeq > fHighestModSeq)
+          fHighestModSeq = modSeq;
+
+        if (PL_strcasestr(fNextToken, ")"))
+        {
+          // eat token chars until we get the ')'
+          fNextToken = strchr(fNextToken, ')');
+          if (fNextToken)
+          {
+            fNextToken++;
+            if (*fNextToken != ')')
+              AdvanceToNextToken();
+          }
+          else
+            SetSyntaxError(PR_TRUE);
+        }
+        else
+        {
+          SetSyntaxError(PR_TRUE);
+        }
+      }
+    }
     else if (!PL_strcasecmp(fNextToken, "RFC822") ||
       !PL_strcasecmp(fNextToken, "RFC822.HEADER") ||
       !PL_strncasecmp(fNextToken, "BODY[HEADER",11) ||
       !PL_strncasecmp(fNextToken, "BODY[]", 6) ||
       !PL_strcasecmp(fNextToken, "RFC822.TEXT") ||
       (!PL_strncasecmp(fNextToken, "BODY[", 5) &&
 				  PL_strstr(fNextToken, "HEADER"))
                                   )
@@ -1779,31 +1825,23 @@ void nsImapServerResponseParser::parse_f
 
   if (labelFlags == 31)
     fSupportsUserDefinedFlags |= kImapMsgLabelFlags;
 
   if (fFlagState)
     fFlagState->SetSupportedUserFlags(fSupportsUserDefinedFlags);
 }
 /*
- resp_text_code  ::= "ALERT" / "PARSE" /
-                              "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
-                              "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
-                              "UIDVALIDITY" SPACE nz_number /
-                              "UNSEEN" SPACE nz_number /
-                              atom [SPACE 1*<any TEXT_CHAR except "]">]
-                              
- was changed to in order to enable a one symbol look ahead predictive
- parser.
- 
   resp_text_code  ::= ("ALERT" / "PARSE" /
                               "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
                               "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
                               "UIDVALIDITY" SPACE nz_number /
                               "UNSEEN" SPACE nz_number /
+                              "HIGHESTMODSEQ" SPACE nz_number /
+                              "NOMODSEQ" /
                               atom [SPACE 1*<any TEXT_CHAR except "]">] )
                       "]"
 
  
 */
 void nsImapServerResponseParser::resp_text_code()
 {
   // this is a special case way of advancing the token
@@ -1917,16 +1955,32 @@ void nsImapServerResponseParser::resp_te
           AdvanceToNextToken();
           // clear copy response uid
           fServerConnection.SetCopyResponseUid(fNextToken);
         }
         if (ContinueParse())
           AdvanceToNextToken();
       }
     }
+    else if (!PL_strcasecmp(fNextToken, "HIGHESTMODSEQ"))
+    {
+      AdvanceToNextToken();
+      if (ContinueParse())
+      {
+        fHighestModSeq = ParseUint64Str(fNextToken); 
+        fUseModSeq = PR_TRUE;
+        AdvanceToNextToken();
+      }
+    }
+    else if (!PL_strcasecmp(fNextToken, "NOMODSEQ"))
+    {
+      fHighestModSeq = 0;
+      fUseModSeq = PR_FALSE;
+      skip_to_CRLF();
+    }
     else 	// just text
     {
       // do nothing but eat tokens until we see the ] or CRLF
       // we should see the ] but we don't want to go into an
       // endless loop if the CRLF is not there
       do 
       {
         AdvanceToNextToken();
@@ -2142,16 +2196,21 @@ void nsImapServerResponseParser::capabil
       else if (! PL_strcasecmp(fNextToken, "XAOL-OPTION"))
         fCapabilityFlag |= kAOLImapCapability;
       else if (! PL_strcasecmp(fNextToken, "QUOTA"))
         fCapabilityFlag |= kQuotaCapability;
       else if (! PL_strcasecmp(fNextToken, "LANGUAGE"))
         fCapabilityFlag |= kHasLanguageCapability;
       else if (! PL_strcasecmp(fNextToken, "IDLE"))
         fCapabilityFlag |= kHasIdleCapability;
+      else if (! PL_strcasecmp(fNextToken, "CONDSTORE"))
+        fCapabilityFlag |= kHasCondStoreCapability;
+      else if (! PL_strcasecmp(fNextToken, "ENABLE"))
+        fCapabilityFlag |= kHasEnableCapability;
+
     }
   } while (fNextToken && 
 			 !fAtEndOfLine &&
                          ContinueParse());
   
   if (fHostSessionList)
     fHostSessionList->SetCapabilityForHost(
     fServerConnection.GetImapServerKey(), 
@@ -2212,16 +2271,28 @@ void nsImapServerResponseParser::xserver
     else if (!PL_strcmp("MANAGEFILTERSURL", fNextToken))
     {
       AdvanceToNextToken();
       fManageFiltersUrl.Adopt(CreateNilString());
     }
   } while (fNextToken && !fAtEndOfLine && ContinueParse());
 }
 
+void nsImapServerResponseParser::enable_data()
+{
+  do
+  {
+    // eat each enable response;
+     AdvanceToNextToken();
+     if (!strcmp("CONDSTORE", fNextToken))
+       fCondStoreEnabled = PR_TRUE;
+  } while (fNextToken && !fAtEndOfLine && ContinueParse());
+  
+}
+
 void nsImapServerResponseParser::language_data()
 {
   // we may want to go out and store the language returned to us
   // by the language command in the host info session stuff. 
 
   // for now, just eat the language....
   do
   {
@@ -3070,16 +3141,17 @@ nsImapMailboxSpec *nsImapServerResponseP
       fHostSessionList->GetNamespaceForMailboxForHost(serverKey, mailboxNameToConvert, ns);	// for
       // delimiter
     returnSpec->mHierarchySeparator = (ns) ? ns->GetDelimiter(): '/';
     
   }
   
   returnSpec->mFolderSelected = !mailboxName; // if mailboxName is null, we're doing a Status
   returnSpec->mFolder_UIDVALIDITY = fFolderUIDValidity;
+  returnSpec->mHighestModSeq = fHighestModSeq;
   returnSpec->mNumOfMessages = (mailboxName) ? fStatusExistingMessages : fNumberOfExistingMessages;
   returnSpec->mNumOfUnseenMessages = (mailboxName) ? fStatusUnseenMessages : fNumberOfUnseenMessages;
   returnSpec->mNumOfRecentMessages = (mailboxName) ? fStatusRecentMessages : fNumberOfRecentMessages;
   
   returnSpec->mSupportedUserFlags = fSupportsUserDefinedFlags;
 
   returnSpec->mBoxFlags = kNoFlags;	// stub
   returnSpec->mOnlineVerified = PR_FALSE;	// we're fabricating this.  The flags aren't verified.
--- a/mailnews/imap/src/nsImapServerResponseParser.h
+++ b/mailnews/imap/src/nsImapServerResponseParser.h
@@ -148,31 +148,35 @@ public:
   void SetFlagState(nsIImapFlagAndUidState *state);
   PRBool GetDownloadingHeaders();
   PRBool GetFillingInShell();
   void UseCachedShell(nsIMAPBodyShell *cachedShell);
   void SetHostSessionList(nsIImapHostSessionList *aHostSession);
   nsIImapHostSessionList *GetHostSessionList();
   char  *fAuthChallenge;    // the challenge returned by the server in
                             //response to authenticate using CRAM-MD5 or NTLM
+  PRBool          fCondStoreEnabled;  
+  PRBool          fUseModSeq;  // can use mod seq for currently selected folder
+  PRUint64        fHighestModSeq;
 
 protected:
   virtual void    flags();
   virtual void    envelope_data();
   virtual void    xaolenvelope_data();
   virtual void    parse_address(nsCAutoString &addressLine);
   virtual void    internal_date();
   virtual nsresult BeginMessageDownload(const char *content_type);
 
   virtual void    response_data();
   virtual void    resp_text();
   virtual void    resp_cond_state(PRBool isTagged);
   virtual void    text_mime2();
   virtual void    text();
   virtual void    parse_folder_flags();
+  virtual void    enable_data();
   virtual void    language_data();
   virtual void    authChallengeResponse_data();
   virtual void    resp_text_code();
   virtual void    response_done();
   virtual void    response_tagged();
   virtual void    response_fatal();
   virtual void    resp_cond_bye();
   virtual void    mailbox_data();
--- a/mailnews/imap/src/nsImapService.cpp
+++ b/mailnews/imap/src/nsImapService.cpp
@@ -508,18 +508,18 @@ NS_IMETHODIMP nsImapService::DisplayMess
       PRUint32 messageSize;
       PRBool useMimePartsOnDemand = gMIMEOnDemand;
       PRBool shouldStoreMsgOffline = PR_FALSE;
       PRBool hasMsgOffline = PR_FALSE;
 
       nsCOMPtr<nsIMsgIncomingServer> aMsgIncomingServer;
 
       if (imapMessageSink)
-        imapMessageSink->GetMessageSizeFromDB(msgKey.get(), PR_TRUE, &messageSize);
-
+        imapMessageSink->GetMessageSizeFromDB(msgKey.get(), &messageSize);
+      
       msgurl->SetMsgWindow(aMsgWindow);
 
       rv = msgurl->GetServer(getter_AddRefs(aMsgIncomingServer));
 
       if (NS_SUCCEEDED(rv) && aMsgIncomingServer)
       {
         nsCOMPtr<nsIImapIncomingServer> aImapServer(do_QueryInterface(aMsgIncomingServer, &rv));
         if (NS_SUCCEEDED(rv) && aImapServer)
--- a/mailnews/imap/src/nsImapUtils.cpp
+++ b/mailnews/imap/src/nsImapUtils.cpp
@@ -131,17 +131,16 @@ nsImapURI2Path(const char* rootURI, cons
     localPath->Create(nsIFile::DIRECTORY_TYPE, 0700);
   }
   if (NS_FAILED(rv)) 
   {
     pathResult = nsnull;
     return rv;
   }
   localPath->AppendRelativeNativePath(newPath);
-  
   NS_IF_ADDREF(*pathResult = localPath);
   return NS_OK;
 }
 
 nsresult
 nsImapURI2FullName(const char* rootURI, const char* hostName, const char* uriStr,
                    char **name)
 {
@@ -222,21 +221,22 @@ nsresult nsCreateImapBaseMessageURI(cons
   if (tailURI.Find(kImapRootURI) == 0)
     tailURI.Cut(0, PL_strlen(kImapRootURI));
   baseMessageURI = kImapMessageRootURI;
   baseMessageURI += tailURI;
   return NS_OK;
 }
 
 // nsImapMailboxSpec definition
-NS_IMPL_ISUPPORTS1(nsImapMailboxSpec, nsIMailboxSpec)
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsImapMailboxSpec, nsIMailboxSpec)
 
 nsImapMailboxSpec::nsImapMailboxSpec()
 {
   mFolder_UIDVALIDITY = 0;
+  mHighestModSeq = 0;
   mNumOfMessages = 0;
   mNumOfUnseenMessages = 0;
   mNumOfRecentMessages = 0;
   
   mBoxFlags = 0;
   mSupportedUserFlags = 0;
   
   mHierarchySeparator = '\0';
@@ -248,16 +248,17 @@ nsImapMailboxSpec::nsImapMailboxSpec()
   mNamespaceForFolder = nsnull;
 }
 
 nsImapMailboxSpec::~nsImapMailboxSpec()
 {
 }
 
 NS_IMPL_GETSET(nsImapMailboxSpec, Folder_UIDVALIDITY, PRInt32, mFolder_UIDVALIDITY)
+NS_IMPL_GETSET(nsImapMailboxSpec, HighestModSeq, PRUint64, mHighestModSeq)
 NS_IMPL_GETSET(nsImapMailboxSpec, NumMessages, PRInt32, mNumOfMessages)
 NS_IMPL_GETSET(nsImapMailboxSpec, NumUnseenMessages, PRInt32, mNumOfUnseenMessages)
 NS_IMPL_GETSET(nsImapMailboxSpec, NumRecentMessages, PRInt32, mNumOfRecentMessages)
 NS_IMPL_GETSET(nsImapMailboxSpec, HierarchySeparator, char, mHierarchySeparator)
 NS_IMPL_GETSET(nsImapMailboxSpec, FolderSelected, PRBool, mFolderSelected)
 NS_IMPL_GETSET(nsImapMailboxSpec, DiscoveredFromLsub, PRBool, mDiscoveredFromLsub)
 NS_IMPL_GETSET(nsImapMailboxSpec, OnlineVerified, PRBool, mOnlineVerified)
 NS_IMPL_GETSET(nsImapMailboxSpec, SupportedUserFlags, PRUint32, mSupportedUserFlags)
@@ -312,16 +313,17 @@ NS_IMETHODIMP nsImapMailboxSpec::SetFlag
   NS_ENSURE_ARG_POINTER(aFlagState);
   mFlagState = aFlagState;
   return NS_OK;
 }
 
 nsImapMailboxSpec& nsImapMailboxSpec::operator= (const nsImapMailboxSpec& aCopy) 
 {
   mFolder_UIDVALIDITY = aCopy.mFolder_UIDVALIDITY;
+  mHighestModSeq = aCopy.mHighestModSeq;
   mNumOfMessages = aCopy.mNumOfMessages;
   mNumOfUnseenMessages = aCopy.mNumOfUnseenMessages;
   mNumOfRecentMessages = aCopy.mNumOfRecentMessages;
 	
   mBoxFlags = aCopy.mBoxFlags;
   mSupportedUserFlags = aCopy.mSupportedUserFlags;
   
   mAllocatedPathName.Assign(aCopy.mAllocatedPathName);
@@ -345,16 +347,20 @@ nsImapMailboxSpec& nsImapMailboxSpec::op
 void AllocateImapUidString(PRUint32 *msgUids, PRUint32 &msgCount, 
                            nsImapFlagAndUidState *flagState, nsCString &returnString)
 {
   PRUint32 startSequence = (msgCount > 0) ? msgUids[0] : 0xFFFFFFFF;
   PRUint32 curSequenceEnd = startSequence;
   PRUint32 total = msgCount;
   PRInt32  curFlagStateIndex = -1;
 
+  // a partial fetch flag state doesn't help us, so don't use it.
+  if (flagState && flagState->GetPartialUIDFetch())
+    flagState = nsnull;
+
   for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++)
   {
     PRUint32 curKey = msgUids[keyIndex];
     PRUint32 nextKey = (keyIndex + 1 < total) ? msgUids[keyIndex + 1] : 0xFFFFFFFF;
     PRBool lastKey = (nextKey == 0xFFFFFFFF);
 
     if (lastKey)
       curSequenceEnd = curKey;
@@ -448,8 +454,19 @@ void ParseUidString(const char *uidStrin
 
 void AppendUid(nsCString &msgIds, PRUint32 uid)
 {
   char buf[20];
   PR_snprintf(buf, sizeof(buf), "%u", uid);
   msgIds.Append(buf);
 }
 
+PRUint64 ParseUint64Str(const char *str)
+{
+#ifdef XP_WIN
+  {
+    char *endPtr;
+    return _strtoui64(str, &endPtr, 10);
+  }
+#else
+  return strtoull(str, nsnull, 10);
+#endif
+}
--- a/mailnews/imap/src/nsImapUtils.h
+++ b/mailnews/imap/src/nsImapUtils.h
@@ -44,16 +44,20 @@
 #include "nsTArray.h"
 #include "nsIMailboxSpec.h"
 
 class nsImapFlagAndUidState;
 class nsImapProtocol;
 
 static const char kImapRootURI[] = "imap:/";
 static const char kImapMessageRootURI[] = "imap-message:/";
+static const char kModSeqPropertyName[] = "highestModSeq";
+static const char kHighestRecordedUIDPropertyName[] = "highestRecordedUID";
+static const char kDeletedHdrCountPropertyName[] = "numDeletedHeaders";
+
 
 extern nsresult
 nsImapURI2Path(const char* rootURI, const char* uriStr, 
                nsILocalFile **pathResult);
 
 extern nsresult
 nsImapURI2FullName(const char* rootURI, const char* hostname, const char* uriStr,
                    char **name);
@@ -66,16 +70,18 @@ nsBuildImapMessageURI(const char *baseUR
 
 extern nsresult
 nsCreateImapBaseMessageURI(const nsACString& baseURI, nsCString& baseMessageURI);
 
 void AllocateImapUidString(PRUint32 *msgUids, PRUint32 &msgCount, nsImapFlagAndUidState *flagState, nsCString &returnString);
 void ParseUidString(const char *uidString, nsTArray<nsMsgKey> &keys);
 void AppendUid(nsCString &msgIds, PRUint32 uid);
 
+/* returns 0 for parse failure */
+PRUint64 ParseUint64Str(const char *str);
 
 class nsImapMailboxSpec : public nsIMailboxSpec
 {
 public:
   nsImapMailboxSpec();
   virtual ~nsImapMailboxSpec();
   
   NS_DECL_ISUPPORTS
@@ -84,16 +90,17 @@ public:
   nsImapMailboxSpec& operator= (const nsImapMailboxSpec& aCopy);
   
   nsCOMPtr<nsIImapFlagAndUidState> mFlagState;
   nsIMAPNamespace                  *mNamespaceForFolder;  
   
   PRUint32  mBoxFlags;
   PRUint32  mSupportedUserFlags;
   PRInt32   mFolder_UIDVALIDITY;
+  PRUint64  mHighestModSeq;
   PRInt32   mNumOfMessages;
   PRInt32   mNumOfUnseenMessages;
   PRInt32   mNumOfRecentMessages;
   nsCString mAllocatedPathName;
   nsCString mHostName;
   nsString  mUnicharPathName;
   char      mHierarchySeparator;
   PRBool    mFolderSelected;
--- a/mailnews/mailnews.js
+++ b/mailnews/mailnews.js
@@ -479,16 +479,18 @@ pref("mail.server.default.allows_special
 pref("mail.server.default.canCreateFolders", true);
 pref("mail.server.default.canFileMessages", true);
 pref("mail.server.default.logon_fallback", true);
 
 // special enhancements for IMAP servers
 pref("mail.server.default.store_read_mail_in_pfc", false);
 pref("mail.server.default.store_sent_mail_in_pfc", false);
 pref("mail.server.default.use_idle", true);
+// in case client or server has bugs in condstore implementation
+pref("mail.server.default.use_condstore", true);
 // for spam
 pref("mail.server.default.spamLevel", 100); // 0 off, 100 on.  not doing bool since we might have real levels one day.
 pref("mail.server.default.moveOnSpam", false);
 pref("mail.server.default.moveTargetMode", 0); // 0 == "Junk" on server, 1 == specific folder
 pref("mail.server.default.spamActionTargetAccount", "");
 pref("mail.server.default.spamActionTargetFolder", "");
 pref("mail.server.default.useWhiteList", true);
 pref("mail.server.default.whiteListAbURI", "moz-abmdbdirectory://abook.mab"); // the Personal addressbook.