mailnews/import/oexpress/WabObject.cpp
author Patrick Cloke <clokep@gmail.com>
Thu, 16 Oct 2014 18:05:41 -0400
changeset 20883 56fe4fac57d0916883a373cb7640681631ee0fe0
parent 17259 9eb3e41bab9bff569c7c6b939e593b390c67c287
child 23443 9d472671f16467be95d3b62eb4c0ff399aba8bc7
permissions -rw-r--r--
Backout part of 2f79b0b939ed that's unrelated. a=Standard8

/* -*- 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 <tchar.h>
#include "nscore.h"
#include "nsOE5File.h"
#include "wabobject.h"
#include <algorithm>

enum {
    ieidPR_DISPLAY_NAME = 0,
    ieidPR_ENTRYID,
    ieidPR_OBJECT_TYPE,
    ieidMax
};

static const SizedSPropTagArray(ieidMax, ptaEid)=
{
    ieidMax,
    {
        PR_DISPLAY_NAME,
        PR_ENTRYID,
    PR_OBJECT_TYPE,
    }
};


enum {
    iemailPR_DISPLAY_NAME = 0,
    iemailPR_ENTRYID,
    iemailPR_EMAIL_ADDRESS,
    iemailPR_OBJECT_TYPE,
    iemailMax
};
static const SizedSPropTagArray(iemailMax, ptaEmail)=
{
    iemailMax,
    {
        PR_DISPLAY_NAME,
        PR_ENTRYID,
        PR_EMAIL_ADDRESS,
        PR_OBJECT_TYPE
    }
};

typedef struct {
  bool      multiLine;
  ULONG    tag;
  char *    pLDIF;
} AddrImportField;

#define  kExtraUserFields  10
AddrImportField    extraUserFields[kExtraUserFields] = {
  {true, PR_COMMENT, "description:"},
  {false, PR_BUSINESS_TELEPHONE_NUMBER, "telephonenumber:"},
  {false, PR_HOME_TELEPHONE_NUMBER, "homephone:"},
  {false, PR_COMPANY_NAME, "o:"},
  {false, PR_TITLE, "title:"},
  {false, PR_BUSINESS_FAX_NUMBER, "facsimiletelephonenumber:"},
  {false, PR_LOCALITY, "locality:"},
  {false, PR_STATE_OR_PROVINCE, "st:"},
  {true, PR_STREET_ADDRESS, "streetaddress:"},
  {false, PR_POSTAL_CODE, "postalcode:"}
};

#define  kWhitespace  " \t\b\r\n"

#define TR_OUTPUT_EOL  "\r\n"

#define  kLDIFPerson    "objectclass: top" TR_OUTPUT_EOL "objectclass: person" TR_OUTPUT_EOL
#define kLDIFGroup    "objectclass: top" TR_OUTPUT_EOL "objectclass: groupOfNames" TR_OUTPUT_EOL

/*********************************************************************************/


// contructor for CWAB object
//
// pszFileName - FileName of WAB file to open
//          if no file name is specified, opens the default
//
CWAB::CWAB(nsIFile *file)
{
    // Here we load the WAB Object and initialize it
    m_pUniBuff = NULL;
  m_uniBuffLen = 0;

  m_bInitialized = false;
  m_lpAdrBook = NULL;
  m_lpWABObject = NULL;
  m_hinstWAB = NULL;

    {
        TCHAR  szWABDllPath[MAX_PATH];
        DWORD  dwType = 0;
        ULONG  cbData = sizeof(szWABDllPath);
        HKEY hKey = NULL;

        *szWABDllPath = '\0';

        // First we look under the default WAB DLL path location in the
        // Registry.
        // WAB_DLL_PATH_KEY is defined in wabapi.h
        //
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, KEY_READ, &hKey)) {
            RegQueryValueEx(hKey, "", NULL, &dwType, (LPBYTE) szWABDllPath, &cbData);
            if (dwType == REG_EXPAND_SZ) {
                // Expand the environment variables
                DWORD bufferSize = ExpandEnvironmentStrings(szWABDllPath, NULL, 0);
                if (bufferSize && bufferSize < MAX_PATH) {
                    TCHAR tmp[MAX_PATH];
                    ExpandEnvironmentStrings(szWABDllPath, tmp, bufferSize);
                    _tcscpy(szWABDllPath, tmp);
                }
                else {
                    // This is an error condition. Nothing else is initialized yet, so simply return.
                    return;
                }

            }
        }
        else {
            if (GetSystemDirectory(szWABDllPath, MAX_PATH)) {
                _tcsncat(szWABDllPath, WAB_DLL_NAME,
                         std::min(_tcslen(WAB_DLL_NAME), MAX_PATH - _tcslen(szWABDllPath) - 1));
            }
            else {
                // Yet another error condition.
                return;
            }
        }

        if(hKey) RegCloseKey(hKey);

        // if the Registry came up blank, we do a loadlibrary on the wab32.dll
        // WAB_DLL_NAME is defined in wabapi.h
        //
        m_hinstWAB = LoadLibrary((lstrlen(szWABDllPath)) ? szWABDllPath : WAB_DLL_NAME);
    }

    if(m_hinstWAB)
    {
        // if we loaded the dll, get the entry point
        //
        m_lpfnWABOpen = (LPWABOPEN) GetProcAddress(m_hinstWAB, "WABOpen");

        if(m_lpfnWABOpen)
        {
          char    fName[2] = {0, 0};
            HRESULT hr = E_FAIL;
            WAB_PARAM wp = {0};
            wp.cbSize = sizeof(WAB_PARAM);
            if (file != nullptr) {
                nsCString path;
              file->GetNativePath(path);
              wp.szFileName = (LPTSTR) ToNewCString(path);
            }
            else
              wp.szFileName = (LPTSTR) fName;

            // if we choose not to pass in a WAB_PARAM object,
            // the default WAB file will be opened up
            //
            hr = m_lpfnWABOpen(&m_lpAdrBook,&m_lpWABObject,&wp,0);

            if(!hr)
                m_bInitialized = TRUE;

        }
    }

}


// Destructor
//
CWAB::~CWAB()
{
  if (m_pUniBuff)
    delete [] m_pUniBuff;

    if(m_bInitialized)
    {
        if(m_lpAdrBook)
            m_lpAdrBook->Release();

        if(m_lpWABObject)
            m_lpWABObject->Release();

        if(m_hinstWAB)
            FreeLibrary(m_hinstWAB);
    }
}


HRESULT CWAB::IterateWABContents(CWabIterator *pIter, int *pDone)
{
  if (!m_bInitialized || !m_lpAdrBook)
    return E_FAIL;

  ULONG      ulObjType =   0;
  LPMAPITABLE    lpAB =  NULL;
  ULONG      cRows =       0;
  LPSRowSet    lpRowAB = NULL;
  LPABCONT    lpContainer = NULL;
  int        cNumRows = 0;
  nsresult      keepGoing;

  HRESULT      hr = E_FAIL;

  ULONG      lpcbEID = 0;
  LPENTRYID    lpEID = NULL;
  ULONG      rowCount = 0;
  ULONG      curCount = 0;

  nsString    uniStr;

  // Get the entryid of the root PAB container
  //
  hr = m_lpAdrBook->GetPAB(&lpcbEID, &lpEID);

  if (HR_FAILED(hr))
    goto exit;

  ulObjType = 0;

  // Open the root PAB container
  // This is where all the WAB contents reside
  //
  hr = m_lpAdrBook->OpenEntry(lpcbEID,
    (LPENTRYID)lpEID,
    NULL,
    0,
    &ulObjType,
    (LPUNKNOWN *)&lpContainer);

  m_lpWABObject->FreeBuffer(lpEID);

  lpEID = NULL;

  if(HR_FAILED(hr))
    goto exit;

  // Get a contents table of all the contents in the
  // WABs root container
  //
  hr = lpContainer->GetContentsTable(0, &lpAB);

  if(HR_FAILED(hr))
    goto exit;

  hr = lpAB->GetRowCount(0, &rowCount);
  if (HR_FAILED(hr))
    rowCount = 100;
  if (rowCount == 0)
    rowCount = 1;

  // Order the columns in the ContentsTable to conform to the
  // ones we want - which are mainly DisplayName, EntryID and
  // ObjectType
  // The table is gauranteed to set the columns in the order
  // requested
  //
  hr =lpAB->SetColumns((LPSPropTagArray)&ptaEid, 0);

  if(HR_FAILED(hr))
    goto exit;


  // Reset to the beginning of the table
  //
  hr = lpAB->SeekRow(BOOKMARK_BEGINNING, 0, NULL);

  if(HR_FAILED(hr))
    goto exit;

  // Read all the rows of the table one by one
  //

  do {

    hr = lpAB->QueryRows(1,  0, &lpRowAB);

    if(HR_FAILED(hr))
      break;

    if(lpRowAB)
    {
      cNumRows = lpRowAB->cRows;

      if (cNumRows)
      {
        LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA;
        LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
        ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;

        // There are 2 kinds of objects - the MAPI_MAILUSER contact object
        // and the MAPI_DISTLIST contact object
        // For the purposes of this sample, we will only consider MAILUSER
        // objects
        //
        if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER)
        {
          // We will now take the entry-id of each object and cache it
          // on the listview item representing that object. This enables
          // us to uniquely identify the object later if we need to
          //
          CStrToUnicode(lpsz, uniStr);
          keepGoing = pIter->EnumUser(uniStr.get(), lpEID, cbEID);
          curCount++;
          if (pDone) {
            *pDone = (curCount * 100) / rowCount;
            if (*pDone > 100)
              *pDone = 100;
          }
        }
      }
      FreeProws(lpRowAB);
    }


  } while (SUCCEEDED(hr) && cNumRows && lpRowAB && NS_SUCCEEDED(keepGoing))  ;

  hr = lpAB->SeekRow(BOOKMARK_BEGINNING, 0, NULL);

  if(HR_FAILED(hr))
    goto exit;

  // Read all the rows of the table one by one
  //
  keepGoing = NS_OK;
  do {

    hr = lpAB->QueryRows(1,  0, &lpRowAB);

    if(HR_FAILED(hr))
      break;

    if(lpRowAB)
    {
      cNumRows = lpRowAB->cRows;

      if (cNumRows)
      {
        LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA;
        LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
        ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;

        // There are 2 kinds of objects - the MAPI_MAILUSER contact object
        // and the MAPI_DISTLIST contact object
        // For the purposes of this sample, we will only consider MAILUSER
        // objects
        //
        if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST)
        {
          LPABCONT distListContainer = NULL;
          // We will now take the entry-id of each object and cache it
          // on the listview item representing that object. This enables
          // us to uniquely identify the object later if we need to
          //
          hr = m_lpAdrBook->OpenEntry(cbEID, lpEID, NULL,
            0,&ulObjType,(LPUNKNOWN *)&distListContainer);

          LPMAPITABLE    distListTable =  NULL;


          // Get a contents table of the dist list
          //
          hr = distListContainer->GetContentsTable(0, &distListTable);
          if (lpAB)
          {
            hr = distListTable->GetRowCount(0, &rowCount);
            if (HR_FAILED(hr))
              rowCount = 100;
            if (rowCount == 0)
              rowCount = 1;

            // Order the columns in the ContentsTable to conform to the
            // ones we want - which are mainly DisplayName, EntryID and
            // ObjectType
            // The table is gauranteed to set the columns in the order
            // requested
            //
            hr = distListTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
            CStrToUnicode(lpsz, uniStr);
            keepGoing = pIter->EnumList(uniStr.get(), lpEID, cbEID, distListTable);
            curCount++;
            if (pDone) {
              *pDone = (curCount * 100) / rowCount;
              if (*pDone > 100)
                *pDone = 100;
            }
          }
          if (distListContainer)
            distListContainer->Release();
          if (distListTable)
            distListTable->Release();
        }
      }
      FreeProws(lpRowAB);
    }

  } while (SUCCEEDED(hr) && cNumRows && lpRowAB && NS_SUCCEEDED(keepGoing))  ;


exit:

  if (lpContainer)
    lpContainer->Release();

  if (lpAB)
    lpAB->Release();

  return hr;
}






void CWAB::FreeProws(LPSRowSet prows)
{
  ULONG    irow;
  if (!prows)
    return;
  for (irow = 0; irow < prows->cRows; ++irow)
    m_lpWABObject->FreeBuffer(prows->aRow[irow].lpProps);
  m_lpWABObject->FreeBuffer(prows);
}


LPDISTLIST CWAB::GetDistList(ULONG cbEid, LPENTRYID pEid)
{
  if (!m_bInitialized || !m_lpAdrBook)
    return NULL;

  LPDISTLIST  lpDistList = NULL;
  ULONG    ulObjType;

  m_lpAdrBook->OpenEntry(cbEid, pEid, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpDistList);
  return lpDistList;
}

LPSPropValue CWAB::GetListProperty(LPDISTLIST pUser, ULONG tag)
{
  if (!pUser)
    return NULL;

  int  sz = CbNewSPropTagArray(1);
  SPropTagArray *pTag = (SPropTagArray *) new char[sz];
  pTag->cValues = 1;
  pTag->aulPropTag[0] = tag;
  LPSPropValue  lpProp = NULL;
  ULONG  cValues = 0;
  HRESULT hr = pUser->GetProps(pTag, 0, &cValues, &lpProp);
  delete [] pTag;
  if (HR_FAILED(hr) || (cValues != 1)) {
    if (lpProp)
      m_lpWABObject->FreeBuffer(lpProp);
    return NULL;
  }
  return lpProp;
}

LPMAILUSER CWAB::GetUser(ULONG cbEid, LPENTRYID pEid)
{
  if (!m_bInitialized || !m_lpAdrBook)
    return NULL;

  LPMAILUSER  lpMailUser = NULL;
  ULONG    ulObjType;

  m_lpAdrBook->OpenEntry(cbEid, pEid, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpMailUser);
  return lpMailUser;
}

LPSPropValue CWAB::GetUserProperty(LPMAILUSER pUser, ULONG tag)
{
  if (!pUser)
    return NULL;

  ULONG  uTag = tag;
  /*
    Getting Unicode does not help with getting the right
    international charset.  Windoze bloze.
  */
  /*
  if (PROP_TYPE(uTag) == PT_STRING8) {
    uTag = CHANGE_PROP_TYPE(tag, PT_UNICODE);
  }
  */

  int  sz = CbNewSPropTagArray(1);
  SPropTagArray *pTag = (SPropTagArray *) new char[sz];
  pTag->cValues = 1;
  pTag->aulPropTag[0] = uTag;
  LPSPropValue  lpProp = NULL;
  ULONG  cValues = 0;
  HRESULT hr = pUser->GetProps(pTag, 0, &cValues, &lpProp);
  if (HR_FAILED(hr) || (cValues != 1)) {
    if (lpProp)
      m_lpWABObject->FreeBuffer(lpProp);
    lpProp = NULL;
    if (uTag != tag) {
      pTag->cValues = 1;
      pTag->aulPropTag[0] = tag;
      cValues = 0;
      hr = pUser->GetProps(pTag, 0, &cValues, &lpProp);
      if (HR_FAILED(hr) || (cValues != 1)) {
        if (lpProp)
          m_lpWABObject->FreeBuffer(lpProp);
        lpProp = NULL;
      }
    }
  }
  delete [] pTag;
  return lpProp;
}

void CWAB::CStrToUnicode(const char *pStr, nsString& result)
{
  result.Truncate();
  int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, -1, m_pUniBuff, 0);
  if (wLen >= m_uniBuffLen) {
    if (m_pUniBuff)
      delete [] m_pUniBuff;
    m_pUniBuff = new char16_t[wLen + 64];
    m_uniBuffLen = wLen + 64;
  }
  if (wLen) {
    MultiByteToWideChar(CP_ACP, 0, pStr, -1, m_pUniBuff, m_uniBuffLen);
    result = m_pUniBuff;
  }
}

// If the value is a string, get it...
void CWAB::GetValueString(LPSPropValue pVal, nsString& val)
{
  val.Truncate();

  if (!pVal)
    return;

  switch(PROP_TYPE(pVal->ulPropTag)) {
    case PT_STRING8:
      CStrToUnicode((const char *) (pVal->Value.lpszA), val);
      break;
    case PT_UNICODE:
      val = (char16_t *) (pVal->Value.lpszW);
      break;
    case PT_MV_STRING8: {
      nsString  tmp;
      ULONG  j;
      for(j = 0; j < pVal->Value.MVszA.cValues; j++) {
        CStrToUnicode((const char *) (pVal->Value.MVszA.lppszA[j]), tmp);
        val += tmp;
        val.Append(NS_ConvertASCIItoUTF16(TR_OUTPUT_EOL));
      }
      break;
    }
    case PT_MV_UNICODE: {
      ULONG  j;
      for(j = 0; j < pVal->Value.MVszW.cValues; j++) {
        val += (char16_t *) (pVal->Value.MVszW.lppszW[j]);
        val.Append(NS_ConvertASCIItoUTF16(TR_OUTPUT_EOL));
      }
      break;
    }
    case PT_I2:
    case PT_LONG:
    case PT_R4:
    case PT_DOUBLE:
    case PT_BOOLEAN: {
      /*
      TCHAR sz[256];
            wsprintf(sz,"%d", pVal->Value.l);
            val = sz;
      */
      break;
    }

    case PT_BINARY:
      break;

    default:
      break;
  }

  val.Trim(kWhitespace, true, true);
}


void CWAB::GetValueTime(LPSPropValue pVal, PRTime& val)
{
  if (!pVal)
    return;

  if (PROP_TYPE(pVal->ulPropTag) != PT_SYSTIME)
    return;

  nsOE5File::FileTimeToPRTime(&pVal->Value.ft, &val);
}

bool CWAB::IsAvailable()
{
  if (!m_bInitialized || !m_lpAdrBook)
    return false;

  ULONG lpcbEID = 0;
  LPENTRYID lpEID = NULL;
  HRESULT hr = m_lpAdrBook->GetPAB(&lpcbEID, &lpEID);
  if (HR_FAILED(hr))
    return false;

  ULONG ulObjType = 0;
  LPABCONT lpContainer = NULL;
  hr = m_lpAdrBook->OpenEntry(lpcbEID,
                              (LPENTRYID)lpEID,
                              NULL,
                              0,
                              &ulObjType,
                              (LPUNKNOWN *)&lpContainer);
  m_lpWABObject->FreeBuffer(lpEID);

  LPMAPITABLE lpAB = NULL;
  hr = lpContainer->GetContentsTable(0, &lpAB);
  if(HR_FAILED(hr)) {
    lpContainer->Release();
    return false;
  }

  ULONG rowCount = 0;
  hr = lpAB->GetRowCount(0, &rowCount);
  lpContainer->Release();
  lpAB->Release();
  return (rowCount != 0);
}

/*
BOOL CWabIterateProcess::SanitizeMultiLine(CString& val)
{
  val.TrimLeft();
  val.TrimRight();
  int idx = val.FindOneOf("\x0D\x0A");
  if (idx == -1)
    return FALSE;

  // needs encoding
  U32 bufSz = UMimeEncode::GetBufferSize(val.GetLength());
  P_U8 pBuf = new U8[bufSz];
  U32 len = UMimeEncode::ConvertBuffer((PC_U8)((PC_S8)val), val.GetLength(), pBuf, 66, 52, "\x0D\x0A ");
  pBuf[len] = 0;
  val = pBuf;
  delete pBuf;
  return TRUE;
}

BOOL CWabIterateProcess::EnumUser(LPCTSTR pName, LPENTRYID pEid, ULONG cbEid)
{
  TRACE1("User: %s\n", pName);

  LPMAILUSER  pUser = m_pWab->GetUser(cbEid, pEid);

  // Get the "required" strings first
  CString    lastName;
  CString    firstName;
  CString    eMail;
  CString    nickName;
  CString    middleName;

  if (!pUser) {
    UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName);
    return FALSE;
  }

  LPSPropValue  pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS);
  if (pProp) {
    m_pWab->GetValueString(pProp, eMail);
    SanitizeValue(eMail);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_GIVEN_NAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, firstName);
    SanitizeValue(firstName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_SURNAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, lastName);
    SanitizeValue(lastName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_MIDDLE_NAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, middleName);
    SanitizeValue(middleName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_NICKNAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, nickName);
    SanitizeValue(nickName);
    m_pWab->FreeProperty(pProp);
  }
  if (nickName.IsEmpty())
    nickName = pName;
  if (firstName.IsEmpty()) {
    firstName = nickName;
    middleName.Empty();
    lastName.Empty();
  }
  if (lastName.IsEmpty())
    middleName.Empty();

  if (eMail.IsEmpty())
    eMail = nickName;


  // We now have the required fields
  // write them out followed by any optional fields!
  BOOL  result = TRUE;

  if (m_recordsDone)
    result = m_out.WriteEol();

  CString    line;
  CString    header;
  line.LoadString(IDS_LDIF_DN_START);
  line += firstName;
  if (!middleName.IsEmpty()) {
    line += ' ';
    line += middleName;
  }
  if (!lastName.IsEmpty()) {
    line += ' ';
    line += lastName;
  }
  header.LoadString(IDS_LDIF_DN_MIDDLE);
  line += header;
  line += eMail;
  result = result && m_out.WriteStr(line);
  result = result && m_out.WriteEol();

  line.LoadString(IDS_FIELD_LDIF_FULLNAME);
  line += ' ';
  line += firstName;
  if (!middleName.IsEmpty()) {
    line += ' ';
    line += middleName;
  }
  if (!lastName.IsEmpty()) {
    line += ' ';
    line += lastName;
  }
  result = result && m_out.WriteStr(line);
  result = result && m_out.WriteEol();


  line.LoadString(IDS_FIELD_LDIF_GIVENNAME);
  line += ' ';
  line += firstName;
  result = result && m_out.WriteStr(line);
  result = result && m_out.WriteEol();

  if (!lastName.IsEmpty()) {
    line.LoadString(IDS_FIELD_LDIF_LASTNAME);
    if (!middleName.IsEmpty()) {
      line += ' ';
      line += middleName;
    }
    line += ' ';
    line += lastName;
    result = result && m_out.WriteStr(line);
    result = result && m_out.WriteEol();
  }

  result = result && m_out.WriteStr(kLDIFPerson);

  line.LoadString(IDS_FIELD_LDIF_EMAIL);
  line += ' ';
  line += eMail;
  result = result && m_out.WriteStr(line);
  result = result && m_out.WriteEol();

  line.LoadString(IDS_FIELD_LDIF_NICKNAME);
  line += ' ';
  line += nickName;
  result = result && m_out.WriteStr(line);
  result = result && m_out.WriteEol();

  // Do all of the extra fields!
  CString  value;
  BOOL  encoded = FALSE;
  for (int i = 0; i < kExtraUserFields; i++) {
    value.Empty();
    pProp = m_pWab->GetUserProperty(pUser, extraUserFields[i].tag);
    if (pProp) {
      m_pWab->GetValueString(pProp, value);
      m_pWab->FreeProperty(pProp);
    }
    if (extraUserFields[i].multiLine) {
      encoded = SanitizeMultiLine(value);
    }
    else
      SanitizeValue(value);
    if (!value.IsEmpty()) {
      line = extraUserFields[i].pLDIF;
      if (encoded) {
        line += ": ";
        encoded = FALSE;
      }
      else
        line += ' ';
      line += value;
      result = result && m_out.WriteStr(line);
      result = result && m_out.WriteEol();
    }
  }

  m_pWab->ReleaseUser(pUser);

  if (!result) {
    UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR);
  }

  m_totalDone += kValuePerUser;
  m_recordsDone++;

  return result;
}
*/




/*
BOOL CWabIterateProcess::EnumList(LPCTSTR pName, LPENTRYID pEid, ULONG cbEid)
{
  TRACE1("List: %s\n", pName);

  LPDISTLIST    pList = m_pWab->GetDistList(cbEid, pEid);
  if (!pList) {
    UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName);
    return FALSE;
  }

  // Find out if this is just a regular entry or a true list...
  CString      eMail;
  LPSPropValue  pProp = m_pWab->GetListProperty(pList, PR_EMAIL_ADDRESS);
  if (pProp) {
    m_pWab->GetValueString(pProp, eMail);
    SanitizeValue(eMail);
    m_pWab->FreeProperty(pProp);
    // Treat this like a regular entry...
    if (!eMail.IsEmpty()) {
      m_pWab->ReleaseDistList(pList);
      return WriteListUserEntry(pName, eMail);
    }
  }

  // This may very well be a list, find the entries...
  m_pListTable = OpenDistList(pList);
  if (m_pListTable) {
    m_pList = pList;
    m_listName = pName;
    m_listDone = 0;
    m_listHeaderDone = FALSE;
    m_state = kEnumListState;
  }
  else {
    m_pWab->ReleaseDistList(pList);
    m_recordsDone++;
    m_totalDone += kValuePerUser;
  }

  return TRUE;
}

BOOL CWabIterateProcess::EnumNextListUser(BOOL *pDone)
{
  HRESULT      hr;
  int        cNumRows = 0;
  LPSRowSet    lpRowAB = NULL;
  BOOL      keepGoing = TRUE;

  if (!m_pListTable)
    return FALSE;

  hr = m_pListTable->QueryRows(1, 0, &lpRowAB);

  if(HR_FAILED(hr)) {
    UDialogs::ErrMessage0(IDS_ERROR_READING_WAB);
    return FALSE;
  }

  if(lpRowAB) {
    cNumRows = lpRowAB->cRows;

    if (cNumRows) {
      LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA;
      LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
      ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
            if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) {
        keepGoing = HandleListList(lpsz, lpEID, cbEID);
        }
      else if (lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) {
        keepGoing = HandleListUser(lpsz, lpEID, cbEID);
      }
    }
    m_pWab->FreeProws(lpRowAB);
   }

  if (!cNumRows || !lpRowAB) {
    *pDone = TRUE;
    m_pListTable->Release();
    m_pListTable = NULL;
    if (m_pList)
      m_pWab->ReleaseDistList(m_pList);
    m_pList = NULL;
    if (m_listDone < kValuePerUser)
      m_totalDone += (kValuePerUser - m_listDone);
    m_recordsDone++;
    return keepGoing;
  }

  if (!keepGoing)
    return FALSE;

  if (m_listDone < kValuePerUser) {
    m_listDone++;
    m_totalDone++;
  }

  return TRUE;
}

BOOL CWabIterateProcess::HandleListList(LPCTSTR pName, LPENTRYID lpEid, ULONG cbEid)
{
  BOOL      result;
  LPDISTLIST    pList = m_pWab->GetDistList(cbEid, lpEid);
  if (!pList) {
    UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName);
    return FALSE;
  }

  CString      eMail;
  LPSPropValue  pProp = m_pWab->GetListProperty(pList, PR_EMAIL_ADDRESS);
  if (pProp) {
    m_pWab->GetValueString(pProp, eMail);
    SanitizeValue(eMail);
    m_pWab->FreeProperty(pProp);
    // Treat this like a regular entry...
    if (!eMail.IsEmpty()) {
      // write out a member based on pName and eMail
      result = WriteGroupMember(pName, eMail);
      m_pWab->ReleaseDistList(pList);
      return result;
    }
  }

  // iterate the list and add each member to the top level list
  LPMAPITABLE  pTable = OpenDistList(pList);
  if (!pTable) {
    TRACE0("Error opening table for list\n");
    m_pWab->ReleaseDistList(pList);
    UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName);
    return FALSE;
  }

  int        cNumRows = 0;
  LPSRowSet    lpRowAB = NULL;
  HRESULT      hr;
  BOOL      keepGoing = TRUE;

  do {
    hr = pTable->QueryRows(1, 0, &lpRowAB);

    if(HR_FAILED(hr)) {
      UDialogs::ErrMessage0(IDS_ERROR_READING_WAB);
      pTable->Release();
      m_pWab->ReleaseDistList(pList);
      return FALSE;
    }

    if(lpRowAB) {
      cNumRows = lpRowAB->cRows;

      if (cNumRows) {
        LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA;
        LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
        ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
        if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) {
          keepGoing = HandleListList(lpsz, lpEID, cbEID);
        }
        else if (lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) {
          keepGoing = HandleListUser(lpsz, lpEID, cbEID);
        }
      }
      m_pWab->FreeProws(lpRowAB);
     }
  }
  while (keepGoing && cNumRows && lpRowAB);

  pTable->Release();
  m_pWab->ReleaseDistList(pList);
  return keepGoing;
}

BOOL CWabIterateProcess::HandleListUser(LPCTSTR pName, LPENTRYID lpEid, ULONG cbEid)
{
  // Get the basic properties for building the member line
  LPMAILUSER  pUser = m_pWab->GetUser(cbEid, lpEid);

  // Get the "required" strings first
  CString    lastName;
  CString    firstName;
  CString    eMail;
  CString    nickName;
  CString    middleName;

  if (!pUser) {
    UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName);
    return FALSE;
  }

  LPSPropValue  pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS);
  if (pProp) {
    m_pWab->GetValueString(pProp, eMail);
    SanitizeValue(eMail);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_GIVEN_NAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, firstName);
    SanitizeValue(firstName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_SURNAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, lastName);
    SanitizeValue(lastName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_MIDDLE_NAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, middleName);
    SanitizeValue(middleName);
    m_pWab->FreeProperty(pProp);
  }
  pProp = m_pWab->GetUserProperty(pUser, PR_NICKNAME);
  if (pProp) {
    m_pWab->GetValueString(pProp, nickName);
    SanitizeValue(nickName);
    m_pWab->FreeProperty(pProp);
  }
  if (nickName.IsEmpty())
    nickName = pName;
  if (firstName.IsEmpty()) {
    firstName = nickName;
    middleName.Empty();
    lastName.Empty();
  }
  if (lastName.IsEmpty())
    middleName.Empty();

  if (eMail.IsEmpty())
    eMail = nickName;

  m_pWab->ReleaseUser(pUser);

  CString  name = firstName;
  if (!middleName.IsEmpty()) {
    name += ' ';
    name += middleName;
  }
  if (!lastName.IsEmpty()) {
    name += ' ';
    name += lastName;
  }
  return WriteGroupMember(name, eMail);
}

BOOL CWabIterateProcess::WriteGroupMember(const char *pName, const char *pEmail)
{
  CString    middle;
  CString    line;
  BOOL    result;

  // Check for the header first
  if (!m_listHeaderDone) {
    if (m_recordsDone)
      result = m_out.WriteEol();
    else
      result = TRUE;
    line.LoadString(IDS_LDIF_DN_START);
    line += m_listName;
    line += TR_OUTPUT_EOL;
    middle.LoadString(IDS_FIELD_LDIF_FULLNAME);
    line += middle;
    line += m_listName;
    line += TR_OUTPUT_EOL;
    if (!result || !m_out.WriteStr(line) || !m_out.WriteStr(kLDIFGroup)) {
      UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR);
      return FALSE;
    }
    m_listHeaderDone = TRUE;
  }


  line.LoadString(IDS_FIELD_LDIF_MEMBER_START);
  line += pName;
  middle.LoadString(IDS_LDIF_DN_MIDDLE);
  line += middle;
  line += pEmail;
  line += TR_OUTPUT_EOL;
  if (!m_out.WriteStr(line)) {
    UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR);
    return FALSE;
  }

  if (m_listDone < kValuePerUser) {
    m_listDone++;
    m_totalDone++;
  }

  return TRUE;
}
*/