Release 2.6.3
Added - Support Argon2id KDF [#5778] - Support XMLv2 key files [#5798] Changed - Improve CSV Import/Export, include time fields and TOTP [#5346] - Support empty area dragging of the application window [#5860] - Display default Auto-Type sequence in preview pane [#5654] - Remove strict length limit on generated passwords [#5748] - Hide key file path by default when unlocking database [#5779] - Document browser extension use with Edge in managed mode [#5692] - Windows: Prevent clipboard history and cloud sync [#5853] - macOS: Update the application icon to Big Sur styling [#5851] Fixed - Re-select previously selected entry on database unlock [#5559] - Properly save special character choice in password generator [#5610] - Fix crash in browser integration with multiple similar entries [#5653] - Remove offset on username field in classic theme [#5788] - Ensure entry history is copied when drag/dropping entries and groups [#5817] - Close modal dialogs when database is locked [#5820] - Prevent crash when KeeShare modifies an entry that is currently being edited [#5827] - Improve preview of entry attributes [#5834] - Always activate/focus database open dialog preventing mistype [#5878] - Reports: fix calculation of average password length [#5862] - Linux: Delay startup on login to correct tray icon issues [#5724]
32
CHANGELOG.md
@ -1,5 +1,37 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2.6.3 (2020-01-12)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support Argon2id KDF [#5778]
|
||||||
|
- Support XMLv2 key files [#5798]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improve CSV Import/Export, include time fields and TOTP [#5346]
|
||||||
|
- Support empty area dragging of the application window [#5860]
|
||||||
|
- Display default Auto-Type sequence in preview pane [#5654]
|
||||||
|
- Remove strict length limit on generated passwords [#5748]
|
||||||
|
- Hide key file path by default when unlocking database [#5779]
|
||||||
|
- Document browser extension use with Edge in managed mode [#5692]
|
||||||
|
- Windows: Prevent clipboard history and cloud sync [#5853]
|
||||||
|
- macOS: Update the application icon to Big Sur styling [#5851]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Re-select previously selected entry on database unlock [#5559]
|
||||||
|
- Properly save special character choice in password generator [#5610]
|
||||||
|
- Fix crash in browser integration with multiple similar entries [#5653]
|
||||||
|
- Remove offset on username field in classic theme [#5788]
|
||||||
|
- Ensure entry history is copied when drag/dropping entries and groups [#5817]
|
||||||
|
- Close modal dialogs when database is locked [#5820]
|
||||||
|
- Prevent crash when KeeShare modifies an entry that is currently being edited [#5827]
|
||||||
|
- Improve preview of entry attributes [#5834]
|
||||||
|
- Always activate/focus database open dialog preventing mistype [#5878]
|
||||||
|
- Reports: fix calculation of average password length [#5862]
|
||||||
|
- Linux: Delay startup on login to correct tray icon issues [#5724]
|
||||||
|
|
||||||
## 2.6.2 (2020-10-21)
|
## 2.6.2 (2020-10-21)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -101,7 +101,7 @@ endif()
|
|||||||
|
|
||||||
set(KEEPASSXC_VERSION_MAJOR "2")
|
set(KEEPASSXC_VERSION_MAJOR "2")
|
||||||
set(KEEPASSXC_VERSION_MINOR "6")
|
set(KEEPASSXC_VERSION_MINOR "6")
|
||||||
set(KEEPASSXC_VERSION_PATCH "2")
|
set(KEEPASSXC_VERSION_PATCH "3")
|
||||||
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
||||||
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
|
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
|
||||||
|
|
||||||
|
@ -23,6 +23,10 @@ You can download the KeePassXC-Browser extension from your web browser. To downl
|
|||||||
|
|
||||||
2. Click the button to install/add the extension to the browser. Accept any confirmation dialogs.
|
2. Click the button to install/add the extension to the browser. Accept any confirmation dialogs.
|
||||||
|
|
||||||
|
// tag::advanced[]
|
||||||
|
NOTE: When Microsoft Edge is installed as a managed application, system administrators are required to deploy a custom native messaging configuration. Instructions for this are found in the advanced section below.
|
||||||
|
// end::advanced[]
|
||||||
|
|
||||||
=== Configure KeePassXC-Browser
|
=== Configure KeePassXC-Browser
|
||||||
To start using KeePassXC-Browser, you must configure it so that it can communicate with the KeePassXC application on your desktop.
|
To start using KeePassXC-Browser, you must configure it so that it can communicate with the KeePassXC application on your desktop.
|
||||||
|
|
||||||
@ -104,5 +108,38 @@ WARNING: We do not recommend changing any of these settings as they may break th
|
|||||||
|
|
||||||
.Advanced browser settings
|
.Advanced browser settings
|
||||||
image::browser_advanced_settings.png[]
|
image::browser_advanced_settings.png[]
|
||||||
|
|
||||||
|
=== Advanced Setup
|
||||||
|
==== Managed Microsoft Edge on Windows
|
||||||
|
1. Deploy *org.keepassxc.keepassxc_browser_edge.json* to, for example, `C:\ProgramData\KeepassXC` on all managed platforms.
|
||||||
|
+
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/"
|
||||||
|
],
|
||||||
|
"description": "KeePassXC integration with native messaging support",
|
||||||
|
"name": "org.keepassxc.keepassxc_browser",
|
||||||
|
"path": "C:\\Program Files\\KeePassXC\\keepassxc-proxy.exe",
|
||||||
|
"type": "stdio"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
2. Configure GPO options (registry result):
|
||||||
|
+
|
||||||
|
----
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Edge\NativeMessagingHosts\org.keepassxc.keepassxc_browser]
|
||||||
|
@="C:\ProgramData\KeepassXC\org.keepassxc.keepassxc_browser_edge.json"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge]
|
||||||
|
"NativeMessagingUserLevelHosts"=dword:00000000
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallAllowlist]
|
||||||
|
"1"="pdffhmdngciaglkoonimfcmckehcpafo"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\NativeMessagingAllowlist]
|
||||||
|
"1"="org.keepassxc.keepassxc_browser"
|
||||||
|
----
|
||||||
// end::advanced[]
|
// end::advanced[]
|
||||||
// end::content[]
|
// end::content[]
|
||||||
|
@ -50,6 +50,33 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.6.3" date="2021-01-12">
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Support Argon2id KDF [#5778]</li>
|
||||||
|
<li>Support XMLv2 key files [#5798]</li>
|
||||||
|
<li>Improve CSV Import/Export, include time fields and TOTP [#5346]</li>
|
||||||
|
<li>Support empty area dragging of the application window [#5860]</li>
|
||||||
|
<li>Display default Auto-Type sequence in preview pane [#5654]</li>
|
||||||
|
<li>Remove strict length limit on generated passwords [#5748]</li>
|
||||||
|
<li>Hide key file path by default when unlocking database [#5779]</li>
|
||||||
|
<li>Document browser extension use with Edge in managed mode [#5692]</li>
|
||||||
|
<li>Windows: Prevent clipboard history and cloud sync [#5853]</li>
|
||||||
|
<li>macOS: Update the application icon to Big Sur styling [#5851]</li>
|
||||||
|
<li>Re-select previously selected entry on database unlock [#5559]</li>
|
||||||
|
<li>Properly save special character choice in password generator [#5610]</li>
|
||||||
|
<li>Fix crash in browser integration with multiple similar entries [#5653]</li>
|
||||||
|
<li>Remove offset on username field in classic theme [#5788]</li>
|
||||||
|
<li>Ensure entry history is copied when drag/dropping entries and groups [#5817]</li>
|
||||||
|
<li>Close modal dialogs when database is locked [#5820]</li>
|
||||||
|
<li>Prevent crash when KeeShare modifies an entry that is currently being edited [#5827]</li>
|
||||||
|
<li>Improve preview of entry attributes [#5834]</li>
|
||||||
|
<li>Always activate/focus database open dialog preventing mistype [#5878]</li>
|
||||||
|
<li>Reports: fix calculation of average password length [#5862]</li>
|
||||||
|
<li>Linux: Delay startup on login to correct tray icon issues [#5724]</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="2.6.2" date="2020-10-21">
|
<release version="2.6.2" date="2020-10-21">
|
||||||
<description>
|
<description>
|
||||||
<ul>
|
<ul>
|
||||||
|
BIN
share/macosx/keepassxc.ai
Executable file
BIN
share/macosx/keepassxc.iconset/icon_128x128.png
Executable file
After Width: | Height: | Size: 9.7 KiB |
BIN
share/macosx/keepassxc.iconset/icon_128x128@2x.png
Executable file
After Width: | Height: | Size: 23 KiB |
BIN
share/macosx/keepassxc.iconset/icon_16x16.png
Executable file
After Width: | Height: | Size: 783 B |
BIN
share/macosx/keepassxc.iconset/icon_16x16@2x.png
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
share/macosx/keepassxc.iconset/icon_256x256.png
Executable file
After Width: | Height: | Size: 23 KiB |
BIN
share/macosx/keepassxc.iconset/icon_256x256@2x.png
Executable file
After Width: | Height: | Size: 58 KiB |
BIN
share/macosx/keepassxc.iconset/icon_32x32.png
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
share/macosx/keepassxc.iconset/icon_32x32@2x.png
Executable file
After Width: | Height: | Size: 4.0 KiB |
BIN
share/macosx/keepassxc.iconset/icon_512x512.png
Executable file
After Width: | Height: | Size: 58 KiB |
BIN
share/macosx/keepassxc.iconset/icon_512x512@2x.png
Executable file
After Width: | Height: | Size: 152 KiB |
@ -245,7 +245,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>(restart program to activate)</source>
|
<source>(restart program to activate)</source>
|
||||||
<translation type="unfinished"/>
|
<translation>(genstart program for at aktivere)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Minimize window after unlocking database</source>
|
<source>Minimize window after unlocking database</source>
|
||||||
|
@ -233,7 +233,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Check for updates at application startup once per week</source>
|
<source>Check for updates at application startup once per week</source>
|
||||||
<translation>Bei Programmstartstart wöchentlich auf Updates prüfen</translation>
|
<translation>Bei Programmstart wöchentlich auf Updates prüfen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Include beta releases when checking for updates</source>
|
<source>Include beta releases when checking for updates</source>
|
||||||
|
@ -1114,6 +1114,14 @@ chrome-laptop.</source>
|
|||||||
<source>Column %1</source>
|
<source>Column %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>TOTP</source>
|
||||||
|
<translation type="unfinished">TOTP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Icon</source>
|
||||||
|
<translation type="unfinished">Icon</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>CsvParserModel</name>
|
<name>CsvParserModel</name>
|
||||||
@ -1218,20 +1226,6 @@ Backup database located at %2</source>
|
|||||||
<source>Refresh</source>
|
<source>Refresh</source>
|
||||||
<translation>Refresh</translation>
|
<translation>Refresh</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Legacy key file format</source>
|
|
||||||
<translation>Legacy key file format</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>You are using a legacy key file format which may become
|
|
||||||
unsupported in the future.
|
|
||||||
|
|
||||||
Please consider generating a new key file.</source>
|
|
||||||
<translation>You are using a legacy key file format which may become
|
|
||||||
unsupported in the future.
|
|
||||||
|
|
||||||
Please consider generating a new key file.</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Don't show this warning again</source>
|
<source>Don't show this warning again</source>
|
||||||
<translation>Don't show this warning again</translation>
|
<translation>Don't show this warning again</translation>
|
||||||
@ -1357,6 +1351,14 @@ If you do not have a key file, please leave the field empty.</source>
|
|||||||
<source>Select hardware key…</source>
|
<source>Select hardware key…</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Old key file format</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You are using an old key file format which KeePassXC may<br>stop supporting in the future.<br><br>Please consider generating a new key file by going to:<br><strong>Database / Database Security / Change Key File.</strong><br></source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>DatabaseSettingWidgetMetaData</name>
|
<name>DatabaseSettingWidgetMetaData</name>
|
||||||
@ -2324,6 +2326,15 @@ Disable safe saves and try again?</translation>
|
|||||||
<source>[PROTECTED] Press Reveal to view or edit</source>
|
<source>[PROTECTED] Press Reveal to view or edit</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Invalid Entry</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>An external merge operation has invalidated this entry.
|
||||||
|
Unfortunately, any changes made have been lost.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>EditEntryWidgetAdvanced</name>
|
<name>EditEntryWidgetAdvanced</name>
|
||||||
@ -3464,11 +3475,6 @@ Are you sure to add this file?</source>
|
|||||||
<source>[PROTECTED]</source>
|
<source>[PROTECTED]</source>
|
||||||
<translation>[PROTECTED]</translation>
|
<translation>[PROTECTED]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source><b>%1</b>: %2</source>
|
|
||||||
<comment>attributes line</comment>
|
|
||||||
<translation><b>%1</b>: %2</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Enabled</source>
|
<source>Enabled</source>
|
||||||
<translation>Enabled</translation>
|
<translation>Enabled</translation>
|
||||||
@ -3489,6 +3495,15 @@ Are you sure to add this file?</source>
|
|||||||
<source>Advanced</source>
|
<source>Advanced</source>
|
||||||
<translation type="unfinished">Advanced</translation>
|
<translation type="unfinished">Advanced</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Default Sequence</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><tr><td><b>%1</b>:</td><td>%2</td></tr></source>
|
||||||
|
<comment>attributes line</comment>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>EntryURLModel</name>
|
<name>EntryURLModel</name>
|
||||||
@ -3522,23 +3537,8 @@ Are you sure to add this file?</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
|
||||||
<name>FdoSecrets::Item</name>
|
|
||||||
<message>
|
|
||||||
<source>Entry "%1" from database "%2" was used by %3</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
<context>
|
||||||
<name>FdoSecrets::Service</name>
|
<name>FdoSecrets::Service</name>
|
||||||
<message numerus="yes">
|
|
||||||
<source>%n Entry(s) was used by %1</source>
|
|
||||||
<comment>%1 is the name of an application</comment>
|
|
||||||
<translation type="unfinished">
|
|
||||||
<numerusform></numerusform>
|
|
||||||
<numerusform></numerusform>
|
|
||||||
</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to register DBus service at %1.<br/></source>
|
<source>Failed to register DBus service at %1.<br/></source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
@ -4362,10 +4362,6 @@ If this reoccurs, then your database file may be corrupt.</source>
|
|||||||
<source><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></source>
|
<source><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></source>
|
||||||
<translation><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></translation>
|
<translation><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Legacy key file format</source>
|
|
||||||
<translation>Legacy key file format</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Error loading the key file '%1'
|
<source>Error loading the key file '%1'
|
||||||
Message: %2</source>
|
Message: %2</source>
|
||||||
@ -4434,10 +4430,11 @@ Are you sure you want to continue with this file?</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You are using a legacy key file format which may become
|
<source>Old key file format</source>
|
||||||
unsupported in the future.
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
Generate a new key file in the database security settings.</source>
|
<message>
|
||||||
|
<source>You selected a key file in an old format which KeePassXC<br>may stop supporting in the future.<br><br>Please consider generating a new key file instead.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
@ -5780,16 +5777,6 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
|||||||
<source>Perform advanced analysis on the password.</source>
|
<source>Perform advanced analysis on the password.</source>
|
||||||
<translation>Perform advanced analysis on the password.</translation>
|
<translation>Perform advanced analysis on the password.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>WARNING: You are using a legacy key file format which may become
|
|
||||||
unsupported in the future.
|
|
||||||
|
|
||||||
Please consider generating a new key file.</source>
|
|
||||||
<translation>WARNING: You are using a legacy key file format which may become
|
|
||||||
unsupported in the future.
|
|
||||||
|
|
||||||
Please consider generating a new key file.</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>
|
<source>
|
||||||
|
|
||||||
@ -6180,10 +6167,6 @@ Available commands:
|
|||||||
<source>%1: (row, col) %2,%3</source>
|
<source>%1: (row, col) %2,%3</source>
|
||||||
<translation>%1: (row, col) %2,%3</translation>
|
<translation>%1: (row, col) %2,%3</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Argon2 (KDBX 4 – recommended)</source>
|
|
||||||
<translation>Argon2 (KDBX 4 – recommended)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>AES-KDF (KDBX 4)</source>
|
<source>AES-KDF (KDBX 4)</source>
|
||||||
<translation>AES-KDF (KDBX 4)</translation>
|
<translation>AES-KDF (KDBX 4)</translation>
|
||||||
@ -6760,10 +6743,6 @@ Kernel: %3 %4</source>
|
|||||||
<source>AES (%1 rounds)</source>
|
<source>AES (%1 rounds)</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Argon2 (%1 rounds, %2 KB)</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>AES 256-bit</source>
|
<source>AES 256-bit</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
@ -6800,6 +6779,45 @@ Kernel: %3 %4</source>
|
|||||||
<source>path to a custom local config file</source>
|
<source>path to a custom local config file</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>WARNING: You are using an old key file format which KeePassXC may
|
||||||
|
stop supporting in the future.
|
||||||
|
|
||||||
|
Please consider generating a new key file.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Argon2%1 (%2 rounds, %3 KB)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Argon2d (KDBX 4 – recommended)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Argon2id (KDBX 4)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>TOTP</source>
|
||||||
|
<translation type="unfinished">TOTP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Icon</source>
|
||||||
|
<translation type="unfinished">Icon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsupported key file version: %1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Checksum mismatch! Key file may be corrupt.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unexpected key file data! Key file may be corrupt.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QtIOCompressor</name>
|
<name>QtIOCompressor</name>
|
||||||
|
@ -2454,7 +2454,7 @@ Disable safe saves and try again?</translation>
|
|||||||
<name>EditEntryWidgetBrowser</name>
|
<name>EditEntryWidgetBrowser</name>
|
||||||
<message>
|
<message>
|
||||||
<source>These settings affect to the entry's behaviour with the browser extension.</source>
|
<source>These settings affect to the entry's behaviour with the browser extension.</source>
|
||||||
<translation>These settings affect to the entry's behavior with the browser extension.</translation>
|
<translation>These settings affect the browser extension’s behavior with regard to this database entry.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>General</source>
|
<source>General</source>
|
||||||
@ -2470,7 +2470,7 @@ Disable safe saves and try again?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Additional URL's</source>
|
<source>Additional URL's</source>
|
||||||
<translation>Additional URL's</translation>
|
<translation>Additional URLs</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add</source>
|
<source>Add</source>
|
||||||
|
@ -229,7 +229,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remember database key files and security dongles</source>
|
<source>Remember database key files and security dongles</source>
|
||||||
<translation>Recordar los últimos ficheros claves y los «dongle» de seguridad</translation>
|
<translation>Recuerde los archivos de clave de la base de datos y los «dongle» de seguridad</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Check for updates at application startup once per week</source>
|
<source>Check for updates at application startup once per week</source>
|
||||||
@ -1177,7 +1177,7 @@ Copia de seguridad de base de datos ubicada en %2</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Recycle Bin</source>
|
<source>Recycle Bin</source>
|
||||||
<translation>Papelera</translation>
|
<translation>Papelera de reciclaje</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Passwords</source>
|
<source>Passwords</source>
|
||||||
@ -1204,7 +1204,7 @@ Copia de seguridad de base de datos ubicada en %2</translation>
|
|||||||
<name>DatabaseOpenWidget</name>
|
<name>DatabaseOpenWidget</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Key File:</source>
|
<source>Key File:</source>
|
||||||
<translation>Fichero clave:</translation>
|
<translation>Fichero Clave:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Refresh</source>
|
<source>Refresh</source>
|
||||||
@ -1258,7 +1258,7 @@ Considere generar un nuevo fichero clave.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key slot selection</source>
|
<source>Hardware key slot selection</source>
|
||||||
<translation>Selección de ranura de clave hardware</translation>
|
<translation>Selección de ranura de llave por hardware</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Browse for key file</source>
|
<source>Browse for key file</source>
|
||||||
@ -1274,11 +1274,11 @@ Considere generar un nuevo fichero clave.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware Key:</source>
|
<source>Hardware Key:</source>
|
||||||
<translation>Clave hardware:</translation>
|
<translation>Llave por hardware:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key help</source>
|
<source>Hardware key help</source>
|
||||||
<translation>Ayuda de clave hardware</translation>
|
<translation>Ayuda de la llave por hardware</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>TouchID for Quick Unlock</source>
|
<source>TouchID for Quick Unlock</source>
|
||||||
@ -1317,7 +1317,7 @@ Para prevenir que aparezca este error, debe ir a «Configuración de base de dat
|
|||||||
<message>
|
<message>
|
||||||
<source><p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p>
|
<source><p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p>
|
||||||
<p>Click for more information...</p></source>
|
<p>Click for more information...</p></source>
|
||||||
<translation><p>Puede usar una clave de seguridad hardware como <strong>YubiKey</strong> o <strong>OnlyKey</strong> con ranuras configuradas para HMAC-SHA1.</p>
|
<translation><p>Puede usar una llave de seguridad por hardware como <strong>YubiKey</strong> o <strong>OnlyKey</strong> con ranuras configuradas para HMAC-SHA1.</p>
|
||||||
<p>Clic para más información...</p></translation>
|
<p>Clic para más información...</p></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -1352,15 +1352,15 @@ Si no tiene un fichero clave, deje el campo vacío.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Detecting hardware keys…</source>
|
<source>Detecting hardware keys…</source>
|
||||||
<translation>Detectando claves hardware...</translation>
|
<translation>Detectando llaves por hardware...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No hardware keys detected</source>
|
<source>No hardware keys detected</source>
|
||||||
<translation>No se detectaron claves hardware</translation>
|
<translation>No se detectaron llaves por hardware</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select hardware key…</source>
|
<source>Select hardware key…</source>
|
||||||
<translation>Seleccionar clave hardware...</translation>
|
<translation>Seleccionar llave por hardware...</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1589,7 +1589,7 @@ Are you sure you want to continue without a password?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key Derivation Function:</source>
|
<source>Key Derivation Function:</source>
|
||||||
<translation>Función de derivación de la llave:</translation>
|
<translation>Función de derivación de la clave:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Transform rounds:</source>
|
<source>Transform rounds:</source>
|
||||||
@ -1670,7 +1670,7 @@ Si conserva este número, ¡su base de datos puede tardar horas o días (o inclu
|
|||||||
<source>You are using a very low number of key transform rounds with AES-KDF.
|
<source>You are using a very low number of key transform rounds with AES-KDF.
|
||||||
|
|
||||||
If you keep this number, your database may be too easy to crack!</source>
|
If you keep this number, your database may be too easy to crack!</source>
|
||||||
<translation>Está utilizando una cantidad muy baja de rondas de transformación de llave con AES-KDF.
|
<translation>Está utilizando un número muy bajo de rondas de transformación de clave con AES-KDF.
|
||||||
|
|
||||||
Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!</translation>
|
Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!</translation>
|
||||||
</message>
|
</message>
|
||||||
@ -1680,7 +1680,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!<
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to transform key with new KDF parameters; KDF unchanged.</source>
|
<source>Failed to transform key with new KDF parameters; KDF unchanged.</source>
|
||||||
<translation>Error al transformar la llave con nuevos parámetros KDF; KDF sin cambios.</translation>
|
<translation>Error al transformar la clave con nuevos parámetros KDF; KDF sin cambios.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source> MiB</source>
|
<source> MiB</source>
|
||||||
@ -1710,7 +1710,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!<
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key derivation function</source>
|
<source>Key derivation function</source>
|
||||||
<translation>Función de derivación de la llave</translation>
|
<translation>Función de derivación de la clave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Transform rounds</source>
|
<source>Transform rounds</source>
|
||||||
@ -2000,7 +2000,7 @@ Esto es definitivamente un error, por favor repórtelo a los desarrolladores.</t
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>Do you really want to move %n entry(s) to the recycle bin?</source>
|
<source>Do you really want to move %n entry(s) to the recycle bin?</source>
|
||||||
<translation><numerusform>¿Desea mover %n apunte a la papelera?</numerusform><numerusform>¿Desea mover %n apuntes a la papelera?</numerusform></translation>
|
<translation><numerusform>¿Desea mover %n apunte a la papelera?</numerusform><numerusform>¿Desea mover %n apuntes a la papelera de reciclaje?</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Execute command?</source>
|
<source>Execute command?</source>
|
||||||
@ -2137,11 +2137,11 @@ Disable safe saves and try again?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Move group to recycle bin?</source>
|
<source>Move group to recycle bin?</source>
|
||||||
<translation>¿Mover grupo a la papelera?</translation>
|
<translation>¿Mover grupo a la papelera de reciclaje?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Do you really want to move the group "%1" to the recycle bin?</source>
|
<source>Do you really want to move the group "%1" to the recycle bin?</source>
|
||||||
<translation>¿Desea mover el grupo «%1» a la papelera?</translation>
|
<translation>¿Desea mover el grupo «%1» a la papelera de reciclaje?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Successfully merged the database files.</source>
|
<source>Successfully merged the database files.</source>
|
||||||
@ -2642,7 +2642,7 @@ Disable safe saves and try again?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add key to agent when database is opened/unlocked</source>
|
<source>Add key to agent when database is opened/unlocked</source>
|
||||||
<translation>Añadir llave al agente cuando la base de datos se abre/desbloquea</translation>
|
<translation>Añadir clave al agente cuando la base de datos se abre/desbloquea</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Comment</source>
|
<source>Comment</source>
|
||||||
@ -2687,7 +2687,7 @@ Disable safe saves and try again?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Require user confirmation when this key is used</source>
|
<source>Require user confirmation when this key is used</source>
|
||||||
<translation>Requiere confirmación del usuario cuando se usa esta llave</translation>
|
<translation>Requiere confirmación del usuario cuando se usa esta clave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove key from agent after specified seconds</source>
|
<source>Remove key from agent after specified seconds</source>
|
||||||
@ -2958,7 +2958,7 @@ Las extensiones soportadas son: %1.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>%n icon(s) already exist in the database</source>
|
<source>%n icon(s) already exist in the database</source>
|
||||||
<translation><numerusform>El icono %n ya existe en la base de datos</numerusform><numerusform>Los %n iconos ya existen en la base de datos</numerusform></translation>
|
<translation><numerusform>El icono %n ya existe en la base de datos</numerusform><numerusform>Los %n icono(s) ya existe(n) en la base de datos</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>The following icon(s) failed:</source>
|
<source>The following icon(s) failed:</source>
|
||||||
@ -2966,7 +2966,7 @@ Las extensiones soportadas son: %1.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</source>
|
<source>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</source>
|
||||||
<translation><numerusform>Este icono es usado en %1 apunte, y será remplazado por el icono por defecto. ¿Está seguro que desea eliminarlo?</numerusform><numerusform>Este icono es usado en %1 apuntes, y será remplazado por el icono predeterminado. ¿Está seguro que desea eliminarlo?</numerusform></translation>
|
<translation><numerusform>Este icono es usado en %1 apunte, y será remplazado por el icono por defecto. ¿Está seguro que desea eliminarlo?</numerusform><numerusform>Este icono es usado en %n apunte(s), y será remplazado por el icono predeterminado. ¿Está seguro que desea eliminarlo?</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security</source>
|
<source>You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security</source>
|
||||||
@ -3203,9 +3203,9 @@ Your database may get very large and reduce performance.
|
|||||||
|
|
||||||
Are you sure to add this file?</source>
|
Are you sure to add this file?</source>
|
||||||
<translation>%1 es un fichero grande (%2 MB).
|
<translation>%1 es un fichero grande (%2 MB).
|
||||||
Tu base de datos puede vovlerse muy grande y reducir el rendimiento.
|
Su base de datos puede vovlerse muy grande y reducir el rendimiento.
|
||||||
|
|
||||||
¿Estás seguro de añadir este fichero?</translation>
|
¿Está seguro de añadir este fichero?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Confirm Attachment</source>
|
<source>Confirm Attachment</source>
|
||||||
@ -3724,7 +3724,7 @@ Si ocurre nuevamente entonces su archivo de base de datos puede estar corrupto.<
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unsupported key derivation function (KDF) or invalid parameters</source>
|
<source>Unsupported key derivation function (KDF) or invalid parameters</source>
|
||||||
<translation>Función de derivación de cerradura no admitida (KDF) o parámetros no válidos</translation>
|
<translation>Función de derivación de clave no admitida (KDF) o parámetros no válidos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Legacy header fields found in KDBX4 file.</source>
|
<source>Legacy header fields found in KDBX4 file.</source>
|
||||||
@ -4118,7 +4118,7 @@ Linea %2, columna %3</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key transformation failed</source>
|
<source>Key transformation failed</source>
|
||||||
<translation>Error en la transformación de la llave</translation>
|
<translation>Error en la transformación de la clave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Invalid group field type number</source>
|
<source>Invalid group field type number</source>
|
||||||
@ -4317,7 +4317,7 @@ Si ocurre nuevamente entonces su archivo de base de datos puede estar corrupto.<
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key File</source>
|
<source>Key File</source>
|
||||||
<translation>Fichero clave</translation>
|
<translation>Fichero Clave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></source>
|
<source><p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p></source>
|
||||||
@ -4325,7 +4325,7 @@ Si ocurre nuevamente entonces su archivo de base de datos puede estar corrupto.<
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Legacy key file format</source>
|
<source>Legacy key file format</source>
|
||||||
<translation>Formato de archivo llave heredado</translation>
|
<translation>Formato de archivo fichero clave heredado</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error loading the key file '%1'
|
<source>Error loading the key file '%1'
|
||||||
@ -4355,7 +4355,7 @@ Mensaje: %2</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select a key file</source>
|
<source>Select a key file</source>
|
||||||
<translation>Seleccione un archivo llave</translation>
|
<translation>Seleccionar un fichero clave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key file selection</source>
|
<source>Key file selection</source>
|
||||||
@ -4829,11 +4829,11 @@ Espere algunos errores y problemas menores, esta versión no está destinada par
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show Toolbar</source>
|
<source>Show Toolbar</source>
|
||||||
<translation>Mostrar barra de herrameintas</translation>
|
<translation>Mostrar barra de herramientas</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show Preview Panel</source>
|
<source>Show Preview Panel</source>
|
||||||
<translation>Mostrar panel de previsualizción</translation>
|
<translation>Mostrar panel de previsualización</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Don't show again for this version</source>
|
<source>Don't show again for this version</source>
|
||||||
@ -5115,7 +5115,7 @@ Espere algunos errores y problemas menores, esta versión no está destinada par
|
|||||||
<name>OpenSSHKey</name>
|
<name>OpenSSHKey</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Invalid key file, expecting an OpenSSH key</source>
|
<source>Invalid key file, expecting an OpenSSH key</source>
|
||||||
<translation>Archivo llave no válido, esperando una llave de OpenSSH</translation>
|
<translation>Fichero clave no válido, esperando una clave de OpenSSH</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>PEM boundary mismatch</source>
|
<source>PEM boundary mismatch</source>
|
||||||
@ -5579,7 +5579,7 @@ Espere algunos errores y problemas menores, esta versión no está destinada par
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Empty</source>
|
<source>Empty</source>
|
||||||
<translation>Vacío</translation>
|
<translation>Vaciar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove</source>
|
<source>Remove</source>
|
||||||
@ -6791,7 +6791,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hover over reason to show additional details. Double-click entries to edit.</source>
|
<source>Hover over reason to show additional details. Double-click entries to edit.</source>
|
||||||
<translation>Pasar por encima de la razón para mostrar detalles adicionales. Doble clic para editar.</translation>
|
<translation>Pasar por encima del motivo para mostrar detalles adicionales. Doble clic para editar.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Bad</source>
|
<source>Bad</source>
|
||||||
@ -6850,7 +6850,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reason</source>
|
<source>Reason</source>
|
||||||
<translation>Razón</translation>
|
<translation>Motivo</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Edit Entry...</source>
|
<source>Edit Entry...</source>
|
||||||
@ -7087,7 +7087,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Agent refused this identity. Possible reasons include:</source>
|
<source>Agent refused this identity. Possible reasons include:</source>
|
||||||
<translation>El agente rechazó esta identidad. Las posibles razones incluyen:</translation>
|
<translation>El agente rechazó esta identidad. Los posibles motivos incluyen:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The key has already been added.</source>
|
<source>The key has already been added.</source>
|
||||||
@ -7569,7 +7569,7 @@ Núcleo: %3 %4</translation>
|
|||||||
<name>TotpDialog</name>
|
<name>TotpDialog</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Timed Password</source>
|
<source>Timed Password</source>
|
||||||
<translation>Contraseña cronometrada</translation>
|
<translation>Contraseña temporizada</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>000000</source>
|
<source>000000</source>
|
||||||
@ -7581,7 +7581,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>Expires in <b>%n</b> second(s)</source>
|
<source>Expires in <b>%n</b> second(s)</source>
|
||||||
<translation><numerusform>Caduca en <b>%n</b> segundo(s)</numerusform><numerusform>Caduca en <b>%n</b> segundo (s)</numerusform></translation>
|
<translation><numerusform>Caduca en <b>%n</b> segundo</numerusform><numerusform>Caduca en <b>%n</b> segundos</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -7601,7 +7601,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Closing in %1 seconds.</source>
|
<source>Closing in %1 seconds.</source>
|
||||||
<translation>Cernado en %1 segundos.</translation>
|
<translation>Cerrando en %1 segundos.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -7807,19 +7807,19 @@ Ejemplo: JBSWY3DPEHPK3PXP</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key is currently in use.</source>
|
<source>Hardware key is currently in use.</source>
|
||||||
<translation>La clave hardware está actualmente en uso.</translation>
|
<translation>La llave por hardware está actualmente en uso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not find hardware key with serial number %1. Please plug it in to continue.</source>
|
<source>Could not find hardware key with serial number %1. Please plug it in to continue.</source>
|
||||||
<translation>No se puede encontrar hardware con número de serie %1. Conéctelo para continuar.</translation>
|
<translation>No se puede encontrar llave por hardware con número de serie %1. Conéctelo para continuar.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key timed out waiting for user interaction.</source>
|
<source>Hardware key timed out waiting for user interaction.</source>
|
||||||
<translation>La clave hardware expiró esperando interacción del usuario.</translation>
|
<translation>La llave por hardware expiró esperando interacción del usuario.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>A USB error ocurred when accessing the hardware key: %1</source>
|
<source>A USB error ocurred when accessing the hardware key: %1</source>
|
||||||
<translation>Ha ocurrido un error USB al acceder a la clave hardware: %1</translation>
|
<translation>Ha ocurrido un error USB al acceder a la llave por hardware: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to complete a challenge-response, the specific error was: %1</source>
|
<source>Failed to complete a challenge-response, the specific error was: %1</source>
|
||||||
@ -7846,23 +7846,23 @@ Ejemplo: JBSWY3DPEHPK3PXP</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key slot selection</source>
|
<source>Hardware key slot selection</source>
|
||||||
<translation>Selección de ranura de clave hardware</translation>
|
<translation>Selección de ranura de llave por hardware</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not find any hardware keys!</source>
|
<source>Could not find any hardware keys!</source>
|
||||||
<translation>¡No se puede encontrar ninguna clave hardware!</translation>
|
<translation>¡No se puede encontrar ninguna llave por hardware!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Selected hardware key slot does not support challenge-response!</source>
|
<source>Selected hardware key slot does not support challenge-response!</source>
|
||||||
<translation>¡La ranura de la clave hardware seleccionada no soporta reto-respuesta!</translation>
|
<translation>¡La ranura de la llave por hardware seleccionada no soporta reto-respuesta!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Detecting hardware keys…</source>
|
<source>Detecting hardware keys…</source>
|
||||||
<translation>Detectando claves hardware...</translation>
|
<translation>Detectando llaves por hardware...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No hardware keys detected</source>
|
<source>No hardware keys detected</source>
|
||||||
<translation>No se detectaron claves hardware</translation>
|
<translation>No se detectaron llaves por hardware</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
@ -137,7 +137,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You must restart the application to set the new language. Would you like to restart now?</source>
|
<source>You must restart the application to set the new language. Would you like to restart now?</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Ohjelma täytyy käynnistää uudelleen, jotta uusi kieli voidaan ottaa käyttöön. Haluatko käynnistää uudelleen nyt?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -342,15 +342,15 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save when locking database</source>
|
<source>Automatically save when locking database</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Tallenna automaattisesti, kun tietokanta lukitaan</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save non-data changes when locking database</source>
|
<source>Automatically save non-data changes when locking database</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Tallenna automaattisesti asetukset jotka eivät liity varsinaisiin tietoihin, kun tietokanta lukitaan</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tray icon type</source>
|
<source>Tray icon type</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Ilmoitusalueen ikonin tyyppi</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -4849,23 +4849,23 @@ Bugeja ja ongelmia voi esiintyä. Tämä versio ei ole tarkoitettu päivittäise
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Perform Auto-Type Sequence</source>
|
<source>Perform Auto-Type Sequence</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Suorita automaattisyötön sekvenssi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}</source>
|
<source>{USERNAME}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{USERNAME}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}{ENTER}</source>
|
<source>{USERNAME}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{USERNAME}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}</source>
|
<source>{PASSWORD}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{PASSWORD}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}{ENTER}</source>
|
<source>{PASSWORD}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{PASSWORD}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -330,15 +330,15 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto-Type typing delay:</source>
|
<source>Auto-Type typing delay:</source>
|
||||||
<translation>Vitesse de remplissage de la saisie automatique :</translation>
|
<translation>Vitesse de remplissage de la saisie automatique :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Global Auto-Type shortcut:</source>
|
<source>Global Auto-Type shortcut:</source>
|
||||||
<translation>Raccourci de la saisie automatique :</translation>
|
<translation>Raccourci de la saisie automatique :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto-Type start delay:</source>
|
<source>Auto-Type start delay:</source>
|
||||||
<translation>Délai de démarrage de la saisie automatique :</translation>
|
<translation>Délai de démarrage de la saisie automatique :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save when locking database</source>
|
<source>Automatically save when locking database</source>
|
||||||
@ -439,11 +439,11 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Require password repeat when it is visible</source>
|
<source>Require password repeat when it is visible</source>
|
||||||
<translation>Demander de confirmer le mot de passe lorsque celui-ci est visible</translation>
|
<translation>Exiger la confirmation du mot de passe s’il est visible</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide passwords when editing them</source>
|
<source>Hide passwords when editing them</source>
|
||||||
<translation>Cacher les mots de passe pendant leur modification</translation>
|
<translation>Cacher les mots de passe lors de leur modification</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use placeholder for empty password fields</source>
|
<source>Use placeholder for empty password fields</source>
|
||||||
@ -564,11 +564,11 @@
|
|||||||
<name>BrowserAccessControlDialog</name>
|
<name>BrowserAccessControlDialog</name>
|
||||||
<message>
|
<message>
|
||||||
<source>KeePassXC - Browser Access Request</source>
|
<source>KeePassXC - Browser Access Request</source>
|
||||||
<translation>Requiert l’accès à KeePassXC-Browser</translation>
|
<translation>KeePassXC-Browser - Requête d'accès au navigateur</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>%1 is requesting access to the following entries:</source>
|
<source>%1 is requesting access to the following entries:</source>
|
||||||
<translation>%1 demande l’accès aux entrées suivantes :</translation>
|
<translation>%1 demande l’accès aux entrées suivantes :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remember access to checked entries</source>
|
<source>Remember access to checked entries</source>
|
||||||
@ -892,7 +892,7 @@ chrome-laptop</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Browser type:</source>
|
<source>Browser type:</source>
|
||||||
<translation>Type de navigateur :</translation>
|
<translation>Type de navigateur :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Toolbar button style</source>
|
<source>Toolbar button style</source>
|
||||||
@ -900,7 +900,7 @@ chrome-laptop</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Config Location:</source>
|
<source>Config Location:</source>
|
||||||
<translation>Emplacement de configuration :</translation>
|
<translation>Emplacement de configuration :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom browser location field</source>
|
<source>Custom browser location field</source>
|
||||||
@ -2764,7 +2764,7 @@ Désactiver les enregistrements sécurisés et ressayer ?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>KeeShare unsigned container</source>
|
<source>KeeShare unsigned container</source>
|
||||||
<translation>Conteneur KeeShare non signé</translation>
|
<translation>Conteneur KeeShare non signé</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>KeeShare signed container</source>
|
<source>KeeShare signed container</source>
|
||||||
@ -4135,7 +4135,7 @@ Ligne %2, colonne %3</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Incorrect group creation time field size</source>
|
<source>Incorrect group creation time field size</source>
|
||||||
<translation>Taille du champ "date du la création du groupe" incorrect.</translation>
|
<translation>Taille du champ « date du la création du groupe » incorrect.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Incorrect group modification time field size</source>
|
<source>Incorrect group modification time field size</source>
|
||||||
@ -4151,7 +4151,7 @@ Ligne %2, colonne %3</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Incorrect group icon field size</source>
|
<source>Incorrect group icon field size</source>
|
||||||
<translation>Taille du champ "icône du groupe" incorrect.</translation>
|
<translation>Taille du champ « icône du groupe » incorrect.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Incorrect group level field size</source>
|
<source>Incorrect group level field size</source>
|
||||||
@ -4911,11 +4911,11 @@ Attendez-vous à des bogues et des problèmes mineurs. Cette version n’est pas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Overwriting %1 [%2]</source>
|
<source>Overwriting %1 [%2]</source>
|
||||||
<translation>Écrasement de %1 [%2]</translation>
|
<translation>Remplacement de %1 [%2]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>older entry merged from database "%1"</source>
|
<source>older entry merged from database "%1"</source>
|
||||||
<translation>ancienne entrée fusionnée de la base de données "%1"</translation>
|
<translation>ancienne entrée fusionnée de la base de données « %1 »</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Adding backup for older target %1 [%2]</source>
|
<source>Adding backup for older target %1 [%2]</source>
|
||||||
@ -5065,7 +5065,7 @@ Attendez-vous à des bogues et des problèmes mineurs. Cette version n’est pas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to process clearText in place</source>
|
<source>Unable to process clearText in place</source>
|
||||||
<translation>Impossible d’activer le traitement de ClearText</translation>
|
<translation>Impossible d’appliquer l'amélioration ClearText</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Expected %1 bytes of clear-text, found %2</source>
|
<source>Expected %1 bytes of clear-text, found %2</source>
|
||||||
@ -5282,7 +5282,7 @@ Attendez-vous à des bogues et des problèmes mineurs. Cette version n’est pas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Character Types</source>
|
<source>Character Types</source>
|
||||||
<translation>Types de caractères:</translation>
|
<translation>Types de caractères</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Numbers</source>
|
<source>Numbers</source>
|
||||||
@ -5326,7 +5326,7 @@ Attendez-vous à des bogues et des problèmes mineurs. Cette version n’est pas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password Quality: %1</source>
|
<source>Password Quality: %1</source>
|
||||||
<translation>Qualité du mot de passe : %1</translation>
|
<translation>Qualité du mot de passe : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Poor</source>
|
<source>Poor</source>
|
||||||
@ -5382,7 +5382,7 @@ Attendez-vous à des bogues et des problèmes mineurs. Cette version n’est pas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add non-hex letters to "do not include" list</source>
|
<source>Add non-hex letters to "do not include" list</source>
|
||||||
<translation>Ajouter les lettres non-hexadécimales à la liste "Ne pas inclure"</translation>
|
<translation>Ajouter les lettres non-hexadécimales à la liste « Ne pas inclure »</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hex</source>
|
<source>Hex</source>
|
||||||
@ -5815,7 +5815,7 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>malformed string</source>
|
<source>malformed string</source>
|
||||||
<translation>chaîne de caractères malformée</translation>
|
<translation>chaîne de caractères incorrecte</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>missing closing quote</source>
|
<source>missing closing quote</source>
|
||||||
@ -5889,7 +5889,7 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Successfully added entry %1.</source>
|
<source>Successfully added entry %1.</source>
|
||||||
<translation>Ajouté avec succès l’entrée %1.</translation>
|
<translation>L’entrée %1 a bien été ajoutée.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Invalid timeout value %1.</source>
|
<source>Invalid timeout value %1.</source>
|
||||||
@ -6091,7 +6091,7 @@ Commandes proposées :
|
|||||||
<message>
|
<message>
|
||||||
<source>Error reading merge file:
|
<source>Error reading merge file:
|
||||||
%1</source>
|
%1</source>
|
||||||
<translation>Erreur lors de la lecture du fichier fusionner :
|
<translation>Erreur lors de la lecture du fichier à fusionner :
|
||||||
%1</translation>
|
%1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -6104,11 +6104,11 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Successfully recycled entry %1.</source>
|
<source>Successfully recycled entry %1.</source>
|
||||||
<translation>Entrée %1 recyclée avec succès.</translation>
|
<translation>L'entrée %1 a bien été recyclée.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Successfully deleted entry %1.</source>
|
<source>Successfully deleted entry %1.</source>
|
||||||
<translation>Supprimé l’entrée %1 avec succès.</translation>
|
<translation>L’entrée %1 a bien été supprimée.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show the entry's current TOTP.</source>
|
<source>Show the entry's current TOTP.</source>
|
||||||
@ -6128,7 +6128,7 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>%1: (row, col) %2,%3</source>
|
<source>%1: (row, col) %2,%3</source>
|
||||||
<translation>%1: (ligne,colonne) %2,%3</translation>
|
<translation>%1 : (ligne, colonne) %2, %3</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Argon2 (KDBX 4 – recommended)</source>
|
<source>Argon2 (KDBX 4 – recommended)</source>
|
||||||
@ -6174,7 +6174,7 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No key is set. Aborting database creation.</source>
|
<source>No key is set. Aborting database creation.</source>
|
||||||
<translation>Aucune clé définie. Abandon de la création de la base de données.</translation>
|
<translation>Aucune clé définie. La création de la base de données a été abandonnée.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save the database: %1.</source>
|
<source>Failed to save the database: %1.</source>
|
||||||
@ -6186,11 +6186,11 @@ Commandes proposées :
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Creating KeyFile %1 failed: %2</source>
|
<source>Creating KeyFile %1 failed: %2</source>
|
||||||
<translation>Creation du fichier clé %1 échoué : %2 </translation>
|
<translation>Impossible de créer le fichier clé %1 : %2 </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Loading KeyFile %1 failed: %2</source>
|
<source>Loading KeyFile %1 failed: %2</source>
|
||||||
<translation>Chargement du fichier clé %1 échoué : %2</translation>
|
<translation>Impossible de charger le fichier clé %1 : %2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Path of the entry to remove.</source>
|
<source>Path of the entry to remove.</source>
|
||||||
@ -6366,7 +6366,7 @@ Noyau : %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to open HIBP file %1: %2</source>
|
<source>Failed to open HIBP file %1: %2</source>
|
||||||
<translation>Échec de l’ouverture du fichier HIBP %1 : %2</translation>
|
<translation>Impossible d'ouvrir le fichier HIBP %1 : %2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Evaluating database entries against HIBP file, this will take a while...</source>
|
<source>Evaluating database entries against HIBP file, this will take a while...</source>
|
||||||
@ -6728,7 +6728,7 @@ Noyau : %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>ChaCha20 256-bit</source>
|
<source>ChaCha20 256-bit</source>
|
||||||
<translation>ChaCha20 : 256 bits {20 256 à ?}</translation>
|
<translation>ChaCha20 : 256 bits {20 256 à ?}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Benchmark %1 delay</source>
|
<source>Benchmark %1 delay</source>
|
||||||
@ -7272,7 +7272,7 @@ Noyau : %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Key:</source>
|
<source>Key:</source>
|
||||||
<translation>Clé :</translation>
|
<translation>Clé :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Generate</source>
|
<source>Generate</source>
|
||||||
@ -7357,7 +7357,7 @@ Noyau : %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</source>
|
<source>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</source>
|
||||||
<translation>Le certificat exporté est différent de celui en cours d’utilisation. Souhaitez-vous exporter le certificat actuel ?</translation>
|
<translation>Le certificat exporté est différent de celui en cours d’utilisation. Souhaitez-vous exporter le certificat actuel ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Signer:</source>
|
<source>Signer:</source>
|
||||||
@ -7565,7 +7565,7 @@ Noyau : %3 %4</translation>
|
|||||||
<name>TotpDialog</name>
|
<name>TotpDialog</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Timed Password</source>
|
<source>Timed Password</source>
|
||||||
<translation>Mot de passe programmé</translation>
|
<translation>Mot de passe planifié</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>000000</source>
|
<source>000000</source>
|
||||||
@ -7633,7 +7633,7 @@ Noyau : %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Code size:</source>
|
<source>Code size:</source>
|
||||||
<translation>Taille du code :</translation>
|
<translation>Taille du code :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Secret Key:</source>
|
<source>Secret Key:</source>
|
||||||
@ -7701,7 +7701,7 @@ Exemple : JBSWY3DPEHPK3PXP</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Error!</source>
|
<source>Update Error!</source>
|
||||||
<translation>Erreur de mise à jour !</translation>
|
<translation>Erreur lors de mise à jour !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>An error occurred in retrieving update information.</source>
|
<source>An error occurred in retrieving update information.</source>
|
||||||
@ -7729,7 +7729,7 @@ Exemple : JBSWY3DPEHPK3PXP</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You're up-to-date!</source>
|
<source>You're up-to-date!</source>
|
||||||
<translation>Votre version est à jour !</translation>
|
<translation>Votre version est à jour !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>KeePassXC %1 is currently the newest version available</source>
|
<source>KeePassXC %1 is currently the newest version available</source>
|
||||||
|
7829
share/translations/keepassx_hr_HR.ts
Normal file
@ -3032,7 +3032,7 @@ Támogatott kiterjesztések: %1.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Plugin Data</source>
|
<source>Plugin Data</source>
|
||||||
<translation>Beépülő adati</translation>
|
<translation>Beépülő adatai</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove</source>
|
<source>Remove</source>
|
||||||
@ -3085,7 +3085,7 @@ Ez a kijelölt bővítmény hibás működését eredményezheti.</translation>
|
|||||||
<name>Entry</name>
|
<name>Entry</name>
|
||||||
<message>
|
<message>
|
||||||
<source>%1 - Clone</source>
|
<source>%1 - Clone</source>
|
||||||
<translation>%1 – Klónozás</translation>
|
<translation>%1 – Klón</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -4851,19 +4851,19 @@ Néhány hiba és kisebb nehézségek várhatóak, ezért ez a verzió nem aján
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}</source>
|
<source>{USERNAME}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{FELHASZNÁLÓNÉV}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}{ENTER}</source>
|
<source>{USERNAME}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{FELHASZNÁLÓNÉV}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}</source>
|
<source>{PASSWORD}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{JELSZÓ}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}{ENTER}</source>
|
<source>{PASSWORD}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{JELSZÓ}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -5510,7 +5510,7 @@ Néhány hiba és kisebb nehézségek várhatóak, ezért ez a verzió nem aján
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Very weak password</source>
|
<source>Very weak password</source>
|
||||||
<translation>Nagy gyenge jelszó</translation>
|
<translation>Nagyon gyenge jelszó</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password entropy is %1 bits</source>
|
<source>Password entropy is %1 bits</source>
|
||||||
|
@ -350,7 +350,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tray icon type</source>
|
<source>Tray icon type</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Systeemvak-pictogram</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -4852,19 +4852,19 @@ Wil je KeePassXC nu opnieuw opstarten?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}</source>
|
<source>{USERNAME}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{GEBRUIKERSNAAM}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}{ENTER}</source>
|
<source>{USERNAME}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{GEBRUIKERSNAAM}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}</source>
|
<source>{PASSWORD}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{WACHTWOORD}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}{ENTER}</source>
|
<source>{PASSWORD}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{WACHTWOORD}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a></source>
|
<source>Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a></source>
|
||||||
<translation>Сообщать об ошибках: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a></translation>
|
<translation>Сообщить об <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">ошибках</a> по https://github.com</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
|
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
|
||||||
@ -19,11 +19,11 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Contributors</source>
|
<source>Contributors</source>
|
||||||
<translation>Авторы</translation>
|
<translation>Соавторы</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a></source>
|
<source><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a></source>
|
||||||
<translation><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Посмотреть вклад на GitHub</a></translation>
|
<translation><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Посмотреть соавторов на GitHub</a></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Debug Info</source>
|
<source>Debug Info</source>
|
||||||
@ -2368,7 +2368,7 @@ Disable safe saves and try again?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source><html><head/><body><p>If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements (e. g. password entropy or re-use). You can set the check mark if the password is beyond your control (e. g. if it needs to be a four-digit PIN) to prevent it from cluttering the reports.</p></body></html></source>
|
<source><html><head/><body><p>If checked, the entry will not appear in reports like Health Check and HIBP even if it doesn't match the quality requirements (e. g. password entropy or re-use). You can set the check mark if the password is beyond your control (e. g. if it needs to be a four-digit PIN) to prevent it from cluttering the reports.</p></body></html></source>
|
||||||
<translation type="unfinished"/>
|
<translation><html><head/><body><p>При включении, запись не появится в отчетах (например, Проверки безопасности или HIBP), даже если она не соответствует требованиям к качеству (энтропия, переиспользование). Можно включить этот параметр, если вы не можете контролировать этот пароль (например, 4-значные пин-кодыж), чтобы не засорять отчет.</p></body></html></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Exclude from database reports</source>
|
<source>Exclude from database reports</source>
|
||||||
@ -2482,7 +2482,7 @@ Disable safe saves and try again?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Only send this setting to the browser for HTTP Auth dialogs. If enabled, normal login forms will not show this entry for selection.</source>
|
<source>Only send this setting to the browser for HTTP Auth dialogs. If enabled, normal login forms will not show this entry for selection.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Отправлять эту настройку только браузерным диалогам для HTTP Auth. Если включено, обычные формы авторизации не покажут запись среди вариантов выбора.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use this entry only with HTTP Basic Auth</source>
|
<source>Use this entry only with HTTP Basic Auth</source>
|
||||||
@ -6709,7 +6709,7 @@ Kernel: %3 %4</source>
|
|||||||
<message>
|
<message>
|
||||||
<source>All clipping programs failed. Tried %1
|
<source>All clipping programs failed. Tried %1
|
||||||
</source>
|
</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Ни одна программа копирования не сработала. Пробовали %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>AES (%1 rounds)</source>
|
<source>AES (%1 rounds)</source>
|
||||||
@ -6847,7 +6847,7 @@ Kernel: %3 %4</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reason</source>
|
<source>Reason</source>
|
||||||
<translation>Причика</translation>
|
<translation>Причина</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Edit Entry...</source>
|
<source>Edit Entry...</source>
|
||||||
|
@ -137,7 +137,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You must restart the application to set the new language. Would you like to restart now?</source>
|
<source>You must restart the application to set the new language. Would you like to restart now?</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Musíte reštartovať aplikáciu, aby sa tieto zmeny prejavili. Chcete ju reštartovať teraz?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -342,15 +342,15 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save when locking database</source>
|
<source>Automatically save when locking database</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Pri zamknutí databázy automaticky uložiť</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save non-data changes when locking database</source>
|
<source>Automatically save non-data changes when locking database</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Pri zamknutí databázy automaticky uložiť nedátové zmeny</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tray icon type</source>
|
<source>Tray icon type</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Typ ikona oznamovacej oblasti</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -956,7 +956,7 @@ chrome-laptop.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select native messaging host folder location</source>
|
<source>Select native messaging host folder location</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Vyberte umiestnenie zložky hostiteľa správe medzi prehliadačom a KeePassXC</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -4854,23 +4854,23 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Perform Auto-Type Sequence</source>
|
<source>Perform Auto-Type Sequence</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Vykonať Automatické vypĺňanie</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}</source>
|
<source>{USERNAME}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{POUŽÍVATEĽ}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}{ENTER}</source>
|
<source>{USERNAME}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{POUŽÍVATEĽ}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}</source>
|
<source>{PASSWORD}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{HESLO}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}{ENTER}</source>
|
<source>{PASSWORD}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{HESLO}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -6571,7 +6571,7 @@ Jadro: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not save the native messaging script file for %1.</source>
|
<source>Could not save the native messaging script file for %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Nemožno uložiť súbor skriptu správ medzi prehliadačom a KeePassXC (native messaging) pre %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy the given attribute to the clipboard. Defaults to "password" if not specified.</source>
|
<source>Copy the given attribute to the clipboard. Defaults to "password" if not specified.</source>
|
||||||
|
@ -3127,7 +3127,7 @@ This may cause the affected plugins to malfunction.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>Are you sure you want to remove %n attachment(s)?</source>
|
<source>Are you sure you want to remove %n attachment(s)?</source>
|
||||||
<translation><numerusform>您确定要删除 %n 个附件吗?</numerusform></translation>
|
<translation><numerusform>您确定要删除所选附件吗?</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save attachments</source>
|
<source>Save attachments</source>
|
||||||
|
@ -4853,19 +4853,19 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}</source>
|
<source>{USERNAME}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{USERNAME}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{USERNAME}{ENTER}</source>
|
<source>{USERNAME}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{USERNAME}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}</source>
|
<source>{PASSWORD}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{PASSWORD}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>{PASSWORD}{ENTER}</source>
|
<source>{PASSWORD}{ENTER}</source>
|
||||||
<translation type="unfinished"/>
|
<translation>{PASSWORD}{ENTER}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: keepassxc
|
name: keepassxc
|
||||||
version: 2.6.2
|
version: 2.6.3
|
||||||
grade: stable
|
grade: stable
|
||||||
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
||||||
description: |
|
description: |
|
||||||
|
@ -321,7 +321,7 @@ QString BrowserService::storeKey(const QString& key)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
QInputDialog keyDialog;
|
QInputDialog keyDialog;
|
||||||
connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &keyDialog, SLOT(reject()));
|
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &keyDialog, SLOT(reject()));
|
||||||
keyDialog.setWindowTitle(tr("KeePassXC: New key association request"));
|
keyDialog.setWindowTitle(tr("KeePassXC: New key association request"));
|
||||||
keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
|
keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
|
||||||
"Give the connection a unique name or ID, for example:\nchrome-laptop.")
|
"Give the connection a unique name or ID, for example:\nchrome-laptop.")
|
||||||
@ -745,7 +745,7 @@ BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr,
|
|||||||
// Sort same priority entries by Title or UserName
|
// Sort same priority entries by Title or UserName
|
||||||
auto entries = priorities.values(key);
|
auto entries = priorities.values(key);
|
||||||
std::sort(entries.begin(), entries.end(), [&sortField](Entry* left, Entry* right) {
|
std::sort(entries.begin(), entries.end(), [&sortField](Entry* left, Entry* right) {
|
||||||
return QString::localeAwareCompare(left->attribute(sortField), right->attribute(sortField));
|
return QString::localeAwareCompare(left->attribute(sortField), right->attribute(sortField)) < 0;
|
||||||
});
|
});
|
||||||
results << entries;
|
results << entries;
|
||||||
if (browserSettings()->bestMatchOnly() && !results.isEmpty()) {
|
if (browserSettings()->bestMatchOnly() && !results.isEmpty()) {
|
||||||
@ -772,7 +772,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
|||||||
updateWindowState();
|
updateWindowState();
|
||||||
BrowserAccessControlDialog accessControlDialog;
|
BrowserAccessControlDialog accessControlDialog;
|
||||||
|
|
||||||
connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &accessControlDialog, SLOT(reject()));
|
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
|
||||||
|
|
||||||
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
|
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
|
||||||
auto entry = pwEntriesToConfirm[item->row()];
|
auto entry = pwEntriesToConfirm[item->row()];
|
||||||
|
@ -83,7 +83,7 @@ int Import::execute(const QStringList& arguments)
|
|||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
Database db;
|
Database db;
|
||||||
db.setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
db.setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D));
|
||||||
db.setKey(key);
|
db.setKey(key);
|
||||||
|
|
||||||
if (!db.import(xmlExportPath, &errorMessage)) {
|
if (!db.import(xmlExportPath, &errorMessage)) {
|
||||||
|
@ -132,9 +132,9 @@ namespace Utils
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileKey->type() != FileKey::Hashed) {
|
if (fileKey->type() != FileKey::KeePass2XMLv2 && fileKey->type() != FileKey::Hashed) {
|
||||||
err << QObject::tr("WARNING: You are using a legacy key file format which may become\n"
|
err << QObject::tr("WARNING: You are using an old key file format which KeePassXC may\n"
|
||||||
"unsupported in the future.\n\n"
|
"stop supporting in the future.\n\n"
|
||||||
"Please consider generating a new key file.")
|
"Please consider generating a new key file.")
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,6 @@ const int Entry::ResolveMaximumDepth = 10;
|
|||||||
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
|
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
|
||||||
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
|
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
|
||||||
|
|
||||||
Entry::CloneFlags Entry::DefaultCloneFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo;
|
|
||||||
|
|
||||||
Entry::Entry()
|
Entry::Entry()
|
||||||
: m_attributes(new EntryAttributes(this))
|
: m_attributes(new EntryAttributes(this))
|
||||||
, m_attachments(new EntryAttachments(this))
|
, m_attachments(new EntryAttachments(this))
|
||||||
@ -465,7 +463,8 @@ void Entry::setTotp(QSharedPointer<Totp::Settings> settings)
|
|||||||
m_data.totpSettings.reset();
|
m_data.totpSettings.reset();
|
||||||
} else {
|
} else {
|
||||||
m_data.totpSettings = std::move(settings);
|
m_data.totpSettings = std::move(settings);
|
||||||
auto text = Totp::writeSettings(m_data.totpSettings, title(), username());
|
auto text = Totp::writeSettings(
|
||||||
|
m_data.totpSettings, resolveMultiplePlaceholders(title()), resolveMultiplePlaceholders(username()));
|
||||||
if (m_data.totpSettings->format != Totp::StorageFormat::LEGACY) {
|
if (m_data.totpSettings->format != Totp::StorageFormat::LEGACY) {
|
||||||
m_attributes->set(Totp::ATTRIBUTE_OTP, text, true);
|
m_attributes->set(Totp::ATTRIBUTE_OTP, text, true);
|
||||||
} else {
|
} else {
|
||||||
@ -493,6 +492,15 @@ QSharedPointer<Totp::Settings> Entry::totpSettings() const
|
|||||||
return m_data.totpSettings;
|
return m_data.totpSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::totpSettingsString() const
|
||||||
|
{
|
||||||
|
if (m_data.totpSettings) {
|
||||||
|
return Totp::writeSettings(
|
||||||
|
m_data.totpSettings, resolveMultiplePlaceholders(title()), resolveMultiplePlaceholders(username()), true);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void Entry::setUuid(const QUuid& uuid)
|
void Entry::setUuid(const QUuid& uuid)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!uuid.isNull());
|
Q_ASSERT(!uuid.isNull());
|
||||||
|
@ -106,6 +106,7 @@ public:
|
|||||||
QString notes() const;
|
QString notes() const;
|
||||||
QString attribute(const QString& key) const;
|
QString attribute(const QString& key) const;
|
||||||
QString totp() const;
|
QString totp() const;
|
||||||
|
QString totpSettingsString() const;
|
||||||
QSharedPointer<Totp::Settings> totpSettings() const;
|
QSharedPointer<Totp::Settings> totpSettings() const;
|
||||||
int size() const;
|
int size() const;
|
||||||
|
|
||||||
@ -159,6 +160,8 @@ public:
|
|||||||
CloneNewUuid = 1, // generate a random uuid for the clone
|
CloneNewUuid = 1, // generate a random uuid for the clone
|
||||||
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
|
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
|
||||||
CloneIncludeHistory = 4, // clone the history items
|
CloneIncludeHistory = 4, // clone the history items
|
||||||
|
CloneDefault = CloneNewUuid | CloneResetTimeInfo,
|
||||||
|
CloneCopy = CloneNewUuid | CloneResetTimeInfo | CloneIncludeHistory,
|
||||||
CloneRenameTitle = 8, // add "-Clone" after the original title
|
CloneRenameTitle = 8, // add "-Clone" after the original title
|
||||||
CloneUserAsRef = 16, // Add the user as a reference to the original entry
|
CloneUserAsRef = 16, // Add the user as a reference to the original entry
|
||||||
ClonePassAsRef = 32, // Add the password as a reference to the original entry
|
ClonePassAsRef = 32, // Add the password as a reference to the original entry
|
||||||
@ -208,7 +211,6 @@ public:
|
|||||||
static const int ResolveMaximumDepth;
|
static const int ResolveMaximumDepth;
|
||||||
static const QString AutoTypeSequenceUsername;
|
static const QString AutoTypeSequenceUsername;
|
||||||
static const QString AutoTypeSequencePassword;
|
static const QString AutoTypeSequencePassword;
|
||||||
static CloneFlags DefaultCloneFlags;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a duplicate of this entry except that the returned entry isn't
|
* Creates a duplicate of this entry except that the returned entry isn't
|
||||||
@ -216,7 +218,7 @@ public:
|
|||||||
* Note that you need to copy the custom icons manually when inserting the
|
* Note that you need to copy the custom icons manually when inserting the
|
||||||
* new entry into another database.
|
* new entry into another database.
|
||||||
*/
|
*/
|
||||||
Entry* clone(CloneFlags flags = DefaultCloneFlags) const;
|
Entry* clone(CloneFlags flags = CloneDefault) const;
|
||||||
void copyDataFrom(const Entry* other);
|
void copyDataFrom(const Entry* other);
|
||||||
QString maskPasswordPlaceholders(const QString& str) const;
|
QString maskPasswordPlaceholders(const QString& str) const;
|
||||||
Entry* resolveReference(const QString& str) const;
|
Entry* resolveReference(const QString& str) const;
|
||||||
|
@ -36,9 +36,6 @@ const int Group::DefaultIconNumber = 48;
|
|||||||
const int Group::RecycleBinIconNumber = 43;
|
const int Group::RecycleBinIconNumber = 43;
|
||||||
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
||||||
|
|
||||||
Group::CloneFlags Group::DefaultCloneFlags =
|
|
||||||
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries;
|
|
||||||
|
|
||||||
Group::Group()
|
Group::Group()
|
||||||
: m_customData(new CustomData(this))
|
: m_customData(new CustomData(this))
|
||||||
, m_updateTimeinfo(true)
|
, m_updateTimeinfo(true)
|
||||||
|
@ -56,6 +56,7 @@ public:
|
|||||||
CloneNewUuid = 1, // generate a random uuid for the clone
|
CloneNewUuid = 1, // generate a random uuid for the clone
|
||||||
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
|
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
|
||||||
CloneIncludeEntries = 4, // clone the group entries
|
CloneIncludeEntries = 4, // clone the group entries
|
||||||
|
CloneDefault = CloneNewUuid | CloneResetTimeInfo | CloneIncludeEntries,
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
|
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
|
||||||
|
|
||||||
@ -108,7 +109,6 @@ public:
|
|||||||
|
|
||||||
static const int DefaultIconNumber;
|
static const int DefaultIconNumber;
|
||||||
static const int RecycleBinIconNumber;
|
static const int RecycleBinIconNumber;
|
||||||
static CloneFlags DefaultCloneFlags;
|
|
||||||
static const QString RootAutoTypeSequence;
|
static const QString RootAutoTypeSequence;
|
||||||
|
|
||||||
Group* findChildByName(const QString& name);
|
Group* findChildByName(const QString& name);
|
||||||
@ -157,8 +157,8 @@ public:
|
|||||||
QSet<QUuid> customIconsRecursive() const;
|
QSet<QUuid> customIconsRecursive() const;
|
||||||
QList<QString> usernamesRecursive(int topN = -1) const;
|
QList<QString> usernamesRecursive(int topN = -1) const;
|
||||||
|
|
||||||
Group* clone(Entry::CloneFlags entryFlags = Entry::DefaultCloneFlags,
|
Group* clone(Entry::CloneFlags entryFlags = Entry::CloneDefault,
|
||||||
CloneFlags groupFlags = DefaultCloneFlags) const;
|
Group::CloneFlags groupFlags = Group::CloneDefault) const;
|
||||||
|
|
||||||
void copyDataFrom(const Group* other);
|
void copyDataFrom(const Group* other);
|
||||||
QString print(bool recursive = false, bool flatten = false, int depth = 0);
|
QString print(bool recursive = false, bool flatten = false, int depth = 0);
|
||||||
|
@ -73,7 +73,7 @@ QString Crypto::debugInfo()
|
|||||||
Q_ASSERT(Crypto::initialized());
|
Q_ASSERT(Crypto::initialized());
|
||||||
|
|
||||||
QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n");
|
QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n");
|
||||||
debugInfo.append(" libgcrypt ").append(m_backendVersion).append("\n");
|
debugInfo.append("- libgcrypt ").append(m_backendVersion).append("\n");
|
||||||
return debugInfo;
|
return debugInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@
|
|||||||
* a 256-bit salt is generated each time the database is saved, the tag length is 256 bits, no secret key
|
* a 256-bit salt is generated each time the database is saved, the tag length is 256 bits, no secret key
|
||||||
* or associated data. KeePass uses the latest version of Argon2, v1.3.
|
* or associated data. KeePass uses the latest version of Argon2, v1.3.
|
||||||
*/
|
*/
|
||||||
Argon2Kdf::Argon2Kdf()
|
Argon2Kdf::Argon2Kdf(Type type)
|
||||||
: Kdf::Kdf(KeePass2::KDF_ARGON2)
|
: Kdf::Kdf(KeePass2::KDF_ARGON2D)
|
||||||
|
, m_type(type)
|
||||||
, m_version(0x13)
|
, m_version(0x13)
|
||||||
, m_memory(1 << 16)
|
, m_memory(1 << 16)
|
||||||
, m_parallelism(static_cast<quint32>(QThread::idealThreadCount()))
|
, m_parallelism(static_cast<quint32>(QThread::idealThreadCount()))
|
||||||
@ -54,6 +55,16 @@ bool Argon2Kdf::setVersion(quint32 version)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Argon2Kdf::Type Argon2Kdf::type() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Argon2Kdf::setType(Type type)
|
||||||
|
{
|
||||||
|
m_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
quint64 Argon2Kdf::memory() const
|
quint64 Argon2Kdf::memory() const
|
||||||
{
|
{
|
||||||
return m_memory;
|
return m_memory;
|
||||||
@ -133,7 +144,11 @@ bool Argon2Kdf::processParameters(const QVariantMap& p)
|
|||||||
QVariantMap Argon2Kdf::writeParameters()
|
QVariantMap Argon2Kdf::writeParameters()
|
||||||
{
|
{
|
||||||
QVariantMap p;
|
QVariantMap p;
|
||||||
p.insert(KeePass2::KDFPARAM_UUID, KeePass2::KDF_ARGON2.toRfc4122());
|
if (type() == Type::Argon2d) {
|
||||||
|
p.insert(KeePass2::KDFPARAM_UUID, KeePass2::KDF_ARGON2D.toRfc4122());
|
||||||
|
} else {
|
||||||
|
p.insert(KeePass2::KDFPARAM_UUID, KeePass2::KDF_ARGON2ID.toRfc4122());
|
||||||
|
}
|
||||||
p.insert(KeePass2::KDFPARAM_ARGON2_VERSION, version());
|
p.insert(KeePass2::KDFPARAM_ARGON2_VERSION, version());
|
||||||
p.insert(KeePass2::KDFPARAM_ARGON2_PARALLELISM, parallelism());
|
p.insert(KeePass2::KDFPARAM_ARGON2_PARALLELISM, parallelism());
|
||||||
p.insert(KeePass2::KDFPARAM_ARGON2_MEMORY, memory() * 1024);
|
p.insert(KeePass2::KDFPARAM_ARGON2_MEMORY, memory() * 1024);
|
||||||
@ -158,18 +173,20 @@ bool Argon2Kdf::transform(const QByteArray& raw, QByteArray& result) const
|
|||||||
{
|
{
|
||||||
result.clear();
|
result.clear();
|
||||||
result.resize(32);
|
result.resize(32);
|
||||||
return transformKeyRaw(raw, seed(), version(), rounds(), memory(), parallelism(), result);
|
return transformKeyRaw(raw, seed(), version(), type(), rounds(), memory(), parallelism(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Argon2Kdf::transformKeyRaw(const QByteArray& key,
|
bool Argon2Kdf::transformKeyRaw(const QByteArray& key,
|
||||||
const QByteArray& seed,
|
const QByteArray& seed,
|
||||||
quint32 version,
|
quint32 version,
|
||||||
|
Type type,
|
||||||
quint32 rounds,
|
quint32 rounds,
|
||||||
quint64 memory,
|
quint64 memory,
|
||||||
quint32 parallelism,
|
quint32 parallelism,
|
||||||
QByteArray& result)
|
QByteArray& result)
|
||||||
{
|
{
|
||||||
// Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length
|
// Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length
|
||||||
|
|
||||||
int rc = argon2_hash(rounds,
|
int rc = argon2_hash(rounds,
|
||||||
memory,
|
memory,
|
||||||
parallelism,
|
parallelism,
|
||||||
@ -181,7 +198,7 @@ bool Argon2Kdf::transformKeyRaw(const QByteArray& key,
|
|||||||
result.size(),
|
result.size(),
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
Argon2_d,
|
type == Type::Argon2d ? Argon2_d : Argon2_id,
|
||||||
version);
|
version);
|
||||||
if (rc != ARGON2_OK) {
|
if (rc != ARGON2_OK) {
|
||||||
qWarning("Argon2 error: %s", argon2_error_message(rc));
|
qWarning("Argon2 error: %s", argon2_error_message(rc));
|
||||||
@ -205,7 +222,7 @@ int Argon2Kdf::benchmarkImpl(int msec) const
|
|||||||
timer.start();
|
timer.start();
|
||||||
|
|
||||||
int rounds = 4;
|
int rounds = 4;
|
||||||
if (transformKeyRaw(key, seed, version(), rounds, memory(), parallelism(), key)) {
|
if (transformKeyRaw(key, seed, version(), type(), rounds, memory(), parallelism(), key)) {
|
||||||
return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed()));
|
return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,5 +231,6 @@ int Argon2Kdf::benchmarkImpl(int msec) const
|
|||||||
|
|
||||||
QString Argon2Kdf::toString() const
|
QString Argon2Kdf::toString() const
|
||||||
{
|
{
|
||||||
return QObject::tr("Argon2 (%1 rounds, %2 KB)").arg(QString::number(rounds()), QString::number(memory()));
|
return QObject::tr("Argon2%1 (%2 rounds, %3 KB)")
|
||||||
|
.arg(type() == Type::Argon2d ? "d" : "id", QString::number(rounds()), QString::number(memory()));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,13 @@
|
|||||||
class Argon2Kdf : public Kdf
|
class Argon2Kdf : public Kdf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Argon2Kdf();
|
enum class Type
|
||||||
|
{
|
||||||
|
Argon2d,
|
||||||
|
Argon2id
|
||||||
|
};
|
||||||
|
|
||||||
|
Argon2Kdf(Type type);
|
||||||
|
|
||||||
bool processParameters(const QVariantMap& p) override;
|
bool processParameters(const QVariantMap& p) override;
|
||||||
QVariantMap writeParameters() override;
|
QVariantMap writeParameters() override;
|
||||||
@ -32,6 +38,8 @@ public:
|
|||||||
|
|
||||||
quint32 version() const;
|
quint32 version() const;
|
||||||
bool setVersion(quint32 version);
|
bool setVersion(quint32 version);
|
||||||
|
Type type() const;
|
||||||
|
void setType(Type type);
|
||||||
quint64 memory() const;
|
quint64 memory() const;
|
||||||
bool setMemory(quint64 kibibytes);
|
bool setMemory(quint64 kibibytes);
|
||||||
quint32 parallelism() const;
|
quint32 parallelism() const;
|
||||||
@ -41,6 +49,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
int benchmarkImpl(int msec) const override;
|
int benchmarkImpl(int msec) const override;
|
||||||
|
|
||||||
|
Type m_type;
|
||||||
quint32 m_version;
|
quint32 m_version;
|
||||||
quint64 m_memory;
|
quint64 m_memory;
|
||||||
quint32 m_parallelism;
|
quint32 m_parallelism;
|
||||||
@ -49,6 +58,7 @@ private:
|
|||||||
Q_REQUIRED_RESULT static bool transformKeyRaw(const QByteArray& key,
|
Q_REQUIRED_RESULT static bool transformKeyRaw(const QByteArray& key,
|
||||||
const QByteArray& seed,
|
const QByteArray& seed,
|
||||||
quint32 version,
|
quint32 version,
|
||||||
|
Type type,
|
||||||
quint32 rounds,
|
quint32 rounds,
|
||||||
quint64 memory,
|
quint64 memory,
|
||||||
quint32 parallelism,
|
quint32 parallelism,
|
||||||
|
@ -67,6 +67,10 @@ QString CsvExporter::exportHeader()
|
|||||||
addColumn(header, "Password");
|
addColumn(header, "Password");
|
||||||
addColumn(header, "URL");
|
addColumn(header, "URL");
|
||||||
addColumn(header, "Notes");
|
addColumn(header, "Notes");
|
||||||
|
addColumn(header, "TOTP");
|
||||||
|
addColumn(header, "Icon");
|
||||||
|
addColumn(header, "Last Modified");
|
||||||
|
addColumn(header, "Created");
|
||||||
return header + QString("\n");
|
return header + QString("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +92,10 @@ QString CsvExporter::exportGroup(const Group* group, QString groupPath)
|
|||||||
addColumn(line, entry->password());
|
addColumn(line, entry->password());
|
||||||
addColumn(line, entry->url());
|
addColumn(line, entry->url());
|
||||||
addColumn(line, entry->notes());
|
addColumn(line, entry->notes());
|
||||||
|
addColumn(line, entry->totpSettingsString());
|
||||||
|
addColumn(line, QString::number(entry->iconNumber()));
|
||||||
|
addColumn(line, entry->timeInfo().lastModificationTime().toString(Qt::ISODate));
|
||||||
|
addColumn(line, entry->timeInfo().creationTime().toString(Qt::ISODate));
|
||||||
|
|
||||||
line.append("\n");
|
line.append("\n");
|
||||||
response.append(line);
|
response.append(line);
|
||||||
|
@ -30,7 +30,8 @@ const QUuid KeePass2::CIPHER_CHACHA20 = QUuid("d6038a2b-8b6f-4cb5-a524-339a31dbb
|
|||||||
|
|
||||||
const QUuid KeePass2::KDF_AES_KDBX3 = QUuid("c9d9f39a-628a-4460-bf74-0d08c18a4fea");
|
const QUuid KeePass2::KDF_AES_KDBX3 = QUuid("c9d9f39a-628a-4460-bf74-0d08c18a4fea");
|
||||||
const QUuid KeePass2::KDF_AES_KDBX4 = QUuid("7c02bb82-79a7-4ac0-927d-114a00648238");
|
const QUuid KeePass2::KDF_AES_KDBX4 = QUuid("7c02bb82-79a7-4ac0-927d-114a00648238");
|
||||||
const QUuid KeePass2::KDF_ARGON2 = QUuid("ef636ddf-8c29-444b-91f7-a9a403e30a0c");
|
const QUuid KeePass2::KDF_ARGON2D = QUuid("ef636ddf-8c29-444b-91f7-a9a403e30a0c");
|
||||||
|
const QUuid KeePass2::KDF_ARGON2ID = QUuid("9e298b19-56db-4773-b23d-fc3ec6f0a1e6");
|
||||||
|
|
||||||
const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xe8\x30\x09\x4b\x97\x20\x5d\x2a");
|
const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xe8\x30\x09\x4b\x97\x20\x5d\x2a");
|
||||||
|
|
||||||
@ -53,7 +54,8 @@ const QList<QPair<QUuid, QString>> KeePass2::CIPHERS{
|
|||||||
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20 256-bit"))};
|
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20 256-bit"))};
|
||||||
|
|
||||||
const QList<QPair<QUuid, QString>> KeePass2::KDFS{
|
const QList<QPair<QUuid, QString>> KeePass2::KDFS{
|
||||||
qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2 (KDBX 4 – recommended)")),
|
qMakePair(KeePass2::KDF_ARGON2D, QObject::tr("Argon2d (KDBX 4 – recommended)")),
|
||||||
|
qMakePair(KeePass2::KDF_ARGON2ID, QObject::tr("Argon2id (KDBX 4)")),
|
||||||
qMakePair(KeePass2::KDF_AES_KDBX4, QObject::tr("AES-KDF (KDBX 4)")),
|
qMakePair(KeePass2::KDF_AES_KDBX4, QObject::tr("AES-KDF (KDBX 4)")),
|
||||||
qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)"))};
|
qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)"))};
|
||||||
|
|
||||||
@ -109,8 +111,11 @@ QSharedPointer<Kdf> KeePass2::uuidToKdf(const QUuid& uuid)
|
|||||||
if (uuid == KDF_AES_KDBX4) {
|
if (uuid == KDF_AES_KDBX4) {
|
||||||
return QSharedPointer<AesKdf>::create();
|
return QSharedPointer<AesKdf>::create();
|
||||||
}
|
}
|
||||||
if (uuid == KDF_ARGON2) {
|
if (uuid == KDF_ARGON2D) {
|
||||||
return QSharedPointer<Argon2Kdf>::create();
|
return QSharedPointer<Argon2Kdf>::create(Argon2Kdf::Type::Argon2d);
|
||||||
|
}
|
||||||
|
if (uuid == KDF_ARGON2ID) {
|
||||||
|
return QSharedPointer<Argon2Kdf>::create(Argon2Kdf::Type::Argon2id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -53,7 +53,8 @@ namespace KeePass2
|
|||||||
|
|
||||||
extern const QUuid KDF_AES_KDBX3;
|
extern const QUuid KDF_AES_KDBX3;
|
||||||
extern const QUuid KDF_AES_KDBX4;
|
extern const QUuid KDF_AES_KDBX4;
|
||||||
extern const QUuid KDF_ARGON2;
|
extern const QUuid KDF_ARGON2D;
|
||||||
|
extern const QUuid KDF_ARGON2ID;
|
||||||
|
|
||||||
extern const QByteArray INNER_STREAM_SALSA20_IV;
|
extern const QByteArray INNER_STREAM_SALSA20_IV;
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ Database* OpVaultReader::readDatabase(QDir& opdataDir, const QString& password)
|
|||||||
key->addKey(QSharedPointer<PasswordKey>::create(password));
|
key->addKey(QSharedPointer<PasswordKey>::create(password));
|
||||||
|
|
||||||
QScopedPointer<Database> db(new Database());
|
QScopedPointer<Database> db(new Database());
|
||||||
db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D));
|
||||||
db->setCipher(KeePass2::CIPHER_AES256);
|
db->setCipher(KeePass2::CIPHER_AES256);
|
||||||
db->setKey(key, true, false);
|
db->setKey(key, true, false);
|
||||||
db->metadata()->setName(vaultName);
|
db->metadata()->setName(vaultName);
|
||||||
|
@ -59,6 +59,7 @@ static const QString aboutContributors = R"(
|
|||||||
<li>Kernellinux</li>
|
<li>Kernellinux</li>
|
||||||
<li>Micha Ober</li>
|
<li>Micha Ober</li>
|
||||||
<li>PublicByte</li>
|
<li>PublicByte</li>
|
||||||
|
<li>Clayton Casciato</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Notable Code Contributions:</h3>
|
<h3>Notable Code Contributions:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
@ -86,7 +87,6 @@ static const QString aboutContributors = R"(
|
|||||||
</ul>
|
</ul>
|
||||||
<h3>Patreon Supporters:</h3>
|
<h3>Patreon Supporters:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Igor Zinovik</li>
|
|
||||||
<li>Alexanderjb</li>
|
<li>Alexanderjb</li>
|
||||||
<li>Richard Ames</li>
|
<li>Richard Ames</li>
|
||||||
<li>SLmanDR</li>
|
<li>SLmanDR</li>
|
||||||
@ -94,7 +94,7 @@ static const QString aboutContributors = R"(
|
|||||||
<li>Tyler Gass</li>
|
<li>Tyler Gass</li>
|
||||||
<li>Nuutti Toivola</li>
|
<li>Nuutti Toivola</li>
|
||||||
<li>Gregory Werbin</li>
|
<li>Gregory Werbin</li>
|
||||||
<li>Lionel Laské</li>
|
<li>Lionel Laské</li>
|
||||||
<li>Ivar</li>
|
<li>Ivar</li>
|
||||||
<li>Darren</li>
|
<li>Darren</li>
|
||||||
<li>Brad</li>
|
<li>Brad</li>
|
||||||
|
@ -59,7 +59,12 @@ void Clipboard::setText(const QString& text, bool clear)
|
|||||||
clipboard->setMimeData(mime, QClipboard::Clipboard);
|
clipboard->setMimeData(mime, QClipboard::Clipboard);
|
||||||
#else
|
#else
|
||||||
mime->setText(text);
|
mime->setText(text);
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
mime->setData("x-kde-passwordManagerHint", QByteArrayLiteral("secret"));
|
mime->setData("x-kde-passwordManagerHint", QByteArrayLiteral("secret"));
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
mime->setData("ExcludeClipboardContentFromMonitorProcessing", QByteArrayLiteral("1"));
|
||||||
|
#endif
|
||||||
clipboard->setMimeData(mime, QClipboard::Clipboard);
|
clipboard->setMimeData(mime, QClipboard::Clipboard);
|
||||||
|
|
||||||
if (clipboard->supportsSelection()) {
|
if (clipboard->supportsSelection()) {
|
||||||
|
@ -20,12 +20,19 @@
|
|||||||
#include "DatabaseWidget.h"
|
#include "DatabaseWidget.h"
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QtPlatformHeaders/QWindowsWindowFunctions>
|
||||||
|
#endif
|
||||||
|
|
||||||
DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
|
DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_view(new DatabaseOpenWidget(this))
|
, m_view(new DatabaseOpenWidget(this))
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Unlock Database - KeePassXC"));
|
setWindowTitle(tr("Unlock Database - KeePassXC"));
|
||||||
setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
|
setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow);
|
||||||
|
#endif
|
||||||
connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool)));
|
connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool)));
|
||||||
auto* layout = new QVBoxLayout();
|
auto* layout = new QVBoxLayout();
|
||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
|
@ -165,6 +165,7 @@ void DatabaseOpenWidget::clearForms()
|
|||||||
m_ui->editPassword->setText("");
|
m_ui->editPassword->setText("");
|
||||||
m_ui->editPassword->setShowPassword(false);
|
m_ui->editPassword->setShowPassword(false);
|
||||||
m_ui->keyFileLineEdit->clear();
|
m_ui->keyFileLineEdit->clear();
|
||||||
|
m_ui->keyFileLineEdit->setShowPassword(false);
|
||||||
m_ui->checkTouchID->setChecked(false);
|
m_ui->checkTouchID->setChecked(false);
|
||||||
m_ui->challengeResponseCombo->clear();
|
m_ui->challengeResponseCombo->clear();
|
||||||
m_db.reset();
|
m_db.reset();
|
||||||
@ -301,12 +302,14 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::buildDatabaseKey()
|
|||||||
m_ui->messageWidget->showMessage(tr("Failed to open key file: %1").arg(errorMsg), MessageWidget::Error);
|
m_ui->messageWidget->showMessage(tr("Failed to open key file: %1").arg(errorMsg), MessageWidget::Error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (key->type() != FileKey::Hashed && !config()->get(Config::Messages_NoLegacyKeyFileWarning).toBool()) {
|
if (key->type() != FileKey::KeePass2XMLv2 && key->type() != FileKey::Hashed
|
||||||
|
&& !config()->get(Config::Messages_NoLegacyKeyFileWarning).toBool()) {
|
||||||
QMessageBox legacyWarning;
|
QMessageBox legacyWarning;
|
||||||
legacyWarning.setWindowTitle(tr("Legacy key file format"));
|
legacyWarning.setWindowTitle(tr("Old key file format"));
|
||||||
legacyWarning.setText(tr("You are using a legacy key file format which may become\n"
|
legacyWarning.setText(tr("You are using an old key file format which KeePassXC may<br>"
|
||||||
"unsupported in the future.\n\n"
|
"stop supporting in the future.<br><br>"
|
||||||
"Please consider generating a new key file."));
|
"Please consider generating a new key file by going to:<br>"
|
||||||
|
"<strong>Database / Database Security / Change Key File.</strong><br>"));
|
||||||
legacyWarning.setIcon(QMessageBox::Icon::Warning);
|
legacyWarning.setIcon(QMessageBox::Icon::Warning);
|
||||||
legacyWarning.addButton(QMessageBox::Ok);
|
legacyWarning.addButton(QMessageBox::Ok);
|
||||||
legacyWarning.setDefaultButton(QMessageBox::Ok);
|
legacyWarning.setDefaultButton(QMessageBox::Ok);
|
||||||
@ -355,7 +358,7 @@ void DatabaseOpenWidget::reject()
|
|||||||
|
|
||||||
void DatabaseOpenWidget::browseKeyFile()
|
void DatabaseOpenWidget::browseKeyFile()
|
||||||
{
|
{
|
||||||
QString filters = QString("%1 (*);;%2 (*.key)").arg(tr("All files"), tr("Key files"));
|
QString filters = QString("%1 (*);;%2 (*.keyx; *.key)").arg(tr("All files"), tr("Key files"));
|
||||||
if (!config()->get(Config::RememberLastKeyFiles).toBool()) {
|
if (!config()->get(Config::RememberLastKeyFiles).toBool()) {
|
||||||
fileDialog()->setNextForgetDialog();
|
fileDialog()->setNextForgetDialog();
|
||||||
}
|
}
|
||||||
@ -378,6 +381,7 @@ void DatabaseOpenWidget::browseKeyFile()
|
|||||||
void DatabaseOpenWidget::clearKeyFileText()
|
void DatabaseOpenWidget::clearKeyFileText()
|
||||||
{
|
{
|
||||||
m_ui->keyFileLineEdit->clear();
|
m_ui->keyFileLineEdit->clear();
|
||||||
|
m_ui->keyFileLineEdit->setShowPassword(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseOpenWidget::pollHardwareKey()
|
void DatabaseOpenWidget::pollHardwareKey()
|
||||||
|
@ -406,7 +406,7 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="keyFileLineEdit">
|
<widget class="PasswordEdit" name="keyFileLineEdit">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
@ -416,6 +416,9 @@
|
|||||||
<property name="accessibleName">
|
<property name="accessibleName">
|
||||||
<string>Key file to unlock the database</string>
|
<string>Key file to unlock the database</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
<property name="clearButtonEnabled">
|
<property name="clearButtonEnabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -216,7 +216,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
|
|||||||
|
|
||||||
#ifdef WITH_XC_SSHAGENT
|
#ifdef WITH_XC_SSHAGENT
|
||||||
if (sshAgent()->isEnabled()) {
|
if (sshAgent()->isEnabled()) {
|
||||||
connect(this, SIGNAL(databaseLockRequested()), sshAgent(), SLOT(databaseLocked()));
|
connect(this, SIGNAL(databaseLocked()), sshAgent(), SLOT(databaseLocked()));
|
||||||
connect(this, SIGNAL(databaseUnlocked()), sshAgent(), SLOT(databaseUnlocked()));
|
connect(this, SIGNAL(databaseUnlocked()), sshAgent(), SLOT(databaseUnlocked()));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -437,6 +437,7 @@ void DatabaseWidget::showTotp()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto totpDialog = new TotpDialog(this, currentEntry);
|
auto totpDialog = new TotpDialog(this, currentEntry);
|
||||||
|
connect(this, &DatabaseWidget::databaseLockRequested, totpDialog, &TotpDialog::close);
|
||||||
totpDialog->open();
|
totpDialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +461,7 @@ void DatabaseWidget::setupTotp()
|
|||||||
|
|
||||||
auto setupTotpDialog = new TotpSetupDialog(this, currentEntry);
|
auto setupTotpDialog = new TotpSetupDialog(this, currentEntry);
|
||||||
connect(setupTotpDialog, SIGNAL(totpUpdated()), SIGNAL(entrySelectionChanged()));
|
connect(setupTotpDialog, SIGNAL(totpUpdated()), SIGNAL(entrySelectionChanged()));
|
||||||
|
connect(this, &DatabaseWidget::databaseLockRequested, setupTotpDialog, &TotpSetupDialog::close);
|
||||||
setupTotpDialog->open();
|
setupTotpDialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +703,7 @@ void DatabaseWidget::showTotpKeyQrCode()
|
|||||||
auto currentEntry = currentSelectedEntry();
|
auto currentEntry = currentSelectedEntry();
|
||||||
if (currentEntry) {
|
if (currentEntry) {
|
||||||
auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry);
|
auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry);
|
||||||
|
connect(this, &DatabaseWidget::databaseLockRequested, totpDisplayDialog, &TotpExportSettingsDialog::close);
|
||||||
totpDisplayDialog->open();
|
totpDisplayDialog->open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1081,6 +1084,9 @@ void DatabaseWidget::loadDatabase(bool accepted)
|
|||||||
replaceDatabase(openWidget->database());
|
replaceDatabase(openWidget->database());
|
||||||
switchToMainView();
|
switchToMainView();
|
||||||
processAutoOpen();
|
processAutoOpen();
|
||||||
|
restoreGroupEntryFocus(m_groupBeforeLock, m_entryBeforeLock);
|
||||||
|
m_groupBeforeLock = QUuid();
|
||||||
|
m_entryBeforeLock = QUuid();
|
||||||
m_saveAttempts = 0;
|
m_saveAttempts = 0;
|
||||||
emit databaseUnlocked();
|
emit databaseUnlocked();
|
||||||
if (config()->get(Config::MinimizeAfterUnlock).toBool()) {
|
if (config()->get(Config::MinimizeAfterUnlock).toBool()) {
|
||||||
@ -1528,6 +1534,11 @@ bool DatabaseWidget::lock()
|
|||||||
|
|
||||||
emit databaseLockRequested();
|
emit databaseLockRequested();
|
||||||
|
|
||||||
|
// ignore event if we are active and a modal dialog is still open (such as a message box or file dialog)
|
||||||
|
if (isVisible() && QApplication::activeModalWidget()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
clipboard()->clearCopiedText();
|
clipboard()->clearCopiedText();
|
||||||
|
|
||||||
if (isEditWidgetModified()) {
|
if (isEditWidgetModified()) {
|
||||||
|
@ -74,6 +74,18 @@ void EditWidget::addPage(const QString& labelText, const QIcon& icon, QWidget* w
|
|||||||
m_ui->categoryList->addCategory(labelText, icon);
|
m_ui->categoryList->addCategory(labelText, icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EditWidget::hasPage(QWidget* widget)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_ui->stackedWidget->count(); i++) {
|
||||||
|
auto* scrollArea = qobject_cast<QScrollArea*>(m_ui->stackedWidget->widget(i));
|
||||||
|
if (scrollArea && scrollArea->widget() == widget) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void EditWidget::setPageHidden(QWidget* widget, bool hidden)
|
void EditWidget::setPageHidden(QWidget* widget, bool hidden)
|
||||||
{
|
{
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
~EditWidget();
|
~EditWidget();
|
||||||
|
|
||||||
void addPage(const QString& labelText, const QIcon& icon, QWidget* widget);
|
void addPage(const QString& labelText, const QIcon& icon, QWidget* widget);
|
||||||
|
bool hasPage(QWidget* widget);
|
||||||
void setPageHidden(QWidget* widget, bool hidden);
|
void setPageHidden(QWidget* widget, bool hidden);
|
||||||
void setCurrentPage(int index);
|
void setCurrentPage(int index);
|
||||||
void setHeadline(const QString& text);
|
void setHeadline(const QString& text);
|
||||||
|
@ -286,14 +286,18 @@ void EntryPreviewWidget::updateEntryAdvancedTab()
|
|||||||
|
|
||||||
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAdvancedTab, hasAttributes || hasAttachments);
|
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAdvancedTab, hasAttributes || hasAttachments);
|
||||||
if (hasAttributes) {
|
if (hasAttributes) {
|
||||||
QString attributesText;
|
QString attributesText("<table>");
|
||||||
for (const QString& key : customAttributes) {
|
for (const QString& key : customAttributes) {
|
||||||
QString value = m_currentEntry->attributes()->value(key);
|
QString value;
|
||||||
if (m_currentEntry->attributes()->isProtected(key)) {
|
if (m_currentEntry->attributes()->isProtected(key)) {
|
||||||
value = "<i>" + tr("[PROTECTED]") + "</i>";
|
value = "<i>" + tr("[PROTECTED]") + "</i>";
|
||||||
|
} else {
|
||||||
|
value = m_currentEntry->attributes()->value(key).toHtmlEscaped();
|
||||||
|
value.replace('\n', QLatin1String("<br/>"));
|
||||||
}
|
}
|
||||||
attributesText.append(tr("<b>%1</b>: %2", "attributes line").arg(key, value).append("<br/>"));
|
attributesText.append(tr("<tr><td><b>%1</b>:</td><td>%2</td></tr>", "attributes line").arg(key, value));
|
||||||
}
|
}
|
||||||
|
attributesText.append("</table>");
|
||||||
m_ui->entryAttributesEdit->setText(attributesText);
|
m_ui->entryAttributesEdit->setText(attributesText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +307,8 @@ void EntryPreviewWidget::updateEntryAdvancedTab()
|
|||||||
void EntryPreviewWidget::updateEntryAutotypeTab()
|
void EntryPreviewWidget::updateEntryAutotypeTab()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_currentEntry);
|
Q_ASSERT(m_currentEntry);
|
||||||
|
|
||||||
|
m_ui->entrySequenceLabel->setText(m_currentEntry->effectiveAutoTypeSequence());
|
||||||
m_ui->entryAutotypeTree->clear();
|
m_ui->entryAutotypeTree->clear();
|
||||||
QList<QTreeWidgetItem*> items;
|
QList<QTreeWidgetItem*> items;
|
||||||
const AutoTypeAssociations* autotypeAssociations = m_currentEntry->autoTypeAssociations();
|
const AutoTypeAssociations* autotypeAssociations = m_currentEntry->autoTypeAssociations();
|
||||||
@ -314,7 +320,7 @@ void EntryPreviewWidget::updateEntryAutotypeTab()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_ui->entryAutotypeTree->addTopLevelItems(items);
|
m_ui->entryAutotypeTree->addTopLevelItems(items);
|
||||||
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, !items.isEmpty());
|
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, m_currentEntry->autoTypeEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntryPreviewWidget::updateGroupHeaderLine()
|
void EntryPreviewWidget::updateGroupHeaderLine()
|
||||||
|
@ -705,6 +705,62 @@
|
|||||||
<string>Autotype</string>
|
<string>Autotype</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="entryAutotypeWidget" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="entrySequenceTitleLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Default Sequence</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="entrySequenceLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">sequence</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeWidget" name="entryAutotypeTree">
|
<widget class="QTreeWidget" name="entryAutotypeTree">
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
|
@ -497,6 +497,14 @@ MainWindow::MainWindow()
|
|||||||
connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp()));
|
connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp()));
|
||||||
connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts()));
|
connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts()));
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
// Install event filter for empty-area drag
|
||||||
|
auto* eventFilter = new MainWindowEventFilter(this);
|
||||||
|
m_ui->menubar->installEventFilter(eventFilter);
|
||||||
|
m_ui->toolBar->installEventFilter(eventFilter);
|
||||||
|
m_ui->tabWidget->tabBar()->installEventFilter(eventFilter);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
setUnifiedTitleAndToolBarOnMac(true);
|
setUnifiedTitleAndToolBarOnMac(true);
|
||||||
#endif
|
#endif
|
||||||
@ -519,6 +527,11 @@ MainWindow::MainWindow()
|
|||||||
m_ui->actionGroupDownloadFavicons->setVisible(false);
|
m_ui->actionGroupDownloadFavicons->setVisible(false);
|
||||||
m_ui->actionEntryDownloadIcon->setVisible(false);
|
m_ui->actionEntryDownloadIcon->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef WITH_XC_DOCS
|
||||||
|
m_ui->actionGettingStarted->setVisible(false);
|
||||||
|
m_ui->actionUserGuide->setVisible(false);
|
||||||
|
m_ui->actionKeyboardShortcuts->setVisible(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
|
connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
|
||||||
@ -743,7 +756,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
|||||||
m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
|
m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
|
||||||
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
|
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
|
||||||
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
|
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
|
||||||
|
#ifdef WITH_XC_NETWORKING
|
||||||
m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
|
m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
|
||||||
|
#endif
|
||||||
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
|
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
|
||||||
&& !recycleBinSelected);
|
&& !recycleBinSelected);
|
||||||
m_ui->actionDatabaseSecurity->setEnabled(true);
|
m_ui->actionDatabaseSecurity->setEnabled(true);
|
||||||
@ -1529,11 +1544,6 @@ void MainWindow::toggleWindow()
|
|||||||
|
|
||||||
void MainWindow::lockDatabasesAfterInactivity()
|
void MainWindow::lockDatabasesAfterInactivity()
|
||||||
{
|
{
|
||||||
// ignore event if a modal dialog is open (such as a message box or file dialog)
|
|
||||||
if (QApplication::activeModalWidget()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ui->tabWidget->lockDatabases();
|
m_ui->tabWidget->lockDatabases();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1754,3 +1764,44 @@ void MainWindow::initViewMenu()
|
|||||||
config()->set(Config::GUI_HidePasswords, checked);
|
config()->set(Config::GUI_HidePasswords, checked);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
|
||||||
|
MainWindowEventFilter::MainWindowEventFilter(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MainWindow event filter to initiate empty-area drag on the toolbar, menubar, and tabbar.
|
||||||
|
*/
|
||||||
|
bool MainWindowEventFilter::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
{
|
||||||
|
auto* mainWindow = getMainWindow();
|
||||||
|
if (!mainWindow || !mainWindow->m_ui) {
|
||||||
|
return QObject::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
if (watched == mainWindow->m_ui->menubar) {
|
||||||
|
mainWindow->windowHandle()->startSystemMove();
|
||||||
|
// Continue processing events, so menus keep working.
|
||||||
|
return false;
|
||||||
|
} else if (watched == mainWindow->m_ui->toolBar) {
|
||||||
|
if (!mainWindow->m_ui->toolBar->isMovable() || mainWindow->m_ui->toolBar->cursor() != Qt::SizeAllCursor) {
|
||||||
|
mainWindow->windowHandle()->startSystemMove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (watched == mainWindow->m_ui->tabWidget->tabBar()) {
|
||||||
|
auto* m = static_cast<QMouseEvent*>(event);
|
||||||
|
if (mainWindow->m_ui->tabWidget->tabBar()->tabAt(m->pos()) == -1) {
|
||||||
|
mainWindow->windowHandle()->startSystemMove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -35,6 +35,7 @@ namespace Ui
|
|||||||
|
|
||||||
class InactivityTimer;
|
class InactivityTimer;
|
||||||
class SearchWidget;
|
class SearchWidget;
|
||||||
|
class MainWindowEventFilter;
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
@ -181,8 +182,21 @@ private:
|
|||||||
QTimer m_updateCheckTimer;
|
QTimer m_updateCheckTimer;
|
||||||
QTimer m_trayIconTriggerTimer;
|
QTimer m_trayIconTriggerTimer;
|
||||||
QSystemTrayIcon::ActivationReason m_trayIconTriggerReason;
|
QSystemTrayIcon::ActivationReason m_trayIconTriggerReason;
|
||||||
|
|
||||||
|
friend class MainWindowEventFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
class MainWindowEventFilter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindowEventFilter(QObject* parent);
|
||||||
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return instance of MainWindow created on app load
|
* Return instance of MainWindow created on app load
|
||||||
* non-gui instances will return nullptr
|
* non-gui instances will return nullptr
|
||||||
|
@ -173,9 +173,9 @@ void PasswordGeneratorWidget::saveSettings()
|
|||||||
|
|
||||||
config()->set(Config::PasswordGenerator_AdvancedMode, m_ui->buttonAdvancedMode->isChecked());
|
config()->set(Config::PasswordGenerator_AdvancedMode, m_ui->buttonAdvancedMode->isChecked());
|
||||||
if (m_ui->buttonAdvancedMode->isChecked()) {
|
if (m_ui->buttonAdvancedMode->isChecked()) {
|
||||||
config()->set(Config::PasswordGenerator_SpecialChars, m_ui->checkBoxSpecialChars->isChecked());
|
|
||||||
} else {
|
|
||||||
config()->set(Config::PasswordGenerator_Logograms, m_ui->checkBoxSpecialChars->isChecked());
|
config()->set(Config::PasswordGenerator_Logograms, m_ui->checkBoxSpecialChars->isChecked());
|
||||||
|
} else {
|
||||||
|
config()->set(Config::PasswordGenerator_SpecialChars, m_ui->checkBoxSpecialChars->isChecked());
|
||||||
}
|
}
|
||||||
config()->set(Config::PasswordGenerator_Braces, m_ui->checkBoxBraces->isChecked());
|
config()->set(Config::PasswordGenerator_Braces, m_ui->checkBoxBraces->isChecked());
|
||||||
config()->set(Config::PasswordGenerator_Punctuation, m_ui->checkBoxPunctuation->isChecked());
|
config()->set(Config::PasswordGenerator_Punctuation, m_ui->checkBoxPunctuation->isChecked());
|
||||||
|
@ -259,9 +259,6 @@ QProgressBar::chunk {
|
|||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
|
||||||
<number>128</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>20</number>
|
<number>20</number>
|
||||||
</property>
|
</property>
|
||||||
|
@ -31,10 +31,7 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
|
|||||||
, m_ui(new Ui::TotpDialog())
|
, m_ui(new Ui::TotpDialog())
|
||||||
, m_entry(entry)
|
, m_entry(entry)
|
||||||
{
|
{
|
||||||
if (!m_entry->hasTotp()) {
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
@ -42,14 +39,11 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
|
|||||||
resetCounter();
|
resetCounter();
|
||||||
updateProgressBar();
|
updateProgressBar();
|
||||||
|
|
||||||
connect(parent, SIGNAL(databaseLocked()), SLOT(close()));
|
|
||||||
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
|
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
|
||||||
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateSeconds()));
|
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateSeconds()));
|
||||||
m_totpUpdateTimer.start(m_step * 10);
|
m_totpUpdateTimer.start(m_step * 10);
|
||||||
updateTotp();
|
updateTotp();
|
||||||
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
|
|
||||||
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));
|
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));
|
||||||
|
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Copy"));
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Copy"));
|
||||||
|
@ -59,7 +59,6 @@ TotpExportSettingsDialog::TotpExportSettingsDialog(DatabaseWidget* parent, Entry
|
|||||||
connect(m_buttonBox, SIGNAL(rejected()), SLOT(close()));
|
connect(m_buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||||
connect(m_buttonBox, SIGNAL(accepted()), SLOT(copyToClipboard()));
|
connect(m_buttonBox, SIGNAL(accepted()), SLOT(copyToClipboard()));
|
||||||
connect(m_timer, SIGNAL(timeout()), SLOT(autoClose()));
|
connect(m_timer, SIGNAL(timeout()), SLOT(autoClose()));
|
||||||
connect(parent, SIGNAL(lockedDatabase()), SLOT(close()));
|
|
||||||
|
|
||||||
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));
|
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "format/KeePass2Writer.h"
|
#include "format/KeePass2Writer.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/MessageWidget.h"
|
#include "gui/MessageWidget.h"
|
||||||
|
#include "totp/totp.h"
|
||||||
|
|
||||||
// I wanted to make the CSV import GUI future-proof, so if one day you need a new field,
|
// I wanted to make the CSV import GUI future-proof, so if one day you need a new field,
|
||||||
// all you have to do is add a field to m_columnHeader, and the GUI will follow:
|
// all you have to do is add a field to m_columnHeader, and the GUI will follow:
|
||||||
@ -39,7 +40,8 @@ CsvImportWidget::CsvImportWidget(QWidget* parent)
|
|||||||
, m_comboModel(new QStringListModel(this))
|
, m_comboModel(new QStringListModel(this))
|
||||||
, m_columnHeader(QStringList() << QObject::tr("Group") << QObject::tr("Title") << QObject::tr("Username")
|
, m_columnHeader(QStringList() << QObject::tr("Group") << QObject::tr("Title") << QObject::tr("Username")
|
||||||
<< QObject::tr("Password") << QObject::tr("URL") << QObject::tr("Notes")
|
<< QObject::tr("Password") << QObject::tr("URL") << QObject::tr("Notes")
|
||||||
<< QObject::tr("Last Modified") << QObject::tr("Created"))
|
<< QObject::tr("TOTP") << QObject::tr("Icon") << QObject::tr("Last Modified")
|
||||||
|
<< QObject::tr("Created"))
|
||||||
, m_fieldSeparatorList(QStringList() << ","
|
, m_fieldSeparatorList(QStringList() << ","
|
||||||
<< ";"
|
<< ";"
|
||||||
<< "-"
|
<< "-"
|
||||||
@ -54,7 +56,7 @@ CsvImportWidget::CsvImportWidget(QWidget* parent)
|
|||||||
m_ui->messageWidget->setHidden(true);
|
m_ui->messageWidget->setHidden(true);
|
||||||
|
|
||||||
m_combos << m_ui->groupCombo << m_ui->titleCombo << m_ui->usernameCombo << m_ui->passwordCombo << m_ui->urlCombo
|
m_combos << m_ui->groupCombo << m_ui->titleCombo << m_ui->usernameCombo << m_ui->passwordCombo << m_ui->urlCombo
|
||||||
<< m_ui->notesCombo << m_ui->lastModifiedCombo << m_ui->createdCombo;
|
<< m_ui->notesCombo << m_ui->totpCombo << m_ui->iconCombo << m_ui->lastModifiedCombo << m_ui->createdCombo;
|
||||||
|
|
||||||
for (auto combo : m_combos) {
|
for (auto combo : m_combos) {
|
||||||
combo->setModel(m_comboModel);
|
combo->setModel(m_comboModel);
|
||||||
@ -206,17 +208,38 @@ void CsvImportWidget::writeDatabase()
|
|||||||
entry->setUrl(m_parserModel->data(m_parserModel->index(r, 4)).toString());
|
entry->setUrl(m_parserModel->data(m_parserModel->index(r, 4)).toString());
|
||||||
entry->setNotes(m_parserModel->data(m_parserModel->index(r, 5)).toString());
|
entry->setNotes(m_parserModel->data(m_parserModel->index(r, 5)).toString());
|
||||||
|
|
||||||
TimeInfo timeInfo;
|
|
||||||
if (m_parserModel->data(m_parserModel->index(r, 6)).isValid()) {
|
if (m_parserModel->data(m_parserModel->index(r, 6)).isValid()) {
|
||||||
qint64 lastModified = m_parserModel->data(m_parserModel->index(r, 6)).toString().toLongLong();
|
auto totp = Totp::parseSettings(m_parserModel->data(m_parserModel->index(r, 6)).toString());
|
||||||
if (lastModified) {
|
entry->setTotp(totp);
|
||||||
timeInfo.setLastModificationTime(Clock::datetimeUtc(lastModified * 1000));
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int icon = m_parserModel->data(m_parserModel->index(r, 7)).toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
entry->setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeInfo timeInfo;
|
||||||
|
if (m_parserModel->data(m_parserModel->index(r, 8)).isValid()) {
|
||||||
|
auto datetime = m_parserModel->data(m_parserModel->index(r, 8)).toString();
|
||||||
|
if (datetime.contains(QRegularExpression("^\\d+$"))) {
|
||||||
|
timeInfo.setLastModificationTime(Clock::datetimeUtc(datetime.toLongLong() * 1000));
|
||||||
|
} else {
|
||||||
|
auto lastModified = QDateTime::fromString(datetime, Qt::ISODate);
|
||||||
|
if (lastModified.isValid()) {
|
||||||
|
timeInfo.setLastModificationTime(lastModified);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_parserModel->data(m_parserModel->index(r, 7)).isValid()) {
|
if (m_parserModel->data(m_parserModel->index(r, 9)).isValid()) {
|
||||||
qint64 created = m_parserModel->data(m_parserModel->index(r, 7)).toString().toLongLong();
|
auto datetime = m_parserModel->data(m_parserModel->index(r, 9)).toString();
|
||||||
if (created) {
|
if (datetime.contains(QRegularExpression("^\\d+$"))) {
|
||||||
timeInfo.setCreationTime(Clock::datetimeUtc(created * 1000));
|
timeInfo.setCreationTime(Clock::datetimeUtc(datetime.toLongLong() * 1000));
|
||||||
|
} else {
|
||||||
|
auto created = QDateTime::fromString(datetime, Qt::ISODate);
|
||||||
|
if (created.isValid()) {
|
||||||
|
timeInfo.setCreationTime(created);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry->setTimeInfo(timeInfo);
|
entry->setTimeInfo(timeInfo);
|
||||||
|
@ -96,21 +96,8 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_combos">
|
<layout class="QGridLayout" name="gridLayout_combos">
|
||||||
<item row="2" column="2">
|
<item row="3" column="1">
|
||||||
<widget class="QLabel" name="lastModifiedLabel">
|
<widget class="QComboBox" name="passwordCombo"/>
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<weight>50</weight>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Last Modified</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="passwordLabel">
|
<widget class="QLabel" name="passwordLabel">
|
||||||
@ -126,13 +113,13 @@
|
|||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="2" column="0">
|
||||||
<widget class="QComboBox" name="passwordCombo"/>
|
<widget class="QLabel" name="usernameLabel">
|
||||||
</item>
|
|
||||||
<item row="3" column="2">
|
|
||||||
<widget class="QLabel" name="createdLabel">
|
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>50</weight>
|
<weight>50</weight>
|
||||||
@ -140,28 +127,21 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Created</string>
|
<string>Username</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="2" column="1">
|
||||||
<widget class="QLabel" name="notesLabel">
|
<widget class="QComboBox" name="usernameCombo"/>
|
||||||
<property name="font">
|
</item>
|
||||||
<font>
|
<item row="1" column="1">
|
||||||
<weight>50</weight>
|
<widget class="QComboBox" name="titleCombo"/>
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Notes</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="titleLabel">
|
<widget class="QLabel" name="titleLabel">
|
||||||
@ -177,6 +157,9 @@
|
|||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
@ -196,9 +179,12 @@
|
|||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="urlLabel">
|
<widget class="QLabel" name="urlLabel">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
@ -212,10 +198,16 @@
|
|||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="4" column="1">
|
||||||
<widget class="QLabel" name="usernameLabel">
|
<widget class="QComboBox" name="urlCombo"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QLabel" name="notesLabel">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>50</weight>
|
<weight>50</weight>
|
||||||
@ -223,30 +215,106 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Username</string>
|
<string>Notes</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QComboBox" name="usernameCombo"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QComboBox" name="titleCombo"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="3">
|
<item row="0" column="3">
|
||||||
<widget class="QComboBox" name="urlCombo"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="3">
|
|
||||||
<widget class="QComboBox" name="notesCombo"/>
|
<widget class="QComboBox" name="notesCombo"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="3">
|
<item row="1" column="2">
|
||||||
<widget class="QComboBox" name="lastModifiedCombo"/>
|
<widget class="QLabel" name="totpLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>TOTP</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<widget class="QComboBox" name="totpCombo"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="3">
|
||||||
|
<widget class="QComboBox" name="createdCombo"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="QLabel" name="createdLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Created</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QLabel" name="lastModifiedLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Last Modified</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="3">
|
<item row="3" column="3">
|
||||||
<widget class="QComboBox" name="createdCombo"/>
|
<widget class="QComboBox" name="lastModifiedCombo"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QLabel" name="iconLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Icon</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="3">
|
||||||
|
<widget class="QComboBox" name="iconCombo"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
@ -682,10 +750,6 @@
|
|||||||
<tabstop>titleCombo</tabstop>
|
<tabstop>titleCombo</tabstop>
|
||||||
<tabstop>usernameCombo</tabstop>
|
<tabstop>usernameCombo</tabstop>
|
||||||
<tabstop>passwordCombo</tabstop>
|
<tabstop>passwordCombo</tabstop>
|
||||||
<tabstop>urlCombo</tabstop>
|
|
||||||
<tabstop>notesCombo</tabstop>
|
|
||||||
<tabstop>lastModifiedCombo</tabstop>
|
|
||||||
<tabstop>createdCombo</tabstop>
|
|
||||||
<tabstop>comboBoxCodec</tabstop>
|
<tabstop>comboBoxCodec</tabstop>
|
||||||
<tabstop>comboBoxTextQualifier</tabstop>
|
<tabstop>comboBoxTextQualifier</tabstop>
|
||||||
<tabstop>comboBoxFieldSeparator</tabstop>
|
<tabstop>comboBoxFieldSeparator</tabstop>
|
||||||
|
@ -47,12 +47,12 @@ bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileKey->type() != FileKey::Hashed) {
|
if (fileKey->type() != FileKey::KeePass2XMLv2 && fileKey->type() != FileKey::Hashed) {
|
||||||
QMessageBox::warning(getMainWindow(),
|
QMessageBox::warning(getMainWindow(),
|
||||||
tr("Legacy key file format"),
|
tr("Old key file format"),
|
||||||
tr("You are using a legacy key file format which may become\n"
|
tr("You selected a key file in an old format which KeePassXC<br>"
|
||||||
"unsupported in the future.\n\n"
|
"may stop supporting in the future.<br><br>"
|
||||||
"Generate a new key file in the database security settings."),
|
"Please consider generating a new key file instead."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ void KeyFileEditWidget::createKeyFile()
|
|||||||
if (!m_compEditWidget) {
|
if (!m_compEditWidget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
|
QString filters = QString("%1 (*.keyx; *.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
|
||||||
QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters);
|
QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters);
|
||||||
|
|
||||||
if (!fileName.isEmpty()) {
|
if (!fileName.isEmpty()) {
|
||||||
@ -119,7 +119,7 @@ void KeyFileEditWidget::browseKeyFile()
|
|||||||
if (!m_compEditWidget) {
|
if (!m_compEditWidget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
|
QString filters = QString("%1 (*.keyx; *.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
|
||||||
QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters);
|
QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters);
|
||||||
|
|
||||||
if (QFileInfo(fileName).canonicalFilePath() == m_parent->getDatabase()->canonicalFilePath()) {
|
if (QFileInfo(fileName).canonicalFilePath() == m_parent->getDatabase()->canonicalFilePath()) {
|
||||||
|
@ -43,7 +43,7 @@ DatabaseSettingsWidgetEncryption::DatabaseSettingsWidgetEncryption(QWidget* pare
|
|||||||
connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
|
connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
|
||||||
connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
|
connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
|
||||||
|
|
||||||
m_ui->compatibilitySelection->addItem(tr("KDBX 4.0 (recommended)"), KeePass2::KDF_ARGON2.toByteArray());
|
m_ui->compatibilitySelection->addItem(tr("KDBX 4.0 (recommended)"), KeePass2::KDF_ARGON2D.toByteArray());
|
||||||
m_ui->compatibilitySelection->addItem(tr("KDBX 3.1"), KeePass2::KDF_AES_KDBX3.toByteArray());
|
m_ui->compatibilitySelection->addItem(tr("KDBX 3.1"), KeePass2::KDF_AES_KDBX3.toByteArray());
|
||||||
m_ui->decryptionTimeSlider->setMinimum(Kdf::MIN_ENCRYPTION_TIME / 100);
|
m_ui->decryptionTimeSlider->setMinimum(Kdf::MIN_ENCRYPTION_TIME / 100);
|
||||||
m_ui->decryptionTimeSlider->setMaximum(Kdf::MAX_ENCRYPTION_TIME / 100);
|
m_ui->decryptionTimeSlider->setMaximum(Kdf::MAX_ENCRYPTION_TIME / 100);
|
||||||
@ -75,6 +75,9 @@ DatabaseSettingsWidgetEncryption::~DatabaseSettingsWidgetEncryption()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define IS_ARGON2(uuid) (uuid == KeePass2::KDF_ARGON2D || uuid == KeePass2::KDF_ARGON2ID)
|
||||||
|
#define IS_AES_KDF(uuid) (uuid == KeePass2::KDF_AES_KDBX3 || uuid == KeePass2::KDF_AES_KDBX4)
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::initialize()
|
void DatabaseSettingsWidgetEncryption::initialize()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_db);
|
Q_ASSERT(m_db);
|
||||||
@ -85,7 +88,7 @@ void DatabaseSettingsWidgetEncryption::initialize()
|
|||||||
bool isDirty = false;
|
bool isDirty = false;
|
||||||
|
|
||||||
if (!m_db->kdf()) {
|
if (!m_db->kdf()) {
|
||||||
m_db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
m_db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D));
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
if (!m_db->key()) {
|
if (!m_db->key()) {
|
||||||
@ -175,7 +178,7 @@ void DatabaseSettingsWidgetEncryption::loadKdfParameters()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
|
m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
|
||||||
if (m_db->kdf()->uuid() == KeePass2::KDF_ARGON2) {
|
if (IS_ARGON2(m_db->kdf()->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
|
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
|
||||||
m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
|
m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
|
||||||
@ -188,13 +191,10 @@ void DatabaseSettingsWidgetEncryption::updateKdfFields()
|
|||||||
{
|
{
|
||||||
QUuid id = m_db->kdf()->uuid();
|
QUuid id = m_db->kdf()->uuid();
|
||||||
|
|
||||||
bool memoryVisible = (id == KeePass2::KDF_ARGON2);
|
m_ui->memoryUsageLabel->setVisible(IS_ARGON2(id));
|
||||||
m_ui->memoryUsageLabel->setVisible(memoryVisible);
|
m_ui->memorySpinBox->setVisible(IS_ARGON2(id));
|
||||||
m_ui->memorySpinBox->setVisible(memoryVisible);
|
m_ui->parallelismLabel->setVisible(IS_ARGON2(id));
|
||||||
|
m_ui->parallelismSpinBox->setVisible(IS_ARGON2(id));
|
||||||
bool parallelismVisible = (id == KeePass2::KDF_ARGON2);
|
|
||||||
m_ui->parallelismLabel->setVisible(parallelismVisible);
|
|
||||||
m_ui->parallelismSpinBox->setVisible(parallelismVisible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::activateChangeDecryptionTime()
|
void DatabaseSettingsWidgetEncryption::activateChangeDecryptionTime()
|
||||||
@ -253,7 +253,7 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
m_db->metadata()->customData()->remove(CD_DECRYPTION_TIME_PREFERENCE_KEY);
|
m_db->metadata()->customData()->remove(CD_DECRYPTION_TIME_PREFERENCE_KEY);
|
||||||
|
|
||||||
// first perform safety check for KDF rounds
|
// first perform safety check for KDF rounds
|
||||||
if (kdf->uuid() == KeePass2::KDF_ARGON2 && m_ui->transformRoundsSpinBox->value() > 10000) {
|
if (IS_ARGON2(kdf->uuid()) && m_ui->transformRoundsSpinBox->value() > 10000) {
|
||||||
QMessageBox warning;
|
QMessageBox warning;
|
||||||
warning.setIcon(QMessageBox::Warning);
|
warning.setIcon(QMessageBox::Warning);
|
||||||
warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
|
warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
|
||||||
@ -266,8 +266,7 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
if (warning.clickedButton() != ok) {
|
if (warning.clickedButton() != ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ((kdf->uuid() == KeePass2::KDF_AES_KDBX3 || kdf->uuid() == KeePass2::KDF_AES_KDBX4)
|
} else if (IS_AES_KDF(kdf->uuid()) && m_ui->transformRoundsSpinBox->value() < 100000) {
|
||||||
&& m_ui->transformRoundsSpinBox->value() < 100000) {
|
|
||||||
QMessageBox warning;
|
QMessageBox warning;
|
||||||
warning.setIcon(QMessageBox::Warning);
|
warning.setIcon(QMessageBox::Warning);
|
||||||
warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
|
warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
|
||||||
@ -286,7 +285,7 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
|
|
||||||
// Save kdf parameters
|
// Save kdf parameters
|
||||||
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
||||||
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
|
if (IS_ARGON2(kdf->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10));
|
argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10));
|
||||||
argon2Kdf->setParallelism(static_cast<quint32>(m_ui->parallelismSpinBox->value()));
|
argon2Kdf->setParallelism(static_cast<quint32>(m_ui->parallelismSpinBox->value()));
|
||||||
@ -317,7 +316,7 @@ void DatabaseSettingsWidgetEncryption::benchmarkTransformRounds(int millisecs)
|
|||||||
// Create a new kdf with the current parameters
|
// Create a new kdf with the current parameters
|
||||||
auto kdf = KeePass2::uuidToKdf(QUuid(m_ui->kdfComboBox->currentData().toByteArray()));
|
auto kdf = KeePass2::uuidToKdf(QUuid(m_ui->kdfComboBox->currentData().toByteArray()));
|
||||||
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
||||||
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
|
if (IS_ARGON2(kdf->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
if (!argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10))) {
|
if (!argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10))) {
|
||||||
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory() / (1 << 10)));
|
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory() / (1 << 10)));
|
||||||
@ -402,7 +401,7 @@ void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool
|
|||||||
auto kdf = KeePass2::uuidToKdf(kdfUuid);
|
auto kdf = KeePass2::uuidToKdf(kdfUuid);
|
||||||
m_db->setKdf(kdf);
|
m_db->setKdf(kdf);
|
||||||
|
|
||||||
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
|
if (IS_ARGON2(kdf->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
// Default to 64 MiB of memory and 2 threads
|
// Default to 64 MiB of memory and 2 threads
|
||||||
// these settings are safe for desktop and mobile devices
|
// these settings are safe for desktop and mobile devices
|
||||||
|
@ -178,9 +178,6 @@ void EditEntryWidget::setupMain()
|
|||||||
|
|
||||||
m_mainUi->expirePresets->setMenu(createPresetsMenu());
|
m_mainUi->expirePresets->setMenu(createPresetsMenu());
|
||||||
connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*)));
|
connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*)));
|
||||||
|
|
||||||
// HACK: Align username text with other line edits. Qt does not let you do this with an application stylesheet.
|
|
||||||
m_mainUi->usernameComboBox->lineEdit()->setStyleSheet("padding-left: 8px;");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditEntryWidget::setupAdvanced()
|
void EditEntryWidget::setupAdvanced()
|
||||||
@ -268,9 +265,8 @@ void EditEntryWidget::setupAutoType()
|
|||||||
#ifdef WITH_XC_BROWSER
|
#ifdef WITH_XC_BROWSER
|
||||||
void EditEntryWidget::setupBrowser()
|
void EditEntryWidget::setupBrowser()
|
||||||
{
|
{
|
||||||
m_browserUi->setupUi(m_browserWidget);
|
|
||||||
|
|
||||||
if (config()->get(Config::Browser_Enabled).toBool()) {
|
if (config()->get(Config::Browser_Enabled).toBool()) {
|
||||||
|
m_browserUi->setupUi(m_browserWidget);
|
||||||
addPage(tr("Browser Integration"), Resources::instance()->icon("internet-web-browser"), m_browserWidget);
|
addPage(tr("Browser Integration"), Resources::instance()->icon("internet-web-browser"), m_browserWidget);
|
||||||
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
|
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
|
||||||
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);
|
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);
|
||||||
@ -935,35 +931,44 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_XC_BROWSER
|
#ifdef WITH_XC_BROWSER
|
||||||
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
|
if (config()->get(Config::Browser_Enabled).toBool()) {
|
||||||
// clang-format off
|
if (!hasPage(m_browserWidget)) {
|
||||||
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
|
setupBrowser();
|
||||||
// clang-format on
|
}
|
||||||
} else {
|
|
||||||
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
|
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
|
||||||
|
// clang-format off
|
||||||
|
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
|
||||||
|
// clang-format on
|
||||||
|
} else {
|
||||||
|
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
|
||||||
|
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY)
|
||||||
|
== TRUE_STR);
|
||||||
|
} else {
|
||||||
|
m_browserUi->hideEntryCheckbox->setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
|
||||||
|
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
||||||
|
== TRUE_STR);
|
||||||
|
} else {
|
||||||
|
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_browserUi->addURLButton->setEnabled(!m_history);
|
||||||
|
m_browserUi->removeURLButton->setEnabled(false);
|
||||||
|
m_browserUi->editURLButton->setEnabled(false);
|
||||||
|
m_browserUi->additionalURLsView->setEditTriggers(editTriggers);
|
||||||
|
|
||||||
|
if (m_additionalURLsDataModel->rowCount() != 0) {
|
||||||
|
m_browserUi->additionalURLsView->setCurrentIndex(m_additionalURLsDataModel->index(0, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
|
setPageHidden(m_browserWidget, !config()->get(Config::Browser_Enabled).toBool());
|
||||||
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR);
|
|
||||||
} else {
|
|
||||||
m_browserUi->hideEntryCheckbox->setChecked(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
|
|
||||||
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
|
||||||
== TRUE_STR);
|
|
||||||
} else {
|
|
||||||
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_browserUi->addURLButton->setEnabled(!m_history);
|
|
||||||
m_browserUi->removeURLButton->setEnabled(false);
|
|
||||||
m_browserUi->editURLButton->setEnabled(false);
|
|
||||||
m_browserUi->additionalURLsView->setEditTriggers(editTriggers);
|
|
||||||
|
|
||||||
if (m_additionalURLsDataModel->rowCount() != 0) {
|
|
||||||
m_browserUi->additionalURLsView->setCurrentIndex(m_additionalURLsDataModel->index(0, 0));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid());
|
m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid());
|
||||||
@ -997,6 +1002,15 @@ bool EditEntryWidget::commitEntry()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: Check that entry pointer is still valid, see https://github.com/keepassxreboot/keepassxc/issues/5722
|
||||||
|
if (!m_entry) {
|
||||||
|
QMessageBox::information(this,
|
||||||
|
tr("Invalid Entry"),
|
||||||
|
tr("An external merge operation has invalidated this entry.\n"
|
||||||
|
"Unfortunately, any changes made have been lost."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check Auto-Type validity early
|
// Check Auto-Type validity early
|
||||||
if (!AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
|
if (!AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -262,13 +262,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
|
|||||||
targetDb->metadata()->copyCustomIcons(customIcons, sourceDb->metadata());
|
targetDb->metadata()->copyCustomIcons(customIcons, sourceDb->metadata());
|
||||||
|
|
||||||
// Always clone the group across db's to reset UUIDs
|
// Always clone the group across db's to reset UUIDs
|
||||||
group = dragGroup->clone();
|
group = dragGroup->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
|
||||||
if (action == Qt::MoveAction) {
|
if (action == Qt::MoveAction) {
|
||||||
// Remove the original group from the sourceDb
|
// Remove the original group from the sourceDb
|
||||||
delete dragGroup;
|
delete dragGroup;
|
||||||
}
|
}
|
||||||
} else if (action == Qt::CopyAction) {
|
} else if (action == Qt::CopyAction) {
|
||||||
group = dragGroup->clone();
|
group = dragGroup->clone(Entry::CloneCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
group->setParent(parentGroup, row);
|
group->setParent(parentGroup, row);
|
||||||
@ -303,13 +303,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
|
|||||||
targetDb->metadata()->addCustomIcon(customIcon, sourceDb->metadata()->customIcon(customIcon));
|
targetDb->metadata()->addCustomIcon(customIcon, sourceDb->metadata()->customIcon(customIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always clone the entry across db's to reset the UUID
|
// Reset the UUID when moving across db boundary
|
||||||
entry = dragEntry->clone();
|
entry = dragEntry->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
|
||||||
if (action == Qt::MoveAction) {
|
if (action == Qt::MoveAction) {
|
||||||
delete dragEntry;
|
delete dragEntry;
|
||||||
}
|
}
|
||||||
} else if (action == Qt::CopyAction) {
|
} else if (action == Qt::CopyAction) {
|
||||||
entry = dragEntry->clone();
|
entry = dragEntry->clone(Entry::CloneCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->setGroup(parentGroup);
|
entry->setGroup(parentGroup);
|
||||||
|
@ -104,10 +104,12 @@ void NixUtils::setLaunchAtStartup(bool enable)
|
|||||||
<< QStringLiteral("StartupNotify=true") << '\n'
|
<< QStringLiteral("StartupNotify=true") << '\n'
|
||||||
<< QStringLiteral("Terminal=false") << '\n'
|
<< QStringLiteral("Terminal=false") << '\n'
|
||||||
<< QStringLiteral("Type=Application") << '\n'
|
<< QStringLiteral("Type=Application") << '\n'
|
||||||
<< QStringLiteral("Version=1.0") << "true" << '\n'
|
<< QStringLiteral("Version=1.0") << '\n'
|
||||||
<< QStringLiteral("Categories=Utility;Security;Qt;") << '\n'
|
<< QStringLiteral("Categories=Utility;Security;Qt;") << '\n'
|
||||||
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
|
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
|
||||||
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << endl;
|
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << '\n'
|
||||||
|
<< QStringLiteral("X-GNOME-Autostart-Delay=2") << '\n'
|
||||||
|
<< QStringLiteral("X-KDE-autostart-after=panel") << endl;
|
||||||
desktopFile.close();
|
desktopFile.close();
|
||||||
} else if (isLaunchAtStartupEnabled()) {
|
} else if (isLaunchAtStartupEnabled()) {
|
||||||
QFile::remove(getAutostartDesktopFilename());
|
QFile::remove(getAutostartDesktopFilename());
|
||||||
|
@ -58,7 +58,8 @@ namespace
|
|||||||
// Get average password length
|
// Get average password length
|
||||||
int averagePwdLength() const
|
int averagePwdLength() const
|
||||||
{
|
{
|
||||||
return m_passwords.empty() ? 0 : pwdTotalLen / m_passwords.size();
|
const auto nPwds = nPwdsUnique + nPwdsReused;
|
||||||
|
return nPwds == 0 ? 0 : std::round(pwdTotalLen / double(nPwds));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get max number of password reuse (=how many entries
|
// Get max number of password reuse (=how many entries
|
||||||
|
@ -4775,7 +4775,11 @@ QRect BaseStyle::subElementRect(SubElement sr, const QStyleOption* opt, const QW
|
|||||||
}
|
}
|
||||||
case SE_LineEditContents: {
|
case SE_LineEditContents: {
|
||||||
QRect r = QCommonStyle::subElementRect(sr, opt, w);
|
QRect r = QCommonStyle::subElementRect(sr, opt, w);
|
||||||
int pad = Phantom::dpiScaled(Phantom::LineEdit_ContentsHPad);
|
int pad = Phantom::LineEdit_ContentsHPad;
|
||||||
|
if (w && qobject_cast<const QComboBox*>(w->parentWidget())) {
|
||||||
|
pad += 3;
|
||||||
|
}
|
||||||
|
pad = Phantom::dpiScaled(pad);
|
||||||
return r.adjusted(pad, 0, -pad, 0);
|
return r.adjusted(pad, 0, -pad, 0);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -13,3 +13,7 @@ DatabaseWidget #SearchBanner, DatabaseWidget #KeeShareBanner {
|
|||||||
border: 1px solid rgb(190, 190, 190);
|
border: 1px solid rgb(190, 190, 190);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QLineEdit {
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
@ -186,23 +186,29 @@ void ShareObserver::handleDatabaseChanged()
|
|||||||
|
|
||||||
void ShareObserver::handleFileUpdated(const QString& path)
|
void ShareObserver::handleFileUpdated(const QString& path)
|
||||||
{
|
{
|
||||||
const Result result = importShare(path);
|
if (!m_inFileUpdate) {
|
||||||
if (!result.isValid()) {
|
QTimer::singleShot(100, this, [this, path] {
|
||||||
return;
|
const Result result = importShare(path);
|
||||||
|
m_inFileUpdate = false;
|
||||||
|
if (!result.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QStringList success;
|
||||||
|
QStringList warning;
|
||||||
|
QStringList error;
|
||||||
|
if (result.isError()) {
|
||||||
|
error << tr("Import from %1 failed (%2)").arg(result.path, result.message);
|
||||||
|
} else if (result.isWarning()) {
|
||||||
|
warning << tr("Import from %1 failed (%2)").arg(result.path, result.message);
|
||||||
|
} else if (result.isInfo()) {
|
||||||
|
success << tr("Import from %1 successful (%2)").arg(result.path, result.message);
|
||||||
|
} else {
|
||||||
|
success << tr("Imported from %1").arg(result.path);
|
||||||
|
}
|
||||||
|
notifyAbout(success, warning, error);
|
||||||
|
});
|
||||||
|
m_inFileUpdate = true;
|
||||||
}
|
}
|
||||||
QStringList success;
|
|
||||||
QStringList warning;
|
|
||||||
QStringList error;
|
|
||||||
if (result.isError()) {
|
|
||||||
error << tr("Import from %1 failed (%2)").arg(result.path, result.message);
|
|
||||||
} else if (result.isWarning()) {
|
|
||||||
warning << tr("Import from %1 failed (%2)").arg(result.path, result.message);
|
|
||||||
} else if (result.isInfo()) {
|
|
||||||
success << tr("Import from %1 successful (%2)").arg(result.path, result.message);
|
|
||||||
} else {
|
|
||||||
success << tr("Imported from %1").arg(result.path);
|
|
||||||
}
|
|
||||||
notifyAbout(success, warning, error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareObserver::Result ShareObserver::importShare(const QString& path)
|
ShareObserver::Result ShareObserver::importShare(const QString& path)
|
||||||
|
@ -83,6 +83,7 @@ private:
|
|||||||
QMap<QPointer<Group>, KeeShareSettings::Reference> m_groupToReference;
|
QMap<QPointer<Group>, KeeShareSettings::Reference> m_groupToReference;
|
||||||
QMap<QString, QPointer<Group>> m_shareToGroup;
|
QMap<QString, QPointer<Group>> m_shareToGroup;
|
||||||
QMap<QString, QSharedPointer<FileWatcher>> m_fileWatchers;
|
QMap<QString, QSharedPointer<FileWatcher>> m_fileWatchers;
|
||||||
|
bool m_inFileUpdate = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_SHAREOBSERVER_H
|
#endif // KEEPASSXC_SHAREOBSERVER_H
|
||||||
|
@ -64,9 +64,10 @@ FileKey::~FileKey()
|
|||||||
* removed in a future version.
|
* removed in a future version.
|
||||||
*
|
*
|
||||||
* @param device input device
|
* @param device input device
|
||||||
|
* @param errorMsg error message in case of fatal failure
|
||||||
* @return true if key file was loaded successfully
|
* @return true if key file was loaded successfully
|
||||||
*/
|
*/
|
||||||
bool FileKey::load(QIODevice* device)
|
bool FileKey::load(QIODevice* device, QString* errorMsg)
|
||||||
{
|
{
|
||||||
m_type = None;
|
m_type = None;
|
||||||
|
|
||||||
@ -75,32 +76,33 @@ bool FileKey::load(QIODevice* device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->size() == 0) {
|
if (device->size() == 0 || !device->reset()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try different legacy key file formats
|
// load XML key file v1 or v2
|
||||||
if (!device->reset()) {
|
QString xmlError;
|
||||||
return false;
|
if (loadXml(device, &xmlError)) {
|
||||||
}
|
|
||||||
if (loadXml(device)) {
|
|
||||||
m_type = KeePass2XML;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device->reset()) {
|
if (!device->reset() || !xmlError.isEmpty()) {
|
||||||
|
if (errorMsg) {
|
||||||
|
*errorMsg = xmlError;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try legacy key file formats
|
||||||
if (loadBinary(device)) {
|
if (loadBinary(device)) {
|
||||||
m_type = FixedBinary;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device->reset()) {
|
if (!device->reset()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadHex(device)) {
|
if (loadHex(device)) {
|
||||||
m_type = FixedBinaryHex;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +111,6 @@ bool FileKey::load(QIODevice* device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (loadHashed(device)) {
|
if (loadHashed(device)) {
|
||||||
m_type = Hashed;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,10 +146,14 @@ bool FileKey::load(const QString& fileName, QString* errorMsg)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool result = load(&file);
|
|
||||||
|
|
||||||
|
bool result = load(&file, errorMsg);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
if (errorMsg && !errorMsg->isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (file.error()) {
|
if (file.error()) {
|
||||||
result = false;
|
result = false;
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
@ -171,16 +176,64 @@ QByteArray FileKey::rawKey() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new key file from random bytes.
|
* Generate a new key file with random bytes.
|
||||||
*
|
*
|
||||||
* @param device output device
|
* @param device output device
|
||||||
* @param number of random bytes to generate
|
* @param number of random bytes to generate
|
||||||
*/
|
*/
|
||||||
void FileKey::create(QIODevice* device, int size)
|
void FileKey::createRandom(QIODevice* device, int size)
|
||||||
{
|
{
|
||||||
device->write(randomGen()->randomArray(size));
|
device->write(randomGen()->randomArray(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new key file in the KeePass2 XML format v2.
|
||||||
|
*
|
||||||
|
* @param device output device
|
||||||
|
* @param number of random bytes to generate
|
||||||
|
*/
|
||||||
|
void FileKey::createXMLv2(QIODevice* device, int size)
|
||||||
|
{
|
||||||
|
QXmlStreamWriter w(device);
|
||||||
|
w.setAutoFormatting(true);
|
||||||
|
w.setAutoFormattingIndent(4);
|
||||||
|
w.writeStartDocument();
|
||||||
|
|
||||||
|
w.writeStartElement("KeyFile");
|
||||||
|
|
||||||
|
w.writeStartElement("Meta");
|
||||||
|
w.writeTextElement("Version", "2.0");
|
||||||
|
w.writeEndElement();
|
||||||
|
|
||||||
|
w.writeStartElement("Key");
|
||||||
|
w.writeStartElement("Data");
|
||||||
|
|
||||||
|
QByteArray key = randomGen()->randomArray(size);
|
||||||
|
CryptoHash hash(CryptoHash::Sha256);
|
||||||
|
hash.addData(key);
|
||||||
|
QByteArray result = hash.result().left(4);
|
||||||
|
key = key.toHex().toUpper();
|
||||||
|
|
||||||
|
w.writeAttribute("Hash", result.toHex().toUpper());
|
||||||
|
w.writeCharacters("\n ");
|
||||||
|
for (int i = 0; i < key.size(); ++i) {
|
||||||
|
// Pretty-print hex value (not strictly necessary, but nicer to read and KeePass2 does it)
|
||||||
|
if (i != 0 && i % 32 == 0) {
|
||||||
|
w.writeCharacters("\n ");
|
||||||
|
} else if (i != 0 && i % 8 == 0) {
|
||||||
|
w.writeCharacters(" ");
|
||||||
|
}
|
||||||
|
w.writeCharacters(QChar(key[i]));
|
||||||
|
}
|
||||||
|
sodium_memzero(key.data(), static_cast<std::size_t>(key.capacity()));
|
||||||
|
w.writeCharacters("\n ");
|
||||||
|
|
||||||
|
w.writeEndElement();
|
||||||
|
w.writeEndElement();
|
||||||
|
|
||||||
|
w.writeEndDocument();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new key file from random bytes.
|
* Create a new key file from random bytes.
|
||||||
*
|
*
|
||||||
@ -189,7 +242,7 @@ void FileKey::create(QIODevice* device, int size)
|
|||||||
* @param number of random bytes to generate
|
* @param number of random bytes to generate
|
||||||
* @return true on successful creation
|
* @return true on successful creation
|
||||||
*/
|
*/
|
||||||
bool FileKey::create(const QString& fileName, QString* errorMsg, int size)
|
bool FileKey::create(const QString& fileName, QString* errorMsg)
|
||||||
{
|
{
|
||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
if (!file.open(QFile::WriteOnly)) {
|
if (!file.open(QFile::WriteOnly)) {
|
||||||
@ -198,7 +251,11 @@ bool FileKey::create(const QString& fileName, QString* errorMsg, int size)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
create(&file, size);
|
if (fileName.endsWith(".keyx")) {
|
||||||
|
createXMLv2(&file);
|
||||||
|
} else {
|
||||||
|
createRandom(&file);
|
||||||
|
}
|
||||||
file.close();
|
file.close();
|
||||||
file.setPermissions(QFile::ReadUser);
|
file.setPermissions(QFile::ReadUser);
|
||||||
|
|
||||||
@ -218,87 +275,86 @@ bool FileKey::create(const QString& fileName, QString* errorMsg, int size)
|
|||||||
*
|
*
|
||||||
* @param device input device
|
* @param device input device
|
||||||
* @return true on success
|
* @return true on success
|
||||||
* @deprecated
|
|
||||||
*/
|
*/
|
||||||
bool FileKey::loadXml(QIODevice* device)
|
bool FileKey::loadXml(QIODevice* device, QString* errorMsg)
|
||||||
{
|
{
|
||||||
QXmlStreamReader xmlReader(device);
|
QXmlStreamReader xmlReader(device);
|
||||||
|
|
||||||
if (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
if (xmlReader.error()) {
|
||||||
if (xmlReader.name() != "KeyFile") {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
if (xmlReader.readNextStartElement() && xmlReader.name() != "KeyFile") {
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool correctMeta = false;
|
struct
|
||||||
QByteArray data;
|
{
|
||||||
|
QString version;
|
||||||
|
QByteArray hash;
|
||||||
|
QByteArray data;
|
||||||
|
} keyFileData;
|
||||||
|
|
||||||
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
||||||
if (xmlReader.name() == "Meta") {
|
if (xmlReader.name() == "Meta") {
|
||||||
correctMeta = loadXmlMeta(xmlReader);
|
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
||||||
|
if (xmlReader.name() == "Version") {
|
||||||
|
keyFileData.version = xmlReader.readElementText();
|
||||||
|
if (keyFileData.version.startsWith("1.0")) {
|
||||||
|
m_type = KeePass2XML;
|
||||||
|
} else if (keyFileData.version == "2.0") {
|
||||||
|
m_type = KeePass2XMLv2;
|
||||||
|
} else {
|
||||||
|
if (errorMsg) {
|
||||||
|
*errorMsg = QObject::tr("Unsupported key file version: %1").arg(keyFileData.version);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (xmlReader.name() == "Key") {
|
} else if (xmlReader.name() == "Key") {
|
||||||
data = loadXmlKey(xmlReader);
|
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
||||||
|
if (xmlReader.name() == "Data") {
|
||||||
|
keyFileData.hash = QByteArray::fromHex(xmlReader.attributes().value("Hash").toLatin1());
|
||||||
|
QByteArray rawData = xmlReader.readElementText().simplified().replace(" ", "").toLatin1();
|
||||||
|
|
||||||
|
if (keyFileData.version.startsWith("1.0") && Tools::isBase64(rawData)) {
|
||||||
|
keyFileData.data = QByteArray::fromBase64(rawData);
|
||||||
|
} else if (keyFileData.version == "2.0" && Tools::isHex(rawData)) {
|
||||||
|
keyFileData.data = QByteArray::fromHex(rawData);
|
||||||
|
|
||||||
|
CryptoHash hash(CryptoHash::Sha256);
|
||||||
|
hash.addData(keyFileData.data);
|
||||||
|
QByteArray result = hash.result().left(4);
|
||||||
|
if (keyFileData.hash != result) {
|
||||||
|
if (errorMsg) {
|
||||||
|
*errorMsg = QObject::tr("Checksum mismatch! Key file may be corrupt.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errorMsg) {
|
||||||
|
*errorMsg = QObject::tr("Unexpected key file data! Key file may be corrupt.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sodium_memzero(rawData.data(), static_cast<std::size_t>(rawData.capacity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (!xmlReader.error() && correctMeta && !data.isEmpty()) {
|
if (!xmlReader.error() && !keyFileData.data.isEmpty()) {
|
||||||
std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size()));
|
std::memcpy(m_key, keyFileData.data.data(), std::min(SHA256_SIZE, keyFileData.data.size()));
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
|
sodium_memzero(keyFileData.data.data(), static_cast<std::size_t>(keyFileData.data.capacity()));
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load meta data from legacy KeePass 2 XML key file.
|
|
||||||
*
|
|
||||||
* @param xmlReader input XML reader
|
|
||||||
* @return true on success
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
bool FileKey::loadXmlMeta(QXmlStreamReader& xmlReader)
|
|
||||||
{
|
|
||||||
bool correctVersion = false;
|
|
||||||
|
|
||||||
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
|
||||||
if (xmlReader.name() == "Version") {
|
|
||||||
if (xmlReader.readElementText() == "1.00") {
|
|
||||||
correctVersion = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return correctVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load base64 key data from legacy KeePass 2 XML key file.
|
|
||||||
*
|
|
||||||
* @param xmlReader input XML reader
|
|
||||||
* @return true on success
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
QByteArray FileKey::loadXmlKey(QXmlStreamReader& xmlReader)
|
|
||||||
{
|
|
||||||
QByteArray data;
|
|
||||||
|
|
||||||
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
|
|
||||||
if (xmlReader.name() == "Data") {
|
|
||||||
QByteArray rawData = xmlReader.readElementText().toLatin1();
|
|
||||||
if (Tools::isBase64(rawData)) {
|
|
||||||
data = QByteArray::fromBase64(rawData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load fixed 32-bit binary key file.
|
* Load fixed 32-bit binary key file.
|
||||||
*
|
*
|
||||||
@ -315,11 +371,12 @@ bool FileKey::loadBinary(QIODevice* device)
|
|||||||
QByteArray data;
|
QByteArray data;
|
||||||
if (!Tools::readAllFromDevice(device, data) || data.size() != 32) {
|
if (!Tools::readAllFromDevice(device, data) || data.size() != 32) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size()));
|
|
||||||
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size()));
|
||||||
|
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
|
||||||
|
m_type = FixedBinary;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,6 +411,7 @@ bool FileKey::loadHex(QIODevice* device)
|
|||||||
std::memcpy(m_key, key.data(), std::min(SHA256_SIZE, key.size()));
|
std::memcpy(m_key, key.data(), std::min(SHA256_SIZE, key.size()));
|
||||||
sodium_memzero(key.data(), static_cast<std::size_t>(key.capacity()));
|
sodium_memzero(key.data(), static_cast<std::size_t>(key.capacity()));
|
||||||
|
|
||||||
|
m_type = FixedBinaryHex;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,6 +437,7 @@ bool FileKey::loadHashed(QIODevice* device)
|
|||||||
std::memcpy(m_key, result.data(), std::min(SHA256_SIZE, result.size()));
|
std::memcpy(m_key, result.data(), std::min(SHA256_SIZE, result.size()));
|
||||||
sodium_memzero(result.data(), static_cast<std::size_t>(result.capacity()));
|
sodium_memzero(result.data(), static_cast<std::size_t>(result.capacity()));
|
||||||
|
|
||||||
|
m_type = Hashed;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,25 +35,25 @@ public:
|
|||||||
None,
|
None,
|
||||||
Hashed,
|
Hashed,
|
||||||
KeePass2XML,
|
KeePass2XML,
|
||||||
|
KeePass2XMLv2,
|
||||||
FixedBinary,
|
FixedBinary,
|
||||||
FixedBinaryHex
|
FixedBinaryHex
|
||||||
};
|
};
|
||||||
|
|
||||||
FileKey();
|
FileKey();
|
||||||
~FileKey() override;
|
~FileKey() override;
|
||||||
bool load(QIODevice* device);
|
bool load(QIODevice* device, QString* errorMsg = nullptr);
|
||||||
bool load(const QString& fileName, QString* errorMsg = nullptr);
|
bool load(const QString& fileName, QString* errorMsg = nullptr);
|
||||||
QByteArray rawKey() const override;
|
QByteArray rawKey() const override;
|
||||||
Type type() const;
|
Type type() const;
|
||||||
static void create(QIODevice* device, int size = 128);
|
static void createRandom(QIODevice* device, int size = 128);
|
||||||
static bool create(const QString& fileName, QString* errorMsg = nullptr, int size = 128);
|
static void createXMLv2(QIODevice* device, int size = 32);
|
||||||
|
static bool create(const QString& fileName, QString* errorMsg = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int SHA256_SIZE = 32;
|
static constexpr int SHA256_SIZE = 32;
|
||||||
|
|
||||||
bool loadXml(QIODevice* device);
|
bool loadXml(QIODevice* device, QString* errorMsg = nullptr);
|
||||||
bool loadXmlMeta(QXmlStreamReader& xmlReader);
|
|
||||||
QByteArray loadXmlKey(QXmlStreamReader& xmlReader);
|
|
||||||
bool loadBinary(QIODevice* device);
|
bool loadBinary(QIODevice* device);
|
||||||
bool loadHex(QIODevice* device);
|
bool loadHex(QIODevice* device);
|
||||||
bool loadHashed(QIODevice* device);
|
bool loadHashed(QIODevice* device);
|
||||||
|
@ -928,10 +928,10 @@ void TestCli::testExport()
|
|||||||
setInput("a");
|
setInput("a");
|
||||||
execCmd(exportCmd, {"export", "-f", "csv", m_dbFile->fileName()});
|
execCmd(exportCmd, {"export", "-f", "csv", m_dbFile->fileName()});
|
||||||
QByteArray csvHeader = m_stdout->readLine();
|
QByteArray csvHeader = m_stdout->readLine();
|
||||||
QCOMPARE(csvHeader, QByteArray("\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\"\n"));
|
QVERIFY(csvHeader.contains(QByteArray("\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\"")));
|
||||||
QByteArray csvData = m_stdout->readAll();
|
QByteArray csvData = m_stdout->readAll();
|
||||||
QVERIFY(csvData.contains(QByteArray(
|
QVERIFY(csvData.contains(QByteArray(
|
||||||
"\"NewDatabase\",\"Sample Entry\",\"User Name\",\"Password\",\"http://www.somesite.com/\",\"Notes\"\n")));
|
"\"NewDatabase\",\"Sample Entry\",\"User Name\",\"Password\",\"http://www.somesite.com/\",\"Notes\"")));
|
||||||
|
|
||||||
// test invalid format
|
// test invalid format
|
||||||
setInput("a");
|
setInput("a");
|
||||||
|
@ -23,11 +23,13 @@
|
|||||||
|
|
||||||
#include "crypto/Crypto.h"
|
#include "crypto/Crypto.h"
|
||||||
#include "format/CsvExporter.h"
|
#include "format/CsvExporter.h"
|
||||||
|
#include "totp/totp.h"
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestCsvExporter)
|
QTEST_GUILESS_MAIN(TestCsvExporter)
|
||||||
|
|
||||||
const QString TestCsvExporter::ExpectedHeaderLine =
|
const QString TestCsvExporter::ExpectedHeaderLine =
|
||||||
QString("\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\"\n");
|
QString("\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\",\"TOTP\",\"Icon\",\"Last "
|
||||||
|
"Modified\",\"Created\"\n");
|
||||||
|
|
||||||
void TestCsvExporter::init()
|
void TestCsvExporter::init()
|
||||||
{
|
{
|
||||||
@ -57,17 +59,23 @@ void TestCsvExporter::testExport()
|
|||||||
entry->setPassword("Test Password");
|
entry->setPassword("Test Password");
|
||||||
entry->setUrl("http://test.url");
|
entry->setUrl("http://test.url");
|
||||||
entry->setNotes("Test Notes");
|
entry->setNotes("Test Notes");
|
||||||
|
entry->setTotp(Totp::createSettings("DFDF", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP));
|
||||||
|
entry->setIcon(5);
|
||||||
|
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
QVERIFY(buffer.open(QIODevice::ReadWrite));
|
QVERIFY(buffer.open(QIODevice::ReadWrite));
|
||||||
m_csvExporter->exportDatabase(&buffer, m_db);
|
m_csvExporter->exportDatabase(&buffer, m_db);
|
||||||
|
auto exported = QString::fromUtf8(buffer.buffer());
|
||||||
|
|
||||||
QString expectedResult = QString()
|
QString expectedResult = QString()
|
||||||
.append(ExpectedHeaderLine)
|
.append(ExpectedHeaderLine)
|
||||||
.append("\"Passwords/Test Group Name\",\"Test Entry Title\",\"Test Username\",\"Test "
|
.append("\"Passwords/Test Group Name\",\"Test Entry Title\",\"Test Username\",\"Test "
|
||||||
"Password\",\"http://test.url\",\"Test Notes\"\n");
|
"Password\",\"http://test.url\",\"Test Notes\"");
|
||||||
|
|
||||||
QCOMPARE(QString::fromUtf8(buffer.buffer().constData()), expectedResult);
|
QVERIFY(exported.startsWith(expectedResult));
|
||||||
|
exported.remove(expectedResult);
|
||||||
|
QVERIFY(exported.contains("otpauth://"));
|
||||||
|
QVERIFY(exported.contains(",\"5\","));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCsvExporter::testEmptyDatabase()
|
void TestCsvExporter::testEmptyDatabase()
|
||||||
@ -95,10 +103,9 @@ void TestCsvExporter::testNestedGroups()
|
|||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
QVERIFY(buffer.open(QIODevice::ReadWrite));
|
QVERIFY(buffer.open(QIODevice::ReadWrite));
|
||||||
m_csvExporter->exportDatabase(&buffer, m_db);
|
m_csvExporter->exportDatabase(&buffer, m_db);
|
||||||
|
auto exported = QString::fromUtf8(buffer.buffer());
|
||||||
QCOMPARE(
|
QVERIFY(exported.startsWith(
|
||||||
QString::fromUtf8(buffer.buffer().constData()),
|
|
||||||
QString()
|
QString()
|
||||||
.append(ExpectedHeaderLine)
|
.append(ExpectedHeaderLine)
|
||||||
.append("\"Passwords/Test Group Name/Test Sub Group Name\",\"Test Entry Title\",\"\",\"\",\"\",\"\"\n"));
|
.append("\"Passwords/Test Group Name/Test Sub Group Name\",\"Test Entry Title\",\"\",\"\",\"\",\"\"")));
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,8 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
void TestKdbx4Argon2::initTestCaseImpl()
|
void TestKdbx4Argon2::initTestCaseImpl()
|
||||||
{
|
{
|
||||||
m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
|
m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D)));
|
||||||
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
|
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Database>
|
QSharedPointer<Database>
|
||||||
@ -108,7 +108,7 @@ void TestKdbx4Argon2::readKdbx(const QString& path,
|
|||||||
void TestKdbx4Argon2::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
|
void TestKdbx4Argon2::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
|
||||||
{
|
{
|
||||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3) {
|
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3) {
|
||||||
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
|
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D)));
|
||||||
}
|
}
|
||||||
KeePass2Writer writer;
|
KeePass2Writer writer;
|
||||||
hasError = writer.writeDatabase(device, db);
|
hasError = writer.writeDatabase(device, db);
|
||||||
@ -213,26 +213,32 @@ void TestKdbx4Argon2::testFormat400Upgrade_data()
|
|||||||
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||||
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||||
|
|
||||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES256 << false << kdbx4;
|
QTest::newRow("Argon2d + AES") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_AES256 << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << false << kdbx4;
|
QTest::newRow("Argon2id + AES") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_AES256 << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << false << kdbx3;
|
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << false << kdbx4;
|
||||||
QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES256 << true << kdbx4;
|
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << false << kdbx3;
|
||||||
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << true << kdbx4;
|
QTest::newRow("Argon2d + AES + CustomData") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_AES256 << true << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << true << kdbx4;
|
QTest::newRow("Argon2id + AES + CustomData") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_AES256 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << true << kdbx4;
|
||||||
|
|
||||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
QTest::newRow("Argon2d + ChaCha20") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
QTest::newRow("Argon2id + ChaCha20") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << false << kdbx3;
|
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||||
QTest::newRow("Argon2 + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << false << kdbx3;
|
||||||
QTest::newRow("AES-KDF + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
QTest::newRow("Argon2d + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
QTest::newRow("Argon2id + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
|
||||||
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
QTest::newRow("Argon2d + Twofish") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
QTest::newRow("Argon2id + Twofish") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << false << kdbx3;
|
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||||
QTest::newRow("Argon2 + Twofish + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << false << kdbx3;
|
||||||
QTest::newRow("AES-KDF + Twofish + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
QTest::newRow("Argon2d + Twofish + CustomData") << KeePass2::KDF_ARGON2D << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + Twofish + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
QTest::newRow("Argon2id + Twofish + CustomData") << KeePass2::KDF_ARGON2ID << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + Twofish + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + Twofish + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -270,7 +276,7 @@ void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity()
|
|||||||
} else if (upgradeAction == "kdf-aes-kdbx3") {
|
} else if (upgradeAction == "kdf-aes-kdbx3") {
|
||||||
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
|
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
|
||||||
} else if (upgradeAction == "kdf-argon2") {
|
} else if (upgradeAction == "kdf-argon2") {
|
||||||
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
|
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D)));
|
||||||
} else if (upgradeAction == "kdf-aes-kdbx4") {
|
} else if (upgradeAction == "kdf-aes-kdbx4") {
|
||||||
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4)));
|
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4)));
|
||||||
} else if (upgradeAction == "public-customdata") {
|
} else if (upgradeAction == "public-customdata") {
|
||||||
|
@ -809,7 +809,7 @@ QSharedPointer<Kdf> TestKeePass2Format::fastKdf(QSharedPointer<Kdf> kdf) const
|
|||||||
{
|
{
|
||||||
kdf->setRounds(1);
|
kdf->setRounds(1);
|
||||||
|
|
||||||
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
|
if (kdf->uuid() == KeePass2::KDF_ARGON2D) {
|
||||||
kdf->processParameters({{KeePass2::KDFPARAM_ARGON2_MEMORY, 1024}, {KeePass2::KDFPARAM_ARGON2_PARALLELISM, 1}});
|
kdf->processParameters({{KeePass2::KDFPARAM_ARGON2_MEMORY, 1024}, {KeePass2::KDFPARAM_ARGON2_PARALLELISM, 1}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,22 +69,36 @@ void TestKeys::testComposite()
|
|||||||
void TestKeys::testFileKey()
|
void TestKeys::testFileKey()
|
||||||
{
|
{
|
||||||
QFETCH(FileKey::Type, type);
|
QFETCH(FileKey::Type, type);
|
||||||
QFETCH(QString, typeString);
|
QFETCH(QString, keyExt);
|
||||||
|
QFETCH(bool, fileKeyOk);
|
||||||
|
|
||||||
QString name = QString("FileKey").append(typeString);
|
QString name = QString("FileKey").append(QTest::currentDataTag());
|
||||||
|
|
||||||
KeePass2Reader reader;
|
KeePass2Reader reader;
|
||||||
|
|
||||||
QString dbFilename = QString("%1/%2.kdbx").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
|
QString dbFilename = QString("%1/%2.kdbx").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
|
||||||
QString keyFilename = QString("%1/%2.key").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
|
QString keyFilename = QString("%1/%2.%3").arg(QString(KEEPASSX_TEST_DATA_DIR), name, keyExt);
|
||||||
|
|
||||||
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
||||||
auto fileKey = QSharedPointer<FileKey>::create();
|
auto fileKey = QSharedPointer<FileKey>::create();
|
||||||
QVERIFY(fileKey->load(keyFilename));
|
QString error;
|
||||||
QCOMPARE(fileKey->rawKey().size(), 32);
|
QVERIFY(fileKey->load(keyFilename, &error) == fileKeyOk);
|
||||||
|
QVERIFY(error.isEmpty() == fileKeyOk);
|
||||||
QCOMPARE(fileKey->type(), type);
|
QCOMPARE(fileKey->type(), type);
|
||||||
|
|
||||||
|
// Test for same behaviour on code path without error parameter
|
||||||
|
auto fileKeyNoErrorParam = QSharedPointer<FileKey>::create();
|
||||||
|
QVERIFY(fileKeyNoErrorParam->load(keyFilename) == fileKeyOk);
|
||||||
|
QCOMPARE(fileKeyNoErrorParam->type(), type);
|
||||||
|
|
||||||
|
QCOMPARE(fileKey->rawKey(), fileKeyNoErrorParam->rawKey());
|
||||||
|
|
||||||
|
if (!fileKeyOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(fileKey->rawKey().size(), 32);
|
||||||
|
|
||||||
compositeKey->addKey(fileKey);
|
compositeKey->addKey(fileKey);
|
||||||
|
|
||||||
auto db = QSharedPointer<Database>::create();
|
auto db = QSharedPointer<Database>::create();
|
||||||
@ -97,12 +111,16 @@ void TestKeys::testFileKey()
|
|||||||
void TestKeys::testFileKey_data()
|
void TestKeys::testFileKey_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<FileKey::Type>("type");
|
QTest::addColumn<FileKey::Type>("type");
|
||||||
QTest::addColumn<QString>("typeString");
|
QTest::addColumn<QString>("keyExt");
|
||||||
QTest::newRow("Xml") << FileKey::KeePass2XML << QString("Xml");
|
QTest::addColumn<bool>("fileKeyOk");
|
||||||
QTest::newRow("XmlBrokenBase64") << FileKey::Hashed << QString("XmlBrokenBase64");
|
QTest::newRow("Xml") << FileKey::KeePass2XML << QString("key") << true;
|
||||||
QTest::newRow("Binary") << FileKey::FixedBinary << QString("Binary");
|
QTest::newRow("XmlBrokenBase64") << FileKey::KeePass2XML << QString("key") << false;
|
||||||
QTest::newRow("Hex") << FileKey::FixedBinaryHex << QString("Hex");
|
QTest::newRow("XmlV2") << FileKey::KeePass2XMLv2 << QString("keyx") << true;
|
||||||
QTest::newRow("Hashed") << FileKey::Hashed << QString("Hashed");
|
QTest::newRow("XmlV2HashFail") << FileKey::KeePass2XMLv2 << QString("keyx") << false;
|
||||||
|
QTest::newRow("XmlV2BrokenHex") << FileKey::KeePass2XMLv2 << QString("keyx") << false;
|
||||||
|
QTest::newRow("Binary") << FileKey::FixedBinary << QString("key") << true;
|
||||||
|
QTest::newRow("Hex") << FileKey::FixedBinaryHex << QString("key") << true;
|
||||||
|
QTest::newRow("Hashed") << FileKey::Hashed << QString("key") << true;
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -111,12 +129,12 @@ void TestKeys::testCreateFileKey()
|
|||||||
QBuffer keyBuffer1;
|
QBuffer keyBuffer1;
|
||||||
keyBuffer1.open(QBuffer::ReadWrite);
|
keyBuffer1.open(QBuffer::ReadWrite);
|
||||||
|
|
||||||
FileKey::create(&keyBuffer1, 128);
|
FileKey::createRandom(&keyBuffer1, 128);
|
||||||
QCOMPARE(keyBuffer1.size(), 128);
|
QCOMPARE(keyBuffer1.size(), 128);
|
||||||
|
|
||||||
QBuffer keyBuffer2;
|
QBuffer keyBuffer2;
|
||||||
keyBuffer2.open(QBuffer::ReadWrite);
|
keyBuffer2.open(QBuffer::ReadWrite);
|
||||||
FileKey::create(&keyBuffer2, 64);
|
FileKey::createRandom(&keyBuffer2, 64);
|
||||||
QCOMPARE(keyBuffer2.size(), 64);
|
QCOMPARE(keyBuffer2.size(), 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +145,7 @@ void TestKeys::testCreateAndOpenFileKey()
|
|||||||
QBuffer keyBuffer;
|
QBuffer keyBuffer;
|
||||||
keyBuffer.open(QBuffer::ReadWrite);
|
keyBuffer.open(QBuffer::ReadWrite);
|
||||||
|
|
||||||
FileKey::create(&keyBuffer);
|
FileKey::createRandom(&keyBuffer);
|
||||||
keyBuffer.reset();
|
keyBuffer.reset();
|
||||||
|
|
||||||
auto fileKey = QSharedPointer<FileKey>::create();
|
auto fileKey = QSharedPointer<FileKey>::create();
|
||||||
@ -166,7 +184,7 @@ void TestKeys::testFileKeyHash()
|
|||||||
QBuffer keyBuffer;
|
QBuffer keyBuffer;
|
||||||
keyBuffer.open(QBuffer::ReadWrite);
|
keyBuffer.open(QBuffer::ReadWrite);
|
||||||
|
|
||||||
FileKey::create(&keyBuffer);
|
FileKey::createRandom(&keyBuffer);
|
||||||
|
|
||||||
CryptoHash cryptoHash(CryptoHash::Sha256);
|
CryptoHash cryptoHash(CryptoHash::Sha256);
|
||||||
cryptoHash.addData(keyBuffer.data());
|
cryptoHash.addData(keyBuffer.data());
|
||||||
|
BIN
tests/data/FileKeyXmlV2.kdbx
Normal file
12
tests/data/FileKeyXmlV2.keyx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<KeyFile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<Meta>
|
||||||
|
<Version>2.0</Version>
|
||||||
|
</Meta>
|
||||||
|
<Key>
|
||||||
|
<Data Hash="FE2949B8">
|
||||||
|
A7007945 D07D54BA 28DF6434 1B4500FC
|
||||||
|
9750DFB1 D36ADA2D 9C32DC19 4C7AB01B
|
||||||
|
</Data>
|
||||||
|
</Key>
|
||||||
|
</KeyFile>
|
BIN
tests/data/FileKeyXmlV2BrokenHex.kdbx
Normal file
12
tests/data/FileKeyXmlV2BrokenHex.keyx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<KeyFile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<Meta>
|
||||||
|
<Version>2.0</Version>
|
||||||
|
</Meta>
|
||||||
|
<Key>
|
||||||
|
<Data Hash="FE2949B8">
|
||||||
|
X7007945 D07D54BA 28DF6434 1B4500FC
|
||||||
|
9750DFB1 D36ADA2D 9C32DC19 4C7AB01B
|
||||||
|
</Data>
|
||||||
|
</Key>
|
||||||
|
</KeyFile>
|
BIN
tests/data/FileKeyXmlV2HashFail.kdbx
Normal file
12
tests/data/FileKeyXmlV2HashFail.keyx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<KeyFile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<Meta>
|
||||||
|
<Version>2.0</Version>
|
||||||
|
</Meta>
|
||||||
|
<Key>
|
||||||
|
<Data Hash="FE2949B9">
|
||||||
|
A7007945 D07D54BA 28DF6434 1B4500FC
|
||||||
|
9750DFB1 D36ADA2D 9C32DC19 4C7AB01B
|
||||||
|
</Data>
|
||||||
|
</Key>
|
||||||
|
</KeyFile>
|
@ -29,14 +29,18 @@
|
|||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
#include <QListWidgetItem>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRadioButton>
|
||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
#include <QTest>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
|
||||||
#include "config-keepassx-tests.h"
|
#include "config-keepassx-tests.h"
|
||||||
#include "core/Bootstrap.h"
|
#include "core/Bootstrap.h"
|
||||||
@ -142,6 +146,9 @@ void TestGui::init()
|
|||||||
fileDialog()->setNextFileName(m_dbFilePath);
|
fileDialog()->setNextFileName(m_dbFilePath);
|
||||||
triggerAction("actionDatabaseOpen");
|
triggerAction("actionDatabaseOpen");
|
||||||
|
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
m_dbWidget = m_tabWidget->currentDatabaseWidget();
|
||||||
auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
|
auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
|
||||||
QVERIFY(databaseOpenWidget);
|
QVERIFY(databaseOpenWidget);
|
||||||
auto* editPassword = databaseOpenWidget->findChild<QLineEdit*>("editPassword");
|
auto* editPassword = databaseOpenWidget->findChild<QLineEdit*>("editPassword");
|
||||||
@ -151,8 +158,10 @@ void TestGui::init()
|
|||||||
QTest::keyClicks(editPassword, "a");
|
QTest::keyClicks(editPassword, "a");
|
||||||
QTest::keyClick(editPassword, Qt::Key_Enter);
|
QTest::keyClick(editPassword, Qt::Key_Enter);
|
||||||
|
|
||||||
m_dbWidget = m_tabWidget->currentDatabaseWidget();
|
QTRY_VERIFY(!m_dbWidget->isLocked());
|
||||||
m_db = m_dbWidget->database();
|
m_db = m_dbWidget->database();
|
||||||
|
|
||||||
|
QApplication::processEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every test ends with closing the temp database without saving
|
// Every test ends with closing the temp database without saving
|
||||||
@ -302,7 +311,7 @@ void TestGui::testCreateDatabase()
|
|||||||
// check key and encryption
|
// check key and encryption
|
||||||
QCOMPARE(m_db->key()->keys().size(), 2);
|
QCOMPARE(m_db->key()->keys().size(), 2);
|
||||||
QCOMPARE(m_db->kdf()->rounds(), 2);
|
QCOMPARE(m_db->kdf()->rounds(), 2);
|
||||||
QCOMPARE(m_db->kdf()->uuid(), KeePass2::KDF_ARGON2);
|
QCOMPARE(m_db->kdf()->uuid(), KeePass2::KDF_ARGON2D);
|
||||||
QCOMPARE(m_db->cipher(), KeePass2::CIPHER_AES256);
|
QCOMPARE(m_db->cipher(), KeePass2::CIPHER_AES256);
|
||||||
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
||||||
compositeKey->addKey(QSharedPointer<PasswordKey>::create("test"));
|
compositeKey->addKey(QSharedPointer<PasswordKey>::create("test"));
|
||||||
@ -1146,24 +1155,45 @@ void TestGui::testEntryPlaceholders()
|
|||||||
|
|
||||||
void TestGui::testDragAndDropEntry()
|
void TestGui::testDragAndDropEntry()
|
||||||
{
|
{
|
||||||
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
auto entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||||
auto* groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
auto groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
||||||
QAbstractItemModel* groupModel = groupView->model();
|
auto groupModel = qobject_cast<GroupModel*>(groupView->model());
|
||||||
|
|
||||||
QModelIndex sourceIndex = entryView->model()->index(0, 1);
|
QModelIndex sourceIndex = entryView->model()->index(0, 1);
|
||||||
QModelIndex targetIndex = groupModel->index(0, 0, groupModel->index(0, 0));
|
QModelIndex targetIndex = groupModel->index(0, 0, groupModel->index(0, 0));
|
||||||
QVERIFY(sourceIndex.isValid());
|
QVERIFY(sourceIndex.isValid());
|
||||||
QVERIFY(targetIndex.isValid());
|
QVERIFY(targetIndex.isValid());
|
||||||
|
auto targetGroup = groupModel->groupFromIndex(targetIndex);
|
||||||
|
|
||||||
QMimeData mimeData;
|
QMimeData mimeData;
|
||||||
QByteArray encoded;
|
QByteArray encoded;
|
||||||
QDataStream stream(&encoded, QIODevice::WriteOnly);
|
QDataStream stream(&encoded, QIODevice::WriteOnly);
|
||||||
Entry* entry = entryView->entryFromIndex(sourceIndex);
|
|
||||||
|
auto entry = entryView->entryFromIndex(sourceIndex);
|
||||||
stream << entry->group()->database()->uuid() << entry->uuid();
|
stream << entry->group()->database()->uuid() << entry->uuid();
|
||||||
mimeData.setData("application/x-keepassx-entry", encoded);
|
mimeData.setData("application/x-keepassx-entry", encoded);
|
||||||
|
|
||||||
|
// Test Copy, UUID should change, history remain
|
||||||
|
QVERIFY(groupModel->dropMimeData(&mimeData, Qt::CopyAction, -1, 0, targetIndex));
|
||||||
|
// Find the copied entry
|
||||||
|
auto newEntry = targetGroup->findEntryByPath(entry->title());
|
||||||
|
QVERIFY(newEntry);
|
||||||
|
QVERIFY(entry->uuid() != newEntry->uuid());
|
||||||
|
QCOMPARE(entry->historyItems().count(), newEntry->historyItems().count());
|
||||||
|
|
||||||
|
encoded.clear();
|
||||||
|
entry = entryView->entryFromIndex(sourceIndex);
|
||||||
|
auto history = entry->historyItems().count();
|
||||||
|
auto uuid = entry->uuid();
|
||||||
|
stream << entry->group()->database()->uuid() << entry->uuid();
|
||||||
|
mimeData.setData("application/x-keepassx-entry", encoded);
|
||||||
|
|
||||||
|
// Test Move, entry pointer should remain the same
|
||||||
|
QCOMPARE(entry->group()->name(), QString("NewDatabase"));
|
||||||
QVERIFY(groupModel->dropMimeData(&mimeData, Qt::MoveAction, -1, 0, targetIndex));
|
QVERIFY(groupModel->dropMimeData(&mimeData, Qt::MoveAction, -1, 0, targetIndex));
|
||||||
QCOMPARE(entry->group()->name(), QString("General"));
|
QCOMPARE(entry->group()->name(), QString("General"));
|
||||||
|
QCOMPARE(entry->uuid(), uuid);
|
||||||
|
QCOMPARE(entry->historyItems().count(), history);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestGui::testDragAndDropGroup()
|
void TestGui::testDragAndDropGroup()
|
||||||
@ -1484,6 +1514,163 @@ void TestGui::testTrayRestoreHide()
|
|||||||
|
|
||||||
trayIcon->activated(QSystemTrayIcon::DoubleClick);
|
trayIcon->activated(QSystemTrayIcon::DoubleClick);
|
||||||
QTRY_VERIFY(!m_mainWindow->isVisible());
|
QTRY_VERIFY(!m_mainWindow->isVisible());
|
||||||
|
|
||||||
|
// Ensure window is visible at the end
|
||||||
|
trayIcon->activated(QSystemTrayIcon::DoubleClick);
|
||||||
|
QTRY_VERIFY(m_mainWindow->isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGui::testAutoType()
|
||||||
|
{
|
||||||
|
// Clear entries from root group to guarantee order
|
||||||
|
for (Entry* entry : m_db->rootGroup()->entries()) {
|
||||||
|
m_db->rootGroup()->removeEntry(entry);
|
||||||
|
}
|
||||||
|
Tools::wait(150);
|
||||||
|
|
||||||
|
// 1. Create an entry with Auto-Type disabled
|
||||||
|
|
||||||
|
// 1.a) Click the new entry button and set the title
|
||||||
|
auto* entryNewAction = m_mainWindow->findChild<QAction*>("actionEntryNew");
|
||||||
|
QVERIFY(entryNewAction->isEnabled());
|
||||||
|
|
||||||
|
auto* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||||
|
QVERIFY(toolBar);
|
||||||
|
|
||||||
|
QWidget* entryNewWidget = toolBar->widgetForAction(entryNewAction);
|
||||||
|
QVERIFY(entryNewWidget->isVisible());
|
||||||
|
QVERIFY(entryNewWidget->isEnabled());
|
||||||
|
|
||||||
|
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||||
|
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
|
||||||
|
|
||||||
|
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
|
||||||
|
QVERIFY(editEntryWidget);
|
||||||
|
|
||||||
|
auto* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
|
||||||
|
QVERIFY(titleEdit);
|
||||||
|
|
||||||
|
QTest::keyClicks(titleEdit, "1. Entry With Disabled Auto-Type");
|
||||||
|
|
||||||
|
auto* usernameComboBox = editEntryWidget->findChild<QComboBox*>("usernameComboBox");
|
||||||
|
QVERIFY(usernameComboBox);
|
||||||
|
|
||||||
|
QTest::mouseClick(usernameComboBox, Qt::LeftButton);
|
||||||
|
QTest::keyClicks(usernameComboBox, "AutocompletionUsername");
|
||||||
|
|
||||||
|
// 1.b) Uncheck Auto-Type checkbox
|
||||||
|
editEntryWidget->setCurrentPage(3);
|
||||||
|
auto* enableAutoTypeButton = editEntryWidget->findChild<QCheckBox*>("enableButton");
|
||||||
|
QVERIFY(enableAutoTypeButton);
|
||||||
|
QVERIFY(enableAutoTypeButton->isVisible());
|
||||||
|
QVERIFY(enableAutoTypeButton->isEnabled());
|
||||||
|
|
||||||
|
enableAutoTypeButton->click();
|
||||||
|
QVERIFY(!enableAutoTypeButton->isChecked());
|
||||||
|
|
||||||
|
// 1.c) Save changes
|
||||||
|
editEntryWidget->setCurrentPage(0);
|
||||||
|
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
|
||||||
|
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||||
|
|
||||||
|
// 2. Create an entry with default/inherited Auto-Type sequence
|
||||||
|
|
||||||
|
// 2.a) Click the new entry button and set the title
|
||||||
|
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||||
|
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
|
||||||
|
QTest::keyClicks(titleEdit, "2. Entry With Default Auto-Type Sequence");
|
||||||
|
QTest::mouseClick(usernameComboBox, Qt::LeftButton);
|
||||||
|
QTest::keyClicks(usernameComboBox, "AutocompletionUsername");
|
||||||
|
|
||||||
|
// 2.b) Confirm AutoType is enabled and default
|
||||||
|
editEntryWidget->setCurrentPage(3);
|
||||||
|
QVERIFY(enableAutoTypeButton->isChecked());
|
||||||
|
auto* inheritSequenceButton = editEntryWidget->findChild<QRadioButton*>("inheritSequenceButton");
|
||||||
|
QVERIFY(inheritSequenceButton->isChecked());
|
||||||
|
|
||||||
|
// 2.c) Save changes
|
||||||
|
editEntryWidget->setCurrentPage(0);
|
||||||
|
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||||
|
|
||||||
|
// 3. Create an entry with custom Auto-Type sequence
|
||||||
|
|
||||||
|
// 3.a) Click the new entry button and set the title
|
||||||
|
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||||
|
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
|
||||||
|
QTest::keyClicks(titleEdit, "3. Entry With Custom Auto-Type Sequence");
|
||||||
|
QTest::mouseClick(usernameComboBox, Qt::LeftButton);
|
||||||
|
QTest::keyClicks(usernameComboBox, "AutocompletionUsername");
|
||||||
|
|
||||||
|
// 3.b) Confirm AutoType is enabled and set custom sequence
|
||||||
|
editEntryWidget->setCurrentPage(3);
|
||||||
|
QVERIFY(enableAutoTypeButton->isChecked());
|
||||||
|
auto* customSequenceButton = editEntryWidget->findChild<QRadioButton*>("customSequenceButton");
|
||||||
|
QTest::mouseClick(customSequenceButton, Qt::LeftButton);
|
||||||
|
QVERIFY(customSequenceButton->isChecked());
|
||||||
|
QVERIFY(!inheritSequenceButton->isChecked());
|
||||||
|
auto* sequenceEdit = editEntryWidget->findChild<QLineEdit*>("sequenceEdit");
|
||||||
|
QVERIFY(sequenceEdit);
|
||||||
|
sequenceEdit->setFocus();
|
||||||
|
QTRY_VERIFY(sequenceEdit->hasFocus());
|
||||||
|
QTest::keyClicks(sequenceEdit, "{USERNAME}{TAB}{TAB}{PASSWORD}{ENTER}");
|
||||||
|
|
||||||
|
// 3.c) Save changes
|
||||||
|
editEntryWidget->setCurrentPage(0);
|
||||||
|
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
// Check total number of entries matches expected
|
||||||
|
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||||
|
QVERIFY(entryView);
|
||||||
|
QTRY_COMPARE(entryView->model()->rowCount(), 3);
|
||||||
|
|
||||||
|
// Sort entries by title
|
||||||
|
entryView->sortByColumn(1, Qt::AscendingOrder);
|
||||||
|
|
||||||
|
// Select first entry
|
||||||
|
entryView->selectionModel()->clearSelection();
|
||||||
|
QModelIndex entryIndex = entryView->model()->index(0, 0);
|
||||||
|
entryView->selectionModel()->select(entryIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
auto* entryPreviewWidget = m_dbWidget->findChild<EntryPreviewWidget*>("previewWidget");
|
||||||
|
QVERIFY(entryPreviewWidget->isVisible());
|
||||||
|
|
||||||
|
// Check that the Autotype tab in entry preview pane is disabled for entry with disabled Auto-Type
|
||||||
|
auto* entryAutotypeTab = entryPreviewWidget->findChild<QWidget*>("entryAutotypeTab");
|
||||||
|
QVERIFY(!entryAutotypeTab->isEnabled());
|
||||||
|
|
||||||
|
// Check that Auto-Type is disabled in the actual entry model as well
|
||||||
|
Entry* entry = entryView->entryFromIndex(entryIndex);
|
||||||
|
QVERIFY(!entry->autoTypeEnabled());
|
||||||
|
|
||||||
|
// Select second entry
|
||||||
|
entryView->selectionModel()->clearSelection();
|
||||||
|
entryIndex = entryView->model()->index(1, 0);
|
||||||
|
entryView->selectionModel()->select(entryIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select);
|
||||||
|
QVERIFY(entryPreviewWidget->isVisible());
|
||||||
|
|
||||||
|
// Check that the Autotype tab in entry preview pane is enabled for entry with default Auto-Type sequence;
|
||||||
|
QVERIFY(entryAutotypeTab->isEnabled());
|
||||||
|
|
||||||
|
// Check that Auto-Type is enabled in the actual entry model as well
|
||||||
|
entry = entryView->entryFromIndex(entryIndex);
|
||||||
|
QVERIFY(entry->autoTypeEnabled());
|
||||||
|
|
||||||
|
// Select third entry
|
||||||
|
entryView->selectionModel()->clearSelection();
|
||||||
|
entryIndex = entryView->model()->index(2, 0);
|
||||||
|
entryView->selectionModel()->select(entryIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select);
|
||||||
|
QVERIFY(entryPreviewWidget->isVisible());
|
||||||
|
|
||||||
|
// Check that the Autotype tab in entry preview pane is enabled for entry with custom Auto-Type sequence
|
||||||
|
QVERIFY(entryAutotypeTab->isEnabled());
|
||||||
|
|
||||||
|
// Check that Auto-Type is enabled in the actual entry model as well
|
||||||
|
entry = entryView->entryFromIndex(entryIndex);
|
||||||
|
QVERIFY(entry->autoTypeEnabled());
|
||||||
|
|
||||||
|
// De-select third entry
|
||||||
|
entryView->selectionModel()->clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
int TestGui::addCannedEntries()
|
int TestGui::addCannedEntries()
|
||||||
|
@ -68,6 +68,7 @@ private slots:
|
|||||||
void testDatabaseLocking();
|
void testDatabaseLocking();
|
||||||
void testDragAndDropKdbxFiles();
|
void testDragAndDropKdbxFiles();
|
||||||
void testSortGroups();
|
void testSortGroups();
|
||||||
|
void testAutoType();
|
||||||
void testTrayRestoreHide();
|
void testTrayRestoreHide();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|