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

View File

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

View File

@@ -47,6 +47,8 @@ namespace ModernKeePassLib.Cryptography
// RijndaelManagedTransform when a stream hasn't been
// read completely (e.g. incorrect master key)
catch(CryptographicException) { }
// Similar to above, at the beginning of the stream
catch(IndexOutOfRangeException) { }
catch(Exception) { Debug.Assert(false); }
if(disposing)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,11 +67,10 @@ namespace ModernKeePassLib.Keys
{
get { return m_pbKeyData; }
}
#if ModernKeePassLib
public KcpKeyFile(StorageFile strKeyFile)
public KcpKeyFile(StorageFile keyFile)
{
Construct(IOConnectionInfo.FromFile(strKeyFile), false);
Construct(IOConnectionInfo.FromStorageFile(keyFile), false);
}
#else
public KcpKeyFile(string strKeyFile)
@@ -193,7 +192,7 @@ namespace ModernKeePassLib.Keys
/// random number generator is used).</param>
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
#if ModernKeePassLib
public static void Create(StorageFile strFilePath, byte[] pbAdditionalEntropy)
public static void Create(StorageFile file, byte[] pbAdditionalEntropy)
#else
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
#endif
@@ -215,7 +214,11 @@ namespace ModernKeePassLib.Keys
}
}
#if ModernKeePassLib
CreateXmlKeyFile(file, pbFinalKey32);
#else
CreateXmlKeyFile(strFilePath, pbFinalKey32);
#endif
}
// ================================================================
@@ -276,19 +279,23 @@ namespace ModernKeePassLib.Keys
return pbKeyData;
}
#if ModernKeePassLib
private static void CreateXmlKeyFile(StorageFile strFile, byte[] pbKeyData)
private static void CreateXmlKeyFile(StorageFile file, byte[] pbKeyData)
{
Debug.Assert(file != null);
if (file == null) throw new ArgumentNullException(nameof(file));
#else
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
#endif
{
Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile");
#endif
Debug.Assert(pbKeyData != null);
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
#if ModernKeePassLib
IOConnectionInfo ioc = IOConnectionInfo.FromFile(strFile);
var ioc = IOConnectionInfo.FromStorageFile(file);
#else
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,6 @@ using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassUAP)
using System.Net.Cache;
using System.Net.Security;
@@ -600,7 +599,12 @@ namespace ModernKeePassLib.Serialization
private static Stream OpenReadLocal(IOConnectionInfo ioc)
{
#if ModernKeePassLib
return ioc.StorageFile.OpenAsync(FileAccessMode.Read).GetAwaiter().GetResult().AsStream();
#else
return new FileStream(ioc.Path, FileMode.Open, FileAccess.Read,
FileShare.Read);
#endif
}
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
@@ -654,7 +658,7 @@ namespace ModernKeePassLib.Serialization
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
#if ModernKeePassLib
return ioc.StorageFile.IsAvailable;
return ioc.StorageFile != null;
#else
if(ioc.IsLocalFile()) return File.Exists(ioc.Path);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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