--- a/mailnews/base/public/nsIMsgFolderListener.idl
+++ b/mailnews/base/public/nsIMsgFolderListener.idl
@@ -31,32 +31,33 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* 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"
+#include "MailNewsTypes2.idl"
interface nsIMsgDBHdr;
interface nsIMsgFolder;
interface nsIArray;
/**
* This is similar to nsIFolderListener, but with slightly different semantics,
* especially w.r.t. moving messages and folders. Some listeners want to know
* about moves, instead of getting an itemAdded and itemRemoved notification.
* Folder listeners also only tend to get called if a view is open on the folder,
* which is not always the case. I don't want to change nsIFolderListener at this
* point since there are lots of extensions that rely on it. Eventually,
* these two interfaces should be combined somehow.
*/
-[scriptable, uuid(63533aa2-9e03-424c-b308-a3ee5ad47bc5)]
+[scriptable, uuid(2f87be72-0565-4e64-a824-0eb9c258f884)]
interface nsIMsgFolderListener : nsISupports {
/**
* Notified immediately after a message is added to a folder. This could be a
* new incoming message to a local folder, or a new message in an IMAP folder
* when it is opened.
*
* You may want to consider using the msgsClassified notification instead of
* this notification if any of the following are true:
@@ -119,30 +120,51 @@ interface nsIMsgFolderListener : nsISupp
/**
* Notified after a command to move or copy a group of messages completes. In
* case of a move, this is before the messages have been deleted from the
* source folder.
*
* @param aMove true if a move, false if a copy
* @param aSrcMsgs An array of the message headers in the source folder
* @param aDestFolder The folder these messages were moved to.
- * @param aDestMsgs Present only for local folder moves, it provides the list
- * of target message headers.
+ * @param aDestMsgs This provides the list of target message headers.
+ For imap messages, these will be "pseudo" headers, with
+ a made up UID. When we download the "real" header, we
+ will send a msgKeyChanged notification. Currently, if
+ the imap move/copy happens strictly online (essentially,
+ not user-initiated), then aDestMsgs will be null.
*
* @note
* If messages are moved from a server which uses the IMAP delete model,
* you'll get aMove = false. That's because the messages are not deleted from
* the source database, but instead simply marked deleted.
*/
void msgsMoveCopyCompleted(in boolean aMove,
in nsIArray aSrcMsgs,
in nsIMsgFolder aDestFolder,
in nsIArray aDestMsgs);
/**
+ * Notification sent when the msg key for a header may have changed.
+ * This is used when we create a header for an offline imap move result,
+ * without knowing what the ultimate UID will be. When we download the
+ * headers for the new message, we replace the old "pseudo" header with
+ * a new header that has the correct UID/message key. The uid of the new hdr
+ * may turn out to be the same as aOldKey if we've guessed correctly but
+ * the listener can use this notification to know that it can ignore the
+ * msgAdded notification that's coming for aNewHdr. We do NOT send a
+ * msgsDeleted notification for the pseudo header.
+ *
+ * @param aOldKey The fake UID. The header with this key has been removed
+ * by the time this is called.
+ * @param aNewHdr The header that replaces the header with aOldKey.
+ */
+ void msgKeyChanged(in nsMsgKey aOldKey, in nsIMsgDBHdr aNewHdr);
+
+ /**
* Notified after a folder has been added.
*
* @param aFolder The folder that has just been added
*/
void folderAdded(in nsIMsgFolder aFolder);
/**
* Notified after a folder has been deleted and its corresponding file(s) deleted from disk.
--- a/mailnews/base/public/nsIMsgFolderNotificationService.idl
+++ b/mailnews/base/public/nsIMsgFolderNotificationService.idl
@@ -31,25 +31,26 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* 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"
+#include "MailNewsTypes2.idl"
interface nsIMsgDBHdr;
interface nsIMsgFolder;
interface nsIMsgFolderListener;
interface nsIArray;
typedef unsigned long msgFolderListenerFlag;
-[scriptable, uuid(45420c55-b460-4a61-bd67-7f763a69a46e)]
+[scriptable, uuid(e54a592c-2f23-4771-9670-bdb9d4f5dbbd)]
interface nsIMsgFolderNotificationService : nsISupports {
/**
* @name Notification flags
* These flags determine which notifications will be sent.
* @{
*/
/// nsIMsgFolderListener::msgAdded notification
const msgFolderListenerFlag msgAdded = 0x1;
@@ -72,16 +73,20 @@ interface nsIMsgFolderNotificationServic
/// nsIMsgFolderListener::folderMoveCopyCompleted notification
const msgFolderListenerFlag folderMoveCopyCompleted = 0x2000;
/// nsIMsgFolderListener::folderRenamed notification
const msgFolderListenerFlag folderRenamed = 0x4000;
/// nsIMsgFolderListener::itemEvent notification
const msgFolderListenerFlag itemEvent = 0x1000000;
+
+ /// nsIMsgFolderListener::msgKeyChanged notification
+ const msgFolderListenerFlag msgKeyChanged = 0x2000000;
+
/** @} */
readonly attribute boolean hasListeners;
void addListener(in nsIMsgFolderListener aListener,
in msgFolderListenerFlag flags);
void removeListener(in nsIMsgFolderListener aListener);
// message-specific functions
@@ -91,16 +96,29 @@ interface nsIMsgFolderNotificationServic
in boolean aJunkProcessed,
in boolean aTraitProcessed);
void notifyMsgsDeleted(in nsIArray aMsgs);
void notifyMsgsMoveCopyCompleted(in boolean aMove,
in nsIArray aSrcMsgs,
in nsIMsgFolder aDestFolder,
in nsIArray aDestMsgs);
+ /**
+ * Notify listeners that the msg key for a header has changed. Currently,
+ * this is used when we create a header for an offline imap move result,
+ * without knowing what the ultimate UID will be. When we download the
+ * headers for the new message, we replace the old "pseudo" header with
+ * a new header that has the correct UID/message key, by cloning the pseudo
+ * header, which maintains all the existing header attributes.
+ *
+ * @param aOldKey The fake UID. The header with this key has been removed
+ * by the time this is called.
+ * @param aNewHdr The header that replaces the header with aOldKey.
+ */
+ void notifyMsgKeyChanged(in nsMsgKey aOldKey, in nsIMsgDBHdr aNewHdr);
// folder specific functions
// single folders, all the time
void notifyFolderAdded(in nsIMsgFolder aFolder);
void notifyFolderDeleted(in nsIMsgFolder aFolder);
void notifyFolderMoveCopyCompleted(in boolean aMove,
in nsIMsgFolder aSrcFolder,
in nsIMsgFolder aDestFolder);
void notifyFolderRenamed(in nsIMsgFolder aOrigFolder,
--- a/mailnews/base/src/nsMsgFolderNotificationService.cpp
+++ b/mailnews/base/src/nsMsgFolderNotificationService.cpp
@@ -160,16 +160,24 @@ NS_IMETHODIMP nsMsgFolderNotificationSer
}
}
NOTIFY_MSGFOLDER_LISTENERS(msgsMoveCopyCompleted, MsgsMoveCopyCompleted,
(isReallyMove, aSrcMsgs, aDestFolder, aDestMsgs));
return NS_OK;
}
+NS_IMETHODIMP
+nsMsgFolderNotificationService::NotifyMsgKeyChanged(nsMsgKey aOldKey,
+ nsIMsgDBHdr *aNewHdr)
+{
+ NOTIFY_MSGFOLDER_LISTENERS(msgKeyChanged, MsgKeyChanged, (aOldKey, aNewHdr));
+ return NS_OK;
+}
+
/* void notifyFolderAdded(in nsIMsgFolder aFolder); */
NS_IMETHODIMP nsMsgFolderNotificationService::NotifyFolderAdded(nsIMsgFolder *aFolder)
{
NOTIFY_MSGFOLDER_LISTENERS(folderAdded, FolderAdded, (aFolder));
return NS_OK;
}
/* void notifyFolderDeleted(in nsIMsgFolder aFolder); */
--- a/mailnews/base/test/unit/test_nsIMsgFolderListener.js
+++ b/mailnews/base/test/unit/test_nsIMsgFolderListener.js
@@ -16,32 +16,32 @@ const nsIMFListener = Ci.nsIMsgFolderLis
const gIndividualFlags =
[
nsIMFNService.msgAdded,
nsIMFNService.msgsClassified,
nsIMFNService.msgsDeleted,
nsIMFNService.msgsMoveCopyCompleted,
+ nsIMFNService.msgKeyChanged,
nsIMFNService.folderAdded,
nsIMFNService.folderDeleted,
nsIMFNService.folderMoveCopyCompleted,
nsIMFNService.folderRenamed,
nsIMFNService.itemEvent,
];
var gMFNService = Cc[mFNSContractID].getService(nsIMFNService);
// Our listener, which captures events.
function gMFListener() {}
gMFListener.prototype =
{
mReceived: 0,
mRemoveSelf: false,
-
msgAdded: function (aMsg)
{
do_check_eq(this.mReceived & nsIMFNService.msgAdded, 0);
this.mReceived |= nsIMFNService.msgAdded;
if (this.mRemoveSelf)
gMFNService.removeListener(this);
},
@@ -63,17 +63,25 @@ gMFListener.prototype =
msgsMoveCopyCompleted: function (aMove, aSrcMsgs, aDestFolder, aDestMsgs)
{
do_check_eq(this.mReceived & nsIMFNService.msgsMoveCopyCompleted, 0);
this.mReceived |= nsIMFNService.msgsMoveCopyCompleted;
if (this.mRemoveSelf)
gMFNService.removeListener(this);
},
-
+
+ msgKeyChanged: function(aOldMsgKey, aNewMsgHdr)
+ {
+ do_check_eq(this.mReceived & nsIMFNService.msgKeyChanged, 0);
+ this.mReceived |= nsIMFNService.msgKeyChanged;
+ if (this.mRemoveSelf)
+ gMFNService.removeListener(this);
+ },
+
folderAdded: function (aFolder)
{
do_check_eq(this.mReceived & nsIMFNService.folderAdded, 0);
this.mReceived |= nsIMFNService.folderAdded;
if (this.mRemoveSelf)
gMFNService.removeListener(this);
},
@@ -111,16 +119,17 @@ gMFListener.prototype =
};
function NotifyMsgFolderListeners()
{
gMFNService.notifyMsgAdded(null);
gMFNService.notifyMsgsClassified(null, null, null);
gMFNService.notifyMsgsDeleted(null);
gMFNService.notifyMsgsMoveCopyCompleted(null, null, null, null);
+ gMFNService.notifyMsgKeyChanged(null, null);
gMFNService.notifyFolderAdded(null);
gMFNService.notifyFolderDeleted(null);
gMFNService.notifyFolderMoveCopyCompleted(null, null, null);
gMFNService.notifyFolderRenamed(null, null);
gMFNService.notifyItemEvent(null, null, null);
}
function run_test()
--- a/mailnews/imap/public/nsIMsgImapMailFolder.idl
+++ b/mailnews/imap/public/nsIMsgImapMailFolder.idl
@@ -74,17 +74,17 @@ interface nsIMsgImapFolderProps : nsISup
void setQuotaStatus(in AString folderQuotaStatus);
/**
* Updates the quota data displayed in the Quota tab.
*/
void setQuotaData(in ACString quotaroot, in unsigned long usedKB, in unsigned long maxKB);
};
-[scriptable, uuid(d33884c6-20d5-4a84-880c-02ac56c1119f)]
+[scriptable, uuid(fea0f455-7adf-4683-bf2f-c95c3fff03df)]
interface nsIMsgImapMailFolder : nsISupports {
void removeSubFolder(in nsIMsgFolder folder);
void createClientSubfolderInfo(in ACString folderName, in char hierarchyDelimiter,
in long flags, in boolean suppressNotification);
void list();
void renameLocal(in ACString newname, in nsIMsgFolder parent);
void prepareToRename();
void performExpand(in nsIMsgWindow aMsgWindow);
@@ -93,17 +93,26 @@ interface nsIMsgImapMailFolder : nsISupp
// these are used for offline synchronization
void storeImapFlags(in long aFlags, in boolean aAddFlags, [array, size_is (aNumKeys)]
in nsMsgKey aKeysToFlag, in unsigned long aNumKeys, in nsIUrlListener aUrlListener);
nsIURI setImapFlags(in string uids, in long flags);
void replayOfflineMoveCopy([array, size_is (numKeys)] in nsMsgKey keys, in unsigned long numKeys, in boolean isMove, in nsIMsgFolder aDstFolder,
in nsIUrlListener aUrlListener, in nsIMsgWindow aWindow);
nsIURI playbackOfflineFolderCreate(in AString folderName, in nsIMsgWindow aWindow);
-
+ /**
+ * This is called by the offline sync code to tell the imap folder to
+ * remember info about the header with this key (messageId and key) because
+ * it's an offline move result header, and we need to generate an
+ * nsIMsgFolderListener.msgKeyChanged notification when we download the
+ * real header from the imap server.
+ *
+ * @param aMsgKey msg key of move result pseudo hdr.
+ */
+ void addMoveResultPseudoKey(in nsMsgKey aMsgKey);
/**
* Select this folder on the imap server without doing a sync of flags or
* headers. This is used for offline playback, where we don't want to
* download hdrs we don't have, because they may have been offline deleted.
*
* @param aUrlListener url listener, can be null
* @param aWindow msg window url is running in, can be null
*/
--- a/mailnews/imap/src/nsImapIncomingServer.cpp
+++ b/mailnews/imap/src/nsImapIncomingServer.cpp
@@ -859,18 +859,27 @@ nsImapIncomingServer::CreateProtocolInst
break;
}
nsIImapProtocol * protocolInstance;
rv = CallCreateInstance(kImapProtocolCID, &protocolInstance);
if (NS_SUCCEEDED(rv) && protocolInstance)
{
nsCOMPtr<nsIImapHostSessionList> hostSession =
do_GetService(kCImapHostSessionListCID, &rv);
- if (NS_SUCCEEDED(rv))
- rv = protocolInstance->Initialize(hostSession, this, aEventTarget);
+ NS_ENSURE_SUCCESS(rv, rv);
+ PRInt32 socketType;
+ if (NS_SUCCEEDED(GetSocketType(&socketType)) &&
+ socketType == nsMsgSocketType::trySTARTTLS)
+ {
+ PRUint32 capability = kCapabilityUndefined;
+ hostSession->GetCapabilityForHost(m_serverKey.get(), capability);
+ if (capability & kHasStartTLSCapability)
+ SetSocketType(nsMsgSocketType::alwaysSTARTTLS);
+ }
+ rv = protocolInstance->Initialize(hostSession, this, aEventTarget);
}
// take the protocol instance and add it to the connectionCache
if (protocolInstance)
m_connectionCache.AppendObject(protocolInstance);
*aImapConnection = protocolInstance; // this is already ref counted.
return rv;
}
--- a/mailnews/imap/src/nsImapMailFolder.cpp
+++ b/mailnews/imap/src/nsImapMailFolder.cpp
@@ -128,16 +128,17 @@
#include "nsArrayEnumerator.h"
#include "nsAutoSyncManager.h"
#include "nsIMsgFilterCustomAction.h"
#include "nsMsgReadStateTxn.h"
#include "nsIStringEnumerator.h"
#include "nsIMsgStatusFeedback.h"
#include "nsAlgorithm.h"
#include "nsPrintfCString.h"
+#include "nsMsgLineBuffer.h"
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kParseMailMsgStateCID, NS_PARSEMAILMSGSTATE_CID);
static NS_DEFINE_CID(kCImapHostSessionList, NS_IIMAPHOSTSESSIONLIST_CID);
nsIAtom* nsImapMailFolder::mImapHdrDownloadedAtom = nsnull;
extern PRLogModuleInfo *gAutoSyncLog;
@@ -2835,33 +2836,29 @@ NS_IMETHODIMP nsImapMailFolder::UpdateIm
aSpec->GetBox_flags(&boxFlags);
FindKeysToDelete(existingKeys, keysToDelete, flagState, boxFlags);
// if this is the result of an expunge then don't grab headers
if (!(boxFlags & kJustExpunged))
FindKeysToAdd(existingKeys, keysToFetch, numNewUnread, flagState);
}
if (!keysToDelete.IsEmpty() && mDatabase)
{
- PRUint32 total;
-
nsCOMPtr<nsIMutableArray> hdrsToDelete(do_CreateInstance(NS_ARRAY_CONTRACTID));
MsgGetHeadersFromKeys(mDatabase, keysToDelete, hdrsToDelete);
// Notify nsIMsgFolderListeners of a mass delete, but only if we actually have headers
PRUint32 numHdrs;
hdrsToDelete->GetLength(&numHdrs);
if (numHdrs)
{
nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
if (notifier)
notifier->NotifyMsgsDeleted(hdrsToDelete);
}
-
// It would be nice to notify RDF or whoever of a mass delete here.
mDatabase->DeleteMessages(keysToDelete.Length(), keysToDelete.Elements(), nsnull);
- total = keysToDelete.Length();
}
PRInt32 numUnreadFromServer;
aSpec->GetNumUnseenMessages(&numUnreadFromServer);
PRBool partialUIDFetch;
flagState->GetPartialUIDFetch(&partialUIDFetch);
// For partial UID fetches, we can only trust the numUnread from the server.
@@ -3002,17 +2999,17 @@ NS_IMETHODIMP nsImapMailFolder::ParseMsg
inputStream->ShareData(msgHdrs, strlen(msgHdrs));
GetMessageHeader(msgKey, getter_AddRefs(msgHdr));
if (msgHdr)
GetMsgPreviewTextFromStream(msgHdr, inputStream);
continue;
}
if (mDatabase && NS_SUCCEEDED(mDatabase->ContainsKey(msgKey, &containsKey)) && containsKey)
{
- NS_ASSERTION(PR_FALSE, "downloading hdrs for hdr we already have");
+ NS_ERROR("downloading hdrs for hdr we already have");
continue;
}
nsresult rv = SetupHeaderParseStream(msgSize, EmptyCString(), nsnull);
NS_ENSURE_SUCCESS(rv, rv);
headerInfo->GetMsgHdrs(&msgHdrs);
rv = ParseAdoptedHeaderLine(msgHdrs, msgKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = NormalEndHeaderParseStream(aProtocol, aImapUrl);
@@ -3182,18 +3179,33 @@ nsresult nsImapMailFolder::NormalEndHead
NotifyFolderEvent(mFiltersAppliedAtom);
}
}
}
}
// here we need to tweak flags from uid state..
if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages()))
{
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ // Check if this header corresponds to a pseudo header
+ // we have from doing a pseudo-offline move and then downloading
+ // the real header from the server. In that case, we notify
+ // db/folder listeners that the pseudo-header has become the new
+ // header, i.e., the key has changed.
+ nsCString newMessageId;
+ nsMsgKey pseudoKey = nsMsgKey_None;
+ newMsgHdr->GetMessageId(getter_Copies(newMessageId));
+ if (m_pseudoHdrs.IsInitialized())
+ m_pseudoHdrs.Get(newMessageId, &pseudoKey);
+ if (notifier && pseudoKey != nsMsgKey_None)
+ {
+ notifier->NotifyMsgKeyChanged(pseudoKey, newMsgHdr);
+ m_pseudoHdrs.Remove(newMessageId);
+ }
mDatabase->AddNewHdrToDB(newMsgHdr, PR_TRUE);
- nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
if (notifier)
notifier->NotifyMsgAdded(newMsgHdr);
// mark the header as not yet reported classified
OrProcessingFlags(m_curMsgUid, nsMsgProcessingFlags::NotReportedClassified);
}
// adjust highestRecordedUID
if (mDatabase)
{
@@ -3891,16 +3903,31 @@ nsImapMailFolder::ReplayOfflineMoveCopy(
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsIUrlListener> folderListener = do_QueryInterface(aDstFolder);
if (folderListener)
mailnewsUrl->RegisterListener(folderListener);
}
return rv;
}
+NS_IMETHODIMP nsImapMailFolder::AddMoveResultPseudoKey(nsMsgKey aMsgKey)
+{
+ nsCOMPtr<nsIMsgDBHdr> pseudoHdr;
+ mDatabase->GetMsgHdrForKey(aMsgKey, getter_AddRefs(pseudoHdr));
+ nsCString messageId;
+ pseudoHdr->GetMessageId(getter_Copies(messageId));
+ // err on the side of caution and ignore messages w/o messageid.
+ if (messageId.IsEmpty())
+ return NS_OK;
+ if (!m_pseudoHdrs.IsInitialized())
+ m_pseudoHdrs.Init(10);
+ m_pseudoHdrs.Put(messageId, aMsgKey);
+ return NS_OK;
+}
+
NS_IMETHODIMP nsImapMailFolder::StoreImapFlags(PRInt32 flags, PRBool addFlags,
nsMsgKey *keys, PRUint32 numKeys,
nsIUrlListener *aUrlListener)
{
nsresult rv;
if (!WeAreOffline())
{
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
@@ -5701,18 +5728,16 @@ nsImapMailFolder::GetMessageId(nsIImapUr
}
return rv;
}
NS_IMETHODIMP
nsImapMailFolder::HeaderFetchCompleted(nsIImapProtocol* aProtocol)
{
nsCOMPtr <nsIMsgWindow> msgWindow; // we might need this for the filter plugins.
- if (mDatabase)
- mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
if (mBackupDatabase)
RemoveBackupMsgDatabase();
SetSizeOnDisk(mFolderSize);
PRInt32 numNewBiffMsgs = 0;
if (m_performingBiff)
GetNumNewMessages(PR_FALSE, &numNewBiffMsgs);
@@ -6694,18 +6719,16 @@ nsImapMailFolder::CopyNextStreamMessage(
nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
if (notifier)
{
PRUint32 numHdrs;
mailCopyState->m_messages->GetLength(&numHdrs);
if (numHdrs)
notifier->NotifyMsgsMoveCopyCompleted(mailCopyState->m_isMove, mailCopyState->m_messages, this, nsnull);
}
-
-
if (mailCopyState->m_isMove)
{
nsCOMPtr<nsIMsgFolder> srcFolder(do_QueryInterface(mailCopyState->m_srcSupport, &rv));
if (NS_SUCCEEDED(rv) && srcFolder)
{
srcFolder->DeleteMessages(mailCopyState->m_messages, nsnull,
PR_TRUE, PR_TRUE, nsnull, PR_FALSE);
// we want to send this notification after the source messages have
@@ -6965,16 +6988,20 @@ nsresult nsImapMailFolder::CopyMessagesO
srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(srcDbFolderInfo), getter_AddRefs(sourceMailDB));
PRBool deleteToTrash = PR_FALSE;
PRBool deleteImmediately = PR_FALSE;
PRUint32 srcCount;
messages->GetLength(&srcCount);
nsCOMPtr<nsIImapIncomingServer> imapServer;
rv = GetImapIncomingServer(getter_AddRefs(imapServer));
nsCOMPtr<nsIMutableArray> msgHdrsCopied(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ nsCOMPtr<nsIMutableArray> destMsgHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ if (!msgHdrsCopied || !destMsgHdrs)
+ return NS_ERROR_OUT_OF_MEMORY;
if (NS_SUCCEEDED(rv) && imapServer)
{
nsMsgImapDeleteModel deleteModel;
imapServer->GetDeleteModel(&deleteModel);
deleteToTrash = (deleteModel == nsMsgImapDeleteModels::MoveToTrash);
deleteImmediately = (deleteModel == nsMsgImapDeleteModels::DeleteNoTrash);
}
@@ -7140,17 +7167,21 @@ nsresult nsImapMailFolder::CopyMessagesO
{
NS_ASSERTION(PR_FALSE, "failed to copy hdr");
stopit = rv;
}
if (NS_SUCCEEDED(stopit))
{
PRBool hasMsgOffline = PR_FALSE;
+
+ destMsgHdrs->AppendElement(newMailHdr, PR_FALSE);
srcFolder->HasMsgOffline(originalKey, &hasMsgOffline);
+ newMailHdr->SetUint32Property("pseudoHdr", 1);
+
if (inputStream && hasMsgOffline && !isLocked)
CopyOfflineMsgBody(srcFolder, newMailHdr, mailHdr, inputStream,
outputStream);
else
mDatabase->MarkOffline(fakeBase + sourceKeyIndex, PR_FALSE, nsnull);
nsCOMPtr <nsIMsgOfflineImapOperation> destOp;
mDatabase->GetOfflineOpForKey(fakeBase + sourceKeyIndex, PR_TRUE, getter_AddRefs(destOp));
@@ -7232,17 +7263,17 @@ nsresult nsImapMailFolder::CopyMessagesO
// Do this before delete, as it destroys the messages
PRUint32 numHdrs;
msgHdrsCopied->GetLength(&numHdrs);
if (numHdrs)
{
nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
if (notifier)
- notifier->NotifyMsgsMoveCopyCompleted(isMove, msgHdrsCopied, this, nsnull);
+ notifier->NotifyMsgsMoveCopyCompleted(isMove, msgHdrsCopied, this, destMsgHdrs);
}
if (isMove && (deleteToTrash || deleteImmediately))
sourceMailDB->DeleteMessages(keysToDelete.Length(), keysToDelete.Elements(), nsnull);
nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
OnCopyCompleted(srcSupport, rv);
@@ -8117,18 +8148,123 @@ nsImapMailFolder::InitCopyState(nsISuppo
m_copyState->m_selectedState = selectedState;
m_copyState->m_msgWindow = msgWindow;
if (listener)
m_copyState->m_listener = do_QueryInterface(listener, &rv);
return rv;
}
nsresult
+nsImapMailFolder::CopyFileToOfflineStore(nsILocalFile *srcFile)
+{
+ nsCOMPtr<nsIMsgDatabase> db;
+ nsresult rv = GetMsgDatabase(getter_AddRefs(db));
+
+ if (mDatabase)
+ {
+ nsMsgKey fakeKey;
+ mDatabase->GetNextFakeOfflineMsgKey(&fakeKey);
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ nsCOMPtr<nsIMsgOfflineImapOperation> op;
+ rv = mDatabase->GetOfflineOpForKey(fakeKey, PR_TRUE, getter_AddRefs(op));
+ if (NS_SUCCEEDED(rv) && op)
+ {
+ nsCString destFolderUri;
+ GetURI(destFolderUri);
+ op->SetOperation(nsIMsgOfflineImapOperation::kMoveResult);
+ op->SetDestinationFolderURI(destFolderUri.get());
+ nsCOMPtr<nsIOutputStream> offlineStore;
+ rv = GetOfflineStoreOutputStream(getter_AddRefs(offlineStore));
+ SetFlag(nsMsgFolderFlags::OfflineEvents);
+
+ if (NS_SUCCEEDED(rv) && offlineStore)
+ {
+ PRInt64 curOfflineStorePos = 0;
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(offlineStore);
+ if (seekable)
+ seekable->Tell(&curOfflineStorePos);
+ else
+ {
+ NS_ERROR("needs to be a random store!");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsCOMPtr<nsIMsgParseMailMsgState> msgParser =
+ do_CreateInstance(NS_PARSEMAILMSGSTATE_CONTRACTID, &rv);
+ msgParser->SetMailDB(mDatabase);
+
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), srcFile);
+ if (NS_SUCCEEDED(rv) && inputStream)
+ {
+ // now, copy the temp file to the offline store for the cur folder.
+ PRInt32 inputBufferSize = 10240;
+ nsMsgLineStreamBuffer *inputStreamBuffer =
+ new nsMsgLineStreamBuffer(inputBufferSize, PR_TRUE, PR_FALSE);
+ PRInt64 fileSize;
+ srcFile->GetFileSize(&fileSize);
+ PRUint32 bytesWritten;
+ rv = NS_OK;
+ msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
+ // set the env pos to fake key so the msg hdr will have that for a key
+ msgParser->SetEnvelopePos(fakeKey);
+ PRBool needMoreData = PR_FALSE;
+ char * newLine = nsnull;
+ PRUint32 numBytesInLine = 0;
+ do
+ {
+ newLine = inputStreamBuffer->ReadNextLine(inputStream, numBytesInLine, needMoreData);
+ if (newLine)
+ {
+ msgParser->ParseAFolderLine(newLine, numBytesInLine);
+ rv = offlineStore->Write(newLine, numBytesInLine, &bytesWritten);
+ NS_Free(newLine);
+ }
+ } while (newLine);
+
+ nsCOMPtr<nsIMsgDBHdr> fakeHdr;
+ msgParser->FinishHeader();
+ msgParser->GetNewMsgHdr(getter_AddRefs(fakeHdr));
+ if (fakeHdr)
+ {
+ if (NS_SUCCEEDED(rv) && fakeHdr)
+ {
+ PRUint32 resultFlags;
+ fakeHdr->SetMessageOffset(curOfflineStorePos);
+ fakeHdr->OrFlags(nsMsgMessageFlags::Offline | nsMsgMessageFlags::Read, &resultFlags);
+ fakeHdr->SetOfflineMessageSize(fileSize);
+ fakeHdr->SetUint32Property("pseudoHdr", 1);
+ mDatabase->AddNewHdrToDB(fakeHdr, PR_TRUE /* notify */);
+ SetFlag(nsMsgFolderFlags::OfflineEvents);
+ messages->AppendElement(fakeHdr, PR_FALSE);
+ SetPendingAttributes(messages, PR_FALSE);
+ }
+ }
+ inputStream->Close();
+ inputStream = nsnull;
+ delete inputStreamBuffer;
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult
nsImapMailFolder::OnCopyCompleted(nsISupports *srcSupport, nsresult rv)
{
+ // if it's a file, and the copy succeeded, then fcc the offline
+ // store, and add a kMoveResult offline op.
+ if (NS_SUCCEEDED(rv) && m_copyState)
+ {
+ nsCOMPtr<nsILocalFile> srcFile(do_QueryInterface(srcSupport));
+ if (srcFile && (mFlags & nsMsgFolderFlags::Offline) && !WeAreOffline())
+ (void) CopyFileToOfflineStore(srcFile);
+ }
m_copyState = nsnull;
nsresult result;
nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &result);
NS_ENSURE_SUCCESS(result, result);
return copyService->NotifyCompletion(srcSupport, this, rv);
}
nsresult nsImapMailFolder::CreateBaseMessageURI(const nsACString& aURI)
--- a/mailnews/imap/src/nsImapMailFolder.h
+++ b/mailnews/imap/src/nsImapMailFolder.h
@@ -371,16 +371,17 @@ protected:
PRUint16 userFlags, nsCString& keywords);
nsresult NotifyMessageFlagsFromHdr(nsIMsgDBHdr *dbHdr, nsMsgKey msgKey, PRUint32 flags);
nsresult SetupHeaderParseStream(PRUint32 size, const nsACString& content_type, nsIMailboxSpec *boxSpec);
nsresult ParseAdoptedHeaderLine(const char *messageLine, PRUint32 msgKey);
nsresult NormalEndHeaderParseStream(nsIImapProtocol *aProtocol, nsIImapUrl *imapUrl);
void EndOfflineDownload();
+ nsresult CopyFileToOfflineStore(nsILocalFile *srcFile);
nsresult MarkMessagesImapDeleted(nsTArray<nsMsgKey> *keyArray, PRBool deleted, nsIMsgDatabase *db);
// Notifies imap autosync that it should update this folder when it
// gets a chance.
void NotifyHasPendingMsgs();
void UpdatePendingCounts();
void SetIMAPDeletedFlag(nsIMsgDatabase *mailDB, const nsTArray<nsMsgKey> &msgids, PRBool markDeleted);
@@ -517,24 +518,26 @@ protected:
PRUint32 m_aclFlags;
PRUint32 m_supportedUserFlags;
static nsIAtom* mImapHdrDownloadedAtom;
// offline imap support
PRBool m_downloadingFolderForOfflineUse;
PRBool m_filterListRequiresBody;
-
+
// auto-sync (preemptive download) support
nsRefPtr<nsAutoSyncState> m_autoSyncStateObj;
-
+
// Quota support
nsCString m_folderQuotaRoot;
PRUint32 m_folderQuotaUsedKB;
PRUint32 m_folderQuotaMaxKB;
-
+
// Pseudo-Offline Playback support
nsPlaybackRequest *m_pendingPlaybackReq;
nsCOMPtr<nsITimer> m_playbackTimer;
nsTArray<nsRefPtr<nsImapMoveCopyMsgTxn> > m_pendingOfflineMoves;
-
+ // hash table of mapping between messageids and message keys
+ // for pseudo hdrs.
+ nsDataHashtable<nsCStringHashKey, nsMsgKey> m_pseudoHdrs;
};
#endif
--- a/mailnews/imap/src/nsImapOfflineSync.cpp
+++ b/mailnews/imap/src/nsImapOfflineSync.cpp
@@ -608,39 +608,43 @@ PRBool nsImapOfflineSync::DestFolderOnSa
PRBool sameServer = PR_FALSE;
if (NS_SUCCEEDED(m_currentFolder->GetServer(getter_AddRefs(srcServer)))
&& NS_SUCCEEDED(destFolder->GetServer(getter_AddRefs(dstServer))))
dstServer->Equals(srcServer, &sameServer);
return sameServer;
}
-void nsImapOfflineSync::ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp)
+void nsImapOfflineSync::ProcessCopyOperation(nsIMsgOfflineImapOperation *aCurrentOp)
{
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = aCurrentOp;
+
nsTArray<nsMsgKey> matchingFlagKeys;
PRUint32 currentKeyIndex = m_KeyIndex;
nsCString copyDestination;
currentOp->GetCopyDestination(0, getter_Copies(copyDestination));
PRBool copyMatches = PR_TRUE;
-
+ nsresult rv;
+
do { // loop for all messsages with the same destination
if (copyMatches)
{
nsMsgKey curKey;
currentOp->GetMessageKey(&curKey);
matchingFlagKeys.AppendElement(curKey);
currentOp->SetPlayingBack(PR_TRUE);
m_currentOpsToClear.AppendObject(currentOp);
}
currentOp = nsnull;
if (++currentKeyIndex < m_CurrentKeys.Length())
{
nsCString nextDestination;
- nsresult rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE, ¤tOp);
+ rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex],
+ PR_FALSE, getter_AddRefs(currentOp));
copyMatches = PR_FALSE;
if (NS_SUCCEEDED(rv) && currentOp)
{
nsOfflineImapOperationType opType;
currentOp->GetOperation(&opType);
if (opType & nsIMsgOfflineImapOperation::kMsgCopy)
{
currentOp->GetCopyDestination(0, getter_Copies(nextDestination));
@@ -658,36 +662,33 @@ void nsImapOfflineSync::ProcessCopyOpera
// going to fail, so clear them out and move on.
if (!destFolder)
{
NS_ERROR("trying to playing back copy to non-existent folder");
ClearCurrentOps();
ProcessNextOperation();
return;
}
- nsresult rv;
nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
if (imapFolder && DestFolderOnSameServer(destFolder))
{
rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys.Elements(), matchingFlagKeys.Length(), PR_FALSE, destFolder,
this, m_window);
}
else
{
nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
if (messages && NS_SUCCEEDED(rv))
{
for (PRUint32 keyIndex = 0; keyIndex < matchingFlagKeys.Length(); keyIndex++)
{
nsCOMPtr<nsIMsgDBHdr> mailHdr = nsnull;
rv = m_currentFolder->GetMessageHeader(matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
if (NS_SUCCEEDED(rv) && mailHdr)
- {
messages->AppendElement(mailHdr, PR_FALSE);
- }
}
nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
if (copyService)
copyService->CopyMessages(m_currentFolder, messages, destFolder, PR_FALSE, this, m_window, PR_FALSE);
}
}
}
@@ -823,26 +824,33 @@ nsresult nsImapOfflineSync::ProcessNextO
if (opType == nsIMsgOfflineImapOperation::kMoveResult)
{
nsMsgKey curKey;
currentOp->GetMessageKey(&curKey);
m_currentDB->RemoveOfflineOp(currentOp);
deletedGhostMsgs = PR_TRUE;
+ // Remember the pseudo headers before we delete them,
+ // and when we download new headers, tell listeners about the
+ // message key change between the pseudo headers and the real
+ // downloaded headers. Note that we're not currently sending
+ // a msgsDeleted notifcation for these headers, but the
+ // db listeners are notified about the deletion.
// for imap folders, we should adjust the pending counts, because we
// have a header that we know about, but don't have in the db.
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
if (imapFolder)
{
PRBool hdrIsRead;
m_currentDB->IsRead(curKey, &hdrIsRead);
imapFolder->ChangePendingTotal(1);
if (!hdrIsRead)
imapFolder->ChangePendingUnread(1);
+ imapFolder->AddMoveResultPseudoKey(curKey);
}
m_currentDB->DeleteMessage(curKey, nsnull, PR_FALSE);
}
}
}
if (deletedGhostMsgs)
m_currentFolder->SummaryChanged();
--- a/mailnews/imap/test/unit/test_imapAttachmentSaves.js
+++ b/mailnews/imap/test/unit/test_imapAttachmentSaves.js
@@ -138,18 +138,18 @@ function testDetach()
// directory.
let checkFile = gProfileDir.clone();
checkFile.append(kAttachFileName);
do_check_true(checkFile.exists());
// The message should now have a detached attachment. Read the message,
// and search for "AttachmentDetached" which is added on detachment.
- // Get the message header
- let msgHdr = firstMsgHdr(gIMAPInbox);
+ // Get the message header - detached copy has UID 2.
+ let msgHdr = gIMAPInbox.GetMessageHeader(2);
do_check_neq(msgHdr, null);
let messageContent = getContentFromMessage(msgHdr);
do_check_true(messageContent.indexOf("AttachmentDetached") != -1);
}
// Cleanup
function endTest()
{
--- a/mailnews/imap/test/unit/test_nsIMsgFolderListenerIMAP.js
+++ b/mailnews/imap/test/unit/test_nsIMsgFolderListenerIMAP.js
@@ -28,16 +28,19 @@ const gMsgFile4 = do_get_file("../../../
const gMsgFile5 = do_get_file("../../../data/bugmail6");
// Copied straight from the example files
const gMsgId1 = "200806061706.m56H6RWT004933@mrapp54.mozilla.org";
const gMsgId2 = "200804111417.m3BEHTk4030129@mrapp51.mozilla.org";
const gMsgId3 = "4849BF7B.2030800@example.com";
const gMsgId4 = "bugmail7.m47LtAEf007542@mrapp51.mozilla.org";
const gMsgId5 = "bugmail6.m47LtAEf007542@mrapp51.mozilla.org";
+var gMsgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(Components.interfaces.nsIMsgWindow);
+
function addFolder(parent, folderName, storeIn)
{
gExpectedEvents = [[gMFNService.folderAdded, parent, folderName, storeIn]];
// No copy listener notification for this
gCurrStatus |= kStatus.onStopCopyDone;
parent.createSubfolder(folderName, null);
gCurrStatus |= kStatus.functionCallDone;
@@ -121,30 +124,32 @@ function addMessagesToServer(messages, m
function copyMessages(messages, isMove, srcFolder, destFolder)
{
let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
messages.forEach(function (message)
{
array.appendElement(message, false);
});
- gExpectedEvents = [[gMFNService.msgsMoveCopyCompleted, isMove, messages, destFolder]];
+ gExpectedEvents = [[gMFNService.msgsMoveCopyCompleted, isMove, messages, destFolder, true]];
// We'll also get the msgAdded events when we go and update the destination
// folder
messages.forEach(function (message)
{
// We can't use the headers directly, because the notifications we'll
// receive are for message headers in the destination folder
+ gExpectedEvents.push([gMFNService.msgKeyChanged,
+ {expectedMessageId: message.messageId}]);
gExpectedEvents.push([gMFNService.msgAdded,
{expectedMessageId: message.messageId}]);
});
gExpectedEvents.push([gMFNService.msgsClassified,
[hdr.messageId for each (hdr in messages)],
false, false]);
- gCopyService.CopyMessages(srcFolder, array, destFolder, isMove, copyListener, null, true);
+ gCopyService.CopyMessages(srcFolder, array, destFolder, isMove, copyListener, gMsgWindow, true);
gCurrStatus |= kStatus.functionCallDone;
gServer.performTest("COPY");
gFolderBeingUpdated = destFolder;
doUpdateFolder(gTest);
if (gCurrStatus == kStatus.everythingDone)
resetStatusAndProceed();
@@ -179,17 +184,17 @@ const gTestArray =
// Add another couple of messages, this time to another folder on the server
function testNewMessageArrival2() {
addMessagesToServer([{file: gMsgFile4, messageId: gMsgId4},
{file: gMsgFile5, messageId: gMsgId5}],
gIMAPDaemon.getMailbox("INBOX"), gIMAPInbox);
},
// Moving/copying messages (this doesn't work right now)
- /* function testCopyMessages1() { copyMessages([gMsgHdrs[0].hdr, gMsgHdrs[1].hdr], false, gIMAPInbox, gIMAPFolder3) } */
+ function testCopyMessages1() { copyMessages([gMsgHdrs[0].hdr, gMsgHdrs[1].hdr], false, gIMAPInbox, gIMAPFolder3) }
];
function run_test()
{
// This is before any of the actual tests, so...
gTest = 0;
// Add a listener.
@@ -265,16 +270,17 @@ function doTest(test)
testFn();
}
else
{
gMFNService.removeListener(gMFListener);
// Cleanup, null out everything, close all cached connections and stop the
// server
gRootFolder = null;
+ gIMAPInbox.msgDatabase = null;
gIMAPInbox = null;
gIMAPFolder2 = null;
gIMAPFolder3 = null;
gIMAPTrashFolder = null;
do_timeout(1000, endTest);
}
}
--- a/mailnews/local/src/nsRssIncomingServer.cpp
+++ b/mailnews/local/src/nsRssIncomingServer.cpp
@@ -277,16 +277,22 @@ NS_IMETHODIMP nsRssIncomingServer::MsgsD
NS_IMETHODIMP nsRssIncomingServer::MsgsMoveCopyCompleted(
PRBool aMove, nsIArray *aSrcMsgs, nsIMsgFolder *aDestFolder,
nsIArray *aDestMsgs)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
+NS_IMETHODIMP nsRssIncomingServer::MsgKeyChanged(nsMsgKey aOldKey,
+ nsIMsgDBHdr *aNewHdr)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
NS_IMETHODIMP nsRssIncomingServer::FolderAdded(nsIMsgFolder *aFolder)
{
return FolderChanged(aFolder, PR_FALSE);
}
NS_IMETHODIMP nsRssIncomingServer::FolderDeleted(nsIMsgFolder *aFolder)
{
return FolderChanged(aFolder, PR_TRUE);
--- a/mailnews/test/resources/msgFolderListenerSetup.js
+++ b/mailnews/test/resources/msgFolderListenerSetup.js
@@ -1,40 +1,21 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- *
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * Setup for nsIMsgFolderListener tests.
- *
- * To create a test on top of this,
- *
- * - define a doTest function which accepts the number of the test as an argument.
- * - for each test, store the expected events as an array in gExpectedEvents, with
- * format [event, item array] or [event, is move, item array, destination folder].
- * - use the copyListener and gMFListener
- * - make sure you set the function call flag once your function call is done.
- */
-
const nsIMsgDBHdr = Ci.nsIMsgDBHdr;
const nsIArray = Ci.nsIArray;
const nsIMsgFolder = Ci.nsIMsgFolder;
const gMFNService = Cc["@mozilla.org/messenger/msgnotificationservice;1"]
.getService(Ci.nsIMsgFolderNotificationService);
const allTestedEvents =
gMFNService.msgAdded |
gMFNService.msgsClassified |
gMFNService.msgsDeleted |
gMFNService.msgsMoveCopyCompleted |
+ gMFNService.msgKeyChanged |
gMFNService.folderAdded |
gMFNService.folderDeleted |
gMFNService.folderMoveCopyCompleted |
gMFNService.folderRenamed |
gMFNService.itemEvent;
const gCopyService = Cc["@mozilla.org/messenger/messagecopyservice;1"]
.getService(Ci.nsIMsgCopyService);
@@ -118,16 +99,27 @@ var gMFListener =
if (gExpectedEvents.length == 0)
{
gCurrStatus |= kStatus.notificationsDone;
if (gCurrStatus == kStatus.everythingDone)
resetStatusAndProceed();
}
},
+ msgKeyChanged: function(aOldKey, aNewMsgHdr)
+ {
+ verify([gMFNService.msgKeyChanged, aOldKey, aNewMsgHdr]);
+ if (gExpectedEvents.length == 0)
+ {
+ gCurrStatus |= kStatus.notificationsDone;
+ if (gCurrStatus == kStatus.everythingDone)
+ resetStatusAndProceed();
+ }
+ },
+
folderAdded: function(aFolder)
{
verify([gMFNService.folderAdded, aFolder]);
if (gExpectedEvents.length == 0)
{
gCurrStatus |= kStatus.notificationsDone;
if (gCurrStatus == kStatus.everythingDone)
resetStatusAndProceed();
@@ -312,47 +304,42 @@ function verify(event)
else { // actual headers
hasExactlyElements(expected[1], event[1]);
}
// aJunkProcessed: was the message processed for junk?
do_check_eq(expected[2], event[2]);
// aTraitProcessed: was the message processed for traits?
do_check_eq(expected[3], event[3]);
break;
+ case gMFNService.msgKeyChanged:
+ do_check_eq(expected[1].messageId, event[2].expectedMessageId);
+ break;
case gMFNService.msgsMoveCopyCompleted:
case gMFNService.folderMoveCopyCompleted:
// Check: Move or copy as expected.
do_check_eq(expected[1], event[1]);
// Check: headers match/folder matches.
hasExactlyElements(expected[2], event[2]);
// Check: destination folder matches.
do_check_eq(expected[3], event[3]);
if (eventType == gMFNService.folderMoveCopyCompleted)
break;
- // Check: destination headers. We only expect these in the local folder
- // case, and in that case, we will not have heard about the headers ahead
- // of time, so the best we can do is make sure they match up. To this end,
- // if null is expected then we check for null. If true is expected, then
+ // Check: destination headers. We expect these for local and imap folders,
+ // but we will not have heard about the headers ahead of time,
+ // so the best we can do is make sure they match up. To this end,
// we check that the message-id header values match up.
- if (expected[4] == null)
+ for (let iMsg = 0; iMsg < event[2].length; iMsg++)
{
- do_check_eq(null, event[4]);
- }
- else
- {
- for (let iMsg = 0; iMsg < event[2].length; iMsg++)
- {
- let srcHdr = event[2].queryElementAt(iMsg, nsIMsgDBHdr);
- let destHdr = event[4].queryElementAt(iMsg, nsIMsgDBHdr);
- do_check_eq(srcHdr.messageId, destHdr.messageId);
- }
+ let srcHdr = event[2].queryElementAt(iMsg, nsIMsgDBHdr);
+ let destHdr = event[4].queryElementAt(iMsg, nsIMsgDBHdr);
+ do_check_eq(srcHdr.messageId, destHdr.messageId);
}
break;
case gMFNService.folderAdded:
// Check: parent folder matches
do_check_eq(event[1].parent, expected[1]);
// Check: folder name matches
do_check_eq(event[1].prettyName, expected[2]);