mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Merge branch 'release/2.4.2' into develop
This commit is contained in:
commit
bc891761b6
@ -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,18 +67,16 @@ 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")
|
||||
@ -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)
|
||||
|
18
INSTALL.md
18
INSTALL.md
@ -25,8 +25,8 @@ 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)
|
||||
* argon2
|
||||
* libsodium (>= 1.0.12)
|
||||
* libargon2
|
||||
* qrencode
|
||||
* yubikey ykpers (optional to support YubiKey)
|
||||
|
||||
@ -99,18 +99,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/`
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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)
|
||||
|
11
snap/local/launchers/README.md
Normal file
11
snap/local/launchers/README.md
Normal 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_
|
||||
```
|
||||
|
14
snap/local/launchers/gtk3-env-launch
Executable file
14
snap/local/launchers/gtk3-env-launch
Executable 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 "${@}"
|
@ -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
|
||||
@ -73,7 +85,7 @@ parts:
|
||||
- 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
|
||||
@ -83,6 +95,14 @@ parts:
|
||||
- -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
|
@ -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)
|
||||
@ -255,8 +253,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)
|
||||
|
||||
@ -271,6 +274,7 @@ target_link_libraries(keepassx_core
|
||||
Qt5::Concurrent
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
${sodium_LIBRARY_RELEASE}
|
||||
${YUBIKEY_LIBRARIES}
|
||||
${ZXCVBN_LIBRARIES}
|
||||
${ARGON2_LIBRARIES}
|
||||
@ -411,25 +415,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")
|
||||
|
@ -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);
|
||||
|
||||
@ -760,6 +766,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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ target_link_libraries(keepassxc-cli
|
||||
keepassx_core
|
||||
Qt5::Core
|
||||
${GCRYPT_LIBRARIES}
|
||||
${sodium_LIBRARY_RELEASE}
|
||||
${ARGON2_LIBRARIES}
|
||||
${GPGERROR_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
89
src/core/Alloc.cpp
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -63,6 +63,10 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
||||
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()
|
||||
|
@ -1158,9 +1158,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 +1259,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 +1524,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 +1542,7 @@ bool DatabaseWidget::save()
|
||||
|
||||
if (ok) {
|
||||
m_saveAttempts = 0;
|
||||
m_blockAutoSave = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
@ -370,6 +375,9 @@ MainWindow::MainWindow()
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
setUnifiedTitleAndToolBarOnMac(true);
|
||||
if (macUtils()->isDarkMode()) {
|
||||
setStyleSheet("QToolButton {color:white;}");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XC_UPDATECHECK
|
||||
@ -396,6 +404,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()) {
|
||||
@ -912,7 +926,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());
|
||||
}
|
||||
@ -1031,10 +1045,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
@ -146,6 +148,9 @@ private:
|
||||
|
||||
bool m_appExitCalled;
|
||||
bool m_appExiting;
|
||||
uint m_lastFocusOutTime;
|
||||
QTimer m_trayIconTriggerTimer;
|
||||
QSystemTrayIcon::ActivationReason m_trayIconTriggerReason;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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()));
|
||||
|
||||
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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());
|
||||
}];
|
||||
|
@ -186,8 +186,10 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
|
||||
QCOMPARE(reader.version(), expectedVersion);
|
||||
QCOMPARE(targetDb->cipher(), cipherUuid);
|
||||
QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData());
|
||||
QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData());
|
||||
QCOMPARE(targetDb->metadata()->customData()->value("CustomPublicData"),
|
||||
sourceDb->metadata()->customData()->value("CustomPublicData"));
|
||||
QCOMPARE(targetDb->rootGroup()->customData()->value("CustomGroupData"),
|
||||
sourceDb->rootGroup()->customData()->value("CustomGroupData"));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@ -346,20 +348,22 @@ void TestKdbx4::testCustomData()
|
||||
const QString customDataKey2 = "CD2";
|
||||
const QString customData1 = "abcäöü";
|
||||
const QString customData2 = "Hello World";
|
||||
const int dataSize = customDataKey1.toUtf8().size() + customDataKey1.toUtf8().size() + customData1.toUtf8().size()
|
||||
+ customData2.toUtf8().size();
|
||||
|
||||
// test custom database data
|
||||
db.metadata()->customData()->set(customDataKey1, customData1);
|
||||
db.metadata()->customData()->set(customDataKey2, customData2);
|
||||
QCOMPARE(db.metadata()->customData()->size(), 2);
|
||||
auto lastModified = db.metadata()->customData()->value(CustomData::LastModified);
|
||||
const int dataSize = customDataKey1.toUtf8().size() + customDataKey1.toUtf8().size() + customData1.toUtf8().size()
|
||||
+ customData2.toUtf8().size() + lastModified.toUtf8().size()
|
||||
+ CustomData::LastModified.toUtf8().size();
|
||||
QCOMPARE(db.metadata()->customData()->size(), 3);
|
||||
QCOMPARE(db.metadata()->customData()->dataSize(), dataSize);
|
||||
|
||||
// test custom root group data
|
||||
Group* root = db.rootGroup();
|
||||
root->customData()->set(customDataKey1, customData1);
|
||||
root->customData()->set(customDataKey2, customData2);
|
||||
QCOMPARE(root->customData()->size(), 2);
|
||||
QCOMPARE(root->customData()->size(), 3);
|
||||
QCOMPARE(root->customData()->dataSize(), dataSize);
|
||||
|
||||
// test copied custom group data
|
||||
@ -378,9 +382,9 @@ void TestKdbx4::testCustomData()
|
||||
|
||||
// test custom data deletion
|
||||
entry->customData()->set("additional item", "foobar");
|
||||
QCOMPARE(entry->customData()->size(), 3);
|
||||
QCOMPARE(entry->customData()->size(), 4);
|
||||
entry->customData()->remove("additional item");
|
||||
QCOMPARE(entry->customData()->size(), 2);
|
||||
QCOMPARE(entry->customData()->size(), 3);
|
||||
QCOMPARE(entry->customData()->dataSize(), dataSize);
|
||||
|
||||
// test custom data on cloned groups
|
||||
|
@ -1164,6 +1164,65 @@ void TestMerge::testMetadata()
|
||||
// will be used - exception is the target has no recycle bin activated
|
||||
}
|
||||
|
||||
void TestMerge::testCustomdata()
|
||||
{
|
||||
QScopedPointer<Database> dbDestination(new Database());
|
||||
QScopedPointer<Database> dbSource(createTestDatabase());
|
||||
QScopedPointer<Database> dbDestination2(new Database());
|
||||
QScopedPointer<Database> dbSource2(createTestDatabase());
|
||||
|
||||
m_clock->advanceSecond(1);
|
||||
|
||||
dbDestination->metadata()->customData()->set("toBeDeleted", "value");
|
||||
dbDestination->metadata()->customData()->set("key3", "oldValue");
|
||||
|
||||
dbSource2->metadata()->customData()->set("key1", "value1");
|
||||
dbSource2->metadata()->customData()->set("key2", "value2");
|
||||
dbSource2->metadata()->customData()->set("key3", "newValue");
|
||||
dbSource2->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]");
|
||||
|
||||
m_clock->advanceSecond(1);
|
||||
|
||||
dbSource->metadata()->customData()->set("key1", "value1");
|
||||
dbSource->metadata()->customData()->set("key2", "value2");
|
||||
dbSource->metadata()->customData()->set("key3", "newValue");
|
||||
dbSource->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]");
|
||||
|
||||
dbDestination2->metadata()->customData()->set("notToBeDeleted", "value");
|
||||
dbDestination2->metadata()->customData()->set("key3", "oldValue");
|
||||
|
||||
// Sanity check.
|
||||
QVERIFY(!dbSource->metadata()->customData()->isEmpty());
|
||||
QVERIFY(!dbSource2->metadata()->customData()->isEmpty());
|
||||
|
||||
m_clock->advanceSecond(1);
|
||||
|
||||
Merger merger(dbSource.data(), dbDestination.data());
|
||||
merger.merge();
|
||||
|
||||
Merger merger2(dbSource2.data(), dbDestination2.data());
|
||||
merger2.merge();
|
||||
|
||||
// Source is newer, data should be merged
|
||||
QVERIFY(!dbDestination->metadata()->customData()->isEmpty());
|
||||
QVERIFY(dbDestination->metadata()->customData()->contains("key1"));
|
||||
QVERIFY(dbDestination->metadata()->customData()->contains("key2"));
|
||||
QVERIFY(dbDestination->metadata()->customData()->contains("Browser"));
|
||||
QVERIFY(!dbDestination->metadata()->customData()->contains("toBeDeleted"));
|
||||
QCOMPARE(dbDestination->metadata()->customData()->value("key1"), QString("value1"));
|
||||
QCOMPARE(dbDestination->metadata()->customData()->value("key2"), QString("value2"));
|
||||
QCOMPARE(dbDestination->metadata()->customData()->value("Browser"), QString("n'8=3W@L^6d->d.]St_>]"));
|
||||
QCOMPARE(dbDestination->metadata()->customData()->value("key3"), QString("newValue")); // Old value should be replaced
|
||||
|
||||
// Target is newer, no data is merged
|
||||
QVERIFY(!dbDestination2->metadata()->customData()->isEmpty());
|
||||
QVERIFY(!dbDestination2->metadata()->customData()->contains("key1"));
|
||||
QVERIFY(!dbDestination2->metadata()->customData()->contains("key2"));
|
||||
QVERIFY(!dbDestination2->metadata()->customData()->contains("Browser"));
|
||||
QVERIFY(dbDestination2->metadata()->customData()->contains("notToBeDeleted"));
|
||||
QCOMPARE(dbDestination2->metadata()->customData()->value("key3"), QString("oldValue")); // Old value should not be replaced
|
||||
}
|
||||
|
||||
void TestMerge::testDeletedEntry()
|
||||
{
|
||||
QScopedPointer<Database> dbDestination(createTestDatabase());
|
||||
|
@ -59,6 +59,7 @@ private slots:
|
||||
void testMergeCustomIcons();
|
||||
void testMergeDuplicateCustomIcons();
|
||||
void testMetadata();
|
||||
void testCustomdata();
|
||||
void testDeletedEntry();
|
||||
void testDeletedGroup();
|
||||
void testDeletedRevertedEntry();
|
||||
|
@ -1286,11 +1286,11 @@ void TestGui::testTrayRestoreHide()
|
||||
QSKIP("QSystemTrayIcon::isSystemTrayAvailable() = false, skipping tray restore/hide test...");
|
||||
}
|
||||
|
||||
m_mainWindow->hideWindow();
|
||||
QVERIFY(!m_mainWindow->isVisible());
|
||||
|
||||
auto* trayIcon = m_mainWindow->findChild<QSystemTrayIcon*>();
|
||||
QVERIFY(m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
QTRY_VERIFY(!m_mainWindow->isVisible());
|
||||
QVERIFY(trayIcon);
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
QTRY_VERIFY(m_mainWindow->isVisible());
|
||||
@ -1298,8 +1298,17 @@ void TestGui::testTrayRestoreHide()
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
QTRY_VERIFY(!m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
trayIcon->activated(QSystemTrayIcon::MiddleClick);
|
||||
QTRY_VERIFY(m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::MiddleClick);
|
||||
QTRY_VERIFY(!m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::DoubleClick);
|
||||
QTRY_VERIFY(m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::DoubleClick);
|
||||
QTRY_VERIFY(!m_mainWindow->isVisible());
|
||||
}
|
||||
|
||||
int TestGui::addCannedEntries()
|
||||
|
Loading…
Reference in New Issue
Block a user