Update to version 2.42.1

Some changes
Removed FutureAccesList code as it works only with UWP
This commit is contained in:
Geoffroy BONNEVILLE
2019-07-26 18:28:53 +02:00
parent 85b0e9f321
commit 26e8e5c223
52 changed files with 1373 additions and 506 deletions

View File

@@ -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()
{

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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];

View File

@@ -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>

View File

@@ -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
{

View File

@@ -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()
{
}

View File

@@ -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

View File

@@ -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
{

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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)
{

View File

@@ -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>

View File

@@ -61,7 +61,7 @@ namespace ModernKeePassLib
}
/// <summary>
/// Methods for merging password databases/entries.
/// Methods for merging databases/entries.
/// </summary>
public enum PwMergeMethod
{

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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; }

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -429,7 +429,7 @@ namespace ModernKeePassLib.Serialization
};
if(!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new InvalidOperationException();
throw new OperationCanceledException();
while(groupStack.Count > 1)
{

View File

@@ -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)

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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/

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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