Update to version 2.42.1

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

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using ModernKeePassLib.Keys; using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Storage; using Windows.Storage;
@@ -24,7 +25,7 @@ namespace ModernKeePassLib.Test.Keys
private const string ExpectedFileEnd = "</Data>\r\n\t</Key>\r\n</KeyFile>"; private const string ExpectedFileEnd = "</Data>\r\n\t</Key>\r\n</KeyFile>";
[Fact] [Fact]
public void TestConstruct() public async Task TestConstruct()
{ {
var expectedKeyData = new byte[] var expectedKeyData = new byte[]
{ {
@@ -34,16 +35,14 @@ namespace ModernKeePassLib.Test.Keys
0x45, 0xfc, 0xc8, 0x92, 0xbd, 0xeb, 0xaf, 0xc3 0x45, 0xfc, 0xc8, 0x92, 0xbd, 0xeb, 0xaf, 0xc3
}; };
var folder = StorageFolder.GetFolderFromPathAsync(Path.GetTempPath()).GetAwaiter().GetResult(); var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetTempPath());
var file = folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting).GetAwaiter().GetResult(); var file = await folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting);
using (var fs = file.OpenStreamForWriteAsync().GetAwaiter().GetResult()) await using (var fs = await file.OpenStreamForWriteAsync())
{ {
using (var sw = new StreamWriter(fs)) await using var sw = new StreamWriter(fs);
{ sw.Write(ExpectedFileStart);
sw.Write(ExpectedFileStart); sw.Write(TestKey);
sw.Write(TestKey); sw.Write(ExpectedFileEnd);
sw.Write(ExpectedFileEnd);
}
} }
try try
@@ -54,19 +53,19 @@ namespace ModernKeePassLib.Test.Keys
} }
finally finally
{ {
file.DeleteAsync().GetAwaiter().GetResult(); await file.DeleteAsync();
} }
} }
[Fact] [Fact]
public void TestCreate() public async Task TestCreate()
{ {
var folder = StorageFolder.GetFolderFromPathAsync(Path.GetTempPath()).GetAwaiter().GetResult(); var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetTempPath());
var file = folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting).GetAwaiter().GetResult(); var file = await folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting);
KcpKeyFile.Create(file, null); KcpKeyFile.Create(file, null);
try try
{ {
var fileContents = FileIO.ReadTextAsync(file).GetAwaiter().GetResult(); var fileContents = await FileIO.ReadTextAsync(file);
Assert.Equal(185, fileContents.Length); Assert.Equal(185, fileContents.Length);
Assert.StartsWith(ExpectedFileStart, fileContents); Assert.StartsWith(ExpectedFileStart, fileContents);
@@ -74,7 +73,7 @@ namespace ModernKeePassLib.Test.Keys
} }
finally finally
{ {
file.DeleteAsync().GetAwaiter().GetResult(); await file.DeleteAsync();
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,86 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.AccessCache;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.UwpTest
{
public class KcpKeyFileTests
{
private const string TestCreateFile = "TestCreate.xml";
private const string TestKey = "0123456789";
private const string ExpectedFileStart =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
"<KeyFile>\r\n" +
"\t<Meta>\r\n" +
"\t\t<Version>1.00</Version>\r\n" +
"\t</Meta>\r\n" +
"\t<Key>\r\n" +
"\t\t<Data>";
private const string ExpectedFileEnd = "</Data>\r\n\t</Key>\r\n</KeyFile>";
[Fact]
public async Task TestConstruct()
{
var expectedKeyData = new byte[]
{
0x95, 0x94, 0xdc, 0xb9, 0x91, 0xc6, 0x65, 0xa0,
0x81, 0xf6, 0x6f, 0xca, 0x07, 0x1a, 0x30, 0xd1,
0x1d, 0x65, 0xcf, 0x8d, 0x9c, 0x60, 0xfb, 0xe6,
0x45, 0xfc, 0xc8, 0x92, 0xbd, 0xeb, 0xaf, 0xc3
};
var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetTempPath());
var file = await folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting);
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
using (var fs = await file.OpenStreamForWriteAsync())
{
using (var sw = new StreamWriter(fs))
{
sw.Write(ExpectedFileStart);
sw.Write(TestKey);
sw.Write(ExpectedFileEnd);
}
}
try
{
var keyFile = new KcpKeyFile(token);
var keyData = keyFile.KeyData.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, expectedKeyData));
}
finally
{
await file.DeleteAsync();
}
}
[Fact]
public async Task TestCreate()
{
var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetTempPath());
var file = await folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting);
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
KcpKeyFile.Create(token, null);
try
{
var fileContents = await FileIO.ReadTextAsync(file);
Assert.Equal(185, fileContents.Length);
Assert.StartsWith(ExpectedFileStart, fileContents);
Assert.EndsWith(ExpectedFileEnd, fileContents);
}
finally
{
await file.DeleteAsync();
}
}
}
}

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.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>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{E43877FF-6725-43DA-A3AF-F1A7CB86EE03}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModernKeePassLib.UwpTest</RootNamespace>
<AssemblyName>ModernKeePassLib.UwpTest</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.17763.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\ARM64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="KcpKeyFileTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UnitTestApp.xaml.cs">
<DependentUpon>UnitTestApp.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="UnitTestApp.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.8</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.4.0</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.4.0</Version>
</PackageReference>
<PackageReference Include="xunit">
<Version>2.4.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModernKeePassLib\ModernKeePassLib.csproj">
<Project>{15a7e2e5-2b46-4975-afbd-2898e47e5ba1}</Project>
<Name>ModernKeePassLib</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.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>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity Name="bdd8e2db-adc6-45a8-b9e9-133fd7361434"
Publisher="CN=GeoffroyBONNEVILLE"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="bdd8e2db-adc6-45a8-b9e9-133fd7361434" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>ModernKeePassLib.UwpTest</DisplayName>
<PublisherDisplayName>GeoffroyBONNEVILLE</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="vstest.executionengine.universal.App"
Executable="$targetnametoken$.exe"
EntryPoint="ModernKeePassLib.UwpTest.App">
<uap:VisualElements
DisplayName="ModernKeePassLib.UwpTest"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="ModernKeePassLib.UwpTest"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClientServer" />
<Capability Name="privateNetworkClientServer" />
</Capabilities>
</Package>

View File

@@ -0,0 +1,18 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("ModernKeePassLib.UwpTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ModernKeePassLib.UwpTest")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyMetadata("TargetPlatform","UAP")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

View File

@@ -0,0 +1,29 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>

View File

@@ -0,0 +1,7 @@
<Application
x:Class="ModernKeePassLib.UwpTest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePassLib.UwpTest">
</Application>

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace ModernKeePassLib.UwpTest
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
// Ensure the current window is active
Window.Current.Activate();
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}

View File

@@ -10,17 +10,57 @@ EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU {15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|ARM.Build.0 = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|ARM64.Build.0 = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|x64.ActiveCfg = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|x64.Build.0 = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|x86.ActiveCfg = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Debug|x86.Build.0 = Debug|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU {15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|Any CPU.Build.0 = Release|Any CPU {15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|Any CPU.Build.0 = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|ARM.ActiveCfg = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|ARM.Build.0 = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|ARM64.ActiveCfg = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|ARM64.Build.0 = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|x64.ActiveCfg = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|x64.Build.0 = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|x86.ActiveCfg = Release|Any CPU
{15A7E2E5-2B46-4975-AFBD-2898E47E5BA1}.Release|x86.Build.0 = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|Any CPU.Build.0 = Debug|Any CPU {4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|ARM.Build.0 = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|ARM64.Build.0 = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|x64.ActiveCfg = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|x64.Build.0 = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|x86.ActiveCfg = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Debug|x86.Build.0 = Debug|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|Any CPU.ActiveCfg = Release|Any CPU {4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|Any CPU.Build.0 = Release|Any CPU {4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|Any CPU.Build.0 = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|ARM.ActiveCfg = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|ARM.Build.0 = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|ARM64.ActiveCfg = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|ARM64.Build.0 = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|x64.ActiveCfg = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|x64.Build.0 = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|x86.ActiveCfg = Release|Any CPU
{4797E768-279A-493A-B21B-CD96AB1F8C20}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -225,6 +225,25 @@ namespace ModernKeePassLib.Cryptography
return MemUtil.BytesToUInt64(pb); return MemUtil.BytesToUInt64(pb);
} }
internal ulong GetRandomUInt64(ulong uMaxExcl)
{
if(uMaxExcl == 0) { Debug.Assert(false); throw new ArgumentOutOfRangeException("uMaxExcl"); }
ulong uGen, uRem;
do
{
uGen = GetRandomUInt64();
uRem = uGen % uMaxExcl;
}
while((uGen - uRem) > (ulong.MaxValue - (uMaxExcl - 1UL)));
// This ensures that the last number of the block (i.e.
// (uGen - uRem) + (uMaxExcl - 1)) is generatable;
// for signed longs, overflow to negative number:
// while((uGen - uRem) + (uMaxExcl - 1) < 0);
return uRem;
}
#if CRSBENCHMARK #if CRSBENCHMARK
public static string Benchmark() public static string Benchmark()
{ {

View File

@@ -28,37 +28,39 @@ using System.Security.Cryptography;
namespace ModernKeePassLib.Cryptography namespace ModernKeePassLib.Cryptography
{ {
public sealed class CryptoStreamEx : CryptoStream public sealed class CryptoStreamEx : CryptoStream
{ {
private ICryptoTransform m_t; private ICryptoTransform m_t;
private SymmetricAlgorithm m_a; private SymmetricAlgorithm m_a;
public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m, public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m,
SymmetricAlgorithm a) : base(s, t, m) SymmetricAlgorithm a) : base(s, t, m)
{ {
m_t = t; m_t = t;
m_a = a; m_a = a;
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
try { base.Dispose(disposing); } try { base.Dispose(disposing); }
// Unnecessary exception from CryptoStream with // Unnecessary exception from CryptoStream with
// RijndaelManagedTransform when a stream hasn't been // RijndaelManagedTransform when a stream hasn't been
// read completely (e.g. incorrect master key) // read completely (e.g. incorrect master key)
catch (CryptographicException) { } catch(CryptographicException) { }
catch (Exception) { Debug.Assert(false); } // Similar to above, at the beginning of the stream
catch(IndexOutOfRangeException) { }
catch(Exception) { Debug.Assert(false); }
if (disposing) if(disposing)
{ {
try { if (m_t != null) { m_t.Dispose(); m_t = null; } } try { if(m_t != null) { m_t.Dispose(); m_t = null; } }
catch (Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
// In .NET 2.0, SymmetricAlgorithm.Dispose() is not public // In .NET 2.0, SymmetricAlgorithm.Dispose() is not public
try { if (m_a != null) { m_a.Clear(); m_a = null; } } try { if(m_a != null) { m_a.Clear(); m_a = null; } }
catch (Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
} }
} }
} }
#endif #endif

View File

@@ -42,13 +42,13 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation
private const uint MaxVersion = 0x13; private const uint MaxVersion = 0x13;
private const int MinSalt = 8; private const int MinSalt = 8;
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec.
internal const ulong MinIterations = 1; internal const ulong MinIterations = 1;
internal const ulong MaxIterations = uint.MaxValue; internal const ulong MaxIterations = uint.MaxValue;
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1 internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec // internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec.
internal const ulong MaxMemory = int.MaxValue; // .NET limit internal const ulong MaxMemory = int.MaxValue; // .NET limit
internal const uint MinParallelism = 1; internal const uint MinParallelism = 1;

View File

@@ -36,20 +36,20 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
if(pwProfile.Length == 0) return PwgError.Success; if(pwProfile.Length == 0) return PwgError.Success;
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString()); PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
PwGenerator.PrepareCharSet(pcs, pwProfile); if(!PwGenerator.PrepareCharSet(pcs, pwProfile))
return PwgError.InvalidCharSet;
char[] v = new char[pwProfile.Length]; char[] v = new char[pwProfile.Length];
try try
{ {
for(int i = 0; i < v.Length; ++i) for(int i = 0; i < v.Length; ++i)
{ {
char ch = PwGenerator.GenerateCharacter(pwProfile, char ch = PwGenerator.GenerateCharacter(pcs, crsRandomSource);
pcs, crsRandomSource);
if(ch == char.MinValue) if(ch == char.MinValue)
return PwgError.TooFewCharacters; return PwgError.TooFewCharacters;
v[i] = ch; v[i] = ch;
if(pwProfile.NoRepeatingCharacters) pcs.Remove(ch);
} }
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v); byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);

View File

@@ -34,92 +34,61 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
{ {
psOut = ProtectedString.Empty; psOut = ProtectedString.Empty;
string strPattern = pwProfile.Pattern;
if(string.IsNullOrEmpty(strPattern)) return PwgError.Success;
CharStream cs = new CharStream(strPattern);
LinkedList<char> llGenerated = new LinkedList<char>(); LinkedList<char> llGenerated = new LinkedList<char>();
PwCharSet pcsCurrent = new PwCharSet(); PwCharSet pcs = new PwCharSet();
PwCharSet pcsCustom = new PwCharSet();
PwCharSet pcsUsed = new PwCharSet();
bool bInCharSetDef = false;
string strPattern = ExpandPattern(pwProfile.Pattern); while(true)
if(strPattern.Length == 0) return PwgError.Success;
CharStream csStream = new CharStream(strPattern);
char ch = csStream.ReadChar();
while(ch != char.MinValue)
{ {
pcsCurrent.Clear(); char ch = cs.ReadChar();
if(ch == char.MinValue) break;
bool bGenerateChar = false; pcs.Clear();
if(ch == '\\') if(ch == '\\')
{ {
ch = csStream.ReadChar(); ch = cs.ReadChar();
if(ch == char.MinValue) // Backslash at the end if(ch == char.MinValue) return PwgError.InvalidPattern;
{
llGenerated.AddLast('\\');
break;
}
if(bInCharSetDef) pcsCustom.Add(ch); pcs.Add(ch); // Allow "{...}" support and char check
else
{
llGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
}
else if(ch == '^')
{
ch = csStream.ReadChar();
if(ch == char.MinValue) // ^ at the end
{
llGenerated.AddLast('^');
break;
}
if(bInCharSetDef) pcsCustom.Remove(ch);
} }
else if(ch == '[') else if(ch == '[')
{ {
pcsCustom.Clear(); if(!ReadCustomCharSet(cs, pcs))
bInCharSetDef = true; return PwgError.InvalidPattern;
} }
else if(ch == ']') else
{ {
pcsCurrent.Add(pcsCustom.ToString()); if(!pcs.AddCharSet(ch))
return PwgError.InvalidPattern;
}
bInCharSetDef = false; int nCount = 1;
bGenerateChar = true; if(cs.PeekChar() == '{')
}
else if(bInCharSetDef)
{ {
if(pcsCustom.AddCharSet(ch) == false) nCount = ReadCount(cs);
pcsCustom.Add(ch); if(nCount < 0) return PwgError.InvalidPattern;
} }
else if(pcsCurrent.AddCharSet(ch) == false)
{
llGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
else bGenerateChar = true;
if(bGenerateChar) for(int i = 0; i < nCount; ++i)
{ {
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile); if(!PwGenerator.PrepareCharSet(pcs, pwProfile))
return PwgError.InvalidCharSet;
if(pwProfile.NoRepeatingCharacters) if(pwProfile.NoRepeatingCharacters)
pcsCurrent.Remove(pcsUsed.ToString()); {
foreach(char chUsed in llGenerated)
char chGen = PwGenerator.GenerateCharacter(pwProfile, pcs.Remove(chUsed);
pcsCurrent, crsRandomSource); }
char chGen = PwGenerator.GenerateCharacter(pcs,
crsRandomSource);
if(chGen == char.MinValue) return PwgError.TooFewCharacters; if(chGen == char.MinValue) return PwgError.TooFewCharacters;
llGenerated.AddLast(chGen); llGenerated.AddLast(chGen);
pcsUsed.Add(chGen);
} }
ch = csStream.ReadChar();
} }
if(llGenerated.Count == 0) return PwgError.Success; if(llGenerated.Count == 0) return PwgError.Success;
@@ -135,53 +104,70 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
MemUtil.ZeroByteArray(pbUtf8); MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(v); MemUtil.ZeroArray<char>(v);
llGenerated.Clear();
return PwgError.Success; return PwgError.Success;
} }
private static string ExpandPattern(string strPattern) private static bool ReadCustomCharSet(CharStream cs, PwCharSet pcsOut)
{ {
if(strPattern == null) { Debug.Assert(false); return string.Empty; } Debug.Assert(cs.PeekChar() != '['); // Consumed already
Debug.Assert(pcsOut.Size == 0);
string str = strPattern;
bool bAdd = true;
while(true) while(true)
{ {
int nOpen = FindFirstUnescapedChar(str, '{'); char ch = cs.ReadChar();
int nClose = FindFirstUnescapedChar(str, '}'); if(ch == char.MinValue) return false;
if(ch == ']') break;
if((nOpen >= 0) && (nOpen < nClose)) if(ch == '\\')
{ {
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1); ch = cs.ReadChar();
str = str.Remove(nOpen, nClose - nOpen + 1); if(ch == char.MinValue) return false;
uint uRepeat; if(bAdd) pcsOut.Add(ch);
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1)) else pcsOut.Remove(ch);
{ }
if(uRepeat == 0) else if(ch == '^')
str = str.Remove(nOpen - 1, 1); {
else if(bAdd) bAdd = false;
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1)); else return false; // '^' toggles the mode only once
} }
else
{
PwCharSet pcs = new PwCharSet();
if(!pcs.AddCharSet(ch)) return false;
if(bAdd) pcsOut.Add(pcs.ToString());
else pcsOut.Remove(pcs.ToString());
} }
else break;
} }
return str; return true;
} }
private static int FindFirstUnescapedChar(string str, char ch) private static int ReadCount(CharStream cs)
{ {
for(int i = 0; i < str.Length; ++i) if(cs.ReadChar() != '{') { Debug.Assert(false); return -1; }
{
char chCur = str[i];
if(chCur == '\\') ++i; // Next is escaped, skip it // Ensure not empty
else if(chCur == ch) return i; char chFirst = cs.PeekChar();
if((chFirst < '0') || (chFirst > '9')) return -1;
long n = 0;
while(true)
{
char ch = cs.ReadChar();
if(ch == '}') break;
if((ch >= '0') && (ch <= '9'))
{
n = (n * 10L) + (long)(ch - '0');
if(n > int.MaxValue) return -1;
}
else return -1;
} }
return -1; return (int)n;
} }
} }
} }

View File

@@ -43,7 +43,6 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
public static readonly string UpperHex = "0123456789ABCDEF"; public static readonly string UpperHex = "0123456789ABCDEF";
public static readonly string LowerHex = "0123456789abcdef"; public static readonly string LowerHex = "0123456789abcdef";
public static readonly string Invalid = "\t\r\n";
public static readonly string LookAlike = @"O0l1I|"; public static readonly string LookAlike = @"O0l1I|";
internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits; internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;

View File

@@ -36,7 +36,9 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
Success = 0, Success = 0,
Unknown = 1, Unknown = 1,
TooFewCharacters = 2, TooFewCharacters = 2,
UnknownAlgorithm = 3 UnknownAlgorithm = 3,
InvalidCharSet = 4,
InvalidPattern = 5
} }
/// <summary> /// <summary>
@@ -94,30 +96,33 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey); return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
} }
internal static char GenerateCharacter(PwProfile pwProfile, internal static char GenerateCharacter(PwCharSet pwCharSet,
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource) CryptoRandomStream crsRandomSource)
{ {
if(pwCharSet.Size == 0) return char.MinValue; uint cc = pwCharSet.Size;
if(cc == 0) return char.MinValue;
ulong uIndex = crsRandomSource.GetRandomUInt64(); uint i = (uint)crsRandomSource.GetRandomUInt64(cc);
uIndex %= (ulong)pwCharSet.Size; return pwCharSet[i];
char ch = pwCharSet[(uint)uIndex];
if(pwProfile.NoRepeatingCharacters)
pwCharSet.Remove(ch);
return ch;
} }
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile) internal static bool PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
{ {
pwCharSet.Remove(PwCharSet.Invalid); uint cc = pwCharSet.Size;
for(uint i = 0; i < cc; ++i)
{
char ch = pwCharSet[i];
if((ch == char.MinValue) || (ch == '\t') || (ch == '\r') ||
(ch == '\n') || char.IsSurrogate(ch))
return false;
}
if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike); if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
if(pwProfile.ExcludeCharacters.Length > 0) if(!string.IsNullOrEmpty(pwProfile.ExcludeCharacters))
pwCharSet.Remove(pwProfile.ExcludeCharacters); pwCharSet.Remove(pwProfile.ExcludeCharacters);
return true;
} }
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource) internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
@@ -127,8 +132,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
for(int i = v.Length - 1; i >= 1; --i) for(int i = v.Length - 1; i >= 1; --i)
{ {
ulong r = crsRandomSource.GetRandomUInt64(); int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1));
int j = (int)(r % (ulong)(i + 1));
char t = v[i]; char t = v[i];
v[i] = v[j]; v[i] = v[j];

View File

@@ -87,7 +87,7 @@ namespace ModernKeePassLib.Interfaces
bool SetText(string strNewText, LogStatusType lsType); bool SetText(string strNewText, LogStatusType lsType);
/// <summary> /// <summary>
/// Check if the user cancelled the current work. /// Check whether the user cancelled the current work.
/// </summary> /// </summary>
/// <returns>Returns <c>true</c> if the caller should continue /// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns> /// the current work.</returns>

View File

@@ -66,7 +66,7 @@ namespace ModernKeePassLib.Interfaces
} }
/// <summary> /// <summary>
/// Flag that determines if the object does expire. /// Flag that determines whether the object expires.
/// </summary> /// </summary>
bool Expires bool Expires
{ {

View File

@@ -21,9 +21,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using System.Threading;
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Native;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -166,7 +169,6 @@ namespace ModernKeePassLib.Keys
{ {
ValidateUserKeys(); ValidateUserKeys();
// Concatenate user key data
List<byte[]> lData = new List<byte[]>(); List<byte[]> lData = new List<byte[]>();
int cbData = 0; int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys) foreach(IUserKey pKey in m_vUserKeys)
@@ -199,13 +201,17 @@ namespace ModernKeePassLib.Keys
{ {
if(ckOther == null) throw new ArgumentNullException("ckOther"); if(ckOther == null) throw new ArgumentNullException("ckOther");
bool bEqual;
byte[] pbThis = CreateRawCompositeKey32(); byte[] pbThis = CreateRawCompositeKey32();
byte[] pbOther = ckOther.CreateRawCompositeKey32(); try
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther); {
MemUtil.ZeroByteArray(pbOther); byte[] pbOther = ckOther.CreateRawCompositeKey32();
MemUtil.ZeroByteArray(pbThis); bEqual = MemUtil.ArraysEqual(pbThis, pbOther);
MemUtil.ZeroByteArray(pbOther);
}
finally { MemUtil.ZeroByteArray(pbThis); }
return bResult; return bEqual;
} }
[Obsolete] [Obsolete]
@@ -231,31 +237,90 @@ namespace ModernKeePassLib.Keys
{ {
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); } if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
byte[] pbRaw32 = CreateRawCompositeKey32(); byte[] pbRaw32 = null, pbTrf32 = null;
if((pbRaw32 == null) || (pbRaw32.Length != 32)) ProtectedBinary pbRet = null;
{ Debug.Assert(false); return null; }
KdfEngine kdf = KdfPool.Get(p.KdfUuid); try
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + p.KdfUuid.ToHexString() + ".");
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{ {
Debug.Assert(false); pbRaw32 = CreateRawCompositeKey32();
pbTrf32 = CryptoUtil.HashSha256(pbTrf32); if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; }
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + p.KdfUuid.ToHexString() + ".");
pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{
Debug.Assert(false);
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
}
pbRet = new ProtectedBinary(true, pbTrf32);
}
finally
{
if(pbRaw32 != null) MemUtil.ZeroByteArray(pbRaw32);
if(pbTrf32 != null) MemUtil.ZeroByteArray(pbTrf32);
} }
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32);
return pbRet; return pbRet;
} }
private sealed class CkGkTaskInfo
{
public volatile ProtectedBinary Key = null;
public volatile string Error = null;
}
internal ProtectedBinary GenerateKey32Ex(KdfParameters p, IStatusLogger sl)
{
if(sl == null) return GenerateKey32(p);
CkGkTaskInfo ti = new CkGkTaskInfo();
ThreadStart f = delegate()
{
if(ti == null) { Debug.Assert(false); return; }
try { ti.Key = GenerateKey32(p); }
catch(ThreadAbortException exAbort)
{
ti.Error = ((exAbort != null) ? exAbort.Message : null);
Thread.ResetAbort();
}
catch(Exception ex)
{
Debug.Assert(false);
ti.Error = ((ex != null) ? ex.Message : null);
}
};
Thread th = new Thread(f);
th.Start();
Debug.Assert(PwDefs.UIUpdateDelay >= 2);
while(!th.Join(PwDefs.UIUpdateDelay / 2))
{
if(!sl.ContinueWork())
{
try { th.Abort(); }
catch(Exception) { Debug.Assert(false); }
throw new OperationCanceledException();
}
}
if(!string.IsNullOrEmpty(ti.Error)) throw new Exception(ti.Error);
Debug.Assert(ti.Key != null);
return ti.Key;
}
private void ValidateUserKeys() private void ValidateUserKeys()
{ {
int nAccounts = 0; int nAccounts = 0;
@@ -280,14 +345,11 @@ namespace ModernKeePassLib.Keys
{ {
get get
{ {
return KLRes.InvalidCompositeKey + MessageService.NewParagraph + return (KLRes.InvalidCompositeKey + MessageService.NewParagraph +
KLRes.InvalidCompositeKeyHint; KLRes.InvalidCompositeKeyHint);
} }
} }
/// <summary>
/// Construct a new invalid composite key exception.
/// </summary>
public InvalidCompositeKeyException() public InvalidCompositeKeyException()
{ {
} }

View File

@@ -67,14 +67,13 @@ namespace ModernKeePassLib.Keys
{ {
get { return m_pbKeyData; } get { return m_pbKeyData; }
} }
#if ModernKeePassLib #if ModernKeePassLib
public KcpKeyFile(StorageFile strKeyFile) public KcpKeyFile(StorageFile keyFile)
{ {
Construct(IOConnectionInfo.FromFile(strKeyFile), false); Construct(IOConnectionInfo.FromStorageFile(keyFile), false);
} }
#else #else
public KcpKeyFile(string strKeyFile) public KcpKeyFile(string strKeyFile)
{ {
Construct(IOConnectionInfo.FromPath(strKeyFile), false); Construct(IOConnectionInfo.FromPath(strKeyFile), false);
} }
@@ -183,19 +182,19 @@ namespace ModernKeePassLib.Keys
return null; return null;
} }
/// <summary> /// <summary>
/// Create a new, random key-file. /// Create a new, random key-file.
/// </summary> /// </summary>
/// <param name="strFilePath">Path where the key-file should be saved to. /// <param name="strFilePath">Path where the key-file should be saved to.
/// If the file exists already, it will be overwritten.</param> /// If the file exists already, it will be overwritten.</param>
/// <param name="pbAdditionalEntropy">Additional entropy used to generate /// <param name="pbAdditionalEntropy">Additional entropy used to generate
/// the random key. May be <c>null</c> (in this case only the KeePass-internal /// the random key. May be <c>null</c> (in this case only the KeePass-internal
/// random number generator is used).</param> /// random number generator is used).</param>
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns> /// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
#if ModernKeePassLib #if ModernKeePassLib
public static void Create(StorageFile strFilePath, byte[] pbAdditionalEntropy) public static void Create(StorageFile file, byte[] pbAdditionalEntropy)
#else #else
public static void Create(string strFilePath, byte[] pbAdditionalEntropy) public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
#endif #endif
{ {
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32); byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
@@ -215,7 +214,11 @@ namespace ModernKeePassLib.Keys
} }
} }
CreateXmlKeyFile(strFilePath, pbFinalKey32); #if ModernKeePassLib
CreateXmlKeyFile(file, pbFinalKey32);
#else
CreateXmlKeyFile(strFilePath, pbFinalKey32);
#endif
} }
// ================================================================ // ================================================================
@@ -276,19 +279,23 @@ namespace ModernKeePassLib.Keys
return pbKeyData; return pbKeyData;
} }
#if ModernKeePassLib #if ModernKeePassLib
private static void CreateXmlKeyFile(StorageFile strFile, byte[] pbKeyData) private static void CreateXmlKeyFile(StorageFile file, byte[] pbKeyData)
{
Debug.Assert(file != null);
if (file == null) throw new ArgumentNullException(nameof(file));
#else #else
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData) private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
#endif
{ {
Debug.Assert(strFile != null); Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile"); if(strFile == null) throw new ArgumentNullException("strFile");
#endif
Debug.Assert(pbKeyData != null); Debug.Assert(pbKeyData != null);
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData"); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
#if ModernKeePassLib #if ModernKeePassLib
IOConnectionInfo ioc = IOConnectionInfo.FromFile(strFile); var ioc = IOConnectionInfo.FromStorageFile(file);
#else #else
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
#endif #endif

View File

@@ -21,9 +21,9 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using ModernKeePassLib.Cryptography;
namespace ModernKeePassLib.Keys namespace ModernKeePassLib.Keys
{ {

View File

@@ -145,7 +145,7 @@ namespace ModernKeePassLib.Keys
public override byte[] GetKey(KeyProviderQueryContext ctx) public override byte[] GetKey(KeyProviderQueryContext ctx)
{ {
return new byte[]{ 2, 3, 5, 7, 11, 13 }; return new byte[] { 2, 3, 5, 7, 11, 13 };
} }
} }
#endif #endif

View File

@@ -3,14 +3,14 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.41.1</Version> <Version>2.42.1</Version>
<Authors>Geoffroy Bonneville</Authors> <Authors>Geoffroy Bonneville</Authors>
<PackageLicenseUrl>https://www.gnu.org/licenses/gpl-3.0.en.html</PackageLicenseUrl> <PackageLicenseUrl>https://www.gnu.org/licenses/gpl-3.0.en.html</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/wismna/ModernKeePass</PackageProjectUrl> <PackageProjectUrl>https://github.com/wismna/ModernKeePass</PackageProjectUrl>
<Description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</Description> <Description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</Description>
<Company>wismna</Company> <Company>wismna</Company>
<Product>ModernKeePassLib</Product> <Product>ModernKeePassLib</Product>
<PackageReleaseNotes>Allow opening Storage File from FutureAccessList with a token</PackageReleaseNotes> <PackageReleaseNotes>Update to version 2.42.1</PackageReleaseNotes>
<PackageTags>KeePass KeePassLib Portable PCL NetStandard ModernKeePass</PackageTags> <PackageTags>KeePass KeePassLib Portable PCL NetStandard ModernKeePass</PackageTags>
<Copyright>Copyright © 2019 Geoffroy Bonneville</Copyright> <Copyright>Copyright © 2019 Geoffroy Bonneville</Copyright>
</PropertyGroup> </PropertyGroup>

View File

@@ -22,6 +22,12 @@ namespace ModernKeePassLib.Native
{ {
return Environment.OSVersion.Platform; return Environment.OSVersion.Platform;
} }
internal static string DecodeArgsToPath(string strApp)
{
if (!string.IsNullOrEmpty(strApp)) return strApp;
return string.Empty;
}
} }
internal static class NativeMethods internal static class NativeMethods

View File

@@ -234,16 +234,16 @@ namespace ModernKeePassLib.Native
{ {
ProcessStartInfo psi = new ProcessStartInfo(); ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = EncodePath(strAppPath);
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
psi.CreateNoWindow = true; psi.CreateNoWindow = true;
psi.FileName = strAppPath;
psi.WindowStyle = ProcessWindowStyle.Hidden; psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = false; psi.UseShellExecute = false;
psi.RedirectStandardOutput = bStdOut; psi.RedirectStandardOutput = bStdOut;
if(strStdInput != null) psi.RedirectStandardInput = true; if(strStdInput != null) psi.RedirectStandardInput = true;
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
Process p = Process.Start(psi); Process p = Process.Start(psi);
pToDispose = p; pToDispose = p;
@@ -291,7 +291,7 @@ namespace ModernKeePassLib.Native
#if !ModernKeePassLib #if !ModernKeePassLib
if((f & AppRunFlags.DoEvents) != AppRunFlags.None) if((f & AppRunFlags.DoEvents) != AppRunFlags.None)
{ {
List<Form> lDisabledForms = new List<Form>(); List<Form> lDisabledForms = new List<Form>();
if((f & AppRunFlags.DisableForms) != AppRunFlags.None) if((f & AppRunFlags.DisableForms) != AppRunFlags.None)
{ {
foreach(Form form in Application.OpenForms) foreach(Form form in Application.OpenForms)
@@ -456,5 +456,76 @@ namespace ModernKeePassLib.Native
// https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs // https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs
return Type.GetType(strType + ", Windows, ContentType=WindowsRuntime", false); return Type.GetType(strType + ", Windows, ContentType=WindowsRuntime", false);
} }
internal static string EncodeDataToArgs(string strData)
{
if(strData == null) { Debug.Assert(false); return string.Empty; }
// Cf. EncodePath and DecodeArgsToPath
if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
{
string str = strData;
str = str.Replace("\\", "\\\\");
str = str.Replace("\"", "\\\"");
// Whether '\'' needs to be encoded depends on the context
// (e.g. surrounding quotes); as we do not know what the
// caller does with the returned string, we assume that
// it will be used in a context where '\'' must not be
// encoded; this behavior is documented
// str = str.Replace("\'", "\\\'");
return str;
}
// See SHELLEXECUTEINFO structure documentation
return strData.Replace("\"", "\"\"\"");
}
/// <summary>
/// Encode a path for <c>Process.Start</c>.
/// </summary>
internal static string EncodePath(string strPath)
{
if(strPath == null) { Debug.Assert(false); return string.Empty; }
// Cf. EncodeDataToArgs and DecodeArgsToPath
if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
{
string str = strPath;
str = str.Replace("\\", "\\\\");
str = str.Replace("\"", "\\\"");
// '\'' must not be encoded in paths (only in args)
// str = str.Replace("\'", "\\\'");
return str;
}
return strPath; // '\"' is not allowed in paths on Windows
}
/// <summary>
/// Decode command line arguments to a path for <c>Process.Start</c>.
/// </summary>
internal static string DecodeArgsToPath(string strArgs)
{
if(strArgs == null) { Debug.Assert(false); return string.Empty; }
string str = strArgs;
// Cf. EncodeDataToArgs and EncodePath
// if(MonoWorkarounds.IsRequired(3471228285U) && IsUnix())
// {
// string strPlh = Guid.NewGuid().ToString();
// str = str.Replace("\\\\", strPlh);
// str = str.Replace("\\\'", "\'");
// str = str.Replace(strPlh, "\\\\"); // Restore
// }
return str; // '\"' is not allowed in paths on Windows
}
} }
} }

View File

@@ -39,6 +39,9 @@ namespace ModernKeePassLib.Native
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000; internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64; internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
internal static readonly Guid FOLDERID_SkyDrive = new Guid(
"A52BBA46-E9E1-435F-B3D9-28DAA648C0F6");
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001; // internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
// internal const uint TF_SFT_HIDDEN = 0x00000008; // internal const uint TF_SFT_HIDDEN = 0x00000008;
@@ -179,6 +182,10 @@ namespace ModernKeePassLib.Native
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData, string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
UInt32 dwFlags, IntPtr hTransaction); UInt32 dwFlags, IntPtr hTransaction);
[DllImport("Shell32.dll")]
private static extern int SHGetKnownFolderPath(ref Guid rfid, uint dwFlags,
IntPtr hToken, out IntPtr ppszPath);
#if (!KeePassLibSD && !KeePassUAP) #if (!KeePassLibSD && !KeePassUAP)
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)] [DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
@@ -256,5 +263,29 @@ namespace ModernKeePassLib.Native
return strRtDir; return strRtDir;
#endif #endif
} }
internal static string GetKnownFolderPath(Guid g)
{
if(Marshal.SystemDefaultCharSize != 2) { Debug.Assert(false); return string.Empty; }
IntPtr pszPath = IntPtr.Zero;
try
{
if(SHGetKnownFolderPath(ref g, 0, IntPtr.Zero, out pszPath) == 0)
{
if(pszPath != IntPtr.Zero)
return Marshal.PtrToStringUni(pszPath);
else { Debug.Assert(false); }
}
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { if(pszPath != IntPtr.Zero) Marshal.FreeCoTaskMem(pszPath); }
catch(Exception) { Debug.Assert(false); }
}
return string.Empty;
}
} }
} }

View File

@@ -21,10 +21,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Drawing;
#if ModernKeePassLib #if !KeePassUAP
using Windows.UI.Xaml.Controls; using System.Drawing;
#endif #endif
using ModernKeePassLib.Collections; using ModernKeePassLib.Collections;
@@ -132,7 +131,7 @@ namespace ModernKeePassLib
} }
/// <summary> /// <summary>
/// <c>IOConnection</c> of the currently opened database file. /// <c>IOConnection</c> of the currently open database file.
/// Is never <c>null</c>. /// Is never <c>null</c>.
/// </summary> /// </summary>
public IOConnectionInfo IOConnectionInfo public IOConnectionInfo IOConnectionInfo
@@ -660,8 +659,8 @@ namespace ModernKeePassLib
} }
/// <summary> /// <summary>
/// Save the currently opened database. The file is written to the location /// Save the currently open database. The file is written to the
/// it has been opened from. /// location it has been opened from.
/// </summary> /// </summary>
/// <param name="slLogger">Logger that recieves status information.</param> /// <param name="slLogger">Logger that recieves status information.</param>
public void Save(IStatusLogger slLogger) public void Save(IStatusLogger slLogger)
@@ -695,16 +694,16 @@ namespace ModernKeePassLib
} }
/// <summary> /// <summary>
/// Save the currently opened database to a different location. If /// Save the currently open database to a different location. If
/// <paramref name="bIsPrimaryNow" /> is <c>true</c>, the specified /// <paramref name="bIsPrimaryNow" /> is <c>true</c>, the specified
/// location is made the default location for future saves /// location is made the default location for future saves
/// using <c>SaveDatabase</c>. /// using <c>SaveDatabase</c>.
/// </summary> /// </summary>
/// <param name="ioConnection">New location to serialize the database to.</param> /// <param name="ioConnection">New location to serialize the database to.</param>
/// <param name="bIsPrimaryNow">If <c>true</c>, the new location is made the /// <param name="bIsPrimaryNow">If <c>true</c>, the new location is made
/// standard location for the database. If <c>false</c>, a copy of the currently /// the standard location for the database. If <c>false</c>, a copy of the
/// opened database is saved to the specified location, but it isn't /// currently open database is saved to the specified location, but it
/// made the default location (i.e. no lock files will be moved for /// isn't made the default location (i.e. no lock files will be moved for
/// example).</param> /// example).</param>
/// <param name="slLogger">Logger that recieves status information.</param> /// <param name="slLogger">Logger that recieves status information.</param>
public void SaveAs(IOConnectionInfo ioConnection, bool bIsPrimaryNow, public void SaveAs(IOConnectionInfo ioConnection, bool bIsPrimaryNow,
@@ -736,8 +735,8 @@ namespace ModernKeePassLib
} }
/// <summary> /// <summary>
/// Closes the currently opened database. No confirmation message is shown /// Closes the currently open database. No confirmation message
/// before closing. Unsaved changes will be lost. /// is shown before closing. Unsaved changes will be lost.
/// </summary> /// </summary>
public void Close() public void Close()
{ {
@@ -801,6 +800,12 @@ namespace ModernKeePassLib
pgNew.Uuid = pg.Uuid; pgNew.Uuid = pg.Uuid;
pgNew.AssignProperties(pg, false, true); pgNew.AssignProperties(pg, false, true);
if(!pgLocalContainer.CanAddGroup(pgNew))
{
Debug.Assert(false);
pgLocalContainer = m_pgRootGroup;
pgLocalContainer.CheckCanAddGroup(pgNew);
}
// pgLocalContainer.AddGroup(pgNew, true); // pgLocalContainer.AddGroup(pgNew, true);
InsertObjectAtBestPos<PwGroup>(pgLocalContainer.Groups, pgNew, ppSrc); InsertObjectAtBestPos<PwGroup>(pgLocalContainer.Groups, pgNew, ppSrc);
pgNew.ParentGroup = pgLocalContainer; pgNew.ParentGroup = pgLocalContainer;
@@ -1087,8 +1092,8 @@ namespace ModernKeePassLib
if(pgLocal.IsContainedIn(pg)) continue; if(pgLocal.IsContainedIn(pg)) continue;
if(!pgLocal.CanAddGroup(pg)) { Debug.Assert(false); continue; }
pg.ParentGroup.Groups.Remove(pg); pg.ParentGroup.Groups.Remove(pg);
// pgLocal.AddGroup(pg, true); // pgLocal.AddGroup(pg, true);
InsertObjectAtBestPos<PwGroup>(pgLocal.Groups, pg, ppSrc); InsertObjectAtBestPos<PwGroup>(pgLocal.Groups, pg, ppSrc);
pg.ParentGroup = pgLocal; pg.ParentGroup = pgLocal;
@@ -1643,7 +1648,7 @@ namespace ModernKeePassLib
return null; return null;
} }
#elif !KeePassLibSD #elif !KeePassLibSD && !ModernKeePassLib
[Obsolete("Additionally specify the size.")] [Obsolete("Additionally specify the size.")]
public Image GetCustomIcon(PwUuid pwIconId) public Image GetCustomIcon(PwUuid pwIconId)
{ {

View File

@@ -55,18 +55,18 @@ namespace ModernKeePassLib
/// e.g. 2.19 = 0x02130000. /// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead. /// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary> /// </summary>
public static readonly uint Version32 = 0x02290000; public static readonly uint Version32 = 0x022A0100;
/// <summary> /// <summary>
/// Version, encoded as 64-bit unsigned integer /// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component). /// (component-wise, 16 bits per component).
/// </summary> /// </summary>
public static readonly ulong FileVersion64 = 0x0002002900000000UL; public static readonly ulong FileVersion64 = 0x0002002A00010000UL;
/// <summary> /// <summary>
/// Version, encoded as string. /// Version, encoded as string.
/// </summary> /// </summary>
public static readonly string VersionString = "2.41"; public static readonly string VersionString = "2.42.1";
public static readonly string Copyright = @"Copyright © 2003-2019 Dominik Reichl"; public static readonly string Copyright = @"Copyright © 2003-2019 Dominik Reichl";
@@ -181,6 +181,12 @@ namespace ModernKeePassLib
/// </summary> /// </summary>
public static readonly string DefaultAutoTypeSequenceTan = @"{PASSWORD}"; public static readonly string DefaultAutoTypeSequenceTan = @"{PASSWORD}";
/// <summary>
/// Maximum time (in milliseconds) after which the user interface
/// should be updated.
/// </summary>
internal const int UIUpdateDelay = 50;
/// <summary> /// <summary>
/// Check if a name is a standard field name. /// Check if a name is a standard field name.
/// </summary> /// </summary>

View File

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

View File

@@ -25,19 +25,24 @@ using System.Text.RegularExpressions;
using ModernKeePassLib.Collections; using ModernKeePassLib.Collections;
using ModernKeePassLib.Delegates; using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
namespace ModernKeePassLib namespace ModernKeePassLib
{ {
/// <summary> /// <summary>
/// A group containing several password entries. /// A group containing subgroups and entries.
/// </summary> /// </summary>
public sealed class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<PwGroup> public sealed class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<PwGroup>
{ {
public const bool DefaultAutoTypeEnabled = true; public const bool DefaultAutoTypeEnabled = true;
public const bool DefaultSearchingEnabled = true; public const bool DefaultSearchingEnabled = true;
// In the tree view of Windows 10, the X coordinate is reset
// to 0 after 256 nested nodes
private const uint MaxDepth = 126; // Depth 126 = level 127 < 256/2
private PwObjectList<PwGroup> m_listGroups = new PwObjectList<PwGroup>(); private PwObjectList<PwGroup> m_listGroups = new PwObjectList<PwGroup>();
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>(); private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
private PwGroup m_pParentGroup = null; private PwGroup m_pParentGroup = null;
@@ -139,7 +144,8 @@ namespace ModernKeePassLib
{ {
get { return m_pParentGroup; } get { return m_pParentGroup; }
// Plugins: use <c>PwGroup.AddGroup</c> instead. // Plugins: use the PwGroup.AddGroup method instead.
// Internal: check depth using CanAddGroup/CheckCanAddGroup.
internal set { Debug.Assert(value != this); m_pParentGroup = value; } internal set { Debug.Assert(value != this); m_pParentGroup = value; }
} }
@@ -1244,7 +1250,7 @@ namespace ModernKeePassLib
PwGroup pg = m_pParentGroup; PwGroup pg = m_pParentGroup;
while(pg != null) while(pg != null)
{ {
if((!bIncludeTopMostGroup) && (pg.m_pParentGroup == null)) if(!bIncludeTopMostGroup && (pg.m_pParentGroup == null))
break; break;
strPath = pg.Name + strSeparator + strPath; strPath = pg.Name + strSeparator + strPath;
@@ -1367,21 +1373,34 @@ namespace ModernKeePassLib
#endif #endif
/// <summary> /// <summary>
/// Get the level of the group (i.e. the number of parent groups). /// Get the depth of this group (i.e. the number of ancestors).
/// </summary> /// </summary>
/// <returns>Number of parent groups.</returns> /// <returns>Depth of this group.</returns>
public uint GetLevel() public uint GetDepth()
{ {
PwGroup pg = m_pParentGroup; PwGroup pg = m_pParentGroup;
uint uLevel = 0; uint d = 0;
while(pg != null) while(pg != null)
{ {
pg = pg.ParentGroup; pg = pg.m_pParentGroup;
++uLevel; ++d;
} }
return uLevel; return d;
}
private uint GetHeight()
{
if(m_listGroups.UCount == 0) return 0;
uint h = 0;
foreach(PwGroup pgSub in m_listGroups)
{
h = Math.Max(h, pgSub.GetHeight());
}
return (h + 1);
} }
public string GetAutoTypeSequenceInherited() public string GetAutoTypeSequenceInherited()
@@ -1520,13 +1539,31 @@ namespace ModernKeePassLib
{ {
if(subGroup == null) throw new ArgumentNullException("subGroup"); if(subGroup == null) throw new ArgumentNullException("subGroup");
CheckCanAddGroup(subGroup);
m_listGroups.Add(subGroup); m_listGroups.Add(subGroup);
if(bTakeOwnership) subGroup.m_pParentGroup = this; if(bTakeOwnership) subGroup.ParentGroup = this;
if(bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.UtcNow; if(bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.UtcNow;
} }
internal bool CanAddGroup(PwGroup pgSub)
{
if(pgSub == null) { Debug.Assert(false); return false; }
uint dCur = GetDepth(), hSub = pgSub.GetHeight();
return ((dCur + hSub + 1) <= MaxDepth);
}
internal void CheckCanAddGroup(PwGroup pgSub)
{
if(!CanAddGroup(pgSub))
{
Debug.Assert(false);
throw new InvalidOperationException(KLRes.StructsTooDeep);
}
}
/// <summary> /// <summary>
/// Add an entry to this group. /// Add an entry to this group.
/// </summary> /// </summary>

View File

@@ -59,6 +59,7 @@ namespace ModernKeePassLib.Resources
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat); m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive); m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth); m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strStructsTooDeep = TryGetEx(dictNew, "StructsTooDeep", m_strStructsTooDeep);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout); m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs); m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId); m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
@@ -101,6 +102,7 @@ namespace ModernKeePassLib.Resources
"OldFormat", "OldFormat",
"Passive", "Passive",
"PreAuth", "PreAuth",
"StructsTooDeep",
"Timeout", "Timeout",
"TryAgainSecs", "TryAgainSecs",
"UnknownHeaderId", "UnknownHeaderId",
@@ -346,10 +348,10 @@ namespace ModernKeePassLib.Resources
} }
private static string m_strFrameworkNotImplExcp = private static string m_strFrameworkNotImplExcp =
@"The .NET framework/runtime under which KeePass is currently running does not support this operation."; @"The .NET Framework/runtime under which KeePass is currently running does not support this operation.";
/// <summary> /// <summary>
/// Look up a localized string similar to /// Look up a localized string similar to
/// 'The .NET framework/runtime under which KeePass is currently running does not support this operation.'. /// 'The .NET Framework/runtime under which KeePass is currently running does not support this operation.'.
/// </summary> /// </summary>
public static string FrameworkNotImplExcp public static string FrameworkNotImplExcp
{ {
@@ -477,6 +479,17 @@ namespace ModernKeePassLib.Resources
get { return m_strPreAuth; } get { return m_strPreAuth; }
} }
private static string m_strStructsTooDeep =
@"Structures are nested too deeply.";
/// <summary>
/// Look up a localized string similar to
/// 'Structures are nested too deeply.'.
/// </summary>
public static string StructsTooDeep
{
get { return m_strStructsTooDeep; }
}
private static string m_strTimeout = private static string m_strTimeout =
@"Timeout"; @"Timeout";
/// <summary> /// <summary>

View File

@@ -9,7 +9,7 @@ namespace ModernKeePassLib.Resources
/// <summary> /// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc. /// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary> /// </summary>
public static class KSRes public static partial class KSRes
{ {
private static string TryGetEx(Dictionary<string, string> dictNew, private static string TryGetEx(Dictionary<string, string> dictNew,
string strName, string strDefault) string strName, string strDefault)

View File

@@ -23,18 +23,19 @@ using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Windows.Storage.AccessCache;
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
using System.Security.AccessControl; using System.Security.AccessControl;
#endif #endif
#if ModernKeePassLib
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.Storage.Streams; #endif
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Native;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
@@ -210,10 +211,10 @@ namespace ModernKeePassLib.Serialization
// trying to set 'Owner' or 'Group' can result in an // trying to set 'Owner' or 'Group' can result in an
// UnauthorizedAccessException; thus we restore 'Access' (DACL) only // UnauthorizedAccessException; thus we restore 'Access' (DACL) only
const AccessControlSections acs = AccessControlSections.Access; const AccessControlSections acs = AccessControlSections.Access;
#endif #endif
bool bEfsEncrypted = false; bool bEfsEncrypted = false;
byte[] pbSec = null; byte[] pbSec = null;
DateTime? otCreation = null; DateTime? otCreation = null;
bool bBaseExists = IOConnection.FileExists(m_iocBase); bool bBaseExists = IOConnection.FileExists(m_iocBase);
@@ -228,9 +229,7 @@ namespace ModernKeePassLib.Serialization
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
#endif #endif
#if ModernKeePassLib #if !ModernKeePassLib
otCreation = m_iocBase.StorageFile.DateCreated.UtcDateTime;
#else
otCreation = File.GetCreationTimeUtc(m_iocBase.Path); otCreation = File.GetCreationTimeUtc(m_iocBase.Path);
#endif #endif
#if !ModernKeePassLib #if !ModernKeePassLib
@@ -238,7 +237,7 @@ namespace ModernKeePassLib.Serialization
FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs); FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs);
if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm(); if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm();
#endif #endif
} }
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); } catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
// if((long)(faBase & FileAttributes.ReadOnly) != 0) // if((long)(faBase & FileAttributes.ReadOnly) != 0)
@@ -337,6 +336,7 @@ namespace ModernKeePassLib.Serialization
{ {
if(NativeLib.IsUnix()) return; if(NativeLib.IsUnix()) return;
if(!m_iocBase.IsLocalFile()) return; if(!m_iocBase.IsLocalFile()) return;
if(IsOneDriveWorkaroundRequired()) return;
string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String( string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String(
CryptoRandom.Instance.GetRandomBytes(16))); CryptoRandom.Instance.GetRandomBytes(16)));
@@ -354,7 +354,7 @@ namespace ModernKeePassLib.Serialization
#if ModernKeePassLib #if ModernKeePassLib
var tempFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter() var tempFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
.GetResult(); .GetResult();
m_iocTemp = IOConnectionInfo.FromFile(tempFile); m_iocTemp = IOConnectionInfo.FromStorageFile(tempFile);
#else #else
m_iocTemp = IOConnectionInfo.FromPath(strTemp); m_iocTemp = IOConnectionInfo.FromPath(strTemp);
#endif #endif
@@ -370,13 +370,10 @@ namespace ModernKeePassLib.Serialization
if(TxfMoveWithTx()) return true; if(TxfMoveWithTx()) return true;
// Move the temporary file onto the base file's drive first, // Move the temporary file onto the base file's drive first,
// such that it cannot happen that both the base file and // such that it cannot happen that both the base file and
// the temporary file are deleted/corrupted // the temporary file are deleted/corrupted
#if ModernKeePassLib #if !ModernKeePassLib
m_iocTemp.StorageFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
.GetResult();
#else
const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED | const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED |
NativeMethods.MOVEFILE_REPLACE_EXISTING); NativeMethods.MOVEFILE_REPLACE_EXISTING);
bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f); bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f);
@@ -391,9 +388,7 @@ namespace ModernKeePassLib.Serialization
private bool TxfMoveWithTx() private bool TxfMoveWithTx()
{ {
#if ModernKeePassLib #if !ModernKeePassLib
return true;
#else
IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE); IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE);
Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE); Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE);
try try
@@ -438,9 +433,8 @@ namespace ModernKeePassLib.Serialization
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
} }
return false;
#endif #endif
return false;
} }
internal static void ClearOld() internal static void ClearOld()
@@ -470,5 +464,89 @@ namespace ModernKeePassLib.Serialization
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
// https://sourceforge.net/p/keepass/discussion/329220/thread/672ffecc65/
// https://sourceforge.net/p/keepass/discussion/329221/thread/514786c23a/
private bool IsOneDriveWorkaroundRequired()
{
#if !ModernKeePassLib
if(NativeLib.IsUnix()) return false;
try
{
string strReleaseId = (Registry.GetValue(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"ReleaseId", string.Empty) as string);
if(strReleaseId != "1809") return false;
string strFile = m_iocBase.Path;
GFunc<string, string, bool> fMatch = delegate(string strRoot, string strSfx)
{
if(string.IsNullOrEmpty(strRoot)) return false;
string strPfx = UrlUtil.EnsureTerminatingSeparator(
strRoot, false) + strSfx;
return strFile.StartsWith(strPfx, StrUtil.CaseIgnoreCmp);
};
GFunc<string, string, bool> fMatchEnv = delegate(string strEnv, string strSfx)
{
return fMatch(Environment.GetEnvironmentVariable(strEnv), strSfx);
};
string strKnown = NativeMethods.GetKnownFolderPath(
NativeMethods.FOLDERID_SkyDrive);
if(fMatch(strKnown, string.Empty)) return true;
if(fMatchEnv("USERPROFILE", "OneDrive\\")) return true;
if(fMatchEnv("OneDrive", string.Empty)) return true;
if(fMatchEnv("OneDriveCommercial", string.Empty)) return true;
if(fMatchEnv("OneDriveConsumer", string.Empty)) return true;
using(RegistryKey kAccs = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\OneDrive\\Accounts", false))
{
string[] vAccs = (((kAccs != null) ? kAccs.GetSubKeyNames() :
null) ?? new string[0]);
foreach(string strAcc in vAccs)
{
if(string.IsNullOrEmpty(strAcc)) { Debug.Assert(false); continue; }
using(RegistryKey kTenants = kAccs.OpenSubKey(
strAcc + "\\Tenants", false))
{
string[] vTenants = (((kTenants != null) ?
kTenants.GetSubKeyNames() : null) ?? new string[0]);
foreach(string strT in vTenants)
{
if(string.IsNullOrEmpty(strT)) { Debug.Assert(false); continue; }
using(RegistryKey kT = kTenants.OpenSubKey(strT, false))
{
string[] vPaths = (((kT != null) ?
kT.GetValueNames() : null) ?? new string[0]);
foreach(string strPath in vPaths)
{
if((strPath == null) || (strPath.Length < 4) ||
(strPath[1] != ':'))
{
Debug.Assert(false);
continue;
}
if(fMatch(strPath, string.Empty)) return true;
}
}
}
}
}
}
}
catch(Exception) { Debug.Assert(false); }
#endif
return false;
}
} }
} }

View File

@@ -24,7 +24,6 @@ using System.IO;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassUAP) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassUAP)
using System.Net.Cache; using System.Net.Cache;
using System.Net.Security; using System.Net.Security;
@@ -600,7 +599,12 @@ namespace ModernKeePassLib.Serialization
private static Stream OpenReadLocal(IOConnectionInfo ioc) private static Stream OpenReadLocal(IOConnectionInfo ioc)
{ {
#if ModernKeePassLib
return ioc.StorageFile.OpenAsync(FileAccessMode.Read).GetAwaiter().GetResult().AsStream(); return ioc.StorageFile.OpenAsync(FileAccessMode.Read).GetAwaiter().GetResult().AsStream();
#else
return new FileStream(ioc.Path, FileMode.Open, FileAccess.Read,
FileShare.Read);
#endif
} }
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
@@ -654,7 +658,7 @@ namespace ModernKeePassLib.Serialization
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists); RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
#if ModernKeePassLib #if ModernKeePassLib
return ioc.StorageFile.IsAvailable; return ioc.StorageFile != null;
#else #else
if(ioc.IsLocalFile()) return File.Exists(ioc.Path); if(ioc.IsLocalFile()) return File.Exists(ioc.Path);
@@ -696,7 +700,7 @@ namespace ModernKeePassLib.Serialization
#if ModernKeePassLib #if ModernKeePassLib
if (!ioc.IsLocalFile()) return; if (!ioc.IsLocalFile()) return;
ioc.StorageFile?.DeleteAsync().GetAwaiter().GetResult(); ioc.StorageFile?.DeleteAsync().GetAwaiter().GetResult();
#else #else
if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; } if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
@@ -718,7 +722,7 @@ namespace ModernKeePassLib.Serialization
} }
#endif #endif
#endif #endif
} }
/// <summary> /// <summary>
/// Rename/move a file. For local file system and WebDAV, the /// Rename/move a file. For local file system and WebDAV, the
@@ -735,7 +739,7 @@ namespace ModernKeePassLib.Serialization
#if ModernKeePassLib #if ModernKeePassLib
if (!iocFrom.IsLocalFile()) return; if (!iocFrom.IsLocalFile()) return;
iocFrom.StorageFile?.RenameAsync(iocTo.Path).GetAwaiter().GetResult(); iocFrom.StorageFile?.RenameAsync(iocTo.Path).GetAwaiter().GetResult();
#else #else
if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; } if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; }

View File

@@ -25,14 +25,12 @@ using System.IO;
using System.Text; using System.Text;
using System.Xml.Serialization; using System.Xml.Serialization;
#if ModernKeePassLib #if ModernKeePassLib
using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
//using PCLStorage;
#endif #endif
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using System.Threading.Tasks;
using Windows.Storage.AccessCache;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
@@ -70,6 +68,8 @@ namespace ModernKeePassLib.Serialization
{ {
// private IOFileFormatHint m_ioHint = IOFileFormatHint.None; // private IOFileFormatHint m_ioHint = IOFileFormatHint.None;
public StorageFile StorageFile { get; set; }
private string m_strUrl = string.Empty; private string m_strUrl = string.Empty;
public string Path public string Path
{ {
@@ -316,29 +316,17 @@ namespace ModernKeePassLib.Serialization
} }
#if ModernKeePassLib #if ModernKeePassLib
public static IOConnectionInfo FromFile(StorageFile file) public static IOConnectionInfo FromStorageFile(StorageFile file)
{ {
IOConnectionInfo ioc = new IOConnectionInfo(); IOConnectionInfo ioc = new IOConnectionInfo();
ioc.StorageFile = file; ioc.StorageFile = file;
ioc.Path = file.Path;
ioc.CredSaveMode = IOCredSaveMode.NoSave;
return ioc;
}
public static IOConnectionInfo FromPath(string strPath)
{
IOConnectionInfo ioc = new IOConnectionInfo();
ioc.Path = strPath;
ioc.StorageFile = StorageApplicationPermissions.FutureAccessList.GetFileAsync(strPath).GetAwaiter().GetResult();
ioc.CredSaveMode = IOCredSaveMode.NoSave; ioc.CredSaveMode = IOCredSaveMode.NoSave;
return ioc; return ioc;
} }
#else #else
public static IOConnectionInfo FromPath(string strPath) public static IOConnectionInfo FromPath(string strPath)
{ {
IOConnectionInfo ioc = new IOConnectionInfo(); IOConnectionInfo ioc = new IOConnectionInfo();
@@ -348,10 +336,6 @@ namespace ModernKeePassLib.Serialization
return ioc; return ioc;
} }
#endif #endif
public StorageFile StorageFile { get; set; }
public bool CanProbablyAccess() public bool CanProbablyAccess()
{ {
#if ModernKeePassLib #if ModernKeePassLib

View File

@@ -30,13 +30,14 @@ using System.Drawing;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Collections; using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
/// <summary> /// <summary>
@@ -97,36 +98,10 @@ namespace ModernKeePassLib.Serialization
private void ReadXmlStreamed(Stream sXml, Stream sParent) private void ReadXmlStreamed(Stream sXml, Stream sParent)
{ {
ReadDocumentStreamed(CreateXmlReader(sXml), sParent); using(XmlReader xr = XmlUtilEx.CreateXmlReader(sXml))
} {
ReadDocumentStreamed(xr, sParent);
internal static XmlReaderSettings CreateStdXmlReaderSettings() }
{
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.CloseInput = true;
xrs.IgnoreComments = true;
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true;
#if ModernKeePassLib || KeePassUAP
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
#if !KeePassLibSD
// Also see PrepMonoDev.sh script
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
#endif
xrs.ValidationType = ValidationType.None;
#endif
return xrs;
}
private static XmlReader CreateXmlReader(Stream readerStream)
{
XmlReaderSettings xrs = CreateStdXmlReaderSettings();
return XmlReader.Create(readerStream, xrs);
} }
private void ReadDocumentStreamed(XmlReader xr, Stream sParentStream) private void ReadDocumentStreamed(XmlReader xr, Stream sParentStream)
@@ -179,7 +154,7 @@ namespace ModernKeePassLib.Serialization
} }
++uTagCounter; ++uTagCounter;
if(((uTagCounter % 256) == 0) && bSupportsStatus) if(((uTagCounter & 0xFFU) == 0) && bSupportsStatus)
{ {
Debug.Assert(lStreamLength == sParentStream.Length); Debug.Assert(lStreamLength == sParentStream.Length);
uint uPct = (uint)((sParentStream.Position * 100) / uint uPct = (uint)((sParentStream.Position * 100) /
@@ -189,7 +164,8 @@ namespace ModernKeePassLib.Serialization
// position/length values (M120413) // position/length values (M120413)
if(uPct > 100) { Debug.Assert(false); uPct = 100; } if(uPct > 100) { Debug.Assert(false); uPct = 100; }
m_slLogger.SetProgress(uPct); if(!m_slLogger.SetProgress(uPct))
throw new OperationCanceledException();
} }
} }
@@ -1028,28 +1004,31 @@ namespace ModernKeePassLib.Serialization
private void ReadUnknown(XmlReader xr) private void ReadUnknown(XmlReader xr)
{ {
Debug.Assert(false); // Unknown node! Debug.Assert(false); // Unknown node!
Debug.Assert(xr.NodeType == XmlNodeType.Element);
if(xr.IsEmptyElement) { m_bReadNextNode = true; return; } bool bRead = false;
int cOpen = 0;
string strUnknownName = xr.Name; do
XorredBuffer xb = ProcessNode(xr);
if(xb != null) { xb.Dispose(); return; } // ProcessNode sets m_bReadNextNode
bool bRead = true;
while(true)
{ {
if(bRead) xr.Read(); if(bRead) xr.Read();
bRead = true;
if(xr.NodeType == XmlNodeType.EndElement) break; if(xr.NodeType == XmlNodeType.EndElement) --cOpen;
if(xr.NodeType != XmlNodeType.Element) { bRead = true; continue; } else if(xr.NodeType == XmlNodeType.Element)
{
if(!xr.IsEmptyElement)
{
XorredBuffer xb = ProcessNode(xr);
if(xb != null) { xb.Dispose(); bRead = m_bReadNextNode; continue; }
ReadUnknown(xr); ++cOpen;
bRead = m_bReadNextNode; }
}
} }
while(cOpen > 0);
Debug.Assert(xr.Name == strUnknownName); // On end tag m_bReadNextNode = bRead;
m_bReadNextNode = true;
} }
private XorredBuffer ProcessNode(XmlReader xr) private XorredBuffer ProcessNode(XmlReader xr)

View File

@@ -55,26 +55,23 @@ namespace ModernKeePassLib.Serialization
/// </summary> /// </summary>
public sealed partial class KdbxFile public sealed partial class KdbxFile
{ {
/// <summary>
/// Load a KDBX file.
/// </summary>
/// <param name="strFilePath">File to load.</param>
/// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param>
#if ModernKeePassLib #if ModernKeePassLib
public void Load(StorageFile file, KdbxFormat fmt, IStatusLogger slLogger) public void Load(StorageFile file, KdbxFormat fmt, IStatusLogger slLogger)
{ {
IOConnectionInfo ioc = IOConnectionInfo.FromFile(file); IOConnectionInfo ioc = IOConnectionInfo.FromStorageFile(file);
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
}
#else #else
/// <summary> public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
/// Load a KDBX file.
/// </summary>
/// <param name="strFilePath">File to load.</param>
/// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param>
public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
{ {
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
#endif
Load(IOConnection.OpenRead(ioc), fmt, slLogger); Load(IOConnection.OpenRead(ioc), fmt, slLogger);
} }
#endif
/// <summary> /// <summary>
/// Load a KDBX file from a stream. /// Load a KDBX file from a stream.
@@ -202,19 +199,17 @@ namespace ModernKeePassLib.Serialization
} }
#if KeePassDebug_WriteXml #if KeePassDebug_WriteXml
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create, #warning XML output is enabled!
// FileAccess.Write, FileShare.None); /* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
// try FileAccess.Write, FileShare.None))
// { {
// while(true) while(true)
// { {
// int b = sXml.ReadByte(); int b = sXml.ReadByte();
// if(b == -1) break; if(b == -1) throw new EndOfStreamException();
// fsOut.WriteByte((byte)b); fsOut.WriteByte((byte)b);
// } }
// } } */
// catch(Exception) { }
// fsOut.Close();
#endif #endif
ReadXmlStreamed(sXml, sHashing); ReadXmlStreamed(sXml, sHashing);
@@ -413,7 +408,7 @@ namespace ModernKeePassLib.Serialization
default: default:
Debug.Assert(false); Debug.Assert(false);
if(m_slLogger != null) if(m_slLogger != null)
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " + m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
kdbID.ToString() + "!", LogStatusType.Warning); kdbID.ToString() + "!", LogStatusType.Warning);
break; break;
} }

View File

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

View File

@@ -378,8 +378,8 @@ namespace ModernKeePassLib.Serialization
Debug.Assert(m_pwDatabase != null); Debug.Assert(m_pwDatabase != null);
Debug.Assert(m_pwDatabase.MasterKey != null); Debug.Assert(m_pwDatabase.MasterKey != null);
ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32( ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32Ex(
m_pwDatabase.KdfParameters); m_pwDatabase.KdfParameters, m_slLogger);
Debug.Assert(pbinUser != null); Debug.Assert(pbinUser != null);
if(pbinUser == null) if(pbinUser == null)
throw new SecurityException(KLRes.InvalidCompositeKey); throw new SecurityException(KLRes.InvalidCompositeKey);
@@ -492,7 +492,7 @@ namespace ModernKeePassLib.Serialization
{ {
if(pb == null) { Debug.Assert(false); return; } if(pb == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(strName)) strName = "File.bin"; strName = UrlUtil.GetSafeFileName(strName);
string strPath; string strPath;
int iTry = 1; int iTry = 1;
@@ -500,8 +500,8 @@ namespace ModernKeePassLib.Serialization
{ {
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false); strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
string strExt = UrlUtil.GetExtension(strName);
string strDesc = UrlUtil.StripExtension(strName); string strDesc = UrlUtil.StripExtension(strName);
string strExt = UrlUtil.GetExtension(strName);
strPath += strDesc; strPath += strDesc;
if(iTry > 1) if(iTry > 1)

View File

@@ -46,6 +46,7 @@ namespace ModernKeePassLib.Translation
public sealed class KPTranslation public sealed class KPTranslation
{ {
public static readonly string FileExtension = "lngx"; public static readonly string FileExtension = "lngx";
internal const string FileExtension1x = "lng";
private KPTranslationProperties m_props = new KPTranslationProperties(); private KPTranslationProperties m_props = new KPTranslationProperties();
public KPTranslationProperties Properties public KPTranslationProperties Properties

View File

@@ -264,8 +264,7 @@ namespace ModernKeePassLib.Utility
[MethodImpl(MioNoOptimize)] [MethodImpl(MioNoOptimize)]
public static void ZeroByteArray(byte[] pbArray) public static void ZeroByteArray(byte[] pbArray)
{ {
Debug.Assert(pbArray != null); if(pbArray == null) { Debug.Assert(false); return; }
if(pbArray == null) throw new ArgumentNullException("pbArray");
Array.Clear(pbArray, 0, pbArray.Length); Array.Clear(pbArray, 0, pbArray.Length);
} }
@@ -277,7 +276,7 @@ namespace ModernKeePassLib.Utility
[MethodImpl(MioNoOptimize)] [MethodImpl(MioNoOptimize)]
public static void ZeroArray<T>(T[] v) public static void ZeroArray<T>(T[] v)
{ {
if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); } if(v == null) { Debug.Assert(false); return; }
Array.Clear(v, 0, v.Length); Array.Clear(v, 0, v.Length);
} }

View File

@@ -116,9 +116,6 @@ namespace ModernKeePassLib.Utility
// 1760: // 1760:
// Input focus is not restored when activating a form. // Input focus is not restored when activating a form.
// https://sourceforge.net/p/keepass/bugs/1760/ // https://sourceforge.net/p/keepass/bugs/1760/
// 2139:
// Shortcut keys are ignored.
// https://sourceforge.net/p/keepass/feature-requests/2139/
// 2140: // 2140:
// Explicit control focusing is ignored. // Explicit control focusing is ignored.
// https://sourceforge.net/p/keepass/feature-requests/2140/ // https://sourceforge.net/p/keepass/feature-requests/2140/

View File

@@ -24,7 +24,6 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
#if !KeePassUAP #if !KeePassUAP
using System.Drawing; using System.Drawing;
@@ -44,45 +43,41 @@ namespace ModernKeePassLib.Utility
/// </summary> /// </summary>
public sealed class CharStream public sealed class CharStream
{ {
private string m_strString = string.Empty; private readonly string m_str;
private int m_nPos = 0; private int m_iPos = 0;
public long Position
{
get { return m_iPos; }
set
{
if((value < 0) || (value > int.MaxValue))
throw new ArgumentOutOfRangeException("value");
m_iPos = (int)value;
}
}
public CharStream(string str) public CharStream(string str)
{ {
Debug.Assert(str != null); if(str == null) { Debug.Assert(false); throw new ArgumentNullException("str"); }
if(str == null) throw new ArgumentNullException("str");
m_strString = str; m_str = str;
}
public void Seek(SeekOrigin org, int nSeek)
{
if(org == SeekOrigin.Begin)
m_nPos = nSeek;
else if(org == SeekOrigin.Current)
m_nPos += nSeek;
else if(org == SeekOrigin.End)
m_nPos = m_strString.Length + nSeek;
} }
public char ReadChar() public char ReadChar()
{ {
if(m_nPos < 0) return char.MinValue; if(m_iPos >= m_str.Length) return char.MinValue;
if(m_nPos >= m_strString.Length) return char.MinValue;
char chRet = m_strString[m_nPos]; return m_str[m_iPos++];
++m_nPos;
return chRet;
} }
public char ReadChar(bool bSkipWhiteSpace) public char ReadChar(bool bSkipWhiteSpace)
{ {
if(bSkipWhiteSpace == false) return ReadChar(); if(!bSkipWhiteSpace) return ReadChar();
while(true) while(true)
{ {
char ch = ReadChar(); char ch = ReadChar();
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n')) if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
return ch; return ch;
} }
@@ -90,29 +85,25 @@ namespace ModernKeePassLib.Utility
public char PeekChar() public char PeekChar()
{ {
if(m_nPos < 0) return char.MinValue; if(m_iPos >= m_str.Length) return char.MinValue;
if(m_nPos >= m_strString.Length) return char.MinValue;
return m_strString[m_nPos]; return m_str[m_iPos];
} }
public char PeekChar(bool bSkipWhiteSpace) public char PeekChar(bool bSkipWhiteSpace)
{ {
if(bSkipWhiteSpace == false) return PeekChar(); if(!bSkipWhiteSpace) return PeekChar();
int iIndex = m_nPos; int i = m_iPos;
while(true) while(i < m_str.Length)
{ {
if(iIndex < 0) return char.MinValue; char ch = m_str[i];
if(iIndex >= m_strString.Length) return char.MinValue;
char ch = m_strString[iIndex];
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n')) if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
return ch; return ch;
++i;
++iIndex;
} }
return char.MinValue;
} }
} }
@@ -405,44 +396,46 @@ namespace ModernKeePassLib.Utility
return str; return str;
} }
/// <summary> public static void SplitCommandLine(string strCmdLine, out string strApp,
/// Split up a command line into application and argument. out string strArgs)
/// </summary>
/// <param name="strCmdLine">Command line to split.</param>
/// <param name="strApp">Application path.</param>
/// <param name="strArgs">Arguments.</param>
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
{ {
Debug.Assert(strCmdLine != null); if(strCmdLine == null) throw new ArgumentNullException("strCmdLine"); SplitCommandLine(strCmdLine, out strApp, out strArgs, true);
}
internal static void SplitCommandLine(string strCmdLine, out string strApp,
out string strArgs, bool bDecodeAppToPath)
{
if(strCmdLine == null) { Debug.Assert(false); throw new ArgumentNullException("strCmdLine"); }
string str = strCmdLine.Trim(); string str = strCmdLine.Trim();
strApp = null;
strApp = null; strArgs = null; strArgs = null;
if(str.StartsWith("\"")) if(str.StartsWith("\""))
{ {
int nSecond = str.IndexOf('\"', 1); int iSecond = UrlUtil.IndexOfSecondEnclQuote(str);
if(nSecond >= 1) if(iSecond >= 1)
{ {
strApp = str.Substring(1, nSecond - 1).Trim(); strApp = str.Substring(1, iSecond - 1).Trim();
strArgs = str.Remove(0, nSecond + 1).Trim(); strArgs = str.Remove(0, iSecond + 1).Trim();
} }
} }
if(strApp == null) if(strApp == null)
{ {
int nSpace = str.IndexOf(' '); int iSpace = str.IndexOf(' ');
if(iSpace >= 0)
if(nSpace >= 0)
{ {
strApp = str.Substring(0, nSpace); strApp = str.Substring(0, iSpace).Trim();
strArgs = str.Remove(0, nSpace).Trim(); strArgs = str.Remove(0, iSpace + 1).Trim();
} }
else strApp = strCmdLine; else strApp = str;
} }
if(strApp == null) strApp = string.Empty; if(strApp == null) { Debug.Assert(false); strApp = string.Empty; }
if(strArgs == null) strArgs = string.Empty; if(strArgs == null) strArgs = string.Empty;
if(bDecodeAppToPath) strApp = NativeLib.DecodeArgsToPath(strApp);
} }
// /// <summary> // /// <summary>
@@ -1289,7 +1282,7 @@ namespace ModernKeePassLib.Utility
if(uBytes <= uGB) return (((uBytes - 1UL) / uMB) + 1UL).ToString() + " MB"; if(uBytes <= uGB) return (((uBytes - 1UL) / uMB) + 1UL).ToString() + " MB";
if(uBytes <= uTB) return (((uBytes - 1UL) / uGB) + 1UL).ToString() + " GB"; if(uBytes <= uTB) return (((uBytes - 1UL) / uGB) + 1UL).ToString() + " GB";
return (((uBytes - 1UL)/ uTB) + 1UL).ToString() + " TB"; return (((uBytes - 1UL) / uTB) + 1UL).ToString() + " TB";
} }
public static string FormatDataSizeKB(ulong uBytes) public static string FormatDataSizeKB(ulong uBytes)

View File

@@ -23,6 +23,7 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
#if ModernKeePassLib #if ModernKeePassLib
using Windows.Storage; using Windows.Storage;
@@ -38,12 +39,8 @@ namespace ModernKeePassLib.Utility
/// </summary> /// </summary>
public static class UrlUtil public static class UrlUtil
{ {
private static readonly char[] m_vDirSeps = new char[] { private static readonly char[] g_vPathTrimCharsWs = new char[] {
'\\', '/', UrlUtil.LocalDirSepChar };
#if !ModernKeePassLib
private static readonly char[] m_vPathTrimCharsWs = new char[] {
'\"', ' ', '\t', '\r', '\n' }; '\"', ' ', '\t', '\r', '\n' };
#endif
public static char LocalDirSepChar public static char LocalDirSepChar
{ {
@@ -57,6 +54,32 @@ namespace ModernKeePassLib.Utility
#endif #endif
} }
private static char[] g_vDirSepChars = null;
private static char[] DirSepChars
{
get
{
if(g_vDirSepChars == null)
{
List<char> l = new List<char>();
l.Add('/'); // For URLs, also on Windows
// On Unix-like systems, '\\' is not a separator
if(!NativeLib.IsUnix()) l.Add('\\');
if(!l.Contains(UrlUtil.LocalDirSepChar))
{
Debug.Assert(false);
l.Add(UrlUtil.LocalDirSepChar);
}
g_vDirSepChars = l.ToArray();
}
return g_vDirSepChars;
}
}
/// <summary> /// <summary>
/// Get the directory (path) of a file name. The returned string may be /// Get the directory (path) of a file name. The returned string may be
/// terminated by a directory separator character. Example: /// terminated by a directory separator character. Example:
@@ -79,7 +102,7 @@ namespace ModernKeePassLib.Utility
Debug.Assert(strFile != null); Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile"); if(strFile == null) throw new ArgumentNullException("strFile");
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps); int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
if(nLastSep < 0) return string.Empty; // No directory if(nLastSep < 0) return string.Empty; // No directory
if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') && if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
@@ -103,7 +126,7 @@ namespace ModernKeePassLib.Utility
{ {
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath"); Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
int nLastSep = strPath.LastIndexOfAny(m_vDirSeps); int nLastSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
if(nLastSep < 0) return strPath; if(nLastSep < 0) return strPath;
if(nLastSep >= (strPath.Length - 1)) return string.Empty; if(nLastSep >= (strPath.Length - 1)) return string.Empty;
@@ -120,7 +143,7 @@ namespace ModernKeePassLib.Utility
{ {
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath"); Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps); int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
int nLastExtDot = strPath.LastIndexOf('.'); int nLastExtDot = strPath.LastIndexOf('.');
if(nLastExtDot <= nLastDirSep) return strPath; if(nLastExtDot <= nLastDirSep) return strPath;
@@ -137,7 +160,7 @@ namespace ModernKeePassLib.Utility
{ {
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath"); Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps); int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
int nLastExtDot = strPath.LastIndexOf('.'); int nLastExtDot = strPath.LastIndexOf('.');
if(nLastExtDot <= nLastDirSep) return string.Empty; if(nLastExtDot <= nLastDirSep) return string.Empty;
@@ -162,11 +185,8 @@ namespace ModernKeePassLib.Utility
if(nLength <= 0) return string.Empty; if(nLength <= 0) return string.Empty;
char chLast = strPath[nLength - 1]; char chLast = strPath[nLength - 1];
if(Array.IndexOf<char>(UrlUtil.DirSepChars, chLast) >= 0)
for(int i = 0; i < m_vDirSeps.Length; ++i) return strPath;
{
if(chLast == m_vDirSeps[i]) return strPath;
}
if(bUrl) return (strPath + '/'); if(bUrl) return (strPath + '/');
return (strPath + UrlUtil.LocalDirSepChar); return (strPath + UrlUtil.LocalDirSepChar);
@@ -230,21 +250,35 @@ namespace ModernKeePassLib.Utility
return false; return false;
} */ } */
internal static int IndexOfSecondEnclQuote(string str)
{
if(str == null) { Debug.Assert(false); return -1; }
if(str.Length <= 1) return -1;
if(str[0] != '\"') { Debug.Assert(false); return -1; }
if(NativeLib.IsUnix())
{
// Find non-escaped quote
string strFlt = str.Replace("\\\\", new string(
StrUtil.GetUnusedChar(str + "\\\""), 2)); // Same length
Match m = Regex.Match(strFlt, "[^\\\\]\\u0022");
int i = (((m != null) && m.Success) ? m.Index : -1);
return ((i >= 0) ? (i + 1) : -1); // Index of quote
}
// Windows does not allow quotes in folder/file names
return str.IndexOf('\"', 1);
}
public static string GetQuotedAppPath(string strPath) public static string GetQuotedAppPath(string strPath)
{ {
if(strPath == null) { Debug.Assert(false); return string.Empty; } if(strPath == null) { Debug.Assert(false); return string.Empty; }
// int nFirst = strPath.IndexOf('\"');
// int nSecond = strPath.IndexOf('\"', nFirst + 1);
// if((nFirst >= 0) && (nSecond >= 0))
// return strPath.Substring(nFirst + 1, nSecond - nFirst - 1);
// return strPath;
string str = strPath.Trim(); string str = strPath.Trim();
if(str.Length <= 1) return str; if(str.Length <= 1) return str;
if(str[0] != '\"') return str; if(str[0] != '\"') return str;
int iSecond = str.IndexOf('\"', 1); int iSecond = IndexOfSecondEnclQuote(str);
if(iSecond <= 0) return str; if(iSecond <= 0) return str;
return str.Substring(1, iSecond - 1); return str.Substring(1, iSecond - 1);
@@ -256,7 +290,7 @@ namespace ModernKeePassLib.Utility
if(strUrl == null) throw new ArgumentNullException("strUrl"); if(strUrl == null) throw new ArgumentNullException("strUrl");
string str = strUrl; string str = strUrl;
if(str.StartsWith(@"file:///", StrUtil.CaseIgnoreCmp)) if(str.StartsWith("file:///", StrUtil.CaseIgnoreCmp))
str = str.Substring(8, str.Length - 8); str = str.Substring(8, str.Length - 8);
str = str.Replace('/', UrlUtil.LocalDirSepChar); str = str.Replace('/', UrlUtil.LocalDirSepChar);
@@ -338,8 +372,8 @@ namespace ModernKeePassLib.Utility
string strBase = GetShortestAbsolutePath(strBaseFile); string strBase = GetShortestAbsolutePath(strBaseFile);
string strTarget = GetShortestAbsolutePath(strTargetFile); string strTarget = GetShortestAbsolutePath(strTargetFile);
string[] vBase = strBase.Split(m_vDirSeps); string[] vBase = strBase.Split(UrlUtil.DirSepChars);
string[] vTarget = strTarget.Split(m_vDirSeps); string[] vTarget = strTarget.Split(UrlUtil.DirSepChars);
int i = 0; int i = 0;
while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) && while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
@@ -416,13 +450,14 @@ namespace ModernKeePassLib.Utility
if(IsUncPath(strPath)) if(IsUncPath(strPath))
{ {
char chSep = strPath[0]; char chSep = strPath[0];
Debug.Assert(Array.IndexOf<char>(m_vDirSeps, chSep) >= 0); char[] vSep = ((chSep == '/') ? (new char[] { '/' }) :
(new char[] { '\\', '/' }));
List<string> l = new List<string>(); List<string> l = new List<string>();
#if !KeePassLibSD #if !KeePassLibSD
string[] v = strPath.Split(m_vDirSeps, StringSplitOptions.None); string[] v = strPath.Split(vSep, StringSplitOptions.None);
#else #else
string[] v = strPath.Split(m_vDirSeps); string[] v = strPath.Split(vSep);
#endif #endif
Debug.Assert((v.Length >= 3) && (v[0].Length == 0) && Debug.Assert((v.Length >= 3) && (v[0].Length == 0) &&
(v[1].Length == 0)); (v[1].Length == 0));
@@ -463,8 +498,8 @@ namespace ModernKeePassLib.Utility
} }
catch(Exception) { Debug.Assert(false); return strPath; } catch(Exception) { Debug.Assert(false); return strPath; }
Debug.Assert(str.IndexOf("\\..\\") < 0); Debug.Assert((str.IndexOf("\\..\\") < 0) || NativeLib.IsUnix());
foreach(char ch in m_vDirSeps) foreach(char ch in UrlUtil.DirSepChars)
{ {
string strSep = new string(ch, 1); string strSep = new string(ch, 1);
str = str.Replace(strSep + "." + strSep, strSep); str = str.Replace(strSep + "." + strSep, strSep);
@@ -494,24 +529,30 @@ namespace ModernKeePassLib.Utility
return nLength; return nLength;
} }
internal static string GetScheme(string strUrl)
{
if(string.IsNullOrEmpty(strUrl)) return string.Empty;
int i = strUrl.IndexOf(':');
if(i > 0) return strUrl.Substring(0, i);
return string.Empty;
}
public static string RemoveScheme(string strUrl) public static string RemoveScheme(string strUrl)
{ {
if(string.IsNullOrEmpty(strUrl)) return string.Empty; if(string.IsNullOrEmpty(strUrl)) return string.Empty;
int nNetScheme = strUrl.IndexOf(@"://", StrUtil.CaseIgnoreCmp); int i = strUrl.IndexOf(':');
int nShScheme = strUrl.IndexOf(@":/", StrUtil.CaseIgnoreCmp); if(i < 0) return strUrl; // No scheme to remove
int nSmpScheme = strUrl.IndexOf(@":", StrUtil.CaseIgnoreCmp); ++i;
if((nNetScheme < 0) && (nShScheme < 0) && (nSmpScheme < 0)) // A single '/' indicates a path (absolute) and should not be removed
return strUrl; // No scheme if(((i + 1) < strUrl.Length) && (strUrl[i] == '/') &&
(strUrl[i + 1] == '/'))
i += 2; // Skip authority prefix
int nMin = Math.Min(Math.Min((nNetScheme >= 0) ? nNetScheme : int.MaxValue, return strUrl.Substring(i);
(nShScheme >= 0) ? nShScheme : int.MaxValue),
(nSmpScheme >= 0) ? nSmpScheme : int.MaxValue);
if(nMin == nNetScheme) return strUrl.Substring(nMin + 3);
if(nMin == nShScheme) return strUrl.Substring(nMin + 2);
return strUrl.Substring(nMin + 1);
} }
public static string ConvertSeparators(string strPath) public static string ConvertSeparators(string strPath)
@@ -538,22 +579,50 @@ namespace ModernKeePassLib.Utility
public static string FilterFileName(string strName) public static string FilterFileName(string strName)
{ {
if(strName == null) { Debug.Assert(false); return string.Empty; } if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return string.Empty; }
string str = strName; // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
str = str.Replace('/', '-'); StringBuilder sb = new StringBuilder(strName.Length);
str = str.Replace('\\', '-'); foreach(char ch in strName)
str = str.Replace(":", string.Empty); {
str = str.Replace("*", string.Empty); if(ch < '\u0020') continue;
str = str.Replace("?", string.Empty);
str = str.Replace("\"", string.Empty);
str = str.Replace(@"'", string.Empty);
str = str.Replace('<', '(');
str = str.Replace('>', ')');
str = str.Replace('|', '-');
return str; switch(ch)
{
case '\"':
case '*':
case ':':
case '?':
break;
case '/':
case '\\':
case '|':
sb.Append('-');
break;
case '<':
sb.Append('(');
break;
case '>':
sb.Append(')');
break;
default: sb.Append(ch); break;
}
}
// Trim trailing spaces and periods
for(int i = sb.Length - 1; i >= 0; --i)
{
char ch = sb[i];
if((ch == ' ') || (ch == '.')) sb.Remove(i, 1);
else break;
}
return sb.ToString();
} }
/// <summary> /// <summary>
@@ -672,7 +741,7 @@ namespace ModernKeePassLib.Utility
foreach(string strPathRaw in v) foreach(string strPathRaw in v)
{ {
if(strPathRaw == null) { Debug.Assert(false); continue; } if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs); string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; } if(strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw); Debug.Assert(strPath == strPathRaw);
@@ -709,7 +778,7 @@ namespace ModernKeePassLib.Utility
if(fi == null) { Debug.Assert(false); continue; } if(fi == null) { Debug.Assert(false); continue; }
string strPathRaw = fi.FullName; string strPathRaw = fi.FullName;
if(strPathRaw == null) { Debug.Assert(false); continue; } if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs); string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; } if(strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw); Debug.Assert(strPath == strPathRaw);
@@ -783,5 +852,19 @@ namespace ModernKeePassLib.Utility
char ch = char.ToUpperInvariant(strPath[0]); char ch = char.ToUpperInvariant(strPath[0]);
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0'); return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
} }
internal static string GetSafeFileName(string strName)
{
Debug.Assert(!string.IsNullOrEmpty(strName));
string str = FilterFileName(GetFileName(strName ?? string.Empty));
if(string.IsNullOrEmpty(str))
{
Debug.Assert(false);
return "File.dat";
}
return str;
}
} }
} }

View File

@@ -50,7 +50,7 @@ namespace ModernKeePassLib.Utility
xrs.IgnoreProcessingInstructions = true; xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true; xrs.IgnoreWhitespace = true;
#if KeePassUAP #if KeePassUAP || ModernKeePassLib
xrs.DtdProcessing = DtdProcessing.Prohibit; xrs.DtdProcessing = DtdProcessing.Prohibit;
#else #else
// Also see PrepMonoDev.sh script // Also see PrepMonoDev.sh script