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]
This commit is contained in:
Jonathan White 2019-05-31 16:03:47 -04:00
commit a775031fe9
No known key found for this signature in database
GPG Key ID: 440FC65F2E0C6E01
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 é referenciado.</translation>
</message>
<message>
<source>The import container %1 is already imported.</source>
<translation type="unfinished"/>
<translation>O contêiner de importado %1 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) existe no banco de dados</numerusform><numerusform>%n ícone(s) 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, 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