Compare commits

...

2244 Commits

Author SHA1 Message Date
Philipp Crocoll
944f44bc4b Manifest for v1.09e-r4 2023-03-09 20:45:25 +01:00
Philipp Crocoll
58047d5386 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-03-09 20:44:29 +01:00
PhilippC
c0a06c9f3a Merge pull request #2264 from PhilippC/l10n_master2
New Crowdin updates
2023-03-09 20:44:02 +01:00
Philipp Crocoll
d0c041a0e2 remove debugging code 2023-03-09 20:16:24 +01:00
PhilippC
df060e2f4b New translations strings.xml (Portuguese, Brazilian) 2023-03-09 19:56:04 +01:00
PhilippC
aea55dad45 New translations strings.xml (Chinese Traditional) 2023-03-09 19:56:01 +01:00
PhilippC
5442dbf441 New translations strings.xml (Chinese Simplified) 2023-03-09 19:56:00 +01:00
PhilippC
317476d9b5 New translations strings.xml (Ukrainian) 2023-03-09 19:55:59 +01:00
PhilippC
ad0acb7a69 New translations strings.xml (Slovenian) 2023-03-09 19:55:56 +01:00
PhilippC
b66ae5d264 New translations strings.xml (Polish) 2023-03-09 19:55:53 +01:00
PhilippC
d87706fa43 New translations strings.xml (Japanese) 2023-03-09 19:55:51 +01:00
PhilippC
cb25d12709 New translations strings.xml (Greek) 2023-03-09 19:55:46 +01:00
PhilippC
dce536009e New translations strings.xml (German) 2023-03-09 19:55:45 +01:00
PhilippC
656e785214 New translations strings.xml (Danish) 2023-03-09 19:55:44 +01:00
PhilippC
35d50a6eb0 New translations strings.xml (French) 2023-03-09 19:55:39 +01:00
PhilippC
786bb646c2 New translations strings.xml (Slovak) 2023-03-09 19:55:37 +01:00
Philipp Crocoll
72cc6ff768 wrap adding fields and hints to dictionary, avoiding to add a duplicate key. Should close #2262 (but I can't reproduce) 2023-03-09 19:46:28 +01:00
Philipp Crocoll
404e07e5c0 Merge remote-tracking branch 'remotes/origin/l10n_master2' 2023-03-09 19:40:36 +01:00
Philipp Crocoll
1c7159ede9 changelog and manifest for 1.09e-r3 2023-03-09 19:39:51 +01:00
PhilippC
2378cd0d7c New translations strings.xml (Greek) 2023-03-08 16:54:54 +01:00
PhilippC
b149bab761 New translations strings.xml (Chinese Simplified) 2023-03-07 03:36:36 +01:00
PhilippC
5ebd8e5e33 New translations strings.xml (Portuguese, Brazilian) 2023-03-06 14:12:17 +01:00
Philipp Crocoll
db6b266a59 add more autofill tests and change AutofillParser to make them pass 2023-03-06 10:26:58 +01:00
Philipp Crocoll
7de28c5aba add preference to control if autofill view details are written to log 2023-03-06 10:26:33 +01:00
PhilippC
ed79df0c6d Merge pull request #2254 from PhilippC/PhilippC-autofill-testing-and-improvements
Autofill testing and improvements
2023-03-06 08:28:42 +01:00
Philipp Crocoll
4949fede32 remove tab in yml file 2023-03-06 08:05:57 +01:00
PhilippC
bddef6442c Merge pull request #2251 from WreckingBANG/master
Added Monochrome Icon
2023-03-06 07:59:11 +01:00
Philipp Crocoll
48a6d0a2ad disable macos build action 2023-03-06 07:58:52 +01:00
PhilippC
ac5f3c9ca5 Merge pull request #2244 from PhilippC/l10n_master2
New Crowdin updates
2023-03-04 09:08:30 +01:00
PhilippC
93e1cf1147 Merge pull request #2240 from hyproman/java-file-storage-android-studio
Get JavaFileStorage working in Android Studio
2023-03-04 09:01:19 +01:00
PhilippC
a805787a95 Merge pull request #2248 from hyproman/build-doc-update
Update build documentation
2023-03-04 09:00:56 +01:00
Philipp Crocoll
85315d0ecc run unit tests in github action 2023-02-28 22:42:04 +01:00
Philipp Crocoll
595a451f77 fix failing test 2023-02-28 22:37:32 +01:00
Philipp Crocoll
914224e4fa refactoring of autofill implementation, extracted some pieces to be independant of Android framework, added some xUnit tests 2023-02-28 22:31:28 +01:00
Philipp Crocoll
e350e8788c Merge branch 'master' into PhilippC-autofill-testing-and-improvements 2023-02-28 22:17:10 +01:00
PhilippC
ca5f6dc43c New translations strings.xml (Slovak) 2023-02-24 20:38:56 +01:00
WreckingBANG
0d4955622d Delete main.yml 2023-02-23 15:04:55 +01:00
WreckingBANG
886daa6b27 Update main.yml 2023-02-23 15:04:18 +01:00
WreckingBANG
8fa0803474 Create main.yml 2023-02-23 15:03:48 +01:00
WreckingBANG
4cad70e750 Update ic_launcher_offline_round.xml 2023-02-23 14:59:54 +01:00
WreckingBANG
c29b789a2b Update ic_launcher_offline.xml 2023-02-23 14:59:40 +01:00
WreckingBANG
cd34896661 Update ic_launcher_online_round.xml 2023-02-23 14:59:31 +01:00
WreckingBANG
1e02db86d6 Update ic_launcher_online.xml 2023-02-23 14:59:13 +01:00
Rick Brown
994741cbf5 Update build documentation based on my experience 2023-02-19 22:47:29 -05:00
PhilippC
58844be6eb New translations strings.xml (Danish) 2023-02-19 23:50:16 +01:00
PhilippC
2d899fa067 New translations strings.xml (Danish) 2023-02-19 22:32:16 +01:00
PhilippC
060bf6a6ee New translations strings.xml (Danish) 2023-02-19 21:31:30 +01:00
PhilippC
890f1bd704 New translations strings.xml (Danish) 2023-02-19 20:33:39 +01:00
PhilippC
139abcaec6 New translations strings.xml (German) 2023-02-18 23:26:56 +01:00
PhilippC
78a48b75b8 New translations strings.xml (German) 2023-02-18 22:13:41 +01:00
PhilippC
3918b06b1f New translations strings.xml (German) 2023-02-18 21:04:46 +01:00
PhilippC
40847ebe31 New translations strings.xml (German) 2023-02-18 19:55:30 +01:00
PhilippC
34cac86a9b New translations strings.xml (German) 2023-02-18 18:58:40 +01:00
PhilippC
d8598a53e0 New translations strings.xml (Japanese) 2023-02-18 17:41:17 +01:00
PhilippC
92d9eb1512 New translations strings.xml (German) 2023-02-18 11:15:28 +01:00
PhilippC
1be7b33f6b New translations strings.xml (German) 2023-02-18 10:09:38 +01:00
PhilippC
8464fa4f29 New translations strings.xml (German) 2023-02-18 09:11:16 +01:00
PhilippC
eff9a96bd5 New translations strings.xml (German) 2023-02-18 07:44:12 +01:00
PhilippC
bd4e321b0e New translations strings.xml (German) 2023-02-18 06:48:57 +01:00
PhilippC
47aaedbfb5 New translations strings.xml (German) 2023-02-18 06:48:56 +01:00
PhilippC
3043f8981d New translations strings.xml (German) 2023-02-18 06:48:55 +01:00
Philipp Crocoll
cfd413f1f4 add more hints for password fields to fix issues with autofill (maybe on newer Android versions only), closes #2184 2023-02-13 09:42:55 +01:00
Philipp Crocoll
30df03eec6 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-02-13 09:40:08 +01:00
PhilippC
5048f63204 Merge pull request #2225 from PhilippC/l10n_master2
New Crowdin updates
2023-02-13 09:37:38 +01:00
PhilippC
7557c0b9fd Merge pull request #2234 from hyproman/bugfix-2223-sftp-db-import-crash
Bugfix for #2223 - crash after import database by SFTP
2023-02-13 09:36:46 +01:00
PhilippC
9346f6bb32 Merge pull request #2226 from hyproman/gradle-version-sync
Reuse a single GradleDaemon instance
2023-02-13 09:36:35 +01:00
Rick Brown
23d7efff53 Bugfix for #2223 - crash after import database by SFTP
Add FLAG_MUTABLE flag to PendingIntent call for API >= 31 to fix an
issue where trying to open an SFTP database (transition to choose a
remote database file) crashes and returns to the Open/New database
screen.
2023-02-12 13:28:45 -05:00
PhilippC
c7eb2bf873 New translations strings.xml (Japanese) 2023-02-12 17:02:59 +01:00
Rick Brown
632121f3ec Get JavaFileStorage working in Android Studio
Resolve issue where AS would fail to import Android API jar
2023-02-11 18:34:20 -05:00
Philipp Crocoll
f7feddcf1f start to refactor Autofill code to extract some logic into an non-Androidlibrary 2023-02-11 06:56:02 +01:00
Philipp Crocoll
e745fee6e2 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-02-11 06:51:06 +01:00
Philipp Crocoll
bcc17d91bd fix potential crash on newer Android versions when trying to close notification drawer 2023-02-11 06:50:52 +01:00
Rick Brown
8c8a8e3968 Normalize gradle jvmargs to reuse a single GradleDaemon
Set max heap to 1.5G across all java projects. Inspection
via VisualVM found 1G to be a little too aggressive, while
2G seemed overkill. In any case a full build now can reuse
a single GradleDaemon instance instead of three.
2023-02-09 20:20:03 -05:00
PhilippC
3c41550404 New translations strings.xml (Polish) 2023-02-09 16:44:10 +01:00
PhilippC
76107b1207 Merge pull request #2207 from PhilippC/l10n_master2
New Crowdin updates
2023-02-06 20:48:03 +01:00
PhilippC
bb0c13b9d8 New translations strings.xml (Slovenian) 2023-02-06 18:57:26 +01:00
PhilippC
405166ba9d New translations strings.xml (Japanese) 2023-02-03 13:58:57 +01:00
PhilippC
f5cb60770e New translations strings.xml (Ukrainian) 2023-02-03 11:26:34 +01:00
Philipp Crocoll
9958a73d4a revert to targetSdkVersion 31 to avoid issues with notifications not showing 2023-02-01 21:16:05 +01:00
PhilippC
fd287b8da7 New translations strings.xml (Basque) 2023-02-01 17:53:32 +01:00
PhilippC
9bea5b13e3 New translations strings.xml (Chinese Traditional) 2023-02-01 00:34:15 +01:00
PhilippC
aa6a728e8c New translations strings.xml (Chinese Traditional) 2023-01-31 23:35:12 +01:00
PhilippC
1c8431a3f9 New translations strings.xml (Chinese Traditional) 2023-01-31 22:35:51 +01:00
PhilippC
6a0eacd8f1 New translations strings.xml (Chinese Traditional) 2023-01-31 21:37:45 +01:00
PhilippC
d27976b737 New translations strings.xml (Chinese Traditional) 2023-01-31 19:41:50 +01:00
PhilippC
f312b50f0c New translations strings.xml (Chinese Traditional) 2023-01-31 18:42:58 +01:00
PhilippC
deb169fece New translations strings.xml (Chinese Traditional) 2023-01-31 18:42:57 +01:00
PhilippC
2df656211d New translations strings.xml (Japanese) 2023-01-31 14:17:53 +01:00
Philipp Crocoll
c86f9b3a4f KP2ASoftkeyboard_AS/gradle/wrapper/gradle-wrapper.properties: back to gradle 7.6 (7.5 was an unintentional change) 2023-01-30 21:02:58 +01:00
Philipp Crocoll
1c517dceb8 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-01-30 10:54:23 +01:00
Philipp Crocoll
bede68b60a update java projects to sourceCompatibility/targetCompatibility=11 and compileSdkVersion 33 2023-01-30 10:54:06 +01:00
PhilippC
8ab7fe84dd Merge pull request #2186 from PhilippC/l10n_master2
New Crowdin updates
2023-01-30 09:12:41 +01:00
PhilippC
305946b552 Merge pull request #2198 from proski/fix-2185
Suppress "Entry is available" notifications on wearable devices
2023-01-30 09:12:23 +01:00
PhilippC
f1fdfa5205 Update README.md 2023-01-30 08:09:34 +01:00
Philipp Crocoll
c1f7460c3e disable linux runner, currently not supported 2023-01-30 08:00:36 +01:00
Philipp Crocoll
1f92e449f5 check for sdk version before calling SetLocalOnly 2023-01-30 07:37:23 +01:00
PhilippC
50aec43b38 Merge pull request #2203 from tenzap/009-updateAGPandGradle
Java modules: upgrade Android gradle plugin & Gradle
2023-01-28 21:02:53 +01:00
PhilippC
dc75565d7c New translations strings.xml (Japanese) 2023-01-27 14:06:54 +01:00
tenzap
91b59c7c9f github workflow: switch to JDK 11
Since Android Gradle plugin was updated, it now requires JDK 11.

Fixes:

> Could not resolve all files for configuration ':classpath'.
   > Could not resolve com.android.tools.build:gradle:7.4.0.
     Required by:
         project :
      > No matching variant of com.android.tools.build:gradle:7.4.0 was found. The consumer was configured to find a runtime of a library compatible with Java 8, packaged as a jar, and its dependencies declared externally, as well as attribute 'org.gradle.plugin.api-version' with value '7.6' but:
          - Variant 'apiElements' capability com.android.tools.build:gradle:7.4.0 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares an API of a component compatible with Java 11 and the consumer needed a runtime of a component compatible with Java 8
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '7.6')
2023-01-26 22:02:25 +01:00
tenzap
e42b4c9654 Java modules: update repository sources 2023-01-26 22:02:09 +01:00
tenzap
d6486d4b98 Java modules: Upgrade to AGP 7.4.0 & Gradle 7.6
Fix the issues that were reported because of that change:

WARNING:The specified Android SDK Build Tools version (28.0.3) is ignored, as it is below the minimum supported version (30.0.3) for Android Gradle Plugin 7.4.0.
Android SDK Build Tools 30.0.3 will be used.
To suppress this warning, remove "buildToolsVersion '28.0.3'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.

android:exported needs to be explicitly specified for element <activity#com.crocoapps.javafilestoragetest2.MainActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

Setting the namespace via a source AndroidManifest.xml's package attribute is deprecated.
Please instead set the namespace (or testNamespace) in the module's build.gradle file, as described here: https://developer.android.com/studio/build/configure-app-module#set-namespace
This migration can be done automatically using the AGP Upgrade Assistant, please refer to https://developer.android.com/studio/build/agp-upgrade-assistant for more information.

.../keepass2android/src/java/KP2ASoftkeyboard_AS/app/src/main/AndroidManifest.xml:5:5-46 Warning:
	uses-sdk:targetSdkVersion value (14) specified in the manifest file is ignored. It is overridden by the value declared in the DSL or the variant API, or 1 if not declared/present. Current value is (18).
2023-01-26 22:02:02 +01:00
PhilippC
5bdf93633f Merge pull request #2200 from tenzap/007-ci
Fix github workflow
2023-01-26 20:17:30 +01:00
PhilippC
81f582bb8b New translations strings.xml (Japanese) 2023-01-26 10:21:28 +01:00
tenzap
7ba6a44888 github workflow: restore download of 'platforms;android-26'
Some targets still target v8.0 (api26):

src/KeePassLib2Android/KeePassLib2Android.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/ZlibAndroid/ZlibAndroid.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/PluginSdkBinding/PluginSdkBinding.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/PCloudBindings/PCloudBindings.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
src/TwofishCipher/TwofishCipher.csproj:    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>

So we need it in ANDROID_SDK_ROOT.

Otherwise build fails with this error (on macos & windows):

C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Xamarin\Android\Xamarin.Android.Tooling.targets(100,5): error XA5207: Could not find android.jar for API level 26. This means the Android SDK platform for API level 26 is not installed. Either install it in the Android SDK Manager (Tools > Android > Android SDK Manager...), or change the Xamarin.Android project to target an API version that is installed. (C:\Android\android-sdk\platforms\android-26\android.jar missing.) [D:\a\keepass2android\keepass2android\src\TwofishCipher\TwofishCipher.csproj]
2023-01-25 09:16:31 +01:00
tenzap
187d4c8f41 github workflow: fix typo 2023-01-25 09:13:41 +01:00
tenzap
58b0b666ce github workflow: restore download of xamarin.android only if it isn't in cache 2023-01-25 09:13:22 +01:00
tenzap
8393694e6c github workflow: restore 'Setup Xamarin.Android' step 2023-01-25 09:11:24 +01:00
tenzap
b99f82d3d7 github workflow: remove 'Install NDK' step
Not needed and I believe nowadays we should use the sdkmanager or cmdline-tools/latest/bin
2023-01-25 09:10:22 +01:00
tenzap
dfbfeb3fa3 remove ndk version specified in KP2ASoftkeyboard_AS/app/build.gradle
Partially reverts 4b3f6657a8
2023-01-25 09:08:11 +01:00
tenzap
3c9a8341ae github workflow: use gradle/gradle-build-action@v2 to manage gradle cache
Should be better than the custom way we used and manages the lock
made on windows runners
2023-01-25 09:06:52 +01:00
Philipp Crocoll
8f69a60307 remove gradle caching on Windows as well 2023-01-24 20:55:37 +01:00
Philipp Crocoll
14c2e64e2f disable gradle cache 2023-01-24 20:50:52 +01:00
Philipp Crocoll
554338012e now really remove install workload 2023-01-24 19:57:16 +01:00
Philipp Crocoll
3185cf1a9c re-enable download of xamarin build tools for linux 2023-01-24 19:43:54 +01:00
Philipp Crocoll
ed91d0db0b fix syntax error 2023-01-24 19:36:10 +01:00
Philipp Crocoll
dd7cb0a088 enable usage of xamarin_url for linux again 2023-01-24 19:31:01 +01:00
Philipp Crocoll
874e1dbae8 remove download of xamarin-33 2023-01-24 19:22:52 +01:00
Philipp Crocoll
649c0739f6 github action: install ndk for windows also, use "workload install" on linux 2023-01-23 20:39:17 +01:00
Philipp Crocoll
3db5ea4713 fix syntax error 2023-01-23 12:53:07 +01:00
Philipp Crocoll
96b5e1c10e install NDK 21.0.6113669 on github workflow runner 2023-01-23 12:41:14 +01:00
Philipp Crocoll
2bf5222505 update github workflow to use Visual studio 2022 and Xamarin for target sdk 33 2023-01-23 12:15:28 +01:00
Philipp Crocoll
24d6909297 changes to make build succeed for Flavor=Net as well 2023-01-23 11:30:41 +01:00
Philipp Crocoll
1c3f4106dd fix to weird autofill behavior: now returning dataset directly, instead of responseBuilder with dataset, allowing to fill the result of the autofill activity immediately. 2023-01-23 10:11:28 +01:00
Philipp Crocoll
4b3f6657a8 * update target-sdk version to 33. update some packages (build currently fails for flavor=Net, but works with NoNet)
* make changes to comply with new target sdk version requirements (mostly Exported-attribute and mutability flags)
* drop samsung fingerprint support (as suggested by Samsung), would require more hacks to keep it with the new target sdk version
* add build-properties.props to allow specifying a flavor in VS builds.
2023-01-23 10:07:15 +01:00
PhilippC
fd75737331 New translations strings.xml (Japanese) 2023-01-23 01:06:47 +01:00
PhilippC
195cdb8ad7 New translations strings.xml (Japanese) 2023-01-23 00:07:54 +01:00
Pavel Roskin
a8e519660b Suppress "Entry is available" notifications on wearable devices
Fixes #2185
2023-01-22 10:49:21 -08:00
PhilippC
54047e8a26 New translations strings.xml (Japanese) 2023-01-22 04:56:19 +01:00
PhilippC
e84c5bacb1 New translations strings.xml (Japanese) 2023-01-22 03:58:28 +01:00
PhilippC
48037d667c New translations strings.xml (Japanese) 2023-01-21 15:49:56 +01:00
PhilippC
41682aab0a New translations strings.xml (Japanese) 2023-01-21 14:42:18 +01:00
PhilippC
9561c99711 New translations strings.xml (Japanese) 2023-01-21 13:31:07 +01:00
PhilippC
1fd538999f New translations strings.xml (Polish) 2023-01-17 12:01:22 +01:00
PhilippC
a4396af352 New translations strings.xml (Basque) 2023-01-16 19:03:40 +01:00
PhilippC
d7c403cf30 New translations strings.xml (Basque) 2023-01-16 16:10:16 +01:00
PhilippC
089eb9bcb2 New translations strings.xml (French) 2023-01-11 13:38:13 +01:00
PhilippC
178383d549 New translations strings.xml (Portuguese, Brazilian) 2023-01-09 13:12:11 +01:00
Philipp Crocoll
f6e6d0e59c update changelog to include 1.09e 2023-01-09 10:58:36 +01:00
PhilippC
30c1cc1e3f Merge pull request #2183 from PhilippC/l10n_master2
New Crowdin updates
2023-01-09 10:00:22 +01:00
PhilippC
e7f579862f New translations strings.xml (Chinese Simplified) 2023-01-08 04:03:15 +01:00
PhilippC
22fc984432 New translations strings.xml (German) 2023-01-07 23:26:13 +01:00
Philipp Crocoll
33b3c78676 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-01-07 21:01:08 +01:00
Philipp Crocoll
0052641b86 pseudo-change to trigger crowdin sync once again 2023-01-07 21:00:57 +01:00
PhilippC
b5b67ed6c4 Merge pull request #2181 from PhilippC/l10n_master2
New Crowdin updates
2023-01-07 20:59:48 +01:00
PhilippC
08e0b6e72d New translations strings.xml (Persian) 2023-01-05 08:51:01 +01:00
PhilippC
6e23b1ef22 New translations strings.xml (Japanese) 2023-01-05 08:50:50 +01:00
PhilippC
e0da7c6bf5 New translations strings.xml (Serbian (Cyrillic)) 2023-01-05 08:50:28 +01:00
Philipp Crocoll
138bd8adef changelog entries for 1.09e 2023-01-04 23:20:58 +01:00
PhilippC
ab527368c0 Merge pull request #2179 from PhilippC/makefile-signing
allow to sign apk using a specific keystore
2023-01-04 23:04:19 +01:00
PhilippC
335c53360d Merge pull request #2142 from nikoyak/fix-1956
fix crashes/unexpected logouts (#1956, #1936, #1909)
2023-01-04 22:23:31 +01:00
PhilippC
7b31f6ec5a Merge pull request #2176 from sratz/jsch
Migrate from com.jcraft:jsch 0.1.55 to com.github.mwiede:jsch 0.2.5
2023-01-04 22:21:36 +01:00
Philipp Crocoll
63a8791752 allow to sign apk using a specific keystore 2023-01-04 21:50:06 +01:00
Sebastian Ratz
e3ce7fe95a Migrate from com.jcraft:jsch 0.1.55 to com.github.mwiede:jsch 0.2.5
com.jcraft:jsch is not actively maintained anymore and lacks support
for modern public key algorithms such as rsa-sha2-256. It only
supports ssh-rsa which is disabled in up-to-date environments.

com.github.mwiede:jsch was created as a drop-in replacement which
works in modern environments [1].

Sources are taken from maven central [2]. The following files were
omitted, because they depend on additional 3rd-party libraries which
we do not have / do not need:

    com/jcraft/jsch/JUnixSocketFactory.java
    com/jcraft/jsch/Log4j2Logger.java
    com/jcraft/jsch/PageantConnector.java
    com/jcraft/jsch/Slf4jLogger.java
    com/jcraft/jsch/SSHAgentConnector.java
    com/jcraft/jsch/bc/*
    com/jcraft/jsch/jgss/GSSContextKrb5.java

Fixes #1812.

[1] https://github.com/mwiede/jsch
[2] https://repo1.maven.org/maven2/com/github/mwiede/jsch/0.2.5/jsch-0.2.5-sources.jar
2023-01-03 15:26:42 +01:00
PhilippC
ce6556496e Merge pull request #2159 from tenzap/003-makefile_java
Makefile: add more source files to deps for java targets
2023-01-03 10:13:48 +01:00
PhilippC
eab5e22c8c Merge pull request #2162 from tenzap/004-gitignore
Fix gitignore with Resource.designer.cs/Resource.Designer.cs
2023-01-03 10:13:06 +01:00
PhilippC
827905e690 Merge pull request #2163 from tenzap/005-nuget.packages
remove src/.nuget/packages.config
2023-01-03 10:12:25 +01:00
PhilippC
7fe407d25e Merge pull request #2164 from tenzap/006-removeUntracked
remove untracked files as per .gitignore
2023-01-03 10:11:49 +01:00
nikoyak
b9d0f56e0f Revert "avoid potential crash in Android 12 while reloading the Database, closes https://github.com/PhilippC/keepass2android/issues/1909"
This reverts commit 40146a42ce,
because the root problem is fixed.
2022-12-20 11:42:31 +03:00
nikoyak
5d06a8b62a fix #1956, #1936, #1909 2022-12-20 11:33:51 +03:00
tenzap
39c1f671d4 remove untracked files as per .gitignore
Somes files that are in .gitignore are still in the repository
This will remove them.

Commands used to do this:

git rm --cached -r . &&
git add . &&
git restore --staged .gitmodul

The jars in src/java/JavaFileStorage/libs are no more used since commit
a63663c30e

If this removes too many files, it means that .gitignore has to be fixed.
To find which rule removes which file run:

git check-ignore -v <file>

Especially, removing .idea/ completely might not be correct depending on ones
objective.
2022-12-19 22:54:50 +01:00
tenzap
4eb8c77304 remove src/.nuget/packages.config
It references a package that is deprecated and which seem to be already replaced
2022-12-19 22:37:28 +01:00
tenzap
c933748077 Fix gitignore with Resource.designer.cs/Resource.Designer.cs
Default value for <AndroidResgenFile> is Resource.designer.cs
https://learn.microsoft.com/en-us/xamarin/android/deploy-test/building-apps/build-properties#androidresgenfile

Two projects were using <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
All other projects use <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
Difference is one has uppercase D, the other lowercase d

.gitignore already has a rule for 'Resource.designer.cs' in it.

The two projects that were with Resource.Designer.cs needed either a specific line in gitignore
or weren't actually ignored.

Selected fix here is to rename the file with a lowercase 'd' instead of uppercase.
This permits to remove one line from .gitignore and keep the other file really ignored
2022-12-19 19:42:26 +01:00
tenzap
3347722d6d Makefile: add more source files to deps for java targets 2022-12-19 12:06:36 +01:00
PhilippC
56c35ac573 Merge pull request #2106 from AriehSchneier/mark-clipboard-sensitive
Mark passwords as sensitive when copy to clipboard
2022-12-19 10:29:35 +01:00
Arieh Schneier
68a61dfcd8 Wrap in version check 2022-12-19 19:07:09 +11:00
PhilippC
664c260b99 Merge pull request #2139 from tenzap/master_pass1.6.2
Add function to support 'SamsungPass v1.2.6' (fixes build failure)
2022-12-19 08:46:35 +01:00
PhilippC
a07ce67c95 Update README.md 2022-12-19 08:45:27 +01:00
PhilippC
6cd71b2bf5 Merge pull request #1904 from PhilippC/l10n_master2
New Crowdin updates
2022-12-19 08:40:03 +01:00
PhilippC
eb3001a57d Merge pull request #2158 from tenzap/002-makefile-speedup
Speed-up makefile processing:
2022-12-19 08:13:14 +01:00
PhilippC
cfd44501d4 Merge pull request #2148 from PhilippC/dependabot/nuget/src/keepass2android/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 11.0.1 to 13.0.1 in /src/keepass2android
2022-12-19 08:13:03 +01:00
PhilippC
62f7592e1d New translations strings.xml (Dutch) 2022-12-18 22:13:41 +01:00
PhilippC
5b07f5d726 New translations strings.xml (Dutch) 2022-12-18 22:13:40 +01:00
PhilippC
3c7cdd150a New translations strings.xml (Dutch) 2022-12-18 21:06:16 +01:00
tenzap
3bafb4e022 Speed-up makefile processing:
On Windows, before the start of the java target, processing took ~10sec while less
than a second on linux.

Disable built-in rules to speed-up the Makefile processing.
2022-12-18 20:56:22 +01:00
PhilippC
477bb87322 Merge pull request #2150 from tenzap/001-build_system
Build system improvements (Makefile, github workflow)
2022-12-18 15:46:35 +01:00
PhilippC
6584303cdf New translations strings.xml (Japanese) 2022-12-16 04:00:42 +01:00
PhilippC
cbcb72158e New translations strings.xml (Japanese) 2022-12-15 14:49:31 +01:00
PhilippC
43c8c7d243 New translations strings.xml (Japanese) 2022-12-15 02:43:10 +01:00
PhilippC
9d31559916 New translations strings.xml (Japanese) 2022-12-14 23:46:21 +01:00
tenzap
3483654830 github workflow: make use of caches for nuget, gradle & xamarin
Speeds job by up to ~3-5 minutes
2022-12-13 14:10:00 +01:00
tenzap
a74b162e37 Build.readme.md: small updates
Mention java
Do minimal required install of Mono on linux (instead of mono-complete
2022-12-13 14:09:59 +01:00
tenzap
52dd73fa9f Makefile: improve java & native targets
So that they get rebuilt if any of their files changes
2022-12-13 14:09:29 +01:00
tenzap
548b92ffe9 Makefile: improve nuget restore & clean
In some cases, nuget restore isn't sufficient to restore everything, especially if project.assets.json files
are missing or were removed. So we add a call to MSBuild -t:restore to build it.

Both nuget & msbuild are required because msbuild can only restore packages in 'packages.config' from version
16.5
2022-12-13 14:07:15 +01:00
PhilippC
1df84ae7a3 New translations strings.xml (Japanese) 2022-12-13 06:48:56 +01:00
PhilippC
ba40179c82 New translations strings.xml (Japanese) 2022-12-13 05:01:53 +01:00
PhilippC
1add52a459 New translations strings.xml (Japanese) 2022-12-13 04:02:55 +01:00
PhilippC
2f105d30fd New translations strings.xml (Japanese) 2022-12-12 16:37:03 +01:00
PhilippC
e473940c26 New translations strings.xml (Japanese) 2022-12-12 16:37:02 +01:00
Arieh Schneier
5f836c381f Mark passwords as sensitive when copy to clipboard 2022-12-13 02:28:05 +11:00
PhilippC
7282fdae0b New translations strings.xml (Japanese) 2022-12-12 14:22:34 +01:00
PhilippC
df2a7412d3 New translations strings.xml (Japanese) 2022-12-12 13:24:16 +01:00
PhilippC
ae4cdeedbf New translations strings.xml (Japanese) 2022-12-12 10:57:38 +01:00
PhilippC
fe6115aefb New translations strings.xml (Japanese) 2022-12-12 09:52:08 +01:00
PhilippC
414dfa57b2 New translations strings.xml (Japanese) 2022-12-12 07:08:35 +01:00
PhilippC
193a39390c New translations strings.xml (Japanese) 2022-12-12 06:06:05 +01:00
PhilippC
43455959e2 New translations strings.xml (Japanese) 2022-12-12 03:19:36 +01:00
PhilippC
2d0e142a79 New translations strings.xml (Japanese) 2022-12-12 02:24:01 +01:00
PhilippC
27714c0cdf New translations strings.xml (Japanese) 2022-12-11 15:40:06 +01:00
PhilippC
37db758ee5 New translations strings.xml (Japanese) 2022-12-11 14:41:35 +01:00
dependabot[bot]
faccbc10e5 Bump Newtonsoft.Json from 11.0.1 to 13.0.1 in /src/keepass2android
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 11.0.1 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/11.0.1...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-11 13:29:25 +00:00
PhilippC
748bd493c4 Merge pull request #2134 from tenzap/master_my
Fix build, support build of 'nonet' flavor inside master branch
2022-12-11 14:28:54 +01:00
PhilippC
8616956df1 New translations strings.xml (Japanese) 2022-12-11 06:33:44 +01:00
PhilippC
e0da5470e2 New translations strings.xml (Japanese) 2022-12-11 05:30:02 +01:00
PhilippC
27e237c5e9 New translations strings.xml (Japanese) 2022-12-11 01:05:29 +01:00
PhilippC
22d4ebbea4 New translations strings.xml (Japanese) 2022-12-10 23:58:51 +01:00
tenzap
586b7fc389 remove src/build-scripts/build-xamarin*.bat
Prefered method to build on command line is with make since it is provided in NDK anyway
2022-12-10 20:44:55 +01:00
tenzap
63f6c1e5cf relocate build.readme.md and mention it in README.md 2022-12-10 20:44:55 +01:00
tenzap
2c128794ac Update build scripts for Windows, linux, support macos & 'build readme'
Remove the older shell scripts, they are superseede by a Makefile
which is more efficient.

BTW update the build README
2022-12-10 20:44:55 +01:00
tenzap
cf0e8a1926 [nonet migration] adapt Makefile and github worklow, update build doc
This will permit to build on the github workflow both the 'net' and 'nonet'
variant
2022-12-10 20:44:55 +01:00
tenzap
06bd468658 [nonet migration] permit to build nonet on master branch through NoNet Flavor
To select the NoNet Flavor, call MSBuild with the 'Flavor' Parameter.
For example MSBuild.exe ....... -p:Flavor=NoNet

For the "net" build, flavor value is "Net"

See: https://learn.microsoft.com/en-us/visualstudio/msbuild/how-to-build-the-same-source-files-with-different-options?view=vs-2019
2022-12-10 20:44:55 +01:00
tenzap
7b6ff73fb9 Add Makefile to be able to build from command line 2022-12-10 20:44:51 +01:00
PhilippC
54231b23f3 New translations strings.xml (Japanese) 2022-12-10 15:50:08 +01:00
PhilippC
77ee958587 New translations strings.xml (Japanese) 2022-12-10 14:46:54 +01:00
PhilippC
69137b2212 New translations strings.xml (Japanese) 2022-12-10 11:45:56 +01:00
PhilippC
208f080bd2 New translations strings.xml (Japanese) 2022-12-10 10:41:40 +01:00
PhilippC
34b738e2fe New translations strings.xml (Japanese) 2022-12-10 03:32:42 +01:00
PhilippC
1664171794 New translations strings.xml (Japanese) 2022-12-10 01:20:54 +01:00
PhilippC
5690ff6c99 New translations strings.xml (Japanese) 2022-12-09 07:25:47 +01:00
PhilippC
e7a43ab7a6 New translations strings.xml (Japanese) 2022-12-09 04:32:52 +01:00
tenzap
96fcc79c93 Autoselect correct AndroidManifest in csproj
This avoids use of an external .bat script and selects the correct manifest based
on the configuration to build for (Debug, Release...)
2022-12-08 12:29:16 +01:00
tenzap
b99272e850 add github actions workflow (build with macos, windows, linux) 2022-12-08 12:29:15 +01:00
PhilippC
b04aba7644 New translations strings.xml (Japanese) 2022-12-08 01:42:55 +01:00
PhilippC
cef13eafa0 New translations strings.xml (Japanese) 2022-12-08 00:16:52 +01:00
PhilippC
37befe2c46 New translations strings.xml (Finnish) 2022-12-07 14:32:24 +01:00
PhilippC
dd84af3bfc New translations strings.xml (Finnish) 2022-12-07 14:32:22 +01:00
PhilippC
65a4d64925 New translations strings.xml (Japanese) 2022-12-07 14:32:21 +01:00
PhilippC
0b2d62917a New translations strings.xml (Japanese) 2022-12-07 11:06:22 +01:00
tenzap
9fc7dca138 [nonet migration] don't use GoogleDriveAppDataFileStorage when building nonet
src/keepass2android/app/App.cs: don't use GoogleDriveAppDataFileStorage when building nonet
2022-12-07 04:12:12 +01:00
tenzap
b50c7e6cf6 [nonet migration] update 'sr' translations from nonet branch 2022-12-07 04:12:12 +01:00
tenzap
944bce14b8 [Build Fix] set AndroidClassParser build property to class-parse
Build reported these warnings:

warning XA4231: The Android class parser value 'jar2xml' is deprecated and will be removed in a future version of Xamarin.Android. Update the project properties to use 'class-parse'

In the code <AndroidClassParser> is not specified, so by default it uses
'jar2xml'. So set its value to 'class-parse'.

See:
https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/messages/xa4231.md
https://github.com/xamarin/xamarin-android/issues/1319

Moving to class-parse instead of jar2xml also fixes issues when building with Java 11 & Xamarin < 12.2
Because of:
- 94e21c7aaa
If the change wasn't done, we would have to stick with JDK-8
2022-12-07 04:12:12 +01:00
tenzap
269952ee67 [Build Fix] fix location of 'Xamarin.Android.Bindings.targets' to 'Xamarin\Android\'
Instead of 'Novell\'

In kp2akeytransform.csproj

Novell\Xamarin.Android.Bindings.targets is not anymore at this place. It is now in
Xamarin\Android\Xamarin.Android.Bindings.targets

Most subprojects already use that path, but this wasn't changed here
2022-12-07 04:12:12 +01:00
tenzap
d1ccd16987 Various fixes to the build scripts (*.bat & *.sh)
build.readme.md: mention how to build the native libs when on windows

Fix target name: rename target to keepass2android-app

*.BAT build-scripts: make script fail when a command fails
  If a command was failing, the script continued to run and didn't fail.
  The build errors could be hidden. This will make them apparent

build-java.bat:
- prefix call to gradlew.bat with 'call'
  Otherwise, the batch file stops at the first gradlew call.
- don't build PluginQR as its output is not used, and the build fails with
  newer Android SDK/NDK

build-xamarin.bat:
- use VS2019 path, download NuGet dependencies
- Call vcvarsall.bat only if not yet done
- build also APK
- Tested with VS Community 2019
- When calling it multiple times, the PATH env var contains duplicate values
  and in the end will be too long, leading to vcvarsall.bat failing,
  and hence build-xamarin.bat too

build-java.sh:
- don't unset ANDROID_NDK_HOME & ANDROID_NDK
  Fixes build issues on github actions
- add missing KP2AKdbLibrary
- add executable bit to src/java/KP2AKdbLibrary/gradlew
- support build with msbuild & force use of xabuild when on linux
- don't build PluginQR as its output is not used, and the build fails with
  newer Android SDK/NDK

build-xamarin.sh:
- put the config (Debug or Release) in a variable

build-apk.sh:
- fix project name to keepass2android-app.csproj
- support build with msbuild & force use of xabuild when on linux
  Needed for github actions, and will also support building on macos with msbuild
- put the config (Debug or Release) in a variable
- Add missing /p:Configuration= /p:Platform=AnyCPU which is needed when building
  nonet on macos (because nonet uses "Release")

remove PluginQR
2022-12-07 04:11:24 +01:00
tenzap
78a7c79376 [Build Fix] KP2AKdbLibrary; upgrade AGP to 4.0.1 & gradle version to 6.1.1
Fixes:
> No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi

See https://stackoverflow.com/a/69293151
2022-12-06 23:36:02 +01:00
tenzap
41c66ecb28 [Build Fix] KP2ASoftkeyboard_AS/build.gradle: upgrade AGP to 4.0.1 & gradle to 6.1.1
Fixes:
> No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi

See https://stackoverflow.com/a/69293151
2022-12-06 23:35:58 +01:00
tenzap
b2f21094b5 remove untracked Kp2aBusinessLogic/Resources/Resource.Designer.cs
Since it is in gitignore
2022-12-06 23:00:52 +01:00
tenzap
32666a0d86 Add function to support 'SamsungPass v1.2.6'
This commit has to be merged with the commit in SamsungPass submodule that
updates SamsungPass to v1.2.6.

Commit message below:

With 1.1.3 build fails as soon as we use Xamarin.Android >= 12.1 (on all
platforms) [1].

Actually, this is due the version of r8.jar shipped by Xamarin which now fails.
Failure is actually normal [2], as expected, while in the past it didn't fail
(problem not catched in older versions).

So the bug is actually in SamsungPass.

This PR has to be taken with caution:
- it was just to check that it would build with a newer version of SamsungPass
- The new functions that are in the jar haven't been ported except those
  mandatory to build
- Content is unsure and part of the code is marked as TODO
- jar was taken from a location whose trustability was not checked [3]

API for 1.2.6 can be found at [4]

[1] https://github.com/xamarin/xamarin-android/issues/7607
[2] https://issuetracker.google.com/issues/192128533
[3] https://github.com/antonjlin/Shack/blob/master/app/libs/pass-v1.2.6.jar
[4] https://web.archive.org/web/20181004165435/http://img-developer.samsung.com/onlinedocs/sms/pass/index.html

Error was:
```
Error in obj/Debug/lp/12/jl/__reference__pass-v1.1.3.jar:com/samsung/android/sdk/pass/SpassFingerprint.class:

java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4

"/tmp/keepass2android/src/KeePass.sln" (keepass2android-app target) (1) ->
"/tmp/keepass2android/src/keepass2android/keepass2android-app.csproj" (default target) (2) ->
(_CompileToDalvik target) ->
  /tmp/keepass2android/l/xamarin.android-oss/bin/Release/lib/xamarin.android/xbuild/Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException :  Index 4 out of bounds for length 4 [/tmp/keepass2android/src/keepass2android/keepass2android-app.csproj]
```
2022-12-05 23:25:32 +01:00
PhilippC
61afc4ed58 New translations strings.xml (Japanese) 2022-12-05 12:25:13 +01:00
PhilippC
9904946ce2 New translations strings.xml (Japanese) 2022-12-05 01:18:16 +01:00
PhilippC
78f76054ad New translations strings.xml (Japanese) 2022-12-05 00:18:52 +01:00
PhilippC
6e71b3fb66 New translations strings.xml (Japanese) 2022-12-04 15:59:42 +01:00
PhilippC
37a8b51a5f New translations strings.xml (Japanese) 2022-12-04 13:23:24 +01:00
PhilippC
02a43799a7 New translations strings.xml (Japanese) 2022-11-22 15:12:32 +01:00
PhilippC
a5994ebe63 New translations strings.xml (Japanese) 2022-11-22 12:22:20 +01:00
PhilippC
7319652db7 New translations strings.xml (Japanese) 2022-11-21 00:29:02 +01:00
PhilippC
35f91fd842 New translations strings.xml (Japanese) 2022-11-19 11:29:21 +01:00
PhilippC
801fd86db5 New translations strings.xml (Japanese) 2022-11-19 10:02:01 +01:00
PhilippC
afb376a3bc New translations strings.xml (Japanese) 2022-11-19 09:05:36 +01:00
PhilippC
3baaef19d4 New translations strings.xml (Japanese) 2022-11-19 06:55:16 +01:00
PhilippC
2e0e0c1d86 New translations strings.xml (Japanese) 2022-11-19 05:51:29 +01:00
PhilippC
c091c5aeda New translations strings.xml (Japanese) 2022-11-19 05:51:28 +01:00
PhilippC
74dd3b67e2 New translations strings.xml (Japanese) 2022-11-19 04:46:05 +01:00
PhilippC
2b3d090fd6 New translations strings.xml (Japanese) 2022-11-18 15:25:48 +01:00
PhilippC
d875f3fb2f New translations strings.xml (Japanese) 2022-11-18 14:27:22 +01:00
PhilippC
92f90eddc7 New translations strings.xml (Japanese) 2022-11-18 10:17:37 +01:00
PhilippC
3feae3b211 New translations strings.xml (Japanese) 2022-11-18 07:12:21 +01:00
PhilippC
03855a604a New translations strings.xml (Japanese) 2022-11-12 05:21:24 +01:00
PhilippC
b88000a5b4 New translations strings.xml (Japanese) 2022-11-12 03:15:17 +01:00
PhilippC
136ba783c8 New translations strings.xml (Japanese) 2022-11-10 17:46:25 +01:00
PhilippC
df6fb03245 New translations strings.xml (Japanese) 2022-11-10 14:50:51 +01:00
PhilippC
9509f5ae52 New translations strings.xml (Japanese) 2022-11-10 13:51:10 +01:00
PhilippC
c69177be6f New translations strings.xml (Hungarian) 2022-09-28 23:09:13 +02:00
PhilippC
daaa7d4016 New translations strings.xml (Hungarian) 2022-09-28 19:55:36 +02:00
PhilippC
d6914456dc New translations strings.xml (German) 2022-09-10 10:43:51 +02:00
PhilippC
4c3efb6b52 New translations strings.xml (German) 2022-09-10 09:21:39 +02:00
PhilippC
f6193103e2 New translations strings.xml (Korean) 2022-08-22 05:34:50 +02:00
PhilippC
68a78c32e3 New translations strings.xml (Korean) 2022-08-22 03:54:30 +02:00
PhilippC
241ea648ee New translations strings.xml (Persian) 2022-08-18 01:43:33 +02:00
PhilippC
a7a4e45c8b New translations strings.xml (Persian) 2022-08-18 01:43:32 +02:00
PhilippC
0b17c5dcda New translations strings.xml (Persian) 2022-08-18 00:42:44 +02:00
PhilippC
60b79a9b8a New translations strings.xml (Persian) 2022-08-17 18:28:30 +02:00
PhilippC
9dfd8bbadf New translations strings.xml (Persian) 2022-08-17 17:13:32 +02:00
PhilippC
7bfd80a122 New translations strings.xml (Catalan) 2022-08-14 19:01:21 +02:00
PhilippC
8b6c6707c7 New translations strings.xml (Persian) 2022-08-14 16:41:11 +02:00
PhilippC
d1ee19243f New translations strings.xml (Persian) 2022-08-14 16:41:10 +02:00
PhilippC
3aca98d1e2 New translations strings.xml (Persian) 2022-08-14 15:37:17 +02:00
PhilippC
b76c6063b0 New translations strings.xml (Persian) 2022-08-14 12:29:55 +02:00
PhilippC
584b4321ca New translations strings.xml (Persian) 2022-08-14 12:29:54 +02:00
PhilippC
b499d12768 New translations strings.xml (Persian) 2022-08-14 11:31:23 +02:00
PhilippC
246f6f8132 New translations strings.xml (Persian) 2022-08-13 02:31:02 +02:00
PhilippC
e07a350159 New translations strings.xml (Persian) 2022-08-13 01:02:26 +02:00
PhilippC
ac05f5c93c New translations strings.xml (Persian) 2022-08-12 23:57:12 +02:00
PhilippC
41cec5ffc3 New translations strings.xml (Persian) 2022-08-12 13:47:15 +02:00
PhilippC
f5ca01c529 New translations strings.xml (Persian) 2022-08-12 12:40:09 +02:00
PhilippC
6b115616b7 New translations strings.xml (Italian) 2022-08-12 12:39:16 +02:00
PhilippC
611efe05ca New translations strings.xml (Ukrainian) 2022-07-29 13:47:19 +02:00
PhilippC
9e3d30e81e New translations strings.xml (Ukrainian) 2022-07-29 13:47:17 +02:00
PhilippC
ba20fd79e0 New translations strings.xml (Ukrainian) 2022-07-29 13:47:16 +02:00
PhilippC
37e6ea89b7 New translations strings.xml (Persian) 2022-07-18 00:11:17 +02:00
PhilippC
a8457067b3 New translations strings.xml (Persian) 2022-07-16 18:12:17 +02:00
PhilippC
6e82ca9ac2 New translations strings.xml (Portuguese) 2022-06-11 19:20:25 +02:00
PhilippC
57a6e4f175 New translations strings.xml (Portuguese) 2022-06-11 18:19:43 +02:00
PhilippC
7621ae0c98 New translations strings.xml (Danish) 2022-06-06 20:33:11 +02:00
PhilippC
70b87adf62 New translations strings.xml (Turkish) 2022-06-06 11:16:12 +02:00
PhilippC
55e6bd42b1 New translations strings.xml (Turkish) 2022-06-06 10:07:28 +02:00
PhilippC
b5711591d5 New translations strings.xml (Turkish) 2022-06-06 05:24:50 +02:00
PhilippC
f29463ef6d New translations strings.xml (Turkish) 2022-06-06 05:24:49 +02:00
PhilippC
18b623548a New translations strings.xml (Turkish) 2022-06-06 04:20:06 +02:00
PhilippC
6c394c22da New translations strings.xml (Swedish) 2022-06-02 18:36:04 +02:00
PhilippC
738f9e7547 New translations strings.xml (Swedish) 2022-06-02 17:34:12 +02:00
PhilippC
d885fddfe6 New translations strings.xml (Greek) 2022-05-05 15:18:42 +02:00
PhilippC
95a1713c8a New translations strings.xml (Italian) 2022-05-04 15:17:15 +02:00
PhilippC
6661ffb580 New translations strings.xml (Italian) 2022-05-04 14:06:22 +02:00
PhilippC
99b7a6ccf7 New translations strings.xml (German) 2022-05-02 21:40:33 +02:00
PhilippC
c63865f043 New translations strings.xml (Norwegian Bokmal) 2022-04-19 13:01:50 +02:00
PhilippC
a00312df84 New translations strings.xml (Hungarian) 2022-04-17 21:10:59 +02:00
PhilippC
9ec3374240 New translations strings.xml (Russian) 2022-04-14 20:22:37 +02:00
PhilippC
81c8e17333 New translations strings.xml (Russian) 2022-04-14 20:22:36 +02:00
PhilippC
cf576dcbfd New translations strings.xml (Russian) 2022-04-14 19:24:05 +02:00
PhilippC
3cbaa09769 New translations strings.xml (Romanian) 2022-04-14 15:30:06 +02:00
PhilippC
89f795e953 New translations strings.xml (Romanian) 2022-04-14 14:34:55 +02:00
PhilippC
86c9ab40cf New translations strings.xml (Spanish) 2022-04-05 20:12:11 +02:00
PhilippC
681a7d17d5 New translations strings.xml (Chinese Traditional) 2022-04-01 15:37:31 +02:00
PhilippC
5a1d0ea51b New translations strings.xml (Ukrainian) 2022-03-27 03:08:55 +02:00
PhilippC
9aa3c02907 New translations strings.xml (Ukrainian) 2022-03-27 01:09:01 +01:00
PhilippC
0cfdfed476 New translations strings.xml (Spanish) 2022-03-24 11:31:17 +01:00
PhilippC
a205614d65 New translations strings.xml (Russian) 2022-03-22 12:58:32 +01:00
PhilippC
db27bcbbe7 New translations strings.xml (Russian) 2022-03-22 11:22:43 +01:00
PhilippC
943430cfce New translations strings.xml (Ukrainian) 2022-03-15 15:12:20 +01:00
PhilippC
6c55d2aec6 New translations strings.xml (Slovak) 2022-03-08 19:49:56 +01:00
PhilippC
7d3ecaca7e New translations strings.xml (Dutch) 2022-03-04 11:51:05 +01:00
PhilippC
f2e8de6938 New translations strings.xml (Dutch) 2022-03-04 10:45:09 +01:00
PhilippC
26090e87b3 New translations strings.xml (Czech) 2022-03-03 13:19:41 +01:00
PhilippC
af82d9fec1 New translations strings.xml (Czech) 2022-03-03 12:18:04 +01:00
PhilippC
86defbb144 New translations strings.xml (Greek) 2022-02-28 18:22:23 +01:00
PhilippC
d0a5557807 New translations strings.xml (Greek) 2022-02-28 17:25:11 +01:00
PhilippC
74788c77e0 New translations strings.xml (Danish) 2022-02-20 01:28:52 +01:00
PhilippC
f77bdc4b9c New translations strings.xml (Danish) 2022-02-20 00:29:45 +01:00
PhilippC
7f659178c5 New translations strings.xml (French) 2022-02-13 10:06:51 +01:00
PhilippC
b67e8e6c85 New translations strings.xml (Greek) 2022-02-11 17:21:34 +01:00
PhilippC
df4e7c0774 New translations strings.xml (Greek) 2022-02-11 16:15:28 +01:00
PhilippC
8d5882cb81 New translations strings.xml (Polish) 2022-02-09 16:14:37 +01:00
PhilippC
d540fda5b0 New translations strings.xml (Slovenian) 2022-02-07 09:39:12 +01:00
PhilippC
d39957b8ca New translations strings.xml (Chinese Simplified) 2022-02-06 09:58:15 +01:00
PhilippC
7160bcc33f New translations strings.xml (Chinese Simplified) 2022-02-06 08:57:30 +01:00
PhilippC
36d8a0aa8d New translations strings.xml (Portuguese, Brazilian) 2022-02-05 23:09:46 +01:00
PhilippC
0dec97fad1 New translations strings.xml (Italian) 2022-02-05 19:00:32 +01:00
PhilippC
9da08e94ac New translations strings.xml (Italian) 2022-02-05 18:01:15 +01:00
PhilippC
62b3fe61b6 New translations strings.xml (Italian) 2022-02-05 16:59:40 +01:00
Philipp Crocoll
50287025a0 manifest and changelog for 1.09d-r0 2022-02-05 16:13:53 +01:00
Philipp Crocoll
ed234c898e can now view entry history and remove/restore previous versions. closes #298 2022-02-05 15:53:08 +01:00
PhilippC
f2775fe0f0 New translations strings.xml (Bulgarian) 2022-02-04 09:21:37 +01:00
PhilippC
135168c1bb New translations strings.xml (Bulgarian) 2022-02-04 08:17:37 +01:00
Philipp Crocoll
35f74f5ea4 add support for Google Drive with restricted scope, closes #622 2022-02-02 03:50:51 +01:00
Philipp Crocoll
26f0ab6661 implement support for MEGA, closes #99 2022-02-02 02:54:35 +01:00
PhilippC
6213c753fe New translations strings.xml (Polish) 2022-01-25 12:12:52 +01:00
PhilippC
3f1b23dc82 New translations strings.xml (Slovenian) 2022-01-24 13:52:51 +01:00
PhilippC
dda76323c6 New translations strings.xml (Belarusian) 2022-01-24 12:52:26 +01:00
PhilippC
c41128401d New translations strings.xml (Belarusian) 2022-01-24 11:52:59 +01:00
PhilippC
9a5fe131cd New translations strings.xml (Belarusian) 2022-01-24 10:30:29 +01:00
PhilippC
e45fe2f279 New translations strings.xml (Belarusian) 2022-01-24 09:35:11 +01:00
PhilippC
4698a948cc New translations strings.xml (French) 2022-01-23 11:18:50 +01:00
Philipp Crocoll
3648213be2 Update AndroidX.Biometric library, an attempt to see if this helps regarding https://github.com/PhilippC/keepass2android/issues/1914 2022-01-20 20:02:16 +01:00
Philipp Crocoll
40146a42ce avoid potential crash in Android 12 while reloading the Database, closes https://github.com/PhilippC/keepass2android/issues/1909 2022-01-20 20:00:37 +01:00
PhilippC
35501c4c48 New translations strings.xml (Slovak) 2022-01-20 14:30:13 +01:00
PhilippC
dc28e5708d New translations strings.xml (Danish) 2022-01-20 11:40:27 +01:00
PhilippC
d4f3f69129 New translations strings.xml (Danish) 2022-01-20 10:42:12 +01:00
PhilippC
936bc7ed72 New translations strings.xml (Chinese Simplified) 2022-01-18 04:00:47 +01:00
PhilippC
8a59fbdb41 New translations strings.xml (Portuguese, Brazilian) 2022-01-17 14:59:40 +01:00
Philipp Crocoll
2c93fd1542 Add option to hide the "Disable autofill for XY" prompt, closes https://github.com/PhilippC/keepass2android/issues/1843 2022-01-17 10:05:16 +01:00
Philipp Crocoll
2981497da9 start adding option for "no disable autofill" 2022-01-17 09:49:44 +01:00
Philipp Crocoll
6745e0486c unmask single field when using the toggle menu option in the popup menu, closes https://github.com/PhilippC/keepass2android/issues/71; add option for specifying the default visibility of the TOTP field, closes https://github.com/PhilippC/keepass2android/issues/1873 2022-01-17 09:46:47 +01:00
PhilippC
76c6f56d73 New translations strings.xml (Portuguese, Brazilian) 2022-01-16 12:18:31 +01:00
PhilippC
21b0c63422 New translations strings.xml (Chinese Simplified) 2022-01-16 06:08:25 +01:00
Philipp Crocoll
093ebb424e Merge branch 'master' of https://github.com/PhilippC/keepass2android 2022-01-15 20:10:53 +01:00
Philipp Crocoll
c344b0f62a add button to scan QR code for TOTP setup in the "setup TOTP" dialog. Closes https://github.com/PhilippC/keepass2android/issues/1575 2022-01-15 20:10:34 +01:00
PhilippC
b447d2e5ad New translations strings.xml (Russian) 2022-01-15 20:01:52 +01:00
Philipp Crocoll
2b85ed473c make sure master password is no longer in unmasked mode when using fingerprint, closes https://github.com/PhilippC/keepass2android/issues/1134 2022-01-15 16:28:00 +01:00
Philipp Crocoll
d536b38acc allow to create a special entry in the database with the QuickUnlock code as password, closes https://github.com/PhilippC/keepass2android/issues/331 2022-01-15 16:17:35 +01:00
Philipp Crocoll
b4a82511ff Add option to always merge on conflict, closes https://github.com/PhilippC/keepass2android/issues/1218 2022-01-15 15:55:34 +01:00
PhilippC
35a84adde3 Merge pull request #1902 from whalehub/patch-1
Fix homepage URL
2022-01-15 14:20:21 +01:00
PhilippC
f1de28e6c1 New translations strings.xml (Italian) 2022-01-14 19:04:49 +01:00
PhilippC
2336255763 New translations strings.xml (Italian) 2022-01-14 19:04:48 +01:00
PhilippC
a3022963c0 New translations strings.xml (Italian) 2022-01-14 17:53:44 +01:00
PhilippC
396a035cfd New translations strings.xml (Slovenian) 2022-01-13 07:05:18 +01:00
PhilippC
2001fd26f8 New translations strings.xml (Chinese Simplified) 2022-01-13 04:39:02 +01:00
PhilippC
9daf2d0448 New translations strings.xml (Portuguese, Brazilian) 2022-01-12 19:12:30 +01:00
PhilippC
b2d14ff400 New translations strings.xml (Chinese Simplified) 2022-01-12 18:08:57 +01:00
Philipp Crocoll
675fd5b735 release 1.09c-r0. This solves the problem with Google Drive API blocked, closes #1833, closes #1859 and closes #1862 2022-01-12 17:30:04 +01:00
PhilippC
14c7663167 Merge pull request #1851 from PhilippC/l10n_master2
New Crowdin updates
2022-01-12 16:58:01 +01:00
PhilippC
788a04aceb New translations strings.xml (Slovenian) 2022-01-12 08:37:49 +01:00
PhilippC
99c70f96d8 New translations strings.xml (Russian) 2022-01-12 08:37:45 +01:00
PhilippC
9f729e8c36 New translations strings.xml (Polish) 2022-01-12 08:37:41 +01:00
PhilippC
35f1a351ba New translations strings.xml (Portuguese, Brazilian) 2022-01-12 08:37:32 +01:00
PhilippC
5f59de2fba New translations strings.xml (Chinese Simplified) 2022-01-12 08:37:23 +01:00
PhilippC
95b7c59606 New translations strings.xml (Ukrainian) 2022-01-12 08:37:21 +01:00
PhilippC
7962b5c8ea New translations strings.xml (Japanese) 2022-01-12 08:37:16 +01:00
PhilippC
d2818a35c6 New translations strings.xml (Slovak) 2022-01-12 08:37:07 +01:00
PhilippC
44183178c6 New translations strings.xml (Spanish) 2022-01-12 08:37:04 +01:00
PhilippC
2ebb35b631 New translations strings.xml (Spanish) 2022-01-12 08:37:03 +01:00
PhilippC
173a5fedf0 New translations strings.xml (French) 2022-01-12 08:37:00 +01:00
PhilippC
00c0bbd942 New translations strings.xml (Romanian) 2022-01-12 08:36:58 +01:00
PhilippC
1dfb38207b New translations strings.xml (Danish) 2022-01-12 08:36:57 +01:00
PhilippC
df0d087646 New translations strings.xml (Italian) 2022-01-12 08:36:53 +01:00
PhilippC
8f84e292b2 New translations strings.xml (Greek) 2022-01-12 08:36:43 +01:00
PhilippC
45bf4247c1 New translations strings.xml (German) 2022-01-12 08:36:40 +01:00
Philipp Crocoll
daecd42d8e Merge branch 'master' of https://github.com/PhilippC/keepass2android 2022-01-12 08:31:38 +01:00
Philipp Crocoll
952aaa76ff update file storage selection to use icon from https://developers.google.com/drive/api/v3/branding 2022-01-12 08:28:09 +01:00
Philipp Crocoll
fe57c05579 prototype implementation using the "Sign in with Google" button 2022-01-12 08:11:18 +01:00
Philipp Crocoll
309fd9f4d1 GoogleDriveFileStorage: update libraries and reimplement authentication workflow to show consent screen (verification by Google now passed); migrate Java filestorage and filechooser projects to AndroidX; disable "app is blocked" message; see #1833 2022-01-12 08:07:28 +01:00
Aaron
9af0d74ba4 Fix homepage URL
Signed-off-by: Aaron <admin@datahoarder.dev>
2022-01-08 05:45:32 +01:00
PhilippC
c1292b585f Merge pull request #1888 from mcpower/xamarin-ci-build-link
Fix Xamarin.Android CI build link
2022-01-06 15:16:57 +01:00
PhilippC
87a6bbe0fd Update README.md 2022-01-06 11:16:04 +01:00
PhilippC
b807431053 Update README.md 2022-01-06 11:15:47 +01:00
Philipp Crocoll
1442af3148 add Belarusian translation to close #1896 2022-01-05 12:47:30 +01:00
PhilippC
eb92645ed0 Merge pull request #1897 from IanSmith123/patch-1
chore: typo in Chinese  version ChangeLog  v1.07
2022-01-05 12:31:54 +01:00
Les1ie
714be0f6ae chore: fix Chinese version ChangeLog v1.07 typo 2022-01-04 19:23:33 +08:00
Philipp Crocoll
e77c3ce1d3 fix issue: inputstick/keelink plugins could not be enabled on Android 11+ 2022-01-03 11:13:43 +01:00
PhilippC
48d080bcda New translations strings.xml (Danish) 2022-01-02 20:39:02 +01:00
PhilippC
531cb51fac New translations strings.xml (Slovak) 2022-01-02 13:27:30 +01:00
mcpower
e8bc949230 Fix Xamarin.Android CI build link 2022-01-02 13:03:47 +11:00
PhilippC
d99d43dde7 New translations strings.xml (Croatian) 2021-12-30 09:23:34 +01:00
PhilippC
b64c304a92 New translations strings.xml (Croatian) 2021-12-30 08:21:45 +01:00
PhilippC
d392b1c434 New translations strings.xml (Croatian) 2021-12-29 15:51:28 +01:00
PhilippC
1f9b5df44b New translations strings.xml (Croatian) 2021-12-28 17:34:46 +01:00
PhilippC
edd4067efc New translations strings.xml (Croatian) 2021-12-28 17:34:46 +01:00
PhilippC
1a23b8363a New translations strings.xml (Croatian) 2021-12-28 17:34:44 +01:00
PhilippC
04df628fe8 New translations strings.xml (German) 2021-12-28 16:20:53 +01:00
PhilippC
2bb5a18f27 New translations strings.xml (Hungarian) 2021-12-26 15:48:49 +01:00
PhilippC
6cc7c3b1fb New translations strings.xml (Hungarian) 2021-12-26 14:41:24 +01:00
PhilippC
21c1b1e913 New translations strings.xml (Hungarian) 2021-12-24 13:22:51 +01:00
PhilippC
a1cdba5baa New translations strings.xml (Chinese Traditional) 2021-12-23 17:35:28 +01:00
PhilippC
3b14ffb331 New translations strings.xml (Chinese Traditional) 2021-12-23 16:36:14 +01:00
Philipp Crocoll
b0e6de8a47 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-12-20 09:27:52 +01:00
Philipp Crocoll
2a3138ca76 increase key size for Sftp publickey to 4096 to close #1874. (Note: ED25519 is not supported by standard JSch at the moment) 2021-12-20 09:14:39 +01:00
PhilippC
56a0c6325d New translations strings.xml (Spanish) 2021-12-17 13:25:21 +01:00
PhilippC
69c0485fe8 New translations strings.xml (Korean) 2021-12-17 09:23:47 +01:00
PhilippC
21205f85aa New translations strings.xml (Spanish) 2021-12-17 08:16:43 +01:00
PhilippC
9d10d75d54 New translations strings.xml (Chinese Traditional) 2021-12-16 17:06:37 +01:00
PhilippC
2757f657c5 New translations strings.xml (Chinese Traditional) 2021-12-16 16:06:26 +01:00
PhilippC
1c4040707e New translations strings.xml (Romanian) 2021-12-13 15:11:59 +01:00
PhilippC
ca4a29a991 New translations strings.xml (Romanian) 2021-12-13 14:13:56 +01:00
PhilippC
9120da776c New translations strings.xml (Romanian) 2021-12-13 14:13:55 +01:00
PhilippC
24b2f1fca5 New translations strings.xml (Italian) 2021-12-13 13:18:04 +01:00
PhilippC
f4f0bb166a New translations strings.xml (Italian) 2021-12-13 12:21:02 +01:00
PhilippC
27c8d78ac9 New translations strings.xml (Italian) 2021-12-13 11:09:44 +01:00
PhilippC
fe32105491 New translations strings.xml (Ukrainian) 2021-12-08 01:13:55 +01:00
PhilippC
ad7ef30f68 New translations strings.xml (French) 2021-12-05 19:51:53 +01:00
PhilippC
c1df53e986 New translations strings.xml (French) 2021-12-05 18:51:26 +01:00
PhilippC
36b8f52929 New translations strings.xml (Russian) 2021-12-05 03:19:40 +01:00
PhilippC
f84ab77a9a New translations strings.xml (Russian) 2021-12-05 02:20:51 +01:00
PhilippC
1ec842c274 New translations strings.xml (Arabic) 2021-12-02 16:50:21 +01:00
PhilippC
e06849cd24 New translations strings.xml (Finnish) 2021-11-29 14:19:28 +01:00
PhilippC
3f31533909 New translations strings.xml (Finnish) 2021-11-29 13:20:11 +01:00
Philipp Crocoll
5a408ccd4a manifest for 1.09b-r1 2021-11-29 11:13:29 +01:00
Philipp Crocoll
07d3a4ef84 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-11-29 10:40:39 +01:00
Philipp Crocoll
eb50ed1bc3 throw exception in Google Drive implementation as access is blocked by Google currently. 2021-11-29 10:40:27 +01:00
PhilippC
681d120e8b New translations strings.xml (Polish) 2021-11-26 19:33:42 +01:00
PhilippC
74ae266fed New translations strings.xml (Polish) 2021-11-26 18:36:29 +01:00
PhilippC
028f16af06 New translations strings.xml (Greek) 2021-11-24 16:05:30 +01:00
PhilippC
fa7980f379 New translations strings.xml (Greek) 2021-11-24 15:05:17 +01:00
PhilippC
4ddc173757 New translations strings.xml (Chinese Simplified) 2021-11-23 04:18:51 +01:00
PhilippC
8bc93d7f23 New translations strings.xml (Portuguese, Brazilian) 2021-11-22 15:04:59 +01:00
PhilippC
de72df99ea New translations strings.xml (Slovenian) 2021-11-22 13:15:06 +01:00
PhilippC
580fd60fd9 Merge pull request #1850 from PhilippC/l10n_master2
New Crowdin updates
2021-11-22 10:22:47 +01:00
PhilippC
c884f93ac3 New translations strings.xml (German) 2021-11-22 10:13:24 +01:00
PhilippC
a57ff299b5 New translations strings.xml (Slovak) 2021-11-22 10:07:51 +01:00
PhilippC
159a90d7e3 New translations strings.xml (Slovenian) 2021-11-22 10:07:43 +01:00
PhilippC
b8fe4639ba New translations strings.xml (German) 2021-11-22 10:07:41 +01:00
PhilippC
6a80021bd1 New translations strings.xml (Danish) 2021-11-22 10:07:40 +01:00
PhilippC
d2dc75d556 New translations strings.xml (Spanish) 2021-11-22 10:07:34 +01:00
PhilippC
2aa93b56cb New translations strings.xml (Ukrainian) 2021-11-22 10:07:29 +01:00
PhilippC
11037daca9 New translations strings.xml (Portuguese, Brazilian) 2021-11-22 10:07:18 +01:00
PhilippC
93932e78f9 New translations strings.xml (Chinese Simplified) 2021-11-22 10:07:13 +01:00
PhilippC
7ad476d543 New translations strings.xml (Japanese) 2021-11-22 10:07:11 +01:00
Philipp Crocoll
9b487f94de fix typo 2021-11-22 10:01:42 +01:00
Philipp Crocoll
68e34eb3bd Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-11-22 09:56:06 +01:00
PhilippC
88f95b96b0 Merge pull request #1687 from PhilippC/l10n_master2
New Crowdin updates
2021-11-22 09:55:46 +01:00
PhilippC
e22a69caf5 New translations strings.xml (Portuguese, Brazilian) 2021-11-18 12:00:25 +01:00
PhilippC
bb339504d4 New translations strings.xml (Portuguese, Brazilian) 2021-11-18 10:59:50 +01:00
PhilippC
2666c5248b New translations strings.xml (Chinese Simplified) 2021-11-18 03:12:44 +01:00
PhilippC
c0df8e9a04 New translations strings.xml (Spanish) 2021-11-17 15:57:29 +01:00
PhilippC
bead854cf1 New translations strings.xml (Spanish) 2021-11-17 14:53:54 +01:00
PhilippC
48da600598 New translations strings.xml (German) 2021-11-17 12:40:27 +01:00
Philipp Crocoll
3b0b349f4c manifest and changelog for 1.09-pre2 2021-11-17 12:06:56 +01:00
Philipp Crocoll
294e6f5edf add option to sync database after quick-unlocking the database. closes https://github.com/PhilippC/keepass2android/issues/188 2021-11-17 11:30:43 +01:00
PhilippC
02fb27f48e New translations strings.xml (Danish) 2021-11-17 00:57:06 +01:00
PhilippC
e57a02408e New translations strings.xml (Ukrainian) 2021-11-16 05:45:43 +01:00
PhilippC
5325381f2f New translations strings.xml (Slovak) 2021-11-15 22:43:41 +01:00
PhilippC
4176b06578 New translations strings.xml (Slovak) 2021-11-15 21:44:56 +01:00
Philipp Crocoll
7014f5d9f1 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-11-15 11:27:08 +01:00
Philipp Crocoll
afbef4272c don't show "Third party app" in Android 11+ (doesn't work due to file access permissions, avoids confusion). Closes https://github.com/PhilippC/keepass2android/issues/1468, closes https://github.com/PhilippC/keepass2android/issues/1606 2021-11-15 11:26:58 +01:00
Philipp Crocoll
45df2d6605 preserve filename case in Dropbox when uploading files, closes https://github.com/PhilippC/keepass2android/issues/898 2021-11-15 09:32:02 +01:00
PhilippC
1c2369c419 New translations strings.xml (Slovenian) 2021-11-15 09:06:35 +01:00
PhilippC
81211ecb74 New translations strings.xml (Japanese) 2021-11-14 15:51:46 +01:00
PhilippC
17a06adfd9 New translations strings.xml (Japanese) 2021-11-14 14:49:25 +01:00
PhilippC
da565e43bb New translations strings.xml (German) 2021-11-14 09:39:46 +01:00
PhilippC
641497a8d3 New translations strings.xml (Portuguese, Brazilian) 2021-11-12 18:50:46 +01:00
PhilippC
7cecadcf82 Merge pull request #1838 from mhayen/patch-1
Update Documentation.md
2021-11-12 11:40:16 +01:00
Philipp Crocoll
cd776754b5 add package visibility for AncientIconSet 2021-11-12 11:23:49 +01:00
Philipp Crocoll
9d2d920fda introduce <queries> in manifest to work with Android 11+ limited package visibility 2021-11-12 10:57:04 +01:00
Philipp Crocoll
cb30d3a79e Merge branch 'master' of c:/ph/keepass2android 2021-11-12 08:37:50 +01:00
Philipp Crocoll
3842cbffc4 manifest and csproj for 1.09b-pre0 2021-11-12 08:37:10 +01:00
PhilippC
1cf7cbc144 New translations strings.xml (Chinese Simplified) 2021-11-12 02:13:58 +01:00
Mark
73e8291cb0 Update Documentation.md
added missing space after github issue url
2021-11-12 00:18:04 +01:00
Philipp Crocoll
1e89c8dac1 remove log, closes https://github.com/PhilippC/keepass2android/issues/1832 2021-11-11 20:22:01 +01:00
Philipp Crocoll
81c4477453 allow to change app language, closes https://github.com/PhilippC/keepass2android/issues/201 2021-11-11 12:12:24 +01:00
Philipp Crocoll
adb0865aa7 add note for Google Drive 2021-11-11 09:16:18 +01:00
Philipp Crocoll
d65ae7767a update targetSdkVersion to 30 (as requested by Play store); manifest for 1.09b-pre0 2021-11-11 09:04:26 +01:00
Philipp Crocoll
d83474d6f6 disable "Inline suggestions preference" on Android < 11 2021-11-11 08:57:10 +01:00
Philipp Crocoll
5cffec4724 create Changelog for 1.09b 2021-11-11 08:52:28 +01:00
Philipp Crocoll
8af2f51eae implement inline suggestions for autofill service on Android 11+, closes https://github.com/PhilippC/keepass2android/issues/1374 and https://github.com/PhilippC/keepass2android/issues/1534 2021-11-10 13:02:35 +01:00
Philipp Crocoll
39579b0183 rename app project and associated file 2021-11-10 13:00:38 +01:00
Philipp Crocoll
caff2e25e2 update to build with Android 11 Build tools. Major reconfiguration of csproj file, simplifying PackageReferences. 2021-11-10 11:21:45 +01:00
Philipp Crocoll
0fee6e2298 Autofill: treat android.widget.AutoCompleteTextView as EditText as well (is direct subclass) 2021-11-08 09:30:42 +01:00
PhilippC
feaad1300a New translations strings.xml (Japanese) 2021-11-08 00:06:00 +01:00
PhilippC
1a59129d88 New translations strings.xml (Swedish) 2021-11-07 20:05:49 +01:00
PhilippC
1c7fa8d41d New translations strings.xml (Japanese) 2021-11-06 05:21:37 +01:00
PhilippC
1480657857 New translations strings.xml (Japanese) 2021-11-06 04:24:23 +01:00
PhilippC
c8ab5836eb New translations strings.xml (Japanese) 2021-11-05 10:58:39 +01:00
Philipp Crocoll
2bb7a2065a Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-10-25 12:49:00 +02:00
Philipp Crocoll
0564a103e1 fix flickering autofill popup in Firefox by disabling compat mode for fenix and later, closes #1808 2021-10-25 12:48:48 +02:00
PhilippC
cd0954badb New translations strings.xml (Portuguese) 2021-10-22 18:59:31 +02:00
PhilippC
2e5a1a9820 New translations strings.xml (Portuguese) 2021-10-22 18:03:33 +02:00
PhilippC
8acc6dec1b New translations strings.xml (German) 2021-10-15 23:16:42 +02:00
PhilippC
3f0bbbb19e New translations strings.xml (Russian) 2021-10-15 22:02:13 +02:00
PhilippC
16e9077cd4 New translations strings.xml (Hungarian) 2021-10-13 11:42:49 +02:00
PhilippC
53bb822013 New translations strings.xml (Dutch) 2021-10-03 11:36:55 +02:00
PhilippC
37c1da8962 New translations strings.xml (Dutch) 2021-10-03 10:35:45 +02:00
PhilippC
75535c8edd New translations strings.xml (Spanish) 2021-09-28 23:11:13 +02:00
PhilippC
36c7e9eecc New translations strings.xml (Spanish) 2021-09-28 22:15:09 +02:00
PhilippC
fee5103bdd New translations strings.xml (Chinese Simplified) 2021-09-22 13:25:31 +02:00
PhilippC
4a5a77de21 Merge pull request #1793 from ConorIA/patch-1
Allow autofill in prerelease Brave browser.
2021-09-20 08:46:15 +02:00
PhilippC
1b5f25f10b New translations strings.xml (Polish) 2021-09-15 20:35:10 +02:00
PhilippC
4cf67734a1 New translations strings.xml (Polish) 2021-09-15 20:35:09 +02:00
PhilippC
b6778fb331 New translations strings.xml (Polish) 2021-09-15 19:39:47 +02:00
PhilippC
2ae50c2fbd New translations strings.xml (Spanish) 2021-09-15 10:12:29 +02:00
Conor Anderson
f33ad9e403 Allow autofill in prerelease Brave browser.
Propose adding the Beta and Nightly versions for Brave. I assume prerelease is fair game since Chrome Canary is on there.
2021-09-14 18:16:41 -04:00
PhilippC
4f7d58d8b4 New translations strings.xml (Portuguese) 2021-09-14 14:46:21 +02:00
PhilippC
6582faa8d7 New translations strings.xml (Chinese Simplified) 2021-09-10 10:08:56 +02:00
PhilippC
387f584b2c New translations strings.xml (Chinese Simplified) 2021-09-10 09:12:34 +02:00
PhilippC
b320c0fd5a New translations strings.xml (Ukrainian) 2021-09-09 01:27:22 +02:00
PhilippC
30294a0195 New translations strings.xml (Polish) 2021-09-08 13:43:22 +02:00
PhilippC
252778bf7c New translations strings.xml (Czech) 2021-08-31 00:43:46 +02:00
PhilippC
80bd3c2043 New translations strings.xml (French) 2021-08-30 22:22:02 +02:00
PhilippC
eb968ba676 New translations strings.xml (Portuguese, Brazilian) 2021-08-29 12:37:30 +02:00
PhilippC
1abe428559 New translations strings.xml (Danish) 2021-08-28 11:45:46 +02:00
PhilippC
9854a2ad33 New translations strings.xml (Slovak) 2021-08-27 22:50:46 +02:00
PhilippC
54ebe89d44 New translations strings.xml (Spanish) 2021-08-24 23:58:21 +02:00
PhilippC
2565a04c83 New translations strings.xml (Slovenian) 2021-08-23 09:35:38 +02:00
PhilippC
bb11bdf423 New translations strings.xml (Chinese Simplified) 2021-08-23 04:32:23 +02:00
PhilippC
d6983265ce New translations strings.xml (Dutch) 2021-08-22 17:45:05 +02:00
PhilippC
0034999256 New translations strings.xml (Korean) 2021-08-22 17:45:03 +02:00
PhilippC
10004bd67e New translations strings.xml (Hungarian) 2021-08-22 17:45:02 +02:00
PhilippC
a5c35b8fa9 New translations strings.xml (Greek) 2021-08-22 17:44:58 +02:00
PhilippC
c0d7626ec3 New translations strings.xml (German) 2021-08-22 17:44:57 +02:00
PhilippC
1fbd310371 New translations strings.xml (Polish) 2021-08-22 17:44:56 +02:00
PhilippC
5f0b5919a4 New translations strings.xml (Danish) 2021-08-22 17:44:55 +02:00
PhilippC
ae41b34f13 New translations strings.xml (Belarusian) 2021-08-22 17:44:53 +02:00
PhilippC
4ecb7ef8a6 New translations strings.xml (Arabic) 2021-08-22 17:44:52 +02:00
PhilippC
bfef18bfe7 New translations strings.xml (Spanish) 2021-08-22 17:44:51 +02:00
PhilippC
0448aca24e New translations strings.xml (French) 2021-08-22 17:44:50 +02:00
PhilippC
af67e29ea3 New translations strings.xml (Czech) 2021-08-22 17:44:49 +02:00
PhilippC
f12ee10b9d New translations strings.xml (Catalan) 2021-08-22 17:44:48 +02:00
PhilippC
aea585a93d New translations strings.xml (Italian) 2021-08-22 17:44:47 +02:00
PhilippC
a5b30b557a New translations strings.xml (Portuguese) 2021-08-22 17:44:46 +02:00
PhilippC
a52ebcf576 New translations strings.xml (Slovak) 2021-08-22 17:44:45 +02:00
PhilippC
62fcfdfc95 New translations strings.xml (Portuguese, Brazilian) 2021-08-22 17:44:39 +02:00
PhilippC
a69e5d9c5f New translations strings.xml (Russian) 2021-08-22 17:44:38 +02:00
PhilippC
72ffaf39f4 New translations strings.xml (Chinese Traditional) 2021-08-22 17:44:36 +02:00
PhilippC
08734d733f New translations strings.xml (Chinese Simplified) 2021-08-22 17:44:35 +02:00
PhilippC
e3ddad2c64 New translations strings.xml (Ukrainian) 2021-08-22 17:44:34 +02:00
PhilippC
693cf3608a New translations strings.xml (Turkish) 2021-08-22 17:44:33 +02:00
PhilippC
ad9f49f66e New translations strings.xml (Swedish) 2021-08-22 17:44:32 +02:00
PhilippC
6910414ba3 New translations strings.xml (Slovenian) 2021-08-22 17:44:31 +02:00
PhilippC
f8bb39384b New translations strings.xml (Japanese) 2021-08-22 17:44:29 +02:00
Philipp Crocoll
e7b4cfe53e Manifest for 1.09a-r3 2021-08-22 17:40:57 +02:00
PhilippC
8e6146dd46 New translations strings.xml (Italian) 2021-08-11 19:55:41 +02:00
PhilippC
50254c497a New translations strings.xml (Japanese) 2021-08-11 05:49:57 +02:00
PhilippC
66a7469cd5 New translations strings.xml (Japanese) 2021-08-11 05:49:56 +02:00
PhilippC
70cf30b8a3 New translations strings.xml (Japanese) 2021-08-11 04:46:11 +02:00
PhilippC
1549d71af0 New translations strings.xml (Japanese) 2021-08-11 03:48:42 +02:00
PhilippC
69c95a4073 New translations strings.xml (Japanese) 2021-08-11 02:45:25 +02:00
PhilippC
6c79c1539a New translations strings.xml (Japanese) 2021-08-10 12:01:38 +02:00
PhilippC
0b7d45d3b9 New translations strings.xml (Dutch) 2021-07-26 19:12:55 +02:00
PhilippC
edd1b683dd New translations strings.xml (Dutch) 2021-07-26 18:15:05 +02:00
PhilippC
eccd8e3810 New translations strings.xml (Russian) 2021-07-22 10:03:59 +02:00
PhilippC
8855541ee1 New translations strings.xml (French) 2021-07-20 07:49:53 +02:00
PhilippC
fd300add4f New translations strings.xml (Belarusian) 2021-07-19 09:48:37 +02:00
PhilippC
3161e04752 New translations strings.xml (Belarusian) 2021-07-19 08:51:44 +02:00
PhilippC
ca1a51749a New translations strings.xml (Czech) 2021-07-05 00:21:19 +02:00
Philipp Crocoll
5aba209857 Merge branch 'master' of c:/ph/keepass2android 2021-07-03 15:50:39 +02:00
Philipp Crocoll
80cfb9e098 fix to superfluous dropbox login requests 2021-07-03 15:50:25 +02:00
Philipp Crocoll
b98c887d71 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 14:52:10 +02:00
PhilippC
be8e8f9b02 New translations strings.xml (Dutch) 2021-07-03 14:49:44 +02:00
PhilippC
87cbf65654 New translations strings.xml (Dutch) 2021-07-03 14:45:24 +02:00
Philipp Crocoll
6a0e132945 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 14:41:21 +02:00
PhilippC
438cf5793a New translations strings.xml (Dutch) 2021-07-03 14:37:44 +02:00
PhilippC
74a736ef87 New translations strings.xml (Spanish) 2021-07-03 14:37:15 +02:00
Philipp Crocoll
4ce451b99c Merge branch 'master' of c:/ph/keepass2android 2021-07-03 13:28:02 +02:00
Philipp Crocoll
f017d3fdf2 remove intermediate file 2021-07-03 13:27:54 +02:00
Philipp Crocoll
54eb656726 1.09a-r1 release manifest 2021-07-03 13:27:38 +02:00
Philipp Crocoll
9d63578110 fix potential crash with invalid OTP strings 2021-07-03 13:27:10 +02:00
Philipp Crocoll
06baeeb79a Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 13:25:20 +02:00
PhilippC
a77fe463d8 New translations strings.xml (Ukrainian) 2021-07-02 17:09:44 +02:00
PhilippC
c016fc164d New translations strings.xml (Polish) 2021-07-02 17:09:43 +02:00
PhilippC
a409bb1318 New translations strings.xml (Polish) 2021-07-02 16:09:14 +02:00
PhilippC
7eeaf205cd New translations strings.xml (Chinese Simplified) 2021-06-29 05:47:30 +02:00
PhilippC
85882b2547 New translations strings.xml (Chinese Simplified) 2021-06-29 04:47:27 +02:00
PhilippC
12e4af7b0d New translations strings.xml (Portuguese, Brazilian) 2021-06-28 19:57:40 +02:00
PhilippC
774406fe00 New translations strings.xml (Portuguese, Brazilian) 2021-06-28 19:00:23 +02:00
PhilippC
e7c8534c01 New translations strings.xml (Slovenian) 2021-06-28 13:04:59 +02:00
PhilippC
0655dd41bb New translations strings.xml (German) 2021-06-28 12:06:31 +02:00
PhilippC
48bb71d5dc New translations strings.xml (Ukrainian) 2021-06-28 11:07:43 +02:00
PhilippC
b2641fe4d3 New translations strings.xml (Chinese Simplified) 2021-06-28 11:07:42 +02:00
PhilippC
e58de5a99f New translations strings.xml (Portuguese, Brazilian) 2021-06-28 11:07:38 +02:00
PhilippC
68df9ada01 New translations strings.xml (Slovenian) 2021-06-28 11:07:34 +02:00
PhilippC
a91a711499 New translations strings.xml (Slovak) 2021-06-28 11:07:23 +02:00
PhilippC
780ab1125d New translations strings.xml (Polish) 2021-06-28 11:07:22 +02:00
PhilippC
2e83495542 New translations strings.xml (Portuguese) 2021-06-28 11:07:18 +02:00
PhilippC
b69b7a8b5f New translations strings.xml (French) 2021-06-28 11:07:13 +02:00
PhilippC
00f264c313 New translations strings.xml (Russian) 2021-06-28 11:07:11 +02:00
PhilippC
ba9eb65fa6 New translations strings.xml (Czech) 2021-06-28 11:07:09 +02:00
PhilippC
824a7484e0 New translations strings.xml (Danish) 2021-06-28 11:07:07 +02:00
PhilippC
7ebab48b97 New translations strings.xml (German) 2021-06-28 11:07:06 +02:00
PhilippC
708bd9bc8a New translations strings.xml (Greek) 2021-06-28 11:07:05 +02:00
PhilippC
960b00b9ca New translations strings.xml (Portuguese) 2021-06-28 11:06:33 +02:00
PhilippC
d654137caa New translations strings.xml (Belarusian) 2021-06-28 11:06:15 +02:00
Philipp Crocoll
e4b36c172b extended changelog 1.09a 2021-06-28 11:02:46 +02:00
Philipp Crocoll
05f8a067f1 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-06-28 10:56:19 +02:00
Philipp Crocoll
d3db23d1b7 update to Dropbox Core SDK v4.0.0, add support for short-lived access tokens + refresh token (as the previous method will be removed) 2021-06-28 10:56:04 +02:00
PhilippC
30288a3106 New translations strings.xml (Belarusian) 2021-06-25 21:36:19 +02:00
PhilippC
89410e60c7 New translations strings.xml (Belarusian) 2021-06-25 20:36:35 +02:00
PhilippC
3e971b5fe9 New translations strings.xml (Belarusian) 2021-06-25 17:39:25 +02:00
PhilippC
7673e4d638 New translations strings.xml (Portuguese) 2021-06-25 16:32:44 +02:00
PhilippC
9a76ea1433 New translations strings.xml (Portuguese) 2021-06-25 15:26:11 +02:00
PhilippC
92ff60c66d New translations strings.xml (Belarusian) 2021-06-25 15:26:10 +02:00
PhilippC
dfad45f83d New translations strings.xml (Belarusian) 2021-06-25 14:28:35 +02:00
PhilippC
6c0a11d300 New translations strings.xml (Belarusian) 2021-06-25 13:16:55 +02:00
PhilippC
54f58067d3 New translations strings.xml (Azerbaijani) 2021-06-25 12:14:28 +02:00
PhilippC
edb23bef1e New translations strings.xml (Azerbaijani) 2021-06-25 12:14:27 +02:00
PhilippC
cddd0705d8 New translations strings.xml (Portuguese) 2021-06-25 12:14:26 +02:00
PhilippC
fc4447748b New translations strings.xml (Belarusian) 2021-06-25 12:14:25 +02:00
PhilippC
433310030f New translations strings.xml (Azerbaijani) 2021-06-25 11:19:05 +02:00
PhilippC
388fb2ba34 New translations strings.xml (Azerbaijani) 2021-06-25 11:19:04 +02:00
PhilippC
d2a19967bc New translations strings.xml (Azerbaijani) 2021-06-25 11:19:03 +02:00
PhilippC
b2538c0133 New translations strings.xml (Portuguese) 2021-06-25 11:19:01 +02:00
PhilippC
dde2fdb103 New translations strings.xml (Azerbaijani) 2021-06-25 10:22:31 +02:00
PhilippC
7a83064745 New translations strings.xml (Swedish) 2021-06-25 08:33:18 +02:00
PhilippC
970ebfede5 New translations strings.xml (Belarusian) 2021-06-25 08:33:17 +02:00
PhilippC
4ecc56fa79 New translations strings.xml (Azerbaijani) 2021-06-25 07:30:19 +02:00
PhilippC
1e25bc6c7c New translations strings.xml (Azerbaijani) 2021-06-25 07:30:18 +02:00
PhilippC
8eca4a1d19 New translations strings.xml (Azerbaijani) 2021-06-25 07:30:17 +02:00
PhilippC
c6f7a0ba1a New translations strings.xml (Belarusian) 2021-06-25 07:30:16 +02:00
PhilippC
fe07cf6060 New translations strings.xml (Belarusian) 2021-06-25 06:31:31 +02:00
PhilippC
022c26c89d New translations strings.xml (Portuguese) 2021-06-24 22:12:06 +02:00
PhilippC
a13bcaf223 New translations strings.xml (Portuguese) 2021-06-24 21:13:33 +02:00
PhilippC
8b469ed263 New translations strings.xml (Portuguese) 2021-06-24 21:13:32 +02:00
PhilippC
ff631935fc New translations strings.xml (Belarusian) 2021-06-24 08:39:37 +02:00
PhilippC
6fd363e071 New translations strings.xml (Belarusian) 2021-06-24 07:34:00 +02:00
PhilippC
57556764e9 New translations strings.xml (Belarusian) 2021-06-24 06:33:51 +02:00
PhilippC
fbef5de705 New translations strings.xml (Belarusian) 2021-06-23 17:06:40 +02:00
PhilippC
bff035f8a5 New translations strings.xml (Belarusian) 2021-06-23 16:05:18 +02:00
PhilippC
272a124d3a New translations strings.xml (Belarusian) 2021-06-23 07:49:26 +02:00
PhilippC
ae5fd9765d New translations strings.xml (Portuguese) 2021-06-22 19:09:30 +02:00
PhilippC
2900b293c3 New translations strings.xml (Portuguese) 2021-06-22 18:02:48 +02:00
PhilippC
b8fbca0d49 New translations strings.xml (Bulgarian) 2021-06-19 16:14:03 +02:00
PhilippC
370f5a5b4e New translations strings.xml (Bulgarian) 2021-06-19 09:42:54 +02:00
PhilippC
9558786b76 New translations strings.xml (Bulgarian) 2021-06-19 09:42:53 +02:00
PhilippC
c0260974bd New translations strings.xml (Bulgarian) 2021-06-19 08:31:57 +02:00
PhilippC
f864af6003 New translations strings.xml (Bulgarian) 2021-06-19 08:31:56 +02:00
PhilippC
30fc5e4646 New translations strings.xml (Bulgarian) 2021-06-19 07:33:12 +02:00
PhilippC
3dee4935c8 New translations strings.xml (Belarusian) 2021-06-18 15:56:37 +02:00
PhilippC
0e088a48b8 New translations strings.xml (Belarusian) 2021-06-18 14:53:27 +02:00
PhilippC
0928ab0224 New translations strings.xml (Belarusian) 2021-06-18 09:53:48 +02:00
PhilippC
ec07a61cab New translations strings.xml (Belarusian) 2021-06-18 08:55:55 +02:00
PhilippC
14d7eaf361 New translations strings.xml (Belarusian) 2021-06-18 07:48:54 +02:00
PhilippC
0f2892bd28 New translations strings.xml (Belarusian) 2021-06-17 16:56:44 +02:00
PhilippC
51ae5a2a0c New translations strings.xml (Belarusian) 2021-06-17 15:55:16 +02:00
PhilippC
5bb5fc9496 New translations strings.xml (Belarusian) 2021-06-17 09:30:17 +02:00
PhilippC
9e550f85cc New translations strings.xml (Belarusian) 2021-06-17 08:16:41 +02:00
PhilippC
4ef46e302d New translations strings.xml (Belarusian) 2021-06-17 07:10:51 +02:00
PhilippC
079806c857 New translations strings.xml (Belarusian) 2021-06-16 15:57:43 +02:00
PhilippC
deed92af0d New translations strings.xml (Belarusian) 2021-06-16 14:53:47 +02:00
PhilippC
48367c5926 New translations strings.xml (Belarusian) 2021-06-16 13:37:40 +02:00
PhilippC
0f3cdf77d4 New translations strings.xml (Belarusian) 2021-06-16 12:38:04 +02:00
PhilippC
4b06833c3f New translations strings.xml (Belarusian) 2021-06-16 10:10:39 +02:00
PhilippC
8a6bfe3288 New translations strings.xml (Belarusian) 2021-06-16 08:57:17 +02:00
PhilippC
2d106f7b87 New translations strings.xml (Belarusian) 2021-06-16 08:57:16 +02:00
PhilippC
130ef249d7 New translations strings.xml (Belarusian) 2021-06-16 08:57:15 +02:00
PhilippC
5b55cf0edc New translations strings.xml (Belarusian) 2021-06-16 07:45:40 +02:00
PhilippC
1d824049cb New translations strings.xml (Belarusian) 2021-06-16 06:37:46 +02:00
PhilippC
2eef3cfa00 New translations strings.xml (Belarusian) 2021-06-14 15:50:17 +02:00
PhilippC
f82cfeed3d New translations strings.xml (Belarusian) 2021-06-14 15:50:14 +02:00
PhilippC
3aba034155 New translations strings.xml (Belarusian) 2021-06-14 14:48:40 +02:00
PhilippC
114a0832cb New translations strings.xml (Belarusian) 2021-06-14 14:48:39 +02:00
PhilippC
66f627fd1a New translations strings.xml (Belarusian) 2021-06-14 13:48:35 +02:00
PhilippC
1ce915cd9e New translations strings.xml (Belarusian) 2021-06-14 12:39:16 +02:00
Philipp Crocoll
712f476da8 call SetInvalidatedByBiometricEnrollment for security reasons, but show a warning to users (again) that they should not forget their master key 2021-06-14 11:43:33 +02:00
PhilippC
2d0e5d208b New translations strings.xml (Belarusian) 2021-06-14 09:55:24 +02:00
PhilippC
053cef944a New translations strings.xml (Belarusian) 2021-06-11 08:40:22 +02:00
PhilippC
ff2ba3f4ed New translations strings.xml (Belarusian) 2021-06-11 07:35:26 +02:00
PhilippC
ec7d908941 New translations strings.xml (Belarusian) 2021-06-11 06:36:43 +02:00
PhilippC
1afbd4c632 New translations strings.xml (Belarusian) 2021-06-10 07:52:15 +02:00
PhilippC
a5af23b2f7 New translations strings.xml (Belarusian) 2021-06-10 07:52:15 +02:00
PhilippC
78ac8f45e9 New translations strings.xml (Belarusian) 2021-06-10 06:55:42 +02:00
PhilippC
c9ade3ad3f New translations strings.xml (Belarusian) 2021-06-09 11:12:10 +02:00
PhilippC
c588d3d581 New translations strings.xml (Belarusian) 2021-06-09 10:04:57 +02:00
PhilippC
0140b4f74e New translations strings.xml (Belarusian) 2021-06-09 09:09:44 +02:00
PhilippC
eb42d33630 New translations strings.xml (Belarusian) 2021-06-09 09:09:43 +02:00
PhilippC
f6b0eab8b1 New translations strings.xml (Belarusian) 2021-06-09 08:04:33 +02:00
PhilippC
b316c21623 New translations strings.xml (Belarusian) 2021-06-09 08:04:32 +02:00
PhilippC
bd661fddb1 New translations strings.xml (Belarusian) 2021-06-09 07:01:16 +02:00
PhilippC
fd5ca57f36 New translations strings.xml (Portuguese, Brazilian) 2021-06-06 13:32:27 +02:00
PhilippC
d38d446a70 New translations strings.xml (Ukrainian) 2021-06-06 00:02:01 +02:00
PhilippC
4f97e6e8b9 New translations strings.xml (Ukrainian) 2021-06-05 23:02:21 +02:00
PhilippC
e79450a54d New translations strings.xml (Belarusian) 2021-06-04 13:05:49 +02:00
PhilippC
44fd071c9d New translations strings.xml (Belarusian) 2021-06-04 13:05:48 +02:00
PhilippC
8487b935c4 New translations strings.xml (Belarusian) 2021-06-04 12:02:23 +02:00
PhilippC
f497194281 New translations strings.xml (Belarusian) 2021-06-04 09:00:43 +02:00
PhilippC
614e014cab New translations strings.xml (Belarusian) 2021-06-04 08:05:06 +02:00
PhilippC
5bf976747c New translations strings.xml (Belarusian) 2021-06-04 07:08:18 +02:00
PhilippC
5c2a7f27e6 New translations strings.xml (Belarusian) 2021-06-01 09:10:28 +02:00
PhilippC
4c059b625c New translations strings.xml (Belarusian) 2021-06-01 08:05:22 +02:00
PhilippC
1860a92fa0 New translations strings.xml (Belarusian) 2021-06-01 08:05:21 +02:00
PhilippC
f4799acf31 New translations strings.xml (Belarusian) 2021-06-01 06:58:46 +02:00
PhilippC
a704e523d9 New translations strings.xml (Belarusian) 2021-05-31 19:42:56 +02:00
PhilippC
725cdf3ec3 New translations strings.xml (Belarusian) 2021-05-31 18:34:07 +02:00
PhilippC
f3a3956d79 New translations strings.xml (Belarusian) 2021-05-31 10:59:49 +02:00
PhilippC
167cd39c8f New translations strings.xml (Belarusian) 2021-05-31 10:00:19 +02:00
PhilippC
681492f5f8 New translations strings.xml (Belarusian) 2021-05-31 10:00:18 +02:00
PhilippC
80766034a6 New translations strings.xml (Belarusian) 2021-05-31 09:00:58 +02:00
PhilippC
4e7b96462c New translations strings.xml (Belarusian) 2021-05-31 07:59:43 +02:00
PhilippC
761cae4a2b New translations strings.xml (Slovak) 2021-05-28 15:02:13 +02:00
PhilippC
d0d526d36d New translations strings.xml (Slovak) 2021-05-28 15:02:12 +02:00
PhilippC
f29cfd55fa New translations strings.xml (Slovak) 2021-05-28 14:05:32 +02:00
PhilippC
7a4116c016 New translations strings.xml (Belarusian) 2021-05-28 10:08:24 +02:00
PhilippC
5639517128 New translations strings.xml (Belarusian) 2021-05-28 09:10:25 +02:00
PhilippC
17c7b9c526 New translations strings.xml (Belarusian) 2021-05-28 09:10:23 +02:00
PhilippC
e40c5aa821 New translations strings.xml (Belarusian) 2021-05-28 09:10:23 +02:00
PhilippC
1915a8b1db New translations strings.xml (Belarusian) 2021-05-28 08:11:46 +02:00
PhilippC
c105312b99 New translations strings.xml (Belarusian) 2021-05-28 08:11:45 +02:00
PhilippC
2b5d4365c2 New translations strings.xml (Belarusian) 2021-05-28 06:54:24 +02:00
PhilippC
f145c68cfe New translations strings.xml (Belarusian) 2021-05-28 06:54:23 +02:00
PhilippC
2fff53f468 New translations strings.xml (Belarusian) 2021-05-27 17:07:32 +02:00
PhilippC
6a1e75bf51 New translations strings.xml (Belarusian) 2021-05-27 17:07:31 +02:00
PhilippC
4d903239ca New translations strings.xml (Belarusian) 2021-05-27 17:07:30 +02:00
PhilippC
570133d365 New translations strings.xml (Belarusian) 2021-05-27 16:09:15 +02:00
PhilippC
9cc49389e4 New translations strings.xml (Belarusian) 2021-05-27 16:09:14 +02:00
PhilippC
cfcc472c4c New translations strings.xml (Belarusian) 2021-05-27 09:36:50 +02:00
PhilippC
79ff2c1adf New translations strings.xml (Belarusian) 2021-05-27 09:36:49 +02:00
PhilippC
01e5aad370 New translations strings.xml (Russian) 2021-05-27 09:36:48 +02:00
PhilippC
50ba6c2750 New translations strings.xml (Czech) 2021-05-27 01:59:51 +02:00
PhilippC
49a4c8e771 New translations strings.xml (Czech) 2021-05-27 00:58:59 +02:00
PhilippC
97de25e2ed New translations strings.xml (Czech) 2021-05-27 00:00:45 +02:00
PhilippC
7a6d9392d3 New translations strings.xml (Russian) 2021-05-26 16:02:19 +02:00
PhilippC
4738db9b7d New translations strings.xml (Belarusian) 2021-05-26 15:06:28 +02:00
PhilippC
172e75e4e8 New translations strings.xml (Russian) 2021-05-26 15:06:27 +02:00
PhilippC
c4795547c8 New translations strings.xml (Belarusian) 2021-05-26 14:08:05 +02:00
PhilippC
fefc6ca04b New translations strings.xml (Danish) 2021-05-26 00:15:45 +02:00
PhilippC
b00945a815 New translations strings.xml (Danish) 2021-05-25 23:14:40 +02:00
PhilippC
3ad7112011 New translations strings.xml (Greek) 2021-05-25 10:58:25 +02:00
PhilippC
9b49df08e3 New translations strings.xml (Greek) 2021-05-25 09:41:14 +02:00
PhilippC
06ba923c28 New translations strings.xml (Danish) 2021-05-24 14:44:22 +02:00
PhilippC
67c5e58c2c New translations strings.xml (Belarusian) 2021-05-24 13:33:32 +02:00
PhilippC
e8c5e0d911 New translations strings.xml (Belarusian) 2021-05-24 13:33:31 +02:00
PhilippC
668836e646 New translations strings.xml (Belarusian) 2021-05-24 13:33:30 +02:00
Philipp Crocoll
e1cd8123cf improve pCloud warning to close #796 2021-05-24 10:37:56 +02:00
PhilippC
6cfeb715e5 New translations strings.xml (Spanish) 2021-05-23 23:03:12 +02:00
PhilippC
19419dc483 New translations strings.xml (Danish) 2021-05-23 18:59:35 +02:00
PhilippC
1aac650a3d New translations strings.xml (Danish) 2021-05-23 18:59:34 +02:00
PhilippC
7030c9e29b New translations strings.xml (Danish) 2021-05-23 18:01:51 +02:00
PhilippC
730fbe211c New translations strings.xml (Danish) 2021-05-23 10:24:09 +02:00
PhilippC
0993ea5e46 New translations strings.xml (Polish) 2021-05-21 17:15:30 +02:00
PhilippC
0cc4fc76fd New translations strings.xml (Polish) 2021-05-21 16:17:01 +02:00
PhilippC
3a6f536cd9 New translations strings.xml (Chinese Simplified) 2021-05-21 05:40:06 +02:00
PhilippC
4809037fdc New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:47 +02:00
PhilippC
3d9c084e59 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:46 +02:00
PhilippC
75f64c31a9 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:45 +02:00
PhilippC
4d64c06f79 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 21:58:10 +02:00
PhilippC
f6a4f46b12 New translations strings.xml (French) 2021-05-19 16:45:14 +02:00
PhilippC
df68fd70f7 New translations strings.xml (French) 2021-05-19 15:48:20 +02:00
PhilippC
2631982e62 New translations strings.xml (Polish) 2021-05-19 14:53:11 +02:00
PhilippC
bcbc89f3a3 New translations strings.xml (Slovenian) 2021-05-18 21:53:27 +02:00
PhilippC
d5f5d9a6b1 Merge pull request #1676 from PhilippC/l10n_master2
New Crowdin updates
2021-05-18 20:55:44 +02:00
PhilippC
01028a956a New translations strings.xml (German) 2021-05-18 20:53:38 +02:00
PhilippC
eb55d15a2f New translations strings.xml (Chinese Simplified) 2021-05-18 20:53:36 +02:00
PhilippC
8219ea74b2 New translations strings.xml (Slovenian) 2021-05-18 20:53:18 +02:00
PhilippC
18b1cb6805 New translations strings.xml (Chinese Simplified) 2021-05-18 20:45:26 +02:00
PhilippC
b0f9dbb062 New translations strings.xml (German) 2021-05-18 20:45:14 +02:00
PhilippC
94118d31f1 New translations strings.xml (Slovenian) 2021-05-18 20:44:49 +02:00
Philipp Crocoll
44c52ee47c Merge branch 'master' of c:/ph/keepass2android 2021-05-18 20:43:49 +02:00
Philipp Crocoll
b71b9c523c add password changes to changelog 2021-05-18 20:43:39 +02:00
PhilippC
06b19dc05f New translations strings.xml (Basque) 2021-05-18 20:32:35 +02:00
PhilippC
6a05c0348b New translations strings.xml (Finnish) 2021-05-18 20:32:33 +02:00
PhilippC
c04184d715 New translations strings.xml (Hebrew) 2021-05-18 20:32:31 +02:00
PhilippC
c3bfdfcc29 New translations strings.xml (Hungarian) 2021-05-18 20:32:29 +02:00
PhilippC
b6a83339e8 New translations strings.xml (Italian) 2021-05-18 20:32:27 +02:00
PhilippC
5a50b1b91f New translations strings.xml (Japanese) 2021-05-18 20:32:26 +02:00
PhilippC
31f3b70fdb New translations strings.xml (Korean) 2021-05-18 20:32:24 +02:00
PhilippC
c4673f87d7 New translations strings.xml (Dutch) 2021-05-18 20:32:22 +02:00
PhilippC
3b5822fd8f New translations strings.xml (Polish) 2021-05-18 20:32:20 +02:00
PhilippC
f0d40713ae New translations strings.xml (Greek) 2021-05-18 20:32:18 +02:00
PhilippC
a1a249421c New translations strings.xml (Romanian) 2021-05-18 20:32:16 +02:00
PhilippC
fe927f8111 New translations strings.xml (French) 2021-05-18 20:32:14 +02:00
PhilippC
b639a0bc9f New translations strings.xml (Arabic) 2021-05-18 20:32:11 +02:00
PhilippC
1ce9afb8be New translations strings.xml (Catalan) 2021-05-18 20:32:08 +02:00
PhilippC
a277511be9 New translations strings.xml (Czech) 2021-05-18 20:32:06 +02:00
PhilippC
25e9b51d04 New translations strings.xml (Danish) 2021-05-18 20:32:04 +02:00
PhilippC
b20076e7b8 New translations strings.xml (German) 2021-05-18 20:32:02 +02:00
PhilippC
0c4d096e36 New translations strings.xml (Bulgarian) 2021-05-18 20:32:01 +02:00
PhilippC
6b3ab59f13 New translations strings.xml (Chinese Simplified) 2021-05-18 20:32:00 +02:00
PhilippC
98e937a61b New translations strings.xml (Portuguese) 2021-05-18 20:31:59 +02:00
PhilippC
b10893dfc6 New translations strings.xml (Portuguese, Brazilian) 2021-05-18 20:31:58 +02:00
PhilippC
ec6d5bb56e New translations strings.xml (Indonesian) 2021-05-18 20:31:56 +02:00
PhilippC
7dbaf6feaa New translations strings.xml (Persian) 2021-05-18 20:31:54 +02:00
PhilippC
6cffa74196 New translations strings.xml (Croatian) 2021-05-18 20:31:52 +02:00
PhilippC
7906c477c4 New translations strings.xml (Norwegian Nynorsk) 2021-05-18 20:31:50 +02:00
PhilippC
5cc139e7f0 New translations strings.xml (Azerbaijani) 2021-05-18 20:31:48 +02:00
PhilippC
a3b2ccfbb9 New translations strings.xml (Malayalam) 2021-05-18 20:31:47 +02:00
PhilippC
f0cde44dc1 New translations strings.xml (Norwegian Bokmal) 2021-05-18 20:31:43 +02:00
PhilippC
c7cf38de48 New translations strings.xml (Russian) 2021-05-18 20:31:40 +02:00
PhilippC
dad72764e8 New translations strings.xml (Slovak) 2021-05-18 20:31:38 +02:00
PhilippC
10ef165112 New translations strings.xml (Slovenian) 2021-05-18 20:31:36 +02:00
PhilippC
47fcc8007d New translations strings.xml (Serbian (Cyrillic)) 2021-05-18 20:31:34 +02:00
PhilippC
bb9a53e6cc New translations strings.xml (Galician) 2021-05-18 20:31:32 +02:00
PhilippC
46544570ad New translations strings.xml (Swedish) 2021-05-18 20:31:31 +02:00
PhilippC
db71a06371 New translations strings.xml (Turkish) 2021-05-18 20:31:30 +02:00
PhilippC
f5b88df4c1 New translations strings.xml (Ukrainian) 2021-05-18 20:31:27 +02:00
PhilippC
a4dfc59126 New translations strings.xml (Chinese Traditional) 2021-05-18 20:31:26 +02:00
PhilippC
c980d37aa2 New translations strings.xml (Vietnamese) 2021-05-18 20:31:24 +02:00
PhilippC
411740cf01 New translations strings.xml (Spanish) 2021-05-18 20:31:22 +02:00
Philipp Crocoll
9d4ca67722 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-05-18 20:30:39 +02:00
PhilippC
ad5b7e5b46 New translations strings.xml (Spanish) 2021-05-18 20:27:26 +02:00
PhilippC
785c0bba8d New translations strings.xml (German) 2021-05-18 20:27:14 +02:00
Philipp Crocoll
f6395908ee add back custom ColorTranslator, fixes PlatformnotSupportedException, closes https://github.com/PhilippC/keepass2android/issues/1679 2021-05-18 20:26:08 +02:00
Philipp Crocoll
4636f5800c add passphrase, password generator profiles, password strength estimation, closes https://github.com/PhilippC/keepass2android/issues/81 2021-05-18 08:42:47 +02:00
Philipp Crocoll
212418f11a include some more options in the password generator (extended special group, at least one from every group, exclude look-alike), closes some items of https://github.com/PhilippC/keepass2android/issues/81 2021-05-17 11:56:16 +02:00
PhilippC
7f443e8505 New translations strings.xml (Slovenian) 2021-05-16 21:08:23 +02:00
PhilippC
488d11b693 New translations strings.xml (Chinese Simplified) 2021-05-16 07:10:30 +02:00
Philipp Crocoll
f0255703c1 1.09a-r0 2021-05-15 16:24:32 +02:00
PhilippC
f7e33728f2 Merge pull request #1579 from PhilippC/l10n_master2
New Crowdin updates
2021-05-15 15:46:47 +02:00
PhilippC
86fcfb49eb New translations strings.xml (German) 2021-05-15 15:42:27 +02:00
Philipp Crocoll
17ae8f7e99 Merge branch 'master' of c:/ph/keepass2android 2021-05-15 15:26:19 +02:00
Philipp Crocoll
638a2ec462 extend changelog for 1.09a 2021-05-15 15:26:06 +02:00
PhilippC
789e474c04 New translations strings.xml (Chinese Simplified) 2021-05-15 06:41:58 +02:00
PhilippC
65ade96fd8 New translations strings.xml (Slovenian) 2021-05-14 18:12:39 +02:00
PhilippC
05e2502831 New translations strings.xml (Basque) 2021-05-14 08:37:34 +02:00
PhilippC
f1a4536a42 New translations strings.xml (Finnish) 2021-05-14 08:37:32 +02:00
PhilippC
0bb372c2e9 New translations strings.xml (Hebrew) 2021-05-14 08:37:29 +02:00
PhilippC
98c38e6f8f New translations strings.xml (Hungarian) 2021-05-14 08:37:26 +02:00
PhilippC
7739a1bb85 New translations strings.xml (Italian) 2021-05-14 08:37:22 +02:00
PhilippC
5dbc4df7c1 New translations strings.xml (Japanese) 2021-05-14 08:37:20 +02:00
PhilippC
3bee5fc4c9 New translations strings.xml (Korean) 2021-05-14 08:37:17 +02:00
PhilippC
62fe696921 New translations strings.xml (Dutch) 2021-05-14 08:37:14 +02:00
PhilippC
603f30cd9d New translations strings.xml (Polish) 2021-05-14 08:37:11 +02:00
PhilippC
764d208695 New translations strings.xml (Greek) 2021-05-14 08:37:07 +02:00
PhilippC
147c83f62b New translations strings.xml (Romanian) 2021-05-14 08:37:03 +02:00
PhilippC
da89fd674f New translations strings.xml (French) 2021-05-14 08:37:00 +02:00
PhilippC
6bc4677f3a New translations strings.xml (Arabic) 2021-05-14 08:36:55 +02:00
PhilippC
0676cd6e7c New translations strings.xml (Catalan) 2021-05-14 08:36:50 +02:00
PhilippC
7dceb7e363 New translations strings.xml (Czech) 2021-05-14 08:36:47 +02:00
PhilippC
6a2be81241 New translations strings.xml (Danish) 2021-05-14 08:36:44 +02:00
PhilippC
b26c9bfa35 New translations strings.xml (German) 2021-05-14 08:36:41 +02:00
PhilippC
19ecbfae85 New translations strings.xml (Bulgarian) 2021-05-14 08:36:40 +02:00
PhilippC
221277bf24 New translations strings.xml (Chinese Simplified) 2021-05-14 08:36:39 +02:00
PhilippC
41d864e69f New translations strings.xml (Portuguese) 2021-05-14 08:36:37 +02:00
PhilippC
46341b5d37 New translations strings.xml (Portuguese, Brazilian) 2021-05-14 08:36:35 +02:00
PhilippC
b3569f1459 New translations strings.xml (Indonesian) 2021-05-14 08:36:32 +02:00
PhilippC
963df05ed3 New translations strings.xml (Persian) 2021-05-14 08:36:29 +02:00
PhilippC
f693d93e03 New translations strings.xml (Croatian) 2021-05-14 08:36:27 +02:00
PhilippC
b3a78194dd New translations strings.xml (Norwegian Bokmal) 2021-05-14 08:36:12 +02:00
PhilippC
2d27c95388 New translations strings.xml (Russian) 2021-05-14 08:36:07 +02:00
PhilippC
3e760df174 New translations strings.xml (Slovak) 2021-05-14 08:36:04 +02:00
PhilippC
9dca986efe New translations strings.xml (Slovenian) 2021-05-14 08:36:01 +02:00
PhilippC
6967bd2b84 New translations strings.xml (Serbian (Cyrillic)) 2021-05-14 08:35:58 +02:00
PhilippC
b32e599223 New translations strings.xml (Galician) 2021-05-14 08:35:55 +02:00
PhilippC
727863e4ee New translations strings.xml (Swedish) 2021-05-14 08:35:54 +02:00
PhilippC
2c157bc76f New translations strings.xml (Turkish) 2021-05-14 08:35:51 +02:00
PhilippC
f78f2b9361 New translations strings.xml (Ukrainian) 2021-05-14 08:35:49 +02:00
PhilippC
3859ce78a5 New translations strings.xml (Chinese Traditional) 2021-05-14 08:35:46 +02:00
PhilippC
1e1c79e8f8 New translations strings.xml (Vietnamese) 2021-05-14 08:35:43 +02:00
PhilippC
beadd426c4 New translations strings.xml (Spanish) 2021-05-14 08:35:40 +02:00
Philipp Crocoll
edc650a66e Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-05-14 08:01:47 +02:00
Philipp Crocoll
562a80132a merge changes from KeePass 2.48 with support for KDBX 4.1 and slightly improved merging behavior with respect to custom icons, closes https://github.com/PhilippC/keepass2android/issues/1670 2021-05-14 08:01:34 +02:00
Philipp Crocoll
f5ee9e3955 fix bad strings 2021-05-13 16:21:12 +02:00
Philipp Crocoll
a0a8a2810c add github sponsor as supporter 2021-05-13 16:21:02 +02:00
Philipp Crocoll
1d6dadea58 implement editor for TOTP settings in EntryEditActivity, closes https://github.com/PhilippC/keepass2android/issues/770 2021-05-13 16:20:17 +02:00
PhilippC
5144fe6fc9 New translations strings.xml (Chinese Simplified) 2021-05-11 02:46:09 +02:00
PhilippC
7e0913fe8b New translations strings.xml (Chinese Simplified) 2021-05-10 20:13:58 +02:00
PhilippC
15f1aa68da New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:32 +02:00
PhilippC
1eb11928c2 New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:31 +02:00
PhilippC
982527b4bf New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:30 +02:00
PhilippC
2a2622ddd8 New translations strings.xml (Chinese Simplified) 2021-05-10 18:09:00 +02:00
PhilippC
39939790c2 New translations strings.xml (Chinese Simplified) 2021-05-10 17:12:52 +02:00
PhilippC
ddabf82ea7 New translations strings.xml (Chinese Simplified) 2021-05-10 16:07:28 +02:00
PhilippC
7dd6745cd9 New translations strings.xml (Spanish) 2021-05-08 21:47:06 +02:00
PhilippC
3ac02da522 New translations strings.xml (Spanish) 2021-05-08 20:47:32 +02:00
PhilippC
d5982ae1e3 New translations strings.xml (Arabic) 2021-04-25 13:38:03 +02:00
PhilippC
fe2ebfcfd9 New translations strings.xml (Arabic) 2021-04-25 12:38:11 +02:00
PhilippC
977fe33d6a New translations strings.xml (Arabic) 2021-04-25 12:38:09 +02:00
PhilippC
951e11f6b0 New translations strings.xml (Swedish) 2021-04-23 00:12:19 +02:00
PhilippC
2fa1cd175d New translations strings.xml (Swedish) 2021-04-22 23:06:55 +02:00
PhilippC
5c9ab11aff New translations strings.xml (Vietnamese) 2021-04-13 11:39:41 +02:00
PhilippC
1f9327db80 Merge pull request #1612 from TiloHeidasch/master
Fixes Typo
2021-04-10 16:27:28 +02:00
PhilippC
e16429f77b New translations strings.xml (Portuguese) 2021-04-05 22:25:20 +02:00
PhilippC
3e58ef5dba New translations strings.xml (Italian) 2021-03-29 12:05:05 +02:00
Philipp Crocoll
84ac5fef4f remove option "Binary directory" (no longer used) 2021-03-29 10:11:02 +02:00
PhilippC
303d98bad7 New translations strings.xml (Norwegian Bokmal) 2021-03-28 19:53:28 +02:00
PhilippC
e39a126229 New translations strings.xml (Italian) 2021-03-27 12:23:29 +01:00
PhilippC
7257657432 New translations strings.xml (Portuguese, Brazilian) 2021-03-26 13:33:06 +01:00
PhilippC
99999dde2c New translations strings.xml (Portuguese, Brazilian) 2021-03-26 13:33:04 +01:00
PhilippC
60fc41d524 New translations strings.xml (Portuguese, Brazilian) 2021-03-26 12:12:23 +01:00
PhilippC
559a6f87b2 New translations strings.xml (Chinese Simplified) 2021-03-23 16:48:57 +01:00
PhilippC
02fbe3ce93 New translations strings.xml (German) 2021-03-17 09:54:00 +01:00
PhilippC
aa221c221e New translations strings.xml (Russian) 2021-03-17 00:00:37 +01:00
PhilippC
796efc490e New translations strings.xml (German) 2021-03-17 00:00:36 +01:00
PhilippC
b3c6b9b8e3 New translations strings.xml (German) 2021-03-16 22:47:57 +01:00
PhilippC
bc44d8a0a6 New translations strings.xml (Greek) 2021-03-16 16:22:39 +01:00
PhilippC
a43c291794 New translations strings.xml (Greek) 2021-03-16 16:22:38 +01:00
TiloHeidasch
2a4c0a8427 Fixes Typo 2021-03-16 11:45:39 +01:00
PhilippC
bca9156a38 New translations strings.xml (Chinese Simplified) 2021-03-14 13:26:34 +01:00
PhilippC
cd97ac8106 New translations strings.xml (Persian) 2021-03-12 11:24:36 +01:00
PhilippC
e464413b05 New translations strings.xml (Portuguese) 2021-03-10 20:42:31 +01:00
PhilippC
39c50eda0c New translations strings.xml (Portuguese) 2021-03-10 20:42:30 +01:00
PhilippC
4901485dc3 New translations strings.xml (Portuguese) 2021-03-10 20:42:28 +01:00
PhilippC
0762c227c1 New translations strings.xml (Portuguese) 2021-03-10 19:36:01 +01:00
Philipp Crocoll
09537689cf additional logging for https://github.com/PhilippC/keepass2android/issues/1257 2021-03-08 09:50:09 +01:00
Philipp Crocoll
92ffe596ce additional logging for keyboard issue 2021-03-08 09:49:48 +01:00
Philipp Crocoll
7037fbfe94 fix URL has two "Open URL" context menu entries 2021-03-08 09:49:21 +01:00
Philipp Crocoll
9eab59dcbe fix broken steam support 2021-03-08 09:48:56 +01:00
PhilippC
d705371f87 New translations strings.xml (Chinese Traditional) 2021-03-02 13:35:20 +01:00
PhilippC
09206a387b New translations strings.xml (Hungarian) 2021-02-24 20:05:26 +01:00
PhilippC
65fc67a4b2 New translations strings.xml (Hungarian) 2021-02-24 20:05:24 +01:00
PhilippC
7faa6a8efc New translations strings.xml (Ukrainian) 2021-02-24 18:54:22 +01:00
PhilippC
4f4b7fdfa4 New translations strings.xml (German) 2021-02-23 15:42:08 +01:00
PhilippC
ce09d9c8a9 New translations strings.xml (Polish) 2021-02-23 13:02:58 +01:00
PhilippC
c9744aeb94 New translations strings.xml (Polish) 2021-02-23 13:02:57 +01:00
PhilippC
a65dbfafa3 New translations strings.xml (Polish) 2021-02-23 13:02:56 +01:00
PhilippC
e40c117228 New translations strings.xml (Polish) 2021-02-23 12:01:21 +01:00
PhilippC
46bded5c16 New translations strings.xml (Japanese) 2021-02-23 10:22:04 +01:00
PhilippC
47ab6df725 New translations strings.xml (Russian) 2021-02-22 12:19:59 +01:00
PhilippC
94a366a13e New translations strings.xml (Dutch) 2021-02-21 21:14:00 +01:00
PhilippC
f23de8f380 New translations strings.xml (French) 2021-02-20 22:56:49 +01:00
PhilippC
f1fb2a47f0 New translations strings.xml (Slovenian) 2021-02-20 21:21:46 +01:00
PhilippC
175c64bdc8 New translations strings.xml (Chinese Simplified) 2021-02-20 10:07:48 +01:00
PhilippC
07ab9f21db New translations strings.xml (Malayalam) 2021-02-20 07:57:13 +01:00
PhilippC
56ece19994 New translations strings.xml (Malayalam) 2021-02-20 07:57:11 +01:00
PhilippC
1163e9956a New translations strings.xml (Japanese) 2021-02-20 05:22:09 +01:00
PhilippC
d02b9438eb New translations strings.xml (Portuguese, Brazilian) 2021-02-20 00:40:22 +01:00
Philipp Crocoll
40cbd51e8b Merge branch 'master' of c:/ph/keepass2android 2021-02-19 12:23:38 +01:00
Philipp Crocoll
490de22dc7 changelog and manifest for 1.09a-pre1 2021-02-19 12:23:18 +01:00
Philipp Crocoll
ef71df7d5f Merge remote-tracking branch 'remotes/origin/l10n_master2'
# resolved Conflicts:
#	src/keepass2android/Resources/values-de/strings.xml
2021-02-19 12:19:24 +01:00
PhilippC
0fbf0179f3 New translations strings.xml (Portuguese, Brazilian) 2021-02-19 12:17:44 +01:00
PhilippC
5a45360ab6 New translations strings.xml (Indonesian) 2021-02-19 12:17:42 +01:00
PhilippC
94ac246d1f New translations strings.xml (Persian) 2021-02-19 12:17:41 +01:00
PhilippC
85ef205fab New translations strings.xml (Croatian) 2021-02-19 12:17:40 +01:00
PhilippC
4dd0215ac8 New translations strings.xml (Norwegian Bokmal) 2021-02-19 12:17:34 +01:00
PhilippC
4fa940af47 New translations strings.xml (Galician) 2021-02-19 12:17:30 +01:00
PhilippC
6e76e4908e New translations strings.xml (Vietnamese) 2021-02-19 12:17:18 +01:00
PhilippC
8991538449 New translations strings.xml (Chinese Simplified) 2021-02-19 12:17:17 +01:00
PhilippC
9a51267b8f New translations strings.xml (Ukrainian) 2021-02-19 12:17:16 +01:00
PhilippC
373128f678 New translations strings.xml (Czech) 2021-02-19 12:17:15 +01:00
PhilippC
563faf6cc5 New translations strings.xml (Romanian) 2021-02-19 12:17:14 +01:00
PhilippC
aff243389d New translations strings.xml (French) 2021-02-19 12:17:12 +01:00
PhilippC
a38be4ad80 New translations strings.xml (Spanish) 2021-02-19 12:17:11 +01:00
PhilippC
bd52090236 New translations strings.xml (Arabic) 2021-02-19 12:17:09 +01:00
PhilippC
5ae11a91c1 New translations strings.xml (Catalan) 2021-02-19 12:17:07 +01:00
PhilippC
f1cc61e8ce New translations strings.xml (Danish) 2021-02-19 12:17:06 +01:00
PhilippC
5af68c5d9b New translations strings.xml (German) 2021-02-19 12:17:04 +01:00
PhilippC
dbe804f223 New translations strings.xml (Greek) 2021-02-19 12:17:03 +01:00
PhilippC
2bbbc4fe94 New translations strings.xml (Basque) 2021-02-19 12:17:02 +01:00
PhilippC
9cd66fc30c New translations strings.xml (Chinese Traditional) 2021-02-19 12:17:00 +01:00
PhilippC
65f6731b61 New translations strings.xml (Finnish) 2021-02-19 12:16:59 +01:00
PhilippC
25defeaf3a New translations strings.xml (Hungarian) 2021-02-19 12:16:58 +01:00
PhilippC
e8d8f13888 New translations strings.xml (Italian) 2021-02-19 12:16:57 +01:00
PhilippC
4a9a767fc6 New translations strings.xml (Japanese) 2021-02-19 12:16:55 +01:00
PhilippC
d8b22b4914 New translations strings.xml (Korean) 2021-02-19 12:16:54 +01:00
PhilippC
ef22f19dec New translations strings.xml (Polish) 2021-02-19 12:16:52 +01:00
PhilippC
4bcf651e5e New translations strings.xml (Portuguese) 2021-02-19 12:16:51 +01:00
PhilippC
19aea7808d New translations strings.xml (Russian) 2021-02-19 12:16:49 +01:00
PhilippC
afa4c9c0f2 New translations strings.xml (Slovak) 2021-02-19 12:16:48 +01:00
PhilippC
d2d853e916 New translations strings.xml (Slovenian) 2021-02-19 12:16:47 +01:00
PhilippC
3ead147be4 New translations strings.xml (Swedish) 2021-02-19 12:16:45 +01:00
PhilippC
c863ff1fd1 New translations strings.xml (Turkish) 2021-02-19 12:16:43 +01:00
PhilippC
6028b10bf1 New translations strings.xml (Dutch) 2021-02-19 12:15:56 +01:00
PhilippC
e74bb8d3b5 New translations strings.xml (Japanese) 2021-02-19 12:06:48 +01:00
Philipp Crocoll
7bbe3fb9b5 allow to choose Light/Dark Design as indicated by the system settings (Android 10+), closes https://github.com/PhilippC/keepass2android/issues/966 2021-02-19 12:04:47 +01:00
Philipp Crocoll
e00eb2a5bc commit unsaved change regarding read-only access to content storage 2021-02-19 11:53:45 +01:00
Philipp Crocoll
23ca92039b pCloud: recover from bad stored access tokens 2021-02-19 11:41:23 +01:00
Philipp Crocoll
54f8916ddd no longer checking for FLAG_SUPPORTS_WRITE for ContontStorage - this often returns false even for non-readonly files, closes https://github.com/PhilippC/keepass2android/issues/1212 2021-02-19 11:41:03 +01:00
Philipp Crocoll
a4b3c933ef allow to move an entry from the EntryActivity menu, closes https://github.com/PhilippC/keepass2android/issues/24 2021-02-19 09:11:35 +01:00
Philipp Crocoll
d0633e883e go back to dx+proguard+shared runtime (r8 seems to break GDrive implementation; shared runtime required for debugging) 2021-02-19 08:44:41 +01:00
Philipp Crocoll
8071e0692a make sure AutoExec items with unknown protocols don't crash the app, closes #1452 2021-02-19 08:44:08 +01:00
Philipp Crocoll
47f07c66bf Merge remote-tracking branch 'remotes/origin/l10n_master2' 2021-02-19 07:04:51 +01:00
Philipp Crocoll
5db30a2b98 Merge branch 'master' of c:/ph/keepass2android 2021-02-19 07:03:15 +01:00
Philipp Crocoll
fcd3cddbc7 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-02-19 07:02:49 +01:00
Philipp Crocoll
3c49d30de6 Merge branch 'master' of c:/ph/keepass2android 2021-02-18 17:57:37 +01:00
Philipp Crocoll
377bffd7a4 Merge branch 'Branch_3fb358c-b' 2021-02-18 17:49:30 +01:00
Philipp Crocoll
6af1da01db fix to autofill: continue if dataset has no name 2021-02-18 17:48:25 +01:00
Philipp Crocoll
a6e430e569 biometric unlock: SetInvalidatedByBiometricEnrollment(false) for API level >= 24 2021-02-18 16:23:23 +01:00
Philipp Crocoll
15d3492ae3 fix for OneDrive file storage unnecessarily prompting for login, closes https://github.com/PhilippC/keepass2android/issues/1038 2021-02-18 16:20:03 +01:00
Philipp Crocoll
7a731da44f code formatting 2021-02-18 16:19:05 +01:00
PhilippC
64a53ce887 New translations strings.xml (Czech) 2021-02-17 00:37:42 +01:00
PhilippC
45355ba972 New translations strings.xml (Ukrainian) 2021-02-16 09:07:54 +01:00
PhilippC
3b769e2a9f New translations strings.xml (Ukrainian) 2021-02-16 08:12:32 +01:00
PhilippC
ed70c3fcb1 New translations strings.xml (Ukrainian) 2021-02-15 11:58:07 +01:00
PhilippC
86d56be044 New translations strings.xml (Ukrainian) 2021-02-15 10:44:25 +01:00
PhilippC
448492cbc1 New translations strings.xml (Ukrainian) 2021-02-12 11:47:00 +01:00
PhilippC
5010eb3a7c New translations strings.xml (Ukrainian) 2021-02-12 11:46:58 +01:00
PhilippC
e429f082e4 New translations strings.xml (Ukrainian) 2021-02-12 10:41:11 +01:00
PhilippC
53be95ac35 New translations strings.xml (Dutch) 2021-02-10 00:48:43 +01:00
PhilippC
1a47b603c7 New translations strings.xml (Dutch) 2021-02-09 23:48:32 +01:00
Philipp Crocoll
4b956af0f7 add debugging to diagnose an issue reported by a user which I can't reproduce 2021-02-09 16:17:47 +01:00
Philipp Crocoll
8a9495edce include subdomains when trying to find an entry from Autofill 2021-02-09 14:45:40 +01:00
Philipp Crocoll
b96e8a5b2b add github support name 2021-02-09 14:45:17 +01:00
Philipp Crocoll
a37789fbfe minor changes adapting to VS upgrade 2021-02-09 14:44:50 +01:00
Philipp Crocoll
8d95fb1660 implement upload sessions for OneDrive to resolve file size restriction (closes https://github.com/PhilippC/keepass2android/issues/1036) 2021-02-09 14:44:02 +01:00
PhilippC
db83855fee New translations strings.xml (Russian) 2021-02-03 22:31:17 +01:00
PhilippC
56d896ca22 New translations strings.xml (Russian) 2021-02-03 21:25:30 +01:00
PhilippC
1169e7183e New translations strings.xml (French) 2021-02-02 22:13:42 +01:00
PhilippC
fe0b377561 New translations strings.xml (Hungarian) 2021-02-01 18:16:36 +01:00
PhilippC
5528d6bc8d New translations strings.xml (Hungarian) 2021-02-01 17:11:32 +01:00
PhilippC
7856ea8130 New translations strings.xml (Chinese Traditional) 2021-01-24 10:29:16 +01:00
PhilippC
dbc684306b New translations strings.xml (Chinese Traditional) 2021-01-24 09:26:46 +01:00
Philipp Crocoll
19187352b5 Merge branch 'master' of c:/ph/keepass2android 2021-01-24 07:06:12 +01:00
Philipp Crocoll
cc7ee35500 fix to all entries being recognized as TOTP entries 2021-01-24 07:05:58 +01:00
PhilippC
63ffd56fd9 New translations strings.xml (German) 2021-01-24 06:42:14 +01:00
PhilippC
4ea3da158d New translations strings.xml (Czech) 2021-01-23 03:14:44 +01:00
PhilippC
00daf35918 New translations strings.xml (Czech) 2021-01-23 02:17:08 +01:00
PhilippC
18bfc06091 New translations strings.xml (Czech) 2021-01-23 01:05:44 +01:00
PhilippC
0cf78f5c8e New translations strings.xml (Japanese) 2021-01-21 11:17:01 +01:00
PhilippC
1193b37a53 New translations strings.xml (Chinese Simplified) 2021-01-19 17:45:56 +01:00
Philipp Crocoll
6cb3fb2354 manifest for 1.08d-r4 2021-01-17 04:54:33 +01:00
PhilippC
1109b40af4 New translations strings.xml (Finnish) 2021-01-17 04:06:04 +01:00
PhilippC
479ab9377e New translations strings.xml (Finnish) 2021-01-17 04:06:02 +01:00
PhilippC
5e01b3c0ab New translations strings.xml (Hebrew) 2021-01-17 04:06:00 +01:00
PhilippC
3b876dde1c New translations strings.xml (Hebrew) 2021-01-17 04:05:58 +01:00
PhilippC
36fdc5e737 New translations strings.xml (Greek) 2021-01-17 04:05:52 +01:00
PhilippC
0410687ae1 New translations strings.xml (Italian) 2021-01-17 04:05:49 +01:00
PhilippC
d05be2b599 New translations strings.xml (Japanese) 2021-01-17 04:05:47 +01:00
PhilippC
39b0d4f0c2 New translations strings.xml (Japanese) 2021-01-17 04:05:45 +01:00
PhilippC
661aefaebc New translations strings.xml (Italian) 2021-01-17 04:05:36 +01:00
PhilippC
d25c951c88 New translations strings.xml (Portuguese) 2021-01-17 04:05:34 +01:00
PhilippC
6d5d8142b3 New translations strings.xml (German) 2021-01-17 04:05:30 +01:00
PhilippC
dee8bdec24 New translations strings.xml (Romanian) 2021-01-17 04:05:25 +01:00
PhilippC
e5010b761d New translations strings.xml (French) 2021-01-17 04:05:21 +01:00
PhilippC
8b345d4fac New translations strings.xml (Spanish) 2021-01-17 04:05:17 +01:00
PhilippC
d9679801e2 New translations strings.xml (Arabic) 2021-01-17 04:05:13 +01:00
PhilippC
293864e62b New translations strings.xml (German) 2021-01-17 04:05:12 +01:00
PhilippC
ddd1a25d93 New translations strings.xml (Arabic) 2021-01-17 04:05:09 +01:00
PhilippC
a44e3bae90 New translations strings.xml (Bulgarian) 2021-01-17 04:05:07 +01:00
PhilippC
97c682416c New translations strings.xml (Czech) 2021-01-17 04:05:01 +01:00
PhilippC
490d32e8d6 New translations strings.xml (Czech) 2021-01-17 04:04:59 +01:00
PhilippC
36739e1b11 New translations strings.xml (Danish) 2021-01-17 04:04:56 +01:00
PhilippC
8639cc313a New translations strings.xml (Danish) 2021-01-17 04:04:54 +01:00
PhilippC
20cacc6dd5 New translations strings.xml (Portuguese) 2021-01-17 04:04:50 +01:00
PhilippC
b60b0f2bf6 New translations strings.xml (Russian) 2021-01-17 04:04:48 +01:00
PhilippC
4db37a0f18 New translations strings.xml (Indonesian) 2021-01-17 04:04:43 +01:00
PhilippC
6a28219b7f New translations strings.xml (Norwegian Bokmal) 2021-01-17 04:04:23 +01:00
PhilippC
904885db6c New translations strings.xml (Norwegian Bokmal) 2021-01-17 04:04:21 +01:00
PhilippC
6a7b003246 New translations strings.xml (Galician) 2021-01-17 04:04:18 +01:00
PhilippC
a2aeb67d6d New translations strings.xml (Russian) 2021-01-17 04:04:16 +01:00
PhilippC
412b4f26b7 New translations strings.xml (Slovenian) 2021-01-17 04:04:10 +01:00
PhilippC
b95e380bd8 New translations strings.xml (Slovenian) 2021-01-17 04:04:08 +01:00
PhilippC
bf46b015a5 New translations strings.xml (Galician) 2021-01-17 04:04:02 +01:00
PhilippC
d23f4c8095 New translations strings.xml (Ukrainian) 2021-01-17 04:03:56 +01:00
PhilippC
70c6c59e74 New translations strings.xml (Ukrainian) 2021-01-17 04:03:54 +01:00
PhilippC
e69f662608 New translations strings.xml (Chinese Traditional) 2021-01-17 04:03:50 +01:00
PhilippC
8d934663c5 New translations strings.xml (Vietnamese) 2021-01-17 04:03:47 +01:00
PhilippC
12070b042f New translations strings.xml (Chinese Traditional) 2021-01-17 04:03:43 +01:00
Philipp Crocoll
2e5ee5f836 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-01-17 03:17:15 +01:00
Philipp Crocoll
177b36d869 fix bug preventing screen protection to work, closes #1543 2021-01-17 03:13:24 +01:00
Philipp Crocoll
cd484c0398 remove no longer valid note regarding pCloud 2021-01-17 03:12:51 +01:00
PhilippC
a6e3c2024f New translations strings.xml (Hungarian) 2021-01-17 03:07:37 +01:00
PhilippC
31e7037d4f New translations strings.xml (Greek) 2021-01-17 03:07:35 +01:00
PhilippC
13c2e297a8 New translations strings.xml (Japanese) 2021-01-17 03:07:30 +01:00
PhilippC
45d08a5762 New translations strings.xml (Dutch) 2021-01-17 03:07:26 +01:00
PhilippC
0568efc94b New translations strings.xml (Polish) 2021-01-17 03:07:22 +01:00
PhilippC
b61edb44bf New translations strings.xml (Italian) 2021-01-17 03:07:19 +01:00
PhilippC
415d01b36d New translations strings.xml (Portuguese) 2021-01-17 03:07:17 +01:00
PhilippC
b2864feb73 New translations strings.xml (German) 2021-01-17 03:07:14 +01:00
PhilippC
9ddafb6525 New translations strings.xml (Korean) 2021-01-17 03:07:12 +01:00
PhilippC
3771e86169 New translations strings.xml (French) 2021-01-17 03:07:07 +01:00
PhilippC
e8533afe78 New translations strings.xml (Spanish) 2021-01-17 03:07:03 +01:00
PhilippC
28361f4105 New translations strings.xml (Catalan) 2021-01-17 03:06:55 +01:00
PhilippC
110ef46f56 New translations strings.xml (Czech) 2021-01-17 03:06:52 +01:00
PhilippC
5cb23cd9b4 New translations strings.xml (Danish) 2021-01-17 03:06:48 +01:00
PhilippC
6d368e57af New translations strings.xml (Russian) 2021-01-17 03:06:42 +01:00
PhilippC
36bbaa7270 New translations strings.xml (Portuguese, Brazilian) 2021-01-17 03:06:41 +01:00
PhilippC
eb4888c361 New translations strings.xml (Slovak) 2021-01-17 03:06:15 +01:00
PhilippC
1773f9672e New translations strings.xml (Slovenian) 2021-01-17 03:06:11 +01:00
PhilippC
59fbaf87e6 New translations strings.xml (Swedish) 2021-01-17 03:06:05 +01:00
PhilippC
305a2aa6dc New translations strings.xml (Turkish) 2021-01-17 03:06:01 +01:00
PhilippC
ed92b3476f New translations strings.xml (Ukrainian) 2021-01-17 03:05:58 +01:00
PhilippC
b32fa9672f New translations strings.xml (Chinese Simplified) 2021-01-17 03:05:54 +01:00
PhilippC
dd07271550 New translations strings.xml (Chinese Traditional) 2021-01-17 03:05:48 +01:00
Philipp Crocoll
430db12d6f re-add Dutch translations to close #1526. They had been removed because of invalid strings causing crashes, these are now fixed. 2021-01-16 12:27:42 +01:00
PhilippC
53b881c451 New translations strings.xml (French) 2021-01-16 12:27:22 +01:00
Philipp Crocoll
a4219bb9c7 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-01-16 11:50:43 +01:00
Philipp Crocoll
7609260218 fix incorrect Unregister/RegisterReceiver calls, closes #1539 2021-01-16 11:50:31 +01:00
PhilippC
2c8bd15474 New translations strings.xml (Slovenian) 2021-01-14 12:20:52 +01:00
PhilippC
d6f3ad8b56 New translations strings.xml (Chinese Simplified) 2021-01-14 04:53:57 +01:00
PhilippC
b9d2f57d41 New translations strings.xml (Greek) 2021-01-13 21:56:45 +01:00
PhilippC
cd34858f9a New translations strings.xml (Japanese) 2021-01-13 21:56:39 +01:00
PhilippC
1639d5abee New translations strings.xml (Dutch) 2021-01-13 21:56:33 +01:00
PhilippC
dd2e4546b9 New translations strings.xml (Korean) 2021-01-13 21:56:21 +01:00
PhilippC
5b6af723b0 New translations strings.xml (Romanian) 2021-01-13 21:56:18 +01:00
PhilippC
9e660f957f New translations strings.xml (French) 2021-01-13 21:56:14 +01:00
PhilippC
1960af5149 New translations strings.xml (Russian) 2021-01-13 21:55:48 +01:00
PhilippC
792d16aba7 New translations strings.xml (Slovenian) 2021-01-13 21:55:06 +01:00
PhilippC
e6e854138b New translations strings.xml (Ukrainian) 2021-01-13 21:54:52 +01:00
PhilippC
2117e5abf9 New translations strings.xml (Chinese Simplified) 2021-01-13 21:54:48 +01:00
PhilippC
04151becfe New translations strings.xml (Chinese Traditional) 2021-01-13 21:54:39 +01:00
Philipp Crocoll
6cc007e47e changelog and manifest for 1.08d-r3 2021-01-13 21:49:47 +01:00
Philipp Crocoll
cd1877d34d fix issue with selecting one of multiple entries for autofill as reported on #1399; also another attempt to work-around the buggy Firefox Autofill 2021-01-13 21:36:56 +01:00
PhilippC
df3bc19fc0 New translations strings.xml (Romanian) 2021-01-13 10:14:58 +01:00
PhilippC
e4e7b1cb48 New translations strings.xml (Romanian) 2021-01-13 09:14:55 +01:00
Philipp Crocoll
c46b7be051 support SHA256 and SHA512 hash algorithm for otpauth:// TOTPs, closes https://github.com/PhilippC/keepass2android/issues/1155 2021-01-12 17:08:55 +01:00
PhilippC
5a8fa21cd1 New translations strings.xml (Slovenian) 2021-01-12 13:13:16 +01:00
Philipp Crocoll
7df048263b add compatibility to Keepass 2.47 TimeOTP-Secret values (but not supporting {TIMEOTP} placeholder) 2021-01-12 12:14:34 +01:00
PhilippC
aba45f4cf5 New translations strings.xml (Slovenian) 2021-01-12 11:18:38 +01:00
Philipp Crocoll
d9bcac600c support for Keyfiles like in Keepass 2.47 2021-01-12 07:31:18 +01:00
Philipp Crocoll
533021da3f Manifest for 1.08d-r2 2021-01-12 07:30:44 +01:00
PhilippC
ceeb7e23b4 New translations strings.xml (Japanese) 2021-01-12 01:25:04 +01:00
PhilippC
f226edf018 New translations strings.xml (Japanese) 2021-01-12 00:18:21 +01:00
PhilippC
e6f0f12240 New translations strings.xml (Japanese) 2021-01-12 00:18:19 +01:00
PhilippC
9df7c03e02 New translations strings.xml (Japanese) 2021-01-11 23:18:59 +01:00
Philipp Crocoll
1b8e290b30 Merge branch 'master' of c:/ph/keepass2android 2021-01-10 22:45:07 +01:00
Philipp Crocoll
a90fd17bea fix issue with incorrect launching of PasswordActivity for AutoExecItems leading to incorrect behavior when launching the app from Autofill (when child databases are opened) 2021-01-10 22:44:33 +01:00
Philipp Crocoll
3fb358ca85 further improvements for Autofill for usability with Firefox (see https://github.com/PhilippC/keepass2android/issues/1399), also fix security bug (closes https://github.com/PhilippC/keepass2android/issues/1527) 2021-01-10 15:25:26 +01:00
PhilippC
83d638e0e5 New translations strings.xml (Dutch) 2021-01-09 14:00:09 +01:00
PhilippC
20a147fd04 New translations strings.xml (Dutch) 2021-01-09 13:00:09 +01:00
Philipp Crocoll
730af8b9ac make sure autofill only returns results when database is unlocked 2021-01-09 11:42:38 +01:00
PhilippC
9a400cecd2 New translations strings.xml (Chinese Simplified) 2021-01-09 08:30:08 +01:00
PhilippC
b5d9ac859f New translations strings.xml (Chinese Simplified) 2021-01-09 07:30:09 +01:00
PhilippC
144cc3c40a New translations strings.xml (Greek) 2021-01-08 12:20:09 +01:00
PhilippC
2b514598d4 New translations strings.xml (Greek) 2021-01-08 11:20:12 +01:00
PhilippC
9c55f7d9e0 New translations strings.xml (Greek) 2021-01-08 11:20:10 +01:00
Philipp Crocoll
7fb070c5fc manifest for 1.08d-r1 2021-01-08 06:45:52 +01:00
PhilippC
b0dccaea98 New translations strings.xml (Chinese Simplified) 2021-01-08 06:42:33 +01:00
PhilippC
7939a0c9e7 New translations strings.xml (Portuguese, Brazilian) 2021-01-08 06:42:30 +01:00
PhilippC
5bccc32bb1 New translations strings.xml (Turkish) 2021-01-08 06:42:26 +01:00
PhilippC
1bf7ce27da New translations strings.xml (Swedish) 2021-01-08 06:42:22 +01:00
PhilippC
59085fd790 New translations strings.xml (Ukrainian) 2021-01-08 06:42:16 +01:00
PhilippC
7f004ca720 New translations strings.xml (Chinese Traditional) 2021-01-08 06:42:13 +01:00
PhilippC
71098fd9ac New translations strings.xml (Czech) 2021-01-08 06:41:48 +01:00
PhilippC
1ec17c77b5 New translations strings.xml (Danish) 2021-01-08 06:41:44 +01:00
PhilippC
adb3a81b2a New translations strings.xml (German) 2021-01-08 06:41:40 +01:00
PhilippC
ccf3a53253 New translations strings.xml (Greek) 2021-01-08 06:41:37 +01:00
PhilippC
9a7a3c6400 New translations strings.xml (Catalan) 2021-01-08 06:41:33 +01:00
PhilippC
1963e2d7bb New translations strings.xml (Korean) 2021-01-08 06:41:28 +01:00
PhilippC
e385eb78e2 New translations strings.xml (French) 2021-01-08 06:41:23 +01:00
PhilippC
e80ac513c4 New translations strings.xml (Spanish) 2021-01-08 06:41:19 +01:00
PhilippC
cf36613c65 New translations strings.xml (Italian) 2021-01-08 06:41:11 +01:00
PhilippC
ca3a9a7722 New translations strings.xml (Polish) 2021-01-08 06:41:08 +01:00
PhilippC
e85d5d519a New translations strings.xml (Portuguese) 2021-01-08 06:41:04 +01:00
PhilippC
2eccf190b4 New translations strings.xml (Russian) 2021-01-08 06:41:01 +01:00
PhilippC
2c18da0fbc New translations strings.xml (Slovak) 2021-01-08 06:40:57 +01:00
PhilippC
776a13ef28 New translations strings.xml (Slovenian) 2021-01-08 06:40:53 +01:00
PhilippC
b5e6e4904c New translations strings.xml (Japanese) 2021-01-08 06:40:44 +01:00
PhilippC
1e2d1503fe New translations strings.xml (Hungarian) 2021-01-08 06:40:39 +01:00
PhilippC
50d16acee9 New translations strings.xml (Dutch) 2021-01-08 06:40:32 +01:00
Philipp Crocoll
710e0f9bba Merge branch 'master' of https://github.com/PhilippC/keepass2android
# Conflicts:
#	src/keepass2android/Resources/values/strings.xml
2021-01-08 06:37:57 +01:00
Philipp Crocoll
84efc14b00 changelog for 1.08d-r1 2021-01-08 06:36:23 +01:00
Philipp Crocoll
ddb58119be update to pCloud SDK 1.2.0 and implement support for different dataHosts (depending on data server location). Closes #1257 2021-01-08 06:35:49 +01:00
PhilippC
3ced852eb4 New translations strings.xml (Korean) 2021-01-08 00:20:13 +01:00
PhilippC
9928c85d0a New translations strings.xml (Korean) 2021-01-07 23:20:11 +01:00
PhilippC
8a095a4032 New translations strings.xml (Korean) 2021-01-07 23:20:09 +01:00
Philipp Crocoll
9372cb7190 avoid potential crash 2021-01-06 18:05:55 +01:00
Philipp Crocoll
bf3a5891e3 bug fixes to activity behavior of PasswordActivity/SelectCurrentDbActivity:
* when a PasswordActivity was still in stopped state and the user triggered opening from Share URL or Autofill and had a child database, the child would not be opened correctly because the child IOC was delivered in OnNewIntent (but not correctly loaded from there)
 * when locking the database from the SelectCurrentDbActivity, the activity would remain active (not really a big problem, DB was locked anyway, but bad UI)
2021-01-06 18:05:35 +01:00
Philipp Crocoll
ca61fa9d1d several changes to fix https://github.com/PhilippC/keepass2android/issues/1399:
* adding a lock to avoid flickering/disappearing prompt
 * returning a fill-response instead of a dataset from authentication activity
 * immediately present one search result (if there is a match) as a dataset item in the autofill prompt
2021-01-02 12:31:19 +01:00
Philipp Crocoll
42eb8222fc use special icon to indicate in search results if an entry is expired 2021-01-02 12:20:47 +01:00
PhilippC
2aca631390 New translations strings.xml (Chinese Simplified) 2021-01-02 02:10:09 +01:00
PhilippC
b175295d43 New translations strings.xml (Chinese Traditional) 2020-12-28 14:30:11 +01:00
PhilippC
0226456d41 New translations strings.xml (Chinese Traditional) 2020-12-28 13:30:10 +01:00
PhilippC
d18aba0747 New translations strings.xml (French) 2020-12-22 08:10:09 +01:00
PhilippC
449b5e3f0a New translations strings.xml (French) 2020-12-19 18:00:09 +01:00
Philipp Crocoll
301216fc2e improve error logging 2020-12-18 20:11:02 +01:00
Philipp Crocoll
c3bef01fe9 add another supporter name 2020-12-17 19:27:25 +01:00
PhilippC
19abf7a1b0 New translations strings.xml (Russian) 2020-12-16 13:40:09 +01:00
PhilippC
3e36a6e5fa New translations strings.xml (Russian) 2020-12-16 12:40:10 +01:00
PhilippC
e65bd9a524 New translations strings.xml (Ukrainian) 2020-12-12 19:50:09 +01:00
Philipp Crocoll
697b331a65 allow to disable DAL verification in Autofill for testing/debugging 2020-12-12 14:16:12 +01:00
Philipp Crocoll
3767450343 allow to disable check for secure screen to solve #1501 2020-12-12 14:14:05 +01:00
PhilippC
2ffa9b06c5 New translations strings.xml (Chinese Simplified) 2020-12-12 11:40:08 +01:00
PhilippC
c1e80d209f New translations strings.xml (Romanian) 2020-12-11 20:00:09 +01:00
PhilippC
a36f5a375a New translations strings.xml (Romanian) 2020-12-11 19:00:10 +01:00
PhilippC
4e7f6d62d2 New translations strings.xml (Russian) 2020-12-08 10:20:10 +01:00
PhilippC
f8e637cdbc New translations strings.xml (Slovenian) 2020-12-07 12:50:10 +01:00
Philipp Crocoll
abf32cf499 manifest and chanelog for 1.08d-r0 2020-12-07 11:03:23 +01:00
Philipp Crocoll
eee24819d5 add support for Argon2id (see https://github.com/keepassxreboot/keepassxc/issues/4317#issuecomment-738101431) 2020-12-07 10:43:46 +01:00
Philipp Crocoll
b7e4bf0be5 fix strings.xml 2020-12-07 10:43:10 +01:00
PhilippC
be65fe2521 New translations strings.xml (Sinhala) 2020-12-05 20:00:08 +01:00
PhilippC
05e62bd40d New translations strings.xml (Sinhala) 2020-12-05 10:00:12 +01:00
PhilippC
ca4e819183 New translations strings.xml (Sinhala) 2020-12-05 10:00:10 +01:00
PhilippC
80b75f11a0 New translations strings.xml (Sinhala) 2020-12-05 10:00:08 +01:00
PhilippC
a18f013d8c New translations strings.xml (Russian) 2020-12-03 11:40:10 +01:00
PhilippC
ffc06404c1 New translations strings.xml (German) 2020-11-30 12:32:18 +01:00
Philipp Crocoll
849cb0c707 changelog for 1.08d 2020-11-30 12:25:12 +01:00
Philipp Crocoll
219932258c Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-11-30 12:23:24 +01:00
Philipp Crocoll
59a5bb0c89 add menu in SelectCurrentDbActivity, closes https://github.com/PhilippC/keepass2android/issues/988 2020-11-30 12:16:04 +01:00
Philipp Crocoll
29fca5c1f0 allow to export imported keyfiles, closes https://github.com/PhilippC/keepass2android/issues/599 2020-11-30 11:56:39 +01:00
Philipp Crocoll
fec0e7768a add more logging output to diagnose an issue describe by a user where merge conflicts seem to be detected even if they are not there 2020-11-30 11:55:07 +01:00
Philipp Crocoll
3dcb8ed32b update JSch to version 0.1.55, closes #1478 2020-11-30 10:10:13 +01:00
PhilippC
2b1f5cfd0a New translations strings.xml (Italian) 2020-11-26 14:20:13 +01:00
PhilippC
84e08d39a4 New translations strings.xml (Czech) 2020-11-23 01:20:13 +01:00
PhilippC
0a171a6a72 New translations strings.xml (Czech) 2020-11-23 01:20:11 +01:00
PhilippC
cdcbe148c9 Merge pull request #1472 from joschahenningsen/readme-patch
Remove Google+ group from readme
2020-11-21 19:20:46 +01:00
Joscha Henningsen
fea28640b5 update readme 2020-11-20 16:47:39 +01:00
PhilippC
295f19c54e New translations strings.xml (Malayalam) 2020-11-18 14:00:09 +01:00
PhilippC
de759e5870 New translations strings.xml (German) 2020-11-18 13:00:09 +01:00
PhilippC
e9995e0c80 New translations strings.xml (Malayalam) 2020-11-18 10:50:10 +01:00
PhilippC
234fd59d22 New translations strings.xml (Malayalam) 2020-11-18 09:50:13 +01:00
PhilippC
7617012292 New translations strings.xml (Malayalam) 2020-11-18 09:50:11 +01:00
PhilippC
e8085c1c0e New translations strings.xml (Malayalam) 2020-11-17 03:30:07 +01:00
PhilippC
9e8a9e7b9e New translations strings.xml (Malayalam) 2020-11-17 02:30:09 +01:00
PhilippC
e47516b0e0 New translations strings.xml (Malayalam) 2020-11-17 02:30:08 +01:00
Philipp Crocoll
b85cb20177 improve logging on SwitchImeActivity 2020-11-16 12:14:42 +01:00
Philipp Crocoll
612c7927e9 fix to keyboard not opening on QuickUnlock screen, closes #1362 2020-11-16 11:35:09 +01:00
Philipp Crocoll
ad4b719f3f Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-11-16 10:21:23 +01:00
Philipp Crocoll
24b690d19b fix typo in changelog 2020-11-16 10:21:12 +01:00
PhilippC
e69af9cfbe New translations strings.xml (Ukrainian) 2020-11-08 20:30:09 +01:00
PhilippC
23f324b1d8 New translations strings.xml (Ukrainian) 2020-11-08 20:30:07 +01:00
PhilippC
a1f7a4245a New translations strings.xml (Slovak) 2020-11-07 23:00:08 +01:00
PhilippC
5dab309921 New translations strings.xml (Japanese) 2020-11-07 16:30:09 +01:00
Philipp Crocoll
57308b476d add some logging to ChooseForAutofillActivity to diagnose a problem of a user 2020-11-02 10:02:57 +01:00
PhilippC
44d324bc7d New translations strings.xml (Serbian (Cyrillic)) 2020-10-30 19:50:10 +01:00
PhilippC
90ae1bd7d7 Merge pull request #1459 from CommName/SerbianTranslation
Serbian translation
2020-10-30 15:57:47 +01:00
CommName
67e40bc284 Fixed typo 2020-10-29 15:28:15 +01:00
CommName
58a0f4d536 Added serbian translation 2020-10-29 15:24:38 +01:00
PhilippC
ebadedae41 New translations strings.xml (Czech) 2020-10-27 14:30:12 +01:00
PhilippC
3f89a34946 New translations strings.xml (Czech) 2020-10-27 14:30:09 +01:00
PhilippC
d823768464 New translations strings.xml (Italian) 2020-10-26 18:30:09 +01:00
Philipp Crocoll
174c440813 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-10-24 09:47:11 +02:00
Philipp Crocoll
1479ecf03f allow to open .xml files (as unencrypted database) in read-only mode, closes https://github.com/PhilippC/keepass2android/issues/1450 2020-10-24 09:47:00 +02:00
PhilippC
c8ac6ec972 Add 2 FAQs regarding Google Drive 2020-10-24 09:20:04 +02:00
PhilippC
ee340b069b New translations strings.xml (Chinese Simplified) 2020-10-24 06:50:08 +02:00
PhilippC
d13a35b9a8 New translations strings.xml (French) 2020-10-22 22:20:08 +02:00
Philipp Crocoll
b4e5b0107a allow to immediately disable donation option if a certain string is typed in search; 1.08c-r1 2020-10-19 19:40:34 +02:00
PhilippC
0ce41b9f54 New translations strings.xml (Slovenian) 2020-10-19 18:20:09 +02:00
PhilippC
1be1829cfe New translations strings.xml (Chinese Traditional) 2020-10-19 14:20:10 +02:00
PhilippC
160922c9fb New translations strings.xml (Dutch) 2020-10-19 11:31:39 +02:00
Philipp Crocoll
8b072a3c3f version and changelog for 1.08c-r0 2020-10-19 11:27:41 +02:00
Philipp Crocoll
7d98ea39e9 update jars in Xamarin build to account for updates in JavaFileStorage 2020-10-19 11:26:28 +02:00
Philipp Crocoll
170a86f433 Merge remote-tracking branch 'remotes/origin/l10n_master2' 2020-10-19 09:06:31 +02:00
Philipp Crocoll
7d4c8d3735 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-10-19 09:05:36 +02:00
Philipp Crocoll
2d23f579e9 when storing package names of target apps in Password entries, no longer use the Url field or KP2A_URL_x fields, instead use AndroidAppX (X being a counter starting at 1), closes #657 2020-10-19 09:04:11 +02:00
PhilippC
9586e7f3ae Merge pull request #1445 from lwis/update-okhttp
Update OkHttp to 4.10.0-RC1 to remove workaround for #747
2020-10-19 08:56:16 +02:00
PhilippC
7bda4095a6 New translations strings.xml (Turkish) 2020-10-17 12:00:11 +02:00
PhilippC
54dc7f46fb New translations strings.xml (Turkish) 2020-10-17 12:00:09 +02:00
PhilippC
87ddfc92a3 New translations strings.xml (Russian) 2020-10-17 12:00:08 +02:00
PhilippC
df98ebeb3b New translations strings.xml (Chinese Simplified) 2020-10-17 09:30:08 +02:00
Lewis Juggins
bbf3b40df8 Import 2020-10-16 13:11:41 +01:00
Lewis Juggins
a01e70012e Update OkHttp to 4.10.0-RC1 to remove workaround for #747 2020-10-16 13:09:47 +01:00
PhilippC
48517665e2 New translations strings.xml (Slovenian) 2020-10-14 07:40:07 +02:00
PhilippC
387998a260 New translations strings.xml (Chinese Traditional) 2020-10-13 08:00:09 +02:00
PhilippC
ef194fe70c New translations strings.xml (Swedish) 2020-10-12 21:10:10 +02:00
PhilippC
c74c718021 New translations strings.xml (Swedish) 2020-10-12 21:10:08 +02:00
Philipp Crocoll
c8f29bd424 include all strings.xml files in keepass2android.csproj, fixes unused translations, closes https://github.com/PhilippC/keepass2android/issues/1338 2020-10-12 21:05:12 +02:00
Philipp Crocoll
64e26fe372 changes to locking of the database: implement an alternative to alarm-based timeout (using a timeout variables checked when resuming), closes https://github.com/PhilippC/keepass2android/issues/1346 2020-10-12 11:57:58 +02:00
PhilippC
da213a1172 New translations strings.xml (Persian) 2020-10-12 10:40:52 +02:00
PhilippC
0e19dc9d17 New translations strings.xml (Slovenian) 2020-10-12 10:40:31 +02:00
Philipp Crocoll
27f9625d4e close SelectCurrentDbActivity after locking, i.e. we don't bring up QuickUnlock dialog anymore, causing trouble in several cases (locking when screen is turned off, face unlock immediately unlocking again), closes https://github.com/PhilippC/keepass2android/issues/1373, closes https://github.com/PhilippC/keepass2android/issues/1291 2020-10-12 10:11:16 +02:00
Philipp Crocoll
1a02741f95 add first mentioning of financial supporter from Github sponsoring 2020-10-12 10:03:29 +02:00
Philipp Crocoll
35c69fcb3a fix bug with potentially not correctly stopping the fingerprint prompt, might cause https://github.com/PhilippC/keepass2android/issues/1423 and https://github.com/PhilippC/keepass2android/issues/1312 2020-10-12 09:34:12 +02:00
Philipp Crocoll
561ff54afd add dates for Oktoberfest 2022-2024 2020-10-12 08:45:54 +02:00
PhilippC
f50c6ea3b9 New translations strings.xml (Finnish) 2020-10-03 01:00:08 +02:00
PhilippC
40f6f5a72e New translations strings.xml (Finnish) 2020-10-03 00:00:08 +02:00
PhilippC
700bedfe81 New translations strings.xml (Finnish) 2020-10-02 23:00:08 +02:00
PhilippC
2b50407127 New translations strings.xml (Italian) 2020-09-30 14:40:10 +02:00
PhilippC
ba68402479 New translations strings.xml (Italian) 2020-09-30 14:40:08 +02:00
PhilippC
7b4c75aa90 New translations strings.xml (Turkish) 2020-09-26 15:10:08 +02:00
PhilippC
a6c9e7a64a New translations strings.xml (Dutch) 2020-09-25 15:30:12 +02:00
PhilippC
4c0c011714 New translations strings.xml (Dutch) 2020-09-25 15:30:10 +02:00
PhilippC
4ba2f648df New translations strings.xml (Swedish) 2020-09-25 14:30:09 +02:00
PhilippC
d8c2966d87 New translations strings.xml (Swedish) 2020-09-25 13:30:10 +02:00
PhilippC
5a608fad93 New translations strings.xml (Vietnamese) 2020-09-17 17:00:10 +02:00
PhilippC
9790d6ff69 New translations strings.xml (Czech) 2020-09-17 13:10:10 +02:00
PhilippC
f24e23d417 New translations strings.xml (Czech) 2020-09-17 13:10:08 +02:00
PhilippC
2eea4c163c New translations strings.xml (Ukrainian) 2020-09-14 09:10:08 +02:00
PhilippC
541106f77b New translations strings.xml (Ukrainian) 2020-09-14 08:10:09 +02:00
PhilippC
2231a9638f New translations strings.xml (Russian) 2020-09-12 23:50:10 +02:00
PhilippC
e01f910869 New translations strings.xml (Russian) 2020-09-12 23:50:09 +02:00
PhilippC
4f511655b4 New translations strings.xml (Russian) 2020-09-12 22:50:08 +02:00
PhilippC
081d3ed4ff New translations strings.xml (Russian) 2020-09-12 22:50:07 +02:00
PhilippC
46703c5ed6 New translations strings.xml (German) 2020-09-12 18:40:07 +02:00
PhilippC
db2ad06e7a New translations strings.xml (French) 2020-09-07 07:41:10 +02:00
PhilippC
328998be57 New translations strings.xml (Chinese Simplified) 2020-09-07 07:40:11 +02:00
PhilippC
cb6dfa1521 New translations strings.xml (Catalan) 2020-09-01 14:10:09 +02:00
PhilippC
f54f127fd4 New translations strings.xml (Catalan) 2020-09-01 14:10:07 +02:00
PhilippC
00bc1e0998 New translations strings.xml (French) 2020-08-26 19:40:07 +02:00
PhilippC
8e0cad21d7 New translations strings.xml (Chinese Simplified) 2020-08-26 05:20:07 +02:00
PhilippC
1df873540c New translations strings.xml (Chinese Simplified) 2020-08-26 04:20:07 +02:00
PhilippC
454f7acb00 New translations strings.xml (Chinese Simplified) 2020-08-26 03:20:07 +02:00
PhilippC
e848a5e4d8 New translations strings.xml (Spanish) 2020-08-25 17:10:09 +02:00
PhilippC
5fa51ca833 New translations strings.xml (Spanish) 2020-08-25 16:10:07 +02:00
PhilippC
28ee65da9a New translations strings.xml (Vietnamese) 2020-08-23 21:10:06 +02:00
PhilippC
f2eeab8609 New translations strings.xml (Chinese Simplified) 2020-08-21 08:10:08 +02:00
PhilippC
37b529a1d8 New translations strings.xml (Slovak) 2020-08-20 14:00:10 +02:00
PhilippC
31f2abb3a8 New translations strings.xml (Slovak) 2020-08-20 14:00:09 +02:00
PhilippC
10b6e0f35e New translations strings.xml (Slovak) 2020-08-20 14:00:07 +02:00
PhilippC
f1d9655ea2 New translations strings.xml (Slovak) 2020-08-20 13:00:08 +02:00
PhilippC
793845c92e New translations strings.xml (Chinese Simplified) 2020-08-17 12:30:08 +02:00
PhilippC
52c8de4b5c New translations strings.xml (Chinese Simplified) 2020-08-17 12:30:07 +02:00
PhilippC
3b3b0d7388 New translations strings.xml (Chinese Simplified) 2020-08-17 11:30:09 +02:00
PhilippC
2bf4565c83 New translations strings.xml (French) 2020-08-16 14:10:07 +02:00
PhilippC
ee95bf3bcc New translations strings.xml (Spanish) 2020-08-15 07:30:07 +02:00
PhilippC
f53f7d0fee New translations strings.xml (Spanish) 2020-08-15 06:30:08 +02:00
PhilippC
8bc6e4c33e New translations strings.xml (Spanish) 2020-08-15 06:30:07 +02:00
PhilippC
4b78fab2ac New translations strings.xml (Japanese) 2020-08-11 15:10:09 +02:00
PhilippC
40f8b6da71 New translations strings.xml (Japanese) 2020-08-11 14:10:10 +02:00
PhilippC
f92cb3d3ea New translations strings.xml (Japanese) 2020-08-11 14:10:08 +02:00
PhilippC
1bc2ba2284 New translations strings.xml (Korean) 2020-08-10 08:30:09 +02:00
PhilippC
8ac84cb7c1 New translations strings.xml (Korean) 2020-08-10 08:30:08 +02:00
PhilippC
385d1b0979 New translations strings.xml (Korean) 2020-08-10 07:30:07 +02:00
PhilippC
01210c401d New translations strings.xml (Hungarian) 2020-08-09 17:30:09 +02:00
PhilippC
d48f54c086 New translations strings.xml (Chinese Traditional) 2020-08-06 16:10:08 +02:00
PhilippC
7a800928e2 New translations strings.xml (Chinese Traditional) 2020-08-06 15:10:08 +02:00
PhilippC
a893150c9e New translations strings.xml (Chinese Traditional) 2020-08-06 13:30:08 +02:00
Philipp Crocoll
50a1092a65 remove duplicates from Changelog 2020-08-06 06:38:36 +02:00
PhilippC
555109c243 Merge pull request #1352 from PhilippC/l10n_master2
New Crowdin updates
2020-08-06 06:34:30 +02:00
PhilippC
7409246425 New translations strings.xml (Chinese Simplified) 2020-08-05 14:40:07 +02:00
PhilippC
feb38f52ea New translations strings.xml (Slovenian) 2020-08-05 10:30:23 +02:00
PhilippC
69ac2d1303 New translations strings.xml (Slovenian) 2020-08-05 10:30:20 +02:00
PhilippC
023a026a85 New translations strings.xml (Finnish) 2020-08-05 05:46:08 +02:00
PhilippC
5198476d06 New translations strings.xml (Italian) 2020-08-05 05:46:02 +02:00
PhilippC
bc614ade20 New translations strings.xml (Japanese) 2020-08-05 05:46:00 +02:00
PhilippC
86cf3a9630 New translations strings.xml (Korean) 2020-08-05 05:45:58 +02:00
PhilippC
aa57120c9c New translations strings.xml (Dutch) 2020-08-05 05:45:56 +02:00
PhilippC
7ee0b78300 New translations strings.xml (Polish) 2020-08-05 05:45:54 +02:00
PhilippC
9ba4d95789 New translations strings.xml (Portuguese) 2020-08-05 05:45:51 +02:00
PhilippC
a221a6bfc3 New translations strings.xml (Greek) 2020-08-05 05:45:50 +02:00
PhilippC
0fea740a7f New translations strings.xml (French) 2020-08-05 05:45:48 +02:00
PhilippC
7755116473 New translations strings.xml (Spanish) 2020-08-05 05:45:45 +02:00
PhilippC
244f64fc4d New translations strings.xml (Czech) 2020-08-05 05:45:39 +02:00
PhilippC
c4d105c02f New translations strings.xml (Danish) 2020-08-05 05:45:37 +02:00
PhilippC
c25bfb8ade New translations strings.xml (German) 2020-08-05 05:45:35 +02:00
PhilippC
e49a1c3d6e New translations strings.xml (Catalan) 2020-08-05 05:45:33 +02:00
PhilippC
d81502e4b5 New translations strings.xml (Russian) 2020-08-05 05:45:30 +02:00
PhilippC
3763374f70 New translations strings.xml (Portuguese, Brazilian) 2020-08-05 05:45:27 +02:00
PhilippC
b043006a74 New translations strings.xml (Slovak) 2020-08-05 05:45:11 +02:00
PhilippC
6897bedd7f New translations strings.xml (Slovenian) 2020-08-05 05:45:09 +02:00
PhilippC
b7fc2c4d1c New translations strings.xml (Swedish) 2020-08-05 05:45:06 +02:00
PhilippC
4012c777a8 New translations strings.xml (Turkish) 2020-08-05 05:45:03 +02:00
PhilippC
0ca0dd0ea5 New translations strings.xml (Ukrainian) 2020-08-05 05:45:01 +02:00
PhilippC
a443507719 New translations strings.xml (Chinese Simplified) 2020-08-05 05:44:59 +02:00
PhilippC
03714c70e0 New translations strings.xml (Chinese Traditional) 2020-08-05 05:44:57 +02:00
PhilippC
6335d642ec New translations strings.xml (Korean) 2020-08-05 05:30:18 +02:00
PhilippC
48eb8c60ea New translations strings.xml (Malayalam) 2020-08-04 18:40:19 +02:00
PhilippC
065acfcb35 New translations strings.xml (Malayalam) 2020-08-04 18:40:17 +02:00
PhilippC
9b0d63b853 New translations strings.xml (Slovenian) 2020-08-04 08:20:09 +02:00
PhilippC
e6b93af59a New translations strings.xml (Slovenian) 2020-08-04 08:20:07 +02:00
PhilippC
024de21188 New translations strings.xml (Italian) 2020-08-04 06:31:58 +02:00
PhilippC
8ccab7fd78 New translations strings.xml (Japanese) 2020-08-04 06:31:55 +02:00
PhilippC
7165f455a4 New translations strings.xml (Korean) 2020-08-04 06:31:53 +02:00
PhilippC
ff5ada3195 New translations strings.xml (Dutch) 2020-08-04 06:31:50 +02:00
PhilippC
cae5f339c9 New translations strings.xml (Polish) 2020-08-04 06:31:48 +02:00
PhilippC
2b7b618302 New translations strings.xml (Greek) 2020-08-04 06:31:44 +02:00
PhilippC
5ec91691c8 New translations strings.xml (French) 2020-08-04 06:31:42 +02:00
PhilippC
690f456482 New translations strings.xml (Spanish) 2020-08-04 06:31:40 +02:00
PhilippC
fa7e3e229e New translations strings.xml (Czech) 2020-08-04 06:31:35 +02:00
PhilippC
08a9af6b80 New translations strings.xml (Danish) 2020-08-04 06:31:32 +02:00
PhilippC
4322d3c09c New translations strings.xml (German) 2020-08-04 06:31:30 +02:00
PhilippC
0ba83b48fd New translations strings.xml (Catalan) 2020-08-04 06:31:28 +02:00
PhilippC
dbdb98280a New translations strings.xml (Russian) 2020-08-04 06:31:25 +02:00
PhilippC
12641a754d New translations strings.xml (Portuguese, Brazilian) 2020-08-04 06:31:22 +02:00
PhilippC
882297877b New translations strings.xml (Slovak) 2020-08-04 06:31:10 +02:00
PhilippC
bef0bd5226 New translations strings.xml (Slovenian) 2020-08-04 06:31:08 +02:00
PhilippC
e20da59b7f New translations strings.xml (Swedish) 2020-08-04 06:31:04 +02:00
PhilippC
97682034a0 New translations strings.xml (Turkish) 2020-08-04 06:31:01 +02:00
PhilippC
4326aa88e9 New translations strings.xml (Ukrainian) 2020-08-04 06:30:58 +02:00
PhilippC
e60a5e6461 New translations strings.xml (Chinese Simplified) 2020-08-04 06:30:56 +02:00
PhilippC
258673062a New translations strings.xml (Chinese Traditional) 2020-08-04 06:30:54 +02:00
Philipp Crocoll
9b9ee82aea remove incorrect string from 1.08 changelog (belongs to 1.08b only) 2020-08-04 06:29:51 +02:00
PhilippC
fb6f1c3a6e Merge pull request #1351 from PhilippC/l10n_master2
New Crowdin updates
2020-08-04 06:28:33 +02:00
PhilippC
e8a2fe42e1 New translations strings.xml (German) 2020-08-04 06:21:51 +02:00
Philipp Crocoll
7dd422b0ce fix changelog: show 1.08 and 1.08b changes 2020-08-04 06:11:07 +02:00
PhilippC
5820def039 New translations strings.xml (German) 2020-08-04 06:10:07 +02:00
Philipp Crocoll
7abfbd3e3b Merge branch 'master' of c:/ph/keepass2android 2020-08-03 21:59:42 +02:00
Philipp Crocoll
9b9ea2ed78 update version to 1.08b-r1 2020-08-03 21:59:36 +02:00
Philipp Crocoll
7abcff24dc remove rPT and rCN folders as the translations are now synced to pt/zh 2020-08-03 21:55:39 +02:00
PhilippC
c1b35a9a7e Merge pull request #1349 from PhilippC/l10n_master2
New Crowdin updates
2020-08-03 21:44:24 +02:00
PhilippC
eeb9cc2324 New translations strings.xml (Norwegian Bokmal) 2020-08-03 21:42:51 +02:00
PhilippC
db564dda61 New translations strings.xml (Basque) 2020-08-03 21:42:49 +02:00
PhilippC
1dc89389db New translations strings.xml (Basque) 2020-08-03 21:42:48 +02:00
PhilippC
74eb2c64e7 New translations strings.xml (Finnish) 2020-08-03 21:42:46 +02:00
PhilippC
04f53d607b New translations strings.xml (Finnish) 2020-08-03 21:42:44 +02:00
PhilippC
ee1d0bd61b New translations strings.xml (Hebrew) 2020-08-03 21:42:43 +02:00
PhilippC
3f1a54a962 New translations strings.xml (Hebrew) 2020-08-03 21:42:41 +02:00
PhilippC
670a50fdce New translations strings.xml (Hungarian) 2020-08-03 21:42:39 +02:00
PhilippC
b1c89d5dbc New translations strings.xml (Hungarian) 2020-08-03 21:42:38 +02:00
PhilippC
5afb5640c4 New translations strings.xml (Italian) 2020-08-03 21:42:36 +02:00
PhilippC
799d9c0fd7 New translations strings.xml (Japanese) 2020-08-03 21:42:34 +02:00
PhilippC
4e833cd382 New translations strings.xml (Japanese) 2020-08-03 21:42:32 +02:00
PhilippC
e933c29715 New translations strings.xml (Korean) 2020-08-03 21:42:30 +02:00
PhilippC
0bb78b5b17 New translations strings.xml (Korean) 2020-08-03 21:42:28 +02:00
PhilippC
12daa78948 New translations strings.xml (Dutch) 2020-08-03 21:42:27 +02:00
PhilippC
8d563a9adf New translations strings.xml (Dutch) 2020-08-03 21:42:25 +02:00
PhilippC
ae3295ce04 New translations strings.xml (Polish) 2020-08-03 21:42:23 +02:00
PhilippC
ca4221b7fb New translations strings.xml (Polish) 2020-08-03 21:42:22 +02:00
PhilippC
816cdd3156 New translations strings.xml (Italian) 2020-08-03 21:42:21 +02:00
PhilippC
eb13676193 New translations strings.xml (Greek) 2020-08-03 21:42:19 +02:00
PhilippC
7111ef5c8e New translations strings.xml (Romanian) 2020-08-03 21:42:17 +02:00
PhilippC
86a40a8f3b New translations strings.xml (French) 2020-08-03 21:42:15 +02:00
PhilippC
268ed3990c New translations strings.xml (French) 2020-08-03 21:42:13 +02:00
PhilippC
bcde5f487d New translations strings.xml (Spanish) 2020-08-03 21:42:11 +02:00
PhilippC
b749728946 New translations strings.xml (Spanish) 2020-08-03 21:42:10 +02:00
PhilippC
7b6ac879db New translations strings.xml (Arabic) 2020-08-03 21:42:08 +02:00
PhilippC
5633403f63 New translations strings.xml (Arabic) 2020-08-03 21:42:07 +02:00
PhilippC
9f5ffca429 New translations strings.xml (Bulgarian) 2020-08-03 21:42:05 +02:00
PhilippC
4a71208629 New translations strings.xml (Greek) 2020-08-03 21:42:03 +02:00
PhilippC
8d7d960173 New translations strings.xml (Bulgarian) 2020-08-03 21:42:02 +02:00
PhilippC
5a69cd878d New translations strings.xml (Catalan) 2020-08-03 21:42:00 +02:00
PhilippC
e81b1d6928 New translations strings.xml (Catalan) 2020-08-03 21:41:59 +02:00
PhilippC
c22bace184 New translations strings.xml (Czech) 2020-08-03 21:41:57 +02:00
PhilippC
1dfaba8dad New translations strings.xml (Czech) 2020-08-03 21:41:55 +02:00
PhilippC
08c5403c00 New translations strings.xml (Danish) 2020-08-03 21:41:54 +02:00
PhilippC
8ac4ef3ce8 New translations strings.xml (Danish) 2020-08-03 21:41:52 +02:00
PhilippC
b6488af6fe New translations strings.xml (German) 2020-08-03 21:41:50 +02:00
PhilippC
65597bba5f New translations strings.xml (German) 2020-08-03 21:41:49 +02:00
PhilippC
88bdeebc93 New translations strings.xml (Romanian) 2020-08-03 21:41:46 +02:00
PhilippC
9eb73c745c New translations strings.xml (Portuguese) 2020-08-03 21:41:45 +02:00
PhilippC
410f5f9437 New translations strings.xml (Galician) 2020-08-03 21:41:43 +02:00
PhilippC
390f20af79 New translations strings.xml (Galician) 2020-08-03 21:41:41 +02:00
PhilippC
d0954ec83d New translations strings.xml (Portuguese, Brazilian) 2020-08-03 21:41:40 +02:00
PhilippC
0718a7b5dd New translations strings.xml (Portuguese, Brazilian) 2020-08-03 21:41:38 +02:00
PhilippC
bc9d80f369 New translations strings.xml (Portuguese, Brazilian) 2020-08-03 21:41:37 +02:00
PhilippC
f94491526f New translations strings.xml (Indonesian) 2020-08-03 21:41:35 +02:00
PhilippC
c60ab35ad3 New translations strings.xml (Indonesian) 2020-08-03 21:41:33 +02:00
PhilippC
2227221725 New translations strings.xml (Persian) 2020-08-03 21:41:31 +02:00
PhilippC
90646f2551 New translations strings.xml (Persian) 2020-08-03 21:41:30 +02:00
PhilippC
8536c26828 New translations strings.xml (Vietnamese) 2020-08-03 21:41:28 +02:00
PhilippC
a02e7a473f New translations strings.xml (Croatian) 2020-08-03 21:41:26 +02:00
PhilippC
80d2f8cebd New translations strings.xml (Norwegian Nynorsk) 2020-08-03 21:41:25 +02:00
PhilippC
94a79b3c11 New translations strings.xml (Norwegian Nynorsk) 2020-08-03 21:41:23 +02:00
PhilippC
13e530d173 New translations strings.xml (Azerbaijani) 2020-08-03 21:41:22 +02:00
PhilippC
832ce4996a New translations strings.xml (Azerbaijani) 2020-08-03 21:41:20 +02:00
PhilippC
5fefd53bab New translations strings.xml (Malayalam) 2020-08-03 21:41:18 +02:00
PhilippC
646e203ea5 New translations strings.xml (Malayalam) 2020-08-03 21:41:17 +02:00
PhilippC
35162c6205 New translations strings.xml (Norwegian Bokmal) 2020-08-03 21:41:15 +02:00
PhilippC
6ef20e2e07 New translations strings.xml (Croatian) 2020-08-03 21:41:14 +02:00
PhilippC
a9724638af New translations strings.xml (Portuguese) 2020-08-03 21:41:12 +02:00
PhilippC
54677a41a4 New translations strings.xml (Vietnamese) 2020-08-03 21:41:10 +02:00
PhilippC
c806130741 New translations strings.xml (Chinese Traditional) 2020-08-03 21:41:09 +02:00
PhilippC
8b79022e48 New translations strings.xml (Russian) 2020-08-03 21:41:07 +02:00
PhilippC
1271596c8c New translations strings.xml (Russian) 2020-08-03 21:41:06 +02:00
PhilippC
e82b7e397e New translations strings.xml (Slovak) 2020-08-03 21:41:04 +02:00
PhilippC
8813753a7e New translations strings.xml (Slovak) 2020-08-03 21:41:02 +02:00
PhilippC
4da61a1f76 New translations strings.xml (Slovenian) 2020-08-03 21:41:00 +02:00
PhilippC
eb7d63a49a New translations strings.xml (Slovenian) 2020-08-03 21:40:59 +02:00
PhilippC
22dc95f8d9 New translations strings.xml (Serbian (Cyrillic)) 2020-08-03 21:40:57 +02:00
PhilippC
ffa306f239 New translations strings.xml (Serbian (Cyrillic)) 2020-08-03 21:40:55 +02:00
PhilippC
3cd8bf12ea New translations strings.xml (Swedish) 2020-08-03 21:40:53 +02:00
PhilippC
ec51d0b52c New translations strings.xml (Turkish) 2020-08-03 21:40:51 +02:00
PhilippC
0622c30e78 New translations strings.xml (Turkish) 2020-08-03 21:40:50 +02:00
PhilippC
35e8338aca New translations strings.xml (Ukrainian) 2020-08-03 21:40:48 +02:00
PhilippC
c5a3312bb1 New translations strings.xml (Ukrainian) 2020-08-03 21:40:47 +02:00
PhilippC
1af865ae4e New translations strings.xml (Chinese Simplified) 2020-08-03 21:40:45 +02:00
PhilippC
542584a531 New translations strings.xml (Chinese Simplified) 2020-08-03 21:40:43 +02:00
PhilippC
6973546803 New translations strings.xml (Chinese Traditional) 2020-08-03 21:40:42 +02:00
PhilippC
875488775b New translations strings.xml (Swedish) 2020-08-03 21:40:40 +02:00
Philipp Crocoll
1c617ee172 update crowdin.yml 2020-08-03 21:36:54 +02:00
PhilippC
ff86ef2114 New translations strings.xml (Portuguese, Brazilian) 2020-08-03 21:33:36 +02:00
Philipp Crocoll
8386ffc6aa update crowdin.yml 2020-08-03 21:32:41 +02:00
PhilippC
5e65563e24 Merge pull request #1347 from PhilippC/l10n_master2
New Crowdin updates
2020-08-03 21:20:51 +02:00
PhilippC
a26fd7df82 New translations strings.xml (Portuguese) 2020-08-03 21:19:24 +02:00
PhilippC
0d659a4170 New translations strings.xml (Portuguese, Brazilian) 2020-08-03 21:19:19 +02:00
PhilippC
5b14afce8b Merge pull request #1345 from PhilippC/l10n_master2
New Crowdin updates
2020-08-03 21:00:08 +02:00
PhilippC
4856c31476 New translations strings.xml (Portuguese) 2020-08-03 20:57:38 +02:00
PhilippC
e07a33bb3f New translations strings.xml (Malayalam) 2020-08-03 12:40:10 +02:00
PhilippC
2d5ae5d24f New translations strings.xml (Slovenian) 2020-08-03 12:40:08 +02:00
PhilippC
2f0da0ae55 New translations strings.xml (Malayalam) 2020-08-03 11:40:57 +02:00
PhilippC
b33ab579bc New translations strings.xml (Catalan) 2020-08-03 11:40:55 +02:00
PhilippC
94c8cb4c33 New translations strings.xml (Czech) 2020-08-03 11:40:54 +02:00
PhilippC
bc1143cc29 New translations strings.xml (Danish) 2020-08-03 11:40:52 +02:00
PhilippC
9e357e63fb New translations strings.xml (German) 2020-08-03 11:40:50 +02:00
PhilippC
f07a8fc831 New translations strings.xml (Greek) 2020-08-03 11:40:49 +02:00
PhilippC
1306ae91e0 New translations strings.xml (Italian) 2020-08-03 11:40:47 +02:00
PhilippC
11f7e6d01f New translations strings.xml (Japanese) 2020-08-03 11:40:45 +02:00
PhilippC
c815ee5a54 New translations strings.xml (Korean) 2020-08-03 11:40:44 +02:00
PhilippC
cd92c7a135 New translations strings.xml (Dutch) 2020-08-03 11:40:42 +02:00
PhilippC
6dcdeb1d81 New translations strings.xml (Spanish) 2020-08-03 11:40:40 +02:00
PhilippC
05b6eb8d47 New translations strings.xml (Polish) 2020-08-03 11:40:39 +02:00
PhilippC
6f12c57780 New translations strings.xml (Slovak) 2020-08-03 11:40:37 +02:00
PhilippC
18c806bb68 New translations strings.xml (Slovenian) 2020-08-03 11:40:35 +02:00
PhilippC
17b984dd75 New translations strings.xml (Swedish) 2020-08-03 11:40:33 +02:00
PhilippC
ac889e9b1f New translations strings.xml (Turkish) 2020-08-03 11:40:32 +02:00
PhilippC
e043c7aa85 New translations strings.xml (Chinese Simplified) 2020-08-03 11:40:30 +02:00
PhilippC
c4a9d0b7b9 New translations strings.xml (Chinese Traditional) 2020-08-03 11:40:28 +02:00
PhilippC
3902e35738 New translations strings.xml (Portuguese, Brazilian) 2020-08-03 11:40:26 +02:00
PhilippC
627684a806 New translations strings.xml (Russian) 2020-08-03 11:40:24 +02:00
PhilippC
b16e95a527 New translations strings.xml (French) 2020-08-03 11:40:22 +02:00
Philipp Crocoll
8fcf608515 Merge branch 'master' of c:/ph/keepass2android 2020-08-03 11:34:48 +02:00
Philipp Crocoll
2d86b7422d manifest and changelog for 1.08b-r0 2020-08-03 11:33:03 +02:00
Philipp Crocoll
3c7cec40a2 Merge remote-tracking branch 'remotes/origin/l10n_master2' 2020-08-03 10:53:26 +02:00
PhilippC
c93515a45a New translations strings.xml (Malayalam) 2020-08-03 10:40:08 +02:00
PhilippC
73e9631459 New translations strings.xml (Chinese Traditional) 2020-07-29 17:30:09 +02:00
PhilippC
e922d01ee7 New translations strings.xml (Chinese Simplified) 2020-07-29 01:40:06 +02:00
PhilippC
d77e45ee41 New translations strings.xml (Chinese Simplified) 2020-07-29 00:40:07 +02:00
PhilippC
b3774ea1a8 New translations strings.xml (Chinese Traditional) 2020-07-28 16:30:07 +02:00
PhilippC
35324c4f59 New translations strings.xml (Chinese Traditional) 2020-07-28 15:30:08 +02:00
PhilippC
81f3b354b4 New translations strings.xml (Spanish) 2020-07-27 15:40:07 +02:00
PhilippC
3c7bc76d13 New translations strings.xml (Italian) 2020-07-27 14:40:09 +02:00
PhilippC
fde262e6a2 New translations strings.xml (Spanish) 2020-07-27 14:40:08 +02:00
PhilippC
9dcefc79ac New translations strings.xml (Spanish) 2020-07-27 13:40:07 +02:00
PhilippC
019a20e983 New translations strings.xml (Dutch) 2020-07-27 12:40:55 +02:00
PhilippC
b7655ad5ee New translations strings.xml (Korean) 2020-07-27 12:40:53 +02:00
PhilippC
6e3d923ba5 New translations strings.xml (Japanese) 2020-07-27 12:40:52 +02:00
PhilippC
c9cd62eff9 New translations strings.xml (Italian) 2020-07-27 12:40:50 +02:00
PhilippC
ade983ae6c New translations strings.xml (Finnish) 2020-07-27 12:40:47 +02:00
PhilippC
5dd91b96c8 New translations strings.xml (Polish) 2020-07-27 12:40:45 +02:00
PhilippC
f4f4a55aba New translations strings.xml (German) 2020-07-27 12:40:43 +02:00
PhilippC
af76080539 New translations strings.xml (Danish) 2020-07-27 12:40:41 +02:00
PhilippC
a76c394f1a New translations strings.xml (Czech) 2020-07-27 12:40:39 +02:00
PhilippC
040f229ec1 New translations strings.xml (Catalan) 2020-07-27 12:40:38 +02:00
PhilippC
617d4e0b99 New translations strings.xml (Spanish) 2020-07-27 12:40:35 +02:00
PhilippC
747f89aa9e New translations strings.xml (Greek) 2020-07-27 12:40:33 +02:00
PhilippC
0b766062fd New translations strings.xml (French) 2020-07-27 12:40:31 +02:00
PhilippC
624a4430ca New translations strings.xml (Portuguese) 2020-07-27 12:40:29 +02:00
PhilippC
04377b03af New translations strings.xml (Slovak) 2020-07-27 12:40:28 +02:00
PhilippC
cbcbdacca1 New translations strings.xml (Portuguese, Brazilian) 2020-07-27 12:40:23 +02:00
PhilippC
1cf6481847 New translations strings.xml (Russian) 2020-07-27 12:40:20 +02:00
PhilippC
902ae521ad New translations strings.xml (Chinese Simplified) 2020-07-27 12:40:18 +02:00
PhilippC
dd679c295d New translations strings.xml (Ukrainian) 2020-07-27 12:40:16 +02:00
PhilippC
f71b44c329 New translations strings.xml (Turkish) 2020-07-27 12:40:14 +02:00
PhilippC
d0f13a7aaf New translations strings.xml (Swedish) 2020-07-27 12:40:12 +02:00
PhilippC
7b071e8bf9 New translations strings.xml (Slovenian) 2020-07-27 12:40:10 +02:00
PhilippC
c24b592ed1 New translations strings.xml (Chinese Traditional) 2020-07-27 12:40:08 +02:00
PhilippC
3faabacfa6 New translations strings.xml (Portuguese) 2020-07-27 11:39:32 +02:00
PhilippC
0587398f86 New translations strings.xml (Portuguese, Brazilian) 2020-07-27 11:39:28 +02:00
PhilippC
b7b4bde51d New translations strings.xml (German) 2020-07-27 11:39:23 +02:00
Philipp Crocoll
160dd70412 adjust changelog for 1.08b-r1 2020-07-27 11:38:30 +02:00
PhilippC
e354bbd647 New translations strings.xml (German) 2020-07-27 11:36:12 +02:00
PhilippC
b3e67cf8d6 New translations strings.xml (Portuguese) 2020-07-27 11:35:15 +02:00
PhilippC
b9c2dd83a9 New translations strings.xml (Portuguese, Brazilian) 2020-07-27 11:34:40 +02:00
Philipp Crocoll
0661598244 improve message title 2020-07-27 11:33:01 +02:00
PhilippC
4d579fabdf New translations strings.xml (Norwegian Bokmal) 2020-07-27 11:26:02 +02:00
PhilippC
6f77fac4b2 New translations strings.xml (Dutch) 2020-07-27 11:26:00 +02:00
PhilippC
cd9bd03c47 New translations strings.xml (Korean) 2020-07-27 11:25:58 +02:00
PhilippC
932cb97e89 New translations strings.xml (Japanese) 2020-07-27 11:25:56 +02:00
PhilippC
bedf92595c New translations strings.xml (Italian) 2020-07-27 11:25:55 +02:00
PhilippC
316668304e New translations strings.xml (Hungarian) 2020-07-27 11:25:53 +02:00
PhilippC
3da270c053 New translations strings.xml (Hebrew) 2020-07-27 11:25:52 +02:00
PhilippC
0d28047dfa New translations strings.xml (Finnish) 2020-07-27 11:25:50 +02:00
PhilippC
ee4677b70e New translations strings.xml (Polish) 2020-07-27 11:25:48 +02:00
PhilippC
f8462fcdbc New translations strings.xml (Basque) 2020-07-27 11:25:44 +02:00
PhilippC
2afb269268 New translations strings.xml (German) 2020-07-27 11:25:43 +02:00
PhilippC
5fa13c6316 New translations strings.xml (Danish) 2020-07-27 11:25:41 +02:00
PhilippC
1c4c7243ed New translations strings.xml (Czech) 2020-07-27 11:25:39 +02:00
PhilippC
c9e6a521dc New translations strings.xml (Catalan) 2020-07-27 11:25:37 +02:00
PhilippC
f2ecb9bc22 New translations strings.xml (Bulgarian) 2020-07-27 11:25:36 +02:00
PhilippC
d60a20f2a5 New translations strings.xml (Arabic) 2020-07-27 11:25:34 +02:00
PhilippC
c32a118157 New translations strings.xml (Spanish) 2020-07-27 11:25:32 +02:00
PhilippC
07e6e440f2 New translations strings.xml (Greek) 2020-07-27 11:25:30 +02:00
PhilippC
4d6db6e394 New translations strings.xml (French) 2020-07-27 11:25:28 +02:00
PhilippC
9fa0f541ae New translations strings.xml (Portuguese) 2020-07-27 11:25:27 +02:00
PhilippC
73c5bab70c New translations strings.xml (Slovak) 2020-07-27 11:25:25 +02:00
PhilippC
929fb69beb New translations strings.xml (Azerbaijani) 2020-07-27 11:25:23 +02:00
PhilippC
e52e8c76a4 New translations strings.xml (Norwegian Nynorsk) 2020-07-27 11:25:22 +02:00
PhilippC
1bf294c6fe New translations strings.xml (Croatian) 2020-07-27 11:25:20 +02:00
PhilippC
d172aca74d New translations strings.xml (Persian) 2020-07-27 11:25:19 +02:00
PhilippC
66a62df64a New translations strings.xml (Indonesian) 2020-07-27 11:25:17 +02:00
PhilippC
902fd70071 New translations strings.xml (Portuguese, Brazilian) 2020-07-27 11:25:15 +02:00
PhilippC
b7b938fef3 New translations strings.xml (Galician) 2020-07-27 11:25:14 +02:00
PhilippC
6a6437fefb New translations strings.xml (Russian) 2020-07-27 11:25:12 +02:00
PhilippC
b90e959e48 New translations strings.xml (Vietnamese) 2020-07-27 11:25:10 +02:00
PhilippC
abeefab726 New translations strings.xml (Chinese Simplified) 2020-07-27 11:25:09 +02:00
PhilippC
1bea5cd1ef New translations strings.xml (Ukrainian) 2020-07-27 11:25:07 +02:00
PhilippC
b4e22849bd New translations strings.xml (Turkish) 2020-07-27 11:25:05 +02:00
PhilippC
07adea2465 New translations strings.xml (Swedish) 2020-07-27 11:25:03 +02:00
PhilippC
e7fdb9239e New translations strings.xml (Serbian (Cyrillic)) 2020-07-27 11:25:01 +02:00
PhilippC
b70adfbc09 New translations strings.xml (Slovenian) 2020-07-27 11:25:00 +02:00
PhilippC
1474549886 New translations strings.xml (Chinese Traditional) 2020-07-27 11:24:58 +02:00
PhilippC
bfdf731fdd New translations strings.xml (Romanian) 2020-07-27 11:24:56 +02:00
Philipp Crocoll
d3ced0cdf3 improve user experience with autofill security alert:
* remember trusted links
 * allow to make an app trusted
 * improve message by displaying application label instead of package name (show package name once for security)
2020-07-27 11:19:29 +02:00
Philipp Crocoll
5fc2f94688 Merge branch 'master' of c:/ph/keepass2android 2020-07-06 21:11:07 +02:00
Philipp Crocoll
5aab4c98cf fix potential crash 2020-07-06 21:10:57 +02:00
Philipp Crocoll
02073c01b6 change to behavior of Autofill: If domain and package are not "compatible" (i.e. package is a trusted web browser or later DAL verification succeeded), allow to insert the credentials for the domain (no longer the package), but ask the user if he trusts the app. 2020-07-06 21:08:15 +02:00
Philipp Crocoll
d14d287c3a improve keyboard activation 2020-06-29 19:45:40 +02:00
Philipp Crocoll
862083c0be only show Google Drive option if device has PlayServices available 2020-06-29 12:02:45 +02:00
Philipp Crocoll
1f9412c5cf avoid potentially failing cast, fixes issue reported on https://github.com/PhilippC/keepass2android/issues/719#issuecomment-645185985 2020-06-17 21:00:39 +02:00
Philipp Crocoll
dd18e505a6 add package reference to GooglePlayServices.Auth to fix NoClassDefFound when using GoogleDrive. Add Changelog For 1.08b-pre2. 2020-06-16 21:46:41 +02:00
Philipp Crocoll
7e2db583a0 Merge branch 'master' of c:/ph/keepass2android 2020-06-15 13:10:20 +02:00
Philipp Crocoll
8d33ced838 manifest for 1.08-r2 release 2020-06-15 13:10:11 +02:00
Philipp Crocoll
f83e5dcc6e update FluentFTP, hoping to see improvements onn https://github.com/PhilippC/keepass2android/issues/1284 2020-06-15 11:31:44 +02:00
Philipp Crocoll
eeaa9f3280 code cleanup 2020-06-15 11:25:08 +02:00
Philipp Crocoll
1fad137c1e fix https://github.com/PhilippC/keepass2android/issues/1015 (bad error message when file not found) 2020-06-15 11:24:38 +02:00
Philipp Crocoll
b88bca35ab fix potential crash when an unknown scheme is entered while editing the URL of a recent file, fixes https://github.com/PhilippC/keepass2android/issues/1055 2020-06-14 20:16:36 +02:00
Philipp Crocoll
e9b9d0a781 fix crash when switching tasks while saving, should fix https://github.com/PhilippC/keepass2android/issues/719 2020-06-14 20:14:39 +02:00
Philipp Crocoll
d30c532a88 fix to password input behind view icon, fixes https://github.com/PhilippC/keepass2android/issues/370 2020-06-14 06:45:16 +02:00
Philipp Crocoll
b4dbc4bc08 improve display of database name/location in notifications, fixes https://github.com/PhilippC/keepass2android/issues/568 2020-06-14 06:29:03 +02:00
Philipp Crocoll
85d852cc54 fix https://github.com/PhilippC/keepass2android/issues/1282 (saving through autofill not working on Android 10 due to missing NewTask flag) 2020-06-13 20:36:46 +02:00
Philipp Crocoll
484a4732f8 fix https://github.com/PhilippC/keepass2android/issues/695 (missing fields on keyboard dialog) 2020-06-13 20:33:58 +02:00
Philipp Crocoll
0649de8cc6 changing the intent filter for ActionView, trying not to be too broad as before but hopefully still catching most intents for viewing kdbx files. Closes https://github.com/PhilippC/keepass2android/issues/816 and https://github.com/PhilippC/keepass2android/issues/157 2020-06-11 11:41:30 +02:00
Philipp Crocoll
69f8481cf1 small code cleanup 2020-06-11 07:27:38 +02:00
Philipp Crocoll
1625e5e3ac specify flag exclude from recents to fix https://github.com/PhilippC/keepass2android/issues/912 2020-06-11 07:27:23 +02:00
Philipp Crocoll
be1cc06a4b reuse WebDav client; forcing WebDav to use http1.1. This is a workaround to fix https://github.com/PhilippC/keepass2android/issues/747 2020-06-11 06:01:47 +02:00
Philipp Crocoll
adc45d8151 show nicer error messages 2020-06-10 21:52:35 +02:00
Philipp Crocoll
965a79f029 don't throw potential exception when checking if file is readonly 2020-05-30 19:48:02 +02:00
Philipp Crocoll
7c72f781a2 add empty file 2020-03-09 12:31:53 +01:00
Philipp Crocoll
b9a15471ef copy FTP data to memory stream to avoid issues when client is closed before reading is complete, closes #1094 2020-03-09 11:58:19 +01:00
Philipp Crocoll
e667f4e89c fix potential NullReferenceException 2020-03-09 11:52:40 +01:00
Philipp Crocoll
349f6c7c90 fix potential crash when opening from internal cache, closes #1179 2020-03-09 10:28:44 +01:00
Philipp Crocoll
e688639f47 Merge branch 'master' into Branch_e1416b9 2020-03-02 10:10:49 +01:00
Philipp Crocoll
8fbeb5c409 upgrading build tools and NuGet packages. switching to Xamarin's Biometric Bindings (even though this reverts from 1.0.1 to 1.0.0) because of build issues/incompatibilities 2020-03-02 10:07:55 +01:00
Philipp Crocoll
9d34dfe23e enable compatibility mode for autofill closes #290, closes #1061 2020-02-17 11:59:04 +01:00
Philipp Crocoll
01ea54932c allow to disable fingerprint unlock (in QuickUnlock mode) after three failed attempts, closes #16 2020-02-16 21:26:33 +01:00
Philipp Crocoll
ad0ac93bd3 add more logging to diagnose why keyboard is switched back sometimes 2020-02-16 05:28:54 +01:00
Philipp Crocoll
dc9d830d16 show username in search result, closes #546 2020-02-15 10:18:38 +01:00
Philipp Crocoll
cab797ee1e delete zh-rCN folder, is now in -zh, closes #651 2020-02-15 10:08:38 +01:00
Philipp Crocoll
e1416b984e Apply password font in EditText views, closes #404 2020-02-15 10:05:27 +01:00
Philipp Crocoll
ff95c32c2d Request focus before calling ShowInputMethod, closes #1132 2020-02-15 09:35:49 +01:00
Philipp Crocoll
2b2deb291a catch invalid dates, closes #868 2020-02-15 04:15:35 +01:00
Philipp Crocoll
e51999ea1f display notifications to allow copying TOTP to clipboard when autofilling, closes #1072 2020-02-15 03:55:43 +01:00
Philipp Crocoll
7ba17b8552 start implementing notifications when searching, e.g. for Autofill 2020-02-15 03:26:35 +01:00
Philipp Crocoll
406723fab0 manifest for 1.08-r1 2020-02-15 03:24:52 +01:00
Philipp Crocoll
e1ea3a1502 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-02-13 21:35:02 +01:00
Philipp Crocoll
3f101c070d update to biometric-1.0.1 2020-02-13 21:34:10 +01:00
Philipp Crocoll
dd49cc9324 Merge branch 'master' of c:/ph/keepass2android 2020-02-10 11:00:59 +01:00
Philipp Crocoll
d607151734 changelog for 1.08-r0 2020-02-10 11:00:51 +01:00
Philipp Crocoll
9b780cb216 update version for 1.08-r0 2020-02-10 10:07:13 +01:00
Philipp Crocoll
bd4c01c630 add missing files, ignore some files 2020-02-10 10:06:59 +01:00
Philipp Crocoll
7f4a151a63 manifest for 1.08-r0 2020-01-31 06:20:04 +01:00
PhilippC
d6174dc69e Merge pull request #1135 from PhilippC/l10n_master
New Crowdin translations
2020-01-31 06:21:24 +01:00
PhilippC
61cd5775a2 New translations strings.xml (German) 2020-01-31 06:20:09 +01:00
Philipp Crocoll
69b4828add Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-01-31 05:24:06 +01:00
Philipp Crocoll
ca09103e98 removed no longer needed subfolder 2020-01-31 05:22:53 +01:00
PhilippC
efadf03238 Merge pull request #1127 from PhilippC/l10n_master
New Crowdin translations
2020-01-31 05:22:14 +01:00
Philipp Crocoll
11727b80f2 simplify code for ftp transaction commit 2020-01-31 05:21:35 +01:00
PhilippC
e57b7179e3 New translations strings.xml (Basque) 2020-01-24 00:30:06 +01:00
PhilippC
1c720ddb7d New translations strings.xml (Basque) 2020-01-24 00:20:07 +01:00
PhilippC
d19396e542 Merge pull request #1107 from PhilippC/l10n_master
New Crowdin translations
2020-01-16 20:15:28 +01:00
PhilippC
ae436249b7 New translations strings.xml (Portuguese, Brazilian) 2020-01-16 20:15:05 +01:00
PhilippC
4518a87f24 New translations strings.xml (Portuguese) 2020-01-16 20:14:41 +01:00
PhilippC
4cc0359c4d New translations strings.xml (Vietnamese) 2020-01-15 20:48:09 +01:00
PhilippC
ec122e078b New translations strings.xml (Greek) 2020-01-15 20:48:07 +01:00
PhilippC
bbc0ba7b3c New translations strings.xml (Croatian) 2020-01-15 20:47:53 +01:00
PhilippC
c4ba08db15 New translations strings.xml (Czech) 2020-01-15 20:47:49 +01:00
PhilippC
75ab8d4eed New translations strings.xml (Danish) 2020-01-15 20:47:44 +01:00
PhilippC
d91cfe7cb0 New translations strings.xml (Dutch) 2020-01-15 20:47:39 +01:00
PhilippC
037c7d35d7 New translations strings.xml (Finnish) 2020-01-15 20:47:35 +01:00
PhilippC
6fd0ab790c New translations strings.xml (French) 2020-01-15 20:47:32 +01:00
PhilippC
1a578ca68e New translations strings.xml (Polish) 2020-01-15 20:47:29 +01:00
PhilippC
60ea6cef08 New translations strings.xml (Hebrew) 2020-01-15 20:47:25 +01:00
PhilippC
d795dbffee New translations strings.xml (Hungarian) 2020-01-15 20:47:23 +01:00
PhilippC
a5163df44c New translations strings.xml (Indonesian) 2020-01-15 20:47:20 +01:00
PhilippC
9fd62088ab New translations strings.xml (Galician) 2020-01-15 20:47:16 +01:00
PhilippC
1f162c85d8 New translations strings.xml (Korean) 2020-01-15 20:47:13 +01:00
PhilippC
9ac6a6a770 New translations strings.xml (Turkish) 2020-01-15 20:47:09 +01:00
PhilippC
78de922261 New translations strings.xml (Swedish) 2020-01-15 20:47:07 +01:00
PhilippC
76c839b091 New translations strings.xml (Spanish) 2020-01-15 20:47:03 +01:00
PhilippC
b5294d6d25 New translations strings.xml (Slovenian) 2020-01-15 20:46:58 +01:00
PhilippC
764963b087 New translations strings.xml (Japanese) 2020-01-15 20:46:54 +01:00
PhilippC
0f7ce379d6 New translations strings.xml (Serbian (Cyrillic)) 2020-01-15 20:46:53 +01:00
PhilippC
1d066f13ba New translations strings.xml (Russian) 2020-01-15 20:46:49 +01:00
PhilippC
1c9450ca10 New translations strings.xml (Slovak) 2020-01-15 20:46:47 +01:00
PhilippC
4f4f9405fe New translations strings.xml (Portuguese, Brazilian) 2020-01-15 20:46:43 +01:00
PhilippC
284e5e472f New translations strings.xml (Portuguese) 2020-01-15 20:46:39 +01:00
PhilippC
14c1026ad1 New translations strings.xml (Persian) 2020-01-15 20:46:34 +01:00
PhilippC
a6f69cc433 New translations strings.xml (Norwegian Bokmal) 2020-01-15 20:46:28 +01:00
PhilippC
dfb50586aa New translations strings.xml (Romanian) 2020-01-15 20:46:26 +01:00
PhilippC
4f1358ffc7 New translations strings.xml (Catalan) 2020-01-15 20:46:22 +01:00
PhilippC
1d12b1c925 New translations strings.xml (Basque) 2020-01-15 20:46:19 +01:00
PhilippC
de8202554a New translations strings.xml (Azerbaijani) 2020-01-15 20:46:17 +01:00
PhilippC
80655e1ca7 New translations strings.xml (Arabic) 2020-01-15 20:46:12 +01:00
PhilippC
4148399f6d New translations strings.xml (Chinese Simplified) 2020-01-15 20:46:10 +01:00
PhilippC
0bcba06ebf New translations strings.xml (Chinese Traditional) 2020-01-15 20:46:09 +01:00
PhilippC
c2cc965c21 New translations strings.xml (Bulgarian) 2020-01-15 20:46:04 +01:00
Philipp Crocoll
dd0c950474 unregister receiver in SelectCurrentDbActivity, closes https://github.com/PhilippC/keepass2android/issues/1111 2020-01-15 05:30:36 +01:00
Philipp Crocoll
b7c8baacc9 OneDrive: improve logging on error; fix creating new file paths, closes https://github.com/PhilippC/keepass2android/issues/1053 2020-01-15 05:18:46 +01:00
PhilippC
5e2fa65a4c New translations strings.xml (Ukrainian) 2020-01-14 00:30:07 +01:00
Philipp Crocoll
52676eb113 make sure we ask if user wants to remember the selected entry even when he didn't select "Choose another entry" (but used search directly), closes https://github.com/PhilippC/keepass2android/issues/742 2020-01-13 13:13:54 +01:00
Philipp Crocoll
bf5ca8908e don't search in groups with the "search disabled flag" when searching for host (i.e. when Share URL is used), closes https://github.com/PhilippC/keepass2android/issues/691 2020-01-13 12:23:45 +01:00
PhilippC
acbe62d6ed New translations strings.xml (Ukrainian) 2020-01-13 11:00:08 +01:00
PhilippC
79ec921abc New translations strings.xml (Ukrainian) 2020-01-13 10:50:09 +01:00
Philipp Crocoll
65bd88af72 changes to search provider, mainly removing special characters from suggestion as several users are reporting crashes on Android 9 when Tabs are in the notes 2020-01-11 17:14:27 +01:00
Philipp Crocoll
0273b4d2bb AndroidContentStorage: truncate file before writing, closes #583 2020-01-08 21:21:36 +01:00
PhilippC
63ef21287a New translations strings.xml (Italian) 2020-01-08 16:00:10 +01:00
PhilippC
1be320b755 New translations strings.xml (Ukrainian) 2020-01-08 11:40:07 +01:00
PhilippC
abcad958ab New translations strings.xml (German) 2020-01-06 14:40:09 +01:00
PhilippC
35b4350455 New translations strings.xml (German) 2020-01-06 14:30:07 +01:00
Philipp Crocoll
8f1251e59b set "no personalized learning" flag for IMEs when entering data to the database, closing #718 2020-01-06 11:57:07 +01:00
Philipp Crocoll
0400ab3c53 upgrade okhttp-digest to 2.0 to fix #1032 2020-01-06 11:21:14 +01:00
Philipp Crocoll
2f3d614ac0 shorten string for Send action to close #734 2019-12-18 05:28:40 +01:00
Philipp Crocoll
fd90b10444 make notes field selectable to close #595 2019-12-16 12:48:36 +01:00
Philipp Crocoll
7f5ec35ada trim TOTP seed to close #340 2019-12-16 12:39:20 +01:00
Philipp Crocoll
b7f7b0d470 allow to add custom fields when editing a template entry, closes #840 2019-12-16 12:32:53 +01:00
Philipp Crocoll
97fb35db90 urldecode display name in WebDavStorage to close #792 2019-12-16 12:18:02 +01:00
Philipp Crocoll
69abcd0786 remove reference to old NetFtp, as this was exchanged for FluentFTP; fix issue with paths that need url encoding, closing #736, closing #615 2019-12-16 12:09:41 +01:00
Philipp Crocoll
d697b93f6a allow to show attachments for template entries 2019-12-16 12:05:10 +01:00
Philipp Crocoll
2290b35317 add jp.hazuki.yuzubrowser to list of trusted browsers, closing #1067 2019-12-08 20:31:15 +01:00
Philipp Crocoll
d838ff0cd9 add support for Lightning browser to close #1034 2019-11-12 06:15:46 +01:00
Philipp Crocoll
a2aa9d61a9 read the reply after writing the file as stated in the docs 2019-11-07 20:09:42 +01:00
Philipp Crocoll
056d791ae9 fix typo on new string, fix compiler error 2019-11-06 22:08:56 +01:00
Philipp Crocoll
d3acacb20b update strings from fingerprint to biometric authentication 2019-11-06 21:56:59 +01:00
Philipp Crocoll
bb74305b26 remove accidentally commited code, was for testing only (into 1.08-pre3) 2019-11-06 20:52:55 +01:00
Philipp Crocoll
32549c9eff make sure biometric prompt does not show up directly after authentication (into 1.08-pre3) 2019-11-06 19:31:57 +01:00
Philipp Crocoll
83b5aa7d7b don't require confirmation for face unlock, as suggested in #795; makes it into pre3 2019-11-06 18:52:34 +01:00
Philipp Crocoll
640a2c7e88 manifest for 1.08-pre3 2019-11-06 18:28:04 +01:00
Philipp Crocoll
31aad963d6 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-11-06 18:27:37 +01:00
Philipp Crocoll
326f4282d7 fix potential crash (opening an activity without NewTask flag) 2019-11-06 18:27:24 +01:00
Philipp Crocoll
e5b3cf3db6 trust more Opera browsers 2019-11-06 17:46:26 +01:00
PhilippC
ca3e4a5135 Merge pull request #1023 from squabbi/patch-1
Update How-to-create-a-plug-in_.md
2019-11-06 11:37:50 -05:00
Jayden
c1607bbfb3 Update How-to-create-a-plug-in_.md
Fix malformed link for QR code Play Store link
2019-11-06 16:05:24 +11:00
Philipp Crocoll
0639201b4c add compatibility with steam encoding for OTPs (e.g. otpauth://totp/test:test@example.com?secret=63BEDWCQZKTQWPESARIERL5DTTQFCJTK&issuer=Valve&algorithm=SHA1&digits=5&period=30&encoder=steam) 2019-11-06 04:50:29 +01:00
Philipp Crocoll
b5fe3b8ce4 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-11-05 22:41:10 +01:00
Philipp Crocoll
2362efe5fc fix SelectCurrentDbActivity not correctly forwarding to PasswordActivity 2019-11-05 22:40:59 +01:00
Philipp Crocoll
308d8889e4 add missing permission and fix app version for 1.08-pre2 (142 was already used for pre1) 2019-11-05 21:33:14 +01:00
Philipp Crocoll
5e4ea21377 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-11-05 20:56:53 +01:00
Philipp Crocoll
45489ad600 changelog and manifest for 1.08-pre2 2019-11-05 20:56:45 +01:00
Philipp Crocoll
683cde5a8b update targetSdkVersion to 29 (Android 10/Q). Change canvas clipping in Soft keyboard to adhere to behavior changes in Android 10. Migrate to AndroidX. Migrate to using BiometricPrompt instead of FingerprintManager, keeping compatibility to SamsungPass API for devices like SGS5. Closes #795, should close #626, closes #910 2019-11-05 20:55:02 +01:00
Philipp Crocoll
8a9c781de2 packages.config to .csproj 2019-11-01 20:35:25 +01:00
PhilippC
6812998c8d Update README.md 2019-10-29 19:43:41 +01:00
Philipp Crocoll
89fda1eee5 update to target Android Q (build tools and targetSdkVersion) 2019-10-28 20:24:20 +01:00
Philipp Crocoll
0cf0170144 changelog and manifest for 1.08-pre1 2019-10-27 22:30:02 +01:00
Philipp Crocoll
cb79c8b1ff Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-10-27 22:19:57 +01:00
Philipp Crocoll
658dd6a0ec add kiwi browser to list of support autofill browsers 2019-10-27 22:11:36 +01:00
Philipp Crocoll
e6049eca2c upgrade OkHttp to 4.2.2 2019-10-27 22:11:03 +01:00
Philipp Crocoll
efcaa36325 no donate notice in changelog when using the app for one of the first times 2019-10-21 10:33:31 +02:00
Philipp Crocoll
905efd0454 Merge branch 'master' of https://github.com/PhilippC/keepass2android
# Resolved Conflicts:
#	src/keepass2android/Resources/values/strings.xml
2019-10-21 10:24:15 +02:00
Philipp Crocoll
3ce1c04cf2 Merge branch 'Branch_1.07'
# Resolved Conflicts:
#	src/keepass2android/Resources/values/strings.xml
2019-10-10 03:25:02 +02:00
Philipp Crocoll
cb979f8975 Merge remote-tracking branch 'remotes/origin/master' 2019-10-10 03:22:31 +02:00
Philipp Crocoll
cc8cea0049 manifest for 1.07b-r0 2019-10-10 03:20:48 +02:00
Philipp Crocoll
a94e6143e1 "rebase" changes to strings.xml to be in sync with translations from crowdin 2019-10-10 03:15:15 +02:00
PhilippC
699c97eaf9 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:09:25 +02:00
PhilippC
10d4ec9bba New translations strings.xml (Portuguese) 2019-10-10 03:09:25 +02:00
PhilippC
e1f1b10f56 New translations strings.xml (French) 2019-10-10 03:09:25 +02:00
PhilippC
cfc09b8f3b New translations strings.xml (Swedish) 2019-10-10 03:09:24 +02:00
PhilippC
4c1571ff2d New translations strings.xml (Spanish) 2019-10-10 03:09:24 +02:00
PhilippC
5aea520cd1 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:09:24 +02:00
PhilippC
00294f1aad New translations strings.xml (Portuguese) 2019-10-10 03:09:23 +02:00
PhilippC
e0fd45f838 New translations strings.xml (German) 2019-10-10 03:09:23 +02:00
PhilippC
a7e8363ed7 New translations strings.xml (Swedish) 2019-10-10 03:09:23 +02:00
PhilippC
cb144c5bcf New translations strings.xml (Swedish) 2019-10-10 03:09:23 +02:00
PhilippC
57a9570d34 New translations strings.xml (Swedish) 2019-10-10 03:09:22 +02:00
PhilippC
28162762ef New translations strings.xml (Czech) 2019-10-10 03:09:22 +02:00
PhilippC
b9e745a7fb New translations strings.xml (Czech) 2019-10-10 03:09:22 +02:00
PhilippC
e08e59c226 New translations strings.xml (Polish) 2019-10-10 03:09:21 +02:00
PhilippC
3d6b02a016 New translations strings.xml (Catalan) 2019-10-10 03:09:21 +02:00
PhilippC
b18a7b1a23 New translations strings.xml (Catalan) 2019-10-10 03:09:21 +02:00
PhilippC
8595674eb0 New translations strings.xml (Catalan) 2019-10-10 03:09:21 +02:00
PhilippC
23fd50df21 New translations strings.xml (Dutch) 2019-10-10 03:09:20 +02:00
PhilippC
af80997ed4 New translations strings.xml (Ukrainian) 2019-10-10 03:09:20 +02:00
PhilippC
7e3ac2959e New translations strings.xml (German) 2019-10-10 03:09:20 +02:00
PhilippC
78124c505e New translations strings.xml (German) 2019-10-10 03:09:19 +02:00
PhilippC
589b3f9540 New translations strings.xml (Russian) 2019-10-10 03:09:19 +02:00
PhilippC
bd505d858a New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:19 +02:00
PhilippC
308909f4eb New translations strings.xml (Danish) 2019-10-10 03:09:19 +02:00
PhilippC
9cfcda2938 New translations strings.xml (Danish) 2019-10-10 03:09:18 +02:00
PhilippC
fa4887ffef New translations strings.xml (Italian) 2019-10-10 03:09:18 +02:00
PhilippC
f70f26149e New translations strings.xml (Italian) 2019-10-10 03:09:18 +02:00
PhilippC
3cc904dc18 New translations strings.xml (Danish) 2019-10-10 03:09:17 +02:00
PhilippC
16c6346797 New translations strings.xml (Japanese) 2019-10-10 03:09:17 +02:00
PhilippC
01b8f92d43 New translations strings.xml (Danish) 2019-10-10 03:09:17 +02:00
PhilippC
082f0a928f New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:17 +02:00
PhilippC
2ec4d6e006 New translations strings.xml (Dutch) 2019-10-10 03:09:16 +02:00
PhilippC
7bd46b2182 New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:16 +02:00
PhilippC
2e9ba3bcfc New translations strings.xml (Spanish) 2019-10-10 03:09:16 +02:00
PhilippC
551e374786 New translations strings.xml (Slovenian) 2019-10-10 03:09:15 +02:00
PhilippC
4366858b73 New translations strings.xml (Spanish) 2019-10-10 03:09:15 +02:00
PhilippC
29b7c24312 New translations strings.xml (Slovak) 2019-10-10 03:09:15 +02:00
PhilippC
3c5894db5d New translations strings.xml (Italian) 2019-10-10 03:09:15 +02:00
PhilippC
1d652272d7 New translations strings.xml (French) 2019-10-10 03:09:14 +02:00
PhilippC
d8c7f479ae New translations strings.xml (Danish) 2019-10-10 03:09:14 +02:00
PhilippC
3a0cb3fc72 New translations strings.xml (Slovak) 2019-10-10 03:09:14 +02:00
PhilippC
65f1ec8bf4 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:09:14 +02:00
PhilippC
0421e16910 New translations strings.xml (Portuguese) 2019-10-10 03:09:13 +02:00
PhilippC
2489d8e95f New translations strings.xml (Spanish) 2019-10-10 03:09:13 +02:00
PhilippC
ac46ac7372 New translations strings.xml (Japanese) 2019-10-10 03:09:13 +02:00
PhilippC
3ed3c90dae New translations strings.xml (Japanese) 2019-10-10 03:09:12 +02:00
PhilippC
a45530393e New translations strings.xml (Spanish) 2019-10-10 03:09:12 +02:00
PhilippC
a3e4b61216 New translations strings.xml (French) 2019-10-10 03:09:12 +02:00
PhilippC
c4939dd231 New translations strings.xml (Spanish) 2019-10-10 03:09:12 +02:00
PhilippC
c20303ebf1 New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:11 +02:00
PhilippC
d7627899ea New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:11 +02:00
PhilippC
87f82fb23f New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:11 +02:00
PhilippC
f866cdd4aa New translations strings.xml (Slovenian) 2019-10-10 03:09:10 +02:00
PhilippC
a9fc44a4a2 New translations strings.xml (Danish) 2019-10-10 03:09:10 +02:00
PhilippC
ee00e66e4f New translations strings.xml (German) 2019-10-10 03:09:10 +02:00
PhilippC
cfa97759da New translations strings.xml (Danish) 2019-10-10 03:09:10 +02:00
PhilippC
cf26b9fd58 New translations strings.xml (Danish) 2019-10-10 03:09:09 +02:00
PhilippC
6a5ddcc06d New translations strings.xml (Polish) 2019-10-10 03:09:09 +02:00
PhilippC
592667a845 New translations strings.xml (Italian) 2019-10-10 03:09:09 +02:00
PhilippC
8f6e3db71d New translations strings.xml (Czech) 2019-10-10 03:09:08 +02:00
PhilippC
70502414c3 New translations strings.xml (French) 2019-10-10 03:09:08 +02:00
PhilippC
c9189d37f5 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:09:08 +02:00
PhilippC
9e62e32e0a New translations strings.xml (Turkish) 2019-10-10 03:09:08 +02:00
PhilippC
f592126d63 New translations strings.xml (Italian) 2019-10-10 03:09:07 +02:00
PhilippC
c26b360bcc New translations strings.xml (Portuguese) 2019-10-10 03:09:07 +02:00
PhilippC
33c844bc97 New translations strings.xml (Dutch) 2019-10-10 03:09:07 +02:00
PhilippC
23a4a90afa New translations strings.xml (Hungarian) 2019-10-10 03:09:06 +02:00
PhilippC
643d8cc644 New translations strings.xml (Turkish) 2019-10-10 03:09:06 +02:00
PhilippC
10d9f73982 New translations strings.xml (Dutch) 2019-10-10 03:09:06 +02:00
PhilippC
a8f3e079a4 New translations strings.xml (Dutch) 2019-10-10 03:09:06 +02:00
PhilippC
8ad258d6b3 New translations strings.xml (Czech) 2019-10-10 03:09:05 +02:00
PhilippC
9a37d61892 New translations strings.xml (German) 2019-10-10 03:09:05 +02:00
PhilippC
30b5443ee1 New translations strings.xml (Vietnamese) 2019-10-10 03:09:05 +02:00
PhilippC
82885da6aa New translations strings.xml (Hungarian) 2019-10-10 03:09:04 +02:00
PhilippC
b498b8b437 New translations strings.xml (Greek) 2019-10-10 03:09:04 +02:00
PhilippC
7109fe405a New translations strings.xml (German) 2019-10-10 03:09:04 +02:00
PhilippC
0611cf2d89 New translations strings.xml (Galician) 2019-10-10 03:09:04 +02:00
PhilippC
2f5a1770c4 New translations strings.xml (French) 2019-10-10 03:09:03 +02:00
PhilippC
f7ee43fc89 New translations strings.xml (Finnish) 2019-10-10 03:09:03 +02:00
PhilippC
1457bdf69b New translations strings.xml (Indonesian) 2019-10-10 03:09:03 +02:00
PhilippC
db4a1a918e New translations strings.xml (Dutch) 2019-10-10 03:09:03 +02:00
PhilippC
66fafc25c8 New translations strings.xml (Czech) 2019-10-10 03:09:02 +02:00
PhilippC
b83cd39754 New translations strings.xml (Croatian) 2019-10-10 03:09:02 +02:00
PhilippC
3010643dc1 New translations strings.xml (Chinese Traditional) 2019-10-10 03:09:02 +02:00
PhilippC
823d6467b9 New translations strings.xml (Chinese Simplified) 2019-10-10 03:09:01 +02:00
PhilippC
9ebdf9c22c New translations strings.xml (Catalan) 2019-10-10 03:09:01 +02:00
PhilippC
0e4421ddc2 New translations strings.xml (Bulgarian) 2019-10-10 03:09:01 +02:00
PhilippC
3fbdf836cd New translations strings.xml (Basque) 2019-10-10 03:09:01 +02:00
PhilippC
124c07efa3 New translations strings.xml (Danish) 2019-10-10 03:09:00 +02:00
PhilippC
5d8cb69938 New translations strings.xml (Italian) 2019-10-10 03:09:00 +02:00
PhilippC
5e2f19b73e New translations strings.xml (Korean) 2019-10-10 03:09:00 +02:00
PhilippC
6e5f2dc910 New translations strings.xml (Ukrainian) 2019-10-10 03:08:59 +02:00
PhilippC
11119c8aa6 New translations strings.xml (Turkish) 2019-10-10 03:08:59 +02:00
PhilippC
b53ebafa9b New translations strings.xml (Swedish) 2019-10-10 03:08:59 +02:00
PhilippC
53bb74b837 New translations strings.xml (Spanish) 2019-10-10 03:08:59 +02:00
PhilippC
8b56ace8d1 New translations strings.xml (Slovenian) 2019-10-10 03:08:58 +02:00
PhilippC
524b8414d5 New translations strings.xml (Slovak) 2019-10-10 03:08:58 +02:00
PhilippC
88bfa05921 New translations strings.xml (Japanese) 2019-10-10 03:08:58 +02:00
PhilippC
4115463922 New translations strings.xml (Russian) 2019-10-10 03:08:57 +02:00
PhilippC
83ab0b2ee4 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:08:57 +02:00
PhilippC
5bacadbca0 New translations strings.xml (Portuguese) 2019-10-10 03:08:57 +02:00
PhilippC
005930c8a0 New translations strings.xml (Polish) 2019-10-10 03:08:56 +02:00
PhilippC
a16e6ff00b New translations strings.xml (Persian) 2019-10-10 03:08:56 +02:00
PhilippC
aef334ec61 New translations strings.xml (Norwegian Bokmal) 2019-10-10 03:08:56 +02:00
PhilippC
7917a59757 New translations strings.xml (Romanian) 2019-10-10 03:08:55 +02:00
PhilippC
954fc0f5fb New translations strings.xml (Arabic) 2019-10-10 03:08:55 +02:00
PhilippC
f9578493b4 New translations strings.xml (French) 2019-10-10 03:08:55 +02:00
PhilippC
f49a82745f New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:55 +02:00
PhilippC
84bd67bdea New translations strings.xml (French) 2019-10-10 03:08:54 +02:00
PhilippC
e32bb0a573 New translations strings.xml (Turkish) 2019-10-10 03:08:54 +02:00
PhilippC
8f5d4be864 New translations strings.xml (Italian) 2019-10-10 03:08:54 +02:00
PhilippC
e99c425120 New translations strings.xml (Spanish) 2019-10-10 03:08:53 +02:00
PhilippC
369379df2f New translations strings.xml (Japanese) 2019-10-10 03:08:53 +02:00
PhilippC
9a35575741 New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:53 +02:00
PhilippC
455ca956ab New translations strings.xml (Portuguese) 2019-10-10 03:08:53 +02:00
PhilippC
b87df11941 New translations strings.xml (Dutch) 2019-10-10 03:08:52 +02:00
PhilippC
beb9e2241b New translations strings.xml (Dutch) 2019-10-10 03:08:52 +02:00
PhilippC
71c78d04c6 New translations strings.xml (Dutch) 2019-10-10 03:08:52 +02:00
PhilippC
9a5b18d842 New translations strings.xml (Norwegian Bokmal) 2019-10-10 03:08:52 +02:00
PhilippC
702baf3614 New translations strings.xml (Dutch) 2019-10-10 03:08:51 +02:00
PhilippC
c8b7a8d51e New translations strings.xml (Dutch) 2019-10-10 03:08:51 +02:00
PhilippC
95f1d2ca14 New translations strings.xml (Dutch) 2019-10-10 03:08:51 +02:00
PhilippC
d4673abd01 New translations strings.xml (Polish) 2019-10-10 03:08:50 +02:00
PhilippC
225f0ab437 New translations strings.xml (Polish) 2019-10-10 03:08:50 +02:00
PhilippC
7a36a53a4b New translations strings.xml (Korean) 2019-10-10 03:08:50 +02:00
PhilippC
d9a3469e19 New translations strings.xml (Swedish) 2019-10-10 03:08:50 +02:00
PhilippC
46a5202edb New translations strings.xml (Swedish) 2019-10-10 03:08:49 +02:00
PhilippC
e28b55df9d New translations strings.xml (Swedish) 2019-10-10 03:08:49 +02:00
PhilippC
91a8d6ac6c New translations strings.xml (Swedish) 2019-10-10 03:08:49 +02:00
PhilippC
151d7bb5ca New translations strings.xml (Swedish) 2019-10-10 03:08:48 +02:00
PhilippC
7432f294b2 New translations strings.xml (Swedish) 2019-10-10 03:08:48 +02:00
PhilippC
6ac2bd3c1f New translations strings.xml (Vietnamese) 2019-10-10 03:08:48 +02:00
PhilippC
3ad238e7e8 New translations strings.xml (Basque) 2019-10-10 03:08:47 +02:00
PhilippC
59230fe57a New translations strings.xml (Bulgarian) 2019-10-10 03:08:47 +02:00
PhilippC
43e1e62b41 New translations strings.xml (Catalan) 2019-10-10 03:08:47 +02:00
PhilippC
afb4dd5ce9 New translations strings.xml (Chinese Simplified) 2019-10-10 03:08:47 +02:00
PhilippC
f2b861c369 New translations strings.xml (Croatian) 2019-10-10 03:08:46 +02:00
PhilippC
4080966331 New translations strings.xml (Czech) 2019-10-10 03:08:46 +02:00
PhilippC
54b4662322 New translations strings.xml (Danish) 2019-10-10 03:08:46 +02:00
PhilippC
26e8e2afc9 New translations strings.xml (Dutch) 2019-10-10 03:08:45 +02:00
PhilippC
9aa705f3e7 New translations strings.xml (Finnish) 2019-10-10 03:08:45 +02:00
PhilippC
cb49c90b4a New translations strings.xml (French) 2019-10-10 03:08:45 +02:00
PhilippC
84b2b83901 New translations strings.xml (Galician) 2019-10-10 03:08:45 +02:00
PhilippC
abebc9d3aa New translations strings.xml (German) 2019-10-10 03:08:44 +02:00
PhilippC
19a6044044 New translations strings.xml (Arabic) 2019-10-10 03:08:44 +02:00
PhilippC
6a396561a8 New translations strings.xml (Greek) 2019-10-10 03:08:43 +02:00
PhilippC
025b822046 New translations strings.xml (Hungarian) 2019-10-10 03:08:43 +02:00
PhilippC
1f95c684d8 New translations strings.xml (Indonesian) 2019-10-10 03:08:43 +02:00
PhilippC
7116370908 New translations strings.xml (Italian) 2019-10-10 03:08:43 +02:00
PhilippC
caca6237b8 New translations strings.xml (Japanese) 2019-10-10 03:08:42 +02:00
PhilippC
c5ddf1644d New translations strings.xml (Romanian) 2019-10-10 03:08:42 +02:00
PhilippC
7147874d79 New translations strings.xml (Russian) 2019-10-10 03:08:42 +02:00
PhilippC
91bcbcf47d New translations strings.xml (Serbian (Cyrillic)) 2019-10-10 03:08:41 +02:00
PhilippC
e9ecff62a3 New translations strings.xml (Slovak) 2019-10-10 03:08:41 +02:00
PhilippC
69e081a279 New translations strings.xml (Slovenian) 2019-10-10 03:08:41 +02:00
PhilippC
51ce9847e2 New translations strings.xml (Spanish) 2019-10-10 03:08:41 +02:00
PhilippC
988cc8bd3d New translations strings.xml (Swedish) 2019-10-10 03:08:40 +02:00
PhilippC
9b34ddfd9d New translations strings.xml (Turkish) 2019-10-10 03:08:40 +02:00
PhilippC
bd158984b7 New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:40 +02:00
PhilippC
ba76baf6ce New translations strings.xml (Polish) 2019-10-10 03:08:39 +02:00
PhilippC
19cf36c1e6 New translations strings.xml (Persian) 2019-10-10 03:08:39 +02:00
PhilippC
2bee860748 New translations strings.xml (Norwegian Bokmal) 2019-10-10 03:08:39 +02:00
PhilippC
edcbfeca1c New translations strings.xml (Korean) 2019-10-10 03:08:39 +02:00
PhilippC
db7a629e7d New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:08:38 +02:00
PhilippC
213ff373a9 New translations strings.xml (Ukrainian) 2019-10-10 03:08:38 +02:00
PhilippC
13fe371b73 New translations strings.xml (Vietnamese) 2019-10-10 03:08:38 +02:00
PhilippC
42e8c28320 New translations strings.xml (Basque) 2019-10-10 03:08:37 +02:00
PhilippC
cefeff50c9 New translations strings.xml (Bulgarian) 2019-10-10 03:08:37 +02:00
PhilippC
d3bb247b65 New translations strings.xml (Catalan) 2019-10-10 03:08:37 +02:00
PhilippC
6a7097a70f New translations strings.xml (Chinese Simplified) 2019-10-10 03:08:37 +02:00
PhilippC
c00ba4ad67 New translations strings.xml (Croatian) 2019-10-10 03:08:36 +02:00
PhilippC
63f3639196 New translations strings.xml (Czech) 2019-10-10 03:08:36 +02:00
PhilippC
5a02b65b66 New translations strings.xml (Danish) 2019-10-10 03:08:36 +02:00
PhilippC
daf0dfa761 New translations strings.xml (Dutch) 2019-10-10 03:08:35 +02:00
PhilippC
9aaf0ff08d New translations strings.xml (Finnish) 2019-10-10 03:08:35 +02:00
PhilippC
afac7406d7 New translations strings.xml (French) 2019-10-10 03:08:35 +02:00
PhilippC
6ae7c6e7ab New translations strings.xml (Galician) 2019-10-10 03:08:34 +02:00
PhilippC
16e8b01bcc New translations strings.xml (German) 2019-10-10 03:08:34 +02:00
PhilippC
ffd37f3658 New translations strings.xml (Arabic) 2019-10-10 03:08:34 +02:00
PhilippC
f6d18b18b9 New translations strings.xml (Greek) 2019-10-10 03:08:34 +02:00
PhilippC
2061ea8e18 New translations strings.xml (Hungarian)
# Conflicts:
#	src/keepass2android/Resources/values-hu/strings.xml
2019-10-10 03:08:33 +02:00
PhilippC
f9ff03d85d New translations strings.xml (Indonesian) 2019-10-10 03:08:33 +02:00
PhilippC
4b8a8e83d2 New translations strings.xml (Italian) 2019-10-10 03:08:33 +02:00
PhilippC
47466e7322 New translations strings.xml (Japanese) 2019-10-10 03:08:32 +02:00
PhilippC
92be6e1b56 New translations strings.xml (Romanian) 2019-10-10 03:08:32 +02:00
PhilippC
f763840c14 New translations strings.xml (Russian)
# Conflicts:
#	src/keepass2android/Resources/values-ru/strings.xml
2019-10-10 03:08:32 +02:00
PhilippC
09fed92c4f New translations strings.xml (Serbian (Cyrillic)) 2019-10-10 03:08:32 +02:00
PhilippC
0cb4e1db64 New translations strings.xml (Slovak) 2019-10-10 03:08:31 +02:00
PhilippC
66c7afc8db New translations strings.xml (Slovenian) 2019-10-10 03:08:31 +02:00
PhilippC
7482006851 New translations strings.xml (Spanish) 2019-10-10 03:08:31 +02:00
PhilippC
45f17d81e7 New translations strings.xml (Swedish) 2019-10-10 03:08:30 +02:00
PhilippC
dc2e557d2c New translations strings.xml (Turkish)
# Conflicts:
#	src/keepass2android/Resources/values-tr/strings.xml
2019-10-10 03:08:30 +02:00
PhilippC
6d4630e2d9 New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:30 +02:00
PhilippC
fc96bc6b4e New translations strings.xml (Polish) 2019-10-10 03:08:30 +02:00
PhilippC
c13986a88f New translations strings.xml (Persian) 2019-10-10 03:08:29 +02:00
PhilippC
6eb7f15045 New translations strings.xml (Norwegian Bokmal) 2019-10-10 03:08:29 +02:00
PhilippC
61346438eb New translations strings.xml (Korean) 2019-10-10 03:08:29 +02:00
PhilippC
47e520c4b0 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:08:28 +02:00
PhilippC
bcdae3d3e7 New translations strings.xml (Ukrainian) 2019-10-10 03:08:28 +02:00
PhilippC
a17aafeabd New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:28 +02:00
PhilippC
286c27f7f8 New translations strings.xml (Ukrainian) 2019-10-10 03:08:28 +02:00
PhilippC
a44d08c352 New translations strings.xml (Polish) 2019-10-10 03:08:27 +02:00
PhilippC
aeb396f000 New translations strings.xml (Polish) 2019-10-10 03:08:27 +02:00
PhilippC
6c8dcbadb4 New translations strings.xml (Polish) 2019-10-10 03:08:27 +02:00
PhilippC
0071502c83 New translations strings.xml (Czech) 2019-10-10 03:08:26 +02:00
PhilippC
eb47e19df7 New translations strings.xml (Czech) 2019-10-10 03:08:26 +02:00
PhilippC
8ff2d10697 New translations strings.xml (Czech) 2019-10-10 03:08:26 +02:00
PhilippC
e1bfb4f7c2 New translations strings.xml (Czech) 2019-10-10 03:08:26 +02:00
PhilippC
8ae699b751 New translations strings.xml (Catalan) 2019-10-10 03:08:25 +02:00
PhilippC
6381d323da New translations strings.xml (Catalan) 2019-10-10 03:08:25 +02:00
PhilippC
8e9ec81591 New translations strings.xml (Turkish) 2019-10-10 03:08:25 +02:00
PhilippC
ad847e0114 New translations strings.xml (Chinese Simplified) 2019-10-10 03:08:24 +02:00
PhilippC
d44dd3e738 New translations strings.xml (Chinese Simplified) 2019-10-10 03:08:24 +02:00
PhilippC
6b6a3db89a New translations strings.xml (Chinese Simplified) 2019-10-10 03:08:24 +02:00
PhilippC
cecdb4e76d New translations strings.xml (Swedish) 2019-10-10 03:08:24 +02:00
PhilippC
c984497454 New translations strings.xml (Swedish) 2019-10-10 03:08:23 +02:00
PhilippC
5a617ad824 New translations strings.xml (Danish) 2019-10-10 03:08:23 +02:00
PhilippC
7f83846937 New translations strings.xml (Chinese Traditional) 2019-10-10 03:08:23 +02:00
PhilippC
19de6e7d52 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 03:08:22 +02:00
Philipp Crocoll
12d2f961cf re-acquire token in OneDrive2FileStorage after expiry, closes https://github.com/PhilippC/keepass2android/issues/983 2019-10-10 03:08:22 +02:00
PhilippC
8fe549fc55 Merge pull request #768 from PhilippC/l10n_master
New Crowdin translations
2019-10-10 02:44:27 +02:00
PhilippC
a99e55e884 New translations strings.xml (Portuguese, Brazilian) 2019-10-10 02:41:12 +02:00
PhilippC
e361f770e8 New translations strings.xml (Portuguese) 2019-10-10 02:41:09 +02:00
PhilippC
a27e13bcb3 New translations strings.xml (French) 2019-10-10 02:34:34 +02:00
PhilippC
03cf25766b New translations strings.xml (Swedish) 2019-10-10 02:33:44 +02:00
PhilippC
9ce42a8e98 New translations strings.xml (Spanish) 2019-10-10 02:33:39 +02:00
PhilippC
c03a5b08fd New translations strings.xml (Portuguese, Brazilian) 2019-10-10 02:33:16 +02:00
PhilippC
9b5853d3f9 New translations strings.xml (Portuguese) 2019-10-10 02:33:11 +02:00
PhilippC
8a3735dc01 New translations strings.xml (German) 2019-10-09 22:30:13 +02:00
PhilippC
aae662ab5e New translations strings.xml (Swedish) 2019-10-09 10:10:14 +02:00
PhilippC
d051f02a43 New translations strings.xml (Swedish) 2019-10-09 10:00:15 +02:00
PhilippC
2e4cf0dfc6 New translations strings.xml (Swedish) 2019-10-09 09:50:09 +02:00
Philipp Crocoll
fc76e2042d remove "pre" from changelog 2019-10-09 02:36:04 +02:00
PhilippC
d7f462d21f New translations strings.xml (Czech) 2019-10-08 21:00:11 +02:00
PhilippC
2b61598c21 New translations strings.xml (Czech) 2019-10-08 20:50:12 +02:00
Philipp Crocoll
799dc3a38f switch to FluentFTP to close #825 2019-10-08 07:11:34 +02:00
PhilippC
9c76dce17d New translations strings.xml (Polish) 2019-10-07 21:50:13 +02:00
PhilippC
28ac79bbe5 New translations strings.xml (Catalan) 2019-10-07 21:50:08 +02:00
PhilippC
bdf825a2b5 New translations strings.xml (Catalan) 2019-10-07 21:40:09 +02:00
PhilippC
37d03a2735 New translations strings.xml (Catalan) 2019-10-07 21:20:08 +02:00
PhilippC
e8fde4ad0a New translations strings.xml (Dutch) 2019-10-07 20:50:08 +02:00
PhilippC
1aa29a82fb New translations strings.xml (Ukrainian) 2019-10-07 18:10:09 +02:00
PhilippC
60f292a5a7 New translations strings.xml (German) 2019-10-07 17:30:10 +02:00
PhilippC
1f956ab1db New translations strings.xml (German) 2019-10-07 17:20:12 +02:00
PhilippC
df39fa5545 New translations strings.xml (Russian) 2019-10-07 17:00:12 +02:00
PhilippC
dd0800f543 New translations strings.xml (Chinese Traditional) 2019-10-07 16:50:14 +02:00
PhilippC
8b3c6eb1ff New translations strings.xml (Danish) 2019-10-07 16:30:13 +02:00
PhilippC
88b35c0051 New translations strings.xml (Danish) 2019-10-07 15:00:10 +02:00
PhilippC
2f83169ddd New translations strings.xml (Italian) 2019-10-07 13:50:08 +02:00
PhilippC
ce4b301f5c New translations strings.xml (Italian) 2019-10-07 13:40:13 +02:00
PhilippC
433f46b8f1 New translations strings.xml (Danish) 2019-10-07 13:40:10 +02:00
PhilippC
03eb52dc14 New translations strings.xml (Japanese) 2019-10-07 13:30:19 +02:00
PhilippC
9d785679c5 New translations strings.xml (Danish) 2019-10-07 13:30:14 +02:00
PhilippC
699a4f96b0 New translations strings.xml (Chinese Traditional) 2019-10-07 13:30:09 +02:00
PhilippC
afd6f0f4d6 New translations strings.xml (Dutch) 2019-10-07 12:40:15 +02:00
PhilippC
eaadc45fce New translations strings.xml (Chinese Traditional) 2019-10-07 12:40:13 +02:00
PhilippC
e9852a6d98 New translations strings.xml (Spanish) 2019-10-07 12:30:17 +02:00
PhilippC
e1b3e769d8 New translations strings.xml (Slovenian) 2019-10-07 12:30:12 +02:00
PhilippC
501535a9a4 New translations strings.xml (Spanish) 2019-10-07 12:20:10 +02:00
PhilippC
fb8608e467 New translations strings.xml (Slovak) 2019-10-07 12:10:29 +02:00
PhilippC
e9eb8c250a New translations strings.xml (Italian) 2019-10-07 12:10:22 +02:00
PhilippC
eaa5a9d375 New translations strings.xml (French) 2019-10-07 12:10:15 +02:00
PhilippC
d28834591a New translations strings.xml (Danish) 2019-10-07 12:02:12 +02:00
PhilippC
49aa0a20cd New translations strings.xml (Slovak) 2019-10-07 12:01:17 +02:00
PhilippC
8d611bd6c6 New translations strings.xml (Portuguese, Brazilian) 2019-10-07 12:01:01 +02:00
PhilippC
bfe8650ec9 New translations strings.xml (Portuguese) 2019-10-07 12:00:56 +02:00
PhilippC
fd52a5f388 New translations strings.xml (Spanish) 2019-10-07 11:40:13 +02:00
PhilippC
6b8c5ea1fb New translations strings.xml (Japanese) 2019-10-07 11:40:09 +02:00
PhilippC
8347feceff New translations strings.xml (Japanese) 2019-10-07 11:30:10 +02:00
PhilippC
626b7fc6b3 New translations strings.xml (Spanish) 2019-10-07 11:20:08 +02:00
Philipp Crocoll
1701c9142b allow to activate search field also after QuickUnlock, not only full unlock. Closes #858 2019-10-07 11:19:34 +02:00
Philipp Crocoll
245794ed53 add Copy TOTP notification, closes #110 2019-10-07 11:18:48 +02:00
PhilippC
24aba30fa8 New translations strings.xml (French) 2019-10-07 10:30:10 +02:00
Philipp Crocoll
6338472e73 order group list with culture-aware sorting, closes #922 2019-10-07 10:30:08 +02:00
Philipp Crocoll
580b46a394 detect if FLAG_SECURE cannot be set and display an explanation, combined with a button to access the app settings to disable screen protection if desired. closes #272. 2019-10-07 10:29:54 +02:00
PhilippC
ea1a34353c New translations strings.xml (Spanish) 2019-10-07 09:50:08 +02:00
PhilippC
f9790e0e3e New translations strings.xml (Chinese Traditional) 2019-10-07 09:30:14 +02:00
PhilippC
540c4a72a2 New translations strings.xml (Chinese Traditional) 2019-10-07 09:20:10 +02:00
PhilippC
bc905304e7 New translations strings.xml (Chinese Traditional) 2019-10-07 09:10:08 +02:00
PhilippC
ad04309f9d New translations strings.xml (Slovenian) 2019-10-07 07:20:07 +02:00
Philipp Crocoll
684ffb8525 show toast in correct thread, closes #371 2019-10-07 07:13:59 +02:00
Philipp Crocoll
049764fe67 don't save files in a file version lower than what was read, closes #791 2019-10-07 02:30:38 +02:00
Philipp Crocoll
a048c45b32 improve clearing of clipboard, closes #936 2019-10-07 02:29:00 +02:00
PhilippC
60c78642b1 New translations strings.xml (Danish) 2019-10-07 00:10:09 +02:00
PhilippC
5c7f551922 New translations strings.xml (German) 2019-10-07 00:00:15 +02:00
PhilippC
ceac7841a0 New translations strings.xml (Danish) 2019-10-07 00:00:11 +02:00
PhilippC
a29fc9d3cc New translations strings.xml (Danish) 2019-10-06 23:50:10 +02:00
PhilippC
f5dbc85450 New translations strings.xml (Polish) 2019-10-06 22:50:10 +02:00
PhilippC
6a86a6906c New translations strings.xml (Italian) 2019-10-06 22:40:12 +02:00
PhilippC
2b4bd1a79d New translations strings.xml (Czech) 2019-10-06 22:40:08 +02:00
PhilippC
2b2ce4eb29 New translations strings.xml (French) 2019-10-06 22:20:10 +02:00
PhilippC
6ac875aeb5 New translations strings.xml (Portuguese, Brazilian) 2019-10-06 22:10:24 +02:00
PhilippC
f43c6d3899 New translations strings.xml (Turkish) 2019-10-06 22:00:26 +02:00
PhilippC
3f240868d0 New translations strings.xml (Italian) 2019-10-06 22:00:22 +02:00
PhilippC
5ccea973ee New translations strings.xml (Portuguese) 2019-10-06 21:51:33 +02:00
PhilippC
c1d59d9203 New translations strings.xml (Dutch) 2019-10-06 21:50:33 +02:00
Philipp Crocoll
c6d3eb7131 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-10-06 21:48:35 +02:00
Philipp Crocoll
f462bbe98b remove "pre" from changelog 2019-10-06 21:48:10 +02:00
PhilippC
c6be5fcf84 New translations strings.xml (Hungarian) 2019-10-06 21:40:10 +02:00
PhilippC
023d9e194f New translations strings.xml (Turkish) 2019-10-06 21:30:10 +02:00
PhilippC
78ad964481 New translations strings.xml (Dutch) 2019-10-06 21:20:08 +02:00
PhilippC
9c9a8277a9 New translations strings.xml (Dutch) 2019-10-06 21:10:13 +02:00
PhilippC
3f414ded5b New translations strings.xml (Czech) 2019-10-06 21:10:11 +02:00
PhilippC
5906824ffc New translations strings.xml (German) 2019-10-06 21:00:09 +02:00
PhilippC
737c87bdf8 New translations strings.xml (Vietnamese) 2019-10-06 20:53:11 +02:00
PhilippC
bb27734a07 New translations strings.xml (Hungarian) 2019-10-06 20:53:07 +02:00
PhilippC
a9e6880add New translations strings.xml (Greek) 2019-10-06 20:52:57 +02:00
PhilippC
a1c3bf19ae New translations strings.xml (German) 2019-10-06 20:52:53 +02:00
PhilippC
0dc86e8662 New translations strings.xml (Galician) 2019-10-06 20:52:50 +02:00
PhilippC
bddc731390 New translations strings.xml (French) 2019-10-06 20:52:46 +02:00
PhilippC
2521aa2621 New translations strings.xml (Finnish) 2019-10-06 20:52:42 +02:00
PhilippC
23af6f3031 New translations strings.xml (Indonesian) 2019-10-06 20:52:39 +02:00
PhilippC
89ac5af115 New translations strings.xml (Dutch) 2019-10-06 20:52:36 +02:00
PhilippC
1f1ac04640 New translations strings.xml (Czech) 2019-10-06 20:52:32 +02:00
PhilippC
67d2eeaaff New translations strings.xml (Croatian) 2019-10-06 20:52:28 +02:00
PhilippC
0788af941e New translations strings.xml (Chinese Traditional) 2019-10-06 20:52:24 +02:00
PhilippC
484cea229c New translations strings.xml (Chinese Simplified) 2019-10-06 20:52:22 +02:00
PhilippC
827a680f81 New translations strings.xml (Catalan) 2019-10-06 20:52:20 +02:00
PhilippC
8b5c170409 New translations strings.xml (Bulgarian) 2019-10-06 20:52:16 +02:00
PhilippC
9ec76c2738 New translations strings.xml (Basque) 2019-10-06 20:52:11 +02:00
PhilippC
ea9539ad0a New translations strings.xml (Danish) 2019-10-06 20:52:06 +02:00
PhilippC
03f4040c09 New translations strings.xml (Italian) 2019-10-06 20:51:58 +02:00
PhilippC
869f5d35bd New translations strings.xml (Korean) 2019-10-06 20:51:54 +02:00
PhilippC
01ac1c601f New translations strings.xml (Ukrainian) 2019-10-06 20:51:50 +02:00
PhilippC
22f3f5ce59 New translations strings.xml (Turkish) 2019-10-06 20:51:47 +02:00
PhilippC
0b430c90f4 New translations strings.xml (Swedish) 2019-10-06 20:51:41 +02:00
PhilippC
080483b2d0 New translations strings.xml (Spanish) 2019-10-06 20:51:35 +02:00
PhilippC
2da645f25b New translations strings.xml (Slovenian) 2019-10-06 20:51:32 +02:00
PhilippC
3ff6c35e35 New translations strings.xml (Slovak) 2019-10-06 20:51:30 +02:00
PhilippC
5f3fb52833 New translations strings.xml (Japanese) 2019-10-06 20:51:23 +02:00
PhilippC
6117053dd7 New translations strings.xml (Russian) 2019-10-06 20:51:18 +02:00
PhilippC
c3a1faffca New translations strings.xml (Portuguese, Brazilian) 2019-10-06 20:51:12 +02:00
PhilippC
5d85fdda46 New translations strings.xml (Portuguese) 2019-10-06 20:51:10 +02:00
PhilippC
907705b657 New translations strings.xml (Polish) 2019-10-06 20:51:04 +02:00
PhilippC
22e460d7bf New translations strings.xml (Persian) 2019-10-06 20:51:00 +02:00
PhilippC
3a81ebfcd9 New translations strings.xml (Norwegian Bokmal) 2019-10-06 20:50:52 +02:00
PhilippC
ec92225e50 New translations strings.xml (Romanian) 2019-10-06 20:50:49 +02:00
PhilippC
5163d472b3 New translations strings.xml (Arabic) 2019-10-06 20:50:46 +02:00
Philipp Crocoll
069a2d287c catch bad paths passed to search activity. closes #801. 2019-10-06 20:36:37 +02:00
Philipp Crocoll
01b8469fbb fix crash when copying entries, closes #920 2019-10-06 20:25:25 +02:00
PhilippC
421a590514 Update Documentation.md 2019-10-06 07:53:37 +02:00
Philipp Crocoll
a0a494e724 fix bug in BinaryDictionary.java which lead to crashes with dictionary using on 64bit devices; enable multi-dex and increase heap size also for Release build 2019-10-04 20:10:58 +02:00
Philipp Crocoll
3f6f0b7430 Merge branch 'master' of c:/ph/keepass2android 2019-10-03 17:19:35 +02:00
Philipp Crocoll
b8d161ffee add sha sources to repo 2019-10-03 17:18:11 +02:00
Philipp Crocoll
e27ff98517 remove Java implementation of OneDriveStorage2 2019-10-03 17:02:34 +02:00
Philipp Crocoll
4c2c6bbb54 Merge branch 'master' of c:/ph/keepass2android
# Resolved Conflicts:
#	src/keepass2android/proguard.cfg
2019-10-03 16:58:33 +02:00
Philipp Crocoll
5de92719f8 keep keyboard native methods in proguard 2019-10-03 16:57:07 +02:00
Philipp Crocoll
34b9d71cbf prepare for preview release 2019-10-03 16:54:35 +02:00
Philipp Crocoll
57e9253270 improvements to OneDrive2 implementation: it's now possible select between different access scopes, also fixed several issues 2019-10-03 16:42:50 +02:00
Philipp Crocoll
99d0ecfd59 refactoring, use less UrlUtil and more IFileStorage 2019-10-01 20:04:03 +02:00
Philipp Crocoll
706debfa33 add missing file OneDrive2FileStorage (with improvements) 2019-09-30 21:18:40 +02:00
Philipp Crocoll
db26fb6f39 first version of OneDrive implementation in C# 2019-09-30 10:42:27 +02:00
Philipp Crocoll
57e36fb728 subrepo update 2019-09-25 19:28:13 +02:00
Philipp Crocoll
7c49eeca97 update SamsungPass 2019-09-25 19:25:00 +02:00
Philipp Crocoll
8d065255d6 update dropbox sdk and gson jars, upgrade to TargetFramework Android 9 2019-09-25 19:17:08 +02:00
Philipp Crocoll
a630543ecc Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-09-23 11:52:38 +02:00
Philipp Crocoll
989e397ee2 Integrate 64-bit native libraries (required by Google Play)
Therefore change the way Keyboard and KdbLibrary are built and add (updated) native files for Keyboard.
2019-09-23 11:52:24 +02:00
Philipp Crocoll
f99843737f implement java side of OneDriveStorage2 2019-09-10 05:26:38 +02:00
PhilippC
5197111a3a Merge pull request #949 from ihanson/patch-1
Fix “CHANGE DATABAS” button bug
2019-09-10 05:04:28 +02:00
Ira Hanson
d12339bb50 Correctly strip ellipsis
Fix bug where button caption is “CHANGE DATABAS”
2019-09-09 19:45:52 -05:00
PhilippC
e166e9f754 Update Documentation.md 2019-09-03 11:43:28 +02:00
PhilippC
1d5bca81da Update Documentation.md 2019-09-03 11:36:33 +02:00
Philipp Crocoll
56891d6183 add contributor names 2019-08-21 05:02:37 +02:00
Philipp Crocoll
d0f1882995 manifest for release 1.07b-pre1 2019-08-20 06:09:54 +02:00
Philipp Crocoll
b1b67807ac added ChangeLog for 1.07b, updated contributors 2019-08-20 06:05:50 +02:00
Philipp Crocoll
265c67c91c change SamsungPass back to https://github.com/PhilippC/Xamarin-Samsung-Pass.git, merged changes for Linux from gilbsgilbs 2019-08-20 06:05:21 +02:00
Philipp Crocoll
7c55c50b4c explicity use 8.0 as target version to fix compile error. (Note: I could also upgrade everything to 8.1 to fix the compile error, but that would require extensive testing; updating target version can come with subtle changes in how the app behaves at many places) 2019-08-20 05:46:53 +02:00
Philipp Crocoll
a42b74b614 add Fennec_fdroid and Bromite to list of browsers for Autofill 2019-08-20 05:10:53 +02:00
Philipp Crocoll
262b931a1e Merge branch 'master' of c:/ph/keepass2android 2019-08-19 14:47:53 +02:00
Philipp Crocoll
3f1db5aa56 changes for 1.07-r1 (not commited correctly) 2019-08-19 14:47:46 +02:00
Philipp Crocoll
95ce6878e6 avoid exception for invalid input of getDisplayName, fixes #771 2019-08-19 14:36:33 +02:00
Philipp Crocoll
9f6fea7038 improve error message to diagnose #801 2019-08-19 14:33:44 +02:00
Philipp Crocoll
28eaa2ee7f Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-08-19 14:32:43 +02:00
Philipp Crocoll
c35cf56d8a fix entry notification being closed immediately after opening when there are TOTP fields in the entry (closes #799) 2019-08-19 14:32:29 +02:00
PhilippC
0526c58913 Update Documentation.md 2019-08-19 12:53:38 +02:00
Philipp Crocoll
1f20c18ad6 fix creation of IfDevice string, closes #888 2019-08-19 12:10:27 +02:00
Philipp Crocoll
4b56eca5e2 add mark.via.gp browser, closes #885 2019-08-19 11:47:42 +02:00
Philipp Crocoll
d14702494e Merge branch 'marcoDallas-fingerprint-improvement-#831' 2019-08-17 23:06:07 +02:00
Philipp Crocoll
f2bda0f2d1 Merge branch 'fingerprint-improvement-#831' of https://github.com/marcoDallas/keepass2android into marcoDallas-fingerprint-improvement-#831 2019-08-17 19:56:01 +02:00
Marco Dalla Libera
5f90628d2d Improved code by using a single call to setPositiveButton.
The positive button is always the same so there's no need to use two different
calls, I just brought one outside the if statement. It was an artifact left
from a previous test where I was changing both of the buttons.
2019-08-15 14:45:24 +02:00
Marco Dalla Libera
409c3eec55 Added underscreen fingerprint improvement to QuickUnlockActivity. 2019-08-15 10:44:03 +02:00
Philipp Crocoll
2a7ea24d17 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-08-15 06:53:09 +02:00
PhilippC
f81a398f4b Merge pull request #886 from DDoSolitary/patch-password-cursor
Restore cursor position after switching password visibility.
2019-08-15 06:52:56 +02:00
Philipp Crocoll
7a94f83019 fix compile errors 2019-08-15 06:51:27 +02:00
Philipp Crocoll
134234da12 Merge branch 'patch-password-cursor' of https://github.com/DDoSolitary/keepass2android 2019-08-15 05:48:53 +02:00
Philipp Crocoll
7354c8933c Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-08-15 05:46:23 +02:00
Philipp Crocoll
0565a83085 update Dropbox SDK version to fix https://github.com/PhilippC/keepass2android/issues/793 2019-08-15 05:45:40 +02:00
PhilippC
eab79eaac2 Merge pull request #905 from marcoDallas/improvement/linux-build-scripts
Improved Android SDK detection in Linux build scripts
2019-08-15 05:42:55 +02:00
PhilippC
1007f7ee6b Merge pull request #906 from marcoDallas/improvement/linux-build-instructions
Added a different path to libzip5 in Linux build instructions
2019-08-15 05:41:40 +02:00
Marco Dalla Libera
ce008843e2 Added a different path to libzip5
In my system (OpenSUSE Tumbleweed 20190806 64bit) its path was
"/usr/lib64/libzip.so.5".
2019-08-12 16:53:54 +02:00
Marco Dalla Libera
ea5ae50813 Improved Android SDK detection
If ANDROID_HOME environment variable is set, add parameter
"/p:AndroidSdkDirectory=$ANDROID_HOME" to improve the Android SDK detection.
2019-08-12 16:37:11 +02:00
Marco Dalla Libera
0970f0659a Fixed wrong indentation in files. 2019-08-12 15:31:45 +02:00
Marco Dalla Libera
ef2123422c IMPROVED UNDERSCREEN FINGERPRINT, ISSUE #831
Improved underscreen fingerprint as request in issue #831 by giving the users
the ability to disable the sensor in the PasswordActivity. Now pressing the
fingerprint icon opens up a dialog that has the option to enable/disable the
sensor.
I added two new strings to the resources (enable sensor - disable sensor) and
already translated them to italian.
2019-08-12 15:09:13 +02:00
PhilippC
6d82642176 Merge pull request #896 from jonalmeida/moz-browser-whitelist
Add support for Fenix Nightly and Reference Browser
2019-08-09 03:46:35 +02:00
Jonathan Almeida
f9c22574a7 Add support for Fenix Nightly and Reference Browser 2019-08-08 19:56:06 -04:00
PhilippC
3fde6af2a2 Merge pull request #892 from PhilippC/Branch_b0f6103
1.07-r1
2019-08-05 07:25:51 +02:00
Philipp Crocoll
5322468819 Merge branch 'Branch_b0f6103' 2019-08-05 07:21:53 +02:00
Philipp Crocoll
e50cb41d1f Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-08-05 06:36:56 +02:00
Philipp Crocoll
428c360abf avoid potential NullReferenceException 2019-08-05 06:36:40 +02:00
DDoSolitary
b89f3906e9 Restore cursor position after switching password visibility. 2019-07-27 13:22:42 +08:00
PhilippC
c3898245ce New translations strings.xml (French) 2019-07-22 17:30:13 +02:00
PhilippC
f15748b4e9 New translations strings.xml (Chinese Traditional) 2019-07-22 14:00:18 +02:00
PhilippC
d11c871526 New translations strings.xml (French) 2019-07-22 13:00:11 +02:00
PhilippC
6944d842c3 New translations strings.xml (Turkish) 2019-07-22 11:40:15 +02:00
PhilippC
183087b441 New translations strings.xml (Italian) 2019-07-22 11:30:13 +02:00
PhilippC
5e081805f1 New translations strings.xml (Spanish) 2019-07-22 11:10:22 +02:00
PhilippC
e1addd1485 New translations strings.xml (Japanese) 2019-07-22 11:10:14 +02:00
PhilippC
801f13c2d3 New translations strings.xml (Chinese Traditional) 2019-07-22 10:50:11 +02:00
PhilippC
6dcb22dfc2 New translations strings.xml (Portuguese) 2019-07-22 09:50:30 +02:00
PhilippC
e5dbd86eb3 Merge pull request #835 from PhilippC/pcloud-help-dialog
PCloud improvements
2019-07-22 09:46:54 +02:00
PhilippC
1706891be4 Merge pull request #836 from PhilippC/fix-linux-build
Fix invalid namespace in manifests causing build failure on Linux.
2019-07-22 09:45:07 +02:00
PhilippC
11323c0206 Merge pull request #855 from caesar/patch-1
Add support for autofill in Firefox Preview/Fenix
2019-07-22 09:44:15 +02:00
PhilippC
c36d252159 Merge pull request #872 from yan12125/argon2-performance
Improve Argon2 performance
2019-07-22 09:39:50 +02:00
Chih-Hsuan Yen
7d1eeb1e34 Use native Argon2 implementation 2019-07-16 21:07:26 +08:00
Chih-Hsuan Yen
e081a6aa2c Build fixes 2019-07-16 21:07:26 +08:00
PhilippC
014f238f6c New translations strings.xml (Dutch) 2019-07-03 10:30:13 +02:00
PhilippC
9482b37672 New translations strings.xml (Dutch) 2019-07-03 10:20:13 +02:00
PhilippC
69c5601de4 New translations strings.xml (Dutch) 2019-07-03 10:10:13 +02:00
Caesar Schinas
96c717f748 Add support for autofill in Firefox Preview/Fenix
Also tidy / reorganize `_trustedBrowsers` list somewhat.

Fixes #758.
2019-07-01 11:39:50 +01:00
PhilippC
856ba36a20 New translations strings.xml (Norwegian Bokmal) 2019-06-27 23:10:10 +02:00
PhilippC
396cf51794 New translations strings.xml (Dutch) 2019-06-26 13:30:10 +02:00
PhilippC
8138790396 New translations strings.xml (Dutch) 2019-06-26 13:20:10 +02:00
PhilippC
e698468c39 New translations strings.xml (Dutch) 2019-06-26 13:10:12 +02:00
PhilippC
da005a35bf New translations strings.xml (Polish) 2019-06-21 02:40:11 +02:00
PhilippC
9b62f28e93 New translations strings.xml (Polish) 2019-06-21 02:30:13 +02:00
PhilippC
5adc738c03 New translations strings.xml (Korean) 2019-06-18 10:10:14 +02:00
PhilippC
d784394ea9 New translations strings.xml (Swedish) 2019-06-02 18:00:11 +02:00
PhilippC
b1cbb7893c New translations strings.xml (Swedish) 2019-06-02 17:50:09 +02:00
PhilippC
51b2049429 New translations strings.xml (Swedish) 2019-06-02 17:40:09 +02:00
PhilippC
84c8af7357 New translations strings.xml (Swedish) 2019-06-02 17:30:10 +02:00
PhilippC
69cbda8a39 New translations strings.xml (Swedish) 2019-06-02 17:20:09 +02:00
PhilippC
48f351ba0d New translations strings.xml (Swedish) 2019-06-02 17:10:11 +02:00
PhilippC
0a7fac77ff New translations strings.xml (Vietnamese) 2019-05-26 13:32:22 +02:00
PhilippC
ff336b40ae New translations strings.xml (Basque) 2019-05-26 13:32:16 +02:00
PhilippC
86a8da3ef3 New translations strings.xml (Bulgarian) 2019-05-26 13:32:12 +02:00
PhilippC
a72b681205 New translations strings.xml (Catalan) 2019-05-26 13:32:07 +02:00
PhilippC
3c75a4a012 New translations strings.xml (Chinese Simplified) 2019-05-26 13:32:01 +02:00
PhilippC
f9fc4b9a06 New translations strings.xml (Croatian) 2019-05-26 13:31:56 +02:00
PhilippC
6869f940ac New translations strings.xml (Czech) 2019-05-26 13:31:52 +02:00
PhilippC
79f0f90f3e New translations strings.xml (Danish) 2019-05-26 13:31:47 +02:00
PhilippC
1119610a10 New translations strings.xml (Dutch) 2019-05-26 13:31:42 +02:00
PhilippC
90fe3f04c0 New translations strings.xml (Finnish) 2019-05-26 13:31:37 +02:00
PhilippC
aca0aef285 New translations strings.xml (French) 2019-05-26 13:31:32 +02:00
PhilippC
8134b73902 New translations strings.xml (Galician) 2019-05-26 13:31:28 +02:00
PhilippC
57414cd0b8 New translations strings.xml (German) 2019-05-26 13:31:23 +02:00
PhilippC
6792eb53f0 New translations strings.xml (Arabic) 2019-05-26 13:31:19 +02:00
PhilippC
a6d4814980 New translations strings.xml (Greek) 2019-05-26 13:31:15 +02:00
PhilippC
3ef0afa6bc New translations strings.xml (Hungarian) 2019-05-26 13:31:10 +02:00
PhilippC
942be17e61 New translations strings.xml (Indonesian) 2019-05-26 13:31:06 +02:00
PhilippC
37e8f14e3b New translations strings.xml (Italian) 2019-05-26 13:31:00 +02:00
PhilippC
64ca771c51 New translations strings.xml (Japanese) 2019-05-26 13:30:54 +02:00
PhilippC
df2c2cd827 New translations strings.xml (Romanian) 2019-05-26 13:30:50 +02:00
PhilippC
4b692e4697 New translations strings.xml (Russian) 2019-05-26 13:30:45 +02:00
PhilippC
a0fa5ff4c2 New translations strings.xml (Serbian (Cyrillic)) 2019-05-26 13:30:42 +02:00
PhilippC
32df0ae8fe New translations strings.xml (Slovak) 2019-05-26 13:30:36 +02:00
PhilippC
73b9bcac7a New translations strings.xml (Slovenian) 2019-05-26 13:30:31 +02:00
PhilippC
11f606631c New translations strings.xml (Spanish) 2019-05-26 13:30:28 +02:00
PhilippC
9f696441e3 New translations strings.xml (Swedish) 2019-05-26 13:30:23 +02:00
PhilippC
118d18613f New translations strings.xml (Turkish) 2019-05-26 13:30:19 +02:00
PhilippC
a39bc9b7f7 New translations strings.xml (Chinese Traditional) 2019-05-26 13:30:12 +02:00
PhilippC
e0e17359c9 New translations strings.xml (Polish) 2019-05-25 21:50:41 +02:00
PhilippC
5ab25b0bc8 New translations strings.xml (Persian) 2019-05-25 21:50:37 +02:00
PhilippC
686e7eb6c1 New translations strings.xml (Norwegian Bokmal) 2019-05-25 21:50:29 +02:00
PhilippC
c3ba8662b9 New translations strings.xml (Korean) 2019-05-25 21:50:25 +02:00
PhilippC
b7e23fd046 New translations strings.xml (Portuguese, Brazilian) 2019-05-25 21:50:20 +02:00
PhilippC
9ff89ba263 New translations strings.xml (Ukrainian) 2019-05-25 21:50:16 +02:00
Gilbert Gilb's
c84f85444a Fix invalid manifest namespace causing build failures on Linux. 2019-05-25 21:49:04 +02:00
Gilbert Gilb's
37c10e7b48 Show a dialog after picking PCloud file storage.
Not seeing any file was confusing to users.

See https://github.com/PhilippC/keepass2android/issues/794
See https://github.com/PhilippC/keepass2android/issues/796
2019-05-25 21:44:48 +02:00
Gilbert Gilb's
02b8292a2e Use new option to overwrite file in PCloud file storage. 2019-05-25 21:36:39 +02:00
Gilbert Gilb's
2d1909c227 Update PCloud SDK to 1.1.0. 2019-05-25 21:35:54 +02:00
PhilippC
f470606af0 New translations strings.xml (Vietnamese) 2019-05-21 15:43:03 +02:00
PhilippC
4a7b3f17c2 New translations strings.xml (Basque) 2019-05-21 15:42:54 +02:00
PhilippC
788df21115 New translations strings.xml (Bulgarian) 2019-05-21 15:42:48 +02:00
PhilippC
2aaaa8b416 New translations strings.xml (Catalan) 2019-05-21 15:42:43 +02:00
PhilippC
2660084401 New translations strings.xml (Chinese Simplified) 2019-05-21 15:42:37 +02:00
PhilippC
e0dfb888c1 New translations strings.xml (Croatian) 2019-05-21 15:42:31 +02:00
PhilippC
ab66b5585d New translations strings.xml (Czech) 2019-05-21 15:42:25 +02:00
PhilippC
bf8b9f32df New translations strings.xml (Danish) 2019-05-21 15:42:20 +02:00
PhilippC
d259143736 New translations strings.xml (Dutch) 2019-05-21 15:42:15 +02:00
PhilippC
83dc82a308 New translations strings.xml (Finnish) 2019-05-21 15:42:09 +02:00
PhilippC
233f4bb04f New translations strings.xml (French) 2019-05-21 15:42:04 +02:00
PhilippC
8183d17706 New translations strings.xml (Galician) 2019-05-21 15:41:57 +02:00
PhilippC
feedcae410 New translations strings.xml (German) 2019-05-21 15:41:52 +02:00
PhilippC
3157ea741e New translations strings.xml (Arabic) 2019-05-21 15:41:47 +02:00
PhilippC
623040bf3e New translations strings.xml (Greek) 2019-05-21 15:41:40 +02:00
PhilippC
6a394f697a New translations strings.xml (Hungarian) 2019-05-21 15:41:34 +02:00
PhilippC
3908f4b5ce New translations strings.xml (Indonesian) 2019-05-21 15:41:29 +02:00
PhilippC
ad21ee1e64 New translations strings.xml (Italian) 2019-05-21 15:41:21 +02:00
PhilippC
6c69796248 New translations strings.xml (Japanese) 2019-05-21 15:41:16 +02:00
PhilippC
e6f79562dc New translations strings.xml (Romanian) 2019-05-21 15:41:10 +02:00
PhilippC
5e9c655672 New translations strings.xml (Russian) 2019-05-21 15:41:06 +02:00
PhilippC
6b01100ca4 New translations strings.xml (Serbian (Cyrillic)) 2019-05-21 15:41:01 +02:00
PhilippC
838382ea0a New translations strings.xml (Slovak) 2019-05-21 15:40:55 +02:00
PhilippC
3c62c22f3a New translations strings.xml (Slovenian) 2019-05-21 15:40:50 +02:00
PhilippC
bc1197b4a0 New translations strings.xml (Spanish) 2019-05-21 15:40:44 +02:00
PhilippC
d691570099 New translations strings.xml (Swedish) 2019-05-21 15:40:35 +02:00
PhilippC
772d55fa45 New translations strings.xml (Turkish) 2019-05-21 15:40:29 +02:00
PhilippC
676072e96b New translations strings.xml (Chinese Traditional) 2019-05-21 15:40:17 +02:00
PhilippC
29dec2d9f7 New translations strings.xml (Polish) 2019-05-21 11:50:55 +02:00
PhilippC
b1d6099e2c New translations strings.xml (Persian) 2019-05-21 11:50:51 +02:00
PhilippC
e5cbf010a3 New translations strings.xml (Norwegian Bokmal) 2019-05-21 11:50:41 +02:00
PhilippC
023364f968 New translations strings.xml (Korean) 2019-05-21 11:50:34 +02:00
PhilippC
26fe7f610d New translations strings.xml (Portuguese, Brazilian) 2019-05-21 11:50:29 +02:00
PhilippC
37d6e54e70 New translations strings.xml (Ukrainian) 2019-05-21 11:50:23 +02:00
PhilippC
adc4c59a99 New translations strings.xml (Chinese Traditional) 2019-05-17 05:20:11 +02:00
PhilippC
16fd528e95 New translations strings.xml (Ukrainian) 2019-05-07 13:30:13 +02:00
PhilippC
efdfdac6cb New translations strings.xml (Polish) 2019-05-05 02:40:09 +02:00
PhilippC
03ebe6b176 New translations strings.xml (Polish) 2019-05-05 02:30:11 +02:00
Philipp Crocoll
730019b420 Merge branch 'master' of c:/ph/keepass2android 2019-04-10 06:01:42 +02:00
PhilippC
477c36d880 New translations strings.xml (Polish) 2019-04-10 01:00:11 +02:00
PhilippC
d4a99ce9b2 New translations strings.xml (Czech) 2019-04-07 15:30:10 +02:00
PhilippC
025c5fe301 New translations strings.xml (Czech) 2019-04-07 15:20:09 +02:00
PhilippC
422f5deafb New translations strings.xml (Czech) 2019-04-07 15:10:09 +02:00
PhilippC
00b5473a7a New translations strings.xml (Czech) 2019-04-07 15:00:09 +02:00
PhilippC
945264d927 New translations strings.xml (Catalan) 2019-04-07 10:50:11 +02:00
PhilippC
74848cd015 New translations strings.xml (Catalan) 2019-04-07 10:40:09 +02:00
PhilippC
c583e3238a New translations strings.xml (Turkish) 2019-04-06 12:20:09 +02:00
PhilippC
0cc7abf322 New translations strings.xml (Chinese Simplified) 2019-04-06 10:20:12 +02:00
PhilippC
8f3b9c532c New translations strings.xml (Chinese Simplified) 2019-04-06 10:10:10 +02:00
PhilippC
4ce35de276 New translations strings.xml (Chinese Simplified) 2019-04-06 10:00:10 +02:00
PhilippC
7c05aa625f New translations strings.xml (Swedish) 2019-04-05 11:20:11 +02:00
PhilippC
139ac0b269 New translations strings.xml (Swedish) 2019-04-05 11:10:12 +02:00
PhilippC
8ba35083d8 New translations strings.xml (Danish) 2019-04-04 01:10:11 +02:00
PhilippC
02003abf1d New translations strings.xml (Chinese Traditional) 2019-04-01 16:12:30 +02:00
PhilippC
88ce729351 New translations strings.xml (Portuguese, Brazilian) 2019-04-01 16:11:53 +02:00
Philipp Crocoll
dcac392e43 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2019-04-01 10:44:31 +02:00
PhilippC
87cfa4c4d4 Merge pull request #766 from PhilippC/l10n_master
New Crowdin translations
2019-04-01 10:44:12 +02:00
PhilippC
58ec829d1a New translations strings.xml (Portuguese) 2019-04-01 10:39:46 +02:00
PhilippC
90fd2daa01 New translations strings.xml (Danish) 2019-04-01 10:39:05 +02:00
Philipp Crocoll
b2b29c692e 1.07-pre9 2019-03-25 10:30:06 +01:00
1076 changed files with 72123 additions and 24630 deletions

351
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,351 @@
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

18
.gitignore vendored
View File

@@ -7,6 +7,10 @@ PCtest
bin
obj
# Makefile-related files
/allow_git_clean
/stamp.nuget_*
Resource.designer.cs
R.java
@@ -105,7 +109,6 @@ 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
@@ -160,3 +163,16 @@ src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-A
/src/.vs
/src/JavaFileStorageBindings/Jars/JavaFileStorage-release.aar
/src/PluginSdkBinding/Jars/app-debug.aar
/src/java/KP2AKdbLibrary/libs/arm64-v8a/libfinal-key.so
/src/java/KP2AKdbLibrary/libs/x86_64/libfinal-key.so
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs/arm64-v8a/libkp2akeyboard_jni.so
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs/armeabi-v7a/libkp2akeyboard_jni.so
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs/x86/libkp2akeyboard_jni.so
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs/x86_64/libkp2akeyboard_jni.so
/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

6
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[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 Normal file
View File

@@ -0,0 +1,349 @@
#!/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,3 +8,27 @@ 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

239
docs/Build.readme.md Normal file
View File

@@ -0,0 +1,239 @@
# 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

@@ -28,14 +28,14 @@ Select "Create new database" from the start screen. Tap the integrated help icon
## Getting passwords into password fields
There are many ways how to enter the passwords from your database in the corresponding fields. By default, the clipboard as well as the KP2A keyboard are activated in the settings:
* The KP2A keyboard is the recommended way because it's safe against clipboard loggers: Whenever you select an entry, the KP2A keyboard notification will appear in the notification bar. Click it to activate the keyboard. (The first time you do this, you are required to enable the keyboard in the system settings. This must be done by the user for Android security reasons.) As soon as it's activated, you can tap a field where you want to enter data from the selected entry. The KP2A keyboard will come up. Click the KP2A key (on the bottom left) to select whether you want to enter Username/password etc. When you're done, click the Keyboard key (next to the KP2A key) to switch back to your favorite keyboard.
* You can enable the Keepass2Android Autofill service in the system's accessibility settings which allows to fill data using Android's accessibility system. This works with many apps including Chrome browser but is not supported for Firefox on Android.
* The clipboard based approach can be used as well: Pull the notification bar down and select "Copy username/password to clipboard". Then long-tap the field where you want to paste the data. A small "paste" button should come up. Note, however, that information in the clipboard can be monitored by all apps on your device.
* You can enable the Keepass2Android Autofill service in the system's Autofill settings (Android 8+) which allows to fill data using Android's accessibility system. This works with many apps including Firefox browser but is not supported for Chrome (when writing this).
* The clipboard based approach can be used as well: Pull the notification bar down and select "Copy username/password to clipboard". Then long-tap the field where you want to paste the data. A small "paste" button should come up. Note, however, that information in the clipboard can be monitored by all apps on your device and clearing the clipboard is not always possible.
These options can be used in different workflows:
### Browser-based workflow
If you are browsing the web and need to enter crendentials for a webpage, a simple and powerful workflow is to use the "Share URL" option from the browser's menu. Then select Keepass2Android (or KP2A Offline). Open your database (if it's not already opened) and select the entry you want to enter (if KP2A did not already select the appropriate entry). Use the built-in keyboard or the clipboard to enter the password.
### Autofill service based workflow
If you have enabled the autofill service and open a (supported) app with a password field, a notification appears in the notification bar. Select this notification to select the appropriate entry. When you return to the app, the password and user field should be filled already.
If you have enabled the autofill service and open a (supported) app with a password field, a dropdown appears. Select "Fill with Keepass2Android" to select the appropriate entry. When you return to the app, the password and user field should be filled already.
### KP2A based workflow for websites
Open KP2A, open your database, select your entry (in this step, the notification bar items should show up already). Now click the URL link of the entry to open a browser window with the website. Use one of the methods described above to enter the credentials.
### KP2A Keyboard based workflow
@@ -70,16 +70,53 @@ Please see the [Advanced usage of the Keepass2Android keyboard](Advanced-usage-o
# FAQ
* Should I use the KP2A keyboard for entering passwords?
## Should I use the KP2A keyboard for entering passwords?
The KP2A keyboard is meant to quickly "paste" or "type" values from your database to any text fields by using the KP2A icon. The QUERTY keyboard is just for convenience (if you just have the KP2A keyboard activated and need to enter a few letters). However, every other (trustworthy) keyboard is ok as well to enter sensitive information: Keyboard's aren't unsafe in Android. Only the clipboard is. Thus, the KP2A keyboard allows to get information out of the database without using the clipboard.
**You can use any keyboard when you enter the main database password**
* 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!
## 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.
* Why is Keepass2Android so big?
## 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.
In all cases, you need to verify that your backup is readable! It's even best to test this on another device (e.g. a PC), so you simulate the case that you may lose your phone.
## I can open my database with fingerprint, but don't remember my master password!
It's time for action! As soon as possible, select Settings - Database - Export and choose unencrypted XML (don't put this on the cloud but on a local file). Transfer this file to a PC and import it to a new kdbx file, e.g. with Keepass2. Choose a new master password and make sure you don't forget this password!
## How can I transfer data from one device to another?
* If you are about to get a new Android device, you should make sure you're not losing your passwords in the transition! The first thing you need to make sure is that you can access your .kdbx file (which stores the passwords) on the new device. If it is already stored in the cloud, you only need to make sure you know how to setup the cloud storage on the new device (it might require a password, so make sure you have access to that!).
* If the .kdbx-file is stored locally on the old device, make sure you have an up-to-date backup (see above). You can then transfer that backup copy to the new device. (Note: transferring via USB causes data corruption in some cases, use MyPhoneExplorer or similar tools to be sure this does not happen.)
* If you are securing your password database with a keyfile, also transfer this key file to the new device.
* If you are opening your database with a fingerprint, make sure you also know the master password because fingerprint will not be available immediately on the new device.
## 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,14 +8,20 @@ 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). Please join the [Beta tester group](https://plus.google.com/communities/107293657110547776032) for news and discussions about the latest beta releases.
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).
# 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).
[![Build Status](https://www.bitrise.io/app/43a23ab54dee9f7e/status.svg?token=2vryTsMQzTX3XRPikhgRwA&branch=master)](https://www.bitrise.io/app/43a23ab54dee9f7e)
# 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)

View File

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

View File

@@ -10,8 +10,10 @@
<RootNamespace>AndroidFileChooserBinding</RootNamespace>
<AssemblyName>AndroidFileChooserBinding</AssemblyName>
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<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 +80,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,5 +1,7 @@
<?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>
@@ -10,8 +12,12 @@
<RootNamespace>JavaFileStorageBindings</RootNamespace>
<AssemblyName>JavaFileStorageBindings</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
<AndroidClassParser>class-parse</AndroidClassParser>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -44,11 +50,130 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Java.Interop" />
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="GooglePlayServicesLib">
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll</HintPath>
<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>
</ItemGroup>
<ItemGroup>
@@ -60,6 +185,7 @@
</LibraryProjectZip>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml" />
@@ -81,13 +207,10 @@
</XamarinComponentReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gson-2.3.1.jar" />
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
<Project>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</Project>
<Name>PCloudBindings</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\msa-auth-0.8.6\classes-msa-auth.jar" />
@@ -98,30 +221,12 @@
<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>
@@ -134,9 +239,6 @@
<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>
@@ -144,12 +246,137 @@
<EmbeddedReferenceJar Include="Jars\jackson-core-2.7.4.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-3.0.3.jar" />
<EmbeddedReferenceJar Include="Jars\okhttp-digest-2.5.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-3.9.0.jar" />
<EmbeddedReferenceJar Include="Jars\okio-2.9.0.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okio-1.13.0.jar" />
<EmbeddedReferenceJar Include="Jars\okhttp-4.10.0-RC1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-4.0.0.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

@@ -0,0 +1,44 @@
<?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

@@ -10,8 +10,10 @@
<RootNamespace>KP2AKdbLibraryBinding</RootNamespace>
<AssemblyName>KP2AKdbLibraryBinding</AssemblyName>
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<AndroidClassParser>class-parse</AndroidClassParser>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -52,10 +54,11 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<LibraryProjectZip Include="..\java\KP2AKdbLibrary\app\build\outputs\aar\app-debug.aar">
<Link>Jars\app-debug.aar</Link>
</LibraryProjectZip>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<EmbeddedNativeLibrary Include="libs\armeabi-v7a\libfinal-key.so" />
<EmbeddedNativeLibrary Include="libs\armeabi\libfinal-key.so" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml">
@@ -65,9 +68,7 @@
<TransformFile Include="Transforms\EnumMethods.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="..\java\KP2AKdbLibrary\bin\kp2akdblibrary.jar">
<Link>Jars\kp2akdblibrary.jar</Link>
</EmbeddedJar>
<Folder Include="libs\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -77,4 +78,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -1,12 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2009
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
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}"
@@ -23,12 +21,14 @@ 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}") = "System.Net.FtpClient.Android", "netftpandroid\System.Net.FtpClient\System.Net.FtpClient.Android.csproj", "{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}"
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,34 +67,6 @@ 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
@@ -255,42 +227,6 @@ 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
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Win32.ActiveCfg = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|x64.ActiveCfg = Debug|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Any CPU.Build.0 = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Win32.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|x64.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|x64.ActiveCfg = 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
@@ -315,6 +251,90 @@ 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-2017 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -33,8 +33,11 @@ namespace KeePassLib.Collections
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
public ProtectedBinarySet()
private readonly bool m_bDedupAdd;
public ProtectedBinarySet(bool bDedupAdd)
{
m_bDedupAdd = bDedupAdd;
}
IEnumerator IEnumerable.GetEnumerator()
@@ -47,15 +50,10 @@ 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;
}
@@ -63,7 +61,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;
@@ -71,12 +69,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;
@@ -84,9 +82,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
@@ -95,28 +93,26 @@ 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; }
int i = Find(pb);
if(i >= 0) return; // Exists already
if (m_bDedupAdd && (Find(pb) >= 0)) return; // Exists already
i = GetFreeID();
m_d[i] = pb;
m_d[GetFreeID()] = 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);
}
@@ -124,16 +120,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);
}
@@ -148,9 +144,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();
@@ -159,9 +155,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-2017 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -20,8 +20,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Interfaces;
@@ -34,48 +34,74 @@ namespace KeePassLib.Collections
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_dict =
private SortedDictionary<string, string> m_d =
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_dict.Count; }
get { return m_d.Count; }
}
public StringDictionaryEx()
{
}
internal StringDictionaryEx(bool bRememberLastMod)
{
if (bRememberLastMod) m_dLastMod = new Dictionary<string, DateTime>();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_dict.GetEnumerator();
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_dict.GetEnumerator();
return m_d.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvp in m_dict)
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
foreach (KeyValuePair<string, string> kvp in m_d)
sdNew.m_d[kvp.Key] = kvp.Value;
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_dict.Count != sdOther.m_dict.Count) return false;
if (m_d.Count != sdOther.m_d.Count) return false;
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
foreach (KeyValuePair<string, string> kvp in sdOther.m_d)
{
string str = Get(kvp.Key);
if((str == null) || (str != kvp.Value)) return false;
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;
}
}
return true;
@@ -83,48 +109,62 @@ 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 s;
if(m_dict.TryGetValue(strName, out s)) return s;
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;
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_dict.ContainsKey(strName);
return m_d.ContainsKey(strName);
}
/// <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)
public void Set(string strName, string strValue)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
m_dict[strField] = strNewValue;
m_d[strName] = strValue;
if (m_dLastMod != null) m_dLastMod[strName] = DateTime.UtcNow;
}
/// <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)
internal void Set(string strName, string strValue, DateTime? odtLastMod)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
return m_dict.Remove(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);
}
}
}

View File

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

View File

@@ -367,5 +367,27 @@ 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-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2020 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,28 +20,67 @@
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];
@@ -49,7 +88,7 @@ namespace KeePassLib.Cryptography
#endif
byte[] pbHash;
using(SHA256Managed h = new SHA256Managed())
using (SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
@@ -66,6 +105,22 @@ 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" />.
@@ -73,34 +128,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);
@@ -125,5 +180,75 @@ 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,6 +46,8 @@ 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;
@@ -56,6 +58,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
private sealed class Argon2Ctx
{
public Argon2Type Type = Argon2Type.D;
public uint Version = 0;
public ulong Lanes = 0;
@@ -89,9 +92,9 @@ namespace KeePassLib.Cryptography.KeyDerivation
}
}
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
private byte[] Argon2Transform(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);
@@ -101,6 +104,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
#endif
Argon2Ctx ctx = new Argon2Ctx();
ctx.Type = m_t;
ctx.Version = uVersion;
ctx.Lanes = uParallel;
@@ -137,7 +141,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(0, pbBuf, 0); // Argon2d type = 0
MemUtil.UInt32ToBytesEx((uint)m_t, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
@@ -487,18 +491,43 @@ 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 ((ti.Pass == 0) && (ti.Slice == 0))
{
uStart = 2;
if (bDataIndependentAddr)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
}
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
@@ -506,17 +535,23 @@ namespace KeePassLib.Cryptography.KeyDerivation
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
for (ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if((uCur % ctx.LaneLength) == 1)
if ((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
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 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;
@@ -536,11 +571,12 @@ 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
@@ -610,6 +646,19 @@ 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

@@ -20,15 +20,27 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
public enum Argon2Type
{
// The values must be the same as in the Argon2 specification
D = 0,
ID = 2
}
public sealed partial class Argon2Kdf : KdfEngine
{
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 });
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 });
public const string ParamSalt = "S"; // Byte[]
public const string ParamParallelism = "P"; // UInt32
@@ -54,28 +66,37 @@ 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 = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 64 * 1024 * 1024; // 64 MB
internal const uint DefaultParallelism = 2;
public override PwUuid Uuid
{
get { return g_uuid; }
}
private readonly Argon2Type m_t;
public override string Name
{
get { return "Argon2"; }
}
public override PwUuid Uuid
{
get { return ((m_t == Argon2Type.D) ? g_uuidD : g_uuidID); }
}
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 Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
@@ -91,7 +112,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
@@ -127,10 +148,59 @@ namespace KeePassLib.Cryptography.KeyDerivation
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
byte[] pbRet;
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
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(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}
@@ -143,5 +213,14 @@ namespace KeePassLib.Cryptography.KeyDerivation
MaxIterations, uMilliseconds, true);
return p;
}
[DllImport("argon2")]
static extern int argon2_hash(
UInt32 t_cost, UInt32 m_cost, UInt32 parallelism,
IntPtr pwd, IntPtr pwdlen,
IntPtr salt, IntPtr saltlen,
IntPtr hash, IntPtr hashlen,
IntPtr encoded, IntPtr encodedlen,
UInt32 type, UInt32 version);
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2020 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-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2020 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,10 +41,11 @@ 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());
g_l.Add(new Argon2Kdf(Argon2Type.D));
g_l.Add(new Argon2Kdf(Argon2Type.ID));
}
internal static KdfParameters GetDefaultParameters()

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-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
@@ -21,17 +21,22 @@ 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;
}
}
}
PwGroup ParentGroup
{
get;
}
PwUuid PreviousParentGroup
{
get;
}
}
}

View File

@@ -12,8 +12,8 @@
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AssemblyName>KeePassLib2Android</AssemblyName>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<NuGetPackageImportStamp>8482b288</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -50,6 +50,7 @@
<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" />
@@ -71,7 +72,9 @@
<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" />
@@ -160,6 +163,7 @@
<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

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

View File

@@ -21,7 +21,6 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Security;
#if !KeePassUAP
@@ -78,26 +77,7 @@ namespace KeePassLib.Keys
// m_pbKeyData = null;
// }
private static string GetUserKeyFilePath(bool bCreate)
{
#if KeePassRT
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else
string strUserDir = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
if (bCreate && !Directory.Exists(strUserDir))
Directory.CreateDirectory(strUserDir);
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
return strUserDir + UserKeyFileName;
}
private static byte[] LoadUserKey(bool bShowWarning)
private static byte[] LoadUserKey(bool bShowWarning)
{
byte[] pbKey = null;

View File

@@ -118,5 +118,10 @@ 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,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
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
@@ -20,61 +18,106 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if !KeePassUAP
using System.Drawing;
using System.IO;
#endif
using KeePassLib.Utility;
namespace KeePassLib
{
/// <summary>
/// Custom icon. <c>PwCustomIcon</c> objects are immutable.
/// </summary>
public sealed class PwCustomIcon
{
private PwUuid m_pwUuid;
private byte[] m_pbImageDataPng;
private Android.Graphics.Bitmap m_pCachedImage;
// 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>();
public PwUuid Uuid
{
get { return m_pwUuid; }
get { return m_uuid; }
}
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 m_pCachedImage; }
get { return GetImage(); } // Backward compatibility
}
public PwCustomIcon(PwUuid pu, 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"); }
m_uuid = pu;
m_pbImageDataPng = pbImageDataPng;
}
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
private static long GetKey(int w, int h)
{
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");
return (((long)w << 32) ^ (long)h);
}
Debug.Assert(pbImageDataPng != null);
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
/// <summary>
/// Get the icon as an <c>Image</c> (original size).
/// </summary>
public Android.Graphics.Bitmap GetImage()
{
const long lKey = -1;
m_pwUuid = pwUuid;
m_pbImageDataPng = pbImageDataPng;
Android.Graphics.Bitmap img;
if (m_dImageCache.TryGetValue(lKey, out img)) return img;
#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
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;
}
}
}

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();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx(true);
private VariantDictionary m_dPublicCustomData = new VariantDictionary();
private byte[] m_pbHashOfFileOnDisk = null;
@@ -587,7 +587,7 @@ namespace KeePassLib
/// </summary>
/// <param name="ioConnection">IO connection of the new database.</param>
/// <param name="pwKey">Key to open the database.</param>
public void New(IOConnectionInfo ioConnection, CompositeKey pwKey)
public void New(IOConnectionInfo ioConnection, CompositeKey pwKey, string filenameWithoutPathAndExt)
{
Debug.Assert(ioConnection != null);
if(ioConnection == null) throw new ArgumentNullException("ioConnection");
@@ -602,26 +602,11 @@ namespace KeePassLib
m_bDatabaseOpened = true;
m_bModified = true;
m_pgRootGroup = new PwGroup(true, true,
UrlUtil.StripExtension(UrlUtil.GetFileName(ioConnection.Path)),
m_pgRootGroup = new PwGroup(true, true, filenameWithoutPathAndExt,
PwIcon.FolderOpen);
m_pgRootGroup.IsExpanded = true;
}
/// <summary>
/// Open a database. The URL may point to any supported data source.
/// </summary>
/// <param name="ioSource">IO connection to load the database from.</param>
/// <param name="pwKey">Key used to open the specified database.</param>
/// <param name="slLogger">Logger, which gets all status messages.</param>
public void Open(IOConnectionInfo ioSource, CompositeKey pwKey,
IStatusLogger slLogger, IDatabaseFormat format)
{
Open(IOConnection.OpenRead(ioSource), UrlUtil.StripExtension(
UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger, format);
}
/// <summary>
/// Open a database. The URL may point to any supported data source.
@@ -669,37 +654,7 @@ namespace KeePassLib
throw;
}
}
/// <summary>
/// Save the currently opened database. The file is written to the location
/// it has been opened from.
/// </summary>
/// <param name="slLogger">Logger that recieves status information.</param>
public void Save(IStatusLogger slLogger)
{
Debug.Assert(!HasDuplicateUuids());
FileLock fl = null;
if(m_bUseFileLocks) fl = new FileLock(m_ioSource);
try
{
FileTransactionEx ft = new FileTransactionEx(m_ioSource,
m_bUseFileTransactions);
Stream s = ft.OpenWrite();
KdbxFile kdb = new KdbxFile(this);
kdb.Save(s, null, KdbpFile.GetFormatToUse(m_ioSource), slLogger);
ft.CommitWrite();
m_pbHashOfLastIO = kdb.HashOfFileOnDisk;
m_pbHashOfFileOnDisk = kdb.HashOfFileOnDisk;
Debug.Assert(m_pbHashOfFileOnDisk != null);
}
finally { if(fl != null) fl.Dispose(); }
m_bModified = false;
}
/// <summary>
/// Save the currently opened database. The file is written to the given stream which is expected to be the original location.
@@ -717,46 +672,6 @@ namespace KeePassLib
m_bModified = false;
}
/// <summary>
/// Save the currently opened database to a different location. If
/// <paramref name="bIsPrimaryNow" /> is <c>true</c>, the specified
/// location is made the default location for future saves
/// using <c>SaveDatabase</c>.
/// </summary>
/// <param name="ioConnection">New location to serialize the database to.</param>
/// <param name="bIsPrimaryNow">If <c>true</c>, the new location is made the
/// standard location for the database. If <c>false</c>, a copy of the currently
/// opened database is saved to the specified location, but it isn't
/// made the default location (i.e. no lock files will be moved for
/// example).</param>
/// <param name="slLogger">Logger that recieves status information.</param>
public void SaveAs(IOConnectionInfo ioConnection, bool bIsPrimaryNow,
IStatusLogger slLogger)
{
Debug.Assert(ioConnection != null);
if(ioConnection == null) throw new ArgumentNullException("ioConnection");
IOConnectionInfo ioCurrent = m_ioSource; // Remember current
m_ioSource = ioConnection;
byte[] pbHashCopy = m_pbHashOfFileOnDisk;
try { this.Save(slLogger); }
catch(Exception)
{
m_ioSource = ioCurrent; // Restore
m_pbHashOfFileOnDisk = pbHashCopy;
m_pbHashOfLastIO = null;
throw;
}
if(!bIsPrimaryNow)
{
m_ioSource = ioCurrent; // Restore
m_pbHashOfFileOnDisk = pbHashCopy;
}
}
/// <summary>
/// Closes the currently opened database. No confirmation message is shown
@@ -775,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);
@@ -792,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;
@@ -801,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
@@ -813,17 +728,23 @@ 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;
@@ -832,9 +753,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);
@@ -845,23 +766,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;
@@ -881,19 +802,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);
@@ -907,13 +828,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);
@@ -923,24 +844,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> 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);
}
// 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);
// Must be called *after* merging groups, because group UUIDs
// are required for recycle bin and entry template UUIDs
MergeInDbProperties(pdSource, mm);
MergeInCustomIcons(pdSource);
MergeInCustomIcons(pdSource, dDel);
Debug.Assert(m_vDeletedObjects.UCount == (uint)dDel.Count);
MaintainBackups();
@@ -948,15 +869,79 @@ namespace KeePassLib
m_slStatus = slPrevStatus;
}
private void MergeInCustomIcons(PwDatabase pdSource)
{
foreach(PwCustomIcon pwci in pdSource.CustomIcons)
{
if(GetCustomIconIndex(pwci.Uuid) >= 0) continue;
m_vCustomIcons.Add(pwci); // PwCustomIcon is immutable
m_bUINeedsIconUpdate = true;
private void MergeInCustomIcons(PwDatabase pdSource,
Dictionary<PwUuid, PwDeletedObject> dDel)
{
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)
{
int iT;
if (d.TryGetValue(ciS.Uuid, out iT))
{
PwCustomIcon ciT = m_vCustomIcons[iT];
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;
}
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()
@@ -1297,7 +1282,9 @@ namespace KeePassLib
PwObjectBlock<T> b = new PwObjectBlock<T>();
DateTime dtLoc;
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc);
PwUuid puPrevParent;
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc,
out puPrevParent);
b.Add(t, dtLoc, pPool);
lBlocks.Add(b);
@@ -1332,7 +1319,7 @@ namespace KeePassLib
}
if(idSrcNext == 0) break;
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc);
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc, out puPrevParent);
b.Add(tNext, dtLoc, pPool);
++u;
@@ -1345,28 +1332,31 @@ namespace KeePassLib
}
private static PwObjectPoolEx GetBestPool<T>(T t, PwObjectPoolEx ppOrg,
PwObjectPoolEx ppSrc, out DateTime dtLoc)
PwObjectPoolEx ppSrc, out DateTime dtLoc, out PwUuid puPrevParent)
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
puPrevParent = PwUuid.Zero;
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if(ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
p = ppOrg;
}
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if (ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
puPrevParent = ptOrg.PreviousParentGroup;
p = ppOrg;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
p = ppSrc;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if ((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
puPrevParent = ptSrc.PreviousParentGroup;
p = ppSrc;
}
Debug.Assert(p != null);
return p;
Debug.Assert(p != null);
return p;
}
private static int FindLocationChangedPivot<T>(List<PwObjectBlock<T>> lBlocks,
@@ -1392,30 +1382,40 @@ namespace KeePassLib
return iPosMax;
}
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;
};
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;
};
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;
};
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;
};
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,12 +1530,18 @@ 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);
m_dCustomData.Set(kvp.Key, kvp.Value, null);
}
VariantDictionary vdLocal = m_dPublicCustomData; // Backup
m_dPublicCustomData = (VariantDictionary)pdSource.m_dPublicCustomData.Clone();
if(!bSourceNewer) vdLocal.CopyTo(m_dPublicCustomData); // Merge
// '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;
}
}
private void MergeEntryHistory(PwEntry pe, PwEntry peSource,
@@ -1628,12 +1634,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;
@@ -1643,15 +1649,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;
}
@@ -1670,68 +1676,64 @@ namespace KeePassLib
else { Debug.Assert(false); return null; }
}
public bool DeleteCustomIcons(List<PwUuid> vUuidsToDelete)
{
Debug.Assert(vUuidsToDelete != null);
if(vUuidsToDelete == null) throw new ArgumentNullException("vUuidsToDelete");
if(vUuidsToDelete.Count <= 0) return true;
public bool DeleteCustomIcons(List<PwUuid> lUuids)
{
if (lUuids == null) { Debug.Assert(false); throw new ArgumentNullException("lUuids"); }
if (lUuids.Count == 0) return false;
GroupHandler gh = delegate(PwGroup pg)
{
PwUuid uuidThis = pg.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return true;
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwUuid pu in lUuids) { dToDel[pu] = true; }
foreach(PwUuid uuidDelete in vUuidsToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pg.CustomIconUuid = PwUuid.Zero;
break;
}
}
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));
}
}
return true;
};
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
m_vCustomIcons.RemoveAll(f);
EntryHandler eh = delegate(PwEntry pe)
{
RemoveCustomIconUuid(pe, vUuidsToDelete);
return true;
};
FixCustomIconRefs();
return true;
}
gh(m_pgRootGroup);
if(!m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh))
{
Debug.Assert(false);
return false;
}
private void FixCustomIconRefs()
{
Dictionary<PwUuid, bool> d = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { d[ci.Uuid] = true; }
foreach(PwUuid pwUuid in vUuidsToDelete)
{
int nIndex = GetCustomIconIndex(pwUuid);
if(nIndex >= 0) m_vCustomIcons.RemoveAt(nIndex);
}
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;
};
return true;
}
EntryHandler eh = delegate (PwEntry pe)
{
FixCustomIconRefs(pe, d);
return true;
};
private static void RemoveCustomIconUuid(PwEntry pe, List<PwUuid> vToDelete)
{
PwUuid uuidThis = pe.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return;
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
foreach(PwUuid uuidDelete in vToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pe.CustomIconUuid = PwUuid.Zero;
break;
}
}
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(PwEntry peHistory in pe.History)
RemoveCustomIconUuid(peHistory, vToDelete);
}
private int GetTotalObjectUuidCount()
{
@@ -2020,61 +2022,119 @@ namespace KeePassLib
return uDeleted;
}
public uint DeleteUnusedCustomIcons()
{
List<PwUuid> lToDelete = new List<PwUuid>();
foreach(PwCustomIcon pwci in m_vCustomIcons)
lToDelete.Add(pwci.Uuid);
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { dToDel[ci.Uuid] = true; }
GroupHandler gh = delegate(PwGroup pg)
GroupHandler gh = delegate (PwGroup pg)
{
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;
}
}
PwUuid pu = pg.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero)) dToDel.Remove(pu);
return true;
};
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry 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;
}
}
RemoveCustomIconsFromDict(dToDel, pe);
return true;
};
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
uint uDeleted = 0;
foreach(PwUuid pwDel in lToDelete)
uint cDel = (uint)dToDel.Count;
if (cDel != 0)
{
int nIndex = GetCustomIconIndex(pwDel);
if(nIndex < 0) { Debug.Assert(false); continue; }
m_vCustomIcons.RemoveAt(nIndex);
++uDeleted;
DeleteCustomIcons(new List<PwUuid>(dToDel.Keys));
m_bUINeedsIconUpdate = true;
}
if(uDeleted > 0) m_bUINeedsIconUpdate = true;
return 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);
}
}
}

View File

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

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-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
@@ -42,14 +42,15 @@ 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_listStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_listAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_listHistory = new PwObjectList<PwEntry>();
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 PwIcon m_pwIcon = PwIcon.Key;
private PwUuid m_pwCustomIconID = PwUuid.Zero;
private PwUuid m_puCustomIcon = PwUuid.Zero;
private Color m_clrForeground = Color.Empty;
private Color m_clrBackground = Color.Empty;
@@ -62,20 +63,21 @@ namespace KeePassLib
private ulong m_uUsageCount = 0;
private string m_strOverrideUrl = string.Empty;
private bool m_bQualityCheck = true;
private List<string> m_vTags = new List<string>();
private List<string> m_lTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary>
/// UUID of this entry.
/// </summary>
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_uuid = value;
}
}
@@ -87,7 +89,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; }
}
@@ -100,17 +102,26 @@ 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_listStrings; }
get { return m_dStrings; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listStrings = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dStrings = value;
}
}
@@ -119,11 +130,11 @@ namespace KeePassLib
/// </summary>
public ProtectedBinaryDictionary Binaries
{
get { return m_listBinaries; }
get { return m_dBinaries; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listBinaries = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dBinaries = value;
}
}
@@ -132,11 +143,11 @@ namespace KeePassLib
/// </summary>
public AutoTypeConfig AutoType
{
get { return m_listAutoType; }
get { return m_cfgAutoType; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listAutoType = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_cfgAutoType = value;
}
}
@@ -145,11 +156,11 @@ namespace KeePassLib
/// </summary>
public PwObjectList<PwEntry> History
{
get { return m_listHistory; }
get { return m_lHistory; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listHistory = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lHistory = value;
}
}
@@ -169,11 +180,11 @@ namespace KeePassLib
/// </summary>
public PwUuid CustomIconUuid
{
get { return m_pwCustomIconID; }
get { return m_puCustomIcon; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_pwCustomIconID = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_puCustomIcon = value;
}
}
@@ -252,28 +263,34 @@ namespace KeePassLib
}
/// <summary>
/// Entry-specific override URL. If this string is non-empty,
/// Entry-specific override URL.
/// </summary>
public string OverrideUrl
{
get { return m_strOverrideUrl; }
set
{
if (value == null) throw new ArgumentNullException("value");
if (value == null) { Debug.Assert(false); 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 { return m_vTags; }
get { StrUtil.NormalizeTags(m_lTags); return m_lTags; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_vTags = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lTags = value;
}
}
@@ -349,19 +366,19 @@ namespace KeePassLib
}
#if DEBUG
/// <summary>
// For display in debugger
public override string ToString()
{
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
return ("PwEntry '" + m_dStrings.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);
@@ -369,14 +386,15 @@ 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_listStrings = m_listStrings.CloneDeep();
peNew.m_listBinaries = m_listBinaries.CloneDeep();
peNew.m_listAutoType = m_listAutoType.CloneDeep();
peNew.m_listHistory = m_listHistory.CloneDeep();
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_pwIcon = m_pwIcon;
peNew.m_pwCustomIconID = m_pwCustomIconID;
peNew.m_puCustomIcon = m_puCustomIcon;
peNew.m_clrForeground = m_clrForeground;
peNew.m_clrBackground = m_clrBackground;
@@ -389,8 +407,9 @@ namespace KeePassLib
peNew.m_uUsageCount = m_uUsageCount;
peNew.m_strOverrideUrl = m_strOverrideUrl;
peNew.m_bQualityCheck = m_bQualityCheck;
peNew.m_vTags = new List<string>(m_vTags);
peNew.m_lTags.AddRange(m_lTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
@@ -457,27 +476,29 @@ 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_listStrings.EqualsDictionary(pe.m_listStrings, pwOpt, mpCmpStr))
if (!m_dStrings.EqualsDictionary(pe.m_dStrings, pwOpt, mpCmpStr))
return false;
if (!m_listBinaries.EqualsDictionary(pe.m_listBinaries)) return false;
if (!m_dBinaries.EqualsDictionary(pe.m_dBinaries)) return false;
if (!m_listAutoType.Equals(pe.m_listAutoType)) return false;
if (!m_cfgAutoType.Equals(pe.m_cfgAutoType)) return false;
if ((pwOpt & PwCompareOptions.IgnoreHistory) == PwCompareOptions.None)
{
bool bIgnoreLastBackup = ((pwOpt & PwCompareOptions.IgnoreLastBackup) !=
PwCompareOptions.None);
if (!bIgnoreLastBackup && (m_listHistory.UCount != pe.m_listHistory.UCount))
if (!bIgnoreLastBackup && (m_lHistory.UCount != pe.m_lHistory.UCount))
return false;
if (bIgnoreLastBackup && (m_listHistory.UCount == 0))
if (bIgnoreLastBackup && (m_lHistory.UCount == 0))
{
Debug.Assert(false);
return false;
}
if (bIgnoreLastBackup && ((m_listHistory.UCount - 1) != pe.m_listHistory.UCount))
if (bIgnoreLastBackup && ((m_lHistory.UCount - 1) != pe.m_lHistory.UCount))
return false;
PwCompareOptions cmpSub = PwCompareOptions.IgnoreParentGroup;
@@ -485,16 +506,16 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpSub |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpSub |= PwCompareOptions.IgnoreLastAccess;
for (uint uHist = 0; uHist < pe.m_listHistory.UCount; ++uHist)
for (uint uHist = 0; uHist < pe.m_lHistory.UCount; ++uHist)
{
if (!m_listHistory.GetAt(uHist).EqualsEntry(pe.m_listHistory.GetAt(
if (!m_lHistory.GetAt(uHist).EqualsEntry(pe.m_lHistory.GetAt(
uHist), cmpSub, MemProtCmpMode.None))
return false;
}
}
if (m_pwIcon != pe.m_pwIcon) return false;
if (!m_pwCustomIconID.Equals(pe.m_pwCustomIconID)) return false;
if (!m_puCustomIcon.Equals(pe.m_puCustomIcon)) return false;
if (m_clrForeground != pe.m_clrForeground) return false;
if (m_clrBackground != pe.m_clrBackground) return false;
@@ -507,12 +528,10 @@ 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;
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;
}
// The Tags property normalizes
if (!MemUtil.ListsEqual<string>(this.Tags, pe.Tags)) return false;
if (!m_dCustomData.Equals(pe.m_dCustomData)) return false;
@@ -543,16 +562,19 @@ namespace KeePassLib
m_uuid = peTemplate.m_uuid;
if (bAssignLocationChanged)
{
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
m_puPrevParentGroup = peTemplate.m_puPrevParentGroup;
}
m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
m_dStrings = peTemplate.m_dStrings.CloneDeep();
m_dBinaries = peTemplate.m_dBinaries.CloneDeep();
m_cfgAutoType = peTemplate.m_cfgAutoType.CloneDeep();
if (bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_lHistory = peTemplate.m_lHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
m_puCustomIcon = peTemplate.m_puCustomIcon; // Immutable
m_clrForeground = peTemplate.m_clrForeground;
m_clrBackground = peTemplate.m_clrBackground;
@@ -565,8 +587,9 @@ namespace KeePassLib
m_uUsageCount = peTemplate.m_uUsageCount;
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_bQualityCheck = peTemplate.m_bQualityCheck;
m_vTags = new List<string>(peTemplate.m_vTags);
m_lTags = new List<string>(peTemplate.m_lTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
}
@@ -629,9 +652,9 @@ namespace KeePassLib
public void CreateBackup(PwDatabase pwHistMntcSettings)
{
PwEntry peCopy = CloneDeep();
peCopy.History = new PwObjectList<PwEntry>(); // Remove history
peCopy.m_lHistory.Clear();
m_listHistory.Add(peCopy); // Must be added at end, see EqualsEntry
m_lHistory.Add(peCopy); // Must be added at end, see EqualsEntry
if (pwHistMntcSettings != null) MaintainBackups(pwHistMntcSettings);
}
@@ -658,12 +681,14 @@ namespace KeePassLib
/// This parameter may be <c>null</c> (no maintenance then).</param>
public void RestoreFromBackup(uint uBackupIndex, PwDatabase pwHistMntcSettings)
{
Debug.Assert(uBackupIndex < m_listHistory.UCount);
if (uBackupIndex >= m_listHistory.UCount)
if (uBackupIndex >= m_lHistory.UCount)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uBackupIndex");
}
PwEntry pe = m_listHistory.GetAt(uBackupIndex);
Debug.Assert(pe != null); if (pe == null) throw new InvalidOperationException();
PwEntry pe = m_lHistory.GetAt(uBackupIndex);
if (pe == null) { Debug.Assert(false); throw new InvalidOperationException(); }
CreateBackup(pwHistMntcSettings); // Backup current data before restoring
AssignProperties(pe, false, false, false);
@@ -679,7 +704,7 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpOpt |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpOpt |= PwCompareOptions.IgnoreLastAccess;
foreach (PwEntry pe in m_listHistory)
foreach (PwEntry pe in m_lHistory)
{
if (pe.EqualsEntry(peData, cmpOpt, MemProtCmpMode.None)) return true;
}
@@ -688,21 +713,28 @@ namespace KeePassLib
}
/// <summary>
/// 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>
/// 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>
/// </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_listHistory.UCount > (uint)nMaxItems)
while (m_lHistory.UCount > (uint)nMaxItems)
{
RemoveOldestBackup();
bDeleted = true;
@@ -715,7 +747,7 @@ namespace KeePassLib
while (true)
{
ulong uHistSize = 0;
foreach (PwEntry pe in m_listHistory) { uHistSize += pe.GetSize(); }
foreach (PwEntry pe in m_lHistory) { uHistSize += pe.GetSize(); }
if (uHistSize > (ulong)lMaxSize)
{
@@ -734,9 +766,9 @@ namespace KeePassLib
DateTime dtMin = TimeUtil.SafeMaxValueUtc;
uint idxRemove = uint.MaxValue;
for (uint u = 0; u < m_listHistory.UCount; ++u)
for (uint u = 0; u < m_lHistory.UCount; ++u)
{
PwEntry pe = m_listHistory.GetAt(u);
PwEntry pe = m_lHistory.GetAt(u);
if (TimeUtil.Compare(pe.LastModificationTime, dtMin, true) < 0)
{
idxRemove = u;
@@ -744,12 +776,12 @@ namespace KeePassLib
}
}
if (idxRemove != uint.MaxValue) m_listHistory.RemoveAt(idxRemove);
if (idxRemove != uint.MaxValue) m_lHistory.RemoveAt(idxRemove);
}
public bool GetAutoTypeEnabled()
{
if (!m_listAutoType.Enabled) return false;
if (!m_cfgAutoType.Enabled) return false;
if (m_pParentGroup != null)
return m_pParentGroup.GetAutoTypeEnabledInherited();
@@ -759,7 +791,7 @@ namespace KeePassLib
public string GetAutoTypeSequence()
{
string strSeq = m_listAutoType.DefaultSequence;
string strSeq = m_cfgAutoType.DefaultSequence;
PwGroup pg = m_pParentGroup;
while (pg != null)
@@ -785,69 +817,67 @@ namespace KeePassLib
}
/// <summary>
/// Approximate the total size of this entry in bytes (including
/// strings, binaries and history entries).
/// Approximate the total size (in process memory) of this entry
/// in bytes (including strings, binaries and history entries).
/// </summary>
/// <returns>Size in bytes.</returns>
public ulong GetSize()
{
ulong uSize = 128; // Approx fixed length data
// This method assumes 64-bit pointers/references and Unicode
// strings (i.e. 2 bytes per character)
foreach (KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
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)
{
uSize += (ulong)kvpStr.Key.Length;
uSize += (ulong)kvpStr.Value.Length;
cc += (ulong)kvpBin.Key.Length;
cb += (ulong)kvpBin.Value.Length;
}
foreach (KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{
uSize += (ulong)kvpBin.Key.Length;
uSize += kvpBin.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;
uSize += (ulong)m_listAutoType.DefaultSequence.Length;
foreach (AutoTypeAssociation a in m_listAutoType.Associations)
{
uSize += (ulong)a.WindowName.Length;
uSize += (ulong)a.Sequence.Length;
}
cb += (ulong)m_lHistory.UCount * 8;
foreach (PwEntry peHistory in m_lHistory)
cb += peHistory.GetSize();
foreach (PwEntry peHistory in m_listHistory)
uSize += peHistory.GetSize();
cc += (ulong)m_strOverrideUrl.Length;
uSize += (ulong)m_strOverrideUrl.Length;
foreach (string strTag in m_vTags)
uSize += (ulong)strTag.Length;
cb += (ulong)m_lTags.Count * 8;
foreach (string strTag in m_lTags)
cc += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
foreach (KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize;
return (cb + (cc << 1));
}
public bool HasTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return true;
}
return false;
// this.Tags normalizes
return this.Tags.Contains(StrUtil.NormalizeTag(strTag));
}
public bool AddTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return false;
}
strTag = StrUtil.NormalizeTag(strTag);
if (this.Tags.Contains(strTag)) return false; // this.Tags normalizes
m_vTags.Add(strTag);
m_lTags.Add(strTag);
return true;
}
@@ -855,16 +885,17 @@ namespace KeePassLib
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
{
m_vTags.RemoveAt(i);
return true;
}
}
// this.Tags normalizes
return this.Tags.Remove(StrUtil.NormalizeTag(strTag));
}
return false;
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;
}
public bool IsContainedIn(PwGroup pgContainer)
@@ -886,10 +917,8 @@ namespace KeePassLib
if (bAlsoChangeHistoryUuids)
{
foreach (PwEntry peHist in m_listHistory)
{
foreach (PwEntry peHist in m_lHistory)
peHist.Uuid = pwNewUuid;
}
}
}

View File

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

View File

@@ -0,0 +1,372 @@
/*
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,8 +24,11 @@ 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);
@@ -41,7 +44,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_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
m_strFileSaveFailed2 = TryGetEx(dictNew, "FileSaveFailed2", m_strFileSaveFailed2);
m_strFileSigInvalid = TryGetEx(dictNew, "FileSigInvalid", m_strFileSigInvalid);
m_strFileUnknownCipher = TryGetEx(dictNew, "FileUnknownCipher", m_strFileUnknownCipher);
m_strFileUnknownCompression = TryGetEx(dictNew, "FileUnknownCompression", m_strFileUnknownCompression);
@@ -55,12 +58,18 @@ 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);
@@ -68,6 +77,9 @@ namespace KeePassLib.Resources
}
private static readonly string[] m_vKeyNames = {
"AlgorithmUnknown",
"CharSetInvalid",
"CharSetTooFewChars",
"CryptoStreamFailed",
"EncDataTooLarge",
"ErrorInClipboard",
@@ -83,7 +95,7 @@ namespace KeePassLib.Resources
"FileNewVerOrPlgReq",
"FileNewVerReq",
"FileSaveCorruptionWarning",
"FileSaveFailed",
"FileSaveFailed2",
"FileSigInvalid",
"FileUnknownCipher",
"FileUnknownCompression",
@@ -97,12 +109,18 @@ namespace KeePassLib.Resources
"KeePass1xHint",
"KeyBits",
"KeyFileDbSel",
"KeyHashMismatch",
"MasterSeedLengthInvalid",
"OldFormat",
"Passive",
"PathBackslash",
"PatternInvalid",
"PreAuth",
"PwGenFailed",
"StructsTooDeep",
"Timeout",
"TryAgainSecs",
"UnknownError",
"UnknownHeaderId",
"UnknownKdf",
"UserAccountKeyError",
@@ -114,6 +132,39 @@ 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>
@@ -279,15 +330,15 @@ namespace KeePassLib.Resources
get { return m_strFileSaveCorruptionWarning; }
}
private static string m_strFileSaveFailed =
@"Failed to save the current database to the specified location!";
private static string m_strFileSaveFailed2 =
@"Failed to save to the specified file!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to save the current database to the specified location!'.
/// 'Failed to save to the specified file!'.
/// </summary>
public static string FileSaveFailed
public static string FileSaveFailed2
{
get { return m_strFileSaveFailed; }
get { return m_strFileSaveFailed2; }
}
private static string m_strFileSigInvalid =
@@ -346,10 +397,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
{
@@ -368,10 +419,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKey =
@"The composite key is invalid!";
@"The master key is invalid!";
/// <summary>
/// Look up a localized string similar to
/// 'The composite key is invalid!'.
/// 'The master key is invalid!'.
/// </summary>
public static string InvalidCompositeKey
{
@@ -379,10 +430,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKeyHint =
@"Make sure the composite key is correct and try again.";
@"Make sure that the master key is correct and try it again.";
/// <summary>
/// Look up a localized string similar to
/// 'Make sure the composite key is correct and try again.'.
/// 'Make sure that the master key is correct and try it again.'.
/// </summary>
public static string InvalidCompositeKeyHint
{
@@ -433,6 +484,17 @@ 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>
@@ -466,6 +528,28 @@ 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>
@@ -477,6 +561,28 @@ 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>
@@ -499,6 +605,17 @@ 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-2017 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -33,11 +33,11 @@ using KeePassLibSD;
namespace KeePassLib.Security
{
/// <summary>
/// Represents an in-memory encrypted string.
/// A string that is protected in process memory.
/// <c>ProtectedString</c> objects are immutable and thread-safe.
/// </summary>
#if (DEBUG && !KeePassLibSD)
[DebuggerDisplay(@"{ReadString()}")]
[DebuggerDisplay("{ReadString()}")]
#endif
public sealed class ProtectedString
{
@@ -48,11 +48,24 @@ namespace 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.
@@ -66,8 +79,8 @@ namespace KeePassLib.Security
{
get
{
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return (pBin.Length == 0);
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return (p.Length == 0);
Debug.Assert(m_strPlainText != null);
return (m_strPlainText.Length == 0);
@@ -75,18 +88,21 @@ namespace 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 pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null)
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null)
{
byte[] pbPlain = pBin.ReadData();
m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain);
MemUtil.ZeroByteArray(pbPlain);
byte[] pbPlain = p.ReadData();
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
finally { MemUtil.ZeroByteArray(pbPlain); }
}
else
{
@@ -140,53 +156,51 @@ namespace KeePassLib.Security
/// to the value passed in the <c>XorredBuffer</c> object.
/// </summary>
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xbProtected"><c>XorredBuffer</c> object containing the
/// <param name="xb"><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 xbProtected)
public ProtectedString(bool bEnableProtection, XorredBuffer xb)
{
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
if (xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
byte[] pb = xb.ReadPlainText();
try { Init(bEnableProtection, pb); }
finally { 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;
// The string already is in memory and immutable,
// As 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 normal string object.
/// Be careful with this function, the returned string object
/// Convert the protected string to a standard string object.
/// Be careful with this function, as the returned string object
/// isn't protected anymore and stored in plain-text in the
/// process memory.
/// </summary>
/// <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 :
@@ -194,82 +208,120 @@ 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
// there's no need to protect it anymore (strings are
// immutable and thus cannot be overwritten)
m_strPlainText = str;
m_pbUtf8 = null; // Thread-safe order
return str;
}
/// <summary>
/// Read out the string and return it as a char array.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text char array.</returns>
public char[] ReadChars()
{
if (m_strPlainText != null) return m_strPlainText.ToCharArray();
byte[] pb = ReadUtf8();
char[] v;
try { v = StrUtil.Utf8.GetChars(pb); }
finally { MemUtil.ZeroByteArray(pb); }
return v;
}
/// <summary>
/// Read out the string and return a byte array that contains the
/// string encoded using UTF-8. The returned string is not protected
/// anymore!
/// string encoded using UTF-8.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text UTF-8 byte array.</returns>
public byte[] ReadUtf8()
{
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return pBin.ReadData();
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return p.ReadData();
return StrUtil.Utf8.GetBytes(m_strPlainText);
}
/// <summary>
/// Read the protected string and return it protected with a sequence
/// of bytes generated by a random stream.
/// Get the string as an UTF-8 sequence xorred with bytes
/// from a <c>CryptoRandomStream</c>.
/// </summary>
/// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected string.</returns>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
if (crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
byte[] pbData = ReadUtf8();
uint uLen = (uint)pbData.Length;
int cb = pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == pbData.Length);
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
Debug.Assert(pbPad.Length == cb);
for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i];
for (int i = 0; i < cb; ++i)
pbData[i] ^= pbPad[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);
if(bProtect) MemUtil.ZeroByteArray(pb);
return ps;
// No need to clear pb; either the current or the new object is unprotected
return new ProtectedString(bProtect, pb);
}
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
{
if (ps == null) throw new ArgumentNullException("ps");
if (object.ReferenceEquals(this, ps)) return true; // Perf. opt.
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
if (bCheckProtEqual && (bPA != bPB)) return false;
if (!bPA && !bPB) return (ReadString() == ps.ReadString());
byte[] pbA = ReadUtf8(), pbB = null;
bool bEq;
try
{
pbB = ps.ReadUtf8();
bEq = MemUtil.ArraysEqual(pbA, pbB);
}
finally
{
if (bPA) MemUtil.ZeroByteArray(pbA);
if (bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
}
return bEq;
}
public ProtectedString Insert(int iStart, string strInsert)
{
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;
// 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)
if (!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert(
iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if(iStart > v.Length)
if (iStart > v.Length)
throw new ArgumentOutOfRangeException("iStart");
char[] vIns = strInsert.ToCharArray();
@@ -279,68 +331,104 @@ 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);
MemUtil.ZeroByteArray(pb);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
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;
// 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)
if (!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove(
iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if((iStart + nCount) > v.Length)
throw new ArgumentException("iStart + nCount");
if ((iStart + nCount) > v.Length)
throw new ArgumentException("(iStart + nCount) > v.Length");
vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount));
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
}
finally
{
MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
{
if (a == null) throw new ArgumentNullException("a");
if (b == null) throw new ArgumentNullException("b");
if (b.IsEmpty) return a.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-2017 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -20,97 +20,90 @@
using System;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Security
{
/// <summary>
/// Represents an object that is encrypted using a XOR pad until
/// it is read. <c>XorredBuffer</c> objects are immutable and
/// thread-safe.
/// A <c>XorredBuffer</c> object stores data that is encrypted
/// using a XOR pad.
/// </summary>
public sealed class XorredBuffer
public sealed class XorredBuffer : IDisposable
{
private byte[] m_pbData; // Never null
private byte[] m_pbXorPad; // Always valid for m_pbData
private byte[] m_pbCT;
private byte[] m_pbXorPad;
/// <summary>
/// Length of the protected data in bytes.
/// </summary>
public uint Length
{
get { return (uint)m_pbData.Length; }
get
{
if (m_pbCT == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
return (uint)m_pbCT.Length;
}
}
/// <summary>
/// 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.
/// 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.
/// The <c>XorredBuffer</c> object takes ownership of the two byte
/// arrays, i.e. the caller must not use or modify them afterwards.
/// arrays, i.e. the caller must not use them afterwards.
/// </summary>
/// <param name="pbProtectedData">Protected data (XOR pad applied).</param>
/// <param name="pbCT">Data with XOR pad applied.</param>
/// <param name="pbXorPad">XOR pad that can be used to decrypt the
/// <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)
/// <paramref name="pbCT" /> byte array.</param>
public XorredBuffer(byte[] pbCT, byte[] pbXorPad)
{
if(pbProtectedData == null) { Debug.Assert(false); throw new ArgumentNullException("pbProtectedData"); }
if(pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("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");
}
Debug.Assert(pbProtectedData.Length == pbXorPad.Length);
if(pbProtectedData.Length != pbXorPad.Length) throw new ArgumentException();
m_pbData = pbProtectedData;
m_pbCT = pbCT;
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>Unprotected plain-text byte array.</returns>
/// <returns>Plain-text byte array.</returns>
public byte[] ReadPlainText()
{
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)
byte[] pbCT = m_pbCT, pbX = m_pbXorPad;
if ((pbCT == null) || (pbX == null) || (pbCT.Length != pbX.Length))
{
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;
Debug.Assert(false);
throw new ObjectDisposedException(null);
}
return true;
byte[] pbPT = new byte[pbCT.Length];
for (int i = 0; i < pbPT.Length; ++i)
pbPT[i] = (byte)(pbCT[i] ^ pbX[i]);
return pbPT;
}
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-2016 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -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,7 +91,8 @@ namespace KeePassLib.Serialization
m_format = fmt;
m_slLogger = slLogger;
m_pbsBinaries.Clear();
// Other applications might not perform a deduplication
m_pbsBinaries = new ProtectedBinarySet(false);
UTF8Encoding encNoBom = StrUtil.Utf8;
byte[] pbCipherKey = null;
@@ -103,153 +104,156 @@ 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);
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);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
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);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
lStreams.Add(sDecrypted);
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);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, strIncomplete);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
lStreams.Add(sDecrypted);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
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();
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);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
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();
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);
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
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();
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if ((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
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"); }
lStreams.Add(sPlain);
if(fmt == KdbxFormat.Default)
{
if(m_pbInnerRandomStreamKey == null)
{
Debug.Assert(false);
throw new SecurityException("Invalid inner random stream key!");
}
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);
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();
#warning XML output is enabled!
/* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
FileAccess.Write, FileShare.None))
{
while(true)
{
int b = sXml.ReadByte();
if(b == -1) throw new EndOfStreamException();
fsOut.WriteByte((byte)b);
}
} */
#endif
var stopWatch = Stopwatch.StartNew();
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
var stopWatch = Stopwatch.StartNew();
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
}
else
{
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
ReadXmlStreamed(sXml, sHashing);
}
else
{
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);
ReadXmlStreamed(sXml, sHashing);
CommonCleanUpRead(lStreams, 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);
CommonCleanUpRead(lStreams, sHashing);
}
#if KDBX_BENCHMARK
swTime.Stop();
@@ -267,9 +271,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
@@ -282,7 +286,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;
@@ -303,25 +307,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;
@@ -335,23 +339,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
@@ -375,7 +379,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;
@@ -389,7 +393,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);
@@ -429,8 +433,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;
}
@@ -443,28 +447,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
@@ -481,15 +485,16 @@ 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:
@@ -502,7 +507,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);
@@ -511,7 +516,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;
@@ -520,12 +525,35 @@ 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)
{
@@ -537,81 +565,14 @@ 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)
{
List<PwEntry> lEntries = new List<PwEntry>();
/* KdbxFile f = new KdbxFile(pwDatabase);
if(msData == null) { Debug.Assert(false); return lEntries; }
f.m_format = KdbxFormat.PlainXml;
if (msData == null) { Debug.Assert(false); return new List<PwEntry>(); }
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;
PwGroup pg = ReadGroup(msData, pdContext, bCopyIcons, true, true);
return pg.GetEntries(true).CloneShallowToList();
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -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,8 +48,6 @@ using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Serialization
{
/// <summary>
@@ -81,14 +79,15 @@ 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;
@@ -96,7 +95,7 @@ namespace KeePassLib.Serialization
byte[] pbCipherKey = null;
byte[] pbHmacKey64 = null;
m_pbsBinaries.Clear();
m_pbsBinaries = new ProtectedBinarySet(true);
m_pbsBinaries.AddFrom(pgRoot);
List<Stream> lStreams = new List<Stream>();
@@ -107,6 +106,10 @@ 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;
@@ -118,30 +121,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;
@@ -156,11 +159,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);
@@ -182,22 +185,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
{
@@ -251,6 +254,8 @@ 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
@@ -259,14 +264,13 @@ 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));
@@ -281,7 +285,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));
@@ -295,10 +299,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);
@@ -306,14 +310,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));
@@ -333,12 +337,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");
@@ -361,13 +365,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();
@@ -375,7 +379,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,
@@ -391,7 +395,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);
@@ -401,7 +405,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);
@@ -417,14 +421,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);
@@ -433,7 +437,7 @@ namespace KeePassLib.Serialization
else
{
groupStack.Pop();
if(groupStack.Count <= 0) return false;
if (groupStack.Count <= 0) return false;
EndGroup();
}
@@ -442,23 +446,25 @@ 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 InvalidOperationException();
if (!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new OperationCanceledException();
while(groupStack.Count > 1)
while (groupStack.Count > 1)
{
m_xmlWriter.WriteEndElement();
groupStack.Pop();
@@ -479,11 +485,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);
@@ -497,7 +503,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);
@@ -515,8 +521,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);
@@ -530,10 +536,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);
@@ -541,7 +547,17 @@ namespace KeePassLib.Serialization
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if(pg.CustomData.Count > 0)
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)
WriteList(ElemCustomData, pg.CustomData);
}
@@ -552,31 +568,39 @@ 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();
@@ -585,18 +609,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);
}
@@ -604,19 +628,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));
new KeyValuePair<string, string>(a.WindowName, a.Sequence), null);
m_xmlWriter.WriteEndElement();
}
@@ -624,7 +648,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);
@@ -636,17 +660,17 @@ namespace KeePassLib.Serialization
WriteObject(ElemUsageCount, times.UsageCount);
WriteObject(ElemLocationChanged, times.LocationChanged);
m_xmlWriter.WriteEndElement(); // Name
m_xmlWriter.WriteEndElement();
}
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();
@@ -655,11 +679,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();
@@ -685,31 +709,45 @@ 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)
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp);
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);
}
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 pwci in m_pwDatabase.CustomIcons)
foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
{
m_xmlWriter.WriteStartElement(ElemCustomIconItem);
WriteObject(ElemCustomIconItemID, pwci.Uuid);
WriteObject(ElemCustomIconItemID, ci.Uuid);
string strData = Convert.ToBase64String(pwci.ImageDataPng);
string strData = Convert.ToBase64String(ci.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();
}
@@ -724,7 +762,7 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(name);
if(bFilterValueXmlChars)
if (bFilterValueXmlChars)
m_xmlWriter.WriteString(StrUtil.SafeXmlString(value));
else m_xmlWriter.WriteString(value);
@@ -741,7 +779,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);
}
@@ -788,7 +826,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);
@@ -805,29 +843,33 @@ 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)
private void WriteObject(string name, string strKeyName, string strValueName,
KeyValuePair<string, string> kvp, DateTime? odtLastMod)
{
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);
@@ -836,30 +878,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[] pbEncoded = value.ReadXorredString(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
byte[] pbEnc = value.ReadXorredString(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
}
else
{
@@ -869,25 +911,24 @@ namespace KeePassLib.Serialization
// string transformation here. By default, language-dependent conversions
// should be applied, otherwise characters could be rendered incorrectly
// (code page problems).
if(m_bLocalizedNames)
if (g_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))
{
System.Globalization.UnicodeCategory cat =
CharUnicodeInfo.GetUnicodeCategory(ch);
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
@@ -907,7 +948,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));
@@ -920,7 +961,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);
@@ -929,13 +970,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);
@@ -945,17 +986,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[] pbEncoded = value.ReadXorredData(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
byte[] pbEnc = value.ReadXorredData(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
}
else
{
if(m_pwDatabase.Compression != PwCompressionAlgorithm.None)
if (m_pwDatabase.Compression != PwCompressionAlgorithm.None)
{
m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue);
@@ -963,18 +1004,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);
}
}
}
@@ -982,7 +1023,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);
@@ -995,7 +1036,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,
@@ -1007,6 +1048,25 @@ 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)
{
@@ -1014,66 +1074,21 @@ 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; }
/* 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; }
PwGroup pg = new PwGroup(true, true);
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);
}
KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null);
WriteGroup(msOutput, pdContext, pg);
return true;
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-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
@@ -67,47 +67,51 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
/// <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;
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>
@@ -119,16 +123,17 @@ namespace KeePassLib.Serialization
internal const uint FileSignature2 = 0xB54BFB67;
/// <summary>
/// File version of files saved by the current <c>KdbxFile</c> class.
/// Maximum supported version of database files.
/// 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 = 0x00040000;
private const uint FileVersion32 = 0x00040001;
public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
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
private const uint FileVersionCriticalMask = 0xFFFF0000;
@@ -143,7 +148,7 @@ namespace KeePassLib.Serialization
private const string ElemMeta = "Meta";
private const string ElemRoot = "Root";
private const string ElemGroup = "Group";
private const string ElemEntry = "Entry";
internal const string ElemEntry = "Entry";
private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash";
@@ -188,12 +193,13 @@ namespace KeePassLib.Serialization
private const string ElemName = "Name";
private const string ElemNotes = "Notes";
private const string ElemUuid = "UUID";
internal 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";
@@ -205,6 +211,8 @@ 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";
@@ -261,7 +269,7 @@ namespace KeePassLib.Serialization
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private byte[] m_pbInnerRandomStreamKey = null;
private ProtectedBinarySet m_pbsBinaries = new ProtectedBinarySet();
private ProtectedBinarySet m_pbsBinaries = null;
private byte[] m_pbHashOfHeader = null;
private byte[] m_pbHashOfFileOnDisk = null;
@@ -271,7 +279,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 m_bLocalizedNames = false;
private static bool g_bLocalizedNames = false;
private enum KdbxHeaderFieldID : byte
{
@@ -345,7 +353,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;
}
@@ -356,54 +364,92 @@ namespace KeePassLib.Serialization
public static void DetermineLanguageId()
{
// Test if localized names should be used. If localized names are used,
// the m_bLocalizedNames value must be set to true. By default, localized
// names should be used! (Otherwise characters could be corrupted
// the g_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;
m_bLocalizedNames = (uTest != NeutralLanguageID);
g_bLocalizedNames = (uTest != NeutralLanguageID);
}
}
private uint GetMinKdbxVersion()
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()
{
if(m_uForceVersion != 0) return m_uForceVersion;
if (m_uForceVersion != 0) return m_uForceVersion;
// 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);
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
uint uMin = 0;
if(m_pwDatabase.PublicCustomData.Count > 0)
return Math.Max(FileVersion32, minVersionForKeys);
bool bCustomData = false;
GroupHandler gh = delegate(PwGroup pg)
GroupHandler gh = delegate (PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return true; }
if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
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);
return true;
};
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
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);
return true;
};
gh(m_pwDatabase.RootGroup);
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(bCustomData)
return Math.Max(FileVersion32, minVersionForKeys);
return Math.Max(FileVersion32_3, minVersionForKeys); ; // KDBX 3.1 is sufficient
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
}
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
@@ -413,12 +459,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);
@@ -428,10 +474,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);
@@ -439,7 +485,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);
}
@@ -451,19 +497,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
{
@@ -478,13 +524,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);
}
@@ -494,7 +540,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);
}
@@ -505,7 +551,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
@@ -513,14 +559,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
@@ -528,18 +574,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; }
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
strName = UrlUtil.GetSafeFileName(strName);
string strPath;
int iTry = 1;
@@ -547,31 +593,23 @@ namespace KeePassLib.Serialization
{
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
string strExt = UrlUtil.GetExtension(strName);
string strDesc = UrlUtil.StripExtension(strName);
string strExt = UrlUtil.GetExtension(strName);
strPath += strDesc;
if(iTry > 1)
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();
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
try { File.WriteAllBytes(strPath, pbData); }
finally { if (pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
}
}
}

View File

@@ -26,11 +26,11 @@ namespace KeePassLib.Serialization
/// <summary>
/// Determines whether the database pointed to by the specified ioc should be (de)serialised in default (xml) or protocol buffers format.
/// </summary>
public static KdbxFormat GetFormatToUse(IOConnectionInfo ioc)
public static KdbxFormat GetFormatToUse(string fileExt)
{
// If the filename ends in .kdbp, use ProtocolBuffers format.
return UrlUtil.GetExtension(UrlUtil.GetFileName(ioc.Path)).Equals(KdbpFile.FileNameExtension, StringComparison.OrdinalIgnoreCase) ? KdbxFormat.ProtocolBuffers : KdbxFormat.Default;
}
return fileExt.Equals(KdbpFile.FileNameExtension, StringComparison.OrdinalIgnoreCase) ? KdbxFormat.ProtocolBuffers :
(fileExt.Equals("xml", StringComparison.OrdinalIgnoreCase) ? KdbxFormat.PlainXml : 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
{
internal static readonly byte[] EmptyByteArray = new byte[0];
public static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
@@ -786,5 +786,28 @@ 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.FileSaveFailed;
str += KLRes.FileSaveFailed2;
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-2017 Dominik Reichl <dominik.reichl@t-online.de>
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
@@ -20,8 +20,10 @@
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;
@@ -33,9 +35,7 @@ namespace KeePassLib.Utility
/// </summary>
public static class UrlUtil
{
private static readonly char[] m_vDirSeps = new char[] {
'\\', '/', UrlUtil.LocalDirSepChar };
private static readonly char[] m_vPathTrimCharsWs = new char[] {
private static readonly char[] g_vPathTrimCharsWs = new char[] {
'\"', ' ', '\t', '\r', '\n' };
public static char LocalDirSepChar
@@ -43,6 +43,32 @@ 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:
@@ -63,16 +89,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(m_vDirSeps);
if(nLastSep < 0) return string.Empty; // No directory
int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
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] == '/'));
}
@@ -87,12 +113,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(m_vDirSeps);
int nLastSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
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);
}
@@ -104,12 +130,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(m_vDirSeps);
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
int nLastExtDot = strPath.LastIndexOf('.');
if(nLastExtDot <= nLastDirSep) return strPath;
if (nLastExtDot <= nLastDirSep) return strPath;
return strPath.Substring(0, nLastExtDot);
}
@@ -121,13 +147,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(m_vDirSeps);
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
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);
}
@@ -142,19 +168,16 @@ 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;
for(int i = 0; i < m_vDirSeps.Length; ++i)
{
if(chLast == m_vDirSeps[i]) return strPath;
}
if(bUrl) return (strPath + '/');
if (bUrl) return (strPath + '/');
return (strPath + UrlUtil.LocalDirSepChar);
}
@@ -216,55 +239,78 @@ 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; }
// 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;
if (strPath == null) { Debug.Assert(false); return string.Empty; }
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 = str.IndexOf('\"', 1);
if(iSecond <= 0) return str;
int iSecond = IndexOfSecondEnclQuote(str);
if (iSecond <= 0) return str;
return str.Substring(1, iSecond - 1);
}
public static string FileUrlToPath(string strUrl)
{
Debug.Assert(strUrl != null);
if(strUrl == null) throw new ArgumentNullException("strUrl");
if (strUrl == null) { Debug.Assert(false); throw new ArgumentNullException("strUrl"); }
if (strUrl.Length == 0) { Debug.Assert(false); return string.Empty; }
string str = strUrl;
if(str.StartsWith(@"file:///", StrUtil.CaseIgnoreCmp))
str = str.Substring(8, str.Length - 8);
if (!strUrl.StartsWith(Uri.UriSchemeFile + ":", StrUtil.CaseIgnoreCmp))
{
Debug.Assert(false);
return strUrl;
}
str = str.Replace('/', UrlUtil.LocalDirSepChar);
try
{
Uri uri = new Uri(strUrl);
string str = uri.LocalPath;
if (!string.IsNullOrEmpty(str)) return str;
}
catch (Exception) { Debug.Assert(false); }
return str;
Debug.Assert(false);
return strUrl;
}
public static bool UnhideFile(string strFile)
{
#if (KeePassLibSD || KeePassRT)
#if KeePassLibSD
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
@@ -272,26 +318,26 @@ namespace KeePassLib.Utility
public static bool HideFile(string strFile, bool bHide)
{
#if (KeePassLibSD || KeePassRT)
#if KeePassLibSD
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
@@ -299,48 +345,47 @@ 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())
#endif
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(m_vDirSeps);
string[] vTarget = strTarget.Split(m_vDirSeps);
string[] vBase = strBase.Split(UrlUtil.DirSepChars);
string[] vTarget = strTarget.Split(UrlUtil.DirSepChars);
int i = 0;
while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
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]);
}
@@ -352,28 +397,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) == false)
if (!NativeMethods.PathRelativePathTo(sb, strBaseFile, 0,
strTargetFile, 0))
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);
@@ -381,55 +426,56 @@ 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];
Debug.Assert(Array.IndexOf<char>(m_vDirSeps, chSep) >= 0);
char[] vSep = ((chSep == '/') ? (new char[] { '/' }) :
(new char[] { '\\', '/' }));
List<string> l = new List<string>();
#if !KeePassLibSD
string[] v = strPath.Split(m_vDirSeps, StringSplitOptions.None);
string[] v = strPath.Split(vSep, StringSplitOptions.None);
#else
string[] v = strPath.Split(m_vDirSeps);
string[] v = strPath.Split(vSep);
#endif
Debug.Assert((v.Length >= 3) && (v[0].Length == 0) &&
(v[1].Length == 0));
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]);
}
@@ -438,20 +484,11 @@ namespace KeePassLib.Utility
}
string str;
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; }
try { str = Path.GetFullPath(strPath); }
catch (Exception) { Debug.Assert(false); return strPath; }
Debug.Assert(str.IndexOf("\\..\\") < 0);
foreach(char ch in m_vDirSeps)
Debug.Assert((str.IndexOf("\\..\\") < 0) || NativeLib.IsUnix());
foreach (char ch in UrlUtil.DirSepChars)
{
string strSep = new string(ch, 1);
str = str.Replace(strSep + "." + strSep, strSep);
@@ -462,17 +499,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;
@@ -481,24 +518,30 @@ 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 nNetScheme = strUrl.IndexOf(@"://", StrUtil.CaseIgnoreCmp);
int nShScheme = strUrl.IndexOf(@":/", StrUtil.CaseIgnoreCmp);
int nSmpScheme = strUrl.IndexOf(@":", StrUtil.CaseIgnoreCmp);
int i = strUrl.IndexOf(':');
if (i < 0) return strUrl; // No scheme to remove
++i;
if((nNetScheme < 0) && (nShScheme < 0) && (nSmpScheme < 0))
return strUrl; // No scheme
// A single '/' indicates a path (absolute) and should not be removed
if (((i + 1) < strUrl.Length) && (strUrl[i] == '/') &&
(strUrl[i + 1] == '/'))
i += 2; // Skip authority prefix
int nMin = Math.Min(Math.Min((nNetScheme >= 0) ? nNetScheme : int.MaxValue,
(nShScheme >= 0) ? nShScheme : int.MaxValue),
(nSmpScheme >= 0) ? nSmpScheme : int.MaxValue);
if(nMin == nNetScheme) return strUrl.Substring(nMin + 3);
if(nMin == nShScheme) return strUrl.Substring(nMin + 2);
return strUrl.Substring(nMin + 1);
return strUrl.Substring(i);
}
public static string ConvertSeparators(string strPath)
@@ -508,7 +551,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);
@@ -518,33 +561,61 @@ 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(strName == null) { Debug.Assert(false); return string.Empty; }
if (string.IsNullOrEmpty(strName)) { Debug.Assert(false); return string.Empty; }
string str = strName;
// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
str = str.Replace('/', '-');
str = str.Replace('\\', '-');
str = str.Replace(":", string.Empty);
str = str.Replace("*", string.Empty);
str = str.Replace("?", string.Empty);
str = str.Replace("\"", string.Empty);
str = str.Replace(@"'", string.Empty);
str = str.Replace('<', '(');
str = str.Replace('>', ')');
str = str.Replace('|', '-');
StringBuilder sb = new StringBuilder(strName.Length);
foreach (char ch in strName)
{
if (ch < '\u0020') continue;
return str;
switch (ch)
{
case '\"':
case '*':
case ':':
case '?':
break;
case '/':
case '\\':
case '|':
sb.Append('-');
break;
case '<':
sb.Append('(');
break;
case '>':
sb.Append(')');
break;
default: sb.Append(ch); break;
}
}
// Trim trailing spaces and periods
for (int i = sb.Length - 1; i >= 0; --i)
{
char ch = sb[i];
if ((ch == ' ') || (ch == '.')) sb.Remove(i, 1);
else break;
}
return sb.ToString();
}
/// <summary>
/// Get the host component of an URL.
/// Get the host component of a URL.
/// This method is faster and more fault-tolerant than creating
/// an <code>Uri</code> object and querying its <code>Host</code>
/// property.
@@ -555,60 +626,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;
}
@@ -619,7 +690,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;
@@ -629,9 +700,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;
}
@@ -642,31 +713,29 @@ 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(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(strPathRaw);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(strPathRaw);
}
}
else l.AddRange(v);
@@ -679,33 +748,31 @@ 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(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(fi);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(fi);
}
}
else l.AddRange(v);
@@ -713,5 +780,82 @@ 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

@@ -0,0 +1,290 @@
/*
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

@@ -0,0 +1,984 @@
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

@@ -0,0 +1,12 @@
<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

@@ -0,0 +1,135 @@
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

@@ -0,0 +1,58 @@
<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

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
{
"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

@@ -0,0 +1,322 @@
{
"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

@@ -0,0 +1,121 @@
{
"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

@@ -0,0 +1,119 @@
{
"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

@@ -0,0 +1,469 @@
{
"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
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
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);
void Lock(bool allowQuickUnlock, bool lockWasTriggeredByTimeout);
/// <summary>
@@ -119,11 +119,11 @@ namespace keepass2android
/// </summary>
IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache);
void TriggerReload(Context context);
void TriggerReload(Context context, Action<bool> actionOnResult /*if not null, called when the user selected yes (true) or no (false)*/);
bool CheckForDuplicateUuids { get; }
#if !NoNet
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
ICertificateErrorHandler CertificateErrorHandler { get; }

View File

@@ -82,7 +82,12 @@ namespace keepass2android.Io
UrlUtil.GetFileName(ioc.Path));
}
public bool RequiresCredentials(IOConnectionInfo ioc)
public string GetFileExtension(IOConnectionInfo ioc)
{
return UrlUtil.GetExtension(ioc.Path);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return false;
}
@@ -244,28 +249,11 @@ namespace keepass2android.Io
reason.Result = UiStringKey.ReadOnlyReason_PreKitKat;
return true;
}
//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());
//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;
}
catch (Exception e)
{
@@ -328,12 +316,13 @@ namespace keepass2android.Io
public void CommitWrite()
{
ParcelFileDescriptor fileDescriptor = _ctx.ContentResolver.OpenFileDescriptor(Android.Net.Uri.Parse(_path), "w");
ParcelFileDescriptor fileDescriptor = _ctx.ContentResolver.OpenFileDescriptor(Android.Net.Uri.Parse(_path), "rwt");
using (var outputStream = new FileOutputStream(fileDescriptor.FileDescriptor))
{
byte[] data = _memoryStream.ToArray();
outputStream.Write(data, 0, data.Length);
outputStream.Write(data);
outputStream.Close();
}
fileDescriptor.Close();

View File

@@ -12,13 +12,10 @@ using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Preferences;
using Android.Support.V13.App;
using Android.Support.V4.App;
using Java.IO;
using Java.Util;
using Android.Support.V4;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using ActivityCompat = Android.Support.V13.App.ActivityCompat;
using File = System.IO.File;
using FileNotFoundException = System.IO.FileNotFoundException;
using IOException = System.IO.IOException;
@@ -204,7 +201,12 @@ namespace keepass2android.Io
}
public bool RequiresCredentials(IOConnectionInfo ioc)
public string GetFileExtension(IOConnectionInfo ioc)
{
return UrlUtil.GetExtension(ioc.Path);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return (!ioc.IsLocalFile()) && (ioc.CredSaveMode != IOCredSaveMode.SaveCred);
}
@@ -244,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,10 +120,14 @@ namespace keepass2android.Io
public bool IsCached(IOConnectionInfo ioc)
{
return File.Exists(CachedFilePath(ioc))
bool result = 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)
{
@@ -448,7 +452,12 @@ namespace keepass2android.Io
return _cachedStorage.GetFilenameWithoutPathAndExt(ioc);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
public string GetFileExtension(IOConnectionInfo ioc)
{
return _cachedStorage.GetFileExtension(ioc);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return _cachedStorage.RequiresCredentials(ioc);
}
@@ -588,11 +597,15 @@ namespace keepass2android.Io
public string GetBaseVersionHash(IOConnectionInfo ioc)
{
return File.ReadAllText(BaseVersionFilePath(ioc));
}
string hash = File.ReadAllText(BaseVersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " baseVersionHash = " + hash);
return hash;
}
public string GetLocalVersionHash(IOConnectionInfo ioc)
{
return File.ReadAllText(VersionFilePath(ioc));
string hash = File.ReadAllText(VersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " localVersionHash = " + hash);
return hash;
}
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.GoogleDriveFileStorage(), app)
base(new Keepass2android.Javafilestorage.GoogleDriveFullFileStorage(), app)
{
}
@@ -27,5 +27,19 @@ 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

@@ -88,11 +88,13 @@ namespace keepass2android.Io
IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction);
string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc);
/// <summary>
/// Returns true if the the given ioc must be filled with username/password
/// </summary>
bool RequiresCredentials(IOConnectionInfo ioc);
string GetFileExtension(IOConnectionInfo ioc);
/// <summary>
/// Returns true if the the given ioc must be filled with username/password
/// </summary>
bool RequiresCredentials(IOConnectionInfo ioc);
/// <summary>
/// Creates the directory described by ioc

View File

@@ -128,10 +128,12 @@ namespace keepass2android.Io
}
//creates a local ioc where the sourceIoc can be stored to
public static IOConnectionInfo GetInternalIoc(IOConnectionInfo sourceIoc, Context ctx)
public static IOConnectionInfo GetInternalIoc(IOConnectionInfo sourceIoc, Context ctx, IKp2aApp app)
{
Java.IO.File internalDirectory = IoUtil.GetInternalDirectory(ctx);
string targetPath = UrlUtil.GetFileName(sourceIoc.Path);
var filestorage = app.GetFileStorage(sourceIoc);
string targetPath = filestorage.GetFilenameWithoutPathAndExt(sourceIoc);
targetPath = targetPath.Trim("|\\?*<\":>+[]/'".ToCharArray());
if (targetPath == "")
targetPath = "internal";
@@ -153,7 +155,7 @@ namespace keepass2android.Io
public static IOConnectionInfo ImportFileToInternalDirectory(IOConnectionInfo sourceIoc, Context ctx, IKp2aApp app)
{
var targetIoc = GetInternalIoc(sourceIoc, ctx);
var targetIoc = GetInternalIoc(sourceIoc, ctx, app);
IoUtil.Copy(targetIoc, sourceIoc, app);

View File

@@ -177,7 +177,12 @@ namespace keepass2android.Io
_jfs.GetFilename(IocToPath(ioc)));
}
public bool RequiresCredentials(IOConnectionInfo ioc)
public string GetFileExtension(IOConnectionInfo ioc)
{
return UrlUtil.GetExtension(ioc.Path);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return false;
}

View File

@@ -0,0 +1,506 @@
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,73 +17,6 @@ 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; }
@@ -147,9 +80,8 @@ namespace keepass2android.Io
public NetFtpFileStorage(Context context, ICertificateValidationHandler app)
{
_app = app;
_app = app;
traceStream = new MemoryStream();
FtpTrace.AddListener(new System.Diagnostics.TextWriterTraceListener(traceStream));
}
@@ -172,9 +104,9 @@ namespace keepass2android.Io
{
using (FtpClient client = GetClient(ioc))
{
string localPath = IocToUri(ioc).PathAndQuery;
string localPath = IocToLocalPath(ioc);
if (client.DirectoryExists(localPath))
client.DeleteDirectory(localPath, true);
client.DeleteDirectory(localPath);
else
client.DeleteFile(localPath);
}
@@ -205,7 +137,8 @@ namespace keepass2android.Io
{
var settings = ConnectionSettings.FromIoc(ioc);
FtpClient client = new RetryConnectFtpClient();
FtpClient client = new FtpClient();
client.RetryAttempts = 3;
if ((settings.Username.Length > 0) || (settings.Password.Length > 0))
client.Credentials = new NetworkCredential(settings.Username, settings.Password);
else
@@ -250,20 +183,22 @@ namespace keepass2android.Io
path = path.Substring(settings.Length + 1);
}
return new Uri(scheme + "://" + path);
Kp2aLog.Log("FTP: IocToUri out = " + scheme + "://" + path);
return new Uri(scheme + "://" + path);
}
private string IocPathFromUri(IOConnectionInfo baseIoc, Uri uri)
private string IocPathFromUri(IOConnectionInfo baseIoc, string 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));
return scheme + "://" + baseSettings + ConnectionSettings.SettingsPostFix + baseHost + uri.AbsolutePath; //TODO does this contain Query?
}
string result = scheme + "://" + baseSettings + ConnectionSettings.SettingsPostFix + baseHost + uri; //TODO does this contain Query?
return result;
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
@@ -281,9 +216,12 @@ namespace keepass2android.Io
try
{
using (var cl = GetClient(ioc))
{
return cl.OpenRead(IocToUri(ioc).PathAndQuery, FtpDataType.Binary, 0);
}
{
var memStream = new MemoryStream();
cl.OpenRead(IocToLocalPath(ioc), FtpDataType.Binary, 0).CopyTo(memStream);
memStream.Seek(0, SeekOrigin.Begin);
return memStream;
}
}
catch (FtpCommandException ex)
{
@@ -314,7 +252,12 @@ namespace keepass2android.Io
UrlUtil.GetFileName(ioc.Path));
}
public bool RequiresCredentials(IOConnectionInfo ioc)
public string GetFileExtension(IOConnectionInfo ioc)
{
return UrlUtil.GetExtension(ioc.Path);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return false;
}
@@ -325,7 +268,7 @@ namespace keepass2android.Io
{
using (var client = GetClient(ioc))
{
client.CreateDirectory(IocToUri(GetFilePath(ioc, newDirName)).PathAndQuery);
client.CreateDirectory(IocToLocalPath(GetFilePath(ioc, newDirName)));
}
}
catch (FtpCommandException ex)
@@ -334,14 +277,19 @@ namespace keepass2android.Io
}
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
public static string IocToLocalPath(IOConnectionInfo ioc)
{
return WebUtility.UrlDecode(IocToUri(ioc).PathAndQuery);
}
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(IocToUri(ioc).PathAndQuery,
foreach (FtpListItem item in client.GetListing(IocToLocalPath(ioc),
FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
{
@@ -355,7 +303,7 @@ namespace keepass2android.Io
DisplayName = item.Name,
IsDirectory = true,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, new Uri(item.FullName))
Path = IocPathFromUri(ioc, item.FullName)
});
break;
case FtpFileSystemObjectType.File:
@@ -366,7 +314,7 @@ namespace keepass2android.Io
DisplayName = item.Name,
IsDirectory = false,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, new Uri(item.FullName)),
Path = IocPathFromUri(ioc, item.FullName),
SizeInBytes = item.Size
});
break;
@@ -391,10 +339,10 @@ namespace keepass2android.Io
//is it very inefficient to connect for each description?
using (FtpClient client = GetClient(ioc))
{
var uri = IocToUri(ioc);
string path = uri.PathAndQuery;
{
string path = IocToLocalPath(ioc);
if (!client.FileExists(path) && (!client.DirectoryExists(path)))
throw new FileNotFoundException();
var fileDesc = new FileDescription()
@@ -506,7 +454,7 @@ namespace keepass2android.Io
{
using (var client = GetClient(ioc))
{
return client.OpenWrite(IocToUri(ioc).PathAndQuery);
return client.OpenWrite(IocToLocalPath(ioc));
}
}
@@ -575,7 +523,7 @@ namespace keepass2android.Io
{
_client = _fileStorage.GetClient(_ioc, false);
_stream = _client.OpenWrite(NetFtpFileStorage.IocToUri(_iocTemp).PathAndQuery);
_stream = _client.OpenWrite(NetFtpFileStorage.IocToLocalPath(_iocTemp));
return _stream;
}
catch (FtpCommandException ex)
@@ -590,22 +538,11 @@ namespace keepass2android.Io
{
Android.Util.Log.Debug("NETFTP","connected: " + _client.IsConnected.ToString());
_stream.Close();
Android.Util.Log.Debug("NETFTP", "connected: " + _client.IsConnected.ToString());
_stream.Dispose();
_client.GetReply();
//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);
_client.MoveFile(NetFtpFileStorage.IocToLocalPath(_iocTemp),
NetFtpFileStorage.IocToLocalPath(_ioc));
}
catch (FtpCommandException ex)

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