/*
 * Author: t-franks
 *
 * Last Modified On: Oct 16, 1998
 * Last Modified By: t-joshp
 *
 */

#include "priv.h"
#include "resource.h"
#include "impexp.h"
#include "mluisupp.h"  // for MLLoadString
#include "apithk.h"

//
// Indices into our imagelist
// (used for the open and closed folder icons on the tree view)
//
#define FOLDER_CLOSED 0
#define FOLDER_OPEN   1

#define ImportCookieFile ImportCookieFileW
#define ExportCookieFile ExportCookieFileW

BOOL ImportCookieFileW(IN LPCWSTR szFilename);
BOOL ExportCookieFileW(IN LPCWSTR szFilename, BOOL fAppend);

extern void SetListViewToString (HWND hLV, LPCTSTR pszString);

//
// used to display "file already exists" and "file not found" messages
//
int WarningMessageBox(HWND hwnd, UINT idTitle, UINT idMessage, LPCTSTR szFile, DWORD dwFlags);

//
// Strings that don't need localizing
//

#define NS3_COOKIE_REG_PATH         TEXT("Software\\Netscape\\Netscape Navigator\\Cookies")
#define NS3_COOKIE_REG_KEY          TEXT("Cookie File")

#ifndef UNIX
#define NS3_BOOKMARK_REG_PATH       TEXT("Software\\Netscape\\Netscape Navigator\\Bookmark List")
#else
#define NS3_BOOKMARK_REG_PATH       TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\unix\\nsbookmarks")
#endif

#define NS3_BOOKMARK_REG_KEY        TEXT("File Location")

#define NS4_USERS_REG_PATH          TEXT("Software\\Netscape\\Netscape Navigator\\Users")
#define NS4_USERPATH_REG_KEY        TEXT("DirRoot")

#define NS_FALLBACK_ROOT_REG_KEY    TEXT("Software\\Netscape\\Netscape Navigator")
#define NS_FALLBACK_VERSION_REG_VAL TEXT("CurrentVersion")
#define NS_FALLBACK_MAIN_REG_VAL    TEXT("Main")
#define NS_FALLBACK_INST_REG_VAL    TEXT("Install Directory")

#ifndef UNIX
#define ALL_FILES_WILDCARD          TEXT("\\*.*")
#else
#define ALL_FILES_WILDCARD          TEXT("/*")
#endif

#define DOT_DIR                     TEXT(".")
#define DOT_DOT_DIR                 TEXT("..")

#ifdef UNIX
#define DIR_SEPARATOR_CHAR  TEXT('/')
#else
#define DIR_SEPARATOR_CHAR  TEXT('\\')
#endif

//*************************************************************
//
//      class ListIterator
//
//  Keeps hold on a position in a list.  Allows basic access
//to a list.  The list is set up to map a name to a value.

class NestedList;

class ListIterator
{
    friend NestedList;
    
    struct node
    {
        LPTSTR _sName;
        LPTSTR _sValue;
        DWORD _cNameSize, _cValueSize;
        node* _pnNext;
        node* _pnSublist;
    };

    //  A position is held by pointing to the
    //current node and the pointer that is directed
    //to that node.  The back pointer is kept so the 
    //list can be manipulated at the current element.
    //  when m_pnCurrent == NULL, the iterator is
    //at the end of the list.
    node** m_ppnPrev;
    node* m_pnCurrent;

    //  The invariant could be broken if two iterators
    //point to the same node, and one inserts or deletes
    //an element.  So only one iterator should exist in 
    //a branch of the list at a time.
    BOOL invariant()
    {
        return *m_ppnPrev == m_pnCurrent;
    }

public:
    ListIterator( node** ppnPrev)
    {
        m_ppnPrev = ppnPrev;
        m_pnCurrent = *m_ppnPrev;
    }

    BOOL Insert( LPCTSTR sName, DWORD cNameSize, LPCTSTR sValue, DWORD cValueSize);
    BOOL Remove();

    ListIterator GetSublist();
    void DeleteSublist();

    BOOL Next();
    BOOL AtEndOfList();

    LPCTSTR GetName();
    DWORD GetNameSize();
    LPCTSTR GetValue();
    DWORD GetValueSize();
};


//*************************************************************
//
//  class NestedList
//      Keeps a pointer to a node which heads a list,
//  and deletes that list on destruction.


class NestedList
{
    ListIterator::node* m_pnRoot;
    
public:
    NestedList();
    ~NestedList();

    operator ListIterator();
};


NestedList::NestedList()
: m_pnRoot(NULL)
{
}


NestedList::~NestedList()
{
    while( ((ListIterator)*this).Remove())
    {
    }
}


NestedList::operator ListIterator()
{
    return ListIterator( &m_pnRoot);
}

//*************************************************************
//*************************************************************
//
//  ListIterator functions
//


//  Inserts an element before the current one,
//leaves iterator pointing at new node.
BOOL ListIterator::Insert( 
    LPCTSTR sName, 
    DWORD cNameSize, 
    LPCTSTR sValue, 
    DWORD cValueSize)
{
    ASSERT( invariant());

    node* pNewNode = (node*)(new BYTE[ sizeof(node) 
                                       + (( cNameSize + cValueSize)
                                          * sizeof(TCHAR))]);

    if( pNewNode == NULL)
        return FALSE;

    //  the name and value will be appended to the node.
    pNewNode->_sName = (LPTSTR)((BYTE*)pNewNode + sizeof(node));
    pNewNode->_sValue = pNewNode->_sName + cNameSize;

    pNewNode->_cNameSize = cNameSize;
    pNewNode->_cValueSize = cValueSize;

    memcpy( pNewNode->_sName, sName, pNewNode->_cNameSize * sizeof(TCHAR));
    memcpy( pNewNode->_sValue, sValue, pNewNode->_cValueSize * sizeof(TCHAR));

    // insert new node in list
    pNewNode->_pnNext = m_pnCurrent;
    *m_ppnPrev = pNewNode;

    //  The iterator now points to the new element.
    m_pnCurrent = *m_ppnPrev;
    
    ASSERT( invariant());

    return TRUE;
}


//  Deletes the current node.
//  Returns FALSE if at end of list.
BOOL ListIterator::Remove()
{
    ASSERT( invariant());
    
    //  If this list is empty, or if the iterator 
    //points at the end of the list, there is nothing to
    //delete.
    if( m_pnCurrent == NULL)
        return FALSE;

    // remove sublist
    DeleteSublist();
    
    //  Remember where target node is
    //so it can be deleted once out of
    //the list.
    node* pOldNode = m_pnCurrent;

    // take the target node out of the list.
    //(iterator points to next node or end of list)
    *m_ppnPrev = m_pnCurrent->_pnNext;
    m_pnCurrent = *m_ppnPrev;

    //  Get rid of target node.
    delete [] (BYTE*)pOldNode;

    ASSERT( invariant());

    return TRUE;    
}


//  Returns the sublist of the current node.
ListIterator ListIterator::GetSublist()
{
    ASSERT( invariant());
    
    return ListIterator( &(m_pnCurrent->_pnSublist));
}


//  deletes the children of the current node.
void ListIterator::DeleteSublist()
{
    ASSERT( invariant());
    
    ListIterator sublist( &(m_pnCurrent->_pnSublist));
    
    while( sublist.Remove())
    {
    }

    ASSERT( invariant());
}


//  Advances to the next node.
//  Returns FALSE if already at end of list.
BOOL ListIterator::Next()
{
    ASSERT( invariant());

    if( m_pnCurrent == NULL)
        return FALSE;

    m_ppnPrev = &(m_pnCurrent->_pnNext);
    m_pnCurrent = *m_ppnPrev;

    ASSERT( invariant());

    return m_pnCurrent != NULL;
}


//  
BOOL ListIterator::AtEndOfList()
{
    return ( m_pnCurrent == NULL) ? TRUE : FALSE;
};


//
LPCTSTR ListIterator::GetName()
{
    ASSERT( invariant() && m_pnCurrent != NULL);

    return m_pnCurrent->_sName;
}


//
DWORD ListIterator::GetNameSize()
{
    ASSERT( invariant() && m_pnCurrent != NULL);

    return m_pnCurrent->_cNameSize;
}


//
LPCTSTR ListIterator::GetValue()
{
    ASSERT( invariant() && m_pnCurrent != NULL);

    return m_pnCurrent->_sValue;
}


//
DWORD ListIterator::GetValueSize()
{
    ASSERT( invariant() && m_pnCurrent != NULL);

    return m_pnCurrent->_cValueSize;
}


//*************************************************************
//*************************************************************
//
//  class ImpExpUserProcess
//
//      maintains the description of an import/export process
//  for an import/export wizard, and finally executes the
//  the import/export.

enum ExternalType { INVALID_EXTERNAL = 0, COOKIES, BOOKMARKS};
enum TransferType { INVALID_TRANSFER = 0, IMPORT, EXPORT};

class ImpExpUserProcess
{
public:
    ImpExpUserProcess();
    ~ImpExpUserProcess();
    
    //  the first step the wizard should do is identify the type of
    //import/export process to be done.
    void SelectExternalType( ExternalType selection)    { m_ExternalType = selection; }
    void SelectTransferType( TransferType selection)    { m_TransferType = selection; }
    ExternalType GetExternalType()                      { return m_ExternalType; }
    TransferType GetTransferType()                      { return m_TransferType; }

    BOOL PopulateComboBoxForExternalSelection( HWND hComboBox);
    BOOL GetExternalManualDefault( LPTSTR sExternal, DWORD* pcSize);

    //
    // used to fill the listbox with names of netscape profiles
    //
    void purgeExternalList();
    BOOL populateExternalList();
    BOOL populateExternalListForCookiesOrBookmarks();

    //
    // for netscape 3.x
    //
    BOOL populateExternalListForCookiesOrBookmarksWithNS3Entry();

    //
    // for netscape 4.x
    //
    BOOL populateExternalListForCookiesOrBookmarksWithNS4Entries();

    //
    // fallback case for "funny" versions of netscape
    //
    BOOL populateExternalListFromFolders(LPTSTR pszPath);
    BOOL populateExternalListWithNSEntriesFallBack();

    //  If the transfer is for favorites, the wizard needs to specify
    //an internal folder to import to or export from.
    LPCTSTR GetInternalSelection()       { return m_pSelectedInternal; }

    BOOL PopulateTreeViewForInternalSelection( HWND TreeView);
    BOOL populateTreeViewWithInternalList( HWND hTreeView, ListIterator iterator, HTREEITEM hParent);
    BOOL ExpandTreeViewRoot ( HWND hTreeView ) ;

    BOOL SelectInternalSelection( HWND TreeView);

    void purgeInternalList();
    BOOL populateInternalList();
    BOOL populateInternalListForBookmarks();
    BOOL appendSubdirsToInternalList( LPTSTR sPath, ListIterator iterator);
    
    //  And then, the import/export can be completed.
    void PerformImpExpProcess(HWND hwnd);

    //
    // The filename that we're exporting to or 
    // importing from.
    //
    TCHAR m_szFileName[MAX_PATH];

private:
    ExternalType m_ExternalType;
    TransferType m_TransferType;

    //  m_ExternalList is a flat list of names associated with files
    //example: name =  "Netscape 4.0 profile - Dr. Falken"
    //         value =  "c:\netscapeprofiledir\DrFalken.chs"
    NestedList m_ExternalList;

    //  m_InternalList is a nested list favorites' pathnames,
    //associated with the complete path.
    NestedList m_InternalList;

    //  Maintain synchronization between m_ExternalType/m_TransferType 
    //and m_InternalList
    ExternalType m_InternalListExternalType;
    TransferType m_InternalListTransferType;

    // if ExternalType == BOOKMARKS,
    //m_pSelectedInternal is the path of a Favorites folder,
    //residing in m_InternalList somewhere, or NULL if a folder
    //hasn't been selected yet.
    LPTSTR m_pSelectedInternal;

};


ImpExpUserProcess::ImpExpUserProcess()
:   m_ExternalType(INVALID_EXTERNAL), m_TransferType(INVALID_TRANSFER),
    m_InternalListExternalType(INVALID_EXTERNAL), m_InternalListTransferType(INVALID_TRANSFER),
    m_pSelectedInternal(0)
{
}


ImpExpUserProcess::~ImpExpUserProcess()
{
}


//*************************************************************
//   PopulateComboBoxForExternal
//
//  Loads content for list box into memory and into List Box,
//associating value of each element with the list element.

//  returns FALSE if the list box is left empty
BOOL ImpExpUserProcess::PopulateComboBoxForExternalSelection( HWND hComboBox)
{
    ASSERT ( m_ExternalType != INVALID_EXTERNAL ) ;

    ComboBox_ResetContent(hComboBox);
   
    //  If ExternalList is invalid, the list box will be left empty.
    if( !populateExternalList() )
        return FALSE;

    ListIterator iterator = m_ExternalList;

    //  Detect and notify if the list is empty.
    if( iterator.AtEndOfList() )
        return FALSE;

    //  add entries from the new ExternalList to the ComboBox.
    do
    {
        int index = ComboBox_AddString( hComboBox, const_cast<LPTSTR>(iterator.GetName() ) );
        ComboBox_SetItemData( hComboBox, index, const_cast<LPTSTR>(iterator.GetValue() ) );
    } while( iterator.Next());

    // set the first one as selected
    ComboBox_SetCurSel( hComboBox, 0 );

    return TRUE;
}


//*************************************************************
//
//  GetExternalManualDefault
//
//  Allows user interface to offer some sort of default
//  filename/location.
//
BOOL ImpExpUserProcess::GetExternalManualDefault(LPTSTR sExternal, DWORD* pcSize)
{
    ASSERT(NULL != pcSize);

    //
    // We only fill it in if it's blank
    //
    if (m_szFileName[0])
    {
        return FALSE;
    }

    ListIterator iterator = m_ExternalList;

    TCHAR szFileName[MAX_PATH];
    INT cchFileName;
    if(m_ExternalType == BOOKMARKS)
        MLLoadString(IDS_NETSCAPE_BOOKMARK_FILE,szFileName,ARRAYSIZE(szFileName));
    else
        MLLoadString(IDS_NETSCAPE_COOKIE_FILE,szFileName,ARRAYSIZE(szFileName));
    cchFileName = lstrlen(szFileName) + 1;

    //  Grab the first item in the External List and use its value.
    if( ((ListIterator)m_ExternalList).AtEndOfList() == FALSE
        && ((ListIterator)m_ExternalList).GetValue() != NULL
        && *pcSize >= ((ListIterator)m_ExternalList).GetValueSize())
    {
        StrCpyN( sExternal,
                 ((ListIterator)m_ExternalList).GetValue(),
                 ((ListIterator)m_ExternalList).GetValueSize());
        *pcSize = ((ListIterator)m_ExternalList).GetValueSize();

        return TRUE;
    }
    //  If there is enough room, specify some file with the correct name
    //  in the "my documents" directory.
    else 
    {
        ASSERT(m_ExternalType == BOOKMARKS || m_ExternalType == COOKIES);
        
        TCHAR szMyDocsPath[MAX_PATH];

        SHGetSpecialFolderPath(NULL,szMyDocsPath,CSIDL_PERSONAL,TRUE);

        *pcSize = wnsprintf(sExternal,MAX_PATH,TEXT("%s%c%s"),szMyDocsPath,DIR_SEPARATOR_CHAR,szFileName);

        return *pcSize > 0;
    }
}


//*************************************************************
//
//
//  purgeExternalList
//
//  Used to clear external target/source list loaded into memory

void ImpExpUserProcess::purgeExternalList()
{
    // delete elements until they're all gone.
    ListIterator iterator = m_ExternalList;

    while( iterator.Remove())
    {
    }

}


//*************************************************************
//
//  populeExternalList
//
//  Used to load external target/source list into memory

BOOL ImpExpUserProcess::populateExternalList()
{
    ASSERT(m_ExternalType != INVALID_EXTERNAL)

    purgeExternalList();

    if(!populateExternalListForCookiesOrBookmarks())
    {
        //
        // If we didn't get any entries using the "standard"
        // techniques, then (and only then) we try the "fallback"
        //
        if (!populateExternalListWithNSEntriesFallBack())
        {
            purgeExternalList();
            return FALSE;
        }

    }

    return TRUE;
}


//*************************************************************
//
//  populateExternalListforCookiesOrBookmarks
//
//  Used to lod external target/source list into memory
//in the case that the content to be transfered is cookies
//or bookmarks.

//  returns TRUE if any elements have been added to the external list
BOOL ImpExpUserProcess::populateExternalListForCookiesOrBookmarks()
{
    ASSERT( m_ExternalType == COOKIES || m_ExternalType == BOOKMARKS);

    BOOL fHasAddedElements = FALSE;

    if( populateExternalListForCookiesOrBookmarksWithNS3Entry())
        fHasAddedElements = TRUE;

    if( populateExternalListForCookiesOrBookmarksWithNS4Entries())
        fHasAddedElements = TRUE;
 
    return fHasAddedElements;
}


//*************************************************************
//
//  populateExternalList..WithNS3Entry
//
//  subfunc of populateExternalListForCookiesOrBookmarks.

//  returns TRUE if any elements have been added to the external list
BOOL ImpExpUserProcess::populateExternalListForCookiesOrBookmarksWithNS3Entry()
{
    BOOL retVal = FALSE;

    //  Determine where to look for reg key
    LPTSTR sNS3RegPath;
    LPTSTR sNS3RegKey;

    if( m_ExternalType == BOOKMARKS)
    {
        sNS3RegPath = NS3_BOOKMARK_REG_PATH;
        sNS3RegKey = NS3_BOOKMARK_REG_KEY;
    }
    else
    {
        sNS3RegPath = NS3_COOKIE_REG_PATH;
        sNS3RegKey = NS3_COOKIE_REG_KEY;
    }

    //  Get the file location and add it to the list
    //  The registry location has the complete path + filename.
    TCHAR sFilePath[MAX_PATH];
    DWORD cbFilePathSize = sizeof(sFilePath);
    DWORD dwType;
    if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, sNS3RegPath, sNS3RegKey,
                                    &dwType, (BYTE*)sFilePath, &cbFilePathSize)
        && (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
    {
        TCHAR szBuffer[MAX_PATH];

        MLLoadString(IDS_NS3_VERSION_CAPTION, szBuffer, MAX_PATH);
        
        retVal = ((ListIterator)m_ExternalList).Insert( 
                   szBuffer, lstrlen(szBuffer)+1,
                   sFilePath, cbFilePathSize / sizeof(TCHAR));
    }

    return retVal;
}


//*************************************************************
//
//  populateExternalList..WithNS4Entries
//
//  subfunc of populateExternalListForCookiesOrBookmarks.

//  returns TRUE if any elements have been added to the external list
BOOL ImpExpUserProcess::populateExternalListForCookiesOrBookmarksWithNS4Entries()
{
    BOOL retVal = FALSE;

    //  Get an iterator to advance position as items are inserted.
    ListIterator iterator = (ListIterator)m_ExternalList;

    //  Get the filename to be attached and the associated string size.
    TCHAR sFilename[MAX_PATH];
    DWORD cFilenameLength;
    if(m_ExternalType == BOOKMARKS)
        MLLoadString(IDS_NETSCAPE_BOOKMARK_FILE,sFilename,ARRAYSIZE(sFilename));
    else
        MLLoadString(IDS_NETSCAPE_COOKIE_FILE,sFilename,ARRAYSIZE(sFilename));
    cFilenameLength = lstrlen(sFilename);

    //  Get the reg key of the root of the NS profiles for enumeration.
    HKEY hUserRootKey = NULL;

    if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, NS4_USERS_REG_PATH, 
                      0, KEY_READ, &hUserRootKey) 
        != ERROR_SUCCESS)
    {
        hUserRootKey = NULL;
        goto donePopulateExternalListForCookiesOrBookmarksWithNS4Entries;
    }

    DWORD dwNumberOfProfiles;
    if( RegQueryInfoKey( hUserRootKey, NULL, NULL, NULL, &dwNumberOfProfiles,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
        || dwNumberOfProfiles == 0)
    {
        goto donePopulateExternalListForCookiesOrBookmarksWithNS4Entries;
    }

    //  Enumerate over the NS profiles, getting their names and
    //directory paths.  Associated the profile name with the path
    //of the desired files by appending the filename to the
    //user's root.
    TCHAR sProfileName[MAX_PATH];
    DWORD cProfileNameSize;  
    cProfileNameSize = MAX_PATH;
    DWORD iEnumIndex;  iEnumIndex = 0;
    while( RegEnumKeyEx( hUserRootKey, (iEnumIndex++), sProfileName, 
                         &cProfileNameSize, NULL, NULL, NULL, NULL) 
           == ERROR_SUCCESS)
    {
        //RegEnumKeyEx gives us the ProfileNameSize w/out the '\0'.
        cProfileNameSize = MAX_PATH;

        HKEY hProfileKey = NULL;

        if( RegOpenKeyEx( hUserRootKey, sProfileName, 0, KEY_READ, &hProfileKey) 
            != ERROR_SUCCESS)
        {
            hProfileKey = NULL;
            goto doneWithEntryInPopulateExternalListForCookiesOrBookmarksWithNS4Entries;
        }

        DWORD dwType;  //  should be REG_SZ when returned from QueryValue
        TCHAR sProfilePath[MAX_PATH];
        DWORD cProfilePathSize;  cProfilePathSize = sizeof(sProfilePath);
        if( (RegQueryValueEx( hProfileKey, NS4_USERPATH_REG_KEY, NULL, &dwType, 
                             (LPBYTE)sProfilePath, &cProfilePathSize) 
                != ERROR_SUCCESS)
            || dwType != REG_SZ)
        {
            goto doneWithEntryInPopulateExternalListForCookiesOrBookmarksWithNS4Entries;
        }
        cProfilePathSize /= sizeof(TCHAR);
        
        if( (MAX_PATH - cProfilePathSize) < cFilenameLength)
        {
            goto doneWithEntryInPopulateExternalListForCookiesOrBookmarksWithNS4Entries;
        }

        //  append "\\sFilename\0" to the path.
        sProfilePath[ cProfilePathSize - 1] = TCHAR(FILENAME_SEPARATOR);
        memcpy( &sProfilePath[cProfilePathSize], 
                sFilename, cFilenameLength * sizeof(TCHAR));
        cProfilePathSize += cFilenameLength;
        sProfilePath[cProfilePathSize++] = TCHAR('\0');

        // we can only import files if they exist!
        if( m_TransferType == IMPORT
            && GetFileAttributes(sProfilePath) == 0xFFFFFFFF)
                goto doneWithEntryInPopulateExternalListForCookiesOrBookmarksWithNS4Entries;

        //
        // construct the string for the combo box
        //
        TCHAR sRawProfileName[MAX_PATH];
        TCHAR sRealProfileName[MAX_PATH];
        UINT cRealProfileName;

        MLLoadString(IDS_NS4_FRIENDLY_PROFILE_NAME, sRawProfileName, MAX_PATH);

        cRealProfileName = 
            wnsprintf(sRealProfileName, MAX_PATH, 
                      sRawProfileName, sProfileName);

        //  Insert the profile into the list.  If it inserts, thats
        //enough to consider the whole functions call a success.
        if( iterator.Insert(sRealProfileName, cRealProfileName + 1,
                            sProfilePath, cProfilePathSize))
            retVal = TRUE;

    doneWithEntryInPopulateExternalListForCookiesOrBookmarksWithNS4Entries:
        if( hProfileKey != NULL)
            RegCloseKey(hProfileKey);
    }

donePopulateExternalListForCookiesOrBookmarksWithNS4Entries:
    if( hUserRootKey != NULL)
        RegCloseKey( hUserRootKey);

    return retVal;
}

BOOL ImpExpUserProcess::populateExternalListFromFolders(LPTSTR pszPath)
{

    BOOL retval = FALSE;
    TCHAR szFileName[MAX_PATH];
    TCHAR szPathWithWildcards[MAX_PATH];

    ListIterator iterator = (ListIterator)m_ExternalList;

    HANDLE hFind = NULL;
    WIN32_FIND_DATA wfd;

    //
    // what are we looking for?
    //
    if(m_ExternalType == BOOKMARKS)
        MLLoadString(IDS_NETSCAPE_BOOKMARK_FILE,szFileName,ARRAYSIZE(szFileName));
    else
        MLLoadString(IDS_NETSCAPE_COOKIE_FILE,szFileName,ARRAYSIZE(szFileName));

    //
    // prepare the path variable
    //
    StrCpyN(szPathWithWildcards,pszPath,MAX_PATH);
    StrCatBuff(szPathWithWildcards,ALL_FILES_WILDCARD,MAX_PATH);

    //
    // start the find file thing
    //
    hFind = FindFirstFile(szPathWithWildcards,&wfd);

    if (hFind == INVALID_HANDLE_VALUE)
        goto Cleanup;

    do
    {

        //
        // the actual bookmark or cookie file
        //
        TCHAR szFullPath[MAX_PATH];
        int cchFullPath;

        //
        // a "friendly" name for the corresponding profile
        //
        TCHAR szProfileFormat[MAX_PATH];
        TCHAR szProfileName[MAX_PATH];
        int cchProfileName;

        //
        // skip over "." and ".."
        //
        if(!StrCmp(wfd.cFileName, DOT_DIR) ||
           !StrCmp(wfd.cFileName, DOT_DOT_DIR))
            continue;

        //
        // skip over any non-directories
        //
        if (!(wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
            continue;

        //
        // generate the path
        //
#ifndef UNIX
        cchFullPath = wnsprintf(szFullPath,MAX_PATH,TEXT("%s\\%s\\%s"),pszPath,wfd.cFileName,szFileName);
#else
        cchFullPath = wnsprintf(szFullPath,MAX_PATH,TEXT("%s/%s/%s"),pszPath,wfd.cFileName,szFileName);
#endif

        //
        // see if the file actually exists
        //
        if (GetFileAttributes(szFullPath) == 0xFFFFFFFF)
            continue;

        //
        // generate the profile name
        //
        MLLoadString(IDS_FB_FRIENDLY_PROFILE_NAME, szProfileFormat, MAX_PATH);
        cchProfileName = wnsprintf(szProfileName, MAX_PATH, szProfileFormat, wfd.cFileName);

        //
        // add the entry to the list
        //
        iterator.Insert(
            szProfileName,cchProfileName+1,
            szFullPath,cchFullPath+1);

        retval = TRUE;

    } while(FindNextFile(hFind,&wfd));

Cleanup:

    if (hFind)
        FindClose(hFind);

    return retval;

}

BOOL ImpExpUserProcess::populateExternalListWithNSEntriesFallBack()
{

    BOOL retVal = FALSE;

    HKEY hRoot = NULL;
    HKEY hCurrentVersion = NULL;
    HKEY hCurrentVersionMain = NULL;

    TCHAR szUsersDir[64]; // will contain "..\\Users"

    DWORD dwType;
    TCHAR szVersion[64];
    TCHAR szPath[MAX_PATH];
    DWORD cbSize;

    LONG result;

    //
    // Open the root of netscape's HKLM registry hierarchy
    //
    result = RegOpenKeyEx(
         HKEY_LOCAL_MACHINE, 
         NS_FALLBACK_ROOT_REG_KEY,
         0, 
         KEY_READ, 
         &hRoot);
    
    if (result != ERROR_SUCCESS)
        goto Cleanup;

    //
    // Retrieve the "CurrentVersion" value
    //
    cbSize = sizeof(szVersion);
    result = RegQueryValueEx(
        hRoot, 
        NS_FALLBACK_VERSION_REG_VAL, 
        NULL, 
        &dwType, 
        (LPBYTE)szVersion, 
        &cbSize);

    if (result != ERROR_SUCCESS || dwType != REG_SZ)
        goto Cleanup;

    //
    // Open the sub-hierarchy corresponding to the current version
    //
    result = RegOpenKeyEx(
         hRoot, 
         szVersion, 
         0, 
         KEY_READ, 
         &hCurrentVersion);

    if (result != ERROR_SUCCESS)
        goto Cleanup;

    //
    // Open the "main" sub-hierarchy
    //
    result = RegOpenKeyEx(
         hCurrentVersion, 
         NS_FALLBACK_MAIN_REG_VAL, 
         0, 
         KEY_READ, 
         &hCurrentVersionMain);

    if (result != ERROR_SUCCESS)
        goto Cleanup;

    //
    // Retrieve the "Install Directory" value
    //
    cbSize = sizeof(szPath);
    result = RegQueryValueEx(
        hCurrentVersionMain, 
        NS_FALLBACK_INST_REG_VAL, 
        NULL, 
        &dwType, 
        (LPBYTE)szPath, 
        &cbSize);

    if (result != ERROR_SUCCESS || dwType != REG_SZ)
        goto Cleanup;

    //
    // Take a wild guess at where the "Users" dir might be
    //
    MLLoadString(IDS_NETSCAPE_USERS_DIR,szUsersDir,ARRAYSIZE(szUsersDir));
    StrCatBuff(szPath,szUsersDir,ARRAYSIZE(szPath));

    //
    // Fill in the list
    //
    if (populateExternalListFromFolders(szPath))
        retVal = TRUE;

Cleanup:

    if (hRoot)
        RegCloseKey(hRoot);

    if (hCurrentVersion)
        RegCloseKey(hCurrentVersion);

    if (hCurrentVersionMain)
        RegCloseKey(hCurrentVersionMain);

    return retVal;

}


//*************************************************************
//
//  PopulateTreeViewForInternalSelection
//
//  Load a nested list of the favorites folders into memory
//and then into a Tree View.

//  returns FALSE if TreeView is left empty.
BOOL ImpExpUserProcess::PopulateTreeViewForInternalSelection( HWND hTreeView)
{
    ASSERT( m_TransferType != INVALID_TRANSFER);

    TreeView_DeleteAllItems( hTreeView);

    if( !populateInternalList())
        return FALSE;

    return populateTreeViewWithInternalList
            ( hTreeView, (ListIterator)m_InternalList, TVI_ROOT);
}


//*************************************************************
//
//  populateTreeViewWithInternalList
//
//  Loads list entries at 'iterator' below tree view item 'hParent'
//  into 'hTreeView'.  Associates value of each list entry with 
//  the Param of the Tree View node.
//
BOOL ImpExpUserProcess::populateTreeViewWithInternalList
(
    HWND hTreeView,
    ListIterator iterator,
    HTREEITEM hParent
)
{
    BOOL retVal = FALSE;
    
    if( iterator.AtEndOfList())
        goto donePopulateTreeViewWithInternalList;

    TVINSERTSTRUCT newTV;
    HTREEITEM hNew;
    
    //  declare parent and intent to put at end of list.
    newTV.hParent = hParent;
    newTV.hInsertAfter = TVI_LAST;

    // build info struct
    newTV.itemex.mask = TVIF_TEXT
                        | TVIF_PARAM
                        | TVIF_CHILDREN
                        | TVIF_IMAGE
                        | TVIF_SELECTEDIMAGE;

    // give name
    newTV.itemex.cchTextMax = lstrlen( iterator.GetName()) + 1;
    newTV.itemex.pszText = const_cast<LPTSTR>(iterator.GetName());
    
    // associate the necessary data
    newTV.itemex.lParam = (LPARAM)iterator.GetValue();

    // tell tree view if there are any children.
    newTV.itemex.cChildren = 
        iterator.GetSublist().AtEndOfList() == TRUE ? FALSE : TRUE;

    //  use correct icons
    newTV.itemex.iSelectedImage = FOLDER_OPEN ;
    newTV.itemex.iImage = FOLDER_CLOSED ;

    hNew = TreeView_InsertItem( hTreeView, &newTV );

    if( hNew == NULL)
        goto donePopulateTreeViewWithInternalList;

    //  an element has been added, so we should return TRUE.
    retVal = TRUE;

    //  add children
    populateTreeViewWithInternalList( hTreeView, iterator.GetSublist(), hNew );

    //  add siblings
    if( iterator.Next())
        populateTreeViewWithInternalList( hTreeView, iterator, hParent );

donePopulateTreeViewWithInternalList:
    return retVal;

}

BOOL ImpExpUserProcess::ExpandTreeViewRoot ( HWND hTreeView ) 
{

    HTREEITEM hRoot ;

    hRoot = TreeView_GetRoot ( hTreeView ) ;

    if ( hRoot != NULL )
        TreeView_Expand ( hTreeView, hRoot, TVE_EXPAND ) ;
    else
        return FALSE ;

    return TRUE ;

}

//*************************************************************
//
//  SelectInternalSelection
//
//  Gets the data associated with the current selection of
//'hTreeView'.

BOOL ImpExpUserProcess::SelectInternalSelection( HWND hTreeView)
{
    HTREEITEM hSelection = TreeView_GetSelection( hTreeView);
    
    if( hSelection == NULL)
        return FALSE;

    //TVITEM is built up to query the lParam
    //(the lParam has been associated with a pointer to the path value)
    TVITEM TV;
    TV.mask = TVIF_PARAM;
    TV.hItem = hSelection;
    
    if( !TreeView_GetItem( hTreeView, &TV))
        return FALSE;

    m_pSelectedInternal = (LPTSTR)TV.lParam;

    ASSERT( m_pSelectedInternal != NULL);
    
    return TRUE;
}


//*************************************************************
//
//  purgeInternalList
//
//  Wipes out whatever has been loaded in the internal
//target/source list.

void ImpExpUserProcess::purgeInternalList()
{
    // clear the list.
    ListIterator iterator = (ListIterator)m_InternalList;

    while( iterator.Remove())
    {
    }

    m_pSelectedInternal = NULL;
    m_InternalListExternalType = INVALID_EXTERNAL;
    m_InternalListTransferType = INVALID_TRANSFER;
}


//*************************************************************
//
//  populateInternalList
//
//  Builds the internal list for potential internal target/sources.
//  This currently only makes sense for bookmarks, where a favorites
//directory has to be picked.

//  returns TRUE if any elements have been added to the internal list
BOOL ImpExpUserProcess::populateInternalList()
{
    ASSERT( m_ExternalType != INVALID_EXTERNAL);

    if( m_InternalListExternalType == m_ExternalType
        && m_InternalListTransferType == m_TransferType)
        return TRUE;

    purgeInternalList();

    // (could switch on different m_ExternalTypes here)
    if( !populateInternalListForBookmarks())
    {
        purgeInternalList();
        return FALSE;
    }

    m_InternalListExternalType = m_ExternalType;
    m_InternalListTransferType = m_TransferType;
    return TRUE;
}


//*************************************************************
//
//  populateInternalListForBookmarks

//  returns TRUE if any elements have been added to the internal list
BOOL ImpExpUserProcess::populateInternalListForBookmarks()
{
    TCHAR szFavoritesPath[MAX_PATH];

    if( SHGetSpecialFolderPath( NULL, szFavoritesPath, CSIDL_FAVORITES, FALSE)
        && appendSubdirsToInternalList( szFavoritesPath, m_InternalList))
    {
        return TRUE;
    }
    else return FALSE;
}


//*************************************************************
//
//  appendSubdirsToInternalList
//
//  Takes 'sPath' as a specification for a file search.  All
//directories that match that are added to the internal list 
//at 'iterator'.
//  Recursively adds subdirectories found.
//
//typical usage:
//     szPath is "c:\Root\Favorites",
//       finds "c:\Root\Favorites",
//   recursively calls itself with
//     szPath = "c:\Root\Favorites\*.*"
//       finding and recursing into all subdirs

//  returns TRUE if any directories have been added to the internal list
//  Edits the contents of the buffer past the last '\\'.
BOOL ImpExpUserProcess::appendSubdirsToInternalList
(
    LPTSTR sPath, 
    ListIterator iterator
)
{
    BOOL fHaveAddedDirectories = FALSE;

    DWORD cPathLength = lstrlen(sPath);

    HANDLE hEnum;
    WIN32_FIND_DATA currentFile;

    hEnum = FindFirstFile( sPath, &currentFile);

    //example:
    //given: "c:\root\*.*"  (will find all dirs in root)
    //want: "c:\root\"
    //given: "c:\favorites" (will find favorites in root)
    //want: "c:\"
    //  left search to '\\' to find the path of the files to be found.
    while( cPathLength > 0
           && sPath[ --cPathLength] != TCHAR(FILENAME_SEPARATOR))
    {
    }
    cPathLength++;

    if( hEnum == INVALID_HANDLE_VALUE)
        return FALSE;

    do
    {
        DWORD cFileNameLength;
        
        // we only handle directories
        if( !(currentFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            continue;

        // we don't want '.' and '..' to show up.
        if( !StrCmp( currentFile.cFileName, DOT_DIR)
            || !StrCmp( currentFile.cFileName, DOT_DOT_DIR))
            continue;

        cFileNameLength = lstrlen( currentFile.cFileName);
        memcpy( sPath + cPathLength, currentFile.cFileName, cFileNameLength * sizeof(TCHAR));
        sPath[ cPathLength + cFileNameLength] = TCHAR('\0');

        if( iterator.Insert( currentFile.cFileName, cFileNameLength + 1,
                             sPath, cPathLength + cFileNameLength + 1))
        {
            memcpy( sPath + cPathLength + cFileNameLength,
                    ALL_FILES_WILDCARD, sizeof(ALL_FILES_WILDCARD));
            appendSubdirsToInternalList( sPath, iterator.GetSublist());
            // we know now that a directory has been added
            fHaveAddedDirectories = TRUE;
        }
    } while( FindNextFile( hEnum, &currentFile));
    ASSERT(ERROR_NO_MORE_FILES == GetLastError());

    FindClose(hEnum);
    return fHaveAddedDirectories;
}


//*************************************************************
//
//  PerformImpExpProcess
//
//  Once everything is set up right, this should do the trick.

void ImpExpUserProcess::PerformImpExpProcess(HWND hwnd)
{
    ASSERT( GetExternalType() != INVALID_EXTERNAL);
    ASSERT( GetTransferType() != INVALID_TRANSFER);
    ASSERT( (GetExternalType() == BOOKMARKS) ? (GetInternalSelection() != NULL) : TRUE);

    HCURSOR hOldCursor;

    //
    // This could take a while, so show an hourglass cursor
    //
    hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

    switch( GetExternalType())
    {
    case COOKIES:

        switch( GetTransferType())
        {
        case IMPORT:
            if (ImportCookieFile(m_szFileName))
            {
                MLShellMessageBox(
                    hwnd, 
                    MAKEINTRESOURCE(IDS_IMPORTSUCCESS_COOK), 
                    MAKEINTRESOURCE(IDS_CONFIRM_IMPTTL_COOK),
                    MB_OK);
            }
            else
            {
                MLShellMessageBox(
                    hwnd, 
                    MAKEINTRESOURCE(IDS_IMPORTFAILURE_COOK), 
                    MAKEINTRESOURCE(IDS_CONFIRM_IMPTTL_COOK),
                    MB_OK);
            }
            break;

        case EXPORT:
            if (SUCCEEDED(SHPathPrepareForWriteWrap(hwnd, NULL, m_szFileName, FO_COPY, (SHPPFW_DEFAULT | SHPPFW_IGNOREFILENAME))))
            {
                //  FALSE specifies that we will overwrite cookies
                if (ExportCookieFile(m_szFileName, FALSE ))
                {
                    MLShellMessageBox(
                        hwnd, 
                        MAKEINTRESOURCE(IDS_EXPORTSUCCESS_COOK), 
                        MAKEINTRESOURCE(IDS_CONFIRM_EXPTTL_COOK),
                        MB_OK);
                }
                else
                {
                    MLShellMessageBox(
                        hwnd, 
                        MAKEINTRESOURCE(IDS_EXPORTFAILURE_COOK), 
                        MAKEINTRESOURCE(IDS_CONFIRM_EXPTTL_COOK),
                        MB_OK);
                }
            }
            break;

        default:
            ASSERT(0);
            
        }
        break;
        
    case BOOKMARKS:

        DoImportOrExport(
            GetTransferType()==IMPORT,
            m_pSelectedInternal,
            m_szFileName,
            FALSE);

        break;

    default:
        ASSERT(0);

    }

    //
    // Put the old cursor back when finished
    //
    SetCursor(hOldCursor);

}


//*************************************************************
//*************************************************************
//
//   ImpExpUserDlg
//
//  Handles the user interface side of things, building
//  up an ImpExpUserProcess then executing it.
//  The dialog procedures below will all have a return value
//  which can be set to something besides FALSE if used, or left
//  as FALSE if not used.  Since only one section of code should
//  attempt to give the return value a value before returning,
//  class RetVal is set up to throw an assertion if two pieces
//  of code intended to pass back a return value at the same
//  time.

class ReturnValue
{

private:
    BOOL_PTR m_value;

public:
    ReturnValue()
    { 
        m_value = FALSE;
    }
    
    BOOL_PTR operator =(BOOL_PTR newVal)
    {
        ASSERT( m_value == FALSE);
        m_value = newVal;
        return m_value;
    }
    
    operator BOOL_PTR ()
    {
        return m_value;
    }
};

class ImpExpUserDlg
{

private:

    static HIMAGELIST m_himl ;
    static BOOL InitImageList ( HWND hwndTree ) ;   
    static BOOL DestroyImageList ( HWND hwndTree ) ;    

    static HFONT m_hfont ;
    static BOOL InitFont ( HWND hwndStatic ) ;
    static BOOL DestroyFont ( HWND hwndStatic ) ;

    //  A sheet knows its resource ID and what process
    //it contributes to.
    struct SheetData
    {
        int _idPage;
        ImpExpUserProcess* _pImpExp;

        SheetData( int idPage, ImpExpUserProcess* pImpExp )
        : _idPage( idPage ), _pImpExp( pImpExp )
        {
        }
    };
    //
    //  InitializePropertySheetPage() will associate a dialog 
    //  with an allocated copy of SheetData, which will be
    //  found at PSN_SETACTIVE with and stored with SetWindowLong.
    //  The allocated SheetData will be cleaned up by callback
    //  procedure PropertySheetPageProc().
    //
    //  Callback functions sure are a drag for maintaining identity.
    //  GetWindowLong and SetWindowLong will be used to keep tabs
    //  on who is who, setting 'ghost' member variables.
    //
    // 'ghost' SheetData*         This;
    // 'ghost' ImpExpUserProcess* m_pImpExp;
    // 'ghost' DWORD              m_idPage;
    //
    //  CommonDialogProc retrieves the 'ghost' values and does other 
    //  shared behavior.
    //
    static DWORD CommonDialogProc
    ( 
        IN HWND hwndDlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam,
        OUT ImpExpUserProcess** ppImpExp, OUT DWORD* pPageId,
        IN OUT ReturnValue& retVal
    );

    static void InitializePropertySheetPage( PROPSHEETPAGE* psp, DWORD idDialogTemplate, DWORD idTitle, DWORD idSubTitle,DLGPROC dlgProc, ImpExpUserProcess* lParam);
    static UINT CALLBACK PropertySheetPageProc( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp);

    //  some dialog procedures
    static BOOL_PTR CALLBACK Wizard97DlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
    static BOOL_PTR CALLBACK TransferTypeDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
    static BOOL_PTR CALLBACK InternalDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
    static BOOL_PTR CALLBACK ExternalDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);

    static void HandleTransferTypeChange ( HWND hwndDlg, ImpExpUserProcess* m_pImpExp, UINT iSelect ) ;

public:
    static BOOL RunNewDialogProcess( HWND hParent ) ;

};

HIMAGELIST ImpExpUserDlg::m_himl = NULL ;

BOOL ImpExpUserDlg::InitImageList ( HWND hwndTree )
{

    //
    // Code to retrieve icons for open and closed folders
    // was based on code in private/samples/sampview/utility.cpp.
    //

    TCHAR       szFolder[MAX_PATH];
    SHFILEINFO  sfi;
    HIMAGELIST  himlOld ;
    DWORD       dwRet ;

    // create the image list
    m_himl = ImageList_Create ( GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLORDDB, 2, 2 ) ;

    if ( m_himl == NULL )
        return FALSE ;

    ImageList_SetBkColor( m_himl, GetSysColor(COLOR_WINDOW) ) ;

    // add the closed folder icon
    GetWindowsDirectory(szFolder, MAX_PATH);
    SHGetFileInfo( szFolder,
                   0,
                   &sfi,
                   sizeof(sfi),
                   SHGFI_ICON | SHGFI_SMALLICON);
    dwRet = ImageList_AddIcon(m_himl, sfi.hIcon);
    ASSERT ( dwRet == FOLDER_CLOSED ) ;

    // add the open folder icon
    SHGetFileInfo( szFolder,
                   0,
                   &sfi,
                   sizeof(sfi),
                   SHGFI_ICON | SHGFI_SMALLICON | SHGFI_OPENICON);
    dwRet = ImageList_AddIcon(m_himl, sfi.hIcon);
    ASSERT ( dwRet == FOLDER_OPEN ) ;

    himlOld = TreeView_SetImageList( hwndTree, m_himl, TVSIL_NORMAL );

    if ( himlOld != NULL )
    {
        BOOL fOk ;
        fOk = ImageList_Destroy ( himlOld ) ;
        ASSERT ( fOk ) ;
    }

    return TRUE ;

}

BOOL ImpExpUserDlg::DestroyImageList ( HWND hwndTree ) 
{
    HIMAGELIST himlOld ;

    himlOld = TreeView_SetImageList( hwndTree, NULL, TVSIL_NORMAL );

    if ( himlOld != NULL )
    {
        BOOL fOk ;
        fOk = ImageList_Destroy ( himlOld ) ;
        ASSERT ( fOk ) ;
    }

    return TRUE ;
}


HFONT ImpExpUserDlg::m_hfont = NULL ;

BOOL ImpExpUserDlg::InitFont ( HWND hwndStatic ) 
{

    HDC hdc = GetDC ( hwndStatic ) ;

    if ( hdc == NULL )
        return FALSE ;

    LOGFONT lf;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfQuality = DEFAULT_QUALITY;
    lf.lfPitchAndFamily = DEFAULT_PITCH;
    lf.lfItalic = 0;
    lf.lfWeight = FW_BOLD;
    lf.lfStrikeOut = 0;
    lf.lfUnderline = 0;
    lf.lfWidth = 0;
    lf.lfHeight = -MulDiv(13, GetDeviceCaps(hdc, LOGPIXELSY), 72);

    LOGFONT lfTmp;
    HFONT   hFontOrig = (HFONT)SendMessage(hwndStatic, WM_GETFONT, (WPARAM)0, (LPARAM)0);
    if (hFontOrig && GetObject(hFontOrig, sizeof(lfTmp), &lfTmp))
    {
        lf.lfCharSet = lfTmp.lfCharSet;
        StrCpyN(lf.lfFaceName, lfTmp.lfFaceName, LF_FACESIZE);
    }
    else
    {
        lf.lfCharSet = GetTextCharset(hdc);
        StrCpyN(lf.lfFaceName, TEXT("MS Shell Dlg"), LF_FACESIZE);
    }

    m_hfont = CreateFontIndirect(&lf);

    if ( m_hfont == NULL )
    {
        ReleaseDC(hwndStatic, hdc);
        return FALSE ;
    }

    SendMessage ( hwndStatic, WM_SETFONT, (WPARAM)m_hfont, MAKELPARAM(FALSE, 0) ) ;

    ReleaseDC ( hwndStatic,hdc ) ;

    return TRUE ;

}

BOOL ImpExpUserDlg::DestroyFont ( HWND hwndDlg )
{

    if ( m_hfont )
        DeleteObject ( m_hfont ) ;

    return TRUE ;
}

void ImpExpUserDlg::InitializePropertySheetPage
(
    PROPSHEETPAGE* psp, 
    DWORD idDialogTemplate,
    DWORD idTitle,
    DWORD idSubTitle,
    DLGPROC dlgProc,
    ImpExpUserProcess* lParam
)
{
    psp->dwFlags |= PSP_USECALLBACK | PSP_USETITLE;
    psp->hInstance = MLGetHinst();
    psp->pszTemplate = MAKEINTRESOURCE(idDialogTemplate);
    psp->pfnDlgProc = dlgProc;
    psp->lParam = (LPARAM)(new SheetData(idDialogTemplate,lParam));
    psp->pfnCallback = PropertySheetPageProc;
    psp->pszHeaderTitle = MAKEINTRESOURCE(idTitle);
    psp->pszHeaderSubTitle = MAKEINTRESOURCE(idSubTitle);
    psp->pszTitle = MAKEINTRESOURCE(IDS_IMPEXP_CAPTION);

    if ( idDialogTemplate == IDD_IMPEXPWELCOME ||
         idDialogTemplate == IDD_IMPEXPCOMPLETE )
    {
        psp->dwFlags |= PSP_HIDEHEADER; 
    }
    else
    {
        psp->dwFlags |= (PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
    }

}


UINT CALLBACK ImpExpUserDlg::PropertySheetPageProc
(
    HWND hwnd, 
    UINT uMsg, 
    LPPROPSHEETPAGE ppsp
)
{

    switch(uMsg)
    {

    case PSPCB_CREATE:
        break;

    case PSPCB_RELEASE:
        delete (SheetData*)ppsp->lParam;
        ppsp->lParam = NULL;
        break;

    default:
        break;

    }

    return TRUE ;
}


//*************************************************************
//
//  RunNewDialogProcess
//
//  Runs the Import Export Wizard.

BOOL_PTR CALLBACK TEMP(
  HWND hwndDlg,  // handle to dialog box
  UINT uMsg,     // message
  WPARAM wParam, // first message parameter
  LPARAM lParam  // second message parameter
)
{
    return (uMsg==WM_INITDIALOG);
}

BOOL ImpExpUserDlg::RunNewDialogProcess(HWND hParent)
{


    const int numPages = 9;
    ImpExpUserProcess* pImpExp = new ImpExpUserProcess();

    if( pImpExp == NULL)
        return FALSE;
        
    PROPSHEETPAGE pspOld[numPages];
    PROPSHEETPAGE* psp = pspOld;

    // dwSkip is set in Whistler_AllocatePropertySheetPage to whatever the size of Whistler's PROPSHEETPAGE
    // is. 0x34 last I checked. (IE5 PROPSHEETPAGE is 0x30)
    DWORD dwSkip = sizeof(PROPSHEETPAGE);
    if (IsOS(OS_WHISTLERORGREATER))
    {
        PROPSHEETPAGE* psp2 = Whistler_AllocatePropertySheetPage(numPages, &dwSkip);
        if (psp2)
        {
            psp = psp2;
        }
    }
    if (psp==pspOld)
    {
        for (int i=0; i<numPages; i++)
        {
            memset(&psp[i], 0, sizeof(PROPSHEETPAGE));
            psp[i].dwSize = sizeof(PROPSHEETPAGE);
        }
    }
     
    PROPSHEETHEADER psh;

    PBYTE pspNext = (PBYTE)psp;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPWELCOME,        0,                              0,                                  Wizard97DlgProc, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPTRANSFERTYPE,   IDS_IMPEXPTRANSFERTYPE_TITLE,   IDS_IMPEXPTRANSFERTYPE_SUBTITLE,    TransferTypeDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPIMPFAVSRC,      IDS_IMPEXPIMPFAVSRC_TITLE,      IDS_IMPEXPIMPFAVSRC_SUBTITLE,       ExternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPIMPFAVDES,      IDS_IMPEXPIMPFAVDES_TITLE,      IDS_IMPEXPIMPFAVDES_SUBTITLE,       InternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPEXPFAVSRC,      IDS_IMPEXPEXPFAVSRC_TITLE,      IDS_IMPEXPEXPFAVSRC_SUBTITLE,       InternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPEXPFAVDES,      IDS_IMPEXPEXPFAVDES_TITLE,      IDS_IMPEXPEXPFAVDES_SUBTITLE,       ExternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPIMPCKSRC,       IDS_IMPEXPIMPCKSRC_TITLE,       IDS_IMPEXPIMPCKSRC_SUBTITLE,        ExternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPEXPCKDES,       IDS_IMPEXPEXPCKDES_TITLE,       IDS_IMPEXPEXPCKDES_SUBTITLE,        ExternalDlg, pImpExp );
    pspNext += dwSkip;
    InitializePropertySheetPage( (PROPSHEETPAGE*)pspNext, IDD_IMPEXPCOMPLETE,       0,                              0,                                  Wizard97DlgProc, pImpExp );

    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_WIZARD97 | PSH_PROPSHEETPAGE | PSH_HEADER | PSH_WATERMARK ; 
    psh.hwndParent = hParent;
    psh.hInstance = MLGetHinst();
    psh.pszCaption = MAKEINTRESOURCE(IDS_IMPEXP_CAPTION);
    psh.nPages = numPages;
    psh.nStartPage = 0;
    psh.ppsp = psp;
    psh.pszbmWatermark = MAKEINTRESOURCE(IDB_IMPEXPWATERMARK);
    psh.pszbmHeader = MAKEINTRESOURCE(IDB_IMPEXPHEADER);

    ULONG_PTR uCookie = 0;
    SHActivateContext(&uCookie);
    int iResult = (int)PropertySheet(&psh) ;
    if (uCookie)
    {
        SHDeactivateContext(uCookie);
    }
    delete pImpExp;
    if (psp!=pspOld)
    {
        HeapFree(GetProcessHeap(), NULL, psp);
    }
    return iResult;

}


//*************************************************************
//
//  CommonDialogProc
//
//  Prepares 'ghost' member variables of the user dialog process,
//  handles ordering details of wizard pages and initializes common
//  dialog elements.
//
//  retVal passes through CommonDialogProc so that it can be set
//  if necessary.  Clients of CommonDialogProc should not need
//  to specify a new return value if CommonDialogProc has specified
//  a non-FALSE return value.
//
//  If CommonDialogProc returns FALSE dialog procedure should
//  considered 'msg' handled and return retVal immediately.
//
//  If this dialog has yet to receive WM_INITDIALOG, the 'ghost'
//  values will be zero (and invalid).
//

DWORD ImpExpUserDlg::CommonDialogProc
( 
    IN HWND hwndDlg, 
    IN UINT msg,
    IN WPARAM wParam,
    IN LPARAM lParam,
    OUT ImpExpUserProcess** ppImpExp,
    OUT DWORD* pPageId,
    ReturnValue& retVal
)
{

    SheetData* sheetData;
    ImpExpUserProcess* m_pImpExp = NULL;
    DWORD m_idPage = 0;

    //
    // Do init-dialog stuff
    //
    if ( WM_INITDIALOG == msg )
    {
        sheetData = (SheetData*)(((PROPSHEETPAGE*)lParam)->lParam);
        SetWindowLongPtr( hwndDlg, DWLP_USER, (LONG_PTR)sheetData);
    }

    //
    // Initialize the sheetData field
    //
    sheetData = (SheetData*)GetWindowLongPtr( hwndDlg, DWLP_USER ) ;
    if ( sheetData != NULL )
    {
        m_pImpExp = *ppImpExp = sheetData->_pImpExp;
        m_idPage = *pPageId = sheetData->_idPage;
    }

    //
    // Next, we check to make sure we're on the correct page.  If not, simply
    // return -1 and the wizard will automatically advance to the next page.
    //
    if( WM_NOTIFY == msg && PSN_SETACTIVE == ((LPNMHDR)lParam)->code )
    {

        BOOL fPageValidation = TRUE ;

        switch( m_idPage )
        {

        case IDD_IMPEXPWELCOME:
        case IDD_IMPEXPTRANSFERTYPE:
        case IDD_IMPEXPCOMPLETE:                    
            break;

        case IDD_IMPEXPIMPFAVSRC:
        case IDD_IMPEXPIMPFAVDES:
            if(m_pImpExp->GetTransferType() != IMPORT || m_pImpExp->GetExternalType() != BOOKMARKS)
                fPageValidation = FALSE;
            break;
    
        case IDD_IMPEXPEXPFAVSRC:
        case IDD_IMPEXPEXPFAVDES:
            if(m_pImpExp->GetTransferType() != EXPORT || m_pImpExp->GetExternalType() != BOOKMARKS)
                fPageValidation = FALSE;
            break;

        case IDD_IMPEXPIMPCKSRC:
            if(m_pImpExp->GetTransferType() != IMPORT || m_pImpExp->GetExternalType() != COOKIES)
                fPageValidation = FALSE;
            break;

        case IDD_IMPEXPEXPCKDES:
            if(m_pImpExp->GetTransferType() != EXPORT || m_pImpExp->GetExternalType() != COOKIES)
                fPageValidation = FALSE;
            break;
        }

        SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, fPageValidation ? 0 : -1 ) ;
        retVal = TRUE ;
        
        if ( ! fPageValidation )
            return FALSE ;

    }

    //
    // Initialize fonts and image lists (if needed)
    //
    if ( WM_NOTIFY == msg )
    {

        HWND hwndTitle = GetDlgItem ( hwndDlg, IDC_IMPEXPTITLETEXT )  ;
        HWND hwndTree = GetDlgItem ( hwndDlg, IDC_IMPEXPFAVTREE )  ;

        switch ( ((LPNMHDR)lParam)->code )
        {

        case PSN_SETACTIVE:
        
            if ( hwndTitle )
                InitFont ( hwndTitle ) ;

            if ( hwndTree )
                InitImageList( hwndTree ) ;
    
            break ;

        case PSN_KILLACTIVE:
        case PSN_QUERYCANCEL:

            if ( hwndTitle )
                DestroyFont ( hwndTitle ) ;

            if ( hwndTree )
                DestroyImageList( hwndTree ) ;

            break;

        }
    
    }

    if( WM_NOTIFY == msg && PSN_SETACTIVE == ((LPNMHDR)lParam)->code )
    {

        HWND hwndParent = GetParent( hwndDlg);

        switch( m_idPage )
        {
        case IDD_IMPEXPWELCOME:
            PropSheet_SetWizButtons( hwndParent, PSWIZB_NEXT );
            break;
            
        case IDD_IMPEXPCOMPLETE:
            {

                UINT idText ;
                const TCHAR *szInsert = m_pImpExp->m_szFileName ;
                TCHAR szRawString[1024] ;
                TCHAR szRealString[1024] ;

                //
                // First, we need to figure out which string should 
                // be used to describe what the wizard is going to 
                // do (for example "Import the cookies from...")
                //
                if ( m_pImpExp->GetTransferType() == IMPORT )
                {
                    if ( m_pImpExp->GetExternalType() == COOKIES )
                        idText = IDS_IMPEXP_COMPLETE_IMPCK ;
                    else
                        idText = IDS_IMPEXP_COMPLETE_IMPFV ;
                }
                else
                {
                    if ( m_pImpExp->GetExternalType() == COOKIES )
                        idText = IDS_IMPEXP_COMPLETE_EXPCK ;
                    else
                        idText = IDS_IMPEXP_COMPLETE_EXPFV ;
                }

                LoadString(MLGetHinst(), idText, szRawString, 1024);

                wnsprintf(szRealString, 1024, szRawString, szInsert);

                //
                // Set the text in the listview, and do all the other magic to make
                // the tooltips work, etc.
                //
                SetListViewToString(GetDlgItem(hwndDlg,IDC_IMPEXPCOMPLETECONFIRM), szRealString);

                //
                // The SetListViewToString function helpfully sets the background color to
                // gray instead of the default (white).  But we actually want it white, so 
                // let's reset it here.
                // 
                ListView_SetBkColor(GetDlgItem(hwndDlg,IDC_IMPEXPCOMPLETECONFIRM), GetSysColor(COLOR_WINDOW));
                ListView_SetTextBkColor(GetDlgItem(hwndDlg,IDC_IMPEXPCOMPLETECONFIRM), GetSysColor(COLOR_WINDOW));
                
                PropSheet_SetWizButtons(hwndParent, PSWIZB_BACK|PSWIZB_FINISH);

            }
            break;
            
        default:
            PropSheet_SetWizButtons( hwndParent, PSWIZB_NEXT | PSWIZB_BACK );
            break;
        }

    }

    return TRUE ;
}


//*************************************************************
//
//  Wizard97DlgProc
//
//  Dialog proc for welcome and complete pages.
//  

BOOL_PTR CALLBACK ImpExpUserDlg::Wizard97DlgProc
(
    HWND hwndDlg, 
    UINT msg, 
    WPARAM wParam, 
    LPARAM lParam
)
{
    ReturnValue retVal;

    ImpExpUserProcess* m_pImpExp = NULL;
    DWORD m_idPage = 0;

    if( !CommonDialogProc( hwndDlg, msg, wParam, lParam, 
                        &m_pImpExp, &m_idPage, retVal))
    {
        return retVal;
    }

    if( m_idPage == IDD_IMPEXPCOMPLETE 
        && msg == WM_NOTIFY
        && PSN_WIZFINISH == ((LPNMHDR)lParam)->code)

    m_pImpExp->PerformImpExpProcess(hwndDlg);

    return retVal;;
}


//*************************************************************
//
//  TransferTypeDlg
//
//  Dialog proc for dialog where user picks transfer type
//  (import vs. export), (cookies vs. bookmarks)

BOOL_PTR CALLBACK ImpExpUserDlg::TransferTypeDlg
(
    HWND hwndDlg, 
    UINT msg, 
    WPARAM wParam, 
    LPARAM lParam
)
{
    ReturnValue retVal;

    ImpExpUserProcess* m_pImpExp = NULL;
    DWORD m_idPage = 0;

    if( !CommonDialogProc( hwndDlg, msg, wParam, lParam, 
                        &m_pImpExp, &m_idPage, retVal))
    {
        return retVal;
    }

    HWND hwndDlgItem;
   
    switch( msg)
    {
    case WM_INITDIALOG:
        {
            hwndDlgItem = GetDlgItem( hwndDlg, IDC_IMPEXPACTIONLISTBOX);

            LRESULT index;
            TCHAR szBuffer[MAX_PATH];
            const DWORD cbSize = MAX_PATH;

            if( MLLoadString( IDS_IMPFAVORITES, szBuffer, cbSize))
            {
                index = ListBox_AddString( hwndDlgItem, szBuffer);
                ListBox_SetItemData( hwndDlgItem, index, IDS_IMPFAVORITES);
            }

            if( MLLoadString( IDS_EXPFAVORITES, szBuffer, cbSize))
            {
                index = ListBox_AddString( hwndDlgItem, szBuffer);
                ListBox_SetItemData( hwndDlgItem, index, IDS_EXPFAVORITES);
            }
            
            if( MLLoadString( IDS_IMPCOOKIES, szBuffer, cbSize))
            {
                index = ListBox_AddString( hwndDlgItem, szBuffer);
                ListBox_SetItemData( hwndDlgItem, index, IDS_IMPCOOKIES);
            }
            
            if( MLLoadString( IDS_EXPCOOKIES, szBuffer, cbSize))
            {
                index = ListBox_AddString( hwndDlgItem, szBuffer);
                ListBox_SetItemData( hwndDlgItem, index, IDS_EXPCOOKIES);
            }

            // Select the first list item, by default
            ListBox_SetCurSel(hwndDlgItem, 0);
            HandleTransferTypeChange(hwndDlg, m_pImpExp, IDS_IMPFAVORITES);

        }  // end of WM_INITDIALOG
        break;
        
    case WM_COMMAND:
        //  when the user selects an option, choose it and
        //and update the description box.
        hwndDlgItem = GetDlgItem(hwndDlg, IDC_IMPEXPACTIONLISTBOX);

        if(hwndDlgItem == (HWND)lParam
           && HIWORD(wParam) == LBN_SELCHANGE)
        {

            //  find out which string resource was selected.
            LRESULT index = ListBox_GetCurSel(hwndDlgItem);
            LRESULT selection = ListBox_GetItemData(hwndDlgItem, index);

            HandleTransferTypeChange ( hwndDlg, m_pImpExp, (UINT)selection ) ;
            retVal = TRUE;

        }
        break;
        
    case WM_NOTIFY:

        //
        //  Prevent advancement until user has made valid choices
        //
        if( ((LPNMHDR)lParam)->code == PSN_WIZNEXT && m_pImpExp
            &&  (m_pImpExp->GetExternalType() == INVALID_EXTERNAL
                 || m_pImpExp->GetTransferType() == INVALID_TRANSFER))
        {
            SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, -1);
            retVal = TRUE;
        }

        //
        // otherwise, set the filename to nul (so we get the default)
        // and allow default navigation behavior
        //

        if (m_pImpExp)
            m_pImpExp->m_szFileName[0] = TEXT('\0');

        break;
    }

    return retVal;
}

void ImpExpUserDlg::HandleTransferTypeChange ( HWND hwndDlg, ImpExpUserProcess* pImpExp, UINT iSelect )
{

    TCHAR szBuffer[MAX_PATH];
    const DWORD cbSize = MAX_PATH;

    if (pImpExp)
    {
        //
        //  Note:  The description of each option has a resource id
        //  which is one higher than the resource id of the option name.
        //
        switch( iSelect )
        {
        case IDS_IMPFAVORITES:
            if( MLLoadString( IDS_IMPFAVORITES + 1, szBuffer, cbSize ) )
                SetWindowText( GetDlgItem( hwndDlg, IDC_IMPEXPACTIONDESCSTATIC ),
                               szBuffer );
            pImpExp->SelectExternalType( BOOKMARKS );
            pImpExp->SelectTransferType( IMPORT );
            break;
        
        case IDS_EXPFAVORITES:
            if( MLLoadString( IDS_EXPFAVORITES + 1, szBuffer, cbSize ) )
                SetWindowText( GetDlgItem( hwndDlg, IDC_IMPEXPACTIONDESCSTATIC ),
                               szBuffer );
            pImpExp->SelectExternalType( BOOKMARKS );
            pImpExp->SelectTransferType( EXPORT );
            break;
        
        case IDS_IMPCOOKIES:
            if( MLLoadString( IDS_IMPCOOKIES + 1, szBuffer, cbSize))
                SetWindowText( GetDlgItem( hwndDlg, IDC_IMPEXPACTIONDESCSTATIC),
                               szBuffer);
            pImpExp->SelectExternalType( COOKIES);
            pImpExp->SelectTransferType( IMPORT);
            break;

        case IDS_EXPCOOKIES:
            if( MLLoadString( IDS_EXPCOOKIES + 1, szBuffer, cbSize))
                SetWindowText( GetDlgItem( hwndDlg, IDC_IMPEXPACTIONDESCSTATIC),
                               szBuffer);
            pImpExp->SelectExternalType( COOKIES);
            pImpExp->SelectTransferType( EXPORT);
            break;
        }
    }

}


//*************************************************************
//
//  InternalDlg
//
//  Allows user to pick internal target/source from tree view.

BOOL_PTR CALLBACK ImpExpUserDlg::InternalDlg
(
    HWND hwndDlg, 
    UINT msg, 
    WPARAM wParam, 
    LPARAM lParam
)
{
    ReturnValue retVal;

    ImpExpUserProcess* m_pImpExp = NULL;
    DWORD m_idPage = 0;

    if( !CommonDialogProc( hwndDlg, msg, wParam, lParam, 
                        &m_pImpExp, &m_idPage, retVal))
    {
        return retVal;
    }


    HWND hwndDlgItem;

    switch( msg)
    {
    case WM_INITDIALOG:

        //
        // Populate the tree control
        //
        hwndDlgItem = GetDlgItem(hwndDlg, IDC_IMPEXPFAVTREE);
        if ( hwndDlgItem )
        {
            if (m_pImpExp)
            {
                m_pImpExp->PopulateTreeViewForInternalSelection(hwndDlgItem);
                m_pImpExp->ExpandTreeViewRoot ( hwndDlgItem ) ;
            }
        }
        else
            ASSERT(0);

        return TRUE;


    case WM_NOTIFY:
        switch( ((LPNMHDR)lParam)->code)
        {

        case PSN_WIZNEXT:

            //  Only allow user to go to next if there is a valid selection.
            if( !m_pImpExp->SelectInternalSelection(GetDlgItem(hwndDlg,IDC_IMPEXPFAVTREE)) )
            {
                SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, -1);
                retVal = TRUE;
            }

        }
    }

    return retVal;
}

BOOL IsValidFileOrURL(LPTSTR szFileOrURL)
{
    if (szFileOrURL == NULL)
        return FALSE;

    //
    // any URL is ok
    //
    if (PathIsURL(szFileOrURL))
        return TRUE;

    //
    // just a directory is no good, we need a filename too
    //
    if (PathIsDirectory(szFileOrURL))
        return FALSE;

    //
    // just a filename is no good, we need a directory too
    //
    if (PathIsFileSpec(szFileOrURL))
        return FALSE;

    //
    // relative paths are no good
    //
    if (PathIsRelative(szFileOrURL))
        return FALSE;

    //
    // now make sure it parses correctly
    //
    if (PathFindFileName(szFileOrURL) == szFileOrURL)
        return FALSE;

    return TRUE;

}

//*************************************************************
//
//  ExternalDlg
//
//  Allows user to pick external target/source from list box
//or manual browse.

BOOL_PTR CALLBACK ImpExpUserDlg::ExternalDlg
(
    HWND hwndDlg, 
    UINT msg, 
    WPARAM wParam, 
    LPARAM lParam
)
{
    ReturnValue retVal;

    ImpExpUserProcess* m_pImpExp = NULL;
    DWORD m_idPage = 0;

    if( !CommonDialogProc( hwndDlg, msg, wParam, lParam, 
                           &m_pImpExp, &m_idPage, retVal))
    {
        return retVal;
    }

    HWND hwndDlgItem;
    
    switch(msg)
    {

    case WM_COMMAND:

        hwndDlgItem = (HWND) lParam;
        if( HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_IMPEXPBROWSE)
        {
            OPENFILENAME ofn;
            TCHAR szFile[MAX_PATH];
            TCHAR szTitle[MAX_PATH];
            TCHAR szFilter[MAX_PATH];
            TCHAR szInitialPath[MAX_PATH];
            int i;

            ZeroMemory(&ofn, sizeof(OPENFILENAME));
            ofn.lStructSize = sizeof(OPENFILENAME);
            ofn.hwndOwner = hwndDlg;
            ofn.hInstance = MLGetHinst();
            ofn.lpstrFilter = szFilter;
            ofn.nFilterIndex = 1;
            ofn.lpstrCustomFilter = NULL;
            ofn.lpstrFile = szFile;
            ofn.nMaxFile = sizeof(szFile);
            ofn.lpstrFileTitle = NULL;
            ofn.lpstrInitialDir = szInitialPath;
            ofn.lpstrTitle = szTitle;
            ofn.lpstrDefExt = (m_pImpExp->GetExternalType()==COOKIES) ? TEXT("txt") : TEXT("htm");

            GetDlgItemText(hwndDlg, IDC_IMPEXPMANUAL, szInitialPath, ARRAYSIZE(szFile));
            szFile[0] = 0;

            if (PathIsDirectory(szInitialPath))
            {
                ofn.lpstrInitialDir = szInitialPath;
                szFile[0] = TEXT('\0');    
            }
            else
            {
                TCHAR *pchFilePart;

                pchFilePart = PathFindFileName(szInitialPath);

                if (pchFilePart == szInitialPath || pchFilePart == NULL)
                {

                    if (PathIsFileSpec(szInitialPath))
                        StrCpyN(szFile,szInitialPath,MAX_PATH);
                    else
                        szFile[0] = TEXT('\0');

                    ofn.lpstrInitialDir = szInitialPath;
                    SHGetSpecialFolderPath(NULL,szInitialPath,CSIDL_DESKTOP,FALSE);

                }
                else
                {
                    pchFilePart[-1] = TEXT('\0');
                    ofn.lpstrInitialDir = szInitialPath;
                    StrCpyN(szFile,pchFilePart,MAX_PATH);
                }

            }
            
            //
            // Work out the title and the filter strings
            //
            if (m_pImpExp->GetExternalType() == BOOKMARKS)
            {
                MLLoadShellLangString(IDS_IMPEXP_CHOSEBOOKMARKFILE,szTitle,MAX_PATH);
                MLLoadShellLangString(IDS_IMPEXP_BOOKMARKFILTER,szFilter,MAX_PATH);
            }
            else
            {
                MLLoadShellLangString(IDS_IMPEXP_CHOSECOOKIEFILE,szTitle,MAX_PATH);
                MLLoadShellLangString(IDS_IMPEXP_COOKIEFILTER,szFilter,MAX_PATH);
            }

            //
            // Search and replace '@' with nul in the filter string
            //
            for (i=0; szFilter[i]; i++)
                if (szFilter[i]==TEXT('@'))
                    szFilter[i]=TEXT('\0');

            //
            // Set the flags for openfilename
            //
            ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY ;
            if (m_pImpExp->GetTransferType() == IMPORT)
                ofn.Flags |= (OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);

            //
            // Show the dialog
            //
            if(GetSaveFileName(&ofn))
                if(SetWindowText(GetDlgItem(hwndDlg, IDC_IMPEXPMANUAL), ofn.lpstrFile))
                {
                    Button_SetCheck(GetDlgItem( hwndDlg, IDC_IMPEXPRADIOFILE), BST_CHECKED);
                    Button_SetCheck(GetDlgItem( hwndDlg, IDC_IMPEXPRADIOAPP), BST_UNCHECKED);
                }

            retVal = TRUE;
        }
        break;

    case WM_NOTIFY:
        switch( ((LPNMHDR)lParam)->code )
        {

        case PSN_SETACTIVE:
            {

                TCHAR sBuffer[MAX_PATH];
                DWORD cbSize = ARRAYSIZE(sBuffer);

                hwndDlgItem = GetDlgItem( hwndDlg, IDC_IMPEXPEXTERNALCOMBO );
                
                //
                // Load the "application list" into the combo box.
                // If the list is empty, then disable the combo box, 
                // disable the associated radio button, and select the
                // "to/from file" option (the second radio button).
                //
                if( hwndDlgItem != NULL
                    && m_pImpExp && m_pImpExp->PopulateComboBoxForExternalSelection( hwndDlgItem ) )
                {
                    EnableWindow ( GetDlgItem(hwndDlg, IDC_IMPEXPRADIOAPP), TRUE ) ;
                    EnableWindow ( hwndDlgItem, TRUE ) ;
                    Button_SetCheck( GetDlgItem( hwndDlg, IDC_IMPEXPRADIOAPP), BST_CHECKED);
                    Button_SetCheck( GetDlgItem( hwndDlg, IDC_IMPEXPRADIOFILE), BST_UNCHECKED);
                }
                else if ( hwndDlgItem != NULL)
                {
                    EnableWindow ( GetDlgItem(hwndDlg, IDC_IMPEXPRADIOAPP), FALSE ) ;
                    EnableWindow( hwndDlgItem, FALSE ) ;
                    Button_SetCheck( GetDlgItem( hwndDlg, IDC_IMPEXPRADIOFILE), BST_CHECKED);
                    Button_SetCheck( GetDlgItem( hwndDlg, IDC_IMPEXPRADIOAPP), BST_UNCHECKED);
                }
                
                //  Put a default value in the browse option.
                if(m_pImpExp->GetExternalManualDefault(sBuffer, &cbSize))
                    SetDlgItemText(hwndDlg, IDC_IMPEXPMANUAL, sBuffer);

                SHAutoComplete(GetDlgItem(hwndDlg, IDC_IMPEXPMANUAL), SHACF_FILESYSTEM);
            }
            break;

        case PSN_WIZNEXT:
            
            //    If the application radio button is checked,
            //  select the selection from the application combo box.  If
            //  the manual button is checked, select the selection
            //  using the manual edit box.

            retVal = TRUE;
            
            if (Button_GetCheck(GetDlgItem(hwndDlg,IDC_IMPEXPRADIOAPP)) == BST_CHECKED)
            {
                
                HWND hwndComboBox = GetDlgItem(hwndDlg,IDC_IMPEXPEXTERNALCOMBO);
                
                if (hwndComboBox != NULL)
                {
                    // Find out the index of the selected item
                    INT nIndex = ComboBox_GetCurSel(hwndDlg);
                    
                    if (nIndex != CB_ERR)
                    {
                        // Retrieve a pointer to the filename
                        LPTSTR pszFileName = (LPTSTR)ComboBox_GetItemData(hwndComboBox, nIndex);
                        
                        if (pszFileName != NULL)
                            StrCpyN(m_pImpExp->m_szFileName,pszFileName,MAX_PATH);
                        
                    }
                    
                }
            }
            else if (Button_GetCheck(GetDlgItem(hwndDlg,IDC_IMPEXPRADIOFILE)) == BST_CHECKED)
            {
                
                // just get the text from the edit box
                GetDlgItemText(hwndDlg,IDC_IMPEXPMANUAL,m_pImpExp->m_szFileName,MAX_PATH);

                //
                // Don't allow "next" if the edit control contains a bogus filename
                //
                if (!IsValidFileOrURL(m_pImpExp->m_szFileName))
                {
                    
                    TCHAR szFmt[128];
                    TCHAR szMsg[INTERNET_MAX_URL_LENGTH+128];
                    MLLoadShellLangString(IDS_INVALIDURLFILE, szFmt, ARRAYSIZE(szFmt));
                    wnsprintf(szMsg, INTERNET_MAX_URL_LENGTH+40, szFmt, m_pImpExp->m_szFileName);
                    MLShellMessageBox(
                        hwndDlg, 
                        szMsg, 
                        (IMPORT == m_pImpExp->GetTransferType()) ? 
                           MAKEINTRESOURCE(IDS_CONFIRM_IMPTTL_FAV) : 
                           MAKEINTRESOURCE(IDS_CONFIRM_EXPTTL_FAV), 
                        MB_OK);
                    
                    SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
                    return retVal;
                }

                //
                // If the user doesn't type an extension, then we add ".htm"
                // or ".txt" as appropriate.  Otherwise, we don't touch it.
                //
                if (*PathFindExtension(m_pImpExp->m_szFileName) == TEXT('\0'))
                {
                    PathRenameExtension(
                        m_pImpExp->m_szFileName,
                        (m_pImpExp->GetExternalType()==COOKIES) ? TEXT(".txt") : TEXT(".htm"));
                }

            }
            else
            {
                ASSERT(0);
                m_pImpExp->m_szFileName[0] = TEXT('\0');
            }

            //
            // Finally, show an overwrite or file-not-found message
            // (but supress it if importing or exporting to a web address)
            //

            if (m_pImpExp->GetExternalType() == COOKIES ||
                !PathIsURL(m_pImpExp->m_szFileName))
            {
                if ( EXPORT == m_pImpExp->GetTransferType() && 
                    GetFileAttributes(m_pImpExp->m_szFileName) != 0xFFFFFFFF )
                {
                    int answer ;
                    UINT idTitle ;
                    
                    if ( m_pImpExp->GetExternalType() == COOKIES )
                        idTitle = IDS_EXPCOOKIES ;
                    else if ( m_pImpExp->GetExternalType() == BOOKMARKS )
                        idTitle = IDS_EXPFAVORITES ;
                    else
                        ASSERT(0);
                    
                    answer = WarningMessageBox(
                        hwndDlg,
                        idTitle,
                        IDS_IMPEXP_FILEEXISTS,
                        m_pImpExp->m_szFileName,
                        MB_YESNO | MB_ICONEXCLAMATION);
                    
                    SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (IDYES==answer)?0:-1);

                }
                else
                {
                    if (IMPORT == m_pImpExp->GetTransferType())
                    {
                        BOOL fError;

                        fError = FALSE;
                        if (PathIsUNC(m_pImpExp->m_szFileName))
                            ;
                        else
                            // Give the user a chance to insert the floppy if it's not already in.
                            fError = FAILED(SHPathPrepareForWriteWrap(hwndDlg,
                                                                      NULL,
                                                                      m_pImpExp->m_szFileName,
                                                                      FO_COPY,
                                                                      (SHPPFW_DEFAULT | SHPPFW_IGNOREFILENAME)));

                        if (!fError)
                            fError = (0xFFFFFFFF == GetFileAttributes(m_pImpExp->m_szFileName));

                        if (fError)
                        {
                            UINT idTitle ;
                    
                            if ( m_pImpExp->GetExternalType() == COOKIES )
                                idTitle = IDS_IMPCOOKIES ;
                            else if ( m_pImpExp->GetExternalType() == BOOKMARKS )
                                idTitle = IDS_IMPFAVORITES ;
                            else
                                ASSERT(0);

                            WarningMessageBox(
                                hwndDlg,
                                idTitle,
                                IDS_IMPEXP_FILENOTFOUND,
                                m_pImpExp->m_szFileName,
                                MB_OK | MB_ICONEXCLAMATION);
                    
                            SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
                    
                        }
                    }
                }

            }
            
            break; // PSN_WIZNEXT

        } // WM_NOTIFY

        break;
    
    } // switch(msg)

    return retVal;
}

BOOL WINAPI RunImportExportFavoritesWizard(HWND hDlg)
{

    ImpExpUserDlg::RunNewDialogProcess(hDlg);
    return TRUE;

}

int WarningMessageBox(HWND hwnd, UINT idTitle, UINT idMessage, LPCTSTR szFile, DWORD dwFlags)
{

    TCHAR szBuffer[1024];
    TCHAR szFormat[1024];

    //
    // load the string (must contain "%s")
    //
    MLLoadShellLangString(idMessage, szFormat, 1024);
   
    //
    // insert the filename
    //
    wnsprintf(szBuffer,1024,szFormat,szFile);

    //
    // display the messagebox
    //
    return MLShellMessageBox(
        hwnd,
        szBuffer,
        MAKEINTRESOURCE(idTitle),
        dwFlags);
}
