Ejemplo n.º 1
0
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;
}