1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/forms/SiteAdvanced.cpp
Martin Prikryl ad7deda14d Removing support for SSL 3.0
It does not work after upgrade to OpenSSL 3. While it should still be supported (by OpenSSL), it's not safe and noone uses it according to the usage statistics, so unless many complain, it's not worth trying to fix it.

Only downsize is that currently the connection times out, instead of failing gracefully.

Source commit: 4aeed919bdb18c7d58d5ec8ba1c6b21adbee5167
2023-05-17 01:13:48 +02:00

1870 lines
65 KiB
C++

//---------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <StrUtils.hpp>
#include <CoreMain.h>
#include <Common.h>
#include <TextsWin.h>
#include <TextsCore.h>
#include <HelpCore.h>
#include <HelpWin.h>
#include <VCLCommon.h>
#include <Cryptography.h>
#include <S3FileSystem.h>
#include "WinInterface.h"
#include "SiteAdvanced.h"
#include "GUITools.h"
#include "Tools.h"
#include "WinConfiguration.h"
#include "PuttyTools.h"
#include "TerminalManager.h"
#include "Authenticate.h"
//---------------------------------------------------------------------
#pragma link "ComboEdit"
#pragma link "PasswordEdit"
#pragma link "UpDownEdit"
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
// Sheet tag:
// 01 top, 02 indented
//---------------------------------------------------------------------------
bool __fastcall DoSiteAdvancedDialog(TSessionData * SessionData)
{
TSiteAdvancedDialog * SiteAdvancedDialog = new TSiteAdvancedDialog(Application);
bool Result;
try
{
Result = SiteAdvancedDialog->Execute(SessionData);
}
__finally
{
delete SiteAdvancedDialog;
}
return Result;
}
//---------------------------------------------------------------------
static const UnicodeString DefaultRecycleBinPath = L"/tmp";
//---------------------------------------------------------------------
__fastcall TSiteAdvancedDialog::TSiteAdvancedDialog(TComponent * AOwner) :
TForm(AOwner)
{
NoUpdate = 0;
FKeyHasCertificate = false;
// we need to make sure that window procedure is set asap
// (so that CM_SHOWINGCHANGED handling is applied)
UseSystemSettings(this);
InitControls();
PageControl->ActivePage = EnvironmentSheet;
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::InitControls()
{
ComboAutoSwitchInitialize(UtfCombo);
ComboAutoSwitchInitialize(BugHMAC2Combo);
ComboAutoSwitchInitialize(BugDeriveKey2Combo);
ComboAutoSwitchInitialize(BugRSAPad2Combo);
ComboAutoSwitchInitialize(BugPKSessID2Combo);
ComboAutoSwitchInitialize(BugRekey2Combo);
ComboAutoSwitchInitialize(BugMaxPkt2Combo);
ComboAutoSwitchInitialize(BugIgnore2Combo);
ComboAutoSwitchInitialize(BugWinAdjCombo);
ComboAutoSwitchInitialize(SFTPRealPathCombo);
ComboAutoSwitchInitialize(SFTPBugSymlinkCombo);
ComboAutoSwitchInitialize(SFTPBugSignedTSCombo);
ComboAutoSwitchInitialize(FtpListAllCombo);
ComboAutoSwitchInitialize(FtpUseMlsdCombo);
ComboAutoSwitchInitialize(FtpForcePasvIpCombo);
ComboAutoSwitchInitialize(FtpHostCombo);
TunnelLocalPortNumberEdit->Items->BeginUpdate();
try
{
UnicodeString TunnelLocalPortNumberAutoassign = TunnelLocalPortNumberEdit->Items->Strings[0];
TunnelLocalPortNumberEdit->Items->Clear();
TunnelLocalPortNumberEdit->Items->Add(TunnelLocalPortNumberAutoassign);
for (int Index = Configuration->TunnelLocalPortNumberLow;
Index <= Configuration->TunnelLocalPortNumberHigh; Index++)
{
TunnelLocalPortNumberEdit->Items->Add(IntToStr(Index));
}
}
__finally
{
TunnelLocalPortNumberEdit->Items->EndUpdate();
}
HintLabel(ProxyTelnetCommandHintText, LoadStr(LOGIN_PROXY_COMMAND_PATTERNS_HINT));
HintLabel(ProxyLocalCommandHintText, LoadStr(LOGIN_PROXY_COMMAND_PATTERNS_HINT));
UpdateNavigationTree();
SelectScaledImageList(ColorImageList);
FColor = TColor();
MenuButton(PrivateKeyToolsButton);
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::LoadSession()
{
{
TAutoNestingCounter NoUpdateCounter(NoUpdate);
// Directories page
SynchronizeBrowsingCheck->Checked = FSessionData->SynchronizeBrowsing;
LocalDirectoryEdit->Text = FSessionData->LocalDirectory;
RemoteDirectoryEdit->Text = FSessionData->RemoteDirectory;
UpdateDirectoriesCheck->Checked = FSessionData->UpdateDirectories;
CacheDirectoriesCheck->Checked = FSessionData->CacheDirectories;
CacheDirectoryChangesCheck->Checked = FSessionData->CacheDirectoryChanges;
PreserveDirectoryChangesCheck->Checked = FSessionData->PreserveDirectoryChanges;
ResolveSymlinksCheck->Checked = FSessionData->ResolveSymlinks;
FollowDirectorySymlinksCheck->Checked = FSessionData->FollowDirectorySymlinks;
// Environment page
switch (FSessionData->DSTMode)
{
case dstmWin:
DSTModeWinCheck->Checked = true;
break;
case dstmKeep:
DSTModeKeepCheck->Checked = true;
break;
default:
case dstmUnix:
DSTModeUnixCheck->Checked = true;
break;
}
if (FSessionData->EOLType == eolLF)
{
EOLTypeCombo->ItemIndex = 0;
}
else
{
EOLTypeCombo->ItemIndex = 1;
}
switch (FSessionData->NotUtf)
{
case asOn:
UtfCombo->ItemIndex = 1;
break;
case asOff:
UtfCombo->ItemIndex = 2;
break;
default:
UtfCombo->ItemIndex = 0;
break;
}
int TimeDifferenceMin = TimeToMinutes(FSessionData->TimeDifference);
TimeDifferenceEdit->AsInteger = TimeDifferenceMin / MinsPerHour;
TimeDifferenceMinutesEdit->AsInteger = TimeDifferenceMin % MinsPerHour;
TimeDifferenceAutoCheck->Checked = FSessionData->TimeDifferenceAuto;
TrimVMSVersionsCheck->Checked = FSessionData->TrimVMSVersions;
VMSAllRevisionsCheck->Checked = FSessionData->VMSAllRevisions;
PuttySettingsEdit->Text = FSessionData->PuttySettings;
// Environment/Recycle bin page
DeleteToRecycleBinCheck->Checked = FSessionData->DeleteToRecycleBin;
OverwrittenToRecycleBinCheck->Checked = FSessionData->OverwrittenToRecycleBin;
RecycleBinPathEdit->Text =
!FSessionData->RecycleBinPath.IsEmpty() ?
FSessionData->RecycleBinPath : DefaultRecycleBinPath;
// SFTP page
if (FSessionData->SftpServer.IsEmpty())
{
SftpServerEdit->Text = SftpServerEdit->Items->Strings[0];
}
else
{
SftpServerEdit->Text = FSessionData->SftpServer;
}
// hide selection, which is wrongly shown initially even when the box has not focus
SftpServerEdit->SelLength = 0;
AllowScpFallbackCheck->Checked = (FSessionData->FSProtocol == fsSFTP);
SFTPMaxVersionCombo->ItemIndex = FSessionData->SFTPMaxVersion;
ComboAutoSwitchLoad(SFTPRealPathCombo, FSessionData->SFTPRealPath);
#define LOAD_SFTP_BUG_COMBO(BUG) \
ComboAutoSwitchLoad(SFTPBug ## BUG ## Combo, FSessionData->SFTPBug[sb ## BUG])
LOAD_SFTP_BUG_COMBO(Symlink);
LOAD_SFTP_BUG_COMBO(SignedTS);
#undef LOAD_SFTP_BUG_COMBO
// FTP page
FtpAccountEdit->Text = FSessionData->FtpAccount;
PostLoginCommandsMemo->Lines->Text = FSessionData->PostLoginCommands;
ComboAutoSwitchLoad(FtpListAllCombo, FSessionData->FtpListAll);
ComboAutoSwitchLoad(FtpUseMlsdCombo, FSessionData->FtpUseMlsd);
ComboAutoSwitchLoad(FtpForcePasvIpCombo, FSessionData->FtpForcePasvIp);
ComboAutoSwitchLoad(FtpHostCombo, FSessionData->FtpHost);
// S3 page
S3DefaultReqionCombo->Text = FSessionData->S3DefaultRegion;
if (FSessionData->S3UrlStyle == s3usPath)
{
S3UrlStyleCombo->ItemIndex = 1;
}
else
{
S3UrlStyleCombo->ItemIndex = 0;
}
UnicodeString S3SessionToken = FSessionData->S3SessionToken;
if (FSessionData->HasAutoCredentials())
{
try
{
S3SessionToken = S3EnvSessionToken(FSessionData->S3Profile);
}
catch (...)
{
// noop
}
}
S3SessionTokenMemo->Lines->Text = S3SessionToken;
// Authentication page
SshNoUserAuthCheck->Checked = FSessionData->SshNoUserAuth;
TryAgentCheck->Checked = FSessionData->TryAgent;
AuthKICheck->Checked = FSessionData->AuthKI;
AuthKIPasswordCheck->Checked = FSessionData->AuthKIPassword;
AuthGSSAPICheck3->Checked = FSessionData->AuthGSSAPI;
GSSAPIFwdTGTCheck->Checked = FSessionData->GSSAPIFwdTGT;
AgentFwdCheck->Checked = FSessionData->AgentFwd;
PrivateKeyEdit3->Text = FSessionData->PublicKeyFile;
DetachedCertificateEdit->Text = FSessionData->DetachedCertificate;
// SSH page
Ssh2LegacyDESCheck->Checked = FSessionData->Ssh2DES;
CompressionCheck->Checked = FSessionData->Compression;
CipherListBox->Items->Clear();
DebugAssert(CIPHER_NAME_WARN+CIPHER_COUNT-1 == CIPHER_NAME_AESGCM);
for (int Index = 0; Index < CIPHER_COUNT; Index++)
{
CipherListBox->Items->AddObject(
LoadStr(CIPHER_NAME_WARN+FSessionData->Cipher[Index]),
(TObject*)FSessionData->Cipher[Index]);
}
// KEX page
KexListBox->Items->Clear();
DebugAssert(KEX_NAME_WARN + KEX_COUNT - 1 == KEX_NAME_NTRU_HYBRID);
for (int Index = 0; Index < KEX_COUNT; Index++)
{
KexListBox->Items->AddObject(
LoadStr(KEX_NAME_WARN+FSessionData->Kex[Index]),
(TObject*)FSessionData->Kex[Index]);
}
AuthGSSAPIKEXCheck->Checked = FSessionData->AuthGSSAPIKEX;
RekeyTimeEdit->AsInteger = FSessionData->RekeyTime;
RekeyDataEdit->Text = FSessionData->RekeyData;
// Connection page
FtpPasvModeCheck->Checked = FSessionData->FtpPasvMode;
BufferSizeCheck->Checked = (FSessionData->SendBuf > 0) && FSessionData->SshSimple;
switch (FSessionData->PingType)
{
case ptNullPacket:
PingNullPacketButton->Checked = true;
break;
case ptDummyCommand:
PingDummyCommandButton->Checked = true;
break;
default:
PingOffButton->Checked = true;
break;
}
PingIntervalSecEdit->AsInteger = FSessionData->PingInterval;
switch (FSessionData->FtpPingType)
{
case ptDummyCommand:
FtpPingDummyCommandButton->Checked = true;
break;
default:
FtpPingOffButton->Checked = true;
break;
}
FtpPingIntervalSecEdit->AsInteger = FSessionData->FtpPingInterval;
TimeoutEdit->AsInteger = FSessionData->Timeout;
switch (FSessionData->AddressFamily)
{
case afIPv4:
IPv4Button->Checked = true;
break;
case afIPv6:
IPv6Button->Checked = true;
break;
case afAuto:
default:
IPAutoButton->Checked = true;
break;
}
// Shell page
if (FSessionData->DefaultShell)
{
ShellEdit->Text = ShellEdit->Items->Strings[0];
}
else
{
ShellEdit->Text = FSessionData->Shell;
}
// hide selection, which is wrongly shown initially even when the box has not focus
ShellEdit->SelLength = 0;
if (FSessionData->DetectReturnVar)
{
ReturnVarEdit->Text = ReturnVarEdit->Items->Strings[0];
}
else
{
ReturnVarEdit->Text = FSessionData->ReturnVar;
}
ReturnVarEdit->SelLength = 0;
ListingCommandEdit->Text = FSessionData->ListingCommand;
ListingCommandEdit->SelLength = 0;
CheckBoxAutoSwitchLoad(LookupUserGroupsCheck, FSessionData->LookupUserGroups);
ClearAliasesCheck->Checked = FSessionData->ClearAliases;
IgnoreLsWarningsCheck->Checked = FSessionData->IgnoreLsWarnings;
Scp1CompatibilityCheck->Checked = FSessionData->Scp1Compatibility;
UnsetNationalVarsCheck->Checked = FSessionData->UnsetNationalVars;
SCPLsFullTimeAutoCheck->Checked = (FSessionData->SCPLsFullTime != asOff);
// Proxy page
SshProxyMethodCombo->ItemIndex = FSessionData->ProxyMethod;
FtpProxyMethodCombo->ItemIndex = GetSupportedFtpProxyMethod(FSessionData->ProxyMethod);
if (FSessionData->FtpProxyLogonType != 0)
{
FtpProxyMethodCombo->ItemIndex = LastSupportedFtpProxyMethod() + FSessionData->FtpProxyLogonType;
}
NeonProxyMethodCombo->ItemIndex = GetSupportedNeonProxyMethod(FSessionData->ProxyMethod);
ProxyHostEdit->Text = FSessionData->ProxyHost;
ProxyPortEdit->AsInteger = FSessionData->ProxyPort;
ProxyUsernameEdit->Text = FSessionData->ProxyUsername;
ProxyPasswordEdit->Text = FSessionData->ProxyPassword;
ProxyTelnetCommandEdit->Text = FSessionData->ProxyTelnetCommand;
ProxyLocalCommandEdit->Text = FSessionData->ProxyLocalCommand;
ProxyLocalhostCheck->Checked = FSessionData->ProxyLocalhost;
ProxyDNSCombo->ItemIndex = 2 - FSessionData->ProxyDNS;
// Bugs page
#define LOAD_BUG_COMBO(BUG) \
ComboAutoSwitchLoad(Bug ## BUG ## Combo, FSessionData->Bug[sb ## BUG])
LOAD_BUG_COMBO(HMAC2);
LOAD_BUG_COMBO(DeriveKey2);
LOAD_BUG_COMBO(RSAPad2);
LOAD_BUG_COMBO(PKSessID2);
LOAD_BUG_COMBO(Rekey2);
LOAD_BUG_COMBO(MaxPkt2);
LOAD_BUG_COMBO(Ignore2);
LOAD_BUG_COMBO(WinAdj);
#undef LOAD_BUG_COMBO
// Tunnel page
TunnelCheck->Checked = FSessionData->Tunnel;
TunnelUserNameEdit->Text = FSessionData->TunnelUserName;
TunnelPortNumberEdit->AsInteger = FSessionData->TunnelPortNumber;
TunnelHostNameEdit->Text = FSessionData->TunnelHostName;
TunnelPasswordEdit->Text = FSessionData->TunnelPassword;
TunnelPrivateKeyEdit3->Text = FSessionData->TunnelPublicKeyFile;
if (FSessionData->TunnelAutoassignLocalPortNumber)
{
TunnelLocalPortNumberEdit->Text = TunnelLocalPortNumberEdit->Items->Strings[0];
}
else
{
TunnelLocalPortNumberEdit->Text = IntToStr(FSessionData->TunnelLocalPortNumber);
}
// hide selection, which is wrongly shown initially even when the box has not focus
TunnelLocalPortNumberEdit->SelLength = 0;
// connection/tls/ssl page
MinTlsVersionCombo->ItemIndex = TlsVersionToIndex(FSessionData->MinTlsVersion);
MaxTlsVersionCombo->ItemIndex = TlsVersionToIndex(FSessionData->MaxTlsVersion);
SslSessionReuseCheck2->Checked = FSessionData->SslSessionReuse;
TlsCertificateFileEdit->Text = FSessionData->TlsCertificateFile;
// Note page
NoteMemo->Lines->Text = FSessionData->Note;
// Encryption page
EncryptFilesCheck->Checked = !FSessionData->EncryptKey.IsEmpty();
GetEncryptKeyEdit()->Text = FSessionData->EncryptKey;
// webdav page
WebDavLiberalEscapingCheck->Checked = FSessionData->WebDavLiberalEscaping;
// color
FColor = (TColor)FSessionData->Color;
}
EnableControl(PuttyGroup, !DoesSessionExistInPutty(FSessionData->StorageKey));
UpdateControls();
}
//---------------------------------------------------------------------
bool TSiteAdvancedDialog::IsDefaultSftpServer()
{
return
SftpServerEdit->Text.IsEmpty() ||
(SftpServerEdit->Text == SftpServerEdit->Items->Strings[0]);
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::SaveSession(TSessionData * SessionData)
{
// Last resort validation,
// in case the test in EncryptKeyEditExit was previously bypassed due to IsCancelButtonBeingClicked
EncryptKeyEditExit(GetEncryptKeyEdit());
// SSH page
SessionData->Compression = CompressionCheck->Checked;
SessionData->Ssh2DES = Ssh2LegacyDESCheck->Checked;
for (int Index = 0; Index < CIPHER_COUNT; Index++)
{
SessionData->Cipher[Index] = (TCipher)CipherListBox->Items->Objects[Index];
}
// Kex page
for (int Index = 0; Index < KEX_COUNT; Index++)
{
SessionData->Kex[Index] = (TKex)KexListBox->Items->Objects[Index];
}
FSessionData->AuthGSSAPIKEX = AuthGSSAPIKEXCheck->Checked;
SessionData->RekeyTime = RekeyTimeEdit->AsInteger;
SessionData->RekeyData = RekeyDataEdit->Text;
// Authentication page
SessionData->SshNoUserAuth = SshNoUserAuthCheck->Checked;
SessionData->TryAgent = TryAgentCheck->Checked;
SessionData->AuthKI = AuthKICheck->Checked;
SessionData->AuthKIPassword = AuthKIPasswordCheck->Checked;
SessionData->AuthGSSAPI = AuthGSSAPICheck3->Checked;
SessionData->GSSAPIFwdTGT = GSSAPIFwdTGTCheck->Checked;
SessionData->AgentFwd = AgentFwdCheck->Checked;
SessionData->PublicKeyFile = PrivateKeyEdit3->Text;
// When user selectes key with certificate, s/he cannot clear the certificate box anymore as it is disabled,
// so let's not save it, in case it causes troubles in PuTTY code.
SessionData->DetachedCertificate = !FKeyHasCertificate ? DetachedCertificateEdit->Text : EmptyStr;
// Connection page
SessionData->FtpPasvMode = FtpPasvModeCheck->Checked;
SessionData->SendBuf = BufferSizeCheck->Checked ? DefaultSendBuf : 0;
SessionData->SshSimple = BufferSizeCheck->Checked;
if (PingNullPacketButton->Checked)
{
SessionData->PingType = ptNullPacket;
}
else if (PingDummyCommandButton->Checked)
{
SessionData->PingType = ptDummyCommand;
}
else
{
SessionData->PingType = ptOff;
}
SessionData->PingInterval = PingIntervalSecEdit->AsInteger;
SessionData->FtpPingType = (FtpPingDummyCommandButton->Checked ? ptDummyCommand : ptOff);
SessionData->FtpPingInterval = FtpPingIntervalSecEdit->AsInteger;
SessionData->Timeout = TimeoutEdit->AsInteger;
if (IPv4Button->Checked)
{
SessionData->AddressFamily = afIPv4;
}
else if (IPv6Button->Checked)
{
SessionData->AddressFamily = afIPv6;
}
else
{
SessionData->AddressFamily = afAuto;
}
// Directories page
SessionData->SynchronizeBrowsing = SynchronizeBrowsingCheck->Checked;
SessionData->LocalDirectory = LocalDirectoryEdit->Text;
SessionData->RemoteDirectory = RemoteDirectoryEdit->Text;
SessionData->UpdateDirectories = UpdateDirectoriesCheck->Checked;
SessionData->CacheDirectories = CacheDirectoriesCheck->Checked;
SessionData->CacheDirectoryChanges = CacheDirectoryChangesCheck->Checked;
SessionData->PreserveDirectoryChanges = PreserveDirectoryChangesCheck->Checked;
SessionData->ResolveSymlinks = ResolveSymlinksCheck->Checked;
SessionData->FollowDirectorySymlinks = FollowDirectorySymlinksCheck->Checked;
// Environment page
if (DSTModeUnixCheck->Checked)
{
SessionData->DSTMode = dstmUnix;
}
else if (DSTModeKeepCheck->Checked)
{
SessionData->DSTMode = dstmKeep;
}
else
{
SessionData->DSTMode = dstmWin;
}
if (EOLTypeCombo->ItemIndex == 0)
{
SessionData->EOLType = eolLF;
}
else
{
SessionData->EOLType = eolCRLF;
}
switch (UtfCombo->ItemIndex)
{
case 1:
SessionData->NotUtf = asOn;
break;
case 2:
SessionData->NotUtf = asOff;
break;
default:
SessionData->NotUtf = asAuto;
break;
}
SessionData->TimeDifference =
(double(TimeDifferenceEdit->AsInteger) / HoursPerDay) +
(double(TimeDifferenceMinutesEdit->AsInteger) / MinsPerDay);
SessionData->TimeDifferenceAuto = TimeDifferenceAutoCheck->Checked;
SessionData->TrimVMSVersions = TrimVMSVersionsCheck->Checked;
SessionData->VMSAllRevisions = VMSAllRevisionsCheck->Checked;
SessionData->PuttySettings = PuttySettingsEdit->Text;
// Environment/Recycle bin page
SessionData->DeleteToRecycleBin = DeleteToRecycleBinCheck->Checked;
SessionData->OverwrittenToRecycleBin = OverwrittenToRecycleBinCheck->Checked;
SessionData->RecycleBinPath =
SessionData->DeleteToRecycleBin || SessionData->OverwrittenToRecycleBin ||
(RecycleBinPathEdit->Text != DefaultRecycleBinPath) ?
RecycleBinPathEdit->Text : UnicodeString();
// SCP page
SessionData->DefaultShell = (ShellEdit->Text == ShellEdit->Items->Strings[0]);
SessionData->Shell = (SessionData->DefaultShell ? UnicodeString() : ShellEdit->Text);
SessionData->DetectReturnVar = (ReturnVarEdit->Text == ReturnVarEdit->Items->Strings[0]);
SessionData->ReturnVar = (SessionData->DetectReturnVar ? UnicodeString() : ReturnVarEdit->Text);
SessionData->ListingCommand = ListingCommandEdit->Text;
SessionData->LookupUserGroups = CheckBoxAutoSwitchSave(LookupUserGroupsCheck);
SessionData->ClearAliases = ClearAliasesCheck->Checked;
SessionData->IgnoreLsWarnings = IgnoreLsWarningsCheck->Checked;
SessionData->Scp1Compatibility = Scp1CompatibilityCheck->Checked;
SessionData->UnsetNationalVars = UnsetNationalVarsCheck->Checked;
SessionData->SCPLsFullTime = SCPLsFullTimeAutoCheck->Checked ? asAuto : asOff;
// SFTP page
SessionData->SftpServer = (IsDefaultSftpServer() ? UnicodeString() : SftpServerEdit->Text);
SessionData->SFTPMaxVersion = SFTPMaxVersionCombo->ItemIndex;
if (AllowScpFallbackCheck->Checked != (SessionData->FSProtocol == fsSFTP))
{
if (AllowScpFallbackCheck->Checked)
{
DebugAssert(SessionData->FSProtocol == fsSFTPonly);
SessionData->FSProtocol = fsSFTP;
}
else
{
DebugAssert(SessionData->FSProtocol == fsSFTP);
SessionData->FSProtocol = fsSFTPonly;
}
}
FSessionData->SFTPRealPath = ComboAutoSwitchSave(SFTPRealPathCombo);
#define SAVE_SFTP_BUG_COMBO(BUG) SessionData->SFTPBug[sb ## BUG] = ComboAutoSwitchSave(SFTPBug ## BUG ## Combo);
SAVE_SFTP_BUG_COMBO(Symlink);
SAVE_SFTP_BUG_COMBO(SignedTS);
#undef SAVE_SFTP_BUG_COMBO
// FTP page
SessionData->FtpAccount = FtpAccountEdit->Text;
SessionData->PostLoginCommands = PostLoginCommandsMemo->Lines->Text;
SessionData->FtpListAll = ComboAutoSwitchSave(FtpListAllCombo);
SessionData->FtpUseMlsd = ComboAutoSwitchSave(FtpUseMlsdCombo);
SessionData->FtpForcePasvIp = ComboAutoSwitchSave(FtpForcePasvIpCombo);
SessionData->FtpHost = ComboAutoSwitchSave(FtpHostCombo);
// S3 page
SessionData->S3DefaultRegion = S3DefaultReqionCombo->Text;
if (S3UrlStyleCombo->ItemIndex == 1)
{
SessionData->S3UrlStyle = s3usPath;
}
else
{
SessionData->S3UrlStyle = s3usVirtualHost;
}
if (SessionData->HasAutoCredentials())
{
SessionData->S3SessionToken = EmptyStr;
}
else
{
// Trim not to try to authenticate with a stray new-line
SessionData->S3SessionToken = S3SessionTokenMemo->Lines->Text.Trim();
}
// Proxy page
SessionData->ProxyMethod = GetProxyMethod();
SessionData->FtpProxyLogonType = GetFtpProxyLogonType();
SessionData->ProxyHost = ProxyHostEdit->Text;
SessionData->ProxyPort = ProxyPortEdit->AsInteger;
SessionData->ProxyUsername = ProxyUsernameEdit->Text;
SessionData->ProxyPassword = ProxyPasswordEdit->Text;
SessionData->ProxyTelnetCommand = ProxyTelnetCommandEdit->Text;
SessionData->ProxyLocalCommand = ProxyLocalCommandEdit->Text;
SessionData->ProxyLocalhost = ProxyLocalhostCheck->Checked;
SessionData->ProxyDNS = (TAutoSwitch)(2 - ProxyDNSCombo->ItemIndex);
// Bugs page
#define SAVE_BUG_COMBO(BUG) SessionData->Bug[sb ## BUG] = ComboAutoSwitchSave(Bug ## BUG ## Combo)
SAVE_BUG_COMBO(HMAC2);
SAVE_BUG_COMBO(DeriveKey2);
SAVE_BUG_COMBO(RSAPad2);
SAVE_BUG_COMBO(PKSessID2);
SAVE_BUG_COMBO(Rekey2);
SAVE_BUG_COMBO(MaxPkt2);
SAVE_BUG_COMBO(Ignore2);
SAVE_BUG_COMBO(WinAdj);
#undef SAVE_BUG_COMBO
// Tunnel page
SessionData->Tunnel = TunnelCheck->Checked;
SessionData->TunnelUserName = TunnelUserNameEdit->Text;
SessionData->TunnelPortNumber = TunnelPortNumberEdit->AsInteger;
SessionData->TunnelHostName = TunnelHostNameEdit->Text;
SessionData->TunnelPassword = TunnelPasswordEdit->Text;
SessionData->TunnelPublicKeyFile = TunnelPrivateKeyEdit3->Text;
if (TunnelLocalPortNumberEdit->Text == TunnelLocalPortNumberEdit->Items->Strings[0])
{
SessionData->TunnelLocalPortNumber = 0;
}
else
{
SessionData->TunnelLocalPortNumber = StrToIntDef(TunnelLocalPortNumberEdit->Text, 0);
}
// connection/tls/ssl page
SessionData->MinTlsVersion = IndexToTlsVersion(MinTlsVersionCombo->ItemIndex);
SessionData->MaxTlsVersion = IndexToTlsVersion(MaxTlsVersionCombo->ItemIndex);
SessionData->SslSessionReuse = SslSessionReuseCheck2->Checked;
SessionData->TlsCertificateFile = TlsCertificateFileEdit->Text;
// Note page
SessionData->Note = NoteMemo->Lines->Text;
// Encryption page
SessionData->EncryptKey = EncryptFilesCheck->Checked ? GetEncryptKeyEdit()->Text : UnicodeString();
// webdav page
SessionData->WebDavLiberalEscaping = WebDavLiberalEscapingCheck->Checked;
// color
SessionData->Color = FColor;
}
//---------------------------------------------------------------------
TSessionData * __fastcall TSiteAdvancedDialog::GetSessionData()
{
std::unique_ptr<TSessionData> SessionData(new TSessionData(UnicodeString()));
SessionData->Assign(FSessionData);
SaveSession(SessionData.get());
return SessionData.release();
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::UpdateNavigationTree()
{
TTreeNode * ActiveNode = NULL;
{
TAutoNestingCounter NoUpdateCounter(NoUpdate);
int Index = 0;
TTreeNode * PrevNode = NULL;
while (Index < PageControl->PageCount)
{
TTabSheet * Tab = PageControl->Pages[Index];
if (Tab->Enabled)
{
bool Indented = ((Tab->Tag % 100) == 2);
UnicodeString Label = Tab->Caption;
TTreeNode * Node;
if (PrevNode == NULL)
{
DebugAssert(!Indented);
Node = NavigationTree->Items->GetFirstNode();
}
else
{
if (Indented)
{
if (PrevNode->Level == 0)
{
Node = PrevNode->getFirstChild();
if (Node == NULL)
{
Node = NavigationTree->Items->AddChild(PrevNode, Label);
}
}
else
{
Node = PrevNode->getNextSibling();
if (Node == NULL)
{
Node = NavigationTree->Items->Add(PrevNode, Label);
}
}
}
else
{
if (PrevNode->Level == 0)
{
// delete all excess children of previous top level node
while ((Node = PrevNode->GetNext()) != PrevNode->getNextSibling())
{
Node->Delete();
}
Node = PrevNode->getNextSibling();
if (Node == NULL)
{
Node = NavigationTree->Items->Add(PrevNode, Label);
}
}
else
{
// delete all excess children of previous top level node
while ((Node = PrevNode->getNextSibling()) != NULL)
{
Node->Delete();
}
Node = PrevNode->GetNext();
if (Node == NULL)
{
Node = NavigationTree->Items->Add(NULL, Label);
}
}
}
}
Node->Text = Label;
Node->SelectedIndex = reinterpret_cast<int>(Tab);
PrevNode = Node;
if (PageControl->ActivePage == Tab)
{
Node->Selected = true;
ActiveNode = Node;
}
}
Index++;
}
TTreeNode * Node;
while ((Node = PrevNode->GetNext()) != NULL)
{
Node->Delete();
}
NavigationTree->FullExpand();
}
// node of active page was hidden
if (ActiveNode == NULL)
{
ChangePage(EnvironmentSheet);
}
}
//---------------------------------------------------------------------
bool TSiteAdvancedDialog::HasCertificate(const UnicodeString & FileName)
{
bool Result = false;
try
{
UnicodeString UnusedComment;
GetPublicKeyLine(FileName, UnusedComment, Result);
}
catch (...)
{
}
return Result;
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::UpdateControls()
{
if (Visible)
{
TAutoNestingCounter NoUpdateCounter(NoUpdate);
bool SshProtocol = FSessionData->UsesSsh;
bool SftpProtocol = (NormalizeFSProtocol(FSessionData->FSProtocol) == fsSFTP);
bool ScpProtocol = (FSessionData->FSProtocol == fsSCPonly);
bool FtpProtocol = (FSessionData->FSProtocol == fsFTP);
bool WebDavProtocol = (FSessionData->FSProtocol == fsWebDAV);
bool S3Protocol = (FSessionData->FSProtocol == fsS3);
bool Neon = IsNeon(FSessionData->FSProtocol);
bool Ssl = (FtpProtocol || WebDavProtocol || S3Protocol) && (FSessionData->Ftps != ftpsNone);
// connection sheet
EnableControl(FtpPasvModeCheck, FtpProtocol);
if (FtpProtocol &&
(FtpProxyMethodCombo->ItemIndex != ::pmNone) &&
SupportedFtpProxyMethod(FtpProxyMethodCombo->ItemIndex) &&
!FtpPasvModeCheck->Checked)
{
FtpPasvModeCheck->Checked = true;
MessageDialog(MainInstructions(LoadStr(FTP_PASV_MODE_REQUIRED)), qtInformation, qaOK);
}
EnableControl(BufferSizeCheck, SshProtocol || FtpProtocol || S3Protocol);
PingGroup->Visible = !FtpProtocol;
EnableControl(PingGroup, SshProtocol);
EnableControl(PingIntervalSecEdit, PingGroup->Enabled && !PingOffButton->Checked);
EnableControl(PingIntervalLabel, PingGroup->Enabled && PingIntervalSecEdit->Enabled);
FtpPingGroup->Visible = FtpProtocol;
EnableControl(FtpPingIntervalSecEdit, !FtpPingOffButton->Checked);
EnableControl(FtpPingIntervalLabel, FtpPingIntervalSecEdit->Enabled);
EnableControl(IPvGroup, SshProtocol || FtpProtocol);
EnableControl(IPAutoButton, IPvGroup->Enabled && SshProtocol);
// ssh/authentication sheet
AuthSheet->Enabled = SshProtocol;
EnableControl(AuthenticationGroup,
!SshNoUserAuthCheck->Enabled || !SshNoUserAuthCheck->Checked);
EnableControl(AuthKIPasswordCheck,
AuthenticationGroup->Enabled && (AuthKICheck->Enabled && AuthKICheck->Checked));
EnableControl(AuthenticationParamsGroup, AuthenticationGroup->Enabled);
EnableControl(AgentFwdCheck, AuthenticationParamsGroup->Enabled && TryAgentCheck->Checked);
if (PrivateKeyEdit3->Text != FLastPrivateKey)
{
FLastPrivateKey = PrivateKeyEdit3->Text;
FKeyHasCertificate =
PrivateKeyEdit3->Enabled &&
!FLastPrivateKey.IsEmpty() &&
HasCertificate(FLastPrivateKey);
}
EnableControl(PrivateKeyViewButton, PrivateKeyEdit3->Enabled && !PrivateKeyEdit3->Text.IsEmpty());
EnableControl(DetachedCertificateEdit, PrivateKeyViewButton->Enabled && !FKeyHasCertificate);
EnableControl(DetachedCertificateLabel, DetachedCertificateEdit->Enabled);
EnableControl(AuthGSSAPICheck3, AuthenticationGroup->Enabled);
EnableControl(GSSAPIFwdTGTCheck,
AuthGSSAPICheck3->Enabled && AuthGSSAPICheck3->Checked);
// ssh sheet
AdvancedSheet->Enabled = SshProtocol;
EnableControl(CipherUpButton, CipherListBox->ItemIndex > 0);
EnableControl(CipherDownButton, CipherListBox->ItemIndex >= 0 &&
CipherListBox->ItemIndex < CipherListBox->Items->Count-1);
// ssh/kex sheet
KexSheet->Enabled = SshProtocol && (BugRekey2Combo->ItemIndex != 2);
EnableControl(KexUpButton, KexListBox->ItemIndex > 0);
EnableControl(KexDownButton, KexListBox->ItemIndex >= 0 &&
KexListBox->ItemIndex < KexListBox->Items->Count-1);
// ssh/bugs sheet
BugsSheet->Enabled = SshProtocol;
// connection/proxy sheet
// this is probaqbly overkill, now we do not allow changing protocol on
// the same window as changing proxy
TComboBox * ProxyMethodCombo =
(SshProtocol ? SshProxyMethodCombo : (FtpProtocol ? FtpProxyMethodCombo : NeonProxyMethodCombo));
TProxyMethod ProxyMethod = GetProxyMethod();
ProxyMethodCombo->Visible = true;
ProxyMethodLabel->FocusControl = ProxyMethodCombo;
if (!SshProtocol)
{
SshProxyMethodCombo->Visible = false;
SshProxyMethodCombo->ItemIndex = ProxyMethod;
}
if (!FtpProtocol)
{
FtpProxyMethodCombo->Visible = false;
FtpProxyMethodCombo->ItemIndex = GetSupportedFtpProxyMethod(ProxyMethod);
}
if (!Neon)
{
NeonProxyMethodCombo->Visible = false;
NeonProxyMethodCombo->ItemIndex = GetSupportedNeonProxyMethod(ProxyMethod);
}
int FtpProxyLogonType = GetFtpProxyLogonType();
UnicodeString ProxyCommand =
((ProxyMethod == pmCmd) ?
ProxyLocalCommandEdit->Text : ProxyTelnetCommandEdit->Text);
EnableControl(ProxyHostEdit,
(ProxyMethod == pmSocks4) ||
(ProxyMethod == pmSocks5) ||
(ProxyMethod == pmHTTP) ||
(ProxyMethod == pmTelnet) ||
((ProxyMethod == pmCmd) && AnsiContainsText(ProxyCommand, L"%proxyhost")) ||
(FtpProxyLogonType > 0));
EnableControl(ProxyHostLabel, ProxyHostEdit->Enabled);
EnableControl(ProxyPortEdit,
(ProxyMethod == pmSocks4) ||
(ProxyMethod == pmSocks5) ||
(ProxyMethod == pmHTTP) ||
(ProxyMethod == pmTelnet) ||
((ProxyMethod == pmCmd) && AnsiContainsText(ProxyCommand, L"%proxyport")) ||
(FtpProxyLogonType > 0));
EnableControl(ProxyPortLabel, ProxyPortEdit->Enabled);
EnableControl(ProxyUsernameEdit,
// FZAPI does not support username for SOCKS4
((ProxyMethod == pmSocks4) && (SshProtocol || Neon)) ||
(ProxyMethod == pmSocks5) ||
(ProxyMethod == pmHTTP) ||
(((ProxyMethod == pmTelnet) ||
(ProxyMethod == pmCmd)) &&
AnsiContainsText(ProxyCommand, L"%user")) ||
((FtpProxyLogonType > 0) && (FtpProxyLogonType != 3) && (FtpProxyLogonType != 5)));
EnableControl(ProxyUsernameLabel, ProxyUsernameEdit->Enabled);
EnableControl(ProxyPasswordEdit,
(ProxyMethod == pmSocks5) ||
(ProxyMethod == pmHTTP) ||
(((ProxyMethod == pmTelnet) ||
(ProxyMethod == pmCmd)) &&
AnsiContainsText(ProxyCommand, L"%pass")) ||
((FtpProxyLogonType > 0) && (FtpProxyLogonType != 3) && (FtpProxyLogonType != 5)));
EnableControl(ProxyPasswordLabel, ProxyPasswordEdit->Enabled);
bool ProxySettings = (ProxyMethod != ::pmNone) && SshProtocol;
ProxySettingsGroup->Visible = SshProtocol;
EnableControl(ProxySettingsGroup, ProxySettings);
EnableControl(ProxyTelnetCommandEdit,
ProxySettings && (ProxyMethod == pmTelnet));
EnableControl(ProxyTelnetCommandLabel, ProxyTelnetCommandEdit->Enabled);
EnableControl(ProxyTelnetCommandHintText, ProxyTelnetCommandEdit->Enabled);
ProxyLocalCommandEdit->Visible = (ProxyMethod == pmCmd);
ProxyLocalCommandLabel->Visible = ProxyLocalCommandEdit->Visible;
ProxyLocalCommandBrowseButton->Visible = ProxyLocalCommandEdit->Visible;
ProxyLocalCommandHintText->Visible = ProxyLocalCommandEdit->Visible;
ProxyTelnetCommandEdit->Visible = !ProxyLocalCommandEdit->Visible;
ProxyTelnetCommandLabel->Visible = ProxyTelnetCommandEdit->Visible;
ProxyTelnetCommandHintText->Visible = ProxyTelnetCommandEdit->Visible;
// environment/directories sheet
EnableControl(SynchronizeBrowsingCheck,
(CustomWinConfiguration->Interface == ifCommander) &&
WinConfiguration->PreservePanelState &&
!WinConfiguration->ScpCommander.PreserveLocalDirectory);
EnableControl(CacheDirectoryChangesCheck,
(!ScpProtocol || CacheDirectoriesCheck->Checked) && DirectoriesSheet->Enabled);
EnableControl(PreserveDirectoryChangesCheck,
CacheDirectoryChangesCheck->Enabled && CacheDirectoryChangesCheck->Checked &&
DirectoriesSheet->Enabled);
EnableControl(ResolveSymlinksCheck, (SftpProtocol || ScpProtocol) && DirectoriesSheet->Enabled);
EnableControl(FollowDirectorySymlinksCheck, (SftpProtocol || ScpProtocol || FtpProtocol));
// environment sheet
EnableControl(EOLTypeCombo, (SftpProtocol || ScpProtocol) && EnvironmentSheet->Enabled);
EnableControl(EOLTypeLabel, EOLTypeCombo->Enabled);
EnableControl(DSTModeGroup, (SftpProtocol || ScpProtocol) && EnvironmentSheet->Enabled);
EnableControl(DSTModeKeepCheck, UsesDaylightHack() && DSTModeGroup->Enabled);
EnableControl(UtfCombo, (SftpProtocol || FtpProtocol || ScpProtocol) && EnvironmentSheet->Enabled);
EnableControl(UtfLabel, UtfCombo->Enabled);
// should be enabled for fsSFTP (SCP fallback) too, but it would cause confusion
bool FtpProtocolWithMlsdOff = FtpProtocol && (ComboAutoSwitchSave(FtpUseMlsdCombo) == asOff);
EnableControl(TimeDifferenceAutoCheck, FtpProtocolWithMlsdOff);
EnableControl(TimeDifferenceEdit,
((FtpProtocolWithMlsdOff && !TimeDifferenceAutoCheck->Checked) ||
ScpProtocol) &&
EnvironmentSheet->Enabled);
EnableControl(TimeDifferenceLabel,
TimeDifferenceEdit->Enabled || TimeDifferenceAutoCheck->Enabled);
EnableControl(TimeDifferenceHoursLabel, TimeDifferenceEdit->Enabled);
EnableControl(TimeDifferenceMinutesEdit, TimeDifferenceEdit->Enabled);
EnableControl(TimeDifferenceMinutesLabel, TimeDifferenceEdit->Enabled);
EnableControl(TrimVMSVersionsCheck, !S3Protocol);
// environment/recycle bin sheet
EnableControl(OverwrittenToRecycleBinCheck, SftpProtocol && RecycleBinSheet->Enabled);
EnableControl(RecycleBinPathEdit,
(DeleteToRecycleBinCheck->Enabled && DeleteToRecycleBinCheck->Checked) ||
(OverwrittenToRecycleBinCheck->Enabled && OverwrittenToRecycleBinCheck->Checked) &&
RecycleBinSheet->Enabled);
EnableControl(RecycleBinPathLabel, RecycleBinPathEdit->Enabled &&
RecycleBinSheet->Enabled);
// environment/sftp sheet
SftpSheet->Enabled = SftpProtocol;
EnableControl(AllowScpFallbackCheck, IsDefaultSftpServer());
// environment/scp/shell
ScpSheet->Enabled = SshProtocol;
ScpSheet->Caption = LoadStr(ScpProtocol ? LOGIN_SCP_SHELL_PAGE : LOGIN_SHELL_PAGE);
// hide also for SFTP with SCP fallback, as if someone wants to configure
// these he/she probably intends to use SCP and should explicitly select it.
// (note that these are not used for separate shell session)
ScpLsOptionsGroup->Visible = ScpProtocol;
OtherShellOptionsGroup->Visible = ScpProtocol;
// environment/ftp
FtpSheet->Enabled = FtpProtocol;
EnableControl(FtpListAllCombo,
(ComboAutoSwitchSave(FtpUseMlsdCombo) == asOff));
EnableControl(FtpListAllLabel, FtpListAllCombo->Enabled);
EnableControl(FtpForcePasvIpCombo,
FtpPasvModeCheck->Checked &&
(IPAutoButton->Checked || IPv4Button->Checked));
EnableControl(FtpForcePasvIpLabel, FtpForcePasvIpCombo->Enabled);
// environment/s3
S3Sheet->Enabled = S3Protocol;
EnableControl(S3SessionTokenMemo, S3Sheet->Enabled && !FSessionData->HasAutoCredentials());
EnableControl(S3SessionTokenLabel, S3SessionTokenMemo->Enabled);
// tunnel sheet
TunnelSheet->Enabled = SshProtocol;
// probably needless
EnableControl(TunnelSessionGroup, TunnelCheck->Enabled && TunnelCheck->Checked);
EnableControl(TunnelOptionsGroup, TunnelSessionGroup->Enabled);
EnableControl(TunnelAuthenticationParamsGroup, TunnelSessionGroup->Enabled);
// connection/ssl/tls
SslSheet->Enabled = Ssl;
// TLS session reuse is not configurable for WebDAV/S3 yet
SslSessionReuseCheck2->Enabled = SslSheet->Enabled && FtpProtocol;
TlsAuthenticationGroup->Visible = Ssl && (FtpProtocol || WebDavProtocol);
// encryption sheet
EncryptionSheet->Enabled = SftpProtocol;
EnableControl(EncryptFilesGroup, EncryptFilesCheck->Checked);
// environment/webdav
WebDavSheet->Enabled = WebDavProtocol;
UpdateNavigationTree();
// color
if (FColor == 0)
{
MenuButton(ColorButton);
}
else
{
ColorButton->Images = ColorImageList;
ColorButton->ImageIndex = GetSessionColorImage(ColorImageList, FColor, 0);
ColorButton->ImageAlignment = iaRight;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::DataChange(TObject * /*Sender*/)
{
if (NoUpdate == 0)
{
UpdateControls();
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::FormShow(TObject * /*Sender*/)
{
InstallPathWordBreakProc(PrivateKeyEdit3);
InstallPathWordBreakProc(TunnelPrivateKeyEdit3);
InstallPathWordBreakProc(RemoteDirectoryEdit);
InstallPathWordBreakProc(LocalDirectoryEdit);
InstallPathWordBreakProc(RecycleBinPathEdit);
InstallPathWordBreakProc(TlsCertificateFileEdit);
InstallPathWordBreakProc(SftpServerEdit);
InstallPathWordBreakProc(ShellEdit);
ChangePage(EnvironmentSheet);
UpdateControls();
}
//---------------------------------------------------------------------------
bool __fastcall TSiteAdvancedDialog::Execute(TSessionData * SessionData)
{
FSessionData = SessionData;
LoadSession();
bool Result = (ShowModal() == DefaultResult(this));
if (Result)
{
SaveSession(SessionData);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::NavigationTreeChange(TObject * /*Sender*/,
TTreeNode * Node)
{
if (NoUpdate == 0)
{
TAutoNestingCounter Guard(NoUpdate);
TTabSheet * Tab = reinterpret_cast<TTabSheet *>(Node->SelectedIndex);
// should happen only while loading language
// (UpdateNavigationTree may not be called yet)
if (Tab != NULL)
{
PageControl->ActivePage = Tab;
// reshow the accelerators, etc
ResetSystemSettings(this);
}
PageChanged();
UpdateControls();
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ChangePage(TTabSheet * Tab)
{
PageControl->ActivePage = Tab;
PageChanged();
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PageChanged()
{
FPrivateKeyMonitors.reset(NULL);
ClosePuttySettings();
bool Found = false;
if (PageControl->ActivePage)
{
for (int Index = 0; Index < NavigationTree->Items->Count; Index++)
{
if (NavigationTree->Items->Item[Index]->SelectedIndex ==
reinterpret_cast<int>(PageControl->ActivePage))
{
NavigationTree->Items->Item[Index]->Selected = true;
Found = true;
}
}
}
if (DebugAlwaysTrue(Found))
{
DataChange(NULL);
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PageControlChange(TObject *)
{
DebugFail(); // should never happen as user cannot change the page
PageChanged();
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::CMDialogKey(TWMKeyDown & Message)
{
if (Message.CharCode == VK_TAB)
{
TShiftState Shift = KeyDataToShiftState(Message.KeyData);
if (Shift.Contains(ssCtrl))
{
TTreeNode * Node = NavigationTree->Selected;
if (!Shift.Contains(ssShift))
{
Node = Node->GetNext();
if (Node == NULL)
{
Node = NavigationTree->Items->GetFirstNode();
}
}
else
{
if (Node->GetPrev() != NULL)
{
Node = Node->GetPrev();
}
else
{
while (Node->GetNext() != NULL)
{
Node = Node->GetNext();
}
}
}
Node->Selected = True;
Message.Result = 1;
return;
}
}
TForm::Dispatch(&Message);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::WMHelp(TWMHelp & Message)
{
DebugAssert(Message.HelpInfo != NULL);
if (Message.HelpInfo->iContextType == HELPINFO_WINDOW)
{
// invoke help for active page (not for whole form), regardless of focus
// (e.g. even if focus is on control outside pagecontrol)
Message.HelpInfo->hItemHandle = PageControl->ActivePage->Handle;
}
TForm::Dispatch(&Message);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::Dispatch(void * Message)
{
TMessage * M = reinterpret_cast<TMessage*>(Message);
DebugAssert(M);
if (M->Msg == CM_DIALOGKEY)
{
CMDialogKey(*((TWMKeyDown *)Message));
}
else if (M->Msg == WM_HELP)
{
WMHelp(*((TWMHelp *)Message));
}
else
{
TForm::Dispatch(Message);
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::AlgListBoxStartDrag(TObject * Sender,
TDragObject *& /*DragObject*/)
{
FAlgDragSource = dynamic_cast<TListBox*>(Sender)->ItemIndex;
FAlgDragDest = -1;
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::AlgListBoxDragOver(TObject * Sender,
TObject * Source, int X, int Y, TDragState /*State*/, bool & Accept)
{
if (Source == Sender)
{
Accept = AllowAlgDrag(dynamic_cast<TListBox*>(Sender), X, Y);
}
else
{
Accept = false;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::AlgListBoxDragDrop(TObject * Sender,
TObject * Source, int X, int Y)
{
if (Source == Sender)
{
TListBox * AlgListBox = dynamic_cast<TListBox*>(Sender);
if (AllowAlgDrag(AlgListBox, X, Y))
{
AlgMove(AlgListBox, FAlgDragSource, FAlgDragDest);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::CipherButtonClick(TObject *Sender)
{
AlgMove(CipherListBox, CipherListBox->ItemIndex,
CipherListBox->ItemIndex + (Sender == CipherUpButton ? -1 : 1));
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::KexButtonClick(TObject *Sender)
{
AlgMove(KexListBox, KexListBox->ItemIndex,
KexListBox->ItemIndex + (Sender == KexUpButton ? -1 : 1));
}
//---------------------------------------------------------------------------
bool __fastcall TSiteAdvancedDialog::AllowAlgDrag(TListBox * AlgListBox, int X, int Y)
{
FAlgDragDest = AlgListBox->ItemAtPos(TPoint(X, Y), true);
return (FAlgDragDest >= 0) && (FAlgDragDest != FAlgDragSource);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::AlgMove(TListBox * AlgListBox, int Source, int Dest)
{
if ((Source >= 0) && (Source < AlgListBox->Items->Count) &&
(Dest >= 0) && (Dest < AlgListBox->Items->Count))
{
AlgListBox->Items->Move(Source, Dest);
AlgListBox->ItemIndex = Dest;
AlgListBox->SetFocus();
}
UpdateControls();
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::AuthGSSAPICheck3Click(TObject * /*Sender*/)
{
if (NoUpdate == 0)
{
UpdateControls();
if (AuthGSSAPICheck3->Checked && !HasGSSAPI(L""))
{
throw Exception(LoadStr(GSSAPI_NOT_INSTALLED2));
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::HelpButtonClick(TObject * /*Sender*/)
{
FormHelp(this);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyEdit3AfterDialog(TObject * Sender,
UnicodeString & Name, bool & Action)
{
PathEditAfterDialog(Sender, Name, Action);
TFilenameEdit * Edit = dynamic_cast<TFilenameEdit *>(Sender);
if (Name != Edit->Text)
{
VerifyAndConvertKey(Name, true);
if (!Name.IsEmpty() && !HasCertificate(Name))
{
try
{
UnicodeString FileName = ExpandEnvironmentVariables(Name);
TKeyType Type = KeyType(FileName);
// This gonna fail for encrypted keys
TPrivateKey * PrivateKey = LoadKey(Type, FileName, EmptyStr);
try
{
UnicodeString CertificateFileName = AddMatchingKeyCertificate(PrivateKey, FileName);
DetachedCertificateEdit->Text = CertificateFileName;
}
__finally
{
FreeKey(PrivateKey);
}
}
catch (Exception & E)
{
// swallow
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::FormCloseQuery(TObject * /*Sender*/,
bool & /*CanClose*/)
{
if (ModalResult == DefaultResult(this))
{
// StripPathQuotes should not be needed as we do not feed quotes anymore
VerifyKey(StripPathQuotes(PrivateKeyEdit3->Text));
VerifyKey(StripPathQuotes(TunnelPrivateKeyEdit3->Text));
VerifyCertificate(StripPathQuotes(TlsCertificateFileEdit->Text));
// Particularly for EncryptKey*Edit's
ExitActiveControl(this);
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PathEditBeforeDialog(TObject * /*Sender*/,
UnicodeString & Name, bool & /*Action*/)
{
// Reset key stats, even if new key is not select, this way the clicking browse button gives the user chance to
// reload the "certificate" state in case the key file is recreated with certificate
FLastPrivateKey = EmptyStr;
FBeforeDialogPath = Name;
Name = ExpandEnvironmentVariables(Name);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PathEditAfterDialog(TObject * /*Sender*/,
UnicodeString & Name, bool & /*Action*/)
{
if (IsPathToSameFile(Name, ExpandEnvironmentVariables(FBeforeDialogPath)))
{
Name = FBeforeDialogPath;
}
}
//---------------------------------------------------------------------------
int __fastcall TSiteAdvancedDialog::LastSupportedFtpProxyMethod()
{
return pmHTTP;
}
//---------------------------------------------------------------------------
bool __fastcall TSiteAdvancedDialog::SupportedFtpProxyMethod(int Method)
{
return (Method >= 0) && (Method <= LastSupportedFtpProxyMethod());
}
//---------------------------------------------------------------------------
int __fastcall TSiteAdvancedDialog::GetSupportedFtpProxyMethod(int Method)
{
if (SupportedFtpProxyMethod(Method))
{
return Method;
}
else
{
return ::pmNone;
}
}
//---------------------------------------------------------------------------
int __fastcall TSiteAdvancedDialog::GetSupportedNeonProxyMethod(int Method)
{
if ((Method >= 0) && (Method <= pmHTTP))
{
return Method;
}
else
{
return ::pmNone;
}
}
//---------------------------------------------------------------------------
bool __fastcall TSiteAdvancedDialog::IsNeon(TFSProtocol FSProtocol)
{
return (FSProtocol == fsWebDAV) || (FSProtocol == fsS3);
}
//---------------------------------------------------------------------------
TProxyMethod __fastcall TSiteAdvancedDialog::GetProxyMethod()
{
TProxyMethod Result;
TFSProtocol FSProtocol = FSessionData->FSProtocol;
if (FSessionData->UsesSsh)
{
Result = (TProxyMethod)SshProxyMethodCombo->ItemIndex;
}
else if (FSProtocol == fsFTP)
{
if (SupportedFtpProxyMethod(FtpProxyMethodCombo->ItemIndex))
{
Result = (TProxyMethod)FtpProxyMethodCombo->ItemIndex;
}
else
{
Result = ::pmNone;
}
}
else if (IsNeon(FSProtocol))
{
Result = (TProxyMethod)NeonProxyMethodCombo->ItemIndex;
}
else
{
DebugFail();
Result = ::pmNone;
}
return Result;
}
//---------------------------------------------------------------------------
int __fastcall TSiteAdvancedDialog::GetFtpProxyLogonType()
{
int Result;
if (FSessionData->FSProtocol != fsFTP)
{
Result = 0;
}
else
{
if (SupportedFtpProxyMethod(FtpProxyMethodCombo->ItemIndex))
{
Result = 0;
}
else
{
Result = FtpProxyMethodCombo->ItemIndex - LastSupportedFtpProxyMethod();
}
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::NavigationTreeCollapsing(
TObject * /*Sender*/, TTreeNode * /*Node*/, bool & AllowCollapse)
{
AllowCollapse = false;
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ProxyLocalCommandBrowseButtonClick(
TObject * /*Sender*/)
{
BrowseForExecutable(ProxyLocalCommandEdit,
LoadStr(LOGIN_SELECT_LOCAL_PROXY),
LoadStr(EXECUTABLE_FILTER), false, true);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ColorButtonClick(TObject * /*Sender*/)
{
// Reason for separate Menu variable is given in TPreferencesDialog::EditorFontColorButtonClick
TPopupMenu * Menu = CreateSessionColorPopupMenu(FColor, SessionColorChange);
// Popup menu has to survive the popup as TBX calls click handler asynchronously (post).
FColorPopupMenu.reset(Menu);
MenuPopup(Menu, ColorButton);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::SessionColorChange(TColor Color)
{
FColor = Color;
UpdateControls();
}
//---------------------------------------------------------------------------
TTlsVersion __fastcall TSiteAdvancedDialog::IndexToTlsVersion(int Index)
{
switch (Index)
{
default:
DebugFail();
case 0:
return tls10;
case 1:
return tls11;
case 2:
return tls12;
case 3:
return tls13;
}
}
//---------------------------------------------------------------------------
int __fastcall TSiteAdvancedDialog::TlsVersionToIndex(TTlsVersion TlsVersion)
{
switch (TlsVersion)
{
default:
DebugFail();
case ssl2:
case ssl3:
case tls10:
return 0;
case tls11:
return 1;
case tls12:
return 2;
case tls13:
return 3;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::MinTlsVersionComboChange(TObject */*Sender*/)
{
TTlsVersion MinTlsVersion = IndexToTlsVersion(MinTlsVersionCombo->ItemIndex);
TTlsVersion MaxTlsVersion = IndexToTlsVersion(MaxTlsVersionCombo->ItemIndex);
if (MaxTlsVersion < MinTlsVersion)
{
MaxTlsVersionCombo->ItemIndex = MinTlsVersionCombo->ItemIndex;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::MaxTlsVersionComboChange(TObject * /*Sender*/)
{
TTlsVersion MinTlsVersion = IndexToTlsVersion(MinTlsVersionCombo->ItemIndex);
TTlsVersion MaxTlsVersion = IndexToTlsVersion(MaxTlsVersionCombo->ItemIndex);
if (MinTlsVersion > MaxTlsVersion)
{
MinTlsVersionCombo->ItemIndex = MaxTlsVersionCombo->ItemIndex;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ProxyAutodetectButtonClick(TObject * /*Sender*/)
{
TInstantOperationVisualizer Visualizer;
UnicodeString ProxyHost;
int ProxyPort;
if (AutodetectProxy(ProxyHost, ProxyPort))
{
ProxyHostEdit->Text = ProxyHost;
ProxyPortEdit->AsInteger = ProxyPort;
SshProxyMethodCombo->ItemIndex = pmHTTP;
FtpProxyMethodCombo->ItemIndex = pmHTTP;
NeonProxyMethodCombo->ItemIndex = pmHTTP;
UpdateControls();
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::NoteMemoKeyDown(
TObject * Sender, WORD & Key, TShiftState Shift)
{
MemoKeyDown(Sender, Key, Shift);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::TlsCertificateFileEditAfterDialog(TObject *Sender,
UnicodeString &Name, bool &Action)
{
PathEditAfterDialog(Sender, Name, Action);
TFilenameEdit * Edit = dynamic_cast<TFilenameEdit *>(Sender);
if (Name != Edit->Text)
{
VerifyCertificate(Name);
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyCreatedOrModified(TObject * /*Sender*/, const UnicodeString FileName)
{
if (SameText(ExtractFileExt(FileName), FORMAT(L".%s", (PuttyKeyExt))))
{
PrivateKeyEdit3->Text = FileName;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyToolsButtonClick(TObject * /*Sender*/)
{
UnicodeString Dummy;
PrivateKeyGenerateItem->Enabled = FindTool(PuttygenTool, Dummy);
PrivateKeyUploadItem->Enabled = (NormalizeFSProtocol(FSessionData->FSProtocol) == fsSFTP);
MenuPopup(PrivateKeyMenu, PrivateKeyToolsButton);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyGenerateItemClick(TObject * /*Sender*/)
{
unsigned int Filters = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE;
TObjectList * PrivateKeyMonitors =
StartCreationDirectoryMonitorsOnEachDrive(Filters, PrivateKeyCreatedOrModified);
FPrivateKeyMonitors.reset(PrivateKeyMonitors);
ExecuteTool(PuttygenTool);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyUploadItemClick(TObject * /*Sender*/)
{
std::unique_ptr<TSessionData> SessionData(GetSessionData());
SessionData->FSProtocol = fsSFTPonly; // no SCP fallback, as SCP does not implement GetHomeDirectory
SessionData->RemoteDirectory = UnicodeString();
UnicodeString FileName = PrivateKeyEdit3->Text;
if (TTerminalManager::Instance()->UploadPublicKey(NULL, SessionData.get(), FileName))
{
PrivateKeyEdit3->Text = FileName;
PrivateKeyEdit3->SetFocus();
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyViewButtonClick(TObject * /*Sender*/)
{
UnicodeString FileName = PrivateKeyEdit3->Text;
VerifyAndConvertKey(FileName, false);
PrivateKeyEdit3->Text = FileName;
UnicodeString CommentDummy;
bool HasCertificate;
UnicodeString Line = GetPublicKeyLine(FileName, CommentDummy, HasCertificate);
std::unique_ptr<TStrings> Messages(TextToStringList(Line));
TClipboardHandler ClipboardHandler;
ClipboardHandler.Text = Line;
TMessageParams Params;
TQueryButtonAlias Aliases[1];
Aliases[0].Button = qaRetry;
Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
Aliases[0].OnSubmit = &ClipboardHandler.Copy;
Params.Aliases = Aliases;
Params.AliasesCount = LENOF(Aliases);
UnicodeString Message = LoadStr(HasCertificate ? LOGIN_KEY_WITH_CERTIFICATE : LOGIN_AUTHORIZED_KEYS);
int Answers = qaOK | qaRetry;
MoreMessageDialog(Message, Messages.get(), qtInformation, Answers, HELP_LOGIN_AUTHORIZED_KEYS, &Params);
}
//---------------------------------------------------------------------------
TCustomEdit * __fastcall TSiteAdvancedDialog::GetEncryptKeyEdit(bool AShow)
{
bool Show = (ShowEncryptionKeyCheck->Checked == AShow);
return (Show ? static_cast<TCustomEdit *>(EncryptKeyVisibleEdit) : static_cast<TCustomEdit *>(EncryptKeyPasswordEdit));
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ShowEncryptionKeyCheckClick(TObject * /*Sender*/)
{
TCustomEdit * ShowEdit = GetEncryptKeyEdit();
TCustomEdit * HideEdit = GetEncryptKeyEdit(false);
if (DebugAlwaysTrue(ShowEdit->Visible != HideEdit->Visible) &&
DebugAlwaysTrue(!ShowEdit->Visible))
{
UnicodeString Key = HideEdit->Text;
ShowEdit->Visible = true;
ShowEdit->Text = Key;
HideEdit->Visible = false;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::GenerateKeyButtonClick(TObject * /*Sender*/)
{
UnicodeString Key = BytesToHex(GenerateEncryptKey());
GetEncryptKeyEdit()->Text = Key;
TClipboardHandler ClipboardHandler;
ClipboardHandler.Text = Key;
TMessageParams Params;
TQueryButtonAlias Aliases[1];
Aliases[0].Button = qaRetry;
Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
Aliases[0].OnSubmit = &ClipboardHandler.Copy;
Params.Aliases = Aliases;
Params.AliasesCount = LENOF(Aliases);
MessageDialog(LoadStr(ENCRYPT_KEY_GENERATED), qtInformation, qaOK | qaRetry, HELP_FILE_ENCRYPTION, &Params);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::EncryptKeyEditExit(TObject * /*Sender*/)
{
if (!IsCancelButtonBeingClicked(this))
{
UnicodeString HexKey = GetEncryptKeyEdit()->Text;
if (!HexKey.IsEmpty())
{
ValidateEncryptKey(HexToBytes(HexKey));
}
}
}
//---------------------------------------------------------------------------
void TSiteAdvancedDialog::SerializePuttyRegistry(const UnicodeString & Key, TStrings * Values)
{
std::unique_ptr<TRegistry> Registry(new TRegistry());
if (DebugAlwaysTrue(Registry->OpenKeyReadOnly(Key)))
{
std::unique_ptr<TStrings> Names(new TStringList());
Registry->GetValueNames(Names.get());
for (int Index = 0; Index < Names->Count; Index++)
{
UnicodeString Name = Names->Strings[Index];
TRegDataType Type = Registry->GetDataType(Name);
UnicodeString Value;
if (Type == rdString)
{
Value = UnicodeString(PuttyStr(Registry->ReadString(Name)));
}
else if (DebugAlwaysTrue(Type == rdInteger))
{
Value = StrToInt(Registry->ReadInteger(Name));
}
SetStringValueEvenIfEmpty(Values, Name, Value);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PuttySettingsTimer(TObject *)
{
if (PuttySettingsEdit->Modified)
{
FPuttySettingsTimer->Enabled = false;
}
else
{
std::unique_ptr<TStrings> NewPuttyRegSettings(new TStringList());
SerializePuttyRegistry(GetPuttySiteKey(), NewPuttyRegSettings.get());
std::unique_ptr<TStrings> PuttySettings(new TStringList());
for (int NewIndex = 0; NewIndex < NewPuttyRegSettings->Count; NewIndex++)
{
UnicodeString Name = NewPuttyRegSettings->Names[NewIndex];
UnicodeString NewValue = NewPuttyRegSettings->ValueFromIndex[NewIndex];
int Index = FPuttyRegSettings->IndexOfName(Name);
// Ignoring values we do not know (from future versions of PuTTY),
// as we cannot tell if they are modified or not.
// Ignoring also all values that we export ourselves from WinSCP settings
// (primarily to avoid collecting all those values, when the user forgets to load the temporary site in PuTTY,
// and only saves the changed terminal settings with the basic session settings set to empty values)
if ((Index >= 0) &&
(FPuttyRegSettings->ValueFromIndex[Index] != NewValue))
{
SetStringValueEvenIfEmpty(PuttySettings.get(), Name, NewValue);
}
}
PuttySettingsEdit->Text = StringsToParams(PuttySettings.get()).TrimLeft();
UpdateControls();
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSiteAdvancedDialog::GetPuttySiteName()
{
return FORMAT(L"(%s)", (FMTLOAD(PUTTY_SETTINGS_SITE_NAME, (FSessionData->SessionName))));
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSiteAdvancedDialog::GetPuttySiteKey()
{
return OriginalPuttyRegistryStorageKey + L"\\" + Configuration->PuttySessionsSubKey + L"\\" + PuttyMungeStr(GetPuttySiteName());
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::ClosePuttySettings()
{
if (FPuttySettingsTimer.get() != NULL)
{
FPuttySettingsTimer.reset(NULL);
std::unique_ptr<TRegistry> Registry(new TRegistry());
Registry->DeleteKey(GetPuttySiteKey());
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PuttySettingsButtonClick(TObject *)
{
ClosePuttySettings();
UnicodeString SiteName = GetPuttySiteName();
MessageDialog(FMTLOAD(PUTTY_SETTINGS_INSTRUCTIONS, (SiteName, SiteName)), qtInformation, qaOK, HELP_PUTTY_SETTINGS);
PuttySettingsEdit->Modified = false;
std::unique_ptr<TSessionData> SessionData(GetSessionData());
UnicodeString PuttySettings = SessionData->PuttySettings;
SessionData->PuttySettings = UnicodeString();
SavePuttyDefaults(SiteName);
ExportSessionToPutty(SessionData.get(), false, SiteName);
UnicodeString PuttySiteKey = GetPuttySiteKey();
FPuttyRegSettings.reset(new TStringList());
SerializePuttyRegistry(PuttySiteKey, FPuttyRegSettings.get());
std::unique_ptr<TStrings> AllOptions(TSessionData::GetAllOptionNames(true));
for (int Index = 0; Index < AllOptions->Count; Index++)
{
int I = FPuttyRegSettings->IndexOfName(AllOptions->Names[Index]);
if (I >= 0)
{
FPuttyRegSettings->Delete(I);
}
}
SessionData->PuttySettings = PuttySettings;
ExportSessionToPutty(SessionData.get(), false, SiteName);
UnicodeString Program, Params, Dir;
SplitCommand(GUIConfiguration->PuttyPath, Program, Params, Dir);
Program = ExpandEnvironmentVariables(Program);
if (!FindFile(Program))
{
throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (Program)));
}
ExecuteShellChecked(Program, L"");
FPuttySettingsTimer.reset(new TTimer(this));
FPuttySettingsTimer->OnTimer = PuttySettingsTimer;
FPuttySettingsTimer->Interval = MSecsPerSec;
FPuttySettingsTimer->Enabled = true;
UpdateControls();
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::FormClose(TObject *, TCloseAction &)
{
ClosePuttySettings();
}
//---------------------------------------------------------------------------