--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -54,16 +54,20 @@
#include "nsNetUtil.h"
#include "nsIMsgFolderNotificationService.h"
#include "nsReadLine.h"
#include "nsArrayUtils.h"
#include "nsIStringEnumerator.h"
#include "mozilla/Services.h"
#include "nsIURIMutator.h"
+/* for logging to Error Console */
+#include "nsIScriptError.h"
+#include "nsIConsoleService.h"
+
//////////////////////////////////////////////////////////////////////////////
// nsLocal
/////////////////////////////////////////////////////////////////////////////
nsLocalMailCopyState::nsLocalMailCopyState()
: m_flags(0),
m_lastProgressTime(PR_IntervalToMilliseconds(PR_IntervalNow())),
m_curDstKey(nsMsgKey_None),
@@ -73,18 +77,25 @@ nsLocalMailCopyState::nsLocalMailCopySta
m_leftOver(0),
m_isMove(false),
m_dummyEnvelopeNeeded(false),
m_fromLineSeen(false),
m_writeFailed(false),
m_notifyFolderLoaded(false) {}
nsLocalMailCopyState::~nsLocalMailCopyState() {
+ nsresult rv;
PR_Free(m_dataBuffer);
- if (m_fileStream) m_fileStream->Close();
+ if (m_fileStream) {
+ rv = m_fileStream->Close();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "close failed: m_fileStream->Close.");
+ if (NS_FAILED(rv))
+ NS_WARNING("close failed: m_fileStream->Close.");
+ m_fileStream = nullptr; // code uniformity
+ }
if (m_messageService) {
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(m_srcSupport);
if (srcFolder && m_message) {
nsCString uri;
srcFolder->GetUriForMsg(m_message, uri);
}
}
}
@@ -311,18 +322,21 @@ NS_IMETHODIMP nsMsgLocalMailFolder::GetD
mDatabase->ForceClosed();
mDatabase = nullptr;
}
nsCOMPtr<nsIFile> summaryFile;
rv = GetSummaryFileLocation(pathFile, getter_AddRefs(summaryFile));
NS_ENSURE_SUCCESS(rv, rv);
// Remove summary file.
- summaryFile->Remove(false);
-
+ rv = summaryFile->Remove(false);
+ // Not sure if need to check this error, but be conservative.
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Could not delete msg summary file");
+ }
// if it's out of date then reopen with upgrade.
rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
NS_ENSURE_SUCCESS(rv, rv);
if (transferInfo && mDatabase) {
SetDBTransferInfo(transferInfo);
mDatabase->SetSummaryValid(false);
}
@@ -456,16 +470,25 @@ NS_IMETHODIMP nsMsgLocalMailFolder::Crea
}
NS_IMETHODIMP
nsMsgLocalMailFolder::CreateSubfolder(const nsAString &folderName,
nsIMsgWindow *msgWindow) {
nsCOMPtr<nsIMsgFolder> newFolder;
nsresult rv =
CreateSubfolderInternal(folderName, msgWindow, getter_AddRefs(newFolder));
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::CreateSubfolder: "
+ "CreateSubfolder(folderName, ...) failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ fprintf(stderr, "(debug) folderName = <<%s>>\n",
+ NS_ConvertUTF16toUTF8(folderName).get());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgFolderNotificationService> notifier(
do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
if (notifier) notifier->NotifyFolderAdded(newFolder);
return NS_OK;
}
@@ -878,19 +901,21 @@ nsresult nsMsgLocalMailFolder::OpenDatab
if (parent) {
// This little dance creates an empty .msf file and then checks
// if the db is valid - this works if the folder is empty, which
// we don't have a direct way of checking.
nsCOMPtr<nsIMsgDatabase> db;
rv = msgDBService->CreateNewDB(this, getter_AddRefs(db));
if (db) {
UpdateSummaryTotals(true);
- db->Close(true);
+ rv = db->Close(true);
+ if (NS_FAILED(rv))
+ NS_WARNING("db->Close(true) failed.");
+ db = nullptr;
mDatabase = nullptr;
- db = nullptr;
rv = msgDBService->OpenFolderDB(this, false, getter_AddRefs(mDatabase));
if (NS_FAILED(rv)) mDatabase = nullptr;
}
}
} else if (NS_FAILED(rv))
mDatabase = nullptr;
return rv;
@@ -1475,25 +1500,38 @@ nsMsgLocalMailFolder::CopyMessages(nsIMs
if (numMsgs > 1 &&
((MsgLowerCaseEqualsLiteral(protocolType, "imap") && !WeAreOffline()) ||
MsgLowerCaseEqualsLiteral(protocolType, "mailbox"))) {
mCopyState->m_copyingMultipleMessages = true;
rv = CopyMessagesTo(mCopyState->m_messages, keyArray, msgWindow, this,
isMove);
if (NS_FAILED(rv)) {
NS_ERROR("copy message failed");
+#ifdef DEBUG
+ // NS_ERROR("copy message failed"); We hit this error during Windows xpcshell-test
+ // when the patch to enable buffering was tested, so we dump context infomration.
+ fprintf(stderr, "(debug) Copy message failed.\n"
+ "rv = CopyMessagesTo(mCopyState->m_messages, keyArray, msgWindow, this, isMove);\n"
+ "rv = 0x%" PRIx32 "\n", static_cast<uint32_t> (rv));
+#endif
(void)OnCopyCompleted(srcSupport, false);
}
} else {
nsCOMPtr<nsISupports> msgSupport =
do_QueryElementAt(mCopyState->m_messages, 0);
if (msgSupport) {
rv = CopyMessageTo(msgSupport, this, msgWindow, isMove);
if (NS_FAILED(rv)) {
- NS_ASSERTION(false, "copy message failed");
+#ifdef DEBUG
+ // NS_ERROR("copy message failed"); We hit this error during Windows xpcshell-test
+ // when the patch to enable buffering was tested, so we dump context infomration.
+ fprintf(stderr, "(debug) Copy message failed.\n"
+ "rv = CopyMessageTo(msgSupport, this, msgWindow, isMove);\n"
+ "rv = 0x%" PRIx32 "\n", static_cast<uint32_t> (rv));
+#endif
(void)OnCopyCompleted(srcSupport, false);
}
}
}
// if this failed immediately, need to turn back on notifications and inform
// FE.
if (NS_FAILED(rv)) {
if (isMove) srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
@@ -1664,16 +1702,19 @@ nsMsgLocalMailFolder::CopyFolderLocal(ns
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return msgStore->CopyFolder(srcFolder, this, isMoveFolder, msgWindow,
aListener, newFolderName);
}
+// XXX TODO Better construct for error handling.
+// We can use goto to clean this routine up!
+// With the current code, it is not clear how to add error recover, etc. for Seek error, for example.
NS_IMETHODIMP
nsMsgLocalMailFolder::CopyFileMessage(nsIFile *aFile, nsIMsgDBHdr *msgToReplace,
bool isDraftOrTemplate,
uint32_t newMsgFlags,
const nsACString &aNewMsgKeywords,
nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener *listener) {
NS_ENSURE_ARG_POINTER(aFile);
@@ -1733,17 +1774,22 @@ nsMsgLocalMailFolder::CopyFileMessage(ns
// mDatabase should have been initialized above - if we got msgDb
// If we were going to delete, here is where we would do it. But because
// existing code already supports doing those deletes, we are just going
// to end the copy.
if (NS_SUCCEEDED(rv) && msgToReplace && mDatabase)
rv = OnCopyCompleted(fileSupport, true);
- if (inputStream) inputStream->Close();
+ if (inputStream) {
+ nsresult rv3 = inputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("inputStream->Close failed");
+ inputStream = nullptr; // code uniformity
+ }
}
if (NS_FAILED(rv)) (void)OnCopyCompleted(fileSupport, false);
return rv;
}
nsresult nsMsgLocalMailFolder::DeleteMessage(nsISupports *message,
@@ -1799,16 +1845,30 @@ NS_IMETHODIMP nsMsgLocalMailFolder::GetN
? localMailServer->GetNewMail(aWindow, aListener, inbox, nullptr)
: localInbox->SetCheckForNewMessagesAfterParsing(true);
}
}
return rv;
}
nsresult nsMsgLocalMailFolder::WriteStartOfNewMessage() {
+ //
+ // Flushing so that we can catch the error from Flush explicitly when buffering is enabled.
+ //
+
+ nsresult rv1 = mCopyState->m_fileStream->Flush();
+ if (NS_FAILED(rv1)) {
+#ifdef DEBUG
+ fflush(stdout);
+ fprintf(stderr, "(debug) Extra %d: mCopyState->m_fileStream->Flush() returned 0x%" PRIx32 " in WriteStartOfNewMEssage \n",
+ __LINE__, static_cast<uint32_t> (rv1));
+#endif
+ NS_WARNING("mCopyState->m_fileStream->Flush() failed;");
+ }
+
nsCOMPtr<nsISeekableStream> seekableStream =
do_QueryInterface(mCopyState->m_fileStream);
int64_t filePos;
seekableStream->Tell(&filePos);
// CopyFileMessage() and CopyMessages() from servers other than pop3
if (mCopyState->m_parseMsgState) {
if (mCopyState->m_parseMsgState->m_newMsgHdr)
@@ -1842,52 +1902,81 @@ nsresult nsMsgLocalMailFolder::WriteStar
statusStrBuf, sizeof(statusStrBuf),
X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK,
dbFlags &
~(nsMsgMessageFlags::RuntimeOnly | nsMsgMessageFlags::Offline) &
0x0000FFFF);
} else
strcpy(statusStrBuf, "X-Mozilla-Status: 0001" MSG_LINEBREAK);
uint32_t bytesWritten;
- mCopyState->m_fileStream->Write(result.get(), result.Length(),
- &bytesWritten);
+ nsresult rv4 = mCopyState->m_fileStream->Write(result.get(),
+ result.Length(),
+ &bytesWritten);
+
+ if (NS_FAILED(rv4) || result.Length() != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && result.Length() == bytesWritten,
+ "mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten) failed.");
+ // XXX TODO better error processing.
+ }
+
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
result.Length());
- mCopyState->m_fileStream->Write(statusStrBuf, strlen(statusStrBuf),
- &bytesWritten);
+ rv4 = mCopyState->m_fileStream->Write(statusStrBuf,
+ strlen(statusStrBuf),
+ &bytesWritten);
+
+ if(NS_FAILED(rv4) || strlen(statusStrBuf) != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && strlen(statusStrBuf) == bytesWritten,
+ "mCopyState->m_fileStream->Write(statusStrBuf, strlen(statusStrBuf), &bytesWritten) failed.");
+ // XXX TODO better error processing.
+ }
+
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->ParseAFolderLine(statusStrBuf,
strlen(statusStrBuf));
result = "X-Mozilla-Status2: 00000000" MSG_LINEBREAK;
- mCopyState->m_fileStream->Write(result.get(), result.Length(),
- &bytesWritten);
+
+ rv4 = mCopyState->m_fileStream->Write(result.get(), result.Length(),
+ &bytesWritten);
+
+ if (NS_FAILED(rv4) || result.Length() != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && result.Length() == bytesWritten,
+ "mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten) failed.");
+ // XXX TODO better error processing.
+ }
+
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
result.Length());
result = X_MOZILLA_KEYWORDS;
- mCopyState->m_fileStream->Write(result.get(), result.Length(),
- &bytesWritten);
+
+ rv4 = mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten);
+ if (NS_FAILED(rv4) || result.Length() != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && result.Length() == bytesWritten,
+ "mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten) failed.");
+ // XXX TODO better error processing.
+ }
+
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
result.Length());
mCopyState->m_fromLineSeen = true;
} else
mCopyState->m_fromLineSeen = false;
mCopyState->m_curCopyIndex++;
return NS_OK;
}
nsresult nsMsgLocalMailFolder::InitCopyMsgHdrAndFileStream() {
nsresult rv = GetMsgStore(getter_AddRefs(mCopyState->m_msgStore));
NS_ENSURE_SUCCESS(rv, rv);
- bool reusable;
rv = mCopyState->m_msgStore->GetNewMsgOutputStream(
- this, getter_AddRefs(mCopyState->m_newHdr), &reusable,
+ this, getter_AddRefs(mCopyState->m_newHdr),
getter_AddRefs(mCopyState->m_fileStream));
NS_ENSURE_SUCCESS(rv, rv);
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->SetNewMsgHdr(mCopyState->m_newHdr);
return rv;
}
// nsICopyMessageListener
@@ -1957,16 +2046,19 @@ NS_IMETHODIMP nsMsgLocalMailFolder::Copy
mCopyState->m_dataBufferSize = aLength + mCopyState->m_leftOver + 3;
}
nsCOMPtr<nsISeekableStream> seekableStream =
do_QueryInterface(mCopyState->m_fileStream, &rv);
NS_ENSURE_SUCCESS(rv, rv);
seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+// BTW, under Linux, and for POP3 case, Read is handled by recvfrom in
+// lower-level code with 32KB buffer: verified from strace data
+
rv = aIStream->Read(mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1,
aLength, &readCount);
NS_ENSURE_SUCCESS(rv, rv);
mCopyState->m_leftOver += readCount;
mCopyState->m_dataBuffer[mCopyState->m_leftOver + 1] = '\0';
char *start = mCopyState->m_dataBuffer + 1;
char *endBuffer = mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1;
@@ -1985,49 +2077,51 @@ NS_IMETHODIMP nsMsgLocalMailFolder::Copy
if (mCopyState->m_wholeMsgInStream) {
end = start + mCopyState->m_leftOver;
memcpy(end, MSG_LINEBREAK "\0", MSG_LINEBREAK_LEN + 1);
} else {
memmove(mCopyState->m_dataBuffer + 1, start, mCopyState->m_leftOver);
break;
}
}
-
// need to set the linebreak_len each time
uint32_t linebreak_len = 1; // assume CR or LF
if (*end == '\r' && *(end + 1) == '\n') linebreak_len = 2; // CRLF
if (!mCopyState->m_fromLineSeen) {
mCopyState->m_fromLineSeen = true;
NS_ASSERTION(strncmp(start, "From ", 5) == 0,
"Fatal ... bad message format\n");
} else if (strncmp(start, "From ", 5) == 0) {
// if we're at the beginning of the buffer, we've reserved a byte to
// insert a '>'. If we're in the middle, we're overwriting the previous
// line ending, but we've already written it to m_fileStream, so it's OK.
*--start = '>';
}
lineLength = end - start + linebreak_len;
+
+ // This Write should be buffered for performance reasons.
rv = mCopyState->m_fileStream->Write(start, lineLength, &bytesWritten);
- if (bytesWritten != lineLength || NS_FAILED(rv)) {
+ if (NS_FAILED(rv) || bytesWritten != lineLength) {
+ NS_WARNING("rv = mCopyState->m_fileStream->Write(start, lineLength, &bytesWritten) failed.");
ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
mCopyState->m_writeFailed = true;
return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
}
if (mCopyState->m_parseMsgState)
mCopyState->m_parseMsgState->ParseAFolderLine(start, lineLength);
start = end + linebreak_len;
if (start >= endBuffer) {
mCopyState->m_leftOver = 0;
break;
}
- }
+ } // while (1)
return rv;
}
void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr,
nsIMsgDBHdr *srcHdr,
bool aIsMove) {
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch(
@@ -2081,23 +2175,37 @@ void nsMsgLocalMailFolder::CopyHdrProper
destHdr->SetLabel(label);
}
NS_IMETHODIMP nsMsgLocalMailFolder::EndCopy(bool aCopySucceeded) {
if (!mCopyState) return NS_OK;
// we are the destination folder for a move/copy
nsresult rv = aCopySucceeded ? NS_OK : NS_ERROR_FAILURE;
+ nsresult rv2 = NS_OK, rv3 = NS_OK;
if (!aCopySucceeded || mCopyState->m_writeFailed) {
if (mCopyState->m_fileStream) {
- if (mCopyState->m_curDstKey != nsMsgKey_None)
- mCopyState->m_msgStore->DiscardNewMessage(mCopyState->m_fileStream,
- mCopyState->m_newHdr);
- mCopyState->m_fileStream->Close();
+ // no longer in this version: nsresult rv3;
+ // bool closed = false;
+ if (mCopyState->m_curDstKey != nsMsgKey_None) {
+ rv2 = mCopyState->m_msgStore->DiscardNewMessage(mCopyState->m_fileStream,
+ mCopyState->m_newHdr);
+ // DiscardNewMessage always closes the 1st argument always and returns error code in rv2
+ mCopyState->m_fileStream = nullptr;
+ // delayed check of DiscardNewMessage() return value.
+ if (NS_FAILED(rv2))
+ NS_WARNING("mCopyState->m_msgStore->DiscardNewMessage() failed");
+ }
+ if (mCopyState->m_fileStream) {
+ rv3 = mCopyState->m_fileStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("mCopyState->m_fileStream->Close() failed");
+ }
+ // XXX TODO better error processing.
}
if (!mCopyState->m_isMove) {
// passing true because the messages that have been successfully
// copied have their corresponding hdrs in place. The message that has
// failed has been truncated so the msf file and berkeley mailbox
// are in sync.
(void)OnCopyCompleted(mCopyState->m_srcSupport, true);
@@ -2134,21 +2242,57 @@ NS_IMETHODIMP nsMsgLocalMailFolder::EndC
do_QueryInterface(mCopyState->m_fileStream));
if (seekableStream) {
seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
rv = FinishNewLocalMessage(mCopyState->m_fileStream, mCopyState->m_newHdr,
mCopyState->m_msgStore,
mCopyState->m_parseMsgState);
if (NS_SUCCEEDED(rv) && mCopyState->m_newHdr)
mCopyState->m_newHdr->GetMessageKey(&mCopyState->m_curDstKey);
- if (multipleCopiesFinished)
- mCopyState->m_fileStream->Close();
- else
- mCopyState->m_fileStream->Flush();
- }
+ if (multipleCopiesFinished) {
+ // mCopyState->m_fileStream can be nullptr in DONT-USE-REUSABLE branch
+ if (mCopyState->m_fileStream)
+ {
+ // We have already closed in this version.
+ // nsresult rv3 = mCopyState->m_fileStream->Close();
+ // MSG_NS_WARN_IF_FALSE(NS_SUCCEEDED(rv3),
+ // "mCopyState->m_fileStream->Close() failed;");
+ mCopyState->m_fileStream = nullptr; // avoid accessing closed stream
+ }
+ else {
+ // Printing something to error console fails xpcshell test, so commented out on June, 2017
+ // MSG_NS_WARN_IF_FALSE(false, "mCopyState->m_fileStream was null and so Close could not be called.");
+ }
+ }
+ else {
+ // We are probably still processing a list of messages.
+ // We have just finished processing only one of the messages.
+ if (mCopyState->m_fileStream) {
+#if 1
+ // We may have already closed it and m_fileStream could be nullptr.
+ nsresult rv3 = mCopyState->m_fileStream->Flush();
+ // During testing by using a Linux CIFS server as mail store
+ // from TB on another Linux this Flush() returned error code.
+ if (NS_FAILED(rv3))
+ {
+ fflush(stdout);
+ fprintf(stderr, "(debug) Flush() returned rv3 =0x%" PRIx32 " (%s:%d)\n",
+ static_cast<uint32_t> (rv3), __FILE__, __LINE__);
+ }
+
+ MSG_NS_WARN_IF_FALSE(NS_SUCCEEDED(rv3), "mCopyState->m_fileStream->Flush() failed.");
+#endif
+ } else {
+ // Printing something to error console fails in xpcshell test, so commented out on June, 2017
+ // MSG_NS_WARN_IF_FALSE(false, "mCopyState->m_fileStream was already null and so Flush could not be called..");
+ // XXX maybe we can record the error in an addtional m_* variable.
+ }
+ }
+ } // if (seekableStream)
+
// Copy the header to the new database
if (mCopyState->m_message) {
// CopyMessages() goes here, and CopyFileMessages() with metadata to save;
nsCOMPtr<nsIMsgDBHdr> newHdr;
if (!mCopyState->m_parseMsgState) {
if (mCopyState->m_destDB) {
if (mCopyState->m_newHdr) {
newHdr = mCopyState->m_newHdr;
@@ -2436,28 +2580,39 @@ NS_IMETHODIMP nsMsgLocalMailFolder::EndM
nsresult rv;
if (localUndoTxn) {
localUndoTxn->GetMsgWindow(getter_AddRefs(msgWindow));
localUndoTxn->AddSrcKey(key);
localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
}
+ // sanity check for dont-use-reusable branch.
+ if (! mCopyState->m_fileStream) {
+ MSG_NS_ERROR("mCopyState->m_fileStream is null.");
+ }
+
// I think this is always true for online to offline copy
mCopyState->m_dummyEnvelopeNeeded = true;
nsCOMPtr<nsISeekableStream> seekableStream =
do_QueryInterface(mCopyState->m_fileStream, &rv);
NS_ENSURE_SUCCESS(rv, rv);
seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
rv = FinishNewLocalMessage(mCopyState->m_fileStream, mCopyState->m_newHdr,
mCopyState->m_msgStore,
mCopyState->m_parseMsgState);
- mCopyState->m_fileStream->Close();
+ // FinishNewLocalMessage now closes the stream.
mCopyState->m_fileStream = nullptr; // all done with the file stream
+ // Check for FinishNewMessage return value should come
+ // after potential Close() if Close() is separated from FinishNewMessage.
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "FinishNewMessage(mCopyState->m_fileStream, mCopyState->m_newHdr) failed.");
+ }
+
// CopyFileMessage() and CopyMessages() from servers other than mailbox
if (mCopyState->m_parseMsgState) {
nsCOMPtr<nsIMsgDatabase> msgDb;
nsCOMPtr<nsIMsgDBHdr> newHdr;
mCopyState->m_parseMsgState->FinishHeader();
rv = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
@@ -2488,27 +2643,38 @@ NS_IMETHODIMP nsMsgLocalMailFolder::EndM
// we can't undo w/o the msg db
}
mCopyState->m_parseMsgState->Clear();
if (mCopyState->m_listener) // CopyFileMessage() only
mCopyState->m_listener->SetMessageKey(mCopyState->m_curDstKey);
}
- if (mCopyState->m_fileStream) mCopyState->m_fileStream->Flush();
+ if (mCopyState->m_fileStream) {
+ nsresult rv3 = mCopyState->m_fileStream->Flush();
+ if (NS_FAILED(rv3))
+ NS_WARNING("mCopyState->m_fileStream->Flush() failed;");
+ // XXX TODO Better error processing. Should we not return rv3 if error?
+ // Error propbagation framework is not quite clear.
+ }
return NS_OK;
}
nsresult nsMsgLocalMailFolder::CopyMessagesTo(nsIArray *messages,
nsTArray<nsMsgKey> &keyArray,
nsIMsgWindow *aMsgWindow,
nsIMsgFolder *dstFolder,
bool isMove) {
- if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
-
+ if (!mCopyState) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::CopyMessagesTo: "
+ "!mCopyState. Returning with error immediately.\n");
+#endif
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
nsresult rv;
nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener =
do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICopyMessageListener> copyListener(
do_QueryInterface(dstFolder, &rv));
@@ -2681,17 +2847,22 @@ nsMsgLocalMailFolder::MarkMsgsOnPop3Serv
if (folderScanState.m_uidl) {
msgPop3Server->AddUidlToMark(folderScanState.m_uidl, mark);
// remember this pop server in list of servers with msgs deleted
if (pop3Servers.IndexOfObject(msgPop3Server) == -1)
pop3Servers.AppendObject(msgPop3Server);
}
}
}
- if (folderScanState.m_inputStream) folderScanState.m_inputStream->Close();
+ if (folderScanState.m_inputStream) {
+ nsresult rv3 = folderScanState.m_inputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("folderScanState.m_inputStream->Close() failed;");
+ folderScanState.m_inputStream = nullptr; // avoid accessing closed stream
+ }
// need to do this for all pop3 mail servers that had messages deleted.
uint32_t serverCount = pop3Servers.Count();
for (uint32_t index = 0; index < serverCount; index++)
pop3Servers[index]->MarkMessages();
return rv;
}
@@ -3286,17 +3457,18 @@ nsMsgLocalMailFolder::GetFolderScanState
}
NS_IMETHODIMP
nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState *aState,
nsIMsgDBHdr *aMsgDBHdr) {
bool more = false;
uint32_t size = 0, len = 0;
const char *accountKey = nullptr;
- nsresult rv = GetMsgInputStream(aMsgDBHdr, &aState->m_streamReusable,
+ bool reusable; // throw away
+ nsresult rv = GetMsgInputStream(aMsgDBHdr, &reusable,
getter_AddRefs(aState->m_inputStream));
aState->m_seekableStream = do_QueryInterface(aState->m_inputStream);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
aState->m_uidl = nullptr;
@@ -3322,19 +3494,23 @@ nsMsgLocalMailFolder::GetUidlFromFolder(
aState->m_uidl = strstr(aState->m_header.get(), X_UIDL);
if (aState->m_uidl) {
aState->m_uidl += X_UIDL_LEN + 2; // skip UIDL: header
break;
}
}
}
}
- if (!aState->m_streamReusable) {
- aState->m_inputStream->Close();
+ if (aState->m_inputStream) {
+ nsresult rv3 = aState->m_inputStream->Close();
+ MSG_NS_WARN_IF_FALSE(NS_SUCCEEDED(rv3), "aState->m_inputStream->Close() failed;");
aState->m_inputStream = nullptr;
+ } else {
+ // original code did not assume this case could happen. So warn.
+ MSG_NS_WARNING("aState->m_inputStream was null.");
}
lineBuffer = nullptr;
return rv;
}
/**
* Adds a message to the end of the folder, parsing it as it goes, and
* applying filters, if applicable.
@@ -3353,16 +3529,17 @@ nsMsgLocalMailFolder::AddMessage(const c
NS_IMETHODIMP
nsMsgLocalMailFolder::AddMessageBatch(uint32_t aMessageCount,
const char **aMessages,
nsIArray **aHdrArray) {
NS_ENSURE_ARG_POINTER(aHdrArray);
nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv1;
nsresult rv = GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgPluggableStore> msgStore;
nsCOMPtr<nsIOutputStream> outFileStream;
nsCOMPtr<nsIMsgDBHdr> newHdr;
rv = server->GetMsgStore(getter_AddRefs(msgStore));
@@ -3374,66 +3551,183 @@ nsMsgLocalMailFolder::AddMessageBatch(ui
bool isLocked;
GetLocked(&isLocked);
if (isLocked) return NS_MSG_FOLDER_BUSY;
AcquireSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // WARNING: We must release semaphore before returning !
+ // We use goto statement here to handle the early error return sequence.
+
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIMutableArray> hdrArray =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
+ // NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("|do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)| failed.");
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return rv;
+ }
for (uint32_t i = 0; i < aMessageCount; i++) {
RefPtr<nsParseNewMailState> newMailParser = new nsParseNewMailState;
- NS_ENSURE_TRUE(newMailParser, NS_ERROR_OUT_OF_MEMORY);
- if (!mGettingNewMessages) newMailParser->DisableFilters();
- bool reusable;
+ // NS_ENSURE_TRUE(newMailParser, NS_ERROR_OUT_OF_MEMORY);
+ if (!newMailParser) {
+ NS_WARNING("|newMailParser = new nsParseNewMailState;| failed.");
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!mGettingNewMessages) newMailParser->DisableFilters();
+ // bool reusable;
rv = msgStore->GetNewMsgOutputStream(this, getter_AddRefs(newHdr),
- &reusable,
getter_AddRefs(outFileStream));
- NS_ENSURE_SUCCESS(rv, rv);
+ // NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("|msgStore->GetNewMsgOutputStream( omission );| failed.");
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::AddMessageBatch: "
+ "(debug) msgStore->GetNewMsgOutputStream() failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+#endif
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return rv;
+ }
// Get a msgWindow. Proceed without one, but filter actions to imap
// folders will silently fail if not signed in and no window for a prompt.
nsCOMPtr<nsIMsgWindow> msgWindow;
nsCOMPtr<nsIMsgMailSession> mailSession =
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
- mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+
+ if (NS_FAILED(rv)) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::AddMessageBatch: "
+ "Could not obtain topmost message window. "
+ "filter actions to imap folders may fail silently!\n:");
+#endif
+ }
rv = newMailParser->Init(rootFolder, this, msgWindow, newHdr,
outFileStream);
+ if (NS_FAILED(rv)) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::AddMessageBatch: "
+ "newMailParser->Init() failed. rv = 0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+#endif
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return rv;
+ }
+
uint32_t bytesWritten, messageLen = strlen(aMessages[i]);
- outFileStream->Write(aMessages[i], messageLen, &bytesWritten);
+ nsresult rv4 = outFileStream->Write(aMessages[i], messageLen, &bytesWritten);
+ if (NS_FAILED(rv4) || messageLen != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && messageLen == bytesWritten,
+ "outFileStream->Write(aMessages[i], messageLen, &bytesWritten) failed.");
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgLocalMailFolder::AddMessageBatch: "
+ "outFileStream->Write failed. rv4 = 0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv4));
+#endif
+ if (NS_FAILED(rv4))
+ rv = rv4;
+ else
+ rv = NS_ERROR_FAILURE; // written data length is shorter than intended.
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return rv;
+ }
+
newMailParser->BufferInput(aMessages[i], messageLen);
- FinishNewLocalMessage(outFileStream, newHdr, msgStore, newMailParser);
- outFileStream->Close();
+ rv1 = FinishNewLocalMessage(outFileStream, newHdr, msgStore, newMailParser);
+ if (NS_FAILED(rv1)) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) In nsMsgLocalMailFolder::AddMessageBatch: msgStore->FinishNewMessage() "
+ " returned 0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv1));
+#endif
+ // XXX During error testing. This error path *IS* taken.
+ // There was no error checking before.
+ // Devise a good error recovery. For now, return the error code and
+ // release the semaphore before return.
+
+ // don't exit before closing the file possibly.
+ // Checking for the return value of FinishNewMessage should
+ // come after potential Close() of the 1st argument and nullifying of
+ // |outFileStream| that stores the file stream reference.
+ // rv = rv1;
+ // break
+ }
+
+ // FinishNewLocalMessage now closes outFileStream always.
outFileStream = nullptr;
+
+ // delayed processing of the error of FinishNewMessage.
+ if (NS_FAILED(rv1)) {
+ rv = rv1;
+ goto epilog;
+ // ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
+ // return rv;
+ }
+
newMailParser->OnStopRequest(nullptr, NS_OK);
newMailParser->EndMsgDownload();
hdrArray->AppendElement(newHdr);
}
hdrArray.forget(aHdrArray);
}
+
+ epilog:
ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder *>(this));
return rv;
}
nsresult nsMsgLocalMailFolder::FinishNewLocalMessage(
nsIOutputStream *aOutputStream, nsIMsgDBHdr *aNewHdr,
nsIMsgPluggableStore *aMsgStore, nsParseMailMessageState *aParseMsgState) {
uint32_t bytesWritten;
- aOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
+
+ // XXX Ensure we don't get interrupted here.
+ // pop3 mail download is single thread as I understand. So it is OK, but
+ // imap (attachment?) download uses an extra thread, etc. BEWARE
+ // XXX Someone who knows this code ought to verify.
+
+ nsresult rv4 = aOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
+ if (NS_FAILED(rv4) || MSG_LINEBREAK_LEN != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && MSG_LINEBREAK_LEN == bytesWritten,
+ "mCopyState->m_fileStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten) failed.");
+ ThrowAlertMsg("EndCopyFailed", mCopyState->m_msgWindow);
+ mCopyState->m_writeFailed = true;
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
if (aParseMsgState)
aParseMsgState->ParseAFolderLine(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
- return aMsgStore->FinishNewMessage(aOutputStream, aNewHdr);
+
+ nsresult rv = aMsgStore->FinishNewMessage(aOutputStream, aNewHdr);
+
+ // Check the error return of FinishNewMessage.
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "mCopyState->m_msgSTore->FinishNewMessage failed.");
+ // XXX TODO Devise a better error handling.
+ // We probably do not need an early return. This may be part of a move/copy of multiple messages.
+ // So we have to figure out whether we are at the end of the multiple mesages or not and act accordingly.
+ }
+ return rv;
+
}
NS_IMETHODIMP
nsMsgLocalMailFolder::WarnIfLocalFileTooBig(nsIMsgWindow *aWindow,
int64_t aSpaceRequested,
bool *aTooBig) {
NS_ENSURE_ARG_POINTER(aTooBig);
--- a/mailnews/local/src/nsMailboxProtocol.cpp
+++ b/mailnews/local/src/nsMailboxProtocol.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h"
#include "nsMailboxProtocol.h"
#include "nscore.h"
@@ -129,18 +129,22 @@ nsresult nsMailboxProtocol::Initialize(n
nsCOMPtr<nsIMsgFolder> folder;
nsCOMPtr<nsIMsgDBHdr> msgHdr;
rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
if (NS_SUCCEEDED(rv) && msgHdr) {
rv = msgHdr->GetFolder(getter_AddRefs(folder));
if (NS_SUCCEEDED(rv) && folder) {
nsCOMPtr<nsIInputStream> stream;
int64_t offset = 0;
- bool reusable = false;
+ bool reusable = false; // throw away
+ // XXX TODO Error recovery
+ // When a error of remote CIFS mount for one's Mail directory is simulated
+ // the following GetMsgInputStream failed.
+ // March 15, 2015
rv = folder->GetMsgInputStream(msgHdr, &reusable,
getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISeekableStream> seekableStream(
do_QueryInterface(stream, &rv));
NS_ENSURE_SUCCESS(rv, rv);
seekableStream->Tell(&offset);
// create input stream transport
@@ -373,34 +377,41 @@ NS_IMETHODIMP nsMailboxProtocol::OnStopR
// when on stop binding is called, we as the protocol are done...let's close
// down the connection releasing all of our interfaces. It's important to
// remember that this on stop binding call is coming from netlib so they are
// never going to ping us again with on data available. This means we'll never
// be going through the Process loop...
if (m_multipleMsgMoveCopyStream) {
- m_multipleMsgMoveCopyStream->Close();
+ nsresult rv3 = m_multipleMsgMoveCopyStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("m_multipleMsgMoveCopyStream->Close() failed;");
m_multipleMsgMoveCopyStream = nullptr;
}
nsMsgProtocol::OnStopRequest(request, aStatus);
return CloseSocket();
}
/////////////////////////////////////////////////////////////////////////////////////////////
// End of nsIStreamListenerSupport
//////////////////////////////////////////////////////////////////////////////////////////////
nsresult nsMailboxProtocol::DoneReadingMessage() {
nsresult rv = NS_OK;
// and close the article file if it was open....
if (m_mailboxAction == nsIMailboxUrl::ActionSaveMessageToDisk &&
- m_msgFileOutputStream)
+ m_msgFileOutputStream) {
rv = m_msgFileOutputStream->Close();
+ if (NS_FAILED(rv))
+ NS_WARNING("m_msgFileOutputStream->Close() failed;");
+ // This error code is returned at the end by "return rv";
+ m_msgFileOutputStream = nullptr; // XXX was missing?
+ }
return rv;
}
nsresult nsMailboxProtocol::SetupMessageExtraction() {
// Determine the number of bytes we are going to need to read out of the
// mailbox url....
nsCOMPtr<nsIMsgDBHdr> msgHdr;
@@ -601,25 +612,31 @@ int32_t nsMailboxProtocol::ReadMessageRe
terminator as it is read.
*/
// mscott - the firstline hack is aimed at making sure we don't write
// out the dummy header when we are trying to display the message.
// The dummy header is the From line with the date tag on it.
if (m_msgFileOutputStream && TestFlag(MAILBOX_MSG_PARSE_FIRST_LINE)) {
uint32_t count = 0;
rv = m_msgFileOutputStream->Write(line, PL_strlen(line), &count);
- if (NS_FAILED(rv)) break;
+ if (NS_FAILED(rv) || PL_strlen(line) != count) {
+ NS_WARNING("Write(line, PL_strlen(line), &count) failed.");
+ break;
+ }
- if (canonicalLineEnding)
+ if (canonicalLineEnding) {
rv = m_msgFileOutputStream->Write(CRLF, 2, &count);
- else
+ if (NS_FAILED(rv) || 2 != count)
+ break;
+ } else {
rv = m_msgFileOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN,
&count);
-
- if (NS_FAILED(rv)) break;
+ if (NS_FAILED(rv) || MSG_LINEBREAK_LEN != count)
+ break;
+ }
} else
SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
PR_Free(line);
}
PR_Free(line);
}
SetFlag(MAILBOX_PAUSE_FOR_READ); // wait for more data to become available...
--- a/mailnews/local/src/nsMovemailService.cpp
+++ b/mailnews/local/src/nsMovemailService.cpp
@@ -437,25 +437,45 @@ nsresult nsMovemailService::GetNewMail(
break;
}
buffer.AppendLiteral(MSG_LINEBREAK);
if (isMore && StringBeginsWith(buffer, NS_LITERAL_CSTRING("From "))) {
// Finish previous header and message, if any.
if (newHdr) {
- outputStream->Flush();
+ nsresult rv3 = outputStream->Flush();
+ if (NS_FAILED(rv3))
+ NS_WARNING("outputStream->Flush() failed;");
newMailParser->PublishMsgHeader(nullptr);
rv = msgStore->FinishNewMessage(outputStream, newHdr);
+ // Note that if FinishNewMessage closes outputstream
+ // (at least Maildir version did) (Bug 1121842, 1122698), then the
+ // optimization attempted by reusable flag below would have
+ // been really screwed up since although the code assumes
+ // that the reusable stream is still open in the next loop when
+ // |GetNewMsgOutputStream| below is called, but
+ // FinishNewMessage would have closed it already.
+ // So a non null value would remain in outputStream but
+ // the file associated with it was already closed(!?). Agha!
+ // XXX one more reason FinishNewMessage should not close the stream.
+ // But in reality, Maildir version of GetNewMsgOutputStream ()
+ // does not return reusable stream, and so it was OK (!!!).
+ // How convoluted.
+ // It is commented here to avoid any misunderstanding.
+
+ outputStream = nullptr;
+
+ // Delayed check of FinishNewMessage().
+
NS_ENSURE_SUCCESS(rv, rv);
newMailParser->Clear();
}
- bool reusable;
+ // bool reusable;
rv = msgStore->GetNewMsgOutputStream(inbox, getter_AddRefs(newHdr),
- &reusable,
getter_AddRefs(outputStream));
NS_ENSURE_SUCCESS(rv, rv);
rv = newMailParser->Init(serverFolder, inbox, nullptr, newHdr,
outputStream);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!outputStream) {
@@ -482,22 +502,36 @@ nsresult nsMovemailService::GetNewMail(
buffer.AssignLiteral("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
newMailParser->HandleLine(buffer.BeginWriting(), buffer.Length());
rv = outputStream->Write(buffer.get(), buffer.Length(), &bytesWritten);
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (bytesWritten == buffer.Length()),
NS_ERROR_FAILURE);
}
}
if (outputStream) {
- outputStream->Flush();
+ // bool closed = false;
+ nsresult rv3 = outputStream->Flush();
+ // nsresult rv4;
+ if (NS_FAILED(rv3))
+ NS_WARNING("outputStream->Flush() failed;");
+
newMailParser->PublishMsgHeader(nullptr);
newMailParser->OnStopRequest(nullptr, NS_OK);
+
+ // Before, there was a possible file descriptor leak.
+ // When finishNewMessage failed, this routine returned
+ // without (?) closing the stream.
+ // Unless we are trying to throw away the previous write
+ // or something, we should close this.
+
+ // We now close outputStream always inside FinishNewMessage.
rv = msgStore->FinishNewMessage(outputStream, newHdr);
+ outputStream = nullptr;
+ // delayed check of FinishNewMessage value.
NS_ENSURE_SUCCESS(rv, rv);
- outputStream->Close();
}
// Truncate the spool file as we parsed it successfully.
rv = spoolFile->SetFileSize(0);
if (NS_FAILED(rv)) {
Error("movemailCantTruncateSpoolFile",
NS_ConvertUTF8toUTF16(spoolPath).get());
}
--- a/mailnews/local/src/nsMsgBrkMBoxStore.cpp
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
@@ -113,17 +113,20 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::CreateF
if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) &&
unusedDB) {
// need to set the folder name
nsCOMPtr<nsIDBFolderInfo> folderInfo;
rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
if (NS_SUCCEEDED(rv)) folderInfo->SetMailboxName(safeFolderName);
unusedDB->SetSummaryValid(true);
- unusedDB->Close(true);
+ nsresult rv3 = unusedDB->Close(true);
+ if (NS_FAILED(rv3))
+ NS_WARNING("unusedDB->Close() failed;");
+ unusedDB = nullptr;
aParent->UpdateSummaryTotals(true);
} else {
path->Remove(false);
rv = NS_MSG_CANT_CREATE_FOLDER;
}
}
child.forget(aResult);
return rv;
@@ -135,16 +138,25 @@ void nsMsgBrkMBoxStore::GetMailboxModPro
uint32_t *aDate) {
// We'll simply return 0 on errors.
*aDate = 0;
*aSize = 0;
nsCOMPtr<nsIFile> pathFile;
nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
NS_ENSURE_SUCCESS_VOID(rv);
+ //
+ // XXX TODO Handle network errors, etc.
+ // Note GetFileSize() can fail for virtual folders, too.
+ // But when an error of remote CIFS mount for one's Mail directory
+ // is simulated the following call to GetFileSize failed, too.
+ // March 15, 2015
+ // For now, I have no idea what we should do.
+ // Mercy on those people who store one's profile on remote CIFS
+ // server.
rv = pathFile->GetFileSize(aSize);
if (NS_FAILED(rv)) return; // expected result for virtual folders
PRTime lastModTime;
rv = pathFile->GetLastModifiedTime(&lastModTime);
NS_ENSURE_SUCCESS_VOID(rv);
*aDate = (uint32_t)(lastModTime / PR_MSEC_PER_SEC);
@@ -554,55 +566,76 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::CopyFol
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgBrkMBoxStore::GetNewMsgOutputStream(nsIMsgFolder *aFolder,
nsIMsgDBHdr **aNewMsgHdr,
- bool *aReusable,
nsIOutputStream **aResult) {
NS_ENSURE_ARG_POINTER(aFolder);
NS_ENSURE_ARG_POINTER(aNewMsgHdr);
- NS_ENSURE_ARG_POINTER(aReusable);
+ // NS_ENSURE_ARG_POINTER(aReusable);
NS_ENSURE_ARG_POINTER(aResult);
#ifdef _DEBUG
NS_ASSERTION(m_streamOutstandingFolder != aFolder, "didn't finish prev msg");
m_streamOutstandingFolder = aFolder;
#endif
- *aReusable = true;
+ // *aReusable = true;
nsCOMPtr<nsIFile> mboxFile;
aFolder->GetFilePath(getter_AddRefs(mboxFile));
nsCOMPtr<nsIMsgDatabase> db;
aFolder->GetMsgDatabase(getter_AddRefs(db));
if (!db && !*aNewMsgHdr) NS_WARNING("no db, and no message header");
bool exists;
nsresult rv;
mboxFile->Exists(&exists);
if (!exists) {
rv = mboxFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCString URI;
aFolder->GetURI(URI);
nsCOMPtr<nsISeekableStream> seekable;
+
+ // Note here, m_outputStreams is a hash table (maybe an array) or something like that in this part of code.
+
if (m_outputStreams.Get(URI, aResult)) {
seekable = do_QueryInterface(*aResult, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
if (NS_FAILED(rv)) {
m_outputStreams.Remove(URI);
NS_RELEASE(*aResult);
}
}
if (!*aResult) {
- rv = MsgGetFileStream(mboxFile, aResult);
+ rv = MsgNewBufferedFileOutputStream(aResult, mboxFile, PR_WRONLY, 00600);
+#ifdef DEBUG
+ // print pathname for easy analysis of the log on tryserver.
+ nsAutoCString mboxFilePath;
+ // GetNativePath is not available under Windows.
+ // GetNativeTarget is.
+ nsresult rv2 = mboxFile->GetNativeTarget(mboxFilePath);
+ if (NS_SUCCEEDED(rv2)) {
+ fprintf(stderr, "(debug) Creating buffered output stream to mboxFile=<<%s>> in nsMsgBrkMBoxStore::GetNewMsgOutputStream in nsMsgBrkMBoxStore.cpp;\n", mboxFilePath.Data());
+ } else
+ fprintf(stderr, "(debug) Creating buffered output stream in nsMsgBrkMBoxStore::GetNewMsgOutputStream in nsMsgBrkMBoxStore.cpp. mboxFile path unknown\n");
+
+ // An error occurred when an |Inbox| directory as opposed to |Inbox| file
+ // existed in the mail store. We should check for errors(!)
+ // 8052000D : NS_ERROR_FILE_IS_DIRECTORY
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) MsgGetFileStream(mboxFile, aResult); returns error rv=0x%08x",
+ (unsigned int)rv);
+ }
+#endif
NS_ASSERTION(NS_SUCCEEDED(rv), "failed opening offline store for output");
if (NS_FAILED(rv))
printf("failed opening offline store for %s\n", URI.get());
NS_ENSURE_SUCCESS(rv, rv);
seekable = do_QueryInterface(*aResult, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
NS_ENSURE_SUCCESS(rv, rv);
@@ -622,41 +655,68 @@ nsMsgBrkMBoxStore::GetNewMsgOutputStream
(*aNewMsgHdr)->SetStringProperty("storeToken", storeToken);
}
return rv;
}
NS_IMETHODIMP
nsMsgBrkMBoxStore::DiscardNewMessage(nsIOutputStream *aOutputStream,
nsIMsgDBHdr *aNewHdr) {
- NS_ENSURE_ARG_POINTER(aOutputStream);
+
+ nsresult rv = NS_OK;
+ // We no longer need the check on |aOututStream|, and actually it is
+ // incorrect. |aOutputStream| can be null when the caller passes a
+ // filestream pointer which is set to nullptr during error
+ // processing.
+ // NS_ENSURE_ARG_POINTER(aOutputStream);
NS_ENSURE_ARG_POINTER(aNewHdr);
#ifdef _DEBUG
m_streamOutstandingFolder = nullptr;
#endif
uint64_t hdrOffset;
aNewHdr->GetMessageOffset(&hdrOffset);
- aOutputStream->Close();
+
+ if (aOutputStream) {
+ // Error of this Close() probably needs to be thrown away since this
+ // is an error path.
+ rv = aOutputStream->Close();
+#ifdef DEBUG
+ fprintf(stderr,
+ "(debug) nsMsgBrkMBoxStore::DiscardNewMessage calls aOutputStream->Close() = 0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+#endif
+ }
+
nsCOMPtr<nsIFile> mboxFile;
nsCOMPtr<nsIMsgFolder> folder;
- nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+ rv = aNewHdr->GetFolder(getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
folder->GetFilePath(getter_AddRefs(mboxFile));
return mboxFile->SetFileSize(hdrOffset);
}
NS_IMETHODIMP
nsMsgBrkMBoxStore::FinishNewMessage(nsIOutputStream *aOutputStream,
nsIMsgDBHdr *aNewHdr) {
+ nsresult rv = NS_OK;
+
#ifdef _DEBUG
m_streamOutstandingFolder = nullptr;
#endif
- NS_ENSURE_ARG_POINTER(aOutputStream);
+ // NS_ENSURE_ARG_POINTER(aOutputStream);
// NS_ENSURE_ARG_POINTER(aNewHdr);
- return NS_OK;
+
+ if (aOutputStream) {
+ rv = aOutputStream->Close();
+#if DEBUG
+ fprintf(stderr,"{deubg} nsMsgBrkMBoxStore::FinishNewMessage aOutputStream->Close() =0x%" PRIx32 "\n",
+ static_cast<uint32_t>(rv));
+#endif
+ }
+ return rv;
}
NS_IMETHODIMP
nsMsgBrkMBoxStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr *aNewHdr,
nsIMsgFolder *aDestFolder,
bool *aResult) {
NS_ENSURE_ARG_POINTER(aNewHdr);
NS_ENSURE_ARG_POINTER(aDestFolder);
@@ -673,28 +733,52 @@ nsMsgBrkMBoxStore::GetMsgInputStream(nsI
nsIInputStream **aResult) {
NS_ENSURE_ARG_POINTER(aMsgFolder);
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aOffset);
// If there is no store token, then we set it to the existing message offset.
if (aMsgToken.IsEmpty()) {
uint64_t offset;
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgBrkMBoxStore::GetMsgInputStream: aMsgToken is empty.\n");
+#endif
NS_ENSURE_ARG_POINTER(aMsgHdr);
aMsgHdr->GetMessageOffset(&offset);
*aOffset = int64_t(offset);
char storeToken[100];
PR_snprintf(storeToken, sizeof(storeToken), "%lld", *aOffset);
aMsgHdr->SetStringProperty("storeToken", storeToken);
} else
*aOffset = ParseUint64Str(PromiseFlatCString(aMsgToken).get());
*aReusable = true;
+ nsCString URI;
nsCOMPtr<nsIFile> mboxFile;
+ aMsgFolder->GetURI(URI);
aMsgFolder->GetFilePath(getter_AddRefs(mboxFile));
- return NS_NewLocalFileInputStream(aResult, mboxFile);
+ nsresult rv;
+ rv = NS_NewLocalFileInputStream(aResult, mboxFile);
+#ifdef DEBUG
+ // Print the file path name for easier failure analysis from the log on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString mboxFilePath;
+ // GetNativePath is not available under Windows.
+ // GetNativeTarget is.
+ nsresult rv2 = mboxFile->GetNativeTarget(mboxFilePath);
+ if (NS_SUCCEEDED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgBrkMBoxStore::GetMsgInputStream: "
+ "NS_NewLocalFileInputStream() failed. mboxFile.get()=%s\n",
+ mboxFilePath.Data());
+ }
+ else
+ fprintf(stderr, "(debug) nsMsgBrkMBoxStore::GetMsgInputStream: "
+ "NS_NewLocalFileInputStream() failed. mboxFile path unknown.\n");
+ }
+#endif
+ return rv;
}
NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteMessages(nsIArray *aHdrArray) {
return ChangeFlags(aHdrArray, nsMsgMessageFlags::Expunged, true);
}
NS_IMETHODIMP
nsMsgBrkMBoxStore::CopyMessages(bool isMove, nsIArray *aHdrArray,
@@ -810,16 +894,17 @@ void nsMsgBrkMBoxStore::SetDBValid(nsIMs
NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeFlags(nsIArray *aHdrArray,
uint32_t aFlags, bool aSet) {
NS_ENSURE_ARG_POINTER(aHdrArray);
nsCOMPtr<nsIOutputStream> outputStream;
nsCOMPtr<nsISeekableStream> seekableStream;
int64_t restoreStreamPos;
uint32_t messageCount;
+ nsresult rv1;
nsresult rv = aHdrArray->GetLength(&messageCount);
NS_ENSURE_SUCCESS(rv, rv);
if (!messageCount) return NS_ERROR_INVALID_ARG;
rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
restoreStreamPos);
NS_ENSURE_SUCCESS(rv, rv);
@@ -827,21 +912,55 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeF
for (uint32_t i = 0; i < messageCount; i++) {
msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
// Seek to x-mozilla-status offset and rewrite value.
rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
if (NS_FAILED(rv)) {
NS_WARNING("updateFolderFlag failed");
break;
}
+
+ rv1 = outputStream->Flush();
+ if (NS_FAILED(rv1))
+ {
+ NS_WARNING("Flush after updateFolderFlag inside a loop failed");
+#ifdef DEBUG
+ /* print verbose log for easier error analysis of the log on tryserver */
+ fflush(stdout);
+ fprintf(stderr, "(debug) %s:%d: outputStream->Flush() inside a loop returned 0x%" PRIx32 "\n",
+ __FILE__, __LINE__, static_cast<uint32_t>(rv1));
+#endif
+ break;
+ }
}
+
+ //
+ // Note that flush() is done inside the loop, but
+ // if updateFlag itself fails for some reason, we need flushing.
+ //
+ rv1 = outputStream->Flush();
+#ifdef DEBUG
+ fflush(stdout);
+ fprintf(stderr, "(debug) %s:%d: outputStream->Flush() returned 0x%" PRIx32 "\n",
+ __FILE__, __LINE__,
+ static_cast<uint32_t> (rv1));
+#endif
+
+ if (NS_FAILED(rv1)) {
+ NS_WARNING("Flush after updateFolderFlag loop failed");
+ }
+
if (restoreStreamPos != -1)
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
- else if (outputStream)
- outputStream->Close();
+ else if (outputStream) {
+ nsresult rv3 = outputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("outputStream->Close() failed;");
+ outputStream = nullptr; // code uniformity.
+ }
if (messageCount > 0) {
msgHdr = do_QueryElementAt(aHdrArray, 0);
SetDBValid(msgHdr);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeKeywords(nsIArray *aHdrArray,
@@ -891,22 +1010,37 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeK
uint64_t messageOffset;
msgHdr->GetMessageOffset(&messageOffset);
uint32_t statusOffset = 0;
(void)msgHdr->GetStatusOffset(&statusOffset);
uint64_t desiredOffset = messageOffset + statusOffset;
ChangeKeywordsHelper(msgHdr, desiredOffset, lineBuffer, keywordArray, aAdd,
outputStream, seekableStream, inputStream);
+ // XXX Detect I/O error ASAP
+ nsresult rv2 = outputStream->Flush();
+#ifdef DEBUG
+ fflush(stdout);
+ fprintf(stderr,
+ "(debug) %s:%d: outputStream->Flush() returned 0x%" PRIx32 "\n",
+ __FILE__, __LINE__,
+ static_cast<uint32_t> (rv2));
+#endif
+ if (NS_FAILED(rv2))
+ NS_WARNING("outputStream->Flush(); failed.");
}
lineBuffer = nullptr;
if (restoreStreamPos != -1)
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
- else if (outputStream)
- outputStream->Close();
+ else if (outputStream) {
+ nsresult rv3 = outputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("outputStream->Close() failed.");
+ outputStream = nullptr;
+ }
if (messageCount > 0) {
msgHdr = do_QueryElementAt(aHdrArray, 0);
SetDBValid(msgHdr);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgBrkMBoxStore::GetStoreType(nsACString &aType) {
--- a/mailnews/local/src/nsMsgLocalStoreUtils.cpp
+++ b/mailnews/local/src/nsMsgLocalStoreUtils.cpp
@@ -82,27 +82,42 @@ void nsMsgLocalStoreUtils::ChangeKeyword
for (uint32_t i = 0; i < keywordArray.Length(); i++) {
nsAutoCString header;
nsAutoCString keywords;
bool done = false;
uint32_t len = 0;
nsAutoCString keywordToWrite(" ");
keywordToWrite.Append(keywordArray[i]);
+
+ // XXX TODO There will be an implicit flush before seek.
+ // We may want to explicitly call it so that a detailed error breakdown can be done.
+ // Not done currently.
+ // nsresult rv1 = fileStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "fileStream->Flush() failed.");
+
+ // XXX TODO: We ought to check for Seek failure, too...
+ // This may happen if a remote file system failure occurs when Maildir is
+ // on such a remote file system. An error after a timeout will return!
+ // Due to man-power shortage, it is on a todo/fixme for a future work.
+
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, desiredOffset);
// need to reset lineBuffer, which is cheaper than creating a new one.
lineBuffer->start = lineBuffer->end = lineBuffer->buf;
bool inKeywordHeader = false;
bool foundKeyword = false;
int64_t offsetToAddKeyword = 0;
bool more;
message->GetMessageSize(&len);
// loop through
while (!done) {
int64_t lineStartPos;
+ // XXX TODO We ought to check for Tell failure, too...
+ // This may happen if a remote file system failure occurs when Maildir is
+ // on such a remote file system. An error after a timeout will return!
seekableStream->Tell(&lineStartPos);
// we need to adjust the linestart pos by how much extra the line
// buffer has read from the stream.
lineStartPos -= (lineBuffer->end - lineBuffer->start);
// NS_ReadLine doesn't return line termination chars.
nsCString keywordHeaders;
nsresult rv = NS_ReadLine(inputStream, lineBuffer, keywordHeaders, &more);
if (NS_SUCCEEDED(rv)) {
@@ -124,19 +139,33 @@ void nsMsgLocalStoreUtils::ChangeKeyword
if (MsgFindKeyword(keywordArray[i], keywordHeaders, &startOffset,
&keywordLength)) {
foundKeyword = true;
if (!aAdd) // if we're removing, remove it, and break;
{
keywordHeaders.Cut(startOffset, keywordLength);
for (int32_t j = keywordLength; j > 0; j--)
keywordHeaders.Append(' ');
+
+ // we call Flush before seek so that a detailed error breakdown can be done.
+
+ nsresult rv2 = outputStream->Flush();
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("outputStream->Flush() failed.");
+ }
+ // XXX we ought to check for Seek failure, too...
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, lineStartPos);
- outputStream->Write(keywordHeaders.get(), keywordHeaders.Length(),
- &bytesWritten);
+ nsresult rv4 = outputStream->Write(keywordHeaders.get(), keywordHeaders.Length(),
+ &bytesWritten);
+ if (NS_FAILED(rv4) || keywordHeaders.Length() != bytesWritten)
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && keywordHeaders.Length() == bytesWritten,
+ "outputStream->Write(keywordHeaders.get(), ...) failed.");
+ // XXX better error handling.
+ }
}
offsetToAddKeyword = 0;
// if adding and we already have the keyword, done
done = true;
break;
}
// argh, we need to check all the lines to see if we already have the
// keyword, but if we don't find it, we want to remember the line and
@@ -151,20 +180,33 @@ void nsMsgLocalStoreUtils::ChangeKeyword
offsetToAddKeyword = lineStartPos + curKeywordHdr.Length();
}
}
}
if (aAdd && !foundKeyword) {
if (!offsetToAddKeyword)
message->SetUint32Property("growKeywords", 1);
else {
+ // This seek is for write.
+ // XXX TODO There will be an implicit flush before seek.
+ // We may want to explicitly call it so that a detailed error breakdown can be done.
+ // Not done currently.
+ // nsresult rv1 = fileStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "fileStream->Flush() failed.");
+
+ // XXX we ought to check for Seek failure, too...
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
offsetToAddKeyword);
- outputStream->Write(keywordToWrite.get(), keywordToWrite.Length(),
+ nsresult rv4 = outputStream->Write(keywordToWrite.get(), keywordToWrite.Length(),
&bytesWritten);
+ if (NS_FAILED(rv4) || keywordToWrite.Length() != bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && keywordToWrite.Length() == bytesWritten,
+ "outputStream->Write(keywordToWrite.get(), ...) failed.");
+ // XXX better error recovery
+ }
}
}
}
}
nsresult nsMsgLocalStoreUtils::UpdateFolderFlag(nsIMsgDBHdr *mailHdr, bool bSet,
nsMsgMessageFlagType flag,
nsIOutputStream *fileStream) {
@@ -175,18 +217,26 @@ nsresult nsMsgLocalStoreUtils::UpdateFol
// we just ignore this.
if (NS_FAILED(rv) || (statusOffset == 0)) return NS_OK;
rv = mailHdr->GetMessageOffset(&msgOffset);
NS_ENSURE_SUCCESS(rv, rv);
uint64_t statusPos = msgOffset + statusOffset;
nsCOMPtr<nsISeekableStream> seekableStream(
do_QueryInterface(fileStream, &rv));
NS_ENSURE_SUCCESS(rv, rv);
+
+ // Is the following seek for read/write?
+ // XXX TODO Here will be an implicit flush before seek if we are writing,
+ // We may want to explicitly call it so that a detailed error breakdown can be done.
+ // Currently not done.
+ // nsresult rv1 = fileStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "fileStream->Flush() failed.");
rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos);
NS_ENSURE_SUCCESS(rv, rv);
+
char buf[50];
buf[0] = '\0';
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(fileStream, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t bytesRead;
if (NS_SUCCEEDED(
inputStream->Read(buf, X_MOZILLA_STATUS_LEN + 6, &bytesRead))) {
buf[bytesRead] = '\0';
@@ -208,45 +258,80 @@ nsresult nsMsgLocalStoreUtils::UpdateFol
(curFlags & ~nsMsgMessageFlags::RuntimeOnly);
if (bSet)
flags |= flag;
else
flags &= ~flag;
} else {
flags &= ~nsMsgMessageFlags::RuntimeOnly;
}
+
+ // This seek is for write.
+ // XXX TODO There will be an implicit flush before seek.
+ // We may want to explicitly call it so that a detailed error breakdown can be done.
+ // Currently not done.
+ // nsresult rv1 = fileStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "fileStream->Flush() failed.");
+
+ // XXX we ought to check for Seek failure, too...
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos);
// We are filing out x-mozilla-status flags here
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT,
flags & 0x0000FFFF);
int32_t lineLen = PL_strlen(buf);
uint64_t status2Pos = statusPos + lineLen;
- fileStream->Write(buf, lineLen, &bytesWritten);
+ nsresult rv4 = fileStream->Write(buf, lineLen, &bytesWritten);
+ if (NS_FAILED(rv4) || lineLen != (int) bytesWritten) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && lineLen == (int) bytesWritten,
+ "fileStream->Write(buf, lineLen, &bytesWritten);");
+ // XXX Better error recovery.
+ }
if (flag & 0xFFFF0000) {
+ // The following seek is for read.
+ // XXX TODO We ought to check for Seek failure, too...
+
// Time to update x-mozilla-status2,
// first find it by finding end of previous line, see bug 234935.
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
do {
rv = inputStream->Read(buf, 1, &bytesRead);
status2Pos++;
} while (NS_SUCCEEDED(rv) && (*buf == '\n' || *buf == '\r'));
status2Pos--;
+
+ // This seek is for read.
+ // XXX TODO We ought to check for Seek failure, too...
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
if (NS_SUCCEEDED(inputStream->Read(buf, X_MOZILLA_STATUS2_LEN + 10,
&bytesRead))) {
if (strncmp(buf, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN) == 0 &&
strncmp(buf + X_MOZILLA_STATUS2_LEN, ": ", 2) == 0 &&
strlen(buf) >= X_MOZILLA_STATUS2_LEN + 10) {
uint32_t dbFlags;
(void)mailHdr->GetFlags(&dbFlags);
dbFlags &= 0xFFFF0000;
+
+ // The following seek is for write.
+ // XXX TODO There will be an implicit flush before seek.
+ // We may want to explicitly call it so that a detailed error breakdown can be done.
+ // Currently not done.
+ // nsresult rv1 = fileStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "fileStream->Flush() failed.");
+
+ // XXX we ought to check for Seek failure, too...
seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT, dbFlags);
- fileStream->Write(buf, PL_strlen(buf), &bytesWritten);
+ nsresult rv4 = fileStream->Write(buf, PL_strlen(buf), &bytesWritten);
+ if (NS_FAILED(rv4) || PL_strlen(buf) != bytesWritten)
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(rv4) && PL_strlen(buf) == bytesWritten,
+ "fileStream->Write(buf, PL_strlen(buf), &bytesWritten) failed");
+ // XXX better error recovery
+ }
}
}
}
} else {
#ifdef DEBUG
printf(
"Didn't find %s where expected at position %ld\n"
"instead, found %s.\n",
--- a/mailnews/local/src/nsMsgMaildirStore.cpp
+++ b/mailnews/local/src/nsMsgMaildirStore.cpp
@@ -238,17 +238,20 @@ NS_IMETHODIMP nsMsgMaildirStore::CreateF
if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) &&
unusedDB) {
// need to set the folder name
nsCOMPtr<nsIDBFolderInfo> folderInfo;
rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
if (NS_SUCCEEDED(rv)) folderInfo->SetMailboxName(safeFolderName);
unusedDB->SetSummaryValid(true);
- unusedDB->Close(true);
+ nsresult rv3 = unusedDB->Close(true);
+ if (NS_FAILED(rv3))
+ NS_WARNING("unusedDB->Close() failed.");
+ unusedDB = nullptr;
aParent->UpdateSummaryTotals(true);
} else {
MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
("CreateFolder - failed creating db for new folder\n"));
path->Remove(true); // recursive
rv = NS_MSG_CANT_CREATE_FOLDER;
}
}
@@ -556,28 +559,26 @@ NS_IMETHODIMP nsMsgMaildirStore::CopyFol
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgMaildirStore::GetNewMsgOutputStream(nsIMsgFolder *aFolder,
nsIMsgDBHdr **aNewMsgHdr,
- bool *aReusable,
nsIOutputStream **aResult) {
NS_ENSURE_ARG_POINTER(aFolder);
NS_ENSURE_ARG_POINTER(aNewMsgHdr);
- NS_ENSURE_ARG_POINTER(aReusable);
NS_ENSURE_ARG_POINTER(aResult);
- *aReusable = false; // message per file
-
nsCOMPtr<nsIMsgDatabase> db;
nsresult rv = aFolder->GetMsgDatabase(getter_AddRefs(db));
NS_ENSURE_SUCCESS(rv, rv);
+ if(!db)
+ NS_ERROR("no db");
if (!*aNewMsgHdr) {
rv = db->CreateNewHdr(nsMsgKey_None, aNewMsgHdr);
NS_ENSURE_SUCCESS(rv, rv);
}
// With maildir, messages have whole file to themselves.
(*aNewMsgHdr)->SetMessageOffset(0);
@@ -614,20 +615,49 @@ nsMsgMaildirStore::GetNewMsgOutputStream
(*aNewMsgHdr)->SetStringProperty("storeToken", newName.get());
return MsgNewBufferedFileOutputStream(aResult, newFile,
PR_WRONLY | PR_CREATE_FILE, 00600);
}
NS_IMETHODIMP
nsMsgMaildirStore::DiscardNewMessage(nsIOutputStream *aOutputStream,
nsIMsgDBHdr *aNewHdr) {
- NS_ENSURE_ARG_POINTER(aOutputStream);
+ // We no longer need the check on |aOutputStream|, and actually it
+ // is incorrect. |aOutputStream| can be null when the caller passes
+ // a filestream pointer which is set to nullptr during error
+ // processing.
+ // NS_ENSURE_ARG_POINTER(aOutputStream);
+
NS_ENSURE_ARG_POINTER(aNewHdr);
- aOutputStream->Close();
+ // In the design scheme when |reusableflag| is used to
+ // keep openstream unclosed as much as possible,
+ // DiscardNewMessage() should not close aOutputStream usually, but
+ // Maildir version is special.
+ // In a symmetrical or synchronized semantics with FinishNewMessage() it should close
+ // the file stream. This is necessary for Windows.
+ // If we keep the stream opened during DiscardNewMessage(), necessary file removal at the end
+ // cannot be done under Windows.
+ // See bug 1121842
+
+ if (aOutputStream) { // aOutputStream can be nullptr due to error processing.
+ // Error here probably can be thrown away since this is an error path.
+ nsresult rv1 = aOutputStream->Close();
+ if (NS_FAILED(rv1))
+ NS_WARNING("nsMsgMaildirStore::DiscardNewMessage: "
+ "aOutputStream->Close() failed.");
+ }
+
+ // XXX not sure what the best error notification/recovery should be.
+ // Failure to close is bad. But at least let us try if we can remove
+ // the file in the subsequent step. This SHOULD WORK under
+ // Linux and OSX even if Close() fails here (eventually at the time of
+ // TB exit, the file should disappear since no reference to it
+ // will be maintained then.).
+
// file path is stored in message header property "storeToken"
nsAutoCString fileName;
aNewHdr->GetStringProperty("storeToken", getter_Copies(fileName));
if (fileName.IsEmpty()) return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> path;
nsCOMPtr<nsIMsgFolder> folder;
nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
@@ -640,48 +670,119 @@ nsMsgMaildirStore::DiscardNewMessage(nsI
path->AppendNative(fileName);
return path->Remove(false);
}
NS_IMETHODIMP
nsMsgMaildirStore::FinishNewMessage(nsIOutputStream *aOutputStream,
nsIMsgDBHdr *aNewHdr) {
- NS_ENSURE_ARG_POINTER(aOutputStream);
+ // XXX What would be the proper error handling if Close() fails.
+ // We don't even tell the error to the user today.
+
+ // aOutputStream can be nullptr due to error processing.
+ // NS_ENSURE_ARG_POINTER(aOutputStream);
+#ifdef DEBUG
+ // Let us check if this strange situation can happen for DEBUG binary.
+ if (!aOutputStream) {
+ fprintf(stderr, "(debug) aOutputStream is nullptr on entry to nsMsgMaildirStore::FinishNewMessage\n");
+ }
+#endif
+
NS_ENSURE_ARG_POINTER(aNewHdr);
- aOutputStream->Close();
+ // In the design scheme when |reusableflag| is used to
+ // keep openstream unclosed as much as possible,
+ // FinishNewMessage() should not close aOutputStream usually, but
+ // Maildir version is special.
+ // In a symmetrical or synchronized semantics with DiscardNewMessage() it should close
+ // the file stream. This is necessary for Windows.
+ // If we keep the stream opened during FinishNewMessage(), necessary file move/copy
+ // between "cur" and "tmp" cannot be done under Windows.
+ // See bug 1121842
+
+ if (aOutputStream) { // aOutputStream can be nullptr due to error processing.
+ nsresult rv1 = aOutputStream->Close();
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "aOutputStream(%p)->Close() = 0x%" PRIx32 "\n",
+ aOutputStream,
+ static_cast<uint32_t>(rv1));
+#endif
+ if (NS_FAILED(rv1))
+ NS_WARNING("nsMsgMaildirStore::FinishNewMessage: "
+ "aOutputStream->Close() failed.");
+ }
+ // Failure to close is bad. But at least let us try proceeding.
+ // Even if close fails, the following processing should work under Linux and OSX.
nsCOMPtr<nsIFile> folderPath;
nsCOMPtr<nsIMsgFolder> folder;
nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "aNewHdr->GetFolder failed rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
rv = folder->GetFilePath(getter_AddRefs(folderPath));
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "folder->GetFilePath failed rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
// tmp filename is stored in "storeToken".
// By now we'll have the Message-ID, which we'll base the final filename on.
nsAutoCString tmpName;
aNewHdr->GetStringProperty("storeToken", getter_Copies(tmpName));
if (tmpName.IsEmpty()) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "no storeToken in msg hdr!!\n");
+#endif
NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!\n");
return NS_ERROR_FAILURE;
}
// path to the new destination
nsCOMPtr<nsIFile> curPath;
folderPath->Clone(getter_AddRefs(curPath));
curPath->Append(NS_LITERAL_STRING("cur"));
// let's check if the folder exists
// XXX TODO: kill this and make sure maildir creation includes cur/tmp
bool exists;
curPath->Exists(&exists);
if (!exists) {
rv = curPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ nsAutoCString nativePath;
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "curPath->Create failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ // GetNativePath is not available under Windows.
+ // GetNativeTarget is.
+ nsresult rv2 = curPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "curPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "curPath->nativePath = <<%s>>\n", nativePath.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
}
// path to the downloaded message
nsCOMPtr<nsIFile> fromPath;
folderPath->Clone(getter_AddRefs(fromPath));
fromPath->Append(NS_LITERAL_STRING("tmp"));
fromPath->AppendNative(tmpName);
@@ -690,25 +791,66 @@ nsMsgMaildirStore::FinishNewMessage(nsIO
// XXX TODO: revisit this. I think it's needed because the
// pairing rules for:
// GetNewMsgOutputStream(), FinishNewMessage(),
// MoveNewlyDownloadedMessage() and DiscardNewMessage()
// are not well defined.
// If they are sorted out, this code can be removed.
fromPath->Exists(&exists);
if (!exists) {
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath does NOT exist?!\n");
+ nsresult rv2 = fromPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath->nativePath = <<%s>>\n", nativePath.Data());
+#endif
// Perhaps the message has already moved. See bug 1028372 to fix this.
nsCOMPtr<nsIFile> existingPath;
curPath->Clone(getter_AddRefs(existingPath));
existingPath->AppendNative(tmpName);
existingPath->Exists(&exists);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ {
+ fprintf(stderr, "(debug) checking existingPath.\n");
+ nsAutoCString nativePath;
+ nsresult rv2 = existingPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "existingPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMsg: "
+ "existingPath->nativePath = <<%s>>\n", nativePath.Data());
+ }
+ }
+#endif
if (exists) // then there is nothing to do
+ {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "cur? does NOT exist, but existingPath exists. So it is OK\n");
+#endif
return NS_OK;
+ }
- NS_ERROR("FinishNewMessage - oops! file does not exist!");
+ // We hit this path during xpcshell-test of windows binary on tryserver
+ // when we experimented with enabling buffered write.
+ // NS_ERROR was a little too severe.
+ // NS_ERROR("FinishNewMessage - oops! file does not exist!");
+ //
+ NS_WARNING("FinishNewMessage - oops! existingPath does not exist!");
return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
}
nsCString msgID;
aNewHdr->GetMessageId(getter_Copies(msgID));
nsCString baseName;
// For missing or suspiciously-short Message-IDs, use a timestamp
@@ -730,16 +872,33 @@ nsMsgMaildirStore::FinishNewMessage(nsIO
nsCOMPtr<nsIFile> toPath;
curPath->Clone(getter_AddRefs(toPath));
nsCString toName(baseName);
toName.Append(".eml");
toPath->AppendNative(toName);
// Using CreateUnique in case we have duplicate Message-Ids
rv = toPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "toPath->CreateUnique failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ nsresult rv2 = toPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "toPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "toPath->nativePath = <<%s>>\n", nativePath.Data());
+ }
+#endif
if (NS_FAILED(rv)) {
// NS_ERROR_FILE_TOO_BIG means CreateUnique() bailed out at 10000 attempts.
if (rv != NS_ERROR_FILE_TOO_BIG) {
NS_ENSURE_SUCCESS(rv, rv);
}
// As a last resort, fall back to using timestamp as filename.
toName.SetLength(0);
toName.AppendInt(static_cast<int64_t>(PR_Now()));
@@ -747,16 +906,46 @@ nsMsgMaildirStore::FinishNewMessage(nsIO
toPath->SetNativeLeafName(toName);
rv = toPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
NS_ENSURE_SUCCESS(rv, rv);
}
// Move into place (using whatever name CreateUnique() settled upon).
toPath->GetNativeLeafName(toName);
rv = fromPath->MoveToNative(curPath, toName);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath->MoveToNative(curPath, toName); failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ // print fromPath, toPath, fileName
+ // fromPath
+ nsresult rv2 = fromPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "fromPath->nativePath = <<%s>>\n", nativePath.Data());
+ // toPath
+ rv2 = toPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "toPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "toPath->nativePath = <<%s>>\n", nativePath.Data());
+ // fileName
+ fprintf(stderr, "(debug) fileName = <<%s>>\n", nativePath.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
// Update the db to reflect the final filename.
aNewHdr->SetStringProperty("storeToken", toName.get());
return NS_OK;
}
NS_IMETHODIMP
nsMsgMaildirStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr *aHdr,
@@ -765,91 +954,221 @@ nsMsgMaildirStore::MoveNewlyDownloadedMe
NS_ENSURE_ARG_POINTER(aHdr);
NS_ENSURE_ARG_POINTER(aDestFolder);
NS_ENSURE_ARG_POINTER(aResult);
nsCOMPtr<nsIFile> folderPath;
nsCOMPtr<nsIMsgFolder> folder;
nsresult rv = aHdr->GetFolder(getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
+ if (!folder) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: folder was NULL!.\n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
rv = folder->GetFilePath(getter_AddRefs(folderPath));
NS_ENSURE_SUCCESS(rv, rv);
+ if (!folderPath) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: folderPath was NULL!.\n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
// file path is stored in message header property
nsAutoCString fileName;
aHdr->GetStringProperty("storeToken", getter_Copies(fileName));
if (fileName.IsEmpty()) {
- NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!\n");
+ // We hit this path during xpcshell-test of windows binary on tryserver.
+ // NS_ERROR was a little too severe.
+ NS_WARNING("MoveNewlyDownloadedMessage - oops! no filename in storeToken!\n");
return NS_ERROR_FAILURE;
}
// path to the downloaded message
nsCOMPtr<nsIFile> fromPath;
folderPath->Clone(getter_AddRefs(fromPath));
+ if (!fromPath) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: fromPath was NULL!.\n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
fromPath->Append(NS_LITERAL_STRING("cur"));
fromPath->AppendNative(fileName);
// let's check if the tmp file exists
- bool exists;
+ bool exists = false;
fromPath->Exists(&exists);
if (!exists) {
- NS_ERROR("FinishNewMessage - oops! file does not exist!");
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ nsAutoCString nativePath;
+ nsresult rv2 = fromPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr,
+ "(debug) fromPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "fromPath->nativePath = <<%s>>\n", nativePath.Data());
+#endif
+ // We hit this path during xpcshell-test of windows binary on tryserver.
+ // NS_ERROR was a little too severe.
+ // NS_ERROR("FinishNewMessage - oops! file does not exist!");
+ //
+ NS_WARNING("FinishNewMessage - oops! file does not exist!\n");
return NS_ERROR_FAILURE;
}
// move to the "cur" subfolder
nsCOMPtr<nsIFile> toPath;
aDestFolder->GetFilePath(getter_AddRefs(folderPath));
+ if (!folderPath) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: folderPath was NULL!.\n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
folderPath->Clone(getter_AddRefs(toPath));
+ if (!toPath) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: toPath was NULL!.\n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
toPath->Append(NS_LITERAL_STRING("cur"));
// let's check if the folder exists
// XXX TODO: kill this and make sure maildir creation includes cur/tmp
+ exists = false;
toPath->Exists(&exists);
if (!exists) {
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "Creating toPath file\n: ");
+ nsresult rv2 = toPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr,
+ "(debug) toPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else {
+ fprintf(stderr, "(debug) toPath->nativePath = <<%s>>\n", nativePath.Data());
+ }
+#endif
+
rv = toPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) toPath->Create() failed. rv = 0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIMsgDatabase> destMailDB;
rv = aDestFolder->GetMsgDatabase(getter_AddRefs(destMailDB));
- NS_WARNING_ASSERTION(destMailDB && NS_SUCCEEDED(rv),
- "failed to open mail db moving message");
+ if (! (destMailDB && NS_SUCCEEDED(rv)))
+ NS_WARNING("failed to open mail db moving message");
nsCOMPtr<nsIMsgDBHdr> newHdr;
if (destMailDB)
rv = destMailDB->CopyHdrFromExistingHdr(nsMsgKey_None, aHdr, true,
getter_AddRefs(newHdr));
if (NS_SUCCEEDED(rv) && !newHdr) rv = NS_ERROR_UNEXPECTED;
if (NS_FAILED(rv)) {
aDestFolder->ThrowAlertMsg("filterFolderHdrAddFailed", nullptr);
return rv;
}
nsCOMPtr<nsIFile> existingPath;
toPath->Clone(getter_AddRefs(existingPath));
+ if (!existingPath) {
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: existingPath was NULL after toPath->Clone()!\n");
+#endif
+ }
existingPath->AppendNative(fileName);
+ exists = false;
existingPath->Exists(&exists);
if (exists) {
rv = existingPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "existingPath->CreateUnique failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ nsresult rv2 = existingPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "existingPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "existingPath->nativePath = <<%s>>\n", nativePath.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
existingPath->GetNativeLeafName(fileName);
newHdr->SetStringProperty("storeToken", fileName.get());
}
rv = fromPath->MoveToNative(toPath, fileName);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "fromPath->MoveToNative(toPath, fileName); failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ // print fromPath, toPath, fileName
+ // fromPath
+ nsresult rv2 = fromPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "fromPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "fromPath->nativePath = <<%s>>\n", nativePath.Data());
+ // toPath
+ rv2 = existingPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "existingPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::MoveNewlyDownloadedMessage: "
+ "existingPath->nativePath = <<%s>>\n", nativePath.Data());
+ // fileName
+ fprintf(stderr, "(debug) fileName = <<%s>>\n", fileName.Data());
+ }
+#endif
*aResult = NS_SUCCEEDED(rv);
if (NS_FAILED(rv))
aDestFolder->ThrowAlertMsg("filterFolderWriteFailed", nullptr);
if (NS_FAILED(rv)) {
- if (destMailDB) destMailDB->Close(true);
-
+ if (destMailDB) {
+ nsresult rv3 = destMailDB->Close(true);
+ if (NS_FAILED(rv3))
+ NS_WARNING("destMailDB->Close(true) failed.");
+ destMailDB = nullptr;
+ }
return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
}
bool movedMsgIsNew = false;
// if we have made it this far then the message has successfully been
// written to the new folder now add the header to the destMailDB.
uint32_t newFlags;
@@ -908,34 +1227,87 @@ nsMsgMaildirStore::GetMsgInputStream(nsI
// construct path to file
nsCOMPtr<nsIFile> path;
nsresult rv = aMsgFolder->GetFilePath(getter_AddRefs(path));
NS_ENSURE_SUCCESS(rv, rv);
if (aMsgToken.IsEmpty()) {
MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
("GetMsgInputStream - empty storeToken!!\n"));
+#ifdef DEBUG
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "aMsgToken was empty, returning NS_ERROR_FAILURE\n");
+#endif
return NS_ERROR_FAILURE;
}
path->Append(NS_LITERAL_STRING("cur"));
// let's check if the folder exists
// XXX TODO: kill this and make sure maildir creation includes cur/tmp
- bool exists;
- path->Exists(&exists);
- if (!exists) {
+ bool exists = false;
+ rv = path->Exists(&exists);
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "path->Exists failed: rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ }
+#endif
+ if (NS_SUCCEEDED(rv) && !exists) {
MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
("GetMsgInputStream - oops! cur subfolder does not exist!\n"));
rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if(NS_FAILED(rv)) {
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: path->Create failed. "
+ "rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+
+ nsAutoCString nativePath;
+ nsresult rv2 = path->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: path->GetNativeTarget failed. "
+ "rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: path->nativePath = <<%s>>\n",
+ nativePath.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
}
path->AppendNative(aMsgToken);
- return NS_NewLocalFileInputStream(aResult, path);
+ rv = NS_NewLocalFileInputStream(aResult, path);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: NS_NewLocalFileInputStream() failed. "
+ "rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ nsresult rv2 = path->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: path->GetNativeTarget failed. "
+ "rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr,
+ "(debug) nsMsgMaildirStore::GetMsgInputStream: path->nativePath = <<%s>>\n",
+ nativePath.Data());
+ }
+#endif
+ return rv;
}
NS_IMETHODIMP nsMsgMaildirStore::DeleteMessages(nsIArray *aHdrArray) {
uint32_t messageCount;
nsresult rv = aHdrArray->GetLength(&messageCount);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgFolder> folder;
@@ -955,17 +1327,17 @@ NS_IMETHODIMP nsMsgMaildirStore::DeleteM
// Perhaps an offline store has not downloaded this particular message.
continue;
}
path->Append(NS_LITERAL_STRING("cur"));
path->AppendNative(fileName);
// Let's check if the message exists.
- bool exists;
+ bool exists = false;
path->Exists(&exists);
if (!exists) {
MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
("DeleteMessages - file does not exist !!\n"));
// Perhaps an offline store has not downloaded this particular message.
continue;
}
path->Remove(false);
@@ -1069,27 +1441,62 @@ nsMsgMaildirStore::CopyMessages(bool aIs
nsCOMPtr<nsIFile> srcFile;
rv = srcFolderPath->Clone(getter_AddRefs(srcFile));
NS_ENSURE_SUCCESS(rv, rv);
srcFile->AppendNative(fileName);
nsCOMPtr<nsIFile> destFile;
destFolderPath->Clone(getter_AddRefs(destFile));
destFile->AppendNative(fileName);
- bool exists;
+ bool exists = false;
destFile->Exists(&exists);
if (exists) {
rv = destFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "destFile->CreateUnique failed. rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ nsresult rv2 = destFile->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) nsMsgMaildirStore::FinishNewMessage: "
+ "destFile->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::GetMsgInputStream: "
+ "destFile->nativePath = <<%s>>\n", nativePath.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
destFile->GetNativeLeafName(fileName);
}
if (aIsMove)
rv = srcFile->MoveToNative(destFolderPath, fileName);
else
rv = srcFile->CopyToNative(destFolderPath, fileName);
+#ifdef DEBUG
+ // print pathnames for easy analysis of the log file on tryserver.
+ if (NS_FAILED(rv)) {
+ nsAutoCString nativePath;
+ fprintf(stderr, "(debug) %s failed. rv = 0x%" PRIx32 "\n",
+ (aIsMove) ? "MoveToNative(destFolderPath, Filename);" :
+ "CopyToNative(destFolderPath, fileName);",
+ static_cast<uint32_t> (rv));
+ nsresult rv2 = destFolderPath->GetNativeTarget(nativePath);
+ if (NS_FAILED(rv2)) {
+ fprintf(stderr, "(debug) destFolderPath->GetNativeTarget failed. rv2=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv2));
+ } else
+ fprintf(stderr, "(debug) nsMsgMaildirStore::CopyMessages: "
+ "destFolderPath->nativePath = <<%s>>\n", nativePath.Data());
+ fprintf(stderr, "(debug) fileName = <<%s>>\n", fileName.Data());
+ }
+#endif
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDBHdr> destHdr;
if (destDB) {
rv = destDB->CopyHdrFromExistingHdr(nsMsgKey_None, srcHdr, true,
getter_AddRefs(destHdr));
NS_ENSURE_SUCCESS(rv, rv);
destHdr->SetStringProperty("storeToken", fileName.get());
@@ -1295,19 +1702,47 @@ NS_IMETHODIMP nsMsgMaildirStore::ChangeF
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < messageCount; i++) {
nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
// get output stream for header
nsCOMPtr<nsIOutputStream> outputStream;
rv = GetOutputStream(msgHdr, outputStream);
NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX Flush before seek for reporting
+ // possible Flush() error in more detail.
+ // This particular instance may be null operation.
+ // Not currently done.
+ // nsresult rv1 = outputStream->Flush();
+ // NS_WARN_IF_FALSE(NS_SUCCEEDED(rv1), "outputStream->Flush(); failed.");
+
// Seek to x-mozilla-status offset and rewrite value.
rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
if (NS_FAILED(rv)) NS_WARNING("updateFolderFlag failed");
+
+ // XXX
+ // CAUTION: watch out for performance regression.
+ // We want to flush after a write in this particular instance.
+ // But we usually want to avoid Flush() as much as possible so
+ // that buffering is effective.
+
+ nsresult rv1 = outputStream->Flush();
+#ifdef DEBUG
+ fflush(stdout);
+ fprintf(stderr,
+ "(debug) : Extra %d: outputStream->Flush() returned in nsMsgMaildirStore.cpp "
+ "0x%" PRIx32 "\n",
+ __LINE__,
+ static_cast<uint32_t>(rv1));
+#endif
+
+ if (NS_FAILED(rv1))
+ NS_WARNING("outputStream->Flush(); failed.");
+
}
return NS_OK;
}
// get output stream from header
nsresult nsMsgMaildirStore::GetOutputStream(
nsIMsgDBHdr *aHdr, nsCOMPtr<nsIOutputStream> &aOutputStream) {
// file name is stored in message header property "storeToken"
@@ -1362,19 +1797,25 @@ NS_IMETHODIMP nsMsgMaildirStore::ChangeK
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISeekableStream> seekableStream(
do_QueryInterface(inputStream, &rv));
NS_ENSURE_SUCCESS(rv, rv);
uint32_t statusOffset = 0;
(void)message->GetStatusOffset(&statusOffset);
uint64_t desiredOffset = statusOffset;
+ // outputStream->Flush() is done inside ChangeKeywordsHelper.
ChangeKeywordsHelper(message, desiredOffset, lineBuffer, keywordArray, aAdd,
outputStream, seekableStream, inputStream);
- if (inputStream) inputStream->Close();
+ if (inputStream) {
+ nsresult rv3 = inputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("inputStream->Close() failed.");
+ inputStream = nullptr;
+ }
// ### TODO - if growKeywords property is set on the message header,
// we need to rewrite the message file with extra room for the keywords,
// or schedule some sort of background task to do this.
}
lineBuffer = nullptr;
return NS_OK;
}
@@ -1401,17 +1842,17 @@ nsresult nsMsgMaildirStore::CreateDirect
nsresult rv = NS_OK;
if (!aIsServer) {
rv = GetDirectoryForFolder(path);
NS_ENSURE_SUCCESS(rv, rv);
}
bool pathIsDirectory = false;
path->IsDirectory(&pathIsDirectory);
if (!pathIsDirectory) {
- bool pathExists;
+ bool pathExists = false;
path->Exists(&pathExists);
// If for some reason there's a file with the directory separator
// then we are going to fail.
rv = pathExists ? NS_MSG_COULD_NOT_CREATE_DIRECTORY
: path->Create(nsIFile::DIRECTORY_TYPE, 0700);
}
return rv;
}
--- a/mailnews/local/src/nsParseMailbox.cpp
+++ b/mailnews/local/src/nsParseMailbox.cpp
@@ -295,16 +295,18 @@ nsresult nsMsgMailboxParser::ProcessMail
nsresult ret = NS_OK;
uint32_t bytesRead = 0;
if (NS_SUCCEEDED(m_inputStream.GrowBuffer(aLength))) {
// OK, this sucks, but we're going to have to copy into our
// own byte buffer, and then pass that to the line buffering code,
// which means a couple buffer copies.
+ // XXX TODO we should repeat Read until all the bytes are read...
+ // Would be handled by future patch. CI
ret = aIStream->Read(m_inputStream.GetBuffer(), aLength, &bytesRead);
if (NS_SUCCEEDED(ret))
ret = BufferInput(m_inputStream.GetBuffer(), bytesRead);
}
if (m_graph_progress_total > 0) {
if (NS_SUCCEEDED(ret)) m_graph_progress_received += bytesRead;
}
return (ret);
@@ -1237,18 +1239,18 @@ nsresult nsParseMailMessageState::Finali
nsAutoCString rawMsgId;
/* Take off <> around message ID. */
if (id) {
if (id->length > 0 && id->value[0] == '<') {
id->length--;
id->value++;
}
- NS_WARNING_ASSERTION(id->length > 0,
- "id->length failure in FinalizeHeaders().");
+ if (id->length <= 0)
+ NS_WARNING("id->length failure in FinalizeHeaders().");
if (id->length > 0 && id->value[id->length - 1] == '>')
/* generate a new null-terminated string without the final > */
rawMsgId.Assign(id->value, id->length - 1);
else
rawMsgId.Assign(id->value);
}
@@ -1605,18 +1607,29 @@ nsresult nsParseNewMailState::Init(nsIMs
aMsgWindow, getter_AddRefs(m_deferredToServerFilterList));
}
}
m_disableFilters = false;
return NS_OK;
}
nsParseNewMailState::~nsParseNewMailState() {
- if (m_mailDB) m_mailDB->Close(true);
- if (m_backupMailDB) m_backupMailDB->ForceClosed();
+ if (m_mailDB) {
+ nsresult rv3 = m_mailDB->Close(true);
+ if (NS_FAILED(rv3))
+ NS_WARNING("m_mailDB->Close(true) failed.");
+ m_mailDB = nullptr; // code uniformity
+ }
+ // XXX we may need to check the return value of ForceClosed().
+ if (m_backupMailDB) {
+ m_backupMailDB->ForceClosed();
+ // Without the following statement,
+ // I experienced a double free
+ m_backupMailDB = nullptr;
+ }
#ifdef DOING_JSFILTERS
JSFilter_cleanup();
#endif
}
// not an IMETHOD so we don't need to do error checking or return an error.
// We only have one caller.
void nsParseNewMailState::GetMsgWindow(nsIMsgWindow **aMsgWindow) {
@@ -1675,22 +1688,34 @@ int32_t nsParseNewMailState::PublishMsgH
// Same for deleting it or moving it to trash.
switch (duplicateAction) {
case nsIMsgIncomingServer::deleteDups: {
nsCOMPtr<nsIMsgPluggableStore> msgStore;
nsresult rv =
m_downloadFolder->GetMsgStore(getter_AddRefs(msgStore));
if (NS_SUCCEEDED(rv)) {
rv = msgStore->DiscardNewMessage(m_outputStream, m_newMsgHdr);
+ // Make sure that m_outputStream is closed in
+ // DiscardNewMessage above, because
+ // under Windows, opened files cannot be moved,
+ // renamed, copied, etc.
+
+ // We always set this to null since we close the stream
+ // irrespective of m_tempMessageStreamReusable value.
+ m_outputStream = nullptr; // avoid accessing closed stream
+
+ // Delayed checking of DiscardNewMessage() error return
+
if (NS_FAILED(rv))
m_rootFolder->ThrowAlertMsg("dupDeleteFolderTruncateFailed",
msgWindow);
}
m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
- } break;
+ }
+ break;
case nsIMsgIncomingServer::moveDupsToTrash: {
nsCOMPtr<nsIMsgFolder> trash;
GetTrashFolder(getter_AddRefs(trash));
if (trash) {
uint32_t newFlags;
bool msgMoved;
m_newMsgHdr->AndFlags(~nsMsgMessageFlags::New, &newFlags);
@@ -2268,51 +2293,70 @@ nsresult nsParseNewMailState::EndMsgDown
nsresult nsParseNewMailState::AppendMsgFromStream(nsIInputStream *fileStream,
nsIMsgDBHdr *aHdr,
uint32_t length,
nsIMsgFolder *destFolder) {
nsCOMPtr<nsIMsgPluggableStore> store;
nsCOMPtr<nsIOutputStream> destOutputStream;
nsresult rv = destFolder->GetMsgStore(getter_AddRefs(store));
NS_ENSURE_SUCCESS(rv, rv);
- bool reusable;
- rv = store->GetNewMsgOutputStream(destFolder, &aHdr, &reusable,
+ rv = store->GetNewMsgOutputStream(destFolder, &aHdr,
getter_AddRefs(destOutputStream));
NS_ENSURE_SUCCESS(rv, rv);
if (!m_ibuffer) {
m_ibuffer_size = FILE_IO_BUFFER_SIZE;
m_ibuffer = (char *)PR_Malloc(m_ibuffer_size);
NS_ASSERTION(m_ibuffer != nullptr, "couldn't get memory to move msg");
}
m_ibuffer_fp = 0;
while (length > 0 && m_ibuffer) {
uint32_t nRead;
- fileStream->Read(m_ibuffer,
- length > m_ibuffer_size ? m_ibuffer_size : length, &nRead);
+ rv = fileStream->Read (m_ibuffer, length > m_ibuffer_size ? m_ibuffer_size : length, &nRead);
+ if (NS_FAILED(rv)) {
+
+ // XXX TODO/FIXME: We need a better error recovery strategy,
+ // etc.
+ // The read above DID return an error during the simulated
+ // test of interrupted remote CIFS mount for one's MAIL
+ // directory. March 15, 2015
+ NS_WARNING("fileStream->Read (m_ibuffer, ...) returned error.");
+ }
if (nRead == 0) break;
uint32_t bytesWritten;
// Check the number of bytes actually written to the stream.
destOutputStream->Write(m_ibuffer, nRead, &bytesWritten);
- if (bytesWritten != nRead) {
- destOutputStream->Close();
+
+ nsresult rv4 = destOutputStream->Write(m_ibuffer, nRead, &bytesWritten);
+ if (NS_FAILED(rv4) || bytesWritten != nRead) {
+
+ // XXX TODO Better network error handling.
+ // When the error was observed for read near line 2460, the close
+ // below also failed. March 15, 2015
+
+ nsresult rv3 = destOutputStream->Close();
+ if (NS_FAILED(rv3))
+ NS_WARNING("destOutputStream->Close() failed;");
+ destOutputStream = nullptr;
+
return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
}
length -= nRead;
}
NS_ASSERTION(length == 0,
"didn't read all of original message in filter move");
// non-reusable streams will get closed by the store.
- if (reusable) destOutputStream->Close();
- return store->FinishNewMessage(destOutputStream, aHdr);
+ nsresult rvfinal = store->FinishNewMessage(destOutputStream, aHdr);
+ destOutputStream = nullptr; // code uniformity
+ return rvfinal;
}
/*
* Moves message pointed to by mailHdr into folder destIFolder.
* After successful move mailHdr is no longer usable by the caller.
*/
nsresult nsParseNewMailState::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
nsIMsgDatabase *sourceDB,
@@ -2372,35 +2416,53 @@ nsresult nsParseNewMailState::MoveIncorp
nsCOMPtr<nsIMsgDatabase> destMailDB;
if (!localFolder) return NS_MSG_POP_FILTER_TARGET_ERROR;
// don't force upgrade in place - open the db here before we start writing to
// the destination file because XP_Stat can return file size including bytes
// written...
rv = localFolder->GetDatabaseWOReparse(getter_AddRefs(destMailDB));
- NS_WARNING_ASSERTION(destMailDB && NS_SUCCEEDED(rv),
- "failed to open mail db parsing folder");
+#ifdef DEBUG
+ // running TB under Linux using another Linux CIFS server as mail store
+ // failed the above line when network error occurred.
+ // 8055005: MailNews, failure, no = 5
+ if (NS_FAILED(rv))
+ {
+ fprintf(stderr, "(debug) localFolder->GetDatabaseWOReparse(getter_AddRefs(destMailDB)) "
+ "returned error rv=0x%" PRIx32 "\n",
+ static_cast<uint32_t> (rv));
+ }
+#endif
+ if (!(destMailDB && NS_SUCCEEDED(rv)))
+ NS_WARNING("failed to open mail db parsing folder");
+
nsCOMPtr<nsIMsgDBHdr> newHdr;
if (destMailDB)
rv = destMailDB->CopyHdrFromExistingHdr(m_new_key, mailHdr, true,
getter_AddRefs(newHdr));
if (NS_SUCCEEDED(rv) && !newHdr) rv = NS_ERROR_UNEXPECTED;
if (NS_FAILED(rv)) {
destIFolder->ThrowAlertMsg("filterFolderHdrAddFailed", msgWindow);
} else {
rv = AppendMsgFromStream(inputStream, newHdr, messageLength, destIFolder);
if (NS_FAILED(rv))
destIFolder->ThrowAlertMsg("filterFolderWriteFailed", msgWindow);
}
if (NS_FAILED(rv)) {
- if (destMailDB) destMailDB->Close(true);
+ if (destMailDB) {
+ destMailDB->Close(true);
+ nsresult rv3 = destMailDB->Close(true);
+ if (NS_FAILED(rv3))
+ NS_WARNING("destMailDB->Close() failed;");
+ destMailDB = nullptr;
+ }
destIFolder->ReleaseSemaphore(myISupports);
return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
}
bool movedMsgIsNew = false;
// if we have made it this far then the message has successfully been written
@@ -2445,17 +2507,25 @@ nsresult nsParseNewMailState::MoveIncorp
EmptyCString());
} else {
NS_WARNING("Can't get folder for message that was moved.");
}
}
nsCOMPtr<nsIMsgPluggableStore> store;
rv = m_downloadFolder->GetMsgStore(getter_AddRefs(store));
- if (store) store->DiscardNewMessage(m_outputStream, mailHdr);
+ if (store) {
+ nsresult rv2 = store->DiscardNewMessage(m_outputStream, mailHdr);
+ // we now have to close m_outputStream on the caller side.
+ m_outputStream = nullptr; // avoid accessing closed stream
+ // delayed check of DiscardNewMessage() return value
+ if (NS_FAILED(rv2))
+ NS_WARNING("store->DiscardNewMessage(...) failed;");
+ }
+
if (sourceDB) sourceDB->RemoveHeaderMdbRow(mailHdr);
// update the folder size so we won't reparse.
UpdateDBFolderInfo(destMailDB);
destIFolder->UpdateSummaryTotals(true);
destMailDB->Commit(nsMsgDBCommitType::kLargeCommit);
return rv;