1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Individual configuration storage sections can be set read-only

Source commit: 832ea7efbd56d8520fb2073c95705db4bec6d3c3
This commit is contained in:
Martin Prikryl
2019-07-09 16:55:31 +00:00
parent bca358e5b4
commit 7861d4c851
2 changed files with 178 additions and 12 deletions

View File

@@ -102,6 +102,9 @@ UnicodeString __fastcall UnMungeIniName(const UnicodeString & Str)
}
}
//===========================================================================
UnicodeString AccessValueName(L"Access");
UnicodeString DefaultAccessString(L"inherit");
//---------------------------------------------------------------------------
__fastcall THierarchicalStorage::THierarchicalStorage(const UnicodeString & AStorage)
{
FStorage = AStorage;
@@ -113,6 +116,8 @@ __fastcall THierarchicalStorage::THierarchicalStorage(const UnicodeString & ASto
// data written in Unicode/UTF8 can be read by all versions back to 5.0.
ForceAnsi = false;
MungeStringValues = true;
FFakeReadOnlyOpens = 0;
FRootAccess = -1;
}
//---------------------------------------------------------------------------
__fastcall THierarchicalStorage::~THierarchicalStorage()
@@ -154,13 +159,86 @@ UnicodeString __fastcall THierarchicalStorage::MungeKeyName(const UnicodeString
{
UnicodeString Result = MungeStr(Key, ForceAnsi, false);
// if there's already ANSI-munged subkey, keep ANSI munging
if ((Result != Key) && !ForceAnsi && DoKeyExists(Key, true))
if ((Result != Key) && !ForceAnsi && CanRead() && DoKeyExists(Key, true))
{
Result = MungeStr(Key, true, false);
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::DoReadRootAccessString()
{
UnicodeString Result;
if (OpenRootKey(false))
{
Result = ReadAccessString();
CloseSubKey();
}
else
{
Result = DefaultAccessString;
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::ReadAccessString()
{
UnicodeString Result;
if (!FKeyHistory.empty())
{
Result = ReadString(AccessValueName, DefaultAccessString);
}
else
{
Result = DoReadRootAccessString();
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall THierarchicalStorage::ReadAccess(unsigned int CurrentAccess)
{
UnicodeString Access = ReadAccessString();
unsigned int Result = 0;
while (!Access.IsEmpty())
{
UnicodeString Token = CutToChar(Access, L',', true);
if (SameText(Token, L"inherit"))
{
Result |= CurrentAccess;
}
else if (SameText(Token, "read"))
{
Result |= hsaRead;
}
else if (SameText(Token, "full"))
{
Result |= hsaRead | hsaWrite;
}
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall THierarchicalStorage::GetCurrentAccess()
{
unsigned int Result;
if (!FKeyHistory.empty())
{
// We must have resolved root access when opening the sub key the latest.
DebugAssert(FRootAccess >= 0);
Result = FKeyHistory.back().Access;
}
else
{
if (FRootAccess < 0)
{
FRootAccess = hsaRead; // Prevent recursion to allow reading the access
FRootAccess = ReadAccess(hsaRead | hsaWrite);
}
Result = FRootAccess;
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::OpenSubKeyPath(const UnicodeString & KeyPath, bool CanCreate)
{
DebugAssert(!KeyPath.IsEmpty() && (KeyPath[KeyPath.Length()] != L'\\'));
@@ -197,14 +275,32 @@ bool __fastcall THierarchicalStorage::OpenSubKey(const UnicodeString & Key, bool
{
UnicodeString MungedKey = MungeKeyName(Key);
bool Result = DoOpenSubKey(MungedKey, CanCreate);
bool Result;
unsigned int InheritAccess;
unsigned int Access;
// For the first open, CanWrite > GetCurrentAccess > ReadAccess has a (needed) side effect of caching root access.
if (!CanWrite() && CanCreate && !KeyExists(MungedKey))
{
InheritAccess = Access = 0; // do not even try to read the access, as the key is actually not opened
FFakeReadOnlyOpens++;
Result = true;
}
else
{
Access = hsaRead; // allow reading the access
InheritAccess = GetCurrentAccess();
Result = DoOpenSubKey(MungedKey, CanCreate);
}
if (Result)
{
TKeyEntry Entry;
Entry.Key = IncludeTrailingBackslash(CurrentSubKey + MungedKey);
Entry.Levels = 1;
Entry.Access = Access;
FKeyHistory.push_back(Entry);
// Read the real access only now, that the key is finally opened (it's in FKeyHistory)
FKeyHistory.back().Access = ReadAccess(InheritAccess);
}
return Result;
@@ -236,7 +332,14 @@ void __fastcall THierarchicalStorage::CloseSubKey()
DebugAssert(FKeyHistory.back().Levels == 1);
FKeyHistory.pop_back();
DoCloseSubKey();
if (FFakeReadOnlyOpens > 0)
{
FFakeReadOnlyOpens--;
}
else
{
DoCloseSubKey();
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::CloseAll()
@@ -259,14 +362,26 @@ void __fastcall THierarchicalStorage::ClearSubKeys()
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::RecursiveDeleteSubKey(const UnicodeString & Key)
{
if (CanWrite())
bool CanWriteParent = CanWrite();
if (OpenSubKey(Key, false))
{
if (OpenSubKey(Key, false))
bool CanWriteKey = CanWrite();
ClearSubKeys();
// Cannot delete the key itself, but can delete its contents, so at least delete the values
// (which would otherwise be deleted implicitly by DoDeleteSubKey)
if (!CanWriteParent && CanWrite())
{
ClearSubKeys();
CloseSubKey();
ClearValues();
}
CloseSubKey();
// Only if all subkeys were successfully deleted in ClearSubKeys
if (CanWriteParent && HasSubKeys())
{
DoDeleteSubKey(Key);
}
DoDeleteSubKey(Key);
}
}
//---------------------------------------------------------------------------
@@ -362,9 +477,17 @@ void __fastcall THierarchicalStorage::WriteValues(TStrings * Strings, bool Maint
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::HasAccess(unsigned int Access)
{
return
FLAGSET(GetCurrentAccess(), Access) &&
// There should never be any kind of access to a non-existent key
DebugAlwaysTrue(FFakeReadOnlyOpens == 0);
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::CanRead()
{
return true;
return HasAccess(hsaRead);
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::GetSubKeyNames(TStrings * Strings)
@@ -384,6 +507,19 @@ void __fastcall THierarchicalStorage::GetValueNames(TStrings * Strings)
if (CanRead())
{
DoGetValueNames(Strings);
int Index = 0;
while (Index < Strings->Count)
{
if (SameText(Strings->Strings[Index], AccessValueName))
{
Strings->Delete(Index);
}
else
{
Index++;
}
}
}
else
{
@@ -531,7 +667,7 @@ RawByteString __fastcall THierarchicalStorage::ReadStringAsBinaryData(const Unic
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::CanWrite()
{
return true;
return HasAccess(hsaWrite);
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteBool(const UnicodeString & Name, bool Value)
@@ -986,6 +1122,9 @@ bool __fastcall TCustomIniFileStorage::OpenSubKey(const UnicodeString & Key, boo
{
bool Result;
// To cache root access in advance, otherwise we end up calling outselves, what TAutoFlag does not like
GetCurrentAccess();
{
TAutoFlag Flag(FOpeningSubKey);
Result = THierarchicalStorage::OpenSubKey(Key, CanCreate);
@@ -1357,6 +1496,21 @@ void __fastcall TCustomIniFileStorage::DoWriteBinaryData(const UnicodeString & N
}
DoWriteStringRawInternal(Name, BytesToHex(RawByteString(static_cast<const char*>(Buffer), Size)));
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomIniFileStorage::DoReadRootAccessString()
{
UnicodeString Result;
if (OpenSubKey(L"_", false))
{
Result = ReadAccessString();
CloseSubKey();
}
else
{
Result = DefaultAccessString;
}
return Result;
}
//===========================================================================
TIniFileStorage * __fastcall TIniFileStorage::CreateFromPath(const UnicodeString & AStorage)
{

View File

@@ -69,10 +69,12 @@ public:
__property bool Temporary = { read = GetTemporary };
protected:
enum THierarchicalStorageAccess { hsaRead = 0x01, hsaWrite = 0x02 };
struct TKeyEntry
{
UnicodeString Key;
int Levels;
unsigned int Access;
};
UnicodeString FStorage;
@@ -82,6 +84,8 @@ protected:
bool FForceSave;
bool FMungeStringValues;
bool FForceAnsi;
int FFakeReadOnlyOpens;
int FRootAccess;
UnicodeString __fastcall GetCurrentSubKey();
UnicodeString __fastcall GetCurrentSubKeyMunged();
@@ -117,9 +121,15 @@ protected:
virtual size_t __fastcall DoBinaryDataSize(const UnicodeString & Name) = 0;
virtual UnicodeString __fastcall DoReadRootAccessString();
size_t __fastcall BinaryDataSize(const UnicodeString & Name);
bool __fastcall CanRead();
bool __fastcall CanWrite();
UnicodeString __fastcall ReadAccessString();
unsigned int __fastcall ReadAccess(unsigned int CurrentAccess);
inline bool __fastcall HasAccess(unsigned int Access);
inline bool __fastcall CanRead();
inline bool __fastcall CanWrite();
unsigned int __fastcall GetCurrentAccess();
};
//---------------------------------------------------------------------------
class TRegistryStorage : public THierarchicalStorage
@@ -217,6 +227,8 @@ protected:
virtual UnicodeString __fastcall DoReadStringRaw(const UnicodeString & Name, const UnicodeString & Default);
virtual size_t __fastcall DoReadBinaryData(const UnicodeString & Name, void * Buffer, size_t Size);
virtual UnicodeString __fastcall DoReadRootAccessString();
void __fastcall CacheSections();
void __fastcall ResetCache();
bool __fastcall DoKeyExistsInternal(const UnicodeString & SubKey);