/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape 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/NPL/
 *
 * 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 Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */
//#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions

#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <time.h>   
#include <dos.h>
#include <winbase.h>
#include <raserror.h>
#include <shlobj.h>
#include <regstr.h>
#include <tapi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <winsock.h>

#include "dialshr.h"
#include "resource.h"
#include "xp_mem.h"
#include "prefapi.h"
    
#define trace

#define MAX_ENTRIES     20

// Navigator quit message (see QuitNavigator)
#define ID_APP_SUPER_EXIT 34593

// The number of times we try to dial
#define NUM_ATTEMPTS    3
#define IDDISCONNECTED  31

enum CallState
{
	StateIdle,
	StateConnecting,
	StateConnected,
	StateDisconnecting
};


CallState					gCallState = StateIdle;

UINT						gDialAttempts = 0;		// keeps the total number of dialing count
HRASCONN					gRasConn;				// handle to the current ras connection
RASCONNSTATE				gRASstate;				// current connection's ras state
RASDIALPARAMS				gDialParams;				// keeps the current connection info
HWND						gHwndStatus = NULL;		// handle to our connection status window
BOOL						gCancelled = FALSE;		// assume connection will be there unless user cancels
BOOL						gLineDrop = FALSE;
BOOL						gDeviceErr = FALSE; 		// assume no hardware err
HWND						gHwndNavigator = NULL;
HANDLE						gRasMon = NULL;		// process handle to RasMon on WinNT

HINSTANCE					m_hRasInst = NULL;

// NT/95 entrypoints
RASDIAL						m_lpfnRasDial;
RASHANGUP					m_lpfnRasHangUp;
RASGETERRORSTRING			m_lpfnRasGetErrorString;
RASSETENTRYPROPERTIES		m_lpfnRasSetEntryProperties;
RASSETENTRYDIALPARAMS		m_lpfnRasSetEntryDialParams;
RASGETCOUNTRYINFO			m_lpfnRasGetCountryInfo;
RASENUMCONNECTIONS			m_lpfnRasEnumConnections;
RASENUMENTRIES				m_lpfnRasEnumEntries;
RASENUMDEVICES				m_lpfnRasEnumDevices;
RASGETENTRYPROPERTIES		m_lpfnRasGetEntryProperties;
RASVALIDATEENTRYNAME		m_lpfnRasValidateEntryName;
RASDELETEENTRY				m_lpfnRasDeleteEntry;

// NT entrypoints
RASSETAUTODIALENABLE		m_lpfnRasSetAutodialEnable;
RASSETAUTODIALADDRESS		m_lpfnRasSetAutodialAddress;
RASGETAUTODIALADDRESS		m_lpfnRasGetAutodialAddress;
RASSETAUTODIALPARAM			m_lpfnRasSetAutodialParam;
RASENUMAUTODIALADDRESSES	m_lpfnRasEnumAutodialAddresses;
RASSETCREDENTIALS			m_lpfnRasSetCredentials;

size_t						stRASENTRY;
size_t						stRASCONN;
size_t						stRASCTRYINFO;
size_t						stRASDIALPARAMS;
size_t						stRASDEVINFO;
size_t						stRASENTRYNAME;

//********************************************************************************
// LoadRasFunctions()
//********************************************************************************
BOOL LoadRasFunctions( LPCSTR lpszLibrary )
{
	//    ASSERT(!m_hRasInst);
	m_hRasInst = ::LoadLibrary( lpszLibrary );
	if ( (UINT)m_hRasInst > 32 ) 
	{
		m_lpfnRasSetEntryProperties = (RASSETENTRYPROPERTIES)::GetProcAddress( m_hRasInst, "RasSetEntryProperties" );
		m_lpfnRasGetCountryInfo = (RASGETCOUNTRYINFO)::GetProcAddress( m_hRasInst, "RasGetCountryInfo" );
		m_lpfnRasEnumDevices = (RASENUMDEVICES)::GetProcAddress( m_hRasInst, "RasEnumDevices" );
		m_lpfnRasGetEntryProperties = (RASGETENTRYPROPERTIES)::GetProcAddress( m_hRasInst, "RasGetEntryProperties");
		m_lpfnRasValidateEntryName = (RASVALIDATEENTRYNAME)::GetProcAddress( m_hRasInst,"RasValidateEntryName" );
		m_lpfnRasDeleteEntry = (RASDELETEENTRY)::GetProcAddress( m_hRasInst, "RasDeleteEntry" );
		m_lpfnRasHangUp = (RASHANGUP)::GetProcAddress( m_hRasInst, "RasHangUpA" );
		m_lpfnRasDial = (RASDIAL)::GetProcAddress( m_hRasInst, "RasDialA" );
		m_lpfnRasEnumConnections = (RASENUMCONNECTIONS)::GetProcAddress( m_hRasInst, "RasEnumConnectionsA" );
		m_lpfnRasSetEntryDialParams = (RASSETENTRYDIALPARAMS)::GetProcAddress( m_hRasInst, "RasSetEntryDialParamsA" );
		m_lpfnRasEnumEntries = (RASENUMENTRIES)::GetProcAddress( m_hRasInst, "RasEnumEntriesA" );
		
		SizeofRAS95();		
		return TRUE;
	} 
	else 
	{
		MessageBox( NULL, "Please install Dial-up Networking", "Netscape", MB_ICONSTOP );     
	
		::FreeLibrary( m_hRasInst );
		m_hRasInst = NULL;
		return FALSE; 
	}
}

//********************************************************************************
// LoadRasFunctionsNT()
//********************************************************************************
BOOL LoadRasFunctionsNT( LPCSTR lpszLibrary )
{
	m_hRasInst = ::LoadLibrary( lpszLibrary );
	
    if ( (UINT)m_hRasInst > 32 ) 
	{
		m_lpfnRasSetEntryProperties = (RASSETENTRYPROPERTIES)::GetProcAddress( m_hRasInst, "RasSetEntryPropertiesA" );		
		m_lpfnRasGetCountryInfo = (RASGETCOUNTRYINFO)::GetProcAddress( m_hRasInst, "RasGetCountryInfoA" );
		m_lpfnRasEnumDevices = (RASENUMDEVICES)::GetProcAddress( m_hRasInst, "RasEnumDevicesA" );
		m_lpfnRasGetEntryProperties = (RASGETENTRYPROPERTIES)::GetProcAddress( m_hRasInst, "RasGetEntryPropertiesA" );
		m_lpfnRasValidateEntryName = (RASVALIDATEENTRYNAME)::GetProcAddress( m_hRasInst, "RasValidateEntryNameA" );
		m_lpfnRasDeleteEntry = (RASDELETEENTRY)::GetProcAddress( m_hRasInst, "RasDeleteEntryA" );
		m_lpfnRasEnumEntries = (RASENUMENTRIES)::GetProcAddress( m_hRasInst, "RasEnumEntriesA" );
		m_lpfnRasHangUp = (RASHANGUP)::GetProcAddress( m_hRasInst, "RasHangUpA" );
		m_lpfnRasDial = (RASDIAL)::GetProcAddress( m_hRasInst, "RasDialA" );
		m_lpfnRasEnumConnections = (RASENUMCONNECTIONS)::GetProcAddress( m_hRasInst, "RasEnumConnectionsA" );
		m_lpfnRasSetEntryDialParams = (RASSETENTRYDIALPARAMS)::GetProcAddress(m_hRasInst, "RasSetEntryDialParamsA" );
		
		// AUTODIAL
		m_lpfnRasSetAutodialEnable = (RASSETAUTODIALENABLE)::GetProcAddress( m_hRasInst, "RasSetAutodialEnableA" );
		m_lpfnRasSetAutodialAddress = (RASSETAUTODIALADDRESS)::GetProcAddress( m_hRasInst, "RasSetAutodialAddressA" );
		m_lpfnRasGetAutodialAddress = (RASGETAUTODIALADDRESS)::GetProcAddress( m_hRasInst, "RasGetAutodialAddressA" );
		m_lpfnRasSetAutodialParam = (RASSETAUTODIALPARAM)::GetProcAddress( m_hRasInst, "RasSetAutodialParamA" );
		m_lpfnRasSetCredentials = (RASSETCREDENTIALS)::GetProcAddress( m_hRasInst, "RasSetCredentialsA" );
		m_lpfnRasEnumAutodialAddresses = (RASENUMAUTODIALADDRESSES)::GetProcAddress( m_hRasInst, "RasEnumAutodialAddressesA" ); 
		
		SizeofRASNT40();
		return TRUE;       
	}                
   	else 
	{
		MessageBox( NULL, "Please install Dial-up Networking", "Netscape", MB_ICONSTOP );
		::FreeLibrary( m_hRasInst );
		m_hRasInst = NULL;
	    return FALSE;       
	}
}
    
//********************************************************************************
// FreeRasFunctions()
//********************************************************************************
void FreeRasFunctions()
{
	if ( (UINT)m_hRasInst > 32 )
		FreeLibrary( m_hRasInst );
}



//********************************************************************************
// GetModemList (s/b GetModems)
// Returns list of modems available for use ('installed' by the user). For Win95
// this list come from the OS, and each entry contains 2 strings - the first is
// the Modem Name, and the second is the device type (both are needed to select
// the device to use to make a Dial-up connection).
//********************************************************************************
BOOL GetModemList( char*** resultModemList, int* numDevices )
{
	DWORD           dwBytes = 0, dwDevices;
    LPRASDEVINFO    lpRnaDevInfo;
    
    // First find out how much memory to allocate
    (*m_lpfnRasEnumDevices)( NULL, &dwBytes, &dwDevices );
    lpRnaDevInfo = (LPRASDEVINFO)malloc( dwBytes );
    
	if ( !lpRnaDevInfo )
		return (FALSE);
    
    lpRnaDevInfo->dwSize = stRASDEVINFO;
	(*m_lpfnRasEnumDevices)( lpRnaDevInfo, &dwBytes, &dwDevices );
    
    // copy all entries to the char array
    *resultModemList = new char* [dwDevices+1];
    if ( *resultModemList == NULL )
		return FALSE;
    
    *numDevices = dwDevices;
    for ( unsigned short i=0; i < dwDevices; i++ ) 
    {
		(*resultModemList)[i] = new char[ strlen( lpRnaDevInfo[ i ].szDeviceName) + 1 ];
		if ( ( *resultModemList)[ i ] == NULL )
			return FALSE;
		strcpy( ( *resultModemList)[ i ], lpRnaDevInfo[ i ].szDeviceName );
    }
    return TRUE;
}
    
    
//********************************************************************************
// GetModemType
// Returns the type for the selected modem.
//********************************************************************************
BOOL GetModemType( char *strModemName, char *strModemType)
{
    DWORD           dwBytes = 0, dwDevices;
    LPRASDEVINFO    lpRnaDevInfo;
      
	// First get Modem (okay - Device) info from Win95
	// find out how much memory to allocate
	(*m_lpfnRasEnumDevices)( NULL, &dwBytes, &dwDevices );
	lpRnaDevInfo = (LPRASDEVINFO)malloc( dwBytes );
	
	if ( !lpRnaDevInfo )
		return NULL;
	
	lpRnaDevInfo->dwSize = stRASDEVINFO;
	(*m_lpfnRasEnumDevices)( lpRnaDevInfo, &dwBytes, &dwDevices );
	
	// If match the modem given from JS then return the associated Type
	for ( unsigned short i = 0; i < dwDevices; i++ ) 
	{
		if ( 0 == strcmp(strModemName, lpRnaDevInfo[i].szDeviceName ) ) 
		{
			strModemType = new char[ strlen( lpRnaDevInfo[ i ].szDeviceType ) + 1 ];
			strcpy( strModemType, lpRnaDevInfo[ i ].szDeviceType );
			return TRUE;
		}                
	}
	return FALSE;
}
    
//********************************************************************************
// EnableDialOnDemand  (win95)
// Set the magic keys in the registry to enable dial on demand
//********************************************************************************
void EnableDialOnDemand95( LPSTR lpProfileName, BOOL flag )
{
	HKEY    hKey;
	DWORD   dwDisposition;
	long    result;
	char*	szData;
	
	// We need to tell windows about dialing on demand
	result = RegCreateKeyEx( HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\Winsock\\Autodial",
							NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition );
	
	// err, oops
	if ( result != ERROR_SUCCESS )
		return;
	
	szData = "url.dll";
	result = RegSetValueEx( hKey, "AutodialDllName32", NULL, REG_SZ, (LPBYTE)szData, strlen( szData ) );
	
	szData = "AutodialHookCallback";
	result = RegSetValueEx( hKey, "AutodialFcnName32", NULL, REG_SZ, (LPBYTE)szData, strlen( szData ) );
	
	RegCloseKey( hKey );
	
	// set the autodial flag first
	result = RegCreateKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
							NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition );
	
	// err, oops
	if ( result != ERROR_SUCCESS )
		return;
	
	// set the autodial and idle-time disconnect
	DWORD		dwValue = flag;
	result = RegSetValueEx( hKey, "EnableAutodial", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof( DWORD ) );
	
	// try setting autodisconnect here
	dwValue = 1;
	result = RegSetValueEx( hKey, "EnableAutoDisconnect", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof( DWORD ) );
	
	// default auto-disconnect after 5 minutes
	dwValue = 5;
	result = RegSetValueEx( hKey, "DisconnectIdleTime", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof( DWORD ) );
	
	RegCloseKey( hKey );
	
	// set the autodisconnect flags here too
	result = RegCreateKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\Internet Settings",
						NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition );
	
	// err, oops
	if ( result != ERROR_SUCCESS )
		return;
	
	dwValue = 1;
	result = RegSetValueEx( hKey, "EnableAutoDisconnect", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof( DWORD ) );
	
	// default auto-disconnect after 5 minutes
	dwValue = 5;
	result = RegSetValueEx( hKey, "DisconnectIdleTime", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof( DWORD ) );
	
	RegCloseKey( hKey );
	
	// OK, let's tell it which profile to autodial
	result = RegCreateKeyEx( HKEY_CURRENT_USER, "RemoteAccess", NULL, NULL, NULL,
						KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition );
	
	// err, oops
	if ( result != ERROR_SUCCESS )
		return;
	
	if ( flag )	// enable dod
	{
		result = RegSetValueEx( hKey, "InternetProfile", NULL, REG_SZ, (LPBYTE)lpProfileName, strlen( lpProfileName ) );
		result = RegSetValueEx( hKey, "Default", NULL, REG_SZ, (LPBYTE)lpProfileName, strlen( lpProfileName ) );
	}
	else			// disable dod
	{
		result = RegSetValueEx( hKey, "InternetProfile", NULL, REG_SZ, NULL, strlen( lpProfileName ) );
		result = RegSetValueEx( hKey, "Default", NULL, REG_SZ, NULL, strlen( lpProfileName ) );
	}
	RegCloseKey( hKey );
}

    
//********************************************************************************
//
// lineCallbackFuncNT  (winNT40)
//
// Sets the RAS structure for Dial on Demand, NT40 doesn't use registry like win95
//********************************************************************************
void FAR PASCAL
lineCallbackFuncNT(DWORD /* hDevice */, DWORD /* dwMsg */, DWORD /* dwCallbackInstance */,
	DWORD /* dwParam1 */, DWORD /* dwParam2 */, DWORD /* dwParam3 */)
{
}
    
void EnableDialOnDemandNT(LPSTR lpProfileName, BOOL flag)
{
	RASAUTODIALENTRY        rasAutodialEntry;
	DWORD                   dwBytes = 0;
	DWORD                   dwNumDevs;
	HLINEAPP                lineApp;
	DWORD                   dwApiVersion;
	LINEINITIALIZEEXPARAMS  lineInitExParams;
	LINETRANSLATECAPS       lineTranslateCaps;
	int                     rtn;
	
	// Initialize TAPI. We need to do this in order to get the dialable
	// number and to bring up the location dialog
	
	dwApiVersion = 0x00020000;
	lineInitExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
	lineInitExParams.dwTotalSize = sizeof( LINEINITIALIZEEXPARAMS );
	lineInitExParams.dwNeededSize = sizeof (LINEINITIALIZEEXPARAMS );
	
	rtn = lineInitializeEx( &lineApp, gDLL, lineCallbackFuncNT, 
		     NULL, &dwNumDevs, &dwApiVersion, &lineInitExParams );

	if ( rtn == 0)
	{
		lineTranslateCaps.dwTotalSize = sizeof( LINETRANSLATECAPS );
		lineTranslateCaps.dwNeededSize = sizeof( LINETRANSLATECAPS );
		rtn = lineGetTranslateCaps( lineApp, dwApiVersion, &lineTranslateCaps );
	}               
	
	rasAutodialEntry.dwFlags = 0;
	rasAutodialEntry.dwDialingLocation = lineTranslateCaps.dwCurrentLocationID;
	strcpy( rasAutodialEntry.szEntry, lpProfileName ); 
	rasAutodialEntry.dwSize = sizeof( RASAUTODIALENTRY );
	
	// set auto dial params
	int     val = flag;
	rtn = ( *m_lpfnRasSetAutodialParam )( RASADP_DisableConnectionQuery, &val, sizeof( int ) );

	if ( rtn == ERROR_INVALID_PARAMETER )
	{
		trace( "dialer.cpp : EnableDialOnDemandNT - Invalid Parameter. Can't set Autodial Parameters. (r)" );
		return;
	}	 
	else if ( rtn == ERROR_INVALID_SIZE )
	{
		trace( "dialer.cpp : EnableDialOnDemandNT - Invalid size. Can't set Autodial Parameters. (r)" );
		return;
	}	 
	else if ( rtn )
	{
		trace( "dialer.cpp : EnableDialOnDemandNT - Can't set Autodial Parameters. Error %d. (r)", rtn );
		return;
	}	 

	
	if ( flag )        // set dod entry if the flag is enabled
	{
		rtn = ( *m_lpfnRasSetAutodialAddress )( "www.netscape.com", 0, &rasAutodialEntry, sizeof(RASAUTODIALENTRY), 1);

		if ( rtn == ERROR_INVALID_PARAMETER )
		{
			trace( "dialer.cpp : EnableDialOnDemandNT - Invalid Parameter. Can't set Autodial Address. (r)" );
			return;
		}
		else if ( rtn == ERROR_INVALID_SIZE )
		{
			trace( "dialer.cpp : EnableDialOnDemandNT - Invalid size. Can't set Autodial Address. (r)" );
			return;
		}
		else if ( rtn )
		{
			trace( "dialer.cpp : EnableDialOnDemandNT - Can't set Autodial Address. Error %d. (r)", rtn );
			return;
		}
	}	 

	rtn = ( *m_lpfnRasSetAutodialEnable )( rasAutodialEntry.dwDialingLocation, flag );

	if ( rtn )
	{
		trace( "dialer.cpp : EnableDialOnDemandNT - Can't set Autodial Enable. Error %d. (r)", rtn );
		return;
	}	 

	return;
}


//********************************************************************************
// utility function
// sameStrings
// checks for string equality between a STRRET and a LPCTSTR
//********************************************************************************
static BOOL sameStrings( LPITEMIDLIST pidl, STRRET& lpStr1, LPCTSTR lpStr2 )
{
	char    buf[ MAX_PATH ];
	char*	mystr;
	int		cch; 
    
	switch ( lpStr1.uType )
	{
		case STRRET_WSTR:
			cch = WideCharToMultiByte( CP_ACP, WC_SEPCHARS | WC_COMPOSITECHECK,
					lpStr1.pOleStr, -1, buf, MAX_PATH, NULL, NULL );
    
			cch = GetLastError();
			return strcmp( lpStr2, buf ) == 0;
    
		case STRRET_OFFSET:
			mystr = ((char*)pidl) + lpStr1.uOffset;
			return strcmp( lpStr2, ((char*)pidl) + lpStr1.uOffset ) == 0;
    
		case STRRET_CSTR:
			mystr = lpStr1.cStr;
			return strcmp( lpStr2, lpStr1.cStr ) == 0;
	}
    
	return FALSE;
}
    
//********************************************************************************
// utility function
// procStrings
//********************************************************************************
static BOOL procStrings( LPITEMIDLIST pidl, STRRET& lpStr1, char* lpStr2 )
{
	char*		mystr;
	int			cch; 
    
	switch ( lpStr1.uType )
	{
		case STRRET_WSTR:
			cch = WideCharToMultiByte( CP_ACP, WC_SEPCHARS | WC_COMPOSITECHECK,
					lpStr1.pOleStr, -1, lpStr2, MAX_PATH, NULL, NULL );
			return TRUE;
						
		case STRRET_OFFSET:
			mystr = ((char*)pidl) + lpStr1.uOffset;
			strcpy( lpStr2, ((char*)pidl) + lpStr1.uOffset );
			return TRUE;
    
		case STRRET_CSTR:
			mystr = lpStr1.cStr;
			strcpy( lpStr2, lpStr1.cStr );
			return TRUE;
	}
    
	return FALSE;
}
    
    
//********************************************************************************
// utility function
// next
//********************************************************************************
static LPITEMIDLIST next( LPCITEMIDLIST pidl )
{
	LPSTR lpMem=(LPSTR)pidl;
	
	lpMem += pidl->mkid.cb;
	return (LPITEMIDLIST)lpMem;
}
    
    
//********************************************************************************
// utility function
// getSize
//********************************************************************************
static UINT getSize(LPCITEMIDLIST pidl)
{
	UINT cbTotal = 0;
    
	if ( pidl )
	{
		cbTotal += sizeof( pidl->mkid.cb );
		while ( pidl->mkid.cb )
		{
			cbTotal += pidl->mkid.cb;
			pidl = next( pidl );
		}
	}
    
	return cbTotal;
}
    
    
//********************************************************************************
// utility function
// create
//********************************************************************************
static LPITEMIDLIST create( UINT cbSize )
{
	IMalloc*     pMalloc;
	LPITEMIDLIST pidl = 0;
    
	if ( FAILED( SHGetMalloc( &pMalloc ) ) )
		return 0;
    
	pidl = (LPITEMIDLIST)pMalloc->Alloc( cbSize );
    
	if ( pidl )
		memset( pidl, 0, cbSize );
     
	pMalloc->Release();
	
	return pidl;
}
    
    
    
//********************************************************************************
// utility function
// ConcatPidls
//********************************************************************************
static LPITEMIDLIST concatPidls( LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2 )
{
	UINT         cb1 = getSize( pidl1 ) - sizeof( pidl1->mkid.cb );
	UINT         cb2 = getSize( pidl2 );
	LPITEMIDLIST pidlNew = create( cb1 + cb2 );
    
	if ( pidlNew )
	{
		memcpy( pidlNew, pidl1, cb1 );
		memcpy( ((LPSTR)pidlNew ) + cb1, pidl2, cb2 );
	}
    
	return pidlNew;
}
    
    
//********************************************************************************
// GetMyComputerFolder
// This routine returns the ISHellFolder for the virtual My Computer folder,
// and also returns the PIDL.
//********************************************************************************
HRESULT GetMyComputerFolder( LPSHELLFOLDER* ppshf, LPITEMIDLIST* ppidl )
{
	IMalloc*        pMalloc;
	HRESULT         hres;
    
	hres = SHGetMalloc( &pMalloc );
	if ( FAILED( hres ) )
		return hres;
    
	// Get the PIDL for "My Computer"
	hres = SHGetSpecialFolderLocation( /*pWndOwner->m_hWnd*/NULL, CSIDL_DRIVES, ppidl );
	if ( SUCCEEDED( hres ) )
	{
		IShellFolder*   pshf;
    
		hres = SHGetDesktopFolder( &pshf );
		if ( SUCCEEDED( hres ) )
		{
			// Get the shell folder for "My Computer"
			hres = pshf->BindToObject( *ppidl, NULL, IID_IShellFolder, (LPVOID *)ppshf );
			pshf->Release();
		}
	}
    
	pMalloc->Release();
    
	return hres;
}
    
    
//********************************************************************************
// GetDialupNetworkingFolder
// This routine returns the ISHellFolder for the virtual Dial-up Networking
// folder, and also returns the PIDL.
//********************************************************************************
static HRESULT getDialUpNetworkingFolder(LPSHELLFOLDER *ppshf, LPITEMIDLIST *ppidl)
{
	HRESULT         hres;
    
	IMalloc*        pMalloc = NULL;
	IShellFolder*   pmcf = NULL;
	LPITEMIDLIST    pidlmc;
    
    
	char szDialupName[ 256 ];
	HKEY hKey;
	DWORD cbData;
    
	// Poke around in the registry to find out what the Dial-Up Networking
	//   folder is called on this machine

	szDialupName[ 0 ] = '\0';
	if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, 
				     "CLSID\\{992CFFA0-F557-101A-88EC-00DD010CCC48}", 
				     NULL, 
				     KEY_QUERY_VALUE, 
				     &hKey ) )
	{
    	cbData = sizeof( szDialupName );
		RegQueryValueEx( hKey, "", NULL, NULL, (LPBYTE)szDialupName, &cbData );
	}
    
	// if we didn't get anything just use the default
	if( szDialupName[ 0 ] == '\0' )
	{
		char*	strText;
		strText = "Dial-Up Networking";
		//CString strText;
		//strText.LoadString(IDS_DIAL_UP_NW);
		strcpy( szDialupName, (LPCSTR)strText );
	}
    
	RegCloseKey( hKey );
    
	// OK, now look for that folder
    
	hres = SHGetMalloc( &pMalloc );
	if ( FAILED( hres ) )
		return hres;
    
	// Get the virtual folder for My Computer
	hres = GetMyComputerFolder( &pmcf, &pidlmc );
	if ( SUCCEEDED( hres ) )
	{
		IEnumIDList*    pEnumIDList;
    
		// Now we need to find the "Dial-Up Networking" folder
		hres = pmcf->EnumObjects( NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList );
		if ( SUCCEEDED( hres ) )
		{
			LPITEMIDLIST    pidl;
    
			int flag = 1;
			STRRET  name;
			while ( ( NOERROR == ( hres = pEnumIDList->Next( 1, &pidl, NULL ) ) ) && ( flag ) ) 
			{
			    memset( &name, 0, sizeof( STRRET ) );
    
				name.uType = STRRET_CSTR;  // preferred choice
				hres = pmcf->GetDisplayNameOf( pidl, SHGDN_INFOLDER, &name );
				if ( FAILED( hres ) )
				{
					pMalloc->Free( pidl );
					flag = 0;
					//break;
				}
    
				if ( sameStrings( pidl, name, szDialupName ) )
				{
					*ppidl = concatPidls(pidlmc, pidl);
					hres = pmcf->BindToObject( pidl, NULL, IID_IShellFolder, (LPVOID*)ppshf );
					int rtn = GetLastError();
					pMalloc->Free( pidl );
					flag = 0;
					//break;
				}
				
				if ( flag )
					pMalloc->Free( pidl );
			}
    
			pEnumIDList->Release();
		}
    
		pmcf->Release();
		pMalloc->Free( pidlmc );
	}
    
	pMalloc->Release();
	return hres;
}

//********************************************************************************
// GetDialUpConnection
//********************************************************************************
BOOL GetDialUpConnection95( CONNECTIONPARAMS** connectionNames, int* numNames )
{
	HRESULT			hres;
    
	IMalloc*		pMalloc = NULL;
	IShellFolder*	pshf = NULL;
	LPITEMIDLIST	pidldun;
	LPITEMIDLIST	pidl;
	STRRET			name;
	char			temp[ MAX_PATH ];
	int				flag = 1;
	int				i =0;
    
    
	// Initialize out parameter
	hres = SHGetMalloc( &pMalloc );
	if ( FAILED( hres ) )
		return FALSE;
    
	// First get the Dial-Up Networking virtual folder
	hres = getDialUpNetworkingFolder( &pshf, &pidldun );
	if ( SUCCEEDED( hres ) && ( pshf != NULL ) ) 
	{
		IEnumIDList*    pEnumIDList;
    
		// Enumerate the files looking for the desired connection
		hres = pshf->EnumObjects( NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList );
    
		if ( SUCCEEDED( hres ) ) 
		{
			*numNames = 0;
			while ( NOERROR == ( hres = pEnumIDList->Next( 1, &pidl, NULL ) ) )
				(*numNames)++;
    
			pEnumIDList->Reset();
    
			*connectionNames = new CONNECTIONPARAMS[ *numNames ];
			if( *connectionNames == NULL )
				return FALSE;
    
			while ( ( NOERROR == ( hres = pEnumIDList->Next( 1, &pidl, NULL ) ) ) && ( flag ) ) 
			{
				name.uType = STRRET_CSTR;  // preferred choice
				hres = pshf->GetDisplayNameOf( pidl, SHGDN_INFOLDER, &name );
	
				if ( FAILED( hres ) ) 
				{
					pMalloc->Free( pidl );
					flag = 0;
					//break;
				}
    
				procStrings( pidl, name, temp );
				if( strcmp( temp, "Make New Connection" ) !=0 )
				{
					strcpy( (*connectionNames)[ i ].szEntryName, temp );
					(*connectionNames)[ i ].pidl = concatPidls( pidldun, pidl );
					i++;
				}

				if ( flag )
					pMalloc->Free( pidl );
			}
    
			pEnumIDList->Release();
		}
    
		pshf->Release();
		pMalloc->Free( pidldun );
	}
    
	pMalloc->Release();
	*numNames = i;
    
	return TRUE;
}

//********************************************************************************
// GetDialUpConnection
//********************************************************************************
BOOL GetDialUpConnectionNT( CONNECTIONPARAMS** connectionNames, int* numNames )
{
	RASENTRYNAME*	rasEntryName;
	DWORD           cb;
	DWORD           cEntries;
	int             i;
	char*			szPhoneBook;
	
	szPhoneBook = NULL;
	rasEntryName = new RASENTRYNAME[ MAX_ENTRIES ]; 
	
	if( rasEntryName == NULL )
		return FALSE;
	
	memset( rasEntryName, 0, MAX_ENTRIES );
	rasEntryName[ 0 ].dwSize = sizeof( RASENTRYNAME );
	cb = sizeof( RASENTRYNAME ) * MAX_ENTRIES;
	
	int rtn = (*m_lpfnRasEnumEntries)( NULL, szPhoneBook, rasEntryName, &cb, &cEntries );
	if ( rtn !=0 )
		return FALSE;
	
	*numNames = cEntries;
	
	*connectionNames = new CONNECTIONPARAMS[ *numNames + 1 ]; 
	
	for( i = 0; i < *numNames; i++ )
		strcpy ( (*connectionNames)[ i ].szEntryName, rasEntryName[ i ].szEntryName );
	
	delete []rasEntryName;
	return TRUE;
}

//********************************************************************************
// getDialupConnectionPIDL
//********************************************************************************
static HRESULT getDialUpConnectionPIDL( LPCTSTR lpConnectionName, LPITEMIDLIST* ppidl )
{
	HRESULT			hres;
	
	IMalloc*		pMalloc = NULL;
	IShellFolder*	pshf = NULL;
	LPITEMIDLIST	pidldun;
	
	// Initialize out parameter
	*ppidl = NULL;
	
	hres = SHGetMalloc( &pMalloc );
	if ( FAILED( hres ) )
		return hres;
	
	// First get the Dial-Up Networking virtual folder
	hres = getDialUpNetworkingFolder( &pshf, &pidldun );
	if ( SUCCEEDED( hres ) && ( pshf != NULL ) )
	{
		IEnumIDList*    pEnumIDList;
	
		// Enumerate the files looking for the desired connection
		hres = pshf->EnumObjects( /*pWndOwner->m_hWnd*/NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList );
		if ( SUCCEEDED( hres ) )
		{
			LPITEMIDLIST    pidl;
	
			int flag = 1;
			while ( ( NOERROR == ( hres = pEnumIDList->Next( 1, &pidl, NULL ) ) ) && flag )
			{
				STRRET  name;
	
				name.uType = STRRET_CSTR;  // preferred choice
				hres = pshf->GetDisplayNameOf( pidl, SHGDN_INFOLDER, &name );
				if ( FAILED( hres ) )
				{
					pMalloc->Free( pidl );
					flag = 0;
					//break;
				}
	
				if ( sameStrings( pidl, name, lpConnectionName ) )
				{
					*ppidl = concatPidls( pidldun, pidl );
					pMalloc->Free( pidl );
					flag = 0;
					//break;
				}
	
				if ( flag )
					pMalloc->Free(pidl);
			}
	
			pEnumIDList->Release();
		}
	
		pshf->Release();
		pMalloc->Free( pidldun );
	}
	
	pMalloc->Release();
	return hres;
}

//********************************************************************************
// getNetscapePIDL
//********************************************************************************
static void getNetscapePidl( LPITEMIDLIST* ppidl )
{
	char				szPath[ MAX_PATH ], *p;
	OLECHAR				olePath[ MAX_PATH ];
	IShellFolder*		pshf;
	
	GetModuleFileName( gDLL, szPath, sizeof( szPath ) );
	//GetModuleFileName(AfxGetInstanceHandle(), szPath, sizeof(szPath));
	
	//we need to take off \plugins\npasw.dll from the path
	p = strrchr( szPath, '\\' );
	if ( p ) 
		*p = '\0';
	p = strrchr( szPath, '\\' );
	if ( p ) 
		*p = '\0';
	strcat( szPath, "\\netscape.exe" );
	
	HRESULT hres = SHGetDesktopFolder( &pshf );
	if ( SUCCEEDED( hres ) )
	{
		MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)szPath, -1, (LPWSTR)olePath, sizeof( olePath ) );
	
		ULONG lEaten;
		pshf->ParseDisplayName( NULL, NULL, (LPOLESTR)olePath, &lEaten, ppidl, NULL );
	}
	
	return;
}


//********************************************************************************
// getMsgString
// loads a Message String from the string table
//********************************************************************************
static BOOL getMsgString( char* buf, UINT uID )
{
	if ( !buf )
		return FALSE;

	int ret = LoadString( gDLL, uID, buf, 255 );

	return ret;
}

//********************************************************************************
// getSystemDirectory_1()
// intended to be a more fail-safe version of GetSystemDirectory
// returns:
//	NULL if the path cannot be obtained for any reason
//	otherwise, the "GetSystemDirectory" path
//********************************************************************************
static char* getSystemDirectory_1()
{
	UINT			startLength = MAX_PATH;
	UINT			sysPathLength = MAX_PATH;
	char*			sysPath;
	
start:
	sysPath = (char*)malloc( sizeof(char) * startLength );
	if ( !sysPath ) 
		return NULL;
	sysPathLength = ::GetSystemDirectory( sysPath, startLength );
	if ( sysPathLength == 0 )
		return NULL;
	if ( sysPathLength > startLength )
	{
		free( sysPath );
		startLength = sysPathLength;
		goto start;
	}
	return sysPath;
}

//********************************************************************************
// getPhoneBookNT()
// returns the path to the NT Phone Book file (rasphone.pbk)
// returns:
//	NULL if the path cannot be obtained for any reason
//	otherwise, the phone book file path
//********************************************************************************
static char* getPhoneBookNT()
{
	char*			sysPath;
	char*			pbPath;
	
	sysPath = getSystemDirectory_1();
	if ( !sysPath )
		return FALSE;
		
	pbPath = (char*)malloc( sizeof(char) * strlen( sysPath ) + 30 );
	if ( !pbPath )
	{
		free( sysPath );
		return FALSE;
	}
	
	strcpy( pbPath, sysPath );
	strcat( pbPath, "\\ras\\rasphone.pbk" );
	strcat( pbPath, "\0" );
	free( sysPath );
	return pbPath;	
}

//********************************************************************************
// startRasMonNT()
// starts the "rasmon" process (rasmon.exe)
// returns:
//	FALSE if the process cannot be started for any reason
//	TRUE otherwise
//********************************************************************************
static BOOL startRasMonNT()
{
	// starts up RASMON process
	PROCESS_INFORMATION pi;
	BOOL                ret;
	STARTUPINFO         sti;
	UINT                err = ERROR_SUCCESS;
	char*				sysPath;
	char*				rasMonPath;
	
	sysPath = getSystemDirectory_1();
	if ( !sysPath )
		return FALSE;

	rasMonPath = (char*)malloc( sizeof(char) * strlen( sysPath ) + 30 );
	if ( !rasMonPath )
	{
		free( sysPath );
		return FALSE;
	}
	
	strcpy( rasMonPath, sysPath );
	strcat( rasMonPath, "\\rasmon.exe" );
	strcat( rasMonPath, "\0" );
	free( sysPath );
	
	memset( &sti, 0, sizeof( sti ) );
	sti.cb = sizeof( STARTUPINFO );
	
	// Run the RASMON app
	ret = ::CreateProcess( rasMonPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &sti, &pi );
	if ( ret == TRUE )
	{
		gRasMon = pi.hProcess;
		Sleep( 3000 );
	}
	free( rasMonPath );
	return ret;
}

static void setStatusDialogText( int iField, const char* pText )
{
	if ( gHwndStatus )
	{
		HWND hField = GetDlgItem( gHwndStatus, iField );
		if ( hField )
			SetWindowText( hField, pText );
	}
}

static void removeStatusDialog()
{
	if ( gHwndStatus )
	{
		EndDialog( gHwndStatus, TRUE );
		gHwndStatus = NULL;
	}
}

static int displayErrMsgWnd( char* text, int style, HWND hwnd )
{
	char		title[ 50 ];
	getMsgString( (char*)&title, IDS_APP_NAME );
	
	if ( hwnd == NULL )
		hwnd = GetActiveWindow();
		
	int ret = MessageBox( hwnd, text, title, style );
	return ret;
}

static void displayDialErrorMsg( DWORD dwError )
{
	char    szErr[ 256 ];
	char    szErrStr[ 256 ];
	HWND	wind;
	
	ASSERT( m_lpfnRasGetErrorString );
	(*m_lpfnRasGetErrorString)( (UINT)dwError, szErr, sizeof(szErr) );
	
	// some of the default error strings are pretty lame
	switch ( dwError )
	{
		case ERROR_NO_DIALTONE:
			getMsgString( szErr, IDS_NO_DIALTONE );
		break;
	
		case ERROR_LINE_BUSY:
			getMsgString( szErr, IDS_LINE_BUSY );
		break;

		case ERROR_PROTOCOL_NOT_CONFIGURED:
			getMsgString( szErr, IDS_PROTOCOL_NOT_CONFIGURED );

		default:
		break;
	}
	
	getMsgString( szErrStr, IDS_CONNECTION_FAILED );
	strcat( szErrStr, szErr );
	
	wind = gHwndStatus;
	if ( !wind )
		wind = gHwndNavigator;
	
	displayErrMsgWnd( szErrStr, MB_OK | MB_ICONEXCLAMATION, wind );  
}

static void connectErr( DWORD dwError )
{
	
	char strText[ 255 ];

	if ( gHwndStatus )
	{
        getMsgString( (char*)strText, IDS_DIAL_ERR );
        setStatusDialogText( IDC_DIAL_STATUS, strText );
		Sleep( 1000 );
		removeStatusDialog();
	}

	gDeviceErr = TRUE;		// some sort of device err
	displayDialErrorMsg( dwError );
}

BOOL CALLBACK statusDialogCallback( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	BOOL		bRetval = FALSE;
	DWORD		dwRet;

	if ( !gHwndStatus )
		gHwndStatus = hWnd;

	switch ( uMsg )
	{
		case WM_INITDIALOG:
			return TRUE;
		break;
		
		case WM_COMMAND:
		{
			WORD wNotifyCode = HIWORD( wParam );
            WORD wID = LOWORD( wParam );
            HWND hControl = (HWND)lParam;

            switch ( wID )
            {
				case IDDISCONNECTED:
//                if (AfxMessageBox(IDS_LOST_CONNECTION, MB_YESNO) == IDYES)
//                   m_pMainWnd->PostMessage(WM_COMMAND, IDC_DIAL);
					break;

				case IDCANCEL:
				{
					// RasHangUp & destroy dialog box
					bRetval = TRUE;
					gCancelled = TRUE;

					ASSERT( m_lpfnRasHangUp );

					char strText[ 255 ];

					getMsgString( strText, IDS_CANCELDIAL );
		            setStatusDialogText( IDC_DIAL_STATUS, strText );		
	
					dwRet = ( *m_lpfnRasHangUp )( gRasConn );

					if ( dwRet == ERROR_INVALID_HANDLE)
						trace("dialer.cpp : statusDialogCallback - Can't hangup. Invalid Connection Handle. (r)");
					else if ( dwRet && dwRet != ERROR_INVALID_HANDLE )
						trace("dialer.cpp : statusDialogCallback - Can't hangup. Error %d. (r)", dwRet);

					Sleep( 3000 );
					removeStatusDialog();
					break;
				}
			}
		}

	}
	return bRetval;
}

static void setCallState( CallState newState )
{
	gCallState = newState;
    
	switch ( gCallState )
	{
		case StateConnected:
			// destroy our connection status window
			removeStatusDialog();
		break;

		case StateConnecting:
			// creates status dialog box
			HWND	hwndParent = GetActiveWindow();
			int		nResult;
			
			nResult = DialogBox( gDLL, MAKEINTRESOURCE( IDD_STATUS ), hwndParent, (DLGPROC)statusDialogCallback );
			ASSERT( nResult != -1 );
		break;
	}
}

//********************************************************************************
// RasDialFunc
//  call back function for RasDial
//********************************************************************************
void CALLBACK
RasDialFunc( HRASCONN hRasConn, UINT uMsg,
	RASCONNSTATE rasConnState, DWORD dwError, DWORD dwExtendedError )
{
	if ( uMsg != WM_RASDIALEVENT )
		return;
	                                     // ignore all other messages
	gRASstate = rasConnState;

	char		strText[ 255 ];
	DWORD		dwRet;

	switch ( rasConnState )
	{
		case RASCS_OpenPort:
			// wait for status dialog to show up first
			while ( gHwndStatus == NULL )
				Sleep( 1000 );
		
			getMsgString( strText, IDS_OPENING_PORT );
			setStatusDialogText( IDC_DIAL_STATUS, strText );		
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		break;
		
		case RASCS_PortOpened:
			getMsgString( strText, IDS_INIT_MODEM );
			setStatusDialogText( IDC_DIAL_STATUS, strText );
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		
		break;
		
		case RASCS_ConnectDevice:
			if ( gDialAttempts == 1 )
			{
				getMsgString( strText, IDS_DIALING );
				setStatusDialogText( IDC_DIAL_STATUS, strText );
			}
			else
			{
				char    szBuf[ 128 ];
		
				getMsgString(strText, IDS_DIALING_OF);
				wsprintf( szBuf, (LPCSTR)strText, gDialAttempts, NUM_ATTEMPTS );
				setStatusDialogText( IDC_DIAL_STATUS, strText );
			}
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		break;
		
		case RASCS_Authenticate:
			getMsgString( strText, IDS_VERIFYING );
			setStatusDialogText( IDC_DIAL_STATUS, strText );
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		break;
		
		case RASCS_Authenticated:
			getMsgString( strText, IDS_LOGGING_ON );
			setStatusDialogText( IDC_DIAL_STATUS, strText );
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		break;
		
		case RASCS_Connected:
			getMsgString( strText, IDS_CONNECTED );
			setStatusDialogText( IDC_DIAL_STATUS, strText );
			setCallState( StateConnected );
			if ( dwError )
				connectErr( dwError );
			else
				Sleep( 1000 );
		break;
		
		case RASCS_Disconnected:
			// If this is an unexpected disconnect then hangup and take 
			// down the status dialog box
			if ( gCallState == StateConnected || gCallState == StateConnecting )
			{
				ASSERT( m_lpfnRasHangUp );
				dwRet = ( *m_lpfnRasHangUp )( gRasConn );
		
				if ( dwRet == ERROR_INVALID_HANDLE )
					trace("dialer.cpp : ProcessRasDialEvent (stateConnected) - Can't hangup. Invalid Connection Handle.");
				else if ( dwRet && dwRet != ERROR_INVALID_HANDLE )
					trace("dialer.cpp : ProcessRasDialEvent (stateConnected) - Can't hangup. Error %d", dwRet);

				Sleep( 3000 );
				if ( gCallState == StateConnecting )
				{
					if ( dwError != SUCCESS )
					{
						if ( gHwndStatus )
						{
					 		getMsgString( strText, IDS_DISCONNECTING );
					 		setStatusDialogText( IDC_DIAL_STATUS, strText );
					 	}
					}
				}

				// here we pass redial msg if needed.
				removeStatusDialog();
				if ( gCallState == StateConnecting )
				{
					gLineDrop = TRUE;  // remove if we ask users for redial
					displayDialErrorMsg( dwError );
				}
			}
		
			setCallState( StateIdle );
		break;
		
		case RASCS_WaitForModemReset:
			getMsgString( strText, IDS_DISCONNECTING );
			setStatusDialogText( IDC_DIAL_STATUS, strText );
			if ( dwError )
			{
				trace("dialer.cpp : ProcessRasDialEvent (WaitForModemReset) - Connection Error %d", dwError);
				connectErr( dwError );
			}
			else
				Sleep( 1000 );
		
		break;
		
		default:
			if ( dwError )
			{
				trace( "dialer.cpp : ProcessRasDialEvent (default case) - Connection Error %d", dwError );
				connectErr( dwError );
			}
		break;
	}
}




//********************************************************************************
// DialerConnect
// initiates a dialer connection (used if Dial on Demand is disabled)
// assumes the dialer has already been configured
// returns:
//	FALSE if the dialer cannot be connected
//	TRUE if the dialer is already connected or if it was able to connect
//		successfully
//********************************************************************************
BOOL DialerConnect()
{
	DWORD			dwError;
	DWORD			dwRet;
	BOOL			connectSucceed = TRUE;

	gHwndNavigator = GetActiveWindow();

	// return if dialer already connected
	if ( IsDialerConnected() )
		return TRUE;
 
	gDialAttempts = 1;
	gRasConn = NULL; 		// init global connection handle
	gCancelled = FALSE;		// assume connection is not canceled by the user, unless otherwise


	char*			pbPath = NULL;
	LPVOID			notifierProc = NULL;
	DWORD			notifierType = 0;
	
	notifierType = 1;
	notifierProc = (LPVOID)RasDialFunc;
	
	// WinNT40 find system phone book first then start RASDIAL
	if ( gPlatformOS == VER_PLATFORM_WIN32_NT )
	{
		startRasMonNT();	// should check the error here, but I don't know
							// what to do if we fail (report that error?)

		pbPath = getPhoneBookNT();
	}

	ASSERT( m_lpfnRasDial );
	// do the dialing here
	dwError = ( *m_lpfnRasDial )( NULL, pbPath, &gDialParams, notifierType, notifierProc, &gRasConn );

	if ( dwError == ERROR_NOT_ENOUGH_MEMORY )
		trace( "dialer.cpp : [native] DialerConnect - Not enough memory for dialing activity. Dialing failed." );

	else if ( dwError && dwError != ERROR_NOT_ENOUGH_MEMORY )
		trace( "dialer.cpp : [native] DialerConnect - Dialing failed. Error code = %d", dwError );

	   
	if ( dwError == SUCCESS )
	{
		// Dialing succeeded
		// display connections status dialog & dispatch window msgs...

		MSG			msg;
		setCallState( StateConnecting );
		while ( (	( gRASstate != RASCS_Connected ) &&
					( gRASstate != RASCS_Disconnected ) ) &&
				( !gCancelled ) &&
				( !gLineDrop ) &&
				( !gDeviceErr) )
		{
			if ( ::GetMessage( &msg, NULL, 0, 0 ) )
			{
				::TranslateMessage( &msg );
				::DispatchMessage( &msg );
			}
			else
			{
				// WM_QUIT!!!
				break;
			}
		}
		
		removeStatusDialog();
		if ( ( gRASstate != RASCS_Connected ) || gCancelled )
			connectSucceed = FALSE;

	}
	else
	{
		// dialing failed!!!, display err msg
		connectSucceed = FALSE;
		displayDialErrorMsg( dwError );
	}

	if ( !connectSucceed )
	{
		// hangup connection
		if ( gRasConn )
		{
			ASSERT( m_lpfnRasHangUp );
			dwRet = ( *m_lpfnRasHangUp )( gRasConn );
			
			if ( dwRet == ERROR_INVALID_HANDLE )
				trace("dialer.cpp : [native] DialerConnect - Can't hangup. Invalid Connection Handle.");
			else if ( dwRet && dwRet != ERROR_INVALID_HANDLE )
				trace("dialer.cpp : [native] DialerConnect - Can't hangup. Error %d", dwRet);

			// give RasHangUp some time till complete hangup
			removeStatusDialog();
			// wait three seconds for tear-down of notification proc.
			// should figure out how to do this better (maybe set something
			// in RasDialFunc which ensures us not getting called again?)
			Sleep( 3000 );
		}
	}

	SetActiveWindow( gHwndNavigator );
                                             
	return connectSucceed;
}


//********************************************************************************
// DialerHangup
// terminates a dialer connection
//	if there is an active connection, it is terminated
// returns:
//	nothing
//********************************************************************************
void DialerHangup() 
{
	RASCONN*		info = NULL;
	RASCONN*		lpTemp = NULL;
	DWORD			code;
	DWORD			count = 0;
	DWORD			dSize = stRASCONN;
	DWORD			dwRet;
    BOOL			triedOnce = FALSE;
    
tryAgain:
	// try to get a buffer to receive the connection data
	info = (RASCONN*)LocalAlloc( LPTR, (UINT)dSize );

	if ( !info )
		return;
	
	// set RAS struct size
 	info->dwSize = dSize;

	ASSERT( m_lpfnRasEnumConnections);
	// enumerate open connections
	code = ( *m_lpfnRasEnumConnections )( info, &dSize, &count );

	if ( code != 0 && triedOnce == TRUE )
	{
		LocalFree( info );
		return;
	}
	
	if ( code == ERROR_BUFFER_TOO_SMALL )
	{
		triedOnce = TRUE;
		
		// free the old buffer & allocate a new bigger one
		LocalFree( info );
		goto tryAgain;
	}
	
	// check for no connections
	if ( count == 0 )
	{
		LocalFree( info );
		return;
	}

	ASSERT( m_lpfnRasHangUp );
	// just hang up everything
	for ( int i = 0; i < (int) count; i++ )
	{
		dwRet = ( *m_lpfnRasHangUp )( info[ i ].hrasconn );

		if ( dwRet == ERROR_INVALID_HANDLE )
			trace( "dialer.cpp : DialerHangup - Can't hangup. Invalid Connection Handle." );
		else if ( dwRet && dwRet != ERROR_INVALID_HANDLE )
			trace("dialer.cpp : DialerHangup - Can't hangup. Error %d.", dwRet);

		Sleep( 3000 );
	}

	LocalFree( info );
}

//********************************************************************************
// findDialerData
// search the javascript array for specific string value
//********************************************************************************
char* findDialerData( char** dialerData, char* name )
{
	char**	entry = dialerData;
	char*	value;
	
	while ( *entry )
	{
		if ( strncmp( *entry, name, strlen( name ) ) == 0 )
		{
			value = strchr( *entry, '=' );
			if ( value )
				value++;
			break;
		}
		entry++;
	}
	
	return (char*)value;
}


//********************************************************************************
// fillAccountParameters
// fill in account information, given from JS array
//********************************************************************************
static void fillAccountParameters( char** dialerData, ACCOUNTPARAMS* account )
{
	char*		value;
	
	value = findDialerData( dialerData, "AccountName" );
	strcpy( account->ISPName, value ? value : "My Account" ); 
	
	// file name
	value = findDialerData( dialerData, "FileName" );
	strcpy( account->FileName, value ? value : "My Account" );
	// DNS
	value = findDialerData( dialerData, "DNSAddress" );
	strcpy( account->DNS, value ? value : "0.0.0.0" );
	// DNS2
	value = findDialerData( dialerData, "DNSAddress2" );
	strcpy( account->DNS2, value ? value : "0.0.0.0" );
	// domain name
	value = findDialerData( dialerData, "DomainName" );
	strcpy( account->DomainName, value ? value : "" );
	// login name
	value = findDialerData( dialerData, "LoginName" );
	strcpy(account->LoginName, value ? value : "");
	// password
	value = findDialerData( dialerData, "Password" );
	strcpy( account->Password, value ? value : "" );
	// script file name
	value = findDialerData( dialerData, "ScriptFileName" );
	strcpy( account->ScriptFileName, value ? value : "" );
	// script enabled?
	value = findDialerData( dialerData, "ScriptEnabled" );
	if ( value )
	{
		account->ScriptEnabled = ( strcmp( value, "TRUE" ) == 0 );
		// get script content
		value = findDialerData( dialerData, "Script" );
		if ( value )
		{
		//	ReggieScript = (char*) malloc(strlen(value) + 1);
		//	strcpy(ReggieScript, value);
		}		   	
	}
	else
		account->ScriptEnabled = 0;
	// need TTY window?
	value = findDialerData( dialerData, "NeedsTTYWindow" );
	account->NeedsTTYWindow = ( value && ( strcmp( value, "TRUE" ) == 0 ) );
	// VJ compression enabled?
	value = findDialerData( dialerData, "VJCompresssionEnabled" );
	account->VJCompressionEnabled = ( value && ( strcmp( value, "TRUE" ) == 0 ) );
	// International mode?
	value = findDialerData( dialerData, "IntlMode" );
	account->IntlMode = ( value && ( strcmp( value, "TRUE" ) == 0 ) );
	// dial on demand?
	value = findDialerData( dialerData, "DialOnDemand" );
	account->DialOnDemand = ( value && ( strcmp( value, "TRUE" ) == 0 ) );
	// isp phone number
	value = findDialerData( dialerData, "ISPPhoneNum" );
	if ( value )
		strcpy( account->ISPPhoneNum, value );
	else
		strcpy( account->ISPPhoneNum, "" );
	// ISDN phone number
	value = findDialerData( dialerData, "ISDNPhoneNum" );
	if ( value )
		strcpy( account->ISDNPhoneNum, value );
	else
		strcpy( account->ISDNPhoneNum, "" );
	
}


//********************************************************************************
// fillLocationParameters
// fill in location information, given from JS array
//********************************************************************************
static void fillLocationParameters( char** dialerData, LOCATIONPARAMS* location )
{
	char*		value;
	
	// modem name
	value = findDialerData( dialerData, "ModemName" );
	strcpy( location->ModemName, value ? value : "" );
	// modem type
	value = findDialerData( dialerData, "ModemType" );
	strcpy( location->ModemType, value ? value : "" );
	// dial type
	value = findDialerData( dialerData, "DialType" );
	location->DialType = !( value && ( strcmp( value, "TONE" ) == 0 ) );
	// outside line access
	value = findDialerData( dialerData, "OutsideLineAccess" );
	strcpy( location->OutsideLineAccess, value ? value : "" );
	// disable call waiting?
	value = findDialerData( dialerData, "DisableCallWaiting");
	location->DisableCallWaiting = ( value && ( strcmp(value, "TRUE") == 0 ) );
	// disable call waiting code
	value = findDialerData( dialerData, "DisableCallWaitingCode" );
	strcpy( location->DisableCallWaitingCode, value ? value : "" );
	// user area code
	value = findDialerData( dialerData, "UserAreaCode" );
	strcpy( location->UserAreaCode, value ? value : "" );
	// user country code
	value = findDialerData( dialerData, "CountryCode" );
	if ( value )
	{
		char*	stopstr = "\0";
		location->UserCountryCode = (short)strtol( value, &stopstr, 10 );
	}
	else
		location->UserCountryCode = 1;   // default to US
	// dial as long distance?
	value = findDialerData( dialerData, "DialAsLongDistance");
	location->DialAsLongDistance = ( value && ( strcmp( value, "TRUE" ) == 0 ) );	
	// long distance access
	value = findDialerData( dialerData, "LongDistanceAccess" );
	strcpy( location->LongDistanceAccess, value ? value : "" );
	// dial area code?
	value = findDialerData( dialerData, "DialAreaCode" );
	location->DialAreaCode = ( value && ( strcmp( value, "TRUE" ) == 0 ) );	
	// dial prefix code
	value = findDialerData( dialerData, "DialPrefix" );
	strcpy( location->DialPrefix, value ? value : "" );
	// dial suffix code
	value = findDialerData( dialerData, "DialSuffix" );
	strcpy( location->DialSuffix, value ? value : "" );
	// use both ISDN lines?
	value = findDialerData( dialerData, "UseBothISDNLines" );
	location->UseBothISDNLines = ( value && ( strcmp( value, "TRUE" ) == 0 ) );	
	// 56k ISDN?
	value = findDialerData( dialerData, "56kISDN" );
	location->b56kISDN = ( value && ( strcmp( value, "TRUE" ) == 0 ) );
	// disconnect time
	value = findDialerData( dialerData, "DisconnectTime" );
	if ( value )
		location->DisconnectTime = atoi( value );
	else
		location->DisconnectTime = 5;
}

//********************************************************************************
// onlyOneSet
// Just an XOR of DialAsLongDistance & DialAreaCode - if only one of them is
// set then we can't use MS Locations (if neither are set then we can use 
// locations, but disable use of both - they just don't allow disabling of each
// individually)
//********************************************************************************
static BOOL onlyOneSet( const LOCATIONPARAMS& location )
{
	return ( location.DialAsLongDistance ?
		( location.DialAreaCode ? FALSE : TRUE ) :
		( location.DialAreaCode ? TRUE : FALSE ) );
}

//********************************************************************************
// prefixAvail
// returns:
//	TRUE if there are prefixes that make location unusable
//********************************************************************************
static BOOL prefixAvail( const LOCATIONPARAMS& location )
{
	return ( location.DisableCallWaiting && location.DisableCallWaitingCode[ 0 ] != 0 ) ||
		( location.OutsideLineAccess && location.OutsideLineAccess[ 0 ] != 0 );
}

//********************************************************************************
// parseNumber
//
// Parses a canonical TAPI phone number into country code, area code, and
// local subscriber number
//********************************************************************************
static void parseNumber( LPCSTR lpszCanonical, LPDWORD lpdwCountryCode,
	LPSTR lpszAreaCode, LPSTR lpszLocal)
{
	//*** sscanf dependency removed for win16 compatibility
	char		temp[ 256 ];
	int			p1, p2;

	// Initialize our return values
	*lpdwCountryCode = 1;  // North America Calling Plan
	*lpszAreaCode = '\0';
	*lpszLocal = '\0';

	if ( !lpszCanonical || !*lpszCanonical )
		return;

	// We allow three variations (two too many):
	//  -: +1 (415) 428-3838    (TAPI canonical number)
	//  -: (415) 428-3838       (TAPI canonical number minus country code)
	//  -: 428-3838             (subscriber number only)
	//
	// NOTE: this approach only works if there is a city/area code. The TAPI
	// spec says the city/area code is optional for countries that have a flat
	// phone numbering system

	// Take my advice, always start at the beginning.
	p1 = 0;

	// Allow spaces
	while ( lpszCanonical[p1] == ' ' )
		p1++;

	// Handle the country code if '+' prefix seen
	if ( lpszCanonical[p1] == '+' )
	{
		p1++;
		if ( !isdigit( lpszCanonical[ p1 ] ) )
			return;

		p2 = p1;
		while ( isdigit( lpszCanonical[ p1 ] ) )
			p1++;
		strncpy( temp, &lpszCanonical[ p2 ], p1 - p2 );
		*lpdwCountryCode = atoi( temp );
	}

	// Allow spaces
	while ( lpszCanonical[p1] == ' ' )
		p1++;

	// Handle the area code if '(' prefix seen
	if ( lpszCanonical[ p1 ] == '(' )
	{
		p1++;
		if ( !isdigit( lpszCanonical[ p1 ] ) )
			return;

		p2 = p1;
		while ( isdigit( lpszCanonical[ p1 ] ) )
			p1++;
		strncpy( lpszAreaCode, &lpszCanonical[ p2 ], p1 - p2 );

		p1++;      // Skip over the trailing ')'
	}

	// Allow spaces
	while ( lpszCanonical[p1] == ' ' )
		p1++;

	// Whatever's left is the subscriber number (possibly including the whole string)
	strcpy( lpszLocal, &lpszCanonical[ p1 ] );
}

//********************************************************************************
// composeNumber
// create a phone number encompassing all of the location information to hack
// around dialup networking ignoring the location information if you turn off
// the "dial area and country code" flag
//********************************************************************************
static void composeNumber( ACCOUNTPARAMS& account, const LOCATIONPARAMS& location, char csNumber[] )
{
	// if they need to dial something to get an outside line next
	if ( location.OutsideLineAccess[ 0 ] != 0 )
	{
        strcat( csNumber, location.OutsideLineAccess );
        strcat( csNumber, " " );
	}

	// add disable call waiting if it exists
	if ( location.DisableCallWaiting && location.DisableCallWaitingCode[ 0 ] != 0 )
	{
		strcat( csNumber, location.DisableCallWaitingCode );
		strcat( csNumber, " ");
	}
	
	if ( account.IntlMode )
	{ 
	
		// In international mode we don't fill out the area code or
		//   anything, just the exchange part
		strcat( csNumber, account.ISPPhoneNum );
	}
	else
	{

		// lets parse the number into pieces so we can get the area code & country code
		DWORD		nCntry;
		char		szAreaCode[ 32 ];
		char		szPhoneNumber[ 32 ];
		parseNumber( account.ISPPhoneNum, &nCntry, szAreaCode, szPhoneNumber );
		
		// dial the 1 (country code) first if they want it
		if ( location.DialAsLongDistance )
		{
			char	cntry[ 10 ];
			ultoa( nCntry, cntry, 10 );
		
			if ( strcmp( location.LongDistanceAccess, "" ) == 0 )
				strcat( csNumber, cntry );
			else 
				strcat( csNumber, location.LongDistanceAccess );
		
			strcat( csNumber, " " );
		}
		
		// dial the area code next if requested
		if ( location.DialAreaCode )
		{
			strcat( csNumber, szAreaCode );
			strcat( csNumber, " " );
		}
	
		// dial the local part of the number
		strcat( csNumber, szPhoneNumber );
	}
}

//********************************************************************************
// toNumericAddress
// converts from dotted address to numeric internet address
//********************************************************************************
static BOOL toNumericAddress( LPCSTR lpszAddress, DWORD& dwAddress )
{
	//*** sscanf dependency removed for win16 compatibility

	char		temp[ 256 ];
	int			a, b, c, d;
	int			p1, p2;

	strcpy( temp, lpszAddress );

	p2 = p1 = 0;
	while ( temp[ p1 ] != '.' )
		p1++;
	temp[ p1 ] = '\0';
	a = atoi( &temp[ p2 ] );
	
	p2 = ++p1;
	while ( temp[ p1 ] != '.' )
		p1++;
	temp[ p1 ] = '\0';
	b = atoi( &temp[ p2 ] );

	p2 = ++p1;
	while ( temp[ p1 ] != '.' )
		p1++;
	temp[ p1 ] = '\0';
	c = atoi( &temp[ p2 ] );

	p2 = ++p1;
	d = atoi( &temp[ p2 ] );

    // Must be in network order (different than Intel byte ordering)
    LPBYTE  lpByte = (LPBYTE)&dwAddress;

    *lpByte++ = BYTE( a );
    *lpByte++ = BYTE( b );
    *lpByte++ = BYTE( c );
    *lpByte = BYTE( d );

	return TRUE;
}

//********************************************************************************
// getCountryID
//********************************************************************************
static BOOL getCountryID( DWORD dwCountryCode, DWORD& dwCountryID )
{
	ASSERT( m_lpfnRasGetCountryInfo );
	if ( !m_lpfnRasGetCountryInfo )
	{
		trace( "dialer.cpp : GetCountryID - RasGetCountryinfo func not availble. (r)" );
		return FALSE;
	}
		
	RASCTRYINFO*		pCI = NULL;
	BOOL				bRetval = FALSE;

	DWORD				dwSize = stRASCTRYINFO + 256;
	pCI = (RASCTRYINFO*)malloc( (UINT) dwSize );
	
	if ( pCI )
	{
		pCI->dwSize = stRASCTRYINFO;
		pCI->dwCountryID = 1;
      
		while ( ( *m_lpfnRasGetCountryInfo )( pCI, &dwSize ) == 0 )
		{
			if ( pCI->dwCountryCode == dwCountryCode )
			{
				dwCountryID = pCI->dwCountryID;
				bRetval = TRUE;
				break;
			}
			pCI->dwCountryID = pCI->dwNextCountryID;
		}

		free( pCI );
		pCI = NULL;
	}

	return bRetval;
}


//********************************************************************************
// createLink
// creates a shell shortcut to the PIDL
//********************************************************************************
static HRESULT createLink( LPITEMIDLIST pidl, LPCTSTR lpszPathLink, LPCTSTR lpszDesc,
	char* iconPath )
{
    HRESULT			hres;
    IShellLink*		psl = NULL;

    // Get a pointer to the IShellLink interface.
	//CoInitialize(NULL); // work around for Nav thread lock bug

	hres = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
							IID_IShellLink, (LPVOID FAR*)&psl );
    if ( SUCCEEDED( hres ) )
    {
		IPersistFile* ppf;
		// Set the path to the shortcut target, and add the description.
		psl->SetIDList( pidl );
		psl->SetDescription( lpszDesc );
		if( iconPath && iconPath[ 0 ] )
			psl->SetIconLocation( iconPath, 0 );

		// Query IShellLink for the IPersistFile interface for saving the
		// shortcut in persistent storage.
		hres = psl->QueryInterface( IID_IPersistFile, (LPVOID FAR*)&ppf );

		if ( SUCCEEDED( hres ) )
		{
			WORD		wsz[ MAX_PATH ];

			// Ensure that the string is ANSI.
			MultiByteToWideChar( CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH );

			// Save the link by calling IPersistFile::Save.
			hres = ppf->Save( (LPCOLESTR)wsz, STGM_READ );
			ppf->Release();
		}

		psl->Release();
	}

	//CoUninitialize();

	return hres;
}

//********************************************************************************
// fileExists
//********************************************************************************
static BOOL fileExists( LPCSTR lpszFileName )
{
	BOOL bResult = FALSE;

	HANDLE hFile=NULL;

	// opens the file for READ
	hFile = CreateFile(lpszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
						FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile != INVALID_HANDLE_VALUE) {  // openned file is valid
		bResult = TRUE;
		CloseHandle(hFile);
	}
	return bResult;
}


//********************************************************************************
// processScriptLogin
// generate a script file and return the name of the file.  The
//   caller is responsible for freeing the script file name
//********************************************************************************
static BOOL processScriptedLogin( LPSTR lpszBuf, const char* lpszScriptFile )
{
    // validate our args just for fun
    if (!lpszBuf || (lpszBuf[0] == '\0') || !lpszScriptFile)
        return(FALSE);

    // open the actual script file  
    FILE * fp = fopen(lpszScriptFile, "w");
    if (!fp)
        return(FALSE);   

    // generate a prolog
    char timebuf[24];
    char datebuf[24];
    _strtime(timebuf);
    _strdate(datebuf);
    fprintf(fp, "; %s\n; Created: %s at %s\n;\n;\nproc main\n", lpszScriptFile, datebuf, timebuf);

    // Send a return to poke the server.  Is this needed?
    fprintf(fp, "transmit \"^M\"\n"); 

    for (int i = 0; lpszBuf; i++) {
       LPSTR   lpszDelim;

       // Each event consists of two parts:
       //   1. string to wait for
       //   2. string to reply with
       //
       // The string to reply with is optional. A '|' separates the two strings
       // and another '|' separates each event
       lpszDelim = strchr(lpszBuf, '|');  
       if(lpszDelim)
          *lpszDelim = '\0';
   
       // we are in the "wait for event"
       fprintf(fp, "waitfor \"%s\",matchcase until 30\n", lpszBuf); 

       // skip to the next bit
       lpszBuf = lpszDelim ? lpszDelim + 1 : NULL;

       if (lpszBuf) {
          // now look for the reply event
          lpszDelim = strchr(lpszBuf, '|');  
          if(lpszDelim)
          *lpszDelim = '\0';
       
          // we are in the "reply with" event
          // NOTE: we will want to get the ^M value from someone else
          //   since different ISPs will probably want different ones
          if (!stricmp(lpszBuf, "%name"))
             fprintf(fp, "transmit $USERID\n"); 
          else if(!stricmp(lpszBuf, "%password"))
             fprintf(fp, "transmit $PASSWORD\n"); 
          else if(lpszBuf[0])
             fprintf(fp, "transmit \"%s\"\n", lpszBuf); 

          fprintf(fp, "transmit \"^M\"\n"); 
       }

      lpszBuf = lpszDelim ? lpszDelim + 1 : NULL;
    }                                     

   // writeout the ending bits and cleanup
    fprintf(fp, "endproc\n");
    fclose(fp);

    return(TRUE);
}


//********************************************************************************
// createPhoneBookEntry
// Create a dial-up networking profile
//********************************************************************************
static BOOL createPhoneBookEntry( ACCOUNTPARAMS account, const LOCATIONPARAMS& location )
{
	DWORD		dwRet;
	BOOL		ret = FALSE;
	RASENTRY	rasEntry;
	
	// abort if RAS API ptrs are invalid & mem alloc fails
	ASSERT( m_lpfnRasSetEntryProperties );
	ASSERT( m_lpfnRasSetEntryDialParams );
		
	if ( !m_lpfnRasSetEntryProperties || 
		 !m_lpfnRasSetEntryDialParams )
		return FALSE;

	// Initialize the RNA struct
	memset( &rasEntry, 0, stRASENTRY );
	rasEntry.dwSize = stRASENTRY;
	rasEntry.dwfOptions = RASEO_ModemLights | RASEO_RemoteDefaultGateway;

	// Only allow compression if reg server says its OK
	if ( account.VJCompressionEnabled )
		 rasEntry.dwfOptions |= RASEO_IpHeaderCompression | RASEO_SwCompression;

	if ( account.NeedsTTYWindow )
		if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
			rasEntry.dwfOptions |= RASEO_TerminalBeforeDial;  //win95 bug! RASEO_TerminalBeforeDial means terminal after dial
		else
			rasEntry.dwfOptions |= RASEO_TerminalAfterDial;

	// If Intl Number (not NorthAmerica), or Area Code w/o LDAccess (1) or
	// visa-versa, then abandon using Location - NOTE: for Intl Number we
	// should be able to use location, check it out!
	if ( account.IntlMode || onlyOneSet( location ) )
	{
		char szNumber[ RAS_MaxPhoneNumber + 1 ];
		szNumber[ 0 ] = '\0';

		composeNumber( account, location, szNumber );
		strcpy( rasEntry.szLocalPhoneNumber, szNumber );
	   
		strcpy( rasEntry.szAreaCode, "415" );	// hack around MS bug -- ignored
		rasEntry.dwCountryCode = 1;				// hack around MS bug -- ignored
	}
	else
	{
		// Let Win95 decide to dial the area code or not
		rasEntry.dwfOptions |= RASEO_UseCountryAndAreaCodes;

		trace( "dialer.cpp : Use country and area codes = %d", rasEntry.dwfOptions );

		// Configure the phone number
		parseNumber( account.ISPPhoneNum, &rasEntry.dwCountryCode, 
						rasEntry.szAreaCode, rasEntry.szLocalPhoneNumber );
		  
		if ( !account.IntlMode )
		{
			// if not internationalize version, check the area code and make
			// sure we got a valid area code, if not throw up a err msg
			if ( rasEntry.szAreaCode[ 0 ] == '\0' )
			{
				// Err: The service provider's phone number is missing its area code
				//    (or is not in TAPI cannonical form in the configuration file).
				//    Account creation will fail until this is fixed.
				char*	buf = (char*)malloc( sizeof(char) * 255 );
				if ( buf )
				{
					if ( getMsgString( buf, IDS_MISSING_AREA_CODE ) )
						displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, gHwndNavigator );
					free( buf );
				}
				return FALSE;
			}
		}
	}

	// Now that we have the country code, we need to find the associated
	// country ID
	getCountryID( rasEntry.dwCountryCode, rasEntry.dwCountryID );

	// Configure the IP data
	rasEntry.dwfOptions |= RASEO_SpecificNameServers;
	if ( account.DNS[ 0 ] )
		toNumericAddress( account.DNS, *(LPDWORD)&rasEntry.ipaddrDns );

	if ( account.DNS2[ 0 ] )
		toNumericAddress( account.DNS2, *(LPDWORD)&rasEntry.ipaddrDnsAlt );

	// Configure the protocol and device settings here:

	// Negotiate TCP/IP
	rasEntry.dwfNetProtocols = RASNP_Ip;

	// Point-to-Point protocol (PPP)
	rasEntry.dwFramingProtocol = RASFP_Ppp;

	// modem's information
	strcpy( rasEntry.szDeviceName, location.ModemName );
	strcpy( rasEntry.szDeviceType, location.ModemType );

	// If we have a script, then store it too
	if ( account.ScriptEnabled ) 
	{
		BOOL rtnval = TRUE;
		
/*		// if there is script content, 'Translate' and store in file 
		if ( ReggieScript )
		{ 
		   	// construct script filename if it does not exists
		 	if ( strlen( account.ScriptFileName ) == 0 )
		   	{
				GetProfileDirectory( account.ScriptFileName );
				int nIndex = strlen( account.ScriptFileName );
				strncat( account.ScriptFileName, account.ISPName, 8 );
				strcat( account.ScriptFileName, ".scp" );
		   	} 
			rtnval = ProcessScriptedLogin( (LPSTR)ReggieScript, account.ScriptFileName );
			free( ReggieScript );
		}


		// if there really is a script file (from ISP or Reggie) then use it
		if ( rtnval && FileExists( account.ScriptFileName ) )
		{
			strcpy( rasEntry.szScript, account.ScriptFileName );

			// convert forward slash to backward slash
			int nLen = strlen( rasEntry.szScript );
			for ( int i = 0; i < nLen; i++ )
				if ( rasEntry.szScript[ i ] == '/' )
					rasEntry.szScript[ i ] = '\\';
		}
*/
	}

	dwRet = ( *m_lpfnRasValidateEntryName )( NULL, (LPSTR)account.ISPName );
	
	if ( dwRet == ERROR_ALREADY_EXISTS )
		dwRet = 0;
		
	ASSERT( dwRet == 0 );
	if ( dwRet == ERROR_INVALID_NAME )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasValidateEntryName) - Invalid Name. Can't set RasEntry properties. (r)");
		return FALSE;
	}
	else if ( dwRet == ERROR_ALREADY_EXISTS )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasValidateEntryName) - This name already exists. (r)");
		return FALSE;
	}
	else if ( dwRet )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasValidateEntryName) - Can't Validate account name. Error %d. (r)", dwRet);
		return FALSE;
	}
	
	dwRet = ( *m_lpfnRasSetEntryProperties )( NULL, (LPSTR)(LPCSTR)account.ISPName,
										  (LPBYTE)&rasEntry, stRASENTRY, NULL, 0 );
	ASSERT( dwRet == 0 );
	//if ( dwRet )
		//return -1;		// ??? this is going to return TRUE
	if ( dwRet == ERROR_BUFFER_INVALID )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryProperties) - Invalid Buffer. Can't set RasEntry properties. (r)");
		return FALSE;
	}
	else if ( dwRet == ERROR_CANNOT_OPEN_PHONEBOOK )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryProperties) - Can't open phonebook. Corrupted phonebook or missing components. Can't set RasEntry properties. (r)");
		return FALSE;
	}
	else if ( dwRet )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryProperties) - Can't set RasEntry properties. Error %d. (r)", dwRet);
		return FALSE;
	}

	// We need to set the login name and password with a separate call
	// why doesn't this work for winNT40??
	memset( &gDialParams, 0, sizeof( gDialParams ) );
	gDialParams.dwSize = stRASDIALPARAMS;
	strcpy( gDialParams.szEntryName, account.ISPName );
	strcpy( gDialParams.szUserName, account.LoginName );
	strcpy( gDialParams.szPassword, account.Password );

	// Creating connection entry!
   	char*		pbPath = NULL;

	if ( gPlatformOS = VER_PLATFORM_WIN32_NT )
		pbPath = getPhoneBookNT();
	else
		pbPath = (char*)account.ISPName;

	dwRet = ( *m_lpfnRasSetEntryDialParams )( pbPath, &gDialParams, FALSE );

	if ( dwRet == ERROR_BUFFER_INVALID )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryDialParams) - Invalid Buffer. Can't set RasEntry Dial properties. (r)");
		return FALSE;
	}
	else if ( dwRet == ERROR_CANNOT_OPEN_PHONEBOOK )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryDialParams) - Can't open phonebook. Corrupted phonebook or missing components. Can't set RasEntry Dial properties. (r)");
		return FALSE;
	}
	else if ( dwRet == ERROR_CANNOT_FIND_PHONEBOOK_ENTRY )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryDialParams) - Phonebook entry does not exist. Can't set RasEntry Dial properties. (r)");
		return FALSE;
	}
	else if ( dwRet )
	{
		trace ("dialer.cpp : CreateRNAEntry (RasSetEntryDialParams) - Can't set RasEntry Dial properties. Error %d. (r)", dwRet);
		return FALSE;
	}

	ret = ( dwRet == 0 );

	if ( gPlatformOS = VER_PLATFORM_WIN32_NT )
	{
		RASCREDENTIALS credentials;

		// sets up user login info for new phonebook entry
		memset( &credentials, 0, sizeof( RASCREDENTIALS ) );
		credentials.dwSize = sizeof( RASCREDENTIALS );
		credentials.dwMask = RASCM_UserName | RASCM_Password;
		strcpy( credentials.szUserName, account.LoginName );
		strcpy( credentials.szPassword, account.Password );
		strcpy( credentials.szDomain, account.DomainName );

		dwRet = ( *m_lpfnRasSetCredentials )( pbPath, (LPSTR)account.ISPName, &credentials, FALSE );

		if ( dwRet == ERROR_INVALID_PARAMETER )
		{
			trace( "dialer.cpp : CreateRNAEntry (RasSetCredentials) - Invalid Parameter. Can't set user credentials. (r)" );
			return FALSE;
		}
		else if ( dwRet == ERROR_CANNOT_OPEN_PHONEBOOK )
		{
			trace( "dialer.cpp : CreateRNAEntry (RasSetCredentials) - Can't open phonebook. Corrupted phonebook or missing components. Can't set user credentials. (r)" );
			return FALSE;
		}
		else if ( dwRet == ERROR_CANNOT_FIND_PHONEBOOK_ENTRY )
		{
			trace( "dialer.cpp : CreateRNAEntry (RasSetCredentials) - Phonebook entry does not exist. Can't set user credentials. (r)" );
			return FALSE;
		}
		else if ( dwRet == ERROR_INVALID_SIZE )
		{
			trace( "dialer.cpp : CreateRNAEntry (RasSetCredentials) - Invalid size value. Can't set user credentials. (r)" );
			return FALSE;
		}
		else if ( dwRet )
		{
			trace( "dialer.cpp : CreateRNAEntry (RasSetCredentials) - Can't set user credentials. Error %d. (r)", dwRet );
			return FALSE;
		}
		ret = ( dwRet == 0 );

		if ( pbPath )
			free( pbPath );
	}	

	// dialing on demand is cool.  let's do that on win95 now
	if ( ret == TRUE && account.DialOnDemand )
		if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
			EnableDialOnDemand95( (LPSTR)account.ISPName, TRUE );
		else if ( gPlatformOS == VER_PLATFORM_WIN32_NT )
			EnableDialOnDemandNT( (LPSTR)account.ISPName, TRUE );
	
	//SetAutoDisconnect( location.DisconnectTime );

	return ret;
}

//********************************************************************************
// DialerConfig
//
// setup and configures the dialer and networking stuff
// used in 3 conditions:
// 1. when calling regi for a new account
// 2. to configure new account from regi on users system
// 3. when optionally register Navigator in existing account path
//********************************************************************************
int DialerConfig( char** dialerDataArray )
{
	gHwndNavigator = GetActiveWindow();
	
	ACCOUNTPARAMS		account;
	LOCATIONPARAMS		location;
	
	if ( !dialerDataArray )
		return -1;
	
	// now we try to get values from the JS array and put them into corresponding 
	// account and location parameters
	fillAccountParameters( dialerDataArray, &account );
	fillLocationParameters( dialerDataArray, &location );
	
	// configure & creating Dial-Up Networking profile here for Win95 & WinNT40
	// win16 use Shiva's RAS 
	if ( !( createPhoneBookEntry( account, location ) ) )
	{
	
		// Err: Unable to crate RNA phone book entry!
		char*	buf = (char*)malloc( sizeof(char) * 255 );
		if ( buf )
		{
			if ( getMsgString( buf, IDS_NO_RNA_REGSERVER ) )
			displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, gHwndNavigator );
			free( buf );
		}
	}
	
//	int	ret;
//	if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
//	{
//		// sets the location stuff
//		ret = SetLocationInfo( account, location );
//		//ret = DisplayDialableNumber();
//	}
//	else
//	{
//		ret = SetLocationInfoNT( account, location );
//	}

	return 0;
}


//********************************************************************************
// IsDialerConnected
// checks if the dialer is still connected
//
// returns:
//	TRUE if the dialer is connected
//	FALSE if the dialer is not connected or there is an error trying to
//		determine if the dialer is connected
//********************************************************************************
BOOL IsDialerConnected()
{
	RASCONN*		info = NULL;
	RASCONN*		lpTemp = NULL;
	DWORD			code = 0;
	DWORD			count = 0;
	DWORD			dSize = stRASCONN;
	
	// try to get a buffer to receive the connection data
	if ( ! ( info = (RASCONN*)LocalAlloc( LPTR, dSize ) ) )
		return FALSE;
	
	// see if there are any open connections
	info->dwSize = stRASCONN;
	code = (*m_lpfnRasEnumConnections)( info, &dSize, &count );
	
	if ( 0 != code ) // ERROR_BUFFER_TOO_SMALL ?
	{
		// free the old buffer
		LocalFree( info );
	
		// allocate a new bigger one
		info = (RASCONN*)LocalAlloc( LPTR, dSize );
		if ( !info )
			return FALSE;
	
		// try to enumerate again
		info->dwSize = dSize;
		if ( (*m_lpfnRasEnumConnections) (info, &dSize, &count ) != 0 )
		{
			LocalFree( info );
			// can't enumerate connections, assume none is active
			return FALSE;
		}
	}
	
	// check for no connections
	if ( 0 == count )
	{
		LocalFree( info );
		return FALSE;
	}
	
	LocalFree( info );
	
	return TRUE;	
}


//********************************************************************************
// SetAutoDisconnect
// Sets the autodisconnect time if idle
// the parameter "disconnectTime" is specified as MINUTES, convert it to SECONDS
// as necessary
//********************************************************************************
void SetAutoDisconnect(DWORD disconnectTime)
{
	HKEY    hKey;
	DWORD   dwDisposition;
	long    result;
	DWORD	dwValue;

	// if it's win95
	if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
	{

		//
		// set the autodial flag first
		//
		result = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
								NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);

		// err, oops
		if (result != ERROR_SUCCESS)
		   return;

		// try setting autodisconnect here
		dwValue = 1;
		result = RegSetValueEx(hKey, "EnableAutoDisconnect", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		// default auto-disconnect after 5 minutes or as specified (with a minimal of 3 minutes)
		if (disconnectTime < 3)
			dwValue = 3;
		else
			dwValue = disconnectTime;
		result = RegSetValueEx(hKey, "DisconnectIdleTime", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		RegCloseKey(hKey);

		//
		// set the autodisconnect flags here too
		//
		result = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\Internet Settings",
								NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);

		// err, oops
		if (result != ERROR_SUCCESS)
		   return;

		dwValue = 1;
		result = RegSetValueEx(hKey, "EnableAutoDisconnect", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		// default auto-disconnect after 5 minutes
		if (disconnectTime < 3)
			dwValue = 3;
		else
			dwValue = disconnectTime;
		result = RegSetValueEx(hKey, "DisconnectIdleTime", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		RegCloseKey(hKey);


		//
		// also set the autodial flag here
		//
		result = RegCreateKeyEx(HKEY_USERS, ".Default\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
								NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);

		// err, oops
		if (result != ERROR_SUCCESS)
		   return;

		// try setting autodisconnect here
		dwValue = 1;
		result = RegSetValueEx(hKey, "EnableAutoDisconnect", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		// default auto-disconnect after 5 minutes or as specified (with a minimal of 3 minutes)
		if (disconnectTime < 3)
			dwValue = 3;
		else
			dwValue = disconnectTime;
		result = RegSetValueEx(hKey, "DisconnectIdleTime", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		RegCloseKey(hKey);

		//
		// set the autodisconnect flags here too
		//
		result = RegCreateKeyEx(HKEY_USERS, ".Default\\Software\\Microsoft\\Windows\\Internet Settings",
								NULL, NULL, NULL, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);

		// err, oops
		if (result != ERROR_SUCCESS)
		   return;

		dwValue = 1;
		result = RegSetValueEx(hKey, "EnableAutoDisconnect", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		// default auto-disconnect after 5 minutes
		if (disconnectTime < 3)
			dwValue = 3;
		else
			dwValue = disconnectTime;
		result = RegSetValueEx(hKey, "DisconnectIdleTime", NULL, REG_BINARY, (LPBYTE)&dwValue, sizeof(DWORD));

		RegCloseKey(hKey);

	} else { // NT40

		// we need to convert disconnectTime to # of seconds for NT40
		dwValue = (disconnectTime * 60);

		result = RegOpenKeyEx(HKEY_USERS, ".DEFAULT\\Software\\Microsoft\\RAS Phonebook", NULL, KEY_ALL_ACCESS, &hKey);

		if (result != ERROR_SUCCESS)
			return;

		// now set the auto disconnect seconds
		result = RegSetValueEx(hKey, "IdleHangupSeconds", NULL, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));

		RegCloseKey(hKey);


	} 

	return;
}


//********************************************************************************
// CheckDNS
// for Win95
// If user has DNS enabled, when setting up an account, we need to warn them
// that there might be problems.
//********************************************************************************
void CheckDNS()
{
    char		buf[ 256 ];
    HKEY		hKey;
    DWORD		cbData;
    LONG		res;

	// open the key if registry
	if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
	             "System\\CurrentControlSet\\Services\\VxD\\MSTCP", 
	             NULL, 
	             KEY_ALL_ACCESS   , 
	             &hKey ) )
		return;
	
	cbData = sizeof( buf );
	res = RegQueryValueEx( hKey, "EnableDNS", NULL, NULL, (LPBYTE)buf, &cbData );
	
	RegCloseKey( hKey );
	
	// if DNS is enabled we need to warn the user
	if( buf[ 0 ] == '1' )
	{
		BOOL		correctWinsockVersion = FALSE;
	
		// check for user's winsock version first, see if it's winsock2
		WORD		wVersionRequested;  
		WSADATA		wsaData; 
		int			err; 
	
		wVersionRequested = MAKEWORD( 2, 2 ); 
	
		err = WSAStartup( wVersionRequested, &wsaData ); 
	
		if ( err != 0 )
		{
			// user doesn't have winsock2, so check their winsock's date
			char		winDir[ MAX_PATH ];
			UINT		uSize = MAX_PATH;
			char		winsockFile[ MAX_PATH ];
		
			winDir[ 0 ] = '\0';
			winsockFile[ 0 ] = '\0';
			GetWindowsDirectory( (char*)&winDir, uSize );
			strcpy( winsockFile, winDir );
			strcat( winsockFile, "\\winsock.dll" );
		
			HANDLE hfile = CreateFile( (char*)&winsockFile, GENERIC_READ, 0, NULL,
								OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
		
			if ( hfile != INVALID_HANDLE_VALUE )
			{
				// openned file is valid
		
				FILETIME		lastWriteTime;
				BOOL rtnval = GetFileTime( hfile, NULL, NULL, &lastWriteTime );
		
				SYSTEMTIME		systemTime;
				rtnval = FileTimeToSystemTime( &lastWriteTime, &systemTime );
	
				// perfect example of why Windows software is so awful	
				if ( ( systemTime.wYear >= 1996 ) && ( systemTime.wMonth >= 8 ) &&
						( systemTime.wDay >= 24 ) )
					correctWinsockVersion = TRUE;
		
				CloseHandle( hfile );
			}
		}
	
		else
			correctWinsockVersion = TRUE;
	
	
		if ( !correctWinsockVersion )
		{
			// Err: Your system is configured for another Domain Name System (DNS) server.
			//    You might need to edit your DNS configuration.  Check the User's Guide
			//    for more information.
			char	buf[ 255 ];
			if ( getMsgString( buf, IDS_DNS_ALREADY ) ) 
				displayErrMsgWnd( buf, MB_OK | MB_ICONASTERISK, NULL );
		}
	}
	
	return;
}



//********************************************************************************
// CheckDUN
// for Win95
// If user doesn't have Dial-Up Networking installed, they will have problem
// setting up an account.
//********************************************************************************
BOOL CheckDUN()
{
	static const char c_szRNA[] = "rundll.exe setupx.dll,InstallHinfSection RNA 0 rna.inf";
	
	BOOL		bInstall = FALSE;
	HKEY		hKey;
	LONG		res;
	char		szBuf[ MAX_PATH ];
	
	// Let's see if its installed
	if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
	                 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\OptionalComponents\\RNA", 
	                 NULL, 
	                 KEY_QUERY_VALUE, 
	                 &hKey ) )
		bInstall = TRUE;
	
	// the key is there, was it actually installed though...
	if( !bInstall )
	{
		DWORD cbData = sizeof( szBuf );
		res = RegQueryValueEx( hKey, "Installed", NULL, NULL, (LPBYTE)szBuf, &cbData );
		if ( szBuf[ 0 ] != '1' && szBuf[ 0 ] != 'Y' && szBuf[ 0 ] != 'y' )
			bInstall = TRUE;
	}
	
	// make sure a random file from the installation exists so that we
	//   know the user actually installed instead of just skipping over
	//   the install step
	if( !bInstall )
	{
		if( GetSystemDirectory( szBuf, sizeof( szBuf ) ) )
		{
			// create a name of one of the files
			strcat( szBuf, "\\pppmac.vxd" );
	
			// let's see if that file exists
			struct _stat stat_struct;
			if( _stat( szBuf, &stat_struct ) != 0 )
			bInstall = TRUE;
	
		}
	}
	
	// if no Dial-Up Networking installed install it now
	if ( bInstall )
	{
		// let the user not install it or exit
		//
		// Err: Dial-Up Networking has not been installed on this machine;
		//    this product will not work until Dial-Up Networking is installed.  
		//    Would you like to install Dial-Up Networking now?
	
		char*	buf = (char*)malloc(sizeof( char ) * 255 );
		if ( !buf )
			// Err: Out of Memory
			return FALSE;
	
		if ( getMsgString( buf, IDS_NO_DUN ) )
		{
			if ( IDOK != displayErrMsgWnd( buf, MB_OKCANCEL | MB_ICONASTERISK, NULL ) )
			{
				free( buf );
				return FALSE;
			}
		}
		free( buf );     
	
	
		// install Dial-Up Networking
		PROCESS_INFORMATION pi;
		STARTUPINFO         sti;
		UINT                err = ERROR_SUCCESS;
	
		memset( &sti, 0, sizeof( sti ) );
		sti.cb = sizeof( STARTUPINFO );
	
		// Run the setup application
		if ( CreateProcess( NULL, (LPSTR)c_szRNA, 
							NULL, NULL, FALSE, 0, NULL, NULL, &sti, &pi ) )
		{
			CloseHandle( pi.hThread );
	
			// Wait for the modem wizard process to complete
			WaitForSingleObject( pi.hProcess, INFINITE );
			CloseHandle( pi.hProcess );
		}
	
	}
	
	RegCloseKey( hKey );
	return TRUE;
}


//********************************************************************************
// check if a user is an Administrator on NT40
//********************************************************************************
static BOOL isWinNTAdmin()
{
	HANDLE			hAccessToken;
    UCHAR			infoBuffer[ 1024 ];
    PTOKEN_GROUPS	ptgGroups = (PTOKEN_GROUPS)infoBuffer;
    DWORD			dwInfoBufferSize;
    PSID			psidAdministrators;
    SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
    UINT			x;
    BOOL			bSuccess;

	if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_READ, &hAccessToken ) )
		return FALSE;
 
	bSuccess = GetTokenInformation( hAccessToken, TokenGroups, infoBuffer,
									1024, &dwInfoBufferSize );

	CloseHandle( hAccessToken );

	if( !bSuccess )
		return FALSE;
 
	if( !AllocateAndInitializeSid( &siaNtAuthority, 2,
		SECURITY_BUILTIN_DOMAIN_RID,
		DOMAIN_ALIAS_RID_ADMINS,
		0, 0, 0, 0, 0, 0,
		&psidAdministrators ) )
		return FALSE;
 
   // assume that we don't find the admin SID.
	bSuccess = FALSE;
 
	for ( x = 0; x < ptgGroups->GroupCount; x++ )
	{
		if ( EqualSid( psidAdministrators, ptgGroups->Groups[ x ].Sid ) )
		{
			bSuccess = TRUE;
			break;
         }
	}

	FreeSid( &psidAdministrators );
	return bSuccess;
}



//********************************************************************************
// CheckDUN_NT
// for WinNT40
// If user doesn't have Dial-Up Networking installed, they will have problem
// setting up an account.
//********************************************************************************
BOOL CheckDUN_NT()
{
	BOOL		bInstall = FALSE;
	BOOL		bAdmin = FALSE;
	HKEY		hKey;
	LONG		res;
	char		szBuf[ MAX_PATH ];
	char*		buf = NULL;
	
	
	// Let's see if its installed
	// "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\RemoteAccess"
	if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE,
						"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess",
						NULL, 
						KEY_QUERY_VALUE,
						&hKey ) )
		bInstall = TRUE;
	
	// the key is there, was it actually installed though...
	// look for some RAS keys
	szBuf[ 0 ] = '\0';
	if ( !bInstall )
	{
		DWORD cbData = sizeof( szBuf );
		res = RegQueryValueEx( hKey, "DisplayName", NULL, NULL, (LPBYTE)szBuf, &cbData );
		if( strlen( szBuf ) == 0 )
			bInstall = TRUE;
	
		RegCloseKey( hKey );
	
	// how about autodial manager....
	// "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\RasAuto"
		if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE,
						"SYSTEM\\CurrentControlSet\\Services\\RasAuto",
						NULL,
						KEY_QUERY_VALUE,
						&hKey ) )
			bInstall = TRUE;
	
		RegCloseKey( hKey );
	}
	
	
	// if no Dial-Up Networking installed, warn the users depending on
	// their premissions and return FALSE
	if ( bInstall )
	{
		bAdmin = isWinNTAdmin();
	
		if ( bAdmin )
		{
			// Err: Dial-Up Networking has not been installed on this machine;
			//    this product will not work until Dial-Up Networking is installed.  
			//    Pleas install Dial-Up Networking before running Accout Setup.
			char buf[ 255 ];
	
			if ( getMsgString( buf, IDS_NO_DUN_NT ) )
				displayErrMsgWnd( buf, MB_OK | MB_ICONASTERISK, NULL );
	
#if 0
			// install Dial-Up Networking
			PROCESS_INFORMATION pi;
			STARTUPINFO         sti;
			UINT                err = ERROR_SUCCESS;
			char				RASphone[ MAX_PATH ];
			
			GetSystemDirectory( RASphone, MAX_PATH );
			strcat( RASphone, "\\rasphone.exe" );

			memset( &sti, 0, sizeof( sti ) );
			sti.cb = sizeof( STARTUPINFO );
			
			// Run the setup application
			if ( CreateProcess( (LPCTSTR)&RASphone, NULL, 
				NULL, NULL, FALSE, 0, NULL, NULL, &sti, &pi ) )
			{
				CloseHandle( pi.hThread );

				// Wait for the Dial-Up Networking install process to complete
				WaitForSingleObject( pi.hProcess, INFINITE );
				CloseHandle( pi.hProcess );
			}
#endif
	
		}
		else
		{
			// user need to have administrator premission to install, and ASW won't
			// work if DUN is not installed
			// Err: You do not have Administrator premission on this machine to intall
			//		Dial-Up Networking. Please make sure you have Administrator premission
			//		in order to install Dial-Up Networking first before running Account Setup.
	
			char		buf[ 255 ];
	
			if ( getMsgString( buf, IDS_NO_ADMIN_PREMISSION ) )
				displayErrMsgWnd( buf, MB_OK | MB_ICONASTERISK, NULL );
		}
		return FALSE;
	}
	return TRUE;
}

//********************************************************************************
// CreateDialerShortcut
// Creates a shell shortcut to the PIDL
//********************************************************************************
static short createDialerShortcut( char* szDesktop,     // Desktop path
	LPCSTR 			accountName,  // connectoid/phonebook entry name
	IMalloc*		pMalloc,
	char*			szPath,     // path to PE folder
	char*			strDesc,
	char*			iconPath )      // shortcut description
{
	HRESULT				hres;
	LPITEMIDLIST		pidl;

	char				desktop[ MAX_PATH ];
	DWORD				cbData;
	HKEY				hKey;
	long				res;

	szDesktop[ 0 ] = '\0';

	// gets Desktop folder path from registry for both win95 & winNT40
	// "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
    if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_CURRENT_USER,
    		REGSTR_PATH_SPECIAL_FOLDERS,
    		NULL,
    		KEY_QUERY_VALUE,
    		&hKey ) )
		return -3;		// huh?

    cbData = MAX_PATH;
    res = RegQueryValueEx( hKey, "Desktop", NULL, NULL, (LPBYTE)desktop, &cbData );

	RegCloseKey( hKey );

	strcpy( szDesktop, desktop );

	// win95 only
	if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
	{
		// Get a PIDL that points to the dial-up connection
		hres = getDialUpConnectionPIDL( accountName, &pidl );

		if ( FAILED( hres ) )
		{
			// Err: Unable to create shortcut to RNA phone book entry
			char*	buf = (char*)malloc(sizeof( char ) * 255 );
			if ( !buf )
				// Err: Out of Memory
				return -6;		// huh?

			if ( getMsgString( buf, IDS_NO_RNA ) )
			{
				if ( IDOK != displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, NULL ) )
				{
					free( buf );
					return FALSE;
				}
			}

			free( buf );     
			return -1;
		}

		// If the dial-up networking folder is open when we create the RNA
		// entry, then we will have failed to get a PIDL above. The dial-up
		// networking folder itself won't display the icon until it is closed
		// and re-opened. There's nothing we can do but return
		if ( !pidl )
		{
			pMalloc->Release();
			return -2;		// huh?
		}

		// Create a shortcut on the desktop
		char strPath[ MAX_PATH ];
		strcpy( strPath, szDesktop );
		strcat( strPath, "\\" );
		strcat( strPath, strDesc );
		strcat( strPath, ".Lnk" );
		createLink( pidl, strPath, strDesc, iconPath );
		
		// And one in our PE folder
		strcpy( strPath, szPath );
		strcat( strPath, "\\" );
		strcat( strPath, strDesc );
		strcat( strPath, ".Lnk" );
		createLink( pidl, strPath, strDesc, iconPath );		
	}
	else if ( gPlatformOS == VER_PLATFORM_WIN32_NT )
	{ 
		// WinNT40 here
		// make sure the phonebook entry we created still exists

		char* sysDir = (char*)malloc(sizeof( char ) * MAX_PATH );
		if ( !sysDir )
			return -5;		// huh?
		
		char pbPath[ MAX_PATH ];
		GetSystemDirectory( sysDir, MAX_PATH );
		strcpy( pbPath, sysDir );
		strcat( pbPath, "\\ras\\rasphone.pbk" );
		strcat( pbPath, "\0" );
		free( sysDir );
		
		RASENTRYNAME rasEntryName[ MAX_PATH ];
		if ( !rasEntryName )
			return -7;		// huh?

		rasEntryName[ 0 ].dwSize = stRASENTRYNAME;
		DWORD size = stRASENTRYNAME * MAX_PATH;
		DWORD entries;
		
		if ( 0 != RasEnumEntries( NULL, pbPath, rasEntryName, &size, &entries ) )
			return -4;

		BOOL		exists = FALSE;
		DWORD		i = 0;
		
		while ( ( i < entries) && ( !exists ) )
		{
			if ( strcmp( rasEntryName[ i ].szEntryName, accountName ) == 0 )
				exists = TRUE;
			i++;
		}

		// create a shortcut file on desktop
		if ( exists )
		{
			HANDLE hfile = NULL;
			
			// create phonebook entry shortcut file on desktop, overwrites if exists
			SECURITY_ATTRIBUTES		secAttrib;
			memset( &secAttrib, 0, sizeof( SECURITY_ATTRIBUTES ) );
			secAttrib.nLength = sizeof( SECURITY_ATTRIBUTES );
			secAttrib.lpSecurityDescriptor = NULL;
			secAttrib.bInheritHandle = FALSE;

			// construct phonebook entry shortcut file name
			char file[ MAX_PATH ];
			strcpy( file, szDesktop );
			strcat( file, "\\" );
			strcat( file, accountName );
			strcat( file, ".rnk" );
			
			hfile = CreateFile( file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
				               &secAttrib, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

			if ( hfile == INVALID_HANDLE_VALUE )
				return -9;	// huh? 

			CloseHandle( hfile );
			hfile = NULL;

			// writes shortcut file data in the following format:
			//    [Dial-Up Shortcut]
			//    Entry=stuff
			//    Phonebook=C:\WINNT40\System32\RAS\rasphone.pbk (default system phonebook)

			WritePrivateProfileString( "Dial-Up Shortcut", "Entry", accountName, file );
			WritePrivateProfileString( "Dial-Up Shortcut", "Phonebook", pbPath, file );

			// create the same shortcut file in our PE folder
			strcpy( file, szPath );
			strcat( file, "\\" );
			strcat( file, accountName );
			strcat( file, ".rnk" );

			hfile = CreateFile( file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
				               &secAttrib, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

			if ( hfile == INVALID_HANDLE_VALUE ) 
				return -10;

			CloseHandle( hfile );
			WritePrivateProfileString( "Dial-Up Shortcut", "Entry", accountName, file );
			WritePrivateProfileString( "Dial-Up Shortcut", "Phonebook", pbPath, file );

		}
		else
			return -8;	// huh?
	}

	return 0;
}


//********************************************************************************
// createProgramItems
// adds 2 icons:
// Dialer - to Dial-Up Networking folder, Desktop & our PE folder
// Navigator - to our PE folder
//********************************************************************************
static short createProgramItems( LPCSTR accountName, LPCSTR iniPath, char* iconFile )
{
	char         szPath[ MAX_PATH ];
	LPITEMIDLIST pidl;
	char         szBuf[ MAX_PATH ];
	
	IMalloc*     pMalloc;

	SHGetMalloc( &pMalloc );
	
	// gets the path to "Programs" folder
	if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
	{
	
		SHGetSpecialFolderLocation( NULL, CSIDL_PROGRAMS, &pidl );
		SHGetPathFromIDList( pidl, szBuf );
		pMalloc->Free( pidl );
	}
	else if ( gPlatformOS == VER_PLATFORM_WIN32_NT )
	{
		// NT4.0: get the "Programs" folder for "All Users"
		HKEY	hKey;
		DWORD	bufsize = sizeof( szBuf );
	
		if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
						 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 
						 NULL, 
						 KEY_QUERY_VALUE,  
						 &hKey ) )
		{
			RegQueryValueEx( hKey, "PathName", NULL, NULL, (LPBYTE)szBuf, &bufsize );
			strcat( szBuf, "\\Profiles\\All Users\\Start Menu\\Programs" );
		}
		else
			return -1;
	}
	
	// gets Netscape PE folder here
	char		buf[ 256 ];
	char*		csFolderName;
	csFolderName = (char*)malloc(sizeof( char ) * 256 );
	
	if ( !csFolderName )
		return -4;		// huh?
	
	strcpy( csFolderName, "Netscape Personal Edition" );
	
	// check for custom folder name
	if( ::GetPrivateProfileString(
				"General",
				"InstallFolder",
				(const char*)csFolderName,
				buf, sizeof( buf ),
				iniPath ) )
	strcpy( csFolderName, buf );	
	strcpy( szPath, szBuf );
	strcat( szPath, "\\" );
	strcat( szPath, csFolderName );
	
	free( csFolderName );
	
	// First do Dialer Icon
	// Create a dialer icon shortcut description
	char strDesc[ MAX_PATH ];
	
#ifdef UPGRADE
	if ( ???entryInfo.bUpgrading )
	{
		char*	csTmp = "Dialer";
		strcpy( strDesc, accountName );
		strcat( strDesc, " " );
		strcat( strDesc, csTmp );
	}
	else
	{
	strcpy( strDesc, "Dial" );
	strcat( strDesc, " " );
	strcat( strDesc, accountName );
	}
#else
	strcpy( strDesc, "Dial" );
	strDesc[ strlen( strDesc ) + 1 ] = '\0';
	strDesc[ strlen( strDesc ) ] = (char)32;
	strcat( strDesc, accountName );
#endif
	
	char		szDesktop[ 512 ];
	
	// create dialer shortcut icon on desktop and in PE folder
	int rtn = createDialerShortcut( szDesktop, accountName, pMalloc, szPath, strDesc, iconFile );
	
	if ( rtn != 0 )
		return rtn;
	
	
#ifdef FOLDER_IN_START_MENU
	// Cleanup
	pMalloc->Free( pidl );
	pMalloc->Release();
#endif
	
	::Sleep( 250 );
	
	return 0;
}


//********************************************************************************
// DesktopConfig
// Sets up user's desktop (creates icons and short cuts)
//********************************************************************************
void DesktopConfig( char* accountName, char* iconFileName,
	char* acctsetIniPath)
{
	static char lastIconFile[ _MAX_PATH ] = { '\0' };

	if ( iconFileName != NULL )
	{
		// JS may pass us different file for icon file
		if ( ( iconFileName ) && ( strcmp( iconFileName, lastIconFile ) != 0 ) )
		{
			if ( strcmp( lastIconFile, "" ) != 0 )
			{
				if ( fileExists( lastIconFile ) ) 	// a temp icon file may already existed
					_unlink( lastIconFile );
			}
			
			// check if icon file exists
			if ( fileExists( iconFileName ) )
				strcpy( lastIconFile, iconFileName );
			else
				lastIconFile[0] = '\0';
		}
	}
	
	// remove the RegiServer RAS
	char	regiRAS[ 50 ];
	getMsgString( (char*)regiRAS, IDS_REGGIE_PROGITEM_NAME );
	(*m_lpfnRasDeleteEntry)( NULL, (LPSTR)regiRAS );

	// creates prgram icons in folders and desktop
	int ret = createProgramItems( accountName, acctsetIniPath, lastIconFile );
}


//********************************************************************************
// QuitNavigator
// quits the navigator
//********************************************************************************
void QuitNavigator()
{
	// Bug#112622 - don't broadcast this, java catches it and dies.
	// Instead, find the hidden window and tell _it_ what to do.

	// The strings in this call are hard-coded because the constants needed
	// do not exist and multiple places are doing similar things.  The 
	// values themselves come from winfe's Netscape.cpp.
	HWND hWnd = FindWindowEx( NULL, NULL, "aHiddenFrameClass", "Netscape's Hidden Frame" );
	PostMessage(/*HWND_BROADCAST*/ hWnd, WM_COMMAND, ID_APP_SUPER_EXIT, 0L );
}

//********************************************************************************
// getCurrentProfileDirectory
// gets the current Navigator user profile directory 
//********************************************************************************
extern char* GetCurrentProfileDirectory()
{
	char*		profilePath = NULL;
	int			bufSize = 256;
	char		buf[ 256 ];
	buf[ 0 ] = '\0';

	if ( PREF_OK == PREF_GetCharPref( "profile.directory", buf, &bufSize ) )
	{
		// make sure we append the last '\' in the profile dir path
		strcat( buf, "\\" );	
		trace( "profile.cpp : GetCurrentProfileDirectory : Got the Current User profile = %s", buf );
	}
	else
		trace( "profile.cpp : GetCurrentProfileDirectory : Error in obtaining Current User profile" );

	profilePath = strdup( buf );

	return profilePath;
}
	

//********************************************************************************
// GetCurrentProfileName
// gets the current Navigator user profile name 
//********************************************************************************
extern char* GetCurrentProfileName()
{
	char*			profileName = NULL;
	int				bufSize = 256;
	char			buf[ 256 ];
	buf[ 0 ] = '\0';

	if ( PREF_OK == PREF_GetCharPref( "profile.name", buf, &bufSize ) )
		trace( "profile.cpp : GetCurrentProfileDirectory : Got the Current profile name = %s", buf );
	else
		trace( "profile.cpp : GetCurrentProfileName : Error in obtaining Current User profile" );

	profileName = strdup( buf );

	return profileName;
}


//********************************************************************************
// SetCurrentProfileName
// changes the current Navigator user profile name to a different name
//********************************************************************************
void SetCurrentProfileName( char* profileName )
{
	assert( profileName );

	if ( NULL == profileName )	// abort if string allocation fails
		return;
	
	if ( PREF_ERROR == PREF_SetDefaultCharPref( "profile.name", profileName ) )
		trace( "profile.cpp : SetCurrentProfileName : Error in setting Current User profile name." );
	else
	{
		trace( "profile.cpp : SetCurrentProfileName : Current User profile is set to = %s", profileName );

		if ( PREF_ERROR == PREF_SetDefaultBoolPref( "profile.temporary", FALSE ) ) 
			trace( "profile.cpp : SetCurrentProfileName : Error in setting Temporary flag to false." );
		else
			trace( "profile.cpp : SetCurrentProfileName : Made the profile to be permanent." );
	}

}

// The format of the entry is
//  LocationX=Y,"name","outside-line-local","outside-line-long-D","area-code",1,0,0,1,"",tone=0,"call-waiting-string"
typedef struct tapiLineStruct
{
	int		nIndex;
	char		csLocationName[ 60 ];
	char		csOutsideLocal[ 20 ];
	char		csOutsideLongDistance[ 20 ];
	char		csAreaCode[ 20 ];
	int		nCountryCode;
	int		nCreditCard;
	int		nDunnoB;
	int		nDunnoC;
	char		csDialAsLongDistance[ 10 ];
	int		nPulseDialing;
	char		csCallWaiting[ 20 ];
} TAPILINE;

//********************************************************************************
// readNextInt
// Pull an int off the front of the string and return the new string ptr
//********************************************************************************
char* readNextInt( char* pInString, int* pInt )
{
	char*		pStr;
	char		buf[ 32 ];
	
	if ( !pInString )
	{
		*pInt = 0;
		return NULL;
	}
	
	// copy the string over.  This strchr should be smarter    
	pStr = strchr( pInString, ',' );
	if ( !pStr )
	{
		*pInt = 0;
		return NULL;
	}
	
	// convert the string            
	strncpy( buf, pInString, pStr - pInString );
	buf[ pStr - pInString ] = '\0';
	*pInt = atoi( buf );
	
	// return the part after the int
	return ( pStr + 1 );	
}

//********************************************************************************
// readNextString
// Pull a string from the front of the incoming string and return
//   the first character after the string
//********************************************************************************
char* readNextString( char* pInString, char* csString )
{
	csString[ 0 ] = '\0';
	int		i = 0, x = 0;
	BOOL	copy = FALSE;
	char	newpInString[ MAX_PATH ];
	
	if ( !pInString )
	{
		csString = "";
		return NULL;
	}
	
	// skip over first quote
	if ( pInString[ i ] == '\"' )
		i++;
	
	// copy over stuff by hand line a moron
	while ( pInString[ i ] != '\"' )
	{
		//strcat(csString, (char *)pInString[i]);
		csString[ x ] = pInString[ i ];
		i++;
		x++;
		copy = TRUE;
	}
	
	if ( copy )
		csString[ x ] = '\0';
	
	
	// skip over the closing quote
	if( pInString[ i ] == '\"' )
		i++;
	
	// skip over the comma at the end
	if( pInString[ i ] == ',' )
		i++;
	
	newpInString[ 0 ]='\0';
	x = 0;

	for ( unsigned short j = i; j < strlen( pInString ); j++ )
	{
		//strcat(newpInString, (char *)pInString[j]);
		newpInString[ x ] = pInString[ j ];
		x++;
	}
	
	newpInString[ x ]='\0';
	strcpy( pInString, newpInString );
	
	return pInString;
}

//********************************************************************************
// readTapiLine
// Read a line out of the telephon.ini file and fill the stuff in
//********************************************************************************
void readTapiLine( char* lpszFile, int nLineNumber, TAPILINE* line ) 
{
	char		buf[ 256 ];
	char		pLocation[ 128 ];
	sprintf( pLocation, "Location%d", nLineNumber );
	::GetPrivateProfileString( "Locations", pLocation, "", buf, sizeof( buf ), lpszFile );
	
	char*		pString = buf;
	pString = readNextInt( pString, &line->nIndex );
	pString = readNextString( pString, (char *)&line->csLocationName );
	pString = readNextString( pString, (char *)&line->csOutsideLocal );
	pString = readNextString( pString, (char *)&line->csOutsideLongDistance );
	pString = readNextString( pString, (char *)&line->csAreaCode );
	pString = readNextInt( pString, &line->nCountryCode );
	pString = readNextInt( pString, &line->nCreditCard );
	pString = readNextInt( pString, &line->nDunnoB );
	pString = readNextInt( pString, &line->nDunnoC );
	pString = readNextString( pString, (char *)&line->csDialAsLongDistance );
	pString = readNextInt( pString, &line->nPulseDialing );
	pString = readNextString( pString, (char *)&line->csCallWaiting );	
}



//********************************************************************************
// writeTapiLine
// Given a tapiLine structure write it out to telephon.ini
//********************************************************************************
void writeTapiLine( char* lpszFile, int nLineNumber, TAPILINE* line ) 
{
    char		buffer[ 256 ];
    sprintf( buffer, "%d,\"%s\",\"%s\",\"%s\",\"%s\",%d,%d,%d,%d,\"%s\",%d,\"%s\"",
        line->nIndex,
        (const char*) line->csLocationName,
        (const char*) line->csOutsideLocal,
        (const char*) line->csOutsideLongDistance,
        (const char*) line->csAreaCode,
        line->nCountryCode,
        line->nCreditCard,
        line->nDunnoB,
        line->nDunnoC,
        (const char*) line->csDialAsLongDistance,
        line->nPulseDialing,
        (const char*) line->csCallWaiting );

    char			pLocation[ 32 ];
    sprintf( pLocation, "Location%d", nLineNumber );
    ::WritePrivateProfileString( "Locations", pLocation, buffer, lpszFile );
}

//********************************************************************************
// SetLocationInfo
// sets the location info for win95 dialers
// The format of the entry is
//  LocationX=Y,"name","outside-line-local","outside-line-long-D","area-code",1,0,0,1,"",tone=0,"call-waiting-string"
//********************************************************************************
BOOL SetLocationInfo( ACCOUNTPARAMS account, LOCATIONPARAMS location )
{
	// first read information from telephon.ini
	char		buf[ 256 ];
	
	// get windows directory
	char		lpszDir[ MAX_PATH ];
	if ( GetWindowsDirectory( lpszDir, MAX_PATH ) == 0 )
		return FALSE; // huh?
	
	strcat( lpszDir, "\\telephon.ini" );
	
	// now we build new line information based on the old one and some info
	// see if there were any locations to begin with
	::GetPrivateProfileString( "Locations", "CurrentLocation", "", buf, sizeof( buf ), lpszDir );
	if( buf[ 0 ] == '\0' )
	{
		// build the string
		TAPILINE		line;
		line.nIndex = 0;
		strcpy( line.csLocationName, "Default Location" );
		strcpy( line.csOutsideLocal, location.OutsideLineAccess );
		strcpy( line.csOutsideLongDistance, location.OutsideLineAccess );
		strcpy( line.csAreaCode, location.UserAreaCode );
		line.nCountryCode = location.UserCountryCode;
		line.nCreditCard = 0;
		line.nDunnoB = 0;
		line.nDunnoC = 1;
	
		if ( location.DialAsLongDistance == TRUE )
			strcpy( line.csDialAsLongDistance, "528" );
		else
			strcpy( line.csDialAsLongDistance, "" );

		line.nPulseDialing = ( location.DialType == 0 ? 1 : 0 );
		if( location.DisableCallWaiting )
			strcpy( line.csCallWaiting, location.DisableCallWaitingCode );
		else
			strcpy( line.csCallWaiting, "" );
	
		writeTapiLine( lpszDir, 0, &line );
	
		// need to create a whole location section
		::WritePrivateProfileString( "Locations", "CurrentLocation", "0,0", lpszDir );
		::WritePrivateProfileString( "Locations", "Locations", "1,1", lpszDir );
		::WritePrivateProfileString( "Locations", "Inited", "1", lpszDir );
	}
	else
	{
	
		int nId, nCount;
		sscanf( buf, "%d,%d", &nId, &nCount );
	
		// read the line
		TAPILINE		line;                                
		readTapiLine( lpszDir, nId, &line );
	
		strcpy( line.csOutsideLocal, location.OutsideLineAccess );
		strcpy( line.csOutsideLongDistance, location.OutsideLineAccess );
		if ( location.DisableCallWaiting )
			strcpy( line.csCallWaiting, location.DisableCallWaitingCode );
		else
			strcpy( line.csCallWaiting, "" );
	
		line.nPulseDialing = ( location.DialType == 0 ? 1 : 0 );
	
		if ( strcmp( location.UserAreaCode, "" ) != 0 )
			strcpy( line.csAreaCode, location.UserAreaCode );
	
		if ( location.DialAsLongDistance == TRUE )
			strcpy( line.csDialAsLongDistance, "528" );
		else
			strcpy( line.csDialAsLongDistance, "" );
	
		// write the line back out
		writeTapiLine( lpszDir, nId, &line );
	}
	
	return TRUE;
}
	

//********************************************************************************
// SetLocationInfoNT
// sets the location info for winNT dialers
//********************************************************************************
BOOL SetLocationInfoNT( ACCOUNTPARAMS account, LOCATIONPARAMS location )
{
	LINEINITIALIZEEXPARAMS  m_LineInitExParams;
	HLINEAPP                m_LineApp;
	DWORD                   dwNumDevs;
	LINETRANSLATECAPS       m_LineTranslateCaps;
	
	DWORD					dwApiVersion = 0x00020000;
	
	
	// Initialize TAPI. in order to get current location ID
	m_LineInitExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
	m_LineInitExParams.dwTotalSize = sizeof( LINEINITIALIZEEXPARAMS );
	m_LineInitExParams.dwNeededSize = sizeof( LINEINITIALIZEEXPARAMS );
	
	
	if ( lineInitialize( &m_LineApp, gDLL, lineCallbackFuncNT, NULL, &dwNumDevs) != 0 )
	{
		char		buf[ 255 ];
		if ( getMsgString( buf, IDS_NO_TAPI ) )
			displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, NULL );
		return FALSE;
	}
	
	m_LineTranslateCaps.dwTotalSize = sizeof( LINETRANSLATECAPS );
	m_LineTranslateCaps.dwNeededSize = sizeof( LINETRANSLATECAPS );
	if ( lineGetTranslateCaps( m_LineApp, dwApiVersion, &m_LineTranslateCaps ) != 0 )
		return FALSE;
	
	
	//m_LineTranslateCaps.dwCurrentLocationID
	
	// gets the location info from registry
	HKEY					hKey;
	char*	keyPath = (char*)malloc( sizeof( char ) * 512 );
	
	assert( keyPath );
	if ( !keyPath )
		return FALSE;
	strcpy( keyPath, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\locations" );
	
	// finds the user profile location in registry
	if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, keyPath, NULL, KEY_ALL_ACCESS, &hKey ) )
		return FALSE;
	
	DWORD					subKeys;
	DWORD					maxSubKeyLen;
	DWORD					maxClassLen;
	DWORD					values;
	DWORD					maxValueNameLen;
	DWORD					maxValueLen;
	DWORD					securityDescriptor;
	FILETIME				lastWriteTime;
	
	// get some information about this reg key 
	if ( ERROR_SUCCESS != RegQueryInfoKey( hKey, NULL, NULL, NULL, &subKeys, &maxSubKeyLen, &maxClassLen, &values, &maxValueNameLen, &maxValueLen, &securityDescriptor, &lastWriteTime ) )
		return FALSE;
	
	
	// now loop through the location keys to find the one that matches current location ID
	if ( subKeys > 0 )
	{
	
		DWORD		subkeyNameSize = maxSubKeyLen + 1;
		char		subkeyName[ 20 ];
	
		for ( DWORD index = 0; index < subKeys; index++ )
		{
			// gets a location key name
			if ( ERROR_SUCCESS != RegEnumKey( hKey, index, subkeyName, subkeyNameSize ) ) 
				return FALSE;
	
			// try open location key
			char		newSubkeyPath[ 260 ];
			HKEY		hkeyNewSubkey;
	
			strcpy( (char*)newSubkeyPath, keyPath );
			strcat( (char*)newSubkeyPath, "\\" ); 
			strcat( (char*)newSubkeyPath, subkeyName );
	
			if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, (char*)newSubkeyPath, NULL, KEY_ALL_ACCESS, &hkeyNewSubkey ) )
				return FALSE;
	
			DWORD		valbuf;
			DWORD		type = REG_SZ;
			DWORD		bufsize = 20;
	
			// get location key's ID value
			if ( ERROR_SUCCESS != RegQueryValueEx( hkeyNewSubkey, "ID", NULL, &type, (LPBYTE)&valbuf, &bufsize ) )
				return FALSE;
	
			// if it matches the default location ID
			if ( valbuf == m_LineTranslateCaps.dwCurrentLocationID )
			{
				// we got the location we want, now change the pulse/tone flag
				DWORD 	flagsVal;
				if ( location.DialType == 0 )
					if ( location.DisableCallWaiting )
						flagsVal = 4;
					else
						flagsVal = 0;
				else if ( location.DisableCallWaiting )
					flagsVal = 5;
				else
					flagsVal = 1;
	
				if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "Flags", NULL, type, (LPBYTE)&flagsVal, bufsize ) )
					return FALSE;
	
				// sets the OutsideAccess
				if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "OutsideAccess", NULL, REG_SZ, (LPBYTE)&location.OutsideLineAccess, strlen(location.OutsideLineAccess ) + 1 ) )
					return FALSE;
	
				if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "LongDistanceAccess", NULL, REG_SZ, (LPBYTE)&location.OutsideLineAccess, strlen(location.OutsideLineAccess ) + 1 ) )
					return FALSE;
	
				// sets call waiting
				char*	callwaiting;
	
				if( location.DisableCallWaiting )
					callwaiting = location.DisableCallWaitingCode;
				else
					callwaiting ="";
	
				if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "DisableCallWaiting", NULL, REG_SZ, (LPBYTE)callwaiting, strlen( callwaiting ) + 1 ) )
					return FALSE;
	
	
				// sets user's area code
				if( strcmp( location.UserAreaCode, "" ) != 0 )
				{
					if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "AreaCode", NULL, REG_SZ, (LPBYTE)location.UserAreaCode, strlen( location.UserAreaCode ) + 1 ) )
						return FALSE;
				}
				else
				{
					// check if we're international, and force a default area code, so that we don't get an error creating a dialer
					if ( account.IntlMode )
					{
						char*	code = "415";
						if ( ERROR_SUCCESS != RegSetValueEx( hkeyNewSubkey, "AreaCode", NULL, REG_SZ, (LPBYTE)code, strlen( code ) + 1 ) )
							return FALSE;
					}
				}
	
			RegCloseKey( hkeyNewSubkey );
			break;
		}
	
		RegCloseKey( hkeyNewSubkey );
		}
	}
	
	RegCloseKey( hKey );
	
	return TRUE;
}

//********************************************************************************
// native method:
// CheckEnvironment
// checks for DUN, RAS function loading correctly
//********************************************************************************
BOOL CheckEnvironment()
{
	// try loading RAS routines in RAS dlls
	// if fails return FALSE
	switch ( gPlatformOS )
	{
		case VER_PLATFORM_WIN32_NT:   // NT
			//check if it's WinNT40 first
			if ( !LoadRasFunctionsNT( "RASAPI32.DLL" ) )
			{
		
				// Err: Unable to dynamically load extended RAS functions!                                
				char*	buf = (char*)malloc( sizeof( char ) * 255 );
				if ( buf )
				{
					if ( getMsgString( buf, IDS_NO_RAS_FUNCTIONS ) )
						displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, NULL );
					free( buf );
				}
		
				return FALSE;
			}
			break;
	
		case VER_PLATFORM_WIN32_WINDOWS:    // defaults to win95	
			if ( !LoadRasFunctions( "RASAPI32.DLL" ) && !LoadRasFunctions( "RNAPH.DLL" ) )
			{
				// Err: Unable to dynamically load extended RAS functions!
				char* buf = (char*)malloc( sizeof( char ) * 255 );
				if ( buf )
				{
					if ( getMsgString( buf, IDS_NO_RAS_FUNCTIONS ) )
						displayErrMsgWnd( buf, MB_OK | MB_ICONEXCLAMATION, NULL);
					free( buf );
				}
	
				return FALSE;
			}
			break;
	}
	
	// Check to make sure Dial-Up Networking is installed.
	//   It may be uninstalled by user.
	// return FALSE if Dialup Networking is not installed
	switch ( gPlatformOS )
	{
		case VER_PLATFORM_WIN32_NT:
			if ( FALSE == CheckDUN_NT() )
			{
				char buf[ 255 ];
				if ( getMsgString( (char*)buf, IDS_NO_DUN_INSTALLED ) )
					displayErrMsgWnd( (char*)buf, MB_OK | MB_ICONEXCLAMATION, NULL );
				return FALSE;
			}
			break;
		default:
			if ( FALSE == CheckDUN() )
			{
				char buf[ 255 ];
				if ( getMsgString( (char*)buf, IDS_NO_DUN_INSTALLED ) )
					displayErrMsgWnd( (char*)buf, MB_OK | MB_ICONEXCLAMATION, NULL );
				return FALSE;
			}
			break;
	}
	
	// for win95 only:
	// Check to see if DNS is already configured for a LAN connection.
	// If so warn the user that this may cause conflicts, and continue.
	if ( gPlatformOS == VER_PLATFORM_WIN32_WINDOWS )
		CheckDNS();
	
	return TRUE;
}


