; ============================================================================
; Machine Authentication 3.0 Network Configuration
; Configures shop floor PCs for 802.1x/ISE Machine VLAN connectivity
; ============================================================================
;
; This installer configures:
; - Wired network: 802.1x using Corporate Holdings RADIUS servers
; - Wireless network: AESFMA SSID using Aerospace FreeRADIUS servers
;
; Silent Installation:
;   MachineAuthSetup.exe /VERYSILENT /SUPPRESSMSGBOXES /LOG="C:\ma3.log"
;
; ============================================================================

[Setup]
AppId={{8A3B4C5D-6E7F-8901-2345-6789ABCDEF01}}
AppName=Machine Authentication 3.0
AppVersion=3.4
AppPublisher=GE Aerospace
DefaultDirName={tmp}\MachineAuth
CreateAppDir=no
PrivilegesRequired=admin
OutputDir=Output
OutputBaseFilename=MachineAuthSetup_v3.4
SolidCompression=yes
Compression=lzma2
WizardStyle=modern
SetupIconFile=gea-logo.ico
WizardImageFile=banner.bmp
WizardSmallImageFile=banner-sm.bmp
DisableWelcomePage=no
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=no
DisableFinishedPage=no
Uninstallable=no

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Messages]
WelcomeLabel2=This will configure your PC for Machine Authentication 3.0 VLAN connectivity.%n%nThis configures 802.1x authentication for both wired and wireless network interfaces.%n%nYour network connection will be briefly interrupted during configuration.%n%nClick Next to continue.

[Files]
; Include the XML profile files - extract to temp
Source: "8021x.xml"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall
Source: "AESFMA.xml"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall

[Code]
const
  // Network interface names by Windows version
  WIN7_WIRED = 'Local Area Connection';
  WIN7_WIRELESS = 'Wireless Network Connection';
  WIN10_WIRED = 'Ethernet';
  WIN10_WIRELESS = 'Wi-Fi';

  // Network types
  NET_WIRED = 1;
  NET_WIRELESS = 2;
  NET_UNKNOWN = 0;

var
  ProgressPage: TOutputProgressWizardPage;
  LogMessages: String;

// ============================================================================
// LOGGING FUNCTIONS
// ============================================================================

procedure LogMsg(const Msg: String);
begin
  LogMessages := LogMessages + Msg + #13#10;
  Log(Msg);
end;

// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================

function IsWindows10OrLater: Boolean;
var
  Version: TWindowsVersion;
begin
  GetWindowsVersionEx(Version);
  Result := (Version.Major >= 10);
end;

function GetWiredInterfaceName: String;
begin
  if IsWindows10OrLater then
    Result := WIN10_WIRED
  else
    Result := WIN7_WIRED;
end;

function GetWirelessInterfaceName: String;
begin
  if IsWindows10OrLater then
    Result := WIN10_WIRELESS
  else
    Result := WIN7_WIRELESS;
end;

// Execute a command and return the exit code
function ExecCmd(const Executable, Params: String): Integer;
var
  ResultCode: Integer;
begin
  LogMsg('Executing: ' + Executable + ' ' + Params);
  if Exec(Executable, Params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
    Result := ResultCode
  else
    Result := -1;
  LogMsg('  Result: ' + IntToStr(Result));
end;

// Execute a command via cmd.exe
function ExecShell(const Command: String): Integer;
begin
  Result := ExecCmd('cmd.exe', '/c ' + Command);
end;

// Execute netsh command
function ExecNetsh(const Params: String): Integer;
begin
  Result := ExecCmd('netsh.exe', Params);
end;

// Execute sc command for service control
function ExecSC(const Params: String): Integer;
begin
  Result := ExecCmd('sc.exe', Params);
end;

// Execute net command
function ExecNet(const Params: String): Integer;
begin
  Result := ExecCmd('net.exe', Params);
end;

// Check if a network interface exists and is connected
function IsInterfaceConnected(const InterfaceName: String; IsWireless: Boolean): Boolean;
var
  TempFile: String;
  OutputLines: TArrayOfString;
  I: Integer;
  Line: String;
  ResultCode: Integer;
begin
  Result := False;
  TempFile := ExpandConstant('{tmp}\netcheck_') + IntToStr(Random(99999)) + '.txt';

  if IsWireless then
    Exec('cmd.exe', '/c netsh wlan show interfaces > "' + TempFile + '" 2>&1', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
  else
    Exec('cmd.exe', '/c netsh lan show interfaces > "' + TempFile + '" 2>&1', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);

  if FileExists(TempFile) then
  begin
    if LoadStringsFromFile(TempFile, OutputLines) then
    begin
      for I := 0 to GetArrayLength(OutputLines) - 1 do
      begin
        Line := OutputLines[I];
        // Check for interface name and connected state
        if (Pos(InterfaceName, Line) > 0) then
        begin
          Result := True;
          Break;
        end;
        // Also check for "State" line showing "connected"
        if (Pos('connected', LowerCase(Line)) > 0) and (Pos('disconnected', LowerCase(Line)) = 0) then
        begin
          Result := True;
        end;
      end;
    end;
    DeleteFile(TempFile);
  end;
end;

// Detect which network type is currently active
function DetectActiveNetwork: Integer;
var
  WiredInterface, WirelessInterface: String;
begin
  Result := NET_UNKNOWN;

  WiredInterface := GetWiredInterfaceName;
  WirelessInterface := GetWirelessInterfaceName;

  LogMsg('Detecting active network interface...');
  LogMsg('  Wired interface name: ' + WiredInterface);
  LogMsg('  Wireless interface name: ' + WirelessInterface);

  // Check wired first (preferred)
  if IsInterfaceConnected(WiredInterface, False) then
  begin
    LogMsg('  Active network: WIRED');
    Result := NET_WIRED;
  end
  else if IsInterfaceConnected(WirelessInterface, True) then
  begin
    LogMsg('  Active network: WIRELESS');
    Result := NET_WIRELESS;
  end
  else
  begin
    // Default to wired if can't detect
    LogMsg('  Could not detect active network, defaulting to WIRED');
    Result := NET_WIRED;
  end;
end;

// ============================================================================
// SERVICE MANAGEMENT
// ============================================================================

procedure StopService(const ServiceName: String);
begin
  LogMsg('Stopping service: ' + ServiceName);
  ExecNet('stop "' + ServiceName + '"');
end;

procedure StartService(const ServiceName: String);
begin
  LogMsg('Starting service: ' + ServiceName);
  ExecNet('start "' + ServiceName + '"');
end;

procedure SetServiceAutoStart(const ServiceName: String);
begin
  LogMsg('Setting service to auto-start: ' + ServiceName);
  ExecSC('config ' + ServiceName + ' start= auto');
end;

// ============================================================================
// NETWORK CONFIGURATION
// ============================================================================

function ConfigureWiredNetwork: Boolean;
var
  InterfaceName, ProfilePath: String;
begin
  Result := True;
  InterfaceName := GetWiredInterfaceName;
  ProfilePath := ExpandConstant('{tmp}\8021x.xml');

  LogMsg('');
  LogMsg('=== Configuring Wired Network ===');
  LogMsg('Interface: ' + InterfaceName);
  LogMsg('Profile: ' + ProfilePath);

  // Enable and start Wired AutoConfig service
  SetServiceAutoStart('dot3svc');
  StartService('Wired AutoConfig');

  // Small delay for service to start
  Sleep(1000);

  // Import 802.1x profile to wired interface
  LogMsg('Importing 802.1x profile...');
  if ExecNetsh('lan add profile filename="' + ProfilePath + '" interface="' + InterfaceName + '"') <> 0 then
  begin
    // Try without interface specification as fallback
    LogMsg('Retrying without interface specification...');
    ExecNetsh('lan add profile filename="' + ProfilePath + '"');
  end;

  LogMsg('Wired network configuration complete.');
end;

function ConfigureWirelessNetwork: Boolean;
var
  InterfaceName, ProfilePath: String;
begin
  Result := True;
  InterfaceName := GetWirelessInterfaceName;
  ProfilePath := ExpandConstant('{tmp}\AESFMA.xml');

  LogMsg('');
  LogMsg('=== Configuring Wireless Network ===');
  LogMsg('Interface: ' + InterfaceName);
  LogMsg('Profile: ' + ProfilePath);

  // Enable and start WLAN AutoConfig service
  SetServiceAutoStart('Wlansvc');
  StartService('WLAN AutoConfig');

  // Small delay for service to start
  Sleep(1000);

  // Import AESFMA profile to wireless interface
  LogMsg('Importing AESFMA profile...');
  if ExecNetsh('wlan add profile filename="' + ProfilePath + '" interface="' + InterfaceName + '"') <> 0 then
  begin
    // Try without interface specification as fallback
    LogMsg('Retrying without interface specification...');
    ExecNetsh('wlan add profile filename="' + ProfilePath + '"');
  end;

  LogMsg('Wireless network configuration complete.');
end;

procedure ReconnectNetwork(NetworkType: Integer);
var
  WiredInterface, WirelessInterface: String;
begin
  WiredInterface := GetWiredInterfaceName;
  WirelessInterface := GetWirelessInterfaceName;

  LogMsg('');
  LogMsg('=== Reconnecting Network ===');

  if NetworkType = NET_WIRED then
  begin
    LogMsg('Reconnecting wired interface...');
    ExecNetsh('lan reconnect interface="' + WiredInterface + '"');
  end
  else if NetworkType = NET_WIRELESS then
  begin
    LogMsg('Connecting to AESFMA wireless network...');
    ExecNetsh('wlan connect name=AESFMA ssid=AESFMA interface="' + WirelessInterface + '"');
  end;
end;

// ============================================================================
// MAIN CONFIGURATION PROCEDURE
// ============================================================================

function PerformConfiguration: Boolean;
var
  ActiveNetwork: Integer;
  WindowsVersion: String;
begin
  Result := True;
  LogMessages := '';

  // Log Windows version
  if IsWindows10OrLater then
    WindowsVersion := 'Windows 10/11'
  else
    WindowsVersion := 'Windows 7/8';

  LogMsg('Machine Authentication 3.0 Configuration');
  LogMsg('========================================');
  LogMsg('Windows Version: ' + WindowsVersion);
  LogMsg('');

  // Stop NetworkAdapterManager if it exists (may not exist on all machines)
  LogMsg('Stopping NetworkAdapterManager (if present)...');
  StopService('NetworkAdapterManager');

  // Detect active network
  ActiveNetwork := DetectActiveNetwork;

  // Configure wired network
  ProgressPage.SetText('Configuring wired network...', '');
  ProgressPage.SetProgress(1, 5);
  ConfigureWiredNetwork;

  // Configure wireless network
  ProgressPage.SetText('Configuring wireless network...', '');
  ProgressPage.SetProgress(2, 5);
  ConfigureWirelessNetwork;

  // Reconnect active network
  ProgressPage.SetText('Reconnecting network...', '');
  ProgressPage.SetProgress(3, 5);
  ReconnectNetwork(ActiveNetwork);

  // Start NetworkAdapterManager if it was stopped
  LogMsg('');
  LogMsg('Starting NetworkAdapterManager (if present)...');
  StartService('NetworkAdapterManager');

  // Wait for network to stabilize
  ProgressPage.SetText('Waiting for network to stabilize...', '');
  ProgressPage.SetProgress(4, 5);
  LogMsg('');
  LogMsg('Waiting 10 seconds for network to stabilize...');
  Sleep(10000);

  ProgressPage.SetProgress(5, 5);

  LogMsg('');
  LogMsg('========================================');
  LogMsg('Configuration complete!');
  LogMsg('');
  LogMsg('The machine should now have access to the Machine VLAN.');
  LogMsg('Test vault access to confirm connectivity.');
end;

// ============================================================================
// WIZARD EVENTS
// ============================================================================

procedure InitializeWizard;
begin
  // Create progress page
  ProgressPage := CreateOutputProgressPage('Configuring Network',
    'Please wait while Machine Authentication 3.0 is configured...');
end;

function InitializeSetup: Boolean;
begin
  Result := True;

  // Verify admin privileges
  if not IsAdmin then
  begin
    MsgBox('This installer requires administrator privileges.' + #13#10 +
           'Please right-click and select "Run as administrator".',
           mbError, MB_OK);
    Result := False;
  end;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  ResultForm: TSetupForm;
  Memo: TNewMemo;
  OKButton: TNewButton;
begin
  if CurStep = ssPostInstall then
  begin
    // Show progress page and perform configuration
    ProgressPage.Show;
    try
      PerformConfiguration;
    finally
      ProgressPage.Hide;
    end;

    // Show results in non-silent mode
    if not WizardSilent then
    begin
      ResultForm := CreateCustomForm;
      ResultForm.Caption := 'Configuration Results';
      ResultForm.ClientWidth := 600;
      ResultForm.ClientHeight := 450;
      ResultForm.Position := poScreenCenter;

      Memo := TNewMemo.Create(ResultForm);
      Memo.Parent := ResultForm;
      Memo.Left := 10;
      Memo.Top := 10;
      Memo.Width := ResultForm.ClientWidth - 20;
      Memo.Height := ResultForm.ClientHeight - 60;
      Memo.ScrollBars := ssVertical;
      Memo.ReadOnly := True;
      Memo.Text := LogMessages;
      Memo.Font.Name := 'Consolas';
      Memo.Font.Size := 9;

      OKButton := TNewButton.Create(ResultForm);
      OKButton.Parent := ResultForm;
      OKButton.Caption := 'OK';
      OKButton.Width := 80;
      OKButton.Height := 30;
      OKButton.Left := (ResultForm.ClientWidth - OKButton.Width) div 2;
      OKButton.Top := ResultForm.ClientHeight - 45;
      OKButton.ModalResult := mrOK;
      OKButton.Default := True;

      ResultForm.ActiveControl := OKButton;
      ResultForm.ShowModal;
      ResultForm.Free;
    end;
  end;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  WinVer, WiredInt, WirelessInt: String;
begin
  if IsWindows10OrLater then
    WinVer := 'Windows 10/11'
  else
    WinVer := 'Windows 7/8';

  WiredInt := GetWiredInterfaceName;
  WirelessInt := GetWirelessInterfaceName;

  Result := 'Machine Authentication 3.0 Configuration' + NewLine +
            NewLine +
            'The following will be configured:' + NewLine +
            NewLine +
            Space + 'Windows Version: ' + WinVer + NewLine +
            Space + 'Wired Interface: ' + WiredInt + NewLine +
            Space + 'Wireless Interface: ' + WirelessInt + NewLine +
            NewLine +
            'Actions:' + NewLine +
            Space + '1. Enable Wired AutoConfig service (dot3svc)' + NewLine +
            Space + '2. Import 802.1x profile for wired authentication' + NewLine +
            Space + '3. Enable WLAN AutoConfig service (Wlansvc)' + NewLine +
            Space + '4. Import AESFMA profile for wireless authentication' + NewLine +
            Space + '5. Reconnect active network interface' + NewLine +
            NewLine +
            'Note: Network will be briefly interrupted during configuration.';
end;
