mirror of
https://github.com/wismna/ModernKeePassLib.git
synced 2025-10-03 15:40:20 -04:00
Update to version 2.42.1
Some changes Removed FutureAccesList code as it works only with UWP
This commit is contained in:
@@ -225,6 +225,25 @@ namespace ModernKeePassLib.Cryptography
|
||||
return MemUtil.BytesToUInt64(pb);
|
||||
}
|
||||
|
||||
internal ulong GetRandomUInt64(ulong uMaxExcl)
|
||||
{
|
||||
if(uMaxExcl == 0) { Debug.Assert(false); throw new ArgumentOutOfRangeException("uMaxExcl"); }
|
||||
|
||||
ulong uGen, uRem;
|
||||
do
|
||||
{
|
||||
uGen = GetRandomUInt64();
|
||||
uRem = uGen % uMaxExcl;
|
||||
}
|
||||
while((uGen - uRem) > (ulong.MaxValue - (uMaxExcl - 1UL)));
|
||||
// This ensures that the last number of the block (i.e.
|
||||
// (uGen - uRem) + (uMaxExcl - 1)) is generatable;
|
||||
// for signed longs, overflow to negative number:
|
||||
// while((uGen - uRem) + (uMaxExcl - 1) < 0);
|
||||
|
||||
return uRem;
|
||||
}
|
||||
|
||||
#if CRSBENCHMARK
|
||||
public static string Benchmark()
|
||||
{
|
||||
|
@@ -28,37 +28,39 @@ using System.Security.Cryptography;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public sealed class CryptoStreamEx : CryptoStream
|
||||
{
|
||||
private ICryptoTransform m_t;
|
||||
private SymmetricAlgorithm m_a;
|
||||
public sealed class CryptoStreamEx : CryptoStream
|
||||
{
|
||||
private ICryptoTransform m_t;
|
||||
private SymmetricAlgorithm m_a;
|
||||
|
||||
public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m,
|
||||
SymmetricAlgorithm a) : base(s, t, m)
|
||||
{
|
||||
m_t = t;
|
||||
m_a = a;
|
||||
}
|
||||
public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m,
|
||||
SymmetricAlgorithm a) : base(s, t, m)
|
||||
{
|
||||
m_t = t;
|
||||
m_a = a;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try { base.Dispose(disposing); }
|
||||
// Unnecessary exception from CryptoStream with
|
||||
// RijndaelManagedTransform when a stream hasn't been
|
||||
// read completely (e.g. incorrect master key)
|
||||
catch (CryptographicException) { }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try { base.Dispose(disposing); }
|
||||
// Unnecessary exception from CryptoStream with
|
||||
// RijndaelManagedTransform when a stream hasn't been
|
||||
// read completely (e.g. incorrect master key)
|
||||
catch(CryptographicException) { }
|
||||
// Similar to above, at the beginning of the stream
|
||||
catch(IndexOutOfRangeException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
try { if (m_t != null) { m_t.Dispose(); m_t = null; } }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
if(disposing)
|
||||
{
|
||||
try { if(m_t != null) { m_t.Dispose(); m_t = null; } }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
// In .NET 2.0, SymmetricAlgorithm.Dispose() is not public
|
||||
try { if (m_a != null) { m_a.Clear(); m_a = null; } }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
// In .NET 2.0, SymmetricAlgorithm.Dispose() is not public
|
||||
try { if(m_a != null) { m_a.Clear(); m_a = null; } }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -42,13 +42,13 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
private const uint MaxVersion = 0x13;
|
||||
|
||||
private const int MinSalt = 8;
|
||||
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec
|
||||
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec.
|
||||
|
||||
internal const ulong MinIterations = 1;
|
||||
internal const ulong MaxIterations = uint.MaxValue;
|
||||
|
||||
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
|
||||
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec
|
||||
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec.
|
||||
internal const ulong MaxMemory = int.MaxValue; // .NET limit
|
||||
|
||||
internal const uint MinParallelism = 1;
|
||||
|
@@ -36,20 +36,20 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
if(pwProfile.Length == 0) return PwgError.Success;
|
||||
|
||||
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
|
||||
PwGenerator.PrepareCharSet(pcs, pwProfile);
|
||||
if(!PwGenerator.PrepareCharSet(pcs, pwProfile))
|
||||
return PwgError.InvalidCharSet;
|
||||
|
||||
char[] v = new char[pwProfile.Length];
|
||||
try
|
||||
{
|
||||
for(int i = 0; i < v.Length; ++i)
|
||||
{
|
||||
char ch = PwGenerator.GenerateCharacter(pwProfile,
|
||||
pcs, crsRandomSource);
|
||||
|
||||
char ch = PwGenerator.GenerateCharacter(pcs, crsRandomSource);
|
||||
if(ch == char.MinValue)
|
||||
return PwgError.TooFewCharacters;
|
||||
|
||||
v[i] = ch;
|
||||
if(pwProfile.NoRepeatingCharacters) pcs.Remove(ch);
|
||||
}
|
||||
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);
|
||||
|
@@ -34,92 +34,61 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
psOut = ProtectedString.Empty;
|
||||
|
||||
string strPattern = pwProfile.Pattern;
|
||||
if(string.IsNullOrEmpty(strPattern)) return PwgError.Success;
|
||||
|
||||
CharStream cs = new CharStream(strPattern);
|
||||
LinkedList<char> llGenerated = new LinkedList<char>();
|
||||
PwCharSet pcsCurrent = new PwCharSet();
|
||||
PwCharSet pcsCustom = new PwCharSet();
|
||||
PwCharSet pcsUsed = new PwCharSet();
|
||||
bool bInCharSetDef = false;
|
||||
PwCharSet pcs = new PwCharSet();
|
||||
|
||||
string strPattern = ExpandPattern(pwProfile.Pattern);
|
||||
if(strPattern.Length == 0) return PwgError.Success;
|
||||
|
||||
CharStream csStream = new CharStream(strPattern);
|
||||
char ch = csStream.ReadChar();
|
||||
|
||||
while(ch != char.MinValue)
|
||||
while(true)
|
||||
{
|
||||
pcsCurrent.Clear();
|
||||
char ch = cs.ReadChar();
|
||||
if(ch == char.MinValue) break;
|
||||
|
||||
bool bGenerateChar = false;
|
||||
pcs.Clear();
|
||||
|
||||
if(ch == '\\')
|
||||
{
|
||||
ch = csStream.ReadChar();
|
||||
if(ch == char.MinValue) // Backslash at the end
|
||||
{
|
||||
llGenerated.AddLast('\\');
|
||||
break;
|
||||
}
|
||||
ch = cs.ReadChar();
|
||||
if(ch == char.MinValue) return PwgError.InvalidPattern;
|
||||
|
||||
if(bInCharSetDef) pcsCustom.Add(ch);
|
||||
else
|
||||
{
|
||||
llGenerated.AddLast(ch);
|
||||
pcsUsed.Add(ch);
|
||||
}
|
||||
}
|
||||
else if(ch == '^')
|
||||
{
|
||||
ch = csStream.ReadChar();
|
||||
if(ch == char.MinValue) // ^ at the end
|
||||
{
|
||||
llGenerated.AddLast('^');
|
||||
break;
|
||||
}
|
||||
|
||||
if(bInCharSetDef) pcsCustom.Remove(ch);
|
||||
pcs.Add(ch); // Allow "{...}" support and char check
|
||||
}
|
||||
else if(ch == '[')
|
||||
{
|
||||
pcsCustom.Clear();
|
||||
bInCharSetDef = true;
|
||||
if(!ReadCustomCharSet(cs, pcs))
|
||||
return PwgError.InvalidPattern;
|
||||
}
|
||||
else if(ch == ']')
|
||||
else
|
||||
{
|
||||
pcsCurrent.Add(pcsCustom.ToString());
|
||||
if(!pcs.AddCharSet(ch))
|
||||
return PwgError.InvalidPattern;
|
||||
}
|
||||
|
||||
bInCharSetDef = false;
|
||||
bGenerateChar = true;
|
||||
}
|
||||
else if(bInCharSetDef)
|
||||
int nCount = 1;
|
||||
if(cs.PeekChar() == '{')
|
||||
{
|
||||
if(pcsCustom.AddCharSet(ch) == false)
|
||||
pcsCustom.Add(ch);
|
||||
nCount = ReadCount(cs);
|
||||
if(nCount < 0) return PwgError.InvalidPattern;
|
||||
}
|
||||
else if(pcsCurrent.AddCharSet(ch) == false)
|
||||
{
|
||||
llGenerated.AddLast(ch);
|
||||
pcsUsed.Add(ch);
|
||||
}
|
||||
else bGenerateChar = true;
|
||||
|
||||
if(bGenerateChar)
|
||||
for(int i = 0; i < nCount; ++i)
|
||||
{
|
||||
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile);
|
||||
|
||||
if(!PwGenerator.PrepareCharSet(pcs, pwProfile))
|
||||
return PwgError.InvalidCharSet;
|
||||
if(pwProfile.NoRepeatingCharacters)
|
||||
pcsCurrent.Remove(pcsUsed.ToString());
|
||||
|
||||
char chGen = PwGenerator.GenerateCharacter(pwProfile,
|
||||
pcsCurrent, crsRandomSource);
|
||||
{
|
||||
foreach(char chUsed in llGenerated)
|
||||
pcs.Remove(chUsed);
|
||||
}
|
||||
|
||||
char chGen = PwGenerator.GenerateCharacter(pcs,
|
||||
crsRandomSource);
|
||||
if(chGen == char.MinValue) return PwgError.TooFewCharacters;
|
||||
|
||||
llGenerated.AddLast(chGen);
|
||||
pcsUsed.Add(chGen);
|
||||
}
|
||||
|
||||
ch = csStream.ReadChar();
|
||||
}
|
||||
|
||||
if(llGenerated.Count == 0) return PwgError.Success;
|
||||
@@ -135,53 +104,70 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
llGenerated.Clear();
|
||||
|
||||
return PwgError.Success;
|
||||
}
|
||||
|
||||
private static string ExpandPattern(string strPattern)
|
||||
private static bool ReadCustomCharSet(CharStream cs, PwCharSet pcsOut)
|
||||
{
|
||||
if(strPattern == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strPattern;
|
||||
Debug.Assert(cs.PeekChar() != '['); // Consumed already
|
||||
Debug.Assert(pcsOut.Size == 0);
|
||||
|
||||
bool bAdd = true;
|
||||
while(true)
|
||||
{
|
||||
int nOpen = FindFirstUnescapedChar(str, '{');
|
||||
int nClose = FindFirstUnescapedChar(str, '}');
|
||||
char ch = cs.ReadChar();
|
||||
if(ch == char.MinValue) return false;
|
||||
if(ch == ']') break;
|
||||
|
||||
if((nOpen >= 0) && (nOpen < nClose))
|
||||
if(ch == '\\')
|
||||
{
|
||||
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1);
|
||||
str = str.Remove(nOpen, nClose - nOpen + 1);
|
||||
ch = cs.ReadChar();
|
||||
if(ch == char.MinValue) return false;
|
||||
|
||||
uint uRepeat;
|
||||
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1))
|
||||
{
|
||||
if(uRepeat == 0)
|
||||
str = str.Remove(nOpen - 1, 1);
|
||||
else
|
||||
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1));
|
||||
}
|
||||
if(bAdd) pcsOut.Add(ch);
|
||||
else pcsOut.Remove(ch);
|
||||
}
|
||||
else if(ch == '^')
|
||||
{
|
||||
if(bAdd) bAdd = false;
|
||||
else return false; // '^' toggles the mode only once
|
||||
}
|
||||
else
|
||||
{
|
||||
PwCharSet pcs = new PwCharSet();
|
||||
if(!pcs.AddCharSet(ch)) return false;
|
||||
|
||||
if(bAdd) pcsOut.Add(pcs.ToString());
|
||||
else pcsOut.Remove(pcs.ToString());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return str;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int FindFirstUnescapedChar(string str, char ch)
|
||||
private static int ReadCount(CharStream cs)
|
||||
{
|
||||
for(int i = 0; i < str.Length; ++i)
|
||||
{
|
||||
char chCur = str[i];
|
||||
if(cs.ReadChar() != '{') { Debug.Assert(false); return -1; }
|
||||
|
||||
if(chCur == '\\') ++i; // Next is escaped, skip it
|
||||
else if(chCur == ch) return i;
|
||||
// Ensure not empty
|
||||
char chFirst = cs.PeekChar();
|
||||
if((chFirst < '0') || (chFirst > '9')) return -1;
|
||||
|
||||
long n = 0;
|
||||
while(true)
|
||||
{
|
||||
char ch = cs.ReadChar();
|
||||
if(ch == '}') break;
|
||||
|
||||
if((ch >= '0') && (ch <= '9'))
|
||||
{
|
||||
n = (n * 10L) + (long)(ch - '0');
|
||||
if(n > int.MaxValue) return -1;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return (int)n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,6 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
public static readonly string UpperHex = "0123456789ABCDEF";
|
||||
public static readonly string LowerHex = "0123456789abcdef";
|
||||
|
||||
public static readonly string Invalid = "\t\r\n";
|
||||
public static readonly string LookAlike = @"O0l1I|";
|
||||
|
||||
internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
|
||||
|
@@ -36,7 +36,9 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
Success = 0,
|
||||
Unknown = 1,
|
||||
TooFewCharacters = 2,
|
||||
UnknownAlgorithm = 3
|
||||
UnknownAlgorithm = 3,
|
||||
InvalidCharSet = 4,
|
||||
InvalidPattern = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,30 +96,33 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
|
||||
}
|
||||
|
||||
internal static char GenerateCharacter(PwProfile pwProfile,
|
||||
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
|
||||
internal static char GenerateCharacter(PwCharSet pwCharSet,
|
||||
CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
if(pwCharSet.Size == 0) return char.MinValue;
|
||||
uint cc = pwCharSet.Size;
|
||||
if(cc == 0) return char.MinValue;
|
||||
|
||||
ulong uIndex = crsRandomSource.GetRandomUInt64();
|
||||
uIndex %= (ulong)pwCharSet.Size;
|
||||
|
||||
char ch = pwCharSet[(uint)uIndex];
|
||||
|
||||
if(pwProfile.NoRepeatingCharacters)
|
||||
pwCharSet.Remove(ch);
|
||||
|
||||
return ch;
|
||||
uint i = (uint)crsRandomSource.GetRandomUInt64(cc);
|
||||
return pwCharSet[i];
|
||||
}
|
||||
|
||||
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
|
||||
internal static bool PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
|
||||
{
|
||||
pwCharSet.Remove(PwCharSet.Invalid);
|
||||
uint cc = pwCharSet.Size;
|
||||
for(uint i = 0; i < cc; ++i)
|
||||
{
|
||||
char ch = pwCharSet[i];
|
||||
if((ch == char.MinValue) || (ch == '\t') || (ch == '\r') ||
|
||||
(ch == '\n') || char.IsSurrogate(ch))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
|
||||
|
||||
if(pwProfile.ExcludeCharacters.Length > 0)
|
||||
if(!string.IsNullOrEmpty(pwProfile.ExcludeCharacters))
|
||||
pwCharSet.Remove(pwProfile.ExcludeCharacters);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
|
||||
@@ -127,8 +132,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
|
||||
for(int i = v.Length - 1; i >= 1; --i)
|
||||
{
|
||||
ulong r = crsRandomSource.GetRandomUInt64();
|
||||
int j = (int)(r % (ulong)(i + 1));
|
||||
int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1));
|
||||
|
||||
char t = v[i];
|
||||
v[i] = v[j];
|
||||
|
@@ -87,7 +87,7 @@ namespace ModernKeePassLib.Interfaces
|
||||
bool SetText(string strNewText, LogStatusType lsType);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the user cancelled the current work.
|
||||
/// Check whether the user cancelled the current work.
|
||||
/// </summary>
|
||||
/// <returns>Returns <c>true</c> if the caller should continue
|
||||
/// the current work.</returns>
|
||||
|
@@ -66,7 +66,7 @@ namespace ModernKeePassLib.Interfaces
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag that determines if the object does expire.
|
||||
/// Flag that determines whether the object expires.
|
||||
/// </summary>
|
||||
bool Expires
|
||||
{
|
||||
|
@@ -21,9 +21,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
@@ -166,7 +169,6 @@ namespace ModernKeePassLib.Keys
|
||||
{
|
||||
ValidateUserKeys();
|
||||
|
||||
// Concatenate user key data
|
||||
List<byte[]> lData = new List<byte[]>();
|
||||
int cbData = 0;
|
||||
foreach(IUserKey pKey in m_vUserKeys)
|
||||
@@ -199,13 +201,17 @@ namespace ModernKeePassLib.Keys
|
||||
{
|
||||
if(ckOther == null) throw new ArgumentNullException("ckOther");
|
||||
|
||||
bool bEqual;
|
||||
byte[] pbThis = CreateRawCompositeKey32();
|
||||
byte[] pbOther = ckOther.CreateRawCompositeKey32();
|
||||
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
|
||||
MemUtil.ZeroByteArray(pbOther);
|
||||
MemUtil.ZeroByteArray(pbThis);
|
||||
try
|
||||
{
|
||||
byte[] pbOther = ckOther.CreateRawCompositeKey32();
|
||||
bEqual = MemUtil.ArraysEqual(pbThis, pbOther);
|
||||
MemUtil.ZeroByteArray(pbOther);
|
||||
}
|
||||
finally { MemUtil.ZeroByteArray(pbThis); }
|
||||
|
||||
return bResult;
|
||||
return bEqual;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
@@ -231,31 +237,90 @@ namespace ModernKeePassLib.Keys
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
|
||||
|
||||
byte[] pbRaw32 = CreateRawCompositeKey32();
|
||||
if((pbRaw32 == null) || (pbRaw32.Length != 32))
|
||||
{ Debug.Assert(false); return null; }
|
||||
byte[] pbRaw32 = null, pbTrf32 = null;
|
||||
ProtectedBinary pbRet = null;
|
||||
|
||||
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
|
||||
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
|
||||
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
|
||||
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
|
||||
"UUID: " + p.KdfUuid.ToHexString() + ".");
|
||||
|
||||
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
|
||||
if(pbTrf32 == null) { Debug.Assert(false); return null; }
|
||||
|
||||
if(pbTrf32.Length != 32)
|
||||
try
|
||||
{
|
||||
Debug.Assert(false);
|
||||
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
|
||||
pbRaw32 = CreateRawCompositeKey32();
|
||||
if((pbRaw32 == null) || (pbRaw32.Length != 32))
|
||||
{ Debug.Assert(false); return null; }
|
||||
|
||||
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
|
||||
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
|
||||
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
|
||||
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
|
||||
"UUID: " + p.KdfUuid.ToHexString() + ".");
|
||||
|
||||
pbTrf32 = kdf.Transform(pbRaw32, p);
|
||||
if(pbTrf32 == null) { Debug.Assert(false); return null; }
|
||||
if(pbTrf32.Length != 32)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
|
||||
}
|
||||
|
||||
pbRet = new ProtectedBinary(true, pbTrf32);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(pbRaw32 != null) MemUtil.ZeroByteArray(pbRaw32);
|
||||
if(pbTrf32 != null) MemUtil.ZeroByteArray(pbTrf32);
|
||||
}
|
||||
|
||||
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
|
||||
MemUtil.ZeroByteArray(pbTrf32);
|
||||
MemUtil.ZeroByteArray(pbRaw32);
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
private sealed class CkGkTaskInfo
|
||||
{
|
||||
public volatile ProtectedBinary Key = null;
|
||||
public volatile string Error = null;
|
||||
}
|
||||
|
||||
internal ProtectedBinary GenerateKey32Ex(KdfParameters p, IStatusLogger sl)
|
||||
{
|
||||
if(sl == null) return GenerateKey32(p);
|
||||
|
||||
CkGkTaskInfo ti = new CkGkTaskInfo();
|
||||
|
||||
ThreadStart f = delegate()
|
||||
{
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
try { ti.Key = GenerateKey32(p); }
|
||||
catch(ThreadAbortException exAbort)
|
||||
{
|
||||
ti.Error = ((exAbort != null) ? exAbort.Message : null);
|
||||
Thread.ResetAbort();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
ti.Error = ((ex != null) ? ex.Message : null);
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(f);
|
||||
th.Start();
|
||||
|
||||
Debug.Assert(PwDefs.UIUpdateDelay >= 2);
|
||||
while(!th.Join(PwDefs.UIUpdateDelay / 2))
|
||||
{
|
||||
if(!sl.ContinueWork())
|
||||
{
|
||||
try { th.Abort(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(ti.Error)) throw new Exception(ti.Error);
|
||||
|
||||
Debug.Assert(ti.Key != null);
|
||||
return ti.Key;
|
||||
}
|
||||
|
||||
private void ValidateUserKeys()
|
||||
{
|
||||
int nAccounts = 0;
|
||||
@@ -280,14 +345,11 @@ namespace ModernKeePassLib.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
return KLRes.InvalidCompositeKey + MessageService.NewParagraph +
|
||||
KLRes.InvalidCompositeKeyHint;
|
||||
return (KLRes.InvalidCompositeKey + MessageService.NewParagraph +
|
||||
KLRes.InvalidCompositeKeyHint);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new invalid composite key exception.
|
||||
/// </summary>
|
||||
public InvalidCompositeKeyException()
|
||||
{
|
||||
}
|
||||
|
@@ -67,14 +67,13 @@ namespace ModernKeePassLib.Keys
|
||||
{
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
public KcpKeyFile(StorageFile strKeyFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromFile(strKeyFile), false);
|
||||
}
|
||||
public KcpKeyFile(StorageFile keyFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromStorageFile(keyFile), false);
|
||||
}
|
||||
#else
|
||||
public KcpKeyFile(string strKeyFile)
|
||||
public KcpKeyFile(string strKeyFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
|
||||
}
|
||||
@@ -183,19 +182,19 @@ namespace ModernKeePassLib.Keys
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, random key-file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">Path where the key-file should be saved to.
|
||||
/// If the file exists already, it will be overwritten.</param>
|
||||
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
|
||||
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
|
||||
/// random number generator is used).</param>
|
||||
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
|
||||
/// <summary>
|
||||
/// Create a new, random key-file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">Path where the key-file should be saved to.
|
||||
/// If the file exists already, it will be overwritten.</param>
|
||||
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
|
||||
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
|
||||
/// random number generator is used).</param>
|
||||
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
|
||||
#if ModernKeePassLib
|
||||
public static void Create(StorageFile strFilePath, byte[] pbAdditionalEntropy)
|
||||
public static void Create(StorageFile file, byte[] pbAdditionalEntropy)
|
||||
#else
|
||||
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
|
||||
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
|
||||
#endif
|
||||
{
|
||||
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
|
||||
@@ -215,7 +214,11 @@ namespace ModernKeePassLib.Keys
|
||||
}
|
||||
}
|
||||
|
||||
CreateXmlKeyFile(strFilePath, pbFinalKey32);
|
||||
#if ModernKeePassLib
|
||||
CreateXmlKeyFile(file, pbFinalKey32);
|
||||
#else
|
||||
CreateXmlKeyFile(strFilePath, pbFinalKey32);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
@@ -276,19 +279,23 @@ namespace ModernKeePassLib.Keys
|
||||
|
||||
return pbKeyData;
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
private static void CreateXmlKeyFile(StorageFile strFile, byte[] pbKeyData)
|
||||
private static void CreateXmlKeyFile(StorageFile file, byte[] pbKeyData)
|
||||
{
|
||||
Debug.Assert(file != null);
|
||||
if (file == null) throw new ArgumentNullException(nameof(file));
|
||||
#else
|
||||
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
|
||||
#endif
|
||||
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
|
||||
{
|
||||
Debug.Assert(strFile != null);
|
||||
if(strFile == null) throw new ArgumentNullException("strFile");
|
||||
#endif
|
||||
Debug.Assert(pbKeyData != null);
|
||||
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
||||
|
||||
#if ModernKeePassLib
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromFile(strFile);
|
||||
var ioc = IOConnectionInfo.FromStorageFile(file);
|
||||
#else
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
|
||||
#endif
|
||||
|
@@ -21,9 +21,9 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
|
@@ -145,7 +145,7 @@ namespace ModernKeePassLib.Keys
|
||||
|
||||
public override byte[] GetKey(KeyProviderQueryContext ctx)
|
||||
{
|
||||
return new byte[]{ 2, 3, 5, 7, 11, 13 };
|
||||
return new byte[] { 2, 3, 5, 7, 11, 13 };
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -3,14 +3,14 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>2.41.1</Version>
|
||||
<Version>2.42.1</Version>
|
||||
<Authors>Geoffroy Bonneville</Authors>
|
||||
<PackageLicenseUrl>https://www.gnu.org/licenses/gpl-3.0.en.html</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/wismna/ModernKeePass</PackageProjectUrl>
|
||||
<Description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</Description>
|
||||
<Company>wismna</Company>
|
||||
<Product>ModernKeePassLib</Product>
|
||||
<PackageReleaseNotes>Allow opening Storage File from FutureAccessList with a token</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>Update to version 2.42.1</PackageReleaseNotes>
|
||||
<PackageTags>KeePass KeePassLib Portable PCL NetStandard ModernKeePass</PackageTags>
|
||||
<Copyright>Copyright © 2019 Geoffroy Bonneville</Copyright>
|
||||
</PropertyGroup>
|
||||
|
@@ -22,6 +22,12 @@ namespace ModernKeePassLib.Native
|
||||
{
|
||||
return Environment.OSVersion.Platform;
|
||||
}
|
||||
|
||||
internal static string DecodeArgsToPath(string strApp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(strApp)) return strApp;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NativeMethods
|
||||
|
@@ -234,16 +234,16 @@ namespace ModernKeePassLib.Native
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
|
||||
psi.FileName = EncodePath(strAppPath);
|
||||
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
|
||||
|
||||
psi.CreateNoWindow = true;
|
||||
psi.FileName = strAppPath;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
psi.UseShellExecute = false;
|
||||
|
||||
psi.RedirectStandardOutput = bStdOut;
|
||||
|
||||
if(strStdInput != null) psi.RedirectStandardInput = true;
|
||||
|
||||
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
|
||||
|
||||
Process p = Process.Start(psi);
|
||||
pToDispose = p;
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace ModernKeePassLib.Native
|
||||
#if !ModernKeePassLib
|
||||
if((f & AppRunFlags.DoEvents) != AppRunFlags.None)
|
||||
{
|
||||
List<Form> lDisabledForms = new List<Form>();
|
||||
List<Form> lDisabledForms = new List<Form>();
|
||||
if((f & AppRunFlags.DisableForms) != AppRunFlags.None)
|
||||
{
|
||||
foreach(Form form in Application.OpenForms)
|
||||
@@ -456,5 +456,76 @@ namespace ModernKeePassLib.Native
|
||||
// https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs
|
||||
return Type.GetType(strType + ", Windows, ContentType=WindowsRuntime", false);
|
||||
}
|
||||
|
||||
internal static string EncodeDataToArgs(string strData)
|
||||
{
|
||||
if(strData == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
// Cf. EncodePath and DecodeArgsToPath
|
||||
if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
|
||||
{
|
||||
string str = strData;
|
||||
|
||||
str = str.Replace("\\", "\\\\");
|
||||
str = str.Replace("\"", "\\\"");
|
||||
|
||||
// Whether '\'' needs to be encoded depends on the context
|
||||
// (e.g. surrounding quotes); as we do not know what the
|
||||
// caller does with the returned string, we assume that
|
||||
// it will be used in a context where '\'' must not be
|
||||
// encoded; this behavior is documented
|
||||
// str = str.Replace("\'", "\\\'");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// See SHELLEXECUTEINFO structure documentation
|
||||
return strData.Replace("\"", "\"\"\"");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode a path for <c>Process.Start</c>.
|
||||
/// </summary>
|
||||
internal static string EncodePath(string strPath)
|
||||
{
|
||||
if(strPath == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
// Cf. EncodeDataToArgs and DecodeArgsToPath
|
||||
if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
|
||||
{
|
||||
string str = strPath;
|
||||
|
||||
str = str.Replace("\\", "\\\\");
|
||||
str = str.Replace("\"", "\\\"");
|
||||
|
||||
// '\'' must not be encoded in paths (only in args)
|
||||
// str = str.Replace("\'", "\\\'");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return strPath; // '\"' is not allowed in paths on Windows
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode command line arguments to a path for <c>Process.Start</c>.
|
||||
/// </summary>
|
||||
internal static string DecodeArgsToPath(string strArgs)
|
||||
{
|
||||
if(strArgs == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strArgs;
|
||||
|
||||
// Cf. EncodeDataToArgs and EncodePath
|
||||
// if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
|
||||
// {
|
||||
// string strPlh = Guid.NewGuid().ToString();
|
||||
// str = str.Replace("\\\\", strPlh);
|
||||
// str = str.Replace("\\\'", "\'");
|
||||
// str = str.Replace(strPlh, "\\\\"); // Restore
|
||||
// }
|
||||
|
||||
return str; // '\"' is not allowed in paths on Windows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,6 +39,9 @@ namespace ModernKeePassLib.Native
|
||||
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
|
||||
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
|
||||
|
||||
internal static readonly Guid FOLDERID_SkyDrive = new Guid(
|
||||
"A52BBA46-E9E1-435F-B3D9-28DAA648C0F6");
|
||||
|
||||
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
|
||||
// internal const uint TF_SFT_HIDDEN = 0x00000008;
|
||||
|
||||
@@ -179,6 +182,10 @@ namespace ModernKeePassLib.Native
|
||||
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
|
||||
UInt32 dwFlags, IntPtr hTransaction);
|
||||
|
||||
[DllImport("Shell32.dll")]
|
||||
private static extern int SHGetKnownFolderPath(ref Guid rfid, uint dwFlags,
|
||||
IntPtr hToken, out IntPtr ppszPath);
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@@ -256,5 +263,29 @@ namespace ModernKeePassLib.Native
|
||||
return strRtDir;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static string GetKnownFolderPath(Guid g)
|
||||
{
|
||||
if(Marshal.SystemDefaultCharSize != 2) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
IntPtr pszPath = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
if(SHGetKnownFolderPath(ref g, 0, IntPtr.Zero, out pszPath) == 0)
|
||||
{
|
||||
if(pszPath != IntPtr.Zero)
|
||||
return Marshal.PtrToStringUni(pszPath);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { if(pszPath != IntPtr.Zero) Marshal.FreeCoTaskMem(pszPath); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,10 +21,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Drawing;
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
@@ -132,7 +131,7 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>IOConnection</c> of the currently opened database file.
|
||||
/// <c>IOConnection</c> of the currently open database file.
|
||||
/// Is never <c>null</c>.
|
||||
/// </summary>
|
||||
public IOConnectionInfo IOConnectionInfo
|
||||
@@ -660,8 +659,8 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the currently opened database. The file is written to the location
|
||||
/// it has been opened from.
|
||||
/// Save the currently open database. The file is written to the
|
||||
/// location it has been opened from.
|
||||
/// </summary>
|
||||
/// <param name="slLogger">Logger that recieves status information.</param>
|
||||
public void Save(IStatusLogger slLogger)
|
||||
@@ -695,16 +694,16 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the currently opened database to a different location. If
|
||||
/// Save the currently open database to a different location. If
|
||||
/// <paramref name="bIsPrimaryNow" /> is <c>true</c>, the specified
|
||||
/// location is made the default location for future saves
|
||||
/// using <c>SaveDatabase</c>.
|
||||
/// </summary>
|
||||
/// <param name="ioConnection">New location to serialize the database to.</param>
|
||||
/// <param name="bIsPrimaryNow">If <c>true</c>, the new location is made the
|
||||
/// standard location for the database. If <c>false</c>, a copy of the currently
|
||||
/// opened database is saved to the specified location, but it isn't
|
||||
/// made the default location (i.e. no lock files will be moved for
|
||||
/// <param name="bIsPrimaryNow">If <c>true</c>, the new location is made
|
||||
/// the standard location for the database. If <c>false</c>, a copy of the
|
||||
/// currently open database is saved to the specified location, but it
|
||||
/// isn't made the default location (i.e. no lock files will be moved for
|
||||
/// example).</param>
|
||||
/// <param name="slLogger">Logger that recieves status information.</param>
|
||||
public void SaveAs(IOConnectionInfo ioConnection, bool bIsPrimaryNow,
|
||||
@@ -736,8 +735,8 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the currently opened database. No confirmation message is shown
|
||||
/// before closing. Unsaved changes will be lost.
|
||||
/// Closes the currently open database. No confirmation message
|
||||
/// is shown before closing. Unsaved changes will be lost.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
@@ -801,6 +800,12 @@ namespace ModernKeePassLib
|
||||
pgNew.Uuid = pg.Uuid;
|
||||
pgNew.AssignProperties(pg, false, true);
|
||||
|
||||
if(!pgLocalContainer.CanAddGroup(pgNew))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
pgLocalContainer = m_pgRootGroup;
|
||||
pgLocalContainer.CheckCanAddGroup(pgNew);
|
||||
}
|
||||
// pgLocalContainer.AddGroup(pgNew, true);
|
||||
InsertObjectAtBestPos<PwGroup>(pgLocalContainer.Groups, pgNew, ppSrc);
|
||||
pgNew.ParentGroup = pgLocalContainer;
|
||||
@@ -1087,8 +1092,8 @@ namespace ModernKeePassLib
|
||||
|
||||
if(pgLocal.IsContainedIn(pg)) continue;
|
||||
|
||||
if(!pgLocal.CanAddGroup(pg)) { Debug.Assert(false); continue; }
|
||||
pg.ParentGroup.Groups.Remove(pg);
|
||||
|
||||
// pgLocal.AddGroup(pg, true);
|
||||
InsertObjectAtBestPos<PwGroup>(pgLocal.Groups, pg, ppSrc);
|
||||
pg.ParentGroup = pgLocal;
|
||||
@@ -1643,7 +1648,7 @@ namespace ModernKeePassLib
|
||||
|
||||
return null;
|
||||
}
|
||||
#elif !KeePassLibSD
|
||||
#elif !KeePassLibSD && !ModernKeePassLib
|
||||
[Obsolete("Additionally specify the size.")]
|
||||
public Image GetCustomIcon(PwUuid pwIconId)
|
||||
{
|
||||
|
@@ -55,18 +55,18 @@ namespace ModernKeePassLib
|
||||
/// e.g. 2.19 = 0x02130000.
|
||||
/// It is highly recommended to use <c>FileVersion64</c> instead.
|
||||
/// </summary>
|
||||
public static readonly uint Version32 = 0x02290000;
|
||||
public static readonly uint Version32 = 0x022A0100;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as 64-bit unsigned integer
|
||||
/// (component-wise, 16 bits per component).
|
||||
/// </summary>
|
||||
public static readonly ulong FileVersion64 = 0x0002002900000000UL;
|
||||
public static readonly ulong FileVersion64 = 0x0002002A00010000UL;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as string.
|
||||
/// </summary>
|
||||
public static readonly string VersionString = "2.41";
|
||||
public static readonly string VersionString = "2.42.1";
|
||||
|
||||
public static readonly string Copyright = @"Copyright © 2003-2019 Dominik Reichl";
|
||||
|
||||
@@ -181,6 +181,12 @@ namespace ModernKeePassLib
|
||||
/// </summary>
|
||||
public static readonly string DefaultAutoTypeSequenceTan = @"{PASSWORD}";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time (in milliseconds) after which the user interface
|
||||
/// should be updated.
|
||||
/// </summary>
|
||||
internal const int UIUpdateDelay = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Check if a name is a standard field name.
|
||||
/// </summary>
|
||||
|
@@ -61,7 +61,7 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Methods for merging password databases/entries.
|
||||
/// Methods for merging databases/entries.
|
||||
/// </summary>
|
||||
public enum PwMergeMethod
|
||||
{
|
||||
|
@@ -25,19 +25,24 @@ using System.Text.RegularExpressions;
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A group containing several password entries.
|
||||
/// A group containing subgroups and entries.
|
||||
/// </summary>
|
||||
public sealed class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<PwGroup>
|
||||
{
|
||||
public const bool DefaultAutoTypeEnabled = true;
|
||||
public const bool DefaultSearchingEnabled = true;
|
||||
|
||||
// In the tree view of Windows 10, the X coordinate is reset
|
||||
// to 0 after 256 nested nodes
|
||||
private const uint MaxDepth = 126; // Depth 126 = level 127 < 256/2
|
||||
|
||||
private PwObjectList<PwGroup> m_listGroups = new PwObjectList<PwGroup>();
|
||||
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
||||
private PwGroup m_pParentGroup = null;
|
||||
@@ -139,7 +144,8 @@ namespace ModernKeePassLib
|
||||
{
|
||||
get { return m_pParentGroup; }
|
||||
|
||||
// Plugins: use <c>PwGroup.AddGroup</c> instead.
|
||||
// Plugins: use the PwGroup.AddGroup method instead.
|
||||
// Internal: check depth using CanAddGroup/CheckCanAddGroup.
|
||||
internal set { Debug.Assert(value != this); m_pParentGroup = value; }
|
||||
}
|
||||
|
||||
@@ -1244,7 +1250,7 @@ namespace ModernKeePassLib
|
||||
PwGroup pg = m_pParentGroup;
|
||||
while(pg != null)
|
||||
{
|
||||
if((!bIncludeTopMostGroup) && (pg.m_pParentGroup == null))
|
||||
if(!bIncludeTopMostGroup && (pg.m_pParentGroup == null))
|
||||
break;
|
||||
|
||||
strPath = pg.Name + strSeparator + strPath;
|
||||
@@ -1367,21 +1373,34 @@ namespace ModernKeePassLib
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the level of the group (i.e. the number of parent groups).
|
||||
/// Get the depth of this group (i.e. the number of ancestors).
|
||||
/// </summary>
|
||||
/// <returns>Number of parent groups.</returns>
|
||||
public uint GetLevel()
|
||||
/// <returns>Depth of this group.</returns>
|
||||
public uint GetDepth()
|
||||
{
|
||||
PwGroup pg = m_pParentGroup;
|
||||
uint uLevel = 0;
|
||||
uint d = 0;
|
||||
|
||||
while(pg != null)
|
||||
{
|
||||
pg = pg.ParentGroup;
|
||||
++uLevel;
|
||||
pg = pg.m_pParentGroup;
|
||||
++d;
|
||||
}
|
||||
|
||||
return uLevel;
|
||||
return d;
|
||||
}
|
||||
|
||||
private uint GetHeight()
|
||||
{
|
||||
if(m_listGroups.UCount == 0) return 0;
|
||||
|
||||
uint h = 0;
|
||||
foreach(PwGroup pgSub in m_listGroups)
|
||||
{
|
||||
h = Math.Max(h, pgSub.GetHeight());
|
||||
}
|
||||
|
||||
return (h + 1);
|
||||
}
|
||||
|
||||
public string GetAutoTypeSequenceInherited()
|
||||
@@ -1520,13 +1539,31 @@ namespace ModernKeePassLib
|
||||
{
|
||||
if(subGroup == null) throw new ArgumentNullException("subGroup");
|
||||
|
||||
CheckCanAddGroup(subGroup);
|
||||
m_listGroups.Add(subGroup);
|
||||
|
||||
if(bTakeOwnership) subGroup.m_pParentGroup = this;
|
||||
if(bTakeOwnership) subGroup.ParentGroup = this;
|
||||
|
||||
if(bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
internal bool CanAddGroup(PwGroup pgSub)
|
||||
{
|
||||
if(pgSub == null) { Debug.Assert(false); return false; }
|
||||
|
||||
uint dCur = GetDepth(), hSub = pgSub.GetHeight();
|
||||
return ((dCur + hSub + 1) <= MaxDepth);
|
||||
}
|
||||
|
||||
internal void CheckCanAddGroup(PwGroup pgSub)
|
||||
{
|
||||
if(!CanAddGroup(pgSub))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException(KLRes.StructsTooDeep);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an entry to this group.
|
||||
/// </summary>
|
||||
|
@@ -59,6 +59,7 @@ namespace ModernKeePassLib.Resources
|
||||
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
|
||||
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
|
||||
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
|
||||
m_strStructsTooDeep = TryGetEx(dictNew, "StructsTooDeep", m_strStructsTooDeep);
|
||||
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
|
||||
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
|
||||
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
|
||||
@@ -101,6 +102,7 @@ namespace ModernKeePassLib.Resources
|
||||
"OldFormat",
|
||||
"Passive",
|
||||
"PreAuth",
|
||||
"StructsTooDeep",
|
||||
"Timeout",
|
||||
"TryAgainSecs",
|
||||
"UnknownHeaderId",
|
||||
@@ -346,10 +348,10 @@ namespace ModernKeePassLib.Resources
|
||||
}
|
||||
|
||||
private static string m_strFrameworkNotImplExcp =
|
||||
@"The .NET framework/runtime under which KeePass is currently running does not support this operation.";
|
||||
@"The .NET Framework/runtime under which KeePass is currently running does not support this operation.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The .NET framework/runtime under which KeePass is currently running does not support this operation.'.
|
||||
/// 'The .NET Framework/runtime under which KeePass is currently running does not support this operation.'.
|
||||
/// </summary>
|
||||
public static string FrameworkNotImplExcp
|
||||
{
|
||||
@@ -477,6 +479,17 @@ namespace ModernKeePassLib.Resources
|
||||
get { return m_strPreAuth; }
|
||||
}
|
||||
|
||||
private static string m_strStructsTooDeep =
|
||||
@"Structures are nested too deeply.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Structures are nested too deeply.'.
|
||||
/// </summary>
|
||||
public static string StructsTooDeep
|
||||
{
|
||||
get { return m_strStructsTooDeep; }
|
||||
}
|
||||
|
||||
private static string m_strTimeout =
|
||||
@"Timeout";
|
||||
/// <summary>
|
||||
|
@@ -9,7 +9,7 @@ namespace ModernKeePassLib.Resources
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
public static class KSRes
|
||||
public static partial class KSRes
|
||||
{
|
||||
private static string TryGetEx(Dictionary<string, string> dictNew,
|
||||
string strName, string strDefault)
|
||||
|
@@ -23,18 +23,19 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Windows.Storage.AccessCache;
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
using System.Security.AccessControl;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
@@ -210,10 +211,10 @@ namespace ModernKeePassLib.Serialization
|
||||
// trying to set 'Owner' or 'Group' can result in an
|
||||
// UnauthorizedAccessException; thus we restore 'Access' (DACL) only
|
||||
const AccessControlSections acs = AccessControlSections.Access;
|
||||
|
||||
#endif
|
||||
bool bEfsEncrypted = false;
|
||||
byte[] pbSec = null;
|
||||
|
||||
DateTime? otCreation = null;
|
||||
|
||||
bool bBaseExists = IOConnection.FileExists(m_iocBase);
|
||||
@@ -228,9 +229,7 @@ namespace ModernKeePassLib.Serialization
|
||||
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
#if ModernKeePassLib
|
||||
otCreation = m_iocBase.StorageFile.DateCreated.UtcDateTime;
|
||||
#else
|
||||
#if !ModernKeePassLib
|
||||
otCreation = File.GetCreationTimeUtc(m_iocBase.Path);
|
||||
#endif
|
||||
#if !ModernKeePassLib
|
||||
@@ -238,7 +237,7 @@ namespace ModernKeePassLib.Serialization
|
||||
FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs);
|
||||
if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
|
||||
// if((long)(faBase & FileAttributes.ReadOnly) != 0)
|
||||
@@ -337,6 +336,7 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
if(NativeLib.IsUnix()) return;
|
||||
if(!m_iocBase.IsLocalFile()) return;
|
||||
if(IsOneDriveWorkaroundRequired()) return;
|
||||
|
||||
string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String(
|
||||
CryptoRandom.Instance.GetRandomBytes(16)));
|
||||
@@ -354,7 +354,7 @@ namespace ModernKeePassLib.Serialization
|
||||
#if ModernKeePassLib
|
||||
var tempFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
|
||||
.GetResult();
|
||||
m_iocTemp = IOConnectionInfo.FromFile(tempFile);
|
||||
m_iocTemp = IOConnectionInfo.FromStorageFile(tempFile);
|
||||
#else
|
||||
m_iocTemp = IOConnectionInfo.FromPath(strTemp);
|
||||
#endif
|
||||
@@ -370,13 +370,10 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
if(TxfMoveWithTx()) return true;
|
||||
|
||||
// Move the temporary file onto the base file's drive first,
|
||||
// such that it cannot happen that both the base file and
|
||||
// the temporary file are deleted/corrupted
|
||||
#if ModernKeePassLib
|
||||
m_iocTemp.StorageFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
|
||||
.GetResult();
|
||||
#else
|
||||
// Move the temporary file onto the base file's drive first,
|
||||
// such that it cannot happen that both the base file and
|
||||
// the temporary file are deleted/corrupted
|
||||
#if !ModernKeePassLib
|
||||
const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED |
|
||||
NativeMethods.MOVEFILE_REPLACE_EXISTING);
|
||||
bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f);
|
||||
@@ -391,9 +388,7 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
private bool TxfMoveWithTx()
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
return true;
|
||||
#else
|
||||
#if !ModernKeePassLib
|
||||
IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE);
|
||||
Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE);
|
||||
try
|
||||
@@ -438,9 +433,8 @@ namespace ModernKeePassLib.Serialization
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void ClearOld()
|
||||
@@ -470,5 +464,89 @@ namespace ModernKeePassLib.Serialization
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
// https://sourceforge.net/p/keepass/discussion/329220/thread/672ffecc65/
|
||||
// https://sourceforge.net/p/keepass/discussion/329221/thread/514786c23a/
|
||||
private bool IsOneDriveWorkaroundRequired()
|
||||
{
|
||||
#if !ModernKeePassLib
|
||||
if(NativeLib.IsUnix()) return false;
|
||||
|
||||
try
|
||||
{
|
||||
string strReleaseId = (Registry.GetValue(
|
||||
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
||||
"ReleaseId", string.Empty) as string);
|
||||
if(strReleaseId != "1809") return false;
|
||||
|
||||
string strFile = m_iocBase.Path;
|
||||
|
||||
GFunc<string, string, bool> fMatch = delegate(string strRoot, string strSfx)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strRoot)) return false;
|
||||
string strPfx = UrlUtil.EnsureTerminatingSeparator(
|
||||
strRoot, false) + strSfx;
|
||||
return strFile.StartsWith(strPfx, StrUtil.CaseIgnoreCmp);
|
||||
};
|
||||
GFunc<string, string, bool> fMatchEnv = delegate(string strEnv, string strSfx)
|
||||
{
|
||||
return fMatch(Environment.GetEnvironmentVariable(strEnv), strSfx);
|
||||
};
|
||||
|
||||
string strKnown = NativeMethods.GetKnownFolderPath(
|
||||
NativeMethods.FOLDERID_SkyDrive);
|
||||
if(fMatch(strKnown, string.Empty)) return true;
|
||||
|
||||
if(fMatchEnv("USERPROFILE", "OneDrive\\")) return true;
|
||||
if(fMatchEnv("OneDrive", string.Empty)) return true;
|
||||
if(fMatchEnv("OneDriveCommercial", string.Empty)) return true;
|
||||
if(fMatchEnv("OneDriveConsumer", string.Empty)) return true;
|
||||
|
||||
using(RegistryKey kAccs = Registry.CurrentUser.OpenSubKey(
|
||||
"Software\\Microsoft\\OneDrive\\Accounts", false))
|
||||
{
|
||||
string[] vAccs = (((kAccs != null) ? kAccs.GetSubKeyNames() :
|
||||
null) ?? new string[0]);
|
||||
|
||||
foreach(string strAcc in vAccs)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strAcc)) { Debug.Assert(false); continue; }
|
||||
|
||||
using(RegistryKey kTenants = kAccs.OpenSubKey(
|
||||
strAcc + "\\Tenants", false))
|
||||
{
|
||||
string[] vTenants = (((kTenants != null) ?
|
||||
kTenants.GetSubKeyNames() : null) ?? new string[0]);
|
||||
|
||||
foreach(string strT in vTenants)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strT)) { Debug.Assert(false); continue; }
|
||||
|
||||
using(RegistryKey kT = kTenants.OpenSubKey(strT, false))
|
||||
{
|
||||
string[] vPaths = (((kT != null) ?
|
||||
kT.GetValueNames() : null) ?? new string[0]);
|
||||
|
||||
foreach(string strPath in vPaths)
|
||||
{
|
||||
if((strPath == null) || (strPath.Length < 4) ||
|
||||
(strPath[1] != ':'))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fMatch(strPath, string.Empty)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,6 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassUAP)
|
||||
using System.Net.Cache;
|
||||
using System.Net.Security;
|
||||
@@ -600,7 +599,12 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
private static Stream OpenReadLocal(IOConnectionInfo ioc)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
return ioc.StorageFile.OpenAsync(FileAccessMode.Read).GetAwaiter().GetResult().AsStream();
|
||||
#else
|
||||
return new FileStream(ioc.Path, FileMode.Open, FileAccess.Read,
|
||||
FileShare.Read);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
@@ -654,7 +658,7 @@ namespace ModernKeePassLib.Serialization
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
|
||||
|
||||
#if ModernKeePassLib
|
||||
return ioc.StorageFile.IsAvailable;
|
||||
return ioc.StorageFile != null;
|
||||
#else
|
||||
if(ioc.IsLocalFile()) return File.Exists(ioc.Path);
|
||||
|
||||
@@ -696,7 +700,7 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
#if ModernKeePassLib
|
||||
if (!ioc.IsLocalFile()) return;
|
||||
ioc.StorageFile?.DeleteAsync().GetAwaiter().GetResult();
|
||||
ioc.StorageFile?.DeleteAsync().GetAwaiter().GetResult();
|
||||
#else
|
||||
if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
|
||||
|
||||
@@ -718,7 +722,7 @@ namespace ModernKeePassLib.Serialization
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rename/move a file. For local file system and WebDAV, the
|
||||
@@ -735,7 +739,7 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
#if ModernKeePassLib
|
||||
if (!iocFrom.IsLocalFile()) return;
|
||||
iocFrom.StorageFile?.RenameAsync(iocTo.Path).GetAwaiter().GetResult();
|
||||
iocFrom.StorageFile?.RenameAsync(iocTo.Path).GetAwaiter().GetResult();
|
||||
#else
|
||||
if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; }
|
||||
|
||||
|
@@ -25,14 +25,12 @@ using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
#if ModernKeePassLib
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
//using PCLStorage;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.AccessCache;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
@@ -70,6 +68,8 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
// private IOFileFormatHint m_ioHint = IOFileFormatHint.None;
|
||||
|
||||
public StorageFile StorageFile { get; set; }
|
||||
|
||||
private string m_strUrl = string.Empty;
|
||||
public string Path
|
||||
{
|
||||
@@ -316,29 +316,17 @@ namespace ModernKeePassLib.Serialization
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
public static IOConnectionInfo FromFile(StorageFile file)
|
||||
public static IOConnectionInfo FromStorageFile(StorageFile file)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.StorageFile = file;
|
||||
ioc.Path = file.Path;
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.Path = strPath;
|
||||
ioc.StorageFile = StorageApplicationPermissions.FutureAccessList.GetFileAsync(strPath).GetAwaiter().GetResult();
|
||||
ioc.StorageFile = file;
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
#else
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
@@ -348,10 +336,6 @@ namespace ModernKeePassLib.Serialization
|
||||
return ioc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public StorageFile StorageFile { get; set; }
|
||||
|
||||
public bool CanProbablyAccess()
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
|
@@ -30,13 +30,14 @@ using System.Drawing;
|
||||
|
||||
using ModernKeePassLib;
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
@@ -97,36 +98,10 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
private void ReadXmlStreamed(Stream sXml, Stream sParent)
|
||||
{
|
||||
ReadDocumentStreamed(CreateXmlReader(sXml), sParent);
|
||||
}
|
||||
|
||||
internal static XmlReaderSettings CreateStdXmlReaderSettings()
|
||||
{
|
||||
XmlReaderSettings xrs = new XmlReaderSettings();
|
||||
|
||||
xrs.CloseInput = true;
|
||||
xrs.IgnoreComments = true;
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
||||
#else
|
||||
#if !KeePassLibSD
|
||||
// Also see PrepMonoDev.sh script
|
||||
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
|
||||
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
|
||||
#endif
|
||||
xrs.ValidationType = ValidationType.None;
|
||||
#endif
|
||||
|
||||
return xrs;
|
||||
}
|
||||
|
||||
private static XmlReader CreateXmlReader(Stream readerStream)
|
||||
{
|
||||
XmlReaderSettings xrs = CreateStdXmlReaderSettings();
|
||||
return XmlReader.Create(readerStream, xrs);
|
||||
using(XmlReader xr = XmlUtilEx.CreateXmlReader(sXml))
|
||||
{
|
||||
ReadDocumentStreamed(xr, sParent);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadDocumentStreamed(XmlReader xr, Stream sParentStream)
|
||||
@@ -179,7 +154,7 @@ namespace ModernKeePassLib.Serialization
|
||||
}
|
||||
|
||||
++uTagCounter;
|
||||
if(((uTagCounter % 256) == 0) && bSupportsStatus)
|
||||
if(((uTagCounter & 0xFFU) == 0) && bSupportsStatus)
|
||||
{
|
||||
Debug.Assert(lStreamLength == sParentStream.Length);
|
||||
uint uPct = (uint)((sParentStream.Position * 100) /
|
||||
@@ -189,7 +164,8 @@ namespace ModernKeePassLib.Serialization
|
||||
// position/length values (M120413)
|
||||
if(uPct > 100) { Debug.Assert(false); uPct = 100; }
|
||||
|
||||
m_slLogger.SetProgress(uPct);
|
||||
if(!m_slLogger.SetProgress(uPct))
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,28 +1004,31 @@ namespace ModernKeePassLib.Serialization
|
||||
private void ReadUnknown(XmlReader xr)
|
||||
{
|
||||
Debug.Assert(false); // Unknown node!
|
||||
Debug.Assert(xr.NodeType == XmlNodeType.Element);
|
||||
|
||||
if(xr.IsEmptyElement) { m_bReadNextNode = true; return; }
|
||||
bool bRead = false;
|
||||
int cOpen = 0;
|
||||
|
||||
string strUnknownName = xr.Name;
|
||||
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null) { xb.Dispose(); return; } // ProcessNode sets m_bReadNextNode
|
||||
|
||||
bool bRead = true;
|
||||
while(true)
|
||||
do
|
||||
{
|
||||
if(bRead) xr.Read();
|
||||
bRead = true;
|
||||
|
||||
if(xr.NodeType == XmlNodeType.EndElement) break;
|
||||
if(xr.NodeType != XmlNodeType.Element) { bRead = true; continue; }
|
||||
if(xr.NodeType == XmlNodeType.EndElement) --cOpen;
|
||||
else if(xr.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
if(!xr.IsEmptyElement)
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null) { xb.Dispose(); bRead = m_bReadNextNode; continue; }
|
||||
|
||||
ReadUnknown(xr);
|
||||
bRead = m_bReadNextNode;
|
||||
++cOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(cOpen > 0);
|
||||
|
||||
Debug.Assert(xr.Name == strUnknownName); // On end tag
|
||||
m_bReadNextNode = true;
|
||||
m_bReadNextNode = bRead;
|
||||
}
|
||||
|
||||
private XorredBuffer ProcessNode(XmlReader xr)
|
||||
|
@@ -55,26 +55,23 @@ namespace ModernKeePassLib.Serialization
|
||||
/// </summary>
|
||||
public sealed partial class KdbxFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Load a KDBX file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">File to load.</param>
|
||||
/// <param name="fmt">Format.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
#if ModernKeePassLib
|
||||
public void Load(StorageFile file, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromFile(file);
|
||||
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
|
||||
}
|
||||
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromStorageFile(file);
|
||||
#else
|
||||
/// <summary>
|
||||
/// Load a KDBX file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">File to load.</param>
|
||||
/// <param name="fmt">Format.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
|
||||
#endif
|
||||
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Load a KDBX file from a stream.
|
||||
@@ -202,19 +199,17 @@ namespace ModernKeePassLib.Serialization
|
||||
}
|
||||
|
||||
#if KeePassDebug_WriteXml
|
||||
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
|
||||
// FileAccess.Write, FileShare.None);
|
||||
// try
|
||||
// {
|
||||
// while(true)
|
||||
// {
|
||||
// int b = sXml.ReadByte();
|
||||
// if(b == -1) break;
|
||||
// fsOut.WriteByte((byte)b);
|
||||
// }
|
||||
// }
|
||||
// catch(Exception) { }
|
||||
// fsOut.Close();
|
||||
#warning XML output is enabled!
|
||||
/* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
|
||||
FileAccess.Write, FileShare.None))
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
int b = sXml.ReadByte();
|
||||
if(b == -1) throw new EndOfStreamException();
|
||||
fsOut.WriteByte((byte)b);
|
||||
}
|
||||
} */
|
||||
#endif
|
||||
|
||||
ReadXmlStreamed(sXml, sHashing);
|
||||
@@ -413,7 +408,7 @@ namespace ModernKeePassLib.Serialization
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
if(m_slLogger != null)
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
|
||||
kdbID.ToString() + "!", LogStatusType.Warning);
|
||||
break;
|
||||
}
|
||||
|
@@ -429,7 +429,7 @@ namespace ModernKeePassLib.Serialization
|
||||
};
|
||||
|
||||
if(!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
|
||||
throw new InvalidOperationException();
|
||||
throw new OperationCanceledException();
|
||||
|
||||
while(groupStack.Count > 1)
|
||||
{
|
||||
|
@@ -378,8 +378,8 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
Debug.Assert(m_pwDatabase != null);
|
||||
Debug.Assert(m_pwDatabase.MasterKey != null);
|
||||
ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32(
|
||||
m_pwDatabase.KdfParameters);
|
||||
ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32Ex(
|
||||
m_pwDatabase.KdfParameters, m_slLogger);
|
||||
Debug.Assert(pbinUser != null);
|
||||
if(pbinUser == null)
|
||||
throw new SecurityException(KLRes.InvalidCompositeKey);
|
||||
@@ -492,7 +492,7 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
|
||||
strName = UrlUtil.GetSafeFileName(strName);
|
||||
|
||||
string strPath;
|
||||
int iTry = 1;
|
||||
@@ -500,8 +500,8 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
|
||||
|
||||
string strExt = UrlUtil.GetExtension(strName);
|
||||
string strDesc = UrlUtil.StripExtension(strName);
|
||||
string strExt = UrlUtil.GetExtension(strName);
|
||||
|
||||
strPath += strDesc;
|
||||
if(iTry > 1)
|
||||
|
@@ -46,6 +46,7 @@ namespace ModernKeePassLib.Translation
|
||||
public sealed class KPTranslation
|
||||
{
|
||||
public static readonly string FileExtension = "lngx";
|
||||
internal const string FileExtension1x = "lng";
|
||||
|
||||
private KPTranslationProperties m_props = new KPTranslationProperties();
|
||||
public KPTranslationProperties Properties
|
||||
|
@@ -264,8 +264,7 @@ namespace ModernKeePassLib.Utility
|
||||
[MethodImpl(MioNoOptimize)]
|
||||
public static void ZeroByteArray(byte[] pbArray)
|
||||
{
|
||||
Debug.Assert(pbArray != null);
|
||||
if(pbArray == null) throw new ArgumentNullException("pbArray");
|
||||
if(pbArray == null) { Debug.Assert(false); return; }
|
||||
|
||||
Array.Clear(pbArray, 0, pbArray.Length);
|
||||
}
|
||||
@@ -277,7 +276,7 @@ namespace ModernKeePassLib.Utility
|
||||
[MethodImpl(MioNoOptimize)]
|
||||
public static void ZeroArray<T>(T[] v)
|
||||
{
|
||||
if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); }
|
||||
if(v == null) { Debug.Assert(false); return; }
|
||||
|
||||
Array.Clear(v, 0, v.Length);
|
||||
}
|
||||
|
@@ -116,9 +116,6 @@ namespace ModernKeePassLib.Utility
|
||||
// 1760:
|
||||
// Input focus is not restored when activating a form.
|
||||
// https://sourceforge.net/p/keepass/bugs/1760/
|
||||
// 2139:
|
||||
// Shortcut keys are ignored.
|
||||
// https://sourceforge.net/p/keepass/feature-requests/2139/
|
||||
// 2140:
|
||||
// Explicit control focusing is ignored.
|
||||
// https://sourceforge.net/p/keepass/feature-requests/2140/
|
||||
|
@@ -24,7 +24,6 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
@@ -44,45 +43,41 @@ namespace ModernKeePassLib.Utility
|
||||
/// </summary>
|
||||
public sealed class CharStream
|
||||
{
|
||||
private string m_strString = string.Empty;
|
||||
private int m_nPos = 0;
|
||||
private readonly string m_str;
|
||||
private int m_iPos = 0;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get { return m_iPos; }
|
||||
set
|
||||
{
|
||||
if((value < 0) || (value > int.MaxValue))
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
m_iPos = (int)value;
|
||||
}
|
||||
}
|
||||
|
||||
public CharStream(string str)
|
||||
{
|
||||
Debug.Assert(str != null);
|
||||
if(str == null) throw new ArgumentNullException("str");
|
||||
if(str == null) { Debug.Assert(false); throw new ArgumentNullException("str"); }
|
||||
|
||||
m_strString = str;
|
||||
}
|
||||
|
||||
public void Seek(SeekOrigin org, int nSeek)
|
||||
{
|
||||
if(org == SeekOrigin.Begin)
|
||||
m_nPos = nSeek;
|
||||
else if(org == SeekOrigin.Current)
|
||||
m_nPos += nSeek;
|
||||
else if(org == SeekOrigin.End)
|
||||
m_nPos = m_strString.Length + nSeek;
|
||||
m_str = str;
|
||||
}
|
||||
|
||||
public char ReadChar()
|
||||
{
|
||||
if(m_nPos < 0) return char.MinValue;
|
||||
if(m_nPos >= m_strString.Length) return char.MinValue;
|
||||
if(m_iPos >= m_str.Length) return char.MinValue;
|
||||
|
||||
char chRet = m_strString[m_nPos];
|
||||
++m_nPos;
|
||||
return chRet;
|
||||
return m_str[m_iPos++];
|
||||
}
|
||||
|
||||
public char ReadChar(bool bSkipWhiteSpace)
|
||||
{
|
||||
if(bSkipWhiteSpace == false) return ReadChar();
|
||||
if(!bSkipWhiteSpace) return ReadChar();
|
||||
|
||||
while(true)
|
||||
{
|
||||
char ch = ReadChar();
|
||||
|
||||
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
||||
return ch;
|
||||
}
|
||||
@@ -90,29 +85,25 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
public char PeekChar()
|
||||
{
|
||||
if(m_nPos < 0) return char.MinValue;
|
||||
if(m_nPos >= m_strString.Length) return char.MinValue;
|
||||
if(m_iPos >= m_str.Length) return char.MinValue;
|
||||
|
||||
return m_strString[m_nPos];
|
||||
return m_str[m_iPos];
|
||||
}
|
||||
|
||||
public char PeekChar(bool bSkipWhiteSpace)
|
||||
{
|
||||
if(bSkipWhiteSpace == false) return PeekChar();
|
||||
if(!bSkipWhiteSpace) return PeekChar();
|
||||
|
||||
int iIndex = m_nPos;
|
||||
while(true)
|
||||
int i = m_iPos;
|
||||
while(i < m_str.Length)
|
||||
{
|
||||
if(iIndex < 0) return char.MinValue;
|
||||
if(iIndex >= m_strString.Length) return char.MinValue;
|
||||
|
||||
char ch = m_strString[iIndex];
|
||||
|
||||
char ch = m_str[i];
|
||||
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
||||
return ch;
|
||||
|
||||
++iIndex;
|
||||
++i;
|
||||
}
|
||||
|
||||
return char.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,44 +396,46 @@ namespace ModernKeePassLib.Utility
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split up a command line into application and argument.
|
||||
/// </summary>
|
||||
/// <param name="strCmdLine">Command line to split.</param>
|
||||
/// <param name="strApp">Application path.</param>
|
||||
/// <param name="strArgs">Arguments.</param>
|
||||
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
|
||||
public static void SplitCommandLine(string strCmdLine, out string strApp,
|
||||
out string strArgs)
|
||||
{
|
||||
Debug.Assert(strCmdLine != null); if(strCmdLine == null) throw new ArgumentNullException("strCmdLine");
|
||||
SplitCommandLine(strCmdLine, out strApp, out strArgs, true);
|
||||
}
|
||||
|
||||
internal static void SplitCommandLine(string strCmdLine, out string strApp,
|
||||
out string strArgs, bool bDecodeAppToPath)
|
||||
{
|
||||
if(strCmdLine == null) { Debug.Assert(false); throw new ArgumentNullException("strCmdLine"); }
|
||||
|
||||
string str = strCmdLine.Trim();
|
||||
|
||||
strApp = null; strArgs = null;
|
||||
strApp = null;
|
||||
strArgs = null;
|
||||
|
||||
if(str.StartsWith("\""))
|
||||
{
|
||||
int nSecond = str.IndexOf('\"', 1);
|
||||
if(nSecond >= 1)
|
||||
int iSecond = UrlUtil.IndexOfSecondEnclQuote(str);
|
||||
if(iSecond >= 1)
|
||||
{
|
||||
strApp = str.Substring(1, nSecond - 1).Trim();
|
||||
strArgs = str.Remove(0, nSecond + 1).Trim();
|
||||
strApp = str.Substring(1, iSecond - 1).Trim();
|
||||
strArgs = str.Remove(0, iSecond + 1).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
if(strApp == null)
|
||||
{
|
||||
int nSpace = str.IndexOf(' ');
|
||||
|
||||
if(nSpace >= 0)
|
||||
int iSpace = str.IndexOf(' ');
|
||||
if(iSpace >= 0)
|
||||
{
|
||||
strApp = str.Substring(0, nSpace);
|
||||
strArgs = str.Remove(0, nSpace).Trim();
|
||||
strApp = str.Substring(0, iSpace).Trim();
|
||||
strArgs = str.Remove(0, iSpace + 1).Trim();
|
||||
}
|
||||
else strApp = strCmdLine;
|
||||
else strApp = str;
|
||||
}
|
||||
|
||||
if(strApp == null) strApp = string.Empty;
|
||||
if(strApp == null) { Debug.Assert(false); strApp = string.Empty; }
|
||||
if(strArgs == null) strArgs = string.Empty;
|
||||
|
||||
if(bDecodeAppToPath) strApp = NativeLib.DecodeArgsToPath(strApp);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
@@ -1289,7 +1282,7 @@ namespace ModernKeePassLib.Utility
|
||||
if(uBytes <= uGB) return (((uBytes - 1UL) / uMB) + 1UL).ToString() + " MB";
|
||||
if(uBytes <= uTB) return (((uBytes - 1UL) / uGB) + 1UL).ToString() + " GB";
|
||||
|
||||
return (((uBytes - 1UL)/ uTB) + 1UL).ToString() + " TB";
|
||||
return (((uBytes - 1UL) / uTB) + 1UL).ToString() + " TB";
|
||||
}
|
||||
|
||||
public static string FormatDataSizeKB(ulong uBytes)
|
||||
|
@@ -23,6 +23,7 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
@@ -38,12 +39,8 @@ namespace ModernKeePassLib.Utility
|
||||
/// </summary>
|
||||
public static class UrlUtil
|
||||
{
|
||||
private static readonly char[] m_vDirSeps = new char[] {
|
||||
'\\', '/', UrlUtil.LocalDirSepChar };
|
||||
#if !ModernKeePassLib
|
||||
private static readonly char[] m_vPathTrimCharsWs = new char[] {
|
||||
private static readonly char[] g_vPathTrimCharsWs = new char[] {
|
||||
'\"', ' ', '\t', '\r', '\n' };
|
||||
#endif
|
||||
|
||||
public static char LocalDirSepChar
|
||||
{
|
||||
@@ -57,6 +54,32 @@ namespace ModernKeePassLib.Utility
|
||||
#endif
|
||||
}
|
||||
|
||||
private static char[] g_vDirSepChars = null;
|
||||
private static char[] DirSepChars
|
||||
{
|
||||
get
|
||||
{
|
||||
if(g_vDirSepChars == null)
|
||||
{
|
||||
List<char> l = new List<char>();
|
||||
l.Add('/'); // For URLs, also on Windows
|
||||
|
||||
// On Unix-like systems, '\\' is not a separator
|
||||
if(!NativeLib.IsUnix()) l.Add('\\');
|
||||
|
||||
if(!l.Contains(UrlUtil.LocalDirSepChar))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
l.Add(UrlUtil.LocalDirSepChar);
|
||||
}
|
||||
|
||||
g_vDirSepChars = l.ToArray();
|
||||
}
|
||||
|
||||
return g_vDirSepChars;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the directory (path) of a file name. The returned string may be
|
||||
/// terminated by a directory separator character. Example:
|
||||
@@ -79,7 +102,7 @@ namespace ModernKeePassLib.Utility
|
||||
Debug.Assert(strFile != null);
|
||||
if(strFile == null) throw new ArgumentNullException("strFile");
|
||||
|
||||
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps);
|
||||
int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
|
||||
if(nLastSep < 0) return string.Empty; // No directory
|
||||
|
||||
if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
|
||||
@@ -103,7 +126,7 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
|
||||
|
||||
int nLastSep = strPath.LastIndexOfAny(m_vDirSeps);
|
||||
int nLastSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
|
||||
|
||||
if(nLastSep < 0) return strPath;
|
||||
if(nLastSep >= (strPath.Length - 1)) return string.Empty;
|
||||
@@ -120,7 +143,7 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
|
||||
|
||||
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
|
||||
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
|
||||
int nLastExtDot = strPath.LastIndexOf('.');
|
||||
|
||||
if(nLastExtDot <= nLastDirSep) return strPath;
|
||||
@@ -137,7 +160,7 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
|
||||
|
||||
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
|
||||
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
|
||||
int nLastExtDot = strPath.LastIndexOf('.');
|
||||
|
||||
if(nLastExtDot <= nLastDirSep) return string.Empty;
|
||||
@@ -162,11 +185,8 @@ namespace ModernKeePassLib.Utility
|
||||
if(nLength <= 0) return string.Empty;
|
||||
|
||||
char chLast = strPath[nLength - 1];
|
||||
|
||||
for(int i = 0; i < m_vDirSeps.Length; ++i)
|
||||
{
|
||||
if(chLast == m_vDirSeps[i]) return strPath;
|
||||
}
|
||||
if(Array.IndexOf<char>(UrlUtil.DirSepChars, chLast) >= 0)
|
||||
return strPath;
|
||||
|
||||
if(bUrl) return (strPath + '/');
|
||||
return (strPath + UrlUtil.LocalDirSepChar);
|
||||
@@ -230,21 +250,35 @@ namespace ModernKeePassLib.Utility
|
||||
return false;
|
||||
} */
|
||||
|
||||
internal static int IndexOfSecondEnclQuote(string str)
|
||||
{
|
||||
if(str == null) { Debug.Assert(false); return -1; }
|
||||
if(str.Length <= 1) return -1;
|
||||
if(str[0] != '\"') { Debug.Assert(false); return -1; }
|
||||
|
||||
if(NativeLib.IsUnix())
|
||||
{
|
||||
// Find non-escaped quote
|
||||
string strFlt = str.Replace("\\\\", new string(
|
||||
StrUtil.GetUnusedChar(str + "\\\""), 2)); // Same length
|
||||
Match m = Regex.Match(strFlt, "[^\\\\]\\u0022");
|
||||
int i = (((m != null) && m.Success) ? m.Index : -1);
|
||||
return ((i >= 0) ? (i + 1) : -1); // Index of quote
|
||||
}
|
||||
|
||||
// Windows does not allow quotes in folder/file names
|
||||
return str.IndexOf('\"', 1);
|
||||
}
|
||||
|
||||
public static string GetQuotedAppPath(string strPath)
|
||||
{
|
||||
if(strPath == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
// int nFirst = strPath.IndexOf('\"');
|
||||
// int nSecond = strPath.IndexOf('\"', nFirst + 1);
|
||||
// if((nFirst >= 0) && (nSecond >= 0))
|
||||
// return strPath.Substring(nFirst + 1, nSecond - nFirst - 1);
|
||||
// return strPath;
|
||||
|
||||
string str = strPath.Trim();
|
||||
if(str.Length <= 1) return str;
|
||||
if(str[0] != '\"') return str;
|
||||
|
||||
int iSecond = str.IndexOf('\"', 1);
|
||||
int iSecond = IndexOfSecondEnclQuote(str);
|
||||
if(iSecond <= 0) return str;
|
||||
|
||||
return str.Substring(1, iSecond - 1);
|
||||
@@ -256,7 +290,7 @@ namespace ModernKeePassLib.Utility
|
||||
if(strUrl == null) throw new ArgumentNullException("strUrl");
|
||||
|
||||
string str = strUrl;
|
||||
if(str.StartsWith(@"file:///", StrUtil.CaseIgnoreCmp))
|
||||
if(str.StartsWith("file:///", StrUtil.CaseIgnoreCmp))
|
||||
str = str.Substring(8, str.Length - 8);
|
||||
|
||||
str = str.Replace('/', UrlUtil.LocalDirSepChar);
|
||||
@@ -338,8 +372,8 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
string strBase = GetShortestAbsolutePath(strBaseFile);
|
||||
string strTarget = GetShortestAbsolutePath(strTargetFile);
|
||||
string[] vBase = strBase.Split(m_vDirSeps);
|
||||
string[] vTarget = strTarget.Split(m_vDirSeps);
|
||||
string[] vBase = strBase.Split(UrlUtil.DirSepChars);
|
||||
string[] vTarget = strTarget.Split(UrlUtil.DirSepChars);
|
||||
|
||||
int i = 0;
|
||||
while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
|
||||
@@ -416,13 +450,14 @@ namespace ModernKeePassLib.Utility
|
||||
if(IsUncPath(strPath))
|
||||
{
|
||||
char chSep = strPath[0];
|
||||
Debug.Assert(Array.IndexOf<char>(m_vDirSeps, chSep) >= 0);
|
||||
char[] vSep = ((chSep == '/') ? (new char[] { '/' }) :
|
||||
(new char[] { '\\', '/' }));
|
||||
|
||||
List<string> l = new List<string>();
|
||||
#if !KeePassLibSD
|
||||
string[] v = strPath.Split(m_vDirSeps, StringSplitOptions.None);
|
||||
string[] v = strPath.Split(vSep, StringSplitOptions.None);
|
||||
#else
|
||||
string[] v = strPath.Split(m_vDirSeps);
|
||||
string[] v = strPath.Split(vSep);
|
||||
#endif
|
||||
Debug.Assert((v.Length >= 3) && (v[0].Length == 0) &&
|
||||
(v[1].Length == 0));
|
||||
@@ -463,8 +498,8 @@ namespace ModernKeePassLib.Utility
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); return strPath; }
|
||||
|
||||
Debug.Assert(str.IndexOf("\\..\\") < 0);
|
||||
foreach(char ch in m_vDirSeps)
|
||||
Debug.Assert((str.IndexOf("\\..\\") < 0) || NativeLib.IsUnix());
|
||||
foreach(char ch in UrlUtil.DirSepChars)
|
||||
{
|
||||
string strSep = new string(ch, 1);
|
||||
str = str.Replace(strSep + "." + strSep, strSep);
|
||||
@@ -494,24 +529,30 @@ namespace ModernKeePassLib.Utility
|
||||
return nLength;
|
||||
}
|
||||
|
||||
internal static string GetScheme(string strUrl)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strUrl)) return string.Empty;
|
||||
|
||||
int i = strUrl.IndexOf(':');
|
||||
if(i > 0) return strUrl.Substring(0, i);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string RemoveScheme(string strUrl)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strUrl)) return string.Empty;
|
||||
|
||||
int nNetScheme = strUrl.IndexOf(@"://", StrUtil.CaseIgnoreCmp);
|
||||
int nShScheme = strUrl.IndexOf(@":/", StrUtil.CaseIgnoreCmp);
|
||||
int nSmpScheme = strUrl.IndexOf(@":", StrUtil.CaseIgnoreCmp);
|
||||
int i = strUrl.IndexOf(':');
|
||||
if(i < 0) return strUrl; // No scheme to remove
|
||||
++i;
|
||||
|
||||
if((nNetScheme < 0) && (nShScheme < 0) && (nSmpScheme < 0))
|
||||
return strUrl; // No scheme
|
||||
// A single '/' indicates a path (absolute) and should not be removed
|
||||
if(((i + 1) < strUrl.Length) && (strUrl[i] == '/') &&
|
||||
(strUrl[i + 1] == '/'))
|
||||
i += 2; // Skip authority prefix
|
||||
|
||||
int nMin = Math.Min(Math.Min((nNetScheme >= 0) ? nNetScheme : int.MaxValue,
|
||||
(nShScheme >= 0) ? nShScheme : int.MaxValue),
|
||||
(nSmpScheme >= 0) ? nSmpScheme : int.MaxValue);
|
||||
|
||||
if(nMin == nNetScheme) return strUrl.Substring(nMin + 3);
|
||||
if(nMin == nShScheme) return strUrl.Substring(nMin + 2);
|
||||
return strUrl.Substring(nMin + 1);
|
||||
return strUrl.Substring(i);
|
||||
}
|
||||
|
||||
public static string ConvertSeparators(string strPath)
|
||||
@@ -538,22 +579,50 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
public static string FilterFileName(string strName)
|
||||
{
|
||||
if(strName == null) { Debug.Assert(false); return string.Empty; }
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strName;
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||
|
||||
str = str.Replace('/', '-');
|
||||
str = str.Replace('\\', '-');
|
||||
str = str.Replace(":", string.Empty);
|
||||
str = str.Replace("*", string.Empty);
|
||||
str = str.Replace("?", string.Empty);
|
||||
str = str.Replace("\"", string.Empty);
|
||||
str = str.Replace(@"'", string.Empty);
|
||||
str = str.Replace('<', '(');
|
||||
str = str.Replace('>', ')');
|
||||
str = str.Replace('|', '-');
|
||||
StringBuilder sb = new StringBuilder(strName.Length);
|
||||
foreach(char ch in strName)
|
||||
{
|
||||
if(ch < '\u0020') continue;
|
||||
|
||||
return str;
|
||||
switch(ch)
|
||||
{
|
||||
case '\"':
|
||||
case '*':
|
||||
case ':':
|
||||
case '?':
|
||||
break;
|
||||
|
||||
case '/':
|
||||
case '\\':
|
||||
case '|':
|
||||
sb.Append('-');
|
||||
break;
|
||||
|
||||
case '<':
|
||||
sb.Append('(');
|
||||
break;
|
||||
|
||||
case '>':
|
||||
sb.Append(')');
|
||||
break;
|
||||
|
||||
default: sb.Append(ch); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim trailing spaces and periods
|
||||
for(int i = sb.Length - 1; i >= 0; --i)
|
||||
{
|
||||
char ch = sb[i];
|
||||
if((ch == ' ') || (ch == '.')) sb.Remove(i, 1);
|
||||
else break;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -672,7 +741,7 @@ namespace ModernKeePassLib.Utility
|
||||
foreach(string strPathRaw in v)
|
||||
{
|
||||
if(strPathRaw == null) { Debug.Assert(false); continue; }
|
||||
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
|
||||
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
|
||||
if(strPath.Length == 0) { Debug.Assert(false); continue; }
|
||||
Debug.Assert(strPath == strPathRaw);
|
||||
|
||||
@@ -709,7 +778,7 @@ namespace ModernKeePassLib.Utility
|
||||
if(fi == null) { Debug.Assert(false); continue; }
|
||||
string strPathRaw = fi.FullName;
|
||||
if(strPathRaw == null) { Debug.Assert(false); continue; }
|
||||
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
|
||||
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
|
||||
if(strPath.Length == 0) { Debug.Assert(false); continue; }
|
||||
Debug.Assert(strPath == strPathRaw);
|
||||
|
||||
@@ -783,5 +852,19 @@ namespace ModernKeePassLib.Utility
|
||||
char ch = char.ToUpperInvariant(strPath[0]);
|
||||
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
|
||||
}
|
||||
|
||||
internal static string GetSafeFileName(string strName)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(strName));
|
||||
|
||||
string str = FilterFileName(GetFileName(strName ?? string.Empty));
|
||||
|
||||
if(string.IsNullOrEmpty(str))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return "File.dat";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ namespace ModernKeePassLib.Utility
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if KeePassUAP
|
||||
#if KeePassUAP || ModernKeePassLib
|
||||
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
||||
#else
|
||||
// Also see PrepMonoDev.sh script
|
||||
|
Reference in New Issue
Block a user