// Walk through all the messages in this folder and look for any
// PARTIAL messages. For each of those, dig thru the mailbox and
// find the Account that the message belongs to. If that Account
// matches the current Account, then look for the Uidl and save
// this message for later processing.
nsresult
nsPop3Sink::FindPartialMessages()
{
  nsCOMPtr<nsISimpleEnumerator> messages;
  bool hasMore = false;
  bool isOpen = false;
  nsLocalFolderScanState folderScanState;
  nsCOMPtr<nsIMsgDatabase> db;
  nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
  m_folder->GetMsgDatabase(getter_AddRefs(db));
  if (!localFolder || !db)
    return NS_ERROR_FAILURE;  // we need it to grub thru the folder

  nsresult rv = db->EnumerateMessages(getter_AddRefs(messages));
  if (messages)
    messages->HasMoreElements(&hasMore);
  while(hasMore && NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsISupports> aSupport;
    uint32_t flags = 0;
    rv = messages->GetNext(getter_AddRefs(aSupport));
    nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryInterface(aSupport, &rv));
    msgDBHdr->GetFlags(&flags);
    if (flags & nsMsgMessageFlags::Partial)
    {
      // Open the various streams we need to seek and read from the mailbox
      if (!isOpen)
      {
        rv = localFolder->GetFolderScanState(&folderScanState);
        if (NS_SUCCEEDED(rv))
          isOpen = true;
        else
          break;
      }
      rv = localFolder->GetUidlFromFolder(&folderScanState, msgDBHdr);
      if (!NS_SUCCEEDED(rv))
        break;

      // If we got the uidl, see if this partial message belongs to this
      // account. Add it to the array if so...
      if (folderScanState.m_uidl && 
          m_accountKey.Equals(folderScanState.m_accountKey, nsCaseInsensitiveCStringComparator()))
      {
        partialRecord *partialMsg = new partialRecord();
        if (partialMsg)
        {
          partialMsg->m_uidl = folderScanState.m_uidl;
          partialMsg->m_msgDBHdr = msgDBHdr;
          m_partialMsgsArray.AppendElement(partialMsg);
        }
      }
    }
    messages->HasMoreElements(&hasMore);
  }
  if (isOpen && folderScanState.m_inputStream)
    folderScanState.m_inputStream->Close();
  return rv;
}
NS_IMETHODIMP
nsMsgMaildirStore::CopyMessages(bool aIsMove, nsIArray *aHdrArray,
                               nsIMsgFolder *aDstFolder,
                               nsIMsgCopyServiceListener *aListener,
                               nsITransaction **aUndoAction,
                               bool *aCopyDone)
{
  NS_ENSURE_ARG_POINTER(aHdrArray);
  NS_ENSURE_ARG_POINTER(aDstFolder);
  NS_ENSURE_ARG_POINTER(aCopyDone);
  NS_ENSURE_ARG_POINTER(aUndoAction);
  uint32_t messageCount;
  nsresult rv = aHdrArray->GetLength(&messageCount);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIMsgFolder> srcFolder;
  nsCOMPtr<nsIFile> destFolderPath;
  nsCOMPtr<nsIMsgDatabase> destDB;
  nsCOMPtr<nsIMsgDatabase> srcDB;
  aDstFolder->GetMsgDatabase(getter_AddRefs(destDB));
  aDstFolder->GetFilePath(getter_AddRefs(destFolderPath));
  destFolderPath->Append(NS_LITERAL_STRING("cur"));

  nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = msgHdr->GetFolder(getter_AddRefs(srcFolder));
  NS_ENSURE_SUCCESS(rv, rv);
  srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
  nsRefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
  NS_ENSURE_TRUE(msgTxn, NS_ERROR_OUT_OF_MEMORY);
  if (NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove)))
  {
    if (aIsMove)
      msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
    else
      msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
  }

  nsCOMPtr<nsIMutableArray> dstHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  for (uint32_t i = 0; i < messageCount; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
    if (NS_FAILED(rv))
      continue;
    nsMsgKey srcKey;
    msgHdr->GetMessageKey(&srcKey);
    msgTxn->AddSrcKey(srcKey);
    msgHdr->GetFolder(getter_AddRefs(srcFolder));
    nsCOMPtr<nsIFile> path;
    rv = srcFolder->GetFilePath(getter_AddRefs(path));
    NS_ENSURE_SUCCESS(rv, rv);
    nsAutoCString fileName;
    msgHdr->GetStringProperty("storeToken", getter_Copies(fileName));
    if (fileName.IsEmpty())
      return NS_ERROR_FAILURE;

    if (fileName.IsEmpty())
    {
      PR_LOG(MailDirLog, PR_LOG_ALWAYS,
             ("GetMsgInputStream - empty storeToken!!\n"));
      return NS_ERROR_FAILURE;
    }

    path->Append(NS_LITERAL_STRING("cur"));
    path->AppendNative(fileName);

    nsCOMPtr<nsIFile> destFile;
    destFolderPath->Clone(getter_AddRefs(destFile));
    destFile->AppendNative(fileName);
    bool exists;
    destFile->Exists(&exists);
    if (exists)
    {
      rv = destFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
      NS_ENSURE_SUCCESS(rv, rv);
      destFile->GetNativeLeafName(fileName);
    }
    if (aIsMove)
      path->MoveToNative(destFolderPath, fileName);
    else
      path->CopyToNative(destFolderPath, fileName);

    nsCOMPtr<nsIMsgDBHdr> destHdr;
    if (destDB)
    {
      rv = destDB->CopyHdrFromExistingHdr(nsMsgKey_None, msgHdr, true, getter_AddRefs(destHdr));
      NS_ENSURE_SUCCESS(rv, rv);
      destHdr->SetStringProperty("storeToken", fileName.get());
      dstHdrs->AppendElement(destHdr, false);
      nsMsgKey dstKey;
      destHdr->GetMessageKey(&dstKey);
      msgTxn->AddDstKey(dstKey);
    }
  }
  nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
  if (notifier)
    notifier->NotifyMsgsMoveCopyCompleted(aIsMove, aHdrArray, aDstFolder,
                                          dstHdrs);
  if (aIsMove)
  {
    for (uint32_t i = 0; i < messageCount; ++i)
    {
      nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryElementAt(aHdrArray, i, &rv));
      rv = srcDB->DeleteHeader(msgDBHdr, nullptr, false, true);
    }
  }
  *aCopyDone = true;
  nsCOMPtr<nsISupports> srcSupports(do_QueryInterface(srcFolder));
  nsCOMPtr<nsIMsgLocalMailFolder> localDest(do_QueryInterface(aDstFolder));
  if (localDest)
    localDest->OnCopyCompleted(srcSupports, true);
  if (aListener)
    aListener->OnStopCopy(NS_OK);
  msgTxn.forget(aUndoAction);
  return NS_OK;
}
NS_IMETHODIMP
nsMsgMaildirStore::CopyMessages(bool aIsMove, nsIArray *aHdrArray,
                               nsIMsgFolder *aDstFolder,
                               nsIMsgCopyServiceListener *aListener,
                               nsITransaction **aUndoAction,
                               bool *aCopyDone)
{
  NS_ENSURE_ARG_POINTER(aHdrArray);
  NS_ENSURE_ARG_POINTER(aDstFolder);
  NS_ENSURE_ARG_POINTER(aCopyDone);
  NS_ENSURE_ARG_POINTER(aUndoAction);

  *aCopyDone = false;

  nsCOMPtr<nsIMsgFolder> srcFolder;
  nsresult rv;
  nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = msgHdr->GetFolder(getter_AddRefs(srcFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  // Both source and destination folders must use maildir type store.
  nsCOMPtr<nsIMsgPluggableStore> srcStore;
  nsAutoCString srcType;
  srcFolder->GetMsgStore(getter_AddRefs(srcStore));
  if (srcStore)
    srcStore->GetStoreType(srcType);
  nsCOMPtr<nsIMsgPluggableStore> dstStore;
  nsAutoCString dstType;
  aDstFolder->GetMsgStore(getter_AddRefs(dstStore));
  if (dstStore)
    dstStore->GetStoreType(dstType);
  if (!srcType.EqualsLiteral("maildir") || !dstType.EqualsLiteral("maildir"))
    return NS_OK;

  // Both source and destination must be local folders. In theory we could
  //   do efficient copies of the offline store of IMAP, but this is not
  //   supported yet. For that, we need to deal with both correct handling
  //   of deletes from the src server, and msgKey = UIDL in the dst folder.
  nsCOMPtr<nsIMsgLocalMailFolder> destLocalFolder(do_QueryInterface(aDstFolder));
  if (!destLocalFolder)
    return NS_OK;
  nsCOMPtr<nsIMsgLocalMailFolder> srcLocalFolder(do_QueryInterface(srcFolder));
  if (!srcLocalFolder)
    return NS_OK;

  // We should be able to use a file move for an efficient copy.

  nsCOMPtr<nsIFile> destFolderPath;
  nsCOMPtr<nsIMsgDatabase> destDB;
  aDstFolder->GetMsgDatabase(getter_AddRefs(destDB));
  rv = aDstFolder->GetFilePath(getter_AddRefs(destFolderPath));
  NS_ENSURE_SUCCESS(rv, rv);
  destFolderPath->Append(NS_LITERAL_STRING("cur"));

  nsCOMPtr<nsIFile> srcFolderPath;
  rv = srcFolder->GetFilePath(getter_AddRefs(srcFolderPath));
  NS_ENSURE_SUCCESS(rv, rv);
  srcFolderPath->Append(NS_LITERAL_STRING("cur"));

  nsCOMPtr<nsIMsgDatabase> srcDB;
  srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
  nsRefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
  NS_ENSURE_TRUE(msgTxn, NS_ERROR_OUT_OF_MEMORY);
  if (NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove)))
  {
    if (aIsMove)
      msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
    else
      msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
  }

  if (aListener)
    aListener->OnStartCopy();

  nsCOMPtr<nsIMutableArray> dstHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  uint32_t messageCount;
  rv = aHdrArray->GetLength(&messageCount);
  NS_ENSURE_SUCCESS(rv, rv);

  for (uint32_t i = 0; i < messageCount; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> srcHdr = do_QueryElementAt(aHdrArray, i, &rv);
    if (NS_FAILED(rv))
    {
      PR_LOG(MailDirLog, PR_LOG_ALWAYS,
             ("srcHdr null\n"));
      continue;
    }
    nsMsgKey srcKey;
    srcHdr->GetMessageKey(&srcKey);
    msgTxn->AddSrcKey(srcKey);
    nsAutoCString fileName;
    msgHdr->GetStringProperty("storeToken", getter_Copies(fileName));
    if (fileName.IsEmpty())
    {
      PR_LOG(MailDirLog, PR_LOG_ALWAYS,
             ("GetMsgInputStream - empty storeToken!!\n"));
      return NS_ERROR_FAILURE;
    }

    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;
    destFile->Exists(&exists);
    if (exists)
    {
      rv = destFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
      NS_ENSURE_SUCCESS(rv, rv);
      destFile->GetNativeLeafName(fileName);
    }
    if (aIsMove)
      rv = srcFile->MoveToNative(destFolderPath, fileName);
    else
      rv = srcFile->CopyToNative(destFolderPath, fileName);
    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());
      dstHdrs->AppendElement(destHdr, false);
      nsMsgKey dstKey;
      destHdr->GetMessageKey(&dstKey);
      msgTxn->AddDstKey(dstKey);
      if (aListener)
        aListener->SetMessageKey(dstKey);
    }
  }
  nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
  if (notifier)
    notifier->NotifyMsgsMoveCopyCompleted(aIsMove, aHdrArray, aDstFolder,
                                          dstHdrs);

  // For now, we only support local dest folders, and for those we are done and
  // can delete the messages. Perhaps this should be moved into the folder
  // when we try to support other folder types.
  if (aIsMove)
  {
    for (uint32_t i = 0; i < messageCount; ++i)
    {
      nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryElementAt(aHdrArray, i, &rv));
      rv = srcDB->DeleteHeader(msgDBHdr, nullptr, false, true);
    }
  }

  *aCopyDone = true;
  nsCOMPtr<nsISupports> srcSupports(do_QueryInterface(srcFolder));
  if (destLocalFolder)
    destLocalFolder->OnCopyCompleted(srcSupports, true);
  if (aListener)
    aListener->OnStopCopy(NS_OK);
  msgTxn.forget(aUndoAction);
  return NS_OK;
}