JT2GO: - Inno Setup installer for JT2Go with prerequisite checking - Checks for VC++ x86/x64 redistributables and .NET 4.8 - Verifies admin privileges before installation - Supports silent installation mode NetworkDriveManager: - Full Inno Setup implementation with integrated PowerShell logic - Menu-based interface for managing legacy domain network drives - Features: backup/restore mappings, credential testing, drive reset - Server migration from rd.ds.ge.com to wjs.geaerospace.net - Three-phase reconnection to prevent error 1219 with multiple shares - Add predefined drives (S:, V:, Y:) functionality Template: - Reusable Inno Setup project template for co-workers - Documented sections with examples and best practices - Includes utility functions and common patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1279 lines
40 KiB
Plaintext
1279 lines
40 KiB
Plaintext
; ============================================================================
|
|
; GE Aerospace Network Drive Manager - Full Inno Setup Implementation
|
|
; All PowerShell logic integrated directly into Inno Setup
|
|
; Version 2.0
|
|
; ============================================================================
|
|
|
|
[Setup]
|
|
AppId={{E3F4A5B6-C7D8-9E0F-1A2B-3C4D5E6F7890}}
|
|
AppName=GE Aerospace Network Drive Manager
|
|
AppVersion=2.0
|
|
AppPublisher=WJDT
|
|
AppPublisherURL=http://tsgwp00525.rd.ds.ge.com
|
|
DefaultDirName={tmp}\NetworkDriveManager
|
|
CreateAppDir=no
|
|
PrivilegesRequired=lowest
|
|
OutputDir=Output
|
|
OutputBaseFilename=NetworkDriveManager
|
|
SolidCompression=yes
|
|
WizardStyle=modern
|
|
SetupIconFile=gea-logo.ico
|
|
WizardImageFile=patrick.bmp
|
|
WizardSmallImageFile=patrick-sm.bmp
|
|
DisableWelcomePage=yes
|
|
DisableDirPage=yes
|
|
DisableProgramGroupPage=yes
|
|
DisableReadyPage=yes
|
|
DisableFinishedPage=yes
|
|
Uninstallable=no
|
|
|
|
[Languages]
|
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
|
|
|
[Code]
|
|
// ============================================================================
|
|
// CONSTANTS AND CONFIGURATION
|
|
// ============================================================================
|
|
|
|
const
|
|
// Legacy domain servers to manage
|
|
// OLD_SERVER is detected in existing mappings, NEW_SERVER is used when remapping
|
|
OLD_SERVER_1 = 'tsgwp00525.rd.ds.ge.com';
|
|
NEW_SERVER_1 = 'tsgwp00525.wjs.geaerospace.net';
|
|
// Add more servers as needed:
|
|
// OLD_SERVER_2 = 'server2.rd.ds.ge.com';
|
|
// NEW_SERVER_2 = 'server2.newdomain.com';
|
|
|
|
LEGACY_DOMAIN = 'logon.ds.ge.com';
|
|
PASSWORD_RESET_URL = 'https://mypassword.ge.com';
|
|
BACKUP_FILENAME = 'NetworkDriveMappings.json';
|
|
|
|
// Menu options
|
|
MENU_VIEW_MAPPINGS = 1;
|
|
MENU_BACKUP = 2;
|
|
MENU_TEST_CREDS = 3;
|
|
MENU_FULL_RESET = 4;
|
|
MENU_RESTORE = 5;
|
|
MENU_ADD_DRIVES = 6;
|
|
MENU_PASSWORD_PORTAL = 7;
|
|
MENU_QUIT = 8;
|
|
|
|
// Predefined drive mappings
|
|
DRIVE_S_PATH = '\\tsgwp00525.wjs.geaerospace.net\shared';
|
|
DRIVE_S_DESC = 'S: - Shared Drive (Standard)';
|
|
|
|
DRIVE_V_PATH = '\\tsgwp00525.wjs.geaerospace.net\eng_apps';
|
|
DRIVE_V_DESC = 'V: - Engineering Apps (Engineers)';
|
|
|
|
DRIVE_Y_SERVER = 'win.cighpeifep036.logon.ds.ge.com';
|
|
DRIVE_Y_PATH = '\\win.cighpeifep036.logon.ds.ge.com\ugcam_res_lib';
|
|
DRIVE_Y_DESC = 'Y: - UGCAM Resource Library (Engineers)';
|
|
|
|
// Newline constant for script building
|
|
NL = #13#10;
|
|
|
|
// ============================================================================
|
|
// GLOBAL VARIABLES
|
|
// ============================================================================
|
|
|
|
var
|
|
MainMenuPage: TWizardPage;
|
|
PasswordPage: TInputQueryWizardPage;
|
|
ProgressPage: TOutputProgressWizardPage;
|
|
ResultsPage: TOutputMsgMemoWizardPage;
|
|
|
|
MenuListBox: TNewListBox;
|
|
StatusLabel: TLabel;
|
|
MappingsListBox: TNewListBox;
|
|
|
|
CurrentPassword: String;
|
|
LegacyUsername: String;
|
|
BackupFilePath: String;
|
|
|
|
SelectedMenuOption: Integer;
|
|
|
|
// ============================================================================
|
|
// UTILITY FUNCTIONS
|
|
// ============================================================================
|
|
|
|
function GetLegacyUsername: String;
|
|
begin
|
|
Result := GetUserNameString + '@' + LEGACY_DOMAIN;
|
|
end;
|
|
|
|
function GetBackupFilePath: String;
|
|
begin
|
|
Result := ExpandConstant('{userdocs}\') + BACKUP_FILENAME;
|
|
end;
|
|
|
|
function GetLegacyServers: TArrayOfString;
|
|
begin
|
|
// Return NEW server for credential testing (that's what we'll connect to)
|
|
SetArrayLength(Result, 1);
|
|
Result[0] := NEW_SERVER_1;
|
|
end;
|
|
|
|
// Execute a command and capture output to a file
|
|
function ExecWithOutput(const Cmd, Params, WorkDir: String; var Output: String): Integer;
|
|
var
|
|
TempFile: String;
|
|
ResultCode: Integer;
|
|
OutputAnsi: AnsiString;
|
|
begin
|
|
TempFile := ExpandConstant('{tmp}\cmd_output_') + IntToStr(Random(99999)) + '.txt';
|
|
|
|
Exec('cmd.exe', '/c ' + Cmd + ' ' + Params + ' > "' + TempFile + '" 2>&1',
|
|
WorkDir, SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
|
|
|
if FileExists(TempFile) then
|
|
begin
|
|
if LoadStringFromFile(TempFile, OutputAnsi) then
|
|
Output := String(OutputAnsi)
|
|
else
|
|
Output := '';
|
|
DeleteFile(TempFile);
|
|
end
|
|
else
|
|
Output := '';
|
|
|
|
Result := ResultCode;
|
|
end;
|
|
|
|
// Execute PowerShell command and capture output
|
|
function ExecPowerShell(const Script: String; var Output: String): Integer;
|
|
var
|
|
TempScript, TempOutput: String;
|
|
ResultCode: Integer;
|
|
OutputAnsi: AnsiString;
|
|
CmdLine: String;
|
|
begin
|
|
TempScript := ExpandConstant('{tmp}\ps_script_') + IntToStr(Random(99999)) + '.ps1';
|
|
TempOutput := ExpandConstant('{tmp}\ps_output_') + IntToStr(Random(99999)) + '.txt';
|
|
|
|
// Save script to temp file
|
|
if not SaveStringToFile(TempScript, Script, False) then
|
|
begin
|
|
Output := 'ERROR: Could not save script to temp file';
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
|
|
// Build command line - must use cmd.exe for redirection to work
|
|
CmdLine := '/c powershell.exe -NoProfile -ExecutionPolicy Bypass -File "' + TempScript + '" > "' + TempOutput + '" 2>&1';
|
|
|
|
// Execute via cmd.exe
|
|
if not Exec('cmd.exe', CmdLine, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
|
|
begin
|
|
Output := 'ERROR: Could not execute PowerShell';
|
|
Result := -1;
|
|
DeleteFile(TempScript);
|
|
Exit;
|
|
end;
|
|
|
|
// Read output
|
|
if FileExists(TempOutput) then
|
|
begin
|
|
if LoadStringFromFile(TempOutput, OutputAnsi) then
|
|
Output := String(OutputAnsi)
|
|
else
|
|
Output := 'ERROR: Could not read output file';
|
|
DeleteFile(TempOutput);
|
|
end
|
|
else
|
|
Output := 'ERROR: No output file created';
|
|
|
|
DeleteFile(TempScript);
|
|
Result := ResultCode;
|
|
end;
|
|
|
|
// Parse error code from net use output
|
|
function ParseNetUseErrorCode(const Output: String; ExitCode: Integer): Integer;
|
|
var
|
|
ErrorPos: Integer;
|
|
ErrorStr: String;
|
|
I: Integer;
|
|
begin
|
|
Result := ExitCode;
|
|
|
|
// Look for "System error XXXX"
|
|
ErrorPos := Pos('System error ', Output);
|
|
if ErrorPos > 0 then
|
|
begin
|
|
ErrorStr := '';
|
|
for I := ErrorPos + 13 to Length(Output) do
|
|
begin
|
|
if (Output[I] >= '0') and (Output[I] <= '9') then
|
|
ErrorStr := ErrorStr + Output[I]
|
|
else
|
|
Break;
|
|
end;
|
|
if ErrorStr <> '' then
|
|
Result := StrToIntDef(ErrorStr, ExitCode);
|
|
end
|
|
else
|
|
begin
|
|
// Look for "error XXXX"
|
|
ErrorPos := Pos('error ', Output);
|
|
if ErrorPos > 0 then
|
|
begin
|
|
ErrorStr := '';
|
|
for I := ErrorPos + 6 to Length(Output) do
|
|
begin
|
|
if (Output[I] >= '0') and (Output[I] <= '9') then
|
|
ErrorStr := ErrorStr + Output[I]
|
|
else
|
|
Break;
|
|
end;
|
|
if ErrorStr <> '' then
|
|
Result := StrToIntDef(ErrorStr, ExitCode);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Get error message for error code
|
|
function GetErrorMessage(ErrorCode: Integer): String;
|
|
begin
|
|
case ErrorCode of
|
|
0: Result := 'Success';
|
|
5: Result := 'Access denied - you may not have permission to this share';
|
|
53: Result := 'Network path not found - Zscaler may need re-authentication';
|
|
64: Result := 'Network connection was dropped unexpectedly';
|
|
67: Result := 'Network name cannot be found - invalid share path';
|
|
85: Result := 'Drive letter is already in use';
|
|
86: Result := 'Invalid password';
|
|
1219: Result := 'Multiple connections to server exist with different credentials';
|
|
1326: Result := 'Logon failure - unknown username or bad password';
|
|
1327: Result := 'Account restriction - policy is preventing logon';
|
|
1330: Result := 'Password has expired';
|
|
1331: Result := 'Account is disabled';
|
|
1909: Result := 'Account is locked out';
|
|
else
|
|
Result := 'Unknown error (code: ' + IntToStr(ErrorCode) + ')';
|
|
end;
|
|
end;
|
|
|
|
// Get action for error code
|
|
function GetErrorAction(ErrorCode: Integer): String;
|
|
begin
|
|
case ErrorCode of
|
|
5: Result := 'contact_manager';
|
|
53: Result := 'zscaler_reauth';
|
|
64: Result := 'retry_or_zscaler';
|
|
67: Result := 'verify_path';
|
|
85: Result := 'reassign_drive';
|
|
86, 1326, 1330, 1909: Result := 'reset_password';
|
|
1219: Result := 'clear_connections';
|
|
1327, 1331: Result := 'contact_helpdesk';
|
|
else
|
|
Result := 'unknown';
|
|
end;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// DRIVE MAPPING FUNCTIONS
|
|
// ============================================================================
|
|
|
|
function GetCurrentDriveMappings(LegacyOnly: Boolean): String;
|
|
var
|
|
Script: String;
|
|
Output: String;
|
|
begin
|
|
Script :=
|
|
'# Detect old, new, and Y: drive server names' + NL +
|
|
'$LegacyServers = @("' + OLD_SERVER_1 + '", "' + NEW_SERVER_1 + '", "' + DRIVE_Y_SERVER + '")' + NL +
|
|
'$mappings = @()' + NL +
|
|
'$netDrives = Get-WmiObject -Class Win32_MappedLogicalDisk -ErrorAction SilentlyContinue' + NL +
|
|
'foreach ($drive in $netDrives) {' + NL +
|
|
' $isLegacy = $false' + NL +
|
|
' foreach ($server in $LegacyServers) {' + NL +
|
|
' if ($drive.ProviderName -like "*$server*") { $isLegacy = $true; break }' + NL +
|
|
' }' + NL;
|
|
|
|
if LegacyOnly then
|
|
Script := Script + ' if ($isLegacy) {' + NL
|
|
else
|
|
Script := Script + ' if ($true) {' + NL;
|
|
|
|
Script := Script +
|
|
' $tag = if ($isLegacy) { " [LEGACY]" } else { "" }' + NL +
|
|
' $mappings += "$($drive.DeviceID) -> $($drive.ProviderName)$tag"' + NL +
|
|
' }' + NL +
|
|
'}' + NL +
|
|
'if ($mappings.Count -eq 0) { Write-Output "No network drives mapped." }' + NL +
|
|
'else { $mappings | ForEach-Object { Write-Output $_ } }';
|
|
|
|
ExecPowerShell(Script, Output);
|
|
Result := Output;
|
|
end;
|
|
|
|
function GetDriveMappingsAsJSON: String;
|
|
var
|
|
Script: String;
|
|
Output: String;
|
|
begin
|
|
Script :=
|
|
'# Detect old, new, and Y: drive server names' + NL +
|
|
'$LegacyServers = @("' + OLD_SERVER_1 + '", "' + NEW_SERVER_1 + '", "' + DRIVE_Y_SERVER + '")' + NL +
|
|
'$mappings = @()' + NL +
|
|
'$netDrives = Get-WmiObject -Class Win32_MappedLogicalDisk -ErrorAction SilentlyContinue' + NL +
|
|
'foreach ($drive in $netDrives) {' + NL +
|
|
' $isLegacy = $false' + NL +
|
|
' foreach ($server in $LegacyServers) {' + NL +
|
|
' if ($drive.ProviderName -like "*$server*") { $isLegacy = $true; break }' + NL +
|
|
' }' + NL +
|
|
' $serverName = if ($drive.ProviderName -match "\\\\([^\\]+)\\") { $Matches[1] } else { "" }' + NL +
|
|
' $shareName = if ($drive.ProviderName -match "\\\\[^\\]+\\(.+)$") { $Matches[1] } else { "" }' + NL +
|
|
' $mappings += [PSCustomObject]@{' + NL +
|
|
' DriveLetter = $drive.DeviceID' + NL +
|
|
' RemotePath = $drive.ProviderName' + NL +
|
|
' ServerName = $serverName' + NL +
|
|
' ShareName = $shareName' + NL +
|
|
' IsLegacy = $isLegacy' + NL +
|
|
' }' + NL +
|
|
'}' + NL +
|
|
'$backup = [PSCustomObject]@{' + NL +
|
|
' BackupDate = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")' + NL +
|
|
' ComputerName = $env:COMPUTERNAME' + NL +
|
|
' Username = $env:USERNAME' + NL +
|
|
' Mappings = $mappings' + NL +
|
|
'}' + NL +
|
|
'$backup | ConvertTo-Json -Depth 3';
|
|
|
|
ExecPowerShell(Script, Output);
|
|
Result := Output;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// BACKUP/RESTORE FUNCTIONS
|
|
// ============================================================================
|
|
|
|
function BackupDriveMappings: Boolean;
|
|
var
|
|
JSONContent: String;
|
|
begin
|
|
Result := False;
|
|
JSONContent := GetDriveMappingsAsJSON;
|
|
|
|
if JSONContent <> '' then
|
|
begin
|
|
Result := SaveStringToFile(BackupFilePath, JSONContent, False);
|
|
end;
|
|
end;
|
|
|
|
function LoadBackupInfo(var BackupDate, MappingsList: String): Boolean;
|
|
var
|
|
Script: String;
|
|
Output: String;
|
|
JSONContent: AnsiString;
|
|
begin
|
|
Result := False;
|
|
BackupDate := '';
|
|
MappingsList := '';
|
|
|
|
if not FileExists(BackupFilePath) then
|
|
Exit;
|
|
|
|
if not LoadStringFromFile(BackupFilePath, JSONContent) then
|
|
Exit;
|
|
|
|
Script :=
|
|
'$json = Get-Content -Path "' + BackupFilePath + '" -Raw | ConvertFrom-Json' + NL +
|
|
'Write-Output "DATE:$($json.BackupDate)"' + NL +
|
|
'foreach ($m in $json.Mappings) {' + NL +
|
|
' $tag = if ($m.IsLegacy) { " [LEGACY]" } else { "" }' + NL +
|
|
' Write-Output "MAP:$($m.DriveLetter) -> $($m.RemotePath)$tag"' + NL +
|
|
'}';
|
|
|
|
if ExecPowerShell(Script, Output) = 0 then
|
|
begin
|
|
// Parse output
|
|
if Pos('DATE:', Output) > 0 then
|
|
begin
|
|
BackupDate := Copy(Output, Pos('DATE:', Output) + 5, 19);
|
|
MappingsList := Output;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// CREDENTIAL TESTING
|
|
// ============================================================================
|
|
|
|
function TestCredentials(const Server, Username, Password: String; var ErrorCode: Integer; var ErrorMsg: String): Boolean;
|
|
var
|
|
Output: String;
|
|
ExitCode: Integer;
|
|
TestPath: String;
|
|
begin
|
|
Result := False;
|
|
TestPath := '\\' + Server + '\IPC$';
|
|
|
|
// First clear any existing connections
|
|
ExecWithOutput('net', 'use "' + TestPath + '" /delete /y', '', Output);
|
|
ExecWithOutput('net', 'use "\\' + Server + '" /delete /y', '', Output);
|
|
|
|
// Try to connect
|
|
ExitCode := ExecWithOutput('net', 'use "' + TestPath + '" /user:' + Username + ' "' + Password + '"', '', Output);
|
|
|
|
// Parse actual error code from output
|
|
ErrorCode := ParseNetUseErrorCode(Output, ExitCode);
|
|
|
|
// Clean up test connection
|
|
ExecWithOutput('net', 'use "' + TestPath + '" /delete /y', '', Output);
|
|
|
|
if ErrorCode = 0 then
|
|
begin
|
|
Result := True;
|
|
ErrorMsg := 'Credentials validated successfully';
|
|
end
|
|
else
|
|
begin
|
|
ErrorMsg := GetErrorMessage(ErrorCode);
|
|
end;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// CONNECTION MANAGEMENT
|
|
// ============================================================================
|
|
|
|
function ClearAllLegacyConnections: String;
|
|
var
|
|
Script: String;
|
|
Output: String;
|
|
begin
|
|
Script :=
|
|
'# Include old, new, and Y: drive server names' + NL +
|
|
'$LegacyServers = @("' + OLD_SERVER_1 + '", "' + NEW_SERVER_1 + '", "' + DRIVE_Y_SERVER + '")' + NL +
|
|
'$results = @()' + NL +
|
|
NL +
|
|
'# Clear IPC$ and server connections' + NL +
|
|
'foreach ($server in $LegacyServers) {' + NL +
|
|
' $null = net use "\\$server\IPC$" /delete /y 2>&1' + NL +
|
|
' $null = net use "\\$server" /delete /y 2>&1' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# Clear mapped drives' + NL +
|
|
'$netUseList = net use 2>&1' + NL +
|
|
'foreach ($server in $LegacyServers) {' + NL +
|
|
' $connections = $netUseList | Select-String -Pattern $server' + NL +
|
|
' foreach ($conn in $connections) {' + NL +
|
|
' if ($conn -match "^(OK|Disconnected|Unavailable)?\s*([A-Z]:)") {' + NL +
|
|
' $drive = $Matches[2]' + NL +
|
|
' $null = net use $drive /delete /y 2>&1' + NL +
|
|
' $results += "Disconnected $drive"' + NL +
|
|
' }' + NL +
|
|
' }' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# Clear cached credentials' + NL +
|
|
'foreach ($server in $LegacyServers) {' + NL +
|
|
' $null = cmdkey /delete:$server 2>&1' + NL +
|
|
' $null = cmdkey /delete:"Domain:target=$server" 2>&1' + NL +
|
|
' $results += "Cleared credentials for $server"' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'if ($results.Count -eq 0) { Write-Output "No connections to clear" }' + NL +
|
|
'else { $results | ForEach-Object { Write-Output $_ } }';
|
|
|
|
ExecPowerShell(Script, Output);
|
|
Result := Output;
|
|
end;
|
|
|
|
function ReconnectDrivesFromBackup(const Username, Password: String): String;
|
|
var
|
|
Script: String;
|
|
Output: String;
|
|
begin
|
|
Script :=
|
|
'$Username = "' + Username + '"' + NL +
|
|
'$Password = "' + Password + '"' + NL +
|
|
'$BackupFile = "' + BackupFilePath + '"' + NL +
|
|
NL +
|
|
'# Server name mapping (old -> new)' + NL +
|
|
'$ServerMapping = @{' + NL +
|
|
' "' + OLD_SERVER_1 + '" = "' + NEW_SERVER_1 + '"' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# All managed servers' + NL +
|
|
'$ManagedServers = @("' + OLD_SERVER_1 + '", "' + NEW_SERVER_1 + '", "' + DRIVE_Y_SERVER + '")' + NL +
|
|
NL +
|
|
'if (-not (Test-Path $BackupFile)) {' + NL +
|
|
' Write-Output "ERROR: No backup file found"' + NL +
|
|
' exit 1' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'$backup = Get-Content $BackupFile -Raw | ConvertFrom-Json' + NL +
|
|
'$results = @()' + NL +
|
|
NL +
|
|
'# PHASE 1: Collect all legacy mappings and prepare new paths' + NL +
|
|
'$drivesToMap = @()' + NL +
|
|
'foreach ($mapping in $backup.Mappings) {' + NL +
|
|
' if ($mapping.IsLegacy) {' + NL +
|
|
' $drive = $mapping.DriveLetter' + NL +
|
|
' $path = $mapping.RemotePath' + NL +
|
|
' $originalPath = $path' + NL +
|
|
NL +
|
|
' # Replace old server names with new ones' + NL +
|
|
' foreach ($oldServer in $ServerMapping.Keys) {' + NL +
|
|
' $newServer = $ServerMapping[$oldServer]' + NL +
|
|
' if ($path -like "*$oldServer*") {' + NL +
|
|
' $path = $path -replace [regex]::Escape($oldServer), $newServer' + NL +
|
|
' $results += "MIGRATING: $originalPath -> $path"' + NL +
|
|
' }' + NL +
|
|
' }' + NL +
|
|
NL +
|
|
' $drivesToMap += [PSCustomObject]@{' + NL +
|
|
' DriveLetter = $drive' + NL +
|
|
' Path = $path' + NL +
|
|
' }' + NL +
|
|
' }' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'if ($drivesToMap.Count -eq 0) {' + NL +
|
|
' Write-Output "No legacy drives to reconnect"' + NL +
|
|
' exit 0' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# PHASE 2: Disconnect ALL drives to managed servers FIRST' + NL +
|
|
'# This prevents error 1219 when multiple drives point to same server' + NL +
|
|
'$results += ""' + NL +
|
|
'$results += "Disconnecting existing drives..."' + NL +
|
|
'foreach ($driveInfo in $drivesToMap) {' + NL +
|
|
' $null = net use $driveInfo.DriveLetter /delete /y 2>&1' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# Also clear IPC$ connections to prevent 1219' + NL +
|
|
'foreach ($server in $ManagedServers) {' + NL +
|
|
' $null = net use "\\$server\IPC$" /delete /y 2>&1' + NL +
|
|
' $null = net use "\\$server" /delete /y 2>&1' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# Clear cached credentials' + NL +
|
|
'foreach ($server in $ManagedServers) {' + NL +
|
|
' $null = cmdkey /delete:$server 2>&1' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'# Small delay to let Windows release connections' + NL +
|
|
'Start-Sleep -Seconds 2' + NL +
|
|
NL +
|
|
'# PHASE 3: Reconnect ALL drives' + NL +
|
|
'$results += ""' + NL +
|
|
'$results += "Reconnecting drives..."' + NL +
|
|
'foreach ($driveInfo in $drivesToMap) {' + NL +
|
|
' $drive = $driveInfo.DriveLetter' + NL +
|
|
' $path = $driveInfo.Path' + NL +
|
|
NL +
|
|
' $output = net use $drive $path /user:$Username $Password /persistent:yes 2>&1' + NL +
|
|
' $exitCode = $LASTEXITCODE' + NL +
|
|
NL +
|
|
' if ($exitCode -eq 0) {' + NL +
|
|
' $results += "OK: $drive -> $path"' + NL +
|
|
' } else {' + NL +
|
|
' $errCode = $exitCode' + NL +
|
|
' $outStr = $output | Out-String' + NL +
|
|
' if ($outStr -match "System error (\d+)") { $errCode = [int]$Matches[1] }' + NL +
|
|
' $results += "FAILED: $drive -> $path (Error $errCode)"' + NL +
|
|
' }' + NL +
|
|
'}' + NL +
|
|
NL +
|
|
'$results | ForEach-Object { Write-Output $_ }';
|
|
|
|
ExecPowerShell(Script, Output);
|
|
Result := Output;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// UI HELPER FUNCTIONS
|
|
// ============================================================================
|
|
|
|
procedure UpdateStatusLabel(const Msg: String);
|
|
begin
|
|
if StatusLabel <> nil then
|
|
StatusLabel.Caption := Msg;
|
|
end;
|
|
|
|
procedure ShowResultsInMemo(const Title, Content: String);
|
|
var
|
|
ResultForm: TSetupForm;
|
|
Memo: TNewMemo;
|
|
OKButton: TNewButton;
|
|
begin
|
|
// Create a modal results dialog
|
|
ResultForm := CreateCustomForm;
|
|
ResultForm.Caption := Title;
|
|
ResultForm.ClientWidth := 500;
|
|
ResultForm.ClientHeight := 400;
|
|
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 := Content;
|
|
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;
|
|
|
|
// ============================================================================
|
|
// MENU OPERATIONS
|
|
// ============================================================================
|
|
|
|
procedure DoViewMappings;
|
|
var
|
|
Mappings: String;
|
|
begin
|
|
Mappings := GetCurrentDriveMappings(False);
|
|
if Mappings = '' then
|
|
Mappings := 'No output received from PowerShell script.';
|
|
ShowResultsInMemo('Current Network Drive Mappings', Mappings);
|
|
end;
|
|
|
|
procedure DoBackupMappings;
|
|
var
|
|
Success: Boolean;
|
|
BackupDate, MappingsList: String;
|
|
begin
|
|
// Check for existing backup
|
|
if FileExists(BackupFilePath) then
|
|
begin
|
|
if LoadBackupInfo(BackupDate, MappingsList) then
|
|
begin
|
|
if MsgBox('Existing backup found from: ' + BackupDate + #13#10 +
|
|
'Do you want to overwrite it?', mbConfirmation, MB_YESNO) = IDNO then
|
|
begin
|
|
ShowResultsInMemo('Backup', 'Backup cancelled - keeping existing backup from ' + BackupDate);
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Success := BackupDriveMappings;
|
|
if Success then
|
|
ShowResultsInMemo('Backup Complete', 'Drive mappings saved to:' + #13#10 + BackupFilePath)
|
|
else
|
|
ShowResultsInMemo('Backup Failed', 'Could not save backup to:' + #13#10 + BackupFilePath + #13#10#13#10 +
|
|
'BackupFilePath: ' + BackupFilePath);
|
|
end;
|
|
|
|
procedure DoTestCredentials;
|
|
var
|
|
ErrorCode: Integer;
|
|
ErrorMsg: String;
|
|
Success: Boolean;
|
|
Servers: TArrayOfString;
|
|
I: Integer;
|
|
Results: String;
|
|
begin
|
|
if CurrentPassword = '' then
|
|
begin
|
|
MsgBox('Please enter your password first.', mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
ProgressPage.SetText('Testing credentials...', '');
|
|
ProgressPage.Show;
|
|
|
|
try
|
|
Servers := GetLegacyServers;
|
|
Results := 'Testing credentials for: ' + LegacyUsername + NL + NL;
|
|
|
|
for I := 0 to GetArrayLength(Servers) - 1 do
|
|
begin
|
|
ProgressPage.SetText('Testing ' + Servers[I] + '...', '');
|
|
ProgressPage.SetProgress((I + 1) * 100 div GetArrayLength(Servers), 100);
|
|
|
|
Success := TestCredentials(Servers[I], LegacyUsername, CurrentPassword, ErrorCode, ErrorMsg);
|
|
|
|
if Success then
|
|
Results := Results + Servers[I] + ': OK' + NL
|
|
else
|
|
begin
|
|
Results := Results + Servers[I] + ': FAILED' + NL;
|
|
Results := Results + ' Error ' + IntToStr(ErrorCode) + ': ' + ErrorMsg + NL;
|
|
|
|
// Handle specific errors
|
|
if ErrorCode = 1219 then
|
|
begin
|
|
Results := Results + NL + 'Clearing existing connections...' + NL;
|
|
Results := Results + ClearAllLegacyConnections + NL;
|
|
Results := Results + NL + 'Please try testing again.' + NL;
|
|
end
|
|
else if (ErrorCode = 86) or (ErrorCode = 1326) or (ErrorCode = 1330) then
|
|
begin
|
|
Results := Results + NL + 'Your password may need to be reset.' + NL;
|
|
Results := Results + 'Use option 6 to open the password reset portal.' + NL;
|
|
end
|
|
else if ErrorCode = 53 then
|
|
begin
|
|
Results := Results + NL + 'Please re-authenticate to Zscaler and try again.' + NL;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ShowResultsInMemo('Credential Test Results', Results);
|
|
finally
|
|
ProgressPage.Hide;
|
|
end;
|
|
end;
|
|
|
|
procedure DoFullReset;
|
|
var
|
|
ErrorCode: Integer;
|
|
ErrorMsg: String;
|
|
Success: Boolean;
|
|
Servers: TArrayOfString;
|
|
Results: String;
|
|
begin
|
|
if CurrentPassword = '' then
|
|
begin
|
|
MsgBox('Please enter your password first.', mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
if MsgBox('This will:' + NL +
|
|
'1. Backup your current drive mappings' + NL +
|
|
'2. Test your credentials' + NL +
|
|
'3. Disconnect all legacy drives' + NL +
|
|
'4. Clear cached credentials' + NL +
|
|
'5. Reconnect drives with new credentials' + NL +
|
|
'Continue?', mbConfirmation, MB_YESNO) = IDNO then
|
|
Exit;
|
|
|
|
ProgressPage.Show;
|
|
Results := '';
|
|
|
|
try
|
|
// Step 1: Backup
|
|
ProgressPage.SetText('Step 1/5: Backing up current mappings...', '');
|
|
ProgressPage.SetProgress(10, 100);
|
|
|
|
if BackupDriveMappings then
|
|
Results := Results + '[OK] Backup saved to ' + BackupFilePath + NL
|
|
else
|
|
Results := Results + '[WARN] Backup failed - continuing anyway' + NL;
|
|
|
|
// Step 2: Test credentials
|
|
ProgressPage.SetText('Step 2/5: Testing credentials...', '');
|
|
ProgressPage.SetProgress(30, 100);
|
|
|
|
Servers := GetLegacyServers;
|
|
Success := TestCredentials(Servers[0], LegacyUsername, CurrentPassword, ErrorCode, ErrorMsg);
|
|
|
|
if not Success then
|
|
begin
|
|
if ErrorCode = 1219 then
|
|
begin
|
|
Results := Results + '[INFO] Clearing existing connections first...' + NL;
|
|
ClearAllLegacyConnections;
|
|
// Retry
|
|
Success := TestCredentials(Servers[0], LegacyUsername, CurrentPassword, ErrorCode, ErrorMsg);
|
|
end;
|
|
end;
|
|
|
|
if Success then
|
|
Results := Results + '[OK] Credentials validated' + NL
|
|
else
|
|
begin
|
|
Results := Results + '[FAILED] Credential test failed: ' + ErrorMsg + NL;
|
|
Results := Results + NL + 'Please fix the credential issue before continuing.' + NL;
|
|
ShowResultsInMemo('Full Reset - Aborted', Results);
|
|
Exit;
|
|
end;
|
|
|
|
// Step 3: Clear connections
|
|
ProgressPage.SetText('Step 3/5: Clearing existing connections...', '');
|
|
ProgressPage.SetProgress(50, 100);
|
|
|
|
Results := Results + NL + ClearAllLegacyConnections + NL;
|
|
|
|
// Step 4: Wait a moment
|
|
ProgressPage.SetText('Step 4/5: Waiting for connections to clear...', '');
|
|
ProgressPage.SetProgress(70, 100);
|
|
Sleep(2000);
|
|
|
|
// Step 5: Reconnect
|
|
ProgressPage.SetText('Step 5/5: Reconnecting drives...', '');
|
|
ProgressPage.SetProgress(90, 100);
|
|
|
|
Results := Results + NL + 'Reconnecting drives:' + NL;
|
|
Results := Results + ReconnectDrivesFromBackup(LegacyUsername, CurrentPassword);
|
|
|
|
ProgressPage.SetProgress(100, 100);
|
|
|
|
Results := Results + NL + '=== Full Reset Complete ===' + NL;
|
|
ShowResultsInMemo('Full Reset Results', Results);
|
|
|
|
finally
|
|
ProgressPage.Hide;
|
|
end;
|
|
end;
|
|
|
|
procedure DoRestoreFromBackup;
|
|
var
|
|
BackupDate, MappingsList: String;
|
|
Results: String;
|
|
begin
|
|
if CurrentPassword = '' then
|
|
begin
|
|
MsgBox('Please enter your password first.', mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
if not FileExists(BackupFilePath) then
|
|
begin
|
|
MsgBox('No backup file found at:' + NL + BackupFilePath, mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
if not LoadBackupInfo(BackupDate, MappingsList) then
|
|
begin
|
|
MsgBox('Could not read backup file.', mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
if MsgBox('Restore drives from backup dated: ' + BackupDate + '?',
|
|
mbConfirmation, MB_YESNO) = IDNO then
|
|
Exit;
|
|
|
|
ProgressPage.SetText('Restoring drives from backup...', '');
|
|
ProgressPage.SetProgress(50, 100);
|
|
ProgressPage.Show;
|
|
|
|
try
|
|
Results := 'Restoring from backup dated: ' + BackupDate + NL + NL;
|
|
Results := Results + ReconnectDrivesFromBackup(LegacyUsername, CurrentPassword);
|
|
ShowResultsInMemo('Restore Results', Results);
|
|
finally
|
|
ProgressPage.Hide;
|
|
end;
|
|
end;
|
|
|
|
procedure DoAddDrives;
|
|
var
|
|
DriveForm: TSetupForm;
|
|
InfoLabel: TLabel;
|
|
ChkDriveS, ChkDriveV, ChkDriveY: TNewCheckBox;
|
|
OKButton, CancelButton: TNewButton;
|
|
Results: String;
|
|
Script: String;
|
|
Output: String;
|
|
AddS, AddV, AddY: Boolean;
|
|
begin
|
|
if CurrentPassword = '' then
|
|
begin
|
|
MsgBox('Please enter your password first.', mbError, MB_OK);
|
|
Exit;
|
|
end;
|
|
|
|
// Create drive selection dialog
|
|
DriveForm := CreateCustomForm;
|
|
DriveForm.Caption := 'Add Network Drives';
|
|
DriveForm.ClientWidth := 450;
|
|
DriveForm.ClientHeight := 280;
|
|
DriveForm.Position := poScreenCenter;
|
|
|
|
InfoLabel := TLabel.Create(DriveForm);
|
|
InfoLabel.Parent := DriveForm;
|
|
InfoLabel.Left := 20;
|
|
InfoLabel.Top := 20;
|
|
InfoLabel.Width := 410;
|
|
InfoLabel.Height := 40;
|
|
InfoLabel.AutoSize := False;
|
|
InfoLabel.WordWrap := True;
|
|
InfoLabel.Caption := 'Select the network drives you want to add. Existing drives with the same letter will be replaced.';
|
|
|
|
ChkDriveS := TNewCheckBox.Create(DriveForm);
|
|
ChkDriveS.Parent := DriveForm;
|
|
ChkDriveS.Left := 20;
|
|
ChkDriveS.Top := 70;
|
|
ChkDriveS.Width := 410;
|
|
ChkDriveS.Height := 20;
|
|
ChkDriveS.Caption := DRIVE_S_DESC;
|
|
ChkDriveS.Checked := True;
|
|
|
|
ChkDriveV := TNewCheckBox.Create(DriveForm);
|
|
ChkDriveV.Parent := DriveForm;
|
|
ChkDriveV.Left := 20;
|
|
ChkDriveV.Top := 100;
|
|
ChkDriveV.Width := 410;
|
|
ChkDriveV.Height := 20;
|
|
ChkDriveV.Caption := DRIVE_V_DESC;
|
|
ChkDriveV.Checked := False;
|
|
|
|
ChkDriveY := TNewCheckBox.Create(DriveForm);
|
|
ChkDriveY.Parent := DriveForm;
|
|
ChkDriveY.Left := 20;
|
|
ChkDriveY.Top := 130;
|
|
ChkDriveY.Width := 410;
|
|
ChkDriveY.Height := 20;
|
|
ChkDriveY.Caption := DRIVE_Y_DESC;
|
|
ChkDriveY.Checked := False;
|
|
|
|
OKButton := TNewButton.Create(DriveForm);
|
|
OKButton.Parent := DriveForm;
|
|
OKButton.Caption := 'Add Selected Drives';
|
|
OKButton.Width := 130;
|
|
OKButton.Height := 30;
|
|
OKButton.Left := 100;
|
|
OKButton.Top := DriveForm.ClientHeight - 50;
|
|
OKButton.ModalResult := mrOK;
|
|
OKButton.Default := True;
|
|
|
|
CancelButton := TNewButton.Create(DriveForm);
|
|
CancelButton.Parent := DriveForm;
|
|
CancelButton.Caption := 'Cancel';
|
|
CancelButton.Width := 80;
|
|
CancelButton.Height := 30;
|
|
CancelButton.Left := 250;
|
|
CancelButton.Top := DriveForm.ClientHeight - 50;
|
|
CancelButton.ModalResult := mrCancel;
|
|
|
|
if DriveForm.ShowModal = mrOK then
|
|
begin
|
|
AddS := ChkDriveS.Checked;
|
|
AddV := ChkDriveV.Checked;
|
|
AddY := ChkDriveY.Checked;
|
|
DriveForm.Free;
|
|
|
|
if not (AddS or AddV or AddY) then
|
|
begin
|
|
ShowResultsInMemo('Add Drives', 'No drives selected.');
|
|
Exit;
|
|
end;
|
|
|
|
Results := 'Adding network drives...' + #13#10#13#10;
|
|
|
|
// Build PowerShell script using three-phase approach to avoid error 1219
|
|
// when multiple drives point to the same server (e.g., S: and V: both on tsgwp00525)
|
|
Script :=
|
|
'$Username = "' + LegacyUsername + '"' + NL +
|
|
'$Password = "' + CurrentPassword + '"' + NL +
|
|
'$results = @()' + NL +
|
|
'$ManagedServers = @("' + NEW_SERVER_1 + '", "' + DRIVE_Y_SERVER + '")' + NL + NL +
|
|
'# Collect drives to add' + NL +
|
|
'$drivesToAdd = @()' + NL;
|
|
|
|
if AddS then
|
|
Script := Script + '$drivesToAdd += [PSCustomObject]@{ Letter = "S:"; Path = "' + DRIVE_S_PATH + '" }' + NL;
|
|
|
|
if AddV then
|
|
Script := Script + '$drivesToAdd += [PSCustomObject]@{ Letter = "V:"; Path = "' + DRIVE_V_PATH + '" }' + NL;
|
|
|
|
if AddY then
|
|
Script := Script + '$drivesToAdd += [PSCustomObject]@{ Letter = "Y:"; Path = "' + DRIVE_Y_PATH + '" }' + NL;
|
|
|
|
Script := Script + NL +
|
|
'# PHASE 1: Disconnect ALL selected drives first' + NL +
|
|
'$results += "Phase 1: Disconnecting existing drives..."' + NL +
|
|
'foreach ($drive in $drivesToAdd) {' + NL +
|
|
' $null = net use $drive.Letter /delete /y 2>&1' + NL +
|
|
'}' + NL + NL +
|
|
'# PHASE 2: Clear IPC$ connections to prevent error 1219' + NL +
|
|
'$results += "Phase 2: Clearing server connections..."' + NL +
|
|
'foreach ($server in $ManagedServers) {' + NL +
|
|
' $null = net use "\\$server\IPC$" /delete /y 2>&1' + NL +
|
|
' $null = net use "\\$server" /delete /y 2>&1' + NL +
|
|
' $null = cmdkey /delete:$server 2>&1' + NL +
|
|
'}' + NL + NL +
|
|
'# Small delay to let Windows release connections' + NL +
|
|
'Start-Sleep -Seconds 2' + NL + NL +
|
|
'# PHASE 3: Reconnect ALL drives' + NL +
|
|
'$results += "Phase 3: Connecting drives..."' + NL +
|
|
'$results += ""' + NL +
|
|
'foreach ($drive in $drivesToAdd) {' + NL +
|
|
' $output = net use $drive.Letter $drive.Path /user:$Username $Password /persistent:yes 2>&1' + NL +
|
|
' if ($LASTEXITCODE -eq 0) {' + NL +
|
|
' $results += "OK: $($drive.Letter) -> $($drive.Path)"' + NL +
|
|
' } else {' + NL +
|
|
' $errCode = $LASTEXITCODE' + NL +
|
|
' $outStr = $output | Out-String' + NL +
|
|
' if ($outStr -match "System error (\d+)") { $errCode = [int]$Matches[1] }' + NL +
|
|
' $results += "FAILED: $($drive.Letter) -> $($drive.Path) (Error $errCode)"' + NL +
|
|
' }' + NL +
|
|
'}' + NL + NL +
|
|
'$results | ForEach-Object { Write-Output $_ }';
|
|
|
|
ExecPowerShell(Script, Output);
|
|
Results := Results + Output;
|
|
ShowResultsInMemo('Add Drives Results', Results);
|
|
end
|
|
else
|
|
DriveForm.Free;
|
|
end;
|
|
|
|
procedure DoOpenPasswordPortal;
|
|
var
|
|
ErrorCode: Integer;
|
|
begin
|
|
ShellExec('open', PASSWORD_RESET_URL, '', '', SW_SHOW, ewNoWait, ErrorCode);
|
|
ShowResultsInMemo('Password Portal',
|
|
'Browser opened to: ' + PASSWORD_RESET_URL + NL +
|
|
'After resetting your password, wait 10 minutes for' + NL +
|
|
'the change to sync to the legacy domain, then return' + NL +
|
|
'here and enter your new password.');
|
|
end;
|
|
|
|
// ============================================================================
|
|
// MENU CLICK HANDLER
|
|
// ============================================================================
|
|
|
|
procedure MenuListBoxClick(Sender: TObject);
|
|
begin
|
|
SelectedMenuOption := MenuListBox.ItemIndex + 1;
|
|
end;
|
|
|
|
procedure MenuListBoxDblClick(Sender: TObject);
|
|
begin
|
|
SelectedMenuOption := MenuListBox.ItemIndex + 1;
|
|
WizardForm.NextButton.OnClick(WizardForm.NextButton);
|
|
end;
|
|
|
|
// ============================================================================
|
|
// PASSWORD PAGE HANDLING
|
|
// ============================================================================
|
|
|
|
procedure PasswordPageOnChange(Sender: TObject);
|
|
begin
|
|
CurrentPassword := PasswordPage.Values[0];
|
|
end;
|
|
|
|
// ============================================================================
|
|
// WIZARD PAGE CREATION
|
|
// ============================================================================
|
|
|
|
procedure InitializeWizard;
|
|
var
|
|
MenuLabel: TLabel;
|
|
PasswordLabel: TLabel;
|
|
UsernameLabel: TLabel;
|
|
begin
|
|
// Initialize globals
|
|
LegacyUsername := GetLegacyUsername;
|
|
BackupFilePath := GetBackupFilePath;
|
|
CurrentPassword := '';
|
|
SelectedMenuOption := 0;
|
|
|
|
// =========================================
|
|
// Main Menu Page
|
|
// =========================================
|
|
MainMenuPage := CreateCustomPage(wpWelcome,
|
|
'Network Drive Manager',
|
|
'Select an option from the menu below');
|
|
|
|
// Status/info label at top
|
|
StatusLabel := TLabel.Create(MainMenuPage);
|
|
StatusLabel.Parent := MainMenuPage.Surface;
|
|
StatusLabel.Left := 0;
|
|
StatusLabel.Top := 0;
|
|
StatusLabel.Width := MainMenuPage.SurfaceWidth;
|
|
StatusLabel.Height := 50;
|
|
StatusLabel.AutoSize := False;
|
|
StatusLabel.WordWrap := True;
|
|
StatusLabel.Caption := 'User: ' + GetUserNameString + #13#10 +
|
|
'Legacy Account: ' + LegacyUsername + #13#10 +
|
|
'Backup: ' + BackupFilePath;
|
|
|
|
// Menu label
|
|
MenuLabel := TLabel.Create(MainMenuPage);
|
|
MenuLabel.Parent := MainMenuPage.Surface;
|
|
MenuLabel.Left := 0;
|
|
MenuLabel.Top := 60;
|
|
MenuLabel.Caption := 'Select an option:';
|
|
MenuLabel.Font.Style := [fsBold];
|
|
|
|
// Menu list box
|
|
MenuListBox := TNewListBox.Create(MainMenuPage);
|
|
MenuListBox.Parent := MainMenuPage.Surface;
|
|
MenuListBox.Left := 0;
|
|
MenuListBox.Top := 80;
|
|
MenuListBox.Width := MainMenuPage.SurfaceWidth;
|
|
MenuListBox.Height := 180;
|
|
MenuListBox.Items.Add('1. View current network drive mappings');
|
|
MenuListBox.Items.Add('2. Backup current mappings');
|
|
MenuListBox.Items.Add('3. Test legacy credentials');
|
|
MenuListBox.Items.Add('4. Full reset - Disconnect, clear creds, and reconnect');
|
|
MenuListBox.Items.Add('5. Restore drives from backup');
|
|
MenuListBox.Items.Add('6. Add new network drives (S:, V:, Y:)');
|
|
MenuListBox.Items.Add('7. Open password reset portal');
|
|
MenuListBox.Items.Add('8. Quit');
|
|
MenuListBox.ItemIndex := 0;
|
|
MenuListBox.OnClick := @MenuListBoxClick;
|
|
MenuListBox.OnDblClick := @MenuListBoxDblClick;
|
|
|
|
// =========================================
|
|
// Password Page
|
|
// =========================================
|
|
PasswordPage := CreateInputQueryPage(MainMenuPage.ID,
|
|
'Enter Legacy Password',
|
|
'Enter your legacy domain password to continue',
|
|
'This password will be used to test credentials and reconnect drives.' + #13#10 +
|
|
'Your password is not stored permanently.');
|
|
|
|
PasswordPage.Add('Legacy Password:', True);
|
|
PasswordPage.Edits[0].OnChange := @PasswordPageOnChange;
|
|
|
|
// Add username display
|
|
UsernameLabel := TLabel.Create(PasswordPage);
|
|
UsernameLabel.Parent := PasswordPage.Surface;
|
|
UsernameLabel.Left := 0;
|
|
UsernameLabel.Top := 0;
|
|
UsernameLabel.Caption := 'Username: ' + LegacyUsername;
|
|
UsernameLabel.Font.Style := [fsBold];
|
|
|
|
// =========================================
|
|
// Progress Page
|
|
// =========================================
|
|
ProgressPage := CreateOutputProgressPage('Working...', 'Please wait while the operation completes.');
|
|
|
|
// =========================================
|
|
// Results Page (comes after password page)
|
|
// =========================================
|
|
ResultsPage := CreateOutputMsgMemoPage(PasswordPage.ID,
|
|
'Results',
|
|
'Operation complete',
|
|
'Results:',
|
|
'');
|
|
ResultsPage.RichEditViewer.ScrollBars := ssVertical;
|
|
|
|
// =========================================
|
|
// Auto-backup on startup
|
|
// =========================================
|
|
if FileExists(BackupFilePath) then
|
|
begin
|
|
// Backup exists - will ask on first backup operation
|
|
end
|
|
else
|
|
begin
|
|
// No backup - create one automatically
|
|
if BackupDriveMappings then
|
|
StatusLabel.Caption := StatusLabel.Caption + NL + '[Auto-backup created]';
|
|
end;
|
|
end;
|
|
|
|
// ============================================================================
|
|
// PAGE NAVIGATION
|
|
// ============================================================================
|
|
|
|
function ShouldSkipPage(PageID: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
|
|
// Skip password page for operations that don't need it
|
|
if PageID = PasswordPage.ID then
|
|
begin
|
|
case SelectedMenuOption of
|
|
MENU_VIEW_MAPPINGS, MENU_BACKUP, MENU_PASSWORD_PORTAL, MENU_QUIT:
|
|
Result := True;
|
|
// These require password: MENU_TEST_CREDS, MENU_FULL_RESET, MENU_RESTORE, MENU_ADD_DRIVES
|
|
end;
|
|
end;
|
|
|
|
// Always skip results page - we use modal dialogs now
|
|
if PageID = ResultsPage.ID then
|
|
Result := True;
|
|
end;
|
|
|
|
function NextButtonClick(CurPageID: Integer): Boolean;
|
|
begin
|
|
Result := True;
|
|
|
|
if CurPageID = MainMenuPage.ID then
|
|
begin
|
|
SelectedMenuOption := MenuListBox.ItemIndex + 1;
|
|
|
|
// Handle quit immediately
|
|
if SelectedMenuOption = MENU_QUIT then
|
|
begin
|
|
WizardForm.Close;
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
// Handle operations that don't need password - stay on main menu
|
|
case SelectedMenuOption of
|
|
MENU_VIEW_MAPPINGS:
|
|
begin
|
|
DoViewMappings;
|
|
Result := False; // Stay on main menu
|
|
end;
|
|
MENU_BACKUP:
|
|
begin
|
|
DoBackupMappings;
|
|
Result := False; // Stay on main menu
|
|
end;
|
|
MENU_PASSWORD_PORTAL:
|
|
begin
|
|
DoOpenPasswordPortal;
|
|
Result := False; // Stay on main menu
|
|
end;
|
|
end;
|
|
// For other options (3,4,5), proceed to password page
|
|
end
|
|
else if CurPageID = PasswordPage.ID then
|
|
begin
|
|
// Validate password entered
|
|
if CurrentPassword = '' then
|
|
begin
|
|
MsgBox('Please enter your password.', mbError, MB_OK);
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
// Execute the selected operation
|
|
case SelectedMenuOption of
|
|
MENU_TEST_CREDS:
|
|
DoTestCredentials;
|
|
MENU_FULL_RESET:
|
|
DoFullReset;
|
|
MENU_RESTORE:
|
|
DoRestoreFromBackup;
|
|
MENU_ADD_DRIVES:
|
|
DoAddDrives;
|
|
end;
|
|
|
|
// Clear password and go back to main menu
|
|
CurrentPassword := '';
|
|
PasswordPage.Values[0] := '';
|
|
Result := False; // Don't proceed forward
|
|
|
|
// Navigate back to main menu
|
|
WizardForm.BackButton.OnClick(WizardForm.BackButton);
|
|
end;
|
|
end;
|
|
|
|
procedure CurPageChanged(CurPageID: Integer);
|
|
begin
|
|
// Customize button text based on page
|
|
if CurPageID = MainMenuPage.ID then
|
|
begin
|
|
WizardForm.NextButton.Caption := 'Select';
|
|
WizardForm.BackButton.Visible := False;
|
|
WizardForm.CancelButton.Caption := 'Quit';
|
|
end
|
|
else if CurPageID = PasswordPage.ID then
|
|
begin
|
|
WizardForm.NextButton.Caption := 'Continue';
|
|
WizardForm.BackButton.Caption := 'Back';
|
|
WizardForm.BackButton.Visible := True;
|
|
end;
|
|
end;
|