fix 471682, local summary files getting out of date when messages moved/copied to them, r/sr=bienvenu
fix 471682, local summary files getting out of date when messages moved/copied to them, r/sr=bienvenu
new file mode 100644
--- /dev/null
+++ b/mailnews/base/test/unit/test_bug471682.js
@@ -0,0 +1,133 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Kent James <kent@caspia.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+/*
+ * Test of message database validity on local copy from bug 471682. What
+ * we want to do here is to copy a couple of message to a new folder, and
+ * then compare the date and filesize of the folder file with the
+ * stored result in dbfolderinfo. If they don't match, that's bad.
+ */
+const copyService = Cc["@mozilla.org/messenger/messagecopyservice;1"]
+ .getService(Ci.nsIMsgCopyService);
+const bugmail1 = do_get_file("../mailnews/test/data/bugmail1");
+var gHdr; // header of test message in local folder
+
+loadLocalMailAccount();
+// create a subfolder as a target for copies
+var gSubfolder = gLocalInboxFolder.addSubfolder("subfolder");
+
+function run_test()
+{
+ do_test_pending();
+ dump(" step 1 ");
+ // step 1: copy a message into the local inbox
+ copyService.CopyFileMessage(bugmail1, gLocalInboxFolder, null, false, 0,
+ "", step2, null);
+ return;
+}
+
+// step 2: copy one message into a subfolder to establish an
+// mbox file time and size
+// nsIMsgCopyServiceListener implementation
+var step2 =
+{
+ OnStartCopy: function() {},
+ OnProgress: function(aProgress, aProgressMax) {},
+ SetMessageKey: function(aKey)
+ {
+ gHdr = gLocalInboxFolder.GetMessageHeader(aKey);
+ },
+ SetMessageId: function(aMessageId) {},
+ OnStopCopy: function(aStatus)
+ {
+ dump(" step 2 ");
+ // copy the message into the subfolder
+ var messages = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ messages.appendElement(gHdr, false);
+ copyService.CopyMessages(gLocalInboxFolder, messages, gSubfolder, false,
+ step3, null, false);
+ }
+};
+
+// step 3: after the copy, delay to allow copy to complete and allow possible
+// file error time
+// nsIMsgCopyServiceListener implementation
+var step3 =
+{
+ OnStartCopy: function() {},
+ OnProgress: function(aProgress, aProgressMax) {},
+ SetMessageKey: function(aKey) {},
+ SetMessageId: function(aMessageId) {},
+ OnStopCopy: function(aStatus)
+ {
+ dump(" step 3 ");
+ do_timeout(2000, "step4();");
+ }
+}
+
+// step 4: start a second copy
+function step4()
+{
+ dump(" step 4 ");
+ var messages = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ messages.appendElement(gHdr, false);
+ copyService.CopyMessages(gLocalInboxFolder, messages, gSubfolder, false,
+ step5, null, false);
+}
+
+// step 5: actual tests of file size and date
+// nsIMsgCopyServiceListener implementation
+var step5 =
+{
+ OnStartCopy: function() {},
+ OnProgress: function(aProgress, aProgressMax) {},
+ SetMessageKey: function(aKey) {},
+ SetMessageId: function(aMessageId) {},
+ OnStopCopy: function(aStatus)
+ {
+ dump(" step 5 ");
+ var dbSize = gSubfolder.msgDatabase.dBFolderInfo.folderSize;
+ var dbDate = gSubfolder.msgDatabase.dBFolderInfo.folderDate;
+ var filePath = gSubfolder.filePath;
+ var date = parseInt(filePath.lastModifiedTime/1000);
+ var size = filePath.fileSize;
+ dump("\nactual size: " + size + " recorded size: " + dbSize);
+ do_check_eq(size, dbSize);
+ dump("\nactual date: " + date + " recorded date: " + dbDate);
+ do_check_eq(date, dbDate);
+ do_test_finished();
+ }
+}
--- a/mailnews/db/msgdb/public/nsMailDatabase.h
+++ b/mailnews/db/msgdb/public/nsMailDatabase.h
@@ -78,17 +78,19 @@ public:
NS_IMETHOD SetFolderStream(nsIOutputStream *aFileStream);
NS_IMETHOD GetFolderStream(nsIOutputStream **aFileStream);
friend class nsMsgOfflineOpEnumerator;
protected:
nsresult GetAllOfflineOpsTable(); // get this on demand
- PRUint32 GetMailboxModDate();
+
+ // get the time and date of the mailbox file
+ void GetMailboxModProperties(PRInt64 *aSize, PRUint32 *aDate);
nsCOMPtr <nsIMdbTable> m_mdbAllOfflineOpsTable;
mdb_token m_offlineOpsRowScopeToken;
mdb_token m_offlineOpsTableKindToken;
virtual PRBool SetHdrFlag(nsIMsgDBHdr *, PRBool bSet, nsMsgMessageFlagType flag);
virtual void UpdateFolderFlag(nsIMsgDBHdr *msgHdr, PRBool bSet,
nsMsgMessageFlagType flag, nsIOutputStream **ppFileStream);
--- a/mailnews/db/msgdb/src/nsMailDatabase.cpp
+++ b/mailnews/db/msgdb/src/nsMailDatabase.cpp
@@ -369,67 +369,71 @@ void nsMailDatabase::UpdateFolderFlag(ns
}
if (!m_folderStream)
*ppFileStream = fileStream; // This tells the caller that we opened the file, and please to close it.
else if (!m_ownFolderStream)
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, folderStreamPos);
}
}
-PRUint32 nsMailDatabase::GetMailboxModDate()
+// Get the current attributes of the mbox file, corrected for caching
+void nsMailDatabase::GetMailboxModProperties(PRInt64 *aSize, PRUint32 *aDate)
{
- PRUint32 retModTime = 0;
+ // We'll simply return 0 on errors.
+ *aDate = 0;
+ *aSize = 0;
+ if (!m_folderFile)
+ return;
+
+ // clone file because nsLocalFile caches sizes and dates.
+ nsCOMPtr<nsIFile> copyFolderFile;
+ nsresult rv = m_folderFile->Clone(getter_AddRefs(copyFolderFile));
+ if (NS_FAILED(rv) || !copyFolderFile)
+ return;
+
+ if (NS_FAILED(copyFolderFile->GetFileSize(aSize))
+ return;
+
PRInt64 lastModTime;
- if (m_folderFile)
- {
- nsresult rv = m_folderFile->GetLastModifiedTime(&lastModTime);
- if (NS_SUCCEEDED(rv))
- {
+ if (NS_FAILED(copyFolderFile->GetLastModifiedTime(&lastModTime))
+ return;
- PRTime temp64;
- PRInt64 thousand;
- LL_I2L(thousand, PR_MSEC_PER_SEC);
- LL_DIV(temp64, lastModTime, thousand);
- LL_L2UI(retModTime, temp64);
- }
- }
- return retModTime;
+ PRTime temp64;
+ PRInt64 thousand;
+ LL_I2L(thousand, PR_MSEC_PER_SEC);
+ LL_DIV(temp64, lastModTime, thousand);
+ LL_L2UI(*aDate, temp64);
+ return;
}
NS_IMETHODIMP nsMailDatabase::GetSummaryValid(PRBool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
PRUint32 folderSize;
PRUint32 folderDate;
- PRUint32 actualFolderTimeStamp;
PRInt32 numUnreadMessages;
nsAutoString errorMsg;
-
*aResult = PR_FALSE;
-
+
if (m_folderFile && m_dbFolderInfo)
{
- actualFolderTimeStamp = GetMailboxModDate();
-
m_dbFolderInfo->GetNumUnreadMessages(&numUnreadMessages);
m_dbFolderInfo->GetFolderSize(&folderSize);
m_dbFolderInfo->GetFolderDate(&folderDate);
// compare current version of db versus filed out version info,
// and file size in db vs file size on disk.
PRUint32 version;
+ m_dbFolderInfo->GetVersion(&version);
- m_dbFolderInfo->GetVersion(&version);
- nsCOMPtr <nsIFile> copyFolderFile;
- // clone file because nsLocalFile caches sizes.
- nsresult rv = m_folderFile->Clone(getter_AddRefs(copyFolderFile));
- NS_ENSURE_SUCCESS(rv, rv);
PRInt64 fileSize;
- copyFolderFile->GetFileSize(&fileSize);
+ PRUint32 actualFolderTimeStamp;
+ GetMailboxModProperties(&fileSize, &actualFolderTimeStamp);
+
if (folderSize == fileSize &&
numUnreadMessages >= 0 && GetCurVersion() == version)
{
GetGlobalPrefs();
// if those values are ok, check time stamp
if (gTimeStampLeeway == 0)
*aResult = folderDate == actualFolderTimeStamp;
else
@@ -479,23 +483,19 @@ NS_IMETHODIMP nsMailDatabase::SetSummary
m_folderFile->Exists(&exists);
if (!exists)
return NS_MSG_ERROR_FOLDER_MISSING;
if (m_dbFolderInfo)
{
if (valid)
{
- PRUint32 actualFolderTimeStamp = GetMailboxModDate();
+ PRUint32 actualFolderTimeStamp;
PRInt64 fileSize;
- nsCOMPtr <nsIFile> copyFolderFile;
- // clone file because nsLocalFile caches sizes.
- rv = m_folderFile->Clone(getter_AddRefs(copyFolderFile));
- NS_ENSURE_SUCCESS(rv, rv);
- copyFolderFile->GetFileSize(&fileSize);
+ GetMailboxModProperties(&fileSize, &actualFolderTimeStamp);
m_dbFolderInfo->SetFolderSize((PRUint32) fileSize);
m_dbFolderInfo->SetFolderDate(actualFolderTimeStamp);
m_dbFolderInfo->SetVersion(GetCurVersion());
}
else
{
m_dbFolderInfo->SetVersion(0); // that ought to do the trick.
}
@@ -730,19 +730,19 @@ nsresult nsMailDatabase::SetFolderInfoVa
#ifdef DEBUG
printf("Exception opening summary file\n");
#endif
return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
{
pMessageDB->m_folderFile = folderName;
- PRUint32 actualFolderTimeStamp = pMessageDB->GetMailboxModDate();
+ PRUint32 actualFolderTimeStamp;
PRInt64 fileSize;
- folderName->GetFileSize(&fileSize);
+ pMessageDB->GetMailboxModProperties(&fileSize, &actualFolderTimeStamp);
pMessageDB->m_dbFolderInfo->SetFolderSize((PRUint32) fileSize);
pMessageDB->m_dbFolderInfo->SetFolderDate(actualFolderTimeStamp);
pMessageDB->m_dbFolderInfo->ChangeNumUnreadMessages(numunread);
pMessageDB->m_dbFolderInfo->ChangeNumMessages(num);
}
// if we opened the db, then we'd better close it. Otherwise, we found it in the cache,
// so just commit and release.
if (bOpenedDB)
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -2445,21 +2445,30 @@ NS_IMETHODIMP nsMsgLocalMailFolder::EndC
nsCOMPtr <nsISeekableStream> seekableStream;
if (mCopyState)
{
NS_ASSERTION(mCopyState->m_leftOver == 0, "whoops, something wrong with previous copy");
mCopyState->m_leftOver = 0; // reset to 0.
// need to reset this in case we're move/copying multiple msgs.
mCopyState->m_fromLineSeen = PR_FALSE;
- // flush the copied message.
+
+ // flush the copied message. Seeking causes a flush, w/o syncing. But we
+ // need a flush at the end to get the file size and time updated correctly.
if (mCopyState->m_fileStream)
{
- seekableStream = do_QueryInterface(mCopyState->m_fileStream);
- seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
+ if (multipleCopiesFinished)
+ {
+ mCopyState->m_fileStream->Flush();
+ }
+ else
+ {
+ seekableStream = do_QueryInterface(mCopyState->m_fileStream);
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
+ }
}
}
//Copy the header to the new database
if (copySucceeded && mCopyState->m_message)
{
// CopyMessages() goes here; CopyFileMessage() never gets in here because
// the mCopyState->m_message will be always null for file message
nsCOMPtr<nsIMsgDBHdr> newHdr;