mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 15:40:18 -04:00
WIP Lib version 2.39.1
This commit is contained in:
52
ModernKeePass.Shared/ModernKeePass.Shared.csproj
Normal file
52
ModernKeePass.Shared/ModernKeePass.Shared.csproj
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A3354969-5AAC-4075-8CBF-EA4805B59EFA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ModernKeePass.Shared</RootNamespace>
|
||||
<AssemblyName>ModernKeePass.Shared</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
30
ModernKeePass.Shared/Properties/AssemblyInfo.cs
Normal file
30
ModernKeePass.Shared/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ModernKeePass.Shared")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ModernKeePass.Shared")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
10
ModernKeePass.Shared/project.json
Normal file
10
ModernKeePass.Shared/project.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"supports": {},
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Portable.Compatibility": "1.0.1",
|
||||
"NETStandard.Library": "1.6.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.2": {}
|
||||
}
|
||||
}
|
@@ -9,8 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.Lib", "Modern
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.LibTest", "ModernKeePassLib.Test\ModernKeePass.LibTest.csproj", "{0A4279CF-2A67-4868-9906-052E50C25F3B}"
|
||||
EndProject
|
||||
Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "Scripts", "Scripts\Scripts.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.AppTest", "ModernKeePassApp.Test\ModernKeePass.AppTest.csproj", "{7E80F5E7-724A-4668-9333-B10F5D75C6D0}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -89,22 +87,6 @@ Global
|
||||
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.ActiveCfg = Release|x86
|
||||
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Build.0 = Release|x86
|
||||
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Deploy.0 = Release|x86
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x64.Build.0 = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
|
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using ModernKeePass.Services;
|
||||
|
||||
namespace ModernKeePass.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class DatabaseChangedAttribute: Attribute
|
||||
{
|
||||
public DatabaseChangedAttribute()
|
||||
{
|
||||
DatabaseService.Instance.HasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
using System.Reflection;
|
||||
using ModernKeePass.Interfaces;
|
||||
|
||||
namespace ModernKeePass.Aop
|
||||
{
|
||||
public class DatabaseChangedProxy<T>: IProxyInvocationHandler
|
||||
{
|
||||
private readonly T _decorated;
|
||||
private readonly IDatabaseService _databaseService;
|
||||
|
||||
public DatabaseChangedProxy(T decorated, IDatabaseService databaseService)
|
||||
{
|
||||
_decorated = decorated;
|
||||
_databaseService = databaseService;
|
||||
}
|
||||
|
||||
public object Invoke(object proxy, MethodInfo method, object[] parameters)
|
||||
{
|
||||
object retVal = null;
|
||||
retVal = method.Invoke(proxy, parameters);
|
||||
_databaseService.HasChanged = true;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
@@ -111,11 +111,9 @@
|
||||
<Compile Include="Actions\ClipboardAction.cs" />
|
||||
<Compile Include="Actions\NavigateToUrlAction.cs" />
|
||||
<Compile Include="Actions\SetupFocusAction.cs" />
|
||||
<Compile Include="Aop\DatabaseChangedProxy.cs" />
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Aop\DatabaseChanged.cs" />
|
||||
<Compile Include="Exceptions\DatabaseOpenedException.cs" />
|
||||
<Compile Include="Interfaces\ILicenseService.cs" />
|
||||
<Compile Include="Interfaces\IProxyInvocationHandler.cs" />
|
||||
|
@@ -136,7 +136,7 @@ namespace ModernKeePassLib.Collections
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ps.ReadString() != kvp.Value.ReadString()) return false;
|
||||
if(!ps.Equals(kvp.Value, false)) return false;
|
||||
}
|
||||
|
||||
if(bNeEqStd)
|
||||
@@ -292,12 +292,7 @@ namespace ModernKeePassLib.Collections
|
||||
if(ps == null) return; // Nothing to do, no assert
|
||||
|
||||
if(ps.IsProtected != bProtect)
|
||||
{
|
||||
byte[] pbData = ps.ReadUtf8();
|
||||
Set(strField, new ProtectedString(bProtect, pbData));
|
||||
|
||||
if(bProtect) MemUtil.ZeroByteArray(pbData);
|
||||
}
|
||||
Set(strField, ps.WithProtection(bProtect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -352,6 +352,13 @@ namespace ModernKeePassLib.Collections
|
||||
m_vObjects.Sort(tComparer);
|
||||
}
|
||||
|
||||
public void Sort(Comparison<T> tComparison)
|
||||
{
|
||||
if(tComparison == null) throw new ArgumentNullException("tComparison");
|
||||
|
||||
m_vObjects.Sort(tComparison);
|
||||
}
|
||||
|
||||
public static PwObjectList<T> FromArray(T[] tArray)
|
||||
{
|
||||
if(tArray == null) throw new ArgumentNullException("tArray");
|
||||
|
@@ -223,48 +223,43 @@ namespace ModernKeePassLib.Cryptography
|
||||
pb = DiagnosticsExt.GetProcessEntropy();
|
||||
MemUtil.Write(ms, pb);
|
||||
#elif !KeePassLibSD
|
||||
Process p = null;
|
||||
try
|
||||
{
|
||||
p = Process.GetCurrentProcess();
|
||||
using(Process p = Process.GetCurrentProcess())
|
||||
{
|
||||
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.HandleCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.Id);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.HandleCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.Id);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
// Not supported in Mono 1.2.6:
|
||||
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
|
||||
// MemUtil.Write(ms, pb);
|
||||
// Not supported in Mono 1.2.6:
|
||||
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
|
||||
// MemUtil.Write(ms, pb);
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
finally
|
||||
{
|
||||
try { if(p != null) p.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@@ -35,6 +35,42 @@ namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public static class CryptoUtil
|
||||
{
|
||||
private static bool? g_obProtData = null;
|
||||
public static bool IsProtectedDataSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
if(g_obProtData.HasValue) return g_obProtData.Value;
|
||||
|
||||
bool b = false;
|
||||
try
|
||||
{
|
||||
Random r = CryptoRandom.NewWeakRandom();
|
||||
|
||||
byte[] pbData = new byte[137];
|
||||
r.NextBytes(pbData);
|
||||
|
||||
byte[] pbEnt = new byte[41];
|
||||
r.NextBytes(pbEnt);
|
||||
|
||||
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
if((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
|
||||
{
|
||||
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
if((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
Debug.Assert(b); // Should be supported on all systems
|
||||
g_obProtData = b;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] HashSha256(byte[] pbData)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData");
|
||||
@@ -165,5 +201,37 @@ namespace ModernKeePassLib.Cryptography
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
|
||||
DataProtectionScope s)
|
||||
{
|
||||
return ProtectDataPriv(pb, true, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
|
||||
DataProtectionScope s)
|
||||
{
|
||||
return ProtectDataPriv(pb, false, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
|
||||
byte[] pbOptEntropy, DataProtectionScope s)
|
||||
{
|
||||
if(pb == null) throw new ArgumentNullException("pb");
|
||||
|
||||
if((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
|
||||
pbOptEntropy = null;
|
||||
|
||||
if(CryptoUtil.IsProtectedDataSupported)
|
||||
{
|
||||
if(bProtect)
|
||||
return ProtectedData.Protect(pb, pbOptEntropy, s);
|
||||
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
byte[] pbCopy = new byte[pb.Length];
|
||||
Array.Copy(pb, pbCopy, pb.Length);
|
||||
return pbCopy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -71,9 +71,6 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
|
||||
private static bool GCryptInitLib()
|
||||
{
|
||||
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4); // Also on 64-bit systems
|
||||
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
|
||||
|
||||
if(!NativeLib.IsUnix()) return false; // Independent of workaround state
|
||||
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off
|
||||
|
||||
|
@@ -239,8 +239,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
PwProfile pp = new PwProfile();
|
||||
Debug.Assert(psPassword != null); if(psPassword == null) return pp;
|
||||
|
||||
byte[] pbUtf8 = psPassword.ReadUtf8();
|
||||
char[] vChars = StrUtil.Utf8.GetChars(pbUtf8);
|
||||
char[] vChars = psPassword.ReadChars();
|
||||
|
||||
pp.GeneratorType = PasswordGeneratorType.CharSet;
|
||||
pp.Length = (uint)vChars.Length;
|
||||
@@ -266,7 +265,6 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<char>(vChars);
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
return pp;
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
#endif
|
||||
#endif
|
||||
|
||||
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
|
||||
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
uDictSize = 0;
|
||||
|
@@ -420,11 +420,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
|
||||
|
||||
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
|
||||
uint uResult = EstimatePasswordBits(vChars);
|
||||
MemUtil.ZeroArray<char>(vChars);
|
||||
char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
|
||||
uint r;
|
||||
try { r = EstimatePasswordBits(v); }
|
||||
finally { MemUtil.ZeroArray<char>(v); }
|
||||
|
||||
return uResult;
|
||||
return r;
|
||||
}
|
||||
|
||||
private static QeCharType GetCharType(char ch)
|
||||
|
@@ -22,6 +22,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
@@ -55,6 +56,25 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// </summary>
|
||||
public static void Perform()
|
||||
{
|
||||
#if KeePassUAP
|
||||
Debug.Assert(Marshal.SizeOf<int>() == 4);
|
||||
Debug.Assert(Marshal.SizeOf<uint>() == 4);
|
||||
Debug.Assert(Marshal.SizeOf<long>() == 8);
|
||||
Debug.Assert(Marshal.SizeOf<ulong>() == 8);
|
||||
Debug.Assert(Marshal.SizeOf<IntPtr>() == IntPtr.Size);
|
||||
#else
|
||||
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4);
|
||||
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
|
||||
Debug.Assert(Marshal.SizeOf(typeof(long)) == 8);
|
||||
Debug.Assert(Marshal.SizeOf(typeof(ulong)) == 8);
|
||||
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == IntPtr.Size);
|
||||
#endif
|
||||
Debug.Assert((IntPtr.Size == 4) || (IntPtr.Size == 8));
|
||||
|
||||
Debug.Assert((int)PwIcon.World == 1);
|
||||
Debug.Assert((int)PwIcon.Warning == 2);
|
||||
Debug.Assert((int)PwIcon.BlackBerry == 68);
|
||||
|
||||
Random r = CryptoRandom.NewWeakRandom();
|
||||
|
||||
TestFipsComplianceProblems(); // Must be the first test
|
||||
@@ -76,10 +96,6 @@ namespace ModernKeePassLib.Cryptography
|
||||
TestStrUtil();
|
||||
TestUrlUtil();
|
||||
|
||||
Debug.Assert((int)PwIcon.World == 1);
|
||||
Debug.Assert((int)PwIcon.Warning == 2);
|
||||
Debug.Assert((int)PwIcon.BlackBerry == 68);
|
||||
|
||||
#if KeePassUAP
|
||||
SelfTestEx.Perform();
|
||||
#endif
|
||||
@@ -944,6 +960,14 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(ps.ReadString() != str)
|
||||
throw new SecurityException("ProtectedString-14");
|
||||
}
|
||||
|
||||
ps = new ProtectedString(false, "ABCD");
|
||||
ps2 = new ProtectedString(true, "EFG");
|
||||
ps += (ps2 + "HI");
|
||||
if(!ps.Equals(new ProtectedString(true, "ABCDEFGHI"), true))
|
||||
throw new SecurityException("ProtectedString-15");
|
||||
if(!ps.Equals(new ProtectedString(false, "ABCDEFGHI"), false))
|
||||
throw new SecurityException("ProtectedString-16");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1041,6 +1065,30 @@ namespace ModernKeePassLib.Cryptography
|
||||
throw new InvalidOperationException("StrUtil-Case1");
|
||||
if(string.Equals(@"a<b", @"a>b", StrUtil.CaseIgnoreCmp))
|
||||
throw new InvalidOperationException("StrUtil-Case2");
|
||||
|
||||
const string strNL = "\n01\r23\n45\r\n67\r";
|
||||
string strW = StrUtil.NormalizeNewLines(strNL, true);
|
||||
string strL = StrUtil.NormalizeNewLines(strNL, false);
|
||||
if(strW != "\r\n01\r\n23\r\n45\r\n67\r\n")
|
||||
throw new InvalidOperationException("StrUtil-NewLine1");
|
||||
if(strL != "\n01\n23\n45\n67\n")
|
||||
throw new InvalidOperationException("StrUtil-NewLine2");
|
||||
if(StrUtil.IsNewLineNormalized(strNL.ToCharArray(), true))
|
||||
throw new InvalidOperationException("StrUtil-NewLine3");
|
||||
if(StrUtil.IsNewLineNormalized(strNL.ToCharArray(), false))
|
||||
throw new InvalidOperationException("StrUtil-NewLine4");
|
||||
if(!StrUtil.IsNewLineNormalized(strW.ToCharArray(), true))
|
||||
throw new InvalidOperationException("StrUtil-NewLine5");
|
||||
if(StrUtil.IsNewLineNormalized(strW.ToCharArray(), false))
|
||||
throw new InvalidOperationException("StrUtil-NewLine6");
|
||||
if(StrUtil.IsNewLineNormalized(strL.ToCharArray(), true))
|
||||
throw new InvalidOperationException("StrUtil-NewLine7");
|
||||
if(!StrUtil.IsNewLineNormalized(strL.ToCharArray(), false))
|
||||
throw new InvalidOperationException("StrUtil-NewLine8");
|
||||
if(!StrUtil.IsNewLineNormalized(string.Empty.ToCharArray(), true))
|
||||
throw new InvalidOperationException("StrUtil-NewLine9");
|
||||
if(!StrUtil.IsNewLineNormalized(string.Empty.ToCharArray(), false))
|
||||
throw new InvalidOperationException("StrUtil-NewLine10");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -275,7 +275,7 @@ namespace ModernKeePassLib.Keys
|
||||
}
|
||||
}
|
||||
#else
|
||||
XmlDocument doc = new XmlDocument();
|
||||
XmlDocument doc = XmlUtilEx.CreateXmlDocument();
|
||||
doc.Load(ms);
|
||||
|
||||
XmlElement el = doc.DocumentElement;
|
||||
@@ -320,49 +320,31 @@ namespace ModernKeePassLib.Keys
|
||||
#else
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
|
||||
#endif
|
||||
Stream sOut = IOConnection.OpenWrite(ioc);
|
||||
using(Stream s = IOConnection.OpenWrite(ioc))
|
||||
{
|
||||
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(s))
|
||||
{
|
||||
xw.WriteStartDocument();
|
||||
xw.WriteStartElement(RootElementName); // <KeyFile>
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
XmlWriterSettings xws = new XmlWriterSettings();
|
||||
xws.Encoding = StrUtil.Utf8;
|
||||
xws.Indent = false;
|
||||
xw.WriteStartElement(MetaElementName); // <Meta>
|
||||
xw.WriteStartElement(VersionElementName); // <Version>
|
||||
xw.WriteString("1.00");
|
||||
xw.WriteEndElement(); // </Version>
|
||||
xw.WriteEndElement(); // </Meta>
|
||||
|
||||
XmlWriter xtw = XmlWriter.Create(sOut, xws);
|
||||
#else
|
||||
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
|
||||
#endif
|
||||
xw.WriteStartElement(KeyElementName); // <Key>
|
||||
|
||||
xtw.WriteStartDocument();
|
||||
xtw.WriteWhitespace("\r\n");
|
||||
xtw.WriteStartElement(RootElementName); // KeyFile
|
||||
xtw.WriteWhitespace("\r\n\t");
|
||||
xw.WriteStartElement(KeyDataElementName); // <Data>
|
||||
xw.WriteString(Convert.ToBase64String(pbKeyData));
|
||||
xw.WriteEndElement(); // </Data>
|
||||
|
||||
xtw.WriteStartElement(MetaElementName); // Meta
|
||||
xtw.WriteWhitespace("\r\n\t\t");
|
||||
xtw.WriteStartElement(VersionElementName); // Version
|
||||
xtw.WriteString("1.00");
|
||||
xtw.WriteEndElement(); // End Version
|
||||
xtw.WriteWhitespace("\r\n\t");
|
||||
xtw.WriteEndElement(); // End Meta
|
||||
xtw.WriteWhitespace("\r\n\t");
|
||||
xw.WriteEndElement(); // </Key>
|
||||
|
||||
xtw.WriteStartElement(KeyElementName); // Key
|
||||
xtw.WriteWhitespace("\r\n\t\t");
|
||||
|
||||
xtw.WriteStartElement(KeyDataElementName); // Data
|
||||
xtw.WriteString(Convert.ToBase64String(pbKeyData));
|
||||
xtw.WriteEndElement(); // End Data
|
||||
xtw.WriteWhitespace("\r\n\t");
|
||||
|
||||
xtw.WriteEndElement(); // End Key
|
||||
xtw.WriteWhitespace("\r\n");
|
||||
|
||||
xtw.WriteEndElement(); // RootElementName
|
||||
xtw.WriteWhitespace("\r\n");
|
||||
xtw.WriteEndDocument(); // End KeyFile
|
||||
xtw.Dispose();
|
||||
|
||||
sOut.Dispose();
|
||||
xw.WriteEndElement(); // </KeyFile>
|
||||
xw.WriteEndDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -65,11 +65,8 @@ namespace ModernKeePassLib.Keys
|
||||
/// </summary>
|
||||
public KcpUserAccount()
|
||||
{
|
||||
// Test if ProtectedData is supported -- throws an exception
|
||||
// when running on an old system (Windows 98 / ME).
|
||||
byte[] pbDummyData = new byte[128];
|
||||
ProtectedData.Protect(pbDummyData, m_pbEntropy,
|
||||
DataProtectionScope.CurrentUser);
|
||||
if(!CryptoUtil.IsProtectedDataSupported)
|
||||
throw new PlatformNotSupportedException(); // Windows 98/ME
|
||||
|
||||
byte[] pbKey = LoadUserKey(false);
|
||||
if(pbKey == null) pbKey = CreateUserKey();
|
||||
@@ -88,17 +85,17 @@ namespace ModernKeePassLib.Keys
|
||||
// m_pbKeyData = null;
|
||||
// }
|
||||
|
||||
private static string GetUserKeyFilePath(bool bCreate)
|
||||
{
|
||||
private static string GetUserKeyFilePath(bool bCreate)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
|
||||
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
|
||||
#else
|
||||
string strUserDir = Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData);
|
||||
#endif
|
||||
|
||||
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
|
||||
strUserDir += PwDefs.ShortProductName;
|
||||
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
|
||||
strUserDir += PwDefs.ShortProductName;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
|
||||
@@ -115,9 +112,9 @@ namespace ModernKeePassLib.Keys
|
||||
byte[] pbKey = null;
|
||||
|
||||
#if !KeePassLibSD
|
||||
try
|
||||
{
|
||||
string strFilePath = GetUserKeyFilePath(false);
|
||||
try
|
||||
{
|
||||
string strFilePath = GetUserKeyFilePath(false);
|
||||
|
||||
#if ModernKeePassLib
|
||||
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForReadAsync().GetAwaiter().GetResult();
|
||||
@@ -128,7 +125,7 @@ namespace ModernKeePassLib.Keys
|
||||
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
|
||||
#endif
|
||||
|
||||
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy,
|
||||
pbKey = CryptoUtil.UnprotectData(pbProtectedKey, m_pbEntropy,
|
||||
DataProtectionScope.CurrentUser);
|
||||
}
|
||||
catch(Exception)
|
||||
@@ -138,7 +135,7 @@ namespace ModernKeePassLib.Keys
|
||||
}
|
||||
#endif
|
||||
|
||||
return pbKey;
|
||||
return pbKey;
|
||||
}
|
||||
|
||||
private static byte[] CreateUserKey()
|
||||
@@ -148,9 +145,9 @@ namespace ModernKeePassLib.Keys
|
||||
#else
|
||||
string strFilePath = GetUserKeyFilePath(true);
|
||||
|
||||
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
|
||||
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
|
||||
m_pbEntropy, DataProtectionScope.CurrentUser);
|
||||
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
|
||||
byte[] pbProtectedKey = CryptoUtil.ProtectData(pbRandomKey,
|
||||
m_pbEntropy, DataProtectionScope.CurrentUser);
|
||||
#if ModernKeePassLib
|
||||
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
|
||||
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);
|
||||
|
@@ -130,6 +130,8 @@
|
||||
<Compile Include="Serialization\IOConnectionInfo.cs" />
|
||||
<Compile Include="Serialization\OldFormatException.cs" />
|
||||
<Compile Include="Utility\AppLogEx.cs" />
|
||||
<Compile Include="Utility\TypeOverridePool.cs" />
|
||||
<Compile Include="Utility\XmlUtilEx.cs" />
|
||||
<None Include="Utility\GfxUtil.cs" />
|
||||
<Compile Include="Utility\GfxUtil.PCL.cs" />
|
||||
<Compile Include="Utility\MemUtil.cs" />
|
||||
@@ -149,7 +151,7 @@
|
||||
<None Include="Libs\Windows.winmd" />
|
||||
<None Include="ModernKeePassLib.nuspec" />
|
||||
<None Include="project.json" />
|
||||
<None Include="Utility\MessageService.cs" />
|
||||
<Compile Include="Utility\MessageService.cs" />
|
||||
<None Include="Native\NativeLib.cs" />
|
||||
<None Include="Native\NativeMethods.cs" />
|
||||
<None Include="Native\NativeMethods.Unix.cs" />
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<package >
|
||||
<metadata>
|
||||
<id>ModernKeePassLib</id>
|
||||
<version>2.38.2</version>
|
||||
<version>2.39.1</version>
|
||||
<title>ModernKeePassLib</title>
|
||||
<authors>Geoffroy Bonneville</authors>
|
||||
<owners>Geoffroy Bonneville</owners>
|
||||
@@ -10,7 +10,7 @@
|
||||
<projectUrl>https://github.com/wismna/ModernKeePass</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</description>
|
||||
<releaseNotes>Deactivation of buggy custom icon implementation</releaseNotes>
|
||||
<releaseNotes>Implementation of KeePass library version 2.39.1</releaseNotes>
|
||||
<copyright>Copyright © 2018 Geoffroy Bonneville</copyright>
|
||||
<tags>KeePass KeePassLib Portable PCL NetStandard</tags>
|
||||
<dependencies>
|
||||
|
190
ModernKeePassLib/Native/ClipboardU.cs
Normal file
190
ModernKeePassLib/Native/ClipboardU.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal static class ClipboardU
|
||||
{
|
||||
private const string XSel = "xsel";
|
||||
private const string XSelV = "--version";
|
||||
private const string XSelR = "--output --clipboard";
|
||||
private const string XSelC = "--clear --clipboard";
|
||||
private const string XSelW = "--input --clipboard";
|
||||
private const string XSelND = " --nodetach";
|
||||
private const AppRunFlags XSelWF = AppRunFlags.WaitForExit;
|
||||
|
||||
private static bool? g_obXSel = null;
|
||||
|
||||
public static string GetText()
|
||||
{
|
||||
// System.Windows.Forms.Clipboard doesn't work properly,
|
||||
// see Mono workaround 1530
|
||||
|
||||
// string str = GtkGetText();
|
||||
// if(str != null) return str;
|
||||
|
||||
return XSelGetText();
|
||||
}
|
||||
|
||||
public static bool SetText(string strText, bool bMayBlock)
|
||||
{
|
||||
string str = (strText ?? string.Empty);
|
||||
|
||||
// System.Windows.Forms.Clipboard doesn't work properly,
|
||||
// see Mono workaround 1530
|
||||
|
||||
// if(GtkSetText(str)) return true;
|
||||
|
||||
return XSelSetText(str, bMayBlock);
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// LibGTK
|
||||
|
||||
// Even though GTK+ 3 appears to be loaded already, performing a
|
||||
// P/Invoke of LibGTK's gtk_init_check function terminates the
|
||||
// process (!) with the following error message:
|
||||
// "Gtk-ERROR **: GTK+ 2.x symbols detected. Using GTK+ 2.x and
|
||||
// GTK+ 3 in the same process is not supported".
|
||||
|
||||
/* private static bool GtkInit()
|
||||
{
|
||||
try
|
||||
{
|
||||
// GTK requires GLib;
|
||||
// the following throws if and only if GLib is unavailable
|
||||
NativeMethods.g_free(IntPtr.Zero);
|
||||
|
||||
if(NativeMethods.gtk_init_check(IntPtr.Zero, IntPtr.Zero) !=
|
||||
NativeMethods.G_FALSE)
|
||||
return true;
|
||||
|
||||
Debug.Assert(false);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GtkGetText()
|
||||
{
|
||||
IntPtr lpText = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
if(GtkInit())
|
||||
{
|
||||
IntPtr h = NativeMethods.gtk_clipboard_get(
|
||||
NativeMethods.GDK_SELECTION_CLIPBOARD);
|
||||
if(h != IntPtr.Zero)
|
||||
{
|
||||
lpText = NativeMethods.gtk_clipboard_wait_for_text(h);
|
||||
if(lpText != IntPtr.Zero)
|
||||
return NativeMethods.Utf8ZToString(lpText);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { NativeMethods.g_free(lpText); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool GtkSetText(string str)
|
||||
{
|
||||
IntPtr lpText = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
if(GtkInit())
|
||||
{
|
||||
lpText = NativeMethods.Utf8ZFromString(str ?? string.Empty);
|
||||
if(lpText == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
bool b = false;
|
||||
for(int i = 0; i < 2; ++i)
|
||||
{
|
||||
IntPtr h = NativeMethods.gtk_clipboard_get((i == 0) ?
|
||||
NativeMethods.GDK_SELECTION_PRIMARY :
|
||||
NativeMethods.GDK_SELECTION_CLIPBOARD);
|
||||
if(h != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.gtk_clipboard_clear(h);
|
||||
NativeMethods.gtk_clipboard_set_text(h, lpText, -1);
|
||||
NativeMethods.gtk_clipboard_store(h);
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { NativeMethods.Utf8ZFree(lpText); }
|
||||
|
||||
return false;
|
||||
} */
|
||||
|
||||
// =============================================================
|
||||
// XSel
|
||||
|
||||
private static bool XSelInit()
|
||||
{
|
||||
if(g_obXSel.HasValue) return g_obXSel.Value;
|
||||
|
||||
string strTest = NativeLib.RunConsoleApp(XSel, XSelV);
|
||||
|
||||
bool b = (strTest != null);
|
||||
g_obXSel = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
private static string XSelGetText()
|
||||
{
|
||||
if(!XSelInit()) return null;
|
||||
|
||||
return NativeLib.RunConsoleApp(XSel, XSelR);
|
||||
}
|
||||
|
||||
private static bool XSelSetText(string str, bool bMayBlock)
|
||||
{
|
||||
if(!XSelInit()) return false;
|
||||
|
||||
string strOpt = (bMayBlock ? XSelND : string.Empty);
|
||||
|
||||
// xsel with an empty input can hang, thus use --clear
|
||||
if(str.Length == 0)
|
||||
return (NativeLib.RunConsoleApp(XSel, XSelC + strOpt,
|
||||
null, XSelWF) != null);
|
||||
|
||||
// Use --nodetach to prevent clipboard corruption;
|
||||
// https://sourceforge.net/p/keepass/bugs/1603/
|
||||
return (NativeLib.RunConsoleApp(XSel, XSelW + strOpt,
|
||||
str, XSelWF) != null);
|
||||
}
|
||||
}
|
||||
}
|
@@ -53,24 +53,6 @@ namespace ModernKeePassLib.Native
|
||||
set { m_bAllowNative = value; }
|
||||
}
|
||||
|
||||
private static int? g_oiPointerSize = null;
|
||||
/// <summary>
|
||||
/// Size of a native pointer (in bytes).
|
||||
/// </summary>
|
||||
public static int PointerSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!g_oiPointerSize.HasValue)
|
||||
#if KeePassUAP
|
||||
g_oiPointerSize = Marshal.SizeOf<IntPtr>();
|
||||
#else
|
||||
g_oiPointerSize = Marshal.SizeOf(typeof(IntPtr));
|
||||
#endif
|
||||
return g_oiPointerSize.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong? m_ouMonoVersion = null;
|
||||
public static ulong MonoVersion
|
||||
{
|
||||
@@ -195,19 +177,21 @@ namespace ModernKeePassLib.Native
|
||||
t = DesktopType.Xfce;
|
||||
else if(strXdg.Equals("MATE", sc))
|
||||
t = DesktopType.Mate;
|
||||
else if(strXdg.Equals("X-Cinnamon", sc))
|
||||
else if(strXdg.Equals("X-Cinnamon", sc)) // Mint 18.3
|
||||
t = DesktopType.Cinnamon;
|
||||
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
|
||||
t = DesktopType.Pantheon;
|
||||
else if(strXdg.Equals("KDE", sc) || // Mint 16
|
||||
else if(strXdg.Equals("KDE", sc) || // Mint 16, Kubuntu 17.10
|
||||
strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04
|
||||
t = DesktopType.Kde;
|
||||
else if(strXdg.Equals("GNOME", sc))
|
||||
{
|
||||
if(strGdm.Equals("cinnamon", sc)) // Mint 13
|
||||
t = DesktopType.Cinnamon;
|
||||
else t = DesktopType.Gnome;
|
||||
else t = DesktopType.Gnome; // Fedora 27
|
||||
}
|
||||
else if(strXdg.Equals("ubuntu:GNOME", sc))
|
||||
t = DesktopType.Gnome;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
@@ -243,6 +227,7 @@ namespace ModernKeePassLib.Native
|
||||
|
||||
RunProcessDelegate fnRun = delegate()
|
||||
{
|
||||
Process pToDispose = null;
|
||||
try
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
@@ -258,6 +243,7 @@ namespace ModernKeePassLib.Native
|
||||
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
|
||||
|
||||
Process p = Process.Start(psi);
|
||||
pToDispose = p;
|
||||
|
||||
if(strStdInput != null)
|
||||
{
|
||||
@@ -274,9 +260,11 @@ namespace ModernKeePassLib.Native
|
||||
p.WaitForExit();
|
||||
else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None)
|
||||
{
|
||||
pToDispose = null; // Thread disposes it
|
||||
|
||||
Thread th = new Thread(delegate()
|
||||
{
|
||||
try { p.WaitForExit(); }
|
||||
try { p.WaitForExit(); p.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
});
|
||||
th.Start();
|
||||
@@ -289,6 +277,11 @@ namespace ModernKeePassLib.Native
|
||||
#else
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
finally
|
||||
{
|
||||
try { if(pToDispose != null) pToDispose.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@@ -31,6 +31,14 @@ namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal const int MAX_PATH = 260;
|
||||
|
||||
internal const long INVALID_HANDLE_VALUE = -1;
|
||||
|
||||
internal const uint MOVEFILE_REPLACE_EXISTING = 0x00000001;
|
||||
internal const uint MOVEFILE_COPY_ALLOWED = 0x00000002;
|
||||
|
||||
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
|
||||
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
|
||||
|
||||
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
|
||||
// internal const uint TF_SFT_HIDDEN = 0x00000008;
|
||||
|
||||
@@ -47,10 +55,9 @@ namespace ModernKeePassLib.Native
|
||||
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
|
||||
UInt64 uRounds)
|
||||
{
|
||||
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
else
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKey32(pBuf256, pKey256, uRounds);
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
}
|
||||
|
||||
[DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")]
|
||||
@@ -66,10 +73,9 @@ namespace ModernKeePassLib.Native
|
||||
internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256,
|
||||
ref UInt64 puRounds, UInt32 uSeconds)
|
||||
{
|
||||
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
|
||||
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
|
||||
else
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
|
||||
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
|
||||
} */
|
||||
|
||||
#if !KeePassUAP
|
||||
@@ -86,10 +92,9 @@ namespace ModernKeePassLib.Native
|
||||
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
|
||||
UInt64 uRounds)
|
||||
{
|
||||
if(NativeLib.PointerSize == 8)
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
else
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKey32(pBuf256, pKey256, uRounds);
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
}
|
||||
|
||||
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")]
|
||||
@@ -100,9 +105,9 @@ namespace ModernKeePassLib.Native
|
||||
|
||||
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
|
||||
{
|
||||
if(NativeLib.PointerSize == 8)
|
||||
return TransformKeyBenchmark64(uTimeMs);
|
||||
return TransformKeyBenchmark32(uTimeMs);
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKeyBenchmark32(uTimeMs);
|
||||
return TransformKeyBenchmark64(uTimeMs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -116,11 +121,64 @@ namespace ModernKeePassLib.Native
|
||||
|
||||
internal static bool TfShowLangBar(uint dwFlags)
|
||||
{
|
||||
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
|
||||
return TF_ShowLangBar64(dwFlags);
|
||||
return TF_ShowLangBar32(dwFlags);
|
||||
if(IntPtr.Size == 4) return TF_ShowLangBar32(dwFlags);
|
||||
return TF_ShowLangBar64(dwFlags);
|
||||
} */
|
||||
|
||||
[DllImport("KeePassLibC32.dll", EntryPoint = "ProtectProcessWithDacl")]
|
||||
private static extern void ProtectProcessWithDacl32();
|
||||
|
||||
[DllImport("KeePassLibC64.dll", EntryPoint = "ProtectProcessWithDacl")]
|
||||
private static extern void ProtectProcessWithDacl64();
|
||||
|
||||
internal static void ProtectProcessWithDacl()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(NativeLib.IsUnix()) return;
|
||||
|
||||
if(IntPtr.Size == 4) ProtectProcessWithDacl32();
|
||||
else ProtectProcessWithDacl64();
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetVolumeInformation(string lpRootPathName,
|
||||
StringBuilder lpVolumeNameBuffer, UInt32 nVolumeNameSize,
|
||||
ref UInt32 lpVolumeSerialNumber, ref UInt32 lpMaximumComponentLength,
|
||||
ref UInt32 lpFileSystemFlags, StringBuilder lpFileSystemNameBuffer,
|
||||
UInt32 nFileSystemNameSize);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool MoveFileEx(string lpExistingFileName,
|
||||
string lpNewFileName, UInt32 dwFlags);
|
||||
|
||||
[DllImport("KtmW32.dll", CharSet = CharSet.Unicode, ExactSpelling = true,
|
||||
SetLastError = true)]
|
||||
internal static extern IntPtr CreateTransaction(IntPtr lpTransactionAttributes,
|
||||
IntPtr lpUOW, UInt32 dwCreateOptions, UInt32 dwIsolationLevel,
|
||||
UInt32 dwIsolationFlags, UInt32 dwTimeout, string lpDescription);
|
||||
|
||||
[DllImport("KtmW32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CommitTransaction(IntPtr hTransaction);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool MoveFileTransacted(string lpExistingFileName,
|
||||
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
|
||||
UInt32 dwFlags, IntPtr hTransaction);
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("wismna")]
|
||||
[assembly: AssemblyProduct("ModernKeePassLib")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017 Geoffroy Bonneville")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018 Geoffroy Bonneville")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -40,5 +40,5 @@ using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
// Assembly version information
|
||||
[assembly: AssemblyVersion("2.37.0.2000")]
|
||||
[assembly: AssemblyFileVersion("2.37.0.2000")]
|
||||
[assembly: AssemblyVersion("2.39.1.*")]
|
||||
[assembly: AssemblyFileVersion("2.39.1.0")]
|
||||
|
@@ -607,9 +607,8 @@ namespace ModernKeePassLib
|
||||
m_bDatabaseOpened = true;
|
||||
m_bModified = true;
|
||||
|
||||
m_pgRootGroup = new PwGroup(true, true,
|
||||
UrlUtil.StripExtension(UrlUtil.GetFileName(ioConnection.Path)),
|
||||
PwIcon.FolderOpen);
|
||||
m_pgRootGroup = new PwGroup(true, true, UrlUtil.StripExtension(
|
||||
UrlUtil.GetFileName(ioConnection.Path)), PwIcon.FolderOpen);
|
||||
m_pgRootGroup.IsExpanded = true;
|
||||
}
|
||||
|
||||
@@ -636,15 +635,15 @@ namespace ModernKeePassLib
|
||||
m_pgRootGroup.IsExpanded = true;
|
||||
|
||||
m_pwUserKey = pwKey;
|
||||
|
||||
m_bModified = false;
|
||||
|
||||
KdbxFile kdbx = new KdbxFile(this);
|
||||
kdbx.DetachBinaries = m_strDetachBins;
|
||||
|
||||
Stream s = IOConnection.OpenRead(ioSource);
|
||||
kdbx.Load(s, KdbxFormat.Default, slLogger);
|
||||
s.Dispose();
|
||||
using(Stream s = IOConnection.OpenRead(ioSource))
|
||||
{
|
||||
kdbx.Load(s, KdbxFormat.Default, slLogger);
|
||||
}
|
||||
|
||||
m_pbHashOfLastIO = kdbx.HashOfFileOnDisk;
|
||||
m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk;
|
||||
@@ -673,17 +672,21 @@ namespace ModernKeePassLib
|
||||
if(m_bUseFileLocks) fl = new FileLock(m_ioSource);
|
||||
try
|
||||
{
|
||||
FileTransactionEx ft = new FileTransactionEx(m_ioSource,
|
||||
m_bUseFileTransactions);
|
||||
Stream s = ft.OpenWrite();
|
||||
KdbxFile kdbx = new KdbxFile(this);
|
||||
|
||||
KdbxFile kdb = new KdbxFile(this);
|
||||
kdb.Save(s, null, KdbxFormat.Default, slLogger);
|
||||
using(FileTransactionEx ft = new FileTransactionEx(m_ioSource,
|
||||
m_bUseFileTransactions))
|
||||
{
|
||||
using(Stream s = ft.OpenWrite())
|
||||
{
|
||||
kdbx.Save(s, null, KdbxFormat.Default, slLogger);
|
||||
}
|
||||
|
||||
ft.CommitWrite();
|
||||
ft.CommitWrite();
|
||||
}
|
||||
|
||||
m_pbHashOfLastIO = kdb.HashOfFileOnDisk;
|
||||
m_pbHashOfFileOnDisk = kdb.HashOfFileOnDisk;
|
||||
m_pbHashOfLastIO = kdbx.HashOfFileOnDisk;
|
||||
m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk;
|
||||
Debug.Assert(m_pbHashOfFileOnDisk != null);
|
||||
}
|
||||
finally { if(fl != null) fl.Dispose(); }
|
||||
@@ -1856,10 +1859,7 @@ namespace ModernKeePassLib
|
||||
using(Stream sOut = IOConnection.OpenWrite(iocBk))
|
||||
{
|
||||
MemUtil.CopyStream(sIn, sOut);
|
||||
sOut.Close();
|
||||
}
|
||||
|
||||
sIn.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1969,7 +1969,7 @@ namespace ModernKeePassLib
|
||||
if(psB == null) return false;
|
||||
|
||||
// Ignore protection setting, compare values only
|
||||
if(!kvpA.Value.ReadString().Equals(psB.ReadString())) return false;
|
||||
if(!psB.Equals(kvpA.Value, false)) return false;
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedString> kvpB in b.Strings)
|
||||
@@ -1980,22 +1980,18 @@ namespace ModernKeePassLib
|
||||
if(psA == null) return false;
|
||||
|
||||
// Must be equal by logic
|
||||
Debug.Assert(kvpB.Value.ReadString().Equals(psA.ReadString()));
|
||||
Debug.Assert(psA.Equals(kvpB.Value, false));
|
||||
}
|
||||
|
||||
if(a.Binaries.UCount != b.Binaries.UCount) return false;
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in a.Binaries)
|
||||
{
|
||||
ProtectedBinary pbA = kvpBin.Value;
|
||||
ProtectedBinary pbB = b.Binaries.Get(kvpBin.Key);
|
||||
if(pbB == null) return false;
|
||||
|
||||
// Ignore protection setting, compare values only
|
||||
byte[] pbDataA = kvpBin.Value.ReadData();
|
||||
byte[] pbDataB = pbB.ReadData();
|
||||
bool bBinEq = MemUtil.ArraysEqual(pbDataA, pbDataB);
|
||||
MemUtil.ZeroByteArray(pbDataA);
|
||||
MemUtil.ZeroByteArray(pbDataB);
|
||||
if(!bBinEq) return false;
|
||||
if(!pbB.Equals(pbA, false)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -55,18 +55,18 @@ namespace ModernKeePassLib
|
||||
/// e.g. 2.19 = 0x02130000.
|
||||
/// It is highly recommended to use <c>FileVersion64</c> instead.
|
||||
/// </summary>
|
||||
public const uint Version32 = 0x02260000;
|
||||
public const uint Version32 = 0x02270100;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as 64-bit unsigned integer
|
||||
/// (component-wise, 16 bits per component).
|
||||
/// </summary>
|
||||
public const ulong FileVersion64 = 0x0002002600000000UL;
|
||||
public const ulong FileVersion64 = 0x0002002700010000UL;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as string.
|
||||
/// </summary>
|
||||
public const string VersionString = "2.38";
|
||||
public const string VersionString = "2.39.1";
|
||||
|
||||
public const string Copyright = @"Copyright © 2003-2018 Dominik Reichl";
|
||||
|
||||
@@ -219,6 +219,15 @@ namespace ModernKeePassLib
|
||||
|
||||
return (pe.Strings.ReadSafe(PwDefs.TitleField) == TanTitle);
|
||||
}
|
||||
|
||||
internal static string GetTranslationDisplayVersion(string strFileVersion)
|
||||
{
|
||||
if(strFileVersion == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
if(strFileVersion == "2.39") return "2.39 / 2.39.1";
|
||||
|
||||
return strFileVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// #pragma warning disable 1591 // Missing XML comments warning
|
||||
|
@@ -782,45 +782,49 @@ namespace ModernKeePassLib
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approximate the total size of this entry in bytes (including
|
||||
/// strings, binaries and history entries).
|
||||
/// Approximate the total size (in process memory) of this entry
|
||||
/// in bytes (including strings, binaries and history entries).
|
||||
/// </summary>
|
||||
/// <returns>Size in bytes.</returns>
|
||||
public ulong GetSize()
|
||||
{
|
||||
ulong uSize = 128; // Approx fixed length data
|
||||
// This method assumes 64-bit pointers/references and Unicode
|
||||
// strings (i.e. 2 bytes per character)
|
||||
|
||||
ulong cb = 248; // Number of bytes; approx. fixed length data
|
||||
ulong cc = 0; // Number of characters
|
||||
|
||||
cb += (ulong)m_listStrings.UCount * 40;
|
||||
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
|
||||
{
|
||||
uSize += (ulong)kvpStr.Key.Length;
|
||||
uSize += (ulong)kvpStr.Value.Length;
|
||||
}
|
||||
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
|
||||
|
||||
cb += (ulong)m_listBinaries.UCount * 65;
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
|
||||
{
|
||||
uSize += (ulong)kvpBin.Key.Length;
|
||||
uSize += kvpBin.Value.Length;
|
||||
cc += (ulong)kvpBin.Key.Length;
|
||||
cb += (ulong)kvpBin.Value.Length;
|
||||
}
|
||||
|
||||
uSize += (ulong)m_listAutoType.DefaultSequence.Length;
|
||||
cc += (ulong)m_listAutoType.DefaultSequence.Length;
|
||||
cb += (ulong)m_listAutoType.AssociationsCount * 24;
|
||||
foreach(AutoTypeAssociation a in m_listAutoType.Associations)
|
||||
{
|
||||
uSize += (ulong)a.WindowName.Length;
|
||||
uSize += (ulong)a.Sequence.Length;
|
||||
}
|
||||
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
|
||||
|
||||
cb += (ulong)m_listHistory.UCount * 8;
|
||||
foreach(PwEntry peHistory in m_listHistory)
|
||||
uSize += peHistory.GetSize();
|
||||
cb += peHistory.GetSize();
|
||||
|
||||
uSize += (ulong)m_strOverrideUrl.Length;
|
||||
cc += (ulong)m_strOverrideUrl.Length;
|
||||
|
||||
cb += (ulong)m_vTags.Count * 8;
|
||||
foreach(string strTag in m_vTags)
|
||||
uSize += (ulong)strTag.Length;
|
||||
cc += (ulong)strTag.Length;
|
||||
|
||||
cb += (ulong)m_dCustomData.Count * 16;
|
||||
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
|
||||
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
|
||||
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
|
||||
|
||||
return uSize;
|
||||
return (cb + (cc << 1));
|
||||
}
|
||||
|
||||
public bool HasTag(string strTag)
|
||||
|
@@ -44,9 +44,8 @@ namespace ModernKeePassLib.Security
|
||||
long lID);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a protected binary, i.e. a byte array that is encrypted
|
||||
/// in memory. A <c>ProtectedBinary</c> object is immutable and
|
||||
/// thread-safe.
|
||||
/// A protected binary, i.e. a byte array that is encrypted in memory.
|
||||
/// A <c>ProtectedBinary</c> object is immutable and thread-safe.
|
||||
/// </summary>
|
||||
public sealed class ProtectedBinary : IEquatable<ProtectedBinary>
|
||||
{
|
||||
@@ -71,7 +70,7 @@ namespace ModernKeePassLib.Security
|
||||
private enum PbMemProt
|
||||
{
|
||||
None = 0,
|
||||
ProtectedMemory,
|
||||
ProtectedMemory, // DPAPI on Windows
|
||||
ChaCha20,
|
||||
ExtCrypt
|
||||
}
|
||||
@@ -90,7 +89,8 @@ namespace ModernKeePassLib.Security
|
||||
bool? ob = g_obProtectedMemorySupported;
|
||||
if(ob.HasValue) return ob.Value;
|
||||
|
||||
// Mono does not implement any encryption for ProtectedMemory;
|
||||
// Mono does not implement any encryption for ProtectedMemory
|
||||
// on Linux (Mono uses DPAPI on Windows);
|
||||
// https://sourceforge.net/p/keepass/feature-requests/1907/
|
||||
if(NativeLib.IsUnix())
|
||||
{
|
||||
@@ -177,7 +177,7 @@ namespace ModernKeePassLib.Security
|
||||
/// i.e. the caller is responsible for clearing it.</param>
|
||||
public ProtectedBinary(bool bEnableProtection, byte[] pbData)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData");
|
||||
if(pbData == null) throw new ArgumentNullException("pbData"); // For .Length
|
||||
|
||||
Init(bEnableProtection, pbData, 0, pbData.Length);
|
||||
}
|
||||
@@ -213,9 +213,8 @@ namespace ModernKeePassLib.Security
|
||||
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
|
||||
|
||||
byte[] pb = xbProtected.ReadPlainText();
|
||||
Init(bEnableProtection, pb, 0, pb.Length);
|
||||
|
||||
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
|
||||
try { Init(bEnableProtection, pb, 0, pb.Length); }
|
||||
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
|
||||
private void Init(bool bEnableProtection, byte[] pbData, int iOffset,
|
||||
@@ -374,7 +373,7 @@ namespace ModernKeePassLib.Security
|
||||
for(int i = 0; i < pb.Length; ++i)
|
||||
h = (h << 3) + h + (int)pb[i];
|
||||
}
|
||||
MemUtil.ZeroByteArray(pb);
|
||||
if(m_bProtected) MemUtil.ZeroByteArray(pb);
|
||||
|
||||
m_hash = h;
|
||||
return h;
|
||||
@@ -382,25 +381,36 @@ namespace ModernKeePassLib.Security
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ProtectedBinary);
|
||||
return this.Equals(obj as ProtectedBinary, true);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedBinary other)
|
||||
{
|
||||
if(other == null) return false; // No assert
|
||||
return this.Equals(other, true);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedBinary other, bool bCheckProtEqual)
|
||||
{
|
||||
if(other == null) return false; // No assert
|
||||
if(object.ReferenceEquals(this, other)) return true; // Perf. opt.
|
||||
|
||||
if(bCheckProtEqual && (m_bProtected != other.m_bProtected))
|
||||
return false;
|
||||
|
||||
if(m_bProtected != other.m_bProtected) return false;
|
||||
if(m_uDataLen != other.m_uDataLen) return false;
|
||||
|
||||
byte[] pbL = ReadData();
|
||||
byte[] pbR = other.ReadData();
|
||||
bool bEq = MemUtil.ArraysEqual(pbL, pbR);
|
||||
MemUtil.ZeroByteArray(pbL);
|
||||
MemUtil.ZeroByteArray(pbR);
|
||||
|
||||
#if DEBUG
|
||||
if(bEq) { Debug.Assert(GetHashCode() == other.GetHashCode()); }
|
||||
#endif
|
||||
byte[] pbL = ReadData(), pbR = null;
|
||||
bool bEq;
|
||||
try
|
||||
{
|
||||
pbR = other.ReadData();
|
||||
bEq = MemUtil.ArraysEqual(pbL, pbR);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(m_bProtected) MemUtil.ZeroByteArray(pbL);
|
||||
if(other.m_bProtected && (pbR != null)) MemUtil.ZeroByteArray(pbR);
|
||||
}
|
||||
|
||||
return bEq;
|
||||
}
|
||||
|
@@ -33,11 +33,11 @@ using KeePassLibSD;
|
||||
namespace ModernKeePassLib.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an in-memory encrypted string.
|
||||
/// A string that is protected in process memory.
|
||||
/// <c>ProtectedString</c> objects are immutable and thread-safe.
|
||||
/// </summary>
|
||||
#if (DEBUG && !KeePassLibSD)
|
||||
[DebuggerDisplay(@"{ReadString()}")]
|
||||
[DebuggerDisplay("{ReadString()}")]
|
||||
#endif
|
||||
public sealed class ProtectedString
|
||||
{
|
||||
@@ -48,11 +48,24 @@ namespace ModernKeePassLib.Security
|
||||
private bool m_bIsProtected;
|
||||
|
||||
private static readonly ProtectedString m_psEmpty = new ProtectedString();
|
||||
/// <summary>
|
||||
/// Get an empty <c>ProtectedString</c> object, without protection.
|
||||
/// </summary>
|
||||
public static ProtectedString Empty
|
||||
{
|
||||
get { return m_psEmpty; }
|
||||
}
|
||||
|
||||
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
|
||||
true, new byte[0]);
|
||||
/// <summary>
|
||||
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
|
||||
/// </summary>
|
||||
public static ProtectedString EmptyEx
|
||||
{
|
||||
get { return m_psEmptyEx; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flag specifying whether the <c>ProtectedString</c> object
|
||||
/// has turned on memory protection or not.
|
||||
@@ -66,8 +79,8 @@ namespace ModernKeePassLib.Security
|
||||
{
|
||||
get
|
||||
{
|
||||
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
|
||||
if(pBin != null) return (pBin.Length == 0);
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null) return (p.Length == 0);
|
||||
|
||||
Debug.Assert(m_strPlainText != null);
|
||||
return (m_strPlainText.Length == 0);
|
||||
@@ -75,18 +88,21 @@ namespace ModernKeePassLib.Security
|
||||
}
|
||||
|
||||
private int m_nCachedLength = -1;
|
||||
/// <summary>
|
||||
/// Length of the protected string, in characters.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_nCachedLength >= 0) return m_nCachedLength;
|
||||
|
||||
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
|
||||
if(pBin != null)
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null)
|
||||
{
|
||||
byte[] pbPlain = pBin.ReadData();
|
||||
m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain);
|
||||
MemUtil.ZeroByteArray(pbPlain);
|
||||
byte[] pbPlain = p.ReadData();
|
||||
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
|
||||
finally { MemUtil.ZeroByteArray(pbPlain); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -149,9 +165,8 @@ namespace ModernKeePassLib.Security
|
||||
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
|
||||
|
||||
byte[] pb = xbProtected.ReadPlainText();
|
||||
Init(bEnableProtection, pb);
|
||||
|
||||
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
|
||||
try { Init(bEnableProtection, pb); }
|
||||
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
|
||||
private void Init(bool bEnableProtection, string str)
|
||||
@@ -160,7 +175,7 @@ namespace ModernKeePassLib.Security
|
||||
|
||||
m_bIsProtected = bEnableProtection;
|
||||
|
||||
// The string already is in memory and immutable,
|
||||
// As the string already is in memory and immutable,
|
||||
// protection would be useless
|
||||
m_strPlainText = str;
|
||||
}
|
||||
@@ -178,8 +193,8 @@ namespace ModernKeePassLib.Security
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the protected string to a normal string object.
|
||||
/// Be careful with this function, the returned string object
|
||||
/// Convert the protected string to a standard string object.
|
||||
/// Be careful with this function, as the returned string object
|
||||
/// isn't protected anymore and stored in plain-text in the
|
||||
/// process memory.
|
||||
/// </summary>
|
||||
@@ -194,23 +209,42 @@ namespace ModernKeePassLib.Security
|
||||
// No need to clear pb
|
||||
|
||||
// As the text is now visible in process memory anyway,
|
||||
// there's no need to protect it anymore
|
||||
// there's no need to protect it anymore (strings are
|
||||
// immutable and thus cannot be overwritten)
|
||||
m_strPlainText = str;
|
||||
m_pbUtf8 = null; // Thread-safe order
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read out the string and return it as a char array.
|
||||
/// The returned array is not protected and should be cleared by
|
||||
/// the caller.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text char array.</returns>
|
||||
public char[] ReadChars()
|
||||
{
|
||||
if(m_strPlainText != null) return m_strPlainText.ToCharArray();
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
char[] v;
|
||||
try { v = StrUtil.Utf8.GetChars(pb); }
|
||||
finally { MemUtil.ZeroByteArray(pb); }
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read out the string and return a byte array that contains the
|
||||
/// string encoded using UTF-8. The returned string is not protected
|
||||
/// anymore!
|
||||
/// string encoded using UTF-8.
|
||||
/// The returned array is not protected and should be cleared by
|
||||
/// the caller.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text UTF-8 byte array.</returns>
|
||||
public byte[] ReadUtf8()
|
||||
{
|
||||
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
|
||||
if(pBin != null) return pBin.ReadData();
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null) return p.ReadData();
|
||||
|
||||
return StrUtil.Utf8.GetBytes(m_strPlainText);
|
||||
}
|
||||
@@ -223,7 +257,7 @@ namespace ModernKeePassLib.Security
|
||||
/// <returns>Protected string.</returns>
|
||||
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
|
||||
if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
|
||||
|
||||
byte[] pbData = ReadUtf8();
|
||||
uint uLen = (uint)pbData.Length;
|
||||
@@ -242,10 +276,34 @@ namespace ModernKeePassLib.Security
|
||||
if(bProtect == m_bIsProtected) return this;
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
ProtectedString ps = new ProtectedString(bProtect, pb);
|
||||
|
||||
if(bProtect) MemUtil.ZeroByteArray(pb);
|
||||
return ps;
|
||||
// No need to clear pb; either the current or the new object is unprotected
|
||||
return new ProtectedString(bProtect, pb);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
|
||||
{
|
||||
if(ps == null) throw new ArgumentNullException("ps");
|
||||
if(object.ReferenceEquals(this, ps)) return true; // Perf. opt.
|
||||
|
||||
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
|
||||
if(bCheckProtEqual && (bPA != bPB)) return false;
|
||||
if(!bPA && !bPB) return (ReadString() == ps.ReadString());
|
||||
|
||||
byte[] pbA = ReadUtf8(), pbB = null;
|
||||
bool bEq;
|
||||
try
|
||||
{
|
||||
pbB = ps.ReadUtf8();
|
||||
bEq = MemUtil.ArraysEqual(pbA, pbB);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(bPA) MemUtil.ZeroByteArray(pbA);
|
||||
if(bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
|
||||
}
|
||||
|
||||
return bEq;
|
||||
}
|
||||
|
||||
public ProtectedString Insert(int iStart, string strInsert)
|
||||
@@ -254,18 +312,14 @@ namespace ModernKeePassLib.Security
|
||||
if(strInsert == null) throw new ArgumentNullException("strInsert");
|
||||
if(strInsert.Length == 0) return this;
|
||||
|
||||
// Only operate directly with strings when m_bIsProtected is
|
||||
// false, not in the case of non-null m_strPlainText, because
|
||||
// the operation creates a new sequence in memory
|
||||
if(!m_bIsProtected)
|
||||
return new ProtectedString(false, ReadString().Insert(
|
||||
iStart, strInsert));
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
char[] v = utf8.GetChars(pb);
|
||||
char[] vNew;
|
||||
char[] v = ReadChars(), vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -279,21 +333,20 @@ namespace ModernKeePassLib.Security
|
||||
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
|
||||
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
|
||||
v.Length - iStart);
|
||||
|
||||
pbNew = utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Insert(iStart, strInsert));
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
MemUtil.ZeroByteArray(pb);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
byte[] pbNew = utf8.GetBytes(vNew);
|
||||
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Insert(iStart, strInsert));
|
||||
|
||||
MemUtil.ZeroArray<char>(vNew);
|
||||
MemUtil.ZeroByteArray(pbNew);
|
||||
return ps;
|
||||
}
|
||||
|
||||
@@ -303,44 +356,81 @@ namespace ModernKeePassLib.Security
|
||||
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
|
||||
if(nCount == 0) return this;
|
||||
|
||||
// Only operate directly with strings when m_bIsProtected is
|
||||
// false, not in the case of non-null m_strPlainText, because
|
||||
// the operation creates a new sequence in memory
|
||||
if(!m_bIsProtected)
|
||||
return new ProtectedString(false, ReadString().Remove(
|
||||
iStart, nCount));
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
char[] v = utf8.GetChars(pb);
|
||||
char[] vNew;
|
||||
char[] v = ReadChars(), vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
if((iStart + nCount) > v.Length)
|
||||
throw new ArgumentException("iStart + nCount");
|
||||
throw new ArgumentException("(iStart + nCount) > v.Length");
|
||||
|
||||
vNew = new char[v.Length - nCount];
|
||||
Array.Copy(v, 0, vNew, 0, iStart);
|
||||
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
|
||||
(iStart + nCount));
|
||||
|
||||
pbNew = utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Remove(iStart, nCount));
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
MemUtil.ZeroByteArray(pb);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
byte[] pbNew = utf8.GetBytes(vNew);
|
||||
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Remove(iStart, nCount));
|
||||
|
||||
MemUtil.ZeroArray<char>(vNew);
|
||||
MemUtil.ZeroByteArray(pbNew);
|
||||
return ps;
|
||||
}
|
||||
|
||||
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
|
||||
{
|
||||
if(a == null) throw new ArgumentNullException("a");
|
||||
if(b == null) throw new ArgumentNullException("b");
|
||||
|
||||
if(b.IsEmpty) return a;
|
||||
if(a.IsEmpty) return b;
|
||||
if(!a.IsProtected && !b.IsProtected)
|
||||
return new ProtectedString(false, a.ReadString() + b.ReadString());
|
||||
|
||||
char[] vA = a.ReadChars(), vB = null, vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
vB = b.ReadChars();
|
||||
|
||||
vNew = new char[vA.Length + vB.Length];
|
||||
Array.Copy(vA, vNew, vA.Length);
|
||||
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
|
||||
|
||||
pbNew = StrUtil.Utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(vA);
|
||||
if(vB != null) MemUtil.ZeroArray<char>(vB);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public static ProtectedString operator +(ProtectedString a, string b)
|
||||
{
|
||||
ProtectedString psB = new ProtectedString(false, b);
|
||||
return (a + psB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -129,9 +129,12 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
s = IOConnection.OpenRead(iocLockFile);
|
||||
if(s == null) return null;
|
||||
StreamReader sr = new StreamReader(s, StrUtil.Utf8);
|
||||
string str = sr.ReadToEnd();
|
||||
sr.Dispose();
|
||||
|
||||
string str = null;
|
||||
using(StreamReader sr = new StreamReader(s, StrUtil.Utf8))
|
||||
{
|
||||
str = sr.ReadToEnd();
|
||||
}
|
||||
if(str == null) { Debug.Assert(false); return null; }
|
||||
|
||||
str = StrUtil.NormalizeNewLines(str, false);
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@@ -32,19 +33,25 @@ using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Resources;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class FileTransactionEx
|
||||
public sealed class FileTransactionEx : IDisposable
|
||||
{
|
||||
private bool m_bTransacted;
|
||||
private IOConnectionInfo m_iocBase;
|
||||
private IOConnectionInfo m_iocBase; // Null means disposed
|
||||
private IOConnectionInfo m_iocTemp;
|
||||
private IOConnectionInfo m_iocTxfMidFallback = null; // Null <=> TxF not used
|
||||
|
||||
private bool m_bMadeUnhidden = false;
|
||||
|
||||
private List<IOConnectionInfo> m_lToDelete = new List<IOConnectionInfo>();
|
||||
|
||||
private const string StrTempSuffix = ".tmp";
|
||||
private const string StrTxfTempPrefix = PwDefs.ShortProductName + "_TxF_";
|
||||
private const string StrTxfTempSuffix = ".tmp";
|
||||
|
||||
private static Dictionary<string, bool> g_dEnabled =
|
||||
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
|
||||
@@ -56,26 +63,23 @@ namespace ModernKeePassLib.Serialization
|
||||
set { g_bExtraSafe = value; }
|
||||
}
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile)
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile) :
|
||||
this(iocBaseFile, true)
|
||||
{
|
||||
Initialize(iocBaseFile, true);
|
||||
}
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted)
|
||||
{
|
||||
Initialize(iocBaseFile, bTransacted);
|
||||
}
|
||||
|
||||
private void Initialize(IOConnectionInfo iocBaseFile, bool bTransacted)
|
||||
{
|
||||
if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile");
|
||||
|
||||
m_bTransacted = bTransacted;
|
||||
|
||||
m_iocBase = iocBaseFile.CloneDeep();
|
||||
if(m_iocBase.IsLocalFile())
|
||||
m_iocBase.Path = UrlUtil.GetShortestAbsolutePath(m_iocBase.Path);
|
||||
|
||||
string strPath = m_iocBase.Path;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
if(m_iocBase.IsLocalFile())
|
||||
{
|
||||
try
|
||||
@@ -93,10 +97,23 @@ namespace ModernKeePassLib.Serialization
|
||||
if((long)(fa & FileAttributes.ReparsePoint) != 0)
|
||||
m_bTransacted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the base and the temporary file are in different
|
||||
// folders and the base file doesn't exist (i.e. we can't
|
||||
// backup the ACL), a transaction would cause the new file
|
||||
// to have the default ACL of the temporary folder instead
|
||||
// of the one of the base folder; therefore, we don't use
|
||||
// a transaction when the base file doesn't exist (this
|
||||
// also results in other applications monitoring the folder
|
||||
// to see one file creation only)
|
||||
m_bTransacted = false;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
#if !ModernKeePassLib
|
||||
// Prevent transactions for FTP URLs under .NET 4.0 in order to
|
||||
// avoid/workaround .NET bug 621450:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
|
||||
@@ -118,90 +135,152 @@ namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
m_iocTemp = m_iocBase.CloneDeep();
|
||||
m_iocTemp.Path += StrTempSuffix;
|
||||
|
||||
TxfPrepare(); // Adjusts m_iocTemp
|
||||
}
|
||||
else m_iocTemp = m_iocBase;
|
||||
}
|
||||
|
||||
~FileTransactionEx()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
{
|
||||
m_iocBase = null;
|
||||
if(!bDisposing) return;
|
||||
|
||||
try
|
||||
{
|
||||
foreach(IOConnectionInfo ioc in m_lToDelete)
|
||||
{
|
||||
if(IOConnection.FileExists(ioc, false))
|
||||
IOConnection.DeleteFile(ioc);
|
||||
}
|
||||
|
||||
m_lToDelete.Clear();
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
public Stream OpenWrite()
|
||||
{
|
||||
if(!m_bTransacted) m_bMadeUnhidden = UrlUtil.UnhideFile(m_iocTemp.Path);
|
||||
else // m_bTransacted
|
||||
{
|
||||
try { IOConnection.DeleteFile(m_iocTemp); }
|
||||
catch(Exception) { }
|
||||
}
|
||||
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
|
||||
|
||||
if(!m_bTransacted) m_bMadeUnhidden |= UrlUtil.UnhideFile(m_iocTemp.Path);
|
||||
|
||||
return IOConnection.OpenWrite(m_iocTemp);
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
if(m_bTransacted) CommitWriteTransaction();
|
||||
else // !m_bTransacted
|
||||
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
|
||||
|
||||
if(!m_bTransacted)
|
||||
{
|
||||
if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true); // Hide again
|
||||
if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true);
|
||||
}
|
||||
else CommitWriteTransaction();
|
||||
|
||||
m_iocBase = null; // Dispose
|
||||
}
|
||||
|
||||
private void CommitWriteTransaction()
|
||||
{
|
||||
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD)
|
||||
FileSecurity bkSecurity = null;
|
||||
bool bEfsEncrypted = false;
|
||||
#endif
|
||||
|
||||
if(g_bExtraSafe)
|
||||
{
|
||||
if(!IOConnection.FileExists(m_iocTemp))
|
||||
throw new FileNotFoundException(m_iocTemp.Path +
|
||||
Environment.NewLine + KLRes.FileSaveFailed);
|
||||
MessageService.NewLine + KLRes.FileSaveFailed);
|
||||
}
|
||||
|
||||
if(IOConnection.FileExists(m_iocBase))
|
||||
{
|
||||
#if (!ModernKeePassLib && !KeePassLibSD)
|
||||
if(m_iocBase.IsLocalFile())
|
||||
{
|
||||
try
|
||||
{
|
||||
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
|
||||
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
|
||||
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
|
||||
|
||||
DateTime tCreation = File.GetCreationTimeUtc(m_iocBase.Path);
|
||||
bkSecurity = File.GetAccessControl(m_iocBase.Path);
|
||||
#if !ModernKeePassLib
|
||||
// 'All' includes 'Audit' (SACL), which requires SeSecurityPrivilege,
|
||||
// which we usually don't have and therefore get an exception;
|
||||
// trying to set 'Owner' or 'Group' can result in an
|
||||
// UnauthorizedAccessException; thus we restore 'Access' (DACL) only
|
||||
const AccessControlSections acs = AccessControlSections.Access;
|
||||
|
||||
File.SetCreationTimeUtc(m_iocTemp.Path, tCreation);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
bool bEfsEncrypted = false;
|
||||
byte[] pbSec = null;
|
||||
#endif
|
||||
DateTime? otCreation = null;
|
||||
|
||||
IOConnection.DeleteFile(m_iocBase);
|
||||
}
|
||||
|
||||
IOConnection.RenameFile(m_iocTemp, m_iocBase);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD)
|
||||
if(m_iocBase.IsLocalFile())
|
||||
bool bBaseExists = IOConnection.FileExists(m_iocBase);
|
||||
if(bBaseExists && m_iocBase.IsLocalFile())
|
||||
{
|
||||
// FileAttributes faBase = FileAttributes.Normal;
|
||||
try
|
||||
{
|
||||
if(bEfsEncrypted)
|
||||
{
|
||||
try { File.Encrypt(m_iocBase.Path); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
if(bkSecurity != null)
|
||||
File.SetAccessControl(m_iocBase.Path, bkSecurity);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#if !ModernKeePassLib
|
||||
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
|
||||
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
|
||||
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
otCreation = File.GetCreationTimeUtc(m_iocBase.Path);
|
||||
#if !ModernKeePassLib
|
||||
// May throw with Mono
|
||||
FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs);
|
||||
if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm();
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
|
||||
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again
|
||||
// if((long)(faBase & FileAttributes.ReadOnly) != 0)
|
||||
// throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if(!TxfMove())
|
||||
{
|
||||
if(bBaseExists) IOConnection.DeleteFile(m_iocBase);
|
||||
IOConnection.RenameFile(m_iocTemp, m_iocBase);
|
||||
}
|
||||
else { Debug.Assert(pbSec != null); } // TxF success => NTFS => has ACL
|
||||
|
||||
try
|
||||
{
|
||||
// If File.GetCreationTimeUtc fails, it may return a
|
||||
// date with year 1601, and Unix times start in 1970,
|
||||
// so testing for 1971 should ensure validity;
|
||||
// https://msdn.microsoft.com/en-us/library/system.io.file.getcreationtimeutc.aspx
|
||||
if(otCreation.HasValue && (otCreation.Value.Year >= 1971))
|
||||
File.SetCreationTimeUtc(m_iocBase.Path, otCreation.Value);
|
||||
|
||||
#if !ModernKeePassLib
|
||||
if(bEfsEncrypted)
|
||||
{
|
||||
try { File.Encrypt(m_iocBase.Path); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
// File.SetAccessControl(m_iocBase.Path, secPrev);
|
||||
// Directly calling File.SetAccessControl with the previous
|
||||
// FileSecurity object does not work; the binary form
|
||||
// indirection is required;
|
||||
// https://sourceforge.net/p/keepass/bugs/1738/
|
||||
// https://msdn.microsoft.com/en-us/library/system.io.file.setaccesscontrol.aspx
|
||||
if((pbSec != null) && (pbSec.Length != 0))
|
||||
{
|
||||
FileSecurity sec = new FileSecurity();
|
||||
sec.SetSecurityDescriptorBinaryForm(pbSec, acs);
|
||||
|
||||
File.SetAccessControl(m_iocBase.Path, sec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true);
|
||||
}
|
||||
|
||||
// For plugins
|
||||
@@ -213,5 +292,155 @@ namespace ModernKeePassLib.Serialization
|
||||
g_dEnabled[strPrefix] = obTransacted.Value;
|
||||
else g_dEnabled.Remove(strPrefix);
|
||||
}
|
||||
|
||||
private static bool TxfIsSupported(char chDriveLetter)
|
||||
{
|
||||
if(chDriveLetter == '\0') return false;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
try
|
||||
{
|
||||
string strRoot = (new string(chDriveLetter, 1)) + ":\\";
|
||||
|
||||
const int cch = NativeMethods.MAX_PATH + 1;
|
||||
StringBuilder sbName = new StringBuilder(cch + 1);
|
||||
uint uSerial = 0, cchMaxComp = 0, uFlags = 0;
|
||||
StringBuilder sbFileSystem = new StringBuilder(cch + 1);
|
||||
|
||||
if(!NativeMethods.GetVolumeInformation(strRoot, sbName, (uint)cch,
|
||||
ref uSerial, ref cchMaxComp, ref uFlags, sbFileSystem, (uint)cch))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((uFlags & NativeMethods.FILE_SUPPORTS_TRANSACTIONS) != 0);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
private void TxfPrepare()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(NativeLib.IsUnix()) return;
|
||||
if(!m_iocBase.IsLocalFile()) return;
|
||||
|
||||
string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String(
|
||||
CryptoRandom.Instance.GetRandomBytes(16)));
|
||||
string strTempDir = UrlUtil.GetTempPath();
|
||||
// See also ClearOld method
|
||||
string strTemp = UrlUtil.EnsureTerminatingSeparator(strTempDir,
|
||||
false) + StrTxfTempPrefix + strID + StrTxfTempSuffix;
|
||||
|
||||
char chB = UrlUtil.GetDriveLetter(m_iocBase.Path);
|
||||
char chT = UrlUtil.GetDriveLetter(strTemp);
|
||||
if(!TxfIsSupported(chB)) return;
|
||||
if((chT != chB) && !TxfIsSupported(chT)) return;
|
||||
|
||||
m_iocTxfMidFallback = m_iocTemp;
|
||||
m_iocTemp = IOConnectionInfo.FromPath(strTemp);
|
||||
|
||||
m_lToDelete.Add(m_iocTemp);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); m_iocTxfMidFallback = null; }
|
||||
}
|
||||
|
||||
#if !ModernKeePassLib
|
||||
private bool TxfMove()
|
||||
{
|
||||
if(m_iocTxfMidFallback == null) return false;
|
||||
|
||||
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
|
||||
const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED |
|
||||
NativeMethods.MOVEFILE_REPLACE_EXISTING);
|
||||
bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f);
|
||||
if(b) b = NativeMethods.MoveFileEx(m_iocTxfMidFallback.Path, m_iocBase.Path, f);
|
||||
if(!b) throw new Win32Exception();
|
||||
|
||||
Debug.Assert(!File.Exists(m_iocTemp.Path));
|
||||
Debug.Assert(!File.Exists(m_iocTxfMidFallback.Path));
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TxfMoveWithTx()
|
||||
{
|
||||
IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE);
|
||||
Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE);
|
||||
try
|
||||
{
|
||||
string strTx = PwDefs.ShortProductName + " TxF - " +
|
||||
StrUtil.AlphaNumericOnly(Convert.ToBase64String(
|
||||
CryptoRandom.Instance.GetRandomBytes(16)));
|
||||
const int mchTx = NativeMethods.MAX_TRANSACTION_DESCRIPTION_LENGTH;
|
||||
if(strTx.Length >= mchTx) strTx = strTx.Substring(0, mchTx - 1);
|
||||
|
||||
hTx = NativeMethods.CreateTransaction(IntPtr.Zero,
|
||||
IntPtr.Zero, 0, 0, 0, 0, strTx);
|
||||
if(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!NativeMethods.MoveFileTransacted(m_iocTemp.Path, m_iocBase.Path,
|
||||
IntPtr.Zero, IntPtr.Zero, (NativeMethods.MOVEFILE_COPY_ALLOWED |
|
||||
NativeMethods.MOVEFILE_REPLACE_EXISTING), hTx))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!NativeMethods.CommitTransaction(hTx))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert(!File.Exists(m_iocTemp.Path));
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
if(hTx.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
try { if(!NativeMethods.CloseHandle(hTx)) { Debug.Assert(false); } }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void ClearOld()
|
||||
{
|
||||
try
|
||||
{
|
||||
// See also TxfPrepare method
|
||||
DirectoryInfo di = new DirectoryInfo(UrlUtil.GetTempPath());
|
||||
List<FileInfo> l = UrlUtil.GetFileInfos(di, StrTxfTempPrefix +
|
||||
"*" + StrTxfTempSuffix, SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach(FileInfo fi in l)
|
||||
{
|
||||
if(fi == null) { Debug.Assert(false); continue; }
|
||||
if(!fi.Name.StartsWith(StrTxfTempPrefix, StrUtil.CaseIgnoreCmp) ||
|
||||
!fi.Name.EndsWith(StrTxfTempSuffix, StrUtil.CaseIgnoreCmp))
|
||||
continue;
|
||||
|
||||
if((DateTime.UtcNow - fi.LastWriteTimeUtc).TotalDays > 1.0)
|
||||
fi.Delete();
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -431,7 +431,7 @@ namespace ModernKeePassLib.Serialization
|
||||
string strInfo = m_strProxyAddr;
|
||||
if(m_strProxyPort.Length > 0)
|
||||
strInfo += ":" + m_strProxyPort;
|
||||
MessageService.ShowWarning(strInfo, ex.Message);
|
||||
MessageService.ShowWarning(strInfo, ex);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -642,7 +642,7 @@ namespace ModernKeePassLib.Serialization
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool FileExists(IOConnectionInfo ioc)
|
||||
public static bool FileExists(IOConnectionInfo ioc)
|
||||
{
|
||||
return FileExists(ioc, false);
|
||||
}
|
||||
|
@@ -530,7 +530,7 @@ namespace ModernKeePassLib.Serialization
|
||||
/* KdbxFile f = new KdbxFile(pwDatabase);
|
||||
f.m_format = KdbxFormat.PlainXml;
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
XmlDocument doc = XmlUtilEx.CreateXmlDocument();
|
||||
doc.Load(msData);
|
||||
|
||||
XmlElement el = doc.DocumentElement;
|
||||
|
@@ -87,6 +87,7 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
m_format = fmt;
|
||||
m_slLogger = slLogger;
|
||||
m_xmlWriter = null;
|
||||
|
||||
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
|
||||
UTF8Encoding encNoBom = StrUtil.Utf8;
|
||||
@@ -203,44 +204,25 @@ namespace ModernKeePassLib.Serialization
|
||||
throw new ArgumentOutOfRangeException("fmt");
|
||||
}
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
XmlWriterSettings xws = new XmlWriterSettings();
|
||||
xws.Encoding = encNoBom;
|
||||
xws.Indent = true;
|
||||
xws.IndentChars = "\t";
|
||||
xws.NewLineOnAttributes = false;
|
||||
#if ModernKeePassLib
|
||||
// This is needed for Argon2Kdf write
|
||||
xws.Async = true;
|
||||
if (m_uFileVersion >= FileVersion32_4) xws.CloseOutput = true;
|
||||
#endif
|
||||
|
||||
XmlWriter xw = XmlWriter.Create(sXml, xws);
|
||||
#else
|
||||
XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom);
|
||||
|
||||
xw.Formatting = Formatting.Indented;
|
||||
xw.IndentChar = '\t';
|
||||
xw.Indentation = 1;
|
||||
#endif
|
||||
m_xmlWriter = xw;
|
||||
m_xmlWriter = XmlUtilEx.CreateXmlWriter(sXml, m_uFileVersion >= FileVersion32_4);
|
||||
|
||||
WriteDocument(pgRoot);
|
||||
|
||||
m_xmlWriter.Flush();
|
||||
m_xmlWriter.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
CommonCleanUpWrite(lStreams, sHashing);
|
||||
|
||||
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
|
||||
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
|
||||
|
||||
CommonCleanUpWrite(lStreams, sHashing);
|
||||
}
|
||||
}
|
||||
|
||||
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
|
||||
{
|
||||
if(m_xmlWriter != null) { m_xmlWriter.Dispose(); m_xmlWriter = null; }
|
||||
|
||||
CloseStreams(lStreams);
|
||||
|
||||
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
|
||||
@@ -249,7 +231,6 @@ namespace ModernKeePassLib.Serialization
|
||||
|
||||
CleanUpInnerRandomStream();
|
||||
|
||||
m_xmlWriter = null;
|
||||
m_pbHashOfHeader = null;
|
||||
}
|
||||
|
||||
|
@@ -403,8 +403,8 @@ namespace ModernKeePassLib.Serialization
|
||||
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
|
||||
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
|
||||
throw new Exception(KLRes.FileUnknownCipher +
|
||||
Environment.NewLine + KLRes.FileNewVerOrPlgReq +
|
||||
Environment.NewLine + "UUID: " + pu.ToHexString() + ".");
|
||||
MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq +
|
||||
MessageService.NewParagraph + "UUID: " + pu.ToHexString() + ".");
|
||||
|
||||
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
|
||||
if(iCipher2 != null)
|
||||
@@ -469,14 +469,14 @@ namespace ModernKeePassLib.Serialization
|
||||
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
|
||||
(lStreams.LastIndexOf(lStreams[i]) == i));
|
||||
|
||||
try { lStreams[i].Dispose(); }
|
||||
// Unnecessary exception from CryptoStream with
|
||||
// RijndaelManagedTransform when a stream hasn't been
|
||||
// read completely (e.g. incorrect master key)
|
||||
try { lStreams[i].Dispose(); }
|
||||
// Unnecessary exception from CryptoStream with
|
||||
// RijndaelManagedTransform when a stream hasn't been
|
||||
// read completely (e.g. incorrect master key)
|
||||
#if !ModernKeePassLib
|
||||
catch(CryptographicException) { }
|
||||
catch(CryptographicException) { }
|
||||
#endif
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
// Do not clear the list
|
||||
@@ -539,8 +539,8 @@ namespace ModernKeePassLib.Serialization
|
||||
FileStream fs = new FileStream(strPath, FileMode.Create,
|
||||
FileAccess.Write, FileShare.None);
|
||||
byte[] pbData = pb.ReadData();
|
||||
fs.Write(pbData, 0, pbData.Length);
|
||||
fs.Close();
|
||||
try { File.WriteAllBytes(strPath, pbData); }
|
||||
finally { if(pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -111,23 +111,17 @@ namespace ModernKeePassLib.Translation
|
||||
if(xs == null) throw new ArgumentNullException("xs");
|
||||
|
||||
#if !KeePassLibSD
|
||||
GZipStream gz = new GZipStream(sOut, CompressionMode.Compress);
|
||||
using(GZipStream gz = new GZipStream(sOut, CompressionMode.Compress))
|
||||
#else
|
||||
GZipOutputStream gz = new GZipOutputStream(sOut);
|
||||
using(GZipOutputStream gz = new GZipOutputStream(sOut))
|
||||
#endif
|
||||
{
|
||||
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(gz))
|
||||
{
|
||||
xs.Serialize(xw, kpTrl);
|
||||
}
|
||||
}
|
||||
|
||||
XmlWriterSettings xws = new XmlWriterSettings();
|
||||
xws.CheckCharacters = true;
|
||||
xws.Encoding = StrUtil.Utf8;
|
||||
xws.Indent = true;
|
||||
xws.IndentChars = "\t";
|
||||
|
||||
XmlWriter xw = XmlWriter.Create(gz, xws);
|
||||
|
||||
xs.Serialize(xw, kpTrl);
|
||||
|
||||
xw.Close();
|
||||
gz.Close();
|
||||
sOut.Close();
|
||||
}
|
||||
|
||||
@@ -148,15 +142,17 @@ namespace ModernKeePassLib.Translation
|
||||
{
|
||||
if(xs == null) throw new ArgumentNullException("xs");
|
||||
|
||||
KPTranslation kpTrl = null;
|
||||
|
||||
#if !KeePassLibSD
|
||||
GZipStream gz = new GZipStream(s, CompressionMode.Decompress);
|
||||
using(GZipStream gz = new GZipStream(s, CompressionMode.Decompress))
|
||||
#else
|
||||
GZipInputStream gz = new GZipInputStream(s);
|
||||
using(GZipInputStream gz = new GZipInputStream(s))
|
||||
#endif
|
||||
{
|
||||
kpTrl = (xs.Deserialize(gz) as KPTranslation);
|
||||
}
|
||||
|
||||
KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation);
|
||||
|
||||
gz.Close();
|
||||
s.Close();
|
||||
return kpTrl;
|
||||
}
|
||||
|
@@ -496,11 +496,21 @@ namespace ModernKeePassLib.Utility
|
||||
return UInt32ToBytes((uint)iValue);
|
||||
}
|
||||
|
||||
public static void Int32ToBytesEx(int iValue, byte[] pb, int iOffset)
|
||||
{
|
||||
UInt32ToBytesEx((uint)iValue, pb, iOffset);
|
||||
}
|
||||
|
||||
public static byte[] Int64ToBytes(long lValue)
|
||||
{
|
||||
return UInt64ToBytes((ulong)lValue);
|
||||
}
|
||||
|
||||
public static void Int64ToBytesEx(long lValue, byte[] pb, int iOffset)
|
||||
{
|
||||
UInt64ToBytesEx((ulong)lValue, pb, iOffset);
|
||||
}
|
||||
|
||||
public static uint RotateLeft32(uint u, int nBits)
|
||||
{
|
||||
return ((u << nBits) | (u >> (32 - nBits)));
|
||||
|
@@ -23,7 +23,7 @@ using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
#if !ModernKeePassLib
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
public sealed class MessageServiceEventArgs : EventArgs
|
||||
{
|
||||
private string m_strTitle = string.Empty;
|
||||
#if !ModernKeePassLib
|
||||
private string m_strTitle = string.Empty;
|
||||
private string m_strText = string.Empty;
|
||||
private MessageBoxButtons m_msgButtons = MessageBoxButtons.OK;
|
||||
private MessageBoxIcon m_msgIcon = MessageBoxIcon.None;
|
||||
@@ -54,12 +55,14 @@ namespace ModernKeePassLib.Utility
|
||||
m_msgButtons = msgButtons;
|
||||
m_msgIcon = msgIcon;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static class MessageService
|
||||
{
|
||||
private static volatile uint m_uCurrentMessageCount = 0;
|
||||
private static volatile uint m_uCurrentMessageCount = 0;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
#if !KeePassLibSD
|
||||
private const MessageBoxIcon m_mbiInfo = MessageBoxIcon.Information;
|
||||
private const MessageBoxIcon m_mbiWarning = MessageBoxIcon.Warning;
|
||||
@@ -73,8 +76,8 @@ namespace ModernKeePassLib.Utility
|
||||
private const MessageBoxIcon m_mbiFatal = MessageBoxIcon.Hand;
|
||||
#endif
|
||||
private const MessageBoxIcon m_mbiQuestion = MessageBoxIcon.Question;
|
||||
|
||||
public static string NewLine
|
||||
#endif
|
||||
public static string NewLine
|
||||
{
|
||||
#if !KeePassLibSD
|
||||
get { return Environment.NewLine; }
|
||||
@@ -123,7 +126,7 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
Exception exObj = (obj as Exception);
|
||||
string strObj = (obj as string);
|
||||
#if (!KeePassLibSD && !KeePassRT)
|
||||
#if (!KeePassLibSD && !ModernKeePassLib)
|
||||
StringCollection scObj = (obj as StringCollection);
|
||||
#endif
|
||||
|
||||
@@ -131,10 +134,10 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
if(bFullExceptions)
|
||||
strAppend = StrUtil.FormatException(exObj);
|
||||
else if((exObj.Message != null) && (exObj.Message.Length > 0))
|
||||
else if(!string.IsNullOrEmpty(exObj.Message))
|
||||
strAppend = exObj.Message;
|
||||
}
|
||||
#if (!KeePassLibSD && !KeePassRT)
|
||||
#if (!KeePassLibSD && !ModernKeePassLib)
|
||||
else if(scObj != null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -146,7 +149,7 @@ namespace ModernKeePassLib.Utility
|
||||
strAppend = sb.ToString();
|
||||
}
|
||||
#endif
|
||||
else if(strObj != null)
|
||||
else if (strObj != null)
|
||||
strAppend = strObj;
|
||||
else
|
||||
strAppend = obj.ToString();
|
||||
@@ -163,7 +166,7 @@ namespace ModernKeePassLib.Utility
|
||||
return sbText.ToString();
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassRT)
|
||||
#if (!KeePassLibSD && !ModernKeePassLib)
|
||||
internal static Form GetTopForm()
|
||||
{
|
||||
FormCollection fc = Application.OpenForms;
|
||||
@@ -173,8 +176,8 @@ namespace ModernKeePassLib.Utility
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !KeePassUAP
|
||||
internal static DialogResult SafeShowMessageBox(string strText, string strTitle,
|
||||
#if !ModernKeePassLib
|
||||
internal static DialogResult SafeShowMessageBox(string strText, string strTitle,
|
||||
MessageBoxButtons mb, MessageBoxIcon mi, MessageBoxDefaultButton mdb)
|
||||
{
|
||||
#if (KeePassLibSD || KeePassRT)
|
||||
@@ -405,7 +408,7 @@ namespace ModernKeePassLib.Utility
|
||||
}
|
||||
#endif // !KeePassUAP
|
||||
|
||||
internal static string GetLoadWarningMessage(string strFilePath,
|
||||
internal static string GetLoadWarningMessage(string strFilePath,
|
||||
Exception ex, bool bFullException)
|
||||
{
|
||||
string str = string.Empty;
|
||||
|
@@ -104,6 +104,9 @@ namespace ModernKeePassLib.Utility
|
||||
// 1690:
|
||||
// Removing items from a list view doesn't work properly.
|
||||
// https://sourceforge.net/p/keepass/bugs/1690/
|
||||
// 1716:
|
||||
// 'Always on Top' doesn't work properly on the Cinnamon desktop.
|
||||
// https://sourceforge.net/p/keepass/bugs/1716/
|
||||
// 2139:
|
||||
// Shortcut keys are ignored.
|
||||
// https://sourceforge.net/p/keepass/feature-requests/2139/
|
||||
@@ -560,7 +563,7 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
// Mono's WriteRecentlyUsedFiles method also loads the
|
||||
// XML file using XmlDocument
|
||||
XmlDocument xd = new XmlDocument();
|
||||
XmlDocument xd = XmlUtilEx.CreateXmlDocument();
|
||||
xd.Load(strFile);
|
||||
}
|
||||
catch(Exception) // The XML file is invalid
|
||||
|
@@ -227,7 +227,7 @@ namespace ModernKeePassLib.Utility
|
||||
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
|
||||
#else
|
||||
#if !KeePassLibSD
|
||||
Encoding.Default.EncodingName,
|
||||
Encoding.Default.EncodingName,
|
||||
#else
|
||||
Encoding.Default.WebName,
|
||||
#endif
|
||||
@@ -301,6 +301,28 @@ namespace ModernKeePassLib.Utility
|
||||
return ("\\u" + sh.ToString(NumberFormatInfo.InvariantInfo) + "?");
|
||||
}
|
||||
|
||||
public static string RtfFix(string strRtf)
|
||||
{
|
||||
if(strRtf == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strRtf;
|
||||
|
||||
// Workaround for .NET bug: the Rtf property of a RichTextBox
|
||||
// can return an RTF text starting with "{\\urtf", but
|
||||
// setting such an RTF text throws an exception (the setter
|
||||
// checks for the RTF text to start with "{\\rtf");
|
||||
// https://sourceforge.net/p/keepass/discussion/329221/thread/7788872f/
|
||||
// https://www.microsoft.com/en-us/download/details.aspx?id=10725
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb774284.aspx
|
||||
// https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/RichTextBox.cs
|
||||
const string p = "{\\urtf"; // Typically "{\\urtf1\\ansi\\ansicpg65001"
|
||||
if(str.StartsWith(p) && (str.Length > p.Length) &&
|
||||
char.IsDigit(str[p.Length]))
|
||||
str = str.Remove(2, 1); // Remove the 'u'
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a string to a HTML sequence representing that string.
|
||||
/// </summary>
|
||||
@@ -324,7 +346,7 @@ namespace ModernKeePassLib.Utility
|
||||
if(bNbsp) str = str.Replace(" ", @" "); // Before <br />
|
||||
|
||||
str = NormalizeNewLines(str, false);
|
||||
str = str.Replace("\n", @"<br />" + Environment.NewLine);
|
||||
str = str.Replace("\n", @"<br />" + MessageService.NewLine);
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -497,40 +519,40 @@ namespace ModernKeePassLib.Utility
|
||||
{
|
||||
string strText = string.Empty;
|
||||
|
||||
if(excp.Message != null)
|
||||
strText += excp.Message + Environment.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.Message))
|
||||
strText += excp.Message + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
if(excp.Source != null)
|
||||
strText += excp.Source + Environment.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.Source))
|
||||
strText += excp.Source + MessageService.NewLine;
|
||||
#endif
|
||||
if(excp.StackTrace != null)
|
||||
strText += excp.StackTrace + Environment.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.StackTrace))
|
||||
strText += excp.StackTrace + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
#if !ModernKeePassLib && !KeePassRT
|
||||
if(excp.TargetSite != null)
|
||||
strText += excp.TargetSite.ToString() + Environment.NewLine;
|
||||
strText += excp.TargetSite.ToString() + MessageService.NewLine;
|
||||
#endif
|
||||
|
||||
if(excp.Data != null)
|
||||
{
|
||||
strText += Environment.NewLine;
|
||||
strText += MessageService.NewLine;
|
||||
foreach(DictionaryEntry de in excp.Data)
|
||||
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
||||
Environment.NewLine;
|
||||
MessageService.NewLine;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(excp.InnerException != null)
|
||||
{
|
||||
strText += Environment.NewLine + "Inner:" + Environment.NewLine;
|
||||
if(excp.InnerException.Message != null)
|
||||
strText += excp.InnerException.Message + Environment.NewLine;
|
||||
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.InnerException.Message))
|
||||
strText += excp.InnerException.Message + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
if(excp.InnerException.Source != null)
|
||||
strText += excp.InnerException.Source + Environment.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.InnerException.Source))
|
||||
strText += excp.InnerException.Source + MessageService.NewLine;
|
||||
#endif
|
||||
if(excp.InnerException.StackTrace != null)
|
||||
strText += excp.InnerException.StackTrace + Environment.NewLine;
|
||||
if(!string.IsNullOrEmpty(excp.InnerException.StackTrace))
|
||||
strText += excp.InnerException.StackTrace + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
#if !ModernKeePassLib && !KeePassRT
|
||||
if(excp.InnerException.TargetSite != null)
|
||||
@@ -539,10 +561,10 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
if(excp.InnerException.Data != null)
|
||||
{
|
||||
strText += Environment.NewLine;
|
||||
strText += MessageService.NewLine;
|
||||
foreach(DictionaryEntry de in excp.InnerException.Data)
|
||||
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
||||
Environment.NewLine;
|
||||
MessageService.NewLine;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1140,32 +1162,58 @@ namespace ModernKeePassLib.Utility
|
||||
return str;
|
||||
}
|
||||
|
||||
private static char[] m_vNewLineChars = null;
|
||||
public static void NormalizeNewLines(ProtectedStringDictionary dict,
|
||||
bool bWindows)
|
||||
{
|
||||
if(dict == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(m_vNewLineChars == null)
|
||||
m_vNewLineChars = new char[]{ '\r', '\n' };
|
||||
|
||||
List<string> vKeys = dict.GetKeys();
|
||||
foreach(string strKey in vKeys)
|
||||
List<string> lKeys = dict.GetKeys();
|
||||
foreach(string strKey in lKeys)
|
||||
{
|
||||
ProtectedString ps = dict.Get(strKey);
|
||||
if(ps == null) { Debug.Assert(false); continue; }
|
||||
|
||||
string strValue = ps.ReadString();
|
||||
if(strValue.IndexOfAny(m_vNewLineChars) < 0) continue;
|
||||
|
||||
dict.Set(strKey, new ProtectedString(ps.IsProtected,
|
||||
NormalizeNewLines(strValue, bWindows)));
|
||||
char[] v = ps.ReadChars();
|
||||
if(!IsNewLineNormalized(v, bWindows))
|
||||
dict.Set(strKey, new ProtectedString(ps.IsProtected,
|
||||
NormalizeNewLines(ps.ReadString(), bWindows)));
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsNewLineNormalized(char[] v, bool bWindows)
|
||||
{
|
||||
if(v == null) { Debug.Assert(false); return true; }
|
||||
|
||||
if(bWindows)
|
||||
{
|
||||
int iFreeCr = -2; // Must be < -1 (for test "!= (i - 1)")
|
||||
|
||||
for(int i = 0; i < v.Length; ++i)
|
||||
{
|
||||
char ch = v[i];
|
||||
|
||||
if(ch == '\r')
|
||||
{
|
||||
if(iFreeCr >= 0) return false;
|
||||
iFreeCr = i;
|
||||
}
|
||||
else if(ch == '\n')
|
||||
{
|
||||
if(iFreeCr != (i - 1)) return false;
|
||||
iFreeCr = -2; // Consume \r
|
||||
}
|
||||
}
|
||||
|
||||
return (iFreeCr < 0); // Ensure no \r at end
|
||||
}
|
||||
|
||||
return (Array.IndexOf<char>(v, '\r') < 0);
|
||||
}
|
||||
|
||||
public static string GetNewLineSeq(string str)
|
||||
{
|
||||
if(str == null) { Debug.Assert(false); return Environment.NewLine; }
|
||||
if(str == null) { Debug.Assert(false); return MessageService.NewLine; }
|
||||
|
||||
int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0;
|
||||
char chLast = char.MinValue;
|
||||
@@ -1187,7 +1235,7 @@ namespace ModernKeePassLib.Utility
|
||||
nLf -= nCrLf;
|
||||
|
||||
int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf));
|
||||
if(nMax == 0) return Environment.NewLine;
|
||||
if(nMax == 0) return MessageService.NewLine;
|
||||
|
||||
if(nCrLf == nMax) return "\r\n";
|
||||
return ((nLf == nMax) ? "\n" : "\r");
|
||||
@@ -1319,7 +1367,7 @@ namespace ModernKeePassLib.Utility
|
||||
try
|
||||
{
|
||||
byte[] pbPlain = StrUtil.Utf8.GetBytes(strPlainText);
|
||||
byte[] pbEnc = ProtectedData.Protect(pbPlain, m_pbOptEnt,
|
||||
byte[] pbEnc = CryptoUtil.ProtectData(pbPlain, m_pbOptEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
@@ -1340,7 +1388,7 @@ namespace ModernKeePassLib.Utility
|
||||
try
|
||||
{
|
||||
byte[] pbEnc = Convert.FromBase64String(strCipherText);
|
||||
byte[] pbPlain = ProtectedData.Unprotect(pbEnc, m_pbOptEnt,
|
||||
byte[] pbPlain = CryptoUtil.UnprotectData(pbEnc, m_pbOptEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
|
||||
return StrUtil.Utf8.GetString(pbPlain, 0, pbPlain.Length);
|
||||
|
65
ModernKeePassLib/Utility/TypeOverridePool.cs
Normal file
65
ModernKeePassLib/Utility/TypeOverridePool.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
|
||||
namespace KeePassLib.Utility
|
||||
{
|
||||
public static class TypeOverridePool
|
||||
{
|
||||
private static Dictionary<Type, GFunc<object>> g_d =
|
||||
new Dictionary<Type, GFunc<object>>();
|
||||
|
||||
public static void Register(Type t, GFunc<object> f)
|
||||
{
|
||||
if(t == null) throw new ArgumentNullException("t");
|
||||
if(f == null) throw new ArgumentNullException("f");
|
||||
|
||||
g_d[t] = f;
|
||||
}
|
||||
|
||||
public static void Unregister(Type t)
|
||||
{
|
||||
if(t == null) throw new ArgumentNullException("t");
|
||||
|
||||
g_d.Remove(t);
|
||||
}
|
||||
|
||||
public static bool IsRegistered(Type t)
|
||||
{
|
||||
if(t == null) throw new ArgumentNullException("t");
|
||||
|
||||
return g_d.ContainsKey(t);
|
||||
}
|
||||
|
||||
public static T CreateInstance<T>()
|
||||
where T : new()
|
||||
{
|
||||
GFunc<object> f;
|
||||
if(g_d.TryGetValue(typeof(T), out f))
|
||||
return (T)(f());
|
||||
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
}
|
@@ -299,7 +299,7 @@ namespace ModernKeePassLib.Utility
|
||||
else // Unhide
|
||||
{
|
||||
fa &= ~FileAttributes.Hidden;
|
||||
if((long)fa == 0) fa |= FileAttributes.Normal;
|
||||
if((long)fa == 0) fa = FileAttributes.Normal;
|
||||
}
|
||||
|
||||
File.SetAttributes(strFile, fa);
|
||||
@@ -635,13 +635,12 @@ namespace ModernKeePassLib.Utility
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !ModernKeePassLib
|
||||
public static string GetTempPath()
|
||||
{
|
||||
string strDir;
|
||||
if(NativeLib.IsUnix())
|
||||
strDir = NativeMethods.GetUserRuntimeDir();
|
||||
#if KeePassRT
|
||||
#if ModernKeePassLib
|
||||
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
|
||||
#else
|
||||
else strDir = Path.GetTempPath();
|
||||
@@ -655,7 +654,6 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
return strDir;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !ModernKeePassLib && !KeePassLibSD
|
||||
// Structurally mostly equivalent to UrlUtil.GetFileInfos
|
||||
@@ -779,5 +777,17 @@ namespace ModernKeePassLib.Utility
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static char GetDriveLetter(string strPath)
|
||||
{
|
||||
if(strPath == null) throw new ArgumentNullException("strPath");
|
||||
|
||||
Debug.Assert(default(char) == '\0');
|
||||
if(strPath.Length < 3) return '\0';
|
||||
if((strPath[1] != ':') || (strPath[2] != '\\')) return '\0';
|
||||
|
||||
char ch = char.ToUpperInvariant(strPath[0]);
|
||||
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
127
ModernKeePassLib/Utility/XmlUtilEx.cs
Normal file
127
ModernKeePassLib/Utility/XmlUtilEx.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Windows.Data.Xml.Dom;
|
||||
|
||||
namespace ModernKeePassLib.Utility
|
||||
{
|
||||
public static class XmlUtilEx
|
||||
{
|
||||
public static XmlDocument CreateXmlDocument()
|
||||
{
|
||||
XmlDocument d = new XmlDocument();
|
||||
|
||||
// .NET 4.5.2 and newer do not resolve external XML resources
|
||||
// by default; for older .NET versions, we explicitly
|
||||
// prevent resolving
|
||||
#if !ModernKeePassLib
|
||||
d.XmlResolver = null; // Default in old .NET: XmlUrlResolver object
|
||||
#endif
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
public static XmlReaderSettings CreateXmlReaderSettings()
|
||||
{
|
||||
XmlReaderSettings xrs = new XmlReaderSettings();
|
||||
|
||||
xrs.CloseInput = false;
|
||||
xrs.IgnoreComments = true;
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if KeePassUAP || ModernKeePassLib
|
||||
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
||||
#else
|
||||
// Also see PrepMonoDev.sh script
|
||||
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
|
||||
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
|
||||
|
||||
xrs.ValidationType = ValidationType.None;
|
||||
xrs.XmlResolver = null;
|
||||
#endif
|
||||
|
||||
return xrs;
|
||||
}
|
||||
|
||||
public static XmlReader CreateXmlReader(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
return XmlReader.Create(s, CreateXmlReaderSettings());
|
||||
}
|
||||
|
||||
public static XmlWriterSettings CreateXmlWriterSettings(bool isVersionGreaterThan4 = false)
|
||||
{
|
||||
XmlWriterSettings xws = new XmlWriterSettings();
|
||||
|
||||
xws.CloseOutput = isVersionGreaterThan4;
|
||||
xws.Encoding = StrUtil.Utf8;
|
||||
xws.Indent = true;
|
||||
xws.IndentChars = "\t";
|
||||
xws.NewLineOnAttributes = false;
|
||||
#if ModernKeePassLib
|
||||
// This is needed for Argon2Kdf write
|
||||
xws.Async = true;
|
||||
#endif
|
||||
|
||||
return xws;
|
||||
}
|
||||
|
||||
public static XmlWriter CreateXmlWriter(Stream s, bool isVersionGreaterThan4 = false)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
return XmlWriter.Create(s, CreateXmlWriterSettings(isVersionGreaterThan4));
|
||||
}
|
||||
|
||||
public static void Serialize<T>(Stream s, T t)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
using(XmlWriter xw = CreateXmlWriter(s))
|
||||
{
|
||||
xs.Serialize(xw, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
|
||||
T t = default(T);
|
||||
using(XmlReader xr = CreateXmlReader(s))
|
||||
{
|
||||
t = (T)xs.Deserialize(xr);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user