Compare commits

..

41 Commits
2.4.1 ... 2.4.2

Author SHA1 Message Date
Jonathan White
a775031fe9 Release 2.4.2
- Improve resilience against memory attacks - overwrite memory before free [#3020]
- Prevent infinite save loop when location is unavailable [#3026]
- Attempt to fix quitting application when shutdown or logout issued [#3199]
- Support merging database custom data [#3002]
- Fix opening URL's with non-http schemes [#3153]
- Fix data loss due to not reading all database attachments if duplicates exist [#3180]
- Fix entry context menu disabling when using keyboard navigation [#3199]
- Fix behaviors when canceling an entry edit [#3199]
- Fix processing of tray icon click and doubleclick [#3112]
- Update group in preview widget when focused [#3199]
- Prefer DuckDuckGo service over direct icon download (increases resolution) [#2996]
- Remove apply button in application settings [#3019]
- Use winqtdeploy on Windows to correct deployment issues [#3025]
- Don't mark entry edit as modified when attribute selection changes [#3041]
- Use console code page CP_UTF8 on Windows if supported [#3050]
- Snap: Fix locking database with session lock [#3046]
- Snap: Fix theming across Linux distributions [#3057]
- Snap: Use SNAP_USER_COMMON and SNAP_USER_DATA directories [#3131]
- KeeShare: Automatically enable WITH_XC_KEESHARE_SECURE if quazip is found [#3088]
- macOS: Fix toolbar text when in dark mode [#2998]
- macOS: Lock database on switching user [#3097]
- macOS: Fix global Auto-Type when the database is locked [#3138]
- Browser: Close popups when database is locked [#3093]
- Browser: Add tests [#3016]
- Browser: Don't create default group if custom group is enabled [#3127]
2019-05-31 16:03:47 -04:00
Jonathan White
3bc78f9946 Update translations 2019-05-31 16:03:35 -04:00
Jonathan White
9a3481e900 2.4.2 Changelog 2019-05-31 16:01:54 -04:00
Jonathan White
b90e9ee428 Fix behavior when saving after canceling entry edit
* Fixes #3141
* Clearing the entry edit widget prior to emitting the editFinished signal caused the widget to be marked modified and prevent new entries from being created. Use an explicit boolean to notify commit success.
* Don't clear password generator on canceling a cancel
* Don't discard changes if saving from a cancel produces an error
2019-05-31 08:22:38 -04:00
Jonathan White
c645e2e303 Set default Argon2 transform rounds to 10
* Fixes #2806
2019-05-31 08:22:38 -04:00
Jonathan White
a747886323 Exit when receiving OS Close Message when in tray
* Fixes #2692
* KeePassXC was ignoring OS close messages on shutdown or logoff when minimize to tray on close was enabled. This change causes a second close message (when KeePassXC is hidden to the tray) to actually exit the application.
2019-05-31 08:22:38 -04:00
Jonathan White
6f443ee9fc Fix argument parsing for OTP TOTP URL's
* Fixes #2915
2019-05-31 08:22:38 -04:00
Jonathan White
96b0ea45dd Update Group in Preview Widget when focused
* Fixes #3129
* Also fix out of bounds access when no entries are present in EntryView and up/down arrow pressed
2019-05-31 08:22:38 -04:00
Jonathan White
91283e7c76 Prevent context menu options from disabling with keyboard navigation
* Fixes #2838
* When navigating the entry context menu with up/down arrow the options would disable due to losing focus on the EntryView. This change preserves the "focus" during this event.
2019-05-31 08:22:38 -04:00
Jonathan White
3d5879b8ed Use Snap-Specific directories
* Fix native messaging to use user-specific common folder
* Open attachments in snap data folder
2019-05-30 15:32:26 -04:00
varjolintu
5341a8215c Browser Integration tests 2019-05-29 09:24:22 -04:00
Jonathan White
6d5c6c7d17 Read all database attachments even if duplicated
* Fixes #3048
* Certain programs that read/write KDBX4 files do not consolidate duplicate attachments into a single binary. This is against the KDBX4 specification. This change ensures KeePassXC will at least read the database in its entirety and not lose information. Upon saving the database in KeePassXC, the duplicate attachment binaries will be reduced to single binaries per the specification.
2019-05-25 15:48:19 -04:00
varjolintu
7ce6f9d3b1 Disable creating the default group 2019-05-19 00:22:11 -04:00
Jonathan White
ecaa4fd6ce Fix opening url's with non-http schema
* Fix #2427
* Changed the openUrl() function to use a QUrl object, which has the appropriate scheme set.
* Preview widget now passes url handling back to DatabaseWidget
2019-05-19 00:20:43 -04:00
Jonathan White
baa55d1597 Support Ubuntu Disco packages in snap 2019-05-16 18:03:29 -04:00
Jonathan White
63855346bf Fix global Auto-Type when database locked
* Store the currently active window right when the global keyboard shortcut is triggered
* Eliminate unnecessary window raise/lower and delays on macOS
* Remove duplicate addition of macutils symbols from mac Auto-Type plugin
* Fix tests to fake trigger a global autotype sequence
2019-05-12 12:37:50 -04:00
varjolintu
fed8a56098 Add option for returning expired credentials 2019-05-09 10:31:51 -04:00
Jonathan White
247b85fe69 Update INSTALL.md and cleanup CMakeLists.txt (#3074)
INSTALL.md
* Better organization of CMake options

CMakeLists.txt
* If WITH_XC_NETWORKING is disabled, also disable WITH_XC_UPDATECHECK
* Move KeeShare logic into KeeShare CMakeLists.txt
* Remove WITH_XC_KEESHARE_SECURE build option
* Attempt to find quazip, if found enable WITH_XC_KEESHARE_SECURE and build with secure container support
2019-05-07 12:56:55 -04:00
Jonathan White
faf7a2bbb3 Robust processing of tray icon triggers
* Support double click on tray icon to always toggle window to/from tray
* Single click on tray icon will bring window to front if in background, otherwise window is toggled
* Fixes #2956
2019-05-04 16:45:03 -04:00
varjolintu
ebe6649683 Lock database on switching user in macOS 2019-05-02 14:32:44 -04:00
Sami Vänttinen
e4eee897f9 Support Database Custom Data Merging (#3002)
* Introduce _LAST_MODIFIED custom data entry that stores the last modified datetime of the database's custom data entries
* Merge custom data from source database to target
* Modify tests to be aware of _LAST_MODIFIED entry
2019-05-01 18:35:08 -04:00
Janek Bevendorff
01a3d5b0ba Fix QuaZip find module on macOS and clean up code.
Finding libquazip failed on macOS due to path differences.
This patch also cleans up the find module's code, aligns it
with the coding style of the other CMake files and removes
clutter that is not needed for KeePassXC such as non-Msys
builds on Windows.
2019-05-01 18:25:32 -04:00
varjolintu
0f8d2986af Close popups when database is locked 2019-05-01 18:05:19 -04:00
Oirio Joshi
a2caa31eca Snap: fix theming (#3057)
Use gtk3 file chooser dialogs, mouse coursor theme if available and force fallback icon theme, fixes issue #2966
2019-04-25 10:39:06 -04:00
Janek Bevendorff
d3a53a702e Set console code page to CP_UTF8 on Windows if supported. (#3050)
Previously, we enforced code page 850 for all console input and output,
which breaks with non-western scripts. Since more recent Windows shells
are able to display Unicode properly, this patch now enforces UTF-8 and
falls back to code page 850 only if UTF-8 is unsupported.

Non-Windows systems default to UTF-8, but can override the codec
by setting the LANG environment variable to something other than C.

Resolves #3049.
2019-04-25 09:28:48 +02:00
Janek Bevendorff
13eb1c0bbd Improve resilience against memory attacks
To reduce residual fragments of secret data in memory after
deallocation, this patch replaces the global delete operator with a
version that zeros out previously allocated memory. It makes use of
the new C++14 sized deallocation, but provides an unsized fallback
with platform-specific size deductions.

This change is only a minor mitigation and cannot protect against
buffer reallocations by the operating system or non-C++ libraries.
Thus, we still cannot guarantee all memory to be wiped after free.

As a further improvement, this patch uses libgcrypt and libsodium
to write long-lived master key component hashes into a secure
memory area and wipe it afterwards.

The patch also fixes compiler flags not being set properly on macOS.
2019-04-21 09:39:28 -04:00
joshirio
c7898fdeee Snap: fix session database locking 2019-04-20 14:48:38 -04:00
Jonathan White
53796a216e Windows: use winqtdeploy instead of DeplyQt4 from CMake (#3025)
* Ensure Qt dlls find plugins in bundled directory
* Reduce complexity of deployment code
* Standardize use of CMAKE_BUILD_TYPE_LOWER for more robust comparisons

Fixes #3023. Fixes part of #1535.
2019-04-20 18:12:00 +02:00
Jonathan White
219a0f40ff Prevent infinite save loop when location is unavailable (#3026)
This bug impacted unsafe saves. When auto save after every change was enabled, an
unsafe save to a location that has become unavailable (eg, dismounted veracrypt drive),
the database modified signal would continually activate a save action that failed.
This caused an infinite loop.

When auto-save on exit was enabled, the database tab and the application itself refused to
close if saving failed for whatever reason.

The fixes in this commit prevent both of these scenarios from occurring.
2019-04-20 18:10:07 +02:00
Jonathan White
bbe7e8a45a Use QLocale for translation search instead of custom method (#3035)
Use built-in facilities of Qt to traverse QLocale::uiLanguages() to find a valid "most preferred"
language, but still respect user's choice in the application settings.
Fixes #3030. Fixes #1924.
2019-04-20 18:00:45 +02:00
Jonathan White
acd6847cd4 Support Ctrl+Enter shortcut on all dialogs with QPushButtonBox (#3039)
* Remove specific action from EditEntryWidget
* Implement key handling at the lowest level
* Fix #3036
2019-04-20 17:54:25 +02:00
Jonathan White
cb442f8c6e Don't mark entry edit as modified when attribute selection changes (#3041)
When selecting another attribute in the advanced tab, do not mark the entry as modified
(nothing was changed). Also do not mark as modified when the notes checkbox is
checked/unchecked (doesn't change entry). Fixes #3013.
2019-04-20 17:51:15 +02:00
Jonathan White
7d46ce3de1 Correct CLI help messages on Windows
* Prevents keepassxc-cli.exe -> keepassxc-cli show.exe
* Fixes #3032
2019-04-20 11:03:06 -04:00
Jonathan White
5b28610c6a Remove apply button from application settings 2019-04-17 08:57:51 -04:00
Jonathan White
42d34a1999 Made changes to streamline icon downloading process 2019-04-16 23:02:46 -04:00
Xaver Maierhofer
12e020b7c2 Add option to prefer DuckDuckGo 2019-04-16 23:02:46 -04:00
Allen Wild
7bd079d48d add Lock Databases option to tray icon menu
This is useful when keepassxc is minimized/hidden to the tray, and all
the plumbing is already in place from the lock icon button in the main
window UI.
2019-04-16 21:09:51 -04:00
Jonathan White
a0c84dbd0d Bump version numbers to 2.4.2 2019-04-16 21:09:42 -04:00
Christian Kieschnick
7067a4d004 Fix canceling cancel request in edited group
In case of a modified group, pressing cancel in the confirmation dialog
of cancel led to discarding the changes instead of returning to the edit widget.
2019-04-16 10:07:46 -04:00
Jonathan White
663467e214 Fix macOS Toolbar Button color
* Correct color setting only if dark mode is enabled
2019-04-16 10:07:30 -04:00
Weslly
7546ba7406 Remove hardcoded background color from search help widget 2019-04-15 14:14:56 -04:00
105 changed files with 2299 additions and 1241 deletions

View File

@@ -1,3 +1,32 @@
2.4.2 (2019-05-31)
=========================
- Improve resilience against memory attacks - overwrite memory before free [#3020]
- Prevent infinite save loop when location is unavailable [#3026]
- Attempt to fix quitting application when shutdown or logout issued [#3199]
- Support merging database custom data [#3002]
- Fix opening URL's with non-http schemes [#3153]
- Fix data loss due to not reading all database attachments if duplicates exist [#3180]
- Fix entry context menu disabling when using keyboard navigation [#3199]
- Fix behaviors when canceling an entry edit [#3199]
- Fix processing of tray icon click and doubleclick [#3112]
- Update group in preview widget when focused [#3199]
- Prefer DuckDuckGo service over direct icon download (increases resolution) [#2996]
- Remove apply button in application settings [#3019]
- Use winqtdeploy on Windows to correct deployment issues [#3025]
- Don't mark entry edit as modified when attribute selection changes [#3041]
- Use console code page CP_UTF8 on Windows if supported [#3050]
- Snap: Fix locking database with session lock [#3046]
- Snap: Fix theming across Linux distributions [#3057]
- Snap: Use SNAP_USER_COMMON and SNAP_USER_DATA directories [#3131]
- KeeShare: Automatically enable WITH_XC_KEESHARE_SECURE if quazip is found [#3088]
- macOS: Fix toolbar text when in dark mode [#2998]
- macOS: Lock database on switching user [#3097]
- macOS: Fix global Auto-Type when the database is locked [#3138]
- Browser: Close popups when database is locked [#3093]
- Browser: Add tests [#3016]
- Browser: Don't create default group if custom group is enabled [#3127]
2.4.1 (2019-04-12)
=========================

View File

@@ -20,9 +20,10 @@ project(KeePassXC)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel."
"Choose the type of build, options are: Debug Release RelWithDebInfo Profile"
FORCE)
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
@@ -40,22 +41,21 @@ option(WITH_ASAN "Enable address sanitizer checks (Linux / macOS only)" OFF)
option(WITH_COVERAGE "Use to build with coverage tests (GCC only)." OFF)
option(WITH_APP_BUNDLE "Enable Application Bundle for macOS" ON)
set(WITH_XC_ALL OFF CACHE BOOLEAN "Build in all available plugins")
set(WITH_XC_ALL OFF CACHE BOOL "Build in all available plugins")
option(WITH_XC_AUTOTYPE "Include Auto-Type." ON)
option(WITH_XC_NETWORKING "Include networking code (e.g. for downlading website icons)." OFF)
option(WITH_XC_NETWORKING "Include networking code (e.g. for downloading website icons)." OFF)
option(WITH_XC_BROWSER "Include browser integration with keepassxc-browser." OFF)
option(WITH_XC_YUBIKEY "Include YubiKey support." OFF)
option(WITH_XC_SSHAGENT "Include SSH agent support." OFF)
option(WITH_XC_KEESHARE "Sharing integration with KeeShare" OFF)
option(WITH_XC_KEESHARE_SECURE "Sharing integration with secured KeeShare containers" OFF)
option(WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)" OFF)
option(WITH_XC_UPDATECHECK "Include automatic update checks; disable for controlled distributions" ON)
if(APPLE)
option(WITH_XC_TOUCHID "Include TouchID support for macOS." OFF)
endif()
if(WITH_XC_ALL)
# Enable all options
# Enable all options (except update check)
set(WITH_XC_AUTOTYPE ON)
set(WITH_XC_NETWORKING ON)
set(WITH_XC_BROWSER ON)
@@ -67,23 +67,21 @@ if(WITH_XC_ALL)
endif()
endif()
if(WITH_XC_KEESHARE_SECURE)
set(WITH_XC_KEESHARE ON)
endif()
if(WITH_XC_SSHAGENT OR WITH_XC_KEESHARE)
set(WITH_XC_CRYPTO_SSH ON)
else()
set(WITH_XC_CRYPTO_SSH OFF)
endif()
if(WITH_XC_UPDATECHECK)
set(WITH_XC_NETWORKING ON)
# Prefer WITH_XC_NETWORKING setting over WITH_XC_UPDATECHECK
if(NOT WITH_XC_NETWORKING AND WITH_XC_UPDATECHECK)
message(STATUS "Disabling WITH_XC_UPDATECHECK because WITH_XC_NETWORKING is disabled")
set(WITH_XC_UPDATECHECK OFF)
endif()
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "4")
set(KEEPASSXC_VERSION_PATCH "1")
set(KEEPASSXC_VERSION_PATCH "2")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
@@ -162,11 +160,15 @@ if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
set(IS_32BIT TRUE)
endif()
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
if("${CMAKE_C_COMPILER}" MATCHES "clang$"
OR "${CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS}" MATCHES "__clang__"
OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1)
endif()
if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$"
OR "${CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS}" MATCHES "__clang__"
OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANGXX 1)
endif()
@@ -199,7 +201,7 @@ add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute")
add_gcc_compiler_flags("-fvisibility=hidden")
add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
add_gcc_compiler_flags("-Werror")
endif()
@@ -230,7 +232,6 @@ if(WITH_ASAN)
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if(CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)")
add_gcc_compiler_flags("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2")
endif()
@@ -264,6 +265,11 @@ endif()
add_gcc_compiler_cflags("-std=c99")
add_gcc_compiler_cxxflags("-std=c++11")
if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9.99) OR
(CMAKE_COMPILER_IS_CLANGXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6.99))
add_gcc_compiler_cxxflags("-fsized-deallocation")
endif()
if(APPLE)
add_gcc_compiler_cxxflags("-stdlib=libc++")
endif()
@@ -276,7 +282,7 @@ if(MINGW)
set(CMAKE_RC_COMPILER_INIT windres)
enable_language(RC)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
if(NOT (CMAKE_BUILD_TYPE_LOWER STREQUAL "debug" OR CMAKE_BUILD_TYPE_LOWER STREQUAL "relwithdebinfo"))
# Enable DEP and ASLR
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
@@ -365,10 +371,17 @@ if(APPLE)
set(CMAKE_MACOSX_RPATH TRUE)
find_program(MACDEPLOYQT_EXE macdeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH)
if(NOT MACDEPLOYQT_EXE)
message(FATAL_ERROR "macdeployqt is required to build in macOS")
message(FATAL_ERROR "macdeployqt is required to build on macOS")
else()
message(STATUS "Using macdeployqt: ${MACDEPLOYQT_EXE}")
endif()
elseif(MINGW)
find_program(WINDEPLOYQT_EXE windeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH)
if(NOT WINDEPLOYQT_EXE)
message(FATAL_ERROR "windeployqt is required to build on Windows")
else()
message(STATUS "Using windeployqt: ${WINDEPLOYQT_EXE}")
endif()
endif()
# Debian sets the the build type to None for package builds.
@@ -380,6 +393,7 @@ find_package(Gcrypt 1.7.0 REQUIRED)
find_package(Argon2 REQUIRED)
find_package(ZLIB REQUIRED)
find_package(QREncode REQUIRED)
find_package(sodium 1.0.12 REQUIRED)
set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR})
@@ -387,20 +401,7 @@ if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0")
message(FATAL_ERROR "zlib 1.2.0 or higher is required to use the gzip format")
endif()
include_directories(SYSTEM ${ARGON2_INCLUDE_DIR})
# Optional
if(WITH_XC_KEESHARE)
set(WITH_XC_KEESHARE_INSECURE ON)
if(WITH_XC_KEESHARE_SECURE)
# ZLIB is needed and already required
find_package(QuaZip REQUIRED)
include_directories(SYSTEM ${QUAZIP_INCLUDE_DIR})
endif()
else()
set(WITH_XC_KEESHARE_INSECURE OFF)
set(WITH_XC_KEESHARE_SECURE OFF)
endif()
include_directories(SYSTEM ${ARGON2_INCLUDE_DIR} ${sodium_INCLUDE_DIR})
# Optional
if(WITH_XC_YUBIKEY)

View File

@@ -25,7 +25,7 @@ The following libraries are required:
* zlib
* libmicrohttpd
* libxi, libxtst, qtx11extras (optional for auto-type on X11)
* libsodium (>= 1.0.12, optional for KeePassXC-Browser support)
* libsodium (>= 1.0.12)
* libargon2
Prepare the Building Environment
@@ -97,18 +97,26 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct
-DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON)
-DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF)
-DWITH_XC_BROWSER=[ON|OFF] Enable/Disable KeePassXC-Browser extension support (default: OFF)
-DWITH_XC_NETWORKING=[ON|OFF] Enable/Disable Networking support (favicon download) (default: OFF)
-DWITH_XC_NETWORKING=[ON|OFF] Enable/Disable Networking support (e.g., favicon downloading) (default: OFF)
-DWITH_XC_SSHAGENT=[ON|OFF] Enable/Disable SSHAgent support (default: OFF)
-DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group syncronization extension (default: OFF)
-DWITH_XC_TOUCHID=[ON|OFF] (macOS Only) Enable/Disable Touch ID unlock (default:OFF)
-DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group synchronization extension (default: OFF)
-DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare signed containers, requires libquazip5 (default: OFF)
-DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (default: OFF)
-DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare secure containers, requires libquazip5 (default: OFF)
-DWITH_XC_UPDATECHECK=[ON|OFF] Enable/Disable automatic updating checking (requires WITH_XC_NETWORKING) (default: ON)
-DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON)
-DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF)
-DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF)
-DWITH_ASAN=[ON|OFF] Enable/Disable address sanitizer checks (Linux / macOS only) (default: OFF)
-DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests (GCC only) (default: OFF)
-DWITH_APP_BUNDLE=[ON|OFF] Enable Application Bundle for macOS (default: ON)
-DKEEPASSXC_BUILD_TYPE=[Snapshot|PreRelease|Release] Set the build type to show/hide stability warnings (default: "Snapshot")
-DKEEPASSXC_DIST_TYPE=[Snap|AppImage|Other] Specify the distribution method (default: "Other")
-DOVERRIDE_VERSION=[X.X.X] Specify a version number when building. Used with snapshot builds (default: "")
-DGIT_HEAD_OVERRIDE=[XXXXXXX] Specify the 7 digit git commit ref for this build. Used with distribution builds (default: "")
```
* If you are on MacOS you must add this parameter to **Cmake**, with the Qt version you have installed<br/> `-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake/`

View File

@@ -112,7 +112,7 @@ mark_as_advanced(
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
if(NOT CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"

View File

@@ -1,41 +1,24 @@
# QUAZIP_FOUND - QuaZip library was found
# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir
# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR)
# QUAZIP_LIBRARIES - List of QuaZip libraries
# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers
# QUAZIP_FOUND - QuaZip library was found
# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir
# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR)
# QUAZIP_LIBRARIES - List of QuaZip libraries
# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers
IF(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
# in cache already
SET(QUAZIP_FOUND TRUE)
ELSE(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
IF(Qt5Core_FOUND)
set(QUAZIP_LIB_VERSION_SUFFIX 5)
ENDIF()
IF(WIN32)
FIND_PATH(QUAZIP_LIBRARY_DIR
WIN32_DEBUG_POSTFIX d
NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll
HINTS "C:/Programme/" "C:/Program Files"
PATH_SUFFIXES QuaZip/lib
if(MINGW)
find_library(QUAZIP_LIBRARIES libquazip5)
find_path(QUAZIP_INCLUDE_DIR quazip.h PATH_SUFFIXES quazip5)
find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h)
else()
find_library(QUAZIP_LIBRARIES
NAMES quazip5 quazip
PATHS /usr/lib /usr/lib64 /usr/local/lib
)
FIND_LIBRARY(QUAZIP_LIBRARIES NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll HINTS ${QUAZIP_LIBRARY_DIR})
FIND_PATH(QUAZIP_INCLUDE_DIR NAMES quazip.h HINTS ${QUAZIP_LIBRARY_DIR}/../ PATH_SUFFIXES include/quazip5)
FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR NAMES zlib.h)
ELSE(WIN32)
FIND_PACKAGE(PkgConfig)
pkg_check_modules(PC_QUAZIP quazip)
FIND_LIBRARY(QUAZIP_LIBRARIES
WIN32_DEBUG_POSTFIX d
NAMES quazip${QUAZIP_LIB_VERSION_SUFFIX}
HINTS /usr/lib /usr/lib64
find_path(QUAZIP_INCLUDE_DIR quazip.h
PATHS /usr/include /usr/local/include
PATH_SUFFIXES quazip5 quazip
)
FIND_PATH(QUAZIP_INCLUDE_DIR quazip.h
HINTS /usr/include /usr/local/include
PATH_SUFFIXES quazip${QUAZIP_LIB_VERSION_SUFFIX}
)
FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR zlib.h HINTS /usr/include /usr/local/include)
ENDIF(WIN32)
INCLUDE(FindPackageHandleStandardArgs)
SET(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR})
find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS)
ENDIF(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h PATHS /usr/include /usr/local/include)
endif()
include(FindPackageHandleStandardArgs)
set(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR})
find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS)

View File

@@ -50,6 +50,37 @@
</screenshots>
<releases>
<release version="2.4.2" date="2019-05-31">
<description>
<ul>
<li>Improve resilience against memory attacks - overwrite memory before free [#3020]</li>
<li>Prevent infinite save loop when location is unavailable [#3026]</li>
<li>Attempt to fix quitting application when shutdown or logout issued [#3199]</li>
<li>Support merging database custom data [#3002]</li>
<li>Fix opening URL's with non-http schemes [#3153]</li>
<li>Fix data loss due to not reading all database attachments if duplicates exist [#3180]</li>
<li>Fix entry context menu disabling when using keyboard navigation [#3199]</li>
<li>Fix behaviors when canceling an entry edit [#3199]</li>
<li>Fix processing of tray icon click and doubleclick [#3112]</li>
<li>Update group in preview widget when focused [#3199]</li>
<li>Prefer DuckDuckGo service over direct icon download (increases resolution) [#2996]</li>
<li>Remove apply button in application settings [#3019]</li>
<li>Use winqtdeploy on Windows to correct deployment issues [#3025]</li>
<li>Don't mark entry edit as modified when attribute selection changes [#3041]</li>
<li>Use console code page CP_UTF8 on Windows if supported [#3050]</li>
<li>Snap: Fix locking database with session lock [#3046]</li>
<li>Snap: Fix theming across Linux distributions [#3057]</li>
<li>Snap: Use SNAP_USER_COMMON and SNAP_USER_DATA directories [#3131]</li>
<li>KeeShare: Automatically enable WITH_XC_KEESHARE_SECURE if quazip is found [#3088]</li>
<li>macOS: Fix toolbar text when in dark mode [#2998]</li>
<li>macOS: Lock database on switching user [#3097]</li>
<li>macOS: Fix global Auto-Type when the database is locked [#3138]</li>
<li>Browser: Close popups when database is locked [#3093]</li>
<li>Browser: Add tests [#3016]</li>
<li>Browser: Don't create default group if custom group is enabled [#3127]</li>
</ul>
</description>
</release>
<release version="2.4.1" date="2019-04-12">
<description>
<ul>

View File

@@ -1531,7 +1531,7 @@ Möchten Sie Ihre Änderungen zusammenführen?</translation>
</message>
<message numerus="yes">
<source>Do you really want to delete %n entry(s) for good?</source>
<translation><numerusform>Sollen tatsächlich %1 Einträge gelöscht werden?</numerusform><numerusform>Sollen tatsächlich %1 Einträge gelöscht werden?</numerusform></translation>
<translation><numerusform>Sollen tatsächlich %n Einträge gelöscht werden?</numerusform><numerusform>Sollen tatsächlich %n Einträge gelöscht werden?</numerusform></translation>
</message>
<message numerus="yes">
<source>Delete entry(s)?</source>

View File

@@ -326,8 +326,8 @@
<translation>Privacy</translation>
</message>
<message>
<source>Use DuckDuckGo as fallback for downloading website icons</source>
<translation>Use DuckDuckGo as fallback for downloading website icons</translation>
<source>Use DuckDuckGo service to download website icons</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -631,6 +631,14 @@ Please select the correct database for saving credentials.</translation>
<source>&amp;Brave</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Returns expired credentials. String [expired] is added to the title.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Allow returning expired credentials.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserService</name>
@@ -2244,10 +2252,6 @@ Supported extensions are: %1.</source>
<source>Custom icon successfully downloaded</source>
<translation>Custom icon successfully downloaded</translation>
</message>
<message>
<source>Hint: You can enable DuckDuckGo as a fallback under Tools&gt;Settings&gt;Security</source>
<translation>Hint: You can enable DuckDuckGo as a fallback under Tools&gt;Settings&gt;Security</translation>
</message>
<message>
<source>Select Image(s)</source>
<translation>Select Image(s)</translation>
@@ -2284,6 +2288,10 @@ Supported extensions are: %1.</source>
<numerusform>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</numerusform>
</translation>
</message>
<message>
<source>You can enable the DuckDuckGo website icon service under Tools -&gt; Settings -&gt; Security</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
@@ -3750,6 +3758,14 @@ Expect some bugs and minor issues, this version is not meant for production use.
<source>Adding missing icon %1</source>
<translation>Adding missing icon %1</translation>
</message>
<message>
<source>Removed custom data %1 [%2]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adding custom data %1 [%2]</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NewDatabaseWizard</name>

View File

@@ -200,27 +200,27 @@
</message>
<message>
<source>Auto-Type</source>
<translation>Auto-Escritura</translation>
<translation>Autoescritura</translation>
</message>
<message>
<source>Use entry title to match windows for global Auto-Type</source>
<translation>Use título de entrada para acertar ventanas en Auto-Escritura global.</translation>
<translation>Usar título de la entrada para emparejar ventanas en autoescritura global</translation>
</message>
<message>
<source>Use entry URL to match windows for global Auto-Type</source>
<translation>Use URL para acertar ventanas en Auto-Escritura global</translation>
<translation>Usar URL de la entrada para emparejar ventanas en autoescritura global</translation>
</message>
<message>
<source>Always ask before performing Auto-Type</source>
<translation>Siempre preguntar antes de hacer Auto-Escritura</translation>
<translation>Siempre preguntar antes de hacer autoescritura</translation>
</message>
<message>
<source>Global Auto-Type shortcut</source>
<translation>Atajo global de Auto-Escritura</translation>
<translation>Atajo global de autoescritura</translation>
</message>
<message>
<source>Auto-Type typing delay</source>
<translation>Escribiendo retardo de la Auto-Escritura</translation>
<translation>Escribiendo retardo de la autoescritura</translation>
</message>
<message>
<source> ms</source>
@@ -229,7 +229,7 @@
</message>
<message>
<source>Auto-Type start delay</source>
<translation>Iniciar retardo de Auto-Escritura</translation>
<translation>Iniciar retardo de autoescritura</translation>
</message>
<message>
<source>Check for updates at application startup</source>
@@ -293,7 +293,7 @@
</message>
<message>
<source>Re-lock previously locked database after performing Auto-Type</source>
<translation>Volver a bloquear la base de datos tras realizar una Auto-Escritura </translation>
<translation>Volver a bloquear la base de datos tras realizar una autoescritura</translation>
</message>
<message>
<source>Don&apos;t require password repeat when it is visible</source>
@@ -332,27 +332,27 @@
</message>
<message>
<source>Auto-Type - KeePassXC</source>
<translation>Auto-Escritura - KeePassXC</translation>
<translation>Autoescritura - KeePassXC</translation>
</message>
<message>
<source>Auto-Type</source>
<translation>Auto-Escritura</translation>
<translation>Autoescritura</translation>
</message>
<message>
<source>The Syntax of your Auto-Type statement is incorrect!</source>
<translation>¡La sintaxis de la declaración de su Auto-Escritura es incorrecta!</translation>
<translation>¡La sintaxis de la declaración de su autoescritura es incorrecta!</translation>
</message>
<message>
<source>This Auto-Type command contains a very long delay. Do you really want to proceed?</source>
<translation>Este comando de Auto-Escritura contiene un retraso muy largo. ¿Realmente desea continuar?</translation>
<translation>Este comando de autoescritura contiene un retraso muy largo. ¿Realmente desea continuar?</translation>
</message>
<message>
<source>This Auto-Type command contains very slow key presses. Do you really want to proceed?</source>
<translation>Este comando de Auto-Escritura contiene pulsaciones de teclas muy lentas. ¿Realmente desea continuar?</translation>
<translation>Este comando de autoescritura contiene pulsaciones de teclas muy lentas. ¿Realmente desea continuar?</translation>
</message>
<message>
<source>This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed?</source>
<translation>Este comando de Auto-Escritura contiene argumentos que se repiten muy a menudo. ¿Realmente desea continuar?</translation>
<translation>Este comando de autoescritura contiene argumentos que se repiten muy a menudo. ¿Realmente desea continuar?</translation>
</message>
</context>
<context>
@@ -393,11 +393,11 @@
<name>AutoTypeSelectDialog</name>
<message>
<source>Auto-Type - KeePassXC</source>
<translation>Auto-Escritura - KeePassXC</translation>
<translation>Autoescritura - KeePassXC</translation>
</message>
<message>
<source>Select entry to Auto-Type:</source>
<translation>Seleccionar entrada para Auto-Escritura:</translation>
<translation>Seleccionar entrada para autoescritura:</translation>
</message>
</context>
<context>
@@ -884,7 +884,7 @@ Es necesario para mantener sus conexiones presentes del navegador.
<name>DatabaseOpenDialog</name>
<message>
<source>Unlock Database - KeePassXC</source>
<translation>Desbloquear Base de Datos - KeePassXC</translation>
<translation>Desbloquear base de datos - KeePassXC</translation>
</message>
</context>
<context>
@@ -1259,7 +1259,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!<
<name>DatabaseSettingsWidgetGeneral</name>
<message>
<source>Database Meta Data</source>
<translation>Metadatos de la Base de Datos</translation>
<translation>Metadatos de la base de datos</translation>
</message>
<message>
<source>Database name:</source>
@@ -1295,7 +1295,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!<
</message>
<message>
<source>Additional Database Settings</source>
<translation>Configuraciones Adicionales de la Base de Datos</translation>
<translation>Configuraciones adicionales de la base de datos</translation>
</message>
<message>
<source>Enable &amp;compression (recommended)</source>
@@ -1438,12 +1438,12 @@ Esto es definitivamente un error, por favor repórtelo a los desarrolladores.</t
</message>
<message>
<source>New Database</source>
<translation>Nueva Base de datos</translation>
<translation>Nueva base de datos</translation>
</message>
<message>
<source>%1 [New Database]</source>
<comment>Database tab name modifier</comment>
<translation>%1 [Nueva Base de Datos]</translation>
<translation>%1 [Nueva base de datos]</translation>
</message>
<message>
<source>%1 [Locked]</source>
@@ -1549,7 +1549,7 @@ Do you want to merge your changes?</source>
</message>
<message>
<source>Lock Database?</source>
<translation>¿Bloquear la Base de datos?</translation>
<translation>¿Bloquear la base de datos?</translation>
</message>
<message>
<source>You are editing an entry. Discard changes and lock anyway?</source>
@@ -1654,7 +1654,7 @@ Disable safe saves and try again?</source>
</message>
<message>
<source>Auto-Type</source>
<translation>Auto-Escritura</translation>
<translation>Autoescritura</translation>
</message>
<message>
<source>Properties</source>
@@ -1800,15 +1800,15 @@ Disable safe saves and try again?</source>
<name>EditEntryWidgetAutoType</name>
<message>
<source>Enable Auto-Type for this entry</source>
<translation>Activar Auto-Escritura para esta entrada</translation>
<translation>Activar autoescritura para esta entrada</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Heredar la secuencia de Auto-Escritura por defecto del &amp;grupo</translation>
<translation>Heredar la secuencia de autoescritura por defecto del &amp;grupo</translation>
</message>
<message>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>&amp;Usar secuencia de Auto-Escritura personalizada:</translation>
<translation>&amp;Usar secuencia de autoescritura personalizada:</translation>
</message>
<message>
<source>Window Associations</source>
@@ -2111,15 +2111,15 @@ Disable safe saves and try again?</source>
</message>
<message>
<source>Auto-Type</source>
<translation>Auto-Escritura</translation>
<translation>Autoescritura</translation>
</message>
<message>
<source>&amp;Use default Auto-Type sequence of parent group</source>
<translation>&amp;Usar por defecto la secuencia de Auto-Escritura del grupo padre</translation>
<translation>&amp;Usar por defecto la secuencia de autoescritura del grupo padre</translation>
</message>
<message>
<source>Set default Auto-Type se&amp;quence</source>
<translation>Seleccionar se&amp;cuencia de Auto-Escritura por defecto</translation>
<translation>Seleccionar se&amp;cuencia de autoescritura por defecto</translation>
</message>
</context>
<context>
@@ -2932,7 +2932,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada
</message>
<message>
<source>Auto-type association window or sequence missing</source>
<translation>Falta de secuencia o ventana de asociación de Auto-Escritura</translation>
<translation>Falta de secuencia o ventana de asociación de autoescritura</translation>
</message>
<message>
<source>Invalid bool value</source>
@@ -3522,7 +3522,7 @@ Le recomendamos que utilice la AppImage disponible en nuestra página de descarg
</message>
<message>
<source>Perform &amp;Auto-Type</source>
<translation>Realizar &amp;Auto-Escritura</translation>
<translation>Realizar &amp;autoescritura</translation>
</message>
<message>
<source>Open &amp;URL</source>
@@ -4857,7 +4857,7 @@ Comandos disponibles:
</message>
<message>
<source>Database password: </source>
<translation>Contraseña de la Base de Datos:</translation>
<translation>Contraseña de la base de datos:</translation>
</message>
<message>
<source>Cannot create new group</source>

File diff suppressed because it is too large Load Diff

View File

@@ -611,7 +611,7 @@ Veuillez sélectionner la base de donnée souhaitée pour enregistrer les identi
</message>
<message>
<source>Due to Snap sandboxing, you must run a script to enable browser integration.&lt;br /&gt;You can obtain this script from %1</source>
<translation type="unfinished"/>
<translation>À cause du mécanisme de sandboxing Snap, vous devez lancer un script pour activer l&apos;intégration du navigateur.&lt;br /&gt;Vous pouvez obtenir ce script depuis %1</translation>
</message>
<message>
<source>Please see special instructions for browser extension use below</source>
@@ -710,7 +710,7 @@ Voulez-vous créer ce groupe ?
<source>Your KeePassXC-Browser settings need to be moved into the database settings.
This is necessary to maintain your current browser connections.
Would you like to migrate your existing settings now?</source>
<translation type="unfinished"/>
<translation>Vos réglages pour KeePassXC-Browser doivent être intégrés dans les réglages de la base de données. Ceci est nécessaire pour maintenir vos connexions actuelles avec le navigateur ouvertes. Souhaitez-vous effectuer la migration de vos réglages maintenant ?</translation>
</message>
</context>
<context>
@@ -874,7 +874,7 @@ Would you like to migrate your existing settings now?</source>
</message>
<message>
<source>Key not transformed. This is a bug, please report it to the developers!</source>
<translation type="unfinished"/>
<translation>La clé n&apos;a pas é transformée. Ceci est un bogue, pouvez-vous s&apos;il vous plaît le signaler aux développeurs ?</translation>
</message>
</context>
<context>
@@ -1071,7 +1071,7 @@ Cela peut empêcher la connexion avec l&apos;extension de navigateur.</translati
<message>
<source>Do you really want forget all site-specific settings on every entry?
Permissions to access entries will be revoked.</source>
<translation type="unfinished"/>
<translation>Êtes-vous sûr de vouloir effacer les préférences de site pour toutes les entrées ? Les permissions d&apos;accès aux entrées seront révoquées.</translation>
</message>
<message>
<source>Removing stored permissions…</source>
@@ -1099,12 +1099,13 @@ Permissions to access entries will be revoked.</source>
</message>
<message>
<source>Move KeePassHTTP attributes to custom data</source>
<translation type="unfinished"/>
<translation>Déplacer les attributs KeePassHTTP vers les données personnalisées</translation>
</message>
<message>
<source>Do you really want to move all legacy browser integration data to the latest standard?
This is necessary to maintain compatibility with the browser plugin.</source>
<translation type="unfinished"/>
<translation>Voulez-vous convertir toutes les anciennes données d&apos;intégration au navigateur en version plus récente ?
Ceci est nécessaire pour assurer la compatibilité de l&apos;extension.</translation>
</message>
</context>
<context>
@@ -1528,7 +1529,7 @@ Voulez-vous fusionner vos changements?</translation>
</message>
<message numerus="yes">
<source>Do you really want to delete %n entry(s) for good?</source>
<translation><numerusform>Voulez-vous vraiment supprimer définitivement%1 entrée?</numerusform><numerusform>Voulez-vous vraiment supprimer définitivement%1 entrées?</numerusform></translation>
<translation><numerusform>Voulez-vous vraiment supprimer définitivement%n entrée?</numerusform><numerusform>Voulez-vous vraiment supprimer définitivement%n entrées?</numerusform></translation>
</message>
<message numerus="yes">
<source>Delete entry(s)?</source>
@@ -3463,7 +3464,7 @@ Nous recommandons l&apos;utilisation de l&apos;AppImage disponible sur notre pag
</message>
<message>
<source>&amp;New database...</source>
<translation>&amp;Ńouvelle base de données...</translation>
<translation>&amp;Nouvelle base de données...</translation>
</message>
<message>
<source>Create a new database</source>
@@ -4513,7 +4514,7 @@ Commandes disponibles :
</message>
<message>
<source>Multi-word extra bits %1</source>
<translation type="unfinished"/>
<translation>Octets additionnels mots multiples %1</translation>
</message>
<message>
<source>Type: Bruteforce</source>
@@ -4561,7 +4562,7 @@ Commandes disponibles :
</message>
<message>
<source>Type: Dict+Leet(Rep)</source>
<translation type="unfinished"/>
<translation>Type : Dictionnaire + Leet (rep)</translation>
</message>
<message>
<source>Type: User Words(Rep)</source>
@@ -4569,7 +4570,7 @@ Commandes disponibles :
</message>
<message>
<source>Type: User+Leet(Rep)</source>
<translation type="unfinished"/>
<translation>Type : Utilisateur + Leet (rep)</translation>
</message>
<message>
<source>Type: Repeated(Rep)</source>
@@ -4597,7 +4598,7 @@ Commandes disponibles :
</message>
<message>
<source>*** Password length (%1) != sum of length of parts (%2) ***</source>
<translation type="unfinished"/>
<translation>*** Longueur du mot de passe (%1) != longueurs additionnées des morceaux (%2) ***</translation>
</message>
<message>
<source>Failed to load key file %1: %2</source>
@@ -4933,11 +4934,11 @@ Commandes disponibles :
</message>
<message>
<source>Search terms are as follows: [modifiers][field:][&quot;]term[&quot;]</source>
<translation type="unfinished"/>
<translation>Les termes de recherche sont construits comme suit : [modificateurs][champ:][&quot;]terme[&quot;]</translation>
</message>
<message>
<source>Every search term must match (ie, logical AND)</source>
<translation type="unfinished"/>
<translation>Tous les termes doivent correspondre (ET logique)</translation>
</message>
<message>
<source>Modifiers</source>
@@ -4961,15 +4962,15 @@ Commandes disponibles :
</message>
<message>
<source>Term Wildcards</source>
<translation type="unfinished"/>
<translation>Caractères spéciaux</translation>
</message>
<message>
<source>match anything</source>
<translation type="unfinished"/>
<translation>correspond à n&apos;importe quel caractère</translation>
</message>
<message>
<source>match one</source>
<translation type="unfinished"/>
<translation>correspond à un seul caractère</translation>
</message>
<message>
<source>logical OR</source>
@@ -5105,7 +5106,7 @@ Commandes disponibles :
<message>
<source>key.share</source>
<comment>Filetype for KeeShare key</comment>
<translation type="unfinished"/>
<translation>cle.share</translation>
</message>
<message>
<source>KeeShare key file</source>
@@ -5121,15 +5122,15 @@ Commandes disponibles :
</message>
<message>
<source>Exporting changed certificate</source>
<translation type="unfinished"/>
<translation>Exportation des certificats modifiés</translation>
</message>
<message>
<source>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</source>
<translation type="unfinished"/>
<translation>Le certificat exporté est différent de celui en cours d&apos;utilisation. Voulez-vous exporter le certificat actuel ?</translation>
</message>
<message>
<source>Signer:</source>
<translation type="unfinished"/>
<translation>Signataire :</translation>
</message>
</context>
<context>
@@ -5140,7 +5141,7 @@ Commandes disponibles :
</message>
<message>
<source>We cannot verify the source of the shared container because it is not signed. Do you really want to import from %1?</source>
<translation type="unfinished"/>
<translation>Nous ne pouvons vérifier la source du conteneur partagé car celui-ci n&apos;est pas signé. Êtes-vous sûr de vouloir importer depuis %1 ?</translation>
</message>
<message>
<source>Import from container with certificate</source>
@@ -5176,7 +5177,7 @@ Commandes disponibles :
</message>
<message>
<source>Signed share container are not supported - import prevented</source>
<translation type="unfinished"/>
<translation>Conteneur de partage signé non pris en charge - importation annulée</translation>
</message>
<message>
<source>File is not readable</source>
@@ -5184,15 +5185,15 @@ Commandes disponibles :
</message>
<message>
<source>Invalid sharing container</source>
<translation type="unfinished"/>
<translation>Conteneur de partage invalide</translation>
</message>
<message>
<source>Untrusted import prevented</source>
<translation type="unfinished"/>
<translation>Importation non sécurisée annulée</translation>
</message>
<message>
<source>Successful signed import</source>
<translation type="unfinished"/>
<translation>Importation signée réussie</translation>
</message>
<message>
<source>Unexpected error</source>
@@ -5200,11 +5201,11 @@ Commandes disponibles :
</message>
<message>
<source>Unsigned share container are not supported - import prevented</source>
<translation type="unfinished"/>
<translation>Conteneur de partage non signé non pris en charge - importation annulée</translation>
</message>
<message>
<source>Successful unsigned import</source>
<translation type="unfinished"/>
<translation>Importation non signée réussie</translation>
</message>
<message>
<source>File does not exist</source>
@@ -5212,27 +5213,27 @@ Commandes disponibles :
</message>
<message>
<source>Unknown share container type</source>
<translation type="unfinished"/>
<translation>Type de conteneur de partage non reconnu</translation>
</message>
<message>
<source>Overwriting signed share container is not supported - export prevented</source>
<translation type="unfinished"/>
<translation>Remplacement de conteneur de partage signé non pris en charge - exportation annulée</translation>
</message>
<message>
<source>Could not write export container (%1)</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;exporter le conteneur (%1)</translation>
</message>
<message>
<source>Overwriting unsigned share container is not supported - export prevented</source>
<translation type="unfinished"/>
<translation>Remplacement de conteneur non signé non pris en charge - exportation annulée</translation>
</message>
<message>
<source>Could not write export container</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;exporter le conteneur</translation>
</message>
<message>
<source>Unexpected export error occurred</source>
<translation type="unfinished"/>
<translation>Une erreur inattendue est survenue lors de l&apos;exportation</translation>
</message>
<message>
<source>Export to %1 failed (%2)</source>
@@ -5248,31 +5249,31 @@ Commandes disponibles :
</message>
<message>
<source>Do you want to trust %1 with the fingerprint of %2 from %3?</source>
<translation type="unfinished"/>
<translation>Voulez-vous autoriser %1 avec l&apos;empreinte de %2 à %3 ? {1 ?} {2 ?}</translation>
</message>
<message>
<source>Multiple import source path to %1 in %2</source>
<translation type="unfinished"/>
<translation>Chemin source d&apos;importation multiple de %1 dans %2</translation>
</message>
<message>
<source>Conflicting export target path %1 in %2</source>
<translation type="unfinished"/>
<translation>Conflit du chemin cible d&apos;exportation %1 dans %2</translation>
</message>
<message>
<source>Could not embed signature: Could not open file to write (%1)</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;intégrer la signature : le fichier (%1) n&apos;a pas pu être ouvert en écriture</translation>
</message>
<message>
<source>Could not embed signature: Could not write file (%1)</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;intégrer la signature : problème d&apos;écriture dans le fichier (%1)</translation>
</message>
<message>
<source>Could not embed database: Could not open file to write (%1)</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;intégrer la base de données : le fichier (%1) n&apos;a pas pu être ouvert en écriture</translation>
</message>
<message>
<source>Could not embed database: Could not write file (%1)</source>
<translation type="unfinished"/>
<translation>Impossible d&apos;intégrer la base de données : problème d&apos;écriture dans le fichier (%1)</translation>
</message>
</context>
<context>

View File

@@ -54,7 +54,7 @@
</message>
<message>
<source>Use OpenSSH for Windows instead of Pageant</source>
<translation type="unfinished"/>
<translation>Usa OpenSSH per Windows al posto di Pageant</translation>
</message>
</context>
<context>
@@ -442,7 +442,8 @@ Seleziona se vuoi consentire l&apos;accesso.</translation>
<message>
<source>You have multiple databases open.
Please select the correct database for saving credentials.</source>
<translation type="unfinished"/>
<translation>C&apos;è più di un database aperto
Selezionare il database corretto dove salvare le credenziali</translation>
</message>
</context>
<context>
@@ -1341,7 +1342,9 @@ Se continui con questo numero, il tuo database potrebbe essere decifrato molto f
<source>WARNING! You have not set a password. Using a database without a password is strongly discouraged!
Are you sure you want to continue without a password?</source>
<translation type="unfinished"/>
<translation>ATTENZIONE! Non è stata impostata una password. Utilizzare un database senza password è fortemente sconsigliato!
Siete sicuri di voler continuare senza password?</translation>
</message>
<message>
<source>Unknown error</source>
@@ -1547,7 +1550,8 @@ Vuoi salvare le modifiche?</translation>
<message>
<source>Database was modified.
Save changes?</source>
<translation type="unfinished"/>
<translation>Il database è stato modificato.
Salvare le modifiche?</translation>
</message>
<message>
<source>Save changes?</source>
@@ -3532,7 +3536,7 @@ Si consiglia di utilizzare l&apos;AppImage disponibile sulla nostra pagina di do
</message>
<message>
<source>Check for Updates...</source>
<translation type="unfinished"/>
<translation>Controllo aggiornamenti...</translation>
</message>
<message>
<source>Share entry</source>
@@ -3545,15 +3549,15 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Check for updates on startup?</source>
<translation type="unfinished"/>
<translation>Controllare gli aggiornamenti all&apos;avvio?</translation>
</message>
<message>
<source>Would you like KeePassXC to check for updates on startup?</source>
<translation type="unfinished"/>
<translation>Volete che KeePassXC controlli eventuali aggiornamenti all&apos;avvio?</translation>
</message>
<message>
<source>You can always check for updates manually from the application menu.</source>
<translation type="unfinished"/>
<translation>È sempre possibile controllare gli aggiornamenti manualmente tramite i menu dell&apos;applicazione.</translation>
</message>
</context>
<context>
@@ -3619,7 +3623,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
<name>NewDatabaseWizard</name>
<message>
<source>Create a new KeePassXC database...</source>
<translation type="unfinished"/>
<translation>Creazione di un nuovo database KeePassXC...</translation>
</message>
<message>
<source>Root</source>
@@ -3639,7 +3643,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Here you can adjust the database encryption settings. Don&apos;t worry, you can change them later in the database settings.</source>
<translation type="unfinished"/>
<translation>Qui è possibile modificare le impostazioni di crittaggio del database. È sempre possibile modificarli dopo nelle impostazioni del database.</translation>
</message>
<message>
<source>Advanced Settings</source>
@@ -3658,7 +3662,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Here you can adjust the database encryption settings. Don&apos;t worry, you can change them later in the database settings.</source>
<translation type="unfinished"/>
<translation>Qui è possibile modificare le impostazioni di crittaggio del database. È sempre possibile modificarli dopo nelle impostazioni del database.</translation>
</message>
</context>
<context>
@@ -3669,7 +3673,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>A master key known only to you protects your database.</source>
<translation type="unfinished"/>
<translation>Una password principale segreta protegge il vostro database.</translation>
</message>
</context>
<context>
@@ -3798,7 +3802,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>&lt;p&gt;A password is the primary method for securing your database.&lt;/p&gt;&lt;p&gt;Good passwords are long and unique. KeePassXC can generate one for you.&lt;/p&gt;</source>
<translation type="unfinished"/>
<translation>&lt;p&gt;Una password è il metodo principale per mantenere sicuro il vostro database.&lt;/p&gt;&lt;p&gt;Una buona password dev&apos;essere lunga ed unica. KeePassXC può generarne una per voi.&lt;/p&gt;</translation>
</message>
<message>
<source>Passwords do not match.</source>
@@ -3958,7 +3962,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>{[(</source>
<translation type="unfinished"/>
<translation>{[(</translation>
</message>
<message>
<source>Punctuation</source>
@@ -3966,7 +3970,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>.,:;</source>
<translation type="unfinished"/>
<translation>.,:;</translation>
</message>
<message>
<source>Quotes</source>
@@ -3974,7 +3978,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>&quot; &apos;</source>
<translation type="unfinished"/>
<translation>&quot; &apos;</translation>
</message>
<message>
<source>Math</source>
@@ -3982,7 +3986,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>&lt;*+!?=</source>
<translation type="unfinished"/>
<translation>&lt;*+!?=</translation>
</message>
<message>
<source>Dashes</source>
@@ -3990,7 +3994,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>\_|-/</source>
<translation type="unfinished"/>
<translation>\_|-/</translation>
</message>
<message>
<source>Logograms</source>
@@ -3998,7 +4002,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>#$%&amp;&amp;@^`~</source>
<translation type="unfinished"/>
<translation>#$%&amp;&amp;@^`~</translation>
</message>
<message>
<source>Switch to simple mode</source>
@@ -4041,21 +4045,21 @@ Expect some bugs and minor issues, this version is not meant for production use.
<name>QApplication</name>
<message>
<source>KeeShare</source>
<translation type="unfinished"/>
<translation>KeeShare</translation>
</message>
</context>
<context>
<name>QFileDialog</name>
<message>
<source>Select</source>
<translation type="unfinished"/>
<translation>Seleziona</translation>
</message>
</context>
<context>
<name>QMessageBox</name>
<message>
<source>Overwrite</source>
<translation type="unfinished"/>
<translation>Sovrascrivi</translation>
</message>
<message>
<source>Delete</source>
@@ -4063,11 +4067,11 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Move</source>
<translation type="unfinished"/>
<translation>Sposta</translation>
</message>
<message>
<source>Empty</source>
<translation type="unfinished"/>
<translation>Vuoto</translation>
</message>
<message>
<source>Remove</source>
@@ -4075,7 +4079,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Skip</source>
<translation type="unfinished"/>
<translation>Salta</translation>
</message>
<message>
<source>Disable</source>
@@ -4083,7 +4087,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Merge</source>
<translation type="unfinished"/>
<translation>Incorpora</translation>
</message>
</context>
<context>
@@ -4743,19 +4747,19 @@ Comandi disponibili:
</message>
<message>
<source>Create a new database.</source>
<translation type="unfinished"/>
<translation>Crea un nuovo database.</translation>
</message>
<message>
<source>File %1 already exists.</source>
<translation type="unfinished"/>
<translation>Il file %1 esiste già.</translation>
</message>
<message>
<source>Loading the key file failed</source>
<translation type="unfinished"/>
<translation>Caricamento del key-file fallito.</translation>
</message>
<message>
<source>No key is set. Aborting database creation.</source>
<translation type="unfinished"/>
<translation>Chiave non impostata. Annullamento creazione database.</translation>
</message>
<message>
<source>Failed to save the database: %1.</source>
@@ -4763,7 +4767,7 @@ Comandi disponibili:
</message>
<message>
<source>Successfully created new database.</source>
<translation type="unfinished"/>
<translation>Nuovo database creato con successo.</translation>
</message>
<message>
<source>Insert password to encrypt database (Press enter to leave blank): </source>

View File

@@ -491,7 +491,7 @@ Por favor, selecione o banco de dados correto para salvar as credenciais.</trans
</message>
<message>
<source>Re&amp;quest to unlock the database if it is locked</source>
<translation>Pe&amp;dir para desbloquear a base de dados se estiver bloqueada</translation>
<translation>Pe&amp;dir para desbloquear a banco de dados se estiver bloqueado</translation>
</message>
<message>
<source>Only entries with the same scheme (http://, https://, ...) are returned.</source>
@@ -1063,7 +1063,7 @@ Isso pode impedir a conexão com o plugin do navegador.</translation>
</message>
<message numerus="yes">
<source>Successfully removed %n encryption key(s) from KeePassXC settings.</source>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>Removido com sucesso% n chave (s) criptográficas das configurações do KeePassXC.</numerusform><numerusform>Removido com sucesso% n chave (s) criptográficas das configurações do KeePassXC.</numerusform></translation>
</message>
<message>
<source>Forget all site-specific settings on entries</source>
@@ -1097,7 +1097,7 @@ Permissões para acessar entradas serão revogadas.</translation>
</message>
<message>
<source>The active database does not contain an entry with permissions.</source>
<translation>A base de dados ativa não contém uma entrada com permissões.</translation>
<translation>O banco de dados ativo não contém uma entrada com permissões.</translation>
</message>
<message>
<source>Move KeePassHTTP attributes to custom data</source>
@@ -1239,7 +1239,7 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!<
<message numerus="yes">
<source> thread(s)</source>
<comment>Threads for parallel execution (KDF settings)</comment>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>processo(s)</numerusform><numerusform>processo(s)</numerusform></translation>
</message>
<message numerus="yes">
<source>%1 ms</source>
@@ -1489,11 +1489,11 @@ Este é definitivamente um bug, por favor denuncie para os desenvolvedores.</tra
</message>
<message>
<source>No current database.</source>
<translation>Nenhuma base de dados atual.</translation>
<translation>Nenhuma banco de dados atual.</translation>
</message>
<message>
<source>No source database, nothing to do.</source>
<translation>Nenhuma base de dados de origem, nada a fazer.</translation>
<translation>Nenhuma banco de dados de origem, nada a fazer.</translation>
</message>
<message>
<source>Search Results (%1)</source>
@@ -1509,7 +1509,7 @@ Este é definitivamente um bug, por favor denuncie para os desenvolvedores.</tra
</message>
<message>
<source>The database file has changed. Do you want to load the changes?</source>
<translation>A base de dados foi alterada. Deseja carregar as alterações?</translation>
<translation>O banco de dados foi alterado. Deseja carregar as alterações?</translation>
</message>
<message>
<source>Merge Request</source>
@@ -1531,7 +1531,7 @@ Você deseja combinar suas alterações?</translation>
</message>
<message numerus="yes">
<source>Do you really want to delete %n entry(s) for good?</source>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>Você realmente quer apagar %n entrada(s) para o bem?</numerusform><numerusform>Você realmente quer apagar %n entrada(s) para o bem?</numerusform></translation>
</message>
<message numerus="yes">
<source>Delete entry(s)?</source>
@@ -1609,7 +1609,7 @@ Deseja desabilitar salvamento seguro e tentar novamente?</translation>
</message>
<message numerus="yes">
<source>Entry &quot;%1&quot; has %2 reference(s). Do you want to overwrite references with values, skip this entry, or delete anyway?</source>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>A entrada &quot;%1&quot; tem %2 referência(s). Deseja substituir referências por valores, ignorar essa entrada ou excluir mesmo assim?</numerusform><numerusform>A entrada &quot;%1&quot; tem %2 referência(s). Deseja substituir referências por valores, ignorar essa entrada ou excluir mesmo assim?</numerusform></translation>
</message>
<message>
<source>Delete group</source>
@@ -2078,15 +2078,15 @@ Deseja desabilitar salvamento seguro e tentar novamente?</translation>
</message>
<message>
<source>The export container %1 is already referenced.</source>
<translation type="unfinished"/>
<translation>O contêiner de exportado %1 já é referenciado.</translation>
</message>
<message>
<source>The import container %1 is already imported.</source>
<translation type="unfinished"/>
<translation>O contêiner de importado %1 já foi importado.</translation>
</message>
<message>
<source>The container %1 imported and export by different groups.</source>
<translation type="unfinished"/>
<translation>O contêiner %1 importado e exportado por diferentes grupos.</translation>
</message>
</context>
<context>
@@ -2168,7 +2168,7 @@ Deseja desabilitar salvamento seguro e tentar novamente?</translation>
</message>
<message>
<source>Hint: You can enable DuckDuckGo as a fallback under Tools&gt;Settings&gt;Security</source>
<translation type="unfinished"/>
<translation>Dica: você pode habilitar o DuckDuckGo como um reserva em Ferramentas&gt; Configurações&gt; Segurança</translation>
</message>
<message>
<source>Select Image(s)</source>
@@ -2184,7 +2184,7 @@ Deseja desabilitar salvamento seguro e tentar novamente?</translation>
</message>
<message numerus="yes">
<source>%n icon(s) already exist in the database</source>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>%n ícone(s) já existe no banco de dados</numerusform><numerusform>%n ícone(s) já existe no banco de dados</numerusform></translation>
</message>
<message numerus="yes">
<source>The following icon(s) failed:</source>
@@ -2192,7 +2192,7 @@ Deseja desabilitar salvamento seguro e tentar novamente?</translation>
</message>
<message numerus="yes">
<source>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</source>
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
<translation><numerusform>Este ícone é usado por %n entrada(s) e será substituído pelo ícone padrão. Tem certeza de que deseja excluí-lo?</numerusform><numerusform>Este ícone é usado por %n entrada(s) e será substituído pelo ícone padrão. Tem certeza de que deseja excluí-lo?</numerusform></translation>
</message>
</context>
<context>
@@ -2593,7 +2593,7 @@ Isto pode causar mal funcionamento dos plugins afetados.</translation>
</message>
<message>
<source>Wrong key or database file is corrupt.</source>
<translation>Chave errada ou base de dados corrompida.</translation>
<translation>Chave errada ou banco de dados corrompido.</translation>
</message>
<message>
<source>missing database headers</source>
@@ -2601,7 +2601,7 @@ Isto pode causar mal funcionamento dos plugins afetados.</translation>
</message>
<message>
<source>Header doesn&apos;t match hash</source>
<translation type="unfinished"/>
<translation>Cabeçalho não corresponde ao hash</translation>
</message>
<message>
<source>Invalid header id size</source>
@@ -2833,7 +2833,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da
</message>
<message>
<source>Unable to parse UUID: %1</source>
<translation type="unfinished"/>
<translation>Não é possível analisar o UUID: %1</translation>
</message>
<message>
<source>Failed to read database file.</source>
@@ -3036,7 +3036,7 @@ Linha %2, coluna %3</translation>
</message>
<message>
<source>Wrong key or database file is corrupt.</source>
<translation>Chave errada ou base de dados corrompida.</translation>
<translation>Chave errada ou banco de dados corrompido.</translation>
</message>
<message>
<source>Key transformation failed</source>
@@ -3225,7 +3225,7 @@ Linha %2, coluna %3</translation>
</message>
<message>
<source>&lt;p&gt;You can add a key file containing random bytes for additional security.&lt;/p&gt;&lt;p&gt;You must keep it secret and never lose it or you will be locked out!&lt;/p&gt;</source>
<translation type="unfinished"/>
<translation>&lt;p&gt;Você pode adicionar um arquivo de chave contendo bytes aleatórios para segurança adicional.&lt;/p&gt;&lt;p&gt;Você deve mantê-lo em segredo e nunca perdê-lo ou você será bloqueado!&lt;/p&gt;</translation>
</message>
<message>
<source>Legacy key file format</source>
@@ -3236,12 +3236,16 @@ Linha %2, coluna %3</translation>
unsupported in the future.
Please go to the master key settings and generate a new key file.</source>
<translation type="unfinished"/>
<translation>Você está usando um formato de arquivo de chave antigo que pode ficar
sem suporte no futuro.
Por favor, vá para as configurações da chave mestra e gere um novo arquivo de chave.</translation>
</message>
<message>
<source>Error loading the key file '%1'
Message: %2</source>
<translation type="unfinished"/>
<translation>Erro ao carregar o arquivo de chave &apos;%1&apos;
Mensagem: %2</translation>
</message>
<message>
<source>Key files</source>
@@ -3261,7 +3265,7 @@ Message: %2</source>
</message>
<message>
<source>Unable to create key file: %1</source>
<translation type="unfinished"/>
<translation>Não foi possível criar arquivo de chave: %1</translation>
</message>
<message>
<source>Select a key file</source>
@@ -3308,11 +3312,11 @@ Message: %2</source>
</message>
<message>
<source>&amp;Save database</source>
<translation>&amp;Salvar base de dados</translation>
<translation>&amp;Salvar banco de dados</translation>
</message>
<message>
<source>&amp;Close database</source>
<translation>&amp;Fechar base de dados</translation>
<translation>&amp;Fechar banco de dados</translation>
</message>
<message>
<source>&amp;Delete entry</source>
@@ -3360,7 +3364,7 @@ Message: %2</source>
</message>
<message>
<source>&amp;Lock databases</source>
<translation>&amp;Trancar base de dados</translation>
<translation>&amp;Trancar banco de dados</translation>
</message>
<message>
<source>&amp;Title</source>
@@ -3591,11 +3595,11 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Adding backup for older target %1 [%2]</source>
<translation type="unfinished"/>
<translation>Adicionando backup para o alvo mais antigo %1 [%2]</translation>
</message>
<message>
<source>Adding backup for older source %1 [%2]</source>
<translation type="unfinished"/>
<translation>Adicionando backup para fonte mais antiga %1 [%2]</translation>
</message>
<message>
<source>Reapplying older target entry on top of newer source %1 [%2]</source>
@@ -3607,7 +3611,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Synchronizing from newer source %1 [%2]</source>
<translation type="unfinished"/>
<translation>Sincronizando de uma fonte mais nova %1 [%2]</translation>
</message>
<message>
<source>Synchronizing from older source %1 [%2]</source>
@@ -3619,7 +3623,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Deleting orphan %1 [%2]</source>
<translation type="unfinished"/>
<translation>Excluindo órfã %1 [%2]</translation>
</message>
<message>
<source>Changed deleted objects</source>
@@ -3937,7 +3941,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>ExtendedASCII</source>
<translation type="unfinished"/>
<translation>ASCIIEstendido</translation>
</message>
<message>
<source>Switch to advanced mode</source>
@@ -3957,7 +3961,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Lower Case Letters A to F</source>
<translation type="unfinished"/>
<translation>Letras minúsculas de A a F</translation>
</message>
<message>
<source>a-z</source>
@@ -4033,7 +4037,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Add non-hex letters to &quot;do not include&quot; list</source>
<translation type="unfinished"/>
<translation>Adicionar letras não hexadecimais à lista &quot;não incluir&quot;</translation>
</message>
<message>
<source>Hex</source>
@@ -4041,7 +4045,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç
</message>
<message>
<source>Excluded characters: &quot;0&quot;, &quot;1&quot;, &quot;l&quot;, &quot;I&quot;, &quot;O&quot;, &quot;|&quot;, &quot;&quot;</source>
<translation type="unfinished"/>
<translation>Caracteres excluídos: &quot;0&quot;, &quot;1&quot;, &quot;l&quot;, &quot;I&quot;, &quot;O&quot;, &quot;|&quot;, &quot;&quot;</translation>
</message>
<message>
<source>Word Co&amp;unt:</source>
@@ -4415,11 +4419,11 @@ Comandos disponíveis:
</message>
<message>
<source>Invalid value for password length %1.</source>
<translation type="unfinished"/>
<translation>Valor inválido para o tamanho da senha %1.</translation>
</message>
<message>
<source>Could not create entry with path %1.</source>
<translation type="unfinished"/>
<translation>Não foi possível criar uma entrada com o caminho %1.</translation>
</message>
<message>
<source>Enter password for new entry: </source>
@@ -4427,15 +4431,15 @@ Comandos disponíveis:
</message>
<message>
<source>Writing the database failed %1.</source>
<translation type="unfinished"/>
<translation>Gravação do banco de dados falhou %1.</translation>
</message>
<message>
<source>Successfully added entry %1.</source>
<translation type="unfinished"/>
<translation>Entrada adicionada com sucesso %1.</translation>
</message>
<message>
<source>Copy the current TOTP to the clipboard.</source>
<translation type="unfinished"/>
<translation>Copie o TOTP atual para a área de transferência.</translation>
</message>
<message>
<source>Invalid timeout value %1.</source>
@@ -4516,11 +4520,11 @@ Comandos disponíveis:
</message>
<message>
<source>Type: Bruteforce</source>
<translation type="unfinished"/>
<translation>Tipo: Força Bruta</translation>
</message>
<message>
<source>Type: Dictionary</source>
<translation type="unfinished"/>
<translation>Tipo: Dicionário</translation>
</message>
<message>
<source>Type: Dict+Leet</source>
@@ -4540,15 +4544,15 @@ Comandos disponíveis:
</message>
<message>
<source>Type: Sequence</source>
<translation type="unfinished"/>
<translation>Tipo: Sequência</translation>
</message>
<message>
<source>Type: Spatial</source>
<translation type="unfinished"/>
<translation>Tipo: Espacial</translation>
</message>
<message>
<source>Type: Date</source>
<translation type="unfinished"/>
<translation>Tipo: Data</translation>
</message>
<message>
<source>Type: Bruteforce(Rep)</source>
@@ -4596,11 +4600,11 @@ Comandos disponíveis:
</message>
<message>
<source>*** Password length (%1) != sum of length of parts (%2) ***</source>
<translation type="unfinished"/>
<translation>*** Comprimento da senha (%1) != soma do comprimento das partes (%2) ***</translation>
</message>
<message>
<source>Failed to load key file %1: %2</source>
<translation type="unfinished"/>
<translation>Falha ao carregar o arquivo de chave %1: %2</translation>
</message>
<message>
<source>File %1 does not exist.</source>
@@ -4613,16 +4617,18 @@ Comandos disponíveis:
<message>
<source>Error while reading the database:
%1</source>
<translation type="unfinished"/>
<translation>Erro ao ler o banco de dados:
%1</translation>
</message>
<message>
<source>Error while parsing the database:
%1</source>
<translation type="unfinished"/>
<translation>Erro ao analisar o banco de dados:
%1</translation>
</message>
<message>
<source>Length of the generated password</source>
<translation type="unfinished"/>
<translation>Comprimento da senha gerada</translation>
</message>
<message>
<source>Use lowercase characters</source>
@@ -4646,11 +4652,11 @@ Comandos disponíveis:
</message>
<message>
<source>Exclude character set</source>
<translation type="unfinished"/>
<translation>Excluir conjunto de caracteres</translation>
</message>
<message>
<source>chars</source>
<translation type="unfinished"/>
<translation>caracteres</translation>
</message>
<message>
<source>Exclude similar looking characters</source>
@@ -4666,7 +4672,7 @@ Comandos disponíveis:
</message>
<message>
<source>Cannot find group %1.</source>
<translation type="unfinished"/>
<translation>Não foi possível encontrar o grupo %1.</translation>
</message>
<message>
<source>Error reading merge file:
@@ -4691,19 +4697,19 @@ Comandos disponíveis:
</message>
<message>
<source>Show the entry&apos;s current TOTP.</source>
<translation type="unfinished"/>
<translation>Mostrar o TOTP atual da entrada.</translation>
</message>
<message>
<source>ERROR: unknown attribute %1.</source>
<translation type="unfinished"/>
<translation>ERRO: atributo desconhecido %1.</translation>
</message>
<message>
<source>No program defined for clipboard manipulation</source>
<translation type="unfinished"/>
<translation>Nenhum programa definido para manipulação da área de transferência</translation>
</message>
<message>
<source>Unable to start program %1</source>
<translation type="unfinished"/>
<translation>Não é possível iniciar o programa %1</translation>
</message>
<message>
<source>file empty</source>
@@ -5268,7 +5274,7 @@ Comandos disponíveis:
</message>
<message>
<source>Could not embed database: Could not write file (%1)</source>
<translation type="unfinished"/>
<translation>Não foi possível incorporar o banco de dados: não foi possível gravar o arquivo (%1)</translation>
</message>
</context>
<context>

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@
</message>
<message>
<source>Project Maintainers:</source>
<translation>Projekt ansvariga:</translation>
<translation>Projektansvariga:</translation>
</message>
<message>
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
@@ -220,7 +220,7 @@
</message>
<message>
<source>Auto-Type typing delay</source>
<translation>Auto-skriv fördröjning</translation>
<translation>Fördröjning för auto-skriv</translation>
</message>
<message>
<source> ms</source>
@@ -765,7 +765,7 @@ Would you like to migrate your existing settings now?</source>
</message>
<message>
<source>Number of headers line to discard</source>
<translation>Antal av rubrik rader att kasta bort</translation>
<translation>Antal rubrikrader att kasta bort</translation>
</message>
<message>
<source>Consider &apos;\&apos; an escape character</source>
@@ -1258,7 +1258,7 @@ If you keep this number, your database may be too easy to crack!</source>
</message>
<message>
<source>Max. history items:</source>
<translation>Maxantal historik poster:</translation>
<translation>Maxantal historikposter:</translation>
</message>
<message>
<source>Max. history size:</source>
@@ -3856,7 +3856,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Word Separator:</source>
<translation>Ord separerare:</translation>
<translation>Ordseparerare:</translation>
</message>
<message>
<source>Copy</source>
@@ -4008,11 +4008,11 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Word Co&amp;unt:</source>
<translation type="unfinished"/>
<translation>&amp;Antal ord:</translation>
</message>
<message>
<source>Regenerate</source>
<translation type="unfinished"/>
<translation>Regenerera</translation>
</message>
</context>
<context>

View File

@@ -73,7 +73,7 @@
</message>
<message>
<source>Access error for config file %1</source>
<translation>%1 yapılandırma dosyası için erişim hatası</translation>
<translation>Yapılandırma dosyası erişim hatası %1</translation>
</message>
<message>
<source>Icon only</source>

View File

@@ -27,11 +27,11 @@
</message>
<message>
<source>Debug Info</source>
<translation>Інформація щодо зневадження</translation>
<translation>Зневаджувальна інформація</translation>
</message>
<message>
<source>Include the following information whenever you report a bug:</source>
<translation>Коли Ви повідомляєте про ваду, завжди долучайте таку інформацію:</translation>
<translation>Повідомляючи про проблему, завжди долучайте наступну інформацію:</translation>
</message>
<message>
<source>Copy to clipboard</source>
@@ -61,7 +61,7 @@
<name>ApplicationSettingsWidget</name>
<message>
<source>Application Settings</source>
<translation>Налаштування застосунку</translation>
<translation>Налаштування програми</translation>
</message>
<message>
<source>General</source>
@@ -100,7 +100,7 @@
<name>ApplicationSettingsWidgetGeneral</name>
<message>
<source>Basic Settings</source>
<translation>Базове налаштування</translation>
<translation>Основні налаштування</translation>
</message>
<message>
<source>Startup</source>
@@ -116,7 +116,7 @@
</message>
<message>
<source>Remember last key files and security dongles</source>
<translation>Пам&apos;ятати останні файли ключів і механізми захисту</translation>
<translation>Пам&apos;ятати останні файли ключів та апаратні ключі</translation>
</message>
<message>
<source>Load previous databases on startup</source>
@@ -172,7 +172,7 @@
</message>
<message>
<source>General</source>
<translation>Загальне</translation>
<translation>Загальні</translation>
</message>
<message>
<source>Hide toolbar (icons)</source>
@@ -1425,7 +1425,7 @@ Are you sure you want to continue without a password?</source>
<source>The created database has no key or KDF, refusing to save it.
This is definitely a bug, please report it to the developers.</source>
<translation>Створене сховище не має ані ключа, ані ФОК, і тому не може бути збереженим.
Це напевно вада у програмі. Будь ласка, повідомте про це розробникам.</translation>
Це певно є вадою програми, будь ласка, повідомте про це розробникам.</translation>
</message>
<message>
<source>The database file does not exist or is not accessible.</source>
@@ -3564,8 +3564,8 @@ We recommend you use the AppImage available on our downloads page.</source>
<message>
<source>NOTE: You are using a pre-release version of KeePassXC!
Expect some bugs and minor issues, this version is not meant for production use.</source>
<translation>&lt;b&gt;Примітка&lt;/b&gt;: Ви використовуєте попередню версію KeePassXC!
Зважайте на можливість деяких вади та незначних проблем, ця версія не призначена для повсякденного користування.</translation>
<translation>&lt;b&gt;Примітка&lt;/b&gt;: Ви використовуєте попередній випуск KeePassXC!
Зважайте на ймовірні помилки та незначні проблеми, ця версія не призначена для повсякденного користування.</translation>
</message>
<message>
<source>Check for updates on startup?</source>

View File

@@ -4672,7 +4672,7 @@ Available commands:
</message>
<message>
<source>Cannot find group %1.</source>
<translation>%1</translation>
<translation>%1</translation>
</message>
<message>
<source>Error reading merge file:
@@ -4760,7 +4760,7 @@ Available commands:
</message>
<message>
<source>No groups found</source>
<translation></translation>
<translation></translation>
</message>
<message>
<source>Create a new database.</source>

View File

@@ -54,7 +54,7 @@
</message>
<message>
<source>Use OpenSSH for Windows instead of Pageant</source>
<translation type="unfinished"/>
<translation>使 OpenSSH for Windows Pageant</translation>
</message>
</context>
<context>
@@ -93,7 +93,7 @@
</message>
<message>
<source>Follow style</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>
@@ -132,11 +132,11 @@
</message>
<message>
<source>Safely save database files (may be incompatible with Dropbox, etc)</source>
<translation type="unfinished"/>
<translation> ( Dropbox )</translation>
</message>
<message>
<source>Backup database file before saving</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Automatically save after every change</source>
@@ -168,7 +168,7 @@
</message>
<message>
<source>Hide the entry preview panel</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>General</source>
@@ -176,11 +176,11 @@
</message>
<message>
<source>Hide toolbar (icons)</source>
<translation type="unfinished"/>
<translation> ()</translation>
</message>
<message>
<source>Minimize instead of app exit</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Show a system tray icon</source>
@@ -220,7 +220,7 @@
</message>
<message>
<source>Auto-Type typing delay</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source> ms</source>
@@ -229,23 +229,23 @@
</message>
<message>
<source>Auto-Type start delay</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Check for updates at application startup</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Include pre-releases when checking for updates</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Movable toolbar</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Button style</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>
@@ -269,11 +269,11 @@
</message>
<message>
<source> min</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Forget TouchID after inactivity of</source>
<translation type="unfinished"/>
<translation> TouchID </translation>
</message>
<message>
<source>Convenience</source>
@@ -285,7 +285,7 @@
</message>
<message>
<source>Forget TouchID when session is locked or lid is closed</source>
<translation type="unfinished"/>
<translation> TouchID</translation>
</message>
<message>
<source>Lock databases after minimizing the window</source>
@@ -293,7 +293,7 @@
</message>
<message>
<source>Re-lock previously locked database after performing Auto-Type</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Don&apos;t require password repeat when it is visible</source>
@@ -301,15 +301,15 @@
</message>
<message>
<source>Don&apos;t hide passwords when editing them</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Don&apos;t use placeholder for empty password fields</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Hide passwords in the entry preview panel</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Hide entry notes by default</source>
@@ -429,11 +429,11 @@ Please select whether you want to allow access.</source>
<name>BrowserEntrySaveDialog</name>
<message>
<source>KeePassXC-Browser Save Entry</source>
<translation type="unfinished"/>
<translation>KeePassXC-Browser </translation>
</message>
<message>
<source>Ok</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Cancel</source>
@@ -442,7 +442,8 @@ Please select whether you want to allow access.</source>
<message>
<source>You have multiple databases open.
Please select the correct database for saving credentials.</source>
<translation type="unfinished"/>
<translation>
</translation>
</message>
</context>
<context>
@@ -589,11 +590,11 @@ Please select the correct database for saving credentials.</source>
</message>
<message>
<source>&amp;Tor Browser</source>
<translation type="unfinished"/>
<translation>&amp;Tor </translation>
</message>
<message>
<source>&lt;b&gt;Warning&lt;/b&gt;, the keepassxc-proxy application was not found!&lt;br /&gt;Please check the KeePassXC installation directory or confirm the custom path in advanced options.&lt;br /&gt;Browser integration WILL NOT WORK without the proxy application.&lt;br /&gt;Expected Path: </source>
<translation type="unfinished"/>
<translation>&lt;b&gt;&lt;/b&gt;,找不到 keepassxc-proxy 應用程式!&lt;br /&gt; KeePassXC &lt;br /&gt;&lt;br /&gt; </translation>
</message>
<message>
<source>Executable Files</source>
@@ -606,7 +607,7 @@ Please select the correct database for saving credentials.</source>
<message>
<source>Do not ask permission for HTTP &amp;Basic Auth</source>
<extracomment>An extra HTTP Basic Auth setting</extracomment>
<translation type="unfinished"/>
<translation> HTTP </translation>
</message>
<message>
<source>Due to Snap sandboxing, you must run a script to enable browser integration.&lt;br /&gt;You can obtain this script from %1</source>
@@ -618,7 +619,7 @@ Please select the correct database for saving credentials.</source>
</message>
<message>
<source>KeePassXC-Browser is needed for the browser integration to work. &lt;br /&gt;Download it for %1 and %2. %3</source>
<translation type="unfinished"/>
<translation> KeePassXC-Browser 使 %1 %2 %3</translation>
</message>
</context>
<context>
@@ -883,7 +884,7 @@ Would you like to migrate your existing settings now?</source>
<name>DatabaseOpenWidget</name>
<message>
<source>Enter master key</source>
<translation></translation>
<translation></translation>
</message>
<message>
<source>Key File:</source>
@@ -972,7 +973,7 @@ Please consider generating a new key file.</source>
</message>
<message>
<source>Master Key</source>
<translation type="unfinished"/>
<translation>主密碼</translation>
</message>
<message>
<source>Encryption Settings</source>
@@ -987,7 +988,7 @@ Please consider generating a new key file.</source>
<name>DatabaseSettingsWidgetBrowser</name>
<message>
<source>KeePassXC-Browser settings</source>
<translation type="unfinished"/>
<translation>KeePassXC-Browser 瀏覽器擴充功能設定</translation>
</message>
<message>
<source>&amp;Disconnect all browsers</source>
@@ -995,7 +996,7 @@ Please consider generating a new key file.</source>
</message>
<message>
<source>Forg&amp;et all site-specific settings on entries</source>
<translation type="unfinished"/>
<translation>遺忘目前項目中所有站台相關的設定 (&amp;e)</translation>
</message>
<message>
<source>Move KeePassHTTP attributes to KeePassXC-Browser &amp;custom data</source>
@@ -1032,7 +1033,7 @@ This may prevent connection to the browser plugin.</source>
</message>
<message>
<source>Disconnect all browsers</source>
<translation type="unfinished"/>
<translation>與所有瀏覽器中斷連線</translation>
</message>
<message>
<source>Do you really want to disconnect all browsers?
@@ -1291,7 +1292,7 @@ If you keep this number, your database may be too easy to crack!</source>
<name>DatabaseSettingsWidgetKeeShare</name>
<message>
<source>Sharing</source>
<translation type="unfinished"/>
<translation>分享</translation>
</message>
<message>
<source>Breadcrumb</source>
@@ -1299,11 +1300,11 @@ If you keep this number, your database may be too easy to crack!</source>
</message>
<message>
<source>Type</source>
<translation type="unfinished"/>
<translation>種類</translation>
</message>
<message>
<source>Path</source>
<translation type="unfinished"/>
<translation>路徑</translation>
</message>
<message>
<source>Last Signer</source>
@@ -1311,12 +1312,12 @@ If you keep this number, your database may be too easy to crack!</source>
</message>
<message>
<source>Certificates</source>
<translation type="unfinished"/>
<translation>憑證</translation>
</message>
<message>
<source> &gt; </source>
<comment>Breadcrumb separator</comment>
<translation type="unfinished"/>
<translation>&gt;</translation>
</message>
</context>
<context>
@@ -1335,13 +1336,15 @@ If you keep this number, your database may be too easy to crack!</source>
</message>
<message>
<source>No password set</source>
<translation type="unfinished"/>
<translation>沒有設定密碼</translation>
</message>
<message>
<source>WARNING! You have not set a password. Using a database without a password is strongly discouraged!
Are you sure you want to continue without a password?</source>
<translation type="unfinished"/>
<translation>警告!您尚未設定密碼。誠心建議不要使用不含密碼的資料庫檔案!
您確定還是要在無密碼的情形下繼續?</translation>
</message>
<message>
<source>Unknown error</source>
@@ -1349,7 +1352,7 @@ Are you sure you want to continue without a password?</source>
</message>
<message>
<source>Failed to change master key</source>
<translation type="unfinished"/>
<translation>更改主密碼失敗</translation>
</message>
</context>
<context>
@@ -1403,7 +1406,7 @@ Are you sure you want to continue without a password?</source>
</message>
<message>
<source>Database creation error</source>
<translation type="unfinished"/>
<translation>資料庫建立錯誤</translation>
</message>
<message>
<source>The created database has no key or KDF, refusing to save it.
@@ -1425,17 +1428,17 @@ This is definitely a bug, please report it to the developers.</source>
<message>
<source>%1 [New Database]</source>
<comment>Database tab name modifier</comment>
<translation type="unfinished"/>
<translation>%1 [新的資料庫]</translation>
</message>
<message>
<source>%1 [Locked]</source>
<comment>Database tab name modifier</comment>
<translation type="unfinished"/>
<translation>%1 [已鎖定]</translation>
</message>
<message>
<source>%1 [Read-only]</source>
<comment>Database tab name modifier</comment>
<translation type="unfinished"/>
<translation>%1 [唯讀]</translation>
</message>
</context>
<context>
@@ -1532,7 +1535,7 @@ Do you want to merge your changes?</source>
</message>
<message>
<source>Lock Database?</source>
<translation type="unfinished"/>
<translation>鎖定資料庫?</translation>
</message>
<message>
<source>You are editing an entry. Discard changes and lock anyway?</source>
@@ -1547,7 +1550,8 @@ Save changes?</source>
<message>
<source>Database was modified.
Save changes?</source>
<translation type="unfinished"/>
<translation>資料庫已修改。
儲存變更?</translation>
</message>
<message>
<source>Save changes?</source>
@@ -1560,7 +1564,7 @@ Error: %1</source>
</message>
<message>
<source>Disable safe saves?</source>
<translation>關閉安全存檔?</translation>
<translation>關閉安全存檔</translation>
</message>
<message>
<source>KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file.
@@ -2565,7 +2569,7 @@ This may cause the affected plugins to malfunction.</source>
<name>Kdbx3Reader</name>
<message>
<source>Unable to calculate master key</source>
<translation>無法計算主金鑰</translation>
<translation>無法計算主密碼</translation>
</message>
<message>
<source>Unable to issue challenge-response.</source>
@@ -2604,7 +2608,7 @@ This may cause the affected plugins to malfunction.</source>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>無法計算主金鑰</translation>
<translation>無法計算主密碼</translation>
</message>
</context>
<context>
@@ -2615,7 +2619,7 @@ This may cause the affected plugins to malfunction.</source>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>無法計算主金鑰</translation>
<translation>無法計算主密碼</translation>
</message>
<message>
<source>Invalid header checksum size</source>
@@ -2743,7 +2747,7 @@ This may cause the affected plugins to malfunction.</source>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>無法計算主金鑰</translation>
<translation>無法計算主密碼</translation>
</message>
<message>
<source>Failed to serialize KDF parameters variant map</source>
@@ -3010,7 +3014,7 @@ Line %2, column %3</source>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>無法計算主金鑰</translation>
<translation>無法計算主密碼</translation>
</message>
<message>
<source>Wrong key or database file is corrupt.</source>
@@ -3174,17 +3178,17 @@ Line %2, column %3</source>
<message>
<source>Change %1</source>
<comment>Change a key component</comment>
<translation type="unfinished"/>
<translation>更改%1</translation>
</message>
<message>
<source>Remove %1</source>
<comment>Remove a key component</comment>
<translation type="unfinished"/>
<translation>移除%1</translation>
</message>
<message>
<source>%1 set, click to change or remove</source>
<comment>Change or remove a key component</comment>
<translation type="unfinished"/>
<translation>%1已設定點選以更改或移除</translation>
</message>
</context>
<context>
@@ -3214,7 +3218,9 @@ Line %2, column %3</source>
unsupported in the future.
Please go to the master key settings and generate a new key file.</source>
<translation type="unfinished"/>
<translation>你正在使用未來將不再支援的舊式金鑰檔案格式。
請至主密碼設定產生新的金鑰。</translation>
</message>
<message>
<source>Error loading the key file '%1'
@@ -3414,11 +3420,11 @@ This version is not meant for production use.</source>
</message>
<message>
<source>&amp;Donate</source>
<translation type="unfinished"/>
<translation> (&amp;D)</translation>
</message>
<message>
<source>Report a &amp;bug</source>
<translation type="unfinished"/>
<translation> (&amp;b)</translation>
</message>
<message>
<source>WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard!
@@ -3427,75 +3433,75 @@ We recommend you use the AppImage available on our downloads page.</source>
</message>
<message>
<source>&amp;Import</source>
<translation type="unfinished"/>
<translation> (&amp;I)</translation>
</message>
<message>
<source>Copy att&amp;ribute...</source>
<translation type="unfinished"/>
<translation> (&amp;r)</translation>
</message>
<message>
<source>TOTP...</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>&amp;New database...</source>
<translation type="unfinished"/>
<translation>(&amp;N)</translation>
</message>
<message>
<source>Create a new database</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>&amp;Merge from database...</source>
<translation type="unfinished"/>
<translation>(&amp;M)</translation>
</message>
<message>
<source>Merge from another KDBX database</source>
<translation type="unfinished"/>
<translation> KDBX </translation>
</message>
<message>
<source>&amp;New entry</source>
<translation type="unfinished"/>
<translation>(&amp;N)</translation>
</message>
<message>
<source>Add a new entry</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>&amp;Edit entry</source>
<translation type="unfinished"/>
<translation>(&amp;E)</translation>
</message>
<message>
<source>View or edit entry</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>&amp;New group</source>
<translation type="unfinished"/>
<translation> (&amp;N)</translation>
</message>
<message>
<source>Add a new group</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Change master &amp;key...</source>
<translation type="unfinished"/>
<translation>(&amp;k)</translation>
</message>
<message>
<source>&amp;Database settings...</source>
<translation type="unfinished"/>
<translation>(&amp;D)</translation>
</message>
<message>
<source>Copy &amp;password</source>
<translation type="unfinished"/>
<translation>(&amp;p)</translation>
</message>
<message>
<source>Perform &amp;Auto-Type</source>
<translation type="unfinished"/>
<translation> (&amp;A)</translation>
</message>
<message>
<source>Open &amp;URL</source>
<translation type="unfinished"/>
<translation>(&amp;U)</translation>
</message>
<message>
<source>KeePass 1 database...</source>
@@ -3515,7 +3521,7 @@ We recommend you use the AppImage available on our downloads page.</source>
</message>
<message>
<source>Show TOTP...</source>
<translation type="unfinished"/>
<translation> TOTP</translation>
</message>
<message>
<source>Show TOTP QR Code...</source>
@@ -3626,11 +3632,11 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>En&amp;cryption Settings</source>
<translation type="unfinished"/>
<translation> (&amp;c)</translation>
</message>
<message>
<source>Here you can adjust the database encryption settings. Don&apos;t worry, you can change them later in the database settings.</source>
<translation type="unfinished"/>
<translation>調</translation>
</message>
<message>
<source>Advanced Settings</source>
@@ -3649,18 +3655,18 @@ Expect some bugs and minor issues, this version is not meant for production use.
</message>
<message>
<source>Here you can adjust the database encryption settings. Don&apos;t worry, you can change them later in the database settings.</source>
<translation type="unfinished"/>
<translation>調</translation>
</message>
</context>
<context>
<name>NewDatabaseWizardPageMasterKey</name>
<message>
<source>Database Master Key</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>A master key known only to you protects your database.</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>
@@ -5047,7 +5053,7 @@ Available commands:
</message>
<message>
<source>Path</source>
<translation type="unfinished"/>
<translation>路徑</translation>
</message>
<message>
<source>Status</source>
@@ -5361,11 +5367,11 @@ Available commands:
</message>
<message>
<source>Please try again later.</source>
<translation type="unfinished"/>
<translation>請稍後再試。</translation>
</message>
<message>
<source>Software Update</source>
<translation type="unfinished"/>
<translation>軟體更新</translation>
</message>
<message>
<source>A new version of KeePassXC is available!</source>
@@ -5377,7 +5383,7 @@ Available commands:
</message>
<message>
<source>Download it at keepassxc.org</source>
<translation type="unfinished"/>
<translation>在 keepassxc.org 下載</translation>
</message>
<message>
<source>You&apos;re up-to-date!</source>

View File

@@ -0,0 +1,11 @@
# /snap/local/launchers
Here are the launchers, or wrapper programs to deal with some runtime-fixable problems for the snapped applications, like setting proper environmental variables in snap.
In convention launchers are named _something_-launch, for dealing certain problem with _something_, and usually can be called in a stacked manner to consolidate their modifications.
```yaml
apps:
_app_name_:
command: foo-launch bar-launch _app_command_
```

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# This is the maintainence launcher for the snap, make necessary runtime environment changes to make the snap work here. You may also insert security confinement/deprecation/obsoletion notice of the snap here.
set \
-o errexit \
-o errtrace \
-o nounset \
-o pipefail
# gtk-common-themes support
export QT_QPA_PLATFORMTHEME=gtk3
# Finally run the next part of the command chain
exec "${@}"

View File

@@ -1,5 +1,5 @@
name: keepassxc
version: 2.4.1
version: 2.4.2
grade: stable
summary: Community-driven port of the Windows application “KeePass Password Safe”
description: |
@@ -9,16 +9,28 @@ description: |
confinement: strict
base: core18
plugs:
icon-themes: # fix mouse cursor theme
plugs: # plugs for theming, font settings, cursor and to use gtk3 file chooser
gtk-3-themes:
interface: content
target: $SNAP/data-dir/themes
default-provider: gtk-common-themes:gtk-3-themes
icon-themes:
interface: content
target: $SNAP/data-dir/icons
default-provider: gtk-common-themes
default-provider: gtk-common-themes:icon-themes
sound-themes:
interface: content
target: $SNAP/data-dir/sounds
default-provider: gtk-common-themes:sounds-themes
apps:
keepassxc:
command: desktop-launch keepassxc
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb, wayland, desktop-legacy]
adapter: full
command: usr/bin/keepassxc -style fusion
command-chain:
- bin/desktop-launch
- bin/gtk3-env-launch
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb, wayland, desktop-legacy, desktop]
desktop: usr/share/applications/org.keepassxc.KeePassXC.desktop
environment:
DISABLE_WAYLAND: 1
@@ -68,12 +80,12 @@ parts:
- libxtst6
- libqt5x11extras5
- libqt5svg5
- libqrencode3
- try: [libqrencode3, libqrencode4]
- libqt5concurrent5
- libquazip5-1
- libusb-1.0-0
- qtwayland5
- qt5-style-plugins # for mouse cursor theme fix
- qt5-gtk-platformtheme # for theming, font settings, cursor and to use gtk3 file chooser
override-build: |
snapcraftctl build
sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.KeePassXC.desktop
@@ -82,7 +94,15 @@ parts:
stage:
- -opt
after: [desktop-qt5]
launchers: # custom launcher to set QT_QPA_PLATFORMTHEME=gtk3 correctly
source: snap/local/launchers
plugin: dump
organize:
'*': bin/
stage:
- -bin/README.*
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
source-subdir: qt

View File

@@ -16,9 +16,6 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
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)
find_library(ZXCVBN_LIBRARIES zxcvbn)
if(NOT ZXCVBN_LIBRARIES)
add_library(zxcvbn STATIC zxcvbn/zxcvbn.c)
@@ -27,6 +24,7 @@ if(NOT ZXCVBN_LIBRARIES)
endif(NOT ZXCVBN_LIBRARIES)
set(keepassx_SOURCES
core/Alloc.cpp
core/AutoTypeAssociations.cpp
core/AutoTypeMatch.cpp
core/Compare.cpp
@@ -167,7 +165,8 @@ if(APPLE)
core/ScreenLockListenerMac.cpp
core/MacPasteboard.cpp
gui/macutils/MacUtils.cpp
gui/macutils/AppKitImpl.mm)
gui/macutils/AppKitImpl.mm
gui/macutils/AppKit.h)
endif()
if(UNIX AND NOT APPLE)
set(keepassx_SOURCES
@@ -192,8 +191,7 @@ 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(KeeShare-Secure WITH_XC_KEESHARE_SECURE "Sharing integration with KeeShare with secure sources")
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)")
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking")
if(APPLE)
@@ -254,8 +252,13 @@ endif()
if(WITH_XC_TOUCHID)
list(APPEND keepassx_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")
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)
@@ -270,6 +273,7 @@ target_link_libraries(keepassx_core
Qt5::Concurrent
Qt5::Network
Qt5::Widgets
${sodium_LIBRARY_RELEASE}
${YUBIKEY_LIBRARIES}
${ZXCVBN_LIBRARIES}
${ARGON2_LIBRARIES}
@@ -410,25 +414,19 @@ if(MINGW)
install(CODE "set(gp_tool \"objdump\")" COMPONENT Runtime)
include(DeployQt4)
install_qt4_executable(${PROGNAME}.exe)
# Deploy all 3rd party library dependencies first
install(CODE "include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${PROGNAME}.exe\" \"\" \"\")"
COMPONENT Runtime)
# install Qt5 plugins
set(PLUGINS_DIR ${Qt5_PREFIX}/share/qt5/plugins)
install(FILES
${PLUGINS_DIR}/platforms/qwindows$<$<CONFIG:Debug>:d>.dll
${PLUGINS_DIR}/platforms/qdirect2d$<$<CONFIG:Debug>:d>.dll
DESTINATION "platforms")
install(FILES ${PLUGINS_DIR}/styles/qwindowsvistastyle$<$<CONFIG:Debug>:d>.dll DESTINATION "styles")
install(FILES ${PLUGINS_DIR}/platforminputcontexts/qtvirtualkeyboardplugin$<$<CONFIG:Debug>:d>.dll DESTINATION "platforminputcontexts")
install(FILES ${PLUGINS_DIR}/iconengines/qsvgicon$<$<CONFIG:Debug>:d>.dll DESTINATION "iconengines")
install(FILES
${PLUGINS_DIR}/imageformats/qgif$<$<CONFIG:Debug>:d>.dll
${PLUGINS_DIR}/imageformats/qicns$<$<CONFIG:Debug>:d>.dll
${PLUGINS_DIR}/imageformats/qico$<$<CONFIG:Debug>:d>.dll
${PLUGINS_DIR}/imageformats/qjpeg$<$<CONFIG:Debug>:d>.dll
${PLUGINS_DIR}/imageformats/qwebp$<$<CONFIG:Debug>:d>.dll
DESTINATION "imageformats")
# Use windeployqt.exe to setup Qt dependencies
set(WINDEPLOYQT_MODE "--release")
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
set(WINDEPLOYQT_MODE "--debug")
endif()
install(CODE "execute_process(COMMAND ${WINDEPLOYQT_EXE} ${PROGNAME}.exe ${WINDEPLOYQT_MODE} WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX} OUTPUT_QUIET)"
COMPONENT Runtime)
# install CA cert chains
install(FILES ${Qt5_PREFIX}/ssl/certs/ca-bundle.crt DESTINATION "ssl/certs")

View File

@@ -47,7 +47,7 @@ AutoType::AutoType(QObject* parent, bool test)
, m_pluginLoader(new QPluginLoader(this))
, m_plugin(nullptr)
, m_executor(nullptr)
, m_windowFromGlobal(0)
, m_windowForGlobal(0)
{
// prevent crash when the plugin has unresolved symbols
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
@@ -90,7 +90,7 @@ void AutoType::loadPlugin(const QString& pluginPath)
if (m_plugin) {
if (m_plugin->isAvailable()) {
m_executor = m_plugin->createExecutor();
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered()));
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType()));
} else {
unloadPlugin();
}
@@ -222,6 +222,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt()));
// Used only for selected entry auto-type
if (!window) {
window = m_plugin->activeWindow();
}
@@ -240,6 +241,9 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
}
m_windowForGlobal = 0;
m_windowTitleForGlobal.clear();
// emit signal only if autotype performed correctly
emit autotypePerformed();
@@ -264,6 +268,13 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
executeAutoTypeActions(entry, hideWindow, sequences.first());
}
void AutoType::startGlobalAutoType()
{
m_windowForGlobal = m_plugin->activeWindow();
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
emit globalAutoTypeTriggered();
}
/**
* Global Autotype entry-point function
* Perform global Auto-Type on the active window
@@ -278,9 +289,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
return;
}
QString windowTitle = m_plugin->activeWindowTitle();
if (windowTitle.isEmpty()) {
if (m_windowTitleForGlobal.isEmpty()) {
m_inGlobalAutoTypeDialog.unlock();
return;
}
@@ -290,7 +299,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
for (const auto& db : dbList) {
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
for (Entry* entry : dbEntries) {
const QSet<QString> sequences = autoTypeSequences(entry, windowTitle).toSet();
const QSet<QString> sequences = autoTypeSequences(entry, m_windowTitleForGlobal).toSet();
for (const QString& sequence : sequences) {
if (!sequence.isEmpty()) {
matchList << AutoTypeMatch(entry, sequence);
@@ -304,8 +313,9 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
auto* msgBox = new QMessageBox();
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowTitle(tr("Auto-Type - KeePassXC"));
msgBox->setText(
tr("Couldn't find an entry that matches the window title:").append("\n\n").append(windowTitle));
msgBox->setText(tr("Couldn't find an entry that matches the window title:")
.append("\n\n")
.append(m_windowTitleForGlobal));
msgBox->setIcon(QMessageBox::Information);
msgBox->setStandardButtons(QMessageBox::Ok);
msgBox->show();
@@ -316,10 +326,9 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
m_inGlobalAutoTypeDialog.unlock();
emit autotypeRejected();
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence, m_windowForGlobal);
m_inGlobalAutoTypeDialog.unlock();
} else {
m_windowFromGlobal = m_plugin->activeWindow();
auto* selectDialog = new AutoTypeSelectDialog();
// connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex
@@ -327,11 +336,12 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal()));
selectDialog->setMatchList(matchList);
#if defined(Q_OS_MACOS)
#ifdef Q_OS_MACOS
m_plugin->raiseOwnWindow();
Tools::wait(500);
Tools::wait(200);
#endif
selectDialog->show();
selectDialog->raise();
// necessary when the main window is minimized
selectDialog->activateWindow();
}
@@ -339,8 +349,8 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
{
m_plugin->raiseWindow(m_windowFromGlobal);
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
m_plugin->raiseWindow(m_windowForGlobal);
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowForGlobal);
// make sure the mutex is definitely locked before we unlock it
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
@@ -353,6 +363,8 @@ void AutoType::autoTypeRejectedFromGlobal()
// so make sure the mutex is locked before we try unlocking it
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
m_inGlobalAutoTypeDialog.unlock();
m_windowForGlobal = 0;
m_windowTitleForGlobal.clear();
emit autotypeRejected();
}

View File

@@ -62,18 +62,19 @@ public slots:
void raiseWindow();
signals:
void globalShortcutTriggered();
void globalAutoTypeTriggered();
void autotypePerformed();
void autotypeRejected();
private slots:
void startGlobalAutoType();
void performAutoTypeFromGlobal(AutoTypeMatch match);
void autoTypeRejectedFromGlobal();
void unloadPlugin();
private:
explicit AutoType(QObject* parent = nullptr, bool test = false);
~AutoType();
~AutoType() override;
void loadPlugin(const QString& pluginPath);
void executeAutoTypeActions(const Entry* entry,
QWidget* hideWindow = nullptr,
@@ -94,9 +95,11 @@ private:
QPluginLoader* m_pluginLoader;
AutoTypePlatformInterface* m_plugin;
AutoTypeExecutor* m_executor;
WId m_windowFromGlobal;
static AutoType* m_instance;
QString m_windowTitleForGlobal;
WId m_windowForGlobal;
Q_DISABLE_COPY(AutoType)
};

View File

@@ -1,10 +1,6 @@
set(autotype_mac_SOURCES AutoTypeMac.cpp)
set(autotype_mac_mm_SOURCES
${CMAKE_SOURCE_DIR}/src/gui/macutils/AppKitImpl.mm
${CMAKE_SOURCE_DIR}/src/gui/macutils/MacUtils.cpp)
add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES} ${autotype_mac_mm_SOURCES})
add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES})
set_target_properties(keepassx-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon")
target_link_libraries(keepassx-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets)

View File

@@ -68,6 +68,11 @@ AutoTypeExecutor* AutoTypePlatformTest::createExecutor()
return new AutoTypeExecutorTest(this);
}
void AutoTypePlatformTest::triggerGlobalAutoType()
{
emit globalShortcutTriggered();
}
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
{
m_activeWindowTitle = title;

View File

@@ -48,6 +48,7 @@ public:
bool raiseOwnWindow() override;
#endif
void triggerGlobalAutoType() override;
void setActiveWindowTitle(const QString& title) override;
QString actionChars() override;

View File

@@ -26,6 +26,7 @@ public:
virtual ~AutoTypeTestInterface()
{
}
virtual void triggerGlobalAutoType() = 0;
virtual void setActiveWindowTitle(const QString& title) = 0;
virtual QString actionChars() = 0;

View File

@@ -94,6 +94,8 @@ private:
QString m_publicKey;
QString m_secretKey;
bool m_associated;
friend class TestBrowser;
};
#endif // BROWSERACTION_H

View File

@@ -120,6 +120,7 @@ void BrowserOptionDialog::loadSettings()
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
m_ui->customProxyLocation->setText(settings->customProxyLocation());
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials());
m_ui->chromeSupport->setChecked(settings->chromeSupport());
m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
@@ -176,6 +177,7 @@ void BrowserOptionDialog::saveSettings()
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked());
settings->setAllowExpiredCredentials(m_ui->allowExpiredCredentials->isChecked());
settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked());
settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked());
settings->setHttpAuthPermission(m_ui->httpAuthPermission->isChecked());

View File

@@ -219,6 +219,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="allowExpiredCredentials">
<property name="toolTip">
<string>Returns expired credentials. String [expired] is added to the title.</string>
</property>
<property name="text">
<string>&amp;Allow returning expired credentials.</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="sortByTitle">
<property name="text">

View File

@@ -172,9 +172,9 @@ QJsonArray BrowserService::getChildrenFromGroup(Group* group)
return groupList;
}
QJsonObject BrowserService::getDatabaseGroups()
QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& selectedDb)
{
auto db = getDatabase();
auto db = selectedDb ? selectedDb : getDatabase();
if (!db) {
return {};
}
@@ -296,6 +296,7 @@ QString BrowserService::storeKey(const QString& key)
do {
QInputDialog keyDialog;
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &keyDialog, SLOT(reject()));
keyDialog.setWindowTitle(tr("KeePassXC: New key association request"));
keyDialog.setLabelText(tr("You have received an association request for the above key.\n\n"
"If you would like to allow it access to your KeePassXC database,\n"
@@ -310,7 +311,7 @@ QString BrowserService::storeKey(const QString& key)
id = keyDialog.textValue();
if (ok != QDialog::Accepted || id.isEmpty()) {
if (ok != QDialog::Accepted || id.isEmpty() || !isDatabaseOpened()) {
hideWindow();
return {};
}
@@ -406,6 +407,11 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
return QJsonArray();
}
// Ensure that database is not locked when the popup was visible
if (!isDatabaseOpened()) {
return QJsonArray();
}
// Sort results
pwEntries = sortEntries(pwEntries, host, submitUrl);
@@ -447,11 +453,6 @@ void BrowserService::addEntry(const QString& id,
return;
}
auto* addEntryGroup = findCreateAddEntryGroup(db);
if (!addEntryGroup) {
return;
}
auto* entry = new Entry();
entry->setUuid(QUuid::createUuid());
entry->setTitle(QUrl(url).host());
@@ -459,16 +460,19 @@ void BrowserService::addEntry(const QString& id,
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
entry->setUsername(login);
entry->setPassword(password);
entry->setGroup(addEntryGroup);
// Select a group for the entry
if (!group.isEmpty()) {
if (db->rootGroup()) {
auto selectedGroup = db->rootGroup()->findGroupByUuid(Tools::hexToUuid(groupUuid));
if (selectedGroup && selectedGroup->name() == group) {
if (selectedGroup) {
entry->setGroup(selectedGroup);
} else {
entry->setGroup(getDefaultEntryGroup(db));
}
}
} else {
entry->setGroup(getDefaultEntryGroup(db));
}
const QString host = QUrl(url).host();
@@ -760,6 +764,7 @@ bool BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
m_dialogActive = true;
BrowserAccessControlDialog accessControlDialog;
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &accessControlDialog, SLOT(reject()));
accessControlDialog.setUrl(url);
accessControlDialog.setItems(pwEntriesToConfirm);
@@ -811,6 +816,10 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
res["totp"] = entry->totp();
}
if (entry->isExpired()) {
res["expired"] = "true";
}
if (browserSettings()->supportKphFields()) {
const EntryAttributes* attr = entry->attributes();
QJsonArray stringFields;
@@ -834,7 +843,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
return Unknown;
}
if (entry->isExpired()) {
return Denied;
return browserSettings()->allowExpiredCredentials() ? Allowed : Denied;
}
if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) {
return Allowed;
@@ -848,7 +857,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
return Unknown;
}
Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer<Database>& selectedDb)
Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb)
{
auto db = selectedDb ? selectedDb : getDatabase();
if (!db) {
@@ -861,7 +870,7 @@ Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer<Database>& s
}
const QString groupName =
QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); // TODO: setting to decide where new keys are created
QLatin1String(KEEPASSXCBROWSER_GROUP_NAME);
for (auto* g : rootGroup->groupsRecursive(true)) {
if (g->name() == groupName && !g->isRecycled()) {

View File

@@ -44,7 +44,7 @@ public:
bool openDatabase(bool triggerUnlock);
QString getDatabaseRootUuid();
QString getDatabaseRecycleBinUuid();
QJsonObject getDatabaseGroups();
QJsonObject getDatabaseGroups(const QSharedPointer<Database>& selectedDb = {});
QJsonObject createNewGroup(const QString& groupName);
QString getKey(const QString& id);
void addEntry(const QString& id,
@@ -114,7 +114,7 @@ private:
const QString& realm);
QJsonObject prepareEntry(const Entry* entry);
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
Group* findCreateAddEntryGroup(const QSharedPointer<Database>& selectedDb = {});
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
int
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
bool matchUrlScheme(const QString& url);
@@ -135,6 +135,8 @@ private:
bool m_bringToFrontRequested;
WindowState m_prevWindowState;
QUuid m_keepassBrowserUUID;
friend class TestBrowser;
};
#endif // BROWSERSERVICE_H

View File

@@ -194,6 +194,16 @@ void BrowserSettings::setUpdateBinaryPath(bool enabled)
config()->set("Browser/UpdateBinaryPath", enabled);
}
bool BrowserSettings::allowExpiredCredentials()
{
return config()->get("Browser/AllowExpiredCredentials", false).toBool();
}
void BrowserSettings::setAllowExpiredCredentials(bool enabled)
{
config()->set("Browser/AllowExpiredCredentials", enabled);
}
bool BrowserSettings::chromeSupport()
{
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);

View File

@@ -64,6 +64,8 @@ public:
void setCustomProxyLocation(const QString& location);
bool updateBinaryPath();
void setUpdateBinaryPath(bool enabled);
bool allowExpiredCredentials();
void setAllowExpiredCredentials(bool enabled);
bool chromeSupport();
void setChromeSupport(bool enabled);
bool chromiumSupport();

View File

@@ -16,7 +16,6 @@
if(WITH_XC_BROWSER)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
find_package(sodium 1.0.12 REQUIRED)
set(keepassxcbrowser_SOURCES
BrowserAccessControlDialog.cpp
@@ -33,5 +32,5 @@ if(WITH_XC_BROWSER)
Variant.cpp)
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network sodium)
target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${sodium_LIBRARY_RELEASE})
endif()

View File

@@ -19,6 +19,8 @@
#include "NativeMessagingBase.h"
#include <QStandardPaths>
#include "config-keepassx.h"
#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
#include <sys/event.h>
#include <sys/time.h>
@@ -138,7 +140,7 @@ QString NativeMessagingBase::getLocalServerPath() const
{
const QString serverPath = "/kpxc_server";
#if defined(KEEPASSXC_DIST_SNAP)
return QProcessEnvironment::systemEnvironment().value("SNAP_COMMON") + serverPath;
return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverPath;
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// Use XDG_RUNTIME_DIR instead of /tmp if it's available
QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);

View File

@@ -84,7 +84,7 @@ int Add::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli add");
errorTextStream << parser.helpText().replace("[options]", "add [options]");
return EXIT_FAILURE;
}

View File

@@ -38,6 +38,7 @@ target_link_libraries(keepassxc-cli
keepassx_core
Qt5::Core
${GCRYPT_LIBRARIES}
${sodium_LIBRARY_RELEASE}
${ARGON2_LIBRARIES}
${GPGERROR_LIBRARIES}
${ZLIB_LIBRARIES}

View File

@@ -63,7 +63,7 @@ int Clip::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2 && args.size() != 3) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli clip");
errorTextStream << parser.helpText().replace("[options]", "clip [options]");
return EXIT_FAILURE;
}

View File

@@ -70,7 +70,7 @@ int Create::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() < 1) {
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli create");
out << parser.helpText().replace("[options]", "create [options]");
return EXIT_FAILURE;
}

View File

@@ -58,7 +58,7 @@ int Diceware::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (!args.isEmpty()) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
errorTextStream << parser.helpText().replace("[options]", "diceware [options]");
return EXIT_FAILURE;
}
@@ -78,7 +78,7 @@ int Diceware::execute(const QStringList& arguments)
}
if (!dicewareGenerator.isValid()) {
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
outputTextStream << parser.helpText().replace("[options]", "diceware [options]");
return EXIT_FAILURE;
}

View File

@@ -88,7 +88,7 @@ int Edit::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit");
errorTextStream << parser.helpText().replace("[options]", "edit [options]");
return EXIT_FAILURE;
}

View File

@@ -171,7 +171,7 @@ int Estimate::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() > 1) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate");
errorTextStream << parser.helpText().replace("[options]", "estimate [options]");
return EXIT_FAILURE;
}

View File

@@ -57,7 +57,7 @@ int Extract::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 1) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract");
errorTextStream << parser.helpText().replace("[options]", "extract [options]");
return EXIT_FAILURE;
}

View File

@@ -84,7 +84,7 @@ int Generate::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (!args.isEmpty()) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
errorTextStream << parser.helpText().replace("[options]", "generate [options]");
return EXIT_FAILURE;
}
@@ -128,7 +128,7 @@ int Generate::execute(const QStringList& arguments)
passwordGenerator.setExcludedChars(parser.value(exclude));
if (!passwordGenerator.isValid()) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
errorTextStream << parser.helpText().replace("[options]", "generate [options]");
return EXIT_FAILURE;
}

View File

@@ -59,7 +59,7 @@ int List::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 1 && args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli ls");
errorTextStream << parser.helpText().replace("[options]", "ls [options]");
return EXIT_FAILURE;
}

View File

@@ -56,7 +56,7 @@ int Locate::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli locate");
errorTextStream << parser.helpText().replace("[options]", "locate [options]");
return EXIT_FAILURE;
}

View File

@@ -69,7 +69,7 @@ int Merge::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli merge");
errorTextStream << parser.helpText().replace("[options]", "merge [options]");
return EXIT_FAILURE;
}

View File

@@ -58,7 +58,7 @@ int Remove::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm");
errorTextStream << parser.helpText().replace("[options]", "rm [options]");
return EXIT_FAILURE;
}

View File

@@ -69,7 +69,7 @@ int Show::execute(const QStringList& arguments)
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli show");
errorTextStream << parser.helpText().replace("[options]", "show [options]");
return EXIT_FAILURE;
}

View File

@@ -19,6 +19,9 @@
#include <QProcessEnvironment>
#include <QTextCodec>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
TextStream::TextStream()
{
@@ -59,12 +62,26 @@ void TextStream::detectCodec()
{
QString codecName = "UTF-8";
auto env = QProcessEnvironment::systemEnvironment();
#ifdef Q_OS_WIN
if (!env.contains("SHELL")) {
// native shell (no Msys or cygwin)
WINBOOL success = false;
#ifdef CP_UTF8
success = SetConsoleOutputCP(CP_UTF8);
#endif
if (!success && !env.contains("SHELL")) {
// Fall back to cp850 if this is Windows without CP_UTF8 and we
// are running in a native shell (i.e., no Msys or Cygwin).
codecName = "Windows-850";
}
#else
if (env.contains("LANG") && !env.value("LANG").isEmpty() && env.value("LANG") != "C") {
// Only override codec if LANG is set, otherwise Qt will assume
// US-ASCII, which is almost always wrong and results in
// Unicode passwords being displayed as question marks.
codecName = QTextCodec::codecForLocale()->name();
}
#endif
codecName = env.value("ENCODING_OVERRIDE", codecName);
auto* codec = QTextCodec::codecForName(codecName.toLatin1());
if (codec) {

89
src/core/Alloc.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2019 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 <QtGlobal>
#include <cstdint>
#include <sodium.h>
#ifdef Q_OS_MACOS
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#if defined(NDEBUG) && !defined(__cpp_sized_deallocation)
#warning "KeePassXC is being compiled without sized deallocation support. Deletes may be slow."
#endif
/**
* Custom sized delete operator which securely zeroes out allocated
* memory before freeing it (requires C++14 sized deallocation support).
*/
void operator delete(void* ptr, std::size_t size) noexcept
{
if (!ptr) {
return;
}
sodium_memzero(ptr, size);
std::free(ptr);
}
void operator delete[](void* ptr, std::size_t size) noexcept
{
::operator delete(ptr, size);
}
/**
* Custom delete operator which securely zeroes out
* allocated memory before freeing it.
*/
void operator delete(void* ptr) noexcept
{
if (!ptr) {
return;
}
#if defined(Q_OS_WIN)
::operator delete(ptr, _msize(ptr));
#elif defined(Q_OS_MACOS)
::operator delete(ptr, malloc_size(ptr));
#elif defined(Q_OS_UNIX)
::operator delete(ptr, malloc_usable_size(ptr));
#else
// whatever OS this is, give up and simply free stuff
std::free(ptr);
#endif
}
void operator delete[](void* ptr) noexcept
{
::operator delete(ptr);
}
/**
* Custom insecure delete operator that does not zero out memory before
* freeing a buffer. Can be used for better performance.
*/
void operator delete(void* ptr, bool) noexcept
{
std::free(ptr);
}
void operator delete[](void* ptr, bool) noexcept
{
::operator delete(ptr, false);
}

View File

@@ -85,6 +85,12 @@ namespace Bootstrap
bootstrap();
MessageBox::initializeButtonDefs();
#ifdef KEEPASSXC_DIST_SNAP
// snap: force fallback theme to avoid using system theme (gtk integration)
// with missing actions just like on Windows and macOS
QIcon::setThemeSearchPaths(QStringList() << ":/icons");
#endif
#ifdef Q_OS_MACOS
// Don't show menu icons on OSX
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);

View File

@@ -16,9 +16,12 @@
*/
#include "CustomData.h"
#include "Clock.h"
#include "core/Global.h"
const QString CustomData::LastModified = "_LAST_MODIFIED";
CustomData::CustomData(QObject* parent)
: QObject(parent)
{
@@ -60,6 +63,7 @@ void CustomData::set(const QString& key, const QString& value)
if (addAttribute || changeValue) {
m_data.insert(key, value);
updateLastModified();
emit customDataModified();
}
@@ -74,6 +78,7 @@ void CustomData::remove(const QString& key)
m_data.remove(key);
updateLastModified();
emit removed(key);
emit customDataModified();
}
@@ -94,6 +99,7 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
m_data.remove(oldKey);
m_data.insert(newKey, data);
updateLastModified();
emit customDataModified();
emit renamed(oldKey, newKey);
}
@@ -108,9 +114,19 @@ void CustomData::copyDataFrom(const CustomData* other)
m_data = other->m_data;
updateLastModified();
emit reset();
emit customDataModified();
}
QDateTime CustomData::getLastModified() const
{
if (m_data.contains(LastModified)) {
return Clock::parse(m_data.value(LastModified));
}
return {};
}
bool CustomData::operator==(const CustomData& other) const
{
return (m_data == other.m_data);
@@ -152,3 +168,13 @@ int CustomData::dataSize() const
}
return size;
}
void CustomData::updateLastModified()
{
if (m_data.size() == 1 && m_data.contains(LastModified)) {
m_data.remove(LastModified);
return;
}
m_data.insert(LastModified, Clock::currentDateTimeUtc().toString());
}

View File

@@ -42,9 +42,12 @@ public:
int size() const;
int dataSize() const;
void copyDataFrom(const CustomData* other);
QDateTime getLastModified() const;
bool operator==(const CustomData& other) const;
bool operator!=(const CustomData& other) const;
static const QString LastModified;
signals:
void customDataModified();
void aboutToBeAdded(const QString& key);
@@ -55,6 +58,10 @@ signals:
void renamed(const QString& oldKey, const QString& newKey);
void aboutToBeReset();
void reset();
void lastModified();
private slots:
void updateLastModified();
private:
QHash<QString, QString> m_data;

View File

@@ -609,9 +609,6 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context)
// TODO HNH: missing handling of recycle bin, names, templates for groups and entries,
// public data (entries of newer dict override keys of older dict - ignoring
// their own age - it is enough if one entry of the whole dict is newer) => possible lost update
// TODO HNH: CustomData is merged with entries of the new customData overwrite entries
// of the older CustomData - the dict with the newest entry is considered
// newer regardless of the age of the other entries => possible lost update
ChangeList changes;
auto* sourceMetadata = context.m_sourceDb->metadata();
auto* targetMetadata = context.m_targetDb->metadata();
@@ -624,5 +621,32 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context)
changes << tr("Adding missing icon %1").arg(QString::fromLatin1(customIconId.toRfc4122().toHex()));
}
}
// Merge Custom Data if source is newer
const auto targetCustomDataModificationTime = sourceMetadata->customData()->getLastModified();
const auto sourceCustomDataModificationTime = targetMetadata->customData()->getLastModified();
if (!targetMetadata->customData()->contains(CustomData::LastModified) ||
(targetCustomDataModificationTime.isValid() && sourceCustomDataModificationTime.isValid() &&
targetCustomDataModificationTime > sourceCustomDataModificationTime)) {
const auto sourceCustomDataKeys = sourceMetadata->customData()->keys();
const auto targetCustomDataKeys = targetMetadata->customData()->keys();
// Check missing keys from source. Remove those from target
for (const auto& key : targetCustomDataKeys) {
if (!sourceMetadata->customData()->contains(key)) {
auto value = targetMetadata->customData()->value(key);
targetMetadata->customData()->remove(key);
changes << tr("Removed custom data %1 [%2]").arg(key, value);
}
}
// Transfer new/existing keys
for (const auto& key : sourceCustomDataKeys) {
auto value = sourceMetadata->customData()->value(key);
targetMetadata->customData()->set(key, value);
changes << tr("Adding custom data %1 [%2]").arg(key, value);
}
}
return changes;
}

View File

@@ -195,7 +195,7 @@ QPixmap Metadata::customIconScaledPixmap(const QUuid& uuid) const
QPixmapCache::Key& cacheKey = m_customIconScaledCacheKeys[uuid];
if (!QPixmapCache::find(cacheKey, &pixmap)) {
QImage image = m_customIcons.value(uuid).scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QImage image = m_customIcons.value(uuid).scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
pixmap = QPixmap::fromImage(image);
cacheKey = QPixmapCache::insert(pixmap);
}

View File

@@ -34,13 +34,14 @@
*/
void Translator::installTranslators()
{
QLocale locale;
QString language = config()->get("GUI/Language").toString();
if (language == "system" || language.isEmpty()) {
language = QLocale::system().name();
}
if (language == "en") {
if (!language.isEmpty() && language != "system") {
// use actual English translation instead of the English locale source language
language = "en_US";
if (language == "en") {
language = "en_US";
}
locale = QLocale(language);
}
const QStringList paths = {
@@ -51,11 +52,12 @@ void Translator::installTranslators()
bool translationsLoaded = false;
for (const QString& path : paths) {
translationsLoaded |= installTranslator(language, path) || installTranslator("en_US", path);
translationsLoaded |= installTranslator(locale, path) || installTranslator(QLocale("en_US"), path);
if (!installQtTranslator(language, path)) {
installQtTranslator("en", path);
installQtTranslator(QLocale("en"), path);
}
}
if (!translationsLoaded) {
// couldn't load configured language or fallback
qWarning("Couldn't load translations.");
@@ -114,10 +116,10 @@ QList<QPair<QString, QString>> Translator::availableLanguages()
* @param path local search path
* @return true on success
*/
bool Translator::installTranslator(const QString& language, const QString& path)
bool Translator::installTranslator(const QLocale& locale, const QString& path)
{
QScopedPointer<QTranslator> translator(new QTranslator(qApp));
if (translator->load(QString("keepassx_%1").arg(language), path)) {
if (translator->load(locale, "keepassx_", "", path)) {
return QCoreApplication::installTranslator(translator.take());
}
return false;
@@ -131,13 +133,12 @@ bool Translator::installTranslator(const QString& language, const QString& path)
* @param path local search path
* @return true on success
*/
bool Translator::installQtTranslator(const QString& language, const QString& path)
bool Translator::installQtTranslator(const QLocale& locale, const QString& path)
{
QScopedPointer<QTranslator> qtTranslator(new QTranslator(qApp));
if (qtTranslator->load(QString("qtbase_%1").arg(language), path)) {
if (qtTranslator->load(locale, "qtbase_", "", path)) {
return QCoreApplication::installTranslator(qtTranslator.take());
} else if (qtTranslator->load(QString("qtbase_%1").arg(language),
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
} else if (qtTranslator->load(locale, "qtbase_", "", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
return QCoreApplication::installTranslator(qtTranslator.take());
}
return false;

View File

@@ -20,6 +20,7 @@
#include <QPair>
#include <QString>
#include <QLocale>
class Translator
{
@@ -28,8 +29,8 @@ public:
static QList<QPair<QString, QString>> availableLanguages();
private:
static bool installTranslator(const QString& language, const QString& path);
static bool installQtTranslator(const QString& language, const QString& path);
static bool installTranslator(const QLocale& locale, const QString& path);
static bool installQtTranslator(const QLocale& locale, const QString& path);
};
#endif // KEEPASSX_TRANSLATOR_H

View File

@@ -35,7 +35,7 @@ Argon2Kdf::Argon2Kdf()
, m_memory(1 << 16)
, m_parallelism(static_cast<quint32>(QThread::idealThreadCount()))
{
m_rounds = 1;
m_rounds = 10;
}
quint32 Argon2Kdf::version() const

View File

@@ -35,7 +35,7 @@ bool Kdbx4Reader::readDatabaseImpl(QIODevice* device,
{
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
m_binaryPoolInverse.clear();
m_binaryPool.clear();
if (hasError()) {
return false;
@@ -273,11 +273,7 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device)
return false;
}
auto data = fieldData.mid(1);
if (m_binaryPoolInverse.contains(data)) {
qWarning("Skipping duplicate binary record");
break;
}
m_binaryPoolInverse.insert(data, QString::number(m_binaryPoolInverse.size()));
m_binaryPool.insert(QString::number(m_binaryPool.size()), data);
break;
}
}
@@ -422,17 +418,5 @@ QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
*/
QHash<QString, QByteArray> Kdbx4Reader::binaryPool() const
{
QHash<QString, QByteArray> binaryPool;
for (auto it = m_binaryPoolInverse.cbegin(); it != m_binaryPoolInverse.cend(); ++it) {
binaryPool.insert(it.value(), it.key());
}
return binaryPool;
}
/**
* @return mapping from binary data to attachment keys
*/
QHash<QByteArray, QString> Kdbx4Reader::binaryPoolInverse() const
{
return m_binaryPoolInverse;
return m_binaryPool;
}

View File

@@ -34,7 +34,6 @@ public:
const QByteArray& headerData,
QSharedPointer<const CompositeKey> key,
Database* db) override;
QHash<QByteArray, QString> binaryPoolInverse() const;
QHash<QString, QByteArray> binaryPool() const;
protected:
@@ -44,7 +43,7 @@ private:
bool readInnerHeaderField(QIODevice* device);
QVariantMap readVariantMap(QIODevice* device);
QHash<QByteArray, QString> m_binaryPoolInverse;
QHash<QString, QByteArray> m_binaryPool;
};
#endif // KEEPASSX_KDBX4READER_H

View File

@@ -64,6 +64,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
, m_globalAutoTypeModifiers(Qt::NoModifier)
{
setHeadline(tr("Application Settings"));
showApplyButton(false);
m_secUi->setupUi(m_secWidget);
m_generalUi->setupUi(m_generalWidget);
@@ -75,7 +76,6 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
}
connect(this, SIGNAL(accepted()), SLOT(saveSettings()));
connect(this, SIGNAL(apply()), SLOT(saveSettings()));
connect(this, SIGNAL(rejected()), SLOT(reject()));
// clang-format off

View File

@@ -212,7 +212,7 @@
<item>
<widget class="QCheckBox" name="fallbackToSearch">
<property name="text">
<string>Use DuckDuckGo as fallback for downloading website icons</string>
<string>Use DuckDuckGo service to download website icons</string>
</property>
</widget>
</item>

View File

@@ -59,10 +59,14 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
connect(autoType(), SIGNAL(globalAutoTypeTriggered()), SLOT(performGlobalAutoType()));
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase()));
// clang-format on
#ifdef Q_OS_MACOS
connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases()));
#endif
}
DatabaseTabWidget::~DatabaseTabWidget()
@@ -558,7 +562,7 @@ void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget,
#ifdef Q_OS_MACOS
if (intent == DatabaseOpenDialog::Intent::AutoType || intent == DatabaseOpenDialog::Intent::Browser) {
macUtils()->raiseOwnWindow();
Tools::wait(500);
Tools::wait(200);
}
#endif

View File

@@ -174,7 +174,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged()));
connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
connect(m_previewView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString)));
connect(m_previewView, SIGNAL(errorOccurred(QString)), SLOT(showErrorMessage(QString)));
connect(m_previewView, SIGNAL(entryUrlActivated(Entry*)), SLOT(openUrlForEntry(Entry*)));
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*)));
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged()));
@@ -190,7 +191,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
connect(m_keepass1OpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
connect(m_csvImportWizard, SIGNAL(importFinished(bool)), SLOT(csvImportFinished(bool)));
connect(m_fileWatcher.data(), SIGNAL(fileChanged()), this, SLOT(reloadDatabaseFile()));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
connect(this, SIGNAL(currentChanged(int)), SLOT(emitCurrentModeChanged()));
// clang-format on
connectDatabaseSignals();
@@ -652,6 +653,10 @@ void DatabaseWidget::openUrl()
void DatabaseWidget::openUrlForEntry(Entry* entry)
{
Q_ASSERT(entry);
if (!entry) {
return;
}
QString cmdString = entry->resolveMultiplePlaceholders(entry->url());
if (cmdString.startsWith("cmd://")) {
// check if decision to execute command was stored
@@ -695,9 +700,9 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
}
}
} else {
QString urlString = entry->webUrl();
if (!urlString.isEmpty()) {
QDesktopServices::openUrl(urlString);
QUrl url = QUrl(entry->url());
if (!url.isEmpty()) {
QDesktopServices::openUrl(url);
}
}
}
@@ -782,6 +787,9 @@ void DatabaseWidget::switchToMainView(bool previousDialogAccepted)
}
m_newParent = nullptr;
} else {
// Workaround: ensure entries are focused so search doesn't reset
m_entryView->setFocus();
}
setCurrentWidget(m_mainWidget);
@@ -1158,9 +1166,10 @@ void DatabaseWidget::onDatabaseModified()
{
if (!m_blockAutoSave && config()->get("AutoSaveAfterEveryChange").toBool()) {
save();
} else {
// Only block once, then reset
m_blockAutoSave = false;
}
m_blockAutoSave = false;
}
QString DatabaseWidget::getCurrentSearch()
@@ -1258,11 +1267,13 @@ bool DatabaseWidget::lock()
}
if (m_db->isModified()) {
bool saved = false;
// Attempt to save on exit, but don't block locking if it fails
if (config()->get("AutoSaveOnExit").toBool()) {
if (!save()) {
return false;
}
} else {
saved = save();
}
if (!saved) {
QString msg;
if (!m_db->metadata()->name().toHtmlEscaped().isEmpty()) {
msg = tr("\"%1\" was modified.\nSave changes?").arg(m_db->metadata()->name().toHtmlEscaped());
@@ -1521,11 +1532,14 @@ bool DatabaseWidget::save()
return true;
}
// Read-only and new databases ask for filename
if (m_db->isReadOnly() || m_db->filePath().isEmpty()) {
return saveAs();
}
// Prevent recursions and infinite save loops
blockAutoReload(true);
m_blockAutoSave = true;
++m_saveAttempts;
// TODO: Make this async, but lock out the database widget to prevent re-entrance
@@ -1536,6 +1550,7 @@ bool DatabaseWidget::save()
if (ok) {
m_saveAttempts = 0;
m_blockAutoSave = false;
return true;
}

View File

@@ -35,7 +35,8 @@ void DialogyWidget::keyPressEvent(QKeyEvent* e)
}
} else
#endif
if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
if (!e->modifiers() || e->modifiers() == Qt::ControlModifier
|| (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:

View File

@@ -197,8 +197,6 @@ void EditWidgetIcons::downloadFavicon()
QString fullyQualifiedDomain = m_url.host();
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
// Determine if host portion of URL is an IP address by resolving it and
// searching for a match with the returned address(es).
bool hostIsIp = false;
@@ -209,32 +207,35 @@ void EditWidgetIcons::downloadFavicon()
}
}
// Determine the second-level domain, if available
QString secondLevelDomain;
if (!hostIsIp) {
QString secondLevelDomain = getSecondLevelDomain(m_url);
// Attempt to simply load the favicon.ico file
if (fullyQualifiedDomain != secondLevelDomain) {
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
}
secondLevelDomain = getSecondLevelDomain(m_url);
}
// Try to use alternative fallback URL, if enabled
// Start with the "fallback" url (if enabled) to try to get the best favicon
if (config()->get("security/IconDownloadFallback", false).toBool()) {
QUrl fallbackUrl = QUrl("https://icons.duckduckgo.com");
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(fullyQualifiedDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
if (!hostIsIp) {
QString secondLevelDomain = getSecondLevelDomain(m_url);
if (fullyQualifiedDomain != secondLevelDomain) {
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(secondLevelDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
}
// Also try a direct pull of the second-level domain (if possible)
if (!hostIsIp && fullyQualifiedDomain != secondLevelDomain) {
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(secondLevelDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
}
}
// Add a direct pull of the website's own favicon.ico file
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
// Also try a direct pull of the second-level domain (if possible)
if (!hostIsIp && fullyQualifiedDomain != secondLevelDomain) {
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
}
// Use the first URL to start the download process
// If a favicon is not found, the next URL will be tried
startFetchFavicon(m_urlsToTry.takeFirst());
#endif
}
@@ -277,7 +278,7 @@ void EditWidgetIcons::fetchFinished()
if (!image.isNull()) {
if (!addCustomIcon(image)) {
emit messageEditEntry(tr("Custom icon already exists"), MessageWidget::Information);
} else if (!this->isVisible()) {
} else if (!isVisible()) {
// Show confirmation message if triggered from Entry tab download button
emit messageEditEntry(tr("Custom icon successfully downloaded"), MessageWidget::Positive);
}
@@ -289,7 +290,7 @@ void EditWidgetIcons::fetchFinished()
if (!fallbackEnabled) {
emit messageEditEntry(
tr("Unable to fetch favicon.") + "\n"
+ tr("Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security"),
+ tr("You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security"),
MessageWidget::Error);
} else {
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);

View File

@@ -54,11 +54,13 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
m_ui->entryAttachmentsWidget->setReadOnly(true);
m_ui->entryAttachmentsWidget->setButtonsVisible(false);
connect(m_ui->entryUrlLabel, SIGNAL(linkActivated(QString)), SLOT(openEntryUrl()));
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool)));
connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide()));
connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool)));
connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel()));
connect(&m_totpTimer, SIGNAL(timeout()), SLOT(updateTotpLabel()));
// Group
m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close"));
@@ -197,11 +199,12 @@ void EntryPreviewWidget::updateEntryGeneralTab()
}
m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl());
const QString url = m_currentEntry->webUrl();
const QString url = m_currentEntry->url();
if (!url.isEmpty()) {
// URL is well formed and can be opened in a browser
m_ui->entryUrlLabel->setUrl(url);
m_ui->entryUrlLabel->setCursor(Qt::PointingHandCursor);
m_ui->entryUrlLabel->setOpenExternalLinks(false);
} else {
m_ui->entryUrlLabel->setUrl({});
m_ui->entryUrlLabel->setCursor(Qt::ArrowCursor);
@@ -327,6 +330,13 @@ void EntryPreviewWidget::updateTabIndexes()
m_selectedTabGroup = m_ui->groupTabWidget->currentIndex();
}
void EntryPreviewWidget::openEntryUrl()
{
if (m_currentEntry) {
emit entryUrlActivated(m_currentEntry);
}
}
void EntryPreviewWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled)
{
const int tabIndex = tabWidget->indexOf(widget);

View File

@@ -43,6 +43,7 @@ public slots:
signals:
void errorOccurred(const QString& error);
void entryUrlActivated(Entry* entry);
private slots:
void updateEntryHeaderLine();
@@ -63,6 +64,7 @@ private slots:
void updateTotpLabel();
void updateTabIndexes();
void openEntryUrl();
private:
void setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled);

View File

@@ -41,6 +41,10 @@
#include "keys/FileKey.h"
#include "keys/PasswordKey.h"
#ifdef Q_OS_MACOS
#include "macutils/MacUtils.h"
#endif
#ifdef WITH_XC_UPDATECHECK
#include "gui/MessageBox.h"
#include "gui/UpdateCheckDialog.h"
@@ -135,6 +139,7 @@ MainWindow::MainWindow()
, m_trayIcon(nullptr)
, m_appExitCalled(false)
, m_appExiting(false)
, m_lastFocusOutTime(0)
{
g_MainWindow = this;
@@ -248,6 +253,9 @@ MainWindow::MainWindow()
m_ui->actionEntryCopyURL->setShortcutVisibleInContextMenu(true);
#endif
connect(m_ui->menuEntries, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
connect(m_ui->menuGroups, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
// Control window state
new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(showMinimized()));
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow()));
@@ -370,6 +378,9 @@ MainWindow::MainWindow()
#ifdef Q_OS_MACOS
setUnifiedTitleAndToolBarOnMac(true);
if (macUtils()->isDarkMode()) {
setStyleSheet("QToolButton {color:white;}");
}
#endif
#ifdef WITH_XC_UPDATECHECK
@@ -396,6 +407,12 @@ MainWindow::MainWindow()
connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock()));
#endif
// Tray Icon setup
connect(Application::instance(), SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*)));
m_trayIconTriggerReason = QSystemTrayIcon::Unknown;
m_trayIconTriggerTimer.setSingleShot(true);
connect(&m_trayIconTriggerTimer, SIGNAL(timeout()), SLOT(processTrayIconTrigger()));
updateTrayIcon();
if (config()->hasAccessError()) {
@@ -529,8 +546,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
switch (mode) {
case DatabaseWidget::Mode::ViewMode: {
// bool inSearch = dbWidget->isInSearchMode();
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && dbWidget->currentEntryHasFocus();
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && dbWidget->currentEntryHasFocus();
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && (m_contextMenuFocusLock || dbWidget->currentEntryHasFocus());
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && (m_contextMenuFocusLock || dbWidget->currentEntryHasFocus());
bool groupSelected = dbWidget->isGroupSelected();
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
@@ -857,7 +874,9 @@ void MainWindow::closeEvent(QCloseEvent* event)
return;
}
if (config()->get("GUI/MinimizeOnClose").toBool() && !m_appExitCalled) {
// Don't ignore close event when the app is hidden to tray.
// This can occur when the OS issues close events on shutdown.
if (config()->get("GUI/MinimizeOnClose").toBool() && !isHidden() && !m_appExitCalled) {
event->ignore();
hideWindow();
return;
@@ -912,7 +931,7 @@ bool MainWindow::saveLastDatabases()
}
QStringList openDatabases;
for (int i=0; i < m_ui->tabWidget->count(); ++i) {
for (int i = 0; i < m_ui->tabWidget->count(); ++i) {
auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i);
openDatabases.append(dbWidget->database()->filePath());
}
@@ -936,6 +955,8 @@ void MainWindow::updateTrayIcon()
QAction* actionToggle = new QAction(tr("Toggle window"), menu);
menu->addAction(actionToggle);
menu->addAction(m_ui->actionLockDatabases);
#ifdef Q_OS_MACOS
QAction* actionQuit = new QAction(tr("Quit KeePassXC"), menu);
menu->addAction(actionQuit);
@@ -969,13 +990,20 @@ void MainWindow::updateTrayIcon()
}
}
void MainWindow::releaseContextFocusLock()
{
m_contextMenuFocusLock = false;
}
void MainWindow::showEntryContextMenu(const QPoint& globalPos)
{
m_contextMenuFocusLock = true;
m_ui->menuEntries->popup(globalPos);
}
void MainWindow::showGroupContextMenu(const QPoint& globalPos)
{
m_contextMenuFocusLock = true;
m_ui->menuGroups->popup(globalPos);
}
@@ -1029,10 +1057,38 @@ void MainWindow::applySettingsChanges()
updateTrayIcon();
}
void MainWindow::focusWindowChanged(QWindow* focusWindow)
{
if (focusWindow != windowHandle()) {
m_lastFocusOutTime = Clock::currentSecondsSinceEpoch();
}
}
void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::MiddleClick) {
if (!m_trayIconTriggerTimer.isActive()) {
m_trayIconTriggerTimer.start(150);
}
// Overcome Qt bug https://bugreports.qt.io/browse/QTBUG-69698
// Store last issued tray icon activation reason to properly
// capture doubleclick events
m_trayIconTriggerReason = reason;
}
void MainWindow::processTrayIconTrigger()
{
if (m_trayIconTriggerReason == QSystemTrayIcon::DoubleClick) {
// Always toggle window on double click
toggleWindow();
} else if (m_trayIconTriggerReason == QSystemTrayIcon::Trigger
|| m_trayIconTriggerReason == QSystemTrayIcon::MiddleClick) {
// On single/middle click focus the window if it is not hidden
// and did not have focus less than a second ago, otherwise toggle
if (isHidden() || (Clock::currentSecondsSinceEpoch() - m_lastFocusOutTime) <= 1) {
toggleWindow();
} else {
bringToFront();
}
}
}

View File

@@ -85,6 +85,7 @@ private slots:
void showAboutDialog();
void showUpdateCheckStartup();
void showUpdateCheckDialog();
void focusWindowChanged(QWindow* focusWindow);
void hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested);
void openDonateUrl();
void openBugReportUrl();
@@ -107,6 +108,7 @@ private slots:
void showGroupContextMenu(const QPoint& globalPos);
void applySettingsChanges();
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
void processTrayIconTrigger();
void lockDatabasesAfterInactivity();
void forgetTouchIDAfterInactivity();
void handleScreenLock();
@@ -115,6 +117,7 @@ private slots:
void selectPreviousDatabaseTab();
void togglePasswordsHidden();
void toggleUsernamesHidden();
void releaseContextFocusLock();
private:
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
@@ -146,6 +149,10 @@ private:
bool m_appExitCalled;
bool m_appExiting;
bool m_contextMenuFocusLock;
uint m_lastFocusOutTime;
QTimer m_trayIconTriggerTimer;
QSystemTrayIcon::ActivationReason m_trayIconTriggerReason;
};
/**

View File

@@ -16,9 +16,6 @@
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">#SearchHelpWidget { background-color: #ffffff }</string>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>

View File

@@ -152,11 +152,6 @@ void EditEntryWidget::setupMain()
m_mainUi->expirePresets->setMenu(createPresetsMenu());
connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*)));
QAction* action = new QAction(this);
action->setShortcut(Qt::CTRL | Qt::Key_Return);
connect(action, SIGNAL(triggered()), this, SLOT(commitEntry()));
this->addAction(action);
m_mainUi->passwordGenerator->hide();
m_mainUi->passwordGenerator->reset();
}
@@ -285,7 +280,6 @@ void EditEntryWidget::setupEntryUpdate()
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(updateFaviconButtonEnable(QString)));
#endif
connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(setModified()));
connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setModified()));
@@ -968,6 +962,7 @@ void EditEntryWidget::cancel()
m_entry->setIcon(Entry::DefaultIconNumber);
}
bool accepted = false;
if (isModified()) {
auto result = MessageBox::question(this,
QString(),
@@ -975,18 +970,17 @@ void EditEntryWidget::cancel()
MessageBox::Cancel | MessageBox::Save | MessageBox::Discard,
MessageBox::Cancel);
if (result == MessageBox::Cancel) {
m_mainUi->passwordGenerator->reset();
return;
}
if (result == MessageBox::Save) {
commitEntry();
setModified(false);
} else if (result == MessageBox::Save) {
accepted = true;
if (!commitEntry()) {
return;
}
}
}
clear();
emit editFinished(!isModified());
emit editFinished(accepted);
}
void EditEntryWidget::clear()
@@ -1111,8 +1105,9 @@ void EditEntryWidget::updateCurrentAttribute()
void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected)
{
// Block signals to prevent extra calls
// Block signals to prevent modified being set
m_advancedUi->protectAttributeButton->blockSignals(true);
m_advancedUi->attributesEdit->blockSignals(true);
if (index.isValid()) {
QString key = m_attributesModel->keyByIndex(index);
@@ -1143,6 +1138,7 @@ void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected)
}
m_advancedUi->protectAttributeButton->blockSignals(false);
m_advancedUi->attributesEdit->blockSignals(false);
}
void EditEntryWidget::protectCurrentAttribute(bool state)

View File

@@ -7,9 +7,11 @@
#include <QFile>
#include <QFileInfo>
#include <QMimeData>
#include <QProcessEnvironment>
#include <QTemporaryFile>
#include "EntryAttachmentsModel.h"
#include "config-keepassx.h"
#include "core/Config.h"
#include "core/EntryAttachments.h"
#include "core/Tools.h"
@@ -324,7 +326,12 @@ bool EntryAttachmentsWidget::openAttachment(const QModelIndex& index, QString& e
const QByteArray attachmentData = m_entryAttachments->value(filename);
// tmp file will be removed once the database (or the application) has been closed
#ifdef KEEPASSXC_DIST_SNAP
const QString tmpFileTemplate =
QString("%1/XXXXXX.%2").arg(QProcessEnvironment::systemEnvironment().value("SNAP_USER_DATA"), filename);
#else
const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
#endif
QScopedPointer<QTemporaryFile> tmpFile(new QTemporaryFile(tmpFileTemplate, this));

View File

@@ -139,17 +139,18 @@ void EntryView::keyPressEvent(QKeyEvent* event)
}
int last = m_model->rowCount() - 1;
if (last > 0) {
if (event->key() == Qt::Key_Up && currentIndex().row() == 0) {
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0));
setCurrentEntry(m_model->entryFromIndex(index));
return;
}
if (event->key() == Qt::Key_Up && currentIndex().row() == 0) {
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0));
setCurrentEntry(m_model->entryFromIndex(index));
return;
}
if (event->key() == Qt::Key_Down && currentIndex().row() == last) {
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
setCurrentEntry(m_model->entryFromIndex(index));
return;
if (event->key() == Qt::Key_Down && currentIndex().row() == last) {
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
setCurrentEntry(m_model->entryFromIndex(index));
return;
}
}
QTreeView::keyPressEvent(event);

View File

@@ -227,6 +227,9 @@ void EditGroupWidget::cancel()
tr("Entry has unsaved changes"),
MessageBox::Cancel | MessageBox::Save | MessageBox::Discard,
MessageBox::Cancel);
if (result == MessageBox::Cancel) {
return;
}
if (result == MessageBox::Save) {
apply();
setModified(false);

View File

@@ -72,6 +72,12 @@ void GroupView::dragMoveEvent(QDragMoveEvent* event)
}
}
void GroupView::focusInEvent(QFocusEvent* event)
{
emitGroupChanged();
QTreeView::focusInEvent(event);
}
Group* GroupView::currentGroup()
{
if (currentIndex() == QModelIndex()) {

View File

@@ -47,6 +47,7 @@ private slots:
protected:
void dragMoveEvent(QDragMoveEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
private:
void recInitExpanded(Group* group);

View File

@@ -19,14 +19,15 @@
#ifndef KEEPASSX_APPKIT_H
#define KEEPASSX_APPKIT_H
#include <QObject>
#include <unistd.h>
extern "C" {
class AppKit
class AppKit : public QObject
{
Q_OBJECT
public:
AppKit();
AppKit(QObject* parent = nullptr);
~AppKit();
pid_t lastActiveProcessId();
@@ -37,10 +38,11 @@ public:
bool isHidden(pid_t pid);
bool isDarkMode();
signals:
void lockDatabases();
private:
void *self;
};
} // extern "C"
#endif // KEEPASSX_APPKIT_H

View File

@@ -22,6 +22,10 @@
#import <AppKit/NSRunningApplication.h>
@interface AppKitImpl : NSObject
{
AppKit *m_appkit;
}
- (id) initWithObject:(AppKit *)appkit;
@property (strong) NSRunningApplication *lastActiveApplication;
@@ -31,5 +35,6 @@
- (bool) hideProcess:(pid_t) pid;
- (bool) isHidden:(pid_t) pid;
- (bool) isDarkMode;
- (void) userSwitchHandler:(NSNotification*) notification;
@end

View File

@@ -22,19 +22,22 @@
@implementation AppKitImpl
AppKit::AppKit()
- (id) initWithObject:(AppKit *)appkit
{
self = [[AppKitImpl alloc] init];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
self = [super init];
if (self) {
m_appkit = appkit;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
selector:@selector(didDeactivateApplicationObserver:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
AppKit::~AppKit()
{
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast<id>(self)];
[static_cast<id>(self) dealloc];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
selector:@selector(userSwitchHandler:)
name:NSWorkspaceSessionDidResignActiveNotification
object:nil];
}
return self;
}
//
@@ -104,10 +107,34 @@ AppKit::~AppKit()
&& NSOrderedSame == [style caseInsensitiveCompare:@"dark"] );
}
//
// Notification for user switch
//
- (void) userSwitchHandler:(NSNotification*) notification
{
if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification] && m_appkit)
{
emit m_appkit->lockDatabases();
}
}
@end
//
// ------------------------- C++ Trampolines -------------------------
//
AppKit::AppKit(QObject* parent) : QObject(parent)
{
self = [[AppKitImpl alloc] initWithObject:this];
}
AppKit::~AppKit()
{
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast<id>(self)];
[static_cast<id>(self) dealloc];
}
pid_t AppKit::lastActiveProcessId()
{
return [static_cast<id>(self) lastActiveApplication].processIdentifier;
@@ -142,5 +169,3 @@ bool AppKit::isDarkMode()
{
return [static_cast<id>(self) isDarkMode];
}
@end

View File

@@ -24,7 +24,7 @@ MacUtils* MacUtils::m_instance = nullptr;
MacUtils::MacUtils(QObject* parent) : QObject(parent)
, m_appkit(new AppKit())
{
connect(m_appkit.data(), SIGNAL(lockDatabases()), SIGNAL(lockDatabases()));
}
MacUtils::~MacUtils()

View File

@@ -39,14 +39,16 @@ public:
bool isHidden();
bool isDarkMode();
signals:
void lockDatabases();
private:
explicit MacUtils(QObject* parent = nullptr);
~MacUtils();
private:
std::unique_ptr<AppKit> m_appkit;
QScopedPointer<AppKit> m_appkit;
static MacUtils* m_instance;
void* self;
Q_DISABLE_COPY(MacUtils)
};

View File

@@ -1,4 +1,6 @@
if(WITH_XC_KEESHARE)
set(WITH_XC_KEESHARE_INSECURE ON PARENT_SCOPE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(keeshare_SOURCES
@@ -15,9 +17,19 @@ if(WITH_XC_KEESHARE)
)
add_library(keeshare STATIC ${keeshare_SOURCES})
if(WITH_XC_KEESHARE_SECURE)
target_link_libraries(keeshare Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${QUAZIP_LIBRARIES} ${crypto_ssh_LIB})
target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB})
# Try to find libquazip5, if found, enable secure sharing
find_package(QuaZip)
if(QUAZIP_FOUND)
set(WITH_XC_KEESHARE_SECURE ON PARENT_SCOPE)
target_include_directories(keeshare SYSTEM PRIVATE ${QUAZIP_INCLUDE_DIR})
target_link_libraries(keeshare PRIVATE ${QUAZIP_LIBRARIES})
else()
target_link_libraries(keeshare Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB})
set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE)
message(STATUS "KeeShare: Secure container support is DISABLED; quazip library not found")
endif()
endif()
else(WITH_XC_KEESHARE)
set(WITH_XC_KEESHARE_INSECURE OFF PARENT_SCOPE)
set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE)
endif(WITH_XC_KEESHARE)

View File

@@ -46,8 +46,8 @@
#include <QStringBuilder>
#if defined(WITH_XC_KEESHARE_SECURE)
#include <quazip5/quazip.h>
#include <quazip5/quazipfile.h>
#include <quazip.h>
#include <quazipfile.h>
#endif
namespace

View File

@@ -18,19 +18,35 @@
#include "FileKey.h"
#include <QFile>
#include "core/Tools.h"
#include "crypto/CryptoHash.h"
#include "crypto/Random.h"
#include <QFile>
#include <sodium.h>
#include <gcrypt.h>
#include <algorithm>
#include <cstring>
QUuid FileKey::UUID("a584cbc4-c9b4-437e-81bb-362ca9709273");
constexpr int FileKey::SHA256_SIZE;
FileKey::FileKey()
: Key(UUID)
, m_key(static_cast<char*>(gcry_malloc_secure(SHA256_SIZE)))
{
}
FileKey::~FileKey()
{
if (m_key) {
gcry_free(m_key);
m_key = nullptr;
}
}
/**
* Read key file from device while trying to detect its file format.
*
@@ -148,7 +164,10 @@ bool FileKey::load(const QString& fileName, QString* errorMsg)
*/
QByteArray FileKey::rawKey() const
{
return m_key;
if (!m_key) {
return {};
}
return QByteArray::fromRawData(m_key, SHA256_SIZE);
}
/**
@@ -223,12 +242,15 @@ bool FileKey::loadXml(QIODevice* device)
}
}
bool ok = false;
if (!xmlReader.error() && correctMeta && !data.isEmpty()) {
m_key = data;
return true;
std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size()));
ok = true;
}
return false;
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
return ok;
}
/**
@@ -293,7 +315,8 @@ bool FileKey::loadBinary(QIODevice* device)
if (!Tools::readAllFromDevice(device, data) || data.size() != 32) {
return false;
} else {
m_key = data;
std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size()));
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
return true;
}
}
@@ -321,12 +344,15 @@ bool FileKey::loadHex(QIODevice* device)
}
QByteArray key = QByteArray::fromHex(data);
sodium_memzero(data.data(), static_cast<std::size_t>(data.capacity()));
if (key.size() != 32) {
return false;
}
m_key = key;
std::memcpy(m_key, key.data(), std::min(SHA256_SIZE, key.size()));
sodium_memzero(key.data(), static_cast<std::size_t>(key.capacity()));
return true;
}
@@ -348,7 +374,9 @@ bool FileKey::loadHashed(QIODevice* device)
cryptoHash.addData(buffer);
} while (!buffer.isEmpty());
m_key = cryptoHash.result();
auto result = cryptoHash.result();
std::memcpy(m_key, result.data(), std::min(SHA256_SIZE, result.size()));
sodium_memzero(result.data(), static_cast<std::size_t>(result.capacity()));
return true;
}

View File

@@ -40,6 +40,7 @@ public:
};
FileKey();
~FileKey() override;
bool load(QIODevice* device);
bool load(const QString& fileName, QString* errorMsg = nullptr);
QByteArray rawKey() const override;
@@ -48,6 +49,8 @@ public:
static bool create(const QString& fileName, QString* errorMsg = nullptr, int size = 128);
private:
static constexpr int SHA256_SIZE = 32;
bool loadXml(QIODevice* device);
bool loadXmlMeta(QXmlStreamReader& xmlReader);
QByteArray loadXmlKey(QXmlStreamReader& xmlReader);
@@ -55,7 +58,7 @@ private:
bool loadHex(QIODevice* device);
bool loadHashed(QIODevice* device);
QByteArray m_key;
char* m_key = nullptr;
Type m_type = None;
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2019 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
@@ -16,35 +16,51 @@
*/
#include "PasswordKey.h"
#include "core/Tools.h"
#include "crypto/CryptoHash.h"
#include <gcrypt.h>
#include <algorithm>
#include <cstring>
QUuid PasswordKey::UUID("77e90411-303a-43f2-b773-853b05635ead");
constexpr int PasswordKey::SHA256_SIZE;
PasswordKey::PasswordKey()
: Key(UUID)
, m_key(static_cast<char*>(gcry_malloc_secure(SHA256_SIZE)))
{
}
PasswordKey::PasswordKey(const QString& password)
: Key(UUID)
, m_key(static_cast<char*>(gcry_malloc_secure(SHA256_SIZE)))
{
setPassword(password);
}
PasswordKey::~PasswordKey()
{
if (m_key) {
gcry_free(m_key);
m_key = nullptr;
}
}
QSharedPointer<PasswordKey> PasswordKey::fromRawKey(const QByteArray& rawKey)
{
auto result = QSharedPointer<PasswordKey>::create();
result->m_key = rawKey;
std::memcpy(result->m_key, rawKey.data(), std::min(SHA256_SIZE, rawKey.size()));
return result;
}
QByteArray PasswordKey::rawKey() const
{
return m_key;
return QByteArray::fromRawData(m_key, SHA256_SIZE);
}
void PasswordKey::setPassword(const QString& password)
{
m_key = CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256);
std::memcpy(m_key, CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256).data(), SHA256_SIZE);
}

View File

@@ -30,13 +30,16 @@ public:
PasswordKey();
explicit PasswordKey(const QString& password);
~PasswordKey() override;
QByteArray rawKey() const override;
void setPassword(const QString& password);
static QSharedPointer<PasswordKey> fromRawKey(const QByteArray& rawKey);
private:
QByteArray m_key;
static constexpr int SHA256_SIZE = 32;
char* m_key = nullptr;
};
#endif // KEEPASSX_PASSWORDKEY_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
* Copyright (C) 2017 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
@@ -32,6 +32,10 @@
#include <QXmlStreamReader>
#include <QtConcurrent>
#include <gcrypt.h>
#include <sodium.h>
#include <cstring>
QUuid YkChallengeResponseKey::UUID("e092495c-e77d-498b-84a1-05ae0d955508");
YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking)
@@ -45,9 +49,18 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking)
}
}
YkChallengeResponseKey::~YkChallengeResponseKey()
{
if (m_key) {
gcry_free(m_key);
m_keySize = 0;
m_key = nullptr;
}
}
QByteArray YkChallengeResponseKey::rawKey() const
{
return m_key;
return QByteArray::fromRawData(m_key, static_cast<int>(m_keySize));
}
/**
@@ -67,14 +80,22 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned int
emit userInteractionRequired();
}
QByteArray key;
auto result = AsyncTask::runAndWaitForFuture(
[this, challenge]() { return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); });
[this, challenge, &key]() { return YubiKey::instance()->challenge(m_slot, true, challenge, key); });
if (m_blocking) {
emit userConfirmed();
}
if (result == YubiKey::SUCCESS) {
if (m_key) {
gcry_free(m_key);
}
m_keySize = static_cast<std::size_t>(key.size());
m_key = static_cast<char*>(gcry_malloc_secure(m_keySize));
std::memcpy(m_key, key.data(), m_keySize);
sodium_memzero(key.data(), static_cast<std::size_t>(key.capacity()));
return true;
}
} while (retries > 0);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2019 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
@@ -32,6 +32,7 @@ public:
static QUuid UUID;
explicit YkChallengeResponseKey(int slot = -1, bool blocking = false);
~YkChallengeResponseKey() override;
QByteArray rawKey() const override;
bool challenge(const QByteArray& challenge) override;
@@ -52,7 +53,8 @@ signals:
void userConfirmed();
private:
QByteArray m_key;
char* m_key = nullptr;
std::size_t m_keySize = 0;
int m_slot;
bool m_blocking;
};

View File

@@ -18,12 +18,13 @@ if(WITH_XC_BROWSER)
include_directories(${BROWSER_SOURCE_DIR})
set(proxy_SOURCES
../core/Alloc.cpp
keepassxc-proxy.cpp
${BROWSER_SOURCE_DIR}/NativeMessagingBase.cpp
NativeMessagingHost.cpp)
add_library(proxy STATIC ${proxy_SOURCES})
target_link_libraries(proxy Qt5::Core Qt5::Network)
target_link_libraries(proxy Qt5::Core Qt5::Network ${sodium_LIBRARY_RELEASE})
add_executable(keepassxc-proxy keepassxc-proxy.cpp)
target_link_libraries(keepassxc-proxy proxy)

View File

@@ -128,9 +128,9 @@ QString Totp::writeSettings(const QSharedPointer<Totp::Settings>& settings,
auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1")
.arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)),
username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)),
QString(Base32::sanitizeInput(settings->key.toLatin1())))
.arg(settings->step)
.arg(settings->digits);
QString(Base32::sanitizeInput(settings->key.toLatin1())),
QString::number(settings->step),
QString::number(settings->digits));
if (!settings->encoder.name.isEmpty()) {
urlstring.append("&encoder=").append(settings->encoder.name);

View File

@@ -15,6 +15,7 @@
inline void debug(const char* message, ...)
{
Q_UNUSED(message);
// qWarning(...);
}
@@ -258,6 +259,7 @@ bool TouchID::authenticate(const QString& message) const
NSString* authMessage = msg.toNSString(); // autoreleased
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:authMessage reply:^(BOOL success, NSError* error) {
Q_UNUSED(error);
result = success ? kTouchIDResultAllowed : kTouchIDResultFailed;
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}];

View File

@@ -220,6 +220,11 @@ add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp
add_unit_test(NAME testtools SOURCES TestTools.cpp
LIBS ${TEST_LIBRARIES})
if(WITH_XC_BROWSER)
add_unit_test(NAME testbrowser SOURCES TestBrowser.cpp
LIBS ${TEST_LIBRARIES})
endif()
if(WITH_GUI_TESTS)
# CLI clip tests need X environment on Linux

View File

@@ -157,6 +157,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch()
void TestAutoType::testGlobalAutoTypeWithOneMatch()
{
m_test->setActiveWindowTitle("custom window");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
@@ -167,6 +168,7 @@ void TestAutoType::testGlobalAutoTypeTitleMatch()
config()->set("AutoTypeEntryTitleMatch", true);
m_test->setActiveWindowTitle("An Entry Title!");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
@@ -177,6 +179,7 @@ void TestAutoType::testGlobalAutoTypeUrlMatch()
config()->set("AutoTypeEntryTitleMatch", true);
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
@@ -187,6 +190,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
config()->set("AutoTypeEntryTitleMatch", true);
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
@@ -195,6 +199,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
{
m_test->setActiveWindowTitle("An Entry Title!");
m_test->triggerGlobalAutoType();
MessageBox::setNextAnswer(MessageBox::Ok);
m_autoType->performGlobalAutoType(m_dbList);
@@ -205,58 +210,68 @@ void TestAutoType::testGlobalAutoTypeRegExp()
{
// substring matches are ok
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex1"));
m_test->clearActions();
// should be case-insensitive
m_test->setActiveWindowTitle("lorem regex1 ipsum");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex1"));
m_test->clearActions();
// exact match
m_test->setActiveWindowTitle("REGEX2");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex2"));
m_test->clearActions();
// a bit more complicated regex
m_test->setActiveWindowTitle("REGEX3-R2D2");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex3"));
m_test->clearActions();
// with custom attributes
m_test->setActiveWindowTitle("CustomAttr1");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
m_test->clearActions();
// with (non uppercase) undefined custom attributes
m_test->setActiveWindowTitle("CustomAttr2");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString(""));
m_test->clearActions();
// with mixedcase default attributes
m_test->setActiveWindowTitle("CustomAttr3");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
m_test->clearActions();
// with resolve placeholders in window association title
m_test->setActiveWindowTitle("AttrValueFirst");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
m_test->clearActions();
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
m_test->clearActions();
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
m_test->triggerGlobalAutoType();
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
m_test->clearActions();

344
tests/TestBrowser.cpp Normal file
View File

@@ -0,0 +1,344 @@
/*
* Copyright (C) 2019 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 "TestBrowser.h"
#include "TestGlobal.h"
#include "crypto/Crypto.h"
#include "sodium/crypto_box.h"
#include "browser/BrowserSettings.h"
#include <QString>
QTEST_GUILESS_MAIN(TestBrowser)
const QString PUBLICKEY = "UIIPObeoya1G8g1M5omgyoPR/j1mR1HlYHu0wHCgMhA=";
const QString SECRETKEY = "B8ei4ZjQJkWzZU2SK/tBsrYRwp+6ztEMf5GFQV+i0yI=";
const QString SERVERPUBLICKEY = "lKnbLhrVCOqzEjuNoUz1xj9EZlz8xeO4miZBvLrUPVQ=";
const QString SERVERSECRETKEY = "tbPQcghxfOgbmsnEqG2qMIj1W2+nh+lOJcNsHncaz1Q=";
const QString NONCE = "zBKdvTjL5bgWaKMCTut/8soM/uoMrFoZ";
const QString CLIENTID = "testClient";
void TestBrowser::initTestCase()
{
QVERIFY(Crypto::init());
m_browserService.reset(new BrowserService(nullptr));
m_browserAction.reset(new BrowserAction(*m_browserService.data()));
}
void TestBrowser::cleanupTestCase()
{
}
/**
* Tests for BrowserAction
*/
void TestBrowser::testChangePublicKeys()
{
QJsonObject json;
json["action"] = "change-public-keys";
json["publicKey"] = PUBLICKEY;
json["nonce"] = NONCE;
auto response = m_browserAction->handleAction(json);
QCOMPARE(response["action"].toString(), QString("change-public-keys"));
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
QCOMPARE(response["success"].toString(), QString("true"));
}
void TestBrowser::testEncryptMessage()
{
QJsonObject message;
message["action"] = "test-action";
m_browserAction->m_publicKey = SERVERPUBLICKEY;
m_browserAction->m_secretKey = SERVERSECRETKEY;
m_browserAction->m_clientPublicKey = PUBLICKEY;
auto encrypted = m_browserAction->encryptMessage(message, NONCE);
QCOMPARE(encrypted, QString("+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP"));
}
void TestBrowser::testDecryptMessage()
{
QString message = "+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP";
m_browserAction->m_publicKey = SERVERPUBLICKEY;
m_browserAction->m_secretKey = SERVERSECRETKEY;
m_browserAction->m_clientPublicKey = PUBLICKEY;
auto decrypted = m_browserAction->decryptMessage(message, NONCE);
QCOMPARE(decrypted["action"].toString(), QString("test-action"));
}
void TestBrowser::testGetBase64FromKey()
{
unsigned char pk[crypto_box_PUBLICKEYBYTES];
for (unsigned int i = 0; i < crypto_box_PUBLICKEYBYTES; ++i) {
pk[i] = i;
}
auto response = m_browserAction->getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
QCOMPARE(response, QString("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="));
}
void TestBrowser::testIncrementNonce()
{
auto result = m_browserAction->incrementNonce(NONCE);
QCOMPARE(result, QString("zRKdvTjL5bgWaKMCTut/8soM/uoMrFoZ"));
}
/**
* Tests for BrowserService
*/
void TestBrowser::testBaseDomain()
{
QString url1 = "https://another.example.co.uk";
QString url2 = "https://www.example.com";
QString url3 = "http://test.net";
QString url4 = "http://so.many.subdomains.co.jp";
QString res1 = m_browserService->baseDomain(url1);
QString res2 = m_browserService->baseDomain(url2);
QString res3 = m_browserService->baseDomain(url3);
QString res4 = m_browserService->baseDomain(url4);
QCOMPARE(res1, QString("example.co.uk"));
QCOMPARE(res2, QString("example.com"));
QCOMPARE(res3, QString("test.net"));
QCOMPARE(res4, QString("subdomains.co.jp"));
}
void TestBrowser::testSortPriority()
{
QString host = "github.com";
QString submitUrl = "https://github.com/session";
QString baseSubmitUrl = "https://github.com";
QScopedPointer<Entry> entry1(new Entry());
QScopedPointer<Entry> entry2(new Entry());
QScopedPointer<Entry> entry3(new Entry());
QScopedPointer<Entry> entry4(new Entry());
QScopedPointer<Entry> entry5(new Entry());
QScopedPointer<Entry> entry6(new Entry());
QScopedPointer<Entry> entry7(new Entry());
QScopedPointer<Entry> entry8(new Entry());
QScopedPointer<Entry> entry9(new Entry());
QScopedPointer<Entry> entry10(new Entry());
entry1->setUrl("https://github.com/login");
entry2->setUrl("https://github.com/login");
entry3->setUrl("https://github.com/");
entry4->setUrl("github.com/login");
entry5->setUrl("http://github.com");
entry6->setUrl("http://github.com/login");
entry7->setUrl("github.com");
entry8->setUrl("github.com/login");
entry9->setUrl("https://github");
entry10->setUrl("github.com");
// The extension uses the submitUrl as default for comparison
auto res1 = m_browserService->sortPriority(entry1.data(), host, "https://github.com/login", baseSubmitUrl);
auto res2 = m_browserService->sortPriority(entry2.data(), host, submitUrl, baseSubmitUrl);
auto res3 = m_browserService->sortPriority(entry3.data(), host, submitUrl, baseSubmitUrl);
auto res4 = m_browserService->sortPriority(entry4.data(), host, submitUrl, baseSubmitUrl);
auto res5 = m_browserService->sortPriority(entry5.data(), host, submitUrl, baseSubmitUrl);
auto res6 = m_browserService->sortPriority(entry6.data(), host, submitUrl, baseSubmitUrl);
auto res7 = m_browserService->sortPriority(entry7.data(), host, submitUrl, baseSubmitUrl);
auto res8 = m_browserService->sortPriority(entry8.data(), host, submitUrl, baseSubmitUrl);
auto res9 = m_browserService->sortPriority(entry9.data(), host, submitUrl, baseSubmitUrl);
auto res10 = m_browserService->sortPriority(entry10.data(), host, submitUrl, baseSubmitUrl);
QCOMPARE(res1, 100);
QCOMPARE(res2, 40);
QCOMPARE(res3, 90);
QCOMPARE(res4, 0);
QCOMPARE(res5, 0);
QCOMPARE(res6, 0);
QCOMPARE(res7, 0);
QCOMPARE(res8, 0);
QCOMPARE(res9, 90);
QCOMPARE(res10, 0);
}
void TestBrowser::testSearchEntries()
{
auto db = QSharedPointer<Database>::create();
auto* root = db->rootGroup();
QList<QString> urls;
urls.push_back("https://github.com/login_page");
urls.push_back("https://github.com/login");
urls.push_back("https://github.com/");
urls.push_back("github.com/login");
urls.push_back("http://github.com");
urls.push_back("http://github.com/login");
urls.push_back("github.com");
urls.push_back("github.com/login");
urls.push_back("https://github");
urls.push_back("github.com");
for (int i = 0; i < urls.length(); ++i) {
auto entry = new Entry();
entry->setGroup(root);
entry->beginUpdate();
entry->setUrl(urls[i]);
entry->setUsername(QString("User %1").arg(i));
entry->endUpdate();
}
browserSettings()->setMatchUrlScheme(false);
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
QCOMPARE(result.length(), 7);
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
QCOMPARE(result[1]->url(), QString("https://github.com/login"));
QCOMPARE(result[2]->url(), QString("https://github.com/"));
QCOMPARE(result[3]->url(), QString("http://github.com"));
QCOMPARE(result[4]->url(), QString("http://github.com/login"));
QCOMPARE(result[5]->url(), QString("github.com"));
QCOMPARE(result[6]->url(), QString("github.com")) ;
// With matching there should be only 5 results
browserSettings()->setMatchUrlScheme(true);
result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
QCOMPARE(result.length(), 5);
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
QCOMPARE(result[1]->url(), QString("https://github.com/login"));
QCOMPARE(result[2]->url(), QString("https://github.com/"));
QCOMPARE(result[3]->url(), QString("github.com"));
QCOMPARE(result[4]->url(), QString("github.com"));
}
void TestBrowser::testSearchEntriesWithPort()
{
auto db = QSharedPointer<Database>::create();
auto* root = db->rootGroup();
QList<QString> urls;
urls.push_back("http://127.0.0.1:443");
urls.push_back("http://127.0.0.1:80");
for (int i = 0; i < urls.length(); ++i) {
auto entry = new Entry();
entry->setGroup(root);
entry->beginUpdate();
entry->setUrl(urls[i]);
entry->setUsername(QString("User %1").arg(i));
entry->endUpdate();
}
auto result = m_browserService->searchEntries(db, "127.0.0.1", "http://127.0.0.1:443"); // db, hostname, url
QCOMPARE(result.length(), 1);
QCOMPARE(result[0]->url(), QString("http://127.0.0.1:443"));
}
void TestBrowser::testSortEntries()
{
auto db = QSharedPointer<Database>::create();
auto* root = db->rootGroup();
QList<QString> urls;
urls.push_back("https://github.com/login_page");
urls.push_back("https://github.com/login");
urls.push_back("https://github.com/");
urls.push_back("github.com/login");
urls.push_back("http://github.com");
urls.push_back("http://github.com/login");
urls.push_back("github.com");
urls.push_back("github.com/login");
urls.push_back("https://github");
urls.push_back("github.com");
QList<Entry*> entries;
for (int i = 0; i < urls.length(); ++i) {
auto entry = new Entry();
entry->setGroup(root);
entry->beginUpdate();
entry->setUrl(urls[i]);
entry->setUsername(QString("User %1").arg(i));
entry->endUpdate();
entries.push_back(entry);
}
browserSettings()->setBestMatchOnly(false);
auto result = m_browserService->sortEntries(entries, "github.com", "https://github.com/session"); // entries, host, submitUrl
QCOMPARE(result.size(), 10);
QCOMPARE(result[0]->username(), QString("User 2"));
QCOMPARE(result[0]->url(), QString("https://github.com/"));
QCOMPARE(result[1]->username(), QString("User 8"));
QCOMPARE(result[1]->url(), QString("https://github"));
QCOMPARE(result[2]->username(), QString("User 0"));
QCOMPARE(result[2]->url(), QString("https://github.com/login_page"));
QCOMPARE(result[3]->username(), QString("User 1"));
QCOMPARE(result[3]->url(), QString("https://github.com/login"));
}
void TestBrowser::testGetDatabaseGroups()
{
auto db = QSharedPointer<Database>::create();
auto* root = db->rootGroup();
QScopedPointer<Group> group1(new Group());
group1->setParent(root);
group1->setName("group1");
QScopedPointer<Group> group2(new Group());
group2->setParent(root);
group2->setName("group2");
QScopedPointer<Group> group3(new Group());
group3->setParent(root);
group3->setName("group3");
QScopedPointer<Group> group2_1(new Group());
group2_1->setParent(group2.data());
group2_1->setName("group2_1");
QScopedPointer<Group> group2_2(new Group());
group2_2->setParent(group2.data());
group2_2->setName("group2_2");
QScopedPointer<Group> group2_1_1(new Group());
group2_1_1->setParent(group2_1.data());
group2_1_1->setName("group2_1_1");
auto result = m_browserService->getDatabaseGroups(db);
QCOMPARE(result.length(), 1);
auto groups = result["groups"].toArray();
auto first = groups.at(0);
auto children = first.toObject()["children"].toArray();
QCOMPARE(first.toObject()["name"].toString(), QString("Root"));
QCOMPARE(children.size(), 3);
auto firstChild = children.at(0);
auto secondChild = children.at(1);
auto thirdChild = children.at(2);
QCOMPARE(firstChild.toObject()["name"].toString(), QString("group1"));
QCOMPARE(secondChild.toObject()["name"].toString(), QString("group2"));
QCOMPARE(thirdChild.toObject()["name"].toString(), QString("group3"));
auto childrenOfSecond = secondChild.toObject()["children"].toArray();
auto firstOfCOS = childrenOfSecond.at(0);
auto secondOfCOS = childrenOfSecond.at(1);
QCOMPARE(firstOfCOS.toObject()["name"].toString(), QString("group2_1"));
QCOMPARE(secondOfCOS.toObject()["name"].toString(), QString("group2_2"));
auto lastChildren = firstOfCOS.toObject()["children"].toArray();
auto lastChild = lastChildren.at(0);
QCOMPARE(lastChild.toObject()["name"].toString(), QString("group2_1_1"));
}

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