/* -*- Mode: C++; tab-width: 4; 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 "nsOEScanBoxes.h"
#include "nsMsgUtils.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIImportService.h"
#include "nsIFile.h"
#include "nsIImportMailboxDescriptor.h"
#include "nsOERegUtil.h"
#include "nsOE5File.h"
#include "nsNetUtil.h"
#include "OEDebugLog.h"
#include "nsIInputStream.h"
#include "nsISeekableStream.h"
#include "plstr.h"
#include <windows.h>
#include "nsIWindowsRegKey.h"

#ifdef MOZILLA_INTERNAL_API
#include "nsNativeCharsetUtils.h"
#else
#include "nsMsgI18N.h"
#define NS_CopyNativeToUnicode(source, dest) \
        nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest)
#define NS_CopyUnicodeToNative(source, dest) \
        nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), source, dest)
#endif

/*
  .nch file format???

  offset 20 - long = offset to first record

*/

static NS_DEFINE_IID(kISupportsIID,      NS_ISUPPORTS_IID);

nsOEScanBoxes::nsOEScanBoxes()
{
  m_pFirst = nullptr;
}

nsOEScanBoxes::~nsOEScanBoxes()
{
  int i, max;
  MailboxEntry *pEntry;
  for (i = 0, max = m_entryArray.Length(); i < max; i++) {
    pEntry = m_entryArray.ElementAt(i);
    delete pEntry;
  }
  // Now free the unprocessed child entries (ie, those without parents for some reason).
  for (i = 0, max = m_pendingChildArray.Length(); i < max; i++)
  {
    pEntry = m_pendingChildArray.ElementAt(i);
    if (!pEntry->processed)
      delete pEntry;
  }
}

/*
 3.x & 4.x registry
  Software/Microsoft/Outlook Express/

 5.0 registry
  Identies - value of "Default User ID" is {GUID}
  Identities/{GUID}/Software/Microsoft/Outlook Express/5.0/
*/

bool nsOEScanBoxes::Find50Mail(nsIFile *pWhere)
{
  nsAutoString userId;
  nsresult rv = nsOERegUtil::GetDefaultUserId(userId);
  NS_ENSURE_SUCCESS(rv, false);

  nsAutoString path(NS_LITERAL_STRING("Identities\\"));
  path.Append(userId);
  path.AppendLiteral("\\Software\\Microsoft\\Outlook Express\\5.0");

  nsCOMPtr<nsIWindowsRegKey> key =
    do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
  NS_ENSURE_SUCCESS(rv, false);

  rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                 path,
                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
  NS_ENSURE_SUCCESS(rv, false);

  nsAutoString storeRoot;
  key->ReadStringValue(NS_LITERAL_STRING("Store Root"), storeRoot);
  NS_ENSURE_SUCCESS(rv, false);

  nsCOMPtr<nsIFile> localWhere = do_QueryInterface(pWhere);
  localWhere->InitWithPath(storeRoot);

  nsAutoCString nativeStoreRoot;
  NS_CopyUnicodeToNative(storeRoot, nativeStoreRoot);
  IMPORT_LOG1("Setting native path: %s\n", nativeStoreRoot.get());

  bool isDir = false;
  rv = localWhere->IsDirectory(&isDir);
  return isDir;
}

bool nsOEScanBoxes::FindMail(nsIFile *pWhere)
{
  if (Find50Mail(pWhere))
    return true;

  nsresult rv;
  nsCOMPtr<nsIWindowsRegKey> key =
    do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
  NS_ENSURE_SUCCESS(rv, false);

  rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                 NS_LITERAL_STRING("Software\\Microsoft\\Outlook Express"),
                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
  NS_ENSURE_SUCCESS(rv, false);

  nsAutoString storeRoot;
  key->ReadStringValue(NS_LITERAL_STRING("Store Root"), storeRoot);
  NS_ENSURE_SUCCESS(rv, false);

  nsCOMPtr<nsIFile> localWhere = do_QueryInterface(pWhere);
  localWhere->InitWithPath(storeRoot);
  localWhere->AppendNative(NS_LITERAL_CSTRING("Mail"));

  bool isDir = false;
  localWhere->IsDirectory(&isDir);

  return isDir;
}

bool nsOEScanBoxes::GetMailboxes(nsIFile *pWhere, nsIArray **pArray)
{
  nsCString path;
  pWhere->GetNativePath(path);
  if (!path.IsEmpty()) {
    IMPORT_LOG1("Looking for mail in: %s\n", path.get());
  }
  else {
    pWhere->GetNativeLeafName(path);
    if (!path.IsEmpty())
      IMPORT_LOG1("Looking for mail in: %s\n", path.get());
    else
      IMPORT_LOG0("Unable to get info about where to look for mail\n");
  }

  nsCOMPtr <nsIFile> location;
  pWhere->Clone(getter_AddRefs(location));
  // 1. Look for 5.0 folders.dbx
  // 2. Look for 3.x & 4.x folders.nch
  // 3. Look for 5.0 *.dbx mailboxes
  // 4. Look for 3.x & 4.x *.mbx mailboxes

  bool    result;

  location->AppendNative(NS_LITERAL_CSTRING("folders.dbx"));
  if (Find50MailBoxes(location)) {
    result = GetMailboxList(pWhere, pArray);
  }
  else {
    // 2. Look for 4.x mailboxes
    pWhere->Clone(getter_AddRefs(location));
    location->AppendNative(NS_LITERAL_CSTRING("folders.nch"));

    if (FindMailBoxes(location)) {
      result = GetMailboxList(pWhere, pArray);
    }
    else {
      // 3 & 4, look for the specific mailbox files.
      pWhere->Clone(getter_AddRefs(location));
      ScanMailboxDir(location);
      result = GetMailboxList(pWhere, pArray);
    }
  }

  return result;
}



void nsOEScanBoxes::Reset(void)
{
  int max = m_entryArray.Length();
  for (int i = 0; i < max; i++) {
    MailboxEntry *pEntry = m_entryArray.ElementAt(i);
    delete pEntry;
  }
  m_entryArray.Clear();
  m_pFirst = nullptr;
}


bool nsOEScanBoxes::FindMailBoxes(nsIFile* descFile)
{
  Reset();

  nsresult  rv;
  bool      isFile = false;

  rv = descFile->IsFile(&isFile);
  if (NS_FAILED(rv) || !isFile)
    return false;

  nsCOMPtr <nsIInputStream> descInputStream;
  rv = NS_NewLocalFileInputStream(getter_AddRefs(descInputStream), descFile);
  NS_ENSURE_SUCCESS(rv, false);

  IMPORT_LOG0("Reading the folders.nch file\n");

  uint32_t    curRec;
  if (!ReadLong(descInputStream, curRec, 20)) {
    return false;
  }

  // Now for each record
  bool        done = false;
  uint32_t    equal;
  uint32_t    size;
  uint32_t    previous;
  uint32_t    next;
  MailboxEntry *  pEntry;
  bool        failed;

  while (!done) {
    if (!ReadLong(descInputStream, equal, curRec)) return false;
    if (curRec != equal) {
      IMPORT_LOG1("Record start invalid: %ld\n", curRec);
      break;
    }
    if (!ReadLong(descInputStream, size, curRec + 4)) return false;
    if (!ReadLong(descInputStream, previous, curRec + 8)) return false;
    if (!ReadLong(descInputStream, next, curRec + 12)) return false;
    failed = false;
    pEntry = new MailboxEntry;
    if (!ReadLong(descInputStream, pEntry->index, curRec + 16)) failed = true;
    if (!ReadString(descInputStream, pEntry->mailName, curRec + 20)) failed = true;
    if (!ReadString(descInputStream, pEntry->fileName, curRec + 279)) failed = true;
    if (!ReadLong(descInputStream, pEntry->parent, curRec + 539)) failed = true;
    if (!ReadLong(descInputStream, pEntry->child, curRec + 543)) failed = true;
    if (!ReadLong(descInputStream, pEntry->sibling, curRec + 547)) failed = true;
    if (!ReadLong(descInputStream, pEntry->type, curRec + 551)) failed = true;
    if (failed) {
      delete pEntry;
      return false;
    }

    #ifdef _TRACE_MAILBOX_ENTRIES
    IMPORT_LOG0("------------\n");
    IMPORT_LOG2("    Offset: %lx, index: %ld\n", curRec, pEntry->index);
    IMPORT_LOG2("      previous: %lx, next: %lx\n", previous, next);
    IMPORT_LOG2("      Name: %S, File: %s\n", (char16_t *) pEntry->mailName, (const char *) pEntry->fileName);
    IMPORT_LOG3("      Parent: %ld, Child: %ld, Sibling: %ld\n", pEntry->parent, pEntry->child, pEntry->sibling);
    #endif

    if (!StringEndsWith(pEntry->fileName, NS_LITERAL_CSTRING(".mbx")))
      pEntry->fileName.Append(".mbx");

    m_entryArray.AppendElement(pEntry);

    curRec = next;
    if (!next)
      done = true;
  }

  MailboxEntry *pZero = GetIndexEntry(0);
  if (pZero)
    m_pFirst = GetIndexEntry(pZero->child);

  IMPORT_LOG1("Read the folders.nch file, found %ld mailboxes\n", (long) m_entryArray.Length());

  return true;
}

bool nsOEScanBoxes::Find50MailBoxes(nsIFile* descFile)
{
  Reset();

  nsresult  rv;
  bool      isFile = false;

  rv = descFile->IsFile(&isFile);
  if (NS_FAILED(rv) || !isFile)
    return false;

  nsCOMPtr <nsIInputStream> descInputStream;
  rv = NS_NewLocalFileInputStream(getter_AddRefs(descInputStream), descFile);
  NS_ENSURE_SUCCESS(rv, false);

  IMPORT_LOG0("Reading the folders.dbx file\n");

  uint32_t *pIndex;
  uint32_t indexSize = 0;
  if (!nsOE5File::ReadIndex(descInputStream, &pIndex, &indexSize)) {
    IMPORT_LOG0("*** NOT USING FOLDERS.DBX!!!\n");
    return false;
  }

  uint32_t  marker;
  uint32_t  size;
  char      *pBytes;
  uint32_t   cntRead;
  int32_t    recordId;
  int32_t    strOffset;

  uint8_t    tag;
  uint32_t   data;
  int32_t    dataOffset;

  uint32_t    id;
  uint32_t    parent;
  uint32_t    numMessages;
  char *      pFileName;
  char *      pDataSource;

  MailboxEntry *  pEntry;
  MailboxEntry *  pLastEntry = nullptr;

  uint32_t  localStoreId = 0;

  for (uint32_t i = 0; i < indexSize; i++) {
    if (!ReadLong(descInputStream, marker, pIndex[i])) continue;
    if (marker != pIndex[i]) continue;
    if (!ReadLong(descInputStream, size, pIndex[i] + 4)) continue;
    size += 4;
    pBytes = new char[size];
    rv = descInputStream->Read(pBytes, size, &cntRead);
    if (NS_FAILED(rv) || ((uint32_t)cntRead != size)) {
      delete [] pBytes;
      continue;
    }
    recordId = pBytes[2];
    strOffset = (recordId * 4) + 4;
    if (recordId == 4)
      strOffset += 4;

    id = 0;
    parent = 0;
    numMessages = 0;
    pFileName = nullptr;
    pDataSource = nullptr;
    dataOffset = 4;
    while (dataOffset < strOffset) {
      tag = (uint8_t) pBytes[dataOffset];

      data = 0; // make sure all bytes are 0 before copying 3 bytes over.
      memcpy(&data, &(pBytes[dataOffset + 1]), 3);
      switch(tag) {
        case 0x80: // id record
          id = data;
        break;
        case 0x81:  // parent id
          parent = data;
        break;
        case 0x87:  // number of messages in this mailbox
          numMessages = data;
        break;
        case 0x03:  // file name for this mailbox
          if (((uint32_t)strOffset + data) < size)
            pFileName = (char *)(pBytes + strOffset + data);
        break;
        case 0x05:  // data source for this record (this is not a mailbox!)
          if (((uint32_t)strOffset + data) < size)
            pDataSource = (char *) (pBytes + strOffset + data);
        break;
      }
      dataOffset += 4;
    }

    // now build an entry if necessary!
    if (pDataSource) {
      if (!PL_strcasecmp(pDataSource, "LocalStore"))
      {
        localStoreId = id;
        // See if we have any child folders that need to be added/processed for this top level parent.
        ProcessPendingChildEntries(localStoreId, localStoreId, m_pendingChildArray);
        // Clean up the pending list.
        RemoveProcessedChildEntries();
      }
    }
    else if (id && localStoreId && parent) {
      // veryify that this mailbox is in the local store
      data = parent;
      while (data && (data != localStoreId)) {
        pEntry = GetIndexEntry(data);
        if (pEntry)
          data = pEntry->parent;
        else
          data = 0;
      }
      if (data == localStoreId) {
        // Create an entry for this bugger
        pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName);
        if (pEntry)
        {
          AddChildEntry(pEntry, localStoreId);
          pEntry->processed =  true;
          // See if we have any child folders that need to be added/processed.
          ProcessPendingChildEntries(id, localStoreId, m_pendingChildArray);
          // Clean up the pending list.
          RemoveProcessedChildEntries();
        }
      }
      else
      {
        // Put this folder into child array and process it when its parent shows up.
        pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName);
        if (pEntry)
          m_pendingChildArray.AppendElement(pEntry);
      }
    }
    else if (pFileName)
    {
      // Put this folder into child array and process it when its parent shows up.
      // For some reason, it's likely that child folders come before their parents.
      pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName);
      if (pEntry)
        m_pendingChildArray.AppendElement(pEntry);
    }

    delete [] pBytes;
  }


  delete [] pIndex;

  return m_entryArray.Length();
}

nsOEScanBoxes::MailboxEntry *nsOEScanBoxes::NewMailboxEntry(uint32_t id, uint32_t parent, const char *prettyName, char *pFileName)
{
  MailboxEntry *pEntry = new MailboxEntry();
  if (!pEntry)
    return nullptr;

  pEntry->index = id;
  pEntry->parent = parent;
  pEntry->child = 0;
  pEntry->type = 0;
  pEntry->sibling = -1;
  pEntry->processed =  false;
  NS_CopyNativeToUnicode(nsDependentCString(prettyName), pEntry->mailName);
  if (pFileName)
    pEntry->fileName = pFileName;
  return pEntry;
}

void nsOEScanBoxes::ProcessPendingChildEntries(uint32_t parent, uint32_t rootIndex, nsTArray<MailboxEntry*>  &childArray)
{
  int32_t i, max;
  MailboxEntry *pEntry;
  for (i = 0, max = childArray.Length(); i < max; i++)
  {
    pEntry = childArray.ElementAt(i);
    if ((!pEntry->processed) && (pEntry->parent == parent))
    {
      AddChildEntry(pEntry, rootIndex);
      pEntry->processed =  true; // indicate it's been processed.
      // See if there are unprocessed child folders for this child in the
      // array as well (ie, both child and grand-child are on the list).
      ProcessPendingChildEntries(pEntry->index, rootIndex, childArray);
    }
  }
}

void nsOEScanBoxes::RemoveProcessedChildEntries()
{
  // Remove already processed entries from the pending list. Note that these entries are also
  // on 'm_entryArray' list so we don't want to deallocate the space for the entries now.
  MailboxEntry * pEntry;
  int32_t i;
  for (i = m_pendingChildArray.Length()-1; i >= 0; i--)
  {
    pEntry = m_pendingChildArray.ElementAt(i);
    if (pEntry->processed)
      m_pendingChildArray.RemoveElementAt(i);
  }
}

void nsOEScanBoxes::AddChildEntry(MailboxEntry *pEntry, uint32_t rootIndex)
{
  if (!m_pFirst) {
    if (pEntry->parent == rootIndex) {
      m_pFirst = pEntry;
      m_entryArray.AppendElement(pEntry);
    }
    else {
      delete pEntry;
    }
    return;
  }

  MailboxEntry *  pParent = nullptr;
  MailboxEntry *  pSibling = nullptr;
  if (pEntry->parent == rootIndex) {
    pSibling = m_pFirst;
  }
  else {
    pParent = GetIndexEntry(pEntry->parent);
  }

  if (!pParent && !pSibling) {
    delete pEntry;
    return;
  }

  if (pParent && (pParent->child == 0)) {
    pParent->child = pEntry->index;
    m_entryArray.AppendElement(pEntry);
    return;
  }

  if (!pSibling)
    pSibling = GetIndexEntry(pParent->child);

  while (pSibling && (pSibling->sibling != -1)) {
    pSibling = GetIndexEntry(pSibling->sibling);
  }

  if (!pSibling) {
    delete pEntry;
    return;
  }

  pSibling->sibling = pEntry->index;
  m_entryArray.AppendElement(pEntry);
}

bool nsOEScanBoxes::Scan50MailboxDir(nsIFile * srcDir)
{
  Reset();

  MailboxEntry *pEntry;
  int32_t      index = 1;
  char         *pLeaf;

  bool hasMore;
  nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
  nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
  NS_ENSURE_SUCCESS(rv, false);

  directoryEnumerator->HasMoreElements(&hasMore);
  bool              isFile;
  nsCOMPtr<nsIFile> entry;
  nsCString         fName;

  while (hasMore && NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsISupports> aSupport;
    rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport));
    nsCOMPtr<nsIFile> entry(do_QueryInterface(aSupport, &rv));
    directoryEnumerator->HasMoreElements(&hasMore);

    isFile = false;
    rv = entry->IsFile(&isFile);
    if (NS_SUCCEEDED(rv) && isFile) {
      pLeaf = nullptr;
      rv = entry->GetNativeLeafName(fName);
      if (NS_SUCCEEDED(rv)  &&
        (StringEndsWith(fName, NS_LITERAL_CSTRING(".dbx")))) {
          // This is a *.dbx file in the mail directory
          if (nsOE5File::IsLocalMailFile(entry)) {
            pEntry = new MailboxEntry;
            pEntry->index = index;
            index++;
            pEntry->parent = 0;
            pEntry->child = 0;
            pEntry->sibling = index;
            pEntry->type = -1;
            fName.SetLength(fName.Length() - 4);
            pEntry->fileName = fName.get();
            NS_CopyNativeToUnicode(fName, pEntry->mailName);
            m_entryArray.AppendElement(pEntry);
          }
      }
    }
  }

  if (m_entryArray.Length() > 0) {
    pEntry = m_entryArray.ElementAt(m_entryArray.Length() - 1);
    pEntry->sibling = -1;
    return true;
  }

  return false;
}

void nsOEScanBoxes::ScanMailboxDir(nsIFile * srcDir)
{
  if (Scan50MailboxDir(srcDir))
    return;

  Reset();

  MailboxEntry *  pEntry;
  int32_t      index = 1;
  nsAutoCString pLeaf;
  uint32_t    sLen;

  bool hasMore;
  nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
  nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
  NS_ENSURE_SUCCESS_VOID(rv);

  directoryEnumerator->HasMoreElements(&hasMore);
  bool              isFile;
  nsCOMPtr<nsIFile> entry;
  nsCString         fName;
  nsCString         ext;
  nsCString         name;

  while (hasMore && NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsISupports> aSupport;
    rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport));
    nsCOMPtr<nsIFile> entry(do_QueryInterface(aSupport, &rv));
    directoryEnumerator->HasMoreElements(&hasMore);

    isFile = false;
    rv = entry->IsFile(&isFile);
    if (NS_SUCCEEDED(rv) && isFile)
    {
      rv = entry->GetNativeLeafName(pLeaf);
      if (NS_SUCCEEDED(rv) && !pLeaf.IsEmpty() &&
        ((sLen = pLeaf.Length()) > 4) &&
        (!PL_strcasecmp(pLeaf.get() + sLen - 3, "mbx")))
      {
          // This is a *.mbx file in the mail directory
          pEntry = new MailboxEntry;
          pEntry->index = index;
          index++;
          pEntry->parent = 0;
          pEntry->child = 0;
          pEntry->sibling = index;
          pEntry->type = -1;
          pEntry->fileName = pLeaf;
          pLeaf.SetLength(sLen - 4);
          NS_CopyNativeToUnicode(pLeaf, pEntry->mailName);
          m_entryArray.AppendElement(pEntry);
      }
    }
  }

  if (m_entryArray.Length() > 0) {
    pEntry = m_entryArray.ElementAt(m_entryArray.Length() - 1);
    pEntry->sibling = -1;
  }
}

uint32_t nsOEScanBoxes::CountMailboxes(MailboxEntry *pBox)
{
  if (pBox == nullptr) {
    if (m_pFirst != nullptr)
      pBox = m_pFirst;
    else {
      if (m_entryArray.Length() > 0)
        pBox = m_entryArray.ElementAt(0);
    }
  }
  uint32_t    count = 0;

  MailboxEntry *  pChild;
  while (pBox) {
    count++;
    if (pBox->child) {
      pChild = GetIndexEntry(pBox->child);
      if (pChild != nullptr)
        count += CountMailboxes(pChild);
    }
    if (pBox->sibling != -1) {
      pBox = GetIndexEntry(pBox->sibling);
    }
    else
      pBox = nullptr;
  }

  return count;
}

bool nsOEScanBoxes::GetMailboxList(nsIFile * root, nsIArray **pArray)
{
  nsresult rv;
  nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
  if (NS_FAILED(rv)) {
    IMPORT_LOG0("FAILED to allocate the nsIArray\n");
    return false;
  }

  BuildMailboxList(nullptr, root, 1, array);
  array.forget(pArray);

  return true;
}

void nsOEScanBoxes::BuildMailboxList(MailboxEntry *pBox, nsIFile * root, int32_t depth, nsIMutableArray *pArray)
{
  if (pBox == nullptr) {
    if (m_pFirst != nullptr) {
      pBox = m_pFirst;

      IMPORT_LOG0("Assigning start of mailbox list to m_pFirst\n");
    }
    else {
      if (m_entryArray.Length() > 0) {
        pBox = m_entryArray.ElementAt(0);

        IMPORT_LOG0("Assigning start of mailbox list to entry at index 0\n");
      }
    }

    if (pBox == nullptr) {
      IMPORT_LOG0("ERROR ASSIGNING STARTING MAILBOX\n");
    }

  }

  nsresult rv;
  nsCOMPtr<nsIFile> file;
  MailboxEntry *pChild;
  nsIImportMailboxDescriptor *pID;
  nsISupports *pInterface;
  int64_t size;

  nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS_VOID(rv);

  while (pBox) {
    rv = impSvc->CreateNewMailboxDescriptor(&pID);
    if (NS_SUCCEEDED(rv)) {
      pID->SetDepth(depth);
      pID->SetIdentifier(pBox->index);
      pID->SetDisplayName((char16_t *)pBox->mailName.get());
      if (!pBox->fileName.IsEmpty()) {
        pID->GetFile(getter_AddRefs(file));
        file->InitWithFile(root);
        file->AppendNative(pBox->fileName);
        size = 0;
        file->GetFileSize(&size);
        pID->SetSize(size);
      }
      rv = pID->QueryInterface(kISupportsIID, (void **) &pInterface);
      pArray->AppendElement(pInterface, false);
      pInterface->Release();
      pID->Release();
    }

    if (pBox->child) {
      pChild = GetIndexEntry(pBox->child);
      if (pChild != nullptr)
        BuildMailboxList(pChild, root, depth + 1, pArray);
    }
    if (pBox->sibling != -1) {
      pBox = GetIndexEntry(pBox->sibling);
    }
    else
      pBox = nullptr;
  }

}

nsOEScanBoxes::MailboxEntry * nsOEScanBoxes::GetIndexEntry(uint32_t index)
{
  int32_t max = m_entryArray.Length();
  for (int32_t i = 0; i < max; i++) {
    MailboxEntry *pEntry = m_entryArray.ElementAt(i);
    if (pEntry->index == index)
      return pEntry;
  }

  return nullptr;
}


// -------------------------------------------------------
// File utility routines
// -------------------------------------------------------

bool nsOEScanBoxes::ReadLong(nsIInputStream * stream, int32_t& val, uint32_t offset)
{
  nsresult  rv;
  nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(stream, &rv);
  NS_ENSURE_SUCCESS(rv, false);

  rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
  NS_ENSURE_SUCCESS(rv, false);

  uint32_t  cntRead;
  char * pReadTo = (char *)&val;
  rv = stream->Read(pReadTo, sizeof(val), &cntRead);

  return NS_SUCCEEDED(rv) && cntRead == sizeof(val);
}

bool nsOEScanBoxes::ReadLong(nsIInputStream * stream, uint32_t& val, uint32_t offset)
{
  nsresult rv;
  nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(stream, &rv);
  NS_ENSURE_SUCCESS(rv, false);
  rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
  NS_ENSURE_SUCCESS(rv, false);

  uint32_t  cntRead;
  char * pReadTo = (char *)&val;
  rv = stream->Read(pReadTo, sizeof(val), &cntRead);
  if (NS_FAILED(rv) || (cntRead != sizeof(val)))
    return false;

  return true;
}

// It appears as though the strings for file name and mailbox
// name are at least 254 chars - verified - they are probably 255
// but why bother going that far!  If a file name is that long then
// the heck with it.
#define  kOutlookExpressStringLength  252
bool nsOEScanBoxes::ReadString(nsIInputStream * stream, nsString& str, uint32_t offset)
{
  nsresult rv;
  nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(stream, &rv);
  NS_ENSURE_SUCCESS(rv, false);

  rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
  NS_ENSURE_SUCCESS(rv, false);

  uint32_t cntRead;
  char buffer[kOutlookExpressStringLength];
  char *pReadTo = buffer;
  rv = stream->Read(pReadTo, kOutlookExpressStringLength, &cntRead);
  if (NS_FAILED(rv) || (cntRead != kOutlookExpressStringLength))
    return false;

  buffer[kOutlookExpressStringLength - 1] = 0;
  str.AssignASCII(buffer);
  return true;
}

bool nsOEScanBoxes::ReadString(nsIInputStream * stream, nsCString& str, uint32_t offset)
{
  nsresult rv;
  nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(stream, &rv);
  NS_ENSURE_SUCCESS(rv, false);

  rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
  NS_ENSURE_SUCCESS(rv, false);

  uint32_t  cntRead;
  char  buffer[kOutlookExpressStringLength];
  char *pReadTo = buffer;
  rv = stream->Read(pReadTo, kOutlookExpressStringLength, &cntRead);
  if (NS_FAILED(rv) || (cntRead != kOutlookExpressStringLength))
    return false;

  buffer[kOutlookExpressStringLength - 1] = 0;
  str = buffer;
  return true;
}
