//--------------------------------------------------------------------------- #include #pragma hdrstop #include #include "Common.h" #include "Exceptions.h" #include "Configuration.h" #include "PuttyIntf.h" #include "TextsCore.h" #include "Interface.h" #include "CoreMain.h" #include "Security.h" #include "FileMasks.h" #include "CopyParam.h" #include #include #include #include //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- const wchar_t * AutoSwitchNames = L"On;Off;Auto"; const wchar_t * NotAutoSwitchNames = L"Off;On;Auto"; //--------------------------------------------------------------------------- // See https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml const UnicodeString Sha1ChecksumAlg(L"sha-1"); const UnicodeString Sha224ChecksumAlg(L"sha-224"); const UnicodeString Sha256ChecksumAlg(L"sha-256"); const UnicodeString Sha384ChecksumAlg(L"sha-384"); const UnicodeString Sha512ChecksumAlg(L"sha-512"); const UnicodeString Md5ChecksumAlg(L"md5"); // Not defined by IANA const UnicodeString Crc32ChecksumAlg(L"crc32"); //--------------------------------------------------------------------------- const UnicodeString SshFingerprintType(L"ssh"); const UnicodeString TlsFingerprintType(L"tls"); //--------------------------------------------------------------------------- const UnicodeString FtpsCertificateStorageKey(L"FtpsCertificates"); const UnicodeString HttpsCertificateStorageKey(L"HttpsCertificates"); const UnicodeString LastFingerprintsStorageKey(L"LastFingerprints"); const UnicodeString DirectoryStatisticsCacheKey(L"DirectoryStatisticsCache"); const UnicodeString SshHostCAsKey(L"SshHostCAs"); const UnicodeString CDCacheKey(L"CDCache"); const UnicodeString BannersKey(L"Banners"); //--------------------------------------------------------------------------- const UnicodeString OpensshFolderName(L".ssh"); const UnicodeString OpensshAuthorizedKeysFileName(L"authorized_keys"); //--------------------------------------------------------------------------- const int BelowNormalLogLevels = 1; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- TSshHostCA::TSshHostCA() { PermitRsaSha1 = false; PermitRsaSha256 = true; PermitRsaSha512 = true; } //--------------------------------------------------------------------------- bool TSshHostCA::Load(THierarchicalStorage * Storage) { PublicKey = DecodeBase64ToStr(Storage->ReadString(L"PublicKey", PublicKey)); ValidityExpression = Storage->ReadString(L"Validity", ValidityExpression); PermitRsaSha1 = Storage->ReadBool(L"PermitRSASHA1", PermitRsaSha1); PermitRsaSha256 = Storage->ReadBool(L"PermitRSASHA256", PermitRsaSha256); PermitRsaSha512 = Storage->ReadBool(L"PermitRSASHA512", PermitRsaSha512); return !PublicKey.IsEmpty() && !ValidityExpression.IsEmpty(); } //--------------------------------------------------------------------------- void TSshHostCA::Save(THierarchicalStorage * Storage) const { Storage->WriteString(L"PublicKey", EncodeStrToBase64(PublicKey)); Storage->WriteString(L"Validity", ValidityExpression); Storage->WriteBool(L"PermitRSASHA1", PermitRsaSha1); Storage->WriteBool(L"PermitRSASHA256", PermitRsaSha256); Storage->WriteBool(L"PermitRSASHA512", PermitRsaSha512); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- TSshHostCAList::TSshHostCAList() { } //--------------------------------------------------------------------------- TSshHostCAList::TSshHostCAList(const TSshHostCA::TList & List) { FList = List; } //--------------------------------------------------------------------------- void TSshHostCAList::Default() { FList.clear(); } //--------------------------------------------------------------------------- void TSshHostCAList::Save(THierarchicalStorage * Storage) { Storage->ClearSubKeys(); TSshHostCA::TList::const_iterator I = FList.begin(); while (I != FList.end()) { const TSshHostCA & SshHostCA = *I; if (Storage->OpenSubKey(SshHostCA.Name, true)) { SshHostCA.Save(Storage); Storage->CloseSubKey(); } ++I; } } //--------------------------------------------------------------------------- void TSshHostCAList::Load(THierarchicalStorage * Storage) { FList.clear(); std::unique_ptr SubKeys(new TStringList()); Storage->GetSubKeyNames(SubKeys.get()); for (int Index = 0; Index < SubKeys->Count; Index++) { TSshHostCA SshHostCA; SshHostCA.Name = SubKeys->Strings[Index]; if (Storage->OpenSubKey(SshHostCA.Name, false)) { if (SshHostCA.Load(Storage)) { FList.push_back(SshHostCA); } Storage->CloseSubKey(); } } } //--------------------------------------------------------------------------- int TSshHostCAList::GetCount() const { return FList.size(); } //--------------------------------------------------------------------------- const TSshHostCA * TSshHostCAList::Get(int Index) const { return &FList[Index]; } //--------------------------------------------------------------------------- const TSshHostCA * TSshHostCAList::Find(const UnicodeString & Name) const { TSshHostCA::TList::const_iterator I = FList.begin(); while (I != FList.end()) { const TSshHostCA & SshHostCA = *I; if (SameStr(SshHostCA.Name, Name)) { return &SshHostCA; } ++I; } return NULL; } //--------------------------------------------------------------------------- const TSshHostCA::TList & TSshHostCAList::GetList() const { return FList; } //--------------------------------------------------------------------------- TSshHostCAList & TSshHostCAList::operator =(const TSshHostCAList & other) { FList = other.FList; return *this; } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- __fastcall TConfiguration::TConfiguration() { FCriticalSection = new TCriticalSection(); FUpdating = 0; FStorage = stDetect; FDontSave = false; FForceSave = false; FApplicationInfo = NULL; FUsage = new TUsage(this); FDefaultCollectUsage = false; FScripting = false; FSshHostCAList.reset(new TSshHostCAList()); UnicodeString RandomSeedPath; if (!GetEnvironmentVariable(L"APPDATA").IsEmpty()) { RandomSeedPath = L"%APPDATA%"; } else { RandomSeedPath = GetShellFolderPath(CSIDL_LOCAL_APPDATA); if (RandomSeedPath.IsEmpty()) { RandomSeedPath = GetShellFolderPath(CSIDL_APPDATA); } } FDefaultRandomSeedFile = IncludeTrailingBackslash(RandomSeedPath) + L"winscp.rnd"; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Default() { TGuard Guard(FCriticalSection); FDisablePasswordStoring = false; FForceBanners = false; FDisableAcceptingHostKeys = false; TRegistryStorage * AdminStorage; AdminStorage = new TRegistryStorage(RegistryStorageKey, HKEY_LOCAL_MACHINE); try { if (AdminStorage->OpenRootKey(false)) { LoadAdmin(AdminStorage); AdminStorage->CloseSubKey(); } } __finally { delete AdminStorage; } RandomSeedFile = FDefaultRandomSeedFile; PuttyRegistryStorageKey = OriginalPuttyRegistryStorageKey; FConfirmOverwriting = true; FConfirmResume = true; FAutoReadDirectoryAfterOp = true; FSessionReopenAuto = 5000; FSessionReopenBackground = 2000; FSessionReopenTimeout = 0; FSessionReopenAutoStall = 60000; FTunnelLocalPortNumberLow = 50000; FTunnelLocalPortNumberHigh = 50099; FCacheDirectoryChangesMaxSize = 100; FShowFtpWelcomeMessage = false; FExternalIpAddress = L""; FLocalPortNumberMin = 0; FLocalPortNumberMax = 0; FTryFtpWhenSshFails = true; FParallelDurationThreshold = 10; FMimeTypes = UnicodeString(); FCertificateStorage = EmptyStr; FAWSMetadataService = EmptyStr; FChecksumCommands = EmptyStr; FDontReloadMoreThanSessions = 1000; FScriptProgressFileNameLimit = 25; FQueueTransfersLimit = 2; FParallelTransferThreshold = -1; // default (currently off), 0 = explicitly off FKeyVersion = 0; FSshHostCAList->Default(); RefreshPuttySshHostCAList(); FSshHostCAsFromPuTTY = false; FHttpsCertificateValidation = 0; CollectUsage = FDefaultCollectUsage; FLogging = false; FPermanentLogging = false; FLogFileName = DefaultLogFileName; FPermanentLogFileName = FLogFileName; FLogFileAppend = true; FLogSensitive = false; FPermanentLogSensitive = FLogSensitive; FLogMaxSize = 0; FPermanentLogMaxSize = FLogMaxSize; FLogMaxCount = 0; FPermanentLogMaxCount = FLogMaxCount; FLogProtocol = 0; FPermanentLogProtocol = FLogProtocol; UpdateActualLogProtocol(); FLogActions = false; FPermanentLogActions = false; FLogActionsRequired = false; FActionsLogFileName = L"%TEMP%\\!S.xml"; FPermanentActionsLogFileName = FActionsLogFileName; FProgramIniPathWrittable = -1; FCustomIniFileStorageName = LoadCustomIniFileStorageName(); Changed(); } //--------------------------------------------------------------------------- __fastcall TConfiguration::~TConfiguration() { DebugAssert(!FUpdating); if (FApplicationInfo) FreeFileInfo(FApplicationInfo); delete FCriticalSection; delete FUsage; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::UpdateStaticUsage() { Usage->Set(L"ConfigurationIniFile", (Storage == stIniFile)); Usage->Set(L"ConfigurationIniFileCustom", !CustomIniFileStorageName.IsEmpty()); Usage->Set("Unofficial", IsUnofficial); // this is called from here, because we are guarded from calling into // master password handler here, see TWinConfiguration::UpdateStaticUsage StoredSessions->UpdateStaticUsage(); } //--------------------------------------------------------------------------- THierarchicalStorage * TConfiguration::CreateConfigStorage() { bool SessionList = false; return CreateScpStorage(SessionList); } //--------------------------------------------------------------------------- THierarchicalStorage * TConfiguration::CreateConfigRegistryStorage() { return new TRegistryStorage(RegistryStorageKey); } //--------------------------------------------------------------------------- THierarchicalStorage * TConfiguration::CreateScpStorage(bool & SessionList) { TGuard Guard(FCriticalSection); THierarchicalStorage * Result; if (Storage == stRegistry) { Result = CreateConfigRegistryStorage(); } else if (Storage == stNul) { Result = TIniFileStorage::CreateNul(); } else { UnicodeString StorageName = IniFileStorageName; Result = TIniFileStorage::CreateFromPath(StorageName); } if ((FOptionsStorage.get() != NULL) && (FOptionsStorage->Count > 0)) { if (!SessionList) { Result = new TOptionsStorage(FOptionsStorage.get(), ConfigurationSubKey, Result); } else { // cannot reuse session list storage for configuration as for it we need // the option-override storage above } } else { // All the above stores can be reused for configuration, // if no options-overrides are set SessionList = false; } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::PropertyToKey(const UnicodeString & Property) { // no longer useful int P = Property.LastDelimiter(L".>"); UnicodeString Result = Property.SubString(P + 1, Property.Length() - P); if ((Result[1] == L'F') && (towupper(Result[2]) == Result[2])) { Result.Delete(1, 1); } return Result; } //--------------------------------------------------------------------------- #define BLOCK(KEY, CANCREATE, BLOCK) \ if (Storage->OpenSubKeyPath(KEY, CANCREATE)) try { BLOCK } __finally { Storage->CloseSubKeyPath(); } #define KEY(TYPE, VAR) KEYEX(TYPE, VAR, PropertyToKey(TEXT(#VAR))) #define REGCONFIG(CANCREATE) \ BLOCK(L"Interface", CANCREATE, \ KEY(String, RandomSeedFile); \ KEY(String, PuttyRegistryStorageKey); \ KEY(Bool, ConfirmOverwriting); \ KEY(Bool, ConfirmResume); \ KEY(Bool, AutoReadDirectoryAfterOp); \ KEY(Integer, SessionReopenAuto); \ KEY(Integer, SessionReopenBackground); \ KEY(Integer, SessionReopenTimeout); \ KEY(Integer, SessionReopenAutoStall); \ KEY(Integer, TunnelLocalPortNumberLow); \ KEY(Integer, TunnelLocalPortNumberHigh); \ KEY(Integer, CacheDirectoryChangesMaxSize); \ KEY(Bool, ShowFtpWelcomeMessage); \ KEY(String, ExternalIpAddress); \ KEY(Integer, LocalPortNumberMin); \ KEY(Integer, LocalPortNumberMax); \ KEY(Bool, TryFtpWhenSshFails); \ KEY(Integer, ParallelDurationThreshold); \ KEY(String, MimeTypes); \ KEY(Integer, DontReloadMoreThanSessions); \ KEY(Integer, ScriptProgressFileNameLimit); \ KEY(Integer, QueueTransfersLimit); \ KEY(Integer, ParallelTransferThreshold); \ KEY(Integer, KeyVersion); \ KEY(Bool, SshHostCAsFromPuTTY); \ KEY(Integer, HttpsCertificateValidation); \ KEY(Bool, CollectUsage); \ KEY(String, CertificateStorage); \ KEY(String, AWSMetadataService); \ ); \ BLOCK(L"Logging", CANCREATE, \ KEYEX(Bool, PermanentLogging, L"Logging"); \ KEYEX(String,PermanentLogFileName, L"LogFileName"); \ KEY(Bool, LogFileAppend); \ KEYEX(Bool, PermanentLogSensitive, L"LogSensitive"); \ KEYEX(Int64, PermanentLogMaxSize, L"LogMaxSize"); \ KEYEX(Integer, PermanentLogMaxCount, L"LogMaxCount"); \ KEYEX(Integer,PermanentLogProtocol, L"LogProtocol"); \ KEYEX(Bool, PermanentLogActions, L"LogActions"); \ KEYEX(String,PermanentActionsLogFileName, L"ActionsLogFileName"); \ ); //--------------------------------------------------------------------------- void __fastcall TConfiguration::SaveData(THierarchicalStorage * Storage, bool /*All*/) { #define KEYEX(TYPE, VAR, NAME) Storage->Write ## TYPE(NAME, VAR) REGCONFIG(true); #undef KEYEX if (Storage->OpenSubKey(L"Usage", true)) { FUsage->Save(Storage); Storage->CloseSubKey(); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Save() { // only modified, implicit DoSave(false, false); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SaveExplicit() { // only modified, explicit DoSave(false, true); } //--------------------------------------------------------------------------- void TConfiguration::DoSave(THierarchicalStorage * AStorage, bool All) { if (AStorage->OpenSubKey(ConfigurationSubKey, true)) { SaveData(AStorage, All); AStorage->CloseSubKey(); } if (AStorage->OpenSubKey(SshHostCAsKey, true)) { FSshHostCAList->Save(AStorage); AStorage->CloseSubKey(); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::DoSave(bool All, bool Explicit) { if (FDontSave) return; THierarchicalStorage * AStorage = CreateConfigStorage(); try { AStorage->AccessMode = smReadWrite; AStorage->Explicit = Explicit; AStorage->ForceSave = FForceSave; // if saving to TOptionsStorage, make sure we save everything so that // all configuration is properly transferred to the master storage bool ConfigAll = All || AStorage->Temporary; DoSave(AStorage, ConfigAll); } __finally { delete AStorage; } Saved(); if (All) { StoredSessions->Save(true, Explicit); } // clean up as last, so that if it fails (read only INI), the saving can proceed if (Storage == stRegistry) { CleanupIniFile(); } SaveCustomIniFileStorageName(); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SaveCustomIniFileStorageName() { // Particularly, not to create an empty "Override" key, unless the custom INI file is ever set if (CustomIniFileStorageName != LoadCustomIniFileStorageName()) { std::unique_ptr RegistryStorage(new TRegistryStorage(GetRegistryStorageOverrideKey())); RegistryStorage->AccessMode = smReadWrite; RegistryStorage->Explicit = true; if (RegistryStorage->OpenRootKey(true)) { RegistryStorage->WriteString(L"IniFile", CustomIniFileStorageName); RegistryStorage->CloseSubKey(); } } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Export(const UnicodeString & FileName) { // not to "append" the export to an existing file if (FileExists(FileName)) { DeleteFileChecked(FileName); } THierarchicalStorage * Storage = NULL; THierarchicalStorage * ExportStorage = NULL; try { ExportStorage = TIniFileStorage::CreateFromPath(FileName); ExportStorage->AccessMode = smReadWrite; ExportStorage->Explicit = true; Storage = CreateConfigStorage(); Storage->AccessMode = smRead; CopyData(Storage, ExportStorage); DoSave(ExportStorage, true); } __finally { delete ExportStorage; delete Storage; } StoredSessions->Export(FileName); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Import(const UnicodeString & FileName) { THierarchicalStorage * Storage = NULL; THierarchicalStorage * ImportStorage = NULL; try { ImportStorage = TIniFileStorage::CreateFromPath(FileName); ImportStorage->AccessMode = smRead; Storage = CreateConfigStorage(); Storage->AccessMode = smReadWrite; Storage->Explicit = true; CopyData(ImportStorage, Storage); Default(); LoadFrom(ImportStorage); Storage->RecursiveDeleteSubKey(Configuration->StoredSessionsSubKey); if (ImportStorage->OpenSubKey(Configuration->StoredSessionsSubKey, false)) { StoredSessions->Clear(); StoredSessions->DefaultSettings->Default(); StoredSessions->Load(ImportStorage); } } __finally { delete ImportStorage; delete Storage; } // save all and explicit DoSave(true, true); FDontSave = true; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::LoadData(THierarchicalStorage * Storage) { #define KEYEX(TYPE, VAR, NAME) VAR = Storage->Read ## TYPE(NAME, VAR) #pragma warn -eas REGCONFIG(false); #pragma warn +eas #undef KEYEX if (Storage->OpenSubKey(L"Usage", false)) { FUsage->Load(Storage); Storage->CloseSubKey(); } if (FPermanentLogActions && FPermanentActionsLogFileName.IsEmpty() && FPermanentLogging && !FPermanentLogFileName.IsEmpty()) { FPermanentActionsLogFileName = FPermanentLogFileName; FPermanentLogging = false; FPermanentLogFileName = L""; } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::LoadAdmin(THierarchicalStorage * Storage) { FDisablePasswordStoring = Storage->ReadBool(L"DisablePasswordStoring", FDisablePasswordStoring); FForceBanners = Storage->ReadBool(L"ForceBanners", FForceBanners); FDisableAcceptingHostKeys = Storage->ReadBool(L"DisableAcceptingHostKeys", FDisableAcceptingHostKeys); FDefaultCollectUsage = Storage->ReadBool(L"DefaultCollectUsage", FDefaultCollectUsage); } //--------------------------------------------------------------------------- void TConfiguration::LoadSshHostCAList(TSshHostCAList * SshHostCAList, THierarchicalStorage * Storage) { if (Storage->OpenSubKey(SshHostCAsKey, false)) { SshHostCAList->Load(Storage); Storage->CloseSubKey(); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::LoadFrom(THierarchicalStorage * Storage) { if (Storage->OpenSubKey(ConfigurationSubKey, false)) { LoadData(Storage); Storage->CloseSubKey(); } LoadSshHostCAList(FSshHostCAList.get(), Storage); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetRegistryStorageOverrideKey() { return GetRegistryStorageKey() + L" Override"; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::LoadCustomIniFileStorageName() { UnicodeString Result; std::unique_ptr RegistryStorage(new TRegistryStorage(GetRegistryStorageOverrideKey())); if (RegistryStorage->OpenRootKey(false)) { Result = RegistryStorage->ReadString(L"IniFile", L""); RegistryStorage->CloseSubKey(); } RegistryStorage.reset(NULL); return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Load(THierarchicalStorage * Storage) { TGuard Guard(FCriticalSection); TStorageAccessMode StorageAccessMode = Storage->AccessMode; try { Storage->AccessMode = smRead; LoadFrom(Storage); } __finally { Storage->AccessMode = StorageAccessMode; } } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::CopySubKey(THierarchicalStorage * Source, THierarchicalStorage * Target, const UnicodeString & Name) { bool Result = Source->OpenSubKey(Name, false); if (Result) { Result = Target->OpenSubKey(Name, true); if (!Result) { Source->CloseSubKey(); } } return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CopyAllStringsInSubKey( THierarchicalStorage * Source, THierarchicalStorage * Target, const UnicodeString & Name) { if (CopySubKey(Source, Target, Name)) { std::unique_ptr Names(new TStringList()); Source->GetValueNames(Names.get()); for (int Index = 0; Index < Names->Count; Index++) { UnicodeString Buf = Source->ReadStringRaw(Names->Strings[Index], UnicodeString()); Target->WriteStringRaw(Names->Strings[Index], Buf); } Target->CloseSubKey(); Source->CloseSubKey(); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CopyData(THierarchicalStorage * Source, THierarchicalStorage * Target) { if (CopySubKey(Source, Target, ConfigurationSubKey)) { if (CopySubKey(Source, Target, CDCacheKey)) { std::unique_ptr Names(new TStringList()); Source->GetValueNames(Names.get()); for (int Index = 0; Index < Names->Count; Index++) { Target->WriteBinaryData(Names->Strings[Index], Source->ReadBinaryData(Names->Strings[Index])); } Target->CloseSubKey(); Source->CloseSubKey(); } CopyAllStringsInSubKey(Source, Target, BannersKey); CopyAllStringsInSubKey(Source, Target, LastFingerprintsStorageKey); Target->CloseSubKey(); Source->CloseSubKey(); } CopyAllStringsInSubKey(Source, Target, SshHostKeysSubKey); CopyAllStringsInSubKey(Source, Target, FtpsCertificateStorageKey); CopyAllStringsInSubKey(Source, Target, HttpsCertificateStorageKey); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::LoadDirectoryChangesCache(const UnicodeString SessionKey, TRemoteDirectoryChangesCache * DirectoryChangesCache) { THierarchicalStorage * Storage = CreateConfigStorage(); try { Storage->AccessMode = smRead; if (Storage->OpenSubKey(ConfigurationSubKey, false) && Storage->OpenSubKey(CDCacheKey, false) && Storage->ValueExists(SessionKey)) { DirectoryChangesCache->Deserialize(Storage->ReadBinaryData(SessionKey)); } } __finally { delete Storage; } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SaveDirectoryChangesCache(const UnicodeString SessionKey, TRemoteDirectoryChangesCache * DirectoryChangesCache) { THierarchicalStorage * Storage = CreateConfigStorage(); try { Storage->AccessMode = smReadWrite; if (Storage->OpenSubKey(ConfigurationSubKey, true) && Storage->OpenSubKey(CDCacheKey, true)) { UnicodeString Data; DirectoryChangesCache->Serialize(Data); Storage->WriteBinaryData(SessionKey, Data); } } __finally { delete Storage; } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::BannerHash(const UnicodeString & Banner) { RawByteString Result; Result.SetLength(16); md5checksum( reinterpret_cast(Banner.c_str()), Banner.Length() * sizeof(wchar_t), (unsigned char*)Result.c_str()); return BytesToHex(Result); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::GetBannerData( const UnicodeString & SessionKey, UnicodeString & BannerHash, unsigned int & Params) { BannerHash = UnicodeString(); Params = 0; std::unique_ptr Storage(CreateConfigStorage()); Storage->AccessMode = smRead; if (Storage->OpenSubKey(ConfigurationSubKey, false) && Storage->OpenSubKey(BannersKey, false)) { UnicodeString S = Storage->ReadString(SessionKey, L""); BannerHash = CutToChar(S, L',', true); Params = StrToIntDef(L"$" + CutToChar(S, L',', true), 0); } } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::ShowBanner( const UnicodeString & SessionKey, const UnicodeString & Banner, unsigned int & Params) { UnicodeString StoredBannerHash; GetBannerData(SessionKey, StoredBannerHash, Params); bool Result = (StoredBannerHash != BannerHash(Banner)); return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetBannerData( const UnicodeString & SessionKey, const UnicodeString & BannerHash, unsigned int Params) { std::unique_ptr Storage(CreateConfigStorage()); Storage->AccessMode = smReadWrite; if (Storage->OpenSubKey(ConfigurationSubKey, true) && Storage->OpenSubKey(BannersKey, true)) { Storage->WriteString(SessionKey, BannerHash + L"," + UIntToStr(Params)); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::NeverShowBanner(const UnicodeString & SessionKey, const UnicodeString & Banner) { UnicodeString DummyBannerHash; unsigned int Params; GetBannerData(SessionKey, DummyBannerHash, Params); SetBannerData(SessionKey, BannerHash(Banner), Params); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetBannerParams(const UnicodeString & SessionKey, unsigned int Params) { UnicodeString BannerHash; unsigned int DummyParams; GetBannerData(SessionKey, BannerHash, DummyParams); SetBannerData(SessionKey, BannerHash, Params); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::FormatFingerprintKey(const UnicodeString & SiteKey, const UnicodeString & FingerprintType) { return FORMAT(L"%s:%s", (SiteKey, FingerprintType)); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::RememberLastFingerprint(const UnicodeString & SiteKey, const UnicodeString & FingerprintType, const UnicodeString & Fingerprint) { std::unique_ptr Storage(CreateConfigStorage()); Storage->AccessMode = smReadWrite; if (Storage->OpenSubKey(ConfigurationSubKey, true) && Storage->OpenSubKey(LastFingerprintsStorageKey, true)) { UnicodeString FingerprintKey = FormatFingerprintKey(SiteKey, FingerprintType); Storage->WriteString(FingerprintKey, Fingerprint); } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::LastFingerprint(const UnicodeString & SiteKey, const UnicodeString & FingerprintType) { UnicodeString Result; std::unique_ptr Storage(CreateConfigStorage()); Storage->AccessMode = smRead; if (Storage->OpenSubKey(ConfigurationSubKey, false) && Storage->OpenSubKey(LastFingerprintsStorageKey, false)) { UnicodeString FingerprintKey = FormatFingerprintKey(SiteKey, FingerprintType); Result = Storage->ReadString(FingerprintKey, L""); } return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Changed() { TNotifyEvent AOnChange = NULL; { TGuard Guard(FCriticalSection); if (FUpdating == 0) { AOnChange = OnChange; } else { FChanged = true; } } // No specific reason to call this outside of a guard, just that it is less of a change to a previous unguarded code if (AOnChange != NULL) { AOnChange(this); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::BeginUpdate() { FCriticalSection->Enter(); if (FUpdating == 0) { FChanged = false; } FUpdating++; // Greater value would probably indicate some nesting problem in code DebugAssert(FUpdating < 6); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::EndUpdate() { DebugAssert(FUpdating > 0); FUpdating--; if ((FUpdating == 0) && FChanged) { FChanged = false; Changed(); } FCriticalSection->Leave(); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CleanupConfiguration() { try { CleanupRegistry(ConfigurationSubKey); if (Storage == stRegistry) { FDontSave = true; } } catch (Exception &E) { throw ExtException(&E, LoadStr(CLEANUP_CONFIG_ERROR)); } } //--------------------------------------------------------------------------- bool TConfiguration::RegistryPathExists(const UnicodeString & RegistryPath) { std::unique_ptr Registry(new TRegistryStorage(RegistryStorageKey)); UnicodeString ParentKey = ExtractFileDir(RegistryPath); UnicodeString SubKey = ExtractFileName(RegistryPath); return Registry->OpenRootKey(false) && (ParentKey.IsEmpty() || Registry->OpenSubKeyPath(ParentKey, false)) && Registry->KeyExists(SubKey); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CleanupRegistry(const UnicodeString & RegistryPath) { std::unique_ptr Registry(new TRegistryStorage(RegistryStorageKey)); UnicodeString ParentKey = ExtractFileDir(RegistryPath); if (ParentKey.IsEmpty() || Registry->OpenSubKeyPath(ParentKey, false)) { UnicodeString SubKey = ExtractFileName(RegistryPath); Registry->RecursiveDeleteSubKey(SubKey); } } //--------------------------------------------------------------------------- TStrings * TConfiguration::GetCaches() { std::unique_ptr Result(new TStringList()); Result->Add(SshHostKeysSubKey); Result->Add(FtpsCertificateStorageKey); Result->Add(HttpsCertificateStorageKey); Result->Add(DirectoryStatisticsCacheKey); Result->Add(TPath::Combine(ConfigurationSubKey, CDCacheKey)); Result->Add(TPath::Combine(ConfigurationSubKey, BannersKey)); Result->Add(TPath::Combine(ConfigurationSubKey, LastFingerprintsStorageKey)); return Result.release(); } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::HasAnyCache() { bool Result = false; std::unique_ptr Caches(GetCaches()); for (int Index = 0; Index < Caches->Count; Index++) { if (RegistryPathExists(Caches->Strings[Index])) { Result = true; break; } } return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CleanupCaches() { try { std::unique_ptr Caches(GetCaches()); for (int Index = 0; Index < Caches->Count; Index++) { CleanupRegistry(Caches->Strings[Index]); } } catch (Exception & E) { throw ExtException(&E, LoadStr(CLEANUP_CACHES_ERROR)); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CleanupRandomSeedFile() { try { DontSaveRandomSeed(); if (FileExists(ApiPath(RandomSeedFileName))) { DeleteFileChecked(RandomSeedFileName); } } catch (Exception &E) { throw ExtException(&E, LoadStr(CLEANUP_SEEDFILE_ERROR)); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::CleanupIniFile() { try { if (FileExists(ApiPath(IniFileStorageNameForReading))) { DeleteFileChecked(IniFileStorageNameForReading); } if (Storage == stIniFile) { FDontSave = true; } } catch (Exception &E) { throw ExtException(&E, LoadStr(CLEANUP_INIFILE_ERROR)); } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::DontSave() { FDontSave = true; } //--------------------------------------------------------------------------- RawByteString __fastcall TConfiguration::EncryptPassword(UnicodeString Password, UnicodeString Key) { if (Password.IsEmpty()) { return RawByteString(); } else { return ::EncryptPassword(Password, Key); } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::DecryptPassword(RawByteString Password, UnicodeString Key) { if (Password.IsEmpty()) { return UnicodeString(); } else { return ::DecryptPassword(Password, Key); } } //--------------------------------------------------------------------------- RawByteString __fastcall TConfiguration::StronglyRecryptPassword(RawByteString Password, UnicodeString /*Key*/) { return Password; } //--------------------------------------------------------------------------- TVSFixedFileInfo *__fastcall TConfiguration::GetFixedApplicationInfo() { return GetFixedFileInfo(ApplicationInfo); } //--------------------------------------------------------------------------- int __fastcall TConfiguration::GetCompoundVersion() { TVSFixedFileInfo * FileInfo = FixedApplicationInfo; return CalculateCompoundVersion( HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS), HIWORD(FileInfo->dwFileVersionLS)); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::ModuleFileName() { return ParamStr(0); } //--------------------------------------------------------------------------- void * __fastcall TConfiguration::GetFileApplicationInfo(const UnicodeString FileName) { void * Result; if (FileName.IsEmpty()) { if (!FApplicationInfo) { FApplicationInfo = CreateFileInfo(ModuleFileName()); } Result = FApplicationInfo; } else { Result = CreateFileInfo(FileName); } return Result; } //--------------------------------------------------------------------------- void * __fastcall TConfiguration::GetApplicationInfo() { return GetFileApplicationInfo(""); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileProductName(const UnicodeString FileName) { return GetFileFileInfoString(L"ProductName", FileName); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileCompanyName(const UnicodeString FileName) { // particularly in IDE build, company name is empty return GetFileFileInfoString(L"CompanyName", FileName, true); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetProductName() { return GetFileProductName(L""); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetCompanyName() { return GetFileCompanyName(L""); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileProductVersion(const UnicodeString FileName) { return TrimVersion(GetFileFileInfoString(L"ProductVersion", FileName)); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileDescription(const UnicodeString & FileName) { return GetFileFileInfoString(L"FileDescription", FileName); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetProductVersion() { return GetFileProductVersion(L""); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetReleaseType() { return GetFileInfoString(L"ReleaseType"); } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetIsUnofficial() { #ifdef BUILD_OFFICIAL return false; #else return true; #endif } //--------------------------------------------------------------------------- static TDateTime GetBuildDate() { UnicodeString BuildDateStr = __DATE__; UnicodeString MonthStr = CutToChar(BuildDateStr, L' ', true); int Month = ParseShortEngMonthName(MonthStr); int Day = StrToInt(CutToChar(BuildDateStr, L' ', true)); int Year = StrToInt(Trim(BuildDateStr)); TDateTime Result = EncodeDateVerbose(static_cast(Year), static_cast(Month), static_cast(Day)); return Result; } //--------------------------------------------------------------------------- UnicodeString TConfiguration::GetFullVersion() { UnicodeString Result = Version; UnicodeString AReleaseType = GetReleaseType(); if (DebugAlwaysTrue(!AReleaseType.IsEmpty()) && !SameText(AReleaseType, L"stable") && !SameText(AReleaseType, L"development")) { Result += L" " + AReleaseType; } return Result; } //--------------------------------------------------------------------------- static UnicodeString GetUnofficialBuildTag() { DebugAssert(Configuration->IsUnofficial); UnicodeString Result; #ifdef _DEBUG Result = LoadStr(VERSION_DEBUG_BUILD); #else Result = LoadStr(VERSION_DEV_BUILD); #endif return Result; } //--------------------------------------------------------------------------- UnicodeString TConfiguration::GetVersionStrHuman() { TGuard Guard(FCriticalSection); try { TDateTime BuildDate = GetBuildDate(); UnicodeString FullVersion = GetFullVersion(); if (IsUnofficial) { UnicodeString BuildStr = GetUnofficialBuildTag(); FullVersion += L" " + BuildStr; } UnicodeString DateStr; TDateTime ANow = Now(); if (BuildDate < ANow) { DateStr = FormatRelativeTime(ANow, BuildDate, true); } else { DateStr = FormatDateTime(L"ddddd", BuildDate); } UnicodeString Result = FORMAT(L"%s (%s)", (FullVersion, DateStr)); return Result; } catch (Exception &E) { throw ExtException(&E, L"Can't get application version"); } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetVersionStr() { TGuard Guard(FCriticalSection); try { UnicodeString BuildStr; if (!IsUnofficial) { BuildStr = LoadStr(VERSION_BUILD); } else { BuildStr = GetUnofficialBuildTag(); } UnicodeString FullVersion = GetFullVersion(); int Build = LOWORD(FixedApplicationInfo->dwFileVersionLS); if (Build > 0) { BuildStr += L" " + IntToStr(Build); } UnicodeString DateStr = FormatDateTime(L"yyyy-mm-dd", GetBuildDate()); AddToList(BuildStr, DateStr, L" "); UnicodeString Result = FMTLOAD(VERSION2, (FullVersion, BuildStr)); if (IsUnofficial) { Result += L" " + LoadStr(VERSION_DONT_DISTRIBUTE); } return Result; } catch (Exception &E) { throw ExtException(&E, L"Can't get application version"); } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileVersion(const UnicodeString & FileName) { UnicodeString Result; void * FileInfo = CreateFileInfo(FileName); try { Result = GetFileVersion(GetFixedFileInfo(FileInfo)); } __finally { FreeFileInfo(FileInfo); } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileVersion(TVSFixedFileInfo * Info) { TGuard Guard(FCriticalSection); try { UnicodeString Result = FormatVersion( HIWORD(Info->dwFileVersionMS), LOWORD(Info->dwFileVersionMS), HIWORD(Info->dwFileVersionLS)); return Result; } catch (Exception &E) { throw ExtException(&E, L"Can't get file version"); } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetVersion() { return GetFileVersion(FixedApplicationInfo); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileFileInfoString(const UnicodeString Key, const UnicodeString FileName, bool AllowEmpty) { TGuard Guard(FCriticalSection); UnicodeString Result; void * Info = GetFileApplicationInfo(FileName); try { if ((Info != NULL) && (GetTranslationCount(Info) > 0)) { TTranslation Translation; Translation = GetTranslation(Info, 0); Result = ::GetFileInfoString(Info, Translation, Key, AllowEmpty); } else { DebugAssert(!FileName.IsEmpty()); } } __finally { if (!FileName.IsEmpty()) { FreeFileInfo(Info); } } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileInfoString(const UnicodeString Key) { return GetFileFileInfoString(Key, L""); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetFileMimeType(const UnicodeString & FileName) { UnicodeString Result; bool Found = false; if (!MimeTypes.IsEmpty()) { UnicodeString FileNameOnly = ExtractFileName(FileName); UnicodeString AMimeTypes = MimeTypes; while (!Found && !AMimeTypes.IsEmpty()) { UnicodeString Token = CutToChar(AMimeTypes, L',', true); UnicodeString MaskStr = CutToChar(Token, L'=', true); TFileMasks Mask(MaskStr); if (Mask.MatchesFileName(FileNameOnly)) { Result = Token.Trim(); Found = true; } } } if (!Found) // allow an override to "no" Content-Type { Result = ::GetFileMimeType(FileName); } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetRegistryStorageKey() { return GetRegistryKey(); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetNulStorage() { FStorage = stNul; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetExplicitIniFileStorageName(const UnicodeString & FileName) { FIniFileStorageName = FileName; FStorage = stIniFile; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetDefaultIniFileExportPath() { UnicodeString PersonalDirectory = GetPersonalFolder(); UnicodeString FileName = IncludeTrailingBackslash(PersonalDirectory) + ExtractFileName(ExpandEnvironmentVariables(IniFileStorageName)); return FileName; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetIniFileStorageNameForReading() { return GetIniFileStorageName(true); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetIniFileStorageNameForReadingWriting() { return GetIniFileStorageName(false); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetAutomaticIniFileStorageName(bool ReadingOnly) { UnicodeString ProgramPath = ParamStr(0); UnicodeString ProgramIniPath = ChangeFileExt(ProgramPath, L".ini"); UnicodeString IniPath; if (FileExists(ApiPath(ProgramIniPath))) { IniPath = ProgramIniPath; } else { UnicodeString AppDataIniPath = IncludeTrailingBackslash(GetShellFolderPath(CSIDL_APPDATA)) + ExtractFileName(ProgramIniPath); if (FileExists(ApiPath(AppDataIniPath))) { IniPath = AppDataIniPath; } else { // avoid expensive test if we are interested in existing files only if (!ReadingOnly && (FProgramIniPathWrittable < 0)) { UnicodeString ProgramDir = ExtractFilePath(ProgramPath); FProgramIniPathWrittable = IsDirectoryWriteable(ProgramDir) ? 1 : 0; } // does not really matter what we return when < 0 IniPath = (FProgramIniPathWrittable == 0) ? AppDataIniPath : ProgramIniPath; } } // BACKWARD COMPATIBILITY with 4.x if (FVirtualIniFileStorageName.IsEmpty() && TPath::IsDriveRooted(IniPath)) { UnicodeString LocalAppDataPath = GetShellFolderPath(CSIDL_LOCAL_APPDATA); // virtual store for non-system drives have a different virtual store, // do not bother about them if (TPath::IsDriveRooted(LocalAppDataPath) && SameText(ExtractFileDrive(IniPath), ExtractFileDrive(LocalAppDataPath))) { FVirtualIniFileStorageName = IncludeTrailingBackslash(LocalAppDataPath) + L"VirtualStore\\" + IniPath.SubString(4, IniPath.Length() - 3); } } if (!FVirtualIniFileStorageName.IsEmpty() && FileExists(ApiPath(FVirtualIniFileStorageName))) { return FVirtualIniFileStorageName; } else { return IniPath; } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetIniFileParamValue() { UnicodeString Result; if (Storage == stNul) { Result = INI_NUL; } else if ((Storage == stIniFile) && !FIniFileStorageName.IsEmpty()) { Result = FIniFileStorageName; } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetIniFileStorageName(bool ReadingOnly) { UnicodeString Result; if (!FIniFileStorageName.IsEmpty()) { Result = FIniFileStorageName; } else if (!FCustomIniFileStorageName.IsEmpty()) { Result = ExpandEnvironmentVariables(FCustomIniFileStorageName); } else { Result = GetAutomaticIniFileStorageName(ReadingOnly); } return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetOptionsStorage(TStrings * value) { TGuard Guard(FCriticalSection); if (FOptionsStorage.get() == NULL) { FOptionsStorage.reset(new TStringList()); } FOptionsStorage->AddStrings(value); } //--------------------------------------------------------------------------- TStrings * __fastcall TConfiguration::GetOptionsStorage() { return FOptionsStorage.get(); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetPuttySessionsSubKey() { return StoredSessionsSubKey; } //--------------------------------------------------------------------------- UnicodeString TConfiguration::GetPuttySessionsKey(const UnicodeString & RootKey) { return RootKey + L"\\" + PuttySessionsSubKey; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::DoGetPuttySessionsKey() { return GetPuttySessionsKey(PuttyRegistryStorageKey); } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetStoredSessionsSubKey() { return L"Sessions"; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetSshHostKeysSubKey() { return L"SshHostKeys"; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetConfigurationSubKey() { return L"Configuration"; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetRootKeyStr() { return RootKeyToStr(HKEY_CURRENT_USER); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::MoveStorage(TStorage AStorage, const UnicodeString & ACustomIniFileStorageName) { if ((FStorage != AStorage) || ((FStorage == stIniFile) && !FIniFileStorageName.IsEmpty()) || // Not expanding, as we want to allow change from explicit path to path with variables and vice versa !IsPathToSameFile(FCustomIniFileStorageName, ACustomIniFileStorageName)) { TStorage StorageBak = FStorage; UnicodeString CustomIniFileStorageNameBak = FCustomIniFileStorageName; UnicodeString IniFileStorageNameBak = FIniFileStorageName; try { THierarchicalStorage * SourceStorage = NULL; THierarchicalStorage * TargetStorage = NULL; try { SourceStorage = CreateConfigStorage(); SourceStorage->AccessMode = smRead; FStorage = AStorage; FCustomIniFileStorageName = ACustomIniFileStorageName; FIniFileStorageName = UnicodeString(); TargetStorage = CreateConfigStorage(); TargetStorage->AccessMode = smReadWrite; TargetStorage->Explicit = true; // copy before save as it removes the ini file, // when switching from ini to registry CopyData(SourceStorage, TargetStorage); } __finally { delete SourceStorage; delete TargetStorage; } // save all and explicit, // this also removes an INI file, when switching to registry storage DoSave(true, true); } catch (...) { // If this fails, do not pretend that storage was switched. // For instance: // - When writing to an INI file fails (unlikely, as we fallback to user profile) // - When removing INI file fails, when switching to registry // (possible, when the INI file is in Program Files folder) FStorage = StorageBak; FCustomIniFileStorageName = CustomIniFileStorageNameBak; FIniFileStorageName = IniFileStorageNameBak; throw; } } } //--------------------------------------------------------------------------- void __fastcall TConfiguration::ScheduleCustomIniFileStorageUse(const UnicodeString & ACustomIniFileStorageName) { FStorage = stIniFile; FCustomIniFileStorageName = ACustomIniFileStorageName; SaveCustomIniFileStorageName(); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::Saved() { // nothing } //--------------------------------------------------------------------------- TStorage __fastcall TConfiguration::GetStorage() { TGuard Guard(FCriticalSection); if (FStorage == stDetect) { if (FileExists(ApiPath(IniFileStorageNameForReading))) { FStorage = stIniFile; } else { FStorage = stRegistry; } } return FStorage; } //--------------------------------------------------------------------- static TStoredSessionList * CreateSessionsForImport(TStoredSessionList * Sessions) { std::unique_ptr Result(new TStoredSessionList(true)); Result->DefaultSettings = Sessions->DefaultSettings; return Result.release(); } //--------------------------------------------------------------------- TStoredSessionList * __fastcall TConfiguration::SelectFilezillaSessionsForImport( TStoredSessionList * Sessions, UnicodeString & Error) { std::unique_ptr ImportSessionList(CreateSessionsForImport(Sessions)); UnicodeString AppDataPath = GetShellFolderPath(CSIDL_APPDATA); UnicodeString FilezillaSiteManagerFile = TPath::Combine(AppDataPath, L"FileZilla\\sitemanager.xml"); UnicodeString FilezillaConfigurationFile = TPath::Combine(AppDataPath, L"FileZilla\\filezilla.xml"); if (FileExists(ApiPath(FilezillaSiteManagerFile))) { ImportSessionList->ImportFromFilezilla(FilezillaSiteManagerFile, FilezillaConfigurationFile); if (ImportSessionList->Count > 0) { ImportSessionList->SelectSessionsToImport(Sessions, true); } else { Error = FMTLOAD(FILEZILLA_NO_SITES, (FilezillaSiteManagerFile)); } } else { Error = FMTLOAD(FILEZILLA_SITE_MANAGER_NOT_FOUND, (FilezillaSiteManagerFile)); } return ImportSessionList.release(); } //--------------------------------------------------------------------- bool __fastcall TConfiguration::AnyFilezillaSessionForImport(TStoredSessionList * Sessions) { try { UnicodeString Error; std::unique_ptr SessionsForImport(SelectFilezillaSessionsForImport(Sessions, Error)); return (SessionsForImport->Count > 0); } catch (...) { return false; } } //--------------------------------------------------------------------- static UnicodeString GetOpensshFolder() { UnicodeString ProfilePath = GetShellFolderPath(CSIDL_PROFILE); UnicodeString Result = TPath::Combine(ProfilePath, OpensshFolderName); return Result; } //--------------------------------------------------------------------- TStoredSessionList * __fastcall TConfiguration::SelectKnownHostsSessionsForImport( TStoredSessionList * Sessions, UnicodeString & Error) { std::unique_ptr ImportSessionList(CreateSessionsForImport(Sessions)); UnicodeString KnownHostsFile = TPath::Combine(GetOpensshFolder(), L"known_hosts"); try { if (FileExists(ApiPath(KnownHostsFile))) { std::unique_ptr Lines(new TStringList()); LoadScriptFromFile(KnownHostsFile, Lines.get(), true); ImportSessionList->ImportFromKnownHosts(Lines.get()); } else { throw Exception(LoadStr(KNOWN_HOSTS_NOT_FOUND)); } } catch (Exception & E) { Error = FORMAT(L"%s\n(%s)", (E.Message, KnownHostsFile)); } return ImportSessionList.release(); } //--------------------------------------------------------------------- TStoredSessionList * __fastcall TConfiguration::SelectKnownHostsSessionsForImport( TStrings * Lines, TStoredSessionList * Sessions, UnicodeString & Error) { std::unique_ptr ImportSessionList(CreateSessionsForImport(Sessions)); try { ImportSessionList->ImportFromKnownHosts(Lines); } catch (Exception & E) { Error = E.Message; } return ImportSessionList.release(); } //--------------------------------------------------------------------------- TStoredSessionList * TConfiguration::SelectOpensshSessionsForImport( TStoredSessionList * Sessions, UnicodeString & Error) { std::unique_ptr ImportSessionList(CreateSessionsForImport(Sessions)); UnicodeString ConfigFile = TPath::Combine(GetOpensshFolder(), L"config"); try { if (FileExists(ApiPath(ConfigFile))) { std::unique_ptr Lines(new TStringList()); LoadScriptFromFile(ConfigFile, Lines.get(), true); ImportSessionList->ImportFromOpenssh(Lines.get()); if (ImportSessionList->Count > 0) { ImportSessionList->SelectSessionsToImport(Sessions, true); } else { throw Exception(LoadStr(OPENSSH_CONFIG_NO_SITES)); } } else { throw Exception(LoadStr(OPENSSH_CONFIG_NOT_FOUND)); } } catch (Exception & E) { Error = FORMAT(L"%s\n(%s)", (E.Message, ConfigFile)); } return ImportSessionList.release(); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetRandomSeedFile(UnicodeString value) { if (RandomSeedFile != value) { UnicodeString PrevRandomSeedFileName = RandomSeedFileName; FRandomSeedFile = value; // never allow empty seed file to avoid Putty trying to reinitialize the path if (RandomSeedFileName.IsEmpty()) { FRandomSeedFile = FDefaultRandomSeedFile; } if (!PrevRandomSeedFileName.IsEmpty() && (PrevRandomSeedFileName != RandomSeedFileName) && FileExists(ApiPath(PrevRandomSeedFileName))) { // ignore any error DeleteFile(ApiPath(PrevRandomSeedFileName)); } } } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetDirectoryStatisticsCacheKey( const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam) { std::unique_ptr RawOptions(new TStringList()); RawOptions->Add(SessionKey); RawOptions->Add(UnixExcludeTrailingBackslash(Path)); TCopyParamType Defaults; TCopyParamType FilterCopyParam; FilterCopyParam.IncludeFileMask = CopyParam.IncludeFileMask; FilterCopyParam.ExcludeHiddenFiles = CopyParam.ExcludeHiddenFiles; FilterCopyParam.ExcludeEmptyDirectories = CopyParam.ExcludeEmptyDirectories; std::unique_ptr OptionsStorage(new TOptionsStorage(RawOptions.get(), true)); FilterCopyParam.Save(OptionsStorage.get(), &Defaults); UTF8String RawOptionsBuf(RawOptions->CommaText.LowerCase()); UnicodeString Result = Sha256(RawOptionsBuf.c_str(), RawOptionsBuf.Length()); return Result; } //--------------------------------------------------------------------------- THierarchicalStorage * TConfiguration::OpenDirectoryStatisticsCache(bool CanCreate) { std::unique_ptr Storage(Configuration->CreateConfigStorage()); Storage->AccessMode = CanCreate ? smReadWrite : smRead; if (!Storage->OpenSubKey(DirectoryStatisticsCacheKey, CanCreate)) { Storage.reset(NULL); } return Storage.release(); } //--------------------------------------------------------------------------- TStrings * __fastcall TConfiguration::LoadDirectoryStatisticsCache( const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam) { std::unique_ptr Storage(OpenDirectoryStatisticsCache(false)); TStrings * Result; if (Storage.get() != NULL) { UnicodeString Key = GetDirectoryStatisticsCacheKey(SessionKey, Path, CopyParam); UnicodeString Buf = Storage->ReadString(Key, UnicodeString()); Result = CommaTextToStringList(Buf); } else { Result = new TStringList(); } return Result; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SaveDirectoryStatisticsCache( const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam, TStrings * DataList) { std::unique_ptr Storage(OpenDirectoryStatisticsCache(true)); if (Storage.get() != NULL) { UnicodeString Key = GetDirectoryStatisticsCacheKey(SessionKey, Path, CopyParam); UnicodeString Buf = DataList->CommaText; Storage->WriteString(Key, Buf); } } //--------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetRandomSeedFileName() { // StripPathQuotes should not be needed as we do not feed quotes anymore return StripPathQuotes(ExpandEnvironmentVariables(FRandomSeedFile)).Trim(); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetExternalIpAddress(UnicodeString value) { SET_CONFIG_PROPERTY(ExternalIpAddress); } //--------------------------------------------------------------------- bool TConfiguration::HasLocalPortNumberLimits() { return (LocalPortNumberMin > 0) && (LocalPortNumberMax >= LocalPortNumberMin); } //--------------------------------------------------------------------- void TConfiguration::SetLocalPortNumberMin(int value) { SET_CONFIG_PROPERTY(LocalPortNumberMin); } //--------------------------------------------------------------------- void TConfiguration::SetLocalPortNumberMax(int value) { SET_CONFIG_PROPERTY(LocalPortNumberMax); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetMimeTypes(UnicodeString value) { SET_CONFIG_PROPERTY(MimeTypes); } //--------------------------------------------------------------------- void TConfiguration::SetCertificateStorage(const UnicodeString & value) { SET_CONFIG_PROPERTY(CertificateStorage); } //--------------------------------------------------------------------- UnicodeString TConfiguration::GetCertificateStorageExpanded() { UnicodeString Result = FCertificateStorage; if (Result.IsEmpty()) { UnicodeString DefaultCertificateStorage = TPath::Combine(ExtractFilePath(ModuleFileName()), L"cacert.pem"); if (FileExists(DefaultCertificateStorage)) { Result = DefaultCertificateStorage; } } return Result; } //--------------------------------------------------------------------- void TConfiguration::SetAWSMetadataService(const UnicodeString & value) { SET_CONFIG_PROPERTY(AWSMetadataService); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetTryFtpWhenSshFails(bool value) { SET_CONFIG_PROPERTY(TryFtpWhenSshFails); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetParallelDurationThreshold(int value) { SET_CONFIG_PROPERTY(ParallelDurationThreshold); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetPuttyRegistryStorageKey(UnicodeString value) { SET_CONFIG_PROPERTY_EX(PuttyRegistryStorageKey, RefreshPuttySshHostCAList()); } //--------------------------------------------------------------------------- TEOLType __fastcall TConfiguration::GetLocalEOLType() { return eolCRLF; } //--------------------------------------------------------------------- bool __fastcall TConfiguration::GetCollectUsage() { return FUsage->Collect; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetCollectUsage(bool value) { FUsage->Collect = value; } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryLogging(const UnicodeString ALogFileName) { if (SameText(ExtractFileExt(ALogFileName), L".xml")) { TemporaryActionsLogging(ALogFileName); } else { FLogging = true; FLogFileName = ALogFileName; UpdateActualLogProtocol(); } } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryActionsLogging(const UnicodeString ALogFileName) { FLogActions = true; FActionsLogFileName = ALogFileName; } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryLogProtocol(int ALogProtocol) { FLogProtocol = ALogProtocol; UpdateActualLogProtocol(); } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryLogSensitive(bool ALogSensitive) { FLogSensitive = ALogSensitive; } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryLogMaxSize(__int64 ALogMaxSize) { FLogMaxSize = ALogMaxSize; } //--------------------------------------------------------------------- void __fastcall TConfiguration::TemporaryLogMaxCount(int ALogMaxCount) { FLogMaxCount = ALogMaxCount; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogging(bool value) { TGuard Guard(FCriticalSection); if (Logging != value) { FPermanentLogging = value; FLogging = value; UpdateActualLogProtocol(); Changed(); } } //--------------------------------------------------------------------- bool __fastcall TConfiguration::GetLogging() { TGuard Guard(FCriticalSection); return FPermanentLogging; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogFileName(UnicodeString value) { TGuard Guard(FCriticalSection); if (LogFileName != value) { FPermanentLogFileName = value; FLogFileName = value; Changed(); } } //--------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetLogFileName() { TGuard Guard(FCriticalSection); return FPermanentLogFileName; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetActionsLogFileName(UnicodeString value) { TGuard Guard(FCriticalSection); if (ActionsLogFileName != value) { FPermanentActionsLogFileName = value; FActionsLogFileName = value; Changed(); } } //--------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetPermanentActionsLogFileName() { TGuard Guard(FCriticalSection); return FPermanentActionsLogFileName; } //--------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetActionsLogFileName() { TGuard Guard(FCriticalSection); return FActionsLogFileName; } //--------------------------------------------------------------------- bool __fastcall TConfiguration::GetLogToFile() { // guarded within GetLogFileName return !LogFileName.IsEmpty(); } //--------------------------------------------------------------------- void __fastcall TConfiguration::UpdateActualLogProtocol() { FActualLogProtocol = FLogging ? FLogProtocol : (-BelowNormalLogLevels - 1); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogProtocol(int value) { TGuard Guard(FCriticalSection); if (LogProtocol != value) { FPermanentLogProtocol = value; FLogProtocol = value; Changed(); UpdateActualLogProtocol(); } } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogActions(bool value) { TGuard Guard(FCriticalSection); if (LogActions != value) { FPermanentLogActions = value; FLogActions = value; Changed(); } } //--------------------------------------------------------------------- bool __fastcall TConfiguration::GetLogActions() { TGuard Guard(FCriticalSection); return FPermanentLogActions; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogFileAppend(bool value) { SET_CONFIG_PROPERTY(LogFileAppend); } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogSensitive(bool value) { if (LogSensitive != value) { FPermanentLogSensitive = value; FLogSensitive = value; Changed(); } } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogMaxSize(__int64 value) { TGuard Guard(FCriticalSection); if (LogMaxSize != value) { FPermanentLogMaxSize = value; FLogMaxSize = value; Changed(); } } //--------------------------------------------------------------------- __int64 __fastcall TConfiguration::GetLogMaxSize() { TGuard Guard(FCriticalSection); return FPermanentLogMaxSize; } //--------------------------------------------------------------------- void __fastcall TConfiguration::SetLogMaxCount(int value) { if (LogMaxCount != value) { FPermanentLogMaxCount = value; FLogMaxCount = value; Changed(); } } //--------------------------------------------------------------------- int __fastcall TConfiguration::GetLogMaxCount() { TGuard Guard(FCriticalSection); return FPermanentLogMaxCount; } //--------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetDefaultLogFileName() { return L"%TEMP%\\!S.log"; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetConfirmOverwriting(bool value) { TGuard Guard(FCriticalSection); SET_CONFIG_PROPERTY(ConfirmOverwriting); } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetConfirmOverwriting() { TGuard Guard(FCriticalSection); return FConfirmOverwriting; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetConfirmResume(bool value) { TGuard Guard(FCriticalSection); SET_CONFIG_PROPERTY(ConfirmResume); } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetConfirmResume() { TGuard Guard(FCriticalSection); return FConfirmResume; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetAutoReadDirectoryAfterOp(bool value) { TGuard Guard(FCriticalSection); SET_CONFIG_PROPERTY(AutoReadDirectoryAfterOp); } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetAutoReadDirectoryAfterOp() { TGuard Guard(FCriticalSection); return FAutoReadDirectoryAfterOp; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetTimeFormat() { return L"h:nn:ss"; } //--------------------------------------------------------------------------- UnicodeString __fastcall TConfiguration::GetDefaultKeyFile() { return L""; } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetRememberPassword() { return false; } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetSessionReopenAuto(int value) { SET_CONFIG_PROPERTY(SessionReopenAuto); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetSessionReopenBackground(int value) { SET_CONFIG_PROPERTY(SessionReopenBackground); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetSessionReopenTimeout(int value) { SET_CONFIG_PROPERTY(SessionReopenTimeout); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetSessionReopenAutoStall(int value) { SET_CONFIG_PROPERTY(SessionReopenAutoStall); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetTunnelLocalPortNumberLow(int value) { SET_CONFIG_PROPERTY(TunnelLocalPortNumberLow); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetTunnelLocalPortNumberHigh(int value) { SET_CONFIG_PROPERTY(TunnelLocalPortNumberHigh); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetCacheDirectoryChangesMaxSize(int value) { SET_CONFIG_PROPERTY(CacheDirectoryChangesMaxSize); } //--------------------------------------------------------------------------- void __fastcall TConfiguration::SetShowFtpWelcomeMessage(bool value) { SET_CONFIG_PROPERTY(ShowFtpWelcomeMessage); } //--------------------------------------------------------------------------- void TConfiguration::SetQueueTransfersLimit(int value) { SET_CONFIG_PROPERTY(QueueTransfersLimit); } //--------------------------------------------------------------------------- const TSshHostCAList * TConfiguration::GetSshHostCAList() { return FSshHostCAList.get(); } //--------------------------------------------------------------------------- void TConfiguration::SetSshHostCAList(const TSshHostCAList * value) { *FSshHostCAList = *value; } //--------------------------------------------------------------------------- const TSshHostCAList * TConfiguration::GetPuttySshHostCAList() { if (FPuttySshHostCAList.get() == NULL) { std::unique_ptr Storage(new TRegistryStorage(PuttyRegistryStorageKey)); Storage->ConfigureForPutty(); FPuttySshHostCAList.reset(new TSshHostCAList()); LoadSshHostCAList(FPuttySshHostCAList.get(), Storage.get()); } return FPuttySshHostCAList.get(); } //--------------------------------------------------------------------------- void TConfiguration::RefreshPuttySshHostCAList() { FPuttySshHostCAList.reset(NULL); } //--------------------------------------------------------------------------- const TSshHostCAList * TConfiguration::GetActiveSshHostCAList() { return FSshHostCAsFromPuTTY ? PuttySshHostCAList : SshHostCAList; } //--------------------------------------------------------------------------- bool __fastcall TConfiguration::GetPersistent() { return (Storage != stNul) && !FDontSave; } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TShortCuts::Add(TShortCut ShortCut) { FShortCuts.insert(ShortCut); } //--------------------------------------------------------------------------- bool __fastcall TShortCuts::Has(TShortCut ShortCut) const { return (FShortCuts.count(ShortCut) != 0); }