NS_IMETHODIMP nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) { if (!m_fileStream || !inStr) return NS_ERROR_FAILURE; nsresult rv = NS_OK; PRUint32 msgFlags; bool checkForKeyword = m_startOfMsg; bool addKeywordHdr = false; PRUint32 needToGrowKeywords = 0; PRUint32 statusOffset; nsCString msgHdrKeywords; if (m_startOfMsg) { m_statusOffset = 0; m_addedHeaderSize = 0; m_messageUri.Truncate(); // clear the previous message uri if (NS_SUCCEEDED(BuildMessageURI(m_baseMessageUri.get(), m_keyArray->m_keys[m_curIndex], m_messageUri))) { rv = GetMessage(getter_AddRefs(m_curSrcHdr)); NS_ENSURE_SUCCESS(rv, rv); if (m_curSrcHdr) { (void) m_curSrcHdr->GetFlags(&msgFlags); (void) m_curSrcHdr->GetStatusOffset(&statusOffset); if (statusOffset == 0) m_needStatusLine = true; // x-mozilla-status lines should be at the start of the headers, and the code // below assumes everything will fit in m_dataBuffer - if there's not // room, skip the keyword stuff. if (statusOffset > sizeof(m_dataBuffer) - 1024) { checkForKeyword = false; NS_ASSERTION(false, "status offset past end of read buffer size"); } } } m_startOfMsg = false; } PRUint32 maxReadCount, readCount, writeCount; PRUint32 bytesWritten; while (NS_SUCCEEDED(rv) && (PRInt32) count > 0) { maxReadCount = count > sizeof(m_dataBuffer) - 1 ? sizeof(m_dataBuffer) - 1 : count; writeCount = 0; rv = inStr->Read(m_dataBuffer, maxReadCount, &readCount); // if status offset is past the number of bytes we read, it's probably bogus, // and we shouldn't do any of the keyword stuff. if (statusOffset + X_MOZILLA_STATUS_LEN > readCount) checkForKeyword = false; if (NS_SUCCEEDED(rv)) { if (checkForKeyword) { // make sure that status offset really points to x-mozilla-status line if (!strncmp(m_dataBuffer + statusOffset, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN)) { const char *keywordHdr = PL_strnrstr(m_dataBuffer, HEADER_X_MOZILLA_KEYWORDS, readCount); if (keywordHdr) m_curSrcHdr->GetUint32Property("growKeywords", &needToGrowKeywords); else addKeywordHdr = true; m_curSrcHdr->GetStringProperty("keywords", getter_Copies(msgHdrKeywords)); } checkForKeyword = false; } PRUint32 blockOffset = 0; if (m_needStatusLine) { m_needStatusLine = false; // we need to parse out the "From " header, write it out, then // write out the x-mozilla-status headers, and set the // status offset of the dest hdr for later use // in OnEndCopy). if (!strncmp(m_dataBuffer, "From ", 5)) { blockOffset = 5; // skip from line MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); char statusLine[50]; m_fileStream->Write(m_dataBuffer, blockOffset, &writeCount); m_statusOffset = blockOffset; PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF); m_fileStream->Write(statusLine, strlen(statusLine), &m_addedHeaderSize); PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000); m_fileStream->Write(statusLine, strlen(statusLine), &bytesWritten); m_addedHeaderSize += bytesWritten; } else { NS_ASSERTION(false, "not an envelope"); // try to mark the db as invalid so it will be reparsed. nsCOMPtr <nsIMsgDatabase> srcDB; m_folder->GetMsgDatabase(getter_AddRefs(srcDB)); if (srcDB) { srcDB->SetSummaryValid(false); srcDB->ForceClosed(); } } } #define EXTRA_KEYWORD_HDR " "MSG_LINEBREAK // if status offset isn't in the first block, this code won't work. There's no good reason // for the status offset not to be at the beginning of the message anyway. if (addKeywordHdr) { // if blockOffset is set, we added x-mozilla-status headers so // file pointer is already past them. if (!blockOffset) { blockOffset = statusOffset; // skip x-mozilla-status and status2 lines. MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); // need to rewrite the headers up to and including the x-mozilla-status2 header m_fileStream->Write(m_dataBuffer, blockOffset, &writeCount); } // we should write out the existing keywords from the msg hdr, if any. if (msgHdrKeywords.IsEmpty()) { // no keywords, so write blank header m_fileStream->Write(X_MOZILLA_KEYWORDS, sizeof(X_MOZILLA_KEYWORDS) - 1, &bytesWritten); m_addedHeaderSize += bytesWritten; } else { if (msgHdrKeywords.Length() < sizeof(X_MOZILLA_KEYWORDS) - sizeof(HEADER_X_MOZILLA_KEYWORDS) + 10 /* allow some slop */) { // keywords fit in normal blank header, so replace blanks in keyword hdr with keywords nsCAutoString keywordsHdr(X_MOZILLA_KEYWORDS); keywordsHdr.Replace(sizeof(HEADER_X_MOZILLA_KEYWORDS) + 1, msgHdrKeywords.Length(), msgHdrKeywords); m_fileStream->Write(keywordsHdr.get(), keywordsHdr.Length(), &bytesWritten); m_addedHeaderSize += bytesWritten; } else { // keywords don't fit, so write out keywords on one line and an extra blank line nsCString newKeywordHeader(HEADER_X_MOZILLA_KEYWORDS ": "); newKeywordHeader.Append(msgHdrKeywords); newKeywordHeader.Append(MSG_LINEBREAK EXTRA_KEYWORD_HDR); m_fileStream->Write(newKeywordHeader.get(), newKeywordHeader.Length(), &bytesWritten); m_addedHeaderSize += bytesWritten; } } addKeywordHdr = false; } else if (needToGrowKeywords) { blockOffset = statusOffset; if (!strncmp(m_dataBuffer + blockOffset, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN)) MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); // skip x-mozilla-status hdr if (!strncmp(m_dataBuffer + blockOffset, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN)) MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); // skip x-mozilla-status2 hdr PRUint32 preKeywordBlockOffset = blockOffset; if (!strncmp(m_dataBuffer + blockOffset, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1)) { do { // skip x-mozilla-keywords hdr and any existing continuation headers MsgAdvanceToNextLine(m_dataBuffer, blockOffset, readCount); } while (m_dataBuffer[blockOffset] == ' '); } PRInt32 oldKeywordSize = blockOffset - preKeywordBlockOffset; // rewrite the headers up to and including the x-mozilla-status2 header m_fileStream->Write(m_dataBuffer, preKeywordBlockOffset, &writeCount); // let's just rewrite all the keywords on several lines and add a blank line, // instead of worrying about which are missing. bool done = false; nsCAutoString keywordHdr(HEADER_X_MOZILLA_KEYWORDS ": "); PRInt32 nextBlankOffset = 0; PRInt32 curHdrLineStart = 0; PRInt32 newKeywordSize = 0; while (!done) { nextBlankOffset = msgHdrKeywords.FindChar(' ', nextBlankOffset); if (nextBlankOffset == kNotFound) { nextBlankOffset = msgHdrKeywords.Length(); done = true; } if (nextBlankOffset - curHdrLineStart > 90 || done) { keywordHdr.Append(nsDependentCSubstring(msgHdrKeywords, curHdrLineStart, msgHdrKeywords.Length() - curHdrLineStart)); keywordHdr.Append(MSG_LINEBREAK); m_fileStream->Write(keywordHdr.get(), keywordHdr.Length(), &bytesWritten); newKeywordSize += bytesWritten; curHdrLineStart = nextBlankOffset; keywordHdr.Assign(' '); } nextBlankOffset++; } m_fileStream->Write(EXTRA_KEYWORD_HDR, sizeof(EXTRA_KEYWORD_HDR) - 1, &bytesWritten); newKeywordSize += bytesWritten; m_addedHeaderSize += newKeywordSize - oldKeywordSize; m_curSrcHdr->SetUint32Property("growKeywords", 0); needToGrowKeywords = false; writeCount += blockOffset - preKeywordBlockOffset; // fudge writeCount } if (readCount <= blockOffset) { NS_ASSERTION(false, "bad block offset"); // not sure what to do to handle this. } m_fileStream->Write(m_dataBuffer + blockOffset, readCount - blockOffset, &bytesWritten); writeCount += bytesWritten; count -= readCount; if (writeCount != readCount) { m_folder->ThrowAlertMsg("compactFolderWriteFailed", m_window); return NS_MSG_ERROR_WRITING_MAIL_FOLDER; } } } return rv; }
nsresult nsEudoraMailbox::ReadNextMessage(ReadFileState *pState, SimpleBufferTonyRCopiedOnce& copy, SimpleBufferTonyRCopiedOnce& header, SimpleBufferTonyRCopiedOnce& body, nsCString& defaultDate, nsCString& bodyType, EudoraTOCEntry *pTocEntry) { header.m_writeOffset = 0; body.m_writeOffset = 0; nsresult rv; int32_t lineLen; char endBuffer = 0; lineLen = -1; // Find the from separator - we should actually be positioned at the // from separator, but for now, we'll verify this. while (lineLen == -1) { if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error, FillMailBuffer FAILED in ReadNextMessage\n"); return rv; } lineLen = IsEudoraFromSeparator(copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, defaultDate); if (lineLen == -1) { while ((lineLen = FindStartLine(copy)) == -1) { copy.m_writeOffset = copy.m_bytesInBuf; if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error, FillMailBuffer FAILED in ReadNextMessage, looking for next start line\n"); return rv; } if (!copy.m_bytesInBuf) { IMPORT_LOG0("*** Error, ReadNextMessage, looking for start of next line, got end of file.\n"); return NS_ERROR_FAILURE; } } copy.m_writeOffset += lineLen; lineLen = -1; } } // Skip past the from line separator while ((lineLen = FindStartLine(copy)) == -1) { copy.m_writeOffset = copy.m_bytesInBuf; if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error, ReadNextMessage, FillMailBuffer failed looking for from sep\n"); return rv; } if (!copy.m_bytesInBuf) { IMPORT_LOG0("*** Error, ReadNextMessage, end of file looking for from sep\n"); return NS_ERROR_FAILURE; } } copy.m_writeOffset += lineLen; if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error, Unable to fill mail buffer after from sep.\n"); return rv; } // This should be the headers... int32_t endLen = -1; while ((endLen = IsEndHeaders(copy)) == -1) { while ((lineLen = FindNextEndLine(copy)) == -1) { copy.m_writeOffset = copy.m_bytesInBuf; if (!header.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** ERROR, writing headers\n"); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading message headers\n"); return rv; } if (!copy.m_bytesInBuf) { IMPORT_LOG0("*** Error, end of file while reading headers\n"); return NS_ERROR_FAILURE; } } copy.m_writeOffset += lineLen; if ((copy.m_writeOffset + 4) >= copy.m_bytesInBuf) { if (!header.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** ERROR, writing headers 2\n"); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading message headers 2\n"); return rv; } } } if (!header.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** Error writing final headers\n"); return NS_ERROR_FAILURE; } if (pTocEntry) { // This is not the prettiest spot to stick this code, but it works and it was convenient. char header_str[128]; // Write X-Mozilla-Status header PR_snprintf(header_str, 128, MSG_LINEBREAK X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, pTocEntry->GetMozillaStatusFlags()); header.Write(header_str, strlen(header_str)); // Write X-Mozilla-Status2 header PR_snprintf(header_str, 128, X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, pTocEntry->GetMozillaStatus2Flags()); header.Write(header_str, strlen(header_str)); // Format and write X-Mozilla-Keys header nsCString keywordHdr(X_MOZILLA_KEYWORDS); if (pTocEntry->HasEudoraLabel()) { PR_snprintf(header_str, 128, "eudoralabel%d", pTocEntry->GetLabelNumber()); keywordHdr.Replace(sizeof(HEADER_X_MOZILLA_KEYWORDS) + 1, strlen(header_str), header_str); } header.Write(keywordHdr.get(), keywordHdr.Length()); } if (!header.Write(&endBuffer, 1)) { IMPORT_LOG0("*** Error writing header trailing null\n"); return NS_ERROR_FAILURE; } copy.m_writeOffset += endLen; if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading beginning of message body\n"); return rv; } EmptyAttachments(); // Get the body! // Read one line at a time here and look for the next separator nsCString tmp; bool insideEudoraTags = false; // by default we consider the body text to be plain text bodyType = "text/plain"; while ((lineLen = IsEudoraFromSeparator(copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, tmp)) == -1) { int32_t tagLength = 0; if (IsEudoraTag (copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, insideEudoraTags, bodyType, tagLength)) { // We don't want to keep eudora tags so skip over them. // let's write the previous text if (!body.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** Error writing to message body\n"); return NS_ERROR_FAILURE; } // we want to skip over the tag...for now we are assuming the tag is always at the start of line. copy.m_writeOffset += tagLength; if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading message body\n"); return rv; } if (!copy.m_bytesInBuf) break; continue; } // Eudora Attachment lines are always outside Eudora Tags // so we shouldn't try to find one here if (!insideEudoraTags) { // Debatable is whether or not to exclude these lines from the // text of the message, I prefer not to in case the original // attachment is actually missing. rv = ExamineAttachment(copy); if (NS_FAILED(rv)) { IMPORT_LOG0("*** Error examining attachment line\n"); return rv; } } while (((lineLen = FindStartLine(copy)) == -1) && copy.m_bytesInBuf) { copy.m_writeOffset = copy.m_bytesInBuf; if (!body.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** Error writing to message body\n"); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading message body\n"); return rv; } } if (!copy.m_bytesInBuf) break; copy.m_writeOffset += lineLen; // found the start of the next line // make sure it's long enough to check for the from line if ((copy.m_writeOffset + 2048) >= copy.m_bytesInBuf) { if (!body.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** Error writing to message body 2\n"); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error reading message body 2\n"); return rv; } } } // the start of the current line is a from, we-re done if (!body.Write(copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0("*** Error writing final message body\n"); return NS_ERROR_FAILURE; } if (!body.Write(&endBuffer, 1)) { IMPORT_LOG0("*** Error writing body trailing null\n"); IMPORT_LOG2("\tbody.m_size: %ld, body.m_writeOffset: %ld\n", body.m_size, body.m_writeOffset); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = FillMailBuffer(pState, copy))) { IMPORT_LOG0("*** Error filling mail buffer for next read message\n"); return rv; } return NS_OK; }