INSTALL.C
/*************************************************************************\ 
* 
* TP 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 
* 
\*************************************************************************/ 
 
#define STRICT 
#include <windows.h> 
#ifdef WIN32 
#include <winsvc.h> 
#endif 
 
#ifdef        WIN32 
        #include <windowsx.h> 
 
BOOL IsWin95Client( void ); 
 
#else 
        #include <windowsx.h16> 
#endif 
 
#include <string.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <stdarg.h> 
#include "install.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, *lpTPName, *lpParameters, *lpTimeout, *lpLocalLUName; 
  char     *divider; 
  FARPROC  lpfn; 
  INT      nIndex, cmd; 
  BOOL     bService; 
  HWND   hwndCntl; 
#ifdef WIN32 
  DWORD   dwVersion; 
  HANDLE   hEvent; 
#endif 
 
  switch (wMsg) 
    { 
      case WM_INITDIALOG: 
        hDialog = hDlg; 
 
        // Set maximum pathname to 512 characters 
        SendMessage(GetDlgItem(hDlg, IDE_PATHNAME), 
                    EM_LIMITTEXT, MAXBINPATHLEN, 0); 
 
        // Set maximum TP name to 128 characters 
        SendMessage(GetDlgItem(hDlg, IDE_TPNAME), 
                    EM_LIMITTEXT, MAXTPLEN, 0); 
 
        // Default for timeout is "infinite" 
        CheckRadioButton(hDlg, IDD_FINITE, IDD_INFINITE, IDD_INFINITE); 
 
        hTimeout = GetDlgItem(hDlg, IDE_TIMEOUT); 
        hList = GetDlgItem(hDlg, IDC_LIST); 
 
        // Allow only numbers to be typed into the timeout box. 
        ValidateField(hTimeout, VALIDATE_INT); 
 
        // Set maximum length of number to be a few digits 
        SendMessage(hTimeout, EM_LIMITTEXT, MAXTIMEOUTLEN, 0); 
 
// 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); 
 
        // Put an initial value in the "timeout" box & grey it out 
        SetDlgItemInt(hDlg, IDE_TIMEOUT, INIT_TIMEOUT, FALSE); 
        EnableWindow(hTimeout, FALSE); 
 
        // Set callback procedure for radiobutton to make the timeout box 
        // sensitive only when "finite" is selected. 
        InstallCallback(hDlg, IDD_INFINITE, &lPrevWndProcInfinite, (FARPROC)InfiniteWndProc); 
 
        // Set callback for "conversation security" to grey out user group 
        // box when it's turned off. 
        InstallCallback(hDlg, IDC_CONVSEC, &lPrevWndProcConvSec, (FARPROC)ConvSecWndProc); 
        // Uncheck conversation security to force grey out of user group box 
        SendMessage(GetDlgItem(hDlg, IDC_CONVSEC), BM_SETCHECK, 0, 0L); 
 
#ifdef WIN32 
// 
// On Win95 don't allow TP to be configured as an NT service 
// 
dwVersion = GetVersion(); 
if( dwVersion < 0x80000000 &&// Windows NT 
!IsWin95Client() )// Not Win95 client 
{ 
        // Set callback for "service" to grey out queued 
        // box when it's turned on. 
        InstallCallback(hDlg, IDC_SERVICE, &lPrevWndProcService, (FARPROC)ServiceWndProc); 
 
        // Default for serviec is "yes" 
        CheckDlgButton(hDlg, IDC_SERVICE, 1); 
} 
else// Non-NT or Win95 client on NT 
{ 
        // Default for service is "no" 
        CheckDlgButton(hDlg, IDC_SERVICE, 0); 
 
// Disable the whole service option 
    hwndCntl = GetDlgItem( hDialog, IDC_SERVICE ); 
    EnableWindow( hwndCntl, FALSE ); 
} 
#endif 
 
        // Default for queued is "yes" 
        CheckDlgButton(hDlg, IDC_QUEUED, 1); 
 
        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 
              ReadString(hDlg, IDE_PATHNAME, &lpPathName, MAXBINPATHLEN); 
 
      // read Local LU Alias 
      ReadString(hDlg, IDE_LOCALLU, &lpLocalLUName, MAXLULEN); 
 
              // Get "timeout" value 
              if (IsDlgButtonChecked(hDlg, IDD_INFINITE) == 1) 
                lpTimeout = INFINITE_TIMEOUT; 
              else 
                { 
                  // Number must be filled in, or else we complain 
                  if (ReadString(hDlg, IDE_TIMEOUT, &lpTimeout, MAXTIMEOUTLEN + 3) 
                      == 0) 
                    { 
                      DisplayError(hDlg, IDS_BADTIMEOUT); 
      free(lpPathName); 
      free(lpTimeout); 
                      free(lpTPName); 
                      return TRUE; 
                    } 
  else 
    { 
  // 
  // convert to milliseconds the hard way 
  // 
  strcat( lpTimeout, "000" ); 
    } 
                } 
 
              // Split off command-line parameters from executable name. 
  // Win95 and NT support spaces in pathnames, it is not safe 
  // to assume that first space marks the end of the executable name. 
  // It is quite safe to assume that executable programs stil end 
  // with extension .exe 
 
  if (divider = strstr(lpPathName, ".exe" )) 
  { 
              if (divider = strchr(divider, ' ')) 
              { 
                  lpParameters = divider + 1; 
                  *divider = '\0'; 
              } 
                else 
{ 
  lpParameters = ""; 
} 
  } 
              else lpParameters = ""; 
 
#ifdef WIN32 
              bService = IsDlgButtonChecked(hDlg, IDC_SERVICE) == 1; 
 
              // Install the new service; if successful, set subkeys 
              if (bService && InstallServiceNT(hDlg, lpTPName, lpPathName) != 0) 
                { 
                  free(lpPathName); 
                  free(lpTPName); 
                  return TRUE; 
                } 
 
              if (CreateKeys(  lpTPName, 
   bService, 
                       lpLocalLUName, 
                               IsDlgButtonChecked(hDlg, IDC_CONVSEC) == 1, 
                               IsDlgButtonChecked(hDlg, IDC_ALREADYVER) == 1, 
                               IsDlgButtonChecked(hDlg, IDC_QUEUED) == 1, 
                               lpTimeout, 
   lpParameters, 
   lpPathName 
                               ) 
                               != 0) 
                { 
                  free(lpPathName); 
                  free(lpTPName); 
                  return TRUE; 
                } 
 
  // 
  // Now try to open the "SNABASE_NEWTP_EVENT" event and signal 
  // the SnaBase to dynamically register this new TP. NOTE this will 
  // only work with the WIN95 SnaBase. WinNT SnaBase doesn't create 
  // this event. 
  // 
  hEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, "SNABASE_NEWTP_EVENT" ); 
  if( hEvent != NULL ) 
  { 
  SetEvent( hEvent ); 
  CloseHandle( hEvent ); 
  } 
 
              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 IDCANCEL: 
              EndDialog(hDlg, FALSE); 
              return TRUE; 
 
            case IDC_ADD: 
            case IDC_EDIT: 
              // Bring up a dialog to add or edit username & password. 
              // The last argument to DialogBoxParam is passed to the dialog 
              // procedure and used to determine whether the procedure was 
              // called from Add or Edit. 
              lpfn = (FARPROC) MakeProcInstance( (FARPROC)UserDlgProc, hInst); 
              DialogBoxParam (hInst, 
                            (LPCSTR)"UserDlg", 
                            hDialog, 
                            (DLGPROC) lpfn, 
                            cmd); 
              FreeProcInstance(lpfn); 
              return TRUE; 
 
            case IDC_DELETE: 
              nIndex = SendMessage(hList, LB_GETCURSEL, 0, 0); 
              if (nIndex == LB_ERR) 
                break; 
 
              DeleteListItem(nIndex); 
              break; 
 
            default: 
              return FALSE; 
          } 
 
        break; 
 
      case WM_DESTROY: 
        PostQuitMessage(0); 
        return TRUE; 
    } 
 
#ifdef WIN32 
  UNREFERENCED_PARAMETER(divider); 
  UNREFERENCED_PARAMETER(lpParameters); 
#endif 
 
    return FALSE; 
} 
/*****************************************************************************/ 
/* 
 * UserDlgProc: Dialog procedure for adding or editing a username/password 
 *   pair. 
 */ 
/*****************************************************************************/ 
BOOL CALLBACK UserDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) 
{ 
  char *szUsername, *szPassword, *szPasswd; 
  WORD nIndex; 
  INT  nPasswdLen; 
  LOCALHANDLE hLocalMem; 
 
  switch(wMsg) 
    { 
      case WM_INITDIALOG: 
        // Set maximum username length 
        SendMessage(GetDlgItem(hDlg, IDE_USERNAME), 
                    EM_LIMITTEXT, MAXUSERNAMELEN, 0); 
 
        // Set maximum password length 
        SendMessage(GetDlgItem(hDlg, IDE_PASSWORD), 
                    EM_LIMITTEXT, MAXPASSWORDLEN, 0); 
 
        // If we were called from the Edit button, we should fill in the 
        // edit boxes with strings from the current selection 
        if (lParam == IDC_EDIT) 
          { 
            // Make sure something is currently selected 
            if ((nIndex = (WORD) SendMessage(hList, LB_GETCURSEL, 0, 0)) != LB_ERR) 
              { 
                szUsername = (char *) malloc(MAXUSERNAMELEN + 1); 
                SendMessage(hList, LB_GETTEXT, (WPARAM) nIndex, 
                            (LPARAM)(LPSTR)szUsername); 
                SendMessage(GetDlgItem(hDlg, IDE_USERNAME), WM_SETTEXT, 0, 
                            (LPARAM)(LPSTR)szUsername); 
                free(szUsername); 
 
                // Get pointer to password and then get password 
                hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, 
                                                      nIndex, 0); 
                if (hLocalMem != NULL) 
                  { 
                    szPasswd = (char *) LocalLock(hLocalMem); 
                    SendMessage(GetDlgItem(hDlg, IDE_PASSWORD), WM_SETTEXT, 0, 
                                    (LPARAM)(LPSTR)szPasswd); 
                    LocalUnlock(hLocalMem); 
                  } 
              } 
          } 
 
        break; 
 
      case WM_COMMAND: 
        switch(GET_WM_COMMAND_ID(wParam, lParam)) 
          { 
            case IDOK: 
              // Complain if username is blank 
              if (ReadString(hDlg, IDE_USERNAME, &szUsername, MAXUSERNAMELEN) 
                == 0) 
                { 
                  DisplayError(hDlg, IDS_NOUSERNAME); 
                  free(szUsername); 
                  return TRUE; 
                } 
 
              // Read password 
              nPasswdLen = ReadString(hDlg, IDE_PASSWORD, 
                                      &szPassword, MAXPASSWORDLEN); 
 
              // Complain if password is blank 
              if (nPasswdLen == 0) 
                { 
                  DisplayError(hDlg, IDS_NOPASSWORD); 
                  free(szUsername); 
                  free(szPassword); 
                  return TRUE; 
                } 
 
              // If user is already listed, replace him by first deleting. 
              // This also takes care of the Edit button, which presumably 
              // will often have a username that already exists in the list. 
              nIndex = (WORD) SendMessage(hList, LB_SELECTSTRING, (WPARAM) -1, 
                                  (LPARAM) (LPSTR)szUsername); 
              if (nIndex != LB_ERR) 
                DeleteListItem(nIndex); 
 
              // Add the new user to the "users" list in the main dialog 
              nIndex = (WORD) SendMessage(hList, LB_ADDSTRING, 0, 
                                    (LPARAM) (LPSTR) szUsername); 
 
      // Allocate some space for the password & store handle in list 
      hLocalMem = LocalAlloc(LHND, nPasswdLen + 1); 
      if (hLocalMem == NULL) 
{ 
  DisplayError(hDlg, IDS_OUTOFMEMORY); 
  SendMessage(hList, LB_SETITEMDATA, nIndex, (LPARAM) NULL); 
        } 
      else 
{ 
  szPasswd = (char *) LocalLock(hLocalMem); 
  strcpy(szPasswd, szPassword); 
  SendMessage(hList, LB_SETITEMDATA, nIndex, (LPARAM)(LPVOID)hLocalMem); 
  LocalUnlock(hLocalMem); 
                } 
 
              // Force highlight to newly inserted user, & enable Delete & Edit 
              SendMessage(hList, LB_SETCURSEL, nIndex, 0L); 
              EnableWindow(GetDlgItem(hDialog, IDC_DELETE), TRUE); 
              EnableWindow(GetDlgItem(hDialog, IDC_EDIT), TRUE); 
 
              EndDialog(hDlg, TRUE); 
              return TRUE; 
 
            case IDCANCEL: 
              EndDialog(hDlg, FALSE); 
              return TRUE; 
 
            default: 
              return FALSE; 
 
          } 
        break; 
    } 
  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 ; 
 
#ifdef WIN32 
  if( IsWin95Client() ) 
  { 
      i = bService ? IDS_REGISTRYPATH_WIN95 : IDS_REGISTRYAPPLPATH_WIN95; 
  } 
#endif 
 
  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 BOOLbAskToReplace = TRUE; 
  DWORD     dwResult; 
  HKEY      hKey; 
  LPSTR     lpKeyName; 
  DWORDdwTemp; 
 
  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)(LPSTR)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); 
} 
 
#ifdef WIN32 
/*****************************************************************************/ 
/* 
 * IsWin95Client( void ):  If this is the Win95 client, return TRUE 
 */ 
/*****************************************************************************/ 
 
BOOL IsWin95Client( void ) 
{ 
    HKEY hKey; 
    BOOL fRetval = FALSE; 
    DWORD dwType=0; 
    char  szBuffer[ 30 ]; 
    DWORD dwDataSize = sizeof( szBuffer ); 
 
 
    if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\SNA Server\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey ) ) 
    { 
        ; // fall through, return FALSE 
    } 
    else 
    { 
        if( ERROR_SUCCESS != RegQueryValueEx( hKey, "ClientType", 0, &dwType, szBuffer, &dwDataSize ) ) 
        { 
            ; // fall through, return FALSE 
        } 
        else if( REG_SZ != dwType ) 
        { 
            ; // fall through, return FALSE 
        } 
        else if( stricmp( szBuffer, "Windows 95" ) ) 
        { 
            ; // fall through, return FALSE 
        } 
        else 
        { 
            fRetval = TRUE; 
        } 
 
        RegCloseKey( hKey ); 
    } 
 
    return fRetval; 
 
} 
 
#endif