/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <tchar.h>
#include "nscore.h"
#include "nsOE5File.h"
#include "wabobject.h"

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] = {
  {PR_TRUE, PR_COMMENT, "description:"},
  {PR_FALSE, PR_BUSINESS_TELEPHONE_NUMBER, "telephonenumber:"},
  {PR_FALSE, PR_HOME_TELEPHONE_NUMBER, "homephone:"},
  {PR_FALSE, PR_COMPANY_NAME, "o:"},
  {PR_FALSE, PR_TITLE, "title:"},
  {PR_FALSE, PR_BUSINESS_FAX_NUMBER, "facsimiletelephonenumber:"},
  {PR_FALSE, PR_LOCALITY, "locality:"},
  {PR_FALSE, PR_STATE_OR_PROVINCE, "st:"},
  {PR_TRUE, PR_STREET_ADDRESS, "streetaddress:"},
  {PR_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(nsILocalFile *file)
{
    // Here we load the WAB Object and initialize it
    m_pUniBuff = NULL;
  m_uniBuffLen = 0;

  m_bInitialized = PR_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,
                         NS_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 != nsnull) {
                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 = TRUE;
  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 PRUnichar[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 = (PRUnichar *) (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 += (PRUnichar *) (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, PR_TRUE, PR_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 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);
}
*/

