Compare commits

..

11 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
cd22010288 Apply code formatting and fix style issues
Co-authored-by: droidmonkey <2809491+droidmonkey@users.noreply.github.com>
2025-06-19 14:37:35 +00:00
copilot-swe-agent[bot]
df4de58541 Implement nested folder support for Bitwarden import
Co-authored-by: droidmonkey <2809491+droidmonkey@users.noreply.github.com>
2025-06-19 10:24:37 -04:00
copilot-swe-agent[bot]
a9e0de34d1 Initial plan for issue 2025-06-19 10:24:36 -04:00
Jonathan White
c4b4be48a5 Add copilot management files (#12207)
* Add copilot management files
* Add AI statements to README and CONTRIBUTING
* Add statement to pull request template
2025-06-19 09:42:32 -04:00
Copilot
2c3a1a03cb Add predefined search for TOTP entries (#12199)
Fixes #9362
This commit was authored by GitHub copilot agent and reviewed by @droidmonkey.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: droidmonkey <2809491+droidmonkey@users.noreply.github.com>
Co-authored-by: Jonathan White <support@dmapps.us>
2025-06-19 09:24:01 -04:00
varjolintu
3c7c3b0a5f Fix loose URL comparison 2025-06-15 09:27:12 -04:00
hueychen27
eac95df000 Replace qdbus with qdbus6 and kwalletd5 with kwalletd6 2025-06-08 06:44:21 -04:00
superuser-does
db0f091536 Use kbd macro in docs where keys are referenced
Experimental enabled in headers so this works. This feature is considered stable, per Asciidoc documentation:
https://docs.asciidoctor.org/asciidoc/latest/macros/keyboard-macro/
2025-05-31 13:15:46 -04:00
varjolintu
5cb6ad6335 Passkeys: Fix ordering of clientDataJSON 2025-05-27 07:09:19 -04:00
Jonathan White
f32ed71dfc Add safeguards to secure input on macOS (#11928)
* Add safeguards to secure input on macOS

* Fixes #11906
* Disable secure input when password widget is hidden as well as focused out
* Add safeguard to ensure the internal counter that macOS keeps is always set to 1 preventing the ability to disable secure input by focus/unfocus a password field
2025-05-23 09:25:25 -04:00
varjolintu
8a32b3bc5e Explicitly allow access to newly created browser group 2025-05-22 16:55:07 -04:00
104 changed files with 1628 additions and 855 deletions

View File

@@ -15,6 +15,7 @@ These are just guidelines, not rules. Use your best judgment, and feel free to p
* [Bug reports](#bug-reports)
* [Discuss with the team](#discuss-with-the-team)
* [Your first code contribution](#your-first-code-contribution)
* [Using AI](#using-ai)
* [Pull requests](#pull-requests)
* [Translations](#translations)
@@ -74,6 +75,10 @@ Unsure where to begin contributing to KeePassXC? You can start by looking throug
Both issue lists are sorted by total number of comments. While not perfect, looking at the number of comments on an issue can give a general idea of how much an impact a given change will have.
### Using AI
Generative AI is fast becoming a first-party feature in most development environments, including GitHub itself. If you use Generative AI to write the vast majority of your submission (e.g., agent-based or vibe coding) then you **must document your use of AI** in your pull request. Please include the service you used and/or model that generated the code. All code submissions go through a rigourous review process regardless of the development workflow used.
### Pull requests
Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code.

View File

@@ -1,12 +1,13 @@
[NOTE]: # ( Describe your changes in detail, why is this change required? )
[NOTE]: # ( Explain large or complex code modifications. )
[NOTE]: # ( Describe your changes in detail. Explain large or complex code modifications. )
[NOTE]: # ( If it fixes an open issue, please add "Fixes #XXX". )
[NOTE]: # ( If you used Generative AI to write the majority of your code, you must state this. )
## Screenshots
[NOTE]: # ( Do not include screenshots of your actual database! )
[TIP]: # ( Use View -> Allow Screen Capture )
## Testing strategy
[NOTE]: # ( Please describe in detail how you tested your changes. )
[TIP]: # ( We expect new code to be covered by unit tests and include helpful comments. )

38
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,38 @@
This is a C++ based repository that uses Qt5 as a primary support and GUI library. This repository is for a password manager application that stores passwords
and other highly sensitive information. The data format that passwords are stored is called KDBX which is a mixed binary and XML format that is fully encrypted
at rest. This format is unpacked into a series of data structures: Database, Groups, and Entries. Please follow these guidelines when contributing:
## Code Standards
### Required Before Each Commit
- Run `cmake --build . --target format` before committing any changes to ensure proper code formatting
- This will run clang-format to ensure all code conforms to the style guide
- From the checkout directory, also run `./release-tool i18n lupdate` to update translation files
### Development Flow
- Setup Build Folder: `mkdir build; cd build`
- Configure: `cmake -G Ninja -DWITH_XC_ALL=ON -DWITH_GUI_TESTS=ON ..`
- Build: `cmake --build . -- -j $(nproc)`
- Test: `ctest`
## Repository Structure
- `docs/topics`: Documentation written in asciidoctor syntax
- `src/`: Main source code files are under this subdirectory
- `src/autotype`: Code that emulates a virtual keyboard to type into interfaces
- `src/browser`: Interface with the KeePassXC Browser Extension using a JSON-based protocol
- `src/cli`: Command Line Interface code
- `src/core`: Contains files that define the data model and other shared code structures
- `src/format`: Code for import/export and reading/writing of KDBX databases
- `src/fdosecrets`: freedesktop.org Secret Service interface code
- `src/quickunlock`: Quick unlock interfaces for various platforms
- `src/sshagent`: SSH Agent interface code to load private keys from the database into ssh-agent
- `tests/`: Test source code files
- `tests/gui`: GUI test source code files
## Key Guidelines
1. Follow C++20 and Qt5 best practices and idiomatic patterns
2. Maintain existing code structure and organization
3. Prefer not to edit cryptographic handling code or other sensitive parts of the code base
4. Write unit tests for new functionality using QTest scaffolding
5. Suggest changes to the `docs/topics` folder when appropriate
6. Unless the change is simple, don't actually make edits to .ui files, just suggest the changes needed

View File

@@ -0,0 +1,29 @@
name: "Copilot Setup Steps"
# Setup the environment for Copilot agents to run in
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
# Needed to clone the repository
permissions:
contents: read
# Install dependencies
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install --no-install-recommends build-essential cmake g++ ninja-build qtbase5-dev qtbase5-private-dev qttools5-dev qttools5-dev-tools libqt5svg5-dev libargon2-dev libkeyutils-dev libminizip-dev libbotan-2-dev libqrencode-dev zlib1g-dev asciidoctor libreadline-dev libpcsclite-dev libusb-1.0-0-dev libxi-dev libxtst-dev libqt5x11extras5-dev

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
# Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
# Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
#
# This program is free software: you can redistribute it and/or modify
@@ -14,80 +14,119 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.16.0)
cmake_minimum_required(VERSION 3.10.0)
project(KeePassXC)
set(APP_ID "org.keepassxc.${PROJECT_NAME}")
# Version Number
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "8")
set(KEEPASSXC_VERSION_PATCH "0")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo Profile"
FORCE)
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug" OR CMAKE_BUILD_TYPE_LOWER STREQUAL "relwithdebinfo")
set(IS_DEBUG_BUILD TRUE)
endif()
# CMake Modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(CMakeToolsHelpers OPTIONAL) # Support Visual Studio Code
# Support Visual Studio Code
include(CMakeToolsHelpers OPTIONAL)
include(FeatureSummary)
include(KPXCMacDeployHelpers)
include(CLangFormat)
include(CompilerFlags)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
# Build Scope
option(WITH_TESTS "Enable building of unit tests" ON)
option(WITH_GUI_TESTS "Enable building of GUI tests" OFF)
option(WITH_WARN_DEPRECATED "Development only: warn about deprecated methods, including Qt." OFF)
option(WITH_DEV_BUILD "Use only for development. Disables/warns about deprecated methods." OFF)
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)
option(WITH_CCACHE "Use ccache for build" OFF)
option(WITH_X11 "Enable building with X11 dependencies" ON)
# Advanced Features Control
option(KPXC_MINIMAL "Build KeePassXC with the minimal feature set required for basic usage" OFF)
option(KPXC_FEATURE_BROWSER "Browser integration and passkeys support" ON)
option(KPXC_FEATURE_SSHAGENT "SSH Agent integration" ON)
option(KPXC_FEATURE_FDOSECRETS "freedesktop.org Secret Service integration; replace system keyring" ON)
set(WITH_XC_ALL OFF CACHE BOOL "Build in all available plugins")
if(KPXC_MINIMAL)
# Disable advanced features in minimal mode
set(KPXC_FEATURE_BROWSER OFF)
set(KPXC_FEATURE_SSHAGENT OFF)
set(KPXC_FEATURE_FDOSECRETS OFF)
endif()
# Minor Feature Flags
option(KPXC_FEATURE_NETWORK "Include code that reaches out to external networks (e.g. downloading icons)" ON)
option(KPXC_FEATURE_UPDATES "Include automatic update checks; disable for managed distributions" ON)
option(KPXC_FEATURE_DOCS "Build offline documentation; requires asciidoctor tool" ON)
# Reconcile update feature with overall network feature
if(NOT KPXC_FEATURE_NETWORK AND KPXC_FEATURE_UPDATES)
message(STATUS "Disabling KPXC_FEATURE_UPDATES because KPXC_FEATURE_NETWORK is disabled")
set(KPXC_FEATURE_UPDATES OFF)
endif()
# FDO Secrets is only available on Linux
if(NOT UNIX OR APPLE OR HAIKU)
set(KPXC_FEATURE_FDOSECRETS OFF)
endif()
# Define feature summaries
add_feature_info("Browser" KPXC_FEATURE_BROWSER "Browser integration and passkeys support")
add_feature_info("SSH Agent" KPXC_FEATURE_SSHAGENT "SSH Agent integration")
option(WITH_XC_AUTOTYPE "Include Auto-Type." ON)
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_BROWSER_PASSKEYS "Passkeys support for browser integration." 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_UPDATECHECK "Include automatic update checks; disable for controlled distributions" ON)
if(UNIX AND NOT APPLE)
add_feature_info("Secret Service" KPXC_FEATURE_FDOSECRETS "Replace system keyring with freedesktop.org Secret Service integration")
option(WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API." OFF)
endif()
add_feature_info("Networking" KPXC_FEATURE_NETWORK "Code that can reach out to external networks is included (e.g. downloading icons)")
add_feature_info("Update Checks" KPXC_FEATURE_UPDATES "Periodic update checks can be performed")
add_feature_info("Documentation" KPXC_FEATURE_DOCS "Offline documentation")
option(WITH_XC_DOCS "Enable building of documentation" ON)
set(WITH_XC_X11 ON CACHE BOOL "Enable building with X11 deps")
if(APPLE)
# Perform the platform checks before applying the stricter compiler flags.
# Otherwise the kSecAccessControlTouchIDCurrentSet deprecation warning will result in an error.
try_compile(XC_APPLE_COMPILER_SUPPORT_BIOMETRY
${CMAKE_CURRENT_BINARY_DIR}/tiometry_test/
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_biometry_support.mm)
message(STATUS "Biometry compiler support: ${XC_APPLE_COMPILER_SUPPORT_BIOMETRY}")
try_compile(XC_APPLE_COMPILER_SUPPORT_TOUCH_ID
${CMAKE_CURRENT_BINARY_DIR}/touch_id_test/
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_touch_id_support.mm)
message(STATUS "Touch ID compiler support: ${XC_APPLE_COMPILER_SUPPORT_TOUCH_ID}")
try_compile(XC_APPLE_COMPILER_SUPPORT_WATCH
${CMAKE_CURRENT_BINARY_DIR}/tiometry_test/
${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler-checks/macos/control_watch_support.mm)
message(STATUS "Apple watch compiler support: ${XC_APPLE_COMPILER_SUPPORT_WATCH}")
endif()
if(WITH_CCACHE)
# Use the Compiler Cache (ccache) program
# (install with: sudo apt get ccache)
find_program(CCACHE_FOUND ccache)
if(NOT CCACHE_FOUND)
message(FATAL_ERROR "ccache requested but cannot be found.")
endif()
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})
endif()
if(WITH_XC_ALL)
# Enable all options (except update check and docs)
set(WITH_XC_AUTOTYPE ON)
set(WITH_XC_NETWORKING ON)
set(WITH_XC_BROWSER ON)
set(WITH_XC_BROWSER_PASSKEYS ON)
set(WITH_XC_YUBIKEY ON)
set(WITH_XC_SSHAGENT ON)
set(WITH_XC_KEESHARE ON)
if(UNIX AND NOT APPLE)
set(WITH_XC_FDOSECRETS ON)
endif()
endif()
# 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()
if(UNIX AND NOT APPLE AND NOT WITH_XC_X11)
message(STATUS "Disabling WITH_XC_AUTOTYPE because WITH_XC_X11 is disabled")
set(WITH_XC_AUTOTYPE OFF)
endif()
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "8")
set(KEEPASSXC_VERSION_PATCH "0")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
set(KEEPASSXC_BUILD_TYPE "Snapshot" CACHE STRING "Set KeePassXC build type to distinguish between stable releases and snapshots")
set_property(CACHE KEEPASSXC_BUILD_TYPE PROPERTY STRINGS Snapshot Release PreRelease)
# Retrieve git HEAD revision hash
set(GIT_HEAD_OVERRIDE "" CACHE STRING "Manually set the Git HEAD hash when missing (eg, when no .git folder exists)")
@@ -103,12 +142,7 @@ elseif(EXISTS ${CMAKE_SOURCE_DIR}/.gitrev)
endif()
message(STATUS "Found Git HEAD Revision: ${GIT_HEAD}\n")
# KeePassXC Versioning and Build Type
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
set(KEEPASSXC_BUILD_TYPE "Snapshot" CACHE STRING "Set KeePassXC build type to distinguish between stable releases and snapshots")
set_property(CACHE KEEPASSXC_BUILD_TYPE PROPERTY STRINGS Snapshot Release)
# Check if on a tag or has .version file, if so build as a release
# Check if on a tag, if so build as a release
execute_process(COMMAND git tag --points-at HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
@@ -123,103 +157,67 @@ endif()
string(REGEX REPLACE "(\r?\n)+" "" OVERRIDE_VERSION "${OVERRIDE_VERSION}")
if(OVERRIDE_VERSION)
if(OVERRIDE_VERSION MATCHES "^[\\.0-9]+$")
if(OVERRIDE_VERSION MATCHES "^[\\.0-9]+-beta[0-9]*")
set(KEEPASSXC_BUILD_TYPE "PreRelease")
set(KEEPASSXC_VERSION ${OVERRIDE_VERSION})
elseif(OVERRIDE_VERSION MATCHES "^[\\.0-9]+$")
set(KEEPASSXC_BUILD_TYPE "Release")
set(KEEPASSXC_VERSION ${OVERRIDE_VERSION})
else()
set(KEEPASSXC_BUILD_TYPE "Snapshot")
set(KEEPASSXC_VERSION ${OVERRIDE_VERSION})
endif()
else()
if(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-preview")
elseif(KEEPASSXC_BUILD_TYPE STREQUAL "Snapshot")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot")
endif()
endif()
if(KEEPASSXC_BUILD_TYPE STREQUAL "Release")
set(KEEPASSXC_BUILD_TYPE_RELEASE ON)
elseif(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease")
set(KEEPASSXC_BUILD_TYPE_PRE_RELEASE ON)
else()
set(KEEPASSXC_BUILD_TYPE_SNAPSHOT ON)
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot")
endif()
message(STATUS "Setting up build for KeePassXC v${KEEPASSXC_VERSION}\n")
# Distribution info
set(KEEPASSXC_DIST_TYPE "Native" CACHE STRING "KeePassXC Distribution Type")
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Flatpak Native)
set(KEEPASSXC_DIST ON)
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution Type")
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Flatpak Other)
if(KEEPASSXC_DIST_TYPE STREQUAL "Snap")
set(KEEPASSXC_DIST_SNAP ON)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage")
set(KEEPASSXC_DIST_APPIMAGE ON)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Flatpak")
set(KEEPASSXC_DIST_FLATPAK ON)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other")
unset(KEEPASSXC_DIST)
endif()
# Standards
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Compiler Features
if(APPLE)
# Perform the platform checks before applying the stricter compiler flags.
# Otherwise the kSecAccessControlTouchIDCurrentSet deprecation warning will result in an error.
try_compile(XC_APPLE_COMPILER_SUPPORT_BIOMETRY
${CMAKE_BINARY_DIR}/macos-trycompile/
${CMAKE_SOURCE_DIR}/cmake/compiler-checks/control_biometry_support.mm)
message(STATUS "Biometry compiler support: ${XC_APPLE_COMPILER_SUPPORT_BIOMETRY}")
try_compile(XC_APPLE_COMPILER_SUPPORT_TOUCH_ID
${CMAKE_BINARY_DIR}/macos-trycompile/
${CMAKE_SOURCE_DIR}/cmake/compiler-checks/control_touch_id_support.mm)
message(STATUS "Touch ID compiler support: ${XC_APPLE_COMPILER_SUPPORT_TOUCH_ID}")
try_compile(XC_APPLE_COMPILER_SUPPORT_WATCH
${CMAKE_BINARY_DIR}/macos-trycompile/
${CMAKE_SOURCE_DIR}/cmake/compiler-checks/control_watch_support.mm)
message(STATUS "Apple watch compiler support: ${XC_APPLE_COMPILER_SUPPORT_WATCH}")
try_compile(HAVE_PT_DENY_ATTACH
${CMAKE_BINARY_DIR}/macos-trycompile/
${CMAKE_SOURCE_DIR}/cmake/compiler-checks/ptrace_deny_attach.cpp)
endif()
if(UNIX)
check_cxx_source_compiles("#include <sys/prctl.h>
int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }"
HAVE_PR_SET_DUMPABLE)
check_cxx_source_compiles("#include <malloc.h>
int main() { return 0; }"
HAVE_MALLOC_H)
check_cxx_source_compiles("#include <malloc.h>
int main() { malloc_usable_size(NULL); return 0; }"
HAVE_MALLOC_USABLE_SIZE)
check_cxx_source_compiles("#include <sys/resource.h>
int main() {
struct rlimit limit;
limit.rlim_cur = 0;
limit.rlim_max = 0;
setrlimit(RLIMIT_CORE, &limit);
return 0;
}" HAVE_RLIMIT_CORE)
endif()
# ccache support
if(WITH_CCACHE)
find_program(CCACHE_FOUND ccache)
if(NOT CCACHE_FOUND)
message(FATAL_ERROR "ccache requested but cannot be found.")
endif()
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})
endif()
# Create position independent code for shared libraries and executables
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0")
cmake_policy(SET CMP0083 NEW)
include(CheckPIESupported)
check_pie_supported()
endif()
# Find Botan early since the version affects subsequent compiler options
find_package(Botan REQUIRED)
if(BOTAN_VERSION VERSION_GREATER_EQUAL "3.0.0")
set(WITH_XC_BOTAN3 TRUE)
elseif(BOTAN_VERSION VERSION_LESS "2.11.0")
# Check for minimum Botan version
message(FATAL_ERROR "Botan 2.11.0 or higher is required")
endif()
include_directories(SYSTEM ${BOTAN_INCLUDE_DIR})
# Create position independent code for shared libraries and executables
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
set(IS_32BIT TRUE)
endif()
@@ -237,7 +235,66 @@ if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$"
set(CMAKE_COMPILER_IS_CLANGXX 1)
endif()
# Compiler Flags
macro(add_gcc_compiler_cxxflags FLAGS)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
endif()
endmacro(add_gcc_compiler_cxxflags)
macro(add_gcc_compiler_cflags FLAGS)
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
endif()
endmacro(add_gcc_compiler_cflags)
macro(add_gcc_compiler_flags FLAGS)
add_gcc_compiler_cxxflags("${FLAGS}")
add_gcc_compiler_cflags("${FLAGS}")
endmacro(add_gcc_compiler_flags)
# Copies of above macros that first ensure the compiler understands a given flag
# Because check_*_compiler_flag() sets -D with name, need to provide "safe" FLAGNAME
macro(check_add_gcc_compiler_cxxflag FLAG FLAGNAME)
check_cxx_compiler_flag("${FLAG}" CXX_HAS${FLAGNAME})
if(CXX_HAS${FLAGNAME})
add_gcc_compiler_cxxflags("${FLAG}")
endif()
endmacro(check_add_gcc_compiler_cxxflag)
macro(check_add_gcc_compiler_cflag FLAG FLAGNAME)
check_c_compiler_flag("${FLAG}" CC_HAS${FLAGNAME})
if(CC_HAS${FLAGNAME})
add_gcc_compiler_cflags("${FLAG}")
endif()
endmacro(check_add_gcc_compiler_cflag)
# This is the "front-end" for the above macros
# Optionally takes additional parameter(s) with language to check (currently "C" or "CXX")
macro(check_add_gcc_compiler_flag FLAG)
string(REGEX REPLACE "[-=]" "_" FLAGNAME "${FLAG}")
set(check_lang_spec ${ARGN})
list(LENGTH check_lang_spec num_extra_args)
set(langs C CXX)
if(num_extra_args GREATER 0)
set(langs "${check_lang_spec}")
endif()
if("C" IN_LIST langs)
check_add_gcc_compiler_cflag("${FLAG}" "${FLAGNAME}")
endif()
if("CXX" IN_LIST langs)
check_add_gcc_compiler_cxxflag("${FLAG}" "${FLAGNAME}")
endif()
endmacro(check_add_gcc_compiler_flag)
add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)
if(NOT IS_DEBUG_BUILD)
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
if(WITH_APP_BUNDLE)
add_definitions(-DWITH_APP_BUNDLE)
endif()
add_gcc_compiler_flags("-fno-common")
find_package(OpenMP)
if(OpenMP_FOUND)
@@ -253,8 +310,10 @@ if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
check_add_gcc_compiler_flag("-Wshadow-compatible-local")
check_add_gcc_compiler_flag("-Wshadow-local")
add_gcc_compiler_flags("-Werror")
# C++20 marks enum arithmetic as deprecated, but we use it in Botan and Qt5
add_gcc_compiler_cxxflags("-Wno-deprecated-enum-enum-conversion -Wno-error=deprecated ")
# This is needed since compiling against Botan3 requires compiling against C++20
if(WITH_XC_BOTAN3)
add_gcc_compiler_cxxflags("-Wno-error=deprecated-enum-enum-conversion -Wno-error=deprecated")
endif()
endif()
if (NOT HAIKU)
@@ -299,6 +358,14 @@ if(UNIX AND NOT APPLE)
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
endif()
set(CMAKE_C_STANDARD 99)
if(WITH_XC_BOTAN3)
set(CMAKE_CXX_STANDARD 20)
else()
set(CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
check_cxx_compiler_flag("-fsized-deallocation" CXX_HAS_fsized_deallocation)
if(CXX_HAS_fsized_deallocation)
# Do additional check: the deallocation functions must be there too.
@@ -316,7 +383,7 @@ if(APPLE AND CMAKE_COMPILER_IS_CLANGXX)
add_gcc_compiler_cxxflags("-stdlib=libc++")
endif()
if(WITH_WARN_DEPRECATED)
if(WITH_DEV_BUILD)
add_definitions(-DQT_DEPRECATED_WARNINGS)
else()
add_definitions(-DQT_NO_DEPRECATED_WARNINGS)
@@ -325,36 +392,54 @@ endif()
# MSVC specific options
if (MSVC)
if(MSVC_TOOLSET_VERSION LESS 142)
message(FATAL_ERROR "Only Microsoft Visual Studio 2019 and newer are supported!")
if(MSVC_TOOLSET_VERSION LESS 141)
message(FATAL_ERROR "Only Microsoft Visual Studio 17 and newer are supported!")
endif()
# Turn on multi-processor support and faster PDB generation (/Zf)
add_compile_options(/permissive- /utf-8 /MP /Zf)
# Enable built-in ASAN
add_compile_definitions(/fsanitize=address)
# Enable high entropy ASLR on release builds
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "release")
add_compile_options(/guard:cf)
add_link_options(/DYNAMICBASE /HIGHENTROPYVA /GUARD:CF)
endif()
elseif(MINGW)
# Enable high entropy ASLR on release builds
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "release")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va")
add_compile_options(/permissive- /utf-8 /MP)
if(IS_DEBUG_BUILD)
add_compile_options(/Zf)
if(MSVC_TOOLSET_VERSION GREATER 141)
add_compile_definitions(/fsanitize=address)
endif()
endif()
endif()
if(WIN32)
set(CMAKE_RC_COMPILER_INIT windres)
enable_language(RC)
if(MINGW)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
endif()
if(NOT IS_DEBUG_BUILD)
if(MSVC)
# By default MSVC enables NXCOMPAT
add_compile_options(/guard:cf)
add_link_options(/DYNAMICBASE /HIGHENTROPYVA /GUARD:CF)
else(MINGW)
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")
# Enable high entropy ASLR for 64-bit builds
if(NOT IS_32BIT)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va")
endif()
endif()
endif()
endif()
if(APPLE AND WITH_APP_BUNDLE OR WIN32)
set(PROGNAME KeePassXC)
else()
set(PROGNAME keepassxc)
endif()
if(WIN32)
set(CLI_INSTALL_DIR ".")
set(PROXY_INSTALL_DIR ".")
set(BIN_INSTALL_DIR ".")
set(PLUGIN_INSTALL_DIR ".")
set(DATA_INSTALL_DIR "share")
elseif(APPLE AND WITH_APP_BUNDLE)
set(PROGNAME KeePassXC)
set(BUNDLE_INSTALL_DIR "${PROGNAME}.app/Contents")
set(CMAKE_INSTALL_MANDIR "${BUNDLE_INSTALL_DIR}/Resources/man")
set(CLI_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/MacOS")
@@ -362,10 +447,9 @@ elseif(APPLE AND WITH_APP_BUNDLE)
set(BIN_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/MacOS")
set(PLUGIN_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/PlugIns")
set(DATA_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/Resources")
add_definitions(-DWITH_APP_BUNDLE)
else()
include(GNUInstallDirs)
set(PROGNAME keepassxc)
set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
set(PROXY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
@@ -373,6 +457,10 @@ else()
set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc")
endif()
if(WITH_TESTS)
enable_testing()
endif(WITH_TESTS)
if(WITH_COVERAGE)
# Include code coverage, use with -DCMAKE_BUILD_TYPE=Debug
include(CodeCoverage)
@@ -401,10 +489,11 @@ if(WITH_COVERAGE)
endif()
endif()
# Find Qt
include(CLangFormat)
set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools)
if(UNIX AND NOT APPLE)
if(WITH_X11)
if(WITH_XC_X11)
list(APPEND QT_COMPONENTS X11Extras)
endif()
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED)
@@ -423,13 +512,14 @@ else()
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
endif()
# Minimum Qt version check
if(Qt5Core_VERSION VERSION_LESS "5.12.0")
message(FATAL_ERROR "Qt version 5.12.0 or higher is required")
endif()
# C++20 is not supported before Qt 5.15.0
if(Qt5Core_VERSION VERSION_LESS "5.15.0")
set(CMAKE_CXX_STANDARD 17)
get_filename_component(Qt5_PREFIX ${Qt5_DIR}/../../.. REALPATH)
if(APPLE)
# Add includes under Qt5 Prefix in case Qt6 is also installed
include_directories(SYSTEM ${Qt5_PREFIX}/include)
endif()
# Process moc automatically
@@ -439,17 +529,7 @@ set(CMAKE_AUTOUIC ON)
# Process .qrc files automatically
set(CMAKE_AUTORCC ON)
add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)
if(NOT CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
get_filename_component(Qt5_PREFIX ${Qt5_DIR}/../../.. REALPATH)
if(APPLE)
# Add includes under Qt5 Prefix in case Qt6 is also installed
include_directories(SYSTEM ${Qt5_PREFIX}/include)
set(CMAKE_MACOSX_RPATH TRUE)
find_program(MACDEPLOYQT_EXE macdeployqt HINTS ${Qt5_PREFIX}/bin ${Qt5_PREFIX}/tools/qt5/bin ENV PATH)
if(NOT MACDEPLOYQT_EXE)
@@ -465,16 +545,9 @@ elseif(WIN32)
message(STATUS "Using windeployqt: ${WINDEPLOYQT_EXE}")
endif()
# Find Botan
# TODO: Increase minimum to 2.19.1 and drop Argon2 package
find_package(Botan REQUIRED)
if(BOTAN_VERSION VERSION_GREATER_EQUAL "3.0.0")
set(WITH_BOTAN3 TRUE)
elseif(BOTAN_VERSION VERSION_LESS "2.12.0")
# Check for minimum Botan version
message(FATAL_ERROR "Botan 2.12.0 or higher is required")
endif()
include_directories(SYSTEM ${BOTAN_INCLUDE_DIR})
# Debian sets the build type to None for package builds.
# Make sure we don't enable asserts there.
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG)
# Find Argon2 -- Botan 2.18 and below does not support threaded Argon2
find_library(ARGON2_LIBRARIES NAMES argon2)
@@ -491,40 +564,70 @@ include_directories(SYSTEM ${ZLIB_INCLUDE_DIR})
# Find Minizip
find_package(Minizip REQUIRED)
# Find PCSC and LibUSB for hardware key support
find_package(PCSC REQUIRED)
include_directories(SYSTEM ${PCSC_INCLUDE_DIRS})
if(WITH_XC_YUBIKEY)
find_package(PCSC REQUIRED)
include_directories(SYSTEM ${PCSC_INCLUDE_DIRS})
if(UNIX AND NOT APPLE)
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 REQUIRED)
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h PATH_SUFFIXES "libusb-1.0" "libusb" REQUIRED)
include_directories(SYSTEM ${LIBUSB_INCLUDE_DIR})
# For PolKit QuickUnlock
find_library(KEYUTILS_LIBRARIES NAMES keyutils REQUIRED)
if(UNIX AND NOT APPLE)
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 REQUIRED)
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h PATH_SUFFIXES "libusb-1.0" "libusb" REQUIRED)
include_directories(SYSTEM ${LIBUSB_INCLUDE_DIR})
endif()
endif()
# Find zxcvbn or use the bundled version
if(UNIX)
check_cxx_source_compiles("#include <sys/prctl.h>
int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }"
HAVE_PR_SET_DUMPABLE)
check_cxx_source_compiles("#include <malloc.h>
int main() { return 0; }"
HAVE_MALLOC_H)
check_cxx_source_compiles("#include <malloc.h>
int main() { malloc_usable_size(NULL); return 0; }"
HAVE_MALLOC_USABLE_SIZE)
check_cxx_source_compiles("#include <sys/resource.h>
int main() {
struct rlimit limit;
limit.rlim_cur = 0;
limit.rlim_max = 0;
setrlimit(RLIMIT_CORE, &limit);
return 0;
}" HAVE_RLIMIT_CORE)
if(APPLE)
check_cxx_source_compiles("#include <sys/types.h>
#include <sys/ptrace.h>
int main() { ptrace(PT_DENY_ATTACH, 0, 0, 0); return 0; }"
HAVE_PT_DENY_ATTACH)
endif()
endif()
include_directories(SYSTEM ${ZLIB_INCLUDE_DIR})
find_library(ZXCVBN_LIBRARIES zxcvbn)
if(NOT ZXCVBN_LIBRARIES)
add_subdirectory(src/thirdparty/zxcvbn)
set(ZXCVBN_LIBRARIES zxcvbn)
endif()
endif(NOT ZXCVBN_LIBRARIES)
# Add KeePassXC sources and tests
add_subdirectory(src)
add_subdirectory(share)
if(WITH_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
endif(WITH_TESTS)
if(KPXC_FEATURE_DOCS)
if(WITH_XC_DOCS)
add_subdirectory(docs)
endif()
# Print out summary information
message(STATUS "")
feature_summary(QUIET_ON_EMPTY WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
feature_summary(QUIET_ON_EMPTY WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:")
if(PRINT_SUMMARY)
# This will print ENABLED, REQUIRED and DISABLED
feature_summary(WHAT ALL)
else()
# This will only print ENABLED and DISABLED feature
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:")
endif()

View File

@@ -1,17 +1,19 @@
# Build and Install KeePassXC
Build and Install KeePassXC
=================
This document will guide you through the steps to build and install KeePassXC from source.
For more information, see also the [_Building KeePassXC_](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC) page on the wiki.
The [QuickStart Guide](https://keepassxc.org/docs/KeePassXC_GettingStarted.html) gets you started using KeePassXC on your Windows, macOS, or Linux computer using pre-compiled binaries from the [downloads page](https://keepassxc.org/download).
## Toolchain and Build Dependencies
Toolchain and Build Dependencies
================================
The following build tools must exist within your PATH:
* cmake (>= 3.16.0)
* cmake (>= 3.10.0)
* make (>= 4.2) or ninja (>= 1.10)
* g++ (>= 9.3.0) or clang++ (>= 10.0)
* g++ (>= 4.9) or clang++ (>= 6.0)
* asciidoctor (>= 2.0)
* Besides a working C++ toolchain, KeePassXC also has a number of direct build and runtime dependencies. For detailed information about how to install them, please refer to the GitHub wiki:
@@ -20,8 +22,8 @@ The following build tools must exist within your PATH:
* [Set up Build Environment on Windows](https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-Windows)
* [Set up Build Environment on macOS](https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-macOS)
## Build Steps
Build Steps
===========
We recommend using the release tool to perform builds, please read up-to-date instructions [on our wiki](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC#building-using-the-release-tool).
To compile from source, open a **Terminal (Linux/MacOS)**, the **MSVC Tools Command Prompt (Windows)**, or **MSYS2-MinGW shell (Windows)**. For code development on Windows, you can use Visual Studio 2022, Visual Studio Code, or CLion.
@@ -53,10 +55,10 @@ To compile from source, open a **Terminal (Linux/MacOS)**, the **MSVC Tools Comm
```
mkdir build
cd build
cmake ..
cmake -DWITH_XC_ALL=ON ..
make
```
If you have `vcpkg` installed, add `-DCMAKE_TOOLCHAIN_FILE=${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake` to the `cmake` command to automatically download and install all required build and runtime dependencies locally to your build directory before compiling KeePassXC. Using `vcpkg` is the preferred way to install dependencies on macOS and required on Windows if using the MSVC toolchain.
For more detailed build instructions for each platform, please refer to the [GitHub wiki](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC).
@@ -75,39 +77,49 @@ When building with ASAN support on macOS, you need to use `export ASAN_OPTIONS=d
If you are using MSYS2, you have to add ```-G "MSYS Makefiles"``` at the beginning of the cmake command.
## Additional CMake Parameters
CMake Configuration Options
==========================
KeePassXC comes with a variety of build options that can turn on/off features. Each of these build options are supplied at the time of calling cmake:
## Recommended CMake Build Parameters
```
-DKPXC_MINIMAL=[ON|OFF] Build KeePassXC with the minimal feature set required for basic usage (default: OFF)
-DKPXC_FEATURE_BROWSER=[ON|OFF] Browser integration and passkeys support (default: ON)
-DKPXC_FEATURE_SSHAGENT=[ON|OFF] SSH Agent integration (default: ON)
-DKPXC_FEATURE_FDOSECRETS=[ON|OFF] (Linux Only) freedesktop.org Secret Service integration; replace system keyring (default:ON)
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_BUILD_TYPE=<RelWithDebInfo/Debug/Release>
-DWITH_GUI_TESTS=ON
```
-DKPXC_FEATURE_NETWORK=[ON|OFF] Include code that reaches out to external networks (e.g. downloading icons) (default: ON)
-DKPXC_FEATURE_UPDATES=[ON|OFF] Include automatic update checks; disable for managed distributions (requires networking) (default: ON)
-DKPXC_FEATURE_DOCS=[ON|OFF] Build offline documentation; requires asciidoctor tool (default: ON)
## Additional CMake Parameters
KeePassXC comes with a variety of build options that can turn on/off features. Most notably, we allow you to build the application with all TCP/IP networking code disabled. Please note that we still require and link against Qt5's network library in order to use local named pipes on all operating systems. Each of these build options are supplied at the time of calling cmake:
```
-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_BROWSER_PASSKEYS=[ON|OFF] Enable/Disable Passkeys support for browser integration (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_FDOSECRETS=[ON|OFF] (Linux Only) Enable/Disable Freedesktop.org Secrets Service support (default:OFF)
-DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group synchronization extension (default: OFF)
-DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (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_WARN_DEPRECATED=[ON|OFF] Development only: warn about deprecated methods, including Qt. (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)
-DWITH_CCACHE=[ON|OFF] Use ccache for build speed optimization (default: OFF)
-DWITH_X11=[ON|OFF] Enable building with X11 dependencies (default: ON)
-DKEEPASSXC_BUILD_TYPE=[Snapshot|Release] Set the build type to show/hide stability warnings (default: "Snapshot")
-DKEEPASSXC_DIST_TYPE=[Snap|AppImage|Flatpak|Native] Specify the distribution method (default: "Native")
-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: "")
```
Note: Even though you can build the application with all TCP/IP networking code disabled, we still require and link against
Qt5's network library to use local named pipes on all operating systems.
## Installation
Installation
============
After you have successfully built KeePassXC, install the binary by executing the following:
@@ -115,7 +127,8 @@ After you have successfully built KeePassXC, install the binary by executing the
sudo make install
```
## Packaging
Packaging
=========
You can create a package to redistribute KeePassXC (zip, deb, rpm, dmg, etc..). Refer to [keepassxc-packaging](https://github.com/keepassxreboot/keepassxc-packaging) for packaging scripts.
@@ -125,23 +138,21 @@ To package using CMake, run the following command using whichever [generators](h
cpack -G "ZIP;WIX"
```
## Testing
Testing
=======
You can perform tests on the built executables with:
```
make test ARGS+="--output-on-failure"
```
On Linux, if you are not currently running on an X Server or Wayland, run the tests as follows:
```
make test ARGS+="-E test\(cli\|gui\) --output-on-failure"
xvfb-run -e errors -a --server-args="-screen 0 1024x768x24" make test ARGS+="-R test\(cli\|gui\) --output-on-failure"
```
Common parameters:
```
CTEST_OUTPUT_ON_FAILURE=1
ARGS+=-jX

View File

@@ -1,5 +1,4 @@
# <img src="https://keepassxc.org/assets/img/keepassxc.svg" width="40" height="40"/> KeePassXC
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/6326/badge)](https://bestpractices.coreinfrastructure.org/projects/6326)
[![TeamCity Build Status](https://ci.keepassxc.org/app/rest/builds/buildType:\(project:KeepassXC\)/statusIcon)](https://ci.keepassxc.org/?guest=1)
[![codecov](https://codecov.io/gh/keepassxreboot/keepassxc/branch/develop/graph/badge.svg)](https://codecov.io/gh/keepassxreboot/keepassxc)
@@ -11,22 +10,16 @@
[KeePassXC](https://keepassxc.org) is a modern, secure, and open-source password manager that stores and manages your most sensitive information. You can run KeePassXC on Windows, macOS, and Linux systems. KeePassXC is for people with extremely high demands of secure personal data management. It saves many different types of information, such as usernames, passwords, URLs, attachments, and notes in an offline, encrypted file that can be stored in any location, including private and public cloud solutions. For easy identification and management, user-defined titles and icons can be specified for entries. In addition, entries are sorted into customizable groups. An integrated search function allows you to use advanced patterns to easily find any entry in your database. A customizable, fast, and easy-to-use password generator utility allows you to create passwords with any combination of characters or easy to remember passphrases.
## Quick Start
The [QuickStart Guide](https://keepassxc.org/docs/KeePassXC_GettingStarted.html) gets you started using KeePassXC on your Windows, macOS, or Linux computer using pre-compiled binaries from the [downloads page](https://keepassxc.org/download). Additionally, individual Linux distributions may ship their own versions, so please check your distribution's package list to see if KeePassXC is available. Detailed documentation is available in the [User Guide](https://keepassxc.org/docs/KeePassXC_UserGuide.html).
## Features List
KeePassXC has numerous features for novice and power users alike. Our goal is to create an application that can be used by anyone while still offering advanced features to those that need them.
### Core Features
### Basic
* Create, open, and save databases in the KDBX format (KeePass-compatible with KDBX4 and KDBX3)
* All information is encrypted at rest and never exposed outside the program
* Store sensitive information in entries that are organized by groups
* Password generator
* Search for entries
* TOTP storage and generation
* YubiKey/OnlyKey challenge-response support
* Password generator
* Auto-Type passwords into applications
* Browser integration with Google Chrome, Mozilla Firefox, Microsoft Edge, Chromium, Vivaldi, Brave, and Tor-Browser
* Support for passkeys using the browser integration
@@ -40,7 +33,10 @@ KeePassXC has numerous features for novice and power users alike. Our goal is to
* Field references between entries
* File attachments and custom attributes
* Entry history and data restoration
* YubiKey/OnlyKey challenge-response support
* Command line interface (keepassxc-cli)
* Auto-Open databases
* KeeShare shared databases (import, export, and synchronize)
* SSH Agent integration
* FreeDesktop.org Secret Service (replace Gnome keyring, etc.)
* Additional encryption choices: Twofish and ChaCha20
@@ -60,6 +56,10 @@ You may directly contribute your own code by submitting a pull request. Please r
Contributors are required to adhere to the project's [Code of Conduct](CODE-OF-CONDUCT.md).
## Generative AI
Generative AI is fast becoming a first-party feature in most development environments, including GitHub itself. If the majority of a code submission is made using Generative AI (e.g., agent-based or vibe coding) then **we will document that in the pull request.** All code submissions go through a rigourous review process regardless of the development workflow or submitter.
## License
KeePassXC code is licensed under GPL-2 or GPL-3. Additional licensing for third-party files is detailed in [COPYING](./COPYING).

View File

@@ -1,65 +0,0 @@
# Copyright (C) 2024 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/>.
macro(add_gcc_compiler_cxxflags FLAGS)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
endif()
endmacro()
macro(add_gcc_compiler_cflags FLAGS)
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
endif()
endmacro()
macro(add_gcc_compiler_flags FLAGS)
add_gcc_compiler_cxxflags("${FLAGS}")
add_gcc_compiler_cflags("${FLAGS}")
endmacro()
# Copies of above macros that first ensure the compiler understands a given flag
# Because check_*_compiler_flag() sets -D with name, need to provide "safe" FLAGNAME
macro(check_add_gcc_compiler_cxxflag FLAG FLAGNAME)
check_cxx_compiler_flag("${FLAG}" CXX_HAS${FLAGNAME})
if(CXX_HAS${FLAGNAME})
add_gcc_compiler_cxxflags("${FLAG}")
endif()
endmacro()
macro(check_add_gcc_compiler_cflag FLAG FLAGNAME)
check_c_compiler_flag("${FLAG}" CC_HAS${FLAGNAME})
if(CC_HAS${FLAGNAME})
add_gcc_compiler_cflags("${FLAG}")
endif()
endmacro()
# This is the "front-end" for the above macros
# Optionally takes additional parameter(s) with language to check (currently "C" or "CXX")
macro(check_add_gcc_compiler_flag FLAG)
string(REGEX REPLACE "[-=]" "_" FLAGNAME "${FLAG}")
set(check_lang_spec ${ARGN})
list(LENGTH check_lang_spec num_extra_args)
set(langs C CXX)
if(num_extra_args GREATER 0)
set(langs "${check_lang_spec}")
endif()
if("C" IN_LIST langs)
check_add_gcc_compiler_cflag("${FLAG}" "${FLAGNAME}")
endif()
if("CXX" IN_LIST langs)
check_add_gcc_compiler_cxxflag("${FLAG}" "${FLAGNAME}")
endif()
endmacro()

View File

@@ -1,8 +0,0 @@
#include <sys/ptrace.h>
#include <sys/types.h>
int main()
{
ptrace(PT_DENY_ATTACH, 0, 0, 0);
return 0;
}

View File

@@ -7,6 +7,7 @@ KeePassXC Team <team@keepassxc.org>
:imagesdir: images
:stylesheet: styles/dark.css
:toc: left
:experimental:
ifdef::backend-pdf[]
:title-page:
:title-logo-image: {imagesdir}/kpxc_logo.png

View File

@@ -7,6 +7,7 @@ KeePassXC Team <team@keepassxc.org>
:stylesheet: styles/dark.css
:toc: left
:sectanchors:
:experimental:
ifdef::backend-pdf[]
:title-page:
:title-logo-image: {imagesdir}/kpxc_logo.png

View File

@@ -4,3 +4,4 @@ KeePassXC Team <team@keepassxc.org>
:stylesheet: ../styles/dark.css
:icons: font
:toc: left
:experimental:

View File

@@ -24,13 +24,13 @@ You can also set the time to remember the last used entry between presses of the
=== Configure Auto-Type Sequences
Each entry in your database can have multiple Auto-Type sequences associated with various window titles. Simulated key presses can be sent to any other currently open window of your choice (web browser windows, login dialogs boxes, and so on). When the Global Auto-Type hotkey is pressed, KeePassXC will search your database for entries matching the current selected window title.
NOTE: The default Auto-Type sequence is `{USERNAME}{TAB}{PASSWORD}{ENTER}`. This means that it first types the username of the selected entry, then presses the `Tab` key, then types the password of the entry and finally presses the `Enter` key.
NOTE: The default Auto-Type sequence is `{USERNAME}{TAB}{PASSWORD}{ENTER}`. This means that it first types the username of the selected entry, then presses the kbd:[Tab] key, then types the password of the entry and finally presses the kbd:[Enter] key.
TIP: To change the default Auto-Type sequence for all entries of your database, edit the root (top-most) group of your database and set a specific sequence. Child groups and entries will inherit this sequence by default.
To configure Auto-Type sequences for your entries, perform the following steps:
1. Navigate to the entries list and open the desired entry for editing. Click the _Auto-Type_ item from the left-hand menu bar *(1)*. Press the `+` button *(2)* to add a new sequence entry. Select the desired window using the drop-down menu, or simply type a window title in the box *(3)*.
1. Navigate to the entries list and open the desired entry for editing. Click the _Auto-Type_ item from the left-hand menu bar *(1)*. Press the kbd:[+] button *(2)* to add a new sequence entry. Select the desired window using the drop-down menu, or simply type a window title in the box *(3)*.
+
TIP: You can use an asterisk (`\*`) to match any value (e.g., when a window title contains a dynamic filename or website name). Set the window title to `*` to match all windows. Leave the window title blank to offer additional default Auto-Type sequences, such as custom attributes.
+
@@ -60,7 +60,7 @@ image::autotype_entry_sequences.png[]
|Press the corresponding keyboard key
|{UP}, {DOWN}, {LEFT}, {RIGHT} |Press the corresponding arrow key
|{LEFTBRACE}, {RIGHTBRACE} |Press `{` or `}`, respectively
|{LEFTBRACE}, {RIGHTBRACE} |Press kbd:[{] or kbd:[}], respectively
|{&lt;KEY&gt; X} |Repeat &lt;KEY&gt; X times (e.g., {SPACE 5} inserts five spaces)
|{DELAY=X} |Set delay between key presses to X milliseconds
|{DELAY X} |Pause typing for X milliseconds
@@ -89,7 +89,7 @@ When you press the global Auto-Type hotkey, KeePassXC searches all unlocked data
.Auto-Type sequence selection
image::autotype_selection_dialog.png[,70%]
Perform the selected Auto-Type sequence by double clicking the desired row or pressing _Enter_. Press the up and down arrows to navigate the list. Sequences can be filtered through the text edit field.
Perform the selected Auto-Type sequence by double clicking the desired row or pressing kbd:[Enter]. Press the up and down arrows to navigate the list. Sequences can be filtered through the text edit field.
.Auto-Type search database
image::autotype_selection_dialog_search.png[,70%]
@@ -104,7 +104,7 @@ The option to type just the username, password, or current TOTP value is availab
TIP: On Windows, you will see an option to use a virtual keyboard in this sub-menu. This is an experimental feature that allows you to type into virtual machines by simulating actual keyboard presses. Some international keyboards may be unsupported due to limitations in the Windows API.
=== Performing Entry-Level Auto-Type
You can quickly activate the default Auto-Type sequence for a particular entry using Entry-Level Auto-Type. For this operation, the KeePassXC window will be minimized and the Auto-Type sequence occurs in the previously selected window. You can perform Entry-Level Auto-Type from the toolbar icon *(A)*, entry context menu *(B)*, or by pressing `Ctrl+Shift+V`.
You can quickly activate the default Auto-Type sequence for a particular entry using Entry-Level Auto-Type. For this operation, the KeePassXC window will be minimized and the Auto-Type sequence occurs in the previously selected window. You can perform Entry-Level Auto-Type from the toolbar icon *(A)*, entry context menu *(B)*, or by pressing kbd:[Ctrl+Shift+V].
WARNING: Be careful when using Entry-Level Auto-Type as you can inadvertently type into the wrong window. For example, a chat window or email.

View File

@@ -75,7 +75,7 @@ NOTE: On Windows, you will be prompted to authenticate to Windows Hello after un
.Windows Hello example
image::quick_unlock_windows_hello.png[]
When your database is locked, you will see the following unlock dialog. Simply press _Enter_ or click on _Unlock Database_ to initiate the biometric authentication process. If you are using a hardware key (e.g. Yubikey), it must be connected to your computer to complete the unlock.
When your database is locked, you will see the following unlock dialog. Simply press kbd:[Enter] or click on _Unlock Database_ to initiate the biometric authentication process. If you are using a hardware key (e.g. Yubikey), it must be connected to your computer to complete the unlock.
.Quick Unlock
image::quick_unlock.png[]
@@ -92,7 +92,7 @@ All the details such as usernames, passwords, URLs, attachments, notes, and so o
To add an entry, perform the following step:
1. Navigate to Entries > New Entry (Or, press Ctrl+N). The following screen appears:
1. Navigate to Entries > New Entry (or press kbd:[Ctrl+N]). The following screen appears:
+
.Adding a new entry
image::edit_entry.png[]
@@ -115,7 +115,7 @@ To edit the details in an entry, perform the following steps:
1. Select the entry you want to edit.
2. Press `Enter`, click the edit toolbar icon, or right-click and select Edit Entry from the menu.
2. Press kbd:[Enter], click the edit toolbar icon, or right-click and select Edit Entry from the menu.
3. Make the desired changes.
@@ -156,13 +156,13 @@ TIP: Each KeePass application has different default icons. If you use a mobile a
==== Deleting an Entry
To delete an entry, perform the following steps:
1. Select the entry you want to delete and press the `Delete` button on your keyboard.
1. Select the entry you want to delete and press the kbd:[Del] button on your keyboard.
2. You will be prompted to move the entry to the Recycle Bin (if enabled).
+
NOTE: You can disable the recycle bin within the Database Settings. If the recycle bin is disabled then deleted entries will be permanently removed from the database.
3. To permanently delete the entry, navigate to the Recycle Bin, select the entry you want to delete and press the `Delete` button on your keyboard.
3. To permanently delete the entry, navigate to the Recycle Bin, select the entry you want to delete and press the kbd:[Del] button on your keyboard.
// tag::advanced[]
==== Clone an Entry
@@ -170,7 +170,7 @@ Creating a clone of an entry provides you a ready-to-use template for creating n
To create a clone of an existing entry, perform the following steps:
1. Right-click on the entry for which you want to create a clone and select _Clone Entry_. Alternatively, select the desired entry and press `Ctrl+K`.
1. Right-click on the entry for which you want to create a clone and select _Clone Entry_. Alternatively, select the desired entry and press kbd:[Ctrl+K].
+
.Clone entry from context menu
image::clone_entry.png[]

View File

@@ -3,52 +3,62 @@ include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
NOTE: On macOS please substitute `Ctrl` with `Cmd` (aka `⌘`).
NOTE: On macOS please substitute kbd:[Ctrl] with kbd:[Cmd] (AKA kbd:[⌘]).
[grid=rows, frame=none, width=75%]
|===
|Action | Keyboard Shortcut
|Action | Keyboard Shortcut
|Settings | Ctrl + ,
|Open Database | Ctrl + O
|Save Database | Ctrl + S
|Save Database As | Ctrl + Shift + S
|New Database | Ctrl + Shift + N
|Close Database | Ctrl + W ; Ctrl + F4
|Lock Current Database | Ctrl + L
|Lock All Databases | Ctrl + Shift + L
|Database Settings | Ctrl + Shift + ,
|Database Reports | Ctrl + Shift + R
|Quit | Ctrl + Q
|New Entry | Ctrl + N
|Edit Entry | Enter ; Ctrl + E
|Delete Entry | Delete
|Clone Entry | Ctrl + D
|Copy Username | Ctrl + B
|Copy Password | Ctrl + C
|Copy URL | Ctrl + U
|Open URL | Ctrl + Shift + U
|Copy TOTP | Ctrl + T
|Copy Password and TOTP | Ctrl + Y
|Show TOTP | Ctrl + Shift + T
|Trigger AutoType | Ctrl + Shift + V
|Add key to SSH Agent | Ctrl + H
|Remove key from SSH Agent | Ctrl + Shift + H
|Move entry up (if unsorted) | Ctrl + Alt + Up
|Move entry down (if unsorted) | Ctrl + Alt + Down
|Sort Groups A-Z | Ctrl + Down
|Sort Groups Z-A | Ctrl + Up
|Minimize Window | Ctrl + M
|Hide Window | Ctrl + Shift + M
|Select Next Database Tab | Ctrl + Tab ; Ctrl + PageDn
|Select Previous Database Tab | Ctrl + Shift + Tab ; Ctrl + PageUp
|Select the nth database | Ctrl + n, where n is the number of the database tab
|Toggle Passwords Hidden | Ctrl + Shift + C
|Toggle Usernames Hidden | Ctrl + Shift + B
|Focus Groups (edit if focused) | F1
|Focus Entries (edit if focused) | F2
|Focus Search | F3 ; Ctrl + F
|Clear Search | Escape
|Show Keyboard Shortcuts | Ctrl + /
|Settings | kbd:[Ctrl + ,]
|Open Database | kbd:[Ctrl + O]
|Save Database | kbd:[Ctrl + S]
|Save Database As | kbd:[Ctrl + Shift + S]
|New Database | kbd:[Ctrl + Shift + N]
|Close Database | kbd:[Ctrl + W] +
_or_ +
kbd:[Ctrl + F4]
|Lock Current Database | kbd:[Ctrl + L]
|Lock All Databases | kbd:[Ctrl + Shift + L]
|Database Settings | kbd:[Ctrl + Shift + ,]
|Database Reports | kbd:[Ctrl + Shift + R]
|Quit | kbd:[Ctrl + Q]
|New Entry | kbd:[Ctrl + N]
|Edit Entry | kbd:[Enter] +
_or_ +
kbd:[Ctrl + E]
|Delete Entry | kbd:[Del]
|Clone Entry | kbd:[Ctrl + D]
|Copy Username | kbd:[Ctrl + B]
|Copy Password | kbd:[Ctrl + C]
|Copy URL | kbd:[Ctrl + U]
|Open URL | kbd:[Ctrl + Shift + U]
|Copy TOTP | kbd:[Ctrl + T]
|Copy Password and TOTP | kbd:[Ctrl + Y]
|Show TOTP | kbd:[Ctrl + Shift + T]
|Trigger AutoType | kbd:[Ctrl + Shift + V]
|Add key to SSH Agent | kbd:[Ctrl + H]
|Remove key from SSH Agent | kbd:[Ctrl + Shift + H]
|Move entry up (if unsorted) | kbd:[Ctrl + Alt + Up]
|Move entry down (if unsorted) | kbd:[Ctrl + Alt + Down]
|Sort Groups A-Z | kbd:[Ctrl + Down]
|Sort Groups Z-A | kbd:[Ctrl + Up]
|Minimize Window | kbd:[Ctrl + M]
|Hide Window | kbd:[Ctrl + Shift + M]
|Select Next Database Tab | kbd:[Ctrl + Tab] +
_or_ +
kbd:[Ctrl + PgDn]
|Select Previous Database Tab | kbd:[Ctrl + Shift + Tab] +
_or_ +
kbd:[Ctrl + PgUp]
|Select the nth database | kbd:[Ctrl + &lt;n&gt;], where kbd:[&lt;n&gt;] is the number of the database tab
|Toggle Passwords Hidden | kbd:[Ctrl + Shift + C]
|Toggle Usernames Hidden | kbd:[Ctrl + Shift + B]
|Focus Groups (edit if focused) | kbd:[F1]
|Focus Entries (edit if focused) | kbd:[F2]
|Focus Search | kbd:[F3] +
_or_ +
kbd:[Ctrl + F]
|Clear Search | kbd:[Esc]
|Show Keyboard Shortcuts | kbd:[Ctrl + /]
|===
// end::content[]

View File

@@ -19,8 +19,8 @@ image::password_generator.png[]
3. Select the length of the desired password by dragging the Length slider.
4. Select the character-sets that you want to include in your password.
5. Use the regenerate button (Ctrl + R) to make a new password using the chosen options.
6. Use the clipboard button (Ctrl + C) to copy the generated password to the clipboard.
5. Use the regenerate button (kbd:[Ctrl + R]) to make a new password using the chosen options.
6. Use the clipboard button (kbd:[Ctrl + C]) to copy the generated password to the clipboard.
7. Click the Advanced button to specify additional conditions for your desired password.
+
.Advanced Password Generator Options
@@ -39,6 +39,6 @@ Word Count slider.
3. In the Word Separator field, enter a character, word, number, or space that you want to use as a separator between the words in your passphrase.
4. _(Optional)_ You can choose a word case between lower, upper, and title case options.
5. _(Optional)_ You can also load your own custom word lists. Click the plus sign button to the right of the wordlist selection dialog to choose a custom word list. You can download alternative lists from the https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases[EFF's Website] or from https://github.com/redacted/XKCD-password-generator#additional-languages[GitHub].
6. Click the Regenerate button (Ctrl + R) to generate a new random passphrase.
7. Click the Clipboard button (Ctrl + C) to copy the passphrase to the clipboard.
6. Click the Regenerate button (kbd:[Ctrl + R]) to generate a new random passphrase.
7. Click the Clipboard button (kbd:[Ctrl + C]) to copy the passphrase to the clipboard.
// end::content[]

View File

@@ -77,8 +77,8 @@ Examples: +
|Press the corresponding keyboard key
|{UP}, {DOWN}, {LEFT}, {RIGHT} |Press the corresponding arrow key
|{F1}, {F2}, ..., {F16} |Press F1, F2, etc.
|{LEFTBRACE}, {RIGHTBRACE} |Press `{` or `}`, respectively
|{F1}, {F2}, ..., {F16} |Press kbd:[F1], kbd:[F2], etc.
|{LEFTBRACE}, {RIGHTBRACE} |Press kbd:[{] or kbd:[}], respectively
|{&lt;KEY&gt; X} |Repeat &lt;KEY&gt; X times (e.g., {SPACE 5} inserts five spaces)
|{DELAY=X} |Set delay between key presses to X milliseconds
|{DELAY X} |Pause typing for X milliseconds
@@ -90,10 +90,10 @@ Examples: +
|===
|Modifier |Description
|+ |SHIFT
|^ |CTRL
|% |ALT
|# |WIN/CMD
|+ |kbd:[Shift]
|^ |kbd:[Ctrl]
|% |kbd:[Alt]
|# |kbd:[Win]/kbd:[Cmd]
|===
*Text Conversions:*

View File

@@ -47,6 +47,7 @@ CMAKE_OPTIONS=""
CPACK_GENERATORS="WIX;ZIP"
COMPILER="g++"
MAKE_OPTIONS="-j$(getconf _NPROCESSORS_ONLN)"
BUILD_PLUGINS="all"
INSTALL_PREFIX="/usr/local"
ORIG_BRANCH=""
ORIG_CWD="$(pwd)"
@@ -131,6 +132,8 @@ Options:
-m, --make-options Make options for compiling sources (default: '${MAKE_OPTIONS}')
-g, --generators Additional CPack generators (default: '${CPACK_GENERATORS}')
-i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}')
-p, --plugins Space-separated list of plugins to build
(default: ${BUILD_PLUGINS})
--snapshot Don't checkout the release tag
-n, --no-source-tarball Don't build source tarball
-h, --help Show this help
@@ -826,6 +829,10 @@ build() {
INSTALL_PREFIX="$2"
shift ;;
-p|--plugins)
BUILD_PLUGINS="$2"
shift ;;
-n|--no-source-tarball)
build_source_tarball=false ;;
@@ -863,9 +870,13 @@ build() {
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Snapshot -DOVERRIDE_VERSION=${RELEASE_NAME}"
else
checkWorkingTreeClean
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Release"
logInfo "Checking out release tag '${TAG_NAME}'..."
if echo "$TAG_NAME" | grep -qE '\-(alpha|beta)[0-9]+$'; then
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=PreRelease"
logInfo "Checking out pre-release tag '${TAG_NAME}'..."
else
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Release"
logInfo "Checking out release tag '${TAG_NAME}'..."
fi
if ! git checkout "$TAG_NAME" > /dev/null 2>&1; then
exitError "Failed to check out target branch."
@@ -911,6 +922,9 @@ build() {
cd "${OUTPUT_DIR}/build-release"
logInfo "Configuring sources..."
for p in ${BUILD_PLUGINS}; do
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On"
done
if [ -n "$OS_LINUX" ] && ${build_appimage}; then
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_DIST_TYPE=AppImage"
# linuxdeploy requires /usr as install prefix

View File

@@ -253,6 +253,7 @@ function Invoke-GpgSignFiles([string[]] $files, [string] $key) {
}
}
# Handle errors and restore state
$OrigDir = (Get-Location).Path
$OrigBranch = & git rev-parse --abbrev-ref HEAD
@@ -344,7 +345,6 @@ if ($Merge) {
Write-Host "All done!"
Write-Host "Please merge the release branch back into the develop branch now and then push your changes."
Write-Host "Don't forget to also push the tags using 'git push --tags'."
} elseif ($Build) {
$Vcpkg = (Resolve-Path "$Vcpkg/scripts/buildsystems/vcpkg.cmake").Path
@@ -357,13 +357,6 @@ if ($Merge) {
Test-RequiredPrograms
# Create directories
New-Item "$OutDir" -ItemType Directory -Force | Out-Null
$OutDir = (Resolve-Path $OutDir).Path
$BuildDir = "$OutDir\build-release"
New-Item "$BuildDir" -ItemType Directory -Force | Out-Null
if ($Snapshot) {
$Tag = "HEAD"
$SourceBranch = & git rev-parse --abbrev-ref HEAD
@@ -373,26 +366,38 @@ if ($Merge) {
} else {
Test-WorkingTreeClean
# Clear build directory except for installed vcpkg files to prevent having to re-sign everything
if (Test-Path $BuildDir) {
Get-ChildItem $BuildDir -Recurse | Where-Object {$_.FullName -notlike "$BuildDir\vcpkg_installed*"} | Remove-Item -Recurse -Force
# Clear output directory
if (Test-Path $OutDir) {
Remove-Item $OutDir -Recurse
}
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=Release $CMakeOptions"
if ($Version -match "-beta\d*$") {
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=PreRelease $CMakeOptions"
} else {
$CMakeOptions = "-DKEEPASSXC_BUILD_TYPE=Release $CMakeOptions"
}
# Setup Tag if not defined then checkout tag
if ($Tag -eq "" -or $null -eq $Tag) {
if ($Tag -eq "" -or $Tag -eq $null) {
$Tag = $Version
}
Write-Host "Checking out tag 'tags/$Tag' to build." -ForegroundColor Cyan
Invoke-Cmd "git" "checkout `"tags/$Tag`""
}
# Create directories
New-Item "$OutDir" -ItemType Directory -Force | Out-Null
$OutDir = (Resolve-Path $OutDir).Path
$BuildDir = "$OutDir\build-release"
New-Item "$BuildDir" -ItemType Directory -Force | Out-Null
# Enter build directory
Set-Location "$BuildDir"
# Setup CMake options
$CMakeOptions = "-DWITH_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE:FILEPATH=`"$Vcpkg`" -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON"
$CMakeOptions = "-DWITH_XC_ALL=ON -DWITH_TESTS=OFF -DCMAKE_BUILD_TYPE=Release $CMakeOptions"
$CMakeOptions = "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=`"$Vcpkg`" -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON $CMakeOptions"
Write-Host "Configuring build..." -ForegroundColor Cyan
Invoke-Cmd "cmake" "-G `"$CMakeGenerator`" $CMakeOptions `"$SourceDir`""
@@ -431,7 +436,6 @@ if ($Merge) {
# Restore state
Invoke-Command {git checkout $OrigBranch}
Set-Location "$OrigDir"
} elseif ($Sign) {
Test-RequiredPrograms
@@ -455,8 +459,8 @@ if ($Merge) {
# SIG # Begin signature block
# MIImVAYJKoZIhvcNAQcCoIImRTCCJkECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnhvt4NN16Hfle
# eDfbtgcr43dOfERdBCPSlLlozTRPoaCCH2owggYUMIID/KADAgECAhB6I67aU2mW
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCRMgDV7DQ6PzRo
# 3ULpsxL1VU2JvIFnZPXlxq/hkfU2Y6CCH2owggYUMIID/KADAgECAhB6I67aU2mW
# D5HIPlz0x+M/MA0GCSqGSIb3DQEBDAUAMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQK
# Ew9TZWN0aWdvIExpbWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIFRpbWUg
# U3RhbXBpbmcgUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5
@@ -628,34 +632,34 @@ if ($Merge) {
# Z28gTGltaXRlZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5n
# IENBIFIzNgIQBkM/zMzkM6iSzBe3RqWMZTANBglghkgBZQMEAgEFAKCBhDAYBgor
# BgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE
# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCB3
# VDtldlpqUJGX7ENprygJeJxr+V2oUTlguFLaSOkkcTANBgkqhkiG9w0BAQEFAASC
# AgAIu1lvIg5zEwMJ87vlGg8H5BvvXdCiQllHuZir0ULpGs/IG03o3uvL9bI8IEt0
# Elq5ePQ1qlg2z3BT3a+7+SyMChGVVoko3U60aQyjjlXPtzh7HUEKdQWDyrabHmEk
# YACxpRF/h6FoJAkQqVr5cF5d+Ey8SdfE9h9MyqcaxeWtJPEFI/qjp4sfQuJZuXpN
# saallK9xM+xKrigwTx8s7C/clWZX8OtAIIegpf8mnU5ETYS6XnvefcAMsXKBmxjI
# 01GadXwlpJ/I7d/1LrZ5OjOmqVrAWnJEOkdV5+5xJbnJInPvgnBOrLeQyR7QEU6V
# XtiD6k+LqgfU7bP8AQCouz33M2DdxmfKZPPReMuZaNI3RvNX9M49wAXey/iuy+pm
# G9+DwGT3IHCIhOrL5GimdTO8vcVkdzIFdPgAb/jOR4n8P3VRaPH1sH0rO5vHuwfG
# nO7Mrb4AMYo4sP0RXkIGWXhj0Z7VrajAYHtcSnwCedUbHxk9faDbF5Vrh4kTCt+T
# utMXrepDzK6kWtr8BX68aA/meoSzN0ZhOchWbWXxC4kPSTtNTnBHAWVKaCjxBVq4
# J+CTlKGLdj78SVMTeE8guyifYNiRkrqRCuXghKQZEIN0ry1SbpQA27wZQWVJrL8E
# XE0F54gWMGrwqCik1GSfAgPYZd3IWdLc6jPR9ec/uS/QaaGCAyIwggMeBgkqhkiG
# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCw
# CjBOFSrHIl5SZxVeFP1D+IfXa4B5pNieNHIkm0/SqTANBgkqhkiG9w0BAQEFAASC
# AgAgFK2xkUz0aie9HSo0e4qyDk83CNX9G/GR7+DObTay5l7OYVZIdB2kOZIS8UbH
# 4gMSsjplIVObVyf1DjGGCctq4bFDABL7wpwqm7P3tEjs2d/HK2Yxoe1c8YFTYMJJ
# Vc6Q9l/nZA7ZC/SCH1NyEgK+w3vQ6SARudN8/ZgFVa1P3DdwOADmLD774v3bOUKq
# XKDOySeYD7bkCekPv6yx6DnrWBBsYIKFRv2Yv4duThki4CC1FMgEVTmdBDJIP3R8
# 1BgXjPvVxYX3aQ9emC3KluyNr/BEPZiVdwBjXCE60n7g/Y8qNgqY0ZaImSpl9MFx
# VkrxE7iNfBcBE8xVCghyDahs1BxyEeEdQk+QlLD1Cv3KGODlyWjgncDAX7fnkC6l
# M7KUttjXGi9uQG3g2dUCX+744wPhRg+DBfch2Em70I0kYsPY6ETyrQogZdi6QzKO
# Hlf/hUW0o9HCc6BrTSL4y8G0mlKVCgUpMOjlrip88bvW05ZUX20arGKxGg1uxFIA
# r7wvQyFn+RvNc0kqWt/xgwp3HAc80ABPCYumLqGwucBWisiMt4P2s+fkLpYJdC/n
# pS/3fRoepfGmv8J1WAIjGiO7e12aDrTQqNP+2RUzkNpy2eRQDL+3VUFQOQqEfkVL
# Y6wpN6nB7olNULhPUlwZChf49v/h+XUxhgHozWN576qoyqGCAyIwggMeBgkqhkiG
# 9w0BCQYxggMPMIIDCwIBATBpMFUxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0
# aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3RpZ28gUHVibGljIFRpbWUgU3RhbXBp
# bmcgQ0EgUjM2AhA6UmoshM5V5h1l/MwS2OmJMA0GCWCGSAFlAwQCAgUAoHkwGAYJ
# KoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjUwMTAzMDQx
# MzI4WjA/BgkqhkiG9w0BCQQxMgQwKlrwmwJXs6mQ/rkiyCJkh8uDUj2DERGpO4aA
# iq1TY56EYG/QvCktwMVZYRTI8Cu1MA0GCSqGSIb3DQEBAQUABIICAGyQ98hsyLlN
# dOnWHLSncZ5ovUQBXEAeeMxuRhB3G7bpdcpi4a/t7q+tLzLH2CKrpCZ5dS0tbRd9
# ng7kH4X8WbllElxzvnnwAmPrYDfX/VO8bVS/JJ8qmfrtgHhs+4dtMFkV11VXNRYs
# IC2xbYMbwuH7cWA/L+muDADM0VLONKB3HECPhbIhfHCSKJr8i2cYyW3AixC77SPG
# mimDoh3umEJLjMvCsGJ32BrVbXKR4hBCOxmDN470nxKDuEurW6JvDh6GjevcdzeT
# ungxb0pnFIfj5IMhjjrZhccCQK1ekvFLmb/ybzfZGbXGb8QHJP4JvFm8sWNAccIn
# dQ3SixhyKW+gOC2r0URTPrIDpbdHuGnm3LVBPhBijrJrv7w1/neEQFFLFXfrjgJb
# K8zc2IMYPW+Vn/hclNGfK8EOBqLGvsn7xB0K1f13kVt7QT36nIYojIE5MwWhvPDi
# 1oIDdJEvZS/P8tmX6lwAXxPRQagPVhNefUUHk11/kUaBUGGi+bRpKOgcS1fPqsP1
# 1RQMSEzjsnGEzLxxqa+AtxLDre2vBJsllCD4JJwrpkQW+xu014bZleJ3i1IFDUaR
# E16d6Qi4KEC0RfZ1NwahKQFqBj3wBCMLGEY6fNndE4jVGO0BkP3vO23pt7n4xmM+
# swJJsR48RDIDkSiV+2vPvlk/9KXVW9ji
# KoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjQwOTE0MDMy
# MTU1WjA/BgkqhkiG9w0BCQQxMgQwwrUMFcAva5866cdprEw/weWm4EfoAA4SCloN
# B50191F7ps9XQIxGfsz+g0vQxzxfMA0GCSqGSIb3DQEBAQUABIICAC3qVFmWQWkL
# kn/AYJPZ3B7Yvwq0P7SqcHO9w5FiV5wsznH6xfvkTzXssQLhKaZdqypnHCTNth8D
# 7mgr6zZYh5CgQQ3SSG2q0xVzs3wanJmZ4g6I7bVeGMLv47tFnCed9G3aP5cywDBn
# vMOiwZnQR1WwM8T6qE4sAb4lKXUYDbIVB1DMRAF3j2rQMAN9e9jF6Ok+ZyQqpBSl
# ve2vBR0TgFXeyidwiz6O2I1FWc1OzwMchbJTANbQqWRKuiQ6gm0Bj/S8dalBb77I
# jxS0Tn7kRH1Sr50ZfWRSxj7H7afsQOKbDHxhWFhctvQfbrmbNj+gHcm9j/rSPpU7
# zj5OvgKyYQnjiLjCnGBTmSML2ZwvXhPv2XkFQ2yL2nYWTRqLjARdcP62kSrkQxEa
# DLAZ7mcndE+HZVMllBGVI9/H5hkE7jINBU4gNvyqQQqF3xTatJMldyrXCQ6R9wfN
# LsdyFB177vZXLrS1EymCzq1COpbrw3oa/LXP+1hZFhoaOYy00LUnCU5Zjd8UFWIh
# FDj3Z7O/Xz3P8BR4t7PGqUu3x8UbxcsGDH0w0e3pvPmxXiBZlspjNieg073YNKxU
# Yuj0b3cX/cpYH0M0Ne/tXuHwbZthwwll3vytT7Aa+oglejolDQjRc8Gv5KW0dUK3
# LmVw9eforeFUrTExSEc/0jf29BmZz9do
# SIG # End signature block

View File

@@ -5943,6 +5943,17 @@ Are you sure you want to continue with this file?</source>
<source>Don&apos;t show again for this version</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: You are using an unstable build of KeePassXC.
There is a high risk of corruption, maintain a backup of your databases.
This version is not meant for production use.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>NOTE: You are using a pre-release version of KeePassXC.
Expect some bugs and minor issues, this version is meant for testing purposes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No Tags</source>
<translation type="unfinished"></translation>
@@ -6290,9 +6301,7 @@ Are you sure you want to continue with this file?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: You are using a development snapshot build of KeePassXC.
Maintain a backup of your databases in the event of unknown bugs.
This version is not meant for production use.</source>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -6307,10 +6316,6 @@ This version is not meant for production use.</source>
<source>Clear all identities in ssh-agent</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ManageDatabase</name>
@@ -8575,6 +8580,10 @@ Kernel: %3 %4</source>
<source>KeeShare</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>YubiKey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Quick Unlock</source>
<translation type="unfinished"></translation>
@@ -8583,6 +8592,10 @@ Kernel: %3 %4</source>
<source>Secret Service Integration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>None</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enabled extensions:</source>
<translation type="unfinished"></translation>
@@ -9164,57 +9177,6 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Only PBKDF and Argon2 are supported, cannot decrypt json file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hardware Keys</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot generate valid passphrases because the wordlist is too short</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Format to use when exporting. Available choices are &apos;xml&apos;, &apos;csv&apos; or &apos;html&apos;. Defaults to &apos;xml&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>malformed string, possible unescaped delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>missing closing delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1, row: %2, column: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Encrypted files are not supported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Proton Pass Import</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tags</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete plugin data?</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Delete plugin data from Entry(s)?</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Reset Shortcuts</source>
<translation type="unfinished"></translation>
@@ -9235,10 +9197,57 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Shortcut %1 conflicts with &apos;%2&apos;. Overwrite shortcut?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot generate valid passphrases because the wordlist is too short</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Encrypted files are not supported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Proton Pass Import</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete plugin data?</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Delete plugin data from Entry(s)?</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Format to use when exporting. Available choices are &apos;xml&apos;, &apos;csv&apos; or &apos;html&apos;. Defaults to &apos;xml&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>start minimized to the system tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>malformed string, possible unescaped delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>missing closing delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1, row: %2, column: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tags</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtIOCompressor</name>
@@ -10149,6 +10158,10 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Weak Passwords</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>TOTP Entries</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TagView</name>

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
# Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
# Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,6 +16,18 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
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(Passkeys WITH_XC_BROWSER_PASSKEYS "Passkeys support for browser integration")
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare")
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking")
if(UNIX AND NOT APPLE)
add_feature_info(FdoSecrets WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API.")
endif()
set(core_SOURCES
core/Alloc.cpp
core/AutoTypeAssociations.cpp
@@ -84,11 +96,6 @@ set(core_SOURCES
keys/FileKey.cpp
keys/PasswordKey.cpp
keys/ChallengeResponseKey.cpp
keys/drivers/YubiKey.h
keys/drivers/YubiKey.cpp
keys/drivers/YubiKeyInterface.cpp
keys/drivers/YubiKeyInterfaceUSB.cpp
keys/drivers/YubiKeyInterfacePCSC.cpp
streams/HashedBlockStream.cpp
streams/HashingStream.cpp
streams/HmacBlockStream.cpp
@@ -187,7 +194,6 @@ set(gui_SOURCES
gui/reports/ReportsPageHibp.cpp
gui/reports/ReportsWidgetStatistics.cpp
gui/reports/ReportsPageStatistics.cpp
gui/osutils/DeviceListener.cpp
gui/osutils/OSUtilsBase.cpp
gui/osutils/ScreenLockListener.cpp
gui/osutils/ScreenLockListenerPrivate.cpp
@@ -209,7 +215,6 @@ set(gui_SOURCES
if(APPLE)
list(APPEND gui_SOURCES
gui/osutils/macutils/DeviceListenerMac.cpp
gui/osutils/macutils/MacPasteboard.cpp
gui/osutils/macutils/MacUtils.cpp
gui/osutils/macutils/ScreenLockListenerMac.cpp
@@ -223,16 +228,14 @@ endif()
if(UNIX AND NOT APPLE)
list(APPEND gui_SOURCES
gui/osutils/nixutils/DeviceListenerLibUsb.cpp
gui/osutils/nixutils/NixUtils.cpp
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
)
gui/osutils/nixutils/NixUtils.cpp)
if("${CMAKE_SYSTEM}" MATCHES "Linux")
list(APPEND core_SOURCES
quickunlock/Polkit.cpp
quickunlock/PolkitDbusTypes.cpp)
endif()
if(WITH_X11)
if(WITH_XC_X11)
list(APPEND gui_SOURCES
gui/osutils/nixutils/X11Funcs.cpp)
endif()
@@ -250,11 +253,15 @@ if(UNIX AND NOT APPLE)
quickunlock/dbus/org.freedesktop.PolicyKit1.Authority.xml
polkit_dbus
)
find_library(KEYUTILS_LIBRARIES NAMES keyutils)
if(NOT KEYUTILS_LIBRARIES)
message(FATAL_ERROR "Could not find libkeyutils")
endif()
endif()
if(WIN32)
list(APPEND gui_SOURCES
gui/osutils/winutils/DeviceListenerWin.cpp
gui/osutils/winutils/ScreenLockListenerWin.cpp
gui/osutils/winutils/WinUtils.cpp)
if (MSVC)
@@ -262,24 +269,86 @@ if(WIN32)
endif()
endif()
if(WITH_XC_YUBIKEY)
list(APPEND gui_SOURCES gui/osutils/DeviceListener.cpp)
if(APPLE)
list(APPEND gui_SOURCES gui/osutils/macutils/DeviceListenerMac.cpp)
elseif(UNIX)
list(APPEND gui_SOURCES gui/osutils/nixutils/DeviceListenerLibUsb.cpp)
elseif(WIN32)
list(APPEND gui_SOURCES gui/osutils/winutils/DeviceListenerWin.cpp)
endif()
endif()
add_subdirectory(browser)
add_subdirectory(proxy)
if(KPXC_FEATURE_BROWSER)
if(WITH_XC_BROWSER)
set(browser_LIB browser)
list(APPEND gui_SOURCES
gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
gui/entry/EntryURLModel.cpp
gui/reports/ReportsPageBrowserStatistics.cpp
gui/reports/ReportsPagePasskeys.cpp
gui/reports/ReportsWidgetBrowserStatistics.cpp
gui/reports/ReportsPageBrowserStatistics.cpp)
endif()
if(WITH_XC_BROWSER_PASSKEYS)
list(APPEND gui_SOURCES
gui/reports/ReportsWidgetPasskeys.cpp
gui/reports/ReportsPagePasskeys.cpp
gui/passkeys/PasskeyExporter.cpp
gui/passkeys/PasskeyExportDialog.cpp
gui/passkeys/PasskeyImporter.cpp
gui/passkeys/PasskeyImportDialog.cpp)
endif()
if(KPXC_FEATURE_NETWORK)
add_subdirectory(autotype)
add_subdirectory(cli)
add_subdirectory(qrcode)
set(qrcode_LIB qrcode)
add_subdirectory(keeshare)
if(WITH_XC_KEESHARE)
set(keeshare_LIB keeshare)
endif()
add_subdirectory(sshagent)
if(WITH_XC_SSHAGENT)
set(sshagent_LIB sshagent)
endif()
add_subdirectory(fdosecrets)
if(WITH_XC_FDOSECRETS)
set(fdosecrets_LIB fdosecrets)
endif()
add_subdirectory(thirdparty)
set(autotype_SOURCES
autotype/AutoType.cpp
autotype/AutoTypeAction.cpp
autotype/AutoTypeMatchModel.cpp
autotype/AutoTypeMatchView.cpp
autotype/AutoTypeSelectDialog.cpp
autotype/PickcharsDialog.cpp
autotype/WindowSelectComboBox.cpp)
add_library(autotype STATIC ${autotype_SOURCES})
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
if(WITH_XC_YUBIKEY)
list(APPEND core_SOURCES
keys/drivers/YubiKey.h
keys/drivers/YubiKey.cpp
keys/drivers/YubiKeyInterface.cpp
keys/drivers/YubiKeyInterfaceUSB.cpp
keys/drivers/YubiKeyInterfacePCSC.cpp)
else()
list(APPEND core_SOURCES
keys/drivers/YubiKey.h
keys/drivers/YubiKeyStub.cpp)
endif()
if(WITH_XC_NETWORKING)
list(APPEND gui_SOURCES
networking/HibpDownloader.cpp
networking/NetworkManager.cpp
@@ -289,36 +358,12 @@ if(KPXC_FEATURE_NETWORK)
gui/IconDownloaderDialog.cpp)
endif()
add_subdirectory(cli)
add_subdirectory(thirdparty)
add_subdirectory(autotype)
set(autotype_LIB autotype)
# TODO: Refactor to gui sources
add_subdirectory(qrcode)
set(qrcode_LIB qrcode)
# TODO: Move to gui sources on refactor
add_subdirectory(keeshare)
set(keeshare_LIB keeshare)
add_subdirectory(sshagent)
if(KPXC_FEATURE_SSHAGENT)
set(sshagent_LIB sshagent)
endif()
add_subdirectory(fdosecrets)
if(KPXC_FEATURE_FDOSECRETS)
set(fdosecrets_LIB fdosecrets)
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)
# Core Library Definition
add_library(keepassxc_core STATIC ${core_SOURCES})
set_target_properties(keepassxc_core PROPERTIES COMPILE_DEFINITIONS KPXC_BUILDING_CORE)
set_target_properties(keepassxc_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
target_link_libraries(keepassxc_core
${qrcode_LIB}
Qt5::Core
@@ -334,12 +379,12 @@ target_link_libraries(keepassxc_core
# GUI Library Definition
add_library(keepassxc_gui STATIC ${gui_SOURCES})
set_target_properties(keepassxc_gui PROPERTIES COMPILE_DEFINITIONS KPXC_BUILDING_CORE)
set_target_properties(keepassxc_gui PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
target_link_libraries(keepassxc_gui
keepassxc_core
Qt5::Network
Qt5::Widgets
${autotype_LIB}
autotype
${browser_LIB}
${fdosecrets_LIB}
${keeshare_LIB}
@@ -356,7 +401,7 @@ if(HAIKU)
endif()
if(UNIX AND NOT APPLE)
target_link_libraries(keepassxc_core Qt5::DBus ${LIBUSB_LIBRARIES})
if(WITH_X11)
if(WITH_XC_X11)
target_link_libraries(keepassxc_gui Qt5::X11Extras X11)
endif()
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
@@ -494,7 +539,7 @@ if(WIN32)
COMPONENT Runtime)
# install OpenSSL library
if(KPXC_FEATURE_NETWORK)
if(WITH_XC_NETWORKING)
find_file(OPENSSL_DLL
NAMES libssl-3.dll libssl-3-x64.dll
HINTS "${OPENSSL_ROOT_DIR}/bin"

View File

@@ -148,7 +148,9 @@ AutoType::AutoType(QObject* parent, bool test)
QString pluginPath = resources()->pluginPath(pluginName);
if (!pluginPath.isEmpty()) {
#ifdef WITH_XC_AUTOTYPE
loadPlugin(pluginPath);
#endif
}
connect(this, SIGNAL(autotypeFinished()), SLOT(resetAutoTypeState()));

View File

@@ -1,21 +1,7 @@
# Main auto-type static library
set(autotype_SOURCES
AutoType.cpp
AutoTypeAction.cpp
AutoTypeMatchModel.cpp
AutoTypeMatchView.cpp
AutoTypeSelectDialog.cpp
PickcharsDialog.cpp
WindowSelectComboBox.cpp)
add_library(autotype STATIC ${autotype_SOURCES})
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
# Platform specific auto-type implementations
if(UNIX AND NOT APPLE AND NOT HAIKU)
if(WITH_X11)
if(WITH_XC_AUTOTYPE)
if(UNIX AND NOT APPLE AND NOT HAIKU)
find_package(X11 REQUIRED COMPONENTS Xi XTest)
find_package(Qt5X11Extras REQUIRED)
find_package(Qt5X11Extras 5.2 REQUIRED)
if(PRINT_SUMMARY)
add_feature_info(libXi X11_Xi_FOUND "The X11 Xi Protocol library is required for auto-type")
add_feature_info(libXtst X11_XTest_FOUND "The X11 XTEST Protocol library is required for auto-type")
@@ -23,14 +9,13 @@ if(UNIX AND NOT APPLE AND NOT HAIKU)
endif()
add_subdirectory(xcb)
elseif(APPLE)
add_subdirectory(mac)
elseif(WIN32)
add_subdirectory(windows)
endif()
elseif(APPLE)
add_subdirectory(mac)
elseif(WIN32)
add_subdirectory(windows)
endif()
# Auto-type tests
if(WITH_TESTS)
add_subdirectory(test)
if(WITH_TESTS)
add_subdirectory(test)
endif()
endif()

View File

@@ -17,9 +17,11 @@
#include "BrowserAction.h"
#include "BrowserMessageBuilder.h"
#ifdef WITH_XC_BROWSER_PASSKEYS
#include "BrowserPasskeys.h"
#include "BrowserSettings.h"
#include "PasskeyUtils.h"
#endif
#include "BrowserSettings.h"
#include "core/Global.h"
#include "core/Tools.h"
@@ -108,10 +110,12 @@ QJsonObject BrowserAction::handleAction(QLocalSocket* socket, const QJsonObject&
return handleGlobalAutoType(json, action);
} else if (action.compare("get-database-entries", Qt::CaseSensitive) == 0) {
return handleGetDatabaseEntries(json, action);
#ifdef WITH_XC_BROWSER_PASSKEYS
} else if (action.compare(BROWSER_REQUEST_PASSKEYS_GET) == 0) {
return handlePasskeysGet(json, action);
} else if (action.compare(BROWSER_REQUEST_PASSKEYS_REGISTER) == 0) {
return handlePasskeysRegister(json, action);
#endif
}
// Action was not recognized
@@ -515,6 +519,7 @@ QJsonObject BrowserAction::handleGlobalAutoType(const QJsonObject& json, const Q
return buildResponse(action, browserRequest.incrementedNonce);
}
#ifdef WITH_XC_BROWSER_PASSKEYS
QJsonObject BrowserAction::handlePasskeysGet(const QJsonObject& json, const QString& action)
{
if (!m_associated) {
@@ -581,6 +586,7 @@ QJsonObject BrowserAction::handlePasskeysRegister(const QJsonObject& json, const
const Parameters params{{"response", response}};
return buildResponse(action, browserRequest.incrementedNonce, params);
}
#endif
QJsonObject BrowserAction::decryptMessage(const QString& message, const QString& nonce)
{

View File

@@ -84,15 +84,19 @@ private:
QJsonObject handleGetTotp(const QJsonObject& json, const QString& action);
QJsonObject handleDeleteEntry(const QJsonObject& json, const QString& action);
QJsonObject handleGlobalAutoType(const QJsonObject& json, const QString& action);
#ifdef WITH_XC_BROWSER_PASSKEYS
QJsonObject handlePasskeysGet(const QJsonObject& json, const QString& action);
QJsonObject handlePasskeysRegister(const QJsonObject& json, const QString& action);
#endif
private:
QJsonObject buildResponse(const QString& action, const QString& nonce, const Parameters& params = {});
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
QJsonObject decryptMessage(const QString& message, const QString& nonce);
BrowserRequest decodeRequest(const QJsonObject& json);
StringPairList getConnectionKeys(const BrowserRequest& browserRequest);
private:
static const int MaxUrlLength;
QString m_clientPublicKey;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 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
@@ -71,7 +71,7 @@ PublicKeyCredential BrowserPasskeys::buildRegisterPublicKeyCredential(const QJso
}
const auto authenticatorAttachment = credentialCreationOptions["authenticatorAttachment"];
const auto clientDataJson = credentialCreationOptions["clientDataJSON"].toObject();
const auto clientDataJson = credentialCreationOptions["clientDataJSON"].toString();
const auto extensions = credentialCreationOptions["extensions"].toString();
const auto credentialId = testingVariables.credentialId.isEmpty()
? browserMessageBuilder()->getRandomBytesAsBase64(ID_BYTES)
@@ -98,7 +98,7 @@ PublicKeyCredential BrowserPasskeys::buildRegisterPublicKeyCredential(const QJso
// Response
QJsonObject responseObject;
responseObject["attestationObject"] = browserMessageBuilder()->getBase64FromArray(attestationObject);
responseObject["clientDataJSON"] = browserMessageBuilder()->getBase64FromJson(clientDataJson);
responseObject["clientDataJSON"] = browserMessageBuilder()->getBase64FromArray(clientDataJson.toUtf8());
responseObject["clientExtensionResults"] = credentialCreationOptions["clientExtensionResults"];
// Additions for extension side functions
@@ -130,8 +130,8 @@ QJsonObject BrowserPasskeys::buildGetPublicKeyCredential(const QJsonObject& asse
const auto authenticatorData =
buildAuthenticatorData(assertionOptions["rpId"].toString(), assertionOptions["extensions"].toString());
const auto clientDataJson = assertionOptions["clientDataJson"].toObject();
const auto clientDataArray = QJsonDocument(clientDataJson).toJson(QJsonDocument::Compact);
const auto clientDataJson = assertionOptions["clientDataJson"].toString();
const auto clientDataArray = clientDataJson.toUtf8();
const auto signature = buildSignature(authenticatorData, clientDataArray, privateKeyPem);
if (signature.isEmpty()) {
@@ -140,7 +140,7 @@ QJsonObject BrowserPasskeys::buildGetPublicKeyCredential(const QJsonObject& asse
QJsonObject responseObject;
responseObject["authenticatorData"] = browserMessageBuilder()->getBase64FromArray(authenticatorData);
responseObject["clientDataJSON"] = browserMessageBuilder()->getBase64FromJson(clientDataJson);
responseObject["clientDataJSON"] = browserMessageBuilder()->getBase64FromArray(clientDataArray);
responseObject["clientExtensionResults"] = assertionOptions["clientExtensionResults"];
responseObject["signature"] = browserMessageBuilder()->getBase64FromArray(signature);
responseObject["userHandle"] = userHandle;
@@ -273,7 +273,7 @@ BrowserPasskeys::buildCredentialPrivateKey(int alg, const QString& predefinedFir
try {
Botan::Ed25519_PrivateKey key(*randomGen()->getRng());
auto publicKey = key.get_public_key();
#ifdef WITH_BOTAN3
#ifdef WITH_XC_BOTAN3
auto privateKey = key.raw_private_key_bits();
#else
auto privateKey = key.get_private_key();
@@ -322,7 +322,7 @@ QByteArray BrowserPasskeys::buildSignature(const QByteArray& authenticatorData,
std::vector<uint8_t> rawSignature;
if (algName == "ECDSA") {
Botan::ECDSA_PrivateKey privateKey(algId, privateKeyBytes);
#ifdef WITH_BOTAN3
#ifdef WITH_XC_BOTAN3
Botan::PK_Signer signer(
privateKey, *randomGen()->getRng(), "EMSA1(SHA-256)", Botan::Signature_Format::DerSequence);
#else

View File

@@ -23,18 +23,20 @@
#include "BrowserEntrySaveDialog.h"
#include "BrowserHost.h"
#include "BrowserMessageBuilder.h"
#include "BrowserPasskeys.h"
#include "BrowserPasskeysClient.h"
#include "BrowserPasskeysConfirmationDialog.h"
#include "BrowserSettings.h"
#include "PasskeyUtils.h"
#include "core/EntryAttributes.h"
#include "core/Tools.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
#include "gui/UrlTools.h"
#include "gui/osutils/OSUtils.h"
#ifdef WITH_XC_BROWSER_PASSKEYS
#include "BrowserPasskeys.h"
#include "BrowserPasskeysClient.h"
#include "BrowserPasskeysConfirmationDialog.h"
#include "PasskeyUtils.h"
#include "gui/passkeys/PasskeyImporter.h"
#endif
#ifdef Q_OS_MACOS
#include "gui/osutils/macutils/MacUtils.h"
#endif
@@ -55,9 +57,11 @@
const QString BrowserService::KEEPASSXCBROWSER_NAME = QStringLiteral("KeePassXC-Browser Settings");
const QString BrowserService::KEEPASSXCBROWSER_OLD_NAME = QStringLiteral("keepassxc-browser Settings");
static const QString KEEPASSXCBROWSER_GROUP_NAME = QStringLiteral("KeePassXC-Browser Passwords");
static const QString PASSKEYS_DEFAULT_GROUP_NAME = QStringLiteral("KeePassXC-Browser Passkeys");
static int KEEPASSXCBROWSER_DEFAULT_ICON = 1;
#ifdef WITH_XC_BROWSER_PASSKEYS
static int KEEPASSXCBROWSER_PASSKEY_ICON = 13;
static const QString PASSKEYS_DEFAULT_GROUP_NAME = QStringLiteral("KeePassXC-Browser Passkeys");
#endif
// These are for the settings and password conversion
static const QString KEEPASSHTTP_NAME = QStringLiteral("KeePassHttp Settings");
static const QString KEEPASSHTTP_GROUP_NAME = QStringLiteral("KeePassHttp Passwords");
@@ -326,6 +330,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName, bool isPass
}
#endif
name = newGroup->name();
newGroup->setCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY, Group::Disable);
uuid = Tools::uuidToHex(newGroup->uuid());
previousGroup = newGroup;
continue;
@@ -629,6 +634,7 @@ QString BrowserService::getKey(const QString& id)
return db->metadata()->customData()->value(CustomData::getKeyWithPrefix(CustomData::BrowserKeyPrefix, id));
}
#ifdef WITH_XC_BROWSER_PASSKEYS
// Passkey registration
QJsonObject BrowserService::showPasskeysRegisterPrompt(const QJsonObject& publicKeyOptions,
const QString& origin,
@@ -854,6 +860,7 @@ void BrowserService::addPasskeyToEntry(Entry* entry,
entry->endUpdate();
}
#endif
void BrowserService::addEntry(const EntryParameters& entryParameters,
const QString& group,
@@ -1041,10 +1048,12 @@ QList<Entry*> BrowserService::searchEntries(const QSharedPointer<Database>& db,
continue;
}
#ifdef WITH_XC_BROWSER_PASSKEYS
// With Passkeys, check for the Relying Party instead of URL
if (passkey && entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY) != siteUrl) {
continue;
}
#endif
// Additional URL check may have already inserted the entry to the list
if (!entries.contains(entry)) {
@@ -1384,6 +1393,7 @@ bool BrowserService::shouldIncludeEntry(Entry* entry,
return false;
}
#ifdef WITH_XC_BROWSER_PASSKEYS
// Returns all Passkey entries for the current Relying Party
QList<Entry*> BrowserService::getPasskeyEntries(const QString& rpId, const StringPairList& keyList)
{
@@ -1458,6 +1468,7 @@ QJsonObject BrowserService::getPasskeyError(int errorCode) const
{
return QJsonObject({{"errorCode", errorCode}});
}
#endif
bool BrowserService::handleURL(const QString& entryUrl,
const QString& siteUrl,

View File

@@ -87,7 +87,7 @@ public:
QSharedPointer<Database> getDatabase(const QUuid& rootGroupUuid = {});
QSharedPointer<Database> selectedDatabase();
QList<QSharedPointer<Database>> getOpenDatabases();
#ifdef WITH_XC_BROWSER_PASSKEYS
QJsonObject showPasskeysRegisterPrompt(const QJsonObject& publicKeyOptions,
const QString& origin,
const QString& groupName,
@@ -111,7 +111,7 @@ public:
const QString& credentialId,
const QString& userHandle,
const QString& privateKey);
#endif
void addEntry(const EntryParameters& entryParameters,
const QString& group,
const QString& groupUuid,
@@ -186,7 +186,7 @@ private:
bool removeFirstDomain(QString& hostname);
bool
shouldIncludeEntry(Entry* entry, const QString& url, const QString& submitUrl, const bool omitWwwSubdomain = false);
#ifdef WITH_XC_BROWSER_PASSKEYS
QList<Entry*> getPasskeyEntries(const QString& rpId, const StringPairList& keyList);
QList<Entry*>
getPasskeyEntriesWithUserHandle(const QString& rpId, const QString& userId, const StringPairList& keyList);
@@ -196,7 +196,7 @@ private:
const QString& rpId,
const StringPairList& keyList);
QJsonObject getPasskeyError(int errorCode) const;
#endif
bool handleURL(const QString& entryUrl,
const QString& siteUrl,
const QString& formUrl,
@@ -223,7 +223,9 @@ private:
Q_DISABLE_COPY(BrowserService);
friend class TestBrowser;
#ifdef WITH_XC_BROWSER_PASSKEYS
friend class TestPasskeys;
#endif
};
static inline BrowserService* browserService()

View File

@@ -13,28 +13,32 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(KPXC_FEATURE_BROWSER)
if(WITH_XC_BROWSER)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(browser_SOURCES
BrowserAccessControlDialog.cpp
BrowserAction.cpp
BrowserCbor.cpp
BrowserEntryConfig.cpp
BrowserEntrySaveDialog.cpp
BrowserHost.cpp
BrowserMessageBuilder.cpp
BrowserPasskeys.cpp
BrowserPasskeysClient.cpp
BrowserPasskeysConfirmationDialog.cpp
BrowserSettingsPage.cpp
BrowserSettingsWidget.cpp
BrowserService.cpp
BrowserSettings.cpp
BrowserShared.cpp
CustomTableWidget.cpp
NativeMessageInstaller.cpp
PasskeyUtils.cpp)
NativeMessageInstaller.cpp)
if(WITH_XC_BROWSER_PASSKEYS)
list(APPEND browser_SOURCES
BrowserCbor.cpp
BrowserPasskeys.cpp
BrowserPasskeysClient.cpp
BrowserPasskeysConfirmationDialog.cpp
PasskeyUtils.cpp)
endif()
add_library(browser STATIC ${browser_SOURCES})
target_link_libraries(browser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN_LIBRARIES})

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 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
@@ -53,8 +53,8 @@ bool PasskeyUtils::checkCredentialCreationOptions(const QJsonObject& credentialC
{
if (!credentialCreationOptions["attestation"].isString()
|| credentialCreationOptions["attestation"].toString().isEmpty()
|| !credentialCreationOptions["clientDataJSON"].isObject()
|| credentialCreationOptions["clientDataJSON"].toObject().isEmpty()
|| !credentialCreationOptions["clientDataJSON"].isString()
|| credentialCreationOptions["clientDataJSON"].toString().isEmpty()
|| !credentialCreationOptions["rp"].isObject() || credentialCreationOptions["rp"].toObject().isEmpty()
|| !credentialCreationOptions["user"].isObject() || credentialCreationOptions["user"].toObject().isEmpty()
|| !credentialCreationOptions["residentKey"].isBool() || credentialCreationOptions["residentKey"].isUndefined()
@@ -75,7 +75,7 @@ bool PasskeyUtils::checkCredentialCreationOptions(const QJsonObject& credentialC
// Basic check for the object that it contains necessary variables in a correct form
bool PasskeyUtils::checkCredentialAssertionOptions(const QJsonObject& assertionOptions) const
{
if (!assertionOptions["clientDataJson"].isObject() || assertionOptions["clientDataJson"].toObject().isEmpty()
if (!assertionOptions["clientDataJson"].isString() || assertionOptions["clientDataJson"].toString().isEmpty()
|| !assertionOptions["rpId"].isString() || assertionOptions["rpId"].toString().isEmpty()
|| !assertionOptions["userPresence"].isBool() || assertionOptions["userPresence"].isUndefined()
|| !assertionOptions["userVerification"].isBool() || assertionOptions["userVerification"].isUndefined()) {
@@ -352,15 +352,11 @@ ExtensionResult PasskeyUtils::buildExtensionData(QJsonObject& extensionObject) c
return {};
}
QJsonObject PasskeyUtils::buildClientDataJson(const QJsonObject& publicKey, const QString& origin, bool get) const
// Serialization order: https://w3c.github.io/webauthn/#clientdatajson-serialization
QString PasskeyUtils::buildClientDataJson(const QJsonObject& publicKey, const QString& origin, bool get) const
{
QJsonObject clientData;
clientData["challenge"] = publicKey["challenge"];
clientData["crossOrigin"] = false;
clientData["origin"] = origin;
clientData["type"] = get ? QString("webauthn.get") : QString("webauthn.create");
return clientData;
return QString("{\"type\":\"%1\",\"challenge\":\"%2\",\"origin\":\"%3\",\"crossOrigin\":false}")
.arg((get ? QString("webauthn.get") : QString("webauthn.create")), publicKey["challenge"].toString(), origin);
}
QStringList PasskeyUtils::getAllowedCredentialsFromAssertionOptions(const QJsonObject& assertionOptions) const

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 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
@@ -58,7 +58,7 @@ public:
bool isUserVerificationRequired(const QJsonObject& authenticatorSelection) const;
bool isOriginAllowedWithLocalhost(bool allowLocalhostWithPasskeys, const QString& origin) const;
ExtensionResult buildExtensionData(QJsonObject& extensionObject) const;
QJsonObject buildClientDataJson(const QJsonObject& publicKey, const QString& origin, bool get) const;
QString buildClientDataJson(const QJsonObject& publicKey, const QString& origin, bool get) const;
QStringList getAllowedCredentialsFromAssertionOptions(const QJsonObject& assertionOptions) const;
QString getCredentialIdFromEntry(const Entry* entry) const;
QString getUsernameFromEntry(const Entry* entry) const;

View File

@@ -27,7 +27,9 @@ DatabaseCommand::DatabaseCommand()
positionalArguments.append({QString("database"), QObject::tr("Path of the database."), QString("")});
options.append(Command::KeyFileOption);
options.append(Command::NoPasswordOption);
#ifdef WITH_XC_YUBIKEY
options.append(Command::YubiKeyOption);
#endif
}
int DatabaseCommand::execute(const QStringList& arguments)
@@ -53,7 +55,11 @@ int DatabaseCommand::execute(const QStringList& arguments)
db = Utils::unlockDatabase(args.at(0),
!parser->isSet(Command::NoPasswordOption),
parser->value(Command::KeyFileOption),
#ifdef WITH_XC_YUBIKEY
parser->value(Command::YubiKeyOption),
#else
"",
#endif
parser->isSet(Command::QuietOption));
if (!db) {
return EXIT_FAILURE;

View File

@@ -51,8 +51,9 @@ Merge::Merge()
options.append(Merge::KeyFileFromOption);
options.append(Merge::NoPasswordFromOption);
options.append(Merge::DryRunOption);
#ifdef WITH_XC_YUBIKEY
options.append(Merge::YubiKeyFromOption);
#endif
positionalArguments.append({QString("database2"), QObject::tr("Path of the database to merge from."), QString("")});
}

View File

@@ -21,8 +21,10 @@
#include "core/Entry.h"
#include "core/EntryAttributes.h"
#include "core/Global.h"
#include "keys/ChallengeResponseKey.h"
#include "keys/FileKey.h"
#ifdef WITH_XC_YUBIKEY
#include "keys/ChallengeResponseKey.h"
#endif
#ifdef Q_OS_WIN
#include <windows.h>
@@ -168,6 +170,7 @@ namespace Utils
compositeKey->addKey(fileKey);
}
#ifdef WITH_XC_YUBIKEY
if (!yubiKeySlot.isEmpty()) {
unsigned int serial = 0;
int slot;
@@ -198,14 +201,18 @@ namespace Utils
YubiKey::instance()->findValidKeys();
}
#else
Q_UNUSED(yubiKeySlot);
#endif // WITH_XC_YUBIKEY
auto db = QSharedPointer<Database>::create();
QString error;
if (!db->open(databaseFilename, compositeKey, &error)) {
if (db->open(databaseFilename, compositeKey, &error)) {
return db;
} else {
err << error << Qt::endl;
return {};
}
return db;
}
/**

View File

@@ -12,36 +12,34 @@
#define KEEPASSX_PLUGIN_DIR "@PLUGIN_INSTALL_DIR@"
#define KEEPASSX_DATA_DIR "@DATA_INSTALL_DIR@"
/* Build Scope */
#cmakedefine WITH_X11
#cmakedefine WITH_BOTAN3
#cmakedefine WITH_XC_AUTOTYPE
#cmakedefine WITH_XC_NETWORKING
#cmakedefine WITH_XC_BROWSER
#cmakedefine WITH_XC_BROWSER_PASSKEYS
#cmakedefine WITH_XC_YUBIKEY
#cmakedefine WITH_XC_SSHAGENT
#cmakedefine WITH_XC_KEESHARE
#cmakedefine WITH_XC_UPDATECHECK
#cmakedefine WITH_XC_FDOSECRETS
#cmakedefine WITH_XC_DOCS
#cmakedefine WITH_XC_X11
#cmakedefine WITH_XC_BOTAN3
/* Advanced Features */
#cmakedefine KPXC_FEATURE_BROWSER
#cmakedefine KPXC_FEATURE_SSHAGENT
#cmakedefine KPXC_FEATURE_FDOSECRETS
/* Minor Features */
#cmakedefine KPXC_FEATURE_NETWORK
#cmakedefine KPXC_FEATURE_UPDATES
#cmakedefine KPXC_FEATURE_DOCS
/* Distribution */
#cmakedefine KEEPASSXC_BUILD_TYPE "@KEEPASSXC_BUILD_TYPE@"
#cmakedefine KEEPASSXC_BUILD_TYPE_RELEASE
#cmakedefine KEEPASSXC_BUILD_TYPE_PRE_RELEASE
#cmakedefine KEEPASSXC_BUILD_TYPE_SNAPSHOT
#cmakedefine KEEPASSXC_DIST
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
#cmakedefine KEEPASSXC_DIST_SNAP
#cmakedefine KEEPASSXC_DIST_APPIMAGE
#cmakedefine KEEPASSXC_DIST_FLATPAK
/* Security Test Results */
#cmakedefine HAVE_PR_SET_DUMPABLE 1
#cmakedefine HAVE_RLIMIT_CORE 1
#cmakedefine HAVE_PT_DENY_ATTACH 1
/* macOS Feature Support */
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_BIOMETRY()
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_TOUCH_ID()
#cmakedefine01 XC_APPLE_COMPILER_SUPPORT_WATCH()

View File

@@ -208,7 +208,7 @@ namespace Bootstrap
goto Cleanup;
}
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
// OpenSSH for Windows ssh-agent service is running as LocalSystem
if (!AddAccessAllowedAce(pACL,
ACL_REVISION,

View File

@@ -221,6 +221,13 @@ bool EntrySearcher::searchEntryImpl(const Entry* entry)
}
found = false;
break;
case Field::Has:
if (term.word.compare("totp", Qt::CaseInsensitive) == 0) {
found = entry->hasTotp();
break;
}
found = false;
break;
case Field::Uuid:
found = term.regex.match(entry->uuidToHex()).hasMatch();
break;
@@ -260,6 +267,7 @@ void EntrySearcher::parseSearchTerms(const QString& searchString)
{QStringLiteral("group"), Field::Group},
{QStringLiteral("tag"), Field::Tag},
{QStringLiteral("is"), Field::Is},
{QStringLiteral("has"), Field::Has},
{QStringLiteral("uuid"), Field::Uuid}};
// Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string

View File

@@ -41,6 +41,7 @@ public:
Group,
Tag,
Is,
Has,
Uuid
};

View File

@@ -24,7 +24,7 @@
#include <QTextStream>
#if defined(Q_OS_WIN)
#if defined(KPXC_BUILDING_CORE)
#if defined(KEEPASSX_BUILDING_CORE)
#define KEEPASSXC_EXPORT Q_DECL_IMPORT
#else
#define KEEPASSXC_EXPORT Q_DECL_EXPORT

View File

@@ -17,8 +17,14 @@
*/
#include "Group.h"
#include "config-keepassx.h"
#include "core/Config.h"
#ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h"
#endif
#include "core/Global.h"
#include "core/Metadata.h"
#include "core/Tools.h"

View File

@@ -51,7 +51,9 @@ namespace Tools
{
QString debugInfo = "KeePassXC - ";
debugInfo.append(QObject::tr("Version %1").arg(KEEPASSXC_VERSION).append("\n"));
#ifndef KEEPASSXC_BUILD_TYPE_RELEASE
debugInfo.append(QObject::tr("Build Type: %1").arg(KEEPASSXC_BUILD_TYPE).append("\n"));
#endif
QString commitHash;
if (!QString(GIT_HEAD).isEmpty()) {
@@ -61,7 +63,9 @@ namespace Tools
debugInfo.append(QObject::tr("Revision: %1").arg(commitHash.left(7)).append("\n"));
}
#ifdef KEEPASSXC_DIST
debugInfo.append(QObject::tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n"));
#endif
// Qt related debugging information.
debugInfo.append("\n");
@@ -82,23 +86,35 @@ namespace Tools
debugInfo.append("\n\n");
QString extensions;
#ifdef WITH_XC_AUTOTYPE
extensions += "\n- " + QObject::tr("Auto-Type");
#endif
#ifdef WITH_XC_BROWSER
extensions += "\n- " + QObject::tr("Browser Integration");
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
extensions += "\n- " + QObject::tr("Passkeys");
#endif
#ifdef WITH_XC_SSHAGENT
extensions += "\n- " + QObject::tr("SSH Agent");
#endif
#ifdef WITH_XC_KEESHARE
extensions += "\n- " + QObject::tr("KeeShare");
extensions += "\n- " + QObject::tr("Hardware Keys");
#endif
#ifdef WITH_XC_YUBIKEY
extensions += "\n- " + QObject::tr("YubiKey");
#endif
#if defined(Q_OS_MACOS) || defined(Q_CC_MSVC)
extensions += "\n- " + QObject::tr("Quick Unlock");
#endif
#ifdef KPXC_FEATURE_BROWSER
extensions += "\n- " + QObject::tr("Browser Integration");
extensions += "\n- " + QObject::tr("Passkeys");
#endif
#ifdef KPXC_FEATURE_SSHAGENT
extensions += "\n- " + QObject::tr("SSH Agent");
#endif
#ifdef KPXC_FEATURE_FDOSECRETS
#ifdef WITH_XC_FDOSECRETS
extensions += "\n- " + QObject::tr("Secret Service Integration");
#endif
if (extensions.isEmpty()) {
extensions = " " + QObject::tr("None");
}
debugInfo.append(QObject::tr("Enabled extensions:").append(extensions).append("\n"));
return debugInfo;
}

View File

@@ -239,7 +239,7 @@ namespace Crypto
{
bool init()
{
#ifdef WITH_BOTAN3
#ifdef WITH_XC_BOTAN3
unsigned int version_major = 3, min_version_minor = 0;
QString versionString = "3.x";
#else

View File

@@ -34,7 +34,7 @@ bool SymmetricCipher::init(Mode mode, Direction direction, const QByteArray& key
try {
auto botanMode = modeToString(mode);
auto botanDirection =
#ifdef WITH_BOTAN3
#ifdef WITH_XC_BOTAN3
(direction == SymmetricCipher::Encrypt ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption);
#else
(direction == SymmetricCipher::Encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);

View File

@@ -1,4 +1,4 @@
if(KPXC_FEATURE_FDOSECRETS)
if(WITH_XC_FDOSECRETS)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
add_library(fdosecrets STATIC

View File

@@ -211,6 +211,78 @@ namespace
return entry.take();
}
/*!
* Create nested folder hierarchy from a path string.
* For example, "Socials/Forums" creates a "Socials" group with a "Forums" child group.
* Returns the deepest (leaf) group in the hierarchy.
*/
Group*
createNestedFolderHierarchy(const QString& folderPath, Group* rootGroup, QMap<QString, Group*>& createdGroups)
{
if (folderPath.isEmpty()) {
return rootGroup;
}
// Check if we've already created this exact path
if (createdGroups.contains(folderPath)) {
return createdGroups.value(folderPath);
}
// Split the path by forward slashes
QStringList pathParts = folderPath.split('/', Qt::SkipEmptyParts);
if (pathParts.isEmpty()) {
return rootGroup;
}
Group* currentParent = rootGroup;
QString currentPath;
// Create each level of the hierarchy
for (int i = 0; i < pathParts.size(); ++i) {
const QString& partName = pathParts[i];
// Build the current path (e.g., "Socials", then "Socials/Forums")
if (currentPath.isEmpty()) {
currentPath = partName;
} else {
currentPath += "/" + partName;
}
// Check if this level already exists
Group* existingGroup = createdGroups.value(currentPath);
if (existingGroup) {
currentParent = existingGroup;
continue;
}
// Find existing child group with this name
existingGroup = nullptr;
for (Group* child : currentParent->children()) {
if (child->name() == partName) {
existingGroup = child;
break;
}
}
if (existingGroup) {
// Use existing group
createdGroups.insert(currentPath, existingGroup);
currentParent = existingGroup;
} else {
// Create new group
auto newGroup = new Group();
newGroup->setUuid(QUuid::createUuid());
newGroup->setName(partName);
newGroup->setParent(currentParent);
createdGroups.insert(currentPath, newGroup);
currentParent = newGroup;
}
}
return currentParent;
}
void writeVaultToDatabase(const QJsonObject& vault, QSharedPointer<Database> db)
{
auto folderField = QString("folders");
@@ -224,15 +296,19 @@ namespace
return;
}
// Create groups from folders and store a temporary map of id -> uuid
// Create groups from folders and store a temporary map of id -> group
QMap<QString, Group*> folderMap;
for (const auto& folder : vault.value(folderField).toArray()) {
auto group = new Group();
group->setUuid(QUuid::createUuid());
group->setName(folder.toObject().value("name").toString());
group->setParent(db->rootGroup());
QMap<QString, Group*> createdGroups; // Track created groups by path to avoid duplicates
folderMap.insert(folder.toObject().value("id").toString(), group);
for (const auto& folder : vault.value(folderField).toArray()) {
const QString folderName = folder.toObject().value("name").toString();
const QString folderId = folder.toObject().value("id").toString();
// Create the nested folder hierarchy
Group* targetGroup = createNestedFolderHierarchy(folderName, db->rootGroup(), createdGroups);
// Map the folder ID to the target group
folderMap.insert(folderId, targetGroup);
}
QString folderId;

View File

@@ -225,6 +225,7 @@ KdbxXmlWriter::BinaryIdxMap Kdbx4Writer::writeAttachments(QIODevice* device, Dat
data.append(entry->attachments()->value(key));
CryptoHash hash(CryptoHash::Sha256);
#ifdef WITH_XC_KEESHARE
// Namespace KeeShare attachments so they don't get deduplicated together with attachments
// from other databases. Prevents potential filesize side channels.
auto group = entry->group();
@@ -236,6 +237,7 @@ KdbxXmlWriter::BinaryIdxMap Kdbx4Writer::writeAttachments(QIODevice* device, Dat
} else {
hash.addData(db->uuid().toByteArray());
}
#endif
hash.addData(data);
// Deduplicate attachments with the same hash

View File

@@ -109,6 +109,7 @@ void KdbxXmlWriter::fillBinaryIdxMap()
for (const QString& key : attachmentKeys) {
QByteArray data = entry->attachments()->value(key);
CryptoHash hash(CryptoHash::Sha256);
#ifdef WITH_XC_KEESHARE
// Namespace KeeShare attachments so they don't get deduplicated together with attachments
// from other databases. Prevents potential filesize side channels.
auto group = entry->group();
@@ -120,6 +121,7 @@ void KdbxXmlWriter::fillBinaryIdxMap()
} else {
hash.addData(m_db->uuid().toByteArray());
}
#endif
hash.addData(data);
const auto hashResult = hash.result();

View File

@@ -34,7 +34,7 @@
#include "FileDialog.h"
#include "MessageBox.h"
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
#include "browser/BrowserSettingsPage.h"
#endif
@@ -100,7 +100,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->setupUi(m_generalWidget);
addPage(tr("General"), icons()->icon("preferences-other"), m_generalWidget);
addPage(tr("Security"), icons()->icon("security-high"), m_secWidget);
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
addSettingsPage(new BrowserSettingsPage());
#endif
@@ -172,7 +172,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->trayIconAppearance->installEventFilter(mouseWheelFilter);
m_generalUi->fontSizeComboBox->installEventFilter(mouseWheelFilter);
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
connect(m_generalUi->checkForUpdatesOnStartupCheckBox, SIGNAL(toggled(bool)), SLOT(checkUpdatesToggled(bool)));
#else
m_generalUi->checkForUpdatesOnStartupCheckBox->setVisible(false);
@@ -180,7 +180,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->checkUpdatesSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
#endif
#ifndef KPXC_FEATURE_NETWORK
#ifndef WITH_XC_NETWORKING
m_secUi->privacy->setVisible(false);
m_generalUi->faviconTimeoutLabel->setVisible(false);
m_generalUi->faviconTimeoutSpinBox->setVisible(false);

View File

@@ -25,7 +25,9 @@
#include "gui/MessageBox.h"
#include "keys/ChallengeResponseKey.h"
#include "keys/FileKey.h"
#ifdef WITH_XC_YUBIKEY
#include "keys/drivers/YubiKeyInterfaceUSB.h"
#endif
#include "quickunlock/QuickUnlockInterface.h"
#include <QCheckBox>
@@ -50,7 +52,9 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
: DialogyWidget(parent)
, m_ui(new Ui::DatabaseOpenWidget())
, m_db(nullptr)
#ifdef WITH_XC_YUBIKEY
, m_deviceListener(new DeviceListener(this))
#endif
{
m_ui->setupUi(this);
@@ -97,6 +101,7 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
sp.setRetainSizeWhenHidden(true);
m_ui->hardwareKeyProgress->setSizePolicy(sp);
#ifdef WITH_XC_YUBIKEY
connect(m_deviceListener, &DeviceListener::devicePlugged, this, [this] { pollHardwareKey(false, 500); });
connect(YubiKey::instance(), SIGNAL(detectComplete(bool)), SLOT(hardwareKeyResponse(bool)), Qt::QueuedConnection);
@@ -117,6 +122,10 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
connect(&m_hideNoHardwareKeysFoundTimer, &QTimer::timeout, this, [this] {
m_ui->noHardwareKeysFoundLabel->setVisible(false);
});
#else
m_ui->noHardwareKeysFoundLabel->setVisible(false);
m_ui->refreshHardwareKeys->setVisible(false);
#endif
// QuickUnlock actions
connect(m_ui->quickUnlockButton, &QPushButton::pressed, this, [this] { openDatabase(); });
@@ -186,6 +195,7 @@ bool DatabaseOpenWidget::event(QEvent* event)
toggleQuickUnlockScreen();
if (type == QEvent::Show) {
#ifdef WITH_XC_YUBIKEY
#ifdef Q_OS_WIN
m_deviceListener->registerHotplugCallback(true,
true,
@@ -200,6 +210,7 @@ bool DatabaseOpenWidget::event(QEvent* event)
#else
m_deviceListener->registerHotplugCallback(true, true, YubiKeyInterfaceUSB::YUBICO_USB_VID);
m_deviceListener->registerHotplugCallback(true, true, YubiKeyInterfaceUSB::ONLYKEY_USB_VID);
#endif
#endif
}
@@ -215,9 +226,11 @@ bool DatabaseOpenWidget::event(QEvent* event)
m_hideTimer.start();
}
#ifdef WITH_XC_YUBIKEY
if (type == QEvent::Hide) {
m_deviceListener->deregisterAllHotplugCallbacks();
}
#endif
ret = true;
}
@@ -282,8 +295,10 @@ void DatabaseOpenWidget::load(const QString& filename)
toggleQuickUnlockScreen();
#ifdef WITH_XC_YUBIKEY
// Do initial auto-poll
pollHardwareKey();
#endif
}
void DatabaseOpenWidget::clearForms()
@@ -474,6 +489,7 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::buildDatabaseKey()
config()->set(Config::LastKeyFiles, lastKeyFiles);
}
#ifdef WITH_XC_YUBIKEY
auto lastChallengeResponse = config()->get(Config::LastChallengeResponse).toHash();
lastChallengeResponse.remove(m_filename);
@@ -490,6 +506,7 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::buildDatabaseKey()
if (config()->get(Config::RememberLastKeyFiles).toBool()) {
config()->set(Config::LastChallengeResponse, lastChallengeResponse);
}
#endif
return databaseKey;
}

View File

@@ -23,9 +23,12 @@
#include <QScopedPointer>
#include <QTimer>
#include "config-keepassx.h"
#include "gui/DialogyWidget.h"
#include "gui/MessageWidget.h"
#ifdef WITH_XC_YUBIKEY
#include "osutils/DeviceListener.h"
#endif
class CompositeKey;
class Database;
@@ -84,7 +87,9 @@ private slots:
void hardwareKeyResponse(bool found);
private:
#ifdef WITH_XC_YUBIKEY
QPointer<DeviceListener> m_deviceListener;
#endif
bool m_pollingHardwareKey = false;
bool m_manualHardwareKeyRefresh = false;
bool m_blockQuickUnlock = false;

View File

@@ -576,6 +576,7 @@ void DatabaseTabWidget::showDatabaseSecurity()
currentDatabaseWidget()->switchToDatabaseSecurity();
}
#ifdef WITH_XC_BROWSER_PASSKEYS
void DatabaseTabWidget::showPasskeys()
{
currentDatabaseWidget()->switchToPasskeys();
@@ -595,6 +596,7 @@ void DatabaseTabWidget::removePasskeyFromEntry()
{
currentDatabaseWidget()->removePasskeyFromEntry();
}
#endif
bool DatabaseTabWidget::isModified(int index) const
{

View File

@@ -86,10 +86,12 @@ public slots:
void showDatabaseReports(bool state);
void showDatabaseSettings(bool state);
void showDatabaseSecurity();
#ifdef WITH_XC_BROWSER_PASSKEYS
void showPasskeys();
void importPasskey();
void importPasskeyToEntry();
void removePasskeyFromEntry();
#endif
void performGlobalAutoType(const QString& search);
void performBrowserUnlock();

View File

@@ -59,15 +59,15 @@
#include "remote/RemoteHandler.h"
#include "remote/RemoteSettings.h"
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
#include "gui/IconDownloaderDialog.h"
#endif
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
#include "sshagent/SSHAgent.h"
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
#include "gui/passkeys/PasskeyImporter.h"
#endif
@@ -140,7 +140,9 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
auto rightHandSideVBox = new QVBoxLayout();
rightHandSideVBox->setMargin(0);
rightHandSideVBox->addWidget(m_searchingLabel);
#ifdef WITH_XC_KEESHARE
rightHandSideVBox->addWidget(m_shareLabel);
#endif
rightHandSideVBox->addWidget(m_previewSplitter);
rightHandSideWidget->setLayout(rightHandSideVBox);
m_entryView = new EntryView(rightHandSideWidget);
@@ -170,10 +172,12 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
m_searchingLabel->setAlignment(Qt::AlignCenter);
m_searchingLabel->setVisible(false);
#ifdef WITH_XC_KEESHARE
m_shareLabel->setObjectName("KeeShareBanner");
m_shareLabel->setRawText(tr("Shared group…"));
m_shareLabel->setAlignment(Qt::AlignCenter);
m_shareLabel->setVisible(false);
#endif
m_previewView->setObjectName("previewWidget");
m_previewView->hide();
@@ -234,9 +238,11 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
m_searchLimitGroup = config()->get(Config::SearchLimitGroup).toBool();
#ifdef WITH_XC_KEESHARE
// We need to reregister the database to allow exports
// from a newly created database
KeeShare::instance()->connectDatabase(m_db, {});
#endif
if (m_db->isInitialized()) {
switchToMainView();
@@ -501,7 +507,12 @@ void DatabaseWidget::replaceDatabase(QSharedPointer<Database> db)
emit databaseReplaced(oldDb, m_db);
#if defined(WITH_XC_KEESHARE)
KeeShare::instance()->connectDatabase(m_db, oldDb);
#else
// Keep the instance active till the end of this function
Q_UNUSED(oldDb);
#endif
oldDb->releaseData();
}
@@ -812,7 +823,7 @@ void DatabaseWidget::setClipboardTextAndMinimize(const QString& text)
}
}
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
void DatabaseWidget::addToAgent()
{
Entry* currentEntry = m_entryView->currentEntry();
@@ -917,7 +928,7 @@ void DatabaseWidget::openUrl()
void DatabaseWidget::downloadSelectedFavicons()
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
QList<Entry*> selectedEntries;
for (const auto& index : m_entryView->selectionModel()->selectedRows()) {
selectedEntries.append(m_entryView->entryFromIndex(index));
@@ -930,7 +941,7 @@ void DatabaseWidget::downloadSelectedFavicons()
void DatabaseWidget::downloadAllFavicons()
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
auto currentGroup = m_groupView->currentGroup();
if (currentGroup) {
performIconDownloads(currentGroup->entries());
@@ -940,7 +951,7 @@ void DatabaseWidget::downloadAllFavicons()
void DatabaseWidget::downloadFaviconInBackground(Entry* entry)
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
performIconDownloads({entry}, true, true);
#else
Q_UNUSED(entry);
@@ -949,7 +960,7 @@ void DatabaseWidget::downloadFaviconInBackground(Entry* entry)
void DatabaseWidget::performIconDownloads(const QList<Entry*>& entries, bool force, bool downloadInBackground)
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
auto* iconDownloaderDialog = new IconDownloaderDialog(this);
connect(this, SIGNAL(databaseLockRequested()), iconDownloaderDialog, SLOT(close()));
@@ -1325,7 +1336,7 @@ void DatabaseWidget::loadDatabase(bool accepted)
m_entryBeforeLock = QUuid();
m_saveAttempts = 0;
emit databaseUnlocked();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
sshAgent()->databaseUnlocked(m_db);
#endif
if (config()->get(Config::MinimizeAfterUnlock).toBool()) {
@@ -1476,7 +1487,7 @@ void DatabaseWidget::unlockDatabase(bool accepted)
processAutoOpen();
emit databaseUnlocked();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
sshAgent()->databaseUnlocked(m_db);
#endif
@@ -1624,7 +1635,7 @@ void DatabaseWidget::switchToRemoteSettings()
m_databaseSettingDialog->showRemoteSettings();
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
void DatabaseWidget::switchToPasskeys()
{
switchToDatabaseReports();
@@ -1721,7 +1732,9 @@ void DatabaseWidget::search(const QString& searchtext)
m_lastSearchText = searchtext;
m_searchingLabel->setVisible(true);
#ifdef WITH_XC_KEESHARE
m_shareLabel->setVisible(false);
#endif
emit searchModeActivated();
}
@@ -1786,6 +1799,7 @@ void DatabaseWidget::onGroupChanged()
m_previewView->setGroup(group);
#ifdef WITH_XC_KEESHARE
auto shareLabel = KeeShare::sharingLabel(group);
if (!shareLabel.isEmpty()) {
m_shareLabel->setRawText(shareLabel);
@@ -1793,6 +1807,7 @@ void DatabaseWidget::onGroupChanged()
} else {
m_shareLabel->setVisible(false);
}
#endif
emit groupChanged();
}
@@ -2076,7 +2091,7 @@ bool DatabaseWidget::lock()
m_entryBeforeLock = currentEntry->uuid();
}
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
sshAgent()->databaseLocked(m_db);
#endif
@@ -2374,7 +2389,7 @@ bool DatabaseWidget::currentEntryHasTotp()
return currentEntry->hasTotp();
}
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
bool DatabaseWidget::currentEntryHasSshKey()
{
Entry* currentEntry = m_entryView->currentEntry();
@@ -2387,7 +2402,7 @@ bool DatabaseWidget::currentEntryHasSshKey()
}
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
bool DatabaseWidget::currentEntryHasPasskey()
{
auto currentEntry = m_entryView->currentEntry();

View File

@@ -116,7 +116,7 @@ public:
bool currentEntryHasUrl();
bool currentEntryHasNotes();
bool currentEntryHasTotp();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
bool currentEntryHasSshKey();
#endif
bool currentEntryHasAutoTypeEnabled();
@@ -204,7 +204,7 @@ public slots:
void copyTotp();
void copyPasswordTotp();
void setupTotp();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
void addToAgent();
void removeFromAgent();
#endif
@@ -232,7 +232,7 @@ public slots:
void switchToDatabaseReports();
void switchToDatabaseSettings();
void switchToRemoteSettings();
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
void switchToPasskeys();
void showImportPasskeyDialog(bool isEntry = false);
void removePasskeyFromEntry();

View File

@@ -28,7 +28,7 @@
#include "gui/IconModels.h"
#include "gui/Icons.h"
#include "gui/MessageBox.h"
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
#include "gui/IconDownloader.h"
#endif
@@ -48,7 +48,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
, m_applyIconTo(ApplyIconToOptions::THIS_ONLY)
, m_defaultIconModel(new DefaultIconModel(this))
, m_customIconModel(new CustomIconModel(this))
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
, m_downloader(new IconDownloader())
#endif
{
@@ -73,14 +73,14 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
this, SIGNAL(widgetUpdated()));
connect(m_ui->customIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SIGNAL(widgetUpdated()));
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
connect(m_downloader.data(),
SIGNAL(finished(const QString&, const QImage&)),
SLOT(iconReceived(const QString&, const QImage&)));
#endif
// clang-format on
#ifndef KPXC_FEATURE_NETWORK
#ifndef WITH_XC_NETWORKING
m_ui->faviconButton->setVisible(false);
m_ui->faviconURL->setVisible(false);
#endif
@@ -188,7 +188,7 @@ void EditWidgetIcons::keyPressEvent(QKeyEvent* event)
void EditWidgetIcons::setUrl(const QString& url)
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
QUrl urlCheck(url);
if (urlCheck.scheme().startsWith("http")) {
m_ui->faviconURL->setText(urlCheck.url(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment));
@@ -203,7 +203,7 @@ void EditWidgetIcons::setUrl(const QString& url)
void EditWidgetIcons::downloadFavicon()
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
auto url = m_ui->faviconURL->text();
if (!url.isEmpty()) {
m_downloader->setUrl(url);
@@ -214,7 +214,7 @@ void EditWidgetIcons::downloadFavicon()
void EditWidgetIcons::iconReceived(const QString& url, const QImage& icon)
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
Q_UNUSED(url);
if (icon.isNull()) {
QString message(tr("Unable to fetch favicon."));
@@ -237,7 +237,7 @@ void EditWidgetIcons::iconReceived(const QString& url, const QImage& icon)
void EditWidgetIcons::abortRequests()
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
if (m_downloader) {
m_downloader->abortDownload();
}

View File

@@ -28,7 +28,7 @@
class Database;
class DefaultIconModel;
class CustomIconModel;
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
class IconDownloader;
#endif
@@ -104,7 +104,7 @@ private:
ApplyIconToOptions m_applyIconTo;
DefaultIconModel* const m_defaultIconModel;
CustomIconModel* const m_customIconModel;
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
QSharedPointer<IconDownloader> m_downloader;
#endif

View File

@@ -25,8 +25,10 @@
#include "core/Totp.h"
#include "gui/Font.h"
#include "gui/Icons.h"
#if defined(WITH_XC_KEESHARE)
#include "keeshare/KeeShare.h"
#include "keeshare/KeeShareSettings.h"
#endif
#include <QScrollBar>
#include <QTabWidget>
@@ -103,6 +105,10 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
connect(m_ui->groupTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
setFocusProxy(m_ui->entryTabWidget);
#if !defined(WITH_XC_KEESHARE)
removeTab(m_ui->groupTabWidget, m_ui->groupShareTab);
#endif
}
EntryPreviewWidget::~EntryPreviewWidget() = default;
@@ -200,7 +206,10 @@ void EntryPreviewWidget::refresh()
} else if (m_currentGroup) {
updateGroupHeaderLine();
updateGroupGeneralTab();
#if defined(WITH_XC_KEESHARE)
updateGroupSharingTab();
#endif
setVisible(!config()->get(Config::GUI_HidePreviewPanel).toBool());
@@ -523,6 +532,7 @@ void EntryPreviewWidget::updateGroupGeneralTab()
}
}
#if defined(WITH_XC_KEESHARE)
void EntryPreviewWidget::updateGroupSharingTab()
{
Q_ASSERT(m_currentGroup);
@@ -531,6 +541,7 @@ void EntryPreviewWidget::updateGroupSharingTab()
m_ui->groupShareTypeLabel->setText(KeeShare::referenceTypeLabel(reference));
m_ui->groupSharePathLabel->setText(reference.path);
}
#endif
void EntryPreviewWidget::updateTotpLabel()
{

View File

@@ -65,7 +65,9 @@ private slots:
void updateGroupHeaderLine();
void updateGroupGeneralTab();
#if defined(WITH_XC_KEESHARE)
void updateGroupSharingTab();
#endif
void updateTotpLabel();
void updateTabIndexes();

View File

@@ -24,12 +24,16 @@
#include <QPaintDevice>
#include <QPainter>
#include "config-keepassx.h"
#include "core/Config.h"
#include "core/Database.h"
#include "gui/DatabaseIcons.h"
#include "gui/MainWindow.h"
#include "gui/osutils/OSUtils.h"
#ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h"
#endif
class AdaptiveIconEngine : public QIconEngine
{
@@ -259,9 +263,12 @@ QPixmap Icons::groupIconPixmap(const Group* group, IconSize size)
if (group->isExpired()) {
icon = databaseIcons()->applyBadge(icon, DatabaseIcons::Badges::Expired);
} else if (KeeShare::isShared(group)) {
}
#ifdef WITH_XC_KEESHARE
else if (KeeShare::isShared(group)) {
icon = KeeShare::indicatorBadge(group, icon);
}
#endif
return icon;
}

View File

@@ -47,25 +47,30 @@
#include "gui/entry/EntryView.h"
#include "gui/osutils/OSUtils.h"
#include "gui/remote/RemoteSettings.h"
#include "keeshare/KeeShare.h"
#include "keeshare/SettingsPageKeeShare.h"
#include "keys/drivers/YubiKey.h"
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
#include "gui/UpdateCheckDialog.h"
#include "networking/UpdateChecker.h"
#endif
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
#include "sshagent/AgentSettingsPage.h"
#include "sshagent/SSHAgent.h"
#endif
#ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h"
#include "keeshare/SettingsPageKeeShare.h"
#endif
#ifdef KPXC_FEATURE_FDOSECRETS
#ifdef WITH_XC_FDOSECRETS
#include "fdosecrets/FdoSecretsPlugin.h"
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_YUBIKEY
#include "keys/drivers/YubiKey.h"
#endif
#ifdef WITH_XC_BROWSER
#include "browser/BrowserService.h"
#endif
@@ -133,7 +138,7 @@ MainWindow::MainWindow()
m_entryContextMenu->addSeparator();
m_entryContextMenu->addAction(m_ui->actionEntryAutoType);
m_entryContextMenu->addSeparator();
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
m_entryContextMenu->addAction(m_ui->actionEntryImportPasskey);
m_entryContextMenu->addAction(m_ui->actionEntryRemovePasskey);
m_entryContextMenu->addSeparator();
@@ -198,12 +203,12 @@ MainWindow::MainWindow()
m_ui->settingsWidget->addSettingsPage(new ShortcutSettingsPage());
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
connect(
browserService(), &BrowserService::requestUnlock, m_ui->tabWidget, &DatabaseTabWidget::performBrowserUnlock);
#endif
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
connect(sshAgent(), SIGNAL(enabledChanged(bool)), this, SLOT(agentEnabled(bool)));
connect(m_ui->actionClearSSHAgent, SIGNAL(triggered()), SLOT(clearSSHAgent()));
@@ -212,13 +217,15 @@ MainWindow::MainWindow()
agentEnabled(false);
#endif
#if defined(WITH_XC_KEESHARE)
KeeShare::init(this);
m_ui->settingsWidget->addSettingsPage(new SettingsPageKeeShare(m_ui->tabWidget));
connect(KeeShare::instance(),
SIGNAL(sharingMessage(QString, MessageWidget::MessageType)),
SLOT(displayGlobalMessage(QString, MessageWidget::MessageType)));
#endif
#ifdef KPXC_FEATURE_FDOSECRETS
#ifdef WITH_XC_FDOSECRETS
auto fdoSS = new FdoSecretsPlugin(m_ui->tabWidget);
connect(fdoSS, &FdoSecretsPlugin::error, this, &MainWindow::showErrorMessage);
connect(fdoSS, &FdoSecretsPlugin::requestSwitchToDatabases, this, &MainWindow::switchToDatabases);
@@ -227,8 +234,10 @@ MainWindow::MainWindow()
m_ui->settingsWidget->addSettingsPage(fdoSS);
#endif
#ifdef WITH_XC_YUBIKEY
connect(YubiKey::instance(), SIGNAL(userInteractionRequest()), SLOT(showYubiKeyPopup()), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(challengeCompleted()), SLOT(hideYubiKeyPopup()), Qt::QueuedConnection);
#endif
setWindowIcon(icons()->applicationIcon());
m_ui->globalMessageWidget->hideMessage();
@@ -419,7 +428,7 @@ MainWindow::MainWindow()
m_ui->actionKeyboardShortcuts->setIcon(icons()->icon("keyboard-shortcuts"));
m_ui->actionCheckForUpdates->setIcon(icons()->icon("system-software-update"));
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionPasskeys->setIcon(icons()->icon("passkey"));
m_ui->actionImportPasskey->setIcon(icons()->icon("document-import"));
m_ui->actionEntryImportPasskey->setIcon(icons()->icon("document-import"));
@@ -470,7 +479,7 @@ MainWindow::MainWindow()
connect(m_ui->actionDatabaseSettings, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(showDatabaseSettings(bool)));
connect(m_ui->actionDatabaseSecurity, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showDatabaseSecurity()));
connect(m_ui->actionReports, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(showDatabaseReports(bool)));
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
connect(m_ui->actionPasskeys, SIGNAL(triggered()), m_ui->tabWidget, SLOT(showPasskeys()));
connect(m_ui->actionImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskey()));
connect(m_ui->actionEntryImportPasskey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importPasskeyToEntry()));
@@ -519,7 +528,7 @@ MainWindow::MainWindow()
m_actionMultiplexer.connect(m_ui->actionEntryAutoTypeTOTP, SIGNAL(triggered()), SLOT(performAutoTypeTOTP()));
m_actionMultiplexer.connect(m_ui->actionEntryOpenUrl, SIGNAL(triggered()), SLOT(openUrl()));
m_actionMultiplexer.connect(m_ui->actionEntryDownloadIcon, SIGNAL(triggered()), SLOT(downloadSelectedFavicons()));
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
m_actionMultiplexer.connect(m_ui->actionEntryAddToAgent, SIGNAL(triggered()), SLOT(addToAgent()));
m_actionMultiplexer.connect(m_ui->actionEntryRemoveFromAgent, SIGNAL(triggered()), SLOT(removeFromAgent()));
#endif
@@ -567,7 +576,7 @@ MainWindow::MainWindow()
setUnifiedTitleAndToolBarOnMac(true);
#endif
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
connect(m_ui->actionCheckForUpdates, SIGNAL(triggered()), SLOT(showUpdateCheckDialog()));
connect(UpdateChecker::instance(),
SIGNAL(updateCheckFinished(bool, QString, bool)),
@@ -581,11 +590,11 @@ MainWindow::MainWindow()
m_ui->actionCheckForUpdates->setVisible(false);
#endif
#ifndef KPXC_FEATURE_NETWORK
#ifndef WITH_XC_NETWORKING
m_ui->actionGroupDownloadFavicons->setVisible(false);
m_ui->actionEntryDownloadIcon->setVisible(false);
#endif
#ifndef KPXC_FEATURE_DOCS
#ifndef WITH_XC_DOCS
m_ui->actionGettingStarted->setVisible(false);
m_ui->actionUserGuide->setVisible(false);
m_ui->actionKeyboardShortcuts->setVisible(false);
@@ -617,27 +626,36 @@ MainWindow::MainWindow()
// Properly shutdown on logoff, restart, and shutdown
connect(qApp, &QGuiApplication::commitDataRequest, this, [this] { m_appExitCalled = true; });
#ifdef KEEPASSXC_BUILD_TYPE_SNAPSHOT
#if defined(KEEPASSXC_BUILD_TYPE_SNAPSHOT) || defined(KEEPASSXC_BUILD_TYPE_PRE_RELEASE)
auto* hidePreRelWarn = new QAction(tr("Don't show again for this version"), m_ui->globalMessageWidget);
m_ui->globalMessageWidget->addAction(hidePreRelWarn);
auto hidePreRelWarnConn = QSharedPointer<QMetaObject::Connection>::create();
*hidePreRelWarnConn = connect(m_ui->globalMessageWidget, &MessageWidget::hideAnimationStarted, [=] {
*hidePreRelWarnConn = connect(m_ui->globalMessageWidget, &KMessageWidget::hideAnimationFinished, [=] {
m_ui->globalMessageWidget->removeAction(hidePreRelWarn);
disconnect(*hidePreRelWarnConn);
hidePreRelWarn->deleteLater();
});
connect(hidePreRelWarn, &QAction::triggered, [=] {
m_ui->globalMessageWidget->hideMessage();
m_ui->globalMessageWidget->animatedHide();
config()->set(Config::Messages_HidePreReleaseWarning, KEEPASSXC_VERSION);
});
#endif
#if defined(KEEPASSXC_BUILD_TYPE_SNAPSHOT)
if (config()->get(Config::Messages_HidePreReleaseWarning) != KEEPASSXC_VERSION) {
m_ui->globalMessageWidget->showMessage(tr("WARNING: You are using a development snapshot build of KeePassXC.\n"
"Maintain a backup of your databases in the event of unknown bugs.\n"
"This version is not meant for production use."),
MessageWidget::Warning,
-1);
m_ui->globalMessageWidget->showMessage(
tr("WARNING: You are using an unstable build of KeePassXC.\n"
"There is a high risk of corruption, maintain a backup of your databases.\n"
"This version is not meant for production use."),
MessageWidget::Warning,
-1);
}
#elif defined(KEEPASSXC_BUILD_TYPE_PRE_RELEASE)
if (config()->get(Config::Messages_HidePreReleaseWarning) != KEEPASSXC_VERSION) {
m_ui->globalMessageWidget->showMessage(
tr("NOTE: You are using a pre-release version of KeePassXC.\n"
"Expect some bugs and minor issues, this version is meant for testing purposes."),
MessageWidget::Information,
-1);
}
#endif
@@ -673,7 +691,7 @@ MainWindow::MainWindow()
MainWindow::~MainWindow()
{
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
sshAgent()->removeAllIdentities();
#endif
}
@@ -725,7 +743,11 @@ void MainWindow::appExit()
*/
bool MainWindow::isHardwareKeySupported()
{
#ifdef WITH_XC_YUBIKEY
return true;
#else
return false;
#endif
}
/**
@@ -737,6 +759,7 @@ bool MainWindow::isHardwareKeySupported()
*/
bool MainWindow::refreshHardwareKeys()
{
#ifdef WITH_XC_YUBIKEY
auto yk = YubiKey::instance();
// find keys sync to allow returning if any key was found
bool found = yk->findValidKeys();
@@ -744,6 +767,9 @@ bool MainWindow::refreshHardwareKeys()
// emit here manually because sync findValidKeys() cannot do that properly
emit yk->detectComplete(found);
return found;
#else
return false;
#endif
}
void MainWindow::updateLastDatabasesMenu()
@@ -958,13 +984,13 @@ void MainWindow::updateMenuActionState()
m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
m_ui->actionEntryDownloadIcon->setEnabled((multiEntrySelected && !singleEntrySelected)
|| (singleEntrySelected && dbWidget->currentEntryHasUrl()));
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionEntryImportPasskey->setVisible(singleEntrySelected);
m_ui->actionEntryImportPasskey->setEnabled(singleEntrySelected);
m_ui->actionEntryRemovePasskey->setVisible(singleEntrySelected && dbWidget->currentEntryHasPasskey());
m_ui->actionEntryRemovePasskey->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPasskey());
#endif
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
bool hasSSHKey = singleEntrySelected && sshAgent()->isEnabled() && dbWidget->currentEntryHasSshKey();
m_ui->actionEntryAddToAgent->setVisible(hasSSHKey);
m_ui->actionEntryAddToAgent->setEnabled(hasSSHKey);
@@ -984,7 +1010,7 @@ void MainWindow::updateMenuActionState()
m_ui->actionGroupSortDesc->setEnabled(groupHasChildren);
m_ui->actionGroupEmptyRecycleBin->setVisible(inRecycleBin);
m_ui->actionGroupEmptyRecycleBin->setEnabled(inRecycleBin);
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
m_ui->actionGroupDownloadFavicons->setVisible(!inRecycleBin);
#endif
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && groupHasEntries && !inRecycleBin);
@@ -1003,7 +1029,7 @@ void MainWindow::updateMenuActionState()
m_ui->menuRemoteSync->setEnabled(inDatabase || inDatabaseSettings);
m_ui->menuExport->setEnabled(inDatabase);
m_ui->actionDatabaseMerge->setEnabled(inDatabase);
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->actionPasskeys->setEnabled(inDatabase || inReports);
m_ui->actionImportPasskey->setEnabled(inDatabase);
#endif
@@ -1078,7 +1104,7 @@ void MainWindow::showAboutDialog()
void MainWindow::performUpdateCheck()
{
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
if (!config()->get(Config::UpdateCheckMessageShown).toBool()) {
auto result =
MessageBox::question(this,
@@ -1101,7 +1127,7 @@ void MainWindow::performUpdateCheck()
void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested)
{
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
if (hasUpdate && !isManuallyRequested) {
auto* updateCheckDialog = new UpdateCheckDialog(this);
updateCheckDialog->showUpdateCheckResponse(hasUpdate, version);
@@ -1116,7 +1142,7 @@ void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool
void MainWindow::showUpdateCheckDialog()
{
#ifdef KPXC_FEATURE_UPDATES
#ifdef WITH_XC_UPDATECHECK
updateCheck()->checkForUpdates(true);
auto* updateCheckDialog = new UpdateCheckDialog(this);
updateCheckDialog->show();

View File

@@ -231,7 +231,7 @@ bool PasswordWidget::eventFilter(QObject* watched, QEvent* event)
if (isVisible() && (type == QEvent::KeyPress || type == QEvent::KeyRelease || type == QEvent::FocusIn)) {
checkCapslockState();
}
if (type == QEvent::FocusIn || type == QEvent::FocusOut) {
if (type == QEvent::FocusIn || type == QEvent::FocusOut || type == QEvent::Hide) {
osUtils->setUserInputProtection(type == QEvent::FocusIn);
}
}

View File

@@ -16,7 +16,7 @@
*/
#include "UrlTools.h"
#if defined(KPXC_FEATURE_NETWORK) || defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
#include <QHostAddress>
#include <QNetworkCookie>
#include <QNetworkCookieJar>
@@ -42,7 +42,7 @@ QUrl UrlTools::convertVariantToUrl(const QVariant& var) const
return url;
}
#if defined(KPXC_FEATURE_NETWORK) || defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
QUrl UrlTools::getRedirectTarget(QNetworkReply* reply) const
{
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
@@ -171,8 +171,8 @@ bool UrlTools::isUrlValid(const QString& urlField, bool looseComparison) const
url.remove(0, 1);
url.remove(url.length() - 1, 1);
} else {
// Do not allow URL with just wildcards, or double wildcards, or no separator (.)
if (url.length() == url.count("*") || url.contains("**") || url.contains("*.*") || !url.contains(".")) {
// Do not allow URL with just wildcards, or double wildcards
if (url.length() == url.count("*") || url.contains("**") || url.contains("*.*")) {
return false;
}

View File

@@ -22,7 +22,7 @@
#include <QObject>
#include <QUrl>
#include <QVariant>
#if defined(KPXC_FEATURE_NETWORK) || defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
#include <QNetworkReply>
#endif
@@ -34,7 +34,7 @@ public:
explicit UrlTools() = default;
static UrlTools* instance();
#if defined(KPXC_FEATURE_NETWORK) || defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
QUrl getRedirectTarget(QNetworkReply* reply) const;
QString getBaseDomainFromUrl(const QString& url) const;
QString getTopLevelDomainFromUrl(const QString& url) const;

View File

@@ -24,16 +24,22 @@
#include "gui/Icons.h"
#include "keys/ChallengeResponseKey.h"
#include "keys/CompositeKey.h"
#ifdef WITH_XC_YUBIKEY
#include "keys/drivers/YubiKeyInterfaceUSB.h"
#endif
YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent)
: KeyComponentWidget(parent)
, m_compUi(new Ui::YubiKeyEditWidget())
#ifdef WITH_XC_YUBIKEY
, m_deviceListener(new DeviceListener(this))
#endif
{
initComponent();
#ifdef WITH_XC_YUBIKEY
connect(YubiKey::instance(), SIGNAL(detectComplete(bool)), SLOT(hardwareKeyResponse(bool)), Qt::QueuedConnection);
connect(m_deviceListener, &DeviceListener::devicePlugged, this, [&](bool, void*, void*) { pollYubikey(); });
#endif
}
YubiKeyEditWidget::~YubiKeyEditWidget() = default;
@@ -84,6 +90,7 @@ void YubiKeyEditWidget::showEvent(QShowEvent* event)
{
KeyComponentWidget::showEvent(event);
#ifdef WITH_XC_YUBIKEY
#ifdef Q_OS_WIN
m_deviceListener->registerHotplugCallback(true,
true,
@@ -99,12 +106,15 @@ void YubiKeyEditWidget::showEvent(QShowEvent* event)
m_deviceListener->registerHotplugCallback(true, true, YubiKeyInterfaceUSB::YUBICO_USB_VID);
m_deviceListener->registerHotplugCallback(true, true, YubiKeyInterfaceUSB::ONLYKEY_USB_VID);
#endif
#endif
}
void YubiKeyEditWidget::hideEvent(QHideEvent* event)
{
KeyComponentWidget::hideEvent(event);
#ifdef WITH_XC_YUBIKEY
m_deviceListener->deregisterAllHotplugCallbacks();
#endif
}
void YubiKeyEditWidget::initComponentEditWidget(QWidget* widget)
@@ -136,6 +146,7 @@ void YubiKeyEditWidget::initComponent()
void YubiKeyEditWidget::pollYubikey()
{
#ifdef WITH_XC_YUBIKEY
if (!m_compEditWidget) {
return;
}
@@ -148,6 +159,7 @@ void YubiKeyEditWidget::pollYubikey()
m_compUi->refreshHardwareKeys->setEnabled(false);
YubiKey::instance()->findValidKeysAsync();
#endif
}
void YubiKeyEditWidget::hardwareKeyResponse(bool found)

View File

@@ -19,7 +19,10 @@
#define KEEPASSXC_YUBIKEYEDITWIDGET_H
#include "KeyComponentWidget.h"
#include "config-keepassx.h"
#ifdef WITH_XC_YUBIKEY
#include "gui/osutils/DeviceListener.h"
#endif
namespace Ui
{
@@ -54,7 +57,9 @@ private slots:
private:
const QScopedPointer<Ui::YubiKeyEditWidget> m_compUi;
QPointer<QWidget> m_compEditWidget;
#ifdef WITH_XC_YUBIKEY
QPointer<DeviceListener> m_deviceListener;
#endif
bool m_isDetected = false;
};

View File

@@ -20,13 +20,15 @@
#include "DatabaseSettingsWidgetDatabaseKey.h"
#include "DatabaseSettingsWidgetEncryption.h"
#include "DatabaseSettingsWidgetGeneral.h"
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
#include "DatabaseSettingsWidgetBrowser.h"
#endif
#include "../remote/DatabaseSettingsWidgetRemote.h"
#include "DatabaseSettingsWidgetMaintenance.h"
#ifdef WITH_XC_KEESHARE
#include "keeshare/DatabaseSettingsWidgetKeeShare.h"
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
#include "fdosecrets/widgets/DatabaseSettingsWidgetFdoSecrets.h"
#endif
@@ -42,11 +44,13 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent)
, m_securityTabWidget(new QTabWidget(this))
, m_databaseKeyWidget(new DatabaseSettingsWidgetDatabaseKey(this))
, m_encryptionWidget(new DatabaseSettingsWidgetEncryption(this))
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
, m_browserWidget(new DatabaseSettingsWidgetBrowser(this))
#endif
#ifdef WITH_XC_KEESHARE
, m_keeShareWidget(new DatabaseSettingsWidgetKeeShare(this))
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
, m_fdoSecretsWidget(new DatabaseSettingsWidgetFdoSecrets(this))
#endif
, m_maintenanceWidget(new DatabaseSettingsWidgetMaintenance(this))
@@ -74,13 +78,15 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent)
addPage(tr("Remote Sync"), icons()->icon("remote-sync"), m_remoteWidget);
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
addPage(tr("Browser Integration"), icons()->icon("internet-web-browser"), m_browserWidget);
#endif
#ifdef WITH_XC_KEESHARE
addPage(tr("KeeShare"), icons()->icon("preferences-system-network-sharing"), m_keeShareWidget);
#endif
#ifdef KPXC_FEATURE_FDOSECRETS
#ifdef WITH_XC_FDOSECRETS
addPage(tr("Secret Service Integration"), icons()->icon(QStringLiteral("freedesktop")), m_fdoSecretsWidget);
#endif
@@ -99,11 +105,13 @@ void DatabaseSettingsDialog::load(const QSharedPointer<Database>& db)
m_databaseKeyWidget->loadSettings(db);
m_encryptionWidget->loadSettings(db);
m_remoteWidget->loadSettings(db);
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
m_browserWidget->loadSettings(db);
#endif
#ifdef WITH_XC_KEESHARE
m_keeShareWidget->loadSettings(db);
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
m_fdoSecretsWidget->loadSettings(db);
#endif
m_maintenanceWidget->loadSettings(db);
@@ -151,8 +159,10 @@ void DatabaseSettingsDialog::save()
// Browser settings don't have anything to save
#ifdef WITH_XC_KEESHARE
m_keeShareWidget->saveSettings();
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
m_fdoSecretsWidget->saveSettings();
#endif
@@ -165,7 +175,7 @@ void DatabaseSettingsDialog::reject()
m_databaseKeyWidget->discard();
m_encryptionWidget->discard();
m_remoteWidget->discard();
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
m_browserWidget->discard();
#endif

View File

@@ -28,11 +28,13 @@ class Database;
class DatabaseSettingsWidgetGeneral;
class DatabaseSettingsWidgetEncryption;
class DatabaseSettingsWidgetDatabaseKey;
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
class DatabaseSettingsWidgetBrowser;
#endif
#ifdef WITH_XC_KEESHARE
class DatabaseSettingsWidgetKeeShare;
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
class DatabaseSettingsWidgetFdoSecrets;
#endif
class DatabaseSettingsWidgetMaintenance;
@@ -65,11 +67,13 @@ private:
QPointer<QTabWidget> m_securityTabWidget;
QPointer<DatabaseSettingsWidgetDatabaseKey> m_databaseKeyWidget;
QPointer<DatabaseSettingsWidgetEncryption> m_encryptionWidget;
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
QPointer<DatabaseSettingsWidgetBrowser> m_browserWidget;
#endif
#ifdef WITH_XC_KEESHARE
QPointer<DatabaseSettingsWidgetKeeShare> m_keeShareWidget;
#ifdef KPXC_FEATURE_FDOSECRETS
#endif
#ifdef WITH_XC_FDOSECRETS
QPointer<DatabaseSettingsWidgetFdoSecrets> m_fdoSecretsWidget;
#endif
QPointer<DatabaseSettingsWidgetMaintenance> m_maintenanceWidget;

View File

@@ -38,7 +38,9 @@ DatabaseSettingsWidgetDatabaseKey::DatabaseSettingsWidgetDatabaseKey(QWidget* pa
, m_additionalKeyOptions(new QWidget(this))
, m_passwordEditWidget(new PasswordEditWidget(this))
, m_keyFileEditWidget(new KeyFileEditWidget(this))
#ifdef WITH_XC_YUBIKEY
, m_yubiKeyEditWidget(new YubiKeyEditWidget(this))
#endif
{
auto* vbox = new QVBoxLayout(this);
vbox->setSizeConstraint(QLayout::SetMinimumSize);
@@ -56,7 +58,9 @@ DatabaseSettingsWidgetDatabaseKey::DatabaseSettingsWidgetDatabaseKey(QWidget* pa
m_additionalKeyOptions->layout()->setMargin(0);
m_additionalKeyOptions->layout()->setSpacing(20);
m_additionalKeyOptions->layout()->addWidget(m_keyFileEditWidget);
#ifdef WITH_XC_YUBIKEY
m_additionalKeyOptions->layout()->addWidget(m_yubiKeyEditWidget);
#endif
m_additionalKeyOptions->setVisible(false);
connect(m_additionalKeyOptionsToggle, SIGNAL(clicked()), SLOT(showAdditionalKeyOptions()));
@@ -87,19 +91,23 @@ void DatabaseSettingsWidgetDatabaseKey::loadSettings(QSharedPointer<Database> db
}
}
#ifdef WITH_XC_YUBIKEY
for (const auto& key : m_db->key()->challengeResponseKeys()) {
if (key->uuid() == ChallengeResponseKey::UUID) {
m_yubiKeyEditWidget->setComponentAdded(true);
hasAdditionalKeys = true;
}
}
#endif
setAdditionalKeyOptionsVisible(hasAdditionalKeys);
}
connect(m_passwordEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
connect(m_keyFileEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
#ifdef WITH_XC_YUBIKEY
connect(m_yubiKeyEditWidget->findChild<QPushButton*>("removeButton"), SIGNAL(clicked()), SLOT(markDirty()));
#endif
}
void DatabaseSettingsWidgetDatabaseKey::initialize()
@@ -107,7 +115,9 @@ void DatabaseSettingsWidgetDatabaseKey::initialize()
bool blocked = blockSignals(true);
m_passwordEditWidget->setComponentAdded(false);
m_keyFileEditWidget->setComponentAdded(false);
#ifdef WITH_XC_YUBIKEY
m_yubiKeyEditWidget->setComponentAdded(false);
#endif
blockSignals(blocked);
}
@@ -119,7 +129,9 @@ bool DatabaseSettingsWidgetDatabaseKey::saveSettings()
{
m_isDirty |= (m_passwordEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
m_isDirty |= (m_keyFileEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
#ifdef WITH_XC_YUBIKEY
m_isDirty |= (m_yubiKeyEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
#endif
if (m_db->key() && !m_db->key()->keys().isEmpty() && !m_isDirty) {
// Key unchanged
@@ -200,9 +212,11 @@ bool DatabaseSettingsWidgetDatabaseKey::saveSettings()
return false;
}
#ifdef WITH_XC_YUBIKEY
if (!addToCompositeKey(m_yubiKeyEditWidget, newKey, oldChallengeResponse)) {
return false;
}
#endif
if (newKey->keys().isEmpty() && newKey->challengeResponseKeys().isEmpty()) {
MessageBox::critical(this,

View File

@@ -19,6 +19,7 @@
#define KEEPASSXC_DATABASESETTINGSWIDGETDATABASEKEY_H
#include "DatabaseSettingsWidget.h"
#include "config-keepassx.h"
#include <QPointer>
@@ -71,7 +72,9 @@ private:
const QPointer<QWidget> m_additionalKeyOptions;
const QPointer<PasswordEditWidget> m_passwordEditWidget;
const QPointer<KeyFileEditWidget> m_keyFileEditWidget;
#ifdef WITH_XC_YUBIKEY
const QPointer<YubiKeyEditWidget> m_yubiKeyEditWidget;
#endif
};
#endif // KEEPASSXC_DATABASESETTINGSWIDGETDATABASEKEY_H

View File

@@ -39,12 +39,12 @@
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/TimeDelta.h"
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
#include "sshagent/OpenSSHKey.h"
#include "sshagent/OpenSSHKeyGenDialog.h"
#include "sshagent/SSHAgent.h"
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
#include "EntryURLModel.h"
#include "browser/BrowserService.h"
#endif
@@ -74,10 +74,10 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
, m_advancedWidget(new QWidget(this))
, m_iconsWidget(new EditWidgetIcons(this))
, m_autoTypeWidget(new QWidget(this))
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
, m_sshAgentWidget(new QWidget(this))
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
, m_browserSettingsChanged(false)
, m_browserWidget(new QWidget(this))
, m_additionalURLsDataModel(new EntryURLModel(this))
@@ -100,11 +100,11 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
setupIcon();
setupAutoType();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
setupSSHAgent();
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
setupBrowser();
#endif
@@ -191,19 +191,19 @@ void EditEntryWidget::setupMain()
m_usernameCompleter->setModel(m_usernameCompleterModel);
m_mainUi->usernameComboBox->setCompleter(m_usernameCompleter);
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
m_mainUi->fetchFaviconButton->setIcon(icons()->icon("favicon-download"));
m_mainUi->fetchFaviconButton->setDisabled(true);
#else
m_mainUi->fetchFaviconButton->setVisible(false);
#endif
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
connect(m_mainUi->fetchFaviconButton, SIGNAL(clicked()), m_iconsWidget, SLOT(downloadFavicon()));
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString)));
m_mainUi->urlEdit->enableVerifyMode();
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(entryURLEdited(const QString&)));
#endif
connect(m_mainUi->expireCheck, &QCheckBox::toggled, [&](bool enabled) {
@@ -298,7 +298,7 @@ void EditEntryWidget::setupAutoType()
// clang-format on
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void EditEntryWidget::setupBrowser()
{
if (config()->get(Config::Browser_Enabled).toBool()) {
@@ -489,7 +489,7 @@ void EditEntryWidget::setupEntryUpdate()
connect(m_mainUi->usernameComboBox->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(setModified()));
connect(m_mainUi->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified()));
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified()));
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(updateFaviconButtonEnable(QString)));
#endif
connect(m_mainUi->tagsList, SIGNAL(tagsEdited()), this, SLOT(setModified()));
@@ -520,7 +520,7 @@ void EditEntryWidget::setupEntryUpdate()
// Properties and History tabs don't need extra connections
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
// SSH Agent tab
if (sshAgent()->isEnabled()) {
connect(m_sshAgentUi->attachmentRadioButton, SIGNAL(toggled(bool)), this, SLOT(setModified()));
@@ -536,7 +536,7 @@ void EditEntryWidget::setupEntryUpdate()
}
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
connect(m_browserUi->skipAutoSubmitCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->hideEntryCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
@@ -584,7 +584,7 @@ void EditEntryWidget::updateHistoryButtons(const QModelIndex& current, const QMo
}
}
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
void EditEntryWidget::setupSSHAgent()
{
m_pendingPrivateKey = "";
@@ -937,7 +937,7 @@ void EditEntryWidget::loadEntry(Entry* entry,
switchToPage(Page::Main);
setPageHidden(m_historyWidget, m_history || m_entry->historyItems().count() < 1);
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
setPageHidden(m_sshAgentWidget, !sshAgent()->isEnabled());
#endif
@@ -1053,13 +1053,13 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
}
updateAutoTypeEnabled();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
if (sshAgent()->isEnabled()) {
updateSSHAgent();
}
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
if (!hasPage(m_browserWidget)) {
setupBrowser();
@@ -1215,7 +1215,7 @@ bool EditEntryWidget::commitEntry()
m_autoTypeAssoc->removeEmpty();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
toKeeAgentSettings(m_sshAgentSettings);
#endif
@@ -1224,7 +1224,7 @@ bool EditEntryWidget::commitEntry()
m_entry->beginUpdate();
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
updateBrowser();
}
@@ -1312,7 +1312,7 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
entry->autoTypeAssociations()->copyDataFrom(m_autoTypeAssoc);
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
if (sshAgent()->isEnabled()) {
m_sshAgentSettings.toEntry(entry);
}
@@ -1398,7 +1398,7 @@ void EditEntryWidget::clear()
hideMessage();
}
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
void EditEntryWidget::updateFaviconButtonEnable(const QString& url)
{
m_mainUi->fetchFaviconButton->setDisabled(url.isEmpty());

View File

@@ -44,11 +44,11 @@ class QMenu;
class QScrollArea;
class QSortFilterProxyModel;
class QStringListModel;
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
#include "sshagent/KeeAgentSettings.h"
class OpenSSHKey;
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
class EntryURLModel;
#endif
@@ -98,7 +98,7 @@ private slots:
void acceptEntry();
bool commitEntry();
void cancel();
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
void updateFaviconButtonEnable(const QString& url);
#endif
void insertAttribute();
@@ -124,7 +124,7 @@ private slots:
void useExpiryPreset(QAction* action);
void toggleHideNotes(bool visible);
void pickColor();
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
void toKeeAgentSettings(KeeAgentSettings& settings) const;
void setSSHAgentSettings();
void updateSSHAgent();
@@ -140,7 +140,7 @@ private slots:
void copyPublicKey();
void generatePrivateKey();
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void updateBrowserModified();
void updateBrowser();
void insertURL();
@@ -155,10 +155,10 @@ private:
void setupAdvanced();
void setupIcon();
void setupAutoType();
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void setupBrowser();
#endif
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
void setupSSHAgent();
#endif
void setupProperties();
@@ -170,7 +170,7 @@ private:
QMenu* createPresetsMenu();
void updateEntryData(Entry* entry) const;
void updateBrowserIntegrationCheckbox(QCheckBox* checkBox, bool enabled, bool value, const QString& option);
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
bool getOpenSSHKey(OpenSSHKey& key, bool decrypt = false);
#endif
@@ -183,7 +183,7 @@ private:
bool m_create;
bool m_history;
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
KeeAgentSettings m_sshAgentSettings;
QString m_pendingPrivateKey;
#endif
@@ -200,10 +200,10 @@ private:
QWidget* const m_advancedWidget;
EditWidgetIcons* const m_iconsWidget;
QWidget* const m_autoTypeWidget;
#ifdef KPXC_FEATURE_SSHAGENT
#ifdef WITH_XC_SSHAGENT
QWidget* const m_sshAgentWidget;
#endif
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
bool m_browserSettingsChanged;
QWidget* const m_browserWidget;
EntryURLModel* const m_additionalURLsDataModel;

View File

@@ -18,7 +18,7 @@
#include "EditGroupWidget.h"
#include "ui_EditGroupWidgetMain.h"
#if defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_BROWSER)
#include "browser/BrowserService.h"
#include "ui_EditGroupWidgetBrowser.h"
#endif
@@ -30,7 +30,10 @@
#include "gui/Font.h"
#include "gui/Icons.h"
#include "gui/MessageBox.h"
#if defined(WITH_XC_KEESHARE)
#include "keeshare/group/EditGroupPageKeeShare.h"
#endif
class EditGroupWidget::ExtraPage
{
@@ -67,7 +70,7 @@ EditGroupWidget::EditGroupWidget(QWidget* parent)
, m_editGroupWidgetMain(new QScrollArea())
, m_editGroupWidgetIcons(new EditWidgetIcons())
, m_editWidgetProperties(new EditWidgetProperties())
#if defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_BROWSER)
, m_browserSettingsChanged(false)
, m_browserUi(new Ui::EditGroupWidgetBrowser())
, m_browserWidget(new QWidget(this))
@@ -78,12 +81,14 @@ EditGroupWidget::EditGroupWidget(QWidget* parent)
addPage(tr("Group"), icons()->icon("document-edit"), m_editGroupWidgetMain);
addPage(tr("Icon"), icons()->icon("preferences-desktop-icons"), m_editGroupWidgetIcons);
#if defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_BROWSER)
if (config()->get(Config::Browser_Enabled).toBool()) {
initializeBrowserPage();
}
#endif
#if defined(WITH_XC_KEESHARE)
addEditPage(new EditGroupPageKeeShare(this));
#endif
addPage(tr("Properties"), icons()->icon("document-properties"), m_editWidgetProperties);
connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool)));
@@ -125,7 +130,7 @@ void EditGroupWidget::setupModifiedTracking()
// Icon tab
connect(m_editGroupWidgetIcons, SIGNAL(widgetUpdated()), SLOT(setModified()));
#if defined(KPXC_FEATURE_BROWSER)
#if defined(WITH_XC_BROWSER)
if (config()->get(Config::Browser_Enabled).toBool()) {
setupBrowserModifiedTracking();
}
@@ -184,7 +189,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
page.set(m_temporaryGroup.data(), m_db);
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
auto inheritHideEntries = false;
auto inheritSkipSubmit = false;
@@ -281,7 +286,7 @@ void EditGroupWidget::apply()
page.assign();
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
if (!m_browserSettingsChanged) {
return;
@@ -348,7 +353,7 @@ void EditGroupWidget::cancel()
emit editFinished(false);
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void EditGroupWidget::initializeBrowserPage()
{
addPage(tr("Browser Integration"), icons()->icon("internet-web-browser"), m_browserWidget);
@@ -445,7 +450,7 @@ Group::TriState EditGroupWidget::triStateFromIndex(int index)
}
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void EditGroupWidget::addRestrictKeyComboBoxItems(QStringList const& keyList, QString inheritValue)
{
auto comboBox = m_browserUi->browserIntegrationRestrictKeyCombobox;

View File

@@ -69,7 +69,7 @@ private slots:
void apply();
void save();
void cancel();
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
void initializeBrowserPage();
void setupBrowserModifiedTracking();
void updateBrowserModified();
@@ -90,7 +90,7 @@ private:
QPointer<QScrollArea> m_editGroupWidgetMain;
QPointer<EditWidgetIcons> m_editGroupWidgetIcons;
QPointer<EditWidgetProperties> m_editWidgetProperties;
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
bool m_browserSettingsChanged;
const QScopedPointer<Ui::EditGroupWidgetBrowser> m_browserUi;
QWidget* const m_browserWidget;

View File

@@ -125,7 +125,9 @@ QVariant GroupModel::data(const QModelIndex& index, int role) const
if (role == Qt::DisplayRole) {
QString nameTemplate = "%1";
#if defined(WITH_XC_KEESHARE)
nameTemplate = KeeShare::indicatorSuffix(group, nameTemplate);
#endif
return nameTemplate.arg(group->name());
} else if (role == Qt::DecorationRole) {
return Icons::groupIconPixmap(group);

View File

@@ -152,11 +152,22 @@ bool MacUtils::isCapslockEnabled()
void MacUtils::setUserInputProtection(bool enable)
{
static bool secureInputEnabled = false;
if (enable) {
/*
* MacOS keeps a single counter over all apps that needs to be zero to disable secure input. By never going
* higher than 1 internally this makes sure secure input doesn't stay active after calling this function
* multiple times.
*/
if (secureInputEnabled) {
DisableSecureEventInput();
}
EnableSecureEventInput();
} else {
DisableSecureEventInput();
}
// Store our last known state
secureInputEnabled = enable;
}
/**

View File

@@ -30,7 +30,7 @@
#include <QStandardPaths>
#include <QStyle>
#include <QTextStream>
#ifdef WITH_X11
#ifdef WITH_XC_X11
#include <QX11Info>
#include <qpa/qplatformnativeinterface.h>
@@ -67,7 +67,7 @@ NixUtils* NixUtils::instance()
NixUtils::NixUtils(QObject* parent)
: OSUtilsBase(parent)
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
dpy = QX11Info::display();
rootWindow = QX11Info::appRootWindow();
#endif
@@ -214,7 +214,7 @@ void NixUtils::launchAtStartupRequested(uint response, const QVariantMap& result
bool NixUtils::isCapslockEnabled()
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
auto* display = native->nativeResourceForWindow("display", nullptr);
if (!display) {
@@ -248,7 +248,7 @@ void NixUtils::registerNativeEventFilter()
bool NixUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
if (eventType != QByteArrayLiteral("xcb_generic_event_t")) {
return false;
}
@@ -270,7 +270,7 @@ bool NixUtils::nativeEventFilter(const QByteArray& eventType, void* message, lon
bool NixUtils::triggerGlobalShortcut(uint keycode, uint modifiers)
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
@@ -288,7 +288,7 @@ bool NixUtils::triggerGlobalShortcut(uint keycode, uint modifiers)
bool NixUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
auto keycode = XKeysymToKeycode(dpy, qcharToNativeKeyCode(key));
auto modifierscode = qtToNativeModifiers(modifiers);
@@ -340,7 +340,7 @@ bool NixUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::Keyb
bool NixUtils::unregisterGlobalShortcut(const QString& name)
{
#ifdef WITH_X11
#ifdef WITH_XC_X11
if (!m_globalShortcuts.contains(name)) {
return false;
}

View File

@@ -21,10 +21,12 @@
#include "ReportsPageHealthcheck.h"
#include "ReportsPageHibp.h"
#include "ReportsPageStatistics.h"
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
#include "ReportsPageBrowserStatistics.h"
#include "ReportsPagePasskeys.h"
#include "ReportsWidgetBrowserStatistics.h"
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
#include "ReportsPagePasskeys.h"
#include "ReportsWidgetPasskeys.h"
#endif
#include "ReportsWidgetHealthcheck.h"
@@ -61,8 +63,10 @@ ReportsDialog::ReportsDialog(QWidget* parent)
, m_healthPage(new ReportsPageHealthcheck())
, m_hibpPage(new ReportsPageHibp())
, m_statPage(new ReportsPageStatistics())
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
, m_browserStatPage(new ReportsPageBrowserStatistics())
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
, m_passkeysPage(new ReportsPagePasskeys())
#endif
, m_editEntryWidget(new EditEntryWidget(this))
@@ -72,8 +76,10 @@ ReportsDialog::ReportsDialog(QWidget* parent)
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
addPage(m_statPage);
addPage(m_healthPage);
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER_PASSKEYS
addPage(m_passkeysPage);
#endif
#ifdef WITH_XC_BROWSER
addPage(m_browserStatPage);
#endif
addPage(m_hibpPage);
@@ -88,10 +94,12 @@ ReportsDialog::ReportsDialog(QWidget* parent)
connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int)));
connect(m_healthPage->m_healthWidget, SIGNAL(entryActivated(Entry*)), SLOT(entryActivationSignalReceived(Entry*)));
connect(m_hibpPage->m_hibpWidget, SIGNAL(entryActivated(Entry*)), SLOT(entryActivationSignalReceived(Entry*)));
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
connect(m_browserStatPage->m_browserWidget,
SIGNAL(entryActivated(Entry*)),
SLOT(entryActivationSignalReceived(Entry*)));
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
connect(
m_passkeysPage->m_passkeysWidget, SIGNAL(entryActivated(Entry*)), SLOT(entryActivationSignalReceived(Entry*)));
#endif
@@ -120,19 +128,23 @@ void ReportsDialog::addPage(QSharedPointer<IReportsPage> page)
m_ui->categoryList->setCurrentCategory(category);
}
#ifdef KPXC_FEATURE_BROWSER
void ReportsDialog::activatePasskeysPage()
{
#ifdef WITH_XC_BROWSER_PASSKEYS
m_ui->stackedWidget->setCurrentWidget(m_passkeysPage->m_passkeysWidget);
auto index = m_ui->stackedWidget->currentIndex();
m_ui->categoryList->setCurrentCategory(index);
#endif
}
bool ReportsDialog::onPassKeysPage()
{
#ifdef WITH_XC_BROWSER_PASSKEYS
return m_ui->stackedWidget->currentWidget() == m_passkeysPage->m_passkeysWidget;
}
#else
return false;
#endif
}
void ReportsDialog::reject()
{
@@ -164,11 +176,12 @@ void ReportsDialog::switchToMainView(bool previousDialogAccepted)
} else if (m_sender == m_hibpPage->m_hibpWidget) {
m_hibpPage->m_hibpWidget->refreshAfterEdit();
}
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
if (m_sender == m_browserStatPage->m_browserWidget) {
m_browserStatPage->m_browserWidget->calculateBrowserStatistics();
}
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
if (m_sender == m_passkeysPage->m_passkeysWidget) {
m_passkeysPage->m_passkeysWidget->updateEntries();
}

View File

@@ -29,8 +29,10 @@ class QTabWidget;
class ReportsPageHealthcheck;
class ReportsPageHibp;
class ReportsPageStatistics;
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
class ReportsPageBrowserStatistics;
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
class ReportsPagePasskeys;
#endif
@@ -61,11 +63,8 @@ public:
void load(const QSharedPointer<Database>& db);
void addPage(QSharedPointer<IReportsPage> page);
#ifdef KPXC_FEATURE_BROWSER
void activatePasskeysPage();
bool onPassKeysPage();
#endif
signals:
void editFinished(bool accepted);
@@ -81,8 +80,10 @@ private:
const QSharedPointer<ReportsPageHealthcheck> m_healthPage;
const QSharedPointer<ReportsPageHibp> m_hibpPage;
const QSharedPointer<ReportsPageStatistics> m_statPage;
#ifdef KPXC_FEATURE_BROWSER
#ifdef WITH_XC_BROWSER
const QSharedPointer<ReportsPageBrowserStatistics> m_browserStatPage;
#endif
#ifdef WITH_XC_BROWSER_PASSKEYS
const QSharedPointer<ReportsPagePasskeys> m_passkeysPage;
#endif
QPointer<EditEntryWidget> m_editEntryWidget;

View File

@@ -71,7 +71,7 @@ ReportsWidgetHibp::ReportsWidgetHibp(QWidget* parent)
connect(m_ui->hibpTableView, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
connect(m_ui->hibpTableView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
connect(m_ui->showKnownBadCheckBox, SIGNAL(stateChanged(int)), this, SLOT(makeHibpTable()));
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
connect(&m_downloader, SIGNAL(hibpResult(QString, int)), SLOT(addHibpResult(QString, int)));
connect(&m_downloader, SIGNAL(fetchFailed(QString)), SLOT(fetchFailed(QString)));
@@ -92,7 +92,7 @@ void ReportsWidgetHibp::loadSettings(QSharedPointer<Database> db)
m_error.clear();
m_rowToEntry.clear();
m_editedEntry = nullptr;
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
m_ui->stackedWidget->setCurrentIndex(0);
m_ui->validationButton->setEnabled(true);
m_ui->progressBar->hide();
@@ -188,7 +188,7 @@ void ReportsWidgetHibp::makeHibpTable()
}
// If we're done and everything is good, display a motivational message
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
if (m_downloader.passwordsRemaining() == 0 && m_pwndPasswords.isEmpty() && m_error.isEmpty()) {
m_referencesModel->clear();
m_referencesModel->setHorizontalHeaderLabels(QStringList() << tr("Congratulations, no exposed passwords!"));
@@ -219,7 +219,7 @@ void ReportsWidgetHibp::addHibpResult(const QString& password, int count)
m_pwndPasswords[password] = count;
}
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
// Update the progress bar
int remaining = m_downloader.passwordsRemaining();
if (remaining > 0) {
@@ -249,7 +249,7 @@ void ReportsWidgetHibp::fetchFailed(const QString& error)
*/
void ReportsWidgetHibp::startValidation()
{
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
// Collect all passwords in the database (unless recycled, and
// unless empty, and unless marked as "known bad") and submit them
// to the downloader.
@@ -345,7 +345,7 @@ void ReportsWidgetHibp::refreshAfterEdit()
m_pwndPasswords.remove(m_editedPassword);
// Validate the new password against HIBP
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
m_downloader.add(m_editedEntry->password());
m_downloader.validate();
#endif

View File

@@ -23,7 +23,7 @@
#include <QWidget>
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
#include "networking/HibpDownloader.h"
#endif
@@ -78,7 +78,7 @@ private:
QString m_editedPassword; // The old password of the entry we're editing
bool m_editedExcluded; // The old "known bad" flag of the entry we're editing
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
HibpDownloader m_downloader; // This performs the actual HIBP online query
#endif
};

View File

@@ -30,7 +30,8 @@ TagModel::TagModel(QObject* parent)
{
m_defaultSearches << qMakePair(tr("Clear Search"), QString("")) << qMakePair(tr("All Entries"), QString("*"))
<< qMakePair(tr("Expired"), QString("is:expired"))
<< qMakePair(tr("Weak Passwords"), QString("is:weak"));
<< qMakePair(tr("Weak Passwords"), QString("is:weak"))
<< qMakePair(tr("TOTP Entries"), QString("has:totp"));
}
TagModel::~TagModel() = default;

View File

@@ -1,16 +1,18 @@
set(keeshare_SOURCES
SettingsPageKeeShare.cpp
SettingsWidgetKeeShare.cpp
DatabaseSettingsWidgetKeeShare.cpp
group/EditGroupWidgetKeeShare.cpp
group/EditGroupPageKeeShare.cpp
KeeShare.cpp
KeeShareSettings.cpp
ShareImport.cpp
ShareExport.cpp
ShareObserver.cpp
)
if(WITH_XC_KEESHARE)
set(keeshare_SOURCES
SettingsPageKeeShare.cpp
SettingsWidgetKeeShare.cpp
DatabaseSettingsWidgetKeeShare.cpp
group/EditGroupWidgetKeeShare.cpp
group/EditGroupPageKeeShare.cpp
KeeShare.cpp
KeeShareSettings.cpp
ShareImport.cpp
ShareExport.cpp
ShareObserver.cpp
)
add_library(keeshare STATIC ${keeshare_SOURCES})
target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN_LIBRARIES} ${ZLIB_LIBRARIES} PRIVATE ${MINIZIP_LIBRARIES})
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
add_library(keeshare STATIC ${keeshare_SOURCES})
target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN_LIBRARIES} ${ZLIB_LIBRARIES} PRIVATE ${MINIZIP_LIBRARIES})
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
endif(WITH_XC_KEESHARE)

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
* Copyright (C) 2017-2021 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 "YubiKey.h"
YubiKey::YubiKey() = default;
YubiKey* YubiKey::m_instance(Q_NULLPTR);
YubiKey* YubiKey::instance()
{
if (!m_instance) {
m_instance = new YubiKey();
}
return m_instance;
}
bool YubiKey::isInitialized()
{
return false;
}
bool YubiKey::findValidKeys()
{
return false;
}
void YubiKey::findValidKeysAsync()
{
}
YubiKey::KeyMap YubiKey::foundKeys()
{
return {};
}
int YubiKey::connectedKeys()
{
return 0;
}
QString YubiKey::errorMessage()
{
return {};
}
bool YubiKey::testChallenge(YubiKeySlot slot, bool* wouldBlock)
{
Q_UNUSED(slot);
Q_UNUSED(wouldBlock);
return false;
}
YubiKey::ChallengeResult YubiKey::challenge(YubiKeySlot slot, const QByteArray& chal, Botan::secure_vector<char>& resp)
{
Q_UNUSED(slot);
Q_UNUSED(chal);
Q_UNUSED(resp);
return YubiKey::ChallengeResult::YCR_ERROR;
}

View File

@@ -22,7 +22,7 @@
#include <QHash>
#include <QObject>
#ifndef KPXC_FEATURE_NETWORK
#ifndef WITH_XC_NETWORKING
#error This file requires KeePassXC to be built with network support.
#endif

View File

@@ -17,7 +17,7 @@
#include "config-keepassx.h"
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
#include "NetworkManager.h"

View File

@@ -20,13 +20,13 @@
#include "config-keepassx.h"
#ifdef KPXC_FEATURE_NETWORK
#ifdef WITH_XC_NETWORKING
class QNetworkAccessManager;
QNetworkAccessManager* getNetMgr();
#else
Q_STATIC_ASSERT_X(false, "Qt Networking used when KPXC_FEATURE_NETWORK is disabled!");
Q_STATIC_ASSERT_X(false, "Qt Networking used when WITH_XC_NETWORKING is disabled!");
#endif
#endif // KEEPASSXC_NETWORKMANAGER_H

View File

@@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(KPXC_FEATURE_BROWSER)
if(WITH_XC_BROWSER)
set(proxy_SOURCES
../browser/BrowserShared.cpp
keepassxc-proxy.cpp

View File

@@ -1,4 +1,4 @@
if(KPXC_FEATURE_SSHAGENT)
if(WITH_XC_SSHAGENT)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(sshagent_SOURCES

View File

@@ -127,7 +127,7 @@ namespace OpenSSHKeyGen
QByteArray privateData;
BinaryStream privateStream(&privateData);
vectorToStream(ed25519Key.get_public_key(), privateStream);
#ifdef WITH_BOTAN3
#ifdef WITH_XC_BOTAN3
vectorToStream(ed25519Key.raw_private_key_bits(), privateStream);
#else
vectorToStream(ed25519Key.get_private_key(), privateStream);

View File

@@ -13,5 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(ykcore)
set(thirdparty_LIBRARIES ${thirdparty_LIBRARIES} ykcore PARENT_SCOPE)
if(WITH_XC_YUBIKEY)
add_subdirectory(ykcore)
set(thirdparty_LIBRARIES ${thirdparty_LIBRARIES} ykcore PARENT_SCOPE)
endif()

View File

@@ -76,6 +76,18 @@ macro(add_unit_test)
endif(KDE4_TEST_OUTPUT STREQUAL "xml")
set_tests_properties(${_test_NAME} PROPERTIES ENVIRONMENT "LANG=en_US.UTF-8")
if(NOT MSVC_IDE) #not needed for the ide
# if the tests are EXCLUDE_FROM_ALL, add a target "buildtests" to build all tests
if(NOT WITH_TESTS)
get_directory_property(_buildtestsAdded BUILDTESTS_ADDED)
if(NOT _buildtestsAdded)
add_custom_target(buildtests)
set_directory_properties(PROPERTIES BUILDTESTS_ADDED TRUE)
endif()
add_dependencies(buildtests ${_test_NAME})
endif()
endif()
endmacro(add_unit_test)
set(TEST_LIBRARIES keepassxc_gui Qt5::Test)
@@ -133,9 +145,27 @@ add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp
add_unit_test(NAME testimports SOURCES TestImports.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testautotype SOURCES TestAutoType.cpp
LIBS testsupport ${TEST_LIBRARIES})
set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON)
if(WITH_XC_NETWORKING)
add_unit_test(NAME testupdatecheck SOURCES TestUpdateCheck.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testicondownloader SOURCES TestIconDownloader.cpp LIBS ${TEST_LIBRARIES})
endif()
if(WITH_XC_AUTOTYPE)
add_unit_test(NAME testautotype SOURCES TestAutoType.cpp
LIBS testsupport ${TEST_LIBRARIES})
set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON)
endif()
if(WITH_XC_SSHAGENT)
add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp
LIBS sshagent testsupport ${TEST_LIBRARIES})
if(NOT WIN32)
add_unit_test(NAME testsshagent SOURCES TestSSHAgent.cpp
LIBS sshagent testsupport ${TEST_LIBRARIES})
endif()
endif()
add_unit_test(NAME testentry SOURCES TestEntry.cpp
LIBS ${TEST_LIBRARIES})
@@ -173,11 +203,16 @@ add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp
add_unit_test(NAME testcsvexporter SOURCES TestCsvExporter.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testykchallengeresponsekey SOURCES TestYkChallengeResponseKey.cpp
if(WITH_XC_YUBIKEY)
add_unit_test(NAME testykchallengeresponsekey
SOURCES TestYkChallengeResponseKey.cpp
LIBS ${TEST_LIBRARIES})
endif()
add_unit_test(NAME testsharing SOURCES TestSharing.cpp
if(WITH_XC_KEESHARE)
add_unit_test(NAME testsharing SOURCES TestSharing.cpp
LIBS testsupport ${TEST_LIBRARIES})
endif()
add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp
LIBS testsupport ${TEST_LIBRARIES})
@@ -188,44 +223,29 @@ add_unit_test(NAME testtools SOURCES TestTools.cpp
add_unit_test(NAME testconfig SOURCES TestConfig.cpp
LIBS testsupport ${TEST_LIBRARIES})
add_unit_test(NAME testcli SOURCES TestCli.cpp
LIBS testsupport cli ${ZXCVBN_LIBRARIES} ${TEST_LIBRARIES})
target_compile_definitions(testcli PRIVATE KEEPASSX_CLI_PATH="$<TARGET_FILE:keepassxc-cli>")
if(KPXC_FEATURE_SSHAGENT)
add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp
LIBS sshagent testsupport ${TEST_LIBRARIES})
if(NOT WIN32)
add_unit_test(NAME testsshagent SOURCES TestSSHAgent.cpp
LIBS sshagent testsupport ${TEST_LIBRARIES})
endif()
endif()
if(KPXC_FEATURE_FDOSECRETS)
if(WITH_XC_FDOSECRETS)
add_unit_test(NAME testfdosecrets SOURCES TestFdoSecrets.cpp
LIBS testsupport fdosecrets ${TEST_LIBRARIES})
endif()
if(KPXC_FEATURE_BROWSER)
if(WITH_XC_BROWSER)
add_unit_test(NAME testbrowser SOURCES TestBrowser.cpp
LIBS browser ${TEST_LIBRARIES})
add_unit_test(NAME testpasskeys SOURCES TestPasskeys.cpp
LIBS browser ${TEST_LIBRARIES})
if(WITH_XC_BROWSER_PASSKEYS)
add_unit_test(NAME testpasskeys SOURCES TestPasskeys.cpp
LIBS browser ${TEST_LIBRARIES})
endif()
endif()
if(KPXC_FEATURE_NETWORK OR KPXC_FEATURE_BROWSER)
if(WITH_XC_NETWORKING OR WITH_XC_BROWSER)
add_unit_test(NAME testurltools SOURCES TestUrlTools.cpp LIBS ${TEST_LIBRARIES})
endif()
if(KPXC_FEATURE_NETWORK)
add_unit_test(NAME testupdatecheck SOURCES TestUpdateCheck.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testicondownloader SOURCES TestIconDownloader.cpp
LIBS ${TEST_LIBRARIES})
endif()
add_unit_test(NAME testcli SOURCES TestCli.cpp
LIBS testsupport cli ${ZXCVBN_LIBRARIES} ${TEST_LIBRARIES})
target_compile_definitions(testcli PRIVATE KEEPASSX_CLI_PATH="$<TARGET_FILE:keepassxc-cli>")
if(WITH_GUI_TESTS)
add_subdirectory(gui)
endif()
endif(WITH_GUI_TESTS)

View File

@@ -18,6 +18,7 @@
#include "TestEntrySearcher.h"
#include "core/Group.h"
#include "core/Tools.h"
#include "core/Totp.h"
#include <QTest>
@@ -394,3 +395,42 @@ void TestEntrySearcher::testUUIDSearch()
m_searchResult = m_entrySearcher.search("uuid:" + Tools::uuidToHex(uuid1), m_rootGroup);
QCOMPARE(m_searchResult.count(), 1);
}
void TestEntrySearcher::testTotpSearch()
{
auto entry1 = new Entry();
entry1->setGroup(m_rootGroup);
entry1->setTitle("Regular Entry");
auto entry2 = new Entry();
entry2->setGroup(m_rootGroup);
entry2->setTitle("TOTP Entry");
// Set up TOTP on entry2
auto totpSettings = Totp::createSettings("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 6, 30);
entry2->setTotp(totpSettings);
auto entry3 = new Entry();
entry3->setGroup(m_rootGroup);
entry3->setTitle("Another TOTP Entry");
// Set up TOTP on entry3
auto totpSettings2 = Totp::createSettings("MFRGG43UEBUXGIDBKRWXAZLSMUQGG6LQ", 6, 30);
entry3->setTotp(totpSettings2);
// Test searching for TOTP entries
m_searchResult = m_entrySearcher.search("has:totp", m_rootGroup);
QCOMPARE(m_searchResult.count(), 2);
QVERIFY(m_searchResult.contains(entry2));
QVERIFY(m_searchResult.contains(entry3));
QVERIFY(!m_searchResult.contains(entry1));
// Test case insensitive search
m_searchResult = m_entrySearcher.search("has:TOTP", m_rootGroup);
QCOMPARE(m_searchResult.count(), 2);
// Test excluding TOTP entries
m_searchResult = m_entrySearcher.search("!has:totp", m_rootGroup);
QCOMPARE(m_searchResult.count(), 1);
QVERIFY(m_searchResult.contains(entry1));
QVERIFY(!m_searchResult.contains(entry2));
QVERIFY(!m_searchResult.contains(entry3));
}

View File

@@ -39,6 +39,7 @@ private slots:
void testGroup();
void testSkipProtected();
void testUUIDSearch();
void testTotpSearch();
private:
Group* m_rootGroup;

View File

@@ -317,6 +317,56 @@ void TestImports::testBitwardenPasskey()
QStringLiteral("aTFtdmFnOHYtS2dxVEJ0by1rSFpLWGg0enlTVC1iUVJReDZ5czJXa3c2aw"));
}
void TestImports::testBitwardenNestedFolders()
{
auto bitwardenPath =
QStringLiteral("%1/%2").arg(KEEPASSX_TEST_DATA_DIR, QStringLiteral("/bitwarden_nested_export.json"));
BitwardenReader reader;
auto db = reader.convert(bitwardenPath);
QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
QVERIFY(db);
// Test nested folder structure: "Socials/Forums" should create Socials -> Forums hierarchy
auto entry = db->rootGroup()->findEntryByPath("/Socials/Forums/Reddit Account");
QVERIFY(entry);
QCOMPARE(entry->title(), QStringLiteral("Reddit Account"));
QCOMPARE(entry->username(), QStringLiteral("myuser"));
// Test deeper nesting: "Work/Projects/Client A"
entry = db->rootGroup()->findEntryByPath("/Work/Projects/Client A/Client Portal");
QVERIFY(entry);
QCOMPARE(entry->title(), QStringLiteral("Client Portal"));
QCOMPARE(entry->username(), QStringLiteral("clientuser"));
// Test simple folder (no nesting): "Personal"
entry = db->rootGroup()->findEntryByPath("/Personal/Personal Email");
QVERIFY(entry);
QCOMPARE(entry->title(), QStringLiteral("Personal Email"));
QCOMPARE(entry->username(), QStringLiteral("personal@email.com"));
// Verify the folder hierarchy exists
auto socialsGroup = db->rootGroup()->findGroupByPath("/Socials");
QVERIFY(socialsGroup);
QCOMPARE(socialsGroup->name(), QStringLiteral("Socials"));
auto forumsGroup = socialsGroup->findGroupByPath("Forums");
QVERIFY(forumsGroup);
QCOMPARE(forumsGroup->name(), QStringLiteral("Forums"));
auto workGroup = db->rootGroup()->findGroupByPath("/Work");
QVERIFY(workGroup);
QCOMPARE(workGroup->name(), QStringLiteral("Work"));
auto projectsGroup = workGroup->findGroupByPath("Projects");
QVERIFY(projectsGroup);
QCOMPARE(projectsGroup->name(), QStringLiteral("Projects"));
auto clientAGroup = projectsGroup->findGroupByPath("Client A");
QVERIFY(clientAGroup);
QCOMPARE(clientAGroup->name(), QStringLiteral("Client A"));
}
void TestImports::testProtonPass()
{
auto protonPassPath =

View File

@@ -31,6 +31,7 @@ private slots:
void testBitwarden();
void testBitwardenEncrypted();
void testBitwardenPasskey();
void testBitwardenNestedFolders();
void testProtonPass();
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 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
@@ -440,12 +440,12 @@ void TestPasskeys::testGet()
auto response = publicKeyCredential["response"].toObject();
QCOMPARE(response["authenticatorData"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvAFAAAAAA"));
QCOMPARE(response["clientDataJSON"].toString(),
QString("eyJjaGFsbGVuZ2UiOiI5ejM2dlRmUVRMOTVMZjdXblpneXRlN29oR2VGLVhSaUx4a0wtTHVHVTF6b3BSbU1JVUExTFZ3ekdwe"
"UltMWZPQm4xUW5SYTBRSDI3QURBYUpHSHlzUSIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3JpZ2luIjoiaHR0cHM6Ly93ZWJhdX"
"Robi5pbyIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ"));
QString("eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiOXozNnZUZlFUTDk1TGY3V25aZ3l0ZTdvaEdlRi1YUmlMeGtML"
"Ux1R1Uxem9wUm1NSVVBMUxWd3pHcHlJbTFmT0JuMVFuUmEwUUgyN0FEQWFKR0h5c1EiLCJvcmlnaW4iOiJodHRwczovL3dlYm"
"F1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ"));
QCOMPARE(
response["signature"].toString(),
QString("MEUCIHFv0lOOGGloi_XoH5s3QDSs__8yAp9ZTMEjNiacMpOxAiEA04LAfO6TE7j12XNxd3zHQpn4kZN82jQFPntPiPBSD5c"));
QString("MEYCIQCpbDaYJ4b2ofqWBxfRNbH3XCpsyao7Iui5lVuJRU9HIQIhAPl5moNZgJu5zmurkKK_P900Ct6wd3ahVIqCEqTeeRdE"));
auto clientDataJson = response["clientDataJSON"].toString();
auto clientDataByteArray = browserMessageBuilder()->getArrayFromBase64(clientDataJson);

View File

@@ -160,6 +160,7 @@ void TestUrlTools::testIsUrlValidWithLooseComparison()
urls["https://example.*"] = false;
urls["https://*.example.*"] = false;
urls["https://example.c*"] = false;
urls["https://myowndomain:8000"] = true;
QHashIterator<QString, bool> i(urls);
while (i.hasNext()) {

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