; ============================================================================
; FQDNUpdate - Drive Remapping Tool
; Scans for drives mapped to legacy server names, backs them up,
; clears stale credentials, and remaps using canonical FQDNs with SSO.
; Pure Pascal Script — no PowerShell dependency.
; ============================================================================

[Setup]
AppId={{B1A2C3D4-E5F6-7890-ABCD-EF1234567890}}
AppName=FQDNUpdate
AppVersion=1.0
AppPublisher=WJDT
DefaultDirName={tmp}\FQDNUpdate
CreateAppDir=no
PrivilegesRequired=lowest
OutputDir=Output
OutputBaseFilename=FQDNUpdate
SolidCompression=yes
WizardStyle=modern
SetupIconFile=gea-logo.ico
WizardImageFile=patrick.bmp
WizardSmallImageFile=patrick-sm.bmp
DisableWelcomePage=no
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=yes
Uninstallable=no

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

[Messages]
WelcomeLabel1=FQDNUpdate - Drive Remapping Tool
WelcomeLabel2=This tool will scan your mapped network drives and remap any that point to legacy server names so they use Windows SSO (no password prompt).%n%nWhat it does:%n  1. Scans for drives mapped to tsgwp00525 or avwesj-gwy01%n  2. Backs up your current mappings%n  3. Disconnects and clears stale credentials%n  4. Remaps drives using the canonical server name%n%nNo password will be required — your Windows login is used automatically.%n%nClick Next to begin.
FinishedHeadingLabel=Remapping Complete

[Code]
// ============================================================================
// CONSTANTS AND CONFIGURATION
// ============================================================================

const
  NL = #13#10;

  // Server group 1
  OLD_SERVER_1 = 'tsgwp00525.rd.ds.ge.com';
  NEW_SERVER_1 = 'tsgwp00525.wjs.geaerospace.net';
  SHORT_NAME_1 = 'tsgwp00525';

  // Server group 2
  OLD_SERVER_2 = 'avwesj-gwy01.av.ge.com';
  NEW_SERVER_2 = 'avwesj-gwy01.wjs.geaerospace.net';
  SHORT_NAME_2 = 'avwesj-gwy01';

  SERVER_VARIANT_COUNT = 6;

// ============================================================================
// GLOBAL VARIABLES
// ============================================================================

var
  ProgressPage: TOutputProgressWizardPage;
  LogContent: String;
  FailureCount: Integer;
  BackupPath: String;
  LogPath: String;

  // Parallel arrays for matched drives
  DriveLetters: TArrayOfString;
  RemotePaths: TArrayOfString;
  ShareNames: TArrayOfString;
  CanonicalFQDNs: TArrayOfString;
  DriveCount: Integer;

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

function ExecWithOutput(const Cmd, Params: 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',
       ExpandConstant('{sys}'), 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;

procedure Log(const Msg: String);
begin
  LogContent := LogContent + Msg + NL;
end;

function GetServerVariants: TArrayOfString;
begin
  SetArrayLength(Result, SERVER_VARIANT_COUNT);
  Result[0] := OLD_SERVER_1;
  Result[1] := NEW_SERVER_1;
  Result[2] := SHORT_NAME_1;
  Result[3] := OLD_SERVER_2;
  Result[4] := NEW_SERVER_2;
  Result[5] := SHORT_NAME_2;
end;

function GetCanonicalFQDN(const ServerName: String): String;
var
  Lower: String;
begin
  Lower := Lowercase(ServerName);
  if (Lower = Lowercase(OLD_SERVER_1)) or (Lower = Lowercase(NEW_SERVER_1)) or (Lower = Lowercase(SHORT_NAME_1)) then
    Result := NEW_SERVER_1
  else if (Lower = Lowercase(OLD_SERVER_2)) or (Lower = Lowercase(NEW_SERVER_2)) or (Lower = Lowercase(SHORT_NAME_2)) then
    Result := NEW_SERVER_2
  else
    Result := '';
end;

function ContainsServerVariant(const S: String): Boolean;
var
  Lower: String;
begin
  Lower := Lowercase(S);
  Result := (Pos(Lowercase(OLD_SERVER_1), Lower) > 0) or
            (Pos(Lowercase(NEW_SERVER_1), Lower) > 0) or
            (Pos(Lowercase(SHORT_NAME_1), Lower) > 0) or
            (Pos(Lowercase(OLD_SERVER_2), Lower) > 0) or
            (Pos(Lowercase(NEW_SERVER_2), Lower) > 0) or
            (Pos(Lowercase(SHORT_NAME_2), Lower) > 0);
end;

function ExtractServerFromUNC(const UNCPath: String): String;
var
  S: String;
  SlashPos: Integer;
begin
  Result := '';
  if (Length(UNCPath) > 2) and (UNCPath[1] = '\') and (UNCPath[2] = '\') then
  begin
    S := Copy(UNCPath, 3, Length(UNCPath) - 2);
    SlashPos := Pos('\', S);
    if SlashPos > 0 then
      Result := Copy(S, 1, SlashPos - 1)
    else
      Result := S;
  end;
end;

function ExtractShareFromUNC(const UNCPath: String): String;
var
  S: String;
  SlashPos: Integer;
begin
  Result := '';
  if (Length(UNCPath) > 2) and (UNCPath[1] = '\') and (UNCPath[2] = '\') then
  begin
    S := Copy(UNCPath, 3, Length(UNCPath) - 2);
    SlashPos := Pos('\', S);
    if SlashPos > 0 then
      Result := Copy(S, SlashPos + 1, Length(S) - SlashPos);
  end;
end;

function GetTimestamp: String;
begin
  Result := GetDateTimeString('yyyymmdd_hhnnss', '-', '-');
end;

function GetLogDir: String;
begin
  Result := ExpandConstant('{userdocs}\wjdt\logs');
  ForceDirectories(Result);
end;

// ============================================================================
// PHASE 1: SCAN & BACKUP
// ============================================================================

procedure ScanAndBackup;
var
  NetOutput, Line, DriveLetter, RemotePath, ServerName, ShareName, Canonical: String;
  I, SpacePos, SecondSpace: Integer;
  BackupContent: String;
begin
  Log('=== Phase 1: Scan & Backup ===');
  Log('');
  DriveCount := 0;
  SetArrayLength(DriveLetters, 26);
  SetArrayLength(RemotePaths, 26);
  SetArrayLength(ShareNames, 26);
  SetArrayLength(CanonicalFQDNs, 26);

  ExecWithOutput('net', 'use', NetOutput);

  NetOutput := NetOutput + NL;
  while Length(NetOutput) > 0 do
  begin
    I := Pos(#13#10, NetOutput);
    if I = 0 then I := Pos(#10, NetOutput);
    if I = 0 then
    begin
      Line := NetOutput;
      NetOutput := '';
    end
    else
    begin
      Line := Copy(NetOutput, 1, I - 1);
      if (I < Length(NetOutput)) and (NetOutput[I] = #13) then
        NetOutput := Copy(NetOutput, I + 2, Length(NetOutput))
      else
        NetOutput := Copy(NetOutput, I + 1, Length(NetOutput));
    end;

    if not ContainsServerVariant(Line) then
      Continue;

    DriveLetter := '';
    RemotePath := '';
    for I := 1 to Length(Line) - 1 do
    begin
      if (Line[I] >= 'A') and (Line[I] <= 'Z') and (Line[I+1] = ':') then
      begin
        DriveLetter := Line[I] + ':';
        SpacePos := I + 2;
        while (SpacePos <= Length(Line)) and (Line[SpacePos] = ' ') do
          SpacePos := SpacePos + 1;

        if (SpacePos < Length(Line)) and (Line[SpacePos] = '\') and (Line[SpacePos+1] = '\') then
        begin
          SecondSpace := SpacePos;
          while (SecondSpace <= Length(Line)) and (Line[SecondSpace] <> ' ') do
            SecondSpace := SecondSpace + 1;
          RemotePath := Copy(Line, SpacePos, SecondSpace - SpacePos);
        end;
        Break;
      end;
    end;

    if (DriveLetter <> '') and (RemotePath <> '') then
    begin
      ServerName := ExtractServerFromUNC(RemotePath);
      ShareName := ExtractShareFromUNC(RemotePath);
      Canonical := GetCanonicalFQDN(ServerName);

      if Canonical <> '' then
      begin
        DriveLetters[DriveCount] := DriveLetter;
        RemotePaths[DriveCount] := RemotePath;
        ShareNames[DriveCount] := ShareName;
        CanonicalFQDNs[DriveCount] := Canonical;
        DriveCount := DriveCount + 1;
      end;
    end;
  end;

  if DriveCount = 0 then
  begin
    Log('No drives found mapped to target servers.');
    Exit;
  end;

  Log('Found ' + IntToStr(DriveCount) + ' drive(s):');
  for I := 0 to DriveCount - 1 do
    Log('  ' + DriveLetters[I] + ' -> ' + RemotePaths[I]);

  BackupContent := '';
  for I := 0 to DriveCount - 1 do
    BackupContent := BackupContent + DriveLetters[I] + '|' + RemotePaths[I] + NL;

  BackupPath := GetLogDir + '\fqdnupdate_' + GetTimestamp + '.txt';
  SaveStringToFile(BackupPath, BackupContent, False);
  Log('');
  Log('Backup saved to ' + BackupPath);
end;

// ============================================================================
// PHASE 2: DISCONNECT & CLEAR
// ============================================================================

procedure DisconnectAndClear;
var
  Output: String;
  Variants: TArrayOfString;
  I: Integer;
begin
  Log('');
  Log('=== Phase 2: Disconnect & Clear ===');
  Log('');
  Variants := GetServerVariants;

  for I := 0 to DriveCount - 1 do
  begin
    ExecWithOutput('net', 'use ' + DriveLetters[I] + ' /delete /y', Output);
    Log('  Disconnected ' + DriveLetters[I]);
  end;

  for I := 0 to GetArrayLength(Variants) - 1 do
  begin
    ExecWithOutput('net', 'use "\\' + Variants[I] + '\IPC$" /delete /y', Output);
    ExecWithOutput('net', 'use "\\' + Variants[I] + '" /delete /y', Output);
  end;
  Log('  Cleared IPC$/server connections');

  for I := 0 to GetArrayLength(Variants) - 1 do
  begin
    ExecWithOutput('cmdkey', '/delete:' + Variants[I], Output);
    ExecWithOutput('cmdkey', '/delete:"Domain:target=' + Variants[I] + '"', Output);
  end;
  Log('  Cleared cached credentials');

  ExecWithOutput('klist', 'purge', Output);
  Log('  Purged Kerberos ticket cache');

  Sleep(2000);
  Log('  Waited 2 seconds for connections to release');
end;

// ============================================================================
// PHASE 3: REMAP WITH SSO
// ============================================================================

procedure RemapDrives;
var
  Output, NewPath: String;
  ExitCode, I: Integer;
begin
  Log('');
  Log('=== Phase 3: Remap with SSO ===');
  Log('');
  FailureCount := 0;

  for I := 0 to DriveCount - 1 do
  begin
    NewPath := '\\' + CanonicalFQDNs[I] + '\' + ShareNames[I];
    ExitCode := ExecWithOutput('net', 'use ' + DriveLetters[I] + ' "' + NewPath + '" /persistent:yes', Output);

    if ExitCode = 0 then
      Log('  OK:     ' + DriveLetters[I] + ' -> ' + NewPath)
    else
    begin
      Log('  FAILED: ' + DriveLetters[I] + ' -> ' + NewPath + ' (Error ' + IntToStr(ExitCode) + ')');
      FailureCount := FailureCount + 1;
    end;
  end;

  Log('');
  Log('=== Complete ===');
end;

// ============================================================================
// WIZARD PAGE CREATION
// ============================================================================

procedure InitializeWizard;
begin
  ProgressPage := CreateOutputProgressPage('Remapping Drives', 'Please wait while your drives are remapped...');
end;

// ============================================================================
// PAGE NAVIGATION
// ============================================================================

function NextButtonClick(CurPageID: Integer): Boolean;
begin
  Result := True;

  if CurPageID = wpWelcome then
  begin
    LogContent := '';
    FailureCount := 0;
    Log('FQDNUpdate Log - ' + GetDateTimeString('yyyy-mm-dd hh:nn:ss', '-', ':'));
    Log('User: ' + GetUserNameString + '  Computer: ' + ExpandConstant('{computername}'));
    Log('');

    ProgressPage.Show;
    try
      // Phase 1: Scan & Backup
      ProgressPage.SetText('Scanning for target drives...', '');
      ProgressPage.SetProgress(10, 100);
      ScanAndBackup;

      if DriveCount > 0 then
      begin
        // Phase 2: Disconnect & Clear
        ProgressPage.SetText('Disconnecting drives and clearing credentials...', '');
        ProgressPage.SetProgress(30, 100);
        DisconnectAndClear;

        // Phase 3: Remap
        ProgressPage.SetText('Remapping drives with SSO...', '');
        ProgressPage.SetProgress(70, 100);
        RemapDrives;
      end;

      ProgressPage.SetProgress(100, 100);
    finally
      ProgressPage.Hide;
    end;

    // Save log
    LogPath := GetLogDir + '\fqdnupdate_' + GetTimestamp + '.log';
    SaveStringToFile(LogPath, LogContent, False);

    // Update finished page text dynamically
    if DriveCount = 0 then
      WizardForm.FinishedLabel.Caption :=
        'No drives were found mapped to target servers.' + NL + NL +
        'Nothing was changed.' + NL + NL +
        'Log saved to:' + NL + LogPath
    else if FailureCount = 0 then
      WizardForm.FinishedLabel.Caption :=
        'All ' + IntToStr(DriveCount) + ' drive(s) were successfully remapped to use Windows SSO.' + NL + NL +
        'Backup: ' + BackupPath + NL +
        'Log: ' + LogPath
    else
      WizardForm.FinishedLabel.Caption :=
        IntToStr(DriveCount - FailureCount) + ' of ' + IntToStr(DriveCount) + ' drive(s) remapped successfully.' + NL +
        IntToStr(FailureCount) + ' drive(s) failed — check the log for details.' + NL + NL +
        'Backup: ' + BackupPath + NL +
        'Log: ' + LogPath;
  end;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
begin
  Result := '';
end;
