Compare commits
127 Commits
release/2.
...
fork_keepa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcb547a072 | ||
|
|
be94f97e05 | ||
|
|
f23b07f609 | ||
|
|
48715c6bda | ||
|
|
e5308d61ca | ||
|
|
709ea1101c | ||
|
|
b42fb1cce1 | ||
|
|
a7ffd28e26 | ||
|
|
3e3e87d3c5 | ||
|
|
c410c380f6 | ||
|
|
2d6f2f7895 | ||
|
|
ad773c567d | ||
|
|
2dbb29fc85 | ||
|
|
12be175d58 | ||
|
|
afc7dcd83c | ||
|
|
3cbe4df8c7 | ||
|
|
033dd79c58 | ||
|
|
6fa34bdbfe | ||
|
|
188fff1488 | ||
|
|
6d23a3bd2c | ||
|
|
a5dee81e45 | ||
|
|
4030dbc4b4 | ||
|
|
3efad4e14b | ||
|
|
ceb2cd2b67 | ||
|
|
c5eaee80cb | ||
|
|
ab95690043 | ||
|
|
37baa6fd25 | ||
|
|
69f05d4c26 | ||
|
|
91fafccb0a | ||
|
|
ef51065c98 | ||
|
|
03f11ce516 | ||
|
|
2583cc4aa4 | ||
|
|
422fd91255 | ||
|
|
d83743ea0b | ||
|
|
a81819914d | ||
|
|
93fedd0fff | ||
|
|
a73f5bc32e | ||
|
|
e3a3734bb6 | ||
|
|
1d00c22244 | ||
|
|
e180980b90 | ||
|
|
e6b2e4e95e | ||
|
|
1af985fde9 | ||
|
|
5e9fa18a4d | ||
|
|
e7a5b3939d | ||
|
|
f096f7d7fa | ||
|
|
f7735afcd6 | ||
|
|
56307e6cad | ||
|
|
1bfb62747c | ||
|
|
affff20b49 | ||
|
|
6f64c84c7d | ||
|
|
f56fcdd79b | ||
|
|
893b398d73 | ||
|
|
db98f114f9 | ||
|
|
b1e7c34b82 | ||
|
|
54f9b25b52 | ||
|
|
9366c5c233 | ||
|
|
e254cad39e | ||
|
|
ac1347f324 | ||
|
|
f9f9a34ba3 | ||
|
|
2b4d286de6 | ||
|
|
e7fe3ff968 | ||
|
|
5caf68988e | ||
|
|
bb4f5c2e94 | ||
|
|
eaa363d8c0 | ||
|
|
d181f80c8c | ||
|
|
33d8b6db62 | ||
|
|
59e2c38635 | ||
|
|
dd15db721a | ||
|
|
ef6d8f1138 | ||
|
|
612c1098ea | ||
|
|
ed7b634dbf | ||
|
|
4c1e5ec74c | ||
|
|
b0a68ea0de | ||
|
|
f32dc96757 | ||
|
|
79ac8b3c95 | ||
|
|
e5bd5f39fb | ||
|
|
dfee59742f | ||
|
|
61f922179b | ||
|
|
9e81c31e5a | ||
|
|
bdeef63fe4 | ||
|
|
ed693e146d | ||
|
|
14f12b0a25 | ||
|
|
e05f6a4c5b | ||
|
|
bd809ba90b | ||
|
|
15b9e82f93 | ||
|
|
aa839e2619 | ||
|
|
a6d3f973fa | ||
|
|
dab6d9408e | ||
|
|
6b05b84895 | ||
|
|
861fe2e5a9 | ||
|
|
63a5e474a6 | ||
|
|
b86c3e64ec | ||
|
|
6cb6f1f007 | ||
|
|
a2aac7066c | ||
|
|
c1dbe27f25 | ||
|
|
9e9ed8b532 | ||
|
|
de3d40b644 | ||
|
|
20a2a96222 | ||
|
|
b1f4e12d34 | ||
|
|
bc0a5a9440 | ||
|
|
e16c007d43 | ||
|
|
924eb6dbc4 | ||
|
|
b5e0572155 | ||
|
|
0f3a2531e7 | ||
|
|
dab7047113 | ||
|
|
6f20f0e2ec | ||
|
|
0f7b674cbb | ||
|
|
e2bf537c4a | ||
|
|
c5467c43bf | ||
|
|
806b8b0901 | ||
|
|
a740fe128c | ||
|
|
ba8f787d0d | ||
|
|
65a1d1b0f7 | ||
|
|
1009650b5c | ||
|
|
b14bec3bb0 | ||
|
|
a4c5997050 | ||
|
|
aa97bd5213 | ||
|
|
a4d4adb1f6 | ||
|
|
7e1d980d08 | ||
|
|
f3f1520f81 | ||
|
|
7e44b67906 | ||
|
|
44333fef0a | ||
|
|
414cb5026c | ||
|
|
f15ee90429 | ||
|
|
9bf61bfc5c | ||
|
|
7edeceec03 | ||
|
|
692c95b11e |
6
.github/CONTRIBUTING.md
vendored
@@ -85,6 +85,12 @@ All pull requests must comply with the above requirements and with the [stylegui
|
||||
Translations are managed on [Transifex](https://www.transifex.com/keepassxc/keepassxc/) which offers a web interface.
|
||||
Please join an existing language team or request a new one if there is none.
|
||||
|
||||
If you open a Pull Request with new strings that require translations, you will need to run the following:
|
||||
```
|
||||
./release-tool i18n lupdate
|
||||
```
|
||||
This will make the new strings available for translation in Transifex.
|
||||
|
||||
## Styleguides
|
||||
|
||||
### Git branch strategy
|
||||
|
||||
69
CHANGELOG.md
@@ -1,5 +1,74 @@
|
||||
# Changelog
|
||||
|
||||
## 2.7.4 (2022-10-29)
|
||||
|
||||
### Changes
|
||||
- Add 2 months expiration preset [#8687]
|
||||
- CLI: Add Unicode support on Windows [#8618]
|
||||
|
||||
### Fixes
|
||||
- Fix crash on macOS when unlocking database [#8676]
|
||||
- Fix display of passwords in preview panel [#8633]
|
||||
- Fix clicking links in entry preview panel [#8644]
|
||||
- Prevent expired entries search if no results returned [#8643]
|
||||
- Browser: Revert code causing connection problems [#8665]
|
||||
- Browser: Fix socket file symbolic link on Linux [#8656]
|
||||
- Flatpak: Fix launching browser proxy service [#8680]
|
||||
- SSH Agent: Fix paegent support on Windows [#8619]
|
||||
|
||||
## 2.7.3 (2022-10-23)
|
||||
|
||||
### Changes
|
||||
- Enhance Tags Support and Add Saved Searches [#8435, #8607]
|
||||
- Significant improvements to entry preview panel [#7993]
|
||||
- Add password strength indicator to all password fields [#7885]
|
||||
- Limit zxcvbn entropy estimation length to 128 characters [#7748]
|
||||
- Try full URL path when fetching favicon [#8565]
|
||||
- Hide usernames in preview panel when hidden in entry view [#8608]
|
||||
- Enable dark title bar on windows when accent color is not used [#8498]
|
||||
- Add option to display passwords in color in preview panel [#7097]
|
||||
- Add XML Export option to GUI [#8524]
|
||||
- Increase entropy required for a "good" password rating to 75 [#8523]
|
||||
- Add shortcut to copy password with TOTP appended [#8443]
|
||||
- Show entry count in status bar [#8435]
|
||||
- Allow KeePassXC to be built without X11 [#8147]
|
||||
- Enable use of VivoKey Apex and Dangerous Things FlexSecure tokens [#8332]
|
||||
- Add setting for number of recent files [#8239]
|
||||
- Add Ctrl+Tab shortcut to cycle databases in unlock dialog [#8168]
|
||||
- Replace offensive words in eff_large.wordlist [#7968]
|
||||
- Auto-Type: PICKCHARS can specify attribute and ignore BEEP [#8118]
|
||||
- Linux: Add isHardwareKeySupported and refreshHardwareKeys to DBus methods [#8055]
|
||||
- Add config variable to specify default database file name [#8042]
|
||||
- Support numeric aware sorting on Windows and macOS [#8363]
|
||||
- CLI: Add `db-edit` command [#8400]
|
||||
- CLI: Add option to display all attributes with `show` command [#8256]
|
||||
- CLI: Show UUID and tags with `show` and `clip` commands [#8241]
|
||||
- Browser: Move socket into separate directory on Linux [#8030]
|
||||
- Browser: Add group setting to omit WWW subdomain when matching URLs [#7988]
|
||||
- FdoSecrets: Ask to unlock the database when creating items [#8022, #8028]
|
||||
- FdoSecrets: Skip entries in recycle bin when searching [#8021]
|
||||
|
||||
### Fixes
|
||||
- Fix potential deadlock in UI when saving [#8606]
|
||||
- Fix newlines when copying notes from preview panel [#8542]
|
||||
- Fix dark mode detection on Linux [#8477]
|
||||
- Fix crash when deleting items in recycle bin while searching [#8117]
|
||||
- Fix crash when trying to close database during unlock [#8144]
|
||||
- Fix tabbing around the interface [#8435, #8520]
|
||||
- Fix OPVault import when there are multiple OTP fields [#8436]
|
||||
- Fix various Windows Hello bugs [#8354]
|
||||
- Fix use of Apple Watch for Quick Unlock [#8311]
|
||||
- Better handling of "Lock on Minimize" setting [#8202]
|
||||
- Check for write permission before entering portable mode [#8447]
|
||||
- Correct regex escape logic to prevent parse errors [#7778]
|
||||
- Normalize slashes and file case for last used databases [#7864, #7214]
|
||||
- Link ykcore against pthread [#7807]
|
||||
- Auto-Type: Fix menu entries in selection dialog on Windows [#7987]
|
||||
- Auto-Type: Fix use of modifiers under macOS [#8111]
|
||||
- CLI: Fix output when using clip with the -t flag [#8271]
|
||||
- Browser: Use asynchronous access confirm dialog [#8273]
|
||||
- Browser: Always send database locked/unlocked status [#8114]
|
||||
|
||||
## 2.7.1 (2022-04-05)
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -62,6 +62,27 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
option(WITH_XC_DOCS "Enable building of documentation" ON)
|
||||
|
||||
set(WITH_XC_X11 ON CACHE BOOL "Enable building with X11 deps")
|
||||
|
||||
if(APPLE)
|
||||
# Perform the platform checks before applying the stricter compiler flags.
|
||||
# Otherwise the kSecAccessControlTouchIDCurrentSet deprecation warning will result in an error.
|
||||
try_compile(XC_APPLE_COMPILER_SUPPORT_BIOMETRY
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tiometry_test/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_biometry_support.mm)
|
||||
message(STATUS "Biometry compiler support: ${XC_APPLE_COMPILER_SUPPORT_BIOMETRY}")
|
||||
|
||||
try_compile(XC_APPLE_COMPILER_SUPPORT_TOUCH_ID
|
||||
${CMAKE_CURRENT_BINARY_DIR}/touch_id_test/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_touch_id_support.mm)
|
||||
message(STATUS "Touch ID compiler support: ${XC_APPLE_COMPILER_SUPPORT_TOUCH_ID}")
|
||||
|
||||
try_compile(XC_APPLE_COMPILER_SUPPORT_WATCH
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tiometry_test/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_watch_support.mm)
|
||||
message(STATUS "Apple watch compiler support: ${XC_APPLE_COMPILER_SUPPORT_WATCH}")
|
||||
endif()
|
||||
|
||||
if(WITH_CCACHE)
|
||||
# Use the Compiler Cache (ccache) program
|
||||
# (install with: sudo apt get ccache)
|
||||
@@ -91,9 +112,25 @@ if(NOT WITH_XC_NETWORKING AND WITH_XC_UPDATECHECK)
|
||||
set(WITH_XC_UPDATECHECK OFF)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT WITH_XC_X11)
|
||||
message(STATUS "Disabling WITH_XC_AUTOTYPE because WITH_XC_X11 is disabled")
|
||||
set(WITH_XC_AUTOTYPE OFF)
|
||||
endif()
|
||||
|
||||
add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing")
|
||||
add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)")
|
||||
add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser")
|
||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare")
|
||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||
add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking")
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_feature_info(FdoSecrets WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API.")
|
||||
endif()
|
||||
|
||||
set(KEEPASSXC_VERSION_MAJOR "2")
|
||||
set(KEEPASSXC_VERSION_MINOR "7")
|
||||
set(KEEPASSXC_VERSION_PATCH "1")
|
||||
set(KEEPASSXC_VERSION_MINOR "8")
|
||||
set(KEEPASSXC_VERSION_PATCH "0")
|
||||
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
||||
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
|
||||
|
||||
@@ -310,9 +347,9 @@ check_add_gcc_compiler_flag("-Wcast-align")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
check_add_gcc_compiler_flag("-Qunused-arguments")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -Wl,--no-undefined")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now -pie")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
|
||||
endif()
|
||||
|
||||
@@ -447,17 +484,20 @@ include(CLangFormat)
|
||||
|
||||
set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools)
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus X11Extras REQUIRED)
|
||||
if(WITH_XC_X11)
|
||||
list(APPEND QT_COMPONENTS X11Extras)
|
||||
endif()
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED)
|
||||
elseif(APPLE)
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED HINTS
|
||||
/usr/local/opt/qt/lib/cmake
|
||||
/usr/local/Cellar/qt/*/lib/cmake
|
||||
/opt/homebrew/opt/qt/lib/cmake
|
||||
/usr/local/opt/qt@5/lib/cmake
|
||||
/usr/local/Cellar/qt@5/*/lib/cmake
|
||||
/opt/homebrew/opt/qt@5/lib/cmake
|
||||
ENV PATH)
|
||||
find_package(Qt5 COMPONENTS MacExtras HINTS
|
||||
/usr/local/opt/qt/lib/cmake
|
||||
/usr/local/Cellar/qt/*/lib/cmake
|
||||
/opt/homebrew/opt/qt/lib/cmake
|
||||
/usr/local/opt/qt@5/lib/cmake
|
||||
/usr/local/Cellar/qt@5/*/lib/cmake
|
||||
/opt/homebrew/opt/qt@5/lib/cmake
|
||||
ENV PATH)
|
||||
else()
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
|
||||
@@ -468,6 +508,10 @@ if(Qt5Core_VERSION VERSION_LESS "5.2.0")
|
||||
endif()
|
||||
|
||||
get_filename_component(Qt5_PREFIX ${Qt5_DIR}/../../.. REALPATH)
|
||||
if(APPLE)
|
||||
# Add includes under Qt5 Prefix in case Qt6 is also installed
|
||||
include_directories(SYSTEM ${Qt5_PREFIX}/include)
|
||||
endif()
|
||||
|
||||
# Process moc automatically
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
32
COPYING
@@ -144,10 +144,21 @@ Files: share/icons/badges/2_Expired.svg
|
||||
Copyright: 2022 KeePassXC Team <team@keepassxc.org>
|
||||
License: MIT
|
||||
|
||||
Files: share/icons/application/scalable/actions/chevron-double-down.svg
|
||||
Files: share/icons/application/scalable/actions/application-exit.svg
|
||||
share/icons/application/scalable/actions/attributes-copy.svg
|
||||
share/icons/application/scalable/actions/auto-type.svg
|
||||
share/icons/application/scalable/actions/bugreport.svg
|
||||
share/icons/application/scalable/actions/chevron-double-down.svg
|
||||
share/icons/application/scalable/actions/chevron-double-right.svg
|
||||
share/icons/application/scalable/actions/clipboard-text.svg
|
||||
share/icons/application/scalable/actions/configure.svg
|
||||
share/icons/application/scalable/actions/database-change-key.svg
|
||||
share/icons/application/scalable/actions/database-lock.svg
|
||||
share/icons/application/scalable/actions/database-lock-all.svg
|
||||
share/icons/application/scalable/actions/database-merge.svg
|
||||
share/icons/application/scalable/actions/database-search.svg
|
||||
share/icons/application/scalable/actions/dialog-close.svg
|
||||
share/icons/application/scalable/actions/dialog-ok.svg
|
||||
share/icons/application/scalable/actions/document-close.svg
|
||||
share/icons/application/scalable/actions/document-edit.svg
|
||||
share/icons/application/scalable/actions/document-export.svg
|
||||
@@ -159,43 +170,60 @@ Files: share/icons/application/scalable/actions/chevron-double-down.svg
|
||||
share/icons/application/scalable/actions/document-save.svg
|
||||
share/icons/application/scalable/actions/document-save-as.svg
|
||||
share/icons/application/scalable/actions/document-save-copy.svg
|
||||
share/icons/application/scalable/actions/donate.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-ltr.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-rtl.svg
|
||||
share/icons/application/scalable/actions/entry-clone.svg
|
||||
share/icons/application/scalable/actions/entry-delete.svg
|
||||
share/icons/application/scalable/actions/entry-restore.svg
|
||||
share/icons/application/scalable/actions/entry-edit.svg
|
||||
share/icons/application/scalable/actions/entry-new.svg
|
||||
share/icons/application/scalable/actions/favicon-download.svg
|
||||
share/icons/application/scalable/actions/fingerprint.svg
|
||||
share/icons/application/scalable/actions/group-clone.svg
|
||||
share/icons/application/scalable/actions/getting-started.svg
|
||||
share/icons/application/scalable/actions/group-delete.svg
|
||||
share/icons/application/scalable/actions/group-edit.svg
|
||||
share/icons/application/scalable/actions/group-clone.svg
|
||||
share/icons/application/scalable/actions/group-empty-trash.svg
|
||||
share/icons/application/scalable/actions/group-new.svg
|
||||
share/icons/application/scalable/actions/hammer-wrench.svg
|
||||
share/icons/application/scalable/actions/health.svg
|
||||
share/icons/application/scalable/actions/help-about.svg
|
||||
share/icons/application/scalable/actions/lock-question.svg
|
||||
share/icons/application/scalable/actions/keyboard-shortcuts.svg
|
||||
share/icons/application/scalable/actions/message-close.svg
|
||||
share/icons/application/scalable/actions/move-down.svg
|
||||
share/icons/application/scalable/actions/move-up.svg
|
||||
share/icons/application/scalable/actions/object-locked.svg
|
||||
share/icons/application/scalable/actions/object-unlocked.svg
|
||||
share/icons/application/scalable/actions/paperclip.svg
|
||||
share/icons/application/scalable/actions/password-copy.svg
|
||||
share/icons/application/scalable/actions/password-generator.svg
|
||||
share/icons/application/scalable/actions/password-show-off.svg
|
||||
share/icons/application/scalable/actions/password-show-on.svg
|
||||
share/icons/application/scalable/actions/qrcode.svg
|
||||
share/icons/application/scalable/actions/refresh.svg
|
||||
share/icons/application/scalable/actions/reports.svg
|
||||
share/icons/application/scalable/actions/reports-exclude.svg
|
||||
share/icons/application/scalable/actions/sort-alphabetical-ascending.svg
|
||||
share/icons/application/scalable/actions/sort-alphabetical-descending.svg
|
||||
share/icons/application/scalable/actions/statistics.svg
|
||||
share/icons/application/scalable/actions/system-help.svg
|
||||
share/icons/application/scalable/actions/system-search.svg
|
||||
share/icons/application/scalable/actions/system-software-update.svg
|
||||
share/icons/application/scalable/actions/tag.svg
|
||||
share/icons/application/scalable/actions/tag-multiple.svg
|
||||
share/icons/application/scalable/actions/tag-search.svg
|
||||
share/icons/application/scalable/actions/totp.svg
|
||||
share/icons/application/scalable/actions/totp-copy.svg
|
||||
share/icons/application/scalable/actions/totp-copy-password.svg
|
||||
share/icons/application/scalable/actions/totp-edit.svg
|
||||
share/icons/application/scalable/actions/trash.svg
|
||||
share/icons/application/scalable/actions/url-copy.svg
|
||||
share/icons/application/scalable/actions/user-guide.svg
|
||||
share/icons/application/scalable/actions/username-copy.svg
|
||||
share/icons/application/scalable/actions/view-history.svg
|
||||
share/icons/application/scalable/actions/web.svg
|
||||
share/icons/application/scalable/apps/internet-web-browser.svg
|
||||
share/icons/application/scalable/apps/keepassxc.svg
|
||||
share/icons/application/scalable/apps/keepassxc-dark.svg
|
||||
|
||||
@@ -57,10 +57,10 @@ To compile from source, open a **Terminal (Linux/MacOS)**, the **MSVC Tools Comm
|
||||
git pull
|
||||
```
|
||||
|
||||
For a stable build, it is recommended to check out the master branch.
|
||||
For a stable build, it is recommended to check out the `latest` tag.
|
||||
|
||||
```
|
||||
git checkout master
|
||||
git checkout latest
|
||||
```
|
||||
|
||||
2. Navigate to the directory where you have downloaded KeePassXC and type these commands:
|
||||
@@ -78,7 +78,7 @@ Note: These steps place the compiled KeePassXC binary inside the `./build/src/`
|
||||
|
||||
If you installed Qt5 via Homebrew, you should be able to compile KeePassXC without any changes. If CMake fails to find your Qt installation, you can specify it manually by adding the following parameter:
|
||||
|
||||
`-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake`
|
||||
`-DCMAKE_PREFIX_PATH=$(brew --prefix qt5)/lib/cmake`
|
||||
|
||||
(or whatever your Qt installation path is)
|
||||
|
||||
@@ -98,7 +98,7 @@ CMake Configuration Options
|
||||
## Common Parameters
|
||||
|
||||
```
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
-DCMAKE_INSTALL_PREFIX=$(brew --prefix)
|
||||
-DCMAKE_VERBOSE_MAKEFILE=ON
|
||||
-DCMAKE_BUILD_TYPE=<RelWithDebInfo/Debug/Release>
|
||||
-DWITH_GUI_TESTS=ON
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# <img src="https://keepassxc.org/images/keepassxc-logo.svg" width="40" height="40"/> KeePassXC
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/6326)
|
||||
[/statusIcon)](https://ci.keepassxc.org/?guest=1)
|
||||
[](https://codecov.io/gh/keepassxreboot/keepassxc)
|
||||
[](https://github.com/keepassxreboot/keepassxc/releases/)
|
||||
@@ -35,7 +36,7 @@ KeePassXC has numerous features for novice and power users alike. Our goal is to
|
||||
* Command line interface (keepassxc-cli)
|
||||
* Auto-Open databases
|
||||
* KeeShare shared databases (import, export, and synchronize)
|
||||
* SSH Agent
|
||||
* SSH Agent integration
|
||||
* FreeDesktop.org Secret Service (replace Gnome keyring, etc.)
|
||||
* Additional encryption choices: Twofish and ChaCha20
|
||||
|
||||
|
||||
5
cmake/compiler-checks/macos/control_biometry_support.mm
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <Security/Security.h>
|
||||
|
||||
int main() {
|
||||
return static_cast<int>(kSecAccessControlBiometryCurrentSet);
|
||||
}
|
||||
5
cmake/compiler-checks/macos/control_touch_id_support.mm
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <Security/Security.h>
|
||||
|
||||
int main() {
|
||||
return static_cast<int>(kSecAccessControlTouchIDCurrentSet);
|
||||
}
|
||||
5
cmake/compiler-checks/macos/control_watch_support.mm
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <Security/Security.h>
|
||||
|
||||
int main() {
|
||||
return static_cast<int>(kSecAccessControlWatch);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ Optionally, build AFL from source:
|
||||
|
||||
## Building KeePassXC For Fuzzing
|
||||
|
||||
A special "instrumented build" is used that allows the fuzzer to look into the program as it executes. We place it in its own build directory so it doesn't confused with the production build.
|
||||
A special "instrumented build" is used that allows the fuzzer to look into the program as it executes. We place it in its own build directory so it doesn't get confused with the production build.
|
||||
|
||||
$ cd your_keepassxc_source_directory
|
||||
$ mkdir buildafl
|
||||
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 126 KiB |
@@ -16,7 +16,7 @@
|
||||
|
||||
= keepassxc-cli(1)
|
||||
KeePassXC Team <team@keepassxc.org>
|
||||
:docdate: 2020-08-31
|
||||
:docdate: 2022-08-20
|
||||
:doctype: manpage
|
||||
:mansource: KeePassXC {revnumber}
|
||||
:manmanual: General Commands Manual
|
||||
@@ -66,6 +66,11 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
The key file will be created if the file that is referred to does not exist.
|
||||
If both the key file and password are empty, no database will be created.
|
||||
|
||||
*db-edit* [_options_] <__database__>::
|
||||
Edits a database.
|
||||
When setting a key file, the key file will be created if the file that is referred to
|
||||
does not exist.
|
||||
|
||||
*db-info* [_options_] <__database__>::
|
||||
Show a database's information.
|
||||
|
||||
@@ -154,7 +159,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
*--no-password*::
|
||||
Deactivates the password key for the database.
|
||||
|
||||
*-y*, *--yubikey* <__slot__>::
|
||||
*-y*, *--yubikey* <__slot[:serial]__>::
|
||||
Specifies a yubikey slot for unlocking the database.
|
||||
In a merge operation this option is used to specify the YubiKey slot for the first database.
|
||||
|
||||
@@ -177,7 +182,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
*--no-password-from*::
|
||||
Deactivates password key for the database to merge from.
|
||||
|
||||
*--yubikey-from* <__slot__>::
|
||||
*--yubikey-from* <__slot[:serial]__>::
|
||||
YubiKey slot for the second database.
|
||||
|
||||
*-s*, *--same-credentials*::
|
||||
@@ -235,16 +240,24 @@ The same password generation options as documented for the generate command can
|
||||
If a unique matching entry is found it will be copied to the clipboard.
|
||||
If multiple entries are found they will be listed to refine the search. (no clip performed)
|
||||
|
||||
=== Create and Import options
|
||||
*-k*, *--set-key-file* <__path__>::
|
||||
=== Db-create, Db-edit and Import options
|
||||
*--set-key-file* <__path__>::
|
||||
Set the key file for the database.
|
||||
|
||||
*-p*, *--set-password*::
|
||||
Set a password for the database.
|
||||
|
||||
=== Db-create, Import options
|
||||
*-t*, *--decryption-time* <__time__>::
|
||||
Target decryption time in MS for the database.
|
||||
|
||||
=== Db-edit options
|
||||
*--unset-password* <__path__>::
|
||||
Removes the password for the database.
|
||||
|
||||
*--unset-key-file* <__path__>::
|
||||
Removes the key file for the database.
|
||||
|
||||
=== Show options
|
||||
*-a*, *--attributes* <__attribute__>...::
|
||||
Shows the named attributes.
|
||||
@@ -252,6 +265,9 @@ The same password generation options as documented for the generate command can
|
||||
If no attributes are specified and *-t* is not specified, a summary of the default attributes is given.
|
||||
Protected attributes will be displayed in clear text if specified explicitly by this option.
|
||||
|
||||
*--all*::
|
||||
Show all the attributes of the entry.
|
||||
|
||||
*-s*, *--show-protected*::
|
||||
Shows the protected attributes in clear text.
|
||||
|
||||
@@ -270,7 +286,7 @@ The same password generation options as documented for the generate command can
|
||||
Sets the Path of the wordlist for the diceware generator.
|
||||
The wordlist must have > 1000 words, otherwise the program will fail.
|
||||
If the wordlist has < 4000 words a warning will be printed to STDERR.
|
||||
Any *diceware*-compatible wordlist can used. Note however that *KeePassXC* will NOT verify the PGP signature of signed wordlists.
|
||||
Any *diceware*-compatible wordlist can be used. Note however that *KeePassXC* will NOT verify the PGP signature of signed wordlists.
|
||||
|
||||
=== Export options
|
||||
*-f*, *--format*::
|
||||
|
||||
@@ -49,9 +49,6 @@ Your wallet works offline and requires no Internet connection.
|
||||
*--pw-stdin*::
|
||||
Read password of the database from stdin.
|
||||
|
||||
*--pw*, *--parent-window* <__handle__>::
|
||||
Parent window handle.
|
||||
|
||||
*--debug-info*::
|
||||
Displays debugging information.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ image::welcome_screen.png[]
|
||||
.Create database - General information
|
||||
image::new_db_wizard_1.png[,80%]
|
||||
|
||||
3. Click Continue. The Encryption Settings screen appears, we don't recommend making any changes besides increasing or decreasing the decryption time using the slider. Setting the Decryption Time slider at a higher values means that the database will have higher level of protection but the time taken by the database to open will increase.
|
||||
3. Click Continue. The Encryption Settings screen appears, we don't recommend making any changes besides increasing or decreasing the decryption time using the slider. Setting the Decryption Time slider at higher values means that the database will have higher level of protection but the time taken by the database to open will increase.
|
||||
+
|
||||
.Create database - Encryption settings
|
||||
image::new_db_wizard_2.png[,80%]
|
||||
@@ -44,7 +44,7 @@ To open an existing database, perform the following steps:
|
||||
.Open an existing database
|
||||
image::open_database.png[]
|
||||
|
||||
2. Navigate to the location of the your database on your computer and open the database file. The database unlock screen will appear:
|
||||
2. Navigate to the location of the database on your computer and open the database file. The database unlock screen will appear:
|
||||
+
|
||||
.Database unlock screen
|
||||
image::unlock_database.png[]
|
||||
@@ -84,7 +84,7 @@ There are three ways that KeePassXC can handle database files. This behavior is
|
||||
|
||||
3. *Direct-write saves* write directly to the existing database file. This is an unsafe operation since any interruption can leave your entire database inaccessible. We only recommend using this option when interfacing with Linux GVFS services (e.g. Google Cloud on Gnome) and other types of storage services that host a virtual drive system.
|
||||
|
||||
In addition to these save options, KeePassXC can create a backup of your existing database file just prior to saving. This backup can be placed in a custom folder with a custom file naming scheme.
|
||||
In addition to these save options, KeePassXC can create a backup of your existing database file just prior to saving. This backup will be saved at the path specified in the *Backup destination* field. This path can be absolute or relative. The latter will be resolved according to the databases path. It is possible to specify a custom naming scheme with placeholders. See xref:UserGuide.adoc#_backup_path_placeholders[Backup Path Placeholders] for available placeholders and examples.
|
||||
|
||||
image::save_options.png[]
|
||||
// end::advanced[]
|
||||
@@ -196,7 +196,7 @@ KeePassXC provides an enhanced and granular search features the enables you to s
|
||||
|* |Term is handled as a regular expression
|
||||
|===
|
||||
|
||||
The following fields can be searched along with their abbreviated name in parenthesis:
|
||||
The following fields can be searched along with their abbreviated name in parentheses:
|
||||
|
||||
* Title (t)
|
||||
* Username (u)
|
||||
@@ -242,7 +242,7 @@ The following tables lists a few samples search queries for your reference:
|
||||
|
||||
== Advanced Entry Options
|
||||
=== Additional Attributes
|
||||
A lot of applications and web sites now require to provide additional information when you create accounts. The additional information is used to block hackers if any suspicious activity is detected. In addition, the additional information you provide can be used to reset passwords if you forget them. You can also store arbitrary information here that can be copied to the clipboard or Auto-Typed using the `{S:<ATTR_NAME>}` action code.
|
||||
A lot of applications and web sites now require providing additional information when you create accounts. The additional information is used to block hackers if any suspicious activity is detected. In addition, the additional information you provide can be used to reset passwords if you forget them. You can also store arbitrary information here that can be copied to the clipboard or Auto-Typed using the `{S:<ATTR_NAME>}` action code.
|
||||
|
||||
To protect an attribute from being displayed by default, activate the _Protect_ checkbox *(A)*. To show the contents of the attribute while keeping it protected, press the _Reveal_ button *(B)*.
|
||||
|
||||
@@ -332,7 +332,7 @@ image::database_settings.png[]
|
||||
* *Max history items:* This is the maximum number of history items that are stored for each entry. When you set this to 0, no history will be saved. Set this value to a low value to prevent the database from getting too large (we recommend no more than 10).
|
||||
* *Max. history size:* When the history of an entry gets above this size, it is truncated. For example, this happens when entries have large attachments. Set this value small to prevent the database from getting too large (we recommend 6 MiB).
|
||||
* *Use recycle bin:* Select this check-box if you want deleted entries to move to the recycle bin instead of being permanently removed. The recycle bin will be created if it does not already exist after your first deletion. To delete entries permanently, you must empty the recycle bin manually.
|
||||
* *Enable compression:* KeePassXC databases can be compressed before being encrypted. Compression reduces the size of the database and does not have any appreciable affect on speed. It is recommended to always save databases with compression.
|
||||
* *Enable compression:* KeePassXC databases can be compressed before being encrypted. Compression reduces the size of the database and does not have any appreciable affect on speed. It is recommended to always save databases with compression.
|
||||
|
||||
3. Click the Security button in the left-hand menu bar to change your database credentials and change encryption settings.
|
||||
+
|
||||
@@ -346,7 +346,7 @@ image::database_security_credentials.png[]
|
||||
+
|
||||
WARNING: Consider creating a backup of your YubiKey. Please refer to <<Creating a YubiKey backup>>
|
||||
|
||||
5. Encryption settings allows you to change the average time it takes to encrypt and decrypt the database. The longer time that is chosen, the harder it will be to brute force attack your database. *We recommend a setting of one second.*
|
||||
5. Encryption settings allow you to change the average time it takes to encrypt and decrypt the database. The longer time that is chosen, the harder it will be to brute force attack your database. *We recommend a setting of one second.*
|
||||
+
|
||||
.Database encryption
|
||||
image::database_security_encryption.png[]
|
||||
@@ -362,7 +362,7 @@ The following key derivation functions are supported:
|
||||
|
||||
* AES-KDF (KDBX 4 and KDBX 3.1): This key derivation function is based on iterating AES. Users can change the number of iterations. The more iterations, the harder are dictionary and guessing attacks, but also database loading/saving takes more time (linearly). KDBX 3.1 only supports AES-KDF; any other key derivation function, like for instance Argon2, requires KDBX 4.
|
||||
|
||||
* Argon2 (KDBX 4 - recommended): KDBX 4, the Argon2 key derivation function can be used for transforming the composite master key (as protection against dictionary attacks). The main advantage of Argon2 over AES-KDF is that it provides a better resistance against GPU/ASIC attacks (due to being a memory-hard function). The number of iterations scales linearly with the required time. By increasing the memory parameter, GPU/ASIC attacks become harder and the required time increases. The parallelism parameter can be used to specify how many threads should be used. We recommend using Argon2id to prevent against timing-based attacks. Argon2d offers maximum compatibility with other KeePass-based apps, the default settings provide sufficient protection against any known attacks.
|
||||
* Argon2 (KDBX 4 - recommended): KDBX 4, the Argon2 key derivation function can be used for transforming the composite master key (as protection against dictionary attacks). The main advantage of Argon2 over AES-KDF is that it provides a better resistance against GPU/ASIC attacks (due to being a memory-hard function). The number of iterations scales linearly with the required time. By increasing the memory parameter, GPU/ASIC attacks become harder and the required time increases. The parallelism parameter can be used to specify how many threads should be used. We recommend using Argon2id to prevent against timing-based attacks. Argon2d offers maximum compatibility with other KeePass-based apps, the default settings provide sufficient protection against any known attacks.
|
||||
|
||||
== Database Maintenance
|
||||
KeePassXC offers some maintenance features that can be applied to clean up your database. Navigate to _Database_ -> _Database settings_ then click on _Maintenance_ on the left hand panel. The following screen appears. On this screen you can delete multiple icons at once and purge any unused icons in your database.
|
||||
|
||||
@@ -4,7 +4,7 @@ include::.sharedheader[]
|
||||
|
||||
// tag::content[]
|
||||
== Importing External Databases
|
||||
KeePassXC allows your to import external databases from the following options:
|
||||
KeePassXC allows you to import external databases from the following options:
|
||||
|
||||
* Comma-Separated Values (CSV) file
|
||||
* 1Password OPVault
|
||||
@@ -19,7 +19,7 @@ To open the CSV file, perform the following steps:
|
||||
|
||||
2. Click Import from CSV button on the welcome screen or use the menu Database > Import > CSV File.
|
||||
|
||||
3. Navigate to the location of the your CSV file on your computer and open the file. The new database wizard will appear. Follow the steps of creating a new database in Chapter 1.
|
||||
3. Navigate to the location of your CSV file on your computer and open the file. The new database wizard will appear. Follow the steps of creating a new database in Chapter 1.
|
||||
|
||||
4. After saving your new database file, the CSV import wizard will appear. On this dialog you can choose the various options for properly importing the data. You may need to select the _First line has field names_ checkbox before starting. Analyze the output in the preview at the bottom to determine the correct import settings.
|
||||
+
|
||||
@@ -46,16 +46,16 @@ To import a KeePass 1 database file in KeePassXC, perform the following steps:
|
||||
|
||||
2. Click Import from KeePass 1 button on the welcome screen or use the menu Database > Import > KeePass 1 Database.
|
||||
|
||||
3. Navigate to the location of the your legacy KeePass 1 database file (`.kdb`) on your computer and open the file. You are prompted for the password and the Key file for your `.kdb` file.
|
||||
3. Navigate to the location of your legacy KeePass 1 database file (`.kdb`) on your computer and open the file. You are prompted for the password and the Key file for your `.kdb` file.
|
||||
|
||||
4. Enter the password for your old `.kdb` file and click *OK*. You are prompted for provide a name for the new database format that KeePassXC recognizes.
|
||||
4. Enter the password for your old `.kdb` file and click *OK*. You are prompted to provide a name for the new database format that KeePassXC recognizes.
|
||||
|
||||
5. Provide a name for the new database format, select a folder on your computer to save the file, and click Save.
|
||||
|
||||
6. The data from the `.kdb` file gets imported and converted to the new format, which is compatible with KeePassXC. You can now start using the new database file (`.kdbx`) in KeePassXC.
|
||||
|
||||
== Exporting Databases
|
||||
KeePassXC supports multiple ways to export your database for transfer to another program or to print out and archive. To export your database into the KDB XML format, you must use the KeePassXC CLI: `keepassxc-cli export <database.kdbx>`.
|
||||
KeePassXC supports multiple ways to export your database for transfer to another program or to print out and archive.
|
||||
|
||||
WARNING: Exporting your database will result in all of your passwords and sensitive information being stored in an unencrypted format. We do not recommend saving your exported database for long periods of time as that can cause a compromise of sensitive information.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ To use sharing, you need to enable it for the application.
|
||||
image::keeshare_application_settings.png[]
|
||||
|
||||
=== Sharing Credentials
|
||||
If you checked _Allow export_ in the Sharing settings you can now share a group of passwords. Sharing is always is defined on a particular group. If you enable sharing on a group, every entry under this group, and its children, are shared. If you enable sharing on the root node, **every password** inside your database gets shared!
|
||||
If you checked _Allow export_ in the Sharing settings you can now share a group of passwords. Sharing is always defined on a particular group. If you enable sharing on a group, every entry under this group, and its children, are shared. If you enable sharing on the root node, **every password** inside your database gets shared!
|
||||
|
||||
NOTE: KeeShare does not synchronize group structure after the initial share is created. At this time, KeeShare operates at the entry level; shared entries moved outside of a shared group are still synchronized.
|
||||
|
||||
@@ -45,7 +45,7 @@ A shared group shows a cloud icon badge over the group icon *(A)* and a banner i
|
||||
image::keeshare_shared_group.png[]
|
||||
|
||||
=== Technical Details and Limitations of Sharing
|
||||
Sharing relies on the combination of file exports and imports as well as the synchronization mechanism provided by KeePassXC. Since the merge algorithm uses the history of entries to prevent data loss, this history must be enabled and have a sufficient size. Furthermore, the merge algorithm is location independent, therefore it does not matter if entries are moved outside of an import group. These entries will be updated none the less. Moving entries outside of export groups will prevent a further export of the entry, but it will not ensure that the already shared data will be removed from any client.
|
||||
Sharing relies on the combination of file exports and imports as well as the synchronization mechanism provided by KeePassXC. Since the merge algorithm uses the history of entries to prevent data loss, this history must be enabled and have a sufficient size. Furthermore, the merge algorithm is location independent, therefore it does not matter if entries are moved outside of an import group. These entries will be updated nonetheless. Moving entries outside of export groups will prevent a further export of the entry, but it will not ensure that the already shared data will be removed from any client.
|
||||
|
||||
KeeShare uses a custom certification mechanism to ensure that the source of the data is the expected one. This ensures that the data was exported by the signer but it is not possible to detect if someone replaced the data with an older version from a valid signer. To prevent this, the container could be placed at a location which is only writeable for valid signers.
|
||||
// end::content[]
|
||||
|
||||
@@ -25,6 +25,7 @@ NOTE: On macOS please substitute `Ctrl` with `Cmd` (aka `⌘`).
|
||||
|Copy URL | Ctrl + U
|
||||
|Open URL | Ctrl + Shift + U
|
||||
|Copy TOTP | Ctrl + T
|
||||
|Copy Password and TOTP | Ctrl + Y
|
||||
|Show TOTP | Ctrl + Shift + T
|
||||
|Trigger AutoType | Ctrl + Shift + V
|
||||
|Add key to SSH Agent | Ctrl + H
|
||||
|
||||
@@ -48,7 +48,7 @@ This section contains full details on advanced features available in KeePassXC.
|
||||
|===
|
||||
|
||||
=== Entry Cross-Reference
|
||||
A reference to another entry's field is possible using the short-hand syntax:
|
||||
A reference to another entry's field is possible using the shorthand syntax:
|
||||
`{REF:<FIELD>@<SEARCH_IN>:<SEARCH_TEXT>}`
|
||||
|
||||
`<FIELD>` and `<SEARCH_IN>` can be one of following:
|
||||
@@ -100,4 +100,29 @@ Convert resolved placeholder (e.g., {USERNAME}, {PASSWORD}, etc.) using the foll
|
||||
|
||||
`{T-REPLACE-RX:/<PLACEHOLDER>/<SEARCH>/<REPLACE>/}` +
|
||||
Use regular expressions to find and replace data from a resolved placeholder. Refer to match groups using $1, $2, etc.
|
||||
|
||||
=== Backup Path Placeholders
|
||||
[grid=rows, frame=none, width=90%]
|
||||
|===
|
||||
|Database Backup Path Placeholder |Description
|
||||
|
||||
|{DB_FILENAME} |The database's filename without extension
|
||||
|{TIME} |The current time formatted as dd_MM_yyyy_hh-mm-ss.
|
||||
|{TIME:<format>} |The current time formatted according to the format string specified by <format>. See https://doc.qt.io/qt-5/qtime.html#toString for a list of available placeholders.
|
||||
|===
|
||||
|
||||
[grid=rows, frame=none, width=90%]
|
||||
|===
|
||||
|Backup path example |Location of backup(s)
|
||||
|
||||
|`{DB_FILENAME}-{TIME}.bak.kdbx` |`C:\Users\MyUsername\MyDatabase-02_01_2022_03-04-05.bak.kdbx` +
|
||||
`C:\Users\MyUsername\MyDatabase-05_01_2022_12-10-00.bak.kdbx`
|
||||
|`backups\\{DB_FILENAME}.bak.kdbx` |`C:\Users\MyUsername\backups\MyDatabase.bak.kdbx`
|
||||
|`C:\Backups\{TIME:dd.MM.yyyy}\\{DB_FILENAME}.kdbx` |`C:\Backups\02.01.2022\MyDatabase.kdbx` +
|
||||
`C:\Backups\05.01.2022\MyDatabase.kdbx`
|
||||
|`C:\Backups\\{DB_FILENAME}\{TIME:MM-dd-yyyy}.kdbx` |`C:\Backups\MyDatabase\01-02-2022.kdbx` +
|
||||
`C:\Backups\MyDatabase\01-05-2022.kdbx`
|
||||
|===
|
||||
|
||||
|
||||
// end::content[]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
= KeePassXC - SSH Agent
|
||||
= KeePassXC - SSH Agent integration
|
||||
include::.sharedheader[]
|
||||
:imagesdir: ../images
|
||||
|
||||
// tag::content[]
|
||||
== SSH Agent
|
||||
== SSH Agent integration
|
||||
SSH (Secure Shell) is a widely used remote secure shell protocol and is considered an industry standard for secure remote access to UNIX-like systems including Linux, BSDs, macOS and more recently even Windows received native support. SSH supports multiple types of authentication and the most widely used ones are either interactive keyboard input with a password or a public-key cryptography pair of keys.
|
||||
|
||||
KeePassXC SSH Agent integration is built to manage SSH keys in a secure manner by either storing them completely within your KeePassXC database or by having only the decryption key of a key file that is stored elsewhere. SSH Agent integration _does not_ provide an agent itself but works as a client for any agent implementation that is OpenSSH compatible.
|
||||
@@ -173,7 +173,7 @@ The last step is to setup an entry to contain the SSH Agent settings and key fil
|
||||
.SSH Agent Entry Settings Page
|
||||
image::sshagent_entry_settings.png[]
|
||||
|
||||
If you chose to not auto-load the key on database unlock, you can manually make the key available by using the context menu from the entry list.
|
||||
If you chose to not autoload the key on database unlock, you can manually make the key available by using the context menu from the entry list.
|
||||
|
||||
.SSH Agent Load Key from Context Menu
|
||||
image::sshagent_context_menu.png[]
|
||||
|
||||
11
release-tool
@@ -51,7 +51,7 @@ BUILD_PLUGINS="all"
|
||||
INSTALL_PREFIX="/usr/local"
|
||||
ORIG_BRANCH=""
|
||||
ORIG_CWD="$(pwd)"
|
||||
MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
TIMESTAMP_SERVER="http://timestamp.sectigo.com"
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@@ -883,7 +883,6 @@ build() {
|
||||
fi
|
||||
fi
|
||||
|
||||
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
||||
if ! ${build_snapshot} && [ -d "$OUTPUT_DIR" ]; then
|
||||
exitError "Output dir '${OUTPUT_DIR}' already exists."
|
||||
fi
|
||||
@@ -892,6 +891,7 @@ build() {
|
||||
if ! mkdir -p "$OUTPUT_DIR"; then
|
||||
exitError "Failed to create output directory!"
|
||||
fi
|
||||
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
||||
|
||||
if ${build_source_tarball}; then
|
||||
logInfo "Creating source tarball..."
|
||||
@@ -930,6 +930,13 @@ build() {
|
||||
# linuxdeploy requires /usr as install prefix
|
||||
INSTALL_PREFIX="/usr"
|
||||
fi
|
||||
if [ -n "$OS_MACOS" ]; then
|
||||
type brew &> /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
INSTALL_PREFIX=$(brew --prefix)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Do not build tests cases
|
||||
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_TESTS=OFF"
|
||||
|
||||
|
||||
149
release-tool.ps1
@@ -10,7 +10,7 @@ Commands:
|
||||
|
||||
.NOTES
|
||||
The following are descriptions of certain parameters:
|
||||
-Vcpkg Specify VCPKG toolchain file (example: C:\vcpkg\scripts\buildsystems\vcpkg.cmake)
|
||||
-Vcpkg Specify VCPKG toolchain location (example: C:\vcpkg)
|
||||
-Tag Release tag to check out (defaults to version number)
|
||||
-Snapshot Build current HEAD without checkout out Tag
|
||||
-CMakeGenerator Override the default CMake generator
|
||||
@@ -290,8 +290,8 @@ if ($Merge) {
|
||||
Invoke-Cmd "tx" "pull -af --minimum-perc=60 --parallel -r keepassxc.share-translations-keepassxc-en-ts--develop"
|
||||
|
||||
# Only commit if there are changes
|
||||
& git diff-index --quiet HEAD --
|
||||
if ($LASTEXITCODE) {
|
||||
$changes = & git status --porcelain
|
||||
if ($changes.Length -gt 0) {
|
||||
Write-Host "Committing translation updates..."
|
||||
Invoke-Cmd "git" "add -A ./share/translations/" -quiet
|
||||
Invoke-Cmd "git" "commit -m `"Update translations`"" -quiet
|
||||
@@ -313,7 +313,10 @@ if ($Merge) {
|
||||
}
|
||||
|
||||
Write-Host "Creating tag for '$Version'..."
|
||||
Invoke-Cmd "git" "tag -a `"$Version`" -m `"Release $Version`" -m `"$Changelog`" -s" -quiet
|
||||
$tmp = New-TemporaryFile
|
||||
"Release $Version`n$Changelog" | Out-File $tmp.FullName
|
||||
Invoke-Cmd "git" "tag -a `"$Version`" -F `"$tmp`" -s" -quiet
|
||||
Remove-Item $tmp.FullName -Force
|
||||
|
||||
Write-Host "Moving latest tag..."
|
||||
Invoke-Cmd "git" "tag -f -a `"latest`" -m `"Latest stable release`" -s" -quiet
|
||||
@@ -322,7 +325,7 @@ if ($Merge) {
|
||||
Write-Host "Please merge the release branch back into the develop branch now and then push your changes."
|
||||
Write-Host "Don't forget to also push the tags using 'git push --tags'."
|
||||
} elseif ($Build) {
|
||||
$Vcpkg = (Resolve-Path $Vcpkg).Path
|
||||
$Vcpkg = (Resolve-Path "$Vcpkg/scripts/buildsystems/vcpkg.cmake").Path
|
||||
|
||||
# Find Visual Studio and establish build environment
|
||||
Invoke-VSToolchain $VSToolChain $SourceDir -Arch "amd64"
|
||||
@@ -333,7 +336,7 @@ if ($Merge) {
|
||||
$Tag = "HEAD"
|
||||
$SourceBranch = & git rev-parse --abbrev-ref HEAD
|
||||
$ReleaseName = "$Version-snapshot"
|
||||
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=Snapshot -DOVERRIDE_VERSION=`"$ReleaseName`""
|
||||
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=Snapshot -DOVERRIDE_VERSION=`"$ReleaseName`" $CMakeOptions"
|
||||
Write-Host "Using current branch '$SourceBranch' to build." -ForegroundColor Cyan
|
||||
} else {
|
||||
Test-WorkingTreeClean
|
||||
@@ -344,9 +347,9 @@ if ($Merge) {
|
||||
}
|
||||
|
||||
if ($Version -match "-beta\d*$") {
|
||||
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=PreRelease"
|
||||
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=PreRelease $CMakeOptions"
|
||||
} else {
|
||||
$CMakeOptions = "$CMakeOptions -DKEEPASSXC_BUILD_TYPE=Release"
|
||||
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=Release $CMakeOptions"
|
||||
}
|
||||
|
||||
# Setup Tag if not defined then checkout tag
|
||||
@@ -368,11 +371,11 @@ if ($Merge) {
|
||||
Set-Location "$BuildDir"
|
||||
|
||||
# Setup CMake options
|
||||
$CMakeOptions = "$CMakeOptions -DWITH_XC_ALL=ON -DWITH_TESTS=OFF -DCMAKE_BUILD_TYPE=Release"
|
||||
$CMakeOptions = "$CMakeOptions -DCMAKE_TOOLCHAIN_FILE:FILEPATH=`"$Vcpkg`" -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON"
|
||||
$CMakeOptions = "-DWITH_XC_ALL=ON -DWITH_TESTS=OFF -DCMAKE_BUILD_TYPE=Release $CMakeOptions"
|
||||
$CMakeOptions = "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=`"$Vcpkg`" -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON $CMakeOptions"
|
||||
|
||||
Write-Host "Configuring build..." -ForegroundColor Cyan
|
||||
Invoke-Cmd "cmake" "$CMakeOptions -G `"$CMakeGenerator`" `"$SourceDir`""
|
||||
Invoke-Cmd "cmake" "-G `"$CMakeGenerator`" $CMakeOptions `"$SourceDir`""
|
||||
|
||||
Write-Host "Compiling sources..." -ForegroundColor Cyan
|
||||
Invoke-Cmd "cmake" "--build . --config Release -- $MakeOptions"
|
||||
@@ -424,10 +427,10 @@ if ($Merge) {
|
||||
}
|
||||
|
||||
# SIG # Begin signature block
|
||||
# MIIThAYJKoZIhvcNAQcCoIITdTCCE3ECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
|
||||
# MIIkvgYJKoZIhvcNAQcCoIIkrzCCJKsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
|
||||
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
|
||||
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUyaXWK5K1LP2TD/IgGb5Tfs8v
|
||||
# C2GgghC8MIIFOjCCBCKgAwIBAgIQWKLXLYzA/YnM/yHg1O3HSjANBgkqhkiG9w0B
|
||||
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUccSicCrmJ6HTiKZr9ZV5mT6i
|
||||
# 9sqggh6mMIIFOjCCBCKgAwIBAgIQWKLXLYzA/YnM/yHg1O3HSjANBgkqhkiG9w0B
|
||||
# AQsFADB8MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVy
|
||||
# MRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxJDAi
|
||||
# BgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2lnbmluZyBDQTAeFw0yMTAzMTUwMDAw
|
||||
@@ -516,17 +519,109 @@ if ($Merge) {
|
||||
# yt39ngVR5UR43QHesXWYDVQk/fBO4+L4g71yuss9Ou7wXheSaG3IYfmm8SoKC6W5
|
||||
# 9J7umDIFhZ7r+YMp08Ysfb06dy6LN0KgaoLtO0qqlBCk4Q34F8W2WnkzGJLjtXX4
|
||||
# oemOCiUe5B7xn1qHI/+fpFGe+zmAEc3btcSnqIBv5VPU4OOiwtJbGvoyJi1qV3Ac
|
||||
# PKRYLqPzW0sH3DJZ84enGm1YMYICMjCCAi4CAQEwgZAwfDELMAkGA1UEBhMCR0Ix
|
||||
# GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEY
|
||||
# MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJTQSBD
|
||||
# b2RlIFNpZ25pbmcgQ0ECEFii1y2MwP2JzP8h4NTtx0owCQYFKw4DAhoFAKB4MBgG
|
||||
# CisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
|
||||
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYE
|
||||
# FPvoURlVLtMyc41aoH1W7jNXhNkUMA0GCSqGSIb3DQEBAQUABIIBACa4ISoVYuy4
|
||||
# LQD5f2XzRDboWCOwR2ClFczB/vOn7uX+RKpbW+vZwllcL0wk0kA4Iotk12yKLAni
|
||||
# K0DkhX8P/Gt5B4hMFaWYKkwTZljITgCEHoAy8vQzpfDUdfJF40R7IIEQLzr2/n5q
|
||||
# Iztv/ApXsPX8SkgEGdikFbBA0i/xtzI8+3sI1QINiRig8xEH/1eOZlR54YHwClvS
|
||||
# 8QhXueb9NbqNN9oKBwx5gRWcOE4I2E5mYAppDDQyhqitbeeY2Pw4Eo5koLM3zTDy
|
||||
# 4/zc+A9lNkAa5eDTavxMHQVqKgO5KomzIYHAdIFnKs85SdntIOr5nSAHnAl6svTh
|
||||
# iJXqSEggdX8=
|
||||
# PKRYLqPzW0sH3DJZ84enGm1YMIIG7DCCBNSgAwIBAgIQMA9vrN1mmHR8qUY2p3gt
|
||||
# uTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBK
|
||||
# ZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRS
|
||||
# VVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlv
|
||||
# biBBdXRob3JpdHkwHhcNMTkwNTAyMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjB9MQsw
|
||||
# CQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQH
|
||||
# EwdTYWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxJTAjBgNVBAMTHFNl
|
||||
# Y3RpZ28gUlNBIFRpbWUgU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||
# DwAwggIKAoICAQDIGwGv2Sx+iJl9AZg/IJC9nIAhVJO5z6A+U++zWsB21hoEpc5H
|
||||
# g7XrxMxJNMvzRWW5+adkFiYJ+9UyUnkuyWPCE5u2hj8BBZJmbyGr1XEQeYf0RirN
|
||||
# xFrJ29ddSU1yVg/cyeNTmDoqHvzOWEnTv/M5u7mkI0Ks0BXDf56iXNc48RaycNOj
|
||||
# xN+zxXKsLgp3/A2UUrf8H5VzJD0BKLwPDU+zkQGObp0ndVXRFzs0IXuXAZSvf4DP
|
||||
# 0REKV4TJf1bgvUacgr6Unb+0ILBgfrhN9Q0/29DqhYyKVnHRLZRMyIw80xSinL0m
|
||||
# /9NTIMdgaZtYClT0Bef9Maz5yIUXx7gpGaQpL0bj3duRX58/Nj4OMGcrRrc1r5a+
|
||||
# 2kxgzKi7nw0U1BjEMJh0giHPYla1IXMSHv2qyghYh3ekFesZVf/QOVQtJu5FGjpv
|
||||
# zdeE8NfwKMVPZIMC1Pvi3vG8Aij0bdonigbSlofe6GsO8Ft96XZpkyAcSpcsdxkr
|
||||
# k5WYnJee647BeFbGRCXfBhKaBi2fA179g6JTZ8qx+o2hZMmIklnLqEbAyfKm/31X
|
||||
# 2xJ2+opBJNQb/HKlFKLUrUMcpEmLQTkUAx4p+hulIq6lw02C0I3aa7fb9xhAV3Pw
|
||||
# caP7Sn1FNsH3jYL6uckNU4B9+rY5WDLvbxhQiddPnTO9GrWdod6VQXqngwIDAQAB
|
||||
# o4IBWjCCAVYwHwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0O
|
||||
# BBYEFBqh+GEZIA/DQXdFKI7RNV8GEgRVMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMB
|
||||
# Af8ECDAGAQH/AgEAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYE
|
||||
# VR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20v
|
||||
# VVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUH
|
||||
# AQEEajBoMD8GCCsGAQUFBzAChjNodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNF
|
||||
# UlRydXN0UlNBQWRkVHJ1c3RDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3Nw
|
||||
# LnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggIBAG1UgaUzXRbhtVOBkXXf
|
||||
# A3oyCy0lhBGysNsqfSoF9bw7J/RaoLlJWZApbGHLtVDb4n35nwDvQMOt0+LkVvlY
|
||||
# Qc/xQuUQff+wdB+PxlwJ+TNe6qAcJlhc87QRD9XVw+K81Vh4v0h24URnbY+wQxAP
|
||||
# jeT5OGK/EwHFhaNMxcyyUzCVpNb0llYIuM1cfwGWvnJSajtCN3wWeDmTk5Sbsdyy
|
||||
# bUFtZ83Jb5A9f0VywRsj1sJVhGbks8VmBvbz1kteraMrQoohkv6ob1olcGKBc2Ne
|
||||
# oLvY3NdK0z2vgwY4Eh0khy3k/ALWPncEvAQ2ted3y5wujSMYuaPCRx3wXdahc1cF
|
||||
# aJqnyTdlHb7qvNhCg0MFpYumCf/RoZSmTqo9CfUFbLfSZFrYKiLCS53xOV5M3kg9
|
||||
# mzSWmglfjv33sVKRzj+J9hyhtal1H3G/W0NdZT1QgW6r8NDT/LKzH7aZlib0PHmL
|
||||
# XGTMze4nmuWgwAxyh8FuTVrTHurwROYybxzrF06Uw3hlIDsPQaof6aFBnf6xuKBl
|
||||
# KjTg3qj5PObBMLvAoGMs/FwWAKjQxH/qEZ0eBsambTJdtDgJK0kHqv3sMNrxpy/P
|
||||
# t/360KOE2See+wFmd7lWEOEgbsausfm2usg1XTN2jvF8IAwqd661ogKGuinutFoA
|
||||
# sYyr4/kKyVRd1LlqdJ69SK6YMIIG9jCCBN6gAwIBAgIRAJA5f5rSSjoT8r2RXwg4
|
||||
# qUMwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0
|
||||
# ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGln
|
||||
# byBMaW1pdGVkMSUwIwYDVQQDExxTZWN0aWdvIFJTQSBUaW1lIFN0YW1waW5nIENB
|
||||
# MB4XDTIyMDUxMTAwMDAwMFoXDTMzMDgxMDIzNTk1OVowajELMAkGA1UEBhMCR0Ix
|
||||
# EzARBgNVBAgTCk1hbmNoZXN0ZXIxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEs
|
||||
# MCoGA1UEAwwjU2VjdGlnbyBSU0EgVGltZSBTdGFtcGluZyBTaWduZXIgIzMwggIi
|
||||
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCQsnE/eeHUuYoXzMOXwpCUcu1a
|
||||
# Om8BQ39zWiifJHygNUAG+pSvCqGDthPkSxUGXmqKIDRxe7slrT9bCqQfL2x9LmFR
|
||||
# 0IxZNz6mXfEeXYC22B9g480Saogfxv4Yy5NDVnrHzgPWAGQoViKxSxnS8JbJRB85
|
||||
# XZywlu1aSY1+cuRDa3/JoD9sSq3VAE+9CriDxb2YLAd2AXBF3sPwQmnq/ybMA0Qf
|
||||
# FijhanS2nEX6tjrOlNEfvYxlqv38wzzoDZw4ZtX8fR6bWYyRWkJXVVAWDUt0cu6g
|
||||
# KjH8JgI0+WQbWf3jOtTouEEpdAE/DeATdysRPPs9zdDn4ZdbVfcqA23VzWLazpwe
|
||||
# /OpwfeZ9S2jOWilh06BcJbOlJ2ijWP31LWvKX2THaygM2qx4Qd6S7w/F7KvfLW8a
|
||||
# VFFsM7ONWWDn3+gXIqN5QWLP/Hvzktqu4DxPD1rMbt8fvCKvtzgQmjSnC//+HV6k
|
||||
# 8+4WOCs/rHaUQZ1kHfqA/QDh/vg61MNeu2lNcpnl8TItUfphrU3qJo5t/KlImD7y
|
||||
# Rg1psbdu9AXbQQXGGMBQ5Pit/qxjYUeRvEa1RlNsxfThhieThDlsdeAdDHpZiy7L
|
||||
# 9GQsQkf0VFiFN+XHaafSJYuWv8at4L2xN/cf30J7qusc6es9Wt340pDVSZo6HYMa
|
||||
# V38cAcLOHH3M+5YVxQIDAQABo4IBgjCCAX4wHwYDVR0jBBgwFoAUGqH4YRkgD8NB
|
||||
# d0UojtE1XwYSBFUwHQYDVR0OBBYEFCUuaDxrmiskFKkfot8mOs8UpvHgMA4GA1Ud
|
||||
# DwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI
|
||||
# MEoGA1UdIARDMEEwNQYMKwYBBAGyMQECAQMIMCUwIwYIKwYBBQUHAgEWF2h0dHBz
|
||||
# Oi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEAjBEBgNVHR8EPTA7MDmgN6A1hjNo
|
||||
# dHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29SU0FUaW1lU3RhbXBpbmdDQS5j
|
||||
# cmwwdAYIKwYBBQUHAQEEaDBmMD8GCCsGAQUFBzAChjNodHRwOi8vY3J0LnNlY3Rp
|
||||
# Z28uY29tL1NlY3RpZ29SU0FUaW1lU3RhbXBpbmdDQS5jcnQwIwYIKwYBBQUHMAGG
|
||||
# F2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQBz2u1o
|
||||
# csvCuUChMbu0A6MtFHsk57RbFX2o6f2t0ZINfD02oGnZ85ow2qxp1nRXJD9+DzzZ
|
||||
# 9cN5JWwm6I1ok87xd4k5f6gEBdo0wxTqnwhUq//EfpZsK9OU67Rs4EVNLLL3Ozta
|
||||
# tcH714l1bZhycvb3Byjz07LQ6xm+FSx4781FoADk+AR2u1fFkL53VJB0ngtPTcSq
|
||||
# E4+XrwE1K8ubEXjp8vmJBDxO44ISYuu0RAx1QcIPNLiIncgi8RNq2xgvbnitxAW0
|
||||
# 6IQIkwf5fYP+aJg05Hflsc6MlGzbA20oBUd+my7wZPvbpAMxEHwa+zwZgNELcLlV
|
||||
# X0e+OWTOt9ojVDLjRrIy2NIphskVXYCVrwL7tNEunTh8NeAPHO0bR0icImpVgtny
|
||||
# ughlA+XxKfNIigkBTKZ58qK2GpmU65co4b59G6F87VaApvQiM5DkhFP8KvrAp5eo
|
||||
# 6rWNes7k4EuhM6sLdqDVaRa3jma/X/ofxKh/p6FIFJENgvy9TZntyeZsNv53Q5m4
|
||||
# aS18YS/to7BJ/lu+aSSR/5P8V2mSS9kFP22GctOi0MBk0jpCwRoD+9DtmiG4P6+m
|
||||
# slFU1UzFyh8SjVfGOe1c/+yfJnatZGZn6Kow4NKtt32xakEnbgOKo3TgigmCbr/j
|
||||
# 9re8ngspGGiBoZw/bhZZSxQJCZrmrr9gFd2G9TGCBYIwggV+AgEBMIGQMHwxCzAJ
|
||||
# BgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcT
|
||||
# B1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2Vj
|
||||
# dGlnbyBSU0EgQ29kZSBTaWduaW5nIENBAhBYotctjMD9icz/IeDU7cdKMAkGBSsO
|
||||
# AwIaBQCgeDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM
|
||||
# BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqG
|
||||
# SIb3DQEJBDEWBBQyqMslxaPRHhE8POQX8uLV4mnwLjANBgkqhkiG9w0BAQEFAASC
|
||||
# AQBhQUgt7fRTbF1rGUv7z9sdfZzNQiLWg2LYMURLZAWcZRFBW6RoP6rTSbpquyRZ
|
||||
# Bs4BlK7JkxCHBXrCeYl5qMx7b6N7twsgyz8OR+EPnYIkEoaKafeqO6B/Q0NhhdOW
|
||||
# vd0wK2YsD5Sb7135a0trAQtS+fnhRr9y9LgMHePBq3iJAo8BWtcUYF5eBbLmJjZU
|
||||
# yzu6aUlXgVakBm8fso0NqLNAVn0vQizJHqsnK610+zCVlzPQ/2HflRpwLgF4X1kQ
|
||||
# Jewj42T2kjzn1Worzcsj3v7WJgbuqThnVR1NIhi+bhfpkrCr4iC/+4QIZZLkzUHl
|
||||
# cS3DhzvxAQ62whQsUAB9z2HBoYIDTDCCA0gGCSqGSIb3DQEJBjGCAzkwggM1AgEB
|
||||
# MIGSMH0xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIx
|
||||
# EDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDElMCMG
|
||||
# A1UEAxMcU2VjdGlnbyBSU0EgVGltZSBTdGFtcGluZyBDQQIRAJA5f5rSSjoT8r2R
|
||||
# Xwg4qUMwDQYJYIZIAWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB
|
||||
# MBwGCSqGSIb3DQEJBTEPFw0yMjEwMjMxMzE4MTZaMD8GCSqGSIb3DQEJBDEyBDAi
|
||||
# pcegfL2b7n0V2o/qV4vNL3exKvlIIxuSCCqMkqibj2h04kPtwOkjhJ064uMHSwcw
|
||||
# DQYJKoZIhvcNAQEBBQAEggIAWMkT++gXvrUNBmS62Sw9TekX6fEKJIOFwLHO5wzh
|
||||
# AdBv/NavsMLB+PNrKizKLL02+Q9v+kyKaeFFlReWa50S+meM2L+wW5YRMGggBKRB
|
||||
# Xhos4qL0ZffKPDbrjmCW0+HdRj408yyNCNB5aPSS6ZLjPpSa6mqVyySfnSdZnyaC
|
||||
# zXYQ2Y4qD3JGSk1MbRvCYB+jCaMM4unyJAS4IA6nWQ97184KLm5U2ktn9ygeWLlG
|
||||
# ujQ2plQ7HuHD+/rMSqesQT6OcwGtERYyfDs+hndpONjKBIulbJJDM1mN6uLQpkfZ
|
||||
# f/TPTZQBDx6EA1oUZ3Evx4cReQFZJjnVlsAJBnKmu3mHheisdlxuFv1DZfu2OD/M
|
||||
# nqY2DeCCgmeC212fosI8ZHUupaKRXVjfcVNElt34lK+3FMzYSKr6rCxiFEXbjq8u
|
||||
# WTG45ZMmcLzs7l9Yaz0eTc642SyBa+5OoTTXs3t9G5z9lVbGonOhfGVbJM+l8JNc
|
||||
# txM+CnQt/OOcjTMDKcjOwG9gcxHjYQhpK8PKiXmPmgpaGYn5vCL5fLvR+s+vTsm4
|
||||
# DJjUTHY87VVXt2IwOu45n1+RBJynewLeaXkwo+79R+/Dn/xoqVVGLRRU6c3yCIiW
|
||||
# qKmsdlIziAr/Fou7jzKcaPFhVJ/NMsI/2c8bkfi6Baoh+go3j39nA3/oDtb4vHWk
|
||||
# Y2E=
|
||||
# SIG # End signature block
|
||||
|
||||
@@ -66,6 +66,10 @@ if(APPLE)
|
||||
install(FILES macosx/keepassxc.icns DESTINATION ${DATA_INSTALL_DIR})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
install(FILES windows/qt.conf DESTINATION ${BIN_INSTALL_DIR})
|
||||
endif()
|
||||
|
||||
install(FILES icons/application/256x256/apps/keepassxc.png DESTINATION ${DATA_INSTALL_DIR}/icons/application/256x256/apps)
|
||||
|
||||
add_custom_target(icons)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /></svg>
|
||||
|
After Width: | Height: | Size: 201 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11 18.95C7.77 18.72 6 17.45 6 17V14.77C7.13 15.32 8.5 15.69 10 15.87C10 15.21 10.04 14.54 10.21 13.89C8.5 13.67 6.97 13.16 6 12.45V9.64C7.43 10.45 9.5 10.97 11.82 11C11.85 10.97 11.87 10.93 11.9 10.9C14.1 8.71 17.5 8.41 20 10.03V7C20 4.79 16.42 3 12 3S4 4.79 4 7V17C4 19.21 7.59 21 12 21C12.34 21 12.68 21 13 20.97C12.62 20.72 12.24 20.44 11.9 20.1C11.55 19.74 11.25 19.36 11 18.95M12 5C15.87 5 18 6.5 18 7S15.87 9 12 9 6 7.5 6 7 8.13 5 12 5M20.31 17.9C20.75 17.21 21 16.38 21 15.5C21 13 19 11 16.5 11S12 13 12 15.5 14 20 16.5 20C17.37 20 18.19 19.75 18.88 19.32L22 22.39L23.39 21L20.31 17.9M16.5 18C15.12 18 14 16.88 14 15.5S15.12 13 16.5 13 19 14.12 19 15.5 17.88 18 16.5 18Z" /></svg>
|
||||
|
After Width: | Height: | Size: 757 B |
1
share/icons/application/scalable/actions/qrcode.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3,11H5V13H3V11M11,5H13V9H11V5M9,11H13V15H11V13H9V11M15,11H17V13H19V11H21V13H19V15H21V19H19V21H17V19H13V21H11V17H15V15H17V13H15V11M19,19V15H17V19H19M15,3H21V9H15V3M17,5V7H19V5H17M3,3H9V9H3V3M5,5V7H7V5H5M3,15H9V21H3V15M5,17V19H7V17H5Z" /></svg>
|
||||
|
After Width: | Height: | Size: 312 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 10C7.3 10 8 9.3 8 8.5S7.3 7 6.5 7 5 7.7 5 8.5 5.7 10 6.5 10M9 6L16 13L11 18L4 11V6H9M9 4H4C2.9 4 2 4.9 2 6V11C2 11.6 2.2 12.1 2.6 12.4L9.6 19.4C9.9 19.8 10.4 20 11 20S12.1 19.8 12.4 19.4L17.4 14.4C17.8 14 18 13.5 18 13C18 12.4 17.8 11.9 17.4 11.6L10.4 4.6C10.1 4.2 9.6 4 9 4M13.5 5.7L14.5 4.7L21.4 11.6C21.8 12 22 12.5 22 13S21.8 14.1 21.4 14.4L16 19.8L15 18.8L20.7 13L13.5 5.7Z" /></svg>
|
||||
|
After Width: | Height: | Size: 462 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 11.11V5C21 3.9 20.11 3 19 3H14.82C14.4 1.84 13.3 1 12 1S9.6 1.84 9.18 3H5C3.9 3 3 3.9 3 5V19C3 20.11 3.9 21 5 21H11.11C12.37 22.24 14.09 23 16 23C19.87 23 23 19.87 23 16C23 14.09 22.24 12.37 21 11.11M12 3C12.55 3 13 3.45 13 4S12.55 5 12 5 11 4.55 11 4 11.45 3 12 3M5 19V5H7V7H17V5H19V9.68C18.09 9.25 17.08 9 16 9H7V11H11.1C10.5 11.57 10.04 12.25 9.68 13H7V15H9.08C9.03 15.33 9 15.66 9 16C9 17.08 9.25 18.09 9.68 19H5M16 21C13.24 21 11 18.76 11 16S13.24 11 16 11 21 13.24 21 16 18.76 21 16 21M16.5 16.25L19.36 17.94L18.61 19.16L15 17V12H16.5V16.25Z" /></svg>
|
||||
|
After Width: | Height: | Size: 630 B |
1
share/icons/application/scalable/actions/totp-copy.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 11.11V5C21 3.9 20.11 3 19 3H14.82C14.4 1.84 13.3 1 12 1S9.6 1.84 9.18 3H5C3.9 3 3 3.9 3 5V19C3 20.11 3.9 21 5 21H11.11C12.37 22.24 14.09 23 16 23C19.87 23 23 19.87 23 16C23 14.09 22.24 12.37 21 11.11M12 3C12.55 3 13 3.45 13 4S12.55 5 12 5 11 4.55 11 4 11.45 3 12 3M5 19V5H7V7H17V5H19V9.68C18.09 9.25 17.08 9 16 9C12.13 9 9 12.13 9 16C9 17.08 9.25 18.09 9.68 19H5M16 21C13.24 21 11 18.76 11 16S13.24 11 16 11 21 13.24 21 16 18.76 21 16 21M16.5 16.25L19.36 17.94L18.61 19.16L15 17V12H16.5V16.25Z" /></svg>
|
||||
|
After Width: | Height: | Size: 576 B |
1
share/icons/application/scalable/actions/totp-edit.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 13.1C20.9 13.1 20.7 13.2 20.6 13.3L19.6 14.3L21.7 16.4L22.7 15.4C22.9 15.2 22.9 14.8 22.7 14.6L21.4 13.3C21.3 13.2 21.2 13.1 21 13.1M19.1 14.9L13 20.9V23H15.1L21.2 16.9L19.1 14.9M12.5 7V12.2L16.5 14.6L15.5 15.6L11 13V7H12.5M11 21.9C5.9 21.4 2 17.1 2 12C2 6.5 6.5 2 12 2C17.3 2 21.6 6.1 22 11.3C21.7 11.2 21.4 11.1 21 11.1C20.6 11.1 20.3 11.2 20 11.3C19.6 7.2 16.2 4 12 4C7.6 4 4 7.6 4 12C4 16.1 7.1 19.5 11.1 19.9L11 20.1V21.9Z" /></svg>
|
||||
|
After Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
@@ -6,17 +6,18 @@
|
||||
<file>application/256x256/apps/keepassxc.png</file>
|
||||
|
||||
<file>application/scalable/actions/application-exit.svg</file>
|
||||
<file>application/scalable/actions/attributes-copy.svg</file>
|
||||
<file>application/scalable/actions/auto-type.svg</file>
|
||||
<file>application/scalable/actions/bugreport.svg</file>
|
||||
<file>application/scalable/actions/chevron-double-down.svg</file>
|
||||
<file>application/scalable/actions/chevron-double-right.svg</file>
|
||||
<file>application/scalable/actions/chronometer.svg</file>
|
||||
<file>application/scalable/actions/clipboard-text.svg</file>
|
||||
<file>application/scalable/actions/configure.svg</file>
|
||||
<file>application/scalable/actions/database-change-key.svg</file>
|
||||
<file>application/scalable/actions/database-lock.svg</file>
|
||||
<file>application/scalable/actions/database-lock-all.svg</file>
|
||||
<file>application/scalable/actions/database-merge.svg</file>
|
||||
<file>application/scalable/actions/database-search.svg</file>
|
||||
<file>application/scalable/actions/dialog-close.svg</file>
|
||||
<file>application/scalable/actions/dialog-ok.svg</file>
|
||||
<file>application/scalable/actions/document-close.svg</file>
|
||||
@@ -62,6 +63,7 @@
|
||||
<file>application/scalable/actions/password-generator.svg</file>
|
||||
<file>application/scalable/actions/password-show-off.svg</file>
|
||||
<file>application/scalable/actions/password-show-on.svg</file>
|
||||
<file>application/scalable/actions/qrcode.svg</file>
|
||||
<file>application/scalable/actions/refresh.svg</file>
|
||||
<file>application/scalable/actions/reports.svg</file>
|
||||
<file>application/scalable/actions/reports-exclude.svg</file>
|
||||
@@ -72,7 +74,12 @@
|
||||
<file>application/scalable/actions/system-search.svg</file>
|
||||
<file>application/scalable/actions/system-software-update.svg</file>
|
||||
<file>application/scalable/actions/tag.svg</file>
|
||||
<file>application/scalable/actions/tag-multiple.svg</file>
|
||||
<file>application/scalable/actions/tag-search.svg</file>
|
||||
<file>application/scalable/actions/totp.svg</file>
|
||||
<file>application/scalable/actions/totp-copy.svg</file>
|
||||
<file>application/scalable/actions/totp-copy-password.svg</file>
|
||||
<file>application/scalable/actions/totp-edit.svg</file>
|
||||
<file>application/scalable/actions/trash.svg</file>
|
||||
<file>application/scalable/actions/url-copy.svg</file>
|
||||
<file>application/scalable/actions/user-guide.svg</file>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<mimetype>application/x-keepass2</mimetype>
|
||||
</mimetypes>
|
||||
<summary>Community-driven port of the Windows application “KeePass Password Safe”</summary>
|
||||
<summary xml:lang="de">Von der Community entwickelter Port der Windows Anwendung “KeePass Password Safe”</summary>
|
||||
<developer_name>KeePassXC Team</developer_name>
|
||||
<description>
|
||||
<p>
|
||||
@@ -16,6 +17,12 @@
|
||||
personal data management. It has a light interface, is cross-platform and
|
||||
published under the terms of the GNU General Public License.
|
||||
</p>
|
||||
<p xml:lang="de">
|
||||
KeePassXC ist eine Anwendung für Menschen, die extrem hohe Anforderungen
|
||||
an die sichere Verwaltung von persönlichen Daten stellen. Sie hat eine leichtgewichtige
|
||||
Benutzeroberfläche, ist auf vielen verschiedenen Plattformen verfügbar und
|
||||
wird unter den Bedingungen der GNU General Public License veröffentlicht.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">org.keepassxc.KeePassXC.desktop</launchable>
|
||||
@@ -25,6 +32,8 @@
|
||||
<url type="faq">https://keepassxc.org/docs#faq</url>
|
||||
<url type="help">https://keepassxc.org/docs</url>
|
||||
<url type="translate">https://www.transifex.com/keepassxc/keepassxc</url>
|
||||
<url type="vcs-browser">https://github.com/keepassxreboot/keepassxc</url>
|
||||
<url type="contribute">https://keepassxc.org/docs#contribute</url>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
@@ -50,6 +59,75 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.4" date="2022-10-29">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add 2 months expiration preset [#8687]</li>
|
||||
<li>CLI: Add Unicode support on Windows [#8618]</li>
|
||||
<li>Fix crash on macOS when unlocking database [#8676]</li>
|
||||
<li>Fix display of passwords in preview panel [#8633]</li>
|
||||
<li>Fix clicking links in entry preview panel [#8644]</li>
|
||||
<li>Prevent expired entries search if no results returned [#8643]</li>
|
||||
<li>Browser: Revert code causing connection problems [#8665]</li>
|
||||
<li>Browser: Fix socket file symbolic link on Linux [#8656]</li>
|
||||
<li>Flatpak: Fix launching browser proxy service [#8680]</li>
|
||||
<li>SSH Agent: Fix paegent support on Windows [#8619]</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.7.3" date="2022-10-23">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Enhance Tags Support and Add Saved Searches [#8435, #8607]</li>
|
||||
<li>Significant improvements to entry preview panel [#7993]</li>
|
||||
<li>Add password strength indicator to all password fields [#7885]</li>
|
||||
<li>Limit zxcvbn entropy estimation length to 128 characters [#7748]</li>
|
||||
<li>Try full URL path when fetching favicon [#8565]</li>
|
||||
<li>Hide usernames in preview panel when hidden in entry view [#8608]</li>
|
||||
<li>Enable dark title bar on windows when accent color is not used [#8498]</li>
|
||||
<li>Add option to display passwords in color in preview panel [#7097]</li>
|
||||
<li>Add XML Export option to GUI [#8524]</li>
|
||||
<li>Increase entropy required for a "good" password rating to 75 [#8523]</li>
|
||||
<li>Add shortcut to copy password with TOTP appended [#8443]</li>
|
||||
<li>Show entry count in status bar [#8435]</li>
|
||||
<li>Allow KeePassXC to be built without X11 [#8147]</li>
|
||||
<li>Enable use of VivoKey Apex and Dangerous Things FlexSecure tokens [#8332]</li>
|
||||
<li>Add setting for number of recent files [#8239]</li>
|
||||
<li>Add Ctrl+Tab shortcut to cycle databases in unlock dialog [#8168]</li>
|
||||
<li>Replace offensive words in eff_large.wordlist [#7968]</li>
|
||||
<li>Auto-Type: PICKCHARS can specify attribute and ignore BEEP [#8118]</li>
|
||||
<li>Linux: Add isHardwareKeySupported and refreshHardwareKeys to DBus methods [#8055]</li>
|
||||
<li>Add config variable to specify default database file name [#8042]</li>
|
||||
<li>CLI: Add `db-edit` command [#8400]</li>
|
||||
<li>CLI: Add option to display all attributes with `show` command [#8256]</li>
|
||||
<li>CLI: Show UUID and tags with `show` and `clip` commands [#8241]</li>
|
||||
<li>Browser: Move socket into separate directory on Linux [#8030]</li>
|
||||
<li>Browser: Add group setting to omit WWW subdomain when matching URLs [#7988]</li>
|
||||
<li>FdoSecrets: Ask to unlock the database when creating items [#8022, #8028]</li>
|
||||
<li>FdoSecrets: Skip entries in recycle bin when searching [#8021]</li>
|
||||
<li>Fix potential deadlock in UI when saving [#8606]</li>
|
||||
<li>Fix newlines when copying notes from preview panel [#8542]</li>
|
||||
<li>Fix dark mode detection on Linux [#8477]</li>
|
||||
<li>Fix crash when deleting items in recycle bin while searching [#8117]</li>
|
||||
<li>Fix crash when trying to close database during unlock [#8144]</li>
|
||||
<li>Fix tabbing around the interface [#8435, #8520]</li>
|
||||
<li>Fix OPVault import when there are multiple OTP fields [#8436]</li>
|
||||
<li>Fix various Windows Hello bugs [#8354]</li>
|
||||
<li>Fix use of Apple Watch for Quick Unlock [#8311]</li>
|
||||
<li>Better handling of "Lock on Minimize" setting [#8202]</li>
|
||||
<li>Support numeric aware sorting on Windows and macOS [#8363]</li>
|
||||
<li>Check for write permission before entering portable mode [#8447]</li>
|
||||
<li>Correct regex escape logic to prevent parse errors [#7778]</li>
|
||||
<li>Normalize slashes and file case for last used databases [#7864, #7214]</li>
|
||||
<li>Link ykcore against pthread [#7807]</li>
|
||||
<li>Auto-Type: Fix menu entries in selection dialog on Windows [#7987]</li>
|
||||
<li>Auto-Type: Fix use of modifiers under macOS [#8111]</li>
|
||||
<li>CLI: Fix output when using clip with the -t flag [#8271]</li>
|
||||
<li>Browser: Use asynchronous access confirm dialog [#8273]</li>
|
||||
<li>Browser: Always send database locked/unlocked status [#8114]</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.7.1" date="2022-04-05">
|
||||
<description>
|
||||
<ul>
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
<string>${PROJECT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${KEEPASSXC_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${KEEPASSXC_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
|
||||
@@ -227,6 +227,10 @@
|
||||
<source>Select backup storage directory</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This setting cannot be enabled when minimize on unlock is enabled.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ApplicationSettingsWidgetGeneral</name>
|
||||
@@ -495,6 +499,14 @@
|
||||
<source>Remember last typed entry for:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> recent files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show passwords in color</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ApplicationSettingsWidgetSecurity</name>
|
||||
@@ -644,6 +656,10 @@
|
||||
<source>Invalid placeholder: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry does not have attribute for PICKCHARS: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>AutoTypeAssociationsModel</name>
|
||||
@@ -1426,10 +1442,6 @@ Backup database located at %2</source>
|
||||
<source>Key File:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>In addition to a password, you can use a secret file to enhance the security of your database. This file can be generated in your database's security settings.</p><p>This is <strong>not</strong> your *.kdbx database file!<br>If you do not have a key file, leave this field empty.</p><p>Click for more information…</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key file help</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -1442,11 +1454,6 @@ Backup database located at %2</source>
|
||||
<source>Hardware Key:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</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>
|
||||
<p>Click for more information…</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hardware key help</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -1581,6 +1588,15 @@ If you do not have a key file, please leave the field empty.</source>
|
||||
<source>Select hardware key…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>In addition to a password, you can use a secret file to enhance the security of your database. This file can be generated in your database's security settings.</p><p>This is <strong>not</strong> your *.kdbx database file!<br>If you do not have a key file, leave this field empty.</p><p>Click for more information…</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</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>
|
||||
<p>Click for more information…</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DatabaseSettingWidgetMetaData</name>
|
||||
@@ -2234,13 +2250,21 @@ This is definitely a bug, please report it to the developers.</source>
|
||||
<comment>Database tab name modifier</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export database to XML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>XML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Writing the XML file failed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DatabaseWidget</name>
|
||||
<message>
|
||||
<source>Database Tags</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Searching…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -2405,6 +2429,22 @@ Disable safe saves and try again?</source>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Searches and Tags</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter a unique name or overwrite an existing search from the list:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Search</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidget</name>
|
||||
@@ -3044,10 +3084,6 @@ Would you like to correct it?</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditGroupWidgetBrowser</name>
|
||||
<message>
|
||||
<source>Edit Group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>These settings affect to the group's behaviour with the browser extension.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -3084,6 +3120,14 @@ Would you like to correct it?</source>
|
||||
<source>Do not use HTTP Auth toggle for this and sub groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Omit WWW subdomain from matching:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Omit WWW subdomain from matching toggle for this and sub groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditGroupWidgetKeeShare</name>
|
||||
@@ -3890,6 +3934,10 @@ Error: %1</source>
|
||||
<source>Disabled</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Double click to copy value</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EntryURLModel</name>
|
||||
@@ -5379,6 +5427,33 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
<source>You must restart the application to apply this setting. Would you like to restart now?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tags</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No Tags</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%1 Entry(s)</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy Password and TOTP</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&XML File…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>XML File…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageDatabase</name>
|
||||
@@ -5749,29 +5824,6 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PasswordEdit</name>
|
||||
<message>
|
||||
<source>Passwords do not match</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords match so far</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle Password (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate Password (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Warning: Caps Lock enabled!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PasswordEditWidget</name>
|
||||
<message>
|
||||
@@ -5950,10 +6002,6 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
<source>Also choose from:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Excluded characters: "0", "1", "l", "I", "O", "|", "﹒"</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exclude look-alike characters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -6103,6 +6151,57 @@ Do you want to overwrite it?</source>
|
||||
<comment>Password quality</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Excluded characters: "0", "1", "l", "I", "O", "|", "﹒"</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PasswordWidget</name>
|
||||
<message>
|
||||
<source>Passwords do not match</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords match so far</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle Password (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate Password (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Warning: Caps Lock enabled!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Quality: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Poor</source>
|
||||
<comment>Password quality</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Weak</source>
|
||||
<comment>Password quality</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Good</source>
|
||||
<comment>Password quality</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Excellent</source>
|
||||
<comment>Password quality</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PickcharsDialog</name>
|
||||
@@ -7764,6 +7863,63 @@ Kernel: %3 %4</source>
|
||||
<source>Please present or touch your YubiKey to continue.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show all the attributes of the entry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit a database.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not change the database key.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database was not modified.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully edited the database.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading the new key file failed: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unset the password for the database.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unset the key file for the database.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot use %1 and %2 at the same time.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot remove all the keys from a database.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot remove password: The database does not have a password.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot remove file key: The database does not have a file key.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Found unexpected Key type %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set the key file for the database.
|
||||
This options is deprecated, use --set-key-file instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtIOCompressor</name>
|
||||
@@ -8320,6 +8476,10 @@ Kernel: %3 %4</source>
|
||||
<source>Limit search to selected group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Search</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsClientModel</name>
|
||||
@@ -8532,10 +8692,6 @@ Kernel: %3 %4</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TagModel</name>
|
||||
<message>
|
||||
<source>All</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expired</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -8544,6 +8700,33 @@ Kernel: %3 %4</source>
|
||||
<source>Weak Passwords</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All Entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear Search</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TagView</name>
|
||||
<message>
|
||||
<source>Remove Search</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove Tag</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Remove Tag</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove tag "%1" from all entries in this database?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TotpDialog</name>
|
||||
|
||||
2
share/windows/qt.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
[Platforms]
|
||||
WindowsArguments = darkmode=1
|
||||
@@ -2006,8 +2006,8 @@ drizzly
|
||||
drone
|
||||
drool
|
||||
droop
|
||||
drop-down
|
||||
dropbox
|
||||
drop-in
|
||||
dropforge
|
||||
dropkick
|
||||
droplet
|
||||
dropout
|
||||
@@ -2102,11 +2102,11 @@ eaten
|
||||
eatery
|
||||
eating
|
||||
eats
|
||||
ebay
|
||||
eaves
|
||||
ebony
|
||||
ebook
|
||||
ecard
|
||||
eccentric
|
||||
echelon
|
||||
echidna
|
||||
echo
|
||||
eclair
|
||||
eclipse
|
||||
@@ -2684,8 +2684,8 @@ footpad
|
||||
footpath
|
||||
footprint
|
||||
footrest
|
||||
footsie
|
||||
footsore
|
||||
footstool
|
||||
footway
|
||||
footwear
|
||||
footwork
|
||||
fossil
|
||||
@@ -2928,7 +2928,7 @@ goldmine
|
||||
goldsmith
|
||||
golf
|
||||
goliath
|
||||
gonad
|
||||
golly
|
||||
gondola
|
||||
gone
|
||||
gong
|
||||
@@ -2937,8 +2937,8 @@ gooey
|
||||
goofball
|
||||
goofiness
|
||||
goofy
|
||||
google
|
||||
goon
|
||||
gooseneck
|
||||
goosey
|
||||
gopher
|
||||
gore
|
||||
gorged
|
||||
@@ -3028,6 +3028,7 @@ groom
|
||||
groove
|
||||
grooving
|
||||
groovy
|
||||
grouch
|
||||
ground
|
||||
grouped
|
||||
grout
|
||||
@@ -3137,7 +3138,7 @@ hangover
|
||||
hangup
|
||||
hankering
|
||||
hankie
|
||||
hanky
|
||||
hanoi
|
||||
haphazard
|
||||
happening
|
||||
happier
|
||||
@@ -3146,9 +3147,10 @@ happily
|
||||
happiness
|
||||
happy
|
||||
harbor
|
||||
hardcopy
|
||||
hardback
|
||||
hardball
|
||||
hardcover
|
||||
harddisk
|
||||
hardedge
|
||||
hardened
|
||||
hardener
|
||||
hardening
|
||||
@@ -3410,10 +3412,10 @@ impurity
|
||||
iodine
|
||||
iodize
|
||||
ion
|
||||
ipad
|
||||
iphone
|
||||
ipod
|
||||
irate
|
||||
iota
|
||||
ire
|
||||
iridium
|
||||
iris
|
||||
irk
|
||||
iron
|
||||
irregular
|
||||
@@ -3423,7 +3425,7 @@ irritably
|
||||
irritant
|
||||
irritate
|
||||
islamic
|
||||
islamist
|
||||
island
|
||||
isolated
|
||||
isolating
|
||||
isolation
|
||||
@@ -3576,7 +3578,7 @@ kite
|
||||
kitten
|
||||
kitty
|
||||
kiwi
|
||||
kleenex
|
||||
knack
|
||||
knapsack
|
||||
knee
|
||||
knelt
|
||||
@@ -3661,7 +3663,7 @@ leggings
|
||||
legible
|
||||
legibly
|
||||
legislate
|
||||
lego
|
||||
legitimate
|
||||
legroom
|
||||
legume
|
||||
legwarmer
|
||||
@@ -3876,7 +3878,7 @@ marshland
|
||||
marshy
|
||||
marsupial
|
||||
marvelous
|
||||
marxism
|
||||
marzipan
|
||||
mascot
|
||||
masculine
|
||||
mashed
|
||||
@@ -5912,7 +5914,7 @@ shun
|
||||
shush
|
||||
shut
|
||||
shy
|
||||
siamese
|
||||
sial
|
||||
siberian
|
||||
sibling
|
||||
siding
|
||||
@@ -6608,6 +6610,7 @@ swimmer
|
||||
swimming
|
||||
swimsuit
|
||||
swimwear
|
||||
swindle
|
||||
swinging
|
||||
swipe
|
||||
swirl
|
||||
@@ -7720,7 +7723,7 @@ wrongful
|
||||
wrongly
|
||||
wrongness
|
||||
wrought
|
||||
xbox
|
||||
xenon
|
||||
xerox
|
||||
yahoo
|
||||
yam
|
||||
@@ -7747,7 +7750,7 @@ yodel
|
||||
yoga
|
||||
yogurt
|
||||
yonder
|
||||
yoyo
|
||||
young
|
||||
yummy
|
||||
zap
|
||||
zealous
|
||||
|
||||
@@ -30,7 +30,7 @@ if(NOT ZXCVBN_LIBRARIES)
|
||||
set(ZXCVBN_LIBRARIES zxcvbn)
|
||||
endif(NOT ZXCVBN_LIBRARIES)
|
||||
|
||||
set(keepassx_SOURCES
|
||||
set(core_SOURCES
|
||||
core/Alloc.cpp
|
||||
core/AutoTypeAssociations.cpp
|
||||
core/Base32.cpp
|
||||
@@ -89,6 +89,19 @@ set(keepassx_SOURCES
|
||||
format/OpVaultReaderAttachments.cpp
|
||||
format/OpVaultReaderBandEntry.cpp
|
||||
format/OpVaultReaderSections.cpp
|
||||
keys/CompositeKey.cpp
|
||||
keys/FileKey.cpp
|
||||
keys/PasswordKey.cpp
|
||||
keys/ChallengeResponseKey.cpp
|
||||
streams/HashedBlockStream.cpp
|
||||
streams/HmacBlockStream.cpp
|
||||
streams/LayeredStream.cpp
|
||||
streams/qtiocompressor.cpp
|
||||
streams/StoreDataStream.cpp
|
||||
streams/SymmetricCipherStream.cpp
|
||||
totp/totp.cpp)
|
||||
|
||||
set(gui_SOURCES
|
||||
gui/styles/styles.qrc
|
||||
gui/styles/StateColorPalette.cpp
|
||||
gui/styles/base/phantomcolor.cpp
|
||||
@@ -121,7 +134,7 @@ set(keepassx_SOURCES
|
||||
gui/MessageBox.cpp
|
||||
gui/MessageWidget.cpp
|
||||
gui/OpVaultOpenWidget.cpp
|
||||
gui/PasswordEdit.cpp
|
||||
gui/PasswordWidget.cpp
|
||||
gui/PasswordGeneratorWidget.cpp
|
||||
gui/ApplicationSettingsWidget.cpp
|
||||
gui/Icons.cpp
|
||||
@@ -150,6 +163,7 @@ set(keepassx_SOURCES
|
||||
gui/group/GroupModel.cpp
|
||||
gui/group/GroupView.cpp
|
||||
gui/tag/TagModel.cpp
|
||||
gui/tag/TagView.cpp
|
||||
gui/tag/TagsEdit.cpp
|
||||
gui/databasekey/KeyComponentWidget.cpp
|
||||
gui/databasekey/PasswordEditWidget.cpp
|
||||
@@ -182,79 +196,61 @@ set(keepassx_SOURCES
|
||||
gui/wizard/NewDatabaseWizardPageMetaData.cpp
|
||||
gui/wizard/NewDatabaseWizardPageEncryption.cpp
|
||||
gui/wizard/NewDatabaseWizardPageDatabaseKey.cpp
|
||||
keys/CompositeKey.cpp
|
||||
keys/FileKey.cpp
|
||||
keys/PasswordKey.cpp
|
||||
keys/ChallengeResponseKey.cpp
|
||||
streams/HashedBlockStream.cpp
|
||||
streams/HmacBlockStream.cpp
|
||||
streams/LayeredStream.cpp
|
||||
streams/qtiocompressor.cpp
|
||||
streams/StoreDataStream.cpp
|
||||
streams/SymmetricCipherStream.cpp
|
||||
totp/totp.cpp)
|
||||
../share/icons/icons.qrc
|
||||
../share/wizard/wizard.qrc)
|
||||
|
||||
if(APPLE)
|
||||
set(keepassx_SOURCES
|
||||
${keepassx_SOURCES}
|
||||
core/MacPasteboard.cpp
|
||||
list(APPEND gui_SOURCES
|
||||
gui/osutils/macutils/MacPasteboard.cpp
|
||||
gui/osutils/macutils/MacUtils.cpp
|
||||
gui/osutils/macutils/ScreenLockListenerMac.cpp
|
||||
gui/osutils/macutils/AppKitImpl.mm
|
||||
gui/osutils/macutils/AppKit.h)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(keepassx_SOURCES
|
||||
${keepassx_SOURCES}
|
||||
list(APPEND gui_SOURCES
|
||||
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
|
||||
gui/osutils/nixutils/NixUtils.cpp
|
||||
gui/osutils/nixutils/NixUtils.cpp)
|
||||
if(WITH_XC_X11)
|
||||
list(APPEND gui_SOURCES
|
||||
gui/osutils/nixutils/X11Funcs.cpp)
|
||||
qt5_add_dbus_adaptor(keepassx_SOURCES
|
||||
endif()
|
||||
qt5_add_dbus_adaptor(gui_SOURCES
|
||||
gui/org.keepassxc.KeePassXC.MainWindow.xml
|
||||
gui/MainWindow.h
|
||||
MainWindow)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(keepassx_SOURCES
|
||||
${keepassx_SOURCES}
|
||||
list(APPEND gui_SOURCES
|
||||
gui/osutils/winutils/ScreenLockListenerWin.cpp
|
||||
gui/osutils/winutils/WinUtils.cpp)
|
||||
if (MSVC)
|
||||
list(APPEND keepassx_SOURCES winhello/WindowsHello.cpp)
|
||||
list(APPEND gui_SOURCES winhello/WindowsHello.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES}
|
||||
../share/icons/icons.qrc
|
||||
../share/wizard/wizard.qrc)
|
||||
|
||||
set(keepassx_SOURCES_MAINEXE main.cpp)
|
||||
|
||||
add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing")
|
||||
add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)")
|
||||
add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser")
|
||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare")
|
||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||
add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking")
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_feature_info(FdoSecrets WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API.")
|
||||
endif()
|
||||
|
||||
add_subdirectory(browser)
|
||||
add_subdirectory(proxy)
|
||||
if(WITH_XC_BROWSER)
|
||||
set(keepassxcbrowser_LIB keepassxcbrowser)
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES} gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp)
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES} gui/entry/EntryURLModel.cpp)
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES} gui/reports/ReportsWidgetBrowserStatistics.cpp)
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES} gui/reports/ReportsPageBrowserStatistics.cpp)
|
||||
list(APPEND gui_SOURCES
|
||||
gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
|
||||
gui/entry/EntryURLModel.cpp
|
||||
gui/reports/ReportsWidgetBrowserStatistics.cpp
|
||||
gui/reports/ReportsPageBrowserStatistics.cpp)
|
||||
endif()
|
||||
|
||||
add_subdirectory(autotype)
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(qrcode)
|
||||
set(qrcode_LIB qrcode)
|
||||
|
||||
add_subdirectory(autotype)
|
||||
if(WITH_XC_AUTOTYPE)
|
||||
set(autotype_LIB autotype)
|
||||
endif()
|
||||
|
||||
add_subdirectory(keeshare)
|
||||
if(WITH_XC_KEESHARE)
|
||||
set(keeshare_LIB keeshare)
|
||||
@@ -270,102 +266,82 @@ if(WITH_XC_FDOSECRETS)
|
||||
set(fdosecrets_LIB fdosecrets)
|
||||
endif()
|
||||
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
set(autotype_SOURCES
|
||||
core/Tools.cpp
|
||||
autotype/AutoType.cpp
|
||||
autotype/AutoTypeAction.cpp
|
||||
autotype/AutoTypeMatchModel.cpp
|
||||
autotype/AutoTypeMatchView.cpp
|
||||
autotype/AutoTypeSelectDialog.cpp
|
||||
autotype/PickcharsDialog.cpp
|
||||
autotype/ShortcutWidget.cpp
|
||||
autotype/WindowSelectComboBox.cpp)
|
||||
|
||||
if(WIN32)
|
||||
set(keepassx_SOURCES_MAINEXE ${keepassx_SOURCES_MAINEXE} ${CMAKE_SOURCE_DIR}/share/windows/icon.rc)
|
||||
endif()
|
||||
|
||||
if(WITH_XC_YUBIKEY)
|
||||
list(APPEND keepassx_SOURCES
|
||||
list(APPEND core_SOURCES
|
||||
keys/drivers/YubiKey.h
|
||||
keys/drivers/YubiKey.cpp
|
||||
keys/drivers/YubiKeyInterface.cpp
|
||||
keys/drivers/YubiKeyInterfaceUSB.cpp
|
||||
keys/drivers/YubiKeyInterfacePCSC.cpp)
|
||||
else()
|
||||
list(APPEND keepassx_SOURCES
|
||||
list(APPEND core_SOURCES
|
||||
keys/drivers/YubiKey.h
|
||||
keys/drivers/YubiKeyStub.cpp)
|
||||
endif()
|
||||
|
||||
if(WITH_XC_NETWORKING)
|
||||
list(APPEND keepassx_SOURCES
|
||||
core/HibpDownloader.cpp
|
||||
core/NetworkManager.cpp
|
||||
list(APPEND gui_SOURCES
|
||||
networking/HibpDownloader.cpp
|
||||
networking/NetworkManager.cpp
|
||||
networking/UpdateChecker.cpp
|
||||
gui/UpdateCheckDialog.cpp
|
||||
gui/IconDownloader.cpp
|
||||
gui/IconDownloaderDialog.cpp
|
||||
updatecheck/UpdateChecker.cpp)
|
||||
gui/IconDownloaderDialog.cpp)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND keepassx_SOURCES touchid/TouchID.mm)
|
||||
list(APPEND core_SOURCES touchid/TouchID.mm)
|
||||
# TODO: Remove -Wno-error once deprecation warnings have been resolved.
|
||||
set_source_files_properties(touchid/TouchID.mm PROPERTY COMPILE_FLAGS "-Wno-old-style-cast -Wno-error")
|
||||
set_source_files_properties(touchid/TouchID.mm PROPERTY COMPILE_FLAGS "-Wno-old-style-cast")
|
||||
endif()
|
||||
|
||||
configure_file(config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h)
|
||||
configure_file(git-info.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/git-info.h)
|
||||
|
||||
add_library(autotype STATIC ${autotype_SOURCES})
|
||||
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
|
||||
|
||||
add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
||||
|
||||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
||||
target_link_libraries(keepassx_core
|
||||
autotype
|
||||
${keepassxcbrowser_LIB}
|
||||
add_library(keepassxc_core STATIC ${core_SOURCES})
|
||||
set_target_properties(keepassxc_core PROPERTIES COMPILE_DEFINITIONS KEEPASSXC_BUILDING_CORE)
|
||||
target_link_libraries(keepassxc_core
|
||||
${qrcode_LIB}
|
||||
${fdosecrets_LIB}
|
||||
Qt5::Core
|
||||
Qt5::Concurrent
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
${BOTAN2_LIBRARIES}
|
||||
${PCSC_LIBRARIES}
|
||||
${ZXCVBN_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${ARGON2_LIBRARIES}
|
||||
${thirdparty_LIBRARIES}
|
||||
)
|
||||
${thirdparty_LIBRARIES})
|
||||
|
||||
if(WITH_XC_SSHAGENT)
|
||||
target_link_libraries(keepassx_core sshagent)
|
||||
endif()
|
||||
if(WITH_XC_KEESHARE)
|
||||
target_link_libraries(keepassx_core keeshare)
|
||||
endif()
|
||||
add_library(keepassxc_gui STATIC ${gui_SOURCES})
|
||||
target_link_libraries(keepassxc_gui
|
||||
keepassxc_core
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
${autotype_LIB}
|
||||
${keepassxcbrowser_LIB}
|
||||
${fdosecrets_LIB}
|
||||
${sshagent_LIB}
|
||||
${keeshare_LIB})
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(keepassx_core "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication")
|
||||
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication")
|
||||
if(Qt5MacExtras_FOUND)
|
||||
target_link_libraries(keepassx_core Qt5::MacExtras)
|
||||
target_link_libraries(keepassxc_gui Qt5::MacExtras)
|
||||
endif()
|
||||
endif()
|
||||
if(HAIKU)
|
||||
target_link_libraries(keepassx_core network)
|
||||
target_link_libraries(keepassxc_gui network)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(keepassx_core Qt5::DBus Qt5::X11Extras X11)
|
||||
target_link_libraries(keepassxc_gui Qt5::DBus)
|
||||
if(WITH_XC_X11)
|
||||
target_link_libraries(keepassxc_gui Qt5::X11Extras X11)
|
||||
endif()
|
||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(WIN32)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib)
|
||||
target_link_libraries(keepassxc_gui Wtsapi32.lib Ws2_32.lib)
|
||||
if (MSVC)
|
||||
target_link_libraries(keepassx_core WindowsApp.lib)
|
||||
target_link_libraries(keepassxc_gui WindowsApp.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -381,8 +357,13 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(${PROGNAME} WIN32 ${keepassx_SOURCES_MAINEXE} ${WIN32_ProductVersionFiles})
|
||||
target_link_libraries(${PROGNAME} keepassx_core)
|
||||
set(mainexe_SOURCES main.cpp)
|
||||
if(WIN32)
|
||||
list(APPEND mainexe_SOURCES ${CMAKE_SOURCE_DIR}/share/windows/icon.rc)
|
||||
endif()
|
||||
|
||||
add_executable(${PROGNAME} WIN32 ${mainexe_SOURCES} ${WIN32_ProductVersionFiles})
|
||||
target_link_libraries(${PROGNAME} keepassxc_gui)
|
||||
|
||||
set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON)
|
||||
|
||||
@@ -525,5 +506,5 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# The install commands in this subdirectory will be executed after all the install commands in the
|
||||
# current scope are ran. It is required for correct functtioning of macdeployqt.
|
||||
# current scope are ran. It is required for correct functioning of macdeployqt.
|
||||
add_subdirectory(post_install)
|
||||
|
||||
@@ -335,7 +335,9 @@ void AutoType::executeAutoTypeActions(const Entry* entry,
|
||||
}
|
||||
|
||||
if (!result.canRetry() || i == max_retries) {
|
||||
MessageBox::critical(getMainWindow(), tr("Auto-Type Error"), result.errorString());
|
||||
if (getMainWindow()) {
|
||||
MessageBox::critical(getMainWindow(), tr("Auto-Type Error"), result.errorString());
|
||||
}
|
||||
emit autotypeRejected();
|
||||
m_inAutoType.unlock();
|
||||
return;
|
||||
@@ -645,13 +647,26 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
|
||||
for (const auto& ch : totp) {
|
||||
actions << QSharedPointer<AutoTypeKey>::create(ch);
|
||||
}
|
||||
} else if (placeholder == "pickchars") {
|
||||
// Ignore this if we are syntax checking
|
||||
} else if (placeholder.startsWith("pickchars")) {
|
||||
// Reset to the original capture to preserve case
|
||||
placeholder = match.captured(3);
|
||||
|
||||
auto attribute = EntryAttributes::PasswordKey;
|
||||
if (placeholder.contains(":")) {
|
||||
attribute = placeholder.section(":", 1);
|
||||
if (!entry->attributes()->hasKey(attribute)) {
|
||||
error = tr("Entry does not have attribute for PICKCHARS: %1").arg(attribute);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Bail out if we are just syntax checking
|
||||
if (syntaxOnly) {
|
||||
continue;
|
||||
}
|
||||
// Show pickchars dialog for entry's password
|
||||
auto password = entry->resolvePlaceholder(entry->password());
|
||||
|
||||
// Show pickchars dialog for the desired attribute
|
||||
auto password = entry->resolvePlaceholder(entry->attribute(attribute));
|
||||
if (!password.isEmpty()) {
|
||||
PickcharsDialog pickcharsDialog(password);
|
||||
if (pickcharsDialog.exec() == QDialog::Accepted && !pickcharsDialog.selectedChars().isEmpty()) {
|
||||
@@ -744,8 +759,8 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
|
||||
mode = AutoTypeExecutor::Mode::VIRTUAL;
|
||||
}
|
||||
actions << QSharedPointer<AutoTypeMode>::create(mode);
|
||||
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|
||||
|| placeholder.startsWith("c:")) {
|
||||
} else if (placeholder.startsWith("beep") || placeholder.startsWith("vkey")
|
||||
|| placeholder.startsWith("appactivate") || placeholder.startsWith("c:")) {
|
||||
// Ignore these commands
|
||||
} else {
|
||||
// Attempt to resolve an entry attribute
|
||||
|
||||
@@ -35,6 +35,14 @@
|
||||
#include "gui/Clipboard.h"
|
||||
#include "gui/Icons.h"
|
||||
|
||||
const auto MENU_FIELD_PROP_NAME = "menu_field";
|
||||
enum MENU_FIELD
|
||||
{
|
||||
USERNAME = 1,
|
||||
PASSWORD,
|
||||
TOTP,
|
||||
};
|
||||
|
||||
AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_ui(new Ui::AutoTypeSelectDialog())
|
||||
@@ -260,14 +268,22 @@ void AutoTypeSelectDialog::updateActionMenu(const AutoTypeMatch& match)
|
||||
bool hasPassword = !match.first->password().isEmpty();
|
||||
bool hasTotp = match.first->hasTotp();
|
||||
|
||||
auto actions = m_actionMenu->actions();
|
||||
Q_ASSERT(actions.size() >= 6);
|
||||
actions[0]->setEnabled(hasUsername);
|
||||
actions[1]->setEnabled(hasPassword);
|
||||
actions[2]->setEnabled(hasTotp);
|
||||
actions[3]->setEnabled(hasUsername);
|
||||
actions[4]->setEnabled(hasPassword);
|
||||
actions[5]->setEnabled(hasTotp);
|
||||
for (auto action : m_actionMenu->actions()) {
|
||||
auto prop = action->property(MENU_FIELD_PROP_NAME);
|
||||
if (prop.isValid()) {
|
||||
switch (prop.toInt()) {
|
||||
case MENU_FIELD::USERNAME:
|
||||
action->setEnabled(hasUsername);
|
||||
break;
|
||||
case MENU_FIELD::PASSWORD:
|
||||
action->setEnabled(hasPassword);
|
||||
break;
|
||||
case MENU_FIELD::TOTP:
|
||||
action->setEnabled(hasTotp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutoTypeSelectDialog::buildActionMenu()
|
||||
@@ -278,19 +294,16 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
auto typeTotpAction = new QAction(icons()->icon("auto-type"), tr("Type {TOTP}"), this);
|
||||
auto copyUsernameAction = new QAction(icons()->icon("username-copy"), tr("Copy Username"), this);
|
||||
auto copyPasswordAction = new QAction(icons()->icon("password-copy"), tr("Copy Password"), this);
|
||||
auto copyTotpAction = new QAction(icons()->icon("chronometer"), tr("Copy TOTP"), this);
|
||||
auto copyTotpAction = new QAction(icons()->icon("totp"), tr("Copy TOTP"), this);
|
||||
m_actionMenu->addAction(typeUsernameAction);
|
||||
m_actionMenu->addAction(typePasswordAction);
|
||||
m_actionMenu->addAction(typeTotpAction);
|
||||
#ifdef Q_OS_WIN
|
||||
auto typeVirtualAction = new QAction(icons()->icon("auto-type"), tr("Use Virtual Keyboard"));
|
||||
m_actionMenu->addAction(typeVirtualAction);
|
||||
#endif
|
||||
m_actionMenu->addAction(copyUsernameAction);
|
||||
m_actionMenu->addAction(copyPasswordAction);
|
||||
m_actionMenu->addAction(copyTotpAction);
|
||||
|
||||
typeUsernameAction->setShortcut(Qt::CTRL + Qt::Key_1);
|
||||
typeUsernameAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::USERNAME);
|
||||
connect(typeUsernameAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{USERNAME}";
|
||||
@@ -298,6 +311,7 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
});
|
||||
|
||||
typePasswordAction->setShortcut(Qt::CTRL + Qt::Key_2);
|
||||
typePasswordAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::PASSWORD);
|
||||
connect(typePasswordAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{PASSWORD}";
|
||||
@@ -305,6 +319,7 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
});
|
||||
|
||||
typeTotpAction->setShortcut(Qt::CTRL + Qt::Key_3);
|
||||
typeTotpAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::TOTP);
|
||||
connect(typeTotpAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{TOTP}";
|
||||
@@ -312,6 +327,8 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
});
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
auto typeVirtualAction = new QAction(icons()->icon("auto-type"), tr("Use Virtual Keyboard"));
|
||||
m_actionMenu->insertAction(copyUsernameAction, typeVirtualAction);
|
||||
typeVirtualAction->setShortcut(Qt::CTRL + Qt::Key_4);
|
||||
connect(typeVirtualAction, &QAction::triggered, this, [&] {
|
||||
m_virtualMode = true;
|
||||
@@ -330,6 +347,7 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
copyUsernameAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::USERNAME);
|
||||
connect(copyUsernameAction, &QAction::triggered, this, [&] {
|
||||
auto entry = m_ui->view->currentMatch().first;
|
||||
if (entry) {
|
||||
@@ -337,6 +355,8 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
copyPasswordAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::PASSWORD);
|
||||
connect(copyPasswordAction, &QAction::triggered, this, [&] {
|
||||
auto entry = m_ui->view->currentMatch().first;
|
||||
if (entry) {
|
||||
@@ -344,6 +364,8 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
copyTotpAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::TOTP);
|
||||
connect(copyTotpAction, &QAction::triggered, this, [&] {
|
||||
auto entry = m_ui->view->currentMatch().first;
|
||||
if (entry) {
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
if(WITH_XC_AUTOTYPE)
|
||||
if(UNIX AND NOT APPLE AND NOT HAIKU)
|
||||
find_package(X11)
|
||||
find_package(Qt5X11Extras 5.2)
|
||||
find_package(X11 REQUIRED COMPONENTS Xi XTest)
|
||||
find_package(Qt5X11Extras 5.2 REQUIRED)
|
||||
if(PRINT_SUMMARY)
|
||||
add_feature_info(libXi X11_Xi_FOUND "The X11 Xi Protocol library is required for auto-type")
|
||||
add_feature_info(libXtst X11_XTest_FOUND "The X11 XTEST Protocol library is required for auto-type")
|
||||
add_feature_info(Qt5X11Extras Qt5X11Extras_FOUND "The Qt5X11Extras library is required for auto-type")
|
||||
endif()
|
||||
|
||||
if(X11_FOUND AND X11_Xi_FOUND AND X11_XTest_FOUND AND Qt5X11Extras_FOUND)
|
||||
add_subdirectory(xcb)
|
||||
endif()
|
||||
add_subdirectory(xcb)
|
||||
elseif(APPLE)
|
||||
add_subdirectory(mac)
|
||||
elseif(WIN32)
|
||||
@@ -20,4 +18,17 @@ if(WITH_XC_AUTOTYPE)
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
set(autotype_SOURCES
|
||||
AutoType.cpp
|
||||
AutoTypeAction.cpp
|
||||
AutoTypeMatchModel.cpp
|
||||
AutoTypeMatchView.cpp
|
||||
AutoTypeSelectDialog.cpp
|
||||
PickcharsDialog.cpp
|
||||
ShortcutWidget.cpp
|
||||
WindowSelectComboBox.cpp)
|
||||
|
||||
add_library(autotype STATIC ${autotype_SOURCES})
|
||||
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
|
||||
endif()
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<layout class="QGridLayout" name="charsGrid"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PasswordEdit" name="selectedChars">
|
||||
<widget class="PasswordWidget" name="selectedChars">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -74,9 +74,10 @@
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PasswordEdit</class>
|
||||
<class>PasswordWidget</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/PasswordEdit.h</header>
|
||||
<header>gui/PasswordWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
|
||||
@@ -90,7 +90,7 @@ void ShortcutWidget::keyEvent(QKeyEvent* event)
|
||||
return;
|
||||
}
|
||||
|
||||
Qt::Key key = static_cast<Qt::Key>(event->key());
|
||||
auto key = static_cast<Qt::Key>(event->key());
|
||||
|
||||
if (key <= 0 || key == Qt::Key_unknown) {
|
||||
return;
|
||||
|
||||
@@ -172,7 +172,7 @@ void AutoTypePlatformMac::sendChar(const QChar& ch, bool isKeyDown)
|
||||
// Send key code to active window
|
||||
// see: Quartz Event Services
|
||||
//
|
||||
void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers = 0)
|
||||
void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
uint16 keyCode = macUtils()->qtToNativeKeyCode(key);
|
||||
if (keyCode == INVALID_KEYCODE) {
|
||||
@@ -238,38 +238,22 @@ AutoTypeAction::Result AutoTypeExecutorMac::execBegin(const AutoTypeBegin* actio
|
||||
|
||||
AutoTypeAction::Result AutoTypeExecutorMac::execType(const AutoTypeKey* action)
|
||||
{
|
||||
if (action->modifiers & Qt::ShiftModifier) {
|
||||
m_platform->sendKey(Qt::Key_Shift, true);
|
||||
}
|
||||
if (action->modifiers & Qt::ControlModifier) {
|
||||
m_platform->sendKey(Qt::Key_Control, true);
|
||||
}
|
||||
if (action->modifiers & Qt::AltModifier) {
|
||||
m_platform->sendKey(Qt::Key_Alt, true);
|
||||
}
|
||||
if (action->modifiers & Qt::MetaModifier) {
|
||||
m_platform->sendKey(Qt::Key_Meta, true);
|
||||
}
|
||||
|
||||
|
||||
if (action->key != Qt::Key_unknown) {
|
||||
m_platform->sendKey(action->key, true);
|
||||
m_platform->sendKey(action->key, false);
|
||||
m_platform->sendKey(action->key, true, action->modifiers);
|
||||
m_platform->sendKey(action->key, false, action->modifiers);
|
||||
} else {
|
||||
m_platform->sendChar(action->character, true);
|
||||
m_platform->sendChar(action->character, false);
|
||||
}
|
||||
|
||||
if (action->modifiers & Qt::ShiftModifier) {
|
||||
m_platform->sendKey(Qt::Key_Shift, false);
|
||||
}
|
||||
if (action->modifiers & Qt::ControlModifier) {
|
||||
m_platform->sendKey(Qt::Key_Control, false);
|
||||
}
|
||||
if (action->modifiers & Qt::AltModifier) {
|
||||
m_platform->sendKey(Qt::Key_Alt, false);
|
||||
}
|
||||
if (action->modifiers & Qt::MetaModifier) {
|
||||
m_platform->sendKey(Qt::Key_Meta, false);
|
||||
if (action->modifiers != Qt::NoModifier) {
|
||||
// If we have modifiers set than we intend to send a key sequence
|
||||
// convert to uppercase to align with Qt Key mappings
|
||||
int ch = action->character.toUpper().toLatin1();
|
||||
m_platform->sendKey(static_cast<Qt::Key>(ch), true, action->modifiers);
|
||||
m_platform->sendKey(static_cast<Qt::Key>(ch), false, action->modifiers);
|
||||
} else {
|
||||
m_platform->sendChar(action->character, true);
|
||||
m_platform->sendChar(action->character, false);
|
||||
}
|
||||
}
|
||||
|
||||
Tools::sleep(execDelayMs);
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
bool raiseOwnWindow() override;
|
||||
|
||||
void sendChar(const QChar& ch, bool isKeyDown);
|
||||
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers);
|
||||
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers = 0);
|
||||
|
||||
private:
|
||||
static int windowLayer(CFDictionaryRef window);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set(autotype_test_SOURCES AutoTypeTest.cpp)
|
||||
|
||||
add_library(keepassxc-autotype-test MODULE ${autotype_test_SOURCES})
|
||||
target_link_libraries(keepassxc-autotype-test keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)
|
||||
target_link_libraries(keepassxc-autotype-test keepassxc_gui ${autotype_LIB} Qt5::Core Qt5::Widgets)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(autotype_win_SOURCES AutoTypeWindows.cpp)
|
||||
|
||||
add_library(keepassxc-autotype-windows MODULE ${autotype_win_SOURCES})
|
||||
target_link_libraries(keepassxc-autotype-windows keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)
|
||||
target_link_libraries(keepassxc-autotype-windows keepassxc_gui ${autotype_LIB} Qt5::Core Qt5::Widgets)
|
||||
install(TARGETS keepassxc-autotype-windows
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)
|
||||
|
||||
@@ -3,7 +3,7 @@ include_directories(SYSTEM ${X11_X11_INCLUDE_PATH})
|
||||
set(autotype_XCB_SOURCES AutoTypeXCB.cpp)
|
||||
|
||||
add_library(keepassxc-autotype-xcb MODULE ${autotype_XCB_SOURCES})
|
||||
target_link_libraries(keepassxc-autotype-xcb keepassx_core Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_XTest_LIB})
|
||||
target_link_libraries(keepassxc-autotype-xcb keepassxc_gui Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_XTest_LIB})
|
||||
install(TARGETS keepassxc-autotype-xcb
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Francois Ferrand
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,17 +21,19 @@
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/Entry.h"
|
||||
#include <QCloseEvent>
|
||||
|
||||
BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_ui(new Ui::BrowserAccessControlDialog())
|
||||
, m_entriesAccepted(false)
|
||||
{
|
||||
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->allowButton, SIGNAL(clicked()), SLOT(accept()));
|
||||
connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(reject()));
|
||||
connect(m_ui->denyButton, SIGNAL(clicked()), SLOT(reject()));
|
||||
}
|
||||
|
||||
BrowserAccessControlDialog::~BrowserAccessControlDialog()
|
||||
@@ -69,13 +71,10 @@ void BrowserAccessControlDialog::setItems(const QList<Entry*>& items, const QStr
|
||||
}
|
||||
});
|
||||
m_ui->itemsTable->setCellWidget(row, 1, disableButton);
|
||||
|
||||
++row;
|
||||
}
|
||||
|
||||
m_ui->itemsTable->resizeColumnsToContents();
|
||||
m_ui->itemsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
||||
m_ui->allowButton->setFocus();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Francois Ferrand
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -48,6 +48,9 @@ signals:
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::BrowserAccessControlDialog> m_ui;
|
||||
QList<Entry*> m_entriesToConfirm;
|
||||
QList<Entry*> m_allowedEntries;
|
||||
bool m_entriesAccepted;
|
||||
};
|
||||
|
||||
#endif // BROWSERACCESSCONTROLDIALOG_H
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<widget class="QPushButton" name="denyButton">
|
||||
<property name="text">
|
||||
<string>Deny All</string>
|
||||
</property>
|
||||
|
||||
@@ -235,6 +235,7 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
{
|
||||
const QString hash = browserService()->getDatabaseHash();
|
||||
const QString nonce = json.value("nonce").toString();
|
||||
const auto incrementedNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
const QString encrypted = json.value("message").toString();
|
||||
|
||||
if (!m_associated) {
|
||||
@@ -263,21 +264,19 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
const QString formUrl = decrypted.value("submitUrl").toString();
|
||||
const QString auth = decrypted.value("httpAuth").toString();
|
||||
const bool httpAuth = auth.compare(TRUE_STR) == 0;
|
||||
const QJsonArray users = browserService()->findMatchingEntries(id, siteUrl, formUrl, "", keyList, httpAuth);
|
||||
|
||||
const QJsonArray users = browserService()->findMatchingEntries(id, siteUrl, formUrl, "", keyList, httpAuth);
|
||||
if (users.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
|
||||
}
|
||||
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(incrementedNonce);
|
||||
message["count"] = users.count();
|
||||
message["entries"] = users;
|
||||
message["hash"] = hash;
|
||||
message["id"] = id;
|
||||
|
||||
return buildResponse(action, message, newNonce);
|
||||
return buildResponse(action, message, incrementedNonce);
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::handleGeneratePassword(QLocalSocket* socket, const QJsonObject& json, const QString& action)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Francois Ferrand
|
||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -18,7 +17,6 @@
|
||||
*/
|
||||
|
||||
#include "BrowserService.h"
|
||||
#include "BrowserAccessControlDialog.h"
|
||||
#include "BrowserAction.h"
|
||||
#include "BrowserEntryConfig.h"
|
||||
#include "BrowserEntrySaveDialog.h"
|
||||
@@ -56,6 +54,7 @@ const QString BrowserService::OPTION_SKIP_AUTO_SUBMIT = QStringLiteral("BrowserS
|
||||
const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry");
|
||||
const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnlyHttpAuth");
|
||||
const QString BrowserService::OPTION_NOT_HTTP_AUTH = QStringLiteral("BrowserNotHttpAuth");
|
||||
const QString BrowserService::OPTION_OMIT_WWW = QStringLiteral("BrowserOmitWww");
|
||||
// Multiple URL's
|
||||
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
|
||||
|
||||
@@ -314,6 +313,134 @@ QString BrowserService::getCurrentTotp(const QString& uuid)
|
||||
return {};
|
||||
}
|
||||
|
||||
QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl,
|
||||
const QString& realm,
|
||||
const StringPairList& keyList,
|
||||
const bool httpAuth)
|
||||
{
|
||||
Q_UNUSED(dbid);
|
||||
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
|
||||
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
|
||||
const QString siteHost = QUrl(siteUrl).host();
|
||||
const QString formHost = QUrl(formUrl).host();
|
||||
|
||||
// Check entries for authorization
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
QList<Entry*> pwEntries;
|
||||
for (auto* entry : searchEntries(siteUrl, formUrl, keyList)) {
|
||||
auto entryCustomData = entry->customData();
|
||||
|
||||
if (!httpAuth
|
||||
&& ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
||||
&& entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
|
||||
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (httpAuth
|
||||
&& ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
|
||||
&& entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
|
||||
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// HTTP Basic Auth always needs a confirmation
|
||||
if (!ignoreHttpAuth && httpAuth) {
|
||||
pwEntriesToConfirm.append(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (checkAccess(entry, siteHost, formHost, realm)) {
|
||||
case Denied:
|
||||
continue;
|
||||
|
||||
case Unknown:
|
||||
if (alwaysAllowAccess) {
|
||||
pwEntries.append(entry);
|
||||
} else {
|
||||
pwEntriesToConfirm.append(entry);
|
||||
}
|
||||
break;
|
||||
|
||||
case Allowed:
|
||||
pwEntries.append(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm entries
|
||||
QList<Entry*> selectedEntriesToConfirm =
|
||||
confirmEntries(pwEntriesToConfirm, siteUrl, siteHost, formHost, realm, httpAuth);
|
||||
if (!selectedEntriesToConfirm.isEmpty()) {
|
||||
pwEntries.append(selectedEntriesToConfirm);
|
||||
}
|
||||
|
||||
if (pwEntries.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Ensure that database is not locked when the popup was visible
|
||||
if (!isDatabaseOpened()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Sort results
|
||||
pwEntries = sortEntries(pwEntries, siteUrl, formUrl);
|
||||
|
||||
// Fill the list
|
||||
QJsonArray result;
|
||||
for (auto* entry : pwEntries) {
|
||||
result.append(prepareEntry(entry));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||
const QString& siteUrl,
|
||||
const QString& siteHost,
|
||||
const QString& formUrl,
|
||||
const QString& realm,
|
||||
const bool httpAuth)
|
||||
{
|
||||
if (pwEntriesToConfirm.isEmpty() || m_dialogActive) {
|
||||
return {};
|
||||
}
|
||||
|
||||
m_dialogActive = true;
|
||||
updateWindowState();
|
||||
BrowserAccessControlDialog accessControlDialog;
|
||||
|
||||
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
|
||||
|
||||
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
|
||||
auto entry = pwEntriesToConfirm[item->row()];
|
||||
denyEntry(entry, siteHost, formUrl, realm);
|
||||
});
|
||||
|
||||
accessControlDialog.setItems(pwEntriesToConfirm, siteUrl, httpAuth);
|
||||
|
||||
QList<Entry*> allowedEntries;
|
||||
auto ret = accessControlDialog.exec();
|
||||
if (ret == QDialog::Accepted) {
|
||||
for (auto item : accessControlDialog.getSelectedEntries()) {
|
||||
auto entry = pwEntriesToConfirm[item->row()];
|
||||
if (accessControlDialog.remember()) {
|
||||
allowEntry(entry, siteHost, formUrl, realm);
|
||||
}
|
||||
allowedEntries.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-hide the application if it wasn't visible before
|
||||
hideWindow();
|
||||
m_dialogActive = false;
|
||||
|
||||
return allowedEntries;
|
||||
}
|
||||
|
||||
void BrowserService::showPasswordGenerator(QLocalSocket* socket,
|
||||
const QString& incrementedNonce,
|
||||
const QString& publicKey,
|
||||
@@ -340,9 +467,11 @@ void BrowserService::showPasswordGenerator(QLocalSocket* socket,
|
||||
[=](const QString& password) {
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(incrementedNonce);
|
||||
message["password"] = password;
|
||||
sendPassword(socket,
|
||||
browserMessageBuilder()->buildResponse(
|
||||
"generate-password", message, incrementedNonce, publicKey, secretKey));
|
||||
m_browserHost->sendClientMessage(
|
||||
socket,
|
||||
browserMessageBuilder()->buildResponse(
|
||||
"generate-password", message, incrementedNonce, publicKey, secretKey));
|
||||
hideWindow();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -352,12 +481,6 @@ void BrowserService::showPasswordGenerator(QLocalSocket* socket,
|
||||
m_passwordGenerator->activateWindow();
|
||||
}
|
||||
|
||||
void BrowserService::sendPassword(QLocalSocket* socket, const QJsonObject& message)
|
||||
{
|
||||
m_browserHost->sendClientMessage(socket, message);
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
bool BrowserService::isPasswordGeneratorRequested() const
|
||||
{
|
||||
return m_passwordGeneratorRequested;
|
||||
@@ -425,96 +548,11 @@ QString BrowserService::getKey(const QString& id)
|
||||
return db->metadata()->customData()->value(CustomData::BrowserKeyPrefix + id);
|
||||
}
|
||||
|
||||
QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr,
|
||||
const QString& realm,
|
||||
const StringPairList& keyList,
|
||||
const bool httpAuth)
|
||||
{
|
||||
Q_UNUSED(dbid);
|
||||
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
|
||||
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
|
||||
const QString siteHost = QUrl(siteUrlStr).host();
|
||||
const QString formHost = QUrl(formUrlStr).host();
|
||||
|
||||
// Check entries for authorization
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
QList<Entry*> pwEntries;
|
||||
for (auto* entry : searchEntries(siteUrlStr, formUrlStr, keyList)) {
|
||||
auto entryCustomData = entry->customData();
|
||||
|
||||
if (!httpAuth
|
||||
&& ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
||||
&& entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
|
||||
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (httpAuth
|
||||
&& ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
|
||||
&& entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
|
||||
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// HTTP Basic Auth always needs a confirmation
|
||||
if (!ignoreHttpAuth && httpAuth) {
|
||||
pwEntriesToConfirm.append(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (checkAccess(entry, siteHost, formHost, realm)) {
|
||||
case Denied:
|
||||
continue;
|
||||
|
||||
case Unknown:
|
||||
if (alwaysAllowAccess) {
|
||||
pwEntries.append(entry);
|
||||
} else {
|
||||
pwEntriesToConfirm.append(entry);
|
||||
}
|
||||
break;
|
||||
|
||||
case Allowed:
|
||||
pwEntries.append(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm entries
|
||||
QList<Entry*> selectedEntriesToConfirm =
|
||||
confirmEntries(pwEntriesToConfirm, siteUrlStr, siteHost, formHost, realm, httpAuth);
|
||||
if (!selectedEntriesToConfirm.isEmpty()) {
|
||||
pwEntries.append(selectedEntriesToConfirm);
|
||||
}
|
||||
|
||||
if (pwEntries.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Ensure that database is not locked when the popup was visible
|
||||
if (!isDatabaseOpened()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Sort results
|
||||
pwEntries = sortEntries(pwEntries, siteUrlStr, formUrlStr);
|
||||
|
||||
// Fill the list
|
||||
QJsonArray result;
|
||||
for (auto* entry : pwEntries) {
|
||||
result.append(prepareEntry(entry));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BrowserService::addEntry(const QString& dbid,
|
||||
const QString& login,
|
||||
const QString& password,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr,
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl,
|
||||
const QString& realm,
|
||||
const QString& group,
|
||||
const QString& groupUuid,
|
||||
@@ -530,8 +568,8 @@ void BrowserService::addEntry(const QString& dbid,
|
||||
|
||||
auto* entry = new Entry();
|
||||
entry->setUuid(QUuid::createUuid());
|
||||
entry->setTitle(QUrl(siteUrlStr).host());
|
||||
entry->setUrl(siteUrlStr);
|
||||
entry->setTitle(QUrl(siteUrl).host());
|
||||
entry->setUrl(siteUrl);
|
||||
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
|
||||
entry->setUsername(login);
|
||||
entry->setPassword(password);
|
||||
@@ -550,8 +588,8 @@ void BrowserService::addEntry(const QString& dbid,
|
||||
entry->setGroup(getDefaultEntryGroup(db));
|
||||
}
|
||||
|
||||
const QString host = QUrl(siteUrlStr).host();
|
||||
const QString submitHost = QUrl(formUrlStr).host();
|
||||
const QString host = QUrl(siteUrl).host();
|
||||
const QString submitHost = QUrl(formUrl).host();
|
||||
BrowserEntryConfig config;
|
||||
config.allow(host);
|
||||
|
||||
@@ -572,8 +610,8 @@ bool BrowserService::updateEntry(const QString& dbid,
|
||||
const QString& uuid,
|
||||
const QString& login,
|
||||
const QString& password,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr)
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl)
|
||||
{
|
||||
// TODO: select database based on this key id
|
||||
Q_UNUSED(dbid);
|
||||
@@ -585,7 +623,7 @@ bool BrowserService::updateEntry(const QString& dbid,
|
||||
Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid));
|
||||
if (!entry) {
|
||||
// If entry is not found for update, add a new one to the selected database
|
||||
addEntry(dbid, login, password, siteUrlStr, formUrlStr, "", "", "", db);
|
||||
addEntry(dbid, login, password, siteUrl, formUrl, "", "", "", db);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -614,7 +652,7 @@ bool BrowserService::updateEntry(const QString& dbid,
|
||||
dialogResult = MessageBox::question(
|
||||
nullptr,
|
||||
tr("KeePassXC: Update Entry"),
|
||||
tr("Do you want to update the information in %1 - %2?").arg(QUrl(siteUrlStr).host(), username),
|
||||
tr("Do you want to update the information in %1 - %2?").arg(QUrl(siteUrl).host(), username),
|
||||
MessageBox::Save | MessageBox::Cancel,
|
||||
MessageBox::Cancel,
|
||||
MessageBox::Raise);
|
||||
@@ -663,7 +701,7 @@ bool BrowserService::deleteEntry(const QString& uuid)
|
||||
}
|
||||
|
||||
QList<Entry*>
|
||||
BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& siteUrlStr, const QString& formUrlStr)
|
||||
BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& siteUrl, const QString& formUrl)
|
||||
{
|
||||
QList<Entry*> entries;
|
||||
auto* rootGroup = db->rootGroup();
|
||||
@@ -677,6 +715,9 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto omitWwwSubdomain =
|
||||
group->resolveCustomDataTriState(BrowserService::OPTION_OMIT_WWW) == Group::Enable;
|
||||
|
||||
for (auto* entry : group->entries()) {
|
||||
if (entry->isRecycled()
|
||||
|| (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
|
||||
@@ -684,16 +725,7 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for additional URL's starting with KP2A_URL
|
||||
for (const auto& key : entry->attributes()->keys()) {
|
||||
if (key.startsWith(ADDITIONAL_URL) && handleURL(entry->attributes()->value(key), siteUrlStr, formUrlStr)
|
||||
&& !entries.contains(entry)) {
|
||||
entries.append(entry);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handleEntry(entry, siteUrlStr, formUrlStr)) {
|
||||
if (!shouldIncludeEntry(entry, siteUrl, formUrl, omitWwwSubdomain)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -708,7 +740,7 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
|
||||
}
|
||||
|
||||
QList<Entry*>
|
||||
BrowserService::searchEntries(const QString& siteUrlStr, const QString& formUrlStr, const StringPairList& keyList)
|
||||
BrowserService::searchEntries(const QString& siteUrl, const QString& formUrl, const StringPairList& keyList)
|
||||
{
|
||||
// Check if database is connected with KeePassXC-Browser
|
||||
auto databaseConnected = [&](const QSharedPointer<Database>& db) {
|
||||
@@ -738,11 +770,11 @@ BrowserService::searchEntries(const QString& siteUrlStr, const QString& formUrlS
|
||||
}
|
||||
|
||||
// Search entries matching the hostname
|
||||
QString hostname = QUrl(siteUrlStr).host();
|
||||
QString hostname = QUrl(siteUrl).host();
|
||||
QList<Entry*> entries;
|
||||
do {
|
||||
for (const auto& db : databases) {
|
||||
entries << searchEntries(db, siteUrlStr, formUrlStr);
|
||||
entries << searchEntries(db, siteUrl, formUrl);
|
||||
}
|
||||
} while (entries.isEmpty() && removeFirstDomain(hostname));
|
||||
|
||||
@@ -827,13 +859,12 @@ void BrowserService::requestGlobalAutoType(const QString& search)
|
||||
emit osUtils->globalShortcutTriggered("autotype", search);
|
||||
}
|
||||
|
||||
QList<Entry*>
|
||||
BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr, const QString& formUrlStr)
|
||||
QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrl, const QString& formUrl)
|
||||
{
|
||||
// Build map of prioritized entries
|
||||
QMultiMap<int, Entry*> priorities;
|
||||
for (auto* entry : pwEntries) {
|
||||
priorities.insert(sortPriority(getEntryURLs(entry), siteUrlStr, formUrlStr), entry);
|
||||
priorities.insert(sortPriority(getEntryURLs(entry), siteUrl, formUrl), entry);
|
||||
}
|
||||
|
||||
auto keys = priorities.uniqueKeys();
|
||||
@@ -852,66 +883,38 @@ BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr,
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||
const QString& siteUrlStr,
|
||||
const QString& siteHost,
|
||||
const QString& formUrlStr,
|
||||
const QString& realm,
|
||||
const bool httpAuth)
|
||||
void BrowserService::allowEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm)
|
||||
{
|
||||
if (pwEntriesToConfirm.isEmpty() || m_dialogActive) {
|
||||
return {};
|
||||
BrowserEntryConfig config;
|
||||
config.load(entry);
|
||||
config.allow(siteHost);
|
||||
|
||||
if (!formUrl.isEmpty() && siteHost != formUrl) {
|
||||
config.allow(formUrl);
|
||||
}
|
||||
|
||||
m_dialogActive = true;
|
||||
updateWindowState();
|
||||
BrowserAccessControlDialog accessControlDialog;
|
||||
|
||||
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
|
||||
|
||||
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
|
||||
auto entry = pwEntriesToConfirm[item->row()];
|
||||
BrowserEntryConfig config;
|
||||
config.load(entry);
|
||||
config.deny(siteHost);
|
||||
if (!formUrlStr.isEmpty() && siteHost != formUrlStr) {
|
||||
config.deny(formUrlStr);
|
||||
}
|
||||
if (!realm.isEmpty()) {
|
||||
config.setRealm(realm);
|
||||
}
|
||||
config.save(entry);
|
||||
});
|
||||
|
||||
accessControlDialog.setItems(pwEntriesToConfirm, siteUrlStr, httpAuth);
|
||||
|
||||
QList<Entry*> allowedEntries;
|
||||
if (accessControlDialog.exec() == QDialog::Accepted) {
|
||||
const auto selectedEntries = accessControlDialog.getSelectedEntries();
|
||||
for (auto item : accessControlDialog.getSelectedEntries()) {
|
||||
auto entry = pwEntriesToConfirm[item->row()];
|
||||
if (accessControlDialog.remember()) {
|
||||
BrowserEntryConfig config;
|
||||
config.load(entry);
|
||||
config.allow(siteHost);
|
||||
if (!formUrlStr.isEmpty() && siteHost != formUrlStr) {
|
||||
config.allow(formUrlStr);
|
||||
}
|
||||
if (!realm.isEmpty()) {
|
||||
config.setRealm(realm);
|
||||
}
|
||||
config.save(entry);
|
||||
}
|
||||
allowedEntries.append(entry);
|
||||
}
|
||||
if (!realm.isEmpty()) {
|
||||
config.setRealm(realm);
|
||||
}
|
||||
|
||||
// Re-hide the application if it wasn't visible before
|
||||
hideWindow();
|
||||
config.save(entry);
|
||||
}
|
||||
|
||||
m_dialogActive = false;
|
||||
void BrowserService::denyEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm)
|
||||
{
|
||||
BrowserEntryConfig config;
|
||||
config.load(entry);
|
||||
config.deny(siteHost);
|
||||
|
||||
return allowedEntries;
|
||||
if (!formUrl.isEmpty() && siteHost != formUrl) {
|
||||
config.deny(formUrl);
|
||||
}
|
||||
|
||||
if (!realm.isEmpty()) {
|
||||
config.setRealm(realm);
|
||||
}
|
||||
|
||||
config.save(entry);
|
||||
}
|
||||
|
||||
QJsonObject BrowserService::prepareEntry(const Entry* entry)
|
||||
@@ -959,7 +962,7 @@ BrowserService::Access
|
||||
BrowserService::checkAccess(const Entry* entry, const QString& siteHost, const QString& formHost, const QString& realm)
|
||||
{
|
||||
if (entry->isExpired()) {
|
||||
return browserSettings()->allowExpiredCredentials() ? Allowed : Denied;
|
||||
return browserSettings()->allowExpiredCredentials() ? Unknown : Denied;
|
||||
}
|
||||
|
||||
BrowserEntryConfig config;
|
||||
@@ -1006,14 +1009,14 @@ Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& sele
|
||||
|
||||
// Returns the maximum sort priority given a set of match urls and the
|
||||
// extension provided site and form url.
|
||||
int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrlStr, const QString& formUrlStr)
|
||||
int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl)
|
||||
{
|
||||
QList<int> priorityList;
|
||||
// NOTE: QUrl::matches is utterly broken in Qt < 5.11, so we work around that
|
||||
// by removing parts of the url that we don't match and direct matching others
|
||||
const auto stdOpts = QUrl::RemoveFragment | QUrl::RemoveUserInfo;
|
||||
const auto siteUrl = QUrl(siteUrlStr).adjusted(stdOpts);
|
||||
const auto formUrl = QUrl(formUrlStr).adjusted(stdOpts);
|
||||
const auto adjustedSiteUrl = QUrl(siteUrl).adjusted(stdOpts);
|
||||
const auto adjustedFormUrl = QUrl(formUrl).adjusted(stdOpts);
|
||||
|
||||
auto getPriority = [&](const QString& givenUrl) {
|
||||
auto url = QUrl::fromUserInput(givenUrl).adjusted(stdOpts);
|
||||
@@ -1031,38 +1034,38 @@ int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrl
|
||||
|
||||
// Reject invalid urls and hosts, except 'localhost', and scheme mismatch
|
||||
if (!url.isValid() || (!url.host().contains(".") && url.host() != "localhost")
|
||||
|| url.scheme() != siteUrl.scheme()) {
|
||||
|| url.scheme() != adjustedSiteUrl.scheme()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Exact match with site url or form url
|
||||
if (url.matches(siteUrl, QUrl::None) || url.matches(formUrl, QUrl::None)) {
|
||||
if (url.matches(adjustedSiteUrl, QUrl::None) || url.matches(adjustedFormUrl, QUrl::None)) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
// Exact match without the query string
|
||||
if (url.matches(siteUrl, QUrl::RemoveQuery) || url.matches(formUrl, QUrl::RemoveQuery)) {
|
||||
if (url.matches(adjustedSiteUrl, QUrl::RemoveQuery) || url.matches(adjustedFormUrl, QUrl::RemoveQuery)) {
|
||||
return 90;
|
||||
}
|
||||
|
||||
// Parent directory match
|
||||
if (url.isParentOf(siteUrl) || url.isParentOf(formUrl)) {
|
||||
if (url.isParentOf(adjustedSiteUrl) || url.isParentOf(adjustedFormUrl)) {
|
||||
return 85;
|
||||
}
|
||||
|
||||
// Match without path (ie, FQDN match), form url prioritizes lower than site url
|
||||
if (url.host() == siteUrl.host()) {
|
||||
if (url.host() == adjustedSiteUrl.host()) {
|
||||
return 80;
|
||||
}
|
||||
if (url.host() == formUrl.host()) {
|
||||
if (url.host() == adjustedFormUrl.host()) {
|
||||
return 70;
|
||||
}
|
||||
|
||||
// Site/form url ends with given url (subdomain mismatch)
|
||||
if (siteUrl.host().endsWith(url.host())) {
|
||||
if (adjustedSiteUrl.host().endsWith(url.host())) {
|
||||
return 60;
|
||||
}
|
||||
if (formUrl.host().endsWith(url.host())) {
|
||||
if (adjustedFormUrl.host().endsWith(url.host())) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@@ -1108,7 +1111,10 @@ bool BrowserService::removeFirstDomain(QString& hostname)
|
||||
|
||||
/* Test if a search URL matches a custom entry. If the URL has the schema "keepassxc", some special checks will be made.
|
||||
* Otherwise, this simply delegates to handleURL(). */
|
||||
bool BrowserService::handleEntry(Entry* entry, const QString& url, const QString& submitUrl)
|
||||
bool BrowserService::shouldIncludeEntry(Entry* entry,
|
||||
const QString& url,
|
||||
const QString& submitUrl,
|
||||
const bool omitWwwSubdomain)
|
||||
{
|
||||
// Use this special scheme to find entries by UUID
|
||||
if (url.startsWith("keepassxc://by-uuid/")) {
|
||||
@@ -1116,10 +1122,21 @@ bool BrowserService::handleEntry(Entry* entry, const QString& url, const QString
|
||||
} else if (url.startsWith("keepassxc://by-path/")) {
|
||||
return url.endsWith("by-path/" + entry->path());
|
||||
}
|
||||
return handleURL(entry->url(), url, submitUrl);
|
||||
|
||||
const auto allEntryUrls = entry->getAllUrls();
|
||||
for (const auto& entryUrl : allEntryUrls) {
|
||||
if (handleURL(entryUrl, url, submitUrl, omitWwwSubdomain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlStr, const QString& formUrlStr)
|
||||
bool BrowserService::handleURL(const QString& entryUrl,
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl,
|
||||
const bool omitWwwSubdomain)
|
||||
{
|
||||
if (entryUrl.isEmpty()) {
|
||||
return false;
|
||||
@@ -1136,9 +1153,14 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlSt
|
||||
}
|
||||
}
|
||||
|
||||
// Remove WWW subdomain from matching if group setting is enabled
|
||||
if (omitWwwSubdomain && entryQUrl.host().startsWith("www.")) {
|
||||
entryQUrl.setHost(entryQUrl.host().remove("www."));
|
||||
}
|
||||
|
||||
// Make a direct compare if a local file is used
|
||||
if (siteUrlStr.startsWith("file://")) {
|
||||
return entryUrl == formUrlStr;
|
||||
if (siteUrl.startsWith("file://")) {
|
||||
return entryUrl == formUrl;
|
||||
}
|
||||
|
||||
// URL host validation fails
|
||||
@@ -1147,7 +1169,7 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlSt
|
||||
}
|
||||
|
||||
// Match port, if used
|
||||
QUrl siteQUrl(siteUrlStr);
|
||||
QUrl siteQUrl(siteUrl);
|
||||
if (entryQUrl.port() > 0 && entryQUrl.port() != siteQUrl.port()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1428,8 +1450,7 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||
|
||||
void BrowserService::activeDatabaseChanged(DatabaseWidget* dbWidget)
|
||||
{
|
||||
// Only emit these signals when we are not searching in all databases
|
||||
if (dbWidget && !browserSettings()->searchInAllDatabases()) {
|
||||
if (dbWidget) {
|
||||
if (dbWidget->isLocked()) {
|
||||
databaseLocked(dbWidget);
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Francois Ferrand
|
||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,6 +19,7 @@
|
||||
#ifndef BROWSERSERVICE_H
|
||||
#define BROWSERSERVICE_H
|
||||
|
||||
#include "BrowserAccessControlDialog.h"
|
||||
#include "core/Entry.h"
|
||||
#include "gui/PasswordGeneratorWidget.h"
|
||||
|
||||
@@ -62,14 +62,13 @@ public:
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey);
|
||||
void sendPassword(QLocalSocket* socket, const QJsonObject& message);
|
||||
bool isPasswordGeneratorRequested() const;
|
||||
|
||||
void addEntry(const QString& dbid,
|
||||
const QString& login,
|
||||
const QString& password,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr,
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl,
|
||||
const QString& realm,
|
||||
const QString& group,
|
||||
const QString& groupUuid,
|
||||
@@ -79,17 +78,15 @@ public:
|
||||
const QString& uuid,
|
||||
const QString& login,
|
||||
const QString& password,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr);
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl);
|
||||
bool deleteEntry(const QString& uuid);
|
||||
|
||||
QJsonArray findMatchingEntries(const QString& dbid,
|
||||
const QString& siteUrlStr,
|
||||
const QString& formUrlStr,
|
||||
const QString& realm,
|
||||
const StringPairList& keyList,
|
||||
const bool httpAuth = false);
|
||||
|
||||
void requestGlobalAutoType(const QString& search);
|
||||
static void convertAttributesToCustomData(QSharedPointer<Database> db);
|
||||
|
||||
@@ -99,6 +96,7 @@ public:
|
||||
static const QString OPTION_HIDE_ENTRY;
|
||||
static const QString OPTION_ONLY_HTTP_AUTH;
|
||||
static const QString OPTION_NOT_HTTP_AUTH;
|
||||
static const QString OPTION_OMIT_WWW;
|
||||
static const QString ADDITIONAL_URL;
|
||||
|
||||
signals:
|
||||
@@ -128,26 +126,31 @@ private:
|
||||
Hidden
|
||||
};
|
||||
|
||||
QList<Entry*>
|
||||
searchEntries(const QSharedPointer<Database>& db, const QString& siteUrlStr, const QString& formUrlStr);
|
||||
QList<Entry*> searchEntries(const QString& siteUrlStr, const QString& formUrlStr, const StringPairList& keyList);
|
||||
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr, const QString& formUrlStr);
|
||||
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& siteUrl, const QString& formUrl);
|
||||
QList<Entry*> searchEntries(const QString& siteUrl, const QString& formUrl, const StringPairList& keyList);
|
||||
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& siteUrl, const QString& formUrl);
|
||||
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||
const QString& siteUrlStr,
|
||||
const QString& siteUrl,
|
||||
const QString& siteHost,
|
||||
const QString& formUrlStr,
|
||||
const QString& formUrl,
|
||||
const QString& realm,
|
||||
const bool httpAuth);
|
||||
QJsonObject prepareEntry(const Entry* entry);
|
||||
void allowEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm);
|
||||
void denyEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm);
|
||||
QJsonArray getChildrenFromGroup(Group* group);
|
||||
Access checkAccess(const Entry* entry, const QString& siteHost, const QString& formHost, const QString& realm);
|
||||
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
||||
int sortPriority(const QStringList& urls, const QString& siteUrlStr, const QString& formUrlStr);
|
||||
int sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl);
|
||||
bool schemeFound(const QString& url);
|
||||
bool isIpAddress(const QString& host) const;
|
||||
bool removeFirstDomain(QString& hostname);
|
||||
bool handleEntry(Entry* entry, const QString& url, const QString& submitUrl);
|
||||
bool handleURL(const QString& entryUrl, const QString& siteUrlStr, const QString& formUrlStr);
|
||||
bool
|
||||
shouldIncludeEntry(Entry* entry, const QString& url, const QString& submitUrl, const bool omitWwwSubdomain = false);
|
||||
bool handleURL(const QString& entryUrl,
|
||||
const QString& siteUrl,
|
||||
const QString& formUrl,
|
||||
const bool omitWwwSubdomain = false);
|
||||
QString getTopLevelDomainFromUrl(const QString& url) const;
|
||||
QString baseDomain(const QString& hostname) const;
|
||||
QSharedPointer<Database> getDatabase();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config-keepassx.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#if defined(KEEPASSXC_DIST_SNAP)
|
||||
#include <QProcessEnvironment>
|
||||
@@ -31,14 +32,24 @@ namespace BrowserShared
|
||||
const auto serverName = QStringLiteral("/org.keepassxc.KeePassXC.BrowserServer");
|
||||
#if defined(KEEPASSXC_DIST_SNAP)
|
||||
return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverName;
|
||||
#elif defined(KEEPASSXC_DIST_FLATPAK)
|
||||
return QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + "/app/" + "org.keepassxc.KeePassXC"
|
||||
+ serverName;
|
||||
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
// Use XDG_RUNTIME_DIR instead of /tmp if it's available
|
||||
// This returns XDG_RUNTIME_DIR or else a temporary subdirectory.
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverName
|
||||
: path + serverName;
|
||||
|
||||
// Put the socket in a dedicated directory.
|
||||
// This directory will be easily mountable by sandbox containers.
|
||||
QString subPath = path + "/app/org.keepassxc.KeePassXC";
|
||||
QDir().mkpath(subPath);
|
||||
|
||||
QString socketPath = subPath + serverName;
|
||||
#ifndef KEEPASSXC_DIST_FLATPAK
|
||||
// Create a symlink at the legacy location for backwards compatibility.
|
||||
const auto origSocketPath = path + serverName;
|
||||
QFile::remove(origSocketPath);
|
||||
QFile::link(socketPath, origSocketPath);
|
||||
#endif
|
||||
|
||||
return socketPath;
|
||||
#elif defined(Q_OS_WIN)
|
||||
// Windows uses named pipes
|
||||
return serverName + "_" + qgetenv("USERNAME");
|
||||
|
||||
@@ -33,5 +33,5 @@ if(WITH_XC_BROWSER)
|
||||
)
|
||||
|
||||
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
|
||||
target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN2_LIBRARIES})
|
||||
target_link_libraries(keepassxcbrowser keepassxc_gui Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
@@ -225,7 +225,7 @@ QString NativeMessageInstaller::getNativeMessagePath(SupportedBrowsers browser)
|
||||
} else {
|
||||
basePath = QDir::homePath() + "/.config";
|
||||
}
|
||||
#elif defined(Q_OS_LINUX)
|
||||
#elif defined(Q_OS_LINUX) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
if (browser == SupportedBrowsers::TOR_BROWSER) {
|
||||
basePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
|
||||
} else if (browser == SupportedBrowsers::FIREFOX) {
|
||||
|
||||
@@ -57,7 +57,7 @@ int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPoin
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* newGroup = new Group();
|
||||
auto newGroup = new Group();
|
||||
newGroup->setUuid(QUuid::createUuid());
|
||||
newGroup->setName(groupName);
|
||||
newGroup->setParent(parentGroup);
|
||||
|
||||
@@ -24,9 +24,9 @@ class AddGroup : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
AddGroup();
|
||||
~AddGroup();
|
||||
~AddGroup() override;
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_ADDGROUP_H
|
||||
|
||||
@@ -22,9 +22,11 @@ set(cli_SOURCES
|
||||
AttachmentRemove.cpp
|
||||
Clip.cpp
|
||||
Close.cpp
|
||||
Create.cpp
|
||||
Command.cpp
|
||||
DatabaseCommand.cpp
|
||||
DatabaseCreate.cpp
|
||||
DatabaseEdit.cpp
|
||||
DatabaseInfo.cpp
|
||||
Diceware.cpp
|
||||
Edit.cpp
|
||||
Estimate.cpp
|
||||
@@ -33,7 +35,6 @@ set(cli_SOURCES
|
||||
Generate.cpp
|
||||
Help.cpp
|
||||
Import.cpp
|
||||
Info.cpp
|
||||
List.cpp
|
||||
Merge.cpp
|
||||
Move.cpp
|
||||
@@ -57,13 +58,16 @@ add_executable(keepassxc-cli keepassxc-cli.cpp)
|
||||
target_link_libraries(keepassxc-cli
|
||||
${GPGERROR_LIBRARIES}
|
||||
cli
|
||||
keepassx_core)
|
||||
keepassxc_core)
|
||||
|
||||
install(TARGETS keepassxc-cli
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${CLI_INSTALL_DIR} COMPONENT Runtime)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(keepassxc-cli
|
||||
PRIVATE keepassxc-cli.exe.manifest)
|
||||
|
||||
# install(CODE "include(BundleUtilities)
|
||||
# fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/keepassxc-cli.exe\" \"\" \"\")"
|
||||
# COMPONENT Runtime)
|
||||
|
||||
@@ -116,8 +116,12 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
selectedAttribute = "totp";
|
||||
found = true;
|
||||
value = entry->totp();
|
||||
} else if (Utils::EntryFieldNames.contains(selectedAttribute)) {
|
||||
value = Utils::getTopLevelField(entry, selectedAttribute);
|
||||
found = true;
|
||||
} else {
|
||||
QStringList attrs = Utils::findAttributes(*entry->attributes(), selectedAttribute);
|
||||
if (attrs.size() > 1) {
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#include "AttachmentRemove.h"
|
||||
#include "Clip.h"
|
||||
#include "Close.h"
|
||||
#include "Create.h"
|
||||
#include "DatabaseCreate.h"
|
||||
#include "DatabaseEdit.h"
|
||||
#include "DatabaseInfo.h"
|
||||
#include "Diceware.h"
|
||||
#include "Edit.h"
|
||||
#include "Estimate.h"
|
||||
@@ -32,7 +34,6 @@
|
||||
#include "Generate.h"
|
||||
#include "Help.h"
|
||||
#include "Import.h"
|
||||
#include "Info.h"
|
||||
#include "List.h"
|
||||
#include "Merge.h"
|
||||
#include "Move.h"
|
||||
@@ -172,8 +173,9 @@ namespace Commands
|
||||
s_commands.insert(QStringLiteral("attachment-rm"), QSharedPointer<Command>(new AttachmentRemove()));
|
||||
s_commands.insert(QStringLiteral("clip"), QSharedPointer<Command>(new Clip()));
|
||||
s_commands.insert(QStringLiteral("close"), QSharedPointer<Command>(new Close()));
|
||||
s_commands.insert(QStringLiteral("db-create"), QSharedPointer<Command>(new Create()));
|
||||
s_commands.insert(QStringLiteral("db-info"), QSharedPointer<Command>(new Info()));
|
||||
s_commands.insert(QStringLiteral("db-create"), QSharedPointer<Command>(new DatabaseCreate()));
|
||||
s_commands.insert(QStringLiteral("db-edit"), QSharedPointer<Command>(new DatabaseEdit()));
|
||||
s_commands.insert(QStringLiteral("db-info"), QSharedPointer<Command>(new DatabaseInfo()));
|
||||
s_commands.insert(QStringLiteral("diceware"), QSharedPointer<Command>(new Diceware()));
|
||||
s_commands.insert(QStringLiteral("edit"), QSharedPointer<Command>(new Edit()));
|
||||
s_commands.insert(QStringLiteral("estimate"), QSharedPointer<Command>(new Estimate()));
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Create.h"
|
||||
#include "DatabaseCreate.h"
|
||||
|
||||
#include "Utils.h"
|
||||
#include "keys/FileKey.h"
|
||||
@@ -23,34 +23,39 @@
|
||||
#include <QCommandLineParser>
|
||||
#include <QFileInfo>
|
||||
|
||||
const QCommandLineOption Create::DecryptionTimeOption =
|
||||
const QCommandLineOption DatabaseCreate::DecryptionTimeOption =
|
||||
QCommandLineOption(QStringList() << "t"
|
||||
<< "decryption-time",
|
||||
QObject::tr("Target decryption time in MS for the database."),
|
||||
QObject::tr("time"));
|
||||
|
||||
const QCommandLineOption Create::SetKeyFileOption =
|
||||
QCommandLineOption(QStringList() << "k"
|
||||
<< "set-key-file",
|
||||
const QCommandLineOption DatabaseCreate::SetKeyFileShortOption = QCommandLineOption(
|
||||
QStringList() << "k",
|
||||
QObject::tr("Set the key file for the database.\nThis options is deprecated, use --set-key-file instead."),
|
||||
QObject::tr("path"));
|
||||
|
||||
const QCommandLineOption DatabaseCreate::SetKeyFileOption =
|
||||
QCommandLineOption(QStringList() << "set-key-file",
|
||||
QObject::tr("Set the key file for the database."),
|
||||
QObject::tr("path"));
|
||||
|
||||
const QCommandLineOption Create::SetPasswordOption =
|
||||
const QCommandLineOption DatabaseCreate::SetPasswordOption =
|
||||
QCommandLineOption(QStringList() << "p"
|
||||
<< "set-password",
|
||||
QObject::tr("Set a password for the database."));
|
||||
|
||||
Create::Create()
|
||||
DatabaseCreate::DatabaseCreate()
|
||||
{
|
||||
name = QString("db-create");
|
||||
description = QObject::tr("Create a new database.");
|
||||
positionalArguments.append({QString("database"), QObject::tr("Path of the database."), QString("")});
|
||||
options.append(Create::SetKeyFileOption);
|
||||
options.append(Create::SetPasswordOption);
|
||||
options.append(Create::DecryptionTimeOption);
|
||||
options.append(DatabaseCreate::SetKeyFileOption);
|
||||
options.append(DatabaseCreate::SetKeyFileShortOption);
|
||||
options.append(DatabaseCreate::SetPasswordOption);
|
||||
options.append(DatabaseCreate::DecryptionTimeOption);
|
||||
}
|
||||
|
||||
QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser)
|
||||
QSharedPointer<Database> DatabaseCreate::initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser)
|
||||
{
|
||||
if (parser.isNull()) {
|
||||
return {};
|
||||
@@ -60,7 +65,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
// Validate the decryption time before asking for a password.
|
||||
QString decryptionTimeValue = parser->value(Create::DecryptionTimeOption);
|
||||
QString decryptionTimeValue = parser->value(DatabaseCreate::DecryptionTimeOption);
|
||||
int decryptionTime = 0;
|
||||
if (decryptionTimeValue.length() != 0) {
|
||||
decryptionTime = decryptionTimeValue.toInt();
|
||||
@@ -78,7 +83,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
if (parser->isSet(Create::SetPasswordOption)) {
|
||||
if (parser->isSet(DatabaseCreate::SetPasswordOption)) {
|
||||
auto passwordKey = Utils::getConfirmedPassword();
|
||||
if (passwordKey.isNull()) {
|
||||
err << QObject::tr("Failed to set database password.") << endl;
|
||||
@@ -87,10 +92,18 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
|
||||
key->addKey(passwordKey);
|
||||
}
|
||||
|
||||
if (parser->isSet(Create::SetKeyFileOption)) {
|
||||
if (parser->isSet(DatabaseCreate::SetKeyFileOption) || parser->isSet(DatabaseCreate::SetKeyFileShortOption)) {
|
||||
QSharedPointer<FileKey> fileKey;
|
||||
|
||||
if (!Utils::loadFileKey(parser->value(Create::SetKeyFileOption), fileKey)) {
|
||||
QString keyFilePath;
|
||||
if (parser->isSet(DatabaseCreate::SetKeyFileShortOption)) {
|
||||
qWarning("The -k option will be deprecated. Please use the --set-key-file option instead.");
|
||||
keyFilePath = parser->value(DatabaseCreate::SetKeyFileShortOption);
|
||||
} else {
|
||||
keyFilePath = parser->value(DatabaseCreate::SetKeyFileOption);
|
||||
}
|
||||
|
||||
if (!Utils::loadFileKey(keyFilePath, fileKey)) {
|
||||
err << QObject::tr("Loading the key file failed") << endl;
|
||||
return {};
|
||||
}
|
||||
@@ -141,7 +154,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
|
||||
*
|
||||
* @return EXIT_SUCCESS on success, or EXIT_FAILURE on failure
|
||||
*/
|
||||
int Create::execute(const QStringList& arguments)
|
||||
int DatabaseCreate::execute(const QStringList& arguments)
|
||||
{
|
||||
QSharedPointer<QCommandLineParser> parser = getCommandLineParser(arguments);
|
||||
if (parser.isNull()) {
|
||||
@@ -159,7 +172,7 @@ int Create::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QSharedPointer<Database> db = Create::initializeDatabaseFromOptions(parser);
|
||||
QSharedPointer<Database> db = DatabaseCreate::initializeDatabaseFromOptions(parser);
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -15,22 +15,23 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_CREATE_H
|
||||
#define KEEPASSXC_CREATE_H
|
||||
#ifndef KEEPASSXC_DATABASECREATE_H
|
||||
#define KEEPASSXC_DATABASECREATE_H
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
class Create : public Command
|
||||
class DatabaseCreate : public Command
|
||||
{
|
||||
public:
|
||||
Create();
|
||||
DatabaseCreate();
|
||||
int execute(const QStringList& arguments) override;
|
||||
|
||||
static QSharedPointer<Database> initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser);
|
||||
|
||||
static const QCommandLineOption SetKeyFileOption;
|
||||
static const QCommandLineOption SetKeyFileShortOption;
|
||||
static const QCommandLineOption SetPasswordOption;
|
||||
static const QCommandLineOption DecryptionTimeOption;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_CREATE_H
|
||||
#endif // KEEPASSXC_DATABASECREATE_H
|
||||
174
src/cli/DatabaseEdit.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DatabaseEdit.h"
|
||||
|
||||
#include "Utils.h"
|
||||
#include "cli/DatabaseCreate.h"
|
||||
#include "keys/ChallengeResponseKey.h"
|
||||
#include "keys/FileKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QFileInfo>
|
||||
|
||||
const QCommandLineOption DatabaseEdit::UnsetPasswordOption =
|
||||
QCommandLineOption(QStringList() << "unset-password", QObject::tr("Unset the password for the database."));
|
||||
const QCommandLineOption DatabaseEdit::UnsetKeyFileOption =
|
||||
QCommandLineOption(QStringList() << "unset-key-file", QObject::tr("Unset the key file for the database."));
|
||||
|
||||
DatabaseEdit::DatabaseEdit()
|
||||
{
|
||||
name = QString("db-edit");
|
||||
description = QObject::tr("Edit a database.");
|
||||
options.append(DatabaseCreate::SetKeyFileOption);
|
||||
options.append(DatabaseCreate::SetPasswordOption);
|
||||
options.append(DatabaseEdit::UnsetKeyFileOption);
|
||||
options.append(DatabaseEdit::UnsetPasswordOption);
|
||||
}
|
||||
|
||||
int DatabaseEdit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
bool databaseWasChanged = false;
|
||||
|
||||
if (parser->isSet(DatabaseCreate::SetPasswordOption) && parser->isSet(DatabaseEdit::UnsetPasswordOption)) {
|
||||
err << QObject::tr("Cannot use %1 and %2 at the same time.")
|
||||
.arg(DatabaseCreate::SetPasswordOption.names().at(0))
|
||||
.arg(DatabaseEdit::UnsetPasswordOption.names().at(0))
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (parser->isSet(DatabaseCreate::SetKeyFileOption) && parser->isSet(DatabaseEdit::UnsetKeyFileOption)) {
|
||||
err << QObject::tr("Cannot use %1 and %2 at the same time.")
|
||||
.arg(DatabaseCreate::SetKeyFileOption.names().at(0))
|
||||
.arg(DatabaseEdit::UnsetKeyFileOption.names().at(0))
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool hasKeyChange =
|
||||
(parser->isSet(DatabaseCreate::SetPasswordOption) || parser->isSet(DatabaseCreate::SetKeyFileOption)
|
||||
|| parser->isSet(DatabaseEdit::UnsetPasswordOption) || parser->isSet(DatabaseEdit::UnsetKeyFileOption));
|
||||
|
||||
if (hasKeyChange) {
|
||||
auto newDatabaseKey = getNewDatabaseKey(database,
|
||||
parser->isSet(DatabaseCreate::SetPasswordOption),
|
||||
parser->isSet(DatabaseEdit::UnsetPasswordOption),
|
||||
parser->value(DatabaseCreate::SetKeyFileOption),
|
||||
parser->isSet(DatabaseEdit::UnsetKeyFileOption));
|
||||
if (newDatabaseKey.isNull()) {
|
||||
err << QObject::tr("Could not change the database key.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
database->setKey(newDatabaseKey);
|
||||
databaseWasChanged = true;
|
||||
}
|
||||
|
||||
if (!databaseWasChanged) {
|
||||
out << QObject::tr("Database was not modified.") << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(Database::Atomic, {}, &errorMessage)) {
|
||||
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out << QObject::tr("Successfully edited the database.") << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
QSharedPointer<CompositeKey> DatabaseEdit::getNewDatabaseKey(QSharedPointer<Database> database,
|
||||
bool updatePassword,
|
||||
bool removePassword,
|
||||
QString newFileKeyPath,
|
||||
bool removeKeyFile)
|
||||
{
|
||||
auto& err = Utils::STDERR;
|
||||
auto newDatabaseKey = QSharedPointer<CompositeKey>::create();
|
||||
bool updateKeyFile = !newFileKeyPath.isEmpty();
|
||||
|
||||
auto currentPasswordKey = database->key()->getKey(PasswordKey::UUID);
|
||||
auto currentFileKey = database->key()->getKey(FileKey::UUID);
|
||||
auto currentChallengeResponseKey = database->key()->getChallengeResponseKey(ChallengeResponseKey::UUID);
|
||||
|
||||
if (removePassword && currentPasswordKey.isNull()) {
|
||||
err << QObject::tr("Cannot remove password: The database does not have a password.") << endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (removeKeyFile && currentFileKey.isNull()) {
|
||||
err << QObject::tr("Cannot remove file key: The database does not have a file key.") << endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (updatePassword) {
|
||||
QSharedPointer<PasswordKey> newPasswordKey = Utils::getConfirmedPassword();
|
||||
if (newPasswordKey.isNull()) {
|
||||
err << QObject::tr("Failed to set database password.") << endl;
|
||||
return {};
|
||||
}
|
||||
newDatabaseKey->addKey(newPasswordKey);
|
||||
} else if (!removePassword && !currentPasswordKey.isNull()) {
|
||||
newDatabaseKey->addKey(currentPasswordKey);
|
||||
}
|
||||
|
||||
if (updateKeyFile) {
|
||||
QSharedPointer<FileKey> newFileKey = QSharedPointer<FileKey>::create();
|
||||
QString errorMessage;
|
||||
if (!Utils::loadFileKey(newFileKeyPath, newFileKey)) {
|
||||
err << QObject::tr("Loading the new key file failed: %1").arg(errorMessage) << endl;
|
||||
return {};
|
||||
}
|
||||
newDatabaseKey->addKey(newFileKey);
|
||||
} else if (!removeKeyFile && !currentFileKey.isNull()) {
|
||||
newDatabaseKey->addKey(currentFileKey);
|
||||
}
|
||||
|
||||
// This is a sanity check to make sure that this function is not used if
|
||||
// new key types are introduced. Otherwise, those key types would be
|
||||
// silently removed from the database.
|
||||
for (const QSharedPointer<Key>& key : database->key()->keys()) {
|
||||
if (key->uuid() != PasswordKey::UUID && key->uuid() != FileKey::UUID) {
|
||||
err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
for (const QSharedPointer<ChallengeResponseKey>& key : database->key()->challengeResponseKeys()) {
|
||||
if (key->uuid() != ChallengeResponseKey::UUID) {
|
||||
err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentChallengeResponseKey.isNull()) {
|
||||
newDatabaseKey->addChallengeResponseKey(currentChallengeResponseKey);
|
||||
}
|
||||
|
||||
if (newDatabaseKey->keys().isEmpty() && newDatabaseKey->challengeResponseKeys().isEmpty()) {
|
||||
err << QObject::tr("Cannot remove all the keys from a database.") << endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
return newDatabaseKey;
|
||||
}
|
||||
41
src/cli/DatabaseEdit.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_DATABASEEDIT_H
|
||||
#define KEEPASSXC_DATABASEEDIT_H
|
||||
|
||||
#include "DatabaseCommand.h"
|
||||
|
||||
class DatabaseEdit : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
DatabaseEdit();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
|
||||
static const QCommandLineOption UnsetKeyFileOption;
|
||||
static const QCommandLineOption UnsetPasswordOption;
|
||||
|
||||
private:
|
||||
QSharedPointer<CompositeKey> getNewDatabaseKey(QSharedPointer<Database> database,
|
||||
bool updatePassword,
|
||||
bool removePassword,
|
||||
QString newFileKeyPath,
|
||||
bool removeKeyFile);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_DATABASEEDIT_H
|
||||
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Info.h"
|
||||
#include "DatabaseInfo.h"
|
||||
|
||||
#include "Utils.h"
|
||||
#include "core/DatabaseStats.h"
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
Info::Info()
|
||||
DatabaseInfo::DatabaseInfo()
|
||||
{
|
||||
name = QString("db-info");
|
||||
description = QObject::tr("Show a database's information.");
|
||||
}
|
||||
|
||||
int Info::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
|
||||
int DatabaseInfo::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
|
||||
{
|
||||
auto& out = Utils::STDOUT;
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_INFO_H
|
||||
#define KEEPASSXC_INFO_H
|
||||
#ifndef KEEPASSXC_DATABASEINFO_H
|
||||
#define KEEPASSXC_DATABASEINFO_H
|
||||
|
||||
#include "DatabaseCommand.h"
|
||||
|
||||
class Info : public DatabaseCommand
|
||||
class DatabaseInfo : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
Info();
|
||||
DatabaseInfo();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_INFO_H
|
||||
#endif // KEEPASSXC_DATABASEINFO_H
|
||||
@@ -41,7 +41,7 @@ static void estimate(const char* pwd, bool advanced)
|
||||
{
|
||||
auto& out = Utils::STDOUT;
|
||||
|
||||
int len = static_cast<int>(strlen(pwd));
|
||||
auto len = static_cast<int>(strlen(pwd));
|
||||
if (!advanced) {
|
||||
const auto e = PasswordHealth(pwd).entropy();
|
||||
// clang-format off
|
||||
|
||||
@@ -98,7 +98,7 @@ QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QComm
|
||||
passwordGenerator->setLength(passwordLength.toInt());
|
||||
}
|
||||
|
||||
PasswordGenerator::CharClasses classes = 0x0;
|
||||
PasswordGenerator::CharClasses classes;
|
||||
|
||||
if (parser->isSet(Generate::LowerCaseOption)) {
|
||||
classes |= PasswordGenerator::LowerLetters;
|
||||
@@ -116,7 +116,7 @@ QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QComm
|
||||
classes |= PasswordGenerator::EASCII;
|
||||
}
|
||||
|
||||
PasswordGenerator::GeneratorFlags flags = 0x0;
|
||||
PasswordGenerator::GeneratorFlags flags;
|
||||
|
||||
if (parser->isSet(Generate::ExcludeSimilarCharsOption)) {
|
||||
flags |= PasswordGenerator::ExcludeLookAlike;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "Import.h"
|
||||
|
||||
#include "Create.h"
|
||||
#include "DatabaseCreate.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
@@ -40,9 +40,10 @@ Import::Import()
|
||||
description = QObject::tr("Import the contents of an XML database.");
|
||||
positionalArguments.append({QString("xml"), QObject::tr("Path of the XML database export."), QString("")});
|
||||
positionalArguments.append({QString("database"), QObject::tr("Path of the new database."), QString("")});
|
||||
options.append(Create::SetKeyFileOption);
|
||||
options.append(Create::SetPasswordOption);
|
||||
options.append(Create::DecryptionTimeOption);
|
||||
options.append(DatabaseCreate::SetKeyFileOption);
|
||||
options.append(DatabaseCreate::SetKeyFileShortOption);
|
||||
options.append(DatabaseCreate::SetPasswordOption);
|
||||
options.append(DatabaseCreate::DecryptionTimeOption);
|
||||
}
|
||||
|
||||
int Import::execute(const QStringList& arguments)
|
||||
@@ -64,7 +65,7 @@ int Import::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QSharedPointer<Database> db = Create::initializeDatabaseFromOptions(parser);
|
||||
QSharedPointer<Database> db = DatabaseCreate::initializeDatabaseFromOptions(parser);
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ class Move : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
Move();
|
||||
~Move();
|
||||
~Move() override;
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_MOVE_H
|
||||
|
||||
@@ -25,7 +25,7 @@ class Remove : public DatabaseCommand
|
||||
public:
|
||||
Remove();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_REMOVE_H
|
||||
|
||||
@@ -24,9 +24,9 @@ class RemoveGroup : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
RemoveGroup();
|
||||
~RemoveGroup();
|
||||
~RemoveGroup() override;
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_REMOVEGROUP_H
|
||||
|
||||
@@ -32,6 +32,9 @@ const QCommandLineOption Show::ProtectedAttributesOption =
|
||||
<< "show-protected",
|
||||
QObject::tr("Show the protected attributes in clear text."));
|
||||
|
||||
const QCommandLineOption Show::AllAttributesOption =
|
||||
QCommandLineOption(QStringList() << "all", QObject::tr("Show all the attributes of the entry."));
|
||||
|
||||
const QCommandLineOption Show::AttachmentsOption =
|
||||
QCommandLineOption(QStringList() << "show-attachments", QObject::tr("Show the attachments of the entry."));
|
||||
|
||||
@@ -51,6 +54,7 @@ Show::Show()
|
||||
options.append(Show::TotpOption);
|
||||
options.append(Show::AttributesOption);
|
||||
options.append(Show::ProtectedAttributesOption);
|
||||
options.append(Show::AllAttributesOption);
|
||||
options.append(Show::AttachmentsOption);
|
||||
positionalArguments.append({QString("entry"), QObject::tr("Name of the entry to show."), QString("")});
|
||||
}
|
||||
@@ -64,6 +68,7 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
const QString& entryPath = args.at(1);
|
||||
bool showTotp = parser->isSet(Show::TotpOption);
|
||||
bool showProtectedAttributes = parser->isSet(Show::ProtectedAttributesOption);
|
||||
bool showAllAttributes = parser->isSet(Show::AllAttributesOption);
|
||||
QStringList attributes = parser->values(Show::AttributesOption);
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
@@ -77,15 +82,41 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If no attributes specified, output the default attribute set.
|
||||
bool showDefaultAttributes = attributes.isEmpty() && !showTotp;
|
||||
if (showDefaultAttributes) {
|
||||
bool attributesWereSpecified = true;
|
||||
if (showAllAttributes) {
|
||||
attributesWereSpecified = false;
|
||||
attributes = EntryAttributes::DefaultAttributes;
|
||||
for (QString fieldName : Utils::EntryFieldNames) {
|
||||
attributes.append(fieldName);
|
||||
}
|
||||
// Adding the custom attributes after the default attributes so that
|
||||
// the default attributes are always shown first.
|
||||
for (QString attributeName : entry->attributes()->keys()) {
|
||||
if (EntryAttributes::DefaultAttributes.contains(attributeName)) {
|
||||
continue;
|
||||
}
|
||||
attributes.append(attributeName);
|
||||
}
|
||||
} else if (attributes.isEmpty() && !showTotp) {
|
||||
// If no attributes are specified, output the default attribute set.
|
||||
attributesWereSpecified = false;
|
||||
attributes = EntryAttributes::DefaultAttributes;
|
||||
for (QString fieldName : Utils::EntryFieldNames) {
|
||||
attributes.append(fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the attributes and output them line-by-line.
|
||||
bool encounteredError = false;
|
||||
for (const QString& attributeName : asConst(attributes)) {
|
||||
if (Utils::EntryFieldNames.contains(attributeName)) {
|
||||
if (!attributesWereSpecified) {
|
||||
out << attributeName << ": ";
|
||||
}
|
||||
out << Utils::getTopLevelField(entry, attributeName) << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName);
|
||||
if (attrs.isEmpty()) {
|
||||
encounteredError = true;
|
||||
@@ -99,10 +130,10 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
continue;
|
||||
}
|
||||
QString canonicalName = attrs[0];
|
||||
if (showDefaultAttributes) {
|
||||
if (!attributesWereSpecified) {
|
||||
out << canonicalName << ": ";
|
||||
}
|
||||
if (entry->attributes()->isProtected(canonicalName) && showDefaultAttributes && !showProtectedAttributes) {
|
||||
if (entry->attributes()->isProtected(canonicalName) && !attributesWereSpecified && !showProtectedAttributes) {
|
||||
out << "PROTECTED" << endl;
|
||||
} else {
|
||||
out << entry->resolveMultiplePlaceholders(entry->attributes()->value(canonicalName)) << endl;
|
||||
|
||||
@@ -25,9 +25,10 @@ class Show : public DatabaseCommand
|
||||
public:
|
||||
Show();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
|
||||
static const QCommandLineOption TotpOption;
|
||||
static const QCommandLineOption AllAttributesOption;
|
||||
static const QCommandLineOption AttributesOption;
|
||||
static const QCommandLineOption ProtectedAttributesOption;
|
||||
static const QCommandLineOption AttachmentsOption;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Utils.h"
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/EntryAttributes.h"
|
||||
#include "keys/FileKey.h"
|
||||
#ifdef WITH_XC_YUBIKEY
|
||||
@@ -62,6 +63,13 @@ namespace Utils
|
||||
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
||||
#endif
|
||||
DEVNULL.setDevice(fd);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// On Windows, we ask via keepassxc-cli.exe.manifest to use UTF-8,
|
||||
// but the console code-page isn't automatically changed to match.
|
||||
SetConsoleCP(GetACP());
|
||||
SetConsoleOutputCP(GetACP());
|
||||
#endif
|
||||
}
|
||||
|
||||
void setStdinEcho(bool enable = true)
|
||||
@@ -368,6 +376,17 @@ namespace Utils
|
||||
return result;
|
||||
}
|
||||
|
||||
QString getTopLevelField(const Entry* entry, const QString& fieldName)
|
||||
{
|
||||
if (fieldName == UuidFieldName) {
|
||||
return entry->uuid().toString();
|
||||
}
|
||||
if (fieldName == TagsFieldName) {
|
||||
return entry->tags();
|
||||
}
|
||||
return QString("");
|
||||
}
|
||||
|
||||
QStringList findAttributes(const EntryAttributes& attributes, const QString& name)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
class CompositeKey;
|
||||
class Database;
|
||||
class Entry;
|
||||
class EntryAttributes;
|
||||
class FileKey;
|
||||
class PasswordKey;
|
||||
@@ -33,6 +34,10 @@ namespace Utils
|
||||
extern QTextStream STDIN;
|
||||
extern QTextStream DEVNULL;
|
||||
|
||||
static const QString UuidFieldName = "Uuid";
|
||||
static const QString TagsFieldName = "Tags";
|
||||
static const QStringList EntryFieldNames(QStringList() << UuidFieldName << TagsFieldName);
|
||||
|
||||
void setDefaultTextStreams();
|
||||
|
||||
void setStdinEcho(bool enable);
|
||||
@@ -55,6 +60,10 @@ namespace Utils
|
||||
* (case-insensitive).
|
||||
*/
|
||||
QStringList findAttributes(const EntryAttributes& attributes, const QString& name);
|
||||
/**
|
||||
* Get the value of a top-level Entry field using its name.
|
||||
*/
|
||||
QString getTopLevelField(const Entry* entry, const QString& fieldName);
|
||||
}; // namespace Utils
|
||||
|
||||
#endif // KEEPASSXC_UTILS_H
|
||||
|
||||
8
src/cli/keepassxc-cli.exe.manifest
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<application>
|
||||
<windowsSettings>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -21,6 +21,7 @@
|
||||
#cmakedefine WITH_XC_UPDATECHECK
|
||||
#cmakedefine WITH_XC_FDOSECRETS
|
||||
#cmakedefine WITH_XC_DOCS
|
||||
#cmakedefine WITH_XC_X11
|
||||
|
||||
#cmakedefine KEEPASSXC_BUILD_TYPE "@KEEPASSXC_BUILD_TYPE@"
|
||||
#cmakedefine KEEPASSXC_BUILD_TYPE_RELEASE
|
||||
@@ -37,4 +38,13 @@
|
||||
#cmakedefine HAVE_RLIMIT_CORE 1
|
||||
#cmakedefine HAVE_PT_DENY_ATTACH 1
|
||||
|
||||
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_BIOMETRY()
|
||||
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_TOUCH_ID()
|
||||
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_WATCH()
|
||||
|
||||
#define XC_COMPILER_SUPPORT(X) XC_COMPILER_SUPPORT_PRIVATE_DEFINITION_##X()
|
||||
#define XC_COMPILER_SUPPORT_PRIVATE_DEFINITION_APPLE_BIOMETRY() XC_APPLE_COMPILER_SUPPORT_BIOMETRY()
|
||||
#define XC_COMPILER_SUPPORT_PRIVATE_DEFINITION_TOUCH_ID() XC_APPLE_COMPILER_SUPPORT_TOUCH_ID()
|
||||
#define XC_COMPILER_SUPPORT_PRIVATE_DEFINITION_WATCH_UNLOCK() XC_APPLE_COMPILER_SUPPORT_WATCH()
|
||||
|
||||
#endif // KEEPASSX_CONFIG_KEEPASSX_H
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#elif defined(HAVE_MALLOC_H)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) && !defined(__cpp_sized_deallocation)
|
||||
|
||||
@@ -89,7 +89,9 @@ namespace Bootstrap
|
||||
success = success && (setrlimit(RLIMIT_CORE, &limit) == 0);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PR_SET_DUMPABLE)
|
||||
// NOTE: Dumps cannot be disabled for snap builds as it prevents desktop portals from working
|
||||
// See https://github.com/keepassxreboot/keepassxc/issues/7607#issuecomment-1109005206
|
||||
#if defined(HAVE_PR_SET_DUMPABLE) && !defined(KEEPASSXC_DIST_SNAP)
|
||||
success = success && (prctl(PR_SET_DUMPABLE, 0) == 0);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||
{Config::GlobalAutoTypeRetypeTime,{QS("GlobalAutoTypeRetypeTime"), Roaming, 15}},
|
||||
{Config::FaviconDownloadTimeout,{QS("FaviconDownloadTimeout"), Roaming, 10}},
|
||||
{Config::UpdateCheckMessageShown,{QS("UpdateCheckMessageShown"), Roaming, false}},
|
||||
{Config::DefaultDatabaseFileName,{QS("DefaultDatabaseFileName"), Roaming, {}}},
|
||||
|
||||
{Config::LastDatabases, {QS("LastDatabases"), Local, {}}},
|
||||
{Config::LastKeyFiles, {QS("LastKeyFiles"), Local, {}}},
|
||||
@@ -104,6 +105,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||
{Config::GUI_HideUsernames, {QS("GUI/HideUsernames"), Roaming, false}},
|
||||
{Config::GUI_HidePasswords, {QS("GUI/HidePasswords"), Roaming, true}},
|
||||
{Config::GUI_AdvancedSettings, {QS("GUI/AdvancedSettings"), Roaming, false}},
|
||||
{Config::GUI_ColorPasswords, {QS("GUI/ColorPasswords"), Roaming, false}},
|
||||
{Config::GUI_MonospaceNotes, {QS("GUI/MonospaceNotes"), Roaming, false}},
|
||||
{Config::GUI_ApplicationTheme, {QS("GUI/ApplicationTheme"), Roaming, QS("auto")}},
|
||||
{Config::GUI_CompactMode, {QS("GUI/CompactMode"), Roaming, false}},
|
||||
@@ -480,8 +482,19 @@ void Config::init(const QString& configFileName, const QString& localConfigFileN
|
||||
QPair<QString, QString> Config::defaultConfigFiles()
|
||||
{
|
||||
// Check if we are running in portable mode, if so store the config files local to the app
|
||||
#ifdef Q_OS_WIN
|
||||
// Enable QFileInfo::isWritable check on Windows
|
||||
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
|
||||
qt_ntfs_permission_lookup++;
|
||||
#endif
|
||||
auto portablePath = QCoreApplication::applicationDirPath().append("/%1");
|
||||
if (QFile::exists(portablePath.arg(".portable"))) {
|
||||
auto portableFile = portablePath.arg(".portable");
|
||||
bool isPortable = QFile::exists(portableFile) && QFileInfo(portableFile).isWritable();
|
||||
#ifdef Q_OS_WIN
|
||||
qt_ntfs_permission_lookup--;
|
||||
#endif
|
||||
|
||||
if (isPortable) {
|
||||
return {portablePath.arg("config/keepassxc.ini"), portablePath.arg("config/keepassxc_local.ini")};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
GlobalAutoTypeRetypeTime,
|
||||
FaviconDownloadTimeout,
|
||||
UpdateCheckMessageShown,
|
||||
DefaultDatabaseFileName,
|
||||
|
||||
LastDatabases,
|
||||
LastKeyFiles,
|
||||
@@ -85,6 +86,7 @@ public:
|
||||
GUI_HideUsernames,
|
||||
GUI_HidePasswords,
|
||||
GUI_AdvancedSettings,
|
||||
GUI_ColorPasswords,
|
||||
GUI_MonospaceNotes,
|
||||
GUI_ApplicationTheme,
|
||||
GUI_CompactMode,
|
||||
|
||||
@@ -253,9 +253,6 @@ bool Database::saveAs(const QString& filePath, SaveAction action, const QString&
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent destructive operations while saving
|
||||
QMutexLocker locker(&m_saveMutex);
|
||||
|
||||
if (filePath == m_data.filePath) {
|
||||
// Fail-safe check to make sure we don't overwrite underlying file changes
|
||||
// that have not yet triggered a file reload/merge operation.
|
||||
@@ -270,6 +267,9 @@ bool Database::saveAs(const QString& filePath, SaveAction action, const QString&
|
||||
// Clear read-only flag
|
||||
m_fileWatcher->stop();
|
||||
|
||||
// Prevent destructive operations while saving
|
||||
QMutexLocker locker(&m_saveMutex);
|
||||
|
||||
QFileInfo fileInfo(filePath);
|
||||
auto realFilePath = fileInfo.exists() ? fileInfo.canonicalFilePath() : fileInfo.absoluteFilePath();
|
||||
bool isNewFile = !QFile::exists(realFilePath);
|
||||
@@ -463,6 +463,7 @@ bool Database::import(const QString& xmlExportPath, QString* error)
|
||||
void Database::releaseData()
|
||||
{
|
||||
// Prevent data release while saving
|
||||
Q_ASSERT(!isSaving());
|
||||
QMutexLocker locker(&m_saveMutex);
|
||||
|
||||
if (m_modified) {
|
||||
@@ -701,8 +702,8 @@ void Database::updateTagList()
|
||||
// Search groups recursively looking for tags
|
||||
// Use a set to prevent adding duplicates
|
||||
QSet<QString> tagSet;
|
||||
for (const auto group : m_rootGroup->groupsRecursive(true)) {
|
||||
for (const auto entry : group->entries()) {
|
||||
for (auto entry : m_rootGroup->entriesRecursive()) {
|
||||
if (!entry->isRecycled()) {
|
||||
for (auto tag : entry->tagList()) {
|
||||
tagSet.insert(tag);
|
||||
}
|
||||
@@ -714,6 +715,17 @@ void Database::updateTagList()
|
||||
emit tagListUpdated();
|
||||
}
|
||||
|
||||
void Database::removeTag(const QString& tag)
|
||||
{
|
||||
if (!m_rootGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto entry : m_rootGroup->entriesRecursive()) {
|
||||
entry->removeTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
const QUuid& Database::cipher() const
|
||||
{
|
||||
return m_data.cipher;
|
||||
|
||||
@@ -129,6 +129,7 @@ public:
|
||||
|
||||
const QStringList& commonUsernames() const;
|
||||
const QStringList& tagList() const;
|
||||
void removeTag(const QString& tag);
|
||||
|
||||
QSharedPointer<const CompositeKey> key() const;
|
||||
bool setKey(const QSharedPointer<const CompositeKey>& key,
|
||||
|
||||
@@ -187,15 +187,12 @@ QString Entry::overrideUrl() const
|
||||
|
||||
QString Entry::tags() const
|
||||
{
|
||||
return m_data.tags;
|
||||
return m_data.tags.join(",");
|
||||
}
|
||||
|
||||
QStringList Entry::tagList() const
|
||||
{
|
||||
static QRegExp rx("(\\,|\\t|\\;)");
|
||||
auto taglist = tags().split(rx, QString::SkipEmptyParts);
|
||||
std::sort(taglist.begin(), taglist.end());
|
||||
return taglist;
|
||||
return m_data.tags;
|
||||
}
|
||||
|
||||
const TimeInfo& Entry::timeInfo() const
|
||||
@@ -654,7 +651,42 @@ void Entry::setOverrideUrl(const QString& url)
|
||||
|
||||
void Entry::setTags(const QString& tags)
|
||||
{
|
||||
set(m_data.tags, tags);
|
||||
static QRegExp rx("(\\,|\\t|\\;)");
|
||||
auto taglist = tags.split(rx, QString::SkipEmptyParts);
|
||||
// Trim whitespace before/after tag text
|
||||
for (auto itr = taglist.begin(); itr != taglist.end(); ++itr) {
|
||||
*itr = itr->trimmed();
|
||||
}
|
||||
// Remove duplicates
|
||||
auto tagSet = QSet<QString>::fromList(taglist);
|
||||
taglist = tagSet.toList();
|
||||
// Sort alphabetically
|
||||
taglist.sort();
|
||||
set(m_data.tags, taglist);
|
||||
}
|
||||
|
||||
void Entry::addTag(const QString& tag)
|
||||
{
|
||||
auto cleanTag = tag.trimmed();
|
||||
cleanTag.remove(QRegExp("(\\,|\\t|\\;)"));
|
||||
|
||||
auto taglist = m_data.tags;
|
||||
if (!taglist.contains(cleanTag)) {
|
||||
taglist.append(cleanTag);
|
||||
taglist.sort();
|
||||
set(m_data.tags, taglist);
|
||||
}
|
||||
}
|
||||
|
||||
void Entry::removeTag(const QString& tag)
|
||||
{
|
||||
auto cleanTag = tag.trimmed();
|
||||
cleanTag.remove(QRegExp("(\\,|\\t|\\;)"));
|
||||
|
||||
auto taglist = m_data.tags;
|
||||
if (taglist.removeAll(tag) > 0) {
|
||||
set(m_data.tags, taglist);
|
||||
}
|
||||
}
|
||||
|
||||
void Entry::setTimeInfo(const TimeInfo& timeInfo)
|
||||
@@ -801,7 +833,6 @@ void Entry::truncateHistory()
|
||||
int histMaxSize = db->metadata()->historyMaxSize();
|
||||
if (histMaxSize > -1) {
|
||||
int size = 0;
|
||||
QSet<QByteArray> foundAttachments = attachments()->values();
|
||||
|
||||
QMutableListIterator<Entry*> i(m_history);
|
||||
i.toBack();
|
||||
@@ -811,7 +842,6 @@ void Entry::truncateHistory()
|
||||
// don't calculate size if it's already above the maximum
|
||||
if (size <= histMaxSize) {
|
||||
size += historyItem->size();
|
||||
foundAttachments += historyItem->attachments()->values();
|
||||
}
|
||||
|
||||
if (size > histMaxSize) {
|
||||
@@ -865,7 +895,7 @@ bool Entry::equals(const Entry* other, CompareItemOptions options) const
|
||||
|
||||
Entry* Entry::clone(CloneFlags flags) const
|
||||
{
|
||||
Entry* entry = new Entry();
|
||||
auto entry = new Entry();
|
||||
entry->setUpdateTimeinfo(false);
|
||||
if (flags & CloneNewUuid) {
|
||||
entry->m_uuid = QUuid::createUuid();
|
||||
|
||||
@@ -58,7 +58,7 @@ struct EntryData
|
||||
QString foregroundColor;
|
||||
QString backgroundColor;
|
||||
QString overrideUrl;
|
||||
QString tags;
|
||||
QStringList tags;
|
||||
bool autoTypeEnabled;
|
||||
int autoTypeObfuscation;
|
||||
QString defaultAutoTypeSequence;
|
||||
@@ -79,7 +79,7 @@ class Entry : public ModifiableObject
|
||||
|
||||
public:
|
||||
Entry();
|
||||
~Entry();
|
||||
~Entry() override;
|
||||
const QUuid& uuid() const;
|
||||
const QString uuidToHex() const;
|
||||
int iconNumber() const;
|
||||
@@ -158,6 +158,9 @@ public:
|
||||
void setPreviousParentGroup(const Group* group);
|
||||
void setPreviousParentGroupUuid(const QUuid& uuid);
|
||||
|
||||
void addTag(const QString& tag);
|
||||
void removeTag(const QString& tag);
|
||||
|
||||
QList<Entry*> historyItems();
|
||||
const QList<Entry*>& historyItems() const;
|
||||
void addHistoryItem(Entry* entry);
|
||||
|
||||
@@ -34,7 +34,7 @@ class EntryAttachments : public ModifiableObject
|
||||
|
||||
public:
|
||||
explicit EntryAttachments(QObject* parent = nullptr);
|
||||
virtual ~EntryAttachments();
|
||||
~EntryAttachments() override;
|
||||
QList<QString> keys() const;
|
||||
bool hasKey(const QString& key) const;
|
||||
QSet<QByteArray> values() const;
|
||||
|
||||
@@ -30,7 +30,6 @@ const QString EntryAttributes::URLKey = "URL";
|
||||
const QString EntryAttributes::NotesKey = "Notes";
|
||||
const QStringList EntryAttributes::DefaultAttributes(QStringList()
|
||||
<< TitleKey << UserNameKey << PasswordKey << URLKey << NotesKey);
|
||||
|
||||
const QString EntryAttributes::WantedFieldGroupName = "WantedField";
|
||||
const QString EntryAttributes::SearchInGroupName = "SearchIn";
|
||||
const QString EntryAttributes::SearchTextGroupName = "SearchText";
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
EntrySearcher::EntrySearcher(bool caseSensitive, bool skipProtected)
|
||||
: m_caseSensitive(caseSensitive)
|
||||
, m_skipProtected(skipProtected)
|
||||
, m_termParser(R"re(([-!*+]+)?(?:(\w*):)?(?:(?=")"((?:[^"\\]|\\.)*)"|([^ ]*))( |$))re")
|
||||
// Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string
|
||||
{
|
||||
}
|
||||
|
||||
@@ -197,11 +195,16 @@ bool EntrySearcher::searchEntryImpl(const Entry* entry)
|
||||
}
|
||||
break;
|
||||
case Field::Tag:
|
||||
found = term.regex.match(entry->tags()).hasMatch();
|
||||
found = entry->tagList().indexOf(term.regex) != -1;
|
||||
break;
|
||||
case Field::Is:
|
||||
if (term.word.compare("expired", Qt::CaseInsensitive) == 0) {
|
||||
found = entry->isExpired();
|
||||
if (term.word.startsWith("expired", Qt::CaseInsensitive)) {
|
||||
auto days = 0;
|
||||
auto parts = term.word.split("-", QString::SkipEmptyParts);
|
||||
if (parts.length() >= 2) {
|
||||
days = parts[1].toInt();
|
||||
}
|
||||
found = entry->willExpireInDays(days) && !entry->isRecycled();
|
||||
break;
|
||||
} else if (term.word.compare("weak", Qt::CaseInsensitive) == 0) {
|
||||
if (!entry->excludeFromReports() && !entry->password().isEmpty() && !entry->isExpired()) {
|
||||
@@ -220,8 +223,7 @@ bool EntrySearcher::searchEntryImpl(const Entry* entry)
|
||||
found = term.regex.match(entry->resolvePlaceholder(entry->title())).hasMatch()
|
||||
|| term.regex.match(entry->resolvePlaceholder(entry->username())).hasMatch()
|
||||
|| term.regex.match(entry->resolvePlaceholder(entry->url())).hasMatch()
|
||||
|| term.regex.match(entry->resolvePlaceholder(entry->tags())).hasMatch()
|
||||
|| term.regex.match(entry->notes()).hasMatch();
|
||||
|| entry->tagList().indexOf(term.regex) != -1 || term.regex.match(entry->notes()).hasMatch();
|
||||
}
|
||||
|
||||
// negate the result if exclude:
|
||||
@@ -246,23 +248,26 @@ void EntrySearcher::parseSearchTerms(const QString& searchString)
|
||||
{QStringLiteral("notes"), Field::Notes},
|
||||
{QStringLiteral("pw"), Field::Password},
|
||||
{QStringLiteral("password"), Field::Password},
|
||||
{QStringLiteral("title"), Field::Title},
|
||||
{QStringLiteral("t"), Field::Title},
|
||||
{QStringLiteral("u"), Field::Username}, // u: stands for username rather than url
|
||||
{QStringLiteral("title"), Field::Title}, // title before tag to capture t:<word>
|
||||
{QStringLiteral("username"), Field::Username}, // username before url to capture u:<word>
|
||||
{QStringLiteral("url"), Field::Url},
|
||||
{QStringLiteral("username"), Field::Username},
|
||||
{QStringLiteral("group"), Field::Group},
|
||||
{QStringLiteral("tag"), Field::Tag},
|
||||
{QStringLiteral("is"), Field::Is}};
|
||||
|
||||
// Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string
|
||||
static QRegularExpression termParser(R"re(([-!*+]+)?(?:(\w*):)?(?:(?=")"((?:[^"\\]|\\.)*)"|([^ ]*))( |$))re");
|
||||
|
||||
m_searchTerms.clear();
|
||||
auto results = m_termParser.globalMatch(searchString);
|
||||
auto results = termParser.globalMatch(searchString);
|
||||
while (results.hasNext()) {
|
||||
auto result = results.next();
|
||||
SearchTerm term{};
|
||||
|
||||
// Quoted string group
|
||||
term.word = result.captured(3);
|
||||
// Unescape quotes
|
||||
term.word.replace("\\\"", "\"");
|
||||
|
||||
// If empty, use the unquoted string group
|
||||
if (term.word.isEmpty()) {
|
||||
|
||||
@@ -71,7 +71,6 @@ private:
|
||||
|
||||
bool m_caseSensitive;
|
||||
bool m_skipProtected;
|
||||
QRegularExpression m_termParser;
|
||||
QList<SearchTerm> m_searchTerms;
|
||||
|
||||
friend class TestEntrySearcher;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef KEEPASSX_EXPORTER_H
|
||||
#define KEEPASSX_EXPORTER_H
|
||||
|
||||
class Database;
|
||||
class Group;
|
||||
|
||||
class Exporter
|
||||
{
|
||||
public:
|
||||
virtual Database* exportGroup(Group* group) = 0;
|
||||
virtual ~Exporter()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_EXPORTER_H
|
||||
@@ -17,13 +17,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_GLOBAL_H
|
||||
#define KEEPASSX_GLOBAL_H
|
||||
#ifndef KEEPASSXC_GLOBAL_H
|
||||
#define KEEPASSXC_GLOBAL_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#if defined(KEEPASSX_BUILDING_CORE)
|
||||
#if defined(KEEPASSXC_BUILDING_CORE)
|
||||
#define KEEPASSXC_EXPORT Q_DECL_IMPORT
|
||||
#else
|
||||
#define KEEPASSXC_EXPORT Q_DECL_EXPORT
|
||||
@@ -67,4 +67,4 @@ template <typename T> constexpr typename AddConst<T>::Type& asConst(T& t) noexce
|
||||
// prevent rvalue arguments:
|
||||
template <typename T> void asConst(const T&&) = delete;
|
||||
|
||||
#endif // KEEPASSX_GLOBAL_H
|
||||
#endif // KEEPASSXC_GLOBAL_H
|
||||
|
||||
@@ -890,7 +890,7 @@ Group* Group::findChildByName(const QString& name)
|
||||
*/
|
||||
Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags) const
|
||||
{
|
||||
Group* clonedGroup = new Group();
|
||||
auto clonedGroup = new Group();
|
||||
|
||||
clonedGroup->setUpdateTimeinfo(false);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
};
|
||||
|
||||
Group();
|
||||
~Group();
|
||||
~Group() override;
|
||||
|
||||
const QUuid& uuid() const;
|
||||
const QString uuidToHex() const;
|
||||
|
||||
@@ -37,7 +37,7 @@ signals:
|
||||
void inactivityDetected();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* watched, QEvent* event);
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void timeout();
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_LISTDELETER_H
|
||||
#define KEEPASSX_LISTDELETER_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
template <typename T> class ListDeleter
|
||||
{
|
||||
public:
|
||||
inline explicit ListDeleter(QList<T>* list)
|
||||
: m_list(list)
|
||||
{
|
||||
}
|
||||
inline ~ListDeleter()
|
||||
{
|
||||
qDeleteAll(*m_list);
|
||||
}
|
||||
|
||||
private:
|
||||
QList<T>* m_list;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_LISTDELETER_H
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QJsonDocument>
|
||||
|
||||
const int Metadata::DefaultHistoryMaxItems = 10;
|
||||
const int Metadata::DefaultHistoryMaxSize = 6 * 1024 * 1024;
|
||||
@@ -487,3 +488,26 @@ void Metadata::setSettingsChanged(const QDateTime& value)
|
||||
Q_ASSERT(value.timeSpec() == Qt::UTC);
|
||||
m_settingsChanged = value;
|
||||
}
|
||||
|
||||
void Metadata::addSavedSearch(const QString& name, const QString& searchtext)
|
||||
{
|
||||
auto searches = savedSearches();
|
||||
searches.insert(name, searchtext);
|
||||
auto json = QJsonDocument::fromVariant(searches);
|
||||
m_customData->set("KPXC_SavedSearch", json.toJson());
|
||||
}
|
||||
|
||||
void Metadata::deleteSavedSearch(const QString& name)
|
||||
{
|
||||
auto searches = savedSearches();
|
||||
searches.remove(name);
|
||||
auto json = QJsonDocument::fromVariant(searches);
|
||||
m_customData->set("KPXC_SavedSearch", json.toJson());
|
||||
}
|
||||
|
||||
QVariantMap Metadata::savedSearches()
|
||||
{
|
||||
auto searches = m_customData->value("KPXC_SavedSearch");
|
||||
auto json = QJsonDocument::fromJson(searches.toUtf8());
|
||||
return json.toVariant().toMap();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QUuid>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "core/CustomData.h"
|
||||
#include "core/Global.h"
|
||||
@@ -150,6 +151,9 @@ public:
|
||||
void setHistoryMaxItems(int value);
|
||||
void setHistoryMaxSize(int value);
|
||||
void setUpdateDatetime(bool value);
|
||||
void addSavedSearch(const QString& name, const QString& searchtext);
|
||||
void deleteSavedSearch(const QString& name);
|
||||
QVariantMap savedSearches();
|
||||
/*
|
||||
* Copy all attributes from other except:
|
||||
* - Group pointers/uuids
|
||||
|
||||