ARSETUP.C

/*************************************************************************\ 
*
* APPC Remote Installer
*
* This program brings up a dialog box that prompts for TP configuration
* information. The information is then placed in the registry under
* Windows NT, and in the WIN.INI file under Windows. The WIN32 compiler
* flag specifies the NT version, while the WINDOWS flag specifies the
* Windows version.
*
* 6/93 Initial coding ARK
* 6/94 Modified to work with APPC Remote T-ALEXWE
*
\*************************************************************************/

#define STRICT
#include <windows.h>
#ifdef WIN32
#include <winsvc.h>
#endif

#ifdef WIN32
#include <windowsx.h>
#else
#include <windowsx.h16>
#endif

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "arsetup.h"

HANDLE hInst; // This program's instance
HWND hDialog; // Global handle to main dialog
HWND hTimeout; // A handle to the "timeout" window
HWND hList; // Handle to the "users" list box

WNDPROC lPrevWndProcInt = NULL, // Holds original integer edit box window proc
lPrevWndProcAppc = NULL, // Holds original APPC edit box window proc
lPrevWndProcInfinite = NULL, // Original window proc for "infinite"
// radio button
lPrevWndProcConvSec = NULL, // Same for "conversation security" box
lPrevWndProcService = NULL; // Same for "service" box

FARPROC lpfnInt = NULL, // Pointer to the ValidateFieldInt
// procedure; becomes non-NULL upon
// initialization in ValidateField().
lpfnAppc = NULL; // Pointer to ValidateFieldAppc procedure

/*************************************************************************\
*
* FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
* PURPOSE: Creates the dialog box.
*
* COMMENTS:
*
\*************************************************************************/

int PASCAL WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DWORD retCode;
FARPROC lpfn;

hInst = hInstance;

lpfn = (FARPROC) MakeProcInstance( (FARPROC)MainDlgProc, hInst);
retCode = DialogBoxParam ((HANDLE)hInst,
(LPCSTR)"MainDlg",
NULL,
(DLGPROC) lpfn,
0);
FreeProcInstance(lpfn);

return (retCode);
}
/************************************************************************/
/*
* MainDlgProc: Handle messages to the main dialog.
* Note: Under Windows NT, installation consists of creating a service
* and creating some keys in the registry, while under Windows
* we instead add slightly different information to the WIN.INI file.
* So two different dialogs are necessary for the different operating
* systems. Appropriate sections of the procedure below are
* #ifdef'ed to handle parts of the installation that are unique
* to a particular operating system.
*/
/************************************************************************/
BOOL CALLBACK MainDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hInfinite;
char lpPathName[MAXBINPATHLEN], *lpTPName, lpParameters[MAXBINPATHLEN];
char *lpTimeout, *lpLocalLUName, *lpCommand, *divider;
char lpFileTitle[MAXBINPATHLEN], lpSysDir[MAXBINPATHLEN];
INT cmd;
char lpszName[MAX_COMPUTERNAME_LENGTH + 1];
int lpcchName = MAX_COMPUTERNAME_LENGTH + 1;
HANDLE hTemp;
WIN32_FIND_DATA fdFile;

#ifdef WIN32
UNREFERENCED_PARAMETER(divider);
UNREFERENCED_PARAMETER(lpParameters);
#endif

switch (wMsg)
{
case WM_INITDIALOG:
hDialog = hDlg;

// Set maximum TP name to 128 characters
SendMessage(GetDlgItem(hDlg, IDE_TPNAME),
EM_LIMITTEXT, MAXTPLEN, 0);

// set the default TPName
SetDlgItemText(hDlg, IDE_TPNAME, "AREMOTE");

// Set maximum pathname to 512 characters
SendMessage(GetDlgItem(hDlg, IDE_CMDPATH),
EM_LIMITTEXT, MAXBINPATHLEN, 0);

// Have it use cmd.exe by default for the command
GetSystemDirectory(lpSysDir, sizeof(lpSysDir));
strcat(lpSysDir, "\\cmd.exe");
SetDlgItemText(hDlg, IDE_CMDPATH, lpSysDir);

// Set maximum pathname to 512 characters
SendMessage(GetDlgItem(hDlg, IDE_REMOTEPATH),
EM_LIMITTEXT, MAXBINPATHLEN, 0);

// Have it use aremote.exe by default for remote
GetCurrentDirectory(sizeof(lpSysDir), lpSysDir);
strcat(lpSysDir, "\\aremote.exe");
SetDlgItemText(hDlg, IDE_REMOTEPATH, lpSysDir);

// Allow only legal characters in "Local LU Alias" box:
ValidateField(GetDlgItem(hDlg, IDE_LOCALLU), VALIDATE_APPC);

// Set maximum length of LU name to be 8 characters
SendMessage(GetDlgItem(hDlg, IDE_LOCALLU), EM_LIMITTEXT, MAXLULEN, 0);

// set the LU name to this computers name by default
GetComputerName(lpszName, &lpcchName);
MakeValidLUName(lpszName);
SetDlgItemText(hDlg, IDE_LOCALLU, lpszName);

return TRUE;

case WM_COMMAND:
switch (cmd = GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
// User hits OK; we get relevant info & try to install.

// If TP name field is blank, error out
if (ReadString(hDlg, IDE_TPNAME, &lpTPName, MAXTPLEN) == 0)
{
DisplayError(hDlg, IDS_BADTPNAME);
free(lpTPName);
return TRUE;
}

// read path name for remote
ReadString(hDlg, IDE_REMOTEPATH, &lpCommand, MAXBINPATHLEN);

// open the file to make sure it exists
hTemp = FindFirstFile(lpCommand, &fdFile);
if (hTemp == INVALID_HANDLE_VALUE) {
DisplayError(hDlg, IDS_BADREMOTEPATH);
return TRUE;
} else CloseHandle(hTemp);

strcpy(lpPathName, lpCommand);
strcat(lpPathName, " /a ");

// read path name for command
ReadString(hDlg, IDE_CMDPATH, &lpCommand, MAXBINPATHLEN);

// open the file to make sure it exists
hTemp = FindFirstFile(lpCommand, &fdFile);
if (hTemp == INVALID_HANDLE_VALUE) {
DisplayError(hDlg, IDS_BADCMDPATH);
return TRUE;
} else CloseHandle(hTemp);

strcat(lpPathName, lpCommand);

strcpy(lpParameters, "/a ");
strcat(lpParameters, lpCommand);

// read Local LU Alias
ReadString(hDlg, IDE_LOCALLU, &lpLocalLUName, MAXLULEN);
if (strlen(lpLocalLUName) < 1) {
DisplayError(hDlg, IDS_BADLUNAME);
return TRUE;
}

lpTimeout = INFINITE_TIMEOUT;

#ifdef WIN32
// Install the new service; if successful, set subkeys
if (InstallServiceNT(hDlg, lpTPName, lpPathName) != 0)
{
free(lpPathName);
free(lpTPName);
return TRUE;
}

if (CreateKeys( lpTPName,
TRUE,
lpLocalLUName,
FALSE,
FALSE,
FALSE,
lpTimeout,
lpParameters,
lpPathName
)
!= 0)
{
free(lpPathName);
free(lpTPName);
return TRUE;
}
DisplayInfo(hDlg, IDS_SUCCESS);

#else

// Add entries to WIN.INI
if (InstallWindows(hDlg, lpTPName, lpPathName,
lpLocalLUName, lpParameters,
IsDlgButtonChecked(hDlg, IDC_CONVSEC) == 1,
IsDlgButtonChecked(hDlg, IDC_ALREADYVER) == 1,
IsDlgButtonChecked(hDlg, IDC_QUEUED) == 1,
lpTimeout
) != 0)
{
free(lpPathName);
free(lpTPName);
return TRUE;
}
DisplayInfo(hDlg, IDS_SUCCESS);

#endif //ifdef WIN32

EndDialog(hDlg, TRUE);
free(lpPathName);
free(lpTPName);
return TRUE;

case IDC_CMDPATH_BROWSE: {
char filters[256];
char lpDirPath[MAXBINPATHLEN], lpFileName[MAXBINPATHLEN];
char *lpTemp;
OPENFILENAME ofn;

GetDlgItemText(hDlg, IDE_CMDPATH, lpFileName,
sizeof(lpFileName));
GetFullPathName(lpFileName, sizeof(lpDirPath),
lpDirPath, &lpTemp);
strcpy(lpFileName, lpTemp);
lpTemp[0] = 0;

memcpy(filters,
"Executable Programs\0*.exe;*.bat;*.com\0\0\0", 256);

memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.lpstrFilter = filters;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 0;
ofn.lpstrFile = lpFileName;
ofn.nMaxFile = sizeof(lpFileName);
ofn.lpstrFileTitle = lpFileTitle;
ofn.nMaxFileTitle = sizeof(lpFileTitle);
ofn.lpstrTitle = "Path to Command";
ofn.lpstrInitialDir = lpDirPath;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY;

if (GetOpenFileName(&ofn)) {
SetDlgItemText(hDlg, IDE_CMDPATH, lpFileName);
}

return TRUE;
}

case IDC_REMOTEPATH_BROWSE: {
char filters[256];
char lpDirPath[MAXBINPATHLEN], lpFileName[MAXBINPATHLEN];
char *lpTemp;
OPENFILENAME ofn;

GetDlgItemText(hDlg, IDE_REMOTEPATH, lpFileName,
sizeof(lpFileName));
GetFullPathName(lpFileName, sizeof(lpDirPath),
lpDirPath, &lpTemp);
strcpy(lpFileName, lpTemp);
lpTemp[0] = 0;

memcpy(filters,
"APPC Remote\0aremote.exe\0Executable Programs\0*.exe;*.bat;*.com\0\0\0",
256);

memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.lpstrFilter = filters;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 0;
ofn.lpstrFile = lpFileName;
ofn.nMaxFile = sizeof(lpFileName);
ofn.lpstrFileTitle = lpFileTitle;
ofn.nMaxFileTitle = sizeof(lpFileTitle);
ofn.lpstrTitle = "Path to APPC Remote";
ofn.lpstrInitialDir = lpDirPath;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY;

if (GetOpenFileName(&ofn)) {
SetDlgItemText(hDlg, IDE_REMOTEPATH, lpFileName);
}

return TRUE;
}

case IDCANCEL:
EndDialog(hDlg, FALSE);
return TRUE;
}

break;

case WM_DESTROY:
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}


#ifdef WIN32

/*****************************************************************************/
/*
* InstallServiceNT( HWND hDlg, LPSTR lpServiceName, LPSTR lpPath )
*
* Windows NT version of installation--register a new service.
*
* Parameters
* ----------
*
* hDlg: Handle to top level dialog; used for error messages.
* lpServiceName: A string giving the name of the TP.
* lpPath: A string giving the full pathname of the TP's executable.
*
* Returns
* -------
*
* Zero upon successful completion, nonzero otherwise.
*
* Comments
* --------
* Installs new service if possible. Puts up appropriate message boxes if
* installation fails.
*/
/*****************************************************************************/
int InstallServiceNT(HWND hDlg, LPSTR lpServiceName, LPSTR lpBinaryPath)
{

SC_HANDLE hSCManager = NULL;
SC_HANDLE hService = NULL;
SC_LOCK lSCLock = NULL;

hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

if( hSCManager != NULL )
{
/*************************************************************************/
/* Lock the service database */
/*************************************************************************/
lSCLock = LockServiceDatabase( hSCManager );
if ( lSCLock != NULL )
{
/***********************************************************************/
/* Create the service */
/***********************************************************************/
hService = CreateService( hSCManager,
lpServiceName, // Service's name
lpServiceName, // Display name (new for NT)
SERVICE_ALL_ACCESS,// Access (allow all)
0x10, // Service type
0x3, // Startup behavior
0x1, // Error control
lpBinaryPath, // Full pathname of binary
NULL, // Load order group
NULL, // Tag ID
NULL, // Dependencies (none)
NULL, // Account name
NULL // Password
);
if ( hService != NULL )
{
/*********************************************************************/
/* Close our handle to the new service */
/*********************************************************************/
CloseServiceHandle( hService );
}
else
{
// Try to display the reason for the create failure
ParseCreateError(hDlg, GetLastError());

// Must release lock in case we are called again
UnlockServiceDatabase( lSCLock );

return 1;
}

/***********************************************************************/
/* Unlock the database */
/***********************************************************************/
UnlockServiceDatabase( lSCLock );
}
else
{
DisplayError(hDlg, IDS_LOCKFAILED);
return 1;
}
/*************************************************************************/
/* Free our handle to the service control manager */
/*************************************************************************/
CloseServiceHandle( hSCManager );
return 0;
}

DisplayError(hDlg, IDS_OPENSCMFAILED);
return 1;
}

/*****************************************************************************/
/*
* CreateKeys: Set the required key values for the newly created service.
* The keys are:
* Linkage:
* OtherDependencies: REG_MULTI_SZ: SnaBase
* Parameters:
* SNAServiceType: REG_DWORD: 0x5
* ConversationSecurity: REG_SZ: "yes" or "no"
* (depending on setting of radio button in dialog box)
* AlreadyVerified: REG_SZ: "yes" or "no"
* If the timeout isn't infinite, there appears the key:
* Timeout: REG_DWORD: <timeout in seconds>
* Parameters: REG_SZ: cmd line parameters
* If ConversationSecurity is yes, there are also these keys:
* <User1>: REG_SZ: <Password1>
* ...
* <Usern>: REG_SZ: <Passwordn>
*
* Note that the users and passwords are read from the list box.
*
* Arguments:
* lpServiceName is the name of the TP.
* lpLUName is the Local LU Alias.
* The two integer arguments give the state of the radio buttons on the
* dialog box. They should be zero to indicate that "No" is selected,
* nonzero otherwise.
* lpTimeout is a string containing the timeout value; if the timeout is
* infinite, it holds the value of INFINITE_TIMEOUT.
*
* Returns: 0 on success, nonzero otherwise.
*/
/*****************************************************************************/
INT CreateKeys(LPSTR lpServiceName, BOOL bService, LPSTR lpLUName, int iConvSec,
int iAlreadyVer, int iQueued, LPSTR lpTimeout, LPSTR lpParameters,
LPSTR lpExeName)
{
LPSTR lpServiceFullName;
int bufsize, i, nCount;
LOCALHANDLE hLocalMem;

// If any of the buttons says "no", change the key values so that they
// will be inserted into the registry below. This is kind of gross, but
// it keeps the loop below simple.
if (iConvSec == 0)
{
keyinfo[CONVSEC].lpData = "no";
keyinfo[CONVSEC].iDataSize = 3;
}
if (iAlreadyVer == 0)
{
keyinfo[ALREADYVER].lpData = "no";
keyinfo[ALREADYVER].iDataSize = 3;
}
if (bService == FALSE && iQueued == 0)
{
//
// the default of 5 is OK for queued support.
// needs to be redefined to 6 for nonqueued.
//
keyinfo[SNASRVTYPE].lpData = "6";
keyinfo[SNASRVTYPE].iDataSize = 4;
}

// Put timeout in structure (it won't be written out if infinite)
keyinfo[TIMEOUT].lpData = lpTimeout;

// Put Parameters in structure
keyinfo[PARAMETERS].lpData = lpParameters;
keyinfo[PARAMETERS].iDataSize = strlen(lpParameters) + 1; // Count null

// Put Local LU Alias in structure; it won't be written out if blank
keyinfo[LUNAME].lpData = lpLUName;
keyinfo[LUNAME].iDataSize = strlen(lpLUName) + 1; // Count null

// Create full path of TP by concatenating the registry path with the
// TP's name
bufsize = MAXREGPATHLEN + 1;
lpServiceFullName = (LPSTR) malloc(bufsize);

i = bService ? IDS_REGISTRYPATH : IDS_REGISTRYAPPLPATH ;
LoadString( hInst, i, lpServiceFullName, bufsize );
lstrcat(lpServiceFullName, lpServiceName);

// Write out the basic keys; first key is skipped for non-service tps
for ( i = bService ? 0 : 1; i < NUMKEYS; i++ )
{
if ( WriteKeyNT(lpServiceFullName, keyinfo[i], TRUE ) )
return 1;
}

// if not a service then write out the path name
if ( bService == FALSE )
{
keyinfo[EXENAME].lpData = lpExeName;
keyinfo[EXENAME].iDataSize = strlen(lpExeName) + 1; // Count null
if (WriteKeyNT(lpServiceFullName, keyinfo[EXENAME], TRUE ) )
return 1;
}

// If timeout isn't infinite, write out the "timeout" key
if (lstrcmp(lpTimeout, INFINITE_TIMEOUT))
{
if (WriteKeyNT(lpServiceFullName, keyinfo[TIMEOUT], TRUE ) )
return 1;
}

// If Local LU Alias isn't blank, write out its key
if (strlen(lpLUName) > 0)
{
if (WriteKeyNT(lpServiceFullName, keyinfo[LUNAME], TRUE ) )
return 1;
}

// Only write out user stuff if conversation security is "yes"
if (iConvSec)
{
keyinfo[USER].lpName = (LPSTR) malloc(MAXUSERNAMELEN + 1);
nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);
for (i = 0; i < nCount; i++)
{
// Get username & make it the name of this key
SendMessage(hList, LB_GETTEXT, (WPARAM) i,
(LPARAM) keyinfo[USER].lpName);

// Get pointer to password and then get password
hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
keyinfo[USER].lpData = (LPSTR) LocalLock(hLocalMem);

// Password shouldn't be blank, but just in case...
if (keyinfo[USER].lpData == NULL)
keyinfo[USER].iDataSize = 0;
else
keyinfo[USER].iDataSize = strlen(keyinfo[USER].lpData);

if (WriteKeyNT(lpServiceFullName, keyinfo[USER], TRUE ) )
{
LocalUnlock(hLocalMem);
return 1;
}
LocalUnlock(hLocalMem);
}
}

free(lpServiceFullName);
return 0;
}
/*****************************************************************************/
/*
* WriteKeyNT: Write out the given key to the registry.
* Return 0 on success, nonzero otherwise.
*/
/*****************************************************************************/
INT WriteKeyNT(LPSTR lpServiceFullName, KEYENTRY keyinfo, BOOL bAppendParent )
{
static BOOL bAskToReplace = TRUE;
DWORD dwResult;
HKEY hKey;
LPSTR lpKeyName;
DWORD dwTemp;

lpKeyName = (LPSTR) malloc(MAXREGPATHLEN + 1);

// Build full pathname of key by appending a backslash and the name of the
// key to the full pathname of the TP.
lstrcpy(lpKeyName, lpServiceFullName);
lstrcat(lpKeyName, TEXT("\\"));
lstrcat(lpKeyName, keyinfo.lpParent);

// Now create the subkeys and set their values
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Predefined key
lpKeyName, // Our subkey's name
0, // Reserved
NULL, // Class string
REG_OPTION_NON_VOLATILE, // Option type (value is saved)
KEY_ALL_ACCESS, // Access mask
NULL, // Security attributes
&hKey, // Handle to key is put here
&dwResult) // Was key opened or created?
== ERROR_SUCCESS)
{
if ( bAskToReplace && dwResult == REG_OPENED_EXISTING_KEY )
{
char szCaption[64];
char szText[256];
LoadString( hInst, IDS_REPLACECAPTION, szCaption, sizeof(szCaption) );
LoadString( hInst, IDS_REPLACETEXT, szText, sizeof(szText) );
switch( MessageBox( hDialog, szText, szCaption, MB_OKCANCEL|MB_ICONQUESTION ) )
{
case IDCANCEL:
return 1;

}
}

//
// after first successful pass no need to query
//
bAskToReplace = FALSE;

// We stored all the values as strings, but some are actually numbers.
// So we have to make a temporary buffer to pass RegSetValueEx the
// pointer to a DWORD that it wants for DWORD data.
if ( keyinfo.iDataType == REG_DWORD )
{
dwTemp = atol(keyinfo.lpData);
keyinfo.lpData = (LPSTR) &dwTemp;
keyinfo.iDataSize = sizeof(DWORD);
}

if (RegSetValueEx(hKey,
keyinfo.lpName, // Key name
0, // Reserved
keyinfo.iDataType, // Type of key data
keyinfo.lpData, // Key's value
keyinfo.iDataSize // Length of value (incl. nulls)
)
!= ERROR_SUCCESS)
{
DisplayError(hDialog, IDS_SETKEYFAILED);
return 1;
}
}
else
{
DisplayError(hDialog, IDS_OPENKEYFAILED);
return 1;
}

free(lpKeyName);
return 0;
}
/*****************************************************************************/
/*
* ParseCreateError: Given an error code from a registry operation, attempt
* to give an appropriate error message.
*/
/*****************************************************************************/
void ParseCreateError(HWND hDlg, UINT uError)
{
UINT code;
switch (uError)
{
case ERROR_INVALID_PARAMETER:
// "Invalid parameter" is a rather generic error name. Since a blank
// service name is the most probable way the error arose, say so:
code = IDS_BADPATHNAME;
break;

case ERROR_INVALID_NAME:
code = IDS_BADTPNAME;
break;

case ERROR_SERVICE_EXISTS:
code = IDS_SERVICEEXISTS;
break;

default:
// Since we don't have a code for this error, the following will bring
// up the "unknown" error message
code = uError;
}
DisplayError(hDlg, code);
}

#else

/*****************************************************************************/
/*
* InstallWindows: Windows version of installation; add lines to WIN.INI
*
* First we must add an entry under the heading SNAServerAutoTPs. This entry
* has the name of the TP and points to another heading that contains TP-
* specific data. Sample WIN.INI:
*
* [SNAServerAutoTPs]
* BounceTP = BounceTPParams
*
* [BounceTPParams]
* PathName = c:\sna\bounce.exe
* Parameters = /t
* etc....
*
* For a list of entries, see the comment at CreateKeys(). The only difference
* is that WIN.INI also contains an entry for the "queued" toggle.
*
*
* Parameters:
* szTPName: The name of the TP
* szBinaryPath: The full pathname of the executable
* szLUName: Local LU Alias
* szParameters: The list of command line parameters
* iConvSec: "Conversation security" toggle (1 = yes, 0 = no)
* iAlreadyVer: "Already verified" toggle
* iQueued: "Queued" toggle
* lpTimeout: A string specifying the timeout in seconds, e.g. "2"
*
* Returns: zero on success, nonzero otherwise
*/
/*****************************************************************************/
INT InstallWindows(HWND hDlg, char *szTPName, char *szBinaryPath, char *szLUName,
char *szParameters,
int iConvSec, int iAlreadyVer, int iQueued, char *lpTimeout
)
{
char szNewKeyName[MAXTPLEN + 6];
int i, nCount;
LOCALHANDLE hLocalMem;

// Make new heading by concatenating TP's name and the string "Params"
strcpy(szNewKeyName, szTPName);
strcat(szNewKeyName, "Params");

// Now add the key to the SNAServerAutoTPs heading
if (WriteKeyWindows("SNAServerAutoTPs", szTPName, szNewKeyName))
return 1;

// Set the data fields of the keys, one at a time
keyinfo[PATHNAME].data = szBinaryPath;
keyinfo[PARAMETERS].data = szParameters;
keyinfo[LUNAME].data = szLUName;
keyinfo[QUEUED].data = iQueued ? "yes" : "no";
keyinfo[TIMEOUT].data = lpTimeout;
keyinfo[CONVSEC].data = iConvSec ? "yes" : "no";
keyinfo[ALREADYVER].data = iAlreadyVer ? "yes" : "no";

// Loop through all the keys and add them under the new "Params" heading
for (i=0; i < NUMKEYS; i++)
if (WriteKeyWindows(szNewKeyName, keyinfo[i].name, keyinfo[i].data))
return 1;

// Only write out Local LU Alias if it's not blank
if (strlen(szLUName) > 0)
if (WriteKeyWindows(szNewKeyName, keyinfo[LUNAME].name, keyinfo[LUNAME].data))
return 1;

// If conversation security is on, write out users & passwords
if (iConvSec)
{
keyinfo[USER].name = (char *) malloc(MAXUSERNAMELEN + 1);
nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);

// Read usernames and passwords from list box & create keys
for (i = 0; i < nCount; i++)
{
// Get username & make it the name of this key
SendMessage(hList, LB_GETTEXT, (WPARAM) i,
(LPARAM) keyinfo[USER].name);

// Get pointer to password and then get password
hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
keyinfo[USER].data = (char *) LocalLock(hLocalMem);

if (WriteKeyWindows(szNewKeyName, keyinfo[USER].name, 
keyinfo[USER].data))
{
LocalUnlock(hLocalMem);
return 1;
}
LocalUnlock(hLocalMem);
}
free(keyinfo[USER].name);

}

return 0;
}
/*****************************************************************************/
/*
* WriteKeyWindows: Write the given key out to WIN.INI
* Return 0 on success, nonzero otherwise.
*/
/*****************************************************************************/
INT WriteKeyWindows(char *heading, char *keyname, char *value)
{
if (WriteProfileString(heading, keyname, value) == FALSE)
{
DisplayError(hDialog, IDS_INIWRITEFAILED);
return 1;
}
return 0;
}

#endif //ifdef WIN32

/*****************************************************************************/
/*
* ValidateField: Install a window procedure that handles messages to the
* given edit window.
*
* Parameters: hwnd is the edit window, usType an identifier for the window
* procuedure to install. Currently the only allowed type is VALIDATE_INT,
* which allows only digits to be typed into the edit box.
*
* Returns: TRUE if window procedure is successfully installed,
* FALSE otherwise.
/*****************************************************************************/
BOOL ValidateField( HWND hwnd, USHORT usType)
{
FARPROC lpfn = NULL;
LONG lPrevWndProc;
LONG FAR *lplNewWndProc;

if (hwnd == NULL) {
return(FALSE);
}

lPrevWndProc = GetWindowLong( hwnd, GWL_WNDPROC );

if (lPrevWndProc == 0) {
return(FALSE);
}

// check that the old wndproc is the same as the one we have stored
// also, get the new wndproc, based on contents of edit field

// We only need two validation procedures, but if you want others
// (such as hexadecimal, filenames, etc.) you can add extra cases
// and procedures below.

switch (usType) {
case VALIDATE_INT:
if (lpfnInt == NULL) {
lpfnInt = MakeProcInstance( (FARPROC)ValidateFieldInt, hInst );
}
lpfn = lpfnInt;

lplNewWndProc = (LONG FAR *) &lPrevWndProcInt;
break;

case VALIDATE_APPC:
if (lpfnAppc == NULL) {
lpfnAppc = MakeProcInstance( (FARPROC)ValidateFieldAppc, hInst );
}
lpfn = lpfnAppc;

lplNewWndProc = (LONG FAR *) &lPrevWndProcAppc;
break;

default:
return(FALSE);
}

if (*lplNewWndProc == (LONG) NULL) {

*lplNewWndProc = lPrevWndProc;

} else {

if (*lplNewWndProc != lPrevWndProc) {
return(FALSE);
}

*lplNewWndProc = lPrevWndProc;
}

if (lpfn == NULL ||
SetWindowLong(hwnd, GWL_WNDPROC, (LONG) lpfn) == (LONG) NULL) {

return(FALSE);
}
return(TRUE);
}

/*****************************************************************************/
/*
* ValidateFieldInt: A window proc that allows only 0 through 9 to be typed to
* an edit box.
*/
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldInt( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
if (msg == WM_CHAR) {

char ch = LOBYTE(LOWORD(wParam));

if ((ch < '0' || ch > '9') && ch != VK_BACK )
return(0);
}
// If character was legal or message wasn't WM_CHAR,
// pass message on to default window proc.
return CallWindowProc( lPrevWndProcInt, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
* ValidateFieldAppc: A window proc that allows only legal APPC name characters
* to be typed to an edit box.
*/
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldAppc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
if (msg == WM_CHAR) {

char ch = LOBYTE(LOWORD(wParam));

if (!(ch >= '0' && ch <= '9' ||
ch >= 'A' && ch <= 'Z' ||
ch >= 'a' && ch <= 'z' ||
ch == '@' ||
ch == '#' ||
ch == '$' ||
ch == '%' ||
ch == VK_BACK ))
return(0);
}
return CallWindowProc( lPrevWndProcAppc, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
* InstallCallback: Install a callback window proc.
* Arguments:
* hDlg: Handle of the parent dialog
* uId: ID of window for which callback will be installed
* OldWndProc: Handle for previous window proc
* NewWndProc: Pointer to new window proc
*/
/*****************************************************************************/
void InstallCallback(HWND hDlg, UINT uId, WNDPROC *OldWndProc, FARPROC NewWndProc)
{
HANDLE hTemp;

hTemp = GetDlgItem(hDlg, uId);
*OldWndProc = (WNDPROC) GetWindowLong(hTemp, GWL_WNDPROC);
SetWindowLong(hTemp, GWL_WNDPROC,
(LONG) MakeProcInstance( (FARPROC)NewWndProc, hInst));
}
/*****************************************************************************/
/*
* InfiniteWndProc: Disable "timeout" box when the "infinite"
* button is pressed, and enable it when "finite" is pressed.
*/
/*****************************************************************************/
LRESULT CALLBACK InfiniteWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
if (msg == BM_SETCHECK)
// wParam is 0 if button is being turned off, 1 if being turned on
EnableWindow(hTimeout, (wParam == 0));
return CallWindowProc(lPrevWndProcInfinite, hwnd, msg, wParam, lParam);
}


/*****************************************************************************/
/*
* ServiceWndProc: Disable "queued" box when the "service" button is pressed
*/
/*****************************************************************************/
LRESULT CALLBACK ServiceWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
HWND hwndCntl;

if (msg == BM_SETCHECK) {
//
// wParam is 0 if button is being turned off, 1 if being turned on
//
hwndCntl = GetDlgItem( hDialog, IDC_QUEUED );
EnableWindow( hwndCntl, wParam == 1 ? FALSE : TRUE );

if ( wParam == 1 ) {
SendMessage( hwndCntl, BM_SETCHECK, 1, 0 );
}
}

return CallWindowProc(lPrevWndProcService, hwnd, msg, wParam, lParam);
}


/*****************************************************************************/
/*
* ConvSecWndProc: Window proc for "conversation security" checkbox. Grey
* out "users" group box when checkbox is off
*/
/*****************************************************************************/
LRESULT CALLBACK ConvSecWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
int nItems = SendMessage(hList, LB_GETCOUNT, 0, 0L);
if (msg == BM_SETCHECK)
{
EnableWindow(GetDlgItem(hDialog, IDC_USERBOX), wParam == 1 ? TRUE : FALSE);
EnableWindow(GetDlgItem(hDialog, IDC_ADD), wParam == 1 ? TRUE : FALSE);
EnableWindow(hList, wParam == 1 ? TRUE : FALSE);
// Only turn on Delete & Edit if there's something in the list box
EnableWindow(GetDlgItem(hDialog, IDC_DELETE), wParam == 1 && nItems);
EnableWindow(GetDlgItem(hDialog, IDC_EDIT), wParam == 1 && nItems);
}
return CallWindowProc(lPrevWndProcConvSec, hwnd, msg, wParam, lParam);
}


/*****************************************************************************/
/*
* DeleteListItem: Handle all aspects of deleting a user from the list box,
* given his index.
*/
/*****************************************************************************/
void DeleteListItem(INT nIndex)
{
LOCALHANDLE hLocalMem;
INT nUsers;

// Free associated password memory, if necessary
if (hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA,
nIndex, 0))
LocalFree(hLocalMem);

// Simply remove currently selected user
SendMessage(hList, LB_DELETESTRING, (WPARAM) nIndex, 0);

nUsers = SendMessage(hList, LB_GETCOUNT, 0, 0);

// Disable Delete & Edit buttons if no one is left
if (0 == nUsers)
{
EnableWindow(GetDlgItem(hDialog, IDC_DELETE), FALSE);
EnableWindow(GetDlgItem(hDialog, IDC_EDIT), FALSE);
return;
}

// Move the highlight:
// 1) If the deleted user was the last one in the list, go to previous user.
// 2) Otherwise go to the next user.
SendMessage(hList, LB_SETCURSEL,
(nUsers == nIndex) ? (nIndex - 1) : nIndex, 0);
}
/*****************************************************************************/
/*
* ReadString: Get the text from an edit box and turn it into a C string.
*
* Parameters:
*
* hDlg: The dialog box.
* id: The dialog item id.
* lpString: A double pointer to a character. ReadString reserves space
* for the incoming string.
* maxlen: The maximum number of characters to read from the edit box.
*
* Returns:
* The number of characters read from the edit box.
*/
/*****************************************************************************/
INT ReadString(HWND hDlg, INT id, char **lpString, INT maxlen)
{
// Leave space for null character
*lpString = malloc((maxlen + 1) * sizeof(char));

return GetDlgItemText(hDlg, id, *lpString, maxlen+1);
}
/*****************************************************************************/
/*
* DisplayError: Bring up MessageBox with given message code's string.
*/
/*****************************************************************************/
void DisplayError( HWND hwnd, UINT uError)
{
char sz[256], szFormat[256];

if ( LoadString( hInst, uError, sz, sizeof(sz)) == 0 ) {
LoadString( hInst, IDS_UNKNOWN, szFormat, sizeof(szFormat) );
sprintf( sz, szFormat, uError );
}

LoadString( hInst, IDS_ERRORTITLE, szFormat, sizeof(szFormat) );
MessageBox( hwnd, sz, szFormat, MB_ICONEXCLAMATION | MB_OK);
}
/*****************************************************************************/
/*
* DisplayInfo: Put up an information box with given message string.
*/
/*****************************************************************************/
void DisplayInfo( HWND hwnd, UINT uInfo)
{
char sz[256], szFormat[256];

if ( LoadString( hInst, uInfo, sz, sizeof(sz) ) == 0 ) {
LoadString( hInst, IDS_NOMESSAGE, szFormat, sizeof(szFormat) );
sprintf( sz, szFormat, uInfo );
}

LoadString( hInst, IDS_INFOTITLE, szFormat, sizeof(szFormat) );
MessageBox( hwnd, sz, szFormat, MB_ICONINFORMATION | MB_OK);
}

void MakeValidLUName(LPTSTR name) {
int o, n;
char oldname[MAX_COMPUTERNAME_LENGTH + 1], c;
int l;

strcpy(oldname, name);

l = strlen(oldname);
for (o = 0, n = 0; o < l; o++) {
c = toupper(oldname[o]);

// make sure this is a valid character
if (((c >= '0') && (c <= '9')) ||
((c >= 'A') && (c <= 'Z')) ||
(c == '@') ||
(c == '#') ||
(c == '$') ||
(c == '%') ||
(c == ' ')) name[n++] = c;

// only allow 8 characters in a LU name
if (n == 8) break;
}
name[n] = 0;
}