Compare commits

..

17 Commits

Author SHA1 Message Date
Philipp Crocoll
f2ca846093 1.07b-r0 offline 2019-10-21 19:29:37 +02:00
Philipp Crocoll
38cee50f34 Merge commit 'cc8cea00491016abea0332799e7f85072a076014' into nonet
# Resolved Conflicts:
#	src/KeePass.sln
#	src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj
#	src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj
#	src/keepass2android/Properties/AndroidManifest_nonet.xml
#	src/keepass2android/keepass2android.csproj
2019-10-21 10:42:39 +02:00
Philipp Crocoll
d45fd99235 manifest for 1.06g offline 2019-10-21 10:34:59 +02:00
Philipp Crocoll
05eea4aae1 don't show donation notice in Changelog when using the app for one of the first times. Disable link to playstore with additional storage options as required by Google. 2019-10-21 10:34:32 +02:00
Philipp Crocoll
8468049935 manifest for 1.06f release 2018-11-10 08:38:13 +01:00
Philipp Crocoll
7cef6c8566 Merge branch 'nonet' of https://github.com/PhilippC/keepass2android into nonet
# Conflicts:
#	src/keepass2android/Properties/AndroidManifest_nonet.xml
#	src/keepass2android/keepass2android.csproj
2018-11-08 05:23:10 +01:00
Philipp Crocoll
98f2a139e9 Merge remote-tracking branch 'remotes/origin/1.06' into nonet
# Conflicts:
#	src/keepass2android/Properties/AndroidManifest_nonet.xml
2018-11-08 04:55:44 +01:00
Philipp Crocoll
832d3b3a95 1.05d release for nonet 2018-06-23 09:07:47 +02:00
Philipp Crocoll
37867634cd Merge branch 'master' into nonet
Conflicts:
	src/keepass2android/Properties/AndroidManifest_nonet.xml
2018-06-18 13:08:07 +02:00
Philipp Crocoll
d9713f8e18 switch to using Release build type instead of ReleaseNoNet (separation no longer required because a custom branch exists for the offline variant) 2018-02-28 06:06:26 +01:00
Philipp Crocoll
c583b58cb9 adjust manifest version for 1.04b release 2018-02-27 06:06:35 +01:00
Philipp Crocoll
bfeaf5dbf5 Merge branch '1.04' into nonet
Conflicts:
	src/keepass2android/Properties/AndroidManifest_nonet.xml
2018-02-21 05:58:01 +01:00
Philipp Crocoll
0907fa5685 manifest for 1.03-nonet release 2017-12-02 15:30:04 +01:00
Philipp Crocoll
ff8dc76c75 Merge branch 'master' into nonet
Conflicts:
	docs/README.md
2017-12-02 14:25:37 +01:00
Philipp Crocoll
0b09e2790f remove online file storages from project file 2017-10-25 06:49:02 +02:00
Philipp Crocoll
781350aa5f manifest for 1.02 release 2017-10-25 06:24:01 +02:00
Philipp Crocoll
9716130336 remove references to libraries only required for online build, adjust build script and README 2017-10-25 06:23:53 +02:00
741 changed files with 23047 additions and 64460 deletions

View File

@@ -1,351 +0,0 @@
name: Build keepass2android app
on: [push, pull_request]
jobs:
# macos:
# Disabled. Does not work, maybe due to nuget version, see https://github.com/PhilippC/keepass2android/actions/runs/4297640426/jobs/7490853348
# should work again when the Project solution is converted to sdk style .csproj files.
# runs-on: macos-12
# steps:
# - uses: actions/checkout@v3
# - name: Fetch submodules
# run: git submodule init && git submodule update
# - name: Setup Gradle
# uses: gradle/gradle-build-action@v2
# - name: Cache NuGet packages
# uses: actions/cache@v3
# with:
# path: ~/.nuget/packages
# key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
# restore-keys: |
# ${{ runner.os }}-nuget-
# # As per https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#visual-studio-for-mac
# - name: Switch to Visual Studio 2019
# if: ${{ false }} # Not needed. We stay with the default 'Visual Studio 2022' of macos-12 runner.
# run: |
# mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app"
# mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app"
# # As of 2022-12-02, keepass2android doesn't build with Xamarin >= 12.1 because there is some issue with SamsungPass. Removing SamsungPass would make the build succeed.
# - name: Set default Xamarin SDK versions
# run: |
# # If using the github runner 'macos-12'
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.3
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.1 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.2 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.3 # Build fails in this case, as of 2022-12-02
# $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=13.1
# # If using the github runner 'macos-11'
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.0
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
# # If using the github runner 'macos-10.15'
# # $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.2
# - name: Switch to JDK-11
# uses: actions/setup-java@v3
# with:
# java-version: '11'
# distribution: 'temurin'
# - name: Display java version
# run: java -version
# # Some components of Keepass2Android currently target android API 26 which are not available on the runner
# - name: Download android-26 API
# run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
# - name: Build native dependencies
# run: make native
# - name: Build java dependencies
# run: make java
# - name: Install NuGet dependencies (net)
# run: make nuget Flavor=Net
# - name: Build keepass2android (net)
# run: |
# make msbuild Flavor=Net
# - name: Build APK (net)
# run: |
# make apk Flavor=Net
# - name: Archive production artifacts (net)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('net' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
# - name: Install NuGet dependencies (nonet)
# run: make nuget Flavor=NoNet
# - name: Build keepass2android (nonet)
# run: |
# make msbuild Flavor=NoNet
# - name: Build APK (nonet)
# run: |
# make apk Flavor=NoNet
# - name: Archive production artifacts (nonet)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('nonet' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
# - name: Perform "make distclean"
# run: make distclean
# linux:
# disabled.
# As per: xamarin/xamarin-android#7235 (comment)
# > Unfortunately the Classic OSS Xamarin.Android packages for Linux are no longer being built and as such they are not available for the v13.0 tag.
# we can re-enable this after porting to .net 6.0
# runs-on: ubuntu-22.04
# env:
# # Build Artifact of xamarin.android-oss dated 2021-02-02, master branch (= version 11.2.99) - *.deb cannot be installed because "lxd" package is not anymore shipped in current ubuntu version
# #xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzM0NTE3L2FydGlmYWN0TmFtZS9JbnN0YWxsZXJzKy0rTGludXg1/content?format=zip
# # Build Artifact of xamarin.android-oss dated 2021-03-23, d16-9 branch (= version 11.2.2) - *.deb cannot be installed because "lxd" package is not anymore shipped in current ubuntu version
# #xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzM3Njg0L2FydGlmYWN0TmFtZS9JbnN0YWxsZXJzKy0rTGludXg1/content?format=zip
# # Build Artifact of xamarin.android-oss dated 2021-07-21, master branch (= version 11.4.99)
# # xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzQzNjU5L2FydGlmYWN0TmFtZS9pbnN0YWxsZXJzLXVuc2lnbmVkKy0rTGludXg1/content?format=zip
# # Build Artifact of xamarin.android-oss dated 2022-02-16, master branch (= version 12.2.99)
# xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzU0OTUzL2FydGlmYWN0TmFtZS9pbnN0YWxsZXJzLXVuc2lnbmVkKy0rTGludXg1/content?format=zip
# steps:
# - uses: actions/checkout@v3
# - name: Fetch submodules
# run: git submodule init && git submodule update
# - name: Setup Gradle
# uses: gradle/gradle-build-action@v2
# - name: Cache NuGet packages
# uses: actions/cache@v3
# with:
# path: ~/.nuget/packages
# key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
# restore-keys: |
# ${{ runner.os }}-nuget-
# - name: Cache Xamarin.Android packages
# id: xamarin_cache
# uses: actions/cache@v3
# with:
# path: ~/xamarin.android-oss
# key: ${{ runner.os }}-xamarin.android-oss-${{ env.xamarin_url }}
# restore-keys: |
# ${{ runner.os }}-xamarin.android-oss-${{ env.xamarin_url }}
# - name: Install Mono
# if: ${{ false }} # disable for now since it is already installed on the runner which uses the same repo https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md#language-and-runtime
# run: |
# sudo apt install gnupg ca-certificates &&
# sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF &&
# echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list &&
# sudo apt update &&
# sudo apt-get -y -t stable-focal install mono-complete
# - name: Download & unpack Xamarin.Android
# if: steps.xamarin_cache.outputs.cache-hit != 'true'
# run: |
# set -x
# cd $HOME &&
# wget -O "installers-unsigned - Linux.zip" ${{ env.xamarin_url }} &&
# unzip "installers-unsigned - Linux.zip" &&
# mkdir -p xamarin.android-oss &&
# DIR=$(unzip -Z -1 installers-unsigned\ -\ Linux.zip | cut -d '/' -f1 | sort -u) &&
# tar -xvf "$DIR"/xamarin.android-oss-*.tar.* --strip-components=1 -C xamarin.android-oss &&
# mv "$DIR"/*.deb xamarin.android-oss
# - name: Setup Xamarin.Android
# run: |
# cd $HOME &&
# sudo apt install -y ./xamarin.android-oss/*.deb &&
# echo "$HOME/xamarin.android-oss/bin/Release/bin" >> $GITHUB_PATH
# - name: Switch to JDK-11
# uses: actions/setup-java@v3
# with:
# java-version: '11'
# distribution: 'temurin'
# - name: Display java version
# run: java -version
# # Some components of Keepass2Android currently target android API 26 which are not available on the runner
# - name: Download android-26 API
# run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
# - name: Install libzip4
# if: ${{ false }} # disable for now since it is already installed on the runner
# run: sudo apt -y install libzip4
# - name: Build native dependencies
# run: make native
# - name: Build java dependencies
# run: make java
# - name: Install NuGet dependencies (net)
# run: make nuget Flavor=Net
# - name: Build keepass2android (net)
# run: |
# make msbuild Flavor=Net
# - name: Build APK (net)
# run: |
# make apk Flavor=Net
# - name: Archive production artifacts (net)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('net' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
# - name: Install NuGet dependencies (nonet)
# run: make nuget Flavor=NoNet
# - name: Build keepass2android (nonet)
# run: |
# make msbuild Flavor=NoNet
# - name: Build APK (nonet)
# run: |
# make apk Flavor=NoNet
# - name: Archive production artifacts (nonet)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('nonet' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
# - name: Perform "make distclean"
# run: make distclean
windows:
# on windows-2022 it builds with:
# Microsoft Visual Studio\2022\Enterprise
# Found Java SDK version 11.0.12
# Found Xamarin.Android 13.1.0.1
#
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Fetch submodules
run: git submodule init && git submodule update
# Workaround an issue when building on windows-2022. Error was
# D8 : OpenJDK 64-Bit Server VM warning : INFO: os::commit_memory(0x00000000ae400000, 330301440, 0) failed; error='The paging file is too small for this operation to complete' (DOS error/errno=1455) [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
# C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Xamarin\Android\Xamarin.Android.D8.targets(81,5): error MSB6006: "java.exe" exited with code 1. [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
- name: Configure Pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
# If we want to also have nmake, use this instead
#uses: ilammy/msvc-dev-cmd@v1
- name: Switch to JDK-11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Display java version
run: java -version
# Some components of Keepass2Android currently target android API 26 which are not available on the runner
- name: Download android-26 API
shell: cmd
run: |
%ANDROID_SDK_ROOT%\cmdline-tools\latest\bin\sdkmanager --install "platforms;android-26"
- name: Build native dependencies
shell: cmd
run: |
make native
- name: Build java dependencies
shell: cmd
run: |
make java
- name: Install NuGet dependencies (net)
run: make nuget Flavor=Net
- name: Build keepass2android (net)
run: |
make msbuild Flavor=Net
- name: Build APK (net)
run: |
make apk Flavor=Net
- name: Archive production artifacts (net)
uses: actions/upload-artifact@v3
with:
name: signed APK ('net' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
- name: Install NuGet dependencies (nonet)
run: make nuget Flavor=NoNet
- name: Build keepass2android (nonet)
run: |
make msbuild Flavor=NoNet
- name: Test Autofill
working-directory: ./src/Kp2aAutofillParserTest
run: dotnet test
- name: Build APK (nonet)
run: |
make apk Flavor=NoNet
- name: Archive production artifacts (nonet)
uses: actions/upload-artifact@v3
with:
name: signed APK ('nonet' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
- name: Perform "make distclean"
run: make distclean

9
.gitignore vendored
View File

@@ -7,10 +7,6 @@ PCtest
bin
obj
# Makefile-related files
/allow_git_clean
/stamp.nuget_*
Resource.designer.cs
R.java
@@ -109,6 +105,7 @@ Thumbs.db
/src/java/JavaFileStorageTest/gen/group/pals/android/lib/ui/filechooser/R.java
/src/java/JavaFileStorageTest/gen/keepass2android/javafilestorage/R.java
/src/TwofishCipher/Resources/Resource.Designer.cs
/src/BindingLibrary1
/src/PluginTOTP
@@ -172,7 +169,3 @@ src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-A
/src/java/KP2AKdbLibrary/app/build
/src/java/KP2ASoftkeyboard_AS/app/.cxx
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs
/src/java/KP2AKdbLibrary/app/.cxx
/src/ActionViewFilterTest
/docs/gdrive-verification
/src/MegaTest

3
.gitmodules vendored
View File

@@ -1,6 +1,9 @@
[submodule "src/SamsungPass"]
path = src/SamsungPass
url = https://github.com/PhilippC/Xamarin-Samsung-Pass.git
[submodule "src/netftpandroid"]
path = src/netftpandroid
url = https://github.com/PhilippC/netftpandroid.git
[submodule "src/java/argon2/phc-winner-argon2"]
path = src/java/argon2/phc-winner-argon2
url = https://github.com/P-H-C/phc-winner-argon2

349
Makefile
View File

@@ -1,349 +0,0 @@
#!/usr/bin/make -f
#
# This Makefile can be used on both unix-like (use make) & windows (with GNU make)
#
# append the Configuration variable to 'make' call with value to use in '/p:Configuration='
# of msbuild command.
#
# append the Flavor variable to 'make' call with value to use in '/p:Flavor='
# of msbuild command.
#
# Example:
# make Configuration=Release Flavor=NoNet
#
#
# Some targets:
# - all: everything (including APK)
# - native: build the native libs
# - java: build the java libs
# - nuget: restore NuGet packages
# - msbuild: build the project
# - apk: same as all
#
# - distclean: run a 'git clean -xdff'. Remove everyhing that is not in the git tree.
# - clean: all clean_* targets below
# - clean_native: clean native lib
# - clean_java: call clean target of java libs
# - clean_nuget: cleanup the 'nuget restore'
# - clean_msbuild: call clean target of msbuild
#
#
#
# Disable built-in rules to speed-up the Makefile processing.
# for example when running 'make java' on Windows it could take ~10 sec more than on linux to start building
# from what this option disables, the "clearing out the default list of suffixes for suffix rules"
# gives the most speed gain.
MAKEFLAGS += --no-builtin-rules
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
detected_OS := Windows
WHICH := where
RM := RMDIR /S /Q
RMFILE := DEL
CP := copy
GRADLEW := gradlew.bat
# Force use of cmd shell (don't use POSIX shell because the user may not have one installed)
SHELL := cmd
else
detected_OS := $(shell uname)
WHICH := which
RM := rm -rf
RMFILE := $(RM)
CP := cp
GRADLEW := ./gradlew
endif
$(info MAKESHELL: $(MAKESHELL))
$(info SHELL: $(SHELL))
$(info )
# On linux use xabuild, on Windows use MSBuild.exe, otherwise (macos?) use msbuild.
ifeq ($(detected_OS),Linux)
MSBUILD_binary := xabuild
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
else ifeq ($(detected_OS),Windows)
MSBUILD_binary := MSBuild.exe
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary) 2> nul)
ifeq ($(MSBUILD),)
# Additional heuristic to find MSBUILD_BINARY on Windows
VSWHERE := "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
VSWHERE_CHECK := $(shell @echo off & $(VSWHERE) 2> nul || echo VSWHERE_NOT_FOUND)
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
MSBUILD := $(shell @echo off & $(VSWHERE) -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe)
VS_INSTALL_PATH := $(shell @echo off & $(VSWHERE) -property installationPath)
endif
endif
else
MSBUILD_binary := msbuild
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
endif
ifeq ($(MSBUILD),)
$(info )
$(info '$(MSBUILD_binary)' binary could not be found. Check it is in your PATH.)
ifeq ($(detected_OS),Windows)
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
$(info )
$(info You may retry after running in the command prompt:)
$(info )
$(info "$(VS_INSTALL_PATH)\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64)
$(info )
$(info If this doesn't work, install/find the location of vcvarsall.bat)
$(info or install and add msbuild.exe to your PATH)
$(info )
endif
endif
$(error )
endif
$(info MSBUILD: $(MSBUILD))
$(info )
ifeq ($(ANDROID_SDK_ROOT),)
$(error set ANDROID_SDK_ROOT environment variable)
endif
$(info ANDROID_SDK_ROOT: $(ANDROID_SDK_ROOT))
ifeq ($(ANDROID_HOME),)
$(error set ANDROID_HOME environment variable)
endif
$(info ANDROID_HOME: $(ANDROID_SDK_ROOT))
ifeq ($(ANDROID_NDK_ROOT),)
$(error set ANDROID_NDK_ROOT environment variable)
endif
$(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT))
ifneq ($(Configuration),)
MSBUILD_PARAM = -p:Configuration="$(Configuration)"
else
$(warning Configuration environment variable not set.)
endif
ifneq ($(Flavor),)
MSBUILD_PARAM += -p:Flavor="$(Flavor)"
else
$(warning Flavor environment variable not set.)
endif
ifneq ($(KeyStore),)
MSBUILD_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass
endif
ifeq ($(detected_OS),Windows)
to_win_path=$(subst /,\,$(1))
to_posix_path=$(subst \,/,$(1))
define remove_dir
if exist $(1) ( $(RM) $(1) )
endef
define remove_files
$(foreach file,$(call to_win_path,$(1)), IF EXIST $(file) ( $(RMFILE) $(file) ) & )
endef
else
define remove_dir
$(RM) $(1)
endef
define remove_files
$(RMFILE) $(1)
endef
endif
# Recursive wildcard: https://stackoverflow.com/a/18258352
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
$(info MSBUILD_PARAM: $(MSBUILD_PARAM))
$(info nuget path: $(shell $(WHICH) nuget))
$(info )
NATIVE_COMPONENTS := argon2
NATIVE_CLEAN_TARGETS := clean_argon2
OUTPUT_argon2 = src/java/argon2/libs/armeabi-v7a/libargon2.so \
src/java/argon2/libs/arm64-v8a/libargon2.so \
src/java/argon2/libs/x86/libargon2.so \
src/java/argon2/libs/x86_64/libargon2.so
JAVA_COMPONENTS := \
JavaFileStorageTest-AS \
KP2ASoftkeyboard_AS \
Keepass2AndroidPluginSDK2 \
KP2AKdbLibrary
#PluginQR # Doesn't seem required
JAVA_CLEAN_TARGETS := \
clean_JavaFileStorageTest-AS \
clean_KP2ASoftkeyboard_AS \
clean_Keepass2AndroidPluginSDK2 \
clean_KP2AKdbLibrary \
clean_PluginQR
INPUT_android-filechooser-AS := $(filter-out $(filter %/app,$(wildcard src/java/android-filechooser-AS/*)),$(wildcard src/java/android-filechooser-AS/*)) \
$(filter-out $(filter %/build,$(wildcard src/java/android-filechooser-AS/app/*)),$(wildcard src/java/android-filechooser-AS/app/*)) \
$(call rwildcard,src/java/android-filechooser-AS/app/src,*)
INPUT_JavaFileStorage := $(filter-out $(filter %/app,$(wildcard src/java/JavaFileStorage/*)),$(wildcard src/java/JavaFileStorage/*)) \
$(filter-out $(filter %/build,$(wildcard src/java/JavaFileStorage/app/*)),$(wildcard src/java/JavaFileStorage/app/*)) \
$(wildcard src/java/JavaFileStorage/libs/*) \
$(call rwildcard,src/java/JavaFileStorage/app/src,*) \
INPUT_JavaFileStorageTest-AS := $(filter-out $(filter %/app,$(wildcard src/java/JavaFileStorageTest-AS/*)),$(wildcard src/java/JavaFileStorageTest-AS/*)) \
$(filter-out $(filter %/build,$(wildcard src/java/JavaFileStorageTest-AS/app/*)),$(wildcard src/java/JavaFileStorageTest-AS/app/*)) \
$(call rwildcard,src/java/JavaFileStorageTest-AS/app/src,*) \
$(INPUT_android-filechooser-AS) \
$(INPUT_JavaFileStorage)
OUTPUT_JavaFileStorageTest-AS = src/java/android-filechooser-AS/app/build/outputs/aar/android-filechooser-debug.aar \
src/java/android-filechooser-AS/app/build/outputs/aar/android-filechooser-release.aar \
src/java/JavaFileStorage/app/build/outputs/aar/JavaFileStorage-debug.aar \
src/java/JavaFileStorage/app/build/outputs/aar/JavaFileStorage-release.aar \
src/java/JavaFileStorageTest-AS/app/build/outputs/apk/debug/app-debug.apk \
src/java/JavaFileStorageTest-AS/app/build/outputs/apk/release/app-release-unsigned.apk
INPUT_KP2ASoftkeyboard_AS := $(wildcard src/java/KP2ASoftkeyboard_AS/*) \
$(wildcard src/java/KP2ASoftkeyboard_AS/app/*) \
$(call rwildcard,src/java/KP2ASoftkeyboard_AS/app/src,*)
OUTPUT_KP2ASoftkeyboard_AS = src/java/KP2ASoftkeyboard_AS/app/build/outputs/aar/app-debug.aar \
src/java/KP2ASoftkeyboard_AS/app/build/outputs/aar/app-release.aar
INPUT_Keepass2AndroidPluginSDK2 := $(wildcard src/java/Keepass2AndroidPluginSDK2/*) \
$(wildcard src/java/Keepass2AndroidPluginSDK2/app/*) \
$(call rwildcard,src/java/Keepass2AndroidPluginSDK2/app/src,*)
OUTPUT_Keepass2AndroidPluginSDK2 = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/app-debug.aar \
src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/app-release.aar
INPUT_KP2AKdbLibrary := $(wildcard src/java/KP2AKdbLibrary/*) \
$(wildcard src/java/KP2AKdbLibrary/app/*) \
$(call rwildcard,src/java/KP2AKdbLibrary/app/src,*)
OUTPUT_KP2AKdbLibrary = src/java/KP2AKdbLibrary/app/build/outputs/aar/app-debug.aar \
src/java/KP2AKdbLibrary/app/build/outputs/aar/app-release.aar
INPUT_PluginQR := $(wildcard src/java/PluginQR/*) \
$(wildcard src/java/PluginQR/app/*) \
$(call rwildcard,src/java/PluginQR/app/src,*) \
$(INPUT_Keepass2AndroidPluginSDK2)
OUTPUT_PluginQR = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepass2AndroidPluginSDK2-debug.aar \
src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepass2AndroidPluginSDK2-release.aar \
src/java/PluginQR/app/build/outputs/apk/debug/app-debug.apk \
src/java/PluginQR/app/build/outputs/apk/debug/app-release-unsigned.apk
##### Targets definition
.PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \
java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \
nuget clean_nuget \
msbuild clean_msbuild \
apk all clean
all: apk
##### Native Dependencies
native: $(NATIVE_COMPONENTS)
argon2: $(OUTPUT_argon2)
$(OUTPUT_argon2): $(wildcard src/java/argon2/phc-winner-argon2/src/*) $(wildcard src/java/argon2/phc-winner-argon2/src/blake2/*)
cd src/java/argon2 && $(ANDROID_NDK_ROOT)/ndk-build
##### Java Dependencies
java: $(JAVA_COMPONENTS)
JavaFileStorageTest-AS: $(OUTPUT_JavaFileStorageTest-AS)
KP2ASoftkeyboard_AS: $(OUTPUT_KP2ASoftkeyboard_AS)
Keepass2AndroidPluginSDK2: $(OUTPUT_Keepass2AndroidPluginSDK2)
KP2AKdbLibrary: $(OUTPUT_KP2AKdbLibrary)
PluginQR: $(OUTPUT_PluginQR)
$(OUTPUT_JavaFileStorageTest-AS): $(INPUT_JavaFileStorageTest-AS)
$(call remove_files,$(OUTPUT_JavaFileStorageTest-AS))
cd src/java/JavaFileStorageTest-AS && $(GRADLEW) assemble
$(OUTPUT_KP2ASoftkeyboard_AS): $(INPUT_KP2ASoftkeyboard_AS)
$(call remove_files,$(OUTPUT_KP2ASoftkeyboard_AS))
cd src/java/KP2ASoftkeyboard_AS && $(GRADLEW) assemble
$(OUTPUT_Keepass2AndroidPluginSDK2): $(INPUT_Keepass2AndroidPluginSDK2)
$(call remove_files,$(OUTPUT_Keepass2AndroidPluginSDK2))
cd src/java/Keepass2AndroidPluginSDK2 && $(GRADLEW) assemble
$(OUTPUT_KP2AKdbLibrary): $(INPUT_KP2AKdbLibrary)
$(call remove_files,$(OUTPUT_KP2AKdbLibrary))
cd src/java/KP2AKdbLibrary && $(GRADLEW) assemble
$(OUTPUT_PluginQR): $(INPUT_PluginQR)
$(call remove_files,$(OUTPUT_PluginQR))
cd src/java/PluginQR && $(GRADLEW) assemble
##### Nuget Dependencies
nuget: stamp.nuget_$(Flavor)
stamp.nuget_$(Flavor): src/KeePass.sln $(wildcard src/*/*.csproj) $(wildcard src/*/packages.config)
ifeq ($(shell $(WHICH) nuget),)
$(error "nuget" command not found. Check it is in your PATH)
endif
$(RMFILE) stamp.nuget_*
nuget restore src/KeePass.sln
$(MSBUILD) src/KeePass.sln -t:restore $(MSBUILD_PARAM) -p:RestorePackagesConfig=true
@echo "" > stamp.nuget_$(Flavor)
#####
src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs:
ifeq ($(detected_OS),Windows)
$(CP) src\Kp2aBusinessLogic\Io\DropboxFileStorageKeysDummy.cs src\Kp2aBusinessLogic\Io\DropboxFileStorageKeys.cs
else
$(CP) src/Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs $@
endif
msbuild: native java nuget src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
$(MSBUILD) src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(MSBUILD_PARAM) -p:Platform="Any CPU" -m
apk: msbuild
$(MSBUILD) src/keepass2android/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m
build_all: msbuild
##### Cleanup targets
clean_native: $(NATIVE_CLEAN_TARGETS)
clean_argon2:
cd src/java/argon2 && $(ANDROID_NDK_ROOT)/ndk-build clean
clean_java: $(JAVA_CLEAN_TARGETS)
clean_JavaFileStorageTest-AS:
cd src/java/JavaFileStorageTest-AS && $(GRADLEW) clean
clean_KP2ASoftkeyboard_AS:
cd src/java/KP2ASoftkeyboard_AS && $(GRADLEW) clean
clean_Keepass2AndroidPluginSDK2:
cd src/java/Keepass2AndroidPluginSDK2 && $(GRADLEW) clean
clean_KP2AKdbLibrary:
cd src/java/KP2AKdbLibrary && $(GRADLEW) clean
clean_PluginQR:
cd src/java/PluginQR && $(GRADLEW) clean
# https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting#other-potential-conditions
clean_nuget:
cd src && $(call remove_dir,packages)
ifeq ($(detected_OS),Windows)
DEL /S src\project.assets.json
DEL /S src\*.nuget.*
else
$(RM) src/*/obj/project.assets.json
$(RM) src/*/obj/*.nuget.*
endif
$(RMFILE) stamp.nuget_*
clean_msbuild:
$(MSBUILD) src/KeePass.sln -target:clean $(MSBUILD_PARAM)
clean: clean_native clean_java clean_nuget clean_msbuild
distclean: clean
ifneq ("$(wildcard ./allow_git_clean)","")
ifeq ($(shell $(WHICH) git),)
$(error "git" command not found. Check it is in your PATH)
endif
git clean -xdff src
else
$(warning 'git clean' skipped for safety reasons. See hint below:)
$(info )
$(info 'git clean' would delete all untracked files, those in '.gitignore' and those in '.git/info/exclude'.)
$(info )
$(info Check which files would be deleted by running: "git clean -n -xdff src")
$(info If listed files are acceptable, you can enable the call to "git clean" by creating an empty file named 'allow_git_clean' next to the Makefile.)
$(info )
endif

View File

@@ -8,27 +8,3 @@ files:
two_letters_code:
zh-CN: zh
zh-TW: zh-rTW
pt-PT: pt
pt-BR: pt-rBR
- source: src/java/android-filechooser-AS/app/src/main/res/values/strings.xml
translation: >-
/src/java/android-filechooser-AS/app/src/main/res/values-%two_letters_code%/%original_file_name%
translate_attributes: '0'
content_segmentation: '0'
languages_mapping:
two_letters_code:
zh-CN: zh
zh-TW: zh-rTW
pt-PT: pt
pt-BR: pt-rBR
- source: src/java/KP2ASoftkeyboard_AS/app/src/main/res/values/strings.xml
translation: >-
/src/java/KP2ASoftkeyboard_AS/app/src/main/res/values-%two_letters_code%/%original_file_name%
translate_attributes: '0'
content_segmentation: '0'
languages_mapping:
two_letters_code:
zh-CN: zh
zh-TW: zh-rTW
pt-PT: pt
pt-BR: pt-rBR

View File

@@ -1,239 +0,0 @@
# How to build Keepass2Android
## Overview
Keepass2Android is a Mono for Android app. This means that you need Xamarin's Mono for Android to build it. However, it also uses several components written in Java, so there are also Android-Studio projects involved. To make things even worse, parts of the keyboard and kdb-library are written in native code.
To build KP2A from scratch, you need:
- Xamarin's Mono for Android (also included in Visual Studio)
- Android SDK & NDK
Prior to building Keepass2Android, you need to build some of its components (from command line). Then you can build the full project either through Visual Studio, or through command line.
By using the command line, you can build on Windows, macOS or Linux.
## Prerequisites
### Common to all architectures
- Install Android SDK & NDK (either manually with Google's [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager), or through Android Studio). Visual Studio also installs a version of it, but in the end the directory must be writable and in a path without spaces (see below) so as to be able to build the components.
- Fetch the main repository of Keepass2Android and all submodules
- Note that VisualStudio can do this for you, otherwise run:
- `git submodule init && git submodule update`
### On Windows or macOS
- Install Visual Studio (for example 2019) with Xamarin.Android (ie. with capability to build Android apps). This should provide the needed tools like
- Xamarin.Android
- MSBuild
- Java JDK
- If you plan to build also from the command line:
- Install the MSVC build tools of visual studio. They provide the `vcvarsall.bat` file which among other things adds MSBuild to the PATH.
- Install [NuGet](https://www.nuget.org/downloads) to build also with "make". Alternatively, on Windows, if you use [chocolatey](https://chocolatey.org), run as administrator:
- `choco install nuget.commandline`
- Check that you have access to 'GNU make'.
- On Windows, it is usually not available by default. But the Android NDK provides it. You can find it in `%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make.exe`. Alternatively, on Windows, if you use [chocolatey](https://chocolatey.org), run as administrator:
- `choco install make`
- On macOS, it is usually only installed if you have developer command line tools installed or if you use [homebrew](https://brew.sh) or [macports](https://www.macports.org/). As an alternative it may be available in the Android NDK at `%ANDROID_NDK_ROOT%/prebuilt/darwin-x86_64/bin/make`.
### On Linux
- Install Java's JDK
- On Debian, for example: `apt install default-jdk-headless`.
- Install [Mono](https://www.mono-project.com/)
- This should provide `msbuild` & `xabuild` binary
- On Debian, after having added the repo from above, install with `apt install -t <repo_name> mono-devel msbuild`. A value for `<repo_name>` could be `stable-buster` for example, depending on which one you chose. You could also install the `mono-complete` package if you prefer.
- Install Xamarin.Android
- ~~Option 1: Use the mono-project [CI builds](https://dev.azure.com/xamarin/public/_build/latest?definitionId=48&branchName=main&stageName=Linux)~~ **NOTE:** KP2A now requires Xamarin.Android v13, which is newer than the current CI build; until a more recent CI build is available, this option is unfortunately no longer viable.
- Option 2: [Build it from source](https://github.com/xamarin/xamarin-android/blob/master/Documentation/README.md#building-from-source)
- Install NuGet package of your distribution
- On Debian/Ubuntu: `apt install nuget`
- Install [libzip](https://libzip.org/) for your distribution for some Xamarin.Android versions
- This may not be relevant anymore: for example, with Xamarin.Android 11.4.99. this is not needed.
- Some versions of Xamarin may require `libzip4`. If you are in this case:
- On Debian/Ubuntu, install it with `apt install libzip4`.
- Other distributions ship only `libzip5`. As a dirty workaround, it's possible to symlink `libzip.so.5` to `libzip.so.4`. Luckily, it appears to be working. For example:
- `sudo ln -s /usr/lib/libzip.so.5 /usr/lib/libzip.so.4`
- or `sudo ln -s /usr/lib64/libzip.so.5 /usr/lib/libzip.so.4`
## Building the required components:
This is done on the command line and requires the Android SDK & NDK and Java JDK.
### On Windows
- Setup your environment:
- Set these environment variables for Android's SDK & NDK
- `ANDROID_HOME` (for example `set ANDROID_HOME=C:\PATH\TO\android-sdk`)
- `ANDROID_SDK_ROOT` (for example `set ANDROID_SDK_ROOT=C:\PATH\TO\android-sdk`)
- `ANDROID_NDK_ROOT` (for example `set ANDROID_NDK_ROOT=C:\PATH\TO\android-sdk\ndk\version`)
**Note:** Care must be taken when setting the above variables to **not** include a trailing backslash in the path. A trailing backslash may cause `make` to fail.
**Note**: If the path to the Android SDK contains spaces, you **must** do one of these:
- either put the Android SDK into a path without spaces.
- or create a symlink to that path which doesn't contain spaces. Attention: this requires **administrator** priveleges. For example:
```
IF NOT EXIST C:\Android ( MKDIR C:\Android ) &&
MKLINK /D C:\Android\android-sdk "C:\Program Files (x86)\Android\android-sdk"
```
This is because [Android NDK doesn't support being installed in a path with spaces](https://github.com/android/ndk/issues/1400).
**Note**: The Android SDK path will require to be writeable because during the build, some missing components might be downloaded & installed.
- If you have "GNU make" available on your windows system, you may build by using the Makefile. You can also find a `make` executable in `%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make.exe`. To use it, see the instructions for Linux/macOS. Basically, just run `make` or `mingw32-make` depending on which distribution of GNU make for windows you have installed.
- Otherwise proceed as below:
1. Build argon2
```
cd src/java/argon2
%ANDROID_NDK_ROOT%/ndk-build.cmd
```
1. Build the other java components
```
cd src/build-scripts
build-java.bat
```
`build-java.bat` will call `gradlew` for several Java modules.
**Notes:**
- For building the java parts, it is suggested to keep a short name (e.g. "c:\projects\keepass2android") for the root project directory. Otherwise the Windows path length limit might be hit when building.
- Before building the java parts, make sure you have set the ANDROID_HOME variable or create a local.properties file inside the directories with a gradlew file. It is recommended to use the same SDK location as that of the Xamarin build.
- On some environments, `make` can fail to properly use the detected `MSBUILD` tools. This seems to be due to long pathnames and/or spaces in pathnames. It may be required to explicitly set the `MSBUILD` path using 8.3 "short" path notation:
- Determine the location of `MSBUILD` (e.g. `C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe`)
- [Generate the "short" path](https://superuser.com/a/728792) of that location (e.g.: `C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`)
- When running `make` specify the location of ``MSBUILD` explicitly (e.g.: `make MSBUILD="C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`
### On Linux/macOS
- Setup your environment:
- Set these environment variables for Android's SDK & NDK
- `ANDROID_HOME` (for example `export ANDROID_HOME=/path/to/android-sdk/`)
- `ANDROID_SDK_ROOT` (for example `export ANDROID_SDK_ROOT=/path/to/android-sdk/`)
- `ANDROID_NDK_ROOT` (for example `export ANDROID_NDK_ROOT=/path/to/android-sdk/ndk/version`)
- Update your PATH environment variable so that it can access `nuget`, `msbuild` or `xabuild` (for linux):
- On Linux:
- add `xabuild` to your path: `export PATH=/path/to/xamarin.android-oss/bin/Release/bin/:$PATH`
- On macOS:
- you may similarly need to add `msbuild` & `nuget` to your PATH.
- Start the build:
- This will use the Makefile at the root of the project (requires GNU make). To build everything (components & Keepass2Android APK) in a single command simply run:
```
make
```
- Otherwise, if you prefer to do step by step
1. Build argon2
```
make native
```
1. Build the other java components
```
make java
```
## Building Keepass2Android:
These are the basic steps to build Keepass2Android. You can also build Keepass2Android Offline. For this, configure the build by using the [Flavors](#Flavors).
### With Visual Studio
- On windows or on macOS open the src/KeePass.sln file with visual studio, and choose to build the project named 'keepass2android-app'
### Command Line
#### Windows, Macos & Linux
to build the APK, simply run:
```
make
```
or to skip building the APK:
```
make msbuild
```
## Where is the APK ?
The Apk can be installed on a device.
It is located in `src/keepass2android/bin/*/*-Signed.apk`
If you build with Visual Studio, the APK is not produced automatically. You need to perform some extra step. See the documentation of Visual Studio on how to proceed.
## Flavors
Keepass2Android is distributed in two flavors.
- Keepass2Android (aka `net`)
- Keepass2Android Offline (aka `nonet`)
The flavor is set through a MSBuild Property named "`Flavor`". The possible values are '`Net`' and '`NoNet`'.
The value of the Flavor property is used in 2 projects:
- `keepass2android-app` (in `src/keepass2android`)
- `Kp2aBusinessLogic` (in `src/keepass2android`)
Its value is set inside the `*.csproj` file (XML format) of each project in the `Project`/`PropertyGroup`/`Flavor` node.
By default its value is set to an empty string so that development is made with `AndroidManifest_debug.xml` on the '`net`' flavor.
This is the behaviour of the build system depending on the value of Flavor:
| Flavor | What is built | `AndroidManifest.xml` used |
| ----- | ----- | ----- |
| `` (empty string): This is the default value. | Keepass2Android | `AndroidManifest_debug.xml` |
| `Net` | Keepass2Android | `AndroidManifest_net.xml` |
| `NoNet` | Keepass2Android Offline | `AndroidManifest_nonet.xml` |
### Select/Change flavor:
When building, by default, the flavor is not set. So the value used is the value of the Flavor property in *.csproj file. This should result on doing a build of the 'net' flavor.
You can force the Flavor by setting the Flavor property.
Proceed this way:
#### Command line
##### Windows, Macos & Linux
To force building 'net' with `make`, run:
```
make Flavor=Net
```
To build 'nonet' with `make`, run:
```
make Flavor=NoNet
```
##### MSBuild
To build with MSBuild directly on the command line, set the flavor with `-p:Flavor=value` argument. For example:
```
MSBuild src/KeePass.sln ... -p:Flavor=NoNet
```
#### Visual Studio
When building with Visual Studio, edit the `*.csproj` file (XML format) and set the value in the `Project`/`PropertyGroup`/`Flavor` node. This is needed only for the projects that use the flavors.
**Note:** When switching between flavors, be sure to clean the previous build before.
## Makefile
It is possible to override the project's default 'Flavor' (Net, NoNet) and 'Configuration' (Release, Debug) by passing it as argument to `make`. See the header of the Makefile to see what can be done.

View File

@@ -77,10 +77,6 @@ The KP2A keyboard is meant to quickly "paste" or "type" values from your databas
## Is it safe to store my kdbx file in the cloud?
While it may happen that someone gets access to your kdbx file in the cloud, there is still no need to worry: the purpose of encryption is to protect the data even in case someone gets the kdbx file! As long as you are using a safe master key, you're safe! [Key files](https://keepass.info/help/base/keys.html#keyfiles) can help with securing the database even more.
## Doesn't Keepass2Android create automatic backups?
Yes and no. Yes: Keepass2Android stores the last successfully opened file as a read-only backup locally on the phone (unless you disable this is in the settings). This should make sure that even if the file gets destroyed during a save operation or gets deleted by accident, you should always have a version that can be opened. (Don't mix this up with the internal file cache which is not meant as a backup and can easily be overwritten even with a corrupt file. This internal file cache is meant for providing writable access even when the original file is not reachable, e.g. when you're offline.)
No: The local backup has two shortcomings: It is only one backup and does not allow to revert to older versions. So if you deleted an entry from the database, it might be deleted in the local backup soon as well. The even more important shortcoming is that it is just a local backup. It won't help when your phone gets lost or broken. Please create additional backups on seperate storage!
## How do I backup the database?
If you have stored your database on the cloud, you might rely on your cloud storage providers backups. Make sure they allow you to revert to older revisions in case the file gets corrupted for some reason.
If you are working with a local database file, make sure you create regular backups. I suggest you have an aumotated mechanism, e.g. with FolderSync (Lite) which can copy local files from your device to other locations, e.g. your PC in a local network. You can also use USB or tools like MyPhoneExploror to transfer data to your PC. Or, you use a removable storage like an SD card which you keep in a safe place after making the backup.
@@ -98,25 +94,6 @@ It's time for action! As soon as possible, select Settings - Database - Export a
## Why is Keepass2Android's apk so big?
Please see [Keepass2Android Apk](Keepass2Android-Apk.md) for more information.
## I get a message "File is trashed" when reading or writing a file on Google Drive
This happens because ocaml-fuse (I guess you are on Linux  and use that) moves files to trash and then creates a new one instead of correctly updating the file on Google Drive (each file has a unique ID which Keepass2Android uses). Fortunately, this was fixed: https://github.com/astrada/google-drive-ocamlfuse/issues/494. After activating this option, please select "Change database" in KP2A, tap ,"Open file" and browse to the file on Google Drive again. After that, the message should no longer pop up.
## I get a message "The name must not be empty: null" when opening from Google Drive
Please follow these steps:
* select "Change database" on the password screen, then "Open database" and browse to your file again
* go to Android app settings and disable all permissions for the KP2A app. Then try again to open the database file.
* reboot the device
(Before running the following steps, make sure you don't have local changes in your database which have not been synchronized with Google Drive (this can happen if you worked offline). If you have, please open the database from the local cache and go to settings - database settings - export database and make a backup copy of the data.)
* clear KP2A's app cache in the Android settings
* uninstall & reinstall
One of these has helped all users so far, but unfortunately it's not totally clear to me why different steps are required (or nothing for most users).
# For developers
If you are interested in adding new features, you have two options:
Either your features can be implemented as a plug-in. Please see [How to create a plug-in?](How-to-create-a-plug-in_.md) for more information. Or you add the features directly in the source code of the projects and create a pull request.
If you want to build Keepass2Android, check the [build guide](Build.readme.md).

View File

@@ -54,7 +54,7 @@ Please also add a few strings in your resource files (e.g. strings.xml) with the
These strings will be displayed to the user when KP2A asks if access should be granted.
## Modifying the entry view
You can add menu options for the full entry or for individual fields of the entry when displayed to the user. This is done, for example, by the QR plugin ([https://play.google.com/store/apps/details?id=keepass2android.plugin.qr](https://play.google.com/store/apps/details?id=keepass2android.plugin.qr)).
You can add menu options for the full entry or for individual fields of the entry when displayed to the user. This is done, for example, by the QR plugin ([https://play.google.com/store/apps/details?id=keepass2android.plugin.qr](https___play.google.com_store_apps_details_id=keepass2android.plugin.qr)).
In addition, it is even possible to add new fields or modify existing fields. Please see the sample plugin "PluginA" in the KP2A repository for a simple example on how to do this:
[https://keepass2android.codeplex.com/SourceControl/latest#src/java/PluginA/src/keepass2android/plugina/PluginAActionReceiver.java](https://keepass2android.codeplex.com/SourceControl/latest#src/java/PluginA/src/keepass2android/plugina/PluginAActionReceiver.java)

View File

@@ -8,20 +8,14 @@ The password database file can be synchronized across different devices. This wo
# Where to get it?
Regular stable releases of Keepass2Android are available on [Google Play](https://play.google.com/store/apps/details?id=keepass2android.keepass2android).
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet).
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet). Please join the [Beta tester group](https://plus.google.com/communities/107293657110547776032) for news and discussions about the latest beta releases.
# How can I contribute?
* Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](http://crowdin.net/project/keepass2android)
* Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts.
* [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC)
* [Make a donation](http://philipp.crocoll.net/donate.php)
# How do I learn more?
Please see the [documentation](Documentation.md).
# How do I build the project?
If you want to build Keepass2Android, check the [build guide](Build.readme.md).
The project homepage is https://philipp.crocoll.net/keepass2android/index.php
<img src="https://github.com/PhilippC/keepass2android/actions/workflows/build.yml/badge.svg" alt="build status" /> [Build status](https://github.com/PhilippC/keepass2android/actions)
[![Build Status](https://www.bitrise.io/app/43a23ab54dee9f7e/status.svg?token=2vryTsMQzTX3XRPikhgRwA&branch=nonet)](https://www.bitrise.io/app/43a23ab54dee9f7e)

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Xamarin.GooglePlayServices" version="27.0.0.0" />
</packages>

View File

@@ -12,8 +12,6 @@
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<AndroidClassParser>class-parse</AndroidClassParser>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -80,4 +78,4 @@
<Visible>False</Visible>
</XamarinComponentReference>
</ItemGroup>
</Project>
</Project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props" Condition="Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props')" />
<Import Project="..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props" Condition="Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -13,11 +11,8 @@
<AssemblyName>JavaFileStorageBindings</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<AndroidClassParser>class-parse</AndroidClassParser>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -50,130 +45,11 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Java.Interop" />
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors" />
<Reference Include="System.Xml" />
<Reference Include="Xamarin.AndroidX.Activity, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Activity.1.6.0\lib\monoandroid12.0\Xamarin.AndroidX.Activity.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Annotation, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Annotation.1.5.0\lib\monoandroid12.0\Xamarin.AndroidX.Annotation.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Annotation.Experimental, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\lib\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Arch.Core.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\lib\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Arch.Core.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\lib\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Collection, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Collection.1.2.0.4\lib\monoandroid12.0\Xamarin.AndroidX.Collection.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Concurrent.Futures, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\lib\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Core, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Core.1.9.0\lib\monoandroid12.0\Xamarin.AndroidX.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Core.Core.Ktx, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\lib\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.CustomView, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\lib\monoandroid12.0\Xamarin.AndroidX.CustomView.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Fragment, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Fragment.1.5.3\lib\monoandroid12.0\Xamarin.AndroidX.Fragment.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Lifecycle.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Lifecycle.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Lifecycle.ViewModel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Lifecycle.ViewModelSavedState, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Loader, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Loader.1.1.0.14\lib\monoandroid12.0\Xamarin.AndroidX.Loader.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.MultiDex, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\lib\monoandroid12.0\Xamarin.AndroidX.MultiDex.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.SavedState, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.SavedState.1.2.0\lib\monoandroid12.0\Xamarin.AndroidX.SavedState.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.Tracing.Tracing, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\lib\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.VersionedParcelable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\lib\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.dll</HintPath>
</Reference>
<Reference Include="Xamarin.AndroidX.ViewPager, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\lib\monoandroid12.0\Xamarin.AndroidX.ViewPager.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Google.Guava, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Google.Guava.28.2.0.1\lib\monoandroid90\Xamarin.Google.Guava.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Google.Guava.FailureAccess, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\lib\monoandroid90\Xamarin.Google.Guava.FailureAccess.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Google.Guava.ListenableFuture, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\lib\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Auth, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Auth.Api.Phone, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Auth.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.Base.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Base.118.1.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Base.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Basement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\lib\monoandroid12.0\Xamarin.GooglePlayServices.Basement.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Fido, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Fido.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Tasks, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\lib\monoandroid12.0\Xamarin.GooglePlayServices.Tasks.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Jetbrains.Annotations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\lib\monoandroid12.0\Xamarin.Jetbrains.Annotations.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Kotlin.StdLib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Kotlin.StdLib.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Kotlin.StdLib.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Kotlin.StdLib.Jdk7, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Kotlin.StdLib.Jdk8, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.dll</HintPath>
</Reference>
<Reference Include="Xamarin.KotlinX.Coroutines.Android, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\lib\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.dll</HintPath>
</Reference>
<Reference Include="Xamarin.KotlinX.Coroutines.Core.Jvm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\lib\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.dll</HintPath>
<Reference Include="GooglePlayServicesLib">
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -185,7 +61,6 @@
</LibraryProjectZip>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml" />
@@ -207,10 +82,10 @@
</XamarinComponentReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
<Project>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</Project>
<Name>PCloudBindings</Name>
</ProjectReference>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\msa-auth-0.8.6\classes-msa-auth.jar" />
@@ -221,12 +96,30 @@
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\commons-logging-1.1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-android-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-services-drive-v2-rev102-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-android-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson2-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-oauth-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\httpclient-4.0.3.jar" />
</ItemGroup>
@@ -239,6 +132,9 @@
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\json_simple-1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\jsr305-1.3.9.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-gson-1.16.0-rc.jar" />
</ItemGroup>
@@ -246,137 +142,15 @@
<EmbeddedReferenceJar Include="Jars\jackson-core-2.7.4.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-digest-2.5.jar" />
<EmbeddedReferenceJar Include="Jars\okhttp-3.9.0.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okio-2.9.0.jar" />
<EmbeddedReferenceJar Include="Jars\okio-1.13.0.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-4.10.0-RC1.jar" />
<EmbeddedJar Include="Jars\dropbox-core-sdk-3.1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-4.0.0.jar" />
<EmbeddedReferenceJar Include="Jars\gson-2.8.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gson-2.8.6.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-android-1.32.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\jsr305-3.0.2.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-1.32.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson2-1.32.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-1.30.5.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-android-1.30.5.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-oauth-client-1.30.4.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\opencensus-contrib-http-util-0.24.0.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\grpc-context-1.22.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\opencensus-api-0.24.0.jar" />
</ItemGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props'))" />
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets'))" />
</Target>
<Import Project="..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets')" />
<Import Project="..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets" Condition="Exists('..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets')" />
<Import Project="..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets" Condition="Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets')" />
<Import Project="..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets')" />
<Import Project="..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets" Condition="Exists('..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets')" />
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets')" />
<Import Project="..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets" Condition="Exists('..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets" Condition="Exists('..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets" Condition="Exists('..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets" Condition="Exists('..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets')" />
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets')" />
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets')" />
<Import Project="..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets" Condition="Exists('..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets')" />
<Import Project="..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets" Condition="Exists('..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets')" />
<Import Project="..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets')" />
</Project>

View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Xamarin.AndroidX.Activity" version="1.6.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Annotation" version="1.5.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Annotation.Experimental" version="1.3.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Arch.Core.Common" version="2.1.0.15" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Arch.Core.Runtime" version="2.1.0.15" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Collection" version="1.2.0.4" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Concurrent.Futures" version="1.1.0.9" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Core" version="1.9.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Core.Core.Ktx" version="1.9.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.CustomView" version="1.1.0.13" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Fragment" version="1.5.3" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Lifecycle.Common" version="2.5.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Lifecycle.LiveData.Core" version="2.5.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Lifecycle.Runtime" version="2.5.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Lifecycle.ViewModel" version="2.5.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Lifecycle.ViewModelSavedState" version="2.5.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Loader" version="1.1.0.14" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Migration" version="1.0.10" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.MultiDex" version="2.0.1.13" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.SavedState" version="1.2.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.Tracing.Tracing" version="1.1.0.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.VersionedParcelable" version="1.1.1.14" targetFramework="monoandroid13.0" />
<package id="Xamarin.AndroidX.ViewPager" version="1.0.0.14" targetFramework="monoandroid13.0" />
<package id="Xamarin.Build.Download" version="0.11.4" targetFramework="monoandroid13.0" />
<package id="Xamarin.Google.Guava" version="28.2.0.1" targetFramework="monoandroid90" />
<package id="Xamarin.Google.Guava.FailureAccess" version="1.0.1.3" targetFramework="monoandroid90" />
<package id="Xamarin.Google.Guava.ListenableFuture" version="1.0.0.9" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Auth" version="120.4.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Auth.Api.Phone" version="118.0.1.2" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Auth.Base" version="118.0.6" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Base" version="118.1.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Basement" version="118.1.0.1" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Fido" version="119.0.0" targetFramework="monoandroid13.0" />
<package id="Xamarin.GooglePlayServices.Tasks" version="118.0.2" targetFramework="monoandroid13.0" />
<package id="Xamarin.Jetbrains.Annotations" version="23.0.0.4" targetFramework="monoandroid13.0" />
<package id="Xamarin.Kotlin.StdLib" version="1.7.10" targetFramework="monoandroid13.0" />
<package id="Xamarin.Kotlin.StdLib.Common" version="1.7.10" targetFramework="monoandroid13.0" />
<package id="Xamarin.Kotlin.StdLib.Jdk7" version="1.7.10" targetFramework="monoandroid13.0" />
<package id="Xamarin.Kotlin.StdLib.Jdk8" version="1.7.10" targetFramework="monoandroid13.0" />
<package id="Xamarin.KotlinX.Coroutines.Android" version="1.6.4" targetFramework="monoandroid13.0" />
<package id="Xamarin.KotlinX.Coroutines.Core.Jvm" version="1.6.4" targetFramework="monoandroid13.0" />
</packages>

View File

@@ -12,8 +12,6 @@
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<AndroidClassParser>class-parse</AndroidClassParser>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -78,4 +76,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -1,18 +1,18 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android", "keepass2android\keepass2android.csproj", "{A6CF8A86-37C1-4197-80FE-519DE2C842F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aKeyboardBinding", "Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj", "{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aBusinessLogic", "Kp2aBusinessLogic\Kp2aBusinessLogic.csproj", "{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwofishCipher", "TwofishCipher\TwofishCipher.csproj", "{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaFileStorageBindings", "JavaFileStorageBindings\JavaFileStorageBindings.csproj", "{48574278-4779-4B3A-A9E4-9CF1BC285D0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidFileChooserBinding", "AndroidFileChooserBinding\AndroidFileChooserBinding.csproj", "{3C0F7FE5-639F-4422-A087-8B26CF862D1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj", "{70D3844A-D9FA-4A64-B205-A84C6A822196}"
@@ -21,14 +21,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginS
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZlibAndroid", "ZlibAndroid\ZlibAndroid.csproj", "{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android-app", "keepass2android\keepass2android-app.csproj", "{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParser", "Kp2aAutofillParser\Kp2aAutofillParser.csproj", "{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -67,6 +63,34 @@ Global
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Win32.Build.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|x64.ActiveCfg = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|x64.Build.0 = Debug|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Build.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Deploy.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Win32.ActiveCfg = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Win32.Build.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|x64.ActiveCfg = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|x64.Build.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.Deploy.0 = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.Deploy.0 = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -83,8 +107,8 @@ Global
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|Win32.Build.0 = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.ActiveCfg = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.Build.0 = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -161,8 +185,8 @@ Global
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Win32.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|x64.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -179,8 +203,8 @@ Global
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Win32.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|x64.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -197,8 +221,8 @@ Global
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Win32.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|x64.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -227,6 +251,24 @@ Global
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Win32.ActiveCfg = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|x64.ActiveCfg = Debug|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.Build.0 = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Win32.ActiveCfg = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|x64.ActiveCfg = Release|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -251,90 +293,6 @@ Global
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.ActiveCfg = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.Build.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.Deploy.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.ActiveCfg = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.Build.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.Deploy.0 = Debug|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.Deploy.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,11 +33,8 @@ namespace KeePassLib.Collections
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
private readonly bool m_bDedupAdd;
public ProtectedBinarySet(bool bDedupAdd)
public ProtectedBinarySet()
{
m_bDedupAdd = bDedupAdd;
}
IEnumerator IEnumerable.GetEnumerator()
@@ -50,10 +47,15 @@ namespace KeePassLib.Collections
return m_d.GetEnumerator();
}
public void Clear()
{
m_d.Clear();
}
private int GetFreeID()
{
int i = m_d.Count;
while (m_d.ContainsKey(i)) { ++i; }
while(m_d.ContainsKey(i)) { ++i; }
Debug.Assert(i == m_d.Count); // m_d.Count should be free
return i;
}
@@ -61,7 +63,7 @@ namespace KeePassLib.Collections
public ProtectedBinary Get(int iID)
{
ProtectedBinary pb;
if (m_d.TryGetValue(iID, out pb)) return pb;
if(m_d.TryGetValue(iID, out pb)) return pb;
// Debug.Assert(false); // No assert
return null;
@@ -69,12 +71,12 @@ namespace KeePassLib.Collections
public int Find(ProtectedBinary pb)
{
if (pb == null) { Debug.Assert(false); return -1; }
if(pb == null) { Debug.Assert(false); return -1; }
// Fast search by reference
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if (object.ReferenceEquals(pb, kvp.Value))
if(object.ReferenceEquals(pb, kvp.Value))
{
Debug.Assert(pb.Equals(kvp.Value));
return kvp.Key;
@@ -82,9 +84,9 @@ namespace KeePassLib.Collections
}
// Slow search by content
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if (pb.Equals(kvp.Value)) return kvp.Key;
if(pb.Equals(kvp.Value)) return kvp.Key;
}
// Debug.Assert(false); // No assert
@@ -93,26 +95,28 @@ namespace KeePassLib.Collections
public void Set(int iID, ProtectedBinary pb)
{
if (iID < 0) { Debug.Assert(false); return; }
if (pb == null) { Debug.Assert(false); return; }
if(iID < 0) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
m_d[iID] = pb;
}
public void Add(ProtectedBinary pb)
{
if (pb == null) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
if (m_bDedupAdd && (Find(pb) >= 0)) return; // Exists already
int i = Find(pb);
if(i >= 0) return; // Exists already
m_d[GetFreeID()] = pb;
i = GetFreeID();
m_d[i] = pb;
}
public void AddFrom(ProtectedBinaryDictionary d)
{
if (d == null) { Debug.Assert(false); return; }
if(d == null) { Debug.Assert(false); return; }
foreach (KeyValuePair<string, ProtectedBinary> kvp in d)
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
{
Add(kvp.Value);
}
@@ -120,16 +124,16 @@ namespace KeePassLib.Collections
public void AddFrom(PwGroup pg)
{
if (pg == null) { Debug.Assert(false); return; }
if(pg == null) { Debug.Assert(false); return; }
EntryHandler eh = delegate (PwEntry pe)
EntryHandler eh = delegate(PwEntry pe)
{
if (pe == null) { Debug.Assert(false); return true; }
if(pe == null) { Debug.Assert(false); return true; }
AddFrom(pe.Binaries);
foreach (PwEntry peHistory in pe.History)
foreach(PwEntry peHistory in pe.History)
{
if (peHistory == null) { Debug.Assert(false); continue; }
if(peHistory == null) { Debug.Assert(false); continue; }
AddFrom(peHistory.Binaries);
}
@@ -144,9 +148,9 @@ namespace KeePassLib.Collections
int n = m_d.Count;
ProtectedBinary[] v = new ProtectedBinary[n];
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if ((kvp.Key < 0) || (kvp.Key >= n))
if((kvp.Key < 0) || (kvp.Key >= n))
{
Debug.Assert(false);
throw new InvalidOperationException();
@@ -155,9 +159,9 @@ namespace KeePassLib.Collections
v[kvp.Key] = kvp.Value;
}
for (int i = 0; i < n; ++i)
for(int i = 0; i < n; ++i)
{
if (v[i] == null)
if(v[i] == null)
{
Debug.Assert(false);
throw new InvalidOperationException();

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Diagnostics;
using KeePassLib.Interfaces;
@@ -34,74 +34,48 @@ namespace KeePassLib.Collections
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_d =
private SortedDictionary<string, string> m_dict =
new SortedDictionary<string, string>();
// Non-null if and only if last mod. times should be remembered
private Dictionary<string, DateTime> m_dLastMod = null;
public int Count
{
get { return m_d.Count; }
get { return m_dict.Count; }
}
public StringDictionaryEx()
{
}
internal StringDictionaryEx(bool bRememberLastMod)
{
if (bRememberLastMod) m_dLastMod = new Dictionary<string, DateTime>();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_d.GetEnumerator();
return m_dict.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_d.GetEnumerator();
return m_dict.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach (KeyValuePair<string, string> kvp in m_d)
sdNew.m_d[kvp.Key] = kvp.Value;
foreach(KeyValuePair<string, string> kvp in m_dict)
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
if (m_dLastMod != null)
sdNew.m_dLastMod = new Dictionary<string, DateTime>(m_dLastMod);
Debug.Assert(Equals(sdNew));
return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if (sdOther == null) { Debug.Assert(false); return false; }
if(sdOther == null) { Debug.Assert(false); return false; }
if (m_d.Count != sdOther.m_d.Count) return false;
if(m_dict.Count != sdOther.m_dict.Count) return false;
foreach (KeyValuePair<string, string> kvp in sdOther.m_d)
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
{
string str = Get(kvp.Key);
if ((str == null) || (str != kvp.Value)) return false;
}
int cLastModT = ((m_dLastMod != null) ? m_dLastMod.Count : -1);
int cLastModO = ((sdOther.m_dLastMod != null) ? sdOther.m_dLastMod.Count : -1);
if (cLastModT != cLastModO) return false;
if (m_dLastMod != null)
{
foreach (KeyValuePair<string, DateTime> kvp in sdOther.m_dLastMod)
{
DateTime? odt = GetLastModificationTime(kvp.Key);
if (!odt.HasValue) return false;
if (odt.Value != kvp.Value) return false;
}
if((str == null) || (str != kvp.Value)) return false;
}
return true;
@@ -109,62 +83,48 @@ namespace KeePassLib.Collections
public string Get(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string str;
m_d.TryGetValue(strName, out str);
return str;
}
internal DateTime? GetLastModificationTime(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod == null) return null;
DateTime dt;
if (m_dLastMod.TryGetValue(strName, out dt)) return dt;
string s;
if(m_dict.TryGetValue(strName, out s)) return s;
return null;
}
public bool Exists(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_d.ContainsKey(strName);
return m_dict.ContainsKey(strName);
}
public void Set(string strName, string strValue)
/// <summary>
/// Set a string.
/// </summary>
/// <param name="strField">Identifier of the string field to modify.</param>
/// <param name="strNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
m_d[strName] = strValue;
if (m_dLastMod != null) m_dLastMod[strName] = DateTime.UtcNow;
m_dict[strField] = strNewValue;
}
internal void Set(string strName, string strValue, DateTime? odtLastMod)
/// <summary>
/// Delete a string.
/// </summary>
/// <param name="strField">Name of the string field to delete.</param>
/// <returns>Returns <c>true</c> if the field has been successfully
/// removed, otherwise the return value is <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(string strField)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
m_d[strName] = strValue;
if (m_dLastMod != null)
{
if (odtLastMod.HasValue) m_dLastMod[strName] = odtLastMod.Value;
else m_dLastMod.Remove(strName);
}
}
public bool Remove(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod != null) m_dLastMod.Remove(strName);
return m_d.Remove(strName);
return m_dict.Remove(strField);
}
}
}

View File

@@ -28,29 +28,15 @@ using KeePassLib.Resources;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class ChaCha20Engine : ICipherEngine2
{
private static PwUuid m_uuid = null;
internal static PwUuid ChaCha20Uuid
{
get
{
PwUuid pu = m_uuid;
if (pu == null)
{
pu = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A });
m_uuid = pu;
}
return pu;
}
}
{
private PwUuid m_uuid = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A
});
public PwUuid CipherUuid
{
get { return ChaCha20Uuid; }
get { return m_uuid; }
}
public string DisplayName

View File

@@ -367,27 +367,5 @@ namespace KeePassLib.Cryptography
Debug.Assert(iPos == pbRes.Length);
return pbRes;
}
private static int g_iWeakSeed = 0;
public static Random NewWeakRandom()
{
long s64 = DateTime.UtcNow.ToBinary();
int s32 = (int)((s64 >> 32) ^ s64);
lock (g_oSyncRoot)
{
unchecked
{
g_iWeakSeed += 0x78A8C4B7; // Prime number
s32 ^= g_iWeakSeed;
}
}
// Prevent overflow in the Random constructor of .NET 2.0
if (s32 == int.MinValue) s32 = int.MaxValue;
return new Random(s32);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,67 +20,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public static class CryptoUtil
{
private static bool? g_obProtData = null;
public static bool IsProtectedDataSupported
{
get
{
if (g_obProtData.HasValue) return g_obProtData.Value;
bool b = false;
try
{
Random r = CryptoRandom.NewWeakRandom();
byte[] pbData = new byte[137];
r.NextBytes(pbData);
byte[] pbEnt = new byte[41];
r.NextBytes(pbEnt);
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
{
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
b = true;
}
}
catch (Exception) { Debug.Assert(false); }
Debug.Assert(b); // Should be supported on all systems
g_obProtData = b;
return b;
}
}
public static byte[] HashSha256(byte[] pbData)
{
if (pbData == null) throw new ArgumentNullException("pbData");
if(pbData == null) throw new ArgumentNullException("pbData");
return HashSha256(pbData, 0, pbData.Length);
}
public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount)
{
if (pbData == null) throw new ArgumentNullException("pbData");
if(pbData == null) throw new ArgumentNullException("pbData");
#if DEBUG
byte[] pbCopy = new byte[pbData.Length];
@@ -88,7 +49,7 @@ namespace KeePassLib.Cryptography
#endif
byte[] pbHash;
using (SHA256Managed h = new SHA256Managed())
using(SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
@@ -105,22 +66,6 @@ namespace KeePassLib.Cryptography
return pbHash;
}
internal static byte[] HashSha256(string strFilePath)
{
byte[] pbHash = null;
using (FileStream fs = new FileStream(strFilePath, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
using (SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(fs);
}
}
return pbHash;
}
/// <summary>
/// Create a cryptographic key of length <paramref name="cbOut" />
/// (in bytes) from <paramref name="pbIn" />.
@@ -128,34 +73,34 @@ namespace KeePassLib.Cryptography
public static byte[] ResizeKey(byte[] pbIn, int iInOffset,
int cbIn, int cbOut)
{
if (pbIn == null) throw new ArgumentNullException("pbIn");
if (cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if(pbIn == null) throw new ArgumentNullException("pbIn");
if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if (cbOut == 0) return MemUtil.EmptyByteArray;
if(cbOut == 0) return MemUtil.EmptyByteArray;
byte[] pbHash;
if (cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
else
{
using (SHA512Managed h = new SHA512Managed())
using(SHA512Managed h = new SHA512Managed())
{
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
}
}
if (cbOut == pbHash.Length) return pbHash;
if(cbOut == pbHash.Length) return pbHash;
byte[] pbRet = new byte[cbOut];
if (cbOut < pbHash.Length)
if(cbOut < pbHash.Length)
Array.Copy(pbHash, pbRet, cbOut);
else
{
int iPos = 0;
ulong r = 0;
while (iPos < cbOut)
while(iPos < cbOut)
{
Debug.Assert(pbHash.Length == 64);
using (HMACSHA256 h = new HMACSHA256(pbHash))
using(HMACSHA256 h = new HMACSHA256(pbHash))
{
byte[] pbR = MemUtil.UInt64ToBytes(r);
byte[] pbPart = h.ComputeHash(pbR);
@@ -180,75 +125,5 @@ namespace KeePassLib.Cryptography
MemUtil.ZeroByteArray(pbHash);
return pbRet;
}
#if !KeePassUAP
private static bool? g_obAesCsp = null;
internal static SymmetricAlgorithm CreateAes()
{
if (g_obAesCsp.HasValue)
return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged());
SymmetricAlgorithm a = CreateAesCsp();
g_obAesCsp = (a != null);
return (a ?? new RijndaelManaged());
}
private static SymmetricAlgorithm CreateAesCsp()
{
try
{
// On Windows, the CSP implementation is only minimally
// faster (and for key derivations it's not used anyway,
// as KeePass uses a native implementation based on
// CNG/BCrypt, which is much faster)
if (!NativeLib.IsUnix()) return null;
string strFqn = Assembly.CreateQualifiedName(
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Security.Cryptography.AesCryptoServiceProvider");
Type t = Type.GetType(strFqn);
if (t == null) return null;
return (Activator.CreateInstance(t) as SymmetricAlgorithm);
}
catch (Exception) { Debug.Assert(false); }
return null;
}
#endif
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, true, pbOptEntropy, s);
}
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, false, pbOptEntropy, s);
}
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
byte[] pbOptEntropy, DataProtectionScope s)
{
if (pb == null) throw new ArgumentNullException("pb");
if ((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
pbOptEntropy = null;
if (CryptoUtil.IsProtectedDataSupported)
{
if (bProtect)
return ProtectedData.Protect(pb, pbOptEntropy, s);
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
}
Debug.Assert(false);
byte[] pbCopy = new byte[pb.Length];
Array.Copy(pb, pbCopy, pb.Length);
return pbCopy;
}
}
}

View File

@@ -46,8 +46,6 @@ namespace KeePassLib.Cryptography.KeyDerivation
private const ulong NbBlockSizeInQW = NbBlockSize / 8UL;
private const ulong NbSyncPoints = 4;
private const ulong NbAddressesInBlock = 128;
private const int NbPreHashDigestLength = 64;
private const int NbPreHashSeedLength = NbPreHashDigestLength + 8;
@@ -58,7 +56,6 @@ namespace KeePassLib.Cryptography.KeyDerivation
private sealed class Argon2Ctx
{
public Argon2Type Type = Argon2Type.D;
public uint Version = 0;
public ulong Lanes = 0;
@@ -92,9 +89,9 @@ namespace KeePassLib.Cryptography.KeyDerivation
}
}
private byte[] Argon2Transform(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
{
pbSecretKey = (pbSecretKey ?? MemUtil.EmptyByteArray);
pbAssocData = (pbAssocData ?? MemUtil.EmptyByteArray);
@@ -104,7 +101,6 @@ namespace KeePassLib.Cryptography.KeyDerivation
#endif
Argon2Ctx ctx = new Argon2Ctx();
ctx.Type = m_t;
ctx.Version = uVersion;
ctx.Lanes = uParallel;
@@ -141,7 +137,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(uVersion, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)m_t, pbBuf, 0);
MemUtil.UInt32ToBytesEx(0, pbBuf, 0); // Argon2d type = 0
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
@@ -491,43 +487,18 @@ namespace KeePassLib.Cryptography.KeyDerivation
private static void FillSegmentThr(object o)
{
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
if (ti == null) { Debug.Assert(false); return; }
if(ti == null) { Debug.Assert(false); return; }
try
{
Argon2Ctx ctx = ti.Context;
if (ctx == null) { Debug.Assert(false); return; }
if(ctx == null) { Debug.Assert(false); return; }
Debug.Assert(ctx.Version >= MinVersion);
bool bCanXor = (ctx.Version >= 0x13U);
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
ulong[] pbAddrInputZero = null;
bool bDataIndependentAddr = ((ctx.Type == Argon2Type.ID) &&
(ti.Pass == 0) && (ti.Slice < (NbSyncPoints / 2)));
if (bDataIndependentAddr)
{
pbAddrInputZero = new ulong[NbBlockSizeInQW * 3];
const int iInput = (int)NbBlockSizeInQW;
pbAddrInputZero[iInput] = ti.Pass;
pbAddrInputZero[iInput + 1] = ti.Lane;
pbAddrInputZero[iInput + 2] = ti.Slice;
pbAddrInputZero[iInput + 3] = ctx.MemoryBlocks;
pbAddrInputZero[iInput + 4] = ctx.TCost;
pbAddrInputZero[iInput + 5] = (ulong)ctx.Type;
}
ulong uStart = 0;
if ((ti.Pass == 0) && (ti.Slice == 0))
{
uStart = 2;
if (bDataIndependentAddr)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
}
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
@@ -535,23 +506,17 @@ namespace KeePassLib.Cryptography.KeyDerivation
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
for (ulong i = uStart; i < ctx.SegmentLength; ++i)
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if ((uCur % ctx.LaneLength) == 1)
if((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand;
if (bDataIndependentAddr)
{
ulong iMod = i % NbAddressesInBlock;
if (iMod == 0)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
uPseudoRand = pbAddrInputZero[iMod];
}
else uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
if ((ti.Pass == 0) && (ti.Slice == 0))
if((ti.Pass == 0) && (ti.Slice == 0))
uRefLane = ti.Lane;
ti.Index = i;
@@ -571,12 +536,11 @@ namespace KeePassLib.Cryptography.KeyDerivation
MemUtil.ZeroArray<ulong>(pbR);
MemUtil.ZeroArray<ulong>(pbTmp);
if (pbAddrInputZero != null) MemUtil.ZeroArray<ulong>(pbAddrInputZero);
}
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
try { ti.Finished.Set(); }
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
}
#if ARGON2_B2ROUND_ARRAYS
@@ -646,19 +610,6 @@ namespace KeePassLib.Cryptography.KeyDerivation
XorBlock(pMem, uNext, pbR, 0);
}
private static void NextAddresses(ulong[] pbAddrInputZero, ulong[] pbR,
ulong[] pbTmp)
{
// pbAddrInputZero contains an address block, an input block and a zero block
const ulong uAddr = 0;
const ulong uInput = NbBlockSizeInQW;
const ulong uZero = NbBlockSizeInQW * 2;
++pbAddrInputZero[uInput + 6];
FillBlock(pbAddrInputZero, uZero, uInput, uAddr, false, pbR, pbTmp);
FillBlock(pbAddrInputZero, uZero, uAddr, uAddr, false, pbR, pbTmp);
}
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
{
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];

View File

@@ -25,22 +25,11 @@ using System.Text;
namespace KeePassLib.Cryptography.KeyDerivation
{
public enum Argon2Type
{
// The values must be the same as in the Argon2 specification
D = 0,
ID = 2
}
public sealed partial class Argon2Kdf : KdfEngine
public sealed partial class Argon2Kdf : KdfEngine
{
private static readonly PwUuid g_uuidD = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
private static readonly PwUuid g_uuidID = new PwUuid(new byte[] {
0x9E, 0x29, 0x8B, 0x19, 0x56, 0xDB, 0x47, 0x73,
0xB2, 0x3D, 0xFC, 0x3E, 0xC6, 0xF0, 0xA1, 0xE6 });
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
public const string ParamSalt = "S"; // Byte[]
public const string ParamParallelism = "P"; // UInt32
@@ -66,37 +55,28 @@ namespace KeePassLib.Cryptography.KeyDerivation
internal const uint MinParallelism = 1;
internal const uint MaxParallelism = (1 << 24) - 1;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 64 * 1024 * 1024; // 64 MB
internal const uint DefaultParallelism = 2;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
private readonly Argon2Type m_t;
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override PwUuid Uuid
{
get { return ((m_t == Argon2Type.D) ? g_uuidD : g_uuidID); }
}
public override string Name
{
get { return "Argon2"; }
}
public override string Name
{
get { return ((m_t == Argon2Type.D) ? "Argon2d" : "Argon2id"); }
}
public Argon2Kdf() : this(Argon2Type.D)
{
}
public Argon2Kdf(Argon2Type t)
{
if ((t != Argon2Type.D) && (t != Argon2Type.ID))
throw new NotSupportedException();
m_t = t;
}
public override byte[] GetSeed(KdfParameters p)
{ return p.GetByteArray(ParamSalt); }
public override KdfParameters GetDefaultParameters()
public Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
@@ -112,7 +92,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
@@ -148,59 +128,46 @@ namespace KeePassLib.Cryptography.KeyDerivation
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
byte[] pbRet;
if (m_t == Argon2Type.ID)
{
pbRet = Argon2Transform(pbMsg, pbSalt, uPar, uMem,
uIt, 32, v, pbSecretKey, pbAssocData);
}
else
{
if (pbSecretKey != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbSecretKey");
}
if (pbAssocData != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbAssocData");
}
/*
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
*/
IntPtr msgPtr = Marshal.AllocHGlobal(pbMsg.Length);
IntPtr saltPtr = Marshal.AllocHGlobal(pbSalt.Length);
IntPtr retPtr = Marshal.AllocHGlobal(32);
Marshal.Copy(pbMsg, 0, msgPtr, pbMsg.Length);
Marshal.Copy(pbSalt, 0, saltPtr, pbSalt.Length);
const UInt32 Argon2_d = 0;
int ret = argon2_hash(
(UInt32)uIt, (UInt32)(uMem / 1024), uPar,
msgPtr, (IntPtr)pbMsg.Length,
saltPtr, (IntPtr)pbSalt.Length,
retPtr, (IntPtr)32,
(IntPtr)0, (IntPtr)0, Argon2_d, v);
if (ret != 0)
{
throw new Exception("argon2_hash failed with " + ret);
}
pbRet = new byte[32];
Marshal.Copy(retPtr, pbRet, 0, 32);
Marshal.FreeHGlobal(msgPtr);
Marshal.FreeHGlobal(saltPtr);
Marshal.FreeHGlobal(retPtr);
if (pbSecretKey != null) {
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbSecretKey");
}
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
if (pbAssocData != null) {
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbAssocData");
}
/*
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
*/
IntPtr msgPtr = Marshal.AllocHGlobal(pbMsg.Length);
IntPtr saltPtr = Marshal.AllocHGlobal(pbSalt.Length);
IntPtr retPtr = Marshal.AllocHGlobal(32);
Marshal.Copy(pbMsg, 0, msgPtr, pbMsg.Length);
Marshal.Copy(pbSalt, 0, saltPtr, pbSalt.Length);
const UInt32 Argon2_d = 0;
int ret = argon2_hash(
(UInt32)uIt, (UInt32)(uMem / 1024), uPar,
msgPtr, (IntPtr)pbMsg.Length,
saltPtr, (IntPtr)pbSalt.Length,
retPtr, (IntPtr)32,
(IntPtr)0, (IntPtr)0, Argon2_d, v);
if (ret != 0) {
throw new Exception("argon2_hash failed with " + ret);
}
byte[] pbRet = new byte[32];
Marshal.Copy(retPtr, pbRet, 0, 32);
Marshal.FreeHGlobal(msgPtr);
Marshal.FreeHGlobal(saltPtr);
Marshal.FreeHGlobal(retPtr);
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,11 +41,10 @@ namespace KeePassLib.Cryptography.KeyDerivation
private static void EnsureInitialized()
{
if(g_l.Count != 0) return;
if(g_l.Count > 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf(Argon2Type.D));
g_l.Add(new Argon2Kdf(Argon2Type.ID));
g_l.Add(new Argon2Kdf());
}
internal static KdfParameters GetDefaultParameters()

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,22 +21,17 @@ using System;
namespace KeePassLib.Interfaces
{
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
PwGroup ParentGroup
{
get;
}
PwUuid PreviousParentGroup
{
get;
}
}
}
PwGroup ParentGroup
{
get;
}
}
}

View File

@@ -50,7 +50,6 @@
<HintPath>..\ProtoBuf\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Security" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Mono.Android" />
@@ -72,9 +71,7 @@
<Compile Include="Cryptography\KeyDerivation\KdfParameters.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfPool.cs" />
<Compile Include="IDatabaseFormat.cs" />
<Compile Include="Keys\KcpKeyFile.Xml.cs" />
<Compile Include="Kp2aLog.cs" />
<Compile Include="PwGroup.Search.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Resources\KLRes.Generated.cs" />
<Compile Include="Resources\KSRes.Generated.cs" />
@@ -163,7 +160,6 @@
<Compile Include="Utility\UrlUtil.cs" />
<Compile Include="Utility\TimeUtil.cs" />
<Compile Include="Delegates\Handlers.cs" />
<Compile Include="Utility\XmlUtilEx.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -1,281 +0,0 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
[XmlType("KeyFile")]
public sealed class KfxFile
{
private const ulong KfxVersionCriticalMask = 0xFFFF000000000000UL;
private const int KfxDataHashLength = 4;
private KfxMeta m_meta = new KfxMeta();
public KfxMeta Meta
{
get { return m_meta; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_meta = value;
}
}
private KfxKey m_key = new KfxKey();
public KfxKey Key
{
get { return m_key; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_key = value;
}
}
public static KfxFile Create(ulong uVersion, byte[] pbKey, byte[] pbHash)
{
if(pbKey == null) throw new ArgumentNullException("pbKey");
if(pbKey.Length == 0) throw new ArgumentOutOfRangeException("pbKey");
if(uVersion == 0) uVersion = 0x0002000000000000;
// Null hash: generate one, empty hash: store no hash
if(pbHash == null) pbHash = HashData(pbKey);
VerifyHash(pbKey, pbHash);
KfxFile kf = new KfxFile();
if(uVersion == 0x0001000000000000)
kf.Meta.Version = "1.00"; // KeePass <= 2.46 used two zeros
else kf.Meta.Version = StrUtil.VersionToString(uVersion, 2);
if(uVersion == 0x0001000000000000)
kf.Key.Data.Value = Convert.ToBase64String(pbKey);
else if(uVersion == 0x0002000000000000)
{
kf.Key.Data.Value = FormatKeyHex(pbKey, 3);
if(pbHash.Length != 0)
kf.Key.Data.Hash = MemUtil.ByteArrayToHexString(pbHash);
}
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return kf;
}
internal static KfxFile Create(ulong uVersion, string strKey, string strHash)
{
byte[] pbKey = ParseKey(uVersion, strKey);
byte[] pbHash = ((strHash != null) ? ParseHash(strHash) : null);
return Create(uVersion, pbKey, pbHash);
}
internal static bool CanLoad(string strFilePath)
{
if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return false; }
try
{
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
using(Stream s = IOConnection.OpenRead(ioc))
{
return (Load(s) != null);
}
}
catch(Exception) { }
return false;
}
public static KfxFile Load(Stream s)
{
return XmlUtilEx.Deserialize<KfxFile>(s);
}
public void Save(Stream s)
{
XmlUtilEx.Serialize<KfxFile>(s, this, true);
}
private static string FormatKeyHex(byte[] pb, int cTabs)
{
StringBuilder sb = new StringBuilder();
string str = MemUtil.ByteArrayToHexString(pb);
for(int i = 0; i < str.Length; ++i)
{
if((i & 0x1F) == 0)
{
sb.AppendLine();
sb.Append('\t', cTabs);
}
else if((i & 0x07) == 0) sb.Append(' ');
sb.Append(str[i]);
}
sb.AppendLine();
if(cTabs > 0) sb.Append('\t', cTabs - 1);
return sb.ToString();
}
private ulong GetVersion()
{
string str = m_meta.Version;
if(string.IsNullOrEmpty(str)) return 0;
return StrUtil.ParseVersion(str);
}
public byte[] GetKey()
{
ulong uVersion = GetVersion();
byte[] pbKey = ParseKey(uVersion, m_key.Data.Value);
if((pbKey == null) || (pbKey.Length == 0))
throw new FormatException(KLRes.FileCorrupted);
byte[] pbHash = ParseHash(m_key.Data.Hash);
VerifyHash(pbKey, pbHash);
return pbKey;
}
private static byte[] HashData(byte[] pb)
{
return MemUtil.Mid(CryptoUtil.HashSha256(pb), 0, KfxDataHashLength);
}
private static void VerifyHash(byte[] pbKey, byte[] pbHash)
{
// The hash is optional; empty hash means success
if((pbHash == null) || (pbHash.Length == 0)) return;
byte[] pbHashCmp = HashData(pbKey);
if(!MemUtil.ArraysEqual(pbHash, pbHashCmp))
throw new Exception("Keyfile hash mismatch!");
}
private static byte[] ParseKey(ulong uVersion, string strKey)
{
if(strKey == null) throw new ArgumentNullException("strKey");
strKey = StrUtil.RemoveWhiteSpace(strKey);
if(string.IsNullOrEmpty(strKey)) return MemUtil.EmptyByteArray;
uVersion &= KfxVersionCriticalMask;
byte[] pbKey;
if(uVersion == 0x0001000000000000)
pbKey = Convert.FromBase64String(strKey);
else if(uVersion == 0x0002000000000000)
pbKey = ParseHex(strKey);
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return pbKey;
}
private static byte[] ParseHash(string strHash)
{
return ParseHex(strHash);
}
private static byte[] ParseHex(string str)
{
if(str == null) throw new ArgumentNullException("str");
if(str.Length == 0) return MemUtil.EmptyByteArray;
if(((str.Length & 1) != 0) || !StrUtil.IsHexString(str, true))
throw new FormatException();
return MemUtil.HexStringToByteArray(str);
}
}
public sealed class KfxMeta
{
private string m_strVersion = string.Empty;
[DefaultValue("")]
public string Version
{
get { return m_strVersion; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strVersion = value;
}
}
}
public sealed class KfxKey
{
private KfxData m_data = new KfxData();
public KfxData Data
{
get { return m_data; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_data = value;
}
}
}
public sealed class KfxData
{
private string m_strHash = string.Empty;
[DefaultValue("")]
[XmlAttribute("Hash")]
public string Hash
{
get { return m_strHash; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strHash = value;
}
}
private string m_strValue = string.Empty;
[DefaultValue("")]
[XmlText]
public string Value
{
get { return m_strValue; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strValue = value;
}
}
}
}

View File

@@ -127,9 +127,10 @@ namespace KeePassLib.Keys
#endif
}
byte[] pbKey = LoadKeyFile(pbFileData);
byte[] pbKey = LoadXmlKeyFile(pbFileData);
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
if (pbKey == null) throw new InvalidOperationException();
if(pbKey == null) throw new InvalidOperationException();
m_ioc = iocKeyFile;
m_pbKeyData = new ProtectedBinary(true, pbKey);
@@ -149,60 +150,54 @@ namespace KeePassLib.Keys
// }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if (pbFileData == null) throw new ArgumentNullException("pbFileData");
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
byte[] pbKey = LoadKeyFileXml(pbFileData);
if (pbKey != null) return pbKey;
int iLength = pbFileData.Length;
int cb = pbFileData.Length;
if (cb == 32) return pbFileData;
byte[] pbKey = null;
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
if (cb == 64)
{
pbKey = LoadKeyFileHex(pbFileData);
if (pbKey != null) return pbKey;
}
if(pbKey == null)
pbKey = CryptoUtil.HashSha256(pbFileData);
return CryptoUtil.HashSha256(pbFileData);
}
return pbKey;
}
private static byte[] LoadKeyFileXml(byte[] pbFileData)
{
KfxFile kf;
try
{
using (MemoryStream ms = new MemoryStream(pbFileData, false))
{
kf = KfxFile.Load(ms);
}
}
catch (Exception) { return null; }
private static byte[] LoadBinaryKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
// We have a syntactically valid XML key file;
// failing to verify the key should throw an exception
return ((kf != null) ? kf.GetKey() : null);
}
return pbFileData;
}
private static byte[] LoadKeyFileHex(byte[] pbFileData)
{
if (pbFileData == null) { Debug.Assert(false); return null; }
private static byte[] LoadHexKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
try
{
int cc = pbFileData.Length;
if ((cc & 1) != 0) { Debug.Assert(false); return null; }
try
{
if(!StrUtil.IsHexString(pbFileData, true)) return null;
if (!StrUtil.IsHexString(pbFileData, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null;
}
string strHex = StrUtil.Utf8.GetString(pbFileData);
return MemUtil.HexStringToByteArray(strHex);
}
catch (Exception) { Debug.Assert(false); }
return pbKey;
}
catch(Exception) { Debug.Assert(false); }
return null;
}
return null;
}
/// <summary>
/// Create a new, random key-file.
/// </summary>

View File

@@ -118,10 +118,5 @@ namespace keepass2android
sendIntent.SetType("text/plain");
ctx.StartActivity(Intent.CreateChooser(sendIntent, "Send log to..."));
}
public static void LogTask(object task, string activityName)
{
Log($"Task in activity {activityName} changed to {task?.GetType()?.Name ?? "null"}");
}
}
}
}

View File

@@ -1,6 +1,8 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,106 +20,61 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if !KeePassUAP
using System.Drawing;
#endif
using System.IO;
using KeePassLib.Utility;
namespace KeePassLib
{
/// <summary>
/// Custom icon. <c>PwCustomIcon</c> objects are immutable.
/// </summary>
public sealed class PwCustomIcon
{
// Recommended maximum sizes, not obligatory
internal const int MaxWidth = 128;
internal const int MaxHeight = 128;
private readonly PwUuid m_uuid;
private readonly byte[] m_pbImageDataPng;
private string m_strName = string.Empty;
private DateTime? m_odtLastMod = null;
private Dictionary<long, Android.Graphics.Bitmap> m_dImageCache = new Dictionary<long, Android.Graphics.Bitmap>();
private PwUuid m_pwUuid;
private byte[] m_pbImageDataPng;
private Android.Graphics.Bitmap m_pCachedImage;
public PwUuid Uuid
{
get { return m_uuid; }
get { return m_pwUuid; }
}
public byte[] ImageDataPng
{
get { return m_pbImageDataPng; }
// When allowing 'set', do not copy the cache in 'Clone'
}
public string Name
{
get { return m_strName; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_strName = value;
}
}
public DateTime? LastModificationTime
{
get { return m_odtLastMod; }
set { m_odtLastMod = value; }
}
[Obsolete("Use GetImage instead.")]
public Android.Graphics.Bitmap Image
{
get { return GetImage(); } // Backward compatibility
}
get { return m_pCachedImage; }
}
public PwCustomIcon(PwUuid pu, byte[] pbImageDataPng)
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
{
if (pu == null) { Debug.Assert(false); throw new ArgumentNullException("pu"); }
if (pu.Equals(PwUuid.Zero)) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pu"); }
if (pbImageDataPng == null) { Debug.Assert(false); throw new ArgumentNullException("pbImageDataPng"); }
Debug.Assert(pwUuid != null);
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
Debug.Assert(!pwUuid.Equals(PwUuid.Zero));
if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0");
m_uuid = pu;
Debug.Assert(pbImageDataPng != null);
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
m_pwUuid = pwUuid;
m_pbImageDataPng = pbImageDataPng;
}
private static long GetKey(int w, int h)
{
return (((long)w << 32) ^ (long)h);
}
/// <summary>
/// Get the icon as an <c>Image</c> (original size).
/// </summary>
public Android.Graphics.Bitmap GetImage()
{
const long lKey = -1;
Android.Graphics.Bitmap img;
if (m_dImageCache.TryGetValue(lKey, out img)) return img;
try { img = GfxUtil.LoadImage(m_pbImageDataPng); }
catch (Exception) { Debug.Assert(false); }
m_dImageCache[lKey] = img;
return img;
}
internal PwCustomIcon Clone()
{
PwCustomIcon ico = new PwCustomIcon(m_uuid, m_pbImageDataPng);
ico.m_strName = m_strName;
ico.m_odtLastMod = m_odtLastMod;
ico.m_dImageCache = m_dImageCache; // Same image data
return ico;
#if !KeePassLibSD
// MemoryStream ms = new MemoryStream(m_pbImageDataPng, false);
// m_pCachedImage = Image.FromStream(ms);
// ms.Close();
m_pCachedImage = GfxUtil.LoadImage(m_pbImageDataPng);
#else
m_pCachedImage = null;
#endif
}
}
}

View File

@@ -97,7 +97,7 @@ namespace KeePassLib
private int m_nHistoryMaxItems = DefaultHistoryMaxItems;
private long m_lHistoryMaxSize = DefaultHistoryMaxSize; // In bytes
private StringDictionaryEx m_dCustomData = new StringDictionaryEx(true);
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
private VariantDictionary m_dPublicCustomData = new VariantDictionary();
private byte[] m_pbHashOfFileOnDisk = null;
@@ -690,9 +690,9 @@ namespace KeePassLib
public void MergeIn(PwDatabase pdSource, PwMergeMethod mm,
IStatusLogger slStatus)
{
if (pdSource == null) throw new ArgumentNullException("pdSource");
if(pdSource == null) throw new ArgumentNullException("pdSource");
if (mm == PwMergeMethod.CreateNewUuids)
if(mm == PwMergeMethod.CreateNewUuids)
{
pdSource.RootGroup.Uuid = new PwUuid(true);
pdSource.RootGroup.CreateNewItemUuids(true, true, true);
@@ -707,7 +707,7 @@ namespace KeePassLib
PwObjectPoolEx ppOrg = PwObjectPoolEx.FromGroup(m_pgRootGroup);
PwObjectPoolEx ppSrc = PwObjectPoolEx.FromGroup(pdSource.RootGroup);
GroupHandler ghSrc = delegate (PwGroup pg)
GroupHandler ghSrc = delegate(PwGroup pg)
{
// if(pg == pdSource.m_pgRootGroup) return true;
@@ -716,11 +716,11 @@ namespace KeePassLib
// pool should not be modified)
PwGroup pgLocal = m_pgRootGroup.FindGroup(pg.Uuid, true);
if (pgLocal == null)
if(pgLocal == null)
{
PwGroup pgSourceParent = pg.ParentGroup;
PwGroup pgLocalContainer;
if (pgSourceParent == null)
if(pgSourceParent == null)
{
// pg is the root group of pdSource, and no corresponding
// local group was found; create the group within the
@@ -728,23 +728,17 @@ namespace KeePassLib
Debug.Assert(pg == pdSource.m_pgRootGroup);
pgLocalContainer = m_pgRootGroup;
}
else if (pgSourceParent == pdSource.m_pgRootGroup)
else if(pgSourceParent == pdSource.m_pgRootGroup)
pgLocalContainer = m_pgRootGroup;
else
pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true);
Debug.Assert(pgLocalContainer != null);
if (pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
PwGroup pgNew = new PwGroup(false, false);
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;
@@ -753,9 +747,9 @@ namespace KeePassLib
{
Debug.Assert(mm != PwMergeMethod.CreateNewUuids);
if (mm == PwMergeMethod.OverwriteExisting)
if(mm == PwMergeMethod.OverwriteExisting)
pgLocal.AssignProperties(pg, false, false);
else if ((mm == PwMergeMethod.OverwriteIfNewer) ||
else if((mm == PwMergeMethod.OverwriteIfNewer) ||
(mm == PwMergeMethod.Synchronize))
{
pgLocal.AssignProperties(pg, true, false);
@@ -766,23 +760,23 @@ namespace KeePassLib
return ((slStatus != null) ? slStatus.ContinueWork() : true);
};
EntryHandler ehSrc = delegate (PwEntry pe)
EntryHandler ehSrc = delegate(PwEntry pe)
{
// PwEntry peLocal = m_pgRootGroup.FindEntry(pe.Uuid, true);
PwEntry peLocal = (ppOrg.GetItemByUuid(pe.Uuid) as PwEntry);
Debug.Assert(object.ReferenceEquals(peLocal,
m_pgRootGroup.FindEntry(pe.Uuid, true)));
if (peLocal == null)
if(peLocal == null)
{
PwGroup pgSourceParent = pe.ParentGroup;
PwGroup pgLocalContainer;
if (pgSourceParent == pdSource.m_pgRootGroup)
if(pgSourceParent == pdSource.m_pgRootGroup)
pgLocalContainer = m_pgRootGroup;
else
pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true);
Debug.Assert(pgLocalContainer != null);
if (pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
PwEntry peNew = new PwEntry(false, false);
peNew.Uuid = pe.Uuid;
@@ -802,19 +796,19 @@ namespace KeePassLib
bool bEquals = peLocal.EqualsEntry(pe, cmpOpt, MemProtCmpMode.None);
bool bOrgBackup = !bEquals;
if (mm != PwMergeMethod.OverwriteExisting)
if(mm != PwMergeMethod.OverwriteExisting)
bOrgBackup &= (TimeUtil.CompareLastMod(pe, peLocal, true) > 0);
bOrgBackup &= !pe.HasBackupOfData(peLocal, false, true);
if (bOrgBackup) peLocal.CreateBackup(null); // Maintain at end
if(bOrgBackup) peLocal.CreateBackup(null); // Maintain at end
bool bSrcBackup = !bEquals && (mm != PwMergeMethod.OverwriteExisting);
bSrcBackup &= (TimeUtil.CompareLastMod(peLocal, pe, true) > 0);
bSrcBackup &= !peLocal.HasBackupOfData(pe, false, true);
if (bSrcBackup) pe.CreateBackup(null); // Maintain at end
if(bSrcBackup) pe.CreateBackup(null); // Maintain at end
if (mm == PwMergeMethod.OverwriteExisting)
if(mm == PwMergeMethod.OverwriteExisting)
peLocal.AssignProperties(pe, false, false, false);
else if ((mm == PwMergeMethod.OverwriteIfNewer) ||
else if((mm == PwMergeMethod.OverwriteIfNewer) ||
(mm == PwMergeMethod.Synchronize))
{
peLocal.AssignProperties(pe, true, false, false);
@@ -828,13 +822,13 @@ namespace KeePassLib
};
ghSrc(pdSource.RootGroup);
if (!pdSource.RootGroup.TraverseTree(TraversalMethod.PreOrder, ghSrc, ehSrc))
if(!pdSource.RootGroup.TraverseTree(TraversalMethod.PreOrder, ghSrc, ehSrc))
throw new InvalidOperationException();
IStatusLogger slPrevStatus = m_slStatus;
m_slStatus = slStatus;
if (mm == PwMergeMethod.Synchronize)
if(mm == PwMergeMethod.Synchronize)
{
RelocateGroups(ppOrg, ppSrc);
RelocateEntries(ppOrg, ppSrc);
@@ -844,24 +838,24 @@ namespace KeePassLib
MergeInLocationChanged(m_pgRootGroup, ppOrg, ppSrc);
ppOrg = null; // Pools are now invalid, because the location
ppSrc = null; // changed times have been merged in
}
// Delete *after* relocating, because relocating might empty
// some groups that are marked for deletion (and objects
// that weren't relocated yet might prevent the deletion)
Dictionary<PwUuid, PwDeletedObject> dDel = CreateDeletedObjectsPool();
if (mm == PwMergeMethod.Synchronize)
MergeInDeletionInfo(pdSource.m_vDeletedObjects, dDel);
ApplyDeletions(m_pgRootGroup, dDel);
// The list and the dictionary should be kept in sync
Debug.Assert(m_vDeletedObjects.UCount == (uint)dDel.Count);
// Delete *after* relocating, because relocating might
// empty some groups that are marked for deletion (and
// objects that weren't relocated yet might prevent the
// deletion)
Dictionary<PwUuid, PwDeletedObject> dOrgDel = CreateDeletedObjectsPool();
MergeInDeletionInfo(pdSource.m_vDeletedObjects, dOrgDel);
ApplyDeletions(m_pgRootGroup, dOrgDel);
// The list and the dictionary should be kept in sync
Debug.Assert(m_vDeletedObjects.UCount == (uint)dOrgDel.Count);
}
// Must be called *after* merging groups, because group UUIDs
// are required for recycle bin and entry template UUIDs
MergeInDbProperties(pdSource, mm);
MergeInCustomIcons(pdSource, dDel);
Debug.Assert(m_vDeletedObjects.UCount == (uint)dDel.Count);
MergeInCustomIcons(pdSource);
MaintainBackups();
@@ -869,79 +863,15 @@ namespace KeePassLib
m_slStatus = slPrevStatus;
}
private void MergeInCustomIcons(PwDatabase pdSource,
Dictionary<PwUuid, PwDeletedObject> dDel)
private void MergeInCustomIcons(PwDatabase pdSource)
{
bool bIconsMod = false;
Dictionary<PwUuid, int> d = new Dictionary<PwUuid, int>();
for (int i = m_vCustomIcons.Count - 1; i >= 0; --i)
d[m_vCustomIcons[i].Uuid] = i;
Debug.Assert(d.Count == m_vCustomIcons.Count); // UUIDs unique
foreach (PwCustomIcon ciS in pdSource.m_vCustomIcons)
foreach(PwCustomIcon pwci in pdSource.CustomIcons)
{
int iT;
if (d.TryGetValue(ciS.Uuid, out iT))
{
PwCustomIcon ciT = m_vCustomIcons[iT];
if(GetCustomIconIndex(pwci.Uuid) >= 0) continue;
DateTime? odtT = ciT.LastModificationTime;
DateTime? odtS = ciS.LastModificationTime;
if (odtT.HasValue && odtS.HasValue)
{
if (odtT.Value >= odtS.Value) continue;
}
else if (odtT.HasValue) continue;
else if (!odtS.HasValue) continue; // Both no time
m_vCustomIcons[iT] = ciS.Clone();
}
else
{
d[ciS.Uuid] = m_vCustomIcons.Count;
m_vCustomIcons.Add(ciS.Clone());
}
bIconsMod = true;
m_vCustomIcons.Add(pwci); // PwCustomIcon is immutable
m_bUINeedsIconUpdate = true;
}
List<PwDeletedObject> lObsoleteDel = new List<PwDeletedObject>();
foreach (KeyValuePair<PwUuid, PwDeletedObject> kvpDel in dDel)
{
int iT;
if (d.TryGetValue(kvpDel.Key, out iT))
{
PwCustomIcon ci = m_vCustomIcons[iT];
if (ci == null) { Debug.Assert(false); continue; } // Dup. del. obj.?
DateTime? odt = ci.LastModificationTime;
if (odt.HasValue && (odt.Value > kvpDel.Value.DeletionTime))
lObsoleteDel.Add(kvpDel.Value);
else
{
m_vCustomIcons[iT] = null; // Preserve indices, removed below
bIconsMod = true;
}
}
}
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
m_vCustomIcons.RemoveAll(f);
foreach (PwDeletedObject pdo in lObsoleteDel)
{
// Prevent future deletion attempts
if (!m_vDeletedObjects.Remove(pdo)) { Debug.Assert(false); }
if (!dDel.Remove(pdo.Uuid)) { Debug.Assert(false); }
}
if (bIconsMod) m_bUINeedsIconUpdate = true;
FixCustomIconRefs();
}
private Dictionary<PwUuid, PwDeletedObject> CreateDeletedObjectsPool()
@@ -1282,9 +1212,7 @@ namespace KeePassLib
PwObjectBlock<T> b = new PwObjectBlock<T>();
DateTime dtLoc;
PwUuid puPrevParent;
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc,
out puPrevParent);
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc);
b.Add(t, dtLoc, pPool);
lBlocks.Add(b);
@@ -1319,7 +1247,7 @@ namespace KeePassLib
}
if(idSrcNext == 0) break;
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc, out puPrevParent);
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc);
b.Add(tNext, dtLoc, pPool);
++u;
@@ -1332,31 +1260,28 @@ namespace KeePassLib
}
private static PwObjectPoolEx GetBestPool<T>(T t, PwObjectPoolEx ppOrg,
PwObjectPoolEx ppSrc, out DateTime dtLoc, out PwUuid puPrevParent)
PwObjectPoolEx ppSrc, out DateTime dtLoc)
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
puPrevParent = PwUuid.Zero;
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if (ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
puPrevParent = ptOrg.PreviousParentGroup;
p = ppOrg;
}
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if(ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
p = ppOrg;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if ((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
puPrevParent = ptSrc.PreviousParentGroup;
p = ppSrc;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
p = ppSrc;
}
Debug.Assert(p != null);
return p;
Debug.Assert(p != null);
return p;
}
private static int FindLocationChangedPivot<T>(List<PwObjectBlock<T>> lBlocks,
@@ -1382,40 +1307,30 @@ namespace KeePassLib
return iPosMax;
}
private static void MergeInLocationChanged(PwGroup pg,
PwObjectPoolEx ppOrg, PwObjectPoolEx ppSrc)
{
GroupHandler gh = delegate (PwGroup pgSub)
{
DateTime dt;
PwUuid puPrevParent;
if (GetBestPool<PwGroup>(pgSub, ppOrg, ppSrc, out dt,
out puPrevParent) != null)
{
pgSub.LocationChanged = dt;
pgSub.PreviousParentGroup = puPrevParent;
}
else { Debug.Assert(false); }
return true;
};
private static void MergeInLocationChanged(PwGroup pg,
PwObjectPoolEx ppOrg, PwObjectPoolEx ppSrc)
{
GroupHandler gh = delegate(PwGroup pgSub)
{
DateTime dt;
if(GetBestPool<PwGroup>(pgSub, ppOrg, ppSrc, out dt) != null)
pgSub.LocationChanged = dt;
else { Debug.Assert(false); }
return true;
};
EntryHandler eh = delegate (PwEntry pe)
{
DateTime dt;
PwUuid puPrevParent;
if (GetBestPool<PwEntry>(pe, ppOrg, ppSrc, out dt,
out puPrevParent) != null)
{
pe.LocationChanged = dt;
pe.PreviousParentGroup = puPrevParent;
}
else { Debug.Assert(false); }
return true;
};
EntryHandler eh = delegate(PwEntry pe)
{
DateTime dt;
if(GetBestPool<PwEntry>(pe, ppOrg, ppSrc, out dt) != null)
pe.LocationChanged = dt;
else { Debug.Assert(false); }
return true;
};
gh(pg);
pg.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
gh(pg);
pg.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
private static void InsertObjectAtBestPos<T>(PwObjectList<T> lItems,
T tNew, PwObjectPoolEx ppSrc)
@@ -1530,18 +1445,12 @@ namespace KeePassLib
foreach(KeyValuePair<string, string> kvp in pdSource.m_dCustomData)
{
if(bSourceNewer || !m_dCustomData.Exists(kvp.Key))
m_dCustomData.Set(kvp.Key, kvp.Value, null);
m_dCustomData.Set(kvp.Key, kvp.Value);
}
// 'Clone' duplicates deep values (e.g. byte arrays)
VariantDictionary vdS = (VariantDictionary)pdSource.m_dPublicCustomData.Clone();
if (bForce || bSourceNewer)
vdS.CopyTo(m_dPublicCustomData);
else
{
m_dPublicCustomData.CopyTo(vdS);
m_dPublicCustomData = vdS;
}
VariantDictionary vdLocal = m_dPublicCustomData; // Backup
m_dPublicCustomData = (VariantDictionary)pdSource.m_dPublicCustomData.Clone();
if(!bSourceNewer) vdLocal.CopyTo(m_dPublicCustomData); // Merge
}
private void MergeEntryHistory(PwEntry pe, PwEntry peSource,
@@ -1634,12 +1543,12 @@ namespace KeePassLib
/// <returns>Index of the icon.</returns>
public int GetCustomIconIndex(PwUuid pwIconId)
{
for (int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
if (pwci.Uuid.Equals(pwIconId))
return i;
}
for(int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
if(pwci.Uuid.Equals(pwIconId))
return i;
}
// Debug.Assert(false); // Do not assert
return -1;
@@ -1649,15 +1558,15 @@ namespace KeePassLib
{
if(pbPngData == null) { Debug.Assert(false); return -1; }
for (int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
byte[] pbEx = pwci.ImageDataPng;
if (pbEx == null) { Debug.Assert(false); continue; }
for(int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
byte[] pbEx = pwci.ImageDataPng;
if(pbEx == null) { Debug.Assert(false); continue; }
if (MemUtil.ArraysEqual(pbEx, pbPngData))
return i;
}
if(MemUtil.ArraysEqual(pbEx, pbPngData))
return i;
}
return -1;
}
@@ -1676,64 +1585,68 @@ namespace KeePassLib
else { Debug.Assert(false); return null; }
}
public bool DeleteCustomIcons(List<PwUuid> lUuids)
{
if (lUuids == null) { Debug.Assert(false); throw new ArgumentNullException("lUuids"); }
if (lUuids.Count == 0) return false;
public bool DeleteCustomIcons(List<PwUuid> vUuidsToDelete)
{
Debug.Assert(vUuidsToDelete != null);
if(vUuidsToDelete == null) throw new ArgumentNullException("vUuidsToDelete");
if(vUuidsToDelete.Count <= 0) return true;
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwUuid pu in lUuids) { dToDel[pu] = true; }
GroupHandler gh = delegate(PwGroup pg)
{
PwUuid uuidThis = pg.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return true;
DateTime dt = DateTime.UtcNow;
for (int i = m_vCustomIcons.Count - 1; i >= 0; --i)
{
PwUuid pu = m_vCustomIcons[i].Uuid;
if (dToDel.ContainsKey(pu))
{
m_vCustomIcons[i] = null; // Removed below
m_vDeletedObjects.Add(new PwDeletedObject(pu, dt));
}
}
foreach(PwUuid uuidDelete in vUuidsToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pg.CustomIconUuid = PwUuid.Zero;
break;
}
}
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
m_vCustomIcons.RemoveAll(f);
return true;
};
FixCustomIconRefs();
return true;
}
EntryHandler eh = delegate(PwEntry pe)
{
RemoveCustomIconUuid(pe, vUuidsToDelete);
return true;
};
private void FixCustomIconRefs()
{
Dictionary<PwUuid, bool> d = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { d[ci.Uuid] = true; }
gh(m_pgRootGroup);
if(!m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh))
{
Debug.Assert(false);
return false;
}
GroupHandler gh = delegate (PwGroup pg)
{
PwUuid pu = pg.CustomIconUuid;
if (pu.Equals(PwUuid.Zero)) return true;
if (!d.ContainsKey(pu)) pg.CustomIconUuid = PwUuid.Zero;
return true;
};
foreach(PwUuid pwUuid in vUuidsToDelete)
{
int nIndex = GetCustomIconIndex(pwUuid);
if(nIndex >= 0) m_vCustomIcons.RemoveAt(nIndex);
}
EntryHandler eh = delegate (PwEntry pe)
{
FixCustomIconRefs(pe, d);
return true;
};
return true;
}
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
private static void RemoveCustomIconUuid(PwEntry pe, List<PwUuid> vToDelete)
{
PwUuid uuidThis = pe.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return;
private void FixCustomIconRefs(PwEntry pe, Dictionary<PwUuid, bool> d)
{
PwUuid pu = pe.CustomIconUuid;
if (pu.Equals(PwUuid.Zero)) return;
if (!d.ContainsKey(pu)) pe.CustomIconUuid = PwUuid.Zero;
foreach (PwEntry peH in pe.History) FixCustomIconRefs(peH, d);
}
foreach(PwUuid uuidDelete in vToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pe.CustomIconUuid = PwUuid.Zero;
break;
}
}
foreach(PwEntry peHistory in pe.History)
RemoveCustomIconUuid(peHistory, vToDelete);
}
private int GetTotalObjectUuidCount()
{
@@ -2022,119 +1935,61 @@ namespace KeePassLib
return uDeleted;
}
public uint DeleteUnusedCustomIcons()
{
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { dToDel[ci.Uuid] = true; }
List<PwUuid> lToDelete = new List<PwUuid>();
foreach(PwCustomIcon pwci in m_vCustomIcons)
lToDelete.Add(pwci.Uuid);
GroupHandler gh = delegate (PwGroup pg)
GroupHandler gh = delegate(PwGroup pg)
{
PwUuid pu = pg.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero)) dToDel.Remove(pu);
PwUuid pwUuid = pg.CustomIconUuid;
if((pwUuid == null) || pwUuid.Equals(PwUuid.Zero)) return true;
for(int i = 0; i < lToDelete.Count; ++i)
{
if(lToDelete[i].Equals(pwUuid))
{
lToDelete.RemoveAt(i);
break;
}
}
return true;
};
EntryHandler eh = delegate (PwEntry pe)
EntryHandler eh = delegate(PwEntry pe)
{
RemoveCustomIconsFromDict(dToDel, pe);
PwUuid pwUuid = pe.CustomIconUuid;
if((pwUuid == null) || pwUuid.Equals(PwUuid.Zero)) return true;
for(int i = 0; i < lToDelete.Count; ++i)
{
if(lToDelete[i].Equals(pwUuid))
{
lToDelete.RemoveAt(i);
break;
}
}
return true;
};
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
uint cDel = (uint)dToDel.Count;
if (cDel != 0)
uint uDeleted = 0;
foreach(PwUuid pwDel in lToDelete)
{
DeleteCustomIcons(new List<PwUuid>(dToDel.Keys));
m_bUINeedsIconUpdate = true;
int nIndex = GetCustomIconIndex(pwDel);
if(nIndex < 0) { Debug.Assert(false); continue; }
m_vCustomIcons.RemoveAt(nIndex);
++uDeleted;
}
return cDel;
}
private static void RemoveCustomIconsFromDict(Dictionary<PwUuid, bool> d,
PwEntry pe)
{
PwUuid pu = pe.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero)) d.Remove(pu);
foreach (PwEntry peH in pe.History) RemoveCustomIconsFromDict(d, peH);
}
internal static void CopyCustomIcons(PwDatabase pdFrom, PwDatabase pdTo,
PwGroup pgSelect, bool bResetIfUnknown)
{
if (pgSelect == null) { Debug.Assert(false); return; }
Dictionary<PwUuid, PwCustomIcon> dFrom = new Dictionary<PwUuid, PwCustomIcon>();
if (pdFrom != null)
{
foreach (PwCustomIcon ci in pdFrom.m_vCustomIcons)
dFrom[ci.Uuid] = ci;
}
Dictionary<PwUuid, int> dTo = new Dictionary<PwUuid, int>();
if (pdTo != null)
{
for (int i = pdTo.m_vCustomIcons.Count - 1; i >= 0; --i)
dTo[pdTo.m_vCustomIcons[i].Uuid] = i;
}
Func<PwUuid, bool> fEnsureIcon = delegate (PwUuid puIcon)
{
if (puIcon.Equals(PwUuid.Zero)) return true;
if (pdTo == null) { Debug.Assert(false); return false; }
PwCustomIcon ciFrom;
if (!dFrom.TryGetValue(puIcon, out ciFrom)) { Debug.Assert(false); return false; }
int iTo;
if (dTo.TryGetValue(puIcon, out iTo))
{
PwCustomIcon ciTo = pdTo.m_vCustomIcons[iTo];
DateTime? odtFrom = ciFrom.LastModificationTime;
DateTime? odtTo = ciTo.LastModificationTime;
if (odtFrom.HasValue && odtTo.HasValue)
{
if (odtFrom.Value <= odtTo.Value) return true;
}
else if (odtTo.HasValue) return true;
else if (!odtFrom.HasValue) return true; // Both no time
pdTo.m_vCustomIcons[iTo] = ciFrom.Clone();
}
else
{
dTo[puIcon] = pdTo.m_vCustomIcons.Count;
pdTo.m_vCustomIcons.Add(ciFrom.Clone());
}
pdTo.Modified = true;
pdTo.UINeedsIconUpdate = true;
return true;
};
GroupHandler gh = delegate (PwGroup pgCur)
{
bool bTo = fEnsureIcon(pgCur.CustomIconUuid);
if (!bTo && bResetIfUnknown) pgCur.CustomIconUuid = PwUuid.Zero;
return true;
};
EntryHandler eh = delegate (PwEntry peCur)
{
bool bTo = fEnsureIcon(peCur.CustomIconUuid);
if (!bTo && bResetIfUnknown) peCur.CustomIconUuid = PwUuid.Zero;
return true;
};
gh(pgSelect);
pgSelect.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(uDeleted > 0) m_bUINeedsIconUpdate = true;
return uDeleted;
}
}
}

View File

@@ -228,18 +228,6 @@ namespace KeePassLib
/// </summary>
public sealed class SearchParameters
{
private string m_strName = string.Empty;
[DefaultValue("")]
public string Name
{
get { return m_strName; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_strName = value;
}
}
private string m_strText = string.Empty;
[DefaultValue("")]
public string SearchString
@@ -247,25 +235,17 @@ namespace KeePassLib
get { return m_strText; }
set
{
if (value == null) throw new ArgumentNullException("value");
if(value == null) throw new ArgumentNullException("value");
m_strText = value;
}
}
private PwSearchMode m_sm = PwSearchMode.Simple;
public PwSearchMode SearchMode
{
get { return m_sm; }
set { m_sm = value; }
}
private bool m_bRegex = false;
[DefaultValue(false)]
[Obsolete]
[XmlIgnore]
public bool RegularExpression
{
get { return (m_sm == PwSearchMode.Regular); }
set { m_sm = (value ? PwSearchMode.Regular : PwSearchMode.Simple); }
get { return m_bRegex; }
set { m_bRegex = value; }
}
private bool m_bSearchInTitles = true;
@@ -316,22 +296,6 @@ namespace KeePassLib
set { m_bSearchInOther = value; }
}
private bool m_bSearchInStringNames = false;
[DefaultValue(false)]
public bool SearchInStringNames
{
get { return m_bSearchInStringNames; }
set { m_bSearchInStringNames = value; }
}
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
private bool m_bSearchInUuids = false;
[DefaultValue(false)]
public bool SearchInUuids
@@ -340,14 +304,6 @@ namespace KeePassLib
set { m_bSearchInUuids = value; }
}
private bool m_bSearchInGroupPaths = false;
[DefaultValue(false)]
public bool SearchInGroupPaths
{
get { return m_bSearchInGroupPaths; }
set { m_bSearchInGroupPaths = value; }
}
private bool m_bSearchInGroupNames = false;
[DefaultValue(false)]
public bool SearchInGroupNames
@@ -356,12 +312,12 @@ namespace KeePassLib
set { m_bSearchInGroupNames = value; }
}
private bool m_bSearchInHistory = false;
[DefaultValue(false)]
public bool SearchInHistory
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInHistory; }
set { m_bSearchInHistory = value; }
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
#if KeePassUAP
@@ -413,7 +369,7 @@ namespace KeePassLib
get { return m_strDataTrf; }
set
{
if (value == null) throw new ArgumentNullException("value");
if(value == null) throw new ArgumentNullException("value");
m_strDataTrf = value;
}
}
@@ -425,24 +381,20 @@ namespace KeePassLib
{
SearchParameters sp = new SearchParameters();
Debug.Assert(sp.m_strName.Length == 0);
Debug.Assert(sp.m_strText.Length == 0);
Debug.Assert(sp.m_sm == PwSearchMode.Simple);
// sp.m_strText = string.Empty;
// sp.m_bRegex = false;
sp.m_bSearchInTitles = false;
sp.m_bSearchInUserNames = false;
Debug.Assert(!sp.m_bSearchInPasswords);
// sp.m_bSearchInPasswords = false;
sp.m_bSearchInUrls = false;
sp.m_bSearchInNotes = false;
sp.m_bSearchInOther = false;
Debug.Assert(!sp.m_bSearchInStringNames);
// sp.m_bSearchInUuids = false;
// sp.SearchInGroupNames = false;
sp.m_bSearchInTags = false;
Debug.Assert(!sp.m_bSearchInUuids);
Debug.Assert(!sp.m_bSearchInGroupPaths);
Debug.Assert(!sp.m_bSearchInGroupNames);
Debug.Assert(!sp.m_bSearchInHistory);
// Debug.Assert(sp.m_scType == StringComparison.InvariantCultureIgnoreCase);
Debug.Assert(!sp.m_bExcludeExpired);
Debug.Assert(sp.m_bRespectEntrySearchingDisabled);
// sp.m_scType = StringComparison.InvariantCultureIgnoreCase;
// sp.m_bExcludeExpired = false;
// m_bRespectEntrySearchingDisabled = true;
return sp;
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -42,15 +42,14 @@ namespace KeePassLib
private PwUuid m_uuid = PwUuid.Zero;
private PwGroup m_pParentGroup = null;
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
private PwUuid m_puPrevParentGroup = PwUuid.Zero;
private ProtectedStringDictionary m_dStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_dBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_cfgAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_lHistory = new PwObjectList<PwEntry>();
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_listAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_listHistory = new PwObjectList<PwEntry>();
private PwIcon m_pwIcon = PwIcon.Key;
private PwUuid m_puCustomIcon = PwUuid.Zero;
private PwUuid m_pwCustomIconID = PwUuid.Zero;
private Color m_clrForeground = Color.Empty;
private Color m_clrBackground = Color.Empty;
@@ -63,21 +62,20 @@ namespace KeePassLib
private ulong m_uUsageCount = 0;
private string m_strOverrideUrl = string.Empty;
private bool m_bQualityCheck = true;
private List<string> m_lTags = new List<string>();
private List<string> m_vTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary>
/// UUID of this entry.
/// </summary>
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_uuid = value;
}
}
@@ -89,7 +87,7 @@ namespace KeePassLib
{
get { return m_pParentGroup; }
// Plugins: use <c>PwGroup.AddEntry</c> instead.
/// Plugins: use <c>PwGroup.AddEntry</c> instead.
internal set { m_pParentGroup = value; }
}
@@ -102,26 +100,17 @@ namespace KeePassLib
set { m_tParentGroupLastMod = value; }
}
public PwUuid PreviousParentGroup
{
get { return m_puPrevParentGroup; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_puPrevParentGroup = value;
}
}
/// <summary>
/// Get or set all entry strings.
/// </summary>
public ProtectedStringDictionary Strings
{
get { return m_dStrings; }
get { return m_listStrings; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dStrings = value;
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listStrings = value;
}
}
@@ -130,11 +119,11 @@ namespace KeePassLib
/// </summary>
public ProtectedBinaryDictionary Binaries
{
get { return m_dBinaries; }
get { return m_listBinaries; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dBinaries = value;
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listBinaries = value;
}
}
@@ -143,11 +132,11 @@ namespace KeePassLib
/// </summary>
public AutoTypeConfig AutoType
{
get { return m_cfgAutoType; }
get { return m_listAutoType; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_cfgAutoType = value;
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listAutoType = value;
}
}
@@ -156,11 +145,11 @@ namespace KeePassLib
/// </summary>
public PwObjectList<PwEntry> History
{
get { return m_lHistory; }
get { return m_listHistory; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lHistory = value;
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listHistory = value;
}
}
@@ -180,11 +169,11 @@ namespace KeePassLib
/// </summary>
public PwUuid CustomIconUuid
{
get { return m_puCustomIcon; }
get { return m_pwCustomIconID; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_puCustomIcon = value;
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_pwCustomIconID = value;
}
}
@@ -263,34 +252,28 @@ namespace KeePassLib
}
/// <summary>
/// Entry-specific override URL.
/// Entry-specific override URL. If this string is non-empty,
/// </summary>
public string OverrideUrl
{
get { return m_strOverrideUrl; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
if (value == null) throw new ArgumentNullException("value");
m_strOverrideUrl = value;
}
}
public bool QualityCheck
{
get { return m_bQualityCheck; }
set { m_bQualityCheck = value; }
}
/// <summary>
/// List of tags associated with this entry.
/// </summary>
public List<string> Tags
{
get { StrUtil.NormalizeTags(m_lTags); return m_lTags; }
get { return m_vTags; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lTags = value;
if (value == null) throw new ArgumentNullException("value");
m_vTags = value;
}
}
@@ -366,19 +349,19 @@ namespace KeePassLib
}
#if DEBUG
// For display in debugger
/// <summary>
public override string ToString()
{
return ("PwEntry '" + m_dStrings.ReadSafe(PwDefs.TitleField) + "'");
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
}
#endif
/// <summary>
/// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference).
/// All mutable members are cloned.
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
public PwEntry CloneDeep()
{
PwEntry peNew = new PwEntry(false, false);
@@ -386,15 +369,14 @@ namespace KeePassLib
peNew.m_uuid = m_uuid; // PwUuid is immutable
peNew.m_pParentGroup = m_pParentGroup;
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
peNew.m_puPrevParentGroup = m_puPrevParentGroup;
peNew.m_dStrings = m_dStrings.CloneDeep();
peNew.m_dBinaries = m_dBinaries.CloneDeep();
peNew.m_cfgAutoType = m_cfgAutoType.CloneDeep();
peNew.m_lHistory = m_lHistory.CloneDeep();
peNew.m_listStrings = m_listStrings.CloneDeep();
peNew.m_listBinaries = m_listBinaries.CloneDeep();
peNew.m_listAutoType = m_listAutoType.CloneDeep();
peNew.m_listHistory = m_listHistory.CloneDeep();
peNew.m_pwIcon = m_pwIcon;
peNew.m_puCustomIcon = m_puCustomIcon;
peNew.m_pwCustomIconID = m_pwCustomIconID;
peNew.m_clrForeground = m_clrForeground;
peNew.m_clrBackground = m_clrBackground;
@@ -407,9 +389,8 @@ namespace KeePassLib
peNew.m_uUsageCount = m_uUsageCount;
peNew.m_strOverrideUrl = m_strOverrideUrl;
peNew.m_bQualityCheck = m_bQualityCheck;
peNew.m_lTags.AddRange(m_lTags);
peNew.m_vTags = new List<string>(m_vTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
@@ -476,29 +457,27 @@ namespace KeePassLib
if (m_pParentGroup != pe.m_pParentGroup) return false;
if (!bIgnoreLastMod && (m_tParentGroupLastMod != pe.m_tParentGroupLastMod))
return false;
if (!m_puPrevParentGroup.Equals(pe.m_puPrevParentGroup))
return false;
}
if (!m_dStrings.EqualsDictionary(pe.m_dStrings, pwOpt, mpCmpStr))
if (!m_listStrings.EqualsDictionary(pe.m_listStrings, pwOpt, mpCmpStr))
return false;
if (!m_dBinaries.EqualsDictionary(pe.m_dBinaries)) return false;
if (!m_listBinaries.EqualsDictionary(pe.m_listBinaries)) return false;
if (!m_cfgAutoType.Equals(pe.m_cfgAutoType)) return false;
if (!m_listAutoType.Equals(pe.m_listAutoType)) return false;
if ((pwOpt & PwCompareOptions.IgnoreHistory) == PwCompareOptions.None)
{
bool bIgnoreLastBackup = ((pwOpt & PwCompareOptions.IgnoreLastBackup) !=
PwCompareOptions.None);
if (!bIgnoreLastBackup && (m_lHistory.UCount != pe.m_lHistory.UCount))
if (!bIgnoreLastBackup && (m_listHistory.UCount != pe.m_listHistory.UCount))
return false;
if (bIgnoreLastBackup && (m_lHistory.UCount == 0))
if (bIgnoreLastBackup && (m_listHistory.UCount == 0))
{
Debug.Assert(false);
return false;
}
if (bIgnoreLastBackup && ((m_lHistory.UCount - 1) != pe.m_lHistory.UCount))
if (bIgnoreLastBackup && ((m_listHistory.UCount - 1) != pe.m_listHistory.UCount))
return false;
PwCompareOptions cmpSub = PwCompareOptions.IgnoreParentGroup;
@@ -506,16 +485,16 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpSub |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpSub |= PwCompareOptions.IgnoreLastAccess;
for (uint uHist = 0; uHist < pe.m_lHistory.UCount; ++uHist)
for (uint uHist = 0; uHist < pe.m_listHistory.UCount; ++uHist)
{
if (!m_lHistory.GetAt(uHist).EqualsEntry(pe.m_lHistory.GetAt(
if (!m_listHistory.GetAt(uHist).EqualsEntry(pe.m_listHistory.GetAt(
uHist), cmpSub, MemProtCmpMode.None))
return false;
}
}
if (m_pwIcon != pe.m_pwIcon) return false;
if (!m_puCustomIcon.Equals(pe.m_puCustomIcon)) return false;
if (!m_pwCustomIconID.Equals(pe.m_pwCustomIconID)) return false;
if (m_clrForeground != pe.m_clrForeground) return false;
if (m_clrBackground != pe.m_clrBackground) return false;
@@ -528,10 +507,12 @@ namespace KeePassLib
if (!bIgnoreLastAccess && (m_uUsageCount != pe.m_uUsageCount)) return false;
if (m_strOverrideUrl != pe.m_strOverrideUrl) return false;
if (m_bQualityCheck != pe.m_bQualityCheck) return false;
// The Tags property normalizes
if (!MemUtil.ListsEqual<string>(this.Tags, pe.Tags)) return false;
if (m_vTags.Count != pe.m_vTags.Count) return false;
for (int iTag = 0; iTag < m_vTags.Count; ++iTag)
{
if (m_vTags[iTag] != pe.m_vTags[iTag]) return false;
}
if (!m_dCustomData.Equals(pe.m_dCustomData)) return false;
@@ -562,19 +543,16 @@ namespace KeePassLib
m_uuid = peTemplate.m_uuid;
if (bAssignLocationChanged)
{
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
m_puPrevParentGroup = peTemplate.m_puPrevParentGroup;
}
m_dStrings = peTemplate.m_dStrings.CloneDeep();
m_dBinaries = peTemplate.m_dBinaries.CloneDeep();
m_cfgAutoType = peTemplate.m_cfgAutoType.CloneDeep();
m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
if (bIncludeHistory)
m_lHistory = peTemplate.m_lHistory.CloneDeep();
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon;
m_puCustomIcon = peTemplate.m_puCustomIcon; // Immutable
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
m_clrForeground = peTemplate.m_clrForeground;
m_clrBackground = peTemplate.m_clrBackground;
@@ -587,9 +565,8 @@ namespace KeePassLib
m_uUsageCount = peTemplate.m_uUsageCount;
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_bQualityCheck = peTemplate.m_bQualityCheck;
m_lTags = new List<string>(peTemplate.m_lTags);
m_vTags = new List<string>(peTemplate.m_vTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
}
@@ -652,9 +629,9 @@ namespace KeePassLib
public void CreateBackup(PwDatabase pwHistMntcSettings)
{
PwEntry peCopy = CloneDeep();
peCopy.m_lHistory.Clear();
peCopy.History = new PwObjectList<PwEntry>(); // Remove history
m_lHistory.Add(peCopy); // Must be added at end, see EqualsEntry
m_listHistory.Add(peCopy); // Must be added at end, see EqualsEntry
if (pwHistMntcSettings != null) MaintainBackups(pwHistMntcSettings);
}
@@ -681,14 +658,12 @@ namespace KeePassLib
/// This parameter may be <c>null</c> (no maintenance then).</param>
public void RestoreFromBackup(uint uBackupIndex, PwDatabase pwHistMntcSettings)
{
if (uBackupIndex >= m_lHistory.UCount)
{
Debug.Assert(false);
Debug.Assert(uBackupIndex < m_listHistory.UCount);
if (uBackupIndex >= m_listHistory.UCount)
throw new ArgumentOutOfRangeException("uBackupIndex");
}
PwEntry pe = m_lHistory.GetAt(uBackupIndex);
if (pe == null) { Debug.Assert(false); throw new InvalidOperationException(); }
PwEntry pe = m_listHistory.GetAt(uBackupIndex);
Debug.Assert(pe != null); if (pe == null) throw new InvalidOperationException();
CreateBackup(pwHistMntcSettings); // Backup current data before restoring
AssignProperties(pe, false, false, false);
@@ -704,7 +679,7 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpOpt |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpOpt |= PwCompareOptions.IgnoreLastAccess;
foreach (PwEntry pe in m_lHistory)
foreach (PwEntry pe in m_listHistory)
{
if (pe.EqualsEntry(peData, cmpOpt, MemProtCmpMode.None)) return true;
}
@@ -713,28 +688,21 @@ namespace KeePassLib
}
/// <summary>
/// Delete old history entries if there are too many or the
/// history size is too large.
/// <returns>If one or more history entries have been deleted,
/// <c>true</c> is returned. Otherwise <c>false</c>.</returns>
/// Delete old history items if there are too many or the history
/// size is too large.
/// <returns>If one or more history items have been deleted, <c>true</c>
/// is returned. Otherwise <c>false</c>.</returns>
/// </summary>
public bool MaintainBackups(PwDatabase pwSettings)
{
if (pwSettings == null) { Debug.Assert(false); return false; }
// Fix UUIDs of history entries; should not be necessary
PwUuid pu = m_uuid;
foreach (PwEntry pe in m_lHistory)
{
if (!pe.Uuid.Equals(pu)) { Debug.Assert(false); pe.Uuid = pu; }
}
bool bDeleted = false;
int nMaxItems = pwSettings.HistoryMaxItems;
if (nMaxItems >= 0)
{
while (m_lHistory.UCount > (uint)nMaxItems)
while (m_listHistory.UCount > (uint)nMaxItems)
{
RemoveOldestBackup();
bDeleted = true;
@@ -747,7 +715,7 @@ namespace KeePassLib
while (true)
{
ulong uHistSize = 0;
foreach (PwEntry pe in m_lHistory) { uHistSize += pe.GetSize(); }
foreach (PwEntry pe in m_listHistory) { uHistSize += pe.GetSize(); }
if (uHistSize > (ulong)lMaxSize)
{
@@ -766,9 +734,9 @@ namespace KeePassLib
DateTime dtMin = TimeUtil.SafeMaxValueUtc;
uint idxRemove = uint.MaxValue;
for (uint u = 0; u < m_lHistory.UCount; ++u)
for (uint u = 0; u < m_listHistory.UCount; ++u)
{
PwEntry pe = m_lHistory.GetAt(u);
PwEntry pe = m_listHistory.GetAt(u);
if (TimeUtil.Compare(pe.LastModificationTime, dtMin, true) < 0)
{
idxRemove = u;
@@ -776,12 +744,12 @@ namespace KeePassLib
}
}
if (idxRemove != uint.MaxValue) m_lHistory.RemoveAt(idxRemove);
if (idxRemove != uint.MaxValue) m_listHistory.RemoveAt(idxRemove);
}
public bool GetAutoTypeEnabled()
{
if (!m_cfgAutoType.Enabled) return false;
if (!m_listAutoType.Enabled) return false;
if (m_pParentGroup != null)
return m_pParentGroup.GetAutoTypeEnabledInherited();
@@ -791,7 +759,7 @@ namespace KeePassLib
public string GetAutoTypeSequence()
{
string strSeq = m_cfgAutoType.DefaultSequence;
string strSeq = m_listAutoType.DefaultSequence;
PwGroup pg = m_pParentGroup;
while (pg != null)
@@ -817,67 +785,69 @@ namespace KeePassLib
}
/// <summary>
/// Approximate the total size (in process memory) of this entry
/// in bytes (including strings, binaries and history entries).
/// Approximate the total size of this entry in bytes (including
/// strings, binaries and history entries).
/// </summary>
/// <returns>Size in bytes.</returns>
public ulong GetSize()
{
// This method assumes 64-bit pointers/references and Unicode
// strings (i.e. 2 bytes per character)
ulong uSize = 128; // Approx fixed length data
ulong cb = 276; // Number of bytes; approx. fixed length data
ulong cc = 0; // Number of characters
cb += (ulong)m_dStrings.UCount * 40;
foreach (KeyValuePair<string, ProtectedString> kvpStr in m_dStrings)
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
cb += (ulong)m_dBinaries.UCount * 65;
foreach (KeyValuePair<string, ProtectedBinary> kvpBin in m_dBinaries)
foreach (KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
{
cc += (ulong)kvpBin.Key.Length;
cb += (ulong)kvpBin.Value.Length;
uSize += (ulong)kvpStr.Key.Length;
uSize += (ulong)kvpStr.Value.Length;
}
cc += (ulong)m_cfgAutoType.DefaultSequence.Length;
cb += (ulong)m_cfgAutoType.AssociationsCount * 24;
foreach (AutoTypeAssociation a in m_cfgAutoType.Associations)
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
foreach (KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{
uSize += (ulong)kvpBin.Key.Length;
uSize += kvpBin.Value.Length;
}
cb += (ulong)m_lHistory.UCount * 8;
foreach (PwEntry peHistory in m_lHistory)
cb += peHistory.GetSize();
uSize += (ulong)m_listAutoType.DefaultSequence.Length;
foreach (AutoTypeAssociation a in m_listAutoType.Associations)
{
uSize += (ulong)a.WindowName.Length;
uSize += (ulong)a.Sequence.Length;
}
cc += (ulong)m_strOverrideUrl.Length;
foreach (PwEntry peHistory in m_listHistory)
uSize += peHistory.GetSize();
cb += (ulong)m_lTags.Count * 8;
foreach (string strTag in m_lTags)
cc += (ulong)strTag.Length;
uSize += (ulong)m_strOverrideUrl.Length;
foreach (string strTag in m_vTags)
uSize += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
foreach (KeyValuePair<string, string> kvp in m_dCustomData)
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return (cb + (cc << 1));
return uSize;
}
public bool HasTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
// this.Tags normalizes
return this.Tags.Contains(StrUtil.NormalizeTag(strTag));
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return true;
}
return false;
}
public bool AddTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
strTag = StrUtil.NormalizeTag(strTag);
if (this.Tags.Contains(strTag)) return false; // this.Tags normalizes
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return false;
}
m_lTags.Add(strTag);
m_vTags.Add(strTag);
return true;
}
@@ -885,17 +855,16 @@ namespace KeePassLib
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
// this.Tags normalizes
return this.Tags.Remove(StrUtil.NormalizeTag(strTag));
}
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
{
m_vTags.RemoveAt(i);
return true;
}
}
internal List<string> GetTagsInherited()
{
List<string> l = ((m_pParentGroup != null) ?
m_pParentGroup.GetTagsInherited(false) : new List<string>());
l.AddRange(this.Tags);
StrUtil.NormalizeTags(l);
return l;
return false;
}
public bool IsContainedIn(PwGroup pgContainer)
@@ -917,8 +886,10 @@ namespace KeePassLib
if (bAlsoChangeHistoryUuids)
{
foreach (PwEntry peHist in m_lHistory)
foreach (PwEntry peHist in m_listHistory)
{
peHist.Uuid = pwNewUuid;
}
}
}

View File

@@ -316,13 +316,4 @@ namespace KeePassLib
Cinnamon,
Pantheon
}
public enum PwSearchMode
{
None = 0,
Simple,
Regular,
XPath
}
}

View File

@@ -1,372 +0,0 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
using KeePassLib.Collections;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace KeePassLib
{
public sealed partial class PwGroup
{
private const int SearchContextStringMaxLength = 50; // Note, doesn't include elipsis, if added
public const string SearchContextUuid = "Uuid";
public const string SearchContextParentGroup = "Parent Group";
public const string SearchContextTags = "Tags";
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage)
{
SearchEntries(sp, listStorage, null);
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
/// <param name="slStatus">Optional status reporting object.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage,
IStatusLogger slStatus)
{
SearchEntries(sp, listStorage, null, slStatus);
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
/// <param name="resultContexts">Dictionary that will be populated with text fragments indicating the context of why each entry (keyed by Uuid) was returned</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage,
IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts,
IStatusLogger slStatus)
{
if (sp == null)
{
Debug.Assert(false);
return;
}
if (listStorage == null)
{
Debug.Assert(false);
return;
}
ulong uCurEntries = 0, uTotalEntries = 0;
List<string> lTerms = StrUtil.SplitSearchTerms(sp.SearchString);
if ((lTerms.Count <= 1) || sp.RegularExpression)
{
if (slStatus != null) uTotalEntries = GetEntriesCount(true);
SearchEntriesSingle(sp, listStorage, resultContexts, slStatus, ref uCurEntries,
uTotalEntries);
return;
}
// Search longer strings first (for improved performance)
lTerms.Sort(StrUtil.CompareLengthGt);
string strFullSearch = sp.SearchString; // Backup
PwGroup pg = this;
for (int iTerm = 0; iTerm < lTerms.Count; ++iTerm)
{
// Update counters for a better state guess
if (slStatus != null)
{
ulong uRemRounds = (ulong) (lTerms.Count - iTerm);
uTotalEntries = uCurEntries + (uRemRounds *
pg.GetEntriesCount(true));
}
PwGroup pgNew = new PwGroup();
sp.SearchString = lTerms[iTerm];
bool bNegate = false;
if (sp.SearchString.StartsWith("-"))
{
sp.SearchString = sp.SearchString.Substring(1);
bNegate = (sp.SearchString.Length > 0);
}
if (!pg.SearchEntriesSingle(sp, pgNew.Entries, resultContexts, slStatus,
ref uCurEntries, uTotalEntries))
{
pg = null;
break;
}
if (bNegate)
{
PwObjectList<PwEntry> lCand = pg.GetEntries(true);
pg = new PwGroup();
foreach (PwEntry peCand in lCand)
{
if (pgNew.Entries.IndexOf(peCand) < 0) pg.Entries.Add(peCand);
}
}
else pg = pgNew;
}
if (pg != null) listStorage.Add(pg.Entries);
sp.SearchString = strFullSearch; // Restore
}
private bool SearchEntriesSingle(SearchParameters spIn,
PwObjectList<PwEntry> listStorage, IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts,
IStatusLogger slStatus,
ref ulong uCurEntries, ulong uTotalEntries)
{
SearchParameters sp = spIn.Clone();
if (sp.SearchString == null)
{
Debug.Assert(false);
return true;
}
sp.SearchString = sp.SearchString.Trim();
bool bTitle = sp.SearchInTitles;
bool bUserName = sp.SearchInUserNames;
bool bPassword = sp.SearchInPasswords;
bool bUrl = sp.SearchInUrls;
bool bNotes = sp.SearchInNotes;
bool bOther = sp.SearchInOther;
bool bUuids = sp.SearchInUuids;
bool bGroupName = sp.SearchInGroupNames;
bool bTags = sp.SearchInTags;
bool bExcludeExpired = sp.ExcludeExpired;
bool bRespectEntrySearchingDisabled = sp.RespectEntrySearchingDisabled;
DateTime dtNow = DateTime.Now;
Regex rx = null;
if (sp.RegularExpression)
{
RegexOptions ro = RegexOptions.None; // RegexOptions.Compiled
if ((sp.ComparisonMode == StringComparison.CurrentCultureIgnoreCase) ||
#if !KeePassUAP
(sp.ComparisonMode == StringComparison.InvariantCultureIgnoreCase) ||
#endif
(sp.ComparisonMode == StringComparison.OrdinalIgnoreCase))
{
ro |= RegexOptions.IgnoreCase;
}
rx = new Regex(sp.SearchString, ro);
}
ulong uLocalCurEntries = uCurEntries;
EntryHandler eh = null;
if (sp.SearchString.Length <= 0) // Report all
{
eh = delegate(PwEntry pe)
{
if (slStatus != null)
{
if (!slStatus.SetProgress((uint) ((uLocalCurEntries *
100UL) / uTotalEntries))) return false;
++uLocalCurEntries;
}
if (bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip
if (bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime))
return true; // Skip
listStorage.Add(pe);
return true;
};
}
else
{
eh = delegate(PwEntry pe)
{
if (slStatus != null)
{
if (!slStatus.SetProgress((uint) ((uLocalCurEntries *
100UL) / uTotalEntries))) return false;
++uLocalCurEntries;
}
if (bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip
if (bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime))
return true; // Skip
uint uInitialResults = listStorage.UCount;
foreach (KeyValuePair<string, ProtectedString> kvp in pe.Strings)
{
string strKey = kvp.Key;
if (strKey == PwDefs.TitleField)
{
if (bTitle)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.UserNameField)
{
if (bUserName)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.PasswordField)
{
if (bPassword)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.UrlField)
{
if (bUrl)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.NotesField)
{
if (bNotes)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (bOther)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
// An entry can match only once => break if we have added it
if (listStorage.UCount > uInitialResults) break;
}
if (bUuids && (listStorage.UCount == uInitialResults))
SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, listStorage, resultContexts,
SearchContextTags);
if (bGroupName && (listStorage.UCount == uInitialResults) &&
(pe.ParentGroup != null))
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, listStorage, resultContexts,
SearchContextParentGroup);
if (bTags)
{
foreach (string strTag in pe.Tags)
{
if (listStorage.UCount != uInitialResults) break; // Match
SearchEvalAdd(sp, strTag, rx, pe, listStorage, resultContexts, SearchContextTags);
}
}
return true;
};
}
if (!PreOrderTraverseTree(null, eh)) return false;
uCurEntries = uLocalCurEntries;
return true;
}
private static void SearchEvalAdd(SearchParameters sp, string strDataField,
Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults,
IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts, string contextFieldName)
{
bool bMatch = false;
int matchPos;
if (rx == null)
{
matchPos = strDataField.IndexOf(sp.SearchString, sp.ComparisonMode);
bMatch = matchPos >= 0;
}
else
{
var match = rx.Match(strDataField);
bMatch = match.Success;
matchPos = match.Index;
}
if (!bMatch && (sp.DataTransformationFn != null))
{
string strCmp = sp.DataTransformationFn(strDataField, pe);
if (!object.ReferenceEquals(strCmp, strDataField))
{
if (rx == null)
{
matchPos = strCmp.IndexOf(sp.SearchString, sp.ComparisonMode);
bMatch = matchPos >= 0;
}
else
{
var match = rx.Match(strCmp);
bMatch = match.Success;
matchPos = match.Index;
}
}
}
if (bMatch)
{
lResults.Add(pe);
if (resultContexts != null)
{
// Trim the value if necessary
var contextString = strDataField;
if (contextString.Length > SearchContextStringMaxLength)
{
// Start 10% before actual data, and don't run over
var startPos = Math.Max(0,
Math.Min(matchPos - (SearchContextStringMaxLength / 10),
contextString.Length - SearchContextStringMaxLength));
contextString = "… " + contextString.Substring(startPos, SearchContextStringMaxLength) +
((startPos + SearchContextStringMaxLength < contextString.Length)
? " …"
: null);
}
resultContexts[pe.Uuid] = new KeyValuePair<string, string>(contextFieldName, contextString);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ namespace KeePassLib.Resources
{
string strTemp;
if (dictNew.TryGetValue(strName, out strTemp))
if(dictNew.TryGetValue(strName, out strTemp))
return strTemp;
return strDefault;
@@ -24,11 +24,8 @@ namespace KeePassLib.Resources
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
{
if (dictNew == null) throw new ArgumentNullException("dictNew");
if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strAlgorithmUnknown = TryGetEx(dictNew, "AlgorithmUnknown", m_strAlgorithmUnknown);
m_strCharSetInvalid = TryGetEx(dictNew, "CharSetInvalid", m_strCharSetInvalid);
m_strCharSetTooFewChars = TryGetEx(dictNew, "CharSetTooFewChars", m_strCharSetTooFewChars);
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
@@ -44,7 +41,7 @@ namespace KeePassLib.Resources
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed2 = TryGetEx(dictNew, "FileSaveFailed2", m_strFileSaveFailed2);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
m_strFileSigInvalid = TryGetEx(dictNew, "FileSigInvalid", m_strFileSigInvalid);
m_strFileUnknownCipher = TryGetEx(dictNew, "FileUnknownCipher", m_strFileUnknownCipher);
m_strFileUnknownCompression = TryGetEx(dictNew, "FileUnknownCompression", m_strFileUnknownCompression);
@@ -58,18 +55,12 @@ namespace KeePassLib.Resources
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strKeyHashMismatch = TryGetEx(dictNew, "KeyHashMismatch", m_strKeyHashMismatch);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPathBackslash = TryGetEx(dictNew, "PathBackslash", m_strPathBackslash);
m_strPatternInvalid = TryGetEx(dictNew, "PatternInvalid", m_strPatternInvalid);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strPwGenFailed = TryGetEx(dictNew, "PwGenFailed", m_strPwGenFailed);
m_strStructsTooDeep = TryGetEx(dictNew, "StructsTooDeep", m_strStructsTooDeep);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownError = TryGetEx(dictNew, "UnknownError", m_strUnknownError);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
@@ -77,9 +68,6 @@ namespace KeePassLib.Resources
}
private static readonly string[] m_vKeyNames = {
"AlgorithmUnknown",
"CharSetInvalid",
"CharSetTooFewChars",
"CryptoStreamFailed",
"EncDataTooLarge",
"ErrorInClipboard",
@@ -95,7 +83,7 @@ namespace KeePassLib.Resources
"FileNewVerOrPlgReq",
"FileNewVerReq",
"FileSaveCorruptionWarning",
"FileSaveFailed2",
"FileSaveFailed",
"FileSigInvalid",
"FileUnknownCipher",
"FileUnknownCompression",
@@ -109,18 +97,12 @@ namespace KeePassLib.Resources
"KeePass1xHint",
"KeyBits",
"KeyFileDbSel",
"KeyHashMismatch",
"MasterSeedLengthInvalid",
"OldFormat",
"Passive",
"PathBackslash",
"PatternInvalid",
"PreAuth",
"PwGenFailed",
"StructsTooDeep",
"Timeout",
"TryAgainSecs",
"UnknownError",
"UnknownHeaderId",
"UnknownKdf",
"UserAccountKeyError",
@@ -132,39 +114,6 @@ namespace KeePassLib.Resources
return m_vKeyNames;
}
private static string m_strAlgorithmUnknown =
@"The algorithm is unknown.";
/// <summary>
/// Look up a localized string similar to
/// 'The algorithm is unknown.'.
/// </summary>
public static string AlgorithmUnknown
{
get { return m_strAlgorithmUnknown; }
}
private static string m_strCharSetInvalid =
@"The character set is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The character set is invalid.'.
/// </summary>
public static string CharSetInvalid
{
get { return m_strCharSetInvalid; }
}
private static string m_strCharSetTooFewChars =
@"There are too few characters in the character set.";
/// <summary>
/// Look up a localized string similar to
/// 'There are too few characters in the character set.'.
/// </summary>
public static string CharSetTooFewChars
{
get { return m_strCharSetTooFewChars; }
}
private static string m_strCryptoStreamFailed =
@"Failed to initialize encryption/decryption stream!";
/// <summary>
@@ -330,15 +279,15 @@ namespace KeePassLib.Resources
get { return m_strFileSaveCorruptionWarning; }
}
private static string m_strFileSaveFailed2 =
@"Failed to save to the specified file!";
private static string m_strFileSaveFailed =
@"Failed to save the current database to the specified location!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to save to the specified file!'.
/// 'Failed to save the current database to the specified location!'.
/// </summary>
public static string FileSaveFailed2
public static string FileSaveFailed
{
get { return m_strFileSaveFailed2; }
get { return m_strFileSaveFailed; }
}
private static string m_strFileSigInvalid =
@@ -397,10 +346,10 @@ namespace KeePassLib.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
{
@@ -419,10 +368,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKey =
@"The master key is invalid!";
@"The composite key is invalid!";
/// <summary>
/// Look up a localized string similar to
/// 'The master key is invalid!'.
/// 'The composite key is invalid!'.
/// </summary>
public static string InvalidCompositeKey
{
@@ -430,10 +379,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKeyHint =
@"Make sure that the master key is correct and try it again.";
@"Make sure the composite key is correct and try again.";
/// <summary>
/// Look up a localized string similar to
/// 'Make sure that the master key is correct and try it again.'.
/// 'Make sure the composite key is correct and try again.'.
/// </summary>
public static string InvalidCompositeKeyHint
{
@@ -484,17 +433,6 @@ namespace KeePassLib.Resources
get { return m_strKeyFileDbSel; }
}
private static string m_strKeyHashMismatch =
@"The key and the hash do not match, i.e. the key or the hash is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The key and the hash do not match, i.e. the key or the hash is invalid.'.
/// </summary>
public static string KeyHashMismatch
{
get { return m_strKeyHashMismatch; }
}
private static string m_strMasterSeedLengthInvalid =
@"The length of the master key seed is invalid!";
/// <summary>
@@ -528,28 +466,6 @@ namespace KeePassLib.Resources
get { return m_strPassive; }
}
private static string m_strPathBackslash =
@"The path contains a backslash. Such paths are not supported (for security reasons).";
/// <summary>
/// Look up a localized string similar to
/// 'The path contains a backslash. Such paths are not supported (for security reasons).'.
/// </summary>
public static string PathBackslash
{
get { return m_strPathBackslash; }
}
private static string m_strPatternInvalid =
@"The pattern is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The pattern is invalid.'.
/// </summary>
public static string PatternInvalid
{
get { return m_strPatternInvalid; }
}
private static string m_strPreAuth =
@"Pre-authenticate";
/// <summary>
@@ -561,28 +477,6 @@ namespace KeePassLib.Resources
get { return m_strPreAuth; }
}
private static string m_strPwGenFailed =
@"Failed to generate a password.";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to generate a password.'.
/// </summary>
public static string PwGenFailed
{
get { return m_strPwGenFailed; }
}
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>
@@ -605,17 +499,6 @@ namespace KeePassLib.Resources
get { return m_strTryAgainSecs; }
}
private static string m_strUnknownError =
@"An unknown error occurred.";
/// <summary>
/// Look up a localized string similar to
/// 'An unknown error occurred.'.
/// </summary>
public static string UnknownError
{
get { return m_strUnknownError; }
}
private static string m_strUnknownHeaderId =
@"Unknown header ID!";
/// <summary>

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,11 +33,11 @@ using KeePassLibSD;
namespace KeePassLib.Security
{
/// <summary>
/// A string that is protected in process memory.
/// Represents an in-memory encrypted string.
/// <c>ProtectedString</c> objects are immutable and thread-safe.
/// </summary>
#if (DEBUG && !KeePassLibSD)
[DebuggerDisplay("{ReadString()}")]
[DebuggerDisplay(@"{ReadString()}")]
#endif
public sealed class ProtectedString
{
@@ -48,24 +48,11 @@ namespace KeePassLib.Security
private bool m_bIsProtected;
private static readonly ProtectedString m_psEmpty = new ProtectedString();
/// <summary>
/// Get an empty <c>ProtectedString</c> object, without protection.
/// </summary>
public static ProtectedString Empty
{
get { return m_psEmpty; }
}
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
true, new byte[0]);
/// <summary>
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
/// </summary>
public static ProtectedString EmptyEx
{
get { return m_psEmptyEx; }
}
/// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object
/// has turned on memory protection or not.
@@ -79,8 +66,8 @@ namespace KeePassLib.Security
{
get
{
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return (p.Length == 0);
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return (pBin.Length == 0);
Debug.Assert(m_strPlainText != null);
return (m_strPlainText.Length == 0);
@@ -88,21 +75,18 @@ namespace KeePassLib.Security
}
private int m_nCachedLength = -1;
/// <summary>
/// Length of the protected string, in characters.
/// </summary>
public int Length
{
get
{
if (m_nCachedLength >= 0) return m_nCachedLength;
if(m_nCachedLength >= 0) return m_nCachedLength;
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null)
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null)
{
byte[] pbPlain = p.ReadData();
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
finally { MemUtil.ZeroByteArray(pbPlain); }
byte[] pbPlain = pBin.ReadData();
m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain);
MemUtil.ZeroByteArray(pbPlain);
}
else
{
@@ -156,51 +140,53 @@ namespace KeePassLib.Security
/// to the value passed in the <c>XorredBuffer</c> object.
/// </summary>
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xb"><c>XorredBuffer</c> object containing the
/// <param name="xbProtected"><c>XorredBuffer</c> object containing the
/// string in UTF-8 representation. The UTF-8 string must not
/// be <c>null</c>-terminated.</param>
public ProtectedString(bool bEnableProtection, XorredBuffer xb)
public ProtectedString(bool bEnableProtection, XorredBuffer xbProtected)
{
if (xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xb.ReadPlainText();
try { Init(bEnableProtection, pb); }
finally { if (bEnableProtection) MemUtil.ZeroByteArray(pb); }
byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
}
private void Init(bool bEnableProtection, string str)
{
if (str == null) throw new ArgumentNullException("str");
if(str == null) throw new ArgumentNullException("str");
m_bIsProtected = bEnableProtection;
// As the string already is in memory and immutable,
// The string already is in memory and immutable,
// protection would be useless
m_strPlainText = str;
}
private void Init(bool bEnableProtection, byte[] pbUtf8)
{
if (pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
m_bIsProtected = bEnableProtection;
if (bEnableProtection)
if(bEnableProtection)
m_pbUtf8 = new ProtectedBinary(true, pbUtf8);
else
m_strPlainText = StrUtil.Utf8.GetString(pbUtf8, 0, pbUtf8.Length);
}
/// <summary>
/// Convert the protected string to a standard string object.
/// Be careful with this function, as the returned string object
/// Convert the protected string to a normal string object.
/// Be careful with this function, the returned string object
/// isn't protected anymore and stored in plain-text in the
/// process memory.
/// </summary>
/// <returns>Plain-text string. Is never <c>null</c>.</returns>
public string ReadString()
{
if (m_strPlainText != null) return m_strPlainText;
if(m_strPlainText != null) return m_strPlainText;
byte[] pb = ReadUtf8();
string str = ((pb.Length == 0) ? string.Empty :
@@ -208,120 +194,82 @@ namespace KeePassLib.Security
// No need to clear pb
// As the text is now visible in process memory anyway,
// there's no need to protect it anymore (strings are
// immutable and thus cannot be overwritten)
// there's no need to protect it anymore
m_strPlainText = str;
m_pbUtf8 = null; // Thread-safe order
return str;
}
/// <summary>
/// Read out the string and return it as a char array.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text char array.</returns>
public char[] ReadChars()
{
if (m_strPlainText != null) return m_strPlainText.ToCharArray();
byte[] pb = ReadUtf8();
char[] v;
try { v = StrUtil.Utf8.GetChars(pb); }
finally { MemUtil.ZeroByteArray(pb); }
return v;
}
/// <summary>
/// Read out the string and return a byte array that contains the
/// string encoded using UTF-8.
/// The returned array is not protected and should be cleared by
/// the caller.
/// string encoded using UTF-8. The returned string is not protected
/// anymore!
/// </summary>
/// <returns>Plain-text UTF-8 byte array.</returns>
public byte[] ReadUtf8()
{
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return p.ReadData();
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return pBin.ReadData();
return StrUtil.Utf8.GetBytes(m_strPlainText);
}
/// <summary>
/// Get the string as an UTF-8 sequence xorred with bytes
/// from a <c>CryptoRandomStream</c>.
/// Read the protected string and return it protected with a sequence
/// of bytes generated by a random stream.
/// </summary>
/// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected string.</returns>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{
if (crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
byte[] pbData = ReadUtf8();
int cb = pbData.Length;
uint uLen = (uint)pbData.Length;
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
Debug.Assert(pbPad.Length == cb);
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == pbData.Length);
for (int i = 0; i < cb; ++i)
pbData[i] ^= pbPad[i];
for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i];
MemUtil.ZeroByteArray(pbPad);
return pbData;
}
public ProtectedString WithProtection(bool bProtect)
{
if (bProtect == m_bIsProtected) return this;
if(bProtect == m_bIsProtected) return this;
byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb);
// No need to clear pb; either the current or the new object is unprotected
return new ProtectedString(bProtect, pb);
}
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
{
if (ps == null) throw new ArgumentNullException("ps");
if (object.ReferenceEquals(this, ps)) return true; // Perf. opt.
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
if (bCheckProtEqual && (bPA != bPB)) return false;
if (!bPA && !bPB) return (ReadString() == ps.ReadString());
byte[] pbA = ReadUtf8(), pbB = null;
bool bEq;
try
{
pbB = ps.ReadUtf8();
bEq = MemUtil.ArraysEqual(pbA, pbB);
}
finally
{
if (bPA) MemUtil.ZeroByteArray(pbA);
if (bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
}
return bEq;
if(bProtect) MemUtil.ZeroByteArray(pb);
return ps;
}
public ProtectedString Insert(int iStart, string strInsert)
{
if (iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if (strInsert == null) throw new ArgumentNullException("strInsert");
if (strInsert.Length == 0) return this;
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(strInsert == null) throw new ArgumentNullException("strInsert");
if(strInsert.Length == 0) return this;
if (!m_bIsProtected)
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert(
iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
try
{
if (iStart > v.Length)
if(iStart > v.Length)
throw new ArgumentOutOfRangeException("iStart");
char[] vIns = strInsert.ToCharArray();
@@ -331,104 +279,68 @@ namespace KeePassLib.Security
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
v.Length - iStart);
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
}
finally
{
MemUtil.ZeroArray<char>(v);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
MemUtil.ZeroByteArray(pb);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public ProtectedString Remove(int iStart, int nCount)
{
if (iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if (nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if (nCount == 0) return this;
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return this;
if (!m_bIsProtected)
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove(
iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
try
{
if ((iStart + nCount) > v.Length)
throw new ArgumentException("(iStart + nCount) > v.Length");
if((iStart + nCount) > v.Length)
throw new ArgumentException("iStart + nCount");
vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount));
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
}
finally
{
MemUtil.ZeroArray<char>(v);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
MemUtil.ZeroByteArray(pb);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
{
if (a == null) throw new ArgumentNullException("a");
if (b == null) throw new ArgumentNullException("b");
if (b.IsEmpty) return a.WithProtection(a.IsProtected || b.IsProtected);
if (a.IsEmpty) return b.WithProtection(a.IsProtected || b.IsProtected);
if (!a.IsProtected && !b.IsProtected)
return new ProtectedString(false, a.ReadString() + b.ReadString());
char[] vA = a.ReadChars(), vB = null, vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
vB = b.ReadChars();
vNew = new char[vA.Length + vB.Length];
Array.Copy(vA, vNew, vA.Length);
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
pbNew = StrUtil.Utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
}
finally
{
MemUtil.ZeroArray<char>(vA);
if (vB != null) MemUtil.ZeroArray<char>(vB);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public static ProtectedString operator +(ProtectedString a, string b)
{
ProtectedString psB = new ProtectedString(false, b);
return (a + psB);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,90 +20,97 @@
using System;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Security
{
/// <summary>
/// A <c>XorredBuffer</c> object stores data that is encrypted
/// using a XOR pad.
/// Represents an object that is encrypted using a XOR pad until
/// it is read. <c>XorredBuffer</c> objects are immutable and
/// thread-safe.
/// </summary>
public sealed class XorredBuffer : IDisposable
public sealed class XorredBuffer
{
private byte[] m_pbCT;
private byte[] m_pbXorPad;
private byte[] m_pbData; // Never null
private byte[] m_pbXorPad; // Always valid for m_pbData
/// <summary>
/// Length of the protected data in bytes.
/// </summary>
public uint Length
{
get
{
if (m_pbCT == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
return (uint)m_pbCT.Length;
}
get { return (uint)m_pbData.Length; }
}
/// <summary>
/// Construct a new <c>XorredBuffer</c> object.
/// The <paramref name="pbCT" /> byte array must have the same
/// length as the <paramref name="pbXorPad" /> byte array.
/// Construct a new XOR-protected object using a protected byte array
/// and a XOR pad that decrypts the protected data. The
/// <paramref name="pbProtectedData" /> byte array must have the same size
/// as the <paramref name="pbXorPad" /> byte array.
/// The <c>XorredBuffer</c> object takes ownership of the two byte
/// arrays, i.e. the caller must not use them afterwards.
/// arrays, i.e. the caller must not use or modify them afterwards.
/// </summary>
/// <param name="pbCT">Data with XOR pad applied.</param>
/// <param name="pbProtectedData">Protected data (XOR pad applied).</param>
/// <param name="pbXorPad">XOR pad that can be used to decrypt the
/// <paramref name="pbCT" /> byte array.</param>
public XorredBuffer(byte[] pbCT, byte[] pbXorPad)
/// <paramref name="pbProtectedData" /> parameter.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
/// <exception cref="System.ArgumentException">Thrown if the byte arrays are
/// of different size.</exception>
public XorredBuffer(byte[] pbProtectedData, byte[] pbXorPad)
{
if (pbCT == null) { Debug.Assert(false); throw new ArgumentNullException("pbCT"); }
if (pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("pbXorPad"); }
if (pbCT.Length != pbXorPad.Length)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbXorPad");
}
if(pbProtectedData == null) { Debug.Assert(false); throw new ArgumentNullException("pbProtectedData"); }
if(pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("pbXorPad"); }
m_pbCT = pbCT;
Debug.Assert(pbProtectedData.Length == pbXorPad.Length);
if(pbProtectedData.Length != pbXorPad.Length) throw new ArgumentException();
m_pbData = pbProtectedData;
m_pbXorPad = pbXorPad;
}
#if DEBUG
~XorredBuffer()
{
Debug.Assert((m_pbCT == null) && (m_pbXorPad == null));
}
#endif
public void Dispose()
{
if (m_pbCT == null) return;
MemUtil.ZeroByteArray(m_pbCT);
m_pbCT = null;
MemUtil.ZeroByteArray(m_pbXorPad);
m_pbXorPad = null;
}
/// <summary>
/// Get a copy of the plain-text. The caller is responsible
/// for clearing the byte array safely after using it.
/// </summary>
/// <returns>Plain-text byte array.</returns>
/// <returns>Unprotected plain-text byte array.</returns>
public byte[] ReadPlainText()
{
byte[] pbCT = m_pbCT, pbX = m_pbXorPad;
if ((pbCT == null) || (pbX == null) || (pbCT.Length != pbX.Length))
byte[] pbPlain = new byte[m_pbData.Length];
for(int i = 0; i < pbPlain.Length; ++i)
pbPlain[i] = (byte)(m_pbData[i] ^ m_pbXorPad[i]);
return pbPlain;
}
/* public bool EqualsValue(XorredBuffer xb)
{
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
if(xb.m_pbData.Length != m_pbData.Length) return false;
for(int i = 0; i < m_pbData.Length; ++i)
{
Debug.Assert(false);
throw new ObjectDisposedException(null);
byte bt1 = (byte)(m_pbData[i] ^ m_pbXorPad[i]);
byte bt2 = (byte)(xb.m_pbData[i] ^ xb.m_pbXorPad[i]);
if(bt1 != bt2) return false;
}
byte[] pbPT = new byte[pbCT.Length];
for (int i = 0; i < pbPT.Length; ++i)
pbPT[i] = (byte)(pbCT[i] ^ pbX[i]);
return pbPT;
return true;
}
public bool EqualsValue(byte[] pb)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if(pb.Length != m_pbData.Length) return false;
for(int i = 0; i < m_pbData.Length; ++i)
{
if((byte)(m_pbData[i] ^ m_pbXorPad[i]) != pb[i]) return false;
}
return true;
} */
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -78,9 +78,9 @@ namespace KeePassLib.Serialization
public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
{
Debug.Assert(sSource != null);
if (sSource == null) throw new ArgumentNullException("sSource");
if(sSource == null) throw new ArgumentNullException("sSource");
if (m_bUsedOnce)
if(m_bUsedOnce)
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
m_bUsedOnce = true;
@@ -91,8 +91,7 @@ namespace KeePassLib.Serialization
m_format = fmt;
m_slLogger = slLogger;
// Other applications might not perform a deduplication
m_pbsBinaries = new ProtectedBinarySet(false);
m_pbsBinaries.Clear();
UTF8Encoding encNoBom = StrUtil.Utf8;
byte[] pbCipherKey = null;
@@ -104,156 +103,153 @@ namespace KeePassLib.Serialization
HashingStreamEx sHashing = new HashingStreamEx(sSource, false, null);
lStreams.Add(sHashing);
try
{
Stream sXml;
if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
{
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
Stream sPlain;
if (m_uFileVersion < FileVersion32_4)
{
Stream sDecrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, false);
if ((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
lStreams.Add(sDecrypted);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, strIncomplete);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
}
else // KDBX >= 4
{
byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
if ((pbStoredHash == null) || (pbStoredHash.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
throw new InvalidDataException(KLRes.FileHeaderCorrupted);
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
if ((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
throw new InvalidCompositeKeyException();
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if ((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
if (m_uFileVersion >= FileVersion32_4)
LoadInnerHeader(sXml); // Binary header before XML
}
else if (fmt == KdbxFormat.PlainXml)
sXml = sHashing;
else
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("fmt");
}
if (fmt == KdbxFormat.Default)
{
if (m_pbInnerRandomStreamKey == null)
{
Debug.Assert(false);
throw new SecurityException("Invalid inner random stream key!");
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo);
#if KeePassDebug_WriteXml
#warning XML output is enabled!
/* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
FileAccess.Write, FileShare.None))
try
{
Stream sXml;
if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
{
while(true)
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
Stream sPlain;
if(m_uFileVersion < FileVersion32_4)
{
int b = sXml.ReadByte();
if(b == -1) throw new EndOfStreamException();
fsOut.WriteByte((byte)b);
Stream sDecrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, false);
if((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
lStreams.Add(sDecrypted);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, strIncomplete);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
}
} */
else // KDBX >= 4
{
byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
throw new InvalidDataException(KLRes.FileHeaderCorrupted);
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
throw new InvalidCompositeKeyException();
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
if(m_uFileVersion >= FileVersion32_4)
LoadInnerHeader(sXml); // Binary header before XML
}
else if(fmt == KdbxFormat.PlainXml)
sXml = sHashing;
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
if(fmt == KdbxFormat.Default)
{
if(m_pbInnerRandomStreamKey == null)
{
Debug.Assert(false);
throw new SecurityException("Invalid inner random stream key!");
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo);
#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();
#endif
var stopWatch = Stopwatch.StartNew();
var stopWatch = Stopwatch.StartNew();
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
}
else
{
}
else
{
ReadXmlStreamed(sXml, sHashing);
ReadXmlStreamed(sXml, sHashing);
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
}
// ReadXmlDom(sXml);
}
catch(CryptographicException) // Thrown on invalid padding
{
throw new CryptographicException(KLRes.FileCorrupted);
}
finally
{
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
}
}
// ReadXmlDom(sXml);
catch (CryptographicException) // Thrown on invalid padding
{
throw new CryptographicException(KLRes.FileCorrupted);
}
finally
{
if (pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if (pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpRead(lStreams, sHashing);
}
CommonCleanUpRead(lStreams, sHashing);
}
#if KDBX_BENCHMARK
swTime.Stop();
@@ -271,9 +267,9 @@ namespace KeePassLib.Serialization
Debug.Assert(m_pbHashOfFileOnDisk != null);
CleanUpInnerRandomStream();
// Reset memory protection settings (to always use reasonable
// defaults)
// defaults)
m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();
// Remove old backups (this call is required here in order to apply
@@ -286,7 +282,7 @@ namespace KeePassLib.Serialization
// Expand the root group, such that in case the user accidently
// collapses the root group he can simply reopen the database
PwGroup pgRoot = m_pwDatabase.RootGroup;
if (pgRoot != null) pgRoot.IsExpanded = true;
if(pgRoot != null) pgRoot.IsExpanded = true;
else { Debug.Assert(false); }
m_pbHashOfHeader = null;
@@ -307,25 +303,25 @@ namespace KeePassLib.Serialization
byte[] pbSig2 = br.ReadBytes(4);
uint uSig2 = MemUtil.BytesToUInt32(pbSig2);
if ((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
if((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
throw new OldFormatException(PwDefs.ShortProductName + @" 1.x",
OldFormatException.OldFormatType.KeePass1x);
if ((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
else if ((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
if((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
else if((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
FileSignaturePreRelease2)) { }
else throw new FormatException(KLRes.FileSigInvalid);
byte[] pb = br.ReadBytes(4);
uint uVersion = MemUtil.BytesToUInt32(pb);
if ((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
throw new FormatException(KLRes.FileVersionUnsupported +
MessageService.NewParagraph + KLRes.FileNewVerReq);
m_uFileVersion = uVersion;
while (true)
while(true)
{
if (!ReadHeaderField(br)) break;
if(!ReadHeaderField(br)) break;
}
br.CopyDataTo = null;
@@ -339,23 +335,23 @@ namespace KeePassLib.Serialization
private bool ReadHeaderField(BinaryReaderEx brSource)
{
Debug.Assert(brSource != null);
if (brSource == null) throw new ArgumentNullException("brSource");
if(brSource == null) throw new ArgumentNullException("brSource");
byte btFieldID = brSource.ReadByte();
int cbSize;
Debug.Assert(m_uFileVersion > 0);
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
else cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
if (cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
byte[] pbData = MemUtil.EmptyByteArray;
if (cbSize > 0) pbData = brSource.ReadBytes(cbSize);
if(cbSize > 0) pbData = brSource.ReadBytes(cbSize);
bool bResult = true;
KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID;
switch (kdbID)
switch(kdbID)
{
case KdbxHeaderFieldID.EndOfHeader:
bResult = false; // Returning false indicates end of header
@@ -379,7 +375,7 @@ namespace KeePassLib.Serialization
Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfS = new AesKdf();
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
// m_pbTransformSeed = pbData;
@@ -393,7 +389,7 @@ namespace KeePassLib.Serialization
Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfR = new AesKdf();
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
// m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
@@ -433,8 +429,8 @@ namespace KeePassLib.Serialization
default:
Debug.Assert(false);
if (m_slLogger != null)
m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
if(m_slLogger != null)
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
kdbID.ToString() + "!", LogStatusType.Warning);
break;
}
@@ -447,28 +443,28 @@ namespace KeePassLib.Serialization
BinaryReaderEx br = new BinaryReaderEx(s, StrUtil.Utf8,
KLRes.FileCorrupted + " " + KLRes.FileIncompleteExpc);
while (true)
while(true)
{
if (!ReadInnerHeaderField(br)) break;
if(!ReadInnerHeaderField(br)) break;
}
}
private bool ReadInnerHeaderField(BinaryReaderEx br)
{
Debug.Assert(br != null);
if (br == null) throw new ArgumentNullException("br");
if(br == null) throw new ArgumentNullException("br");
byte btFieldID = br.ReadByte();
int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4));
if (cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
byte[] pbData = MemUtil.EmptyByteArray;
if (cbSize > 0) pbData = br.ReadBytes(cbSize);
if(cbSize > 0) pbData = br.ReadBytes(cbSize);
bool bResult = true;
KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID;
switch (kdbID)
switch(kdbID)
{
case KdbxInnerHeaderFieldID.EndOfHeader:
bResult = false; // Returning false indicates end of header
@@ -485,16 +481,15 @@ namespace KeePassLib.Serialization
break;
case KdbxInnerHeaderFieldID.Binary:
if (pbData.Length < 1) throw new FormatException();
if(pbData.Length < 1) throw new FormatException();
KdbxBinaryFlags f = (KdbxBinaryFlags)pbData[0];
bool bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None);
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
1, pbData.Length - 1);
Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
m_pbsBinaries.Add(pb);
if (bProt) MemUtil.ZeroByteArray(pbData);
if(bProt) MemUtil.ZeroByteArray(pbData);
break;
default:
@@ -507,7 +502,7 @@ namespace KeePassLib.Serialization
private void SetCipher(byte[] pbID)
{
if ((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
throw new FormatException(KLRes.FileUnknownCipher);
m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
@@ -516,7 +511,7 @@ namespace KeePassLib.Serialization
private void SetCompressionFlags(byte[] pbFlags)
{
int nID = (int)MemUtil.BytesToUInt32(pbFlags);
if ((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
if((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
throw new FormatException(KLRes.FileUnknownCompression);
m_pwDatabase.Compression = (PwCompressionAlgorithm)nID;
@@ -525,35 +520,12 @@ namespace KeePassLib.Serialization
private void SetInnerRandomStreamID(byte[] pbID)
{
uint uID = MemUtil.BytesToUInt32(pbID);
if (uID >= (uint)CrsAlgorithm.Count)
if(uID >= (uint)CrsAlgorithm.Count)
throw new FormatException(KLRes.FileUnknownCipher);
m_craInnerRandomStream = (CrsAlgorithm)uID;
}
internal static PwGroup ReadGroup(Stream msData, PwDatabase pdContext,
bool bCopyIcons, bool bNewUuids, bool bSetCreatedNow)
{
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
KdbxFile f = new KdbxFile(pd);
f.Load(msData, KdbxFormat.PlainXml, null);
if (bCopyIcons)
PwDatabase.CopyCustomIcons(pd, pdContext, pd.RootGroup, true);
if (bNewUuids)
{
pd.RootGroup.Uuid = new PwUuid(true);
pd.RootGroup.CreateNewItemUuids(true, true, true);
}
if (bSetCreatedNow) pd.RootGroup.SetCreatedNow(true);
return pd.RootGroup;
}
[Obsolete]
public static List<PwEntry> ReadEntries(Stream msData)
{
@@ -565,14 +537,81 @@ namespace KeePassLib.Serialization
{
return ReadEntries(msData, pdContext, true);
}
/// <summary>
/// Read entries from a stream.
/// </summary>
/// <param name="msData">Input stream to read the entries from.</param>
/// <returns>Extracted entries.</returns>
/// <param name="pdContext">Context database (e.g. for storing icons).</param>
/// <param name="bCopyIcons">If <c>true</c>, custom icons required by
/// the loaded entries are copied to the context database.</param>
/// <returns>Loaded entries.</returns>
public static List<PwEntry> ReadEntries(Stream msData, PwDatabase pdContext,
bool bCopyIcons)
{
if (msData == null) { Debug.Assert(false); return new List<PwEntry>(); }
List<PwEntry> lEntries = new List<PwEntry>();
/* KdbxFile f = new KdbxFile(pwDatabase);
if(msData == null) { Debug.Assert(false); return lEntries; }
f.m_format = KdbxFormat.PlainXml;
PwGroup pg = ReadGroup(msData, pdContext, bCopyIcons, true, true);
return pg.GetEntries(true).CloneShallowToList();
XmlDocument doc = new XmlDocument();
doc.Load(msData);
XmlElement el = doc.DocumentElement;
if(el.Name != ElemRoot) throw new FormatException();
List<PwEntry> vEntries = new List<PwEntry>();
foreach(XmlNode xmlChild in el.ChildNodes)
{
if(xmlChild.Name == ElemEntry)
{
PwEntry pe = f.ReadEntry(xmlChild);
pe.Uuid = new PwUuid(true);
foreach(PwEntry peHistory in pe.History)
peHistory.Uuid = pe.Uuid;
vEntries.Add(pe);
}
else { Debug.Assert(false); }
}
return vEntries; */
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
KdbxFile f = new KdbxFile(pd);
f.Load(msData, KdbxFormat.PlainXml, null);
foreach(PwEntry pe in pd.RootGroup.Entries)
{
pe.SetUuid(new PwUuid(true), true);
lEntries.Add(pe);
if(bCopyIcons && (pdContext != null))
{
PwUuid pu = pe.CustomIconUuid;
if(!pu.Equals(PwUuid.Zero))
{
int iSrc = pd.GetCustomIconIndex(pu);
int iDst = pdContext.GetCustomIconIndex(pu);
if(iSrc < 0) { Debug.Assert(false); }
else if(iDst < 0)
{
pdContext.CustomIcons.Add(pd.CustomIcons[iSrc]);
pdContext.Modified = true;
pdContext.UINeedsIconUpdate = true;
}
}
}
}
return lEntries;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ using System.IO;
using System.Security;
using System.Text;
using System.Xml;
using keepass2android;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
@@ -48,6 +48,8 @@ using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Serialization
{
/// <summary>
@@ -79,15 +81,14 @@ namespace KeePassLib.Serialization
IStatusLogger slLogger)
{
Debug.Assert(sSaveTo != null);
if (sSaveTo == null) throw new ArgumentNullException("sSaveTo");
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
if (m_bUsedOnce)
if(m_bUsedOnce)
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
m_bUsedOnce = true;
m_format = fmt;
m_slLogger = slLogger;
m_xmlWriter = null;
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
UTF8Encoding encNoBom = StrUtil.Utf8;
@@ -95,7 +96,7 @@ namespace KeePassLib.Serialization
byte[] pbCipherKey = null;
byte[] pbHmacKey64 = null;
m_pbsBinaries = new ProtectedBinarySet(true);
m_pbsBinaries.Clear();
m_pbsBinaries.AddFrom(pgRoot);
List<Stream> lStreams = new List<Stream>();
@@ -106,10 +107,6 @@ namespace KeePassLib.Serialization
try
{
// Fix history entries (should not be necessary; just for safety,
// as e.g. XPath searches depend on correct history entry UUIDs)
if (m_pwDatabase.MaintainBackups()) { Debug.Assert(false); }
m_uFileVersion = GetMinKdbxVersion();
int cbEncKey, cbEncIV;
@@ -121,30 +118,30 @@ namespace KeePassLib.Serialization
// m_pbTransformSeed = cr.GetRandomBytes(32);
PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid;
KdfEngine kdf = KdfPool.Get(puKdf);
if (kdf == null)
if(kdf == null)
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
// KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + puKdf.ToHexString() + ".");
kdf.Randomize(m_pwDatabase.KdfParameters);
if (m_format == KdbxFormat.Default)
if(m_format == KdbxFormat.Default)
{
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
{
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
m_pbInnerRandomStreamKey = cr.GetRandomBytes(32);
}
else // KDBX >= 4
{
m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
}
else // KDBX >= 4
{
m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
m_pbInnerRandomStreamKey = cr.GetRandomBytes(64);
}
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream sXml;
@@ -159,11 +156,11 @@ namespace KeePassLib.Serialization
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
Stream sPlain;
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
{
Stream sEncrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, true);
if ((sEncrypted == null) || (sEncrypted == sHashing))
if((sEncrypted == null) || (sEncrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
lStreams.Add(sEncrypted);
@@ -185,22 +182,22 @@ namespace KeePassLib.Serialization
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, true);
if ((sPlain == null) || (sPlain == sBlocks))
if((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Compress);
lStreams.Add(sXml);
}
else sXml = sPlain;
if (m_uFileVersion >= FileVersion32_4)
if(m_uFileVersion >= FileVersion32_4)
WriteInnerHeader(sXml); // Binary header before XML
}
else if (m_format == KdbxFormat.PlainXml)
else if(m_format == KdbxFormat.PlainXml)
sXml = sHashing;
else
{
@@ -254,8 +251,6 @@ namespace KeePassLib.Serialization
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{
if (m_xmlWriter != null) { m_xmlWriter.Close(); m_xmlWriter = null; }
CloseStreams(lStreams);
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
@@ -264,13 +259,14 @@ namespace KeePassLib.Serialization
CleanUpInnerRandomStream();
m_xmlWriter = null;
m_pbHashOfHeader = null;
}
private byte[] GenerateHeader()
{
byte[] pbHeader;
using (MemoryStream ms = new MemoryStream())
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
@@ -285,7 +281,7 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
{
Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
(new AesKdf()).Uuid));
@@ -299,10 +295,10 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
if (m_pbEncryptionIV.Length > 0)
if(m_pbEncryptionIV.Length > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
{
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamKey,
m_pbInnerRandomStreamKey);
@@ -310,14 +306,14 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
m_pbStreamStartBytes);
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
}
// Write public custom data only when there is at least one item,
// because KDBX 3.1 didn't support this field yet
if (m_pwDatabase.PublicCustomData.Count > 0)
if(m_pwDatabase.PublicCustomData.Count > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
@@ -337,12 +333,12 @@ namespace KeePassLib.Serialization
byte[] pb = (pbData ?? MemUtil.EmptyByteArray);
int cb = pb.Length;
if (cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
Debug.Assert(m_uFileVersion > 0);
if (m_uFileVersion < FileVersion32_4)
if(m_uFileVersion < FileVersion32_4)
{
if (cb > (int)ushort.MaxValue)
if(cb > (int)ushort.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbData");
@@ -365,13 +361,13 @@ namespace KeePassLib.Serialization
m_pbInnerRandomStreamKey, null);
ProtectedBinary[] vBin = m_pbsBinaries.ToArray();
for (int i = 0; i < vBin.Length; ++i)
for(int i = 0; i < vBin.Length; ++i)
{
ProtectedBinary pb = vBin[i];
if (pb == null) throw new InvalidOperationException();
if(pb == null) throw new InvalidOperationException();
KdbxBinaryFlags f = KdbxBinaryFlags.None;
if (pb.IsProtected) f |= KdbxBinaryFlags.Protected;
if(pb.IsProtected) f |= KdbxBinaryFlags.Protected;
byte[] pbFlags = new byte[1] { (byte)f };
byte[] pbData = pb.ReadData();
@@ -379,7 +375,7 @@ namespace KeePassLib.Serialization
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.Binary,
pbFlags, pbData);
if (pb.IsProtected) MemUtil.ZeroByteArray(pbData);
if(pb.IsProtected) MemUtil.ZeroByteArray(pbData);
}
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.EndOfHeader,
@@ -395,7 +391,7 @@ namespace KeePassLib.Serialization
byte[] pb2 = (pbData2 ?? MemUtil.EmptyByteArray);
int cb = pb1.Length + pb2.Length;
if (cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
MemUtil.Write(s, pb1);
@@ -405,7 +401,7 @@ namespace KeePassLib.Serialization
private void WriteDocument(PwGroup pgRoot)
{
Debug.Assert(m_xmlWriter != null);
if (m_xmlWriter == null) throw new InvalidOperationException();
if(m_xmlWriter == null) throw new InvalidOperationException();
uint uNumGroups, uNumEntries, uCurEntry = 0;
pgRoot.GetCounts(true, out uNumGroups, out uNumEntries);
@@ -421,14 +417,14 @@ namespace KeePassLib.Serialization
Stack<PwGroup> groupStack = new Stack<PwGroup>();
groupStack.Push(pgRoot);
GroupHandler gh = delegate (PwGroup pg)
GroupHandler gh = delegate(PwGroup pg)
{
Debug.Assert(pg != null);
if (pg == null) throw new ArgumentNullException("pg");
if(pg == null) throw new ArgumentNullException("pg");
while (true)
while(true)
{
if (pg.ParentGroup == groupStack.Peek())
if(pg.ParentGroup == groupStack.Peek())
{
groupStack.Push(pg);
StartGroup(pg);
@@ -437,7 +433,7 @@ namespace KeePassLib.Serialization
else
{
groupStack.Pop();
if (groupStack.Count <= 0) return false;
if(groupStack.Count <= 0) return false;
EndGroup();
}
@@ -446,25 +442,23 @@ namespace KeePassLib.Serialization
return true;
};
EntryHandler eh = delegate (PwEntry pe)
EntryHandler eh = delegate(PwEntry pe)
{
Debug.Assert(pe != null);
WriteEntry(pe, false);
++uCurEntry;
if (m_slLogger != null)
{
if (!m_slLogger.SetProgress((100 * uCurEntry) / uNumEntries))
if(m_slLogger != null)
if(!m_slLogger.SetProgress((100 * uCurEntry) / uNumEntries))
return false;
}
return true;
};
if (!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new OperationCanceledException();
if(!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new InvalidOperationException();
while (groupStack.Count > 1)
while(groupStack.Count > 1)
{
m_xmlWriter.WriteEndElement();
groupStack.Pop();
@@ -485,11 +479,11 @@ namespace KeePassLib.Serialization
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false);
if ((m_pbHashOfHeader != null) && (m_uFileVersion < FileVersion32_4))
if((m_pbHashOfHeader != null) && (m_uFileVersion < FileVersion32_4))
WriteObject(ElemHeaderHash, Convert.ToBase64String(
m_pbHashOfHeader), false);
if (m_uFileVersion >= FileVersion32_4)
if(m_uFileVersion >= FileVersion32_4)
WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
WriteObject(ElemDbName, m_pwDatabase.Name, true);
@@ -503,7 +497,7 @@ namespace KeePassLib.Serialization
WriteObject(ElemDbKeyChanged, m_pwDatabase.MasterKeyChanged);
WriteObject(ElemDbKeyChangeRec, m_pwDatabase.MasterKeyChangeRec);
WriteObject(ElemDbKeyChangeForce, m_pwDatabase.MasterKeyChangeForce);
if (m_pwDatabase.MasterKeyChangeForceOnce)
if(m_pwDatabase.MasterKeyChangeForceOnce)
WriteObject(ElemDbKeyChangeForceOnce, true);
WriteList(ElemMemoryProt, m_pwDatabase.MemoryProtection);
@@ -521,8 +515,8 @@ namespace KeePassLib.Serialization
WriteObject(ElemLastSelectedGroup, m_pwDatabase.LastSelectedGroup);
WriteObject(ElemLastTopVisibleGroup, m_pwDatabase.LastTopVisibleGroup);
if ((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
WriteBinPool();
if((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
WriteBinPool();
WriteList(ElemCustomData, m_pwDatabase.CustomData);
@@ -536,10 +530,10 @@ namespace KeePassLib.Serialization
WriteObject(ElemName, pg.Name, true);
WriteObject(ElemNotes, pg.Notes, true);
WriteObject(ElemIcon, (int)pg.IconId);
if (!pg.CustomIconUuid.Equals(PwUuid.Zero))
if(!pg.CustomIconUuid.Equals(PwUuid.Zero))
WriteObject(ElemCustomIconID, pg.CustomIconUuid);
WriteList(ElemTimes, pg);
WriteObject(ElemIsExpanded, pg.IsExpanded);
WriteObject(ElemGroupDefaultAutoTypeSeq, pg.DefaultAutoTypeSequence, true);
@@ -547,17 +541,7 @@ namespace KeePassLib.Serialization
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if (m_uFileVersion >= FileVersion32_4_1)
{
if (!pg.PreviousParentGroup.Equals(PwUuid.Zero))
WriteObject(ElemPreviousParentGroup, pg.PreviousParentGroup);
List<string> lTags = pg.Tags;
if (lTags.Count != 0)
WriteObject(ElemTags, StrUtil.TagsToString(lTags, false), true);
}
if (pg.CustomData.Count > 0)
if(pg.CustomData.Count > 0)
WriteList(ElemCustomData, pg.CustomData);
}
@@ -568,39 +552,31 @@ namespace KeePassLib.Serialization
private void WriteEntry(PwEntry pe, bool bIsHistory)
{
Debug.Assert(pe != null); if (pe == null) throw new ArgumentNullException("pe");
Debug.Assert(pe != null); if(pe == null) throw new ArgumentNullException("pe");
m_xmlWriter.WriteStartElement(ElemEntry);
WriteObject(ElemUuid, pe.Uuid);
WriteObject(ElemIcon, (int)pe.IconId);
if (!pe.CustomIconUuid.Equals(PwUuid.Zero))
if(!pe.CustomIconUuid.Equals(PwUuid.Zero))
WriteObject(ElemCustomIconID, pe.CustomIconUuid);
WriteObject(ElemFgColor, StrUtil.ColorToUnnamedHtml(pe.ForegroundColor, true), false);
WriteObject(ElemBgColor, StrUtil.ColorToUnnamedHtml(pe.BackgroundColor, true), false);
WriteObject(ElemOverrideUrl, pe.OverrideUrl, true);
if ((m_uFileVersion >= FileVersion32_4_1) && !pe.QualityCheck)
WriteObject(ElemQualityCheck, false);
WriteObject(ElemTags, StrUtil.TagsToString(pe.Tags, false), true);
if ((m_uFileVersion >= FileVersion32_4_1) &&
!pe.PreviousParentGroup.Equals(PwUuid.Zero))
WriteObject(ElemPreviousParentGroup, pe.PreviousParentGroup);
WriteList(ElemTimes, pe);
WriteList(pe.Strings, true);
WriteList(pe.Binaries);
WriteList(ElemAutoType, pe.AutoType);
if (pe.CustomData.Count > 0)
if(pe.CustomData.Count > 0)
WriteList(ElemCustomData, pe.CustomData);
if (!bIsHistory) WriteList(ElemHistory, pe.History, true);
if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
else { Debug.Assert(pe.History.UCount == 0); }
m_xmlWriter.WriteEndElement();
@@ -609,18 +585,18 @@ namespace KeePassLib.Serialization
private void WriteList(ProtectedStringDictionary dictStrings, bool bEntryStrings)
{
Debug.Assert(dictStrings != null);
if (dictStrings == null) throw new ArgumentNullException("dictStrings");
if(dictStrings == null) throw new ArgumentNullException("dictStrings");
foreach (KeyValuePair<string, ProtectedString> kvp in dictStrings)
foreach(KeyValuePair<string, ProtectedString> kvp in dictStrings)
WriteObject(kvp.Key, kvp.Value, bEntryStrings);
}
private void WriteList(ProtectedBinaryDictionary dictBinaries)
{
Debug.Assert(dictBinaries != null);
if (dictBinaries == null) throw new ArgumentNullException("dictBinaries");
if(dictBinaries == null) throw new ArgumentNullException("dictBinaries");
foreach (KeyValuePair<string, ProtectedBinary> kvp in dictBinaries)
foreach(KeyValuePair<string, ProtectedBinary> kvp in dictBinaries)
WriteObject(kvp.Key, kvp.Value, true);
}
@@ -628,19 +604,19 @@ namespace KeePassLib.Serialization
{
Debug.Assert(name != null);
Debug.Assert(cfgAutoType != null);
if (cfgAutoType == null) throw new ArgumentNullException("cfgAutoType");
if(cfgAutoType == null) throw new ArgumentNullException("cfgAutoType");
m_xmlWriter.WriteStartElement(name);
WriteObject(ElemAutoTypeEnabled, cfgAutoType.Enabled);
WriteObject(ElemAutoTypeObfuscation, (int)cfgAutoType.ObfuscationOptions);
if (cfgAutoType.DefaultSequence.Length > 0)
if(cfgAutoType.DefaultSequence.Length > 0)
WriteObject(ElemAutoTypeDefaultSeq, cfgAutoType.DefaultSequence, true);
foreach (AutoTypeAssociation a in cfgAutoType.Associations)
foreach(AutoTypeAssociation a in cfgAutoType.Associations)
WriteObject(ElemAutoTypeItem, ElemWindow, ElemKeystrokeSequence,
new KeyValuePair<string, string>(a.WindowName, a.Sequence), null);
new KeyValuePair<string, string>(a.WindowName, a.Sequence));
m_xmlWriter.WriteEndElement();
}
@@ -648,7 +624,7 @@ namespace KeePassLib.Serialization
private void WriteList(string name, ITimeLogger times)
{
Debug.Assert(name != null);
Debug.Assert(times != null); if (times == null) throw new ArgumentNullException("times");
Debug.Assert(times != null); if(times == null) throw new ArgumentNullException("times");
m_xmlWriter.WriteStartElement(name);
@@ -660,17 +636,17 @@ namespace KeePassLib.Serialization
WriteObject(ElemUsageCount, times.UsageCount);
WriteObject(ElemLocationChanged, times.LocationChanged);
m_xmlWriter.WriteEndElement();
m_xmlWriter.WriteEndElement(); // Name
}
private void WriteList(string name, PwObjectList<PwEntry> value, bool bIsHistory)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach (PwEntry pe in value)
foreach(PwEntry pe in value)
WriteEntry(pe, bIsHistory);
m_xmlWriter.WriteEndElement();
@@ -679,11 +655,11 @@ namespace KeePassLib.Serialization
private void WriteList(string name, PwObjectList<PwDeletedObject> value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach (PwDeletedObject pdo in value)
foreach(PwDeletedObject pdo in value)
WriteObject(ElemDeletedObject, pdo);
m_xmlWriter.WriteEndElement();
@@ -709,45 +685,31 @@ namespace KeePassLib.Serialization
private void WriteList(string name, StringDictionaryEx value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach (KeyValuePair<string, string> kvp in value)
{
DateTime? odtLastMod = null;
if (m_uFileVersion >= FileVersion32_4_1)
odtLastMod = value.GetLastModificationTime(kvp.Key);
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp, odtLastMod);
}
foreach(KeyValuePair<string, string> kvp in value)
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp);
m_xmlWriter.WriteEndElement();
}
private void WriteCustomIconList()
{
if (m_pwDatabase.CustomIcons.Count == 0) return;
if(m_pwDatabase.CustomIcons.Count == 0) return;
m_xmlWriter.WriteStartElement(ElemCustomIcons);
foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
foreach(PwCustomIcon pwci in m_pwDatabase.CustomIcons)
{
m_xmlWriter.WriteStartElement(ElemCustomIconItem);
WriteObject(ElemCustomIconItemID, ci.Uuid);
WriteObject(ElemCustomIconItemID, pwci.Uuid);
string strData = Convert.ToBase64String(ci.ImageDataPng);
string strData = Convert.ToBase64String(pwci.ImageDataPng);
WriteObject(ElemCustomIconItemData, strData, false);
if (m_uFileVersion >= FileVersion32_4_1)
{
if (ci.Name.Length != 0)
WriteObject(ElemName, ci.Name, true);
if (ci.LastModificationTime.HasValue)
WriteObject(ElemLastModTime, ci.LastModificationTime.Value);
}
m_xmlWriter.WriteEndElement();
}
@@ -762,7 +724,7 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(name);
if (bFilterValueXmlChars)
if(bFilterValueXmlChars)
m_xmlWriter.WriteString(StrUtil.SafeXmlString(value));
else m_xmlWriter.WriteString(value);
@@ -779,7 +741,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, PwUuid value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
WriteObject(name, Convert.ToBase64String(value.UuidBytes), false);
}
@@ -826,7 +788,7 @@ namespace KeePassLib.Serialization
Debug.Assert(value.Kind == DateTimeKind.Utc);
// Cf. ReadTime
if ((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
if((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
{
DateTime dt = TimeUtil.ToUtc(value, false);
@@ -843,33 +805,29 @@ namespace KeePassLib.Serialization
byte[] pb = MemUtil.Int64ToBytes(lSec);
WriteObject(name, Convert.ToBase64String(pb), false);
}
}
else WriteObject(name, TimeUtil.SerializeUtc(value), false);
}
private void WriteObject(string name, string strKeyName, string strValueName,
KeyValuePair<string, string> kvp, DateTime? odtLastMod)
private void WriteObject(string name, string strKeyName,
string strValueName, KeyValuePair<string, string> kvp)
{
m_xmlWriter.WriteStartElement(name);
m_xmlWriter.WriteStartElement(strKeyName);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Key));
m_xmlWriter.WriteEndElement();
m_xmlWriter.WriteStartElement(strValueName);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Value));
m_xmlWriter.WriteEndElement();
if (odtLastMod.HasValue)
WriteObject(ElemLastModTime, odtLastMod.Value);
m_xmlWriter.WriteEndElement();
}
private void WriteObject(string name, ProtectedString value, bool bIsEntryString)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(ElemString);
m_xmlWriter.WriteStartElement(ElemKey);
@@ -878,30 +836,30 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemValue);
bool bProtected = value.IsProtected;
if (bIsEntryString)
if(bIsEntryString)
{
// Adjust memory protection setting (which might be different
// from the database default, e.g. due to an import which
// didn't specify the correct setting)
if (name == PwDefs.TitleField)
if(name == PwDefs.TitleField)
bProtected = m_pwDatabase.MemoryProtection.ProtectTitle;
else if (name == PwDefs.UserNameField)
else if(name == PwDefs.UserNameField)
bProtected = m_pwDatabase.MemoryProtection.ProtectUserName;
else if (name == PwDefs.PasswordField)
else if(name == PwDefs.PasswordField)
bProtected = m_pwDatabase.MemoryProtection.ProtectPassword;
else if (name == PwDefs.UrlField)
else if(name == PwDefs.UrlField)
bProtected = m_pwDatabase.MemoryProtection.ProtectUrl;
else if (name == PwDefs.NotesField)
else if(name == PwDefs.NotesField)
bProtected = m_pwDatabase.MemoryProtection.ProtectNotes;
}
if (bProtected && (m_format == KdbxFormat.Default))
if(bProtected && (m_format == KdbxFormat.Default))
{
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
byte[] pbEnc = value.ReadXorredString(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
byte[] pbEncoded = value.ReadXorredString(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
}
else
{
@@ -911,24 +869,25 @@ namespace KeePassLib.Serialization
// string transformation here. By default, language-dependent conversions
// should be applied, otherwise characters could be rendered incorrectly
// (code page problems).
if (g_bLocalizedNames)
if(m_bLocalizedNames)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in strValue)
foreach(char ch in strValue)
{
char chMapped = ch;
// Symbols and surrogates must be moved into the correct code
// page area
if (char.IsSymbol(ch) || char.IsSurrogate(ch))
if(char.IsSymbol(ch) || char.IsSurrogate(ch))
{
UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory(ch);
System.Globalization.UnicodeCategory cat =
CharUnicodeInfo.GetUnicodeCategory(ch);
// Map character to correct position in code page
chMapped = (char)((int)cat * 32 + ch);
}
else if (char.IsControl(ch))
else if(char.IsControl(ch))
{
if (ch >= 256) // Control character in high ANSI code page
if(ch >= 256) // Control character in high ANSI code page
{
// Some of the control characters map to corresponding ones
// in the low ANSI range (up to 255) when calling
@@ -948,7 +907,7 @@ namespace KeePassLib.Serialization
strValue = sb.ToString(); // Correct string for current code page
}
if ((m_format == KdbxFormat.PlainXml) && bProtected)
if((m_format == KdbxFormat.PlainXml) && bProtected)
m_xmlWriter.WriteAttributeString(AttrProtectedInMemPlainXml, ValTrue);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(strValue));
@@ -961,7 +920,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, ProtectedBinary value, bool bAllowRef)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(ElemBinary);
m_xmlWriter.WriteStartElement(ElemKey);
@@ -970,13 +929,13 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemValue);
string strRef = null;
if (bAllowRef)
if(bAllowRef)
{
int iRef = m_pbsBinaries.Find(value);
if (iRef >= 0) strRef = iRef.ToString(NumberFormatInfo.InvariantInfo);
if(iRef >= 0) strRef = iRef.ToString(NumberFormatInfo.InvariantInfo);
else { Debug.Assert(false); }
}
if (strRef != null)
if(strRef != null)
m_xmlWriter.WriteAttributeString(AttrRef, strRef);
else SubWriteValue(value);
@@ -986,17 +945,17 @@ namespace KeePassLib.Serialization
private void SubWriteValue(ProtectedBinary value)
{
if (value.IsProtected && (m_format == KdbxFormat.Default))
if(value.IsProtected && (m_format == KdbxFormat.Default))
{
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
byte[] pbEnc = value.ReadXorredData(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
byte[] pbEncoded = value.ReadXorredData(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
}
else
{
if (m_pwDatabase.Compression != PwCompressionAlgorithm.None)
if(m_pwDatabase.Compression != PwCompressionAlgorithm.None)
{
m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue);
@@ -1004,18 +963,18 @@ namespace KeePassLib.Serialization
byte[] pbCmp = MemUtil.Compress(pbRaw);
m_xmlWriter.WriteBase64(pbCmp, 0, pbCmp.Length);
if (value.IsProtected)
if(value.IsProtected)
{
MemUtil.ZeroByteArray(pbRaw);
MemUtil.ZeroByteArray(pbCmp);
}
}
}
else
{
byte[] pbRaw = value.ReadData();
m_xmlWriter.WriteBase64(pbRaw, 0, pbRaw.Length);
if (value.IsProtected) MemUtil.ZeroByteArray(pbRaw);
if(value.IsProtected) MemUtil.ZeroByteArray(pbRaw);
}
}
}
@@ -1023,7 +982,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, PwDeletedObject value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
WriteObject(ElemUuid, value.Uuid);
@@ -1036,7 +995,7 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemBinaries);
ProtectedBinary[] v = m_pbsBinaries.ToArray();
for (int i = 0; i < v.Length; ++i)
for(int i = 0; i < v.Length; ++i)
{
m_xmlWriter.WriteStartElement(ElemBinary);
m_xmlWriter.WriteAttributeString(AttrId,
@@ -1048,25 +1007,6 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteEndElement();
}
internal static void WriteGroup(Stream msOutput, PwDatabase pdContext,
PwGroup pg)
{
if (msOutput == null) throw new ArgumentNullException("msOutput");
// pdContext may be null
if (pg == null) throw new ArgumentNullException("pg");
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), pg.Name);
pd.RootGroup = pg.CloneDeep();
pd.RootGroup.ParentGroup = null;
PwDatabase.CopyCustomIcons(pdContext, pd, pd.RootGroup, true);
KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null);
}
[Obsolete]
public static bool WriteEntries(Stream msOutput, PwEntry[] vEntries)
{
@@ -1074,21 +1014,66 @@ namespace KeePassLib.Serialization
}
public static bool WriteEntries(Stream msOutput, PwDatabase pdContext,
PwEntry[] vEntries)
PwEntry[] vEntries)
{
if (msOutput == null) { Debug.Assert(false); return false; }
// pdContext may be null
if (vEntries == null) { Debug.Assert(false); return false; }
PwGroup pg = new PwGroup(true, true);
/* KdbxFile f = new KdbxFile(pwDatabase);
f.m_format = KdbxFormat.PlainXml;
XmlTextWriter xtw = null;
try { xtw = new XmlTextWriter(msOutput, StrUtil.Utf8); }
catch(Exception) { Debug.Assert(false); return false; }
if(xtw == null) { Debug.Assert(false); return false; }
f.m_xmlWriter = xtw;
xtw.Formatting = Formatting.Indented;
xtw.IndentChar = '\t';
xtw.Indentation = 1;
xtw.WriteStartDocument(true);
xtw.WriteStartElement(ElemRoot);
foreach(PwEntry pe in vEntries)
f.WriteEntry(pe, false);
xtw.WriteEndElement();
xtw.WriteEndDocument();
xtw.Flush();
xtw.Close();
return true; */
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
PwGroup pg = pd.RootGroup;
if (pg == null) { Debug.Assert(false); return false; }
foreach (PwEntry pe in vEntries)
{
PwUuid pu = pe.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero) && (pd.GetCustomIconIndex(pu) < 0))
{
int i = -1;
if (pdContext != null) i = pdContext.GetCustomIconIndex(pu);
if (i >= 0)
{
PwCustomIcon ci = pdContext.CustomIcons[i];
pd.CustomIcons.Add(ci);
}
else { Debug.Assert(pdContext == null); }
}
PwEntry peCopy = pe.CloneDeep();
pg.AddEntry(peCopy, true);
}
WriteGroup(msOutput, pdContext, pg);
KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null);
return true;
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -67,51 +67,47 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
/// <summary>
/// System.Drawing.ColorTranslator is not supported on Android. Provide a custom implementation here for loading colors from file.
/// </summary>
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
/// <summary>
/// File identifier, first 32-bit value.
/// </summary>
@@ -123,17 +119,16 @@ namespace KeePassLib.Serialization
internal const uint FileSignature2 = 0xB54BFB67;
/// <summary>
/// Maximum supported version of database files.
/// File version of files saved by the current <c>KdbxFile</c> class.
/// KeePass 2.07 has version 1.01, 2.08 has 1.02, 2.09 has 2.00,
/// 2.10 has 2.02, 2.11 has 2.04, 2.15 has 3.00, 2.20 has 3.01.
/// The first 2 bytes are critical (i.e. loading will fail, if the
/// file version is too high), the last 2 bytes are informational.
/// </summary>
private const uint FileVersion32 = 0x00040001;
private const uint FileVersion32 = 0x00040000;
public const uint FileVersion32_4_1 = 0x00040001; // 4.1
public const uint FileVersion32_4 = 0x00040000; // 4.0
public const uint FileVersion32_3_1 = 0x00030001; // 3.1
public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
private const uint FileVersionCriticalMask = 0xFFFF0000;
@@ -148,7 +143,7 @@ namespace KeePassLib.Serialization
private const string ElemMeta = "Meta";
private const string ElemRoot = "Root";
private const string ElemGroup = "Group";
internal const string ElemEntry = "Entry";
private const string ElemEntry = "Entry";
private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash";
@@ -193,13 +188,12 @@ namespace KeePassLib.Serialization
private const string ElemName = "Name";
private const string ElemNotes = "Notes";
internal const string ElemUuid = "UUID";
private const string ElemUuid = "UUID";
private const string ElemIcon = "IconID";
private const string ElemCustomIconID = "CustomIconUUID";
private const string ElemFgColor = "ForegroundColor";
private const string ElemBgColor = "BackgroundColor";
private const string ElemOverrideUrl = "OverrideURL";
private const string ElemQualityCheck = "QualityCheck";
private const string ElemTimes = "Times";
private const string ElemTags = "Tags";
@@ -211,8 +205,6 @@ namespace KeePassLib.Serialization
private const string ElemUsageCount = "UsageCount";
private const string ElemLocationChanged = "LocationChanged";
private const string ElemPreviousParentGroup = "PreviousParentGroup";
private const string ElemGroupDefaultAutoTypeSeq = "DefaultAutoTypeSequence";
private const string ElemEnableAutoType = "EnableAutoType";
private const string ElemEnableSearching = "EnableSearching";
@@ -269,7 +261,7 @@ namespace KeePassLib.Serialization
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private byte[] m_pbInnerRandomStreamKey = null;
private ProtectedBinarySet m_pbsBinaries = null;
private ProtectedBinarySet m_pbsBinaries = new ProtectedBinarySet();
private byte[] m_pbHashOfHeader = null;
private byte[] m_pbHashOfFileOnDisk = null;
@@ -279,7 +271,7 @@ namespace KeePassLib.Serialization
private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs
private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs
private const uint NeutralLanguageID = NeutralLanguageOffset + NeutralLanguageIDSec;
private static bool g_bLocalizedNames = false;
private static bool m_bLocalizedNames = false;
private enum KdbxHeaderFieldID : byte
{
@@ -353,7 +345,7 @@ namespace KeePassLib.Serialization
public KdbxFile(PwDatabase pwDataStore)
{
Debug.Assert(pwDataStore != null);
if (pwDataStore == null) throw new ArgumentNullException("pwDataStore");
if(pwDataStore == null) throw new ArgumentNullException("pwDataStore");
m_pwDatabase = pwDataStore;
}
@@ -364,92 +356,54 @@ namespace KeePassLib.Serialization
public static void DetermineLanguageId()
{
// Test if localized names should be used. If localized names are used,
// the g_bLocalizedNames value must be set to true. By default, localized
// names should be used (otherwise characters could be corrupted
// the m_bLocalizedNames value must be set to true. By default, localized
// names should be used! (Otherwise characters could be corrupted
// because of different code pages).
unchecked
{
uint uTest = 0;
foreach (char ch in PwDatabase.LocalizedAppName)
foreach(char ch in PwDatabase.LocalizedAppName)
uTest = uTest * 5 + ch;
g_bLocalizedNames = (uTest != NeutralLanguageID);
m_bLocalizedNames = (uTest != NeutralLanguageID);
}
}
private uint GetMinKdbxVersion()
{
if (m_uForceVersion != 0) return m_uForceVersion;
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
uint minRequiredVersion = Math.Max(minVersionForKeys, m_uFileVersion); //don't save a version lower than what we read
return Math.Max(minRequiredVersion, GetMinKdbxVersionOrig());
}
private uint GetMinKdbxVersionOrig()
private uint GetMinKdbxVersion()
{
if (m_uForceVersion != 0) return m_uForceVersion;
if(m_uForceVersion != 0) return m_uForceVersion;
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
uint uMin = 0;
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
AesKdf kdfAes = new AesKdf();
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
return Math.Max(FileVersion32, minVersionForKeys);
GroupHandler gh = delegate (PwGroup pg)
if(m_pwDatabase.PublicCustomData.Count > 0)
return Math.Max(FileVersion32, minVersionForKeys);
bool bCustomData = false;
GroupHandler gh = delegate(PwGroup pg)
{
if (pg == null) { Debug.Assert(false); return true; }
if (pg.Tags.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4_1);
if (pg.CustomData.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4);
if(pg == null) { Debug.Assert(false); return true; }
if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
return true;
};
EntryHandler eh = delegate (PwEntry pe)
EntryHandler eh = delegate(PwEntry pe)
{
if (pe == null) { Debug.Assert(false); return true; }
if (!pe.QualityCheck)
uMin = Math.Max(uMin, FileVersion32_4_1);
if (pe.CustomData.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4);
if(pe == null) { Debug.Assert(false); return true; }
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
return true;
};
gh(m_pwDatabase.RootGroup);
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(bCustomData)
return Math.Max(FileVersion32, minVersionForKeys);
if (uMin >= FileVersion32_4_1) return uMin; // All below is <= 4.1
foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
{
if ((ci.Name.Length != 0) || ci.LastModificationTime.HasValue)
return FileVersion32_4_1;
}
foreach (KeyValuePair<string, string> kvp in m_pwDatabase.CustomData)
{
DateTime? odt = m_pwDatabase.CustomData.GetLastModificationTime(kvp.Key);
if (odt.HasValue) return FileVersion32_4_1;
}
if (uMin >= FileVersion32_4) return uMin; // All below is <= 4
if (m_pwDatabase.DataCipherUuid.Equals(ChaCha20Engine.ChaCha20Uuid))
return FileVersion32_4;
AesKdf kdfAes = new AesKdf();
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfAes.Uuid))
return FileVersion32_4;
if (m_pwDatabase.PublicCustomData.Count != 0)
return FileVersion32_4;
return FileVersion32_3_1; // KDBX 3.1 is sufficient
return Math.Max(FileVersion32_3, minVersionForKeys); ; // KDBX 3.1 is sufficient
}
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
@@ -459,12 +413,12 @@ namespace KeePassLib.Serialization
try
{
Debug.Assert(m_pbMasterSeed != null);
if (m_pbMasterSeed == null)
if(m_pbMasterSeed == null)
throw new ArgumentNullException("m_pbMasterSeed");
Debug.Assert(m_pbMasterSeed.Length == 32);
if (m_pbMasterSeed.Length != 32)
if(m_pbMasterSeed.Length != 32)
throw new FormatException(KLRes.MasterSeedLengthInvalid);
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(m_pwDatabase != null);
Debug.Assert(m_pwDatabase.MasterKey != null);
@@ -474,10 +428,10 @@ namespace KeePassLib.Serialization
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(pbinUser != null);
if (pbinUser == null)
if(pbinUser == null)
throw new SecurityException(KLRes.InvalidCompositeKey);
byte[] pUserKey32 = pbinUser.ReadData();
if ((pUserKey32 == null) || (pUserKey32.Length != 32))
if((pUserKey32 == null) || (pUserKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
Array.Copy(pUserKey32, 0, pbCmp, 32, 32);
MemUtil.ZeroByteArray(pUserKey32);
@@ -485,7 +439,7 @@ namespace KeePassLib.Serialization
pbCipherKey = CryptoUtil.ResizeKey(pbCmp, 0, 64, cbCipherKey);
pbCmp[64] = 1;
using (SHA512Managed h = new SHA512Managed())
using(SHA512Managed h = new SHA512Managed())
{
pbHmacKey64 = h.ComputeHash(pbCmp);
}
@@ -497,19 +451,19 @@ namespace KeePassLib.Serialization
{
PwUuid pu = m_pwDatabase.DataCipherUuid;
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
if (iCipher == null) // CryptographicExceptions are translated to "file corrupted"
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.FileUnknownCipher +
MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq +
MessageService.NewParagraph + "UUID: " + pu.ToHexString() + ".");
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
if (iCipher2 != null)
if(iCipher2 != null)
{
cbEncKey = iCipher2.KeyLength;
if (cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
if(cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
cbEncIV = iCipher2.IVLength;
if (cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
if(cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
}
else
{
@@ -524,13 +478,13 @@ namespace KeePassLib.Serialization
byte[] pbKey, int cbIV, bool bEncrypt)
{
byte[] pbIV = (m_pbEncryptionIV ?? MemUtil.EmptyByteArray);
if (pbIV.Length != cbIV)
if(pbIV.Length != cbIV)
{
Debug.Assert(false);
throw new Exception(KLRes.FileCorrupted);
}
if (bEncrypt)
if(bEncrypt)
return iCipher.EncryptStream(s, pbKey, pbIV);
return iCipher.DecryptStream(s, pbKey, pbIV);
}
@@ -540,7 +494,7 @@ namespace KeePassLib.Serialization
byte[] pbHeaderHmac;
byte[] pbBlockKey = HmacBlockStream.GetHmacKey64(
pbKey, ulong.MaxValue);
using (HMACSHA256 h = new HMACSHA256(pbBlockKey))
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
pbHeaderHmac = h.ComputeHash(pbHeader);
}
@@ -551,7 +505,7 @@ namespace KeePassLib.Serialization
private void CloseStreams(List<Stream> lStreams)
{
if (lStreams == null) { Debug.Assert(false); return; }
if(lStreams == null) { Debug.Assert(false); return; }
// Typically, closing a stream also closes its base
// stream; however, there may be streams that do not
@@ -559,14 +513,14 @@ namespace KeePassLib.Serialization
// we close all streams manually, from the innermost
// to the outermost
for (int i = lStreams.Count - 1; i >= 0; --i)
for(int i = lStreams.Count - 1; i >= 0; --i)
{
// Check for duplicates
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
(lStreams.LastIndexOf(lStreams[i]) == i));
try { lStreams[i].Close(); }
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
}
// Do not clear the list
@@ -574,18 +528,18 @@ namespace KeePassLib.Serialization
private void CleanUpInnerRandomStream()
{
if (m_randomStream != null) m_randomStream.Dispose();
if(m_randomStream != null) m_randomStream.Dispose();
if (m_pbInnerRandomStreamKey != null)
if(m_pbInnerRandomStreamKey != null)
MemUtil.ZeroByteArray(m_pbInnerRandomStreamKey);
}
private static void SaveBinary(string strName, ProtectedBinary pb,
string strSaveDir)
{
if (pb == null) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
strName = UrlUtil.GetSafeFileName(strName);
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
string strPath;
int iTry = 1;
@@ -593,23 +547,31 @@ namespace KeePassLib.Serialization
{
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
string strDesc = UrlUtil.StripExtension(strName);
string strExt = UrlUtil.GetExtension(strName);
string strDesc = UrlUtil.StripExtension(strName);
strPath += strDesc;
if (iTry > 1)
if(iTry > 1)
strPath += " (" + iTry.ToString(NumberFormatInfo.InvariantInfo) +
")";
if (!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
if(!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
++iTry;
}
while (File.Exists(strPath));
while(File.Exists(strPath));
#if !KeePassLibSD
byte[] pbData = pb.ReadData();
try { File.WriteAllBytes(strPath, pbData); }
finally { if (pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
File.WriteAllBytes(strPath, pbData);
MemUtil.ZeroByteArray(pbData);
#else
FileStream fs = new FileStream(strPath, FileMode.Create,
FileAccess.Write, FileShare.None);
byte[] pbData = pb.ReadData();
fs.Write(pbData, 0, pbData.Length);
fs.Close();
#endif
}
}
}

View File

@@ -28,9 +28,9 @@ namespace KeePassLib.Serialization
/// </summary>
public static KdbxFormat GetFormatToUse(string fileExt)
{
return fileExt.Equals(KdbpFile.FileNameExtension, StringComparison.OrdinalIgnoreCase) ? KdbxFormat.ProtocolBuffers :
(fileExt.Equals("xml", StringComparison.OrdinalIgnoreCase) ? KdbxFormat.PlainXml : KdbxFormat.Default);
}
// If the filename ends in .kdbp, use ProtocolBuffers format.
return fileExt.Equals(KdbpFile.FileNameExtension, StringComparison.OrdinalIgnoreCase) ? KdbxFormat.ProtocolBuffers : KdbxFormat.Default;
}
public static void WriteDocument(PwDatabase database, Stream stream, byte[] protectedStreamKey, byte[] hashOfHeader)
{

View File

@@ -37,7 +37,7 @@ namespace KeePassLib.Utility
/// </summary>
public static class MemUtil
{
public static readonly byte[] EmptyByteArray = new byte[0];
internal static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
@@ -786,28 +786,5 @@ namespace KeePassLib.Utility
yield break;
}
internal static bool ListsEqual<T>(List<T> a, List<T> b)
where T : class, IEquatable<T>
{
if (object.ReferenceEquals(a, b)) return true;
if ((a == null) || (b == null)) return false;
int n = a.Count;
if (n != b.Count) return false;
for (int i = 0; i < n; ++i)
{
T tA = a[i], tB = b[i];
if (tA == null)
{
if (tB != null) return false;
}
else if (tB == null) return false;
else if (!tA.Equals(tB)) return false;
}
return true;
}
}
}

View File

@@ -429,7 +429,7 @@ Clipboard.SetText(ObjectsToMessage(vLines, true));*/
if ((strFilePath != null) && (strFilePath.Length > 0))
str += strFilePath + MessageService.NewParagraph;
str += KLRes.FileSaveFailed2;
str += KLRes.FileSaveFailed;
if ((ex != null) && (ex.Message != null) && (ex.Message.Length > 0))
str += MessageService.NewParagraph + ex.Message;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,10 +20,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using KeePassLib.Native;
@@ -35,7 +33,9 @@ namespace KeePassLib.Utility
/// </summary>
public static class UrlUtil
{
private static readonly char[] g_vPathTrimCharsWs = new char[] {
private static readonly char[] m_vDirSeps = new char[] {
'\\', '/', UrlUtil.LocalDirSepChar };
private static readonly char[] m_vPathTrimCharsWs = new char[] {
'\"', ' ', '\t', '\r', '\n' };
public static char LocalDirSepChar
@@ -43,32 +43,6 @@ namespace KeePassLib.Utility
get { return Path.DirectorySeparatorChar; }
}
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:
@@ -89,16 +63,16 @@ namespace KeePassLib.Utility
bool bEnsureValidDirSpec)
{
Debug.Assert(strFile != null);
if (strFile == null) throw new ArgumentNullException("strFile");
if(strFile == null) throw new ArgumentNullException("strFile");
int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
if (nLastSep < 0) return string.Empty; // No directory
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps);
if(nLastSep < 0) return string.Empty; // No directory
if (bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
(strFile[2] == '\\')) // Length >= 3 and Windows root directory
bAppendTerminatingChar = true;
if (!bAppendTerminatingChar) return strFile.Substring(0, nLastSep);
if(!bAppendTerminatingChar) return strFile.Substring(0, nLastSep);
return EnsureTerminatingSeparator(strFile.Substring(0, nLastSep),
(strFile[nLastSep] == '/'));
}
@@ -113,12 +87,12 @@ namespace KeePassLib.Utility
/// an empty string (<c>""</c>) if the input parameter is <c>null</c>.</returns>
public static string GetFileName(string strPath)
{
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(UrlUtil.DirSepChars);
int nLastSep = strPath.LastIndexOfAny(m_vDirSeps);
if (nLastSep < 0) return strPath;
if (nLastSep >= (strPath.Length - 1)) return string.Empty;
if(nLastSep < 0) return strPath;
if(nLastSep >= (strPath.Length - 1)) return string.Empty;
return strPath.Substring(nLastSep + 1);
}
@@ -130,12 +104,12 @@ namespace KeePassLib.Utility
/// <returns>File name without extension.</returns>
public static string StripExtension(string strPath)
{
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(UrlUtil.DirSepChars);
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
int nLastExtDot = strPath.LastIndexOf('.');
if (nLastExtDot <= nLastDirSep) return strPath;
if(nLastExtDot <= nLastDirSep) return strPath;
return strPath.Substring(0, nLastExtDot);
}
@@ -147,13 +121,13 @@ namespace KeePassLib.Utility
/// <returns>Extension without prepending dot.</returns>
public static string GetExtension(string strPath)
{
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(UrlUtil.DirSepChars);
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
int nLastExtDot = strPath.LastIndexOf('.');
if (nLastExtDot <= nLastDirSep) return string.Empty;
if (nLastExtDot == (strPath.Length - 1)) return string.Empty;
if(nLastExtDot <= nLastDirSep) return string.Empty;
if(nLastExtDot == (strPath.Length - 1)) return string.Empty;
return strPath.Substring(nLastExtDot + 1);
}
@@ -168,16 +142,19 @@ namespace KeePassLib.Utility
/// <returns>Path having a directory separator as last character.</returns>
public static string EnsureTerminatingSeparator(string strPath, bool bUrl)
{
Debug.Assert(strPath != null); if (strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
int nLength = strPath.Length;
if (nLength <= 0) return string.Empty;
if(nLength <= 0) return string.Empty;
char chLast = strPath[nLength - 1];
if (Array.IndexOf<char>(UrlUtil.DirSepChars, chLast) >= 0)
return strPath;
if (bUrl) return (strPath + '/');
for(int i = 0; i < m_vDirSeps.Length; ++i)
{
if(chLast == m_vDirSeps[i]) return strPath;
}
if(bUrl) return (strPath + '/');
return (strPath + UrlUtil.LocalDirSepChar);
}
@@ -239,78 +216,55 @@ namespace KeePassLib.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; }
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;
if(str.Length <= 1) return str;
if(str[0] != '\"') return str;
int iSecond = IndexOfSecondEnclQuote(str);
if (iSecond <= 0) return str;
int iSecond = str.IndexOf('\"', 1);
if(iSecond <= 0) return str;
return str.Substring(1, iSecond - 1);
}
public static string FileUrlToPath(string strUrl)
{
if (strUrl == null) { Debug.Assert(false); throw new ArgumentNullException("strUrl"); }
if (strUrl.Length == 0) { Debug.Assert(false); return string.Empty; }
Debug.Assert(strUrl != null);
if(strUrl == null) throw new ArgumentNullException("strUrl");
if (!strUrl.StartsWith(Uri.UriSchemeFile + ":", StrUtil.CaseIgnoreCmp))
{
Debug.Assert(false);
return strUrl;
}
string str = strUrl;
if(str.StartsWith(@"file:///", StrUtil.CaseIgnoreCmp))
str = str.Substring(8, str.Length - 8);
try
{
Uri uri = new Uri(strUrl);
string str = uri.LocalPath;
if (!string.IsNullOrEmpty(str)) return str;
}
catch (Exception) { Debug.Assert(false); }
str = str.Replace('/', UrlUtil.LocalDirSepChar);
Debug.Assert(false);
return strUrl;
return str;
}
public static bool UnhideFile(string strFile)
{
#if KeePassLibSD
#if (KeePassLibSD || KeePassRT)
return false;
#else
if (strFile == null) throw new ArgumentNullException("strFile");
if(strFile == null) throw new ArgumentNullException("strFile");
try
{
FileAttributes fa = File.GetAttributes(strFile);
if ((long)(fa & FileAttributes.Hidden) == 0) return false;
if((long)(fa & FileAttributes.Hidden) == 0) return false;
return HideFile(strFile, false);
}
catch (Exception) { }
catch(Exception) { }
return false;
#endif
@@ -318,26 +272,26 @@ namespace KeePassLib.Utility
public static bool HideFile(string strFile, bool bHide)
{
#if KeePassLibSD
#if (KeePassLibSD || KeePassRT)
return false;
#else
if (strFile == null) throw new ArgumentNullException("strFile");
if(strFile == null) throw new ArgumentNullException("strFile");
try
{
FileAttributes fa = File.GetAttributes(strFile);
if (bHide) fa = ((fa & ~FileAttributes.Normal) | FileAttributes.Hidden);
if(bHide) fa = ((fa & ~FileAttributes.Normal) | FileAttributes.Hidden);
else // Unhide
{
fa &= ~FileAttributes.Hidden;
if ((long)fa == 0) fa = FileAttributes.Normal;
if((long)fa == 0) fa = FileAttributes.Normal;
}
File.SetAttributes(strFile, fa);
return true;
}
catch (Exception) { }
catch(Exception) { }
return false;
#endif
@@ -345,47 +299,48 @@ namespace KeePassLib.Utility
public static string MakeRelativePath(string strBaseFile, string strTargetFile)
{
if (strBaseFile == null) throw new ArgumentNullException("strBasePath");
if (strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if (strBaseFile.Length == 0) return strTargetFile;
if (strTargetFile.Length == 0) return string.Empty;
if(strBaseFile == null) throw new ArgumentNullException("strBasePath");
if(strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if(strBaseFile.Length == 0) return strTargetFile;
if(strTargetFile.Length == 0) return string.Empty;
// Test whether on different Windows drives
if ((strBaseFile.Length >= 3) && (strTargetFile.Length >= 3))
if((strBaseFile.Length >= 3) && (strTargetFile.Length >= 3))
{
if ((strBaseFile[1] == ':') && (strTargetFile[1] == ':') &&
if((strBaseFile[1] == ':') && (strTargetFile[1] == ':') &&
(strBaseFile[2] == '\\') && (strTargetFile[2] == '\\') &&
(strBaseFile[0] != strTargetFile[0]))
return strTargetFile;
}
#if (!KeePassLibSD && !KeePassUAP)
if (NativeLib.IsUnix())
{
if(NativeLib.IsUnix())
#endif
{
bool bBaseUnc = IsUncPath(strBaseFile);
bool bTargetUnc = IsUncPath(strTargetFile);
if ((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
if((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
return strTargetFile;
string strBase = GetShortestAbsolutePath(strBaseFile);
string strTarget = GetShortestAbsolutePath(strTargetFile);
string[] vBase = strBase.Split(UrlUtil.DirSepChars);
string[] vTarget = strTarget.Split(UrlUtil.DirSepChars);
string[] vBase = strBase.Split(m_vDirSeps);
string[] vTarget = strTarget.Split(m_vDirSeps);
int i = 0;
while ((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
(vBase[i] == vTarget[i])) { ++i; }
StringBuilder sbRel = new StringBuilder();
for (int j = i; j < (vBase.Length - 1); ++j)
for(int j = i; j < (vBase.Length - 1); ++j)
{
if (sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
if(sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
sbRel.Append("..");
}
for (int k = i; k < vTarget.Length; ++k)
for(int k = i; k < vTarget.Length; ++k)
{
if (sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
if(sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
sbRel.Append(vTarget[k]);
}
@@ -397,28 +352,28 @@ namespace KeePassLib.Utility
{
const int nMaxPath = NativeMethods.MAX_PATH * 2;
StringBuilder sb = new StringBuilder(nMaxPath + 2);
if (!NativeMethods.PathRelativePathTo(sb, strBaseFile, 0,
strTargetFile, 0))
if(NativeMethods.PathRelativePathTo(sb, strBaseFile, 0,
strTargetFile, 0) == false)
return strTargetFile;
string str = sb.ToString();
while (str.StartsWith(".\\")) str = str.Substring(2, str.Length - 2);
while(str.StartsWith(".\\")) str = str.Substring(2, str.Length - 2);
return str;
}
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
return strTargetFile;
#endif
}
public static string MakeAbsolutePath(string strBaseFile, string strTargetFile)
{
if (strBaseFile == null) throw new ArgumentNullException("strBasePath");
if (strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if (strBaseFile.Length == 0) return strTargetFile;
if (strTargetFile.Length == 0) return string.Empty;
if(strBaseFile == null) throw new ArgumentNullException("strBasePath");
if(strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if(strBaseFile.Length == 0) return strTargetFile;
if(strTargetFile.Length == 0) return string.Empty;
if (IsAbsolutePath(strTargetFile)) return strTargetFile;
if(IsAbsolutePath(strTargetFile)) return strTargetFile;
string strBaseDir = GetFileDirectory(strBaseFile, true, false);
return GetShortestAbsolutePath(strBaseDir + strTargetFile);
@@ -426,56 +381,55 @@ namespace KeePassLib.Utility
public static bool IsAbsolutePath(string strPath)
{
if (strPath == null) throw new ArgumentNullException("strPath");
if (strPath.Length == 0) return false;
if(strPath == null) throw new ArgumentNullException("strPath");
if(strPath.Length == 0) return false;
if (IsUncPath(strPath)) return true;
if(IsUncPath(strPath)) return true;
try { return Path.IsPathRooted(strPath); }
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
return true;
}
public static string GetShortestAbsolutePath(string strPath)
{
if (strPath == null) throw new ArgumentNullException("strPath");
if (strPath.Length == 0) return string.Empty;
if(strPath == null) throw new ArgumentNullException("strPath");
if(strPath.Length == 0) return string.Empty;
// Path.GetFullPath is incompatible with UNC paths traversing over
// different server shares (which are created by PathRelativePathTo);
// we need to build the absolute path on our own...
if (IsUncPath(strPath))
if(IsUncPath(strPath))
{
char chSep = strPath[0];
char[] vSep = ((chSep == '/') ? (new char[] { '/' }) :
(new char[] { '\\', '/' }));
Debug.Assert(Array.IndexOf<char>(m_vDirSeps, chSep) >= 0);
List<string> l = new List<string>();
#if !KeePassLibSD
string[] v = strPath.Split(vSep, StringSplitOptions.None);
string[] v = strPath.Split(m_vDirSeps, StringSplitOptions.None);
#else
string[] v = strPath.Split(vSep);
string[] v = strPath.Split(m_vDirSeps);
#endif
Debug.Assert((v.Length >= 3) && (v[0].Length == 0) &&
(v[1].Length == 0));
foreach (string strPart in v)
foreach(string strPart in v)
{
if (strPart.Equals(".")) continue;
else if (strPart.Equals(".."))
if(strPart.Equals(".")) continue;
else if(strPart.Equals(".."))
{
if (l.Count > 0) l.RemoveAt(l.Count - 1);
if(l.Count > 0) l.RemoveAt(l.Count - 1);
else { Debug.Assert(false); }
}
else l.Add(strPart); // Do not ignore zero length parts
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < l.Count; ++i)
for(int i = 0; i < l.Count; ++i)
{
// Don't test length of sb, might be 0 due to initial UNC seps
if (i > 0) sb.Append(chSep);
if(i > 0) sb.Append(chSep);
sb.Append(l[i]);
}
@@ -484,11 +438,20 @@ namespace KeePassLib.Utility
}
string str;
try { str = Path.GetFullPath(strPath); }
catch (Exception) { Debug.Assert(false); return strPath; }
try
{
#if KeePassRT
var dirT = Windows.Storage.StorageFolder.GetFolderFromPathAsync(
strPath).AwaitEx();
str = dirT.Path;
#else
str = Path.GetFullPath(strPath);
#endif
}
catch(Exception) { Debug.Assert(false); return strPath; }
Debug.Assert((str.IndexOf("\\..\\") < 0) || NativeLib.IsUnix());
foreach (char ch in UrlUtil.DirSepChars)
Debug.Assert(str.IndexOf("\\..\\") < 0);
foreach(char ch in m_vDirSeps)
{
string strSep = new string(ch, 1);
str = str.Replace(strSep + "." + strSep, strSep);
@@ -499,17 +462,17 @@ namespace KeePassLib.Utility
public static int GetUrlLength(string strText, int nOffset)
{
if (strText == null) throw new ArgumentNullException("strText");
if (nOffset > strText.Length) throw new ArgumentException(); // Not >= (0 len)
if(strText == null) throw new ArgumentNullException("strText");
if(nOffset > strText.Length) throw new ArgumentException(); // Not >= (0 len)
int iPosition = nOffset, nLength = 0, nStrLen = strText.Length;
while (iPosition < nStrLen)
while(iPosition < nStrLen)
{
char ch = strText[iPosition];
++iPosition;
if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
if((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
break;
++nLength;
@@ -518,30 +481,24 @@ namespace KeePassLib.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;
if(string.IsNullOrEmpty(strUrl)) return string.Empty;
int i = strUrl.IndexOf(':');
if (i < 0) return strUrl; // No scheme to remove
++i;
int nNetScheme = strUrl.IndexOf(@"://", StrUtil.CaseIgnoreCmp);
int nShScheme = strUrl.IndexOf(@":/", StrUtil.CaseIgnoreCmp);
int nSmpScheme = strUrl.IndexOf(@":", StrUtil.CaseIgnoreCmp);
// 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
if((nNetScheme < 0) && (nShScheme < 0) && (nSmpScheme < 0))
return strUrl; // No scheme
return strUrl.Substring(i);
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);
}
public static string ConvertSeparators(string strPath)
@@ -551,7 +508,7 @@ namespace KeePassLib.Utility
public static string ConvertSeparators(string strPath, char chSeparator)
{
if (string.IsNullOrEmpty(strPath)) return string.Empty;
if(string.IsNullOrEmpty(strPath)) return string.Empty;
strPath = strPath.Replace('/', chSeparator);
strPath = strPath.Replace('\\', chSeparator);
@@ -561,61 +518,33 @@ namespace KeePassLib.Utility
public static bool IsUncPath(string strPath)
{
if (strPath == null) throw new ArgumentNullException("strPath");
if(strPath == null) throw new ArgumentNullException("strPath");
return (strPath.StartsWith("\\\\") || strPath.StartsWith("//"));
}
public static string FilterFileName(string strName)
{
if (string.IsNullOrEmpty(strName)) { Debug.Assert(false); return string.Empty; }
if(strName == null) { Debug.Assert(false); return string.Empty; }
// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
string str = strName;
StringBuilder sb = new StringBuilder(strName.Length);
foreach (char ch in strName)
{
if (ch < '\u0020') continue;
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('|', '-');
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();
return str;
}
/// <summary>
/// Get the host component of a URL.
/// Get the host component of an URL.
/// This method is faster and more fault-tolerant than creating
/// an <code>Uri</code> object and querying its <code>Host</code>
/// property.
@@ -626,60 +555,60 @@ namespace KeePassLib.Utility
/// </example>
public static string GetHost(string strUrl)
{
if (strUrl == null) { Debug.Assert(false); return string.Empty; }
if(strUrl == null) { Debug.Assert(false); return string.Empty; }
StringBuilder sb = new StringBuilder();
bool bInExtHost = false;
for (int i = 0; i < strUrl.Length; ++i)
for(int i = 0; i < strUrl.Length; ++i)
{
char ch = strUrl[i];
if (bInExtHost)
if(bInExtHost)
{
if (ch == '/')
if(ch == '/')
{
if (sb.Length == 0) { } // Ignore leading '/'s
if(sb.Length == 0) { } // Ignore leading '/'s
else break;
}
else sb.Append(ch);
}
else // !bInExtHost
{
if (ch == ':') bInExtHost = true;
if(ch == ':') bInExtHost = true;
}
}
string str = sb.ToString();
if (str.Length == 0) str = strUrl;
if(str.Length == 0) str = strUrl;
// Remove the login part
int nLoginLen = str.IndexOf('@');
if (nLoginLen >= 0) str = str.Substring(nLoginLen + 1);
if(nLoginLen >= 0) str = str.Substring(nLoginLen + 1);
// Remove the port
int iPort = str.LastIndexOf(':');
if (iPort >= 0) str = str.Substring(0, iPort);
if(iPort >= 0) str = str.Substring(0, iPort);
return str;
}
public static bool AssemblyEquals(string strExt, string strShort)
{
if ((strExt == null) || (strShort == null)) { Debug.Assert(false); return false; }
if((strExt == null) || (strShort == null)) { Debug.Assert(false); return false; }
if (strExt.Equals(strShort, StrUtil.CaseIgnoreCmp) ||
if(strExt.Equals(strShort, StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ",", StrUtil.CaseIgnoreCmp))
return true;
if (!strShort.EndsWith(".dll", StrUtil.CaseIgnoreCmp))
if(!strShort.EndsWith(".dll", StrUtil.CaseIgnoreCmp))
{
if (strExt.Equals(strShort + ".dll", StrUtil.CaseIgnoreCmp) ||
if(strExt.Equals(strShort + ".dll", StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ".dll,", StrUtil.CaseIgnoreCmp))
return true;
}
if (!strShort.EndsWith(".exe", StrUtil.CaseIgnoreCmp))
if(!strShort.EndsWith(".exe", StrUtil.CaseIgnoreCmp))
{
if (strExt.Equals(strShort + ".exe", StrUtil.CaseIgnoreCmp) ||
if(strExt.Equals(strShort + ".exe", StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ".exe,", StrUtil.CaseIgnoreCmp))
return true;
}
@@ -690,7 +619,7 @@ namespace KeePassLib.Utility
public static string GetTempPath()
{
string strDir;
if (NativeLib.IsUnix())
if(NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassUAP
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
@@ -700,9 +629,9 @@ namespace KeePassLib.Utility
try
{
if (!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
if(!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
}
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
return strDir;
}
@@ -713,29 +642,31 @@ namespace KeePassLib.Utility
SearchOption opt)
{
List<string> l = new List<string>();
if (strDir == null) { Debug.Assert(false); return l; }
if (strPattern == null) { Debug.Assert(false); return l; }
if(strDir == null) { Debug.Assert(false); return l; }
if(strPattern == null) { Debug.Assert(false); return l; }
string[] v = Directory.GetFiles(strDir, strPattern, opt);
if (v == null) { Debug.Assert(false); return l; }
if(v == null) { Debug.Assert(false); return l; }
// Only accept files with the correct extension; GetFiles may
// return additional files, see GetFiles documentation
string strExt = GetExtension(strPattern);
if (!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
if(!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
(strExt.IndexOf('?') < 0))
{
strExt = "." + strExt;
foreach (string strPathRaw in v)
foreach(string strPathRaw in v)
{
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(strPathRaw);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(strPathRaw);
}
}
else l.AddRange(v);
@@ -748,31 +679,33 @@ namespace KeePassLib.Utility
SearchOption opt)
{
List<FileInfo> l = new List<FileInfo>();
if (di == null) { Debug.Assert(false); return l; }
if (strPattern == null) { Debug.Assert(false); return l; }
if(di == null) { Debug.Assert(false); return l; }
if(strPattern == null) { Debug.Assert(false); return l; }
FileInfo[] v = di.GetFiles(strPattern, opt);
if (v == null) { Debug.Assert(false); return l; }
if(v == null) { Debug.Assert(false); return l; }
// Only accept files with the correct extension; GetFiles may
// return additional files, see GetFiles documentation
string strExt = GetExtension(strPattern);
if (!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
if(!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
(strExt.IndexOf('?') < 0))
{
strExt = "." + strExt;
foreach (FileInfo fi in v)
foreach(FileInfo fi in v)
{
if (fi == null) { Debug.Assert(false); continue; }
if(fi == null) { Debug.Assert(false); continue; }
string strPathRaw = fi.FullName;
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(fi);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(fi);
}
}
else l.AddRange(v);
@@ -780,82 +713,5 @@ namespace KeePassLib.Utility
return l;
}
#endif
public static char GetDriveLetter(string strPath)
{
if (strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(default(char) == '\0');
if (strPath.Length < 3) return '\0';
if ((strPath[1] != ':') || (strPath[2] != '\\')) return '\0';
char ch = char.ToUpperInvariant(strPath[0]);
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
}
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;
}
internal static string GetCanonicalUri(string strUri)
{
if (string.IsNullOrEmpty(strUri)) { Debug.Assert(false); return strUri; }
try
{
Uri uri = new Uri(strUri);
if (uri.IsAbsoluteUri) return uri.AbsoluteUri;
else { Debug.Assert(false); }
}
catch (Exception) { Debug.Assert(false); }
return strUri;
}
/* internal static Dictionary<string, string> ParseQuery(string strQuery)
{
Dictionary<string, string> d = new Dictionary<string, string>();
if(string.IsNullOrEmpty(strQuery)) return d;
string[] vKvp = strQuery.Split(new char[] { '?', '&' });
if(vKvp == null) { Debug.Assert(false); return d; }
foreach(string strKvp in vKvp)
{
if(string.IsNullOrEmpty(strKvp)) continue;
string strKey, strValue;
int iSep = strKvp.IndexOf('=');
if(iSep < 0)
{
strKey = strKvp;
strValue = string.Empty;
}
else
{
strKey = strKvp.Substring(0, iSep);
strValue = strKvp.Substring(iSep + 1);
}
strKey = Uri.UnescapeDataString(strKey);
strValue = Uri.UnescapeDataString(strValue);
d[strKey] = strValue;
}
return d;
} */
}
}

View File

@@ -1,290 +0,0 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Serialization;
namespace KeePassLib.Utility
{
public static class XmlUtilEx
{
public static XmlDocument CreateXmlDocument()
{
XmlDocument d = new XmlDocument();
// .NET 4.5.2 and newer do not resolve external XML resources
// by default; for older .NET versions, we explicitly
// prevent resolving
d.XmlResolver = null; // Default in old .NET: XmlUrlResolver object
return d;
}
public static XmlReaderSettings CreateXmlReaderSettings()
{
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.CloseInput = false;
xrs.IgnoreComments = true;
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true;
#if KeePassUAP
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
// Also see PrepMonoDev.sh script
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
#endif
xrs.ValidationType = ValidationType.None;
xrs.XmlResolver = null;
return xrs;
}
public static XmlReader CreateXmlReader(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlReader.Create(s, CreateXmlReaderSettings());
}
public static XmlWriterSettings CreateXmlWriterSettings()
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.CloseOutput = false;
xws.Encoding = StrUtil.Utf8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.NewLineOnAttributes = false;
return xws;
}
public static XmlWriter CreateXmlWriter(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlWriter.Create(s, CreateXmlWriterSettings());
}
public static T Deserialize<T>(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
T t = default(T);
using(XmlReader xr = CreateXmlReader(s))
{
t = (T)xs.Deserialize(xr);
}
return t;
}
public static void Serialize<T>(Stream s, T t)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
using(XmlWriter xw = CreateXmlWriter(s))
{
xs.Serialize(xw, t);
}
}
internal static void Serialize<T>(Stream s, T t, bool bRemoveXsdXsi)
{
// One way to remove the "xsd" and "xsi" namespace declarations
// is to use an XmlSerializerNamespaces object containing only
// a ""/"" pair; this seems to work, but Microsoft's
// documentation explicitly states that it isn't supported:
// https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializernamespaces
// There are other, more complex ways, but these either rely on
// undocumented details or require the type T to be modified.
string str;
using(MemoryStream ms = new MemoryStream())
{
Serialize<T>(ms, t);
str = StrUtil.Utf8.GetString(ms.ToArray());
}
Func<string, string, bool> fFindPfx = delegate(string strText, string strSub)
{
int i = strText.IndexOf(strSub, StringComparison.Ordinal);
if(i < 0) return false;
if(i == 0) return true;
return char.IsWhiteSpace(strText[i - 1]);
};
if(bRemoveXsdXsi)
{
if(!fFindPfx(str, "xsd:") && !fFindPfx(str, "xsi:"))
{
Debug.Assert(str.IndexOf("xmlns:xsd") > 0);
str = str.Replace(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", string.Empty);
Debug.Assert(str.IndexOf("xmlns:xsd") < 0);
Debug.Assert(str.IndexOf("xmlns:xsi") > 0);
str = str.Replace(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", string.Empty);
Debug.Assert(str.IndexOf("xmlns:xsi") < 0);
}
else { Debug.Assert(false); } // "xsd"/"xsi" decl. may be required
}
MemUtil.Write(s, StrUtil.Utf8.GetBytes(str));
}
#if DEBUG
internal static void ValidateXml(string strXml, bool bReplaceStdEntities)
{
if(strXml == null) throw new ArgumentNullException("strXml");
if(strXml.Length == 0) { Debug.Assert(false); return; }
string str = strXml;
if(bReplaceStdEntities)
str = str.Replace("&nbsp;", "&#160;");
XmlDocument d = new XmlDocument();
d.LoadXml(str);
}
#endif
internal static XPathNodeIterator FindNodes(PwDatabase pd, string strXPath,
IStatusLogger sl, out XmlDocument xd)
{
if(pd == null) throw new ArgumentNullException("pd");
if(strXPath == null) { Debug.Assert(false); strXPath = string.Empty; }
KdbxFile kdbx = new KdbxFile(pd);
byte[] pbXml;
using(MemoryStream ms = new MemoryStream())
{
kdbx.Save(ms, null, KdbxFormat.PlainXml, sl);
pbXml = ms.ToArray();
}
string strXml = StrUtil.Utf8.GetString(pbXml);
xd = CreateXmlDocument();
xd.LoadXml(strXml);
XPathNavigator xpNav = xd.CreateNavigator();
return xpNav.Select(strXPath);
// XPathExpression xpExpr = xpNav.Compile(strXPath);
// xpExpr.SetContext(new XuXsltContext());
// return xpNav.Select(xpExpr);
}
/* private sealed class XuFnMatches : IXsltContextFunction
{
private readonly XPathResultType[] m_vArgTypes = new XPathResultType[] {
XPathResultType.String, XPathResultType.String, XPathResultType.String
};
public XPathResultType[] ArgTypes { get { return m_vArgTypes; } }
public int Maxargs { get { return 3; } }
public int Minargs { get { return 2; } }
public XPathResultType ReturnType { get { return XPathResultType.Boolean; } }
private static string GetArgString(object[] args, int i, string strDefault)
{
if(args == null) { Debug.Assert(false); return strDefault; }
if(i >= args.Length) return strDefault;
object o = args[i];
if(o == null) return strDefault;
XPathNodeIterator it = (o as XPathNodeIterator);
if(it != null) o = it.Current.Value;
return (o.ToString() ?? strDefault);
}
public object Invoke(XsltContext xsltContext, object[] args,
XPathNavigator docContext)
{
string strInput = GetArgString(args, 0, string.Empty);
string strPattern = GetArgString(args, 1, string.Empty);
string strFlags = GetArgString(args, 2, null);
RegexOptions ro = RegexOptions.None;
if(!string.IsNullOrEmpty(strFlags))
{
if(strFlags.IndexOf('s') >= 0) ro |= RegexOptions.Singleline;
if(strFlags.IndexOf('m') >= 0) ro |= RegexOptions.Multiline;
if(strFlags.IndexOf('i') >= 0) ro |= RegexOptions.IgnoreCase;
if(strFlags.IndexOf('x') >= 0) ro |= RegexOptions.IgnorePatternWhitespace;
}
return Regex.IsMatch(strInput, strPattern, ro);
}
}
private sealed class XuXsltContext : XsltContext
{
public override bool Whitespace { get { return false; } }
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return string.CompareOrdinal(baseUri, nextbaseUri);
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return false;
}
public override IXsltContextFunction ResolveFunction(string prefix,
string name, XPathResultType[] ArgTypes)
{
if(prefix != "kp") { Debug.Assert(false); return null; }
if(name == "matches") return new XuFnMatches();
Debug.Assert(false);
return null;
}
public override IXsltContextVariable ResolveVariable(string prefix,
string name)
{
Debug.Assert(false);
return null;
}
} */
}
}

View File

@@ -1,984 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Newtonsoft.Json;
using Formatting = System.Xml.Formatting;
namespace Kp2aAutofillParser
{
public class W3cHints
{
// Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill)
public const string HONORIFIC_PREFIX = "honorific-prefix";
public const string NAME = "name";
public const string GIVEN_NAME = "given-name";
public const string ADDITIONAL_NAME = "additional-name";
public const string FAMILY_NAME = "family-name";
public const string HONORIFIC_SUFFIX = "honorific-suffix";
public const string USERNAME = "username";
public const string NEW_PASSWORD = "new-password";
public const string CURRENT_PASSWORD = "current-password";
public const string ORGANIZATION_TITLE = "organization-title";
public const string ORGANIZATION = "organization";
public const string STREET_ADDRESS = "street-address";
public const string ADDRESS_LINE1 = "address-line1";
public const string ADDRESS_LINE2 = "address-line2";
public const string ADDRESS_LINE3 = "address-line3";
public const string ADDRESS_LEVEL4 = "address-level4";
public const string ADDRESS_LEVEL3 = "address-level3";
public const string ADDRESS_LEVEL2 = "address-level2";
public const string ADDRESS_LEVEL1 = "address-level1";
public const string COUNTRY = "country";
public const string COUNTRY_NAME = "country-name";
public const string POSTAL_CODE = "postal-code";
public const string CC_NAME = "cc-name";
public const string CC_GIVEN_NAME = "cc-given-name";
public const string CC_ADDITIONAL_NAME = "cc-additional-name";
public const string CC_FAMILY_NAME = "cc-family-name";
public const string CC_NUMBER = "cc-number";
public const string CC_EXPIRATION = "cc-exp";
public const string CC_EXPIRATION_MONTH = "cc-exp-month";
public const string CC_EXPIRATION_YEAR = "cc-exp-year";
public const string CC_CSC = "cc-csc";
public const string CC_TYPE = "cc-type";
public const string TRANSACTION_CURRENCY = "transaction-currency";
public const string TRANSACTION_AMOUNT = "transaction-amount";
public const string LANGUAGE = "language";
public const string BDAY = "bday";
public const string BDAY_DAY = "bday-day";
public const string BDAY_MONTH = "bday-month";
public const string BDAY_YEAR = "bday-year";
public const string SEX = "sex";
public const string URL = "url";
public const string PHOTO = "photo";
// Optional W3C prefixes
public const string PREFIX_SECTION = "section-";
public const string SHIPPING = "shipping";
public const string BILLING = "billing";
// W3C prefixes below...
public const string PREFIX_HOME = "home";
public const string PREFIX_WORK = "work";
public const string PREFIX_FAX = "fax";
public const string PREFIX_PAGER = "pager";
// ... require those suffix
public const string TEL = "tel";
public const string TEL_COUNTRY_CODE = "tel-country-code";
public const string TEL_NATIONAL = "tel-national";
public const string TEL_AREA_CODE = "tel-area-code";
public const string TEL_LOCAL = "tel-local";
public const string TEL_LOCAL_PREFIX = "tel-local-prefix";
public const string TEL_LOCAL_SUFFIX = "tel-local-suffix";
public const string TEL_EXTENSION = "tel_extension";
public const string EMAIL = "email";
public const string IMPP = "impp";
private W3cHints()
{
}
public static bool isW3cSectionPrefix(string hint)
{
return hint.ToLower().StartsWith(W3cHints.PREFIX_SECTION);
}
public static bool isW3cAddressType(string hint)
{
switch (hint.ToLower())
{
case W3cHints.SHIPPING:
case W3cHints.BILLING:
return true;
}
return false;
}
public static bool isW3cTypePrefix(string hint)
{
switch (hint.ToLower())
{
case W3cHints.PREFIX_WORK:
case W3cHints.PREFIX_FAX:
case W3cHints.PREFIX_HOME:
case W3cHints.PREFIX_PAGER:
return true;
}
return false;
}
public static bool isW3cTypeHint(string hint)
{
switch (hint.ToLower())
{
case W3cHints.TEL:
case W3cHints.TEL_COUNTRY_CODE:
case W3cHints.TEL_NATIONAL:
case W3cHints.TEL_AREA_CODE:
case W3cHints.TEL_LOCAL:
case W3cHints.TEL_LOCAL_PREFIX:
case W3cHints.TEL_LOCAL_SUFFIX:
case W3cHints.TEL_EXTENSION:
case W3cHints.EMAIL:
case W3cHints.IMPP:
return true;
}
return false;
}
}
/// <summary>
/// FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
/// plus the dataset name associated with it.
/// </summary>
public class FilledAutofillFieldCollection<FieldT> where FieldT:InputField
{
public Dictionary<string, FilledAutofillField<FieldT>> HintMap { get; }
public string DatasetName { get; set; }
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField<FieldT>> hintMap, string datasetName = "")
{
//recreate hint map making sure we compare case insensitive
HintMap = BuildHintMap();
foreach (var p in hintMap)
HintMap.Add(p.Key, p.Value);
DatasetName = datasetName;
}
public FilledAutofillFieldCollection() : this(BuildHintMap())
{ }
private static Dictionary<string, FilledAutofillField<FieldT>> BuildHintMap()
{
return new Dictionary<string, FilledAutofillField<FieldT>>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// Adds a filledAutofillField to the collection, indexed by all of its hints.
/// </summary>
/// <returns>The add.</returns>
/// <param name="filledAutofillField">Filled autofill field.</param>
public void Add(FilledAutofillField<FieldT> filledAutofillField)
{
foreach (string hint in filledAutofillField.AutofillHints)
{
if (AutofillHintsHelper.IsSupportedHint(hint))
{
HintMap.TryAdd(hint, filledAutofillField);
}
}
}
/// <summary>
/// Takes in a list of autofill hints (`autofillHints`), usually associated with a View or set of
/// Views. Returns whether any of the filled fields on the page have at least 1 of these
/// `autofillHint`s.
/// </summary>
/// <returns><c>true</c>, if with hints was helpsed, <c>false</c> otherwise.</returns>
/// <param name="autofillHints">Autofill hints.</param>
public bool HelpsWithHints(List<string> autofillHints)
{
for (int i = 0; i < autofillHints.Count; i++)
{
var autofillHint = autofillHints[i];
if (HintMap.ContainsKey(autofillHint) && !HintMap[autofillHint].IsNull())
{
return true;
}
}
return false;
}
}
public class AutofillHintsHelper
{
public const string AutofillHint2faAppOtp = "2faAppOTPCode";
public const string AutofillHintBirthDateDay = "birthDateDay";
public const string AutofillHintBirthDateFull = "birthDateFull";
public const string AutofillHintBirthDateMonth = "birthDateMonth";
public const string AutofillHintBirthDateYear = "birthDateYear";
public const string AutofillHintCreditCardExpirationDate = "creditCardExpirationDate";
public const string AutofillHintCreditCardExpirationDay = "creditCardExpirationDay";
public const string AutofillHintCreditCardExpirationMonth = "creditCardExpirationMonth";
public const string AutofillHintCreditCardExpirationYear = "creditCardExpirationYear";
public const string AutofillHintCreditCardNumber = "creditCardNumber";
public const string AutofillHintCreditCardSecurityCode = "creditCardSecurityCode";
public const string AutofillHintEmailAddress = "emailAddress";
public const string AutofillHintEmailOtp = "emailOTPCode";
public const string AutofillHintGender = "gender";
public const string AutofillHintName = "name";
public const string AutofillHintNewPassword = "newPassword";
public const string AutofillHintNewUsername = "newUsername";
public const string AutofillHintNotApplicable = "notApplicable";
public const string AutofillHintPassword = "password";
public const string AutofillHintPersonName = "personName";
public const string AutofillHintPersonNameFAMILY = "personFamilyName";
public const string AutofillHintPersonNameGIVEN = "personGivenName";
public const string AutofillHintPersonNameMIDDLE = "personMiddleName";
public const string AutofillHintPersonNameMIDDLE_INITIAL = "personMiddleInitial";
public const string AutofillHintPersonNamePREFIX = "personNamePrefix";
public const string AutofillHintPersonNameSUFFIX = "personNameSuffix";
public const string AutofillHintPhone = "phone";
public const string AutofillHintPhoneContryCode = "phoneCountryCode";
public const string AutofillHintPostalAddressAPT_NUMBER = "aptNumber";
public const string AutofillHintPostalAddressCOUNTRY = "addressCountry";
public const string AutofillHintPostalAddressDEPENDENT_LOCALITY = "dependentLocality";
public const string AutofillHintPostalAddressEXTENDED_ADDRESS = "extendedAddress";
public const string AutofillHintPostalAddressEXTENDED_POSTAL_CODE = "extendedPostalCode";
public const string AutofillHintPostalAddressLOCALITY = "addressLocality";
public const string AutofillHintPostalAddressREGION = "addressRegion";
public const string AutofillHintPostalAddressSTREET_ADDRESS = "streetAddress";
public const string AutofillHintPostalCode = "postalCode";
public const string AutofillHintPromoCode = "promoCode";
public const string AutofillHintSMS_OTP = "smsOTPCode";
public const string AutofillHintUPI_VPA = "upiVirtualPaymentAddress";
public const string AutofillHintUsername = "username";
public const string AutofillHintWifiPassword = "wifiPassword";
public const string AutofillHintPhoneNational = "phoneNational";
public const string AutofillHintPhoneNumber = "phoneNumber";
public const string AutofillHintPhoneNumberDevice = "phoneNumberDevice";
public const string AutofillHintPostalAddress = "postalAddress";
private static readonly HashSet<string> _allSupportedHints = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
AutofillHintCreditCardExpirationDate,
AutofillHintCreditCardExpirationDay,
AutofillHintCreditCardExpirationMonth,
AutofillHintCreditCardExpirationYear,
AutofillHintCreditCardNumber,
AutofillHintCreditCardSecurityCode,
AutofillHintEmailAddress,
AutofillHintPhone,
AutofillHintName,
AutofillHintPassword,
AutofillHintPostalAddress,
AutofillHintPostalCode,
AutofillHintUsername,
W3cHints.HONORIFIC_PREFIX,
W3cHints.NAME,
W3cHints.GIVEN_NAME,
W3cHints.ADDITIONAL_NAME,
W3cHints.FAMILY_NAME,
W3cHints.HONORIFIC_SUFFIX,
W3cHints.USERNAME,
W3cHints.NEW_PASSWORD,
W3cHints.CURRENT_PASSWORD,
W3cHints.ORGANIZATION_TITLE,
W3cHints.ORGANIZATION,
W3cHints.STREET_ADDRESS,
W3cHints.ADDRESS_LINE1,
W3cHints.ADDRESS_LINE2,
W3cHints.ADDRESS_LINE3,
W3cHints.ADDRESS_LEVEL4,
W3cHints.ADDRESS_LEVEL3,
W3cHints.ADDRESS_LEVEL2,
W3cHints.ADDRESS_LEVEL1,
W3cHints.COUNTRY,
W3cHints.COUNTRY_NAME,
W3cHints.POSTAL_CODE,
W3cHints.CC_NAME,
W3cHints.CC_GIVEN_NAME,
W3cHints.CC_ADDITIONAL_NAME,
W3cHints.CC_FAMILY_NAME,
W3cHints.CC_NUMBER,
W3cHints.CC_EXPIRATION,
W3cHints.CC_EXPIRATION_MONTH,
W3cHints.CC_EXPIRATION_YEAR,
W3cHints.CC_CSC,
W3cHints.CC_TYPE,
W3cHints.TRANSACTION_CURRENCY,
W3cHints.TRANSACTION_AMOUNT,
W3cHints.LANGUAGE,
W3cHints.BDAY,
W3cHints.BDAY_DAY,
W3cHints.BDAY_MONTH,
W3cHints.BDAY_YEAR,
W3cHints.SEX,
W3cHints.URL,
W3cHints.PHOTO,
W3cHints.TEL,
W3cHints.TEL_COUNTRY_CODE,
W3cHints.TEL_NATIONAL,
W3cHints.TEL_AREA_CODE,
W3cHints.TEL_LOCAL,
W3cHints.TEL_LOCAL_PREFIX,
W3cHints.TEL_LOCAL_SUFFIX,
W3cHints.TEL_EXTENSION,
W3cHints.EMAIL,
W3cHints.IMPP,
};
private static readonly List<HashSet<string>> partitionsOfCanonicalHints = new List<HashSet<string>>()
{
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
AutofillHintEmailAddress,
AutofillHintPhone,
AutofillHintName,
AutofillHintPassword,
AutofillHintUsername,
W3cHints.HONORIFIC_PREFIX,
W3cHints.EMAIL,
W3cHints.NAME,
W3cHints.GIVEN_NAME,
W3cHints.ADDITIONAL_NAME,
W3cHints.FAMILY_NAME,
W3cHints.HONORIFIC_SUFFIX,
W3cHints.ORGANIZATION_TITLE,
W3cHints.ORGANIZATION,
W3cHints.LANGUAGE,
W3cHints.BDAY,
W3cHints.BDAY_DAY,
W3cHints.BDAY_MONTH,
W3cHints.BDAY_YEAR,
W3cHints.SEX,
W3cHints.URL,
W3cHints.PHOTO,
W3cHints.TEL,
W3cHints.TEL_COUNTRY_CODE,
W3cHints.TEL_NATIONAL,
W3cHints.TEL_AREA_CODE,
W3cHints.TEL_LOCAL,
W3cHints.TEL_LOCAL_PREFIX,
W3cHints.TEL_LOCAL_SUFFIX,
W3cHints.TEL_EXTENSION,
W3cHints.IMPP,
},
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
AutofillHintPostalAddress,
AutofillHintPostalCode,
W3cHints.STREET_ADDRESS,
W3cHints.ADDRESS_LINE1,
W3cHints.ADDRESS_LINE2,
W3cHints.ADDRESS_LINE3,
W3cHints.ADDRESS_LEVEL4,
W3cHints.ADDRESS_LEVEL3,
W3cHints.ADDRESS_LEVEL2,
W3cHints.ADDRESS_LEVEL1,
W3cHints.COUNTRY,
W3cHints.COUNTRY_NAME
},
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
AutofillHintCreditCardExpirationDate,
AutofillHintCreditCardExpirationDay,
AutofillHintCreditCardExpirationMonth,
AutofillHintCreditCardExpirationYear,
AutofillHintCreditCardNumber,
AutofillHintCreditCardSecurityCode,
W3cHints.CC_NAME,
W3cHints.CC_GIVEN_NAME,
W3cHints.CC_ADDITIONAL_NAME,
W3cHints.CC_FAMILY_NAME,
W3cHints.CC_TYPE,
W3cHints.TRANSACTION_CURRENCY,
W3cHints.TRANSACTION_AMOUNT,
},
};
private static readonly Dictionary<string, string> hintToCanonicalReplacement = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{W3cHints.EMAIL, AutofillHintEmailAddress},
{W3cHints.USERNAME, AutofillHintUsername},
{W3cHints.CURRENT_PASSWORD, AutofillHintPassword},
{W3cHints.NEW_PASSWORD, AutofillHintPassword},
{W3cHints.CC_EXPIRATION_MONTH, AutofillHintCreditCardExpirationMonth },
{W3cHints.CC_EXPIRATION_YEAR, AutofillHintCreditCardExpirationYear },
{W3cHints.CC_EXPIRATION, AutofillHintCreditCardExpirationDate },
{W3cHints.CC_NUMBER, AutofillHintCreditCardNumber },
{W3cHints.CC_CSC, AutofillHintCreditCardSecurityCode },
{W3cHints.POSTAL_CODE, AutofillHintPostalCode },
};
public static bool IsSupportedHint(string hint)
{
return _allSupportedHints.Contains(hint);
}
public static string[] FilterForSupportedHints(string[] hints)
{
if (hints == null)
return Array.Empty<string>();
var filteredHints = new string[hints.Length];
int i = 0;
foreach (var hint in hints)
{
if (IsSupportedHint(hint))
{
filteredHints[i++] = hint;
}
}
var finalFilteredHints = new string[i];
Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
return finalFilteredHints;
}
/// <summary>
/// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase
/// </summary>
public static List<string> ConvertToCanonicalLowerCaseHints(string[] supportedHints)
{
List<string> result = new List<string>();
foreach (string hint in supportedHints)
{
var canonicalHint = ToCanonicalHint(hint);
result.Add(canonicalHint.ToLower());
}
return result;
}
public static string ToCanonicalHint(string hint)
{
string canonicalHint;
if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint))
canonicalHint = hint;
return canonicalHint;
}
public static int GetPartitionIndex(string hint)
{
for (int i = 0; i < partitionsOfCanonicalHints.Count; i++)
{
if (partitionsOfCanonicalHints[i].Contains(hint))
{
return i;
}
}
return -1;
}
public static FilledAutofillFieldCollection<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> autofillFields, int partitionIndex) where FieldT: InputField
{
FilledAutofillFieldCollection<FieldT> filteredCollection =
new FilledAutofillFieldCollection<FieldT> { DatasetName = autofillFields.DatasetName };
if (partitionIndex == -1)
return filteredCollection;
foreach (var field in autofillFields.HintMap.Values.Distinct())
{
foreach (var hint in field.AutofillHints)
{
if (GetPartitionIndex(hint) == partitionIndex)
{
filteredCollection.Add(field);
break;
}
}
}
return filteredCollection;
}
public static FilledAutofillFieldCollection<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> filledAutofillFieldCollection, List<string> autofillFieldsFocusedAutofillCanonicalHints) where FieldT: InputField
{
//only apply partition data if we have FocusedAutofillCanonicalHints. This may be empty on buggy Firefox.
if (autofillFieldsFocusedAutofillCanonicalHints.Any())
{
int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFieldsFocusedAutofillCanonicalHints.FirstOrDefault());
return AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex);
}
return filledAutofillFieldCollection;
}
}
/// <summary>
/// This enum represents the Android.Text.InputTypes values. For testability, this is duplicated here.
/// </summary>
public enum InputTypes
{
ClassDatetime = 4,
ClassNumber = 2,
ClassPhone = 3,
ClassText = 1,
DatetimeVariationDate = 16,
DatetimeVariationNormal = 0,
DatetimeVariationTime = 32,
MaskClass = 15,
MaskFlags = 16773120,
MaskVariation = 4080,
Null = 0,
NumberFlagDecimal = 8192,
NumberFlagSigned = 4096,
NumberVariationNormal = 0,
NumberVariationPassword = 16,
TextFlagAutoComplete = 65536,
TextFlagAutoCorrect = 32768,
TextFlagCapCharacters = 4096,
TextFlagCapSentences = 16384,
TextFlagCapWords = 8192,
TextFlagEnableTextConversionSuggestions = 1048576,
TextFlagImeMultiLine = 262144,
TextFlagMultiLine = 131072,
TextFlagNoSuggestions = 524288,
TextVariationEmailAddress = 32,
TextVariationEmailSubject = 48,
TextVariationFilter = 176,
TextVariationLongMessage = 80,
TextVariationNormal = 0,
TextVariationPassword = 128,
TextVariationPersonName = 96,
TextVariationPhonetic = 192,
TextVariationPostalAddress = 112,
TextVariationShortMessage = 64,
TextVariationUri = 16,
TextVariationVisiblePassword = 144,
TextVariationWebEditText = 160,
TextVariationWebEmailAddress = 208,
TextVariationWebPassword = 224
}
public interface IKp2aDigitalAssetLinksDataSource
{
bool IsTrustedApp(string packageName);
bool IsTrustedLink(string domain, string targetPackage);
bool IsEnabled();
}
class TimeUtil
{
private static DateTime? m_dtUnixRoot = null;
public static DateTime ConvertUnixTime(double dtUnix)
{
try
{
if (!m_dtUnixRoot.HasValue)
m_dtUnixRoot = (new DateTime(1970, 1, 1, 0, 0, 0, 0,
DateTimeKind.Utc)).ToLocalTime();
return m_dtUnixRoot.Value.AddSeconds(dtUnix);
}
catch (Exception) { Debug.Assert(false); }
return DateTime.UtcNow;
}
}
public class FilledAutofillField<FieldT> where FieldT : InputField
{
private string[] _autofillHints;
public string TextValue { get; set; }
public long? DateValue { get; set; }
public bool? ToggleValue { get; set; }
public string ValueToString()
{
if (DateValue != null)
{
return TimeUtil.ConvertUnixTime((long)DateValue / 1000.0).ToLongDateString();
}
if (ToggleValue != null)
return ToggleValue.ToString();
return TextValue;
}
/// <summary>
/// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison.
/// </summary>
public string[] AutofillHints
{
get
{
return _autofillHints;
}
set
{
_autofillHints = value;
for (int i = 0; i < _autofillHints.Length; i++)
_autofillHints[i] = _autofillHints[i].ToLower();
}
}
public FilledAutofillField()
{ }
public FilledAutofillField(FieldT inputField)
: this(inputField, inputField.AutofillHints)
{
}
public FilledAutofillField(FieldT inputField, string[] hints)
{
string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints);
List<string> hintList = new List<string>();
string nextHint = null;
for (int i = 0; i < rawHints.Length; i++)
{
string hint = rawHints[i];
if (i < rawHints.Length - 1)
{
nextHint = rawHints[i + 1];
}
// First convert the compound W3C autofill hints
if (W3cHints.isW3cSectionPrefix(hint) && i < rawHints.Length - 1)
{
hint = rawHints[++i];
if (i < rawHints.Length - 1)
{
nextHint = rawHints[i + 1];
}
}
if (W3cHints.isW3cTypePrefix(hint) && nextHint != null && W3cHints.isW3cTypeHint(nextHint))
{
hint = nextHint;
i++;
}
if (W3cHints.isW3cAddressType(hint) && nextHint != null)
{
hint = nextHint;
i++;
}
// Then check if the "actual" hint is supported.
if (AutofillHintsHelper.IsSupportedHint(hint))
{
hintList.Add(hint);
}
else
{
}
}
AutofillHints = AutofillHintsHelper.ConvertToCanonicalLowerCaseHints(hintList.ToArray()).ToArray();
}
public bool IsNull()
{
return TextValue == null && DateValue == null && ToggleValue == null;
}
public override bool Equals(object obj)
{
if (this == obj) return true;
if (obj == null || GetType() != obj.GetType()) return false;
FilledAutofillField<FieldT> that = (FilledAutofillField<FieldT>)obj;
if (!TextValue?.Equals(that.TextValue) ?? that.TextValue != null)
return false;
if (DateValue != null ? !DateValue.Equals(that.DateValue) : that.DateValue != null)
return false;
return ToggleValue != null ? ToggleValue.Equals(that.ToggleValue) : that.ToggleValue == null;
}
public override int GetHashCode()
{
unchecked
{
var result = TextValue != null ? TextValue.GetHashCode() : 0;
result = 31 * result + (DateValue != null ? DateValue.GetHashCode() : 0);
result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
return result;
}
}
}
/// <summary>
/// Base class for everything that is (or could be) an input field which might (or might not) be autofilled.
/// For testability, this is independent from Android classes like ViewNode
/// </summary>
public abstract class InputField
{
public string? IdEntry { get; set; }
public string? Hint { get; set; }
public string ClassName { get; set; }
public string[] AutofillHints { get; set; }
public bool IsFocused { get; set; }
public InputTypes InputType { get; set; }
public string HtmlInfoTag { get; set; }
public string HtmlInfoTypeAttribute { get; set; }
}
/// <summary>
/// Serializable structure defining the contents of the current view (from an autofill perspective)
/// </summary>
/// <typeparam name="TField"></typeparam>
public class AutofillView<TField> where TField : InputField
{
public List<TField> InputFields { get; set; } = new List<TField>();
public string PackageId { get; set; } = null;
public string WebDomain { get; set; } = null;
}
public interface ILogger
{
void Log(string x);
}
public class StructureParserBase<FieldT> where FieldT: InputField
{
private readonly ILogger _log;
private readonly IKp2aDigitalAssetLinksDataSource _digitalAssetLinksDataSource;
private readonly List<string> _autofillHintsForLogin = new List<string>
{
AutofillHintsHelper.AutofillHintPassword,
AutofillHintsHelper.AutofillHintUsername,
AutofillHintsHelper.AutofillHintEmailAddress
};
public string PackageId { get; set; }
public Dictionary<FieldT, string[]> FieldsMappedToHints = new Dictionary<FieldT, string[]>();
public StructureParserBase(ILogger logger, IKp2aDigitalAssetLinksDataSource digitalAssetLinksDataSource)
{
_log = logger;
_digitalAssetLinksDataSource = digitalAssetLinksDataSource;
}
public class AutofillTargetId
{
public string PackageName { get; set; }
public string PackageNameWithPseudoSchema
{
get { return AndroidAppScheme + PackageName; }
}
public const string AndroidAppScheme = "androidapp://";
public string WebDomain { get; set; }
/// <summary>
/// If PackageName and WebDomain are not compatible (by DAL or because PackageName is a trusted browser in which case we treat all domains as "compatible"
/// we need to issue a warning. If we would fill credentials for the package, a malicious website could try to get credentials for the app.
/// If we would fill credentials for the domain, a malicious app could get credentials for the domain.
/// </summary>
public bool IncompatiblePackageAndDomain { get; set; }
public string DomainOrPackage
{
get
{
return WebDomain ?? PackageNameWithPseudoSchema;
}
}
}
public AutofillTargetId ParseForFill(bool isManual, AutofillView<FieldT> autofillView)
{
return Parse(true, isManual, autofillView);
}
public AutofillTargetId ParseForSave(AutofillView<FieldT> autofillView)
{
return Parse(false, true, autofillView);
}
/// <summary>
/// Traverse AssistStructure and add ViewNode metadata to a flat list.
/// </summary>
/// <returns>The parse.</returns>
/// <param name="forFill">If set to <c>true</c> for fill.</param>
/// <param name="isManualRequest"></param>
protected virtual AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<FieldT> autofillView)
{
AutofillTargetId result = new AutofillTargetId()
{
PackageName = autofillView.PackageId,
WebDomain = autofillView.WebDomain
};
_log.Log("parsing autofillStructure...");
if (LogAutofillView)
{
string debugInfo = JsonConvert.SerializeObject(autofillView, Newtonsoft.Json.Formatting.Indented);
_log.Log("This is the autofillStructure: \n\n " + debugInfo);
}
//go through each input field and determine username/password fields.
//Depending on the target this can require more or less heuristics.
// * if there is a valid & supported autofill hint, we assume that all fields which should be filled do have an appropriate Autofill hint
// * if there is no such autofill hint, we use IsPassword to
HashSet<string> autofillHintsOfAllFields = autofillView.InputFields.Where(f => f.AutofillHints != null)
.SelectMany(f => f.AutofillHints).Select(AutofillHintsHelper.ToCanonicalHint).ToHashSet();
bool hasLoginAutofillHints = autofillHintsOfAllFields.Intersect(_autofillHintsForLogin).Any();
if (hasLoginAutofillHints)
{
foreach (var viewNode in autofillView.InputFields)
{
string[] viewHints = viewNode.AutofillHints;
if (viewHints == null)
continue;
if (viewHints.Select(AutofillHintsHelper.ToCanonicalHint).Intersect(_autofillHintsForLogin).Any())
{
AddFieldToHintMap(viewNode, viewHints.Select(AutofillHintsHelper.ToCanonicalHint).ToHashSet().ToArray());
}
}
}
else
{
//determine password fields, first by type, then by hint:
List<FieldT> editTexts = autofillView.InputFields.Where(f => IsEditText(f)).ToList();
List<FieldT> passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && IsPassword(f)).ToList();
if (!passwordFields.Any())
{
passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && HasPasswordHint(f)).ToList();
}
//determine username fields. Try by hint, if that fails use the one before the password
List<FieldT> usernameFields = autofillView.InputFields.Where(f => IsEditText(f) && HasUsernameHint(f)).ToList();
if (!usernameFields.Any())
{
foreach (var passwordField in passwordFields)
{
var lastInputBeforePassword = autofillView.InputFields.Where(IsEditText)
.TakeWhile(f => f != passwordField && !passwordFields.Contains(f)).LastOrDefault();
if (lastInputBeforePassword != null)
usernameFields.Add(lastInputBeforePassword);
}
}
//for "heuristic determination" we demand that one of the filled fields is focused:
if (passwordFields.Concat(usernameFields).Any(f => f.IsFocused))
{
foreach (var uf in usernameFields)
AddFieldToHintMap(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
foreach (var pf in passwordFields.Except(usernameFields))
AddFieldToHintMap(pf, new string[] { AutofillHintsHelper.AutofillHintPassword });
}
}
if (!string.IsNullOrEmpty(autofillView.WebDomain) && _digitalAssetLinksDataSource.IsEnabled())
{
result.IncompatiblePackageAndDomain = !_digitalAssetLinksDataSource.IsTrustedLink(autofillView.WebDomain, result.PackageName);
if (result.IncompatiblePackageAndDomain)
{
_log.Log($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
}
}
else
{
result.IncompatiblePackageAndDomain = false;
}
return result;
}
private void AddFieldToHintMap(FieldT field, string[] hints)
{
if (FieldsMappedToHints.ContainsKey(field))
{
FieldsMappedToHints[field] = FieldsMappedToHints[field].Concat(hints).ToArray();
}
else
{
FieldsMappedToHints[field] = hints;
}
}
public bool LogAutofillView { get; set; }
private bool IsEditText(FieldT f)
{
return (f.ClassName == "android.widget.EditText"
|| f.ClassName == "android.widget.AutoCompleteTextView"
|| f.HtmlInfoTag == "input");
}
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password", "passwort", "passwordAuto", "pswd" };
private static bool HasPasswordHint(InputField f)
{
return IsAny(f.IdEntry, _passwordHints) ||
IsAny(f.Hint, _passwordHints);
}
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email", "e-mail", "username", "user id" };
private static bool HasUsernameHint(InputField f)
{
return IsAny(f.IdEntry?.ToLower(), _usernameHints) ||
IsAny(f.Hint?.ToLower(), _usernameHints);
}
private static bool IsAny(string? value, IEnumerable<string> terms)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
var lowerValue = value.ToLowerInvariant();
return terms.Any(t => lowerValue == t);
}
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
{
if (!InputTypes.MaskClass.HasFlag(inputTypeClass))
throw new Exception("invalid inputTypeClass");
return (((int)inputType) & (int)InputTypes.MaskClass) == (int)(inputTypeClass);
}
private static bool IsInputTypeVariation(InputTypes inputType, InputTypes inputTypeVariation)
{
if (!InputTypes.MaskVariation.HasFlag(inputTypeVariation))
throw new Exception("invalid inputTypeVariation");
return (((int)inputType) & (int)InputTypes.MaskVariation) == (int)(inputTypeVariation);
}
private static bool IsPassword(InputField f)
{
InputTypes inputType = f.InputType;
return
(!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) &&
(!f.Hint?.ToLowerInvariant().Contains("search") ?? true) &&
(
(IsInputTypeClass(inputType, InputTypes.ClassText)
&&
(
IsInputTypeVariation(inputType, InputTypes.TextVariationPassword)
|| IsInputTypeVariation(inputType, InputTypes.TextVariationVisiblePassword)
|| IsInputTypeVariation(inputType, InputTypes.TextVariationWebPassword)
)
)
|| (f.AutofillHints != null && f.AutofillHints.First() == "passwordAuto")
|| (f.HtmlInfoTypeAttribute == "password")
);
}
}
}

View File

@@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

View File

@@ -1,135 +0,0 @@
using Kp2aAutofillParser;
using Newtonsoft.Json;
using System.IO;
using System.Reflection;
using Xunit.Abstractions;
namespace Kp2aAutofillParserTest
{
public class AutofillTest
{
private readonly ITestOutputHelper _testOutputHelper;
public AutofillTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
class TestInputField: InputField
{
public string[] ExpectedAssignedHints { get; set; }
}
[Fact]
public void TestNotFocusedPasswordAutoIsNotFilled()
{
var resourceName = "Kp2aAutofillParserTest.com-servicenet-mobile-no-focus.json";
RunTestFromAutofillInput(resourceName, "com.servicenet.mobile");
}
[Fact]
public void TestFocusedPasswordAutoIsFilled()
{
var resourceName = "Kp2aAutofillParserTest.com-servicenet-mobile-focused.json";
RunTestFromAutofillInput(resourceName, "com.servicenet.mobile" );
}
[Fact]
public void TestMulitpleUnfocusedLoginsIsFilled()
{
var resourceName = "Kp2aAutofillParserTest.firefox-amazon-it.json";
RunTestFromAutofillInput(resourceName, "org.mozilla.firefox", "www.amazon.it");
}
[Fact]
public void CanDetectFieldsWithoutAutofillHints()
{
var resourceName = "Kp2aAutofillParserTest.chrome-android10-amazon-it.json";
RunTestFromAutofillInput(resourceName, "com.android.chrome", "www.amazon.it");
}
[Fact]
public void DetectsUsernameFieldDespitePasswordAutoHint()
{
var resourceName = "Kp2aAutofillParserTest.com-ifs-banking-fiid3364-android13.json";
RunTestFromAutofillInput(resourceName, "com.ifs.banking.fiid3364", null);
}
[Fact]
public void DetectsEmailAutofillHint()
{
var resourceName = "Kp2aAutofillParserTest.com-expressvpn-vpn-android13.json";
RunTestFromAutofillInput(resourceName, "com.expressvpn.vpn", null);
}
private void RunTestFromAutofillInput(string resourceName, string expectedPackageName = null, string expectedWebDomain = null)
{
var assembly = Assembly.GetExecutingAssembly();
string input;
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
input = reader.ReadToEnd();
}
AutofillView<TestInputField>? autofillView =
JsonConvert.DeserializeObject<AutofillView<TestInputField>>(input);
StructureParserBase<TestInputField> parser =
new StructureParserBase<TestInputField>(new TestLogger(), new TestDalSourceTrustAll());
var result = parser.ParseForFill(false, autofillView);
if (expectedPackageName != null)
Assert.Equal(expectedPackageName, result.PackageName);
if (expectedWebDomain != null)
Assert.Equal(expectedWebDomain, result.WebDomain);
foreach (var field in autofillView.InputFields)
{
string[] expectedHints = field.ExpectedAssignedHints;
if (expectedHints == null)
expectedHints = new string[0];
string[] actualHints;
parser.FieldsMappedToHints.TryGetValue(field, out actualHints);
if (actualHints == null)
actualHints = new string[0];
if (actualHints.Any() || expectedHints.Any())
{
_testOutputHelper.WriteLine($"field = {field.IdEntry} {field.Hint} {string.Join(",", field.AutofillHints ?? new string[]{})}");
_testOutputHelper.WriteLine("actual Hints = " + string.Join(", ", actualHints));
_testOutputHelper.WriteLine("expected Hints = " + string.Join(", ", expectedHints));
}
Assert.Equal(expectedHints.Length, actualHints.Length);
Assert.Equal(expectedHints.OrderBy(x => x), actualHints.OrderBy(x => x));
}
}
}
public class TestDalSourceTrustAll : IKp2aDigitalAssetLinksDataSource
{
public bool IsTrustedApp(string packageName)
{
return true;
}
public bool IsTrustedLink(string domain, string targetPackage)
{
return true;
}
public bool IsEnabled()
{
return true;
}
}
public class TestLogger : ILogger
{
public void Log(string x)
{
Console.WriteLine(x);
}
}
}

View File

@@ -1,58 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="chrome-android10-amazon-it.json" />
<None Remove="com-expressvpn-vpn-android13.json" />
<None Remove="com-ifs-banking-fiid3364-android13.json" />
<None Remove="com-servicenet-mobile-focused.json" />
<None Remove="com-servicenet-mobile-no-focus.json" />
<None Remove="firefox-amazon-it.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Kp2aAutofillParser\Kp2aAutofillParser.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="chrome-android10-amazon-it.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="com-expressvpn-vpn-android13.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="com-ifs-banking-fiid3364-android13.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="firefox-amazon-it.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="com-servicenet-mobile-focused.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="com-servicenet-mobile-no-focus.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1 +0,0 @@
global using Xunit;

File diff suppressed because it is too large Load Diff

View File

@@ -1,226 +0,0 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_root",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_mode_bar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "layout",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "textView2",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "emailLayout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "email",
"Hint": "E-Mail",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"email"
],
"IsFocused": true,
"InputType": 33,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "emailAddress" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "passwordLayout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password",
"Hint": "Passwort",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"password",
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [
"password",
"passwordAuto"
]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "textinput_suffix_text",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgotPassword",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "amazonInfo",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "signInButtonBarrier",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "signIn",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "newUser",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "focusThief",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "activatingContainer",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.expressvpn.vpn",
"WebDomain": null
}

View File

@@ -1,322 +0,0 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_root",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_mode_bar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "loginParent",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "rooted_device_error_screen",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "scroll",
"Hint": null,
"ClassName": "android.widget.ScrollView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_box_layout",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "loginFragment_container_view",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_box",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "Edt_UserId",
"Hint": "User ID",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": true,
"InputType": 145,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "username" ]
},
{
"IdEntry": "login_save_userid_switch",
"Hint": null,
"ClassName": "android.widget.CompoundButton",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "Edt_Password_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "Edt_Password",
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "textinput_prefix_text",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "textinput_suffix_text",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "textinput_placeholder",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgot_login_btn",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "Btn_Login",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_fab_fragment_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "biometric_fragment_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "biometricLayout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_menu_container",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "deposit_insurance_systems_textview",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_menu",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_menu_item_border_right",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "login_menu_item_border_left",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "sign_up_link",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "locations_link",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "more_link",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.ifs.banking.fiid3364",
"WebDomain": null
}

View File

@@ -1,121 +0,0 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_root",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_mode_bar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "content",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username",
"Hint": "Username",
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": true,
"InputType": 97,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "username" ]
},
{
"IdEntry": "password_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password",
"Hint": "Password",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": "login_button",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "progressBar",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgot_password",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.servicenet.mobile",
"WebDomain": null
}

View File

@@ -1,119 +0,0 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_root",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_mode_bar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "content",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username",
"Hint": "Username",
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 97,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password",
"Hint": "Password",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
},
{
"IdEntry": "login_button",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "progressBar",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgot_password",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.servicenet.mobile",
"WebDomain": null
}

View File

@@ -1,469 +0,0 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_root",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_mode_bar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "rootContainer",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "navigationToolbarStub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "gestureLayout",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "browserWindow",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "browserLayout",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "swipeRefresh",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "engineView",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": "",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "form",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"password"
],
"IsFocused": false,
"InputType": 225,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "password",
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"emailAddress"
],
"IsFocused": false,
"InputType": 33,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "email",
"ExpectedAssignedHints": [ "emailAddress" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "checkbox"
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "submit"
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "form",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"password"
],
"IsFocused": false,
"InputType": 225,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "password",
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"emailAddress"
],
"IsFocused": false,
"InputType": 33,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "email",
"ExpectedAssignedHints": [ "emailAddress" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "submit"
},
{
"IdEntry": "stubFindInPage",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "viewDynamicDownloadDialog",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "crash_reporter_view",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "toolbar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_navigation_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_origin_view",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_title_view",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_url_view",
"Hint": "Suche oder Adresse",
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_page_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_browser_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "counter_root",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "counter_text",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_menu",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_progress",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_container",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_actions_start",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_url_view",
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 17,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_actions_end",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "readerViewControlsBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "addressSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "creditCardSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "loginSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tabPreview",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "org.mozilla.firefox",
"WebDomain": "www.amazon.it"
}

View File

@@ -11,7 +11,7 @@ using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using KeePassLib.Interfaces;
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
using Keepass2android.Javafilestorage;
#endif
@@ -29,7 +29,7 @@ namespace keepass2android
}
/// <summary>
/// <summary>
/// Interface through which Activities and the logic layer can access some app specific functionalities and Application static data
/// </summary>
/// This also contains methods which are UI specific and should be replacable for testing.
@@ -38,7 +38,7 @@ namespace keepass2android
/// <summary>
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
/// </summary>
void Lock(bool allowQuickUnlock, bool lockWasTriggeredByTimeout);
void Lock(bool allowQuickUnlock);
/// <summary>
@@ -119,11 +119,11 @@ namespace keepass2android
/// </summary>
IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache);
void TriggerReload(Context context, Action<bool> actionOnResult /*if not null, called when the user selected yes (true) or no (false)*/);
void TriggerReload(Context context);
bool CheckForDuplicateUuids { get; }
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
ICertificateErrorHandler CertificateErrorHandler { get; }

View File

@@ -249,11 +249,28 @@ namespace keepass2android.Io
reason.Result = UiStringKey.ReadOnlyReason_PreKitKat;
return true;
}
//in previous implementations, we were checking for FLAG_SUPPORTS_WRITE in the document flags,
//but it seems like this is very poorly supported, e.g. Dropbox and OneDrive return !FLAG_SUPPORTS_WRITE
//even though writing work.
return false;
//KitKat or later...
var uri = Android.Net.Uri.Parse(ioc.Path);
cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null);
if (cursor != null && cursor.MoveToFirst())
{
int column = cursor.GetColumnIndex(DocumentsContract.Document.ColumnFlags);
if (column < 0)
return false; //seems like this is not supported. See below for reasoning to return false.
int flags = cursor.GetInt(column);
Kp2aLog.Log("File flags: " + flags);
if ((flags & (long) DocumentContractFlags.SupportsWrite) == 0)
{
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyFlag;
return true;
}
else return false;
}
else throw new Exception("couldn't move to first result element: " + (cursor == null) + uri.ToString());
}
catch (Exception e)
{
@@ -316,13 +333,12 @@ namespace keepass2android.Io
public void CommitWrite()
{
ParcelFileDescriptor fileDescriptor = _ctx.ContentResolver.OpenFileDescriptor(Android.Net.Uri.Parse(_path), "rwt");
ParcelFileDescriptor fileDescriptor = _ctx.ContentResolver.OpenFileDescriptor(Android.Net.Uri.Parse(_path), "w");
using (var outputStream = new FileOutputStream(fileDescriptor.FileDescriptor))
{
byte[] data = _memoryStream.ToArray();
outputStream.Write(data);
outputStream.Write(data, 0, data.Length);
outputStream.Close();
}
fileDescriptor.Close();

View File

@@ -246,7 +246,7 @@ namespace keepass2android.Io
else
{
Intent intent = new Intent();
activity.IocToIntent(intent, new IOConnectionInfo() { Path = protocolId+"://", });
activity.IocToIntent(intent, new IOConnectionInfo() { Path = protocolId+"://"});
activity.OnImmediateResult(requestCode, (int) FileStorageResults.FileChooserPrepared, intent);
}
}

View File

@@ -120,14 +120,10 @@ namespace keepass2android.Io
public bool IsCached(IOConnectionInfo ioc)
{
bool result = File.Exists(CachedFilePath(ioc))
return File.Exists(CachedFilePath(ioc))
&& File.Exists(VersionFilePath(ioc))
&& File.Exists(BaseVersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " isCached = " + result);
return result;
}
}
public void Delete(IOConnectionInfo ioc)
{
@@ -597,15 +593,11 @@ namespace keepass2android.Io
public string GetBaseVersionHash(IOConnectionInfo ioc)
{
string hash = File.ReadAllText(BaseVersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " baseVersionHash = " + hash);
return hash;
}
return File.ReadAllText(BaseVersionFilePath(ioc));
}
public string GetLocalVersionHash(IOConnectionInfo ioc)
{
string hash = File.ReadAllText(VersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " localVersionHash = " + hash);
return hash;
return File.ReadAllText(VersionFilePath(ioc));
}
public bool HasLocalChanges(IOConnectionInfo ioc)
{

View File

@@ -17,7 +17,7 @@ namespace keepass2android.Io
public class GoogleDriveFileStorage : JavaFileStorage
{
public GoogleDriveFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.GoogleDriveFullFileStorage(), app)
base(new Keepass2android.Javafilestorage.GoogleDriveFileStorage(), app)
{
}
@@ -27,19 +27,5 @@ namespace keepass2android.Io
get { return false; }
}
}
public class GoogleDriveAppDataFileStorage : JavaFileStorage
{
public GoogleDriveAppDataFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.GoogleDriveAppDataFileStorage(), app)
{
}
public override bool UserShouldBackup
{
get { return false; }
}
}
}
#endif

View File

@@ -1,506 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Util;
using CG.Web.MegaApiClient;
using Group.Pals.Android.Lib.UI.Filechooser.Utils;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace keepass2android.Io
{
public class MegaFileStorage : IFileStorage
{
private readonly Context _appContext;
public const string ProtocolId = "mega";
private const string PreferenceKey = "KP2A-Mega-Accounts";
public MegaFileStorage(Context appContext)
{
_appContext = appContext;
}
//we don't want to store passwords in plain text, encrypt them with this key at least:
public static readonly byte[] EncryptionKey = new byte[] { 86,239,128,218,160,22,245,114,193,92,151,10,134,104,121,170,
183,110,60,38,179,181,24,206,169,43,125,193,142,156,47,45};
public class AccountSettings
{
public Dictionary<string, string> PasswordByUsername { get; set; } = new Dictionary<string, string>();
public static byte[] exclusiveOR(byte[] arr1, byte[] arr2)
{
byte[] result = new byte[arr1.Length];
for (int i = 0; i < arr1.Length; ++i)
result[i] = (byte)(arr1[i] ^ arr2[i % arr2.Length]);
return result;
}
static string Encrypt(string s)
{
var plainTextBytes = exclusiveOR(System.Text.Encoding.UTF8.GetBytes(s), EncryptionKey);
return System.Convert.ToBase64String(plainTextBytes);
}
static string Decrypt(string s)
{
var base64EncodedBytes = System.Convert.FromBase64String(s);
return System.Text.Encoding.UTF8.GetString(exclusiveOR(base64EncodedBytes, EncryptionKey));
}
public string Serialize()
{
Dictionary<string, string> encryptedPasswordByUsername = PasswordByUsername
.Select(kvp => new KeyValuePair<string, string>(kvp.Key, Encrypt(kvp.Value)))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
return Newtonsoft.Json.JsonConvert.SerializeObject(encryptedPasswordByUsername);
}
public void Deserialize(string data)
{
if (string.IsNullOrEmpty(data))
{
PasswordByUsername = new Dictionary<string, string>();
return;
}
Dictionary<string, string> encryptedPasswordByUsername =
Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
PasswordByUsername = encryptedPasswordByUsername
.Select(kvp => new KeyValuePair<string, string>(kvp.Key, Decrypt(kvp.Value)))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
public IEnumerable<string> SupportedProtocols
{
get { yield return ProtocolId; }
}
public bool UserShouldBackup
{
get { return false; }
}
class MegaFileStorageWriteTransaction : IWriteTransaction
{
public bool UseFileTransaction { get; }
private readonly string _path;
private readonly MegaFileStorage _filestorage;
private MemoryStream _memoryStream;
public MegaFileStorageWriteTransaction(string path, MegaFileStorage filestorage, bool useFileTransaction)
{
UseFileTransaction = useFileTransaction;
_path = path;
_filestorage = filestorage;
}
public void Dispose()
{
_memoryStream.Dispose();
}
public Stream OpenFile()
{
_memoryStream = new MemoryStream();
return _memoryStream;
}
public void CommitWrite()
{
_filestorage.UploadFile(_path, new MemoryStream(_memoryStream.ToArray()), UseFileTransaction);
}
}
private void UploadFile(string path, MemoryStream memoryStream, bool useTransaction)
{
var accountData = GetAccountData(path);
if (accountData.TryGetNode(path, out var node))
{
if (useTransaction)
{
string temporaryName = node.Name + "." + new Guid().ToString() + ".tmp";
var newNode = accountData.Client.Upload(memoryStream, temporaryName, accountData.GetParentNode(node));
accountData.Client.Delete(node);
newNode = accountData.Client.Rename(newNode, node.Name);
accountData._nodes.Remove(node);
accountData._nodes.Add(newNode);
}
else
{
var newNode = accountData.Client.Upload(memoryStream, node.Name, accountData.GetParentNode(node));
//we now have two nodes with the same name. Delete the old one:
accountData.Client.Delete(node);
accountData._nodes.Remove(node);
accountData._nodes.Add(newNode);
}
}
else
{
//file did not exist yet
string parentPath = GetParentPath(new IOConnectionInfo() { Path = path }).Path;
string name = path.Substring(parentPath.Length + 1);
var newNode = accountData.Client.Upload(memoryStream, name, accountData.GetNode(parentPath));
accountData._nodes.Add(newNode);
}
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode,
string protocolId)
{
activity.PerformManualFileSelect(isForSave, requestCode, protocolId);
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
}
public void OnResume(IFileStorageSetupActivity activity)
{
}
public void OnStart(IFileStorageSetupActivity activity)
{
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
return new MegaFileStorageWriteTransaction(ioc.Path, this, useFileTransaction);
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
return UrlUtil.StripExtension(
UrlUtil.GetFileName(ioc.Path));
}
public string GetFileExtension(IOConnectionInfo ioc)
{
return UrlUtil.GetExtension(ioc.Path);
}
public string CreateFilePath(string parent, string newFilename)
{
if (!parent.EndsWith("/"))
parent += "/";
return parent + newFilename;
}
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{
return false;
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
return true;
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
IOConnectionInfo res = folderPath.CloneDeep();
if (!res.Path.EndsWith("/"))
res.Path += "/";
res.Path += filename;
return res;
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
return IoUtil.GetParentPath(ioc);
}
public string GetDisplayName(IOConnectionInfo ioc)
{
return ioc.GetDisplayName();
}
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
//nothing to do
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
}
public string IocToPath(IOConnectionInfo ioc)
{
return ioc.Path;
}
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return false;
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
var accountData = GetAccountData(ioc);
return MakeFileDescription(accountData, accountData.GetNode(ioc));
}
class AccountData
{
public string Account { get; set; }
public IMegaApiClient Client { get; set; }
public void RefreshMetadata()
{
//make sure we refresh meta data after one minute:
if (DateTime.Now.Subtract(_nodesLoadingTime).TotalMinutes > 1.0)
{
_nodes.Clear();
EnsureMetadataLoaded();
}
}
public List<INode> _nodes = new List<INode>();
private DateTime _nodesLoadingTime;
private INode _rootNode;
public INode GetNode(IOConnectionInfo ioc)
{
return GetNode(ioc.Path);
}
public bool TryGetNode(string path, out INode node)
{
try
{
node = GetNode(path);
return true;
}
catch (Exception e)
{
node = null;
return false;
}
}
public INode GetNode(string path)
{
EnsureMetadataLoaded();
if (!path.StartsWith(ProtocolId + "://"))
throw new Exception("Invalid Mega URL: " + path);
path = path.Substring(ProtocolId.Length + 3);
var parts = path.Split('/');
if (parts.Length < 1 || parts[0] == "")
throw new Exception("Invalid Mega URL: " + path);
INode node = _rootNode;
for (int i = 1; i < parts.Length; i++)
{
if (parts[i] == "")
continue;
var matchingChildren = _nodes.Where(n => n.ParentId == node.Id && n.Name == parts[i]).ToList();
if (matchingChildren.Count == 0)
throw new FileNotFoundException("Did not find " + path);
if (matchingChildren.Count > 1)
throw new Java.IO.FileNotFoundException(
$"Found more than one child with name {parts[i]} while trying to get node for {path}");
node = matchingChildren.Single();
}
return node;
}
private void EnsureMetadataLoaded()
{
if (_nodes.Any() == false)
{
_nodes = Client.GetNodes().ToList();
_rootNode = _nodes.Single(n => n.Type == NodeType.Root);
_nodesLoadingTime = DateTime.Now;
}
}
public INode GetParentNode(INode node)
{
return _nodes.Single(n => n.Id == node.ParentId);
}
internal void InvalidateMetaData()
{
_nodes.Clear();
}
public IEnumerable<INode> GetChildNodes(INode node)
{
EnsureMetadataLoaded();
return _nodes.Where(n => n.ParentId == node.Id);
}
public string GetPath(INode node)
{
if (node.Type == NodeType.Root)
return ProtocolId + "://" + this.Account;
var parent = _nodes.Single(n => n.Id == node.ParentId);
return GetPath(parent) + "/" + node.Name;
}
}
readonly Dictionary<string /*account*/, AccountData> _allAccountData = new Dictionary<string, AccountData>();
public string GetAccount(IOConnectionInfo ioc)
{
return GetAccount(ioc.Path);
}
public static string GetAccount(string path)
{
if (!path.StartsWith(ProtocolId + "://"))
throw new Exception("Invalid Mega URL: " + path);
path = path.Substring(ProtocolId.Length + 3);
var parts = path.Split('/');
if (parts.Length < 1 || parts[0] == "")
throw new Exception("Invalid Mega URL: " + path);
return parts[0];
}
private AccountData GetAccountData(IOConnectionInfo ioc)
{
return GetAccountData(ioc.Path);
}
public static AccountSettings GetAccountSettings(Context ctx)
{
string accountSettingsString = PreferenceManager.GetDefaultSharedPreferences(ctx).GetString(PreferenceKey, null);
AccountSettings settings = new AccountSettings();
settings.Deserialize(accountSettingsString);
return settings;
}
public static void UpdateAccountSettings(AccountSettings settings, Context ctx)
{
PreferenceManager.GetDefaultSharedPreferences(ctx).Edit().PutString(PreferenceKey, settings.Serialize())
.Commit();
}
private AccountData GetAccountData(string path)
{
string account = GetAccount(path);
if (_allAccountData.TryGetValue(account, out var accountData))
{
return accountData;
}
AccountData newAccountData = new AccountData()
{
Account = account,
Client = new MegaApiClient()
};
var settings = GetAccountSettings(_appContext);
if (!settings.PasswordByUsername.TryGetValue(account, out string password))
{
throw new Exception("No account configured with username = " + account);
}
try
{
newAccountData.Client.Login(account, password);
}
catch (CG.Web.MegaApiClient.ApiException e)
{
if (e.ApiResultCode == CG.Web.MegaApiClient.ApiResultCode.ResourceNotExists)
{
throw new Exception("Failed to login to MEGA account. Please check username and password!");
}
}
_allAccountData[account] = newAccountData;
return newAccountData;
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
AccountData accountData = GetAccountData(ioc);
accountData.RefreshMetadata();
return accountData.GetChildNodes(accountData.GetNode(ioc)).Select(n => MakeFileDescription(accountData, n));
}
private FileDescription MakeFileDescription(AccountData account, INode n)
{
return new FileDescription()
{
CanRead = true,
CanWrite = true,
DisplayName = n.Name ?? (n.Type == NodeType.Root ? "root" : ""),
IsDirectory = n.Type != NodeType.File,
LastModified = n.ModificationDate ?? n.CreationDate ?? DateTime.MinValue,
Path = account.GetPath(n),
SizeInBytes = n.Size
};
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
var accountData = GetAccountData(ioc);
var newNode = accountData.Client.CreateFolder(newDirName, accountData.GetNode(ioc));
accountData._nodes.Add(newNode);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return false;
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
var accountData = GetAccountData(ioc);
return accountData.Client.Download(accountData.GetNode(ioc));
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
return null;
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
return false;
}
public void Delete(IOConnectionInfo ioc)
{
var accountData = GetAccountData(ioc);
accountData.Client.Delete(accountData.GetNode(ioc));
}
}
}

View File

@@ -3,12 +3,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.FtpClient;
using System.Reflection;
using System.Threading;
using Android.Content;
using Android.OS;
using Android.Preferences;
using FluentFTP;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
@@ -17,6 +17,73 @@ namespace keepass2android.Io
{
public class NetFtpFileStorage: IFileStorage
{
class RetryConnectFtpClient : FtpClient
{
protected override FtpClient CloneConnection()
{
RetryConnectFtpClient conn = new RetryConnectFtpClient();
conn.m_isClone = true;
foreach (PropertyInfo prop in GetType().GetProperties())
{
object[] attributes = prop.GetCustomAttributes(typeof(FtpControlConnectionClone), true);
if (attributes != null && attributes.Length > 0)
{
prop.SetValue(conn, prop.GetValue(this, null), null);
}
}
// always accept certficate no matter what because if code execution ever
// gets here it means the certificate on the control connection object being
// cloned was already accepted.
conn.ValidateCertificate += new FtpSslValidation(
delegate(FtpClient obj, FtpSslValidationEventArgs e)
{
e.Accept = true;
});
return conn;
}
private static T DoInRetryLoop<T>(Func<T> func)
{
double timeout = 30.0;
double timePerRequest = 1.0;
var startTime = DateTime.Now;
while (true)
{
var attemptStartTime = DateTime.Now;
try
{
return func();
}
catch (System.Net.Sockets.SocketException e)
{
if ((e.ErrorCode != 10061) || (DateTime.Now > startTime.AddSeconds(timeout)))
{
throw;
}
double secondsSinceAttemptStart = (DateTime.Now - attemptStartTime).TotalSeconds;
if (secondsSinceAttemptStart < timePerRequest)
{
Thread.Sleep(TimeSpan.FromSeconds(timePerRequest - secondsSinceAttemptStart));
}
}
}
}
public override void Connect()
{
DoInRetryLoop(() =>
{
base.Connect();
return true;
}
);
}
}
public struct ConnectionSettings
{
public FtpEncryptionMode EncryptionMode {get; set; }
@@ -80,8 +147,9 @@ namespace keepass2android.Io
public NetFtpFileStorage(Context context, ICertificateValidationHandler app)
{
_app = app;
_app = app;
traceStream = new MemoryStream();
FtpTrace.AddListener(new System.Diagnostics.TextWriterTraceListener(traceStream));
}
@@ -104,9 +172,9 @@ namespace keepass2android.Io
{
using (FtpClient client = GetClient(ioc))
{
string localPath = IocToLocalPath(ioc);
string localPath = IocToUri(ioc).PathAndQuery;
if (client.DirectoryExists(localPath))
client.DeleteDirectory(localPath);
client.DeleteDirectory(localPath, true);
else
client.DeleteFile(localPath);
}
@@ -137,8 +205,7 @@ namespace keepass2android.Io
{
var settings = ConnectionSettings.FromIoc(ioc);
FtpClient client = new FtpClient();
client.RetryAttempts = 3;
FtpClient client = new RetryConnectFtpClient();
if ((settings.Username.Length > 0) || (settings.Password.Length > 0))
client.Credentials = new NetworkCredential(settings.Username, settings.Password);
else
@@ -183,22 +250,20 @@ namespace keepass2android.Io
path = path.Substring(settings.Length + 1);
}
Kp2aLog.Log("FTP: IocToUri out = " + scheme + "://" + path);
return new Uri(scheme + "://" + path);
return new Uri(scheme + "://" + path);
}
private string IocPathFromUri(IOConnectionInfo baseIoc, string uri)
private string IocPathFromUri(IOConnectionInfo baseIoc, Uri uri)
{
string basePath = baseIoc.Path;
string basePath = baseIoc.Path;
int schemeLength = basePath.IndexOf("://", StringComparison.Ordinal);
string scheme = basePath.Substring(0, schemeLength);
basePath = basePath.Substring(schemeLength + 3);
string baseSettings = basePath.Substring(0, basePath.IndexOf(ConnectionSettings.SettingsPostFix, StringComparison.Ordinal));
basePath = basePath.Substring(baseSettings.Length+1);
string baseHost = basePath.Substring(0, basePath.IndexOf("/", StringComparison.Ordinal));
string result = scheme + "://" + baseSettings + ConnectionSettings.SettingsPostFix + baseHost + uri; //TODO does this contain Query?
return result;
}
return scheme + "://" + baseSettings + ConnectionSettings.SettingsPostFix + baseHost + uri.AbsolutePath; //TODO does this contain Query?
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
@@ -216,12 +281,9 @@ namespace keepass2android.Io
try
{
using (var cl = GetClient(ioc))
{
var memStream = new MemoryStream();
cl.OpenRead(IocToLocalPath(ioc), FtpDataType.Binary, 0).CopyTo(memStream);
memStream.Seek(0, SeekOrigin.Begin);
return memStream;
}
{
return cl.OpenRead(IocToUri(ioc).PathAndQuery, FtpDataType.Binary, 0);
}
}
catch (FtpCommandException ex)
{
@@ -268,7 +330,7 @@ namespace keepass2android.Io
{
using (var client = GetClient(ioc))
{
client.CreateDirectory(IocToLocalPath(GetFilePath(ioc, newDirName)));
client.CreateDirectory(IocToUri(GetFilePath(ioc, newDirName)).PathAndQuery);
}
}
catch (FtpCommandException ex)
@@ -277,19 +339,14 @@ namespace keepass2android.Io
}
}
public static string IocToLocalPath(IOConnectionInfo ioc)
{
return WebUtility.UrlDecode(IocToUri(ioc).PathAndQuery);
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
try
{
using (var client = GetClient(ioc))
using (var client = GetClient(ioc))
{
List<FileDescription> files = new List<FileDescription>();
foreach (FtpListItem item in client.GetListing(IocToLocalPath(ioc),
foreach (FtpListItem item in client.GetListing(IocToUri(ioc).PathAndQuery,
FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
{
@@ -303,7 +360,7 @@ namespace keepass2android.Io
DisplayName = item.Name,
IsDirectory = true,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName)
Path = IocPathFromUri(ioc, new Uri(item.FullName))
});
break;
case FtpFileSystemObjectType.File:
@@ -314,7 +371,7 @@ namespace keepass2android.Io
DisplayName = item.Name,
IsDirectory = false,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName),
Path = IocPathFromUri(ioc, new Uri(item.FullName)),
SizeInBytes = item.Size
});
break;
@@ -339,10 +396,10 @@ namespace keepass2android.Io
//is it very inefficient to connect for each description?
using (FtpClient client = GetClient(ioc))
{
string path = IocToLocalPath(ioc);
{
var uri = IocToUri(ioc);
string path = uri.PathAndQuery;
if (!client.FileExists(path) && (!client.DirectoryExists(path)))
throw new FileNotFoundException();
var fileDesc = new FileDescription()
@@ -454,7 +511,7 @@ namespace keepass2android.Io
{
using (var client = GetClient(ioc))
{
return client.OpenWrite(IocToLocalPath(ioc));
return client.OpenWrite(IocToUri(ioc).PathAndQuery);
}
}
@@ -523,7 +580,7 @@ namespace keepass2android.Io
{
_client = _fileStorage.GetClient(_ioc, false);
_stream = _client.OpenWrite(NetFtpFileStorage.IocToLocalPath(_iocTemp));
_stream = _client.OpenWrite(NetFtpFileStorage.IocToUri(_iocTemp).PathAndQuery);
return _stream;
}
catch (FtpCommandException ex)
@@ -538,11 +595,22 @@ namespace keepass2android.Io
{
Android.Util.Log.Debug("NETFTP","connected: " + _client.IsConnected.ToString());
_stream.Close();
_stream.Dispose();
_client.GetReply();
Android.Util.Log.Debug("NETFTP", "connected: " + _client.IsConnected.ToString());
_client.MoveFile(NetFtpFileStorage.IocToLocalPath(_iocTemp),
NetFtpFileStorage.IocToLocalPath(_ioc));
//make sure target file does not exist:
//try
{
if (_client.FileExists(NetFtpFileStorage.IocToUri(_ioc).PathAndQuery))
_client.DeleteFile(NetFtpFileStorage.IocToUri(_ioc).PathAndQuery);
}
//catch (FtpCommandException)
{
//TODO get a new clien? might be stale
}
_client.Rename(NetFtpFileStorage.IocToUri(_iocTemp).PathAndQuery,
NetFtpFileStorage.IocToUri(_ioc).PathAndQuery);
}
catch (FtpCommandException ex)

View File

@@ -204,7 +204,6 @@ namespace keepass2android.Io
private readonly string _specialFolder;
public IGraphServiceClient client;
public OneDrive2ItemLocation<OneDrive2PrefixContainerType> itemLocation;
public bool verbose;
public PathItemBuilder(string specialFolder)
{
@@ -214,7 +213,6 @@ namespace keepass2android.Io
public IDriveItemRequestBuilder getPathItem()
{
Kp2aLog.Log("getPathItem for " + itemLocation.ToString());
IDriveItemRequestBuilder pathItem;
if (!hasShare())
{
@@ -222,33 +220,20 @@ namespace keepass2android.Io
}
if ("me".Equals(itemLocation.Share.Id))
{
if (verbose) Kp2aLog.Log("Path share is me");
if (_specialFolder == null)
{
if (verbose) Kp2aLog.Log("No special folder. Use drive root.");
pathItem = client.Me.Drive.Root;
}
else
{
if (verbose) Kp2aLog.Log("Special folder = " + _specialFolder);
pathItem = client.Me.Drive.Special[_specialFolder];
}
if (itemLocation.LocalPath.Any())
{
if (verbose) Kp2aLog.Log("LocalPath = " + itemLocation.LocalPathString);
pathItem = pathItem.ItemWithPath(itemLocation.LocalPathString);
}
}
else
{
if (verbose) Kp2aLog.Log("Path share is not me");
if (!itemLocation.LocalPath.Any())
{
String webUrl = itemLocation.Share.WebUrl;
if (verbose) Kp2aLog.Log("Share WebUrl = " + webUrl);
var encodedShareId = CalculateEncodedShareId(webUrl);
return client.Shares[encodedShareId].Root;
}
@@ -264,7 +249,6 @@ namespace keepass2android.Io
Android.Util.Log.Debug("KP2A", "encodedShareId = " + encodedShareId);
pathItem = client.Shares[encodedShareId].Root;
*/
if (verbose) Kp2aLog.Log("Using driveId=" + itemLocation.DriveId + " and item id=" + itemLocation.LocalPath.Last().Id);
return client.Drives[itemLocation.DriveId].Items[itemLocation.LocalPath.Last().Id];
}
@@ -322,37 +306,20 @@ namespace keepass2android.Io
private async Task<IGraphServiceClient> TryGetMsGraphClient(String path, bool tryConnect)
{
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id;
logDebug("TryGetMsGraphClient for " + userId);
if (mClientByUser.ContainsKey(userId))
{
logDebug("TryGetMsGraphClient found user " + userId);
GraphServiceClientWithState clientWithState = mClientByUser[userId];
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) ||
(clientWithState.Client == null)))
{
logDebug("TryGetMsGraphClient returning client");
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) || (clientWithState.Client == null)))
return clientWithState.Client;
}
else
{
logDebug("not returning client because " + clientWithState.RequiresUserInteraction + " " +
(clientWithState.TokenExpiryDate < DateTime.Now) + " " + (clientWithState.Client == null));
}
}
if (tryConnect)
{
logDebug("trying to connect...");
if (await TryLoginSilent(path) != null)
{
logDebug("trying to connect ok");
return mClientByUser[userId].Client;
}
logDebug("trying to connect failed");
}
logDebug("TryGetMsGraphClient for " + userId + " returns null");
return null;
}
@@ -384,7 +351,7 @@ namespace keepass2android.Io
if (authenticationResult.Account == null)
throw new Exception("authenticationResult.Account == null!");
mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = clientWithState;
logDebug("buildClient ok.");
return clientWithState.Client;
}
@@ -392,9 +359,7 @@ namespace keepass2android.Io
private void logDebug(string str)
{
#if DEBUG
Log.Debug("KP2A", "OneDrive2: " + str);
#endif
Log.Debug("KP2A", str);
}
@@ -550,61 +515,17 @@ namespace keepass2android.Io
Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
//for small files <2MB use the direct upload:
if (stream.Length < 2* 1024 * 1024)
{
return await
pathItemBuilder
.getPathItem()
.Content
.Request()
.PutAsync<DriveItem>(stream);
}
//for larger files use an upload session. This is required for 4MB and beyond, but as the docs are not very clear about this
//limit, let's use it a bit more often to be safe.
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" }
}
};
var uploadSession = await pathItemBuilder
.getPathItem()
.CreateUploadSession(uploadProps)
.Request()
.PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, stream, maxSliceSize);
var uploadResult = await fileUploadTask.UploadAsync();
if (!uploadResult.UploadSucceeded)
{
throw new Exception("Failed to upload data!");
}
return uploadResult.ItemResponse;
return await
pathItemBuilder
.getPathItem()
.Content
.Request()
.PutAsync<DriveItem>(stream);
}).Wait();
}
catch (Exception e)
{
Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
pathItemBuilder.verbose = true;
pathItemBuilder.getPathItem();
}).Wait();
throw convertException(e);
}
}
@@ -877,17 +798,14 @@ namespace keepass2android.Io
public async void OnStart(IFileStorageSetupActivity activity)
{
logDebug("OneDrive2.OnStart");
if (activity.ProcessName.Equals(FileStorageSetupDefs.ProcessNameFileUsageSetup))
activity.State.PutString(FileStorageSetupDefs.ExtraPath, activity.Ioc.Path);
string rootPathForUser = await TryLoginSilent(activity.Ioc.Path);
if (rootPathForUser != null)
{
logDebug("rootPathForUser not null");
FinishActivityWithSuccess(activity, rootPathForUser);
return;
}
logDebug("rootPathForUser null");
try
{
@@ -915,14 +833,13 @@ namespace keepass2android.Io
private async Task<string> TryLoginSilent(string iocPath)
{
logDebug("Login Silent for " + iocPath);
IAccount account = null;
try
{
if (IsConnected(iocPath))
{
logDebug("Login Silent ok, connected");
return iocPath;
}
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(iocPath).User?.Id;
@@ -951,9 +868,7 @@ namespace keepass2android.Io
/*User me = await graphClient.Me.Request().WithForceRefresh(true).GetAsync();
logDebug("received name " + me.DisplayName);*/
var rootFolder = BuildRootPathForUser(authResult);
logDebug("Found RootPath for user");
return rootFolder;
return BuildRootPathForUser(authResult);
}
catch (MsalUiRequiredException ex)
@@ -1113,6 +1028,9 @@ namespace keepass2android.Io
private async Task<string> CreateFilePathAsync(string parent, string newFilename)
{
DriveItem driveItem = new DriveItem();
driveItem.Name = newFilename;
driveItem.File = new File();
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(parent);
//see if such a file exists already:
@@ -1126,11 +1044,9 @@ namespace keepass2android.Io
logDebug("building request for " + pathItemBuilder.itemLocation);
DriveItem res = await pathItemBuilder.getPathItem()
.ItemWithPath(newFilename)
.Content
.Children
.Request()
.PutAsync<DriveItem>(new MemoryStream());
.AddAsync(driveItem);
return pathItemBuilder.itemLocation.BuildLocalChildLocation(res.Name, res.Id, res.ParentReference?.DriveId)
.ToString();

View File

@@ -9,14 +9,14 @@ using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
using Keepass2android.Javafilestorage;
#endif
using KeePassLib.Serialization;
namespace keepass2android.Io
{
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
public class WebDavFileStorage: JavaFileStorage
{
public WebDavFileStorage(IKp2aApp app) : base(new Keepass2android.Javafilestorage.WebDavStorage(app.CertificateErrorHandler), app)

View File

@@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="showFlavor">
<Target Name="showFlavor" AfterTargets="Build">
<Message Importance="high" Text="building flavor $(Flavor)"></Message>
</Target>
<Import Project="../build-properties.props"/>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -14,11 +10,10 @@
<RootNamespace>keepass2android</RootNamespace>
<AssemblyName>Kp2aBusinessLogic</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<Flavor Condition=" '$(Flavor)' != '' ">$(Flavor)</Flavor>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
@@ -27,7 +22,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;_EXCLUDE_TWOFISH;_EXCLUDE_KEYBOARD;_EXCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;_EXCLUDE_KEYBOARD;_EXCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
@@ -36,19 +31,11 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;NoNet;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<JavaMaximumHeapSize>4G</JavaMaximumHeapSize>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
</PropertyGroup>
<Choose>
<When Condition=" '$(Flavor)'=='NoNet' ">
<PropertyGroup>
<DefineConstants>$(DefineConstants);NoNet;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
</PropertyGroup>
</When>
</Choose>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
<OutputPath>bin\ReleaseNoNet\</OutputPath>
<DefineConstants>TRACE;NoNet;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
@@ -69,23 +56,6 @@
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup Condition=" '$(Flavor)'!='NoNet' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Condition=" '$(Flavor)'!='NoNet' ">
<Compile Include="Io\DropboxFileStorage.cs" />
<Compile Include="Io\DropboxFileStorageKeys.cs" />
<Compile Include="Io\GDriveFileStorage.cs" />
<Compile Include="Io\JavaFileStorage.cs" />
<Compile Include="Io\MegaFileStorage.cs" />
<Compile Include="Io\NetFtpFileStorage.cs" />
<Compile Include="Io\OneDrive2FileStorage.cs" />
<Compile Include="Io\OneDrive2PrefixContainer.cs" />
<Compile Include="Io\PCloudFileStorage.cs" />
<Compile Include="Io\SftpFileStorage.cs" />
<Compile Include="Io\OneDriveFileStorage.cs" />
<Compile Include="Io\WebDavFileStorage.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\AddTemplateEntries.cs" />
@@ -112,6 +82,7 @@
<Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" />
<Compile Include="Io\OfflineSwitchableFileStorage.cs" />
<Compile Include="IProgressDialog.cs" />
<Compile Include="PreferenceKey.cs" />
<Compile Include="SelectStorageLocationActivityBase.cs" />
@@ -146,20 +117,6 @@
<Compile Include="Utils\Spr\SprEngine.cs" />
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(Flavor)'!='NoNet' ">
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj">
<Project>{3c0f7fe5-639f-4422-a087-8b26cf862d1b}</Project>
<Name>AndroidFileChooserBinding</Name>
</ProjectReference>
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
<Name>JavaFileStorageBindings</Name>
</ProjectReference>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
<Project>{2db80c77-d46f-4970-b967-e9ffa9b2ac2e}</Project>
<Name>PCloudBindings</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
@@ -169,6 +126,7 @@
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
<Name>KP2AKdbLibraryBinding</Name>
</ProjectReference>
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
<Project>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</Project>
<Name>TwofishCipher</Name>
@@ -180,128 +138,108 @@
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup Condition=" '$(Flavor)'!='NoNet' ">
<PackageReference Include="FluentFTP">
<Version>31.3.1</Version>
</PackageReference>
<PackageReference Include="MegaApiClient">
<Version>1.10.3</Version>
</PackageReference>
<PackageReference Include="Microsoft.Graph">
<Version>1.21.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Graph.Auth">
<Version>1.0.0-preview.7</Version>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.49.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Android.Arch.Core.Common">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Core.Runtime">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Lifecycle.Common">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Lifecycle.LiveData">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Lifecycle.LiveData.Core">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Lifecycle.Runtime">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Lifecycle.ViewModel">
<Version>1.1.1.3</Version>
<Version>1.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Animated.Vector.Drawable">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Annotations">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.AsyncLayoutInflater">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Collections">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Compat">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.CoordinaterLayout">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Core.UI">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Core.Utils">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.CursorAdapter">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.CustomTabs">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.CustomView">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.DocumentFile">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.DrawerLayout">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Fragment">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Interpolator">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Loader">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.LocalBroadcastManager">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Media.Compat">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Print">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.SlidingPaneLayout">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.SwipeRefreshLayout">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.v13">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.v4">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Vector.Drawable">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.VersionedParcelable">
<Version>28.0.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.ViewPager">
<Version>28.0.0.3</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.Preference">
<Version>1.2.0.3</Version>
<Version>28.0.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />

View File

@@ -49,11 +49,6 @@ namespace keepass2android
get { return _activeActivity; }
private set
{
if (_activeActivity != null && _activeActivity != _previouslyActiveActivity)
{
_previouslyActiveActivity = _activeActivity;
}
_activeActivity = value;
if (_task != null)
_task.ActiveActivity = _activeActivity;
@@ -65,18 +60,12 @@ namespace keepass2android
}
}
public Activity PreviouslyActiveActivity
{
get { return _previouslyActiveActivity; }
}
private readonly Handler _handler;
private readonly Handler _handler;
private readonly RunnableOnFinish _task;
private IProgressDialog _progressDialog;
private readonly IKp2aApp _app;
private Thread _thread;
private Activity _activeActivity, _previouslyActiveActivity;
private Activity _activeActivity;
private ProgressDialogStatusLogger _progressDialogStatusLogger;
public ProgressTask(IKp2aApp app, Activity activity, RunnableOnFinish task)

View File

View File

@@ -72,8 +72,8 @@ namespace keepass2android
SearchParameters sp = SearchParameters.None;
sp.SearchInUrls = true;
sp.SearchString = url;
if(sp.RegularExpression) // Validate regular expression
if(sp.RegularExpression) // Validate regular expression
{
new Regex(sp.SearchString);
}
@@ -104,9 +104,7 @@ namespace keepass2android
if (String.IsNullOrWhiteSpace(host))
return pgResults;
foreach (PwEntry entry in database.EntriesById.Values)
{
if (!entry.GetSearchingEnabled())
continue;
{
string otherUrl = entry.Strings.ReadSafe(PwDefs.UrlField);
otherUrl = SprEngine.Compile(otherUrl, new SprContext(entry, database.KpDatabase, SprCompileFlags.References));
String otherHost = ExtractHost(otherUrl);

Some files were not shown because too many files have changed in this diff Show More