NS_IMETHODIMP nsMsgMaildirStore::ChangeKeywords(nsIArray *aHdrArray,
                                             const nsACString &aKeywords,
                                             bool aAdd)
{
  NS_ENSURE_ARG_POINTER(aHdrArray);
  NS_ENSURE_ARG_POINTER(aHdrArray);
  nsCOMPtr<nsIOutputStream> outputStream;
  nsCOMPtr<nsISeekableStream> seekableStream;

  uint32_t messageCount;
  nsresult rv = aHdrArray->GetLength(&messageCount);
  NS_ENSURE_SUCCESS(rv, rv);
  if (!messageCount)
    return NS_ERROR_INVALID_ARG;

  nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
  NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);

  nsTArray<nsCString> keywordArray;
  ParseString(aKeywords, ' ', keywordArray);

  for (uint32_t i = 0; i < messageCount; ++i) // for each message
  {
    nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aHdrArray, i, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    // get output stream for header
    nsCOMPtr<nsIOutputStream> outputStream;
    rv = GetOutputStream(message, outputStream);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr <nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
    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;

    ChangeKeywordsHelper(message, desiredOffset, lineBuffer, keywordArray,
                         aAdd, outputStream, seekableStream, inputStream);
    if (inputStream)
      inputStream->Close();
    // ### 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;
}
NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeKeywords(nsIArray *aHdrArray,
                                             const nsACString &aKeywords,
                                             bool aAdd)
{
  NS_ENSURE_ARG_POINTER(aHdrArray);
  nsCOMPtr<nsIOutputStream> outputStream;
  nsCOMPtr<nsISeekableStream> seekableStream;
  int64_t restoreStreamPos;

  uint32_t messageCount;
  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);

  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
  NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);

  // For each message, we seek to the beginning of the x-mozilla-status header,
  // and start reading lines, looking for x-mozilla-keys: headers; If we're
  // adding the keyword and we find
  // a header with the desired keyword already in it, we don't need to
  // do anything. Likewise, if removing keyword and we don't find it,
  // we don't need to do anything. Otherwise, if adding, we need to
  // see if there's an x-mozilla-keys
  // header with room for the new keyword. If so, we replace the
  // corresponding number of spaces with the keyword. If no room,
  // we can't do anything until the folder is compacted and another
  // x-mozilla-keys header is added. In that case, we set a property
  // on the header, which the compaction code will check.

  nsTArray<nsCString> keywordArray;
  ParseString(aKeywords, ' ', keywordArray);

  nsCOMPtr<nsIMsgDBHdr> msgHdr;
  for (uint32_t i = 0; i < messageCount; ++i) // for each message
  {
    msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    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);
  }
  lineBuffer = nullptr;
  if (restoreStreamPos != -1)
    seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
  else if (outputStream)
    outputStream->Close();
  if (messageCount > 0)
  {
    msgHdr = do_QueryElementAt(aHdrArray, 0);
    SetDBValid(msgHdr);
  }
  return NS_OK;
}