diff --git a/CHANGELOG b/CHANGELOG index 059fce8b38..c25533f229 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,32 @@ +2.4.2 (2019-05-31) +========================= + +- Improve resilience against memory attacks - overwrite memory before free [#3020] +- Prevent infinite save loop when location is unavailable [#3026] +- Attempt to fix quitting application when shutdown or logout issued [#3199] +- Support merging database custom data [#3002] +- Fix opening URL's with non-http schemes [#3153] +- Fix data loss due to not reading all database attachments if duplicates exist [#3180] +- Fix entry context menu disabling when using keyboard navigation [#3199] +- Fix behaviors when canceling an entry edit [#3199] +- Fix processing of tray icon click and doubleclick [#3112] +- Update group in preview widget when focused [#3199] +- Prefer DuckDuckGo service over direct icon download (increases resolution) [#2996] +- Remove apply button in application settings [#3019] +- Use winqtdeploy on Windows to correct deployment issues [#3025] +- Don't mark entry edit as modified when attribute selection changes [#3041] +- Use console code page CP_UTF8 on Windows if supported [#3050] +- Snap: Fix locking database with session lock [#3046] +- Snap: Fix theming across Linux distributions [#3057] +- Snap: Use SNAP_USER_COMMON and SNAP_USER_DATA directories [#3131] +- KeeShare: Automatically enable WITH_XC_KEESHARE_SECURE if quazip is found [#3088] +- macOS: Fix toolbar text when in dark mode [#2998] +- macOS: Lock database on switching user [#3097] +- macOS: Fix global Auto-Type when the database is locked [#3138] +- Browser: Close popups when database is locked [#3093] +- Browser: Add tests [#3016] +- Browser: Don't create default group if custom group is enabled [#3127] + 2.4.1 (2019-04-12) ========================= diff --git a/CMakeLists.txt b/CMakeLists.txt index 16d574f01d..689515a399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,9 +20,10 @@ project(KeePassXC) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel." + "Choose the type of build, options are: Debug Release RelWithDebInfo Profile" FORCE) endif() +string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) @@ -40,22 +41,21 @@ option(WITH_ASAN "Enable address sanitizer checks (Linux / macOS only)" OFF) option(WITH_COVERAGE "Use to build with coverage tests (GCC only)." OFF) option(WITH_APP_BUNDLE "Enable Application Bundle for macOS" ON) -set(WITH_XC_ALL OFF CACHE BOOLEAN "Build in all available plugins") +set(WITH_XC_ALL OFF CACHE BOOL "Build in all available plugins") option(WITH_XC_AUTOTYPE "Include Auto-Type." ON) -option(WITH_XC_NETWORKING "Include networking code (e.g. for downlading website icons)." OFF) +option(WITH_XC_NETWORKING "Include networking code (e.g. for downloading website icons)." OFF) option(WITH_XC_BROWSER "Include browser integration with keepassxc-browser." OFF) option(WITH_XC_YUBIKEY "Include YubiKey support." OFF) option(WITH_XC_SSHAGENT "Include SSH agent support." OFF) -option(WITH_XC_KEESHARE "Sharing integration with KeeShare" OFF) -option(WITH_XC_KEESHARE_SECURE "Sharing integration with secured KeeShare containers" OFF) +option(WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)" OFF) option(WITH_XC_UPDATECHECK "Include automatic update checks; disable for controlled distributions" ON) if(APPLE) option(WITH_XC_TOUCHID "Include TouchID support for macOS." OFF) endif() if(WITH_XC_ALL) - # Enable all options + # Enable all options (except update check) set(WITH_XC_AUTOTYPE ON) set(WITH_XC_NETWORKING ON) set(WITH_XC_BROWSER ON) @@ -67,23 +67,21 @@ if(WITH_XC_ALL) endif() endif() -if(WITH_XC_KEESHARE_SECURE) - set(WITH_XC_KEESHARE ON) -endif() - if(WITH_XC_SSHAGENT OR WITH_XC_KEESHARE) set(WITH_XC_CRYPTO_SSH ON) else() set(WITH_XC_CRYPTO_SSH OFF) endif() -if(WITH_XC_UPDATECHECK) - set(WITH_XC_NETWORKING ON) +# Prefer WITH_XC_NETWORKING setting over WITH_XC_UPDATECHECK +if(NOT WITH_XC_NETWORKING AND WITH_XC_UPDATECHECK) + message(STATUS "Disabling WITH_XC_UPDATECHECK because WITH_XC_NETWORKING is disabled") + set(WITH_XC_UPDATECHECK OFF) endif() set(KEEPASSXC_VERSION_MAJOR "2") set(KEEPASSXC_VERSION_MINOR "4") -set(KEEPASSXC_VERSION_PATCH "1") +set(KEEPASSXC_VERSION_PATCH "2") set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}") set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds") @@ -162,11 +160,15 @@ if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") set(IS_32BIT TRUE) endif() -if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_C_COMPILER}" MATCHES "clang$" + OR "${CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS}" MATCHES "__clang__" + OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_COMPILER_IS_CLANG 1) endif() -if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$" + OR "${CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS}" MATCHES "__clang__" + OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_COMPILER_IS_CLANGXX 1) endif() @@ -199,7 +201,7 @@ add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute") add_gcc_compiler_flags("-fvisibility=hidden") add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden") -if(CMAKE_BUILD_TYPE STREQUAL "Debug") +if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") add_gcc_compiler_flags("-Werror") endif() @@ -230,7 +232,6 @@ if(WITH_ASAN) endif() -string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if(CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)") add_gcc_compiler_flags("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") endif() @@ -264,6 +265,11 @@ endif() add_gcc_compiler_cflags("-std=c99") add_gcc_compiler_cxxflags("-std=c++11") +if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9.99) OR + (CMAKE_COMPILER_IS_CLANGXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6.99)) + add_gcc_compiler_cxxflags("-fsized-deallocation") +endif() + if(APPLE) add_gcc_compiler_cxxflags("-stdlib=libc++") endif() @@ -276,7 +282,7 @@ if(MINGW) set(CMAKE_RC_COMPILER_INIT windres) enable_language(RC) set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") - if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + if(NOT (CMAKE_BUILD_TYPE_LOWER STREQUAL "debug" OR CMAKE_BUILD_TYPE_LOWER STREQUAL "relwithdebinfo")) # Enable DEP and ASLR set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") @@ -365,10 +371,17 @@ if(APPLE) set(CMAKE_MACOSX_RPATH TRUE) find_program(MACDEPLOYQT_EXE macdeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH) if(NOT MACDEPLOYQT_EXE) - message(FATAL_ERROR "macdeployqt is required to build in macOS") + message(FATAL_ERROR "macdeployqt is required to build on macOS") else() message(STATUS "Using macdeployqt: ${MACDEPLOYQT_EXE}") endif() +elseif(MINGW) + find_program(WINDEPLOYQT_EXE windeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH) + if(NOT WINDEPLOYQT_EXE) + message(FATAL_ERROR "windeployqt is required to build on Windows") + else() + message(STATUS "Using windeployqt: ${WINDEPLOYQT_EXE}") + endif() endif() # Debian sets the the build type to None for package builds. @@ -380,6 +393,7 @@ find_package(Gcrypt 1.7.0 REQUIRED) find_package(Argon2 REQUIRED) find_package(ZLIB REQUIRED) find_package(QREncode REQUIRED) +find_package(sodium 1.0.12 REQUIRED) set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) @@ -387,20 +401,7 @@ if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0") message(FATAL_ERROR "zlib 1.2.0 or higher is required to use the gzip format") endif() -include_directories(SYSTEM ${ARGON2_INCLUDE_DIR}) - -# Optional -if(WITH_XC_KEESHARE) - set(WITH_XC_KEESHARE_INSECURE ON) - if(WITH_XC_KEESHARE_SECURE) - # ZLIB is needed and already required - find_package(QuaZip REQUIRED) - include_directories(SYSTEM ${QUAZIP_INCLUDE_DIR}) - endif() -else() - set(WITH_XC_KEESHARE_INSECURE OFF) - set(WITH_XC_KEESHARE_SECURE OFF) -endif() +include_directories(SYSTEM ${ARGON2_INCLUDE_DIR} ${sodium_INCLUDE_DIR}) # Optional if(WITH_XC_YUBIKEY) diff --git a/INSTALL.md b/INSTALL.md index d3927536fd..957e6d37df 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -25,7 +25,7 @@ The following libraries are required: * zlib * libmicrohttpd * libxi, libxtst, qtx11extras (optional for auto-type on X11) -* libsodium (>= 1.0.12, optional for KeePassXC-Browser support) +* libsodium (>= 1.0.12) * libargon2 Prepare the Building Environment @@ -97,18 +97,26 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON) -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF) -DWITH_XC_BROWSER=[ON|OFF] Enable/Disable KeePassXC-Browser extension support (default: OFF) - -DWITH_XC_NETWORKING=[ON|OFF] Enable/Disable Networking support (favicon download) (default: OFF) + -DWITH_XC_NETWORKING=[ON|OFF] Enable/Disable Networking support (e.g., favicon downloading) (default: OFF) -DWITH_XC_SSHAGENT=[ON|OFF] Enable/Disable SSHAgent support (default: OFF) - -DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group syncronization extension (default: OFF) -DWITH_XC_TOUCHID=[ON|OFF] (macOS Only) Enable/Disable Touch ID unlock (default:OFF) + -DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group synchronization extension (default: OFF) + -DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare signed containers, requires libquazip5 (default: OFF) -DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (default: OFF) - -DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare secure containers, requires libquazip5 (default: OFF) + + -DWITH_XC_UPDATECHECK=[ON|OFF] Enable/Disable automatic updating checking (requires WITH_XC_NETWORKING) (default: ON) + -DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON) -DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF) -DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF) -DWITH_ASAN=[ON|OFF] Enable/Disable address sanitizer checks (Linux / macOS only) (default: OFF) -DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests (GCC only) (default: OFF) -DWITH_APP_BUNDLE=[ON|OFF] Enable Application Bundle for macOS (default: ON) + + -DKEEPASSXC_BUILD_TYPE=[Snapshot|PreRelease|Release] Set the build type to show/hide stability warnings (default: "Snapshot") + -DKEEPASSXC_DIST_TYPE=[Snap|AppImage|Other] Specify the distribution method (default: "Other") + -DOVERRIDE_VERSION=[X.X.X] Specify a version number when building. Used with snapshot builds (default: "") + -DGIT_HEAD_OVERRIDE=[XXXXXXX] Specify the 7 digit git commit ref for this build. Used with distribution builds (default: "") ``` * If you are on MacOS you must add this parameter to **Cmake**, with the Qt version you have installed
`-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake/` diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index d10791745a..f5287b75b3 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -112,7 +112,7 @@ mark_as_advanced( CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) -if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") +if(NOT CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" diff --git a/cmake/FindQuaZip.cmake b/cmake/FindQuaZip.cmake index 8d3091810d..a387e2f81c 100644 --- a/cmake/FindQuaZip.cmake +++ b/cmake/FindQuaZip.cmake @@ -1,41 +1,24 @@ -# QUAZIP_FOUND - QuaZip library was found -# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir -# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR) -# QUAZIP_LIBRARIES - List of QuaZip libraries -# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers +# QUAZIP_FOUND - QuaZip library was found +# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir +# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR) +# QUAZIP_LIBRARIES - List of QuaZip libraries +# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers -IF(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES) - # in cache already - SET(QUAZIP_FOUND TRUE) -ELSE(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES) - IF(Qt5Core_FOUND) - set(QUAZIP_LIB_VERSION_SUFFIX 5) - ENDIF() - IF(WIN32) - FIND_PATH(QUAZIP_LIBRARY_DIR - WIN32_DEBUG_POSTFIX d - NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll - HINTS "C:/Programme/" "C:/Program Files" - PATH_SUFFIXES QuaZip/lib +if(MINGW) + find_library(QUAZIP_LIBRARIES libquazip5) + find_path(QUAZIP_INCLUDE_DIR quazip.h PATH_SUFFIXES quazip5) + find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h) +else() + find_library(QUAZIP_LIBRARIES + NAMES quazip5 quazip + PATHS /usr/lib /usr/lib64 /usr/local/lib ) - FIND_LIBRARY(QUAZIP_LIBRARIES NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll HINTS ${QUAZIP_LIBRARY_DIR}) - FIND_PATH(QUAZIP_INCLUDE_DIR NAMES quazip.h HINTS ${QUAZIP_LIBRARY_DIR}/../ PATH_SUFFIXES include/quazip5) - FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR NAMES zlib.h) - ELSE(WIN32) - FIND_PACKAGE(PkgConfig) - pkg_check_modules(PC_QUAZIP quazip) - FIND_LIBRARY(QUAZIP_LIBRARIES - WIN32_DEBUG_POSTFIX d - NAMES quazip${QUAZIP_LIB_VERSION_SUFFIX} - HINTS /usr/lib /usr/lib64 + find_path(QUAZIP_INCLUDE_DIR quazip.h + PATHS /usr/include /usr/local/include + PATH_SUFFIXES quazip5 quazip ) - FIND_PATH(QUAZIP_INCLUDE_DIR quazip.h - HINTS /usr/include /usr/local/include - PATH_SUFFIXES quazip${QUAZIP_LIB_VERSION_SUFFIX} - ) - FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR zlib.h HINTS /usr/include /usr/local/include) - ENDIF(WIN32) - INCLUDE(FindPackageHandleStandardArgs) - SET(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR}) - find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS) -ENDIF(QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES) + find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h PATHS /usr/include /usr/local/include) +endif() +include(FindPackageHandleStandardArgs) +set(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR}) +find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS) diff --git a/share/linux/org.keepassxc.KeePassXC.appdata.xml b/share/linux/org.keepassxc.KeePassXC.appdata.xml index 5c616b69a8..644436a8f7 100644 --- a/share/linux/org.keepassxc.KeePassXC.appdata.xml +++ b/share/linux/org.keepassxc.KeePassXC.appdata.xml @@ -50,6 +50,37 @@ + + +
    +
  • Improve resilience against memory attacks - overwrite memory before free [#3020]
  • +
  • Prevent infinite save loop when location is unavailable [#3026]
  • +
  • Attempt to fix quitting application when shutdown or logout issued [#3199]
  • +
  • Support merging database custom data [#3002]
  • +
  • Fix opening URL's with non-http schemes [#3153]
  • +
  • Fix data loss due to not reading all database attachments if duplicates exist [#3180]
  • +
  • Fix entry context menu disabling when using keyboard navigation [#3199]
  • +
  • Fix behaviors when canceling an entry edit [#3199]
  • +
  • Fix processing of tray icon click and doubleclick [#3112]
  • +
  • Update group in preview widget when focused [#3199]
  • +
  • Prefer DuckDuckGo service over direct icon download (increases resolution) [#2996]
  • +
  • Remove apply button in application settings [#3019]
  • +
  • Use winqtdeploy on Windows to correct deployment issues [#3025]
  • +
  • Don't mark entry edit as modified when attribute selection changes [#3041]
  • +
  • Use console code page CP_UTF8 on Windows if supported [#3050]
  • +
  • Snap: Fix locking database with session lock [#3046]
  • +
  • Snap: Fix theming across Linux distributions [#3057]
  • +
  • Snap: Use SNAP_USER_COMMON and SNAP_USER_DATA directories [#3131]
  • +
  • KeeShare: Automatically enable WITH_XC_KEESHARE_SECURE if quazip is found [#3088]
  • +
  • macOS: Fix toolbar text when in dark mode [#2998]
  • +
  • macOS: Lock database on switching user [#3097]
  • +
  • macOS: Fix global Auto-Type when the database is locked [#3138]
  • +
  • Browser: Close popups when database is locked [#3093]
  • +
  • Browser: Add tests [#3016]
  • +
  • Browser: Don't create default group if custom group is enabled [#3127]
  • +
+
+
    diff --git a/share/translations/keepassx_de.ts b/share/translations/keepassx_de.ts index 954a16a2e7..fa0746f73d 100644 --- a/share/translations/keepassx_de.ts +++ b/share/translations/keepassx_de.ts @@ -1531,7 +1531,7 @@ Möchten Sie Ihre Änderungen zusammenführen? Do you really want to delete %n entry(s) for good? - Sollen tatsächlich %1 Einträge gelöscht werden?Sollen tatsächlich %1 Einträge gelöscht werden? + Sollen tatsächlich %n Einträge gelöscht werden?Sollen tatsächlich %n Einträge gelöscht werden? Delete entry(s)? diff --git a/share/translations/keepassx_en.ts b/share/translations/keepassx_en.ts index f49602bb5b..3579db2005 100644 --- a/share/translations/keepassx_en.ts +++ b/share/translations/keepassx_en.ts @@ -326,8 +326,8 @@ Privacy - Use DuckDuckGo as fallback for downloading website icons - Use DuckDuckGo as fallback for downloading website icons + Use DuckDuckGo service to download website icons + @@ -631,6 +631,14 @@ Please select the correct database for saving credentials. &Brave + + Returns expired credentials. String [expired] is added to the title. + + + + &Allow returning expired credentials. + + BrowserService @@ -2244,10 +2252,6 @@ Supported extensions are: %1. Custom icon successfully downloaded Custom icon successfully downloaded - - Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security - Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security - Select Image(s) Select Image(s) @@ -2284,6 +2288,10 @@ Supported extensions are: %1. This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it? + + You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security + + EditWidgetProperties @@ -3750,6 +3758,14 @@ Expect some bugs and minor issues, this version is not meant for production use. Adding missing icon %1 Adding missing icon %1 + + Removed custom data %1 [%2] + + + + Adding custom data %1 [%2] + + NewDatabaseWizard diff --git a/share/translations/keepassx_es.ts b/share/translations/keepassx_es.ts index 4615ba30e9..ddef34d3a8 100644 --- a/share/translations/keepassx_es.ts +++ b/share/translations/keepassx_es.ts @@ -200,27 +200,27 @@ Auto-Type - Auto-Escritura + Autoescritura Use entry title to match windows for global Auto-Type - Use título de entrada para acertar ventanas en Auto-Escritura global. + Usar título de la entrada para emparejar ventanas en autoescritura global Use entry URL to match windows for global Auto-Type - Use URL para acertar ventanas en Auto-Escritura global + Usar URL de la entrada para emparejar ventanas en autoescritura global Always ask before performing Auto-Type - Siempre preguntar antes de hacer Auto-Escritura + Siempre preguntar antes de hacer autoescritura Global Auto-Type shortcut - Atajo global de Auto-Escritura + Atajo global de autoescritura Auto-Type typing delay - Escribiendo retardo de la Auto-Escritura + Escribiendo retardo de la autoescritura ms @@ -229,7 +229,7 @@ Auto-Type start delay - Iniciar retardo de Auto-Escritura + Iniciar retardo de autoescritura Check for updates at application startup @@ -293,7 +293,7 @@ Re-lock previously locked database after performing Auto-Type - Volver a bloquear la base de datos tras realizar una Auto-Escritura + Volver a bloquear la base de datos tras realizar una autoescritura Don't require password repeat when it is visible @@ -332,27 +332,27 @@ Auto-Type - KeePassXC - Auto-Escritura - KeePassXC + Autoescritura - KeePassXC Auto-Type - Auto-Escritura + Autoescritura The Syntax of your Auto-Type statement is incorrect! - ¡La sintaxis de la declaración de su Auto-Escritura es incorrecta! + ¡La sintaxis de la declaración de su autoescritura es incorrecta! This Auto-Type command contains a very long delay. Do you really want to proceed? - Este comando de Auto-Escritura contiene un retraso muy largo. ¿Realmente desea continuar? + Este comando de autoescritura contiene un retraso muy largo. ¿Realmente desea continuar? This Auto-Type command contains very slow key presses. Do you really want to proceed? - Este comando de Auto-Escritura contiene pulsaciones de teclas muy lentas. ¿Realmente desea continuar? + Este comando de autoescritura contiene pulsaciones de teclas muy lentas. ¿Realmente desea continuar? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? - Este comando de Auto-Escritura contiene argumentos que se repiten muy a menudo. ¿Realmente desea continuar? + Este comando de autoescritura contiene argumentos que se repiten muy a menudo. ¿Realmente desea continuar? @@ -393,11 +393,11 @@ AutoTypeSelectDialog Auto-Type - KeePassXC - Auto-Escritura - KeePassXC + Autoescritura - KeePassXC Select entry to Auto-Type: - Seleccionar entrada para Auto-Escritura: + Seleccionar entrada para autoescritura: @@ -884,7 +884,7 @@ Es necesario para mantener sus conexiones presentes del navegador. DatabaseOpenDialog Unlock Database - KeePassXC - Desbloquear Base de Datos - KeePassXC + Desbloquear base de datos - KeePassXC @@ -1259,7 +1259,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!< DatabaseSettingsWidgetGeneral Database Meta Data - Metadatos de la Base de Datos + Metadatos de la base de datos Database name: @@ -1295,7 +1295,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!< Additional Database Settings - Configuraciones Adicionales de la Base de Datos + Configuraciones adicionales de la base de datos Enable &compression (recommended) @@ -1438,12 +1438,12 @@ Esto es definitivamente un error, por favor repórtelo a los desarrolladores. New Database - Nueva Base de datos + Nueva base de datos %1 [New Database] Database tab name modifier - %1 [Nueva Base de Datos] + %1 [Nueva base de datos] %1 [Locked] @@ -1549,7 +1549,7 @@ Do you want to merge your changes? Lock Database? - ¿Bloquear la Base de datos? + ¿Bloquear la base de datos? You are editing an entry. Discard changes and lock anyway? @@ -1654,7 +1654,7 @@ Disable safe saves and try again? Auto-Type - Auto-Escritura + Autoescritura Properties @@ -1800,15 +1800,15 @@ Disable safe saves and try again? EditEntryWidgetAutoType Enable Auto-Type for this entry - Activar Auto-Escritura para esta entrada + Activar autoescritura para esta entrada Inherit default Auto-Type sequence from the &group - Heredar la secuencia de Auto-Escritura por defecto del &grupo + Heredar la secuencia de autoescritura por defecto del &grupo &Use custom Auto-Type sequence: - &Usar secuencia de Auto-Escritura personalizada: + &Usar secuencia de autoescritura personalizada: Window Associations @@ -2111,15 +2111,15 @@ Disable safe saves and try again? Auto-Type - Auto-Escritura + Autoescritura &Use default Auto-Type sequence of parent group - &Usar por defecto la secuencia de Auto-Escritura del grupo padre + &Usar por defecto la secuencia de autoescritura del grupo padre Set default Auto-Type se&quence - Seleccionar se&cuencia de Auto-Escritura por defecto + Seleccionar se&cuencia de autoescritura por defecto @@ -2932,7 +2932,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Auto-type association window or sequence missing - Falta de secuencia o ventana de asociación de Auto-Escritura + Falta de secuencia o ventana de asociación de autoescritura Invalid bool value @@ -3522,7 +3522,7 @@ Le recomendamos que utilice la AppImage disponible en nuestra página de descarg Perform &Auto-Type - Realizar &Auto-Escritura + Realizar &autoescritura Open &URL @@ -4857,7 +4857,7 @@ Comandos disponibles: Database password: - Contraseña de la Base de Datos: + Contraseña de la base de datos: Cannot create new group diff --git a/share/translations/keepassx_eu.ts b/share/translations/keepassx_eu.ts index 93fca6455c..a556f17d5d 100644 --- a/share/translations/keepassx_eu.ts +++ b/share/translations/keepassx_eu.ts @@ -37,30 +37,6 @@ Copy to clipboard Kopiatu arbelera - - Revision: %1 - Berrikuspena: %1 - - - Distribution: %1 - Banaketa: %1 - - - Libraries: - Liburutegiak: - - - Operating system: %1 -CPU architecture: %2 -Kernel: %3 %4 - Sistema eragilea: %1 -CPU arkitektura: %2 -Kernel: %3 %4 - - - Enabled extensions: - Gaitutako hedapenak: - Project Maintainers: @@ -69,50 +45,6 @@ Kernel: %3 %4 Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. - - Version %1 - - - - Build Type: %1 - - - - Auto-Type - - - - Browser Integration - - - - SSH Agent - - - - YubiKey - - - - TouchID - - - - None - - - - KeeShare (signed and unsigned sharing) - - - - KeeShare (only signed sharing) - - - - KeeShare (only unsigned sharing) - - AgentSettingsWidget @@ -145,23 +77,23 @@ Kernel: %3 %4 Icon only - + Ikonoa bakarrik Text only - + Testua bakarrik Text beside icon - + Testua ikonoaren ondoan Text under icon - + Testua ikonoaren azpian Follow style - + Eutsi estiloari @@ -654,14 +586,6 @@ Please select the correct database for saving credentials. Select custom proxy location - - We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - - - - KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. - - &Tor Browser @@ -683,6 +607,18 @@ Please select the correct database for saving credentials. An extra HTTP Basic Auth setting + + Due to Snap sandboxing, you must run a script to enable browser integration.<br />You can obtain this script from %1 + + + + Please see special instructions for browser extension use below + + + + KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3 + + BrowserService @@ -752,9 +688,19 @@ Moved %2 keys to custom data. - Legacy browser integration settings have been detected. -Do you want to upgrade the settings to the latest standard? -This is necessary to maintain compatibility with the browser plugin. + KeePassXC: Create a new group + + + + A request for creating a new group "%1" has been received. +Do you want to create this group? + + + + + Your KeePassXC-Browser settings need to be moved into the database settings. +This is necessary to maintain your current browser connections. +Would you like to migrate your existing settings now? @@ -878,15 +824,15 @@ This is necessary to maintain compatibility with the browser plugin. %1, %2, %3 file info: bytes, rows, columns - + %1, %2, %3 %n byte(s) - + byte %n%n byte %n row(s) - + lerro %n%n lerro @@ -894,34 +840,38 @@ This is necessary to maintain compatibility with the browser plugin. Root Root group name - + Erroa File %1 does not exist. - + %1 fitxategia ez da existitzen. Unable to open file %1. - + Ezin izan da %1 fitxategia ireki. Error while reading the database: %1 - + Errorea gertatu da datu-basea irakurtzean: %1 Could not save, database has no file name. - + Ezin da gorde, datu-baseak ez dauka izenik. File cannot be written as it is opened in read-only mode. - + Ezin da fitxategian idatzi, irakurtzeko moduan ireki baita. + + + Key not transformed. This is a bug, please report it to the developers! + Eraldatu gabeko gakoa. Hau akats bat da, mesedez jakinarazi garatzaileei! DatabaseOpenDialog Unlock Database - KeePassXC - + Desblokeatu datu-basea - KeePassXC @@ -1003,7 +953,7 @@ Please consider generating a new key file. DatabaseSettingsDialog Advanced Settings - + Aukera aurreratuak General @@ -1015,15 +965,15 @@ Please consider generating a new key file. Master Key - + Gako nagusia Encryption Settings - + Zifraketa ezarpenak Browser Integration - + Integrazioa nabigatzaileekin @@ -1046,7 +996,7 @@ Please consider generating a new key file. Stored keys - + Gordetako gakoak Remove @@ -1054,7 +1004,7 @@ Please consider generating a new key file. Delete the selected key? - + Ezabatu aukeratutako gakoa? Do you really want to delete the selected key? @@ -1063,11 +1013,11 @@ This may prevent connection to the browser plugin. Key - + Gakoa Value - + Balioa Enable Browser Integration to access these settings. @@ -1283,7 +1233,7 @@ If you keep this number, your database may be too easy to crack! DatabaseSettingsWidgetGeneral Database Meta Data - + Datu-basearen meta-datuak Database name: @@ -1299,15 +1249,15 @@ If you keep this number, your database may be too easy to crack! History Settings - + Historiaren ezarpenak Max. history items: - + Historiaren gehienezko sarrerak: Max. history size: - + Historiaren gehienezko tamaina: MiB @@ -1315,30 +1265,30 @@ If you keep this number, your database may be too easy to crack! Use recycle bin - + Erabili zakarrontzia Additional Database Settings - + Datu-basearen ezarpen gehigarriak Enable &compression (recommended) - + Erabili &konpresioa (gomendatua) DatabaseSettingsWidgetKeeShare Sharing - + Partekatu Breadcrumb - + Laguntza Type - + Mota Path @@ -1346,60 +1296,62 @@ If you keep this number, your database may be too easy to crack! Last Signer - + Azken sinatzailea Certificates - + Ziurtagiriak > Breadcrumb separator - + > DatabaseSettingsWidgetMasterKey Add additional protection... - + Gehitu babes gehigarria... No encryption key added - + Ez da zifraketa gakorik gehitu You must add at least one encryption key to secure your database! - + Gutxienez zifraketa gako bat gehitu behar duzu datu-basea babesteko! No password set - + Pasahitza ezarri gabe WARNING! You have not set a password. Using a database without a password is strongly discouraged! Are you sure you want to continue without a password? - + KONTUZ! Ez duzu pasahitzik ezarri. Pasahitzik gabeko datu-basea erabiltzea ez da batere gomendagarria! + +Ziur zaude pasahitzik gabe jarraitu nahi duzula? Unknown error - + Errore ezezaguna Failed to change master key - + Ezin izan da gako nagusia aldatu DatabaseSettingsWidgetMetaDataSimple Database Name: - + Datu-basearen izena: Description: - + Deskribapena: @@ -1438,11 +1390,11 @@ Are you sure you want to continue without a password? Writing the CSV file failed. - + Ezin izan da CSV fitxategia idatzi. Database creation error - + Ezin izan da datu-basea sortu The created database has no key or KDF, refusing to save it. @@ -1451,30 +1403,30 @@ This is definitely a bug, please report it to the developers. The database file does not exist or is not accessible. - + Datu-basea ez da existitzen edo ez dago eskuragarri Select CSV file - + Aukeratu CSV fitxategia New Database - + Datu-base berria %1 [New Database] Database tab name modifier - + %1 [Datu-base berria] %1 [Locked] Database tab name modifier - + %1 [Blokeatuta] %1 [Read-only] Database tab name modifier - + %1 [Irakurtzeko soilik] @@ -1485,11 +1437,11 @@ This is definitely a bug, please report it to the developers. Do you really want to delete the entry "%1" for good? - + Ziur zaude "%1" sarrera betirako ezabatu nahi duzula? Do you really want to move entry "%1" to the recycle bin? - + Ziur zaude "%1" sarrera zaborrontzira mugitu nahi duzula? Do you really want to move %n entry(s) to the recycle bin? @@ -1501,19 +1453,19 @@ This is definitely a bug, please report it to the developers. Do you really want to execute the following command?<br><br>%1<br> - + Ziur zaude ondorengo agindua exekutatu nahi duzula?<br><br>%1<br> Remember my choice - + Gogoratu nire aukera Do you really want to delete the group "%1" for good? - + Ziur zaude "%1" taldea betirako ezabatu nahi duzula? No current database. - + Une honetan ez dago datu-baserik. No source database, nothing to do. @@ -1529,62 +1481,65 @@ This is definitely a bug, please report it to the developers. File has changed - + Fitxategia aldatu da The database file has changed. Do you want to load the changes? - + Datu-basea aldatu da. Aldaketak kargatu nahi dituzu? Merge Request - + Bateratze eskaera The database file has changed and you have unsaved changes. Do you want to merge your changes? - + Datu-basearen fitxategia aldatu da, eta gorde gabeko aldaketak dituzu. +Aldaketak bateratu nahi dituzu? Empty recycle bin? - + Zakarrontzia hustu nahi duzu? Are you sure you want to permanently delete everything from your recycle bin? - + Ziur zaude zakarrontzia betiko hustu nahi duzula? Do you really want to delete %n entry(s) for good? - + Ziur zaude %n sarrera betiko ezabatu nahi dituzula?Ziur zaude %n sarrera betiko ezabatu nahi dituzula? Delete entry(s)? - + Sarrera ezabatu nahi duzu?Sarrerak ezabatu nahi dituzu? Move entry(s) to recycle bin? - + Mugitu nahi dituzu sarrerak zakarrontzira?Mugitu nahi dituzu sarrerak zakarrontzira? File opened in read only mode. - + Fitxategia irakurtzeko bakarrik ireki da. Lock Database? - + Blokeatu nahi duzu datu-basea? You are editing an entry. Discard changes and lock anyway? - + Sarrera bat editatzen ari zara. Aldaketak baztertu, eta datu-basea blokeatu nahi duzu? "%1" was modified. Save changes? - + "%1" aldatu da. +Aldaketak gorde nahi dituzu? Database was modified. Save changes? - + Datu-basea aldatu da. +Aldaketak gorde nahi dituzu? Save changes? @@ -1607,7 +1562,7 @@ Disable safe saves and try again? Writing the database failed. %1 - + Ezin izan da datu-basean idatzi. %1 Passwords @@ -1631,15 +1586,15 @@ Disable safe saves and try again? Delete group - + Ezabatu taldea Move group to recycle bin? - + Mugitu taldea zakarrontzira? Do you really want to move the group "%1" to the recycle bin? - + Ziur zaude "%1" taldea zakarrontzira mugitu nahi duzula? Successfully merged the database files. @@ -1649,6 +1604,10 @@ Disable safe saves and try again? Database was not modified by merge operation. + + Shared group... + + EditEntryWidget @@ -1690,15 +1649,15 @@ Disable safe saves and try again? Select private key - + Aukeratu gako pribatua File too large to be a private key - + Fitxategia handiegia da gako pribatua izateko Failed to open private key - + Ezin izan da gako pribatua ireki Entry history @@ -1722,7 +1681,7 @@ Disable safe saves and try again? Are you sure you want to remove this attribute? - + Ziur zaude ezaugarri hau ezabatu nahi duzula? Tomorrow @@ -1738,19 +1697,19 @@ Disable safe saves and try again? Apply generated password? - + Sortutako pasahitza erabili nahi duzu? Do you want to apply the generated password to this entry? - + Sortutako pasahitza sarrera honetan erabili nahi duzu? Entry updated successfully. - + Sarrera behar bezala eguneratu da. Entry has unsaved changes - + Sarrerak gorde gabeko aldaketak ditu New attribute %1 @@ -1762,18 +1721,18 @@ Disable safe saves and try again? %n year(s) - + %n urte%n urte Confirm Removal - + Berretsi ezabatzea EditEntryWidgetAdvanced Additional attributes - + Ezaugarri gehigarriak Add @@ -1797,15 +1756,15 @@ Disable safe saves and try again? Attachments - + Eranskinak Foreground Color: - + Lehen planoaren kolorea: Background Color: - + Atzeko planoaren kolorea: @@ -1917,7 +1876,7 @@ Disable safe saves and try again? Fingerprint - + Hatz-marka Remove key from agent when database is closed/locked @@ -2020,7 +1979,7 @@ Disable safe saves and try again? Type: - + Mota: Path: @@ -2086,6 +2045,22 @@ Disable safe saves and try again? Select import/export file + + Clear + Garbitu + + + The export container %1 is already referenced. + + + + The import container %1 is already imported. + + + + The container %1 imported and export by different groups. + + EditGroupWidgetMain @@ -2122,11 +2097,11 @@ Disable safe saves and try again? EditWidgetIcons &Use default icon - + %Erabili lehenetsitako ikonoa Use custo&m icon - + Erabili ikono pertsonalizatua Add custom icon @@ -2154,7 +2129,7 @@ Disable safe saves and try again? Custom icon already exists - + Dagoeneko badago pertsonalizatutako ikonoa Confirm Delete @@ -2162,7 +2137,7 @@ Disable safe saves and try again? Custom icon successfully downloaded - + Pertsonalizatutako ikonoa behar bezala deskargatu da Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security @@ -2170,7 +2145,7 @@ Disable safe saves and try again? Select Image(s) - + Aukeratu irudia(k) Successfully loaded %1 of %n icon(s) @@ -2178,15 +2153,15 @@ Disable safe saves and try again? No icons were loaded - + Ez da ikonorik kargatu %n icon(s) already exist in the database - + %n ikonoa(k) dagoeneko badago/badaude datu-basean%n ikonoa(k) dagoeneko badago/badaude datu-basean The following icon(s) failed: - + Ikono hauek huts egin dute:Ikono hauek huts egin dute: This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it? @@ -2213,7 +2188,7 @@ Disable safe saves and try again? Plugin Data - + Gehigarrien datuak Remove @@ -2221,27 +2196,28 @@ Disable safe saves and try again? Delete plugin data? - + Ezabatu gehigarrien datuak? Do you really want to delete the selected plugin data? This may cause the affected plugins to malfunction. - + Ziur zaude aukeratutako gehigarriaren datuak ezabatu nahi dituzula? +Honek gehigarriek behar bezala ez funtzionatzea eragin dezake. Key - + Gakoa Value - + Balioa Entry %1 - Clone - + %1 - Klonatu @@ -2292,7 +2268,7 @@ This may cause the affected plugins to malfunction. Unable to create directory: %1 - + Ezin izan da honako direktorioa sortu: %1 Are you sure you want to overwrite the existing file "%1" with the attachment? @@ -2300,31 +2276,31 @@ This may cause the affected plugins to malfunction. Confirm overwrite - + Berretsi gainidaztea Unable to save attachments: %1 - + Ezin izan dira eranskin hauek gorde: %1 Unable to open attachment: %1 - + Ezin izan dira eranskin hauek ireki: %1 Unable to open attachments: %1 - + Ezin izan dira eranskin hauek ireki: %1 Confirm remove - + Berretsi ezabatzea Unable to open file(s): %1 - + Ezin izan dira fitxategi hauek ireki: %1Ezin izan dira fitxategi hauek ireki: %1 @@ -2406,7 +2382,7 @@ This may cause the affected plugins to malfunction. Attachments - + Eranskinak Yes @@ -2449,11 +2425,11 @@ This may cause the affected plugins to malfunction. Attributes - + Ezaugarriak Attachments - + Eranskinak Notes @@ -2513,31 +2489,31 @@ This may cause the affected plugins to malfunction. EntryView Customize View - + Pertsonalizatu bista Hide Usernames - + Ezkutatu erabiltzaile-izenak Hide Passwords - + Ezkutatu pasahitzak Fit to window - + Egokitu leihora Fit to contents - + Egokitu edukira Reset to defaults - + Berrezarri lehenetsitako ezarpenak Attachments (icon) - + Eranskinak (ikonoa) @@ -2549,22 +2525,14 @@ This may cause the affected plugins to malfunction. [empty] group has no children - - - - - GroupModel - - %1 - Template for name without annotation - + [hutsik] HostInstaller KeePassXC: Cannot save file! - + KeePassXC: Ezin izan da fitxategia gorde! Cannot save the native messaging script file. @@ -3024,7 +2992,7 @@ Line %2, column %3 Root - + Erroa Unable to calculate master key @@ -3149,6 +3117,22 @@ Line %2, column %3 Synchronize with + + Disabled share %1 + + + + Import from share %1 + + + + Export to share %1 + + + + Synchronize with share %1 + + KeyComponentWidget @@ -3256,7 +3240,7 @@ Message: %2 &Recent databases - + &Azken datu-baseak &Help @@ -3284,7 +3268,7 @@ Message: %2 &Open database... - + &Ireki datu-basea... &Save database @@ -3308,7 +3292,7 @@ Message: %2 Sa&ve database as... - + Go&rde datu-basea honela... Database settings @@ -3340,7 +3324,7 @@ Message: %2 &Lock databases - + &Blokeatu datu-baseak &Title @@ -3348,7 +3332,7 @@ Message: %2 Copy title to clipboard - + Kopiatu izenburua arbelera &URL @@ -3356,7 +3340,7 @@ Message: %2 Copy URL to clipboard - + Kopiatu URL-a arbelera &Notes @@ -3364,11 +3348,11 @@ Message: %2 Copy notes to clipboard - + Kopiatu oharrak arbelera &Export to CSV file... - + &Esportatu CSV fitxategira... Set up TOTP... @@ -3427,11 +3411,11 @@ We recommend you use the AppImage available on our downloads page. &Import - + &Inportatu Copy att&ribute... - + Kopiatu eza&ugarria... TOTP... @@ -3439,11 +3423,11 @@ We recommend you use the AppImage available on our downloads page. &New database... - + &Datu-base berria... Create a new database - + Sortu datu-base berria &Merge from database... @@ -3455,39 +3439,39 @@ We recommend you use the AppImage available on our downloads page. &New entry - + &Sarrera berria Add a new entry - + Gehitu sarrera berria &Edit entry - + &Editatu sarrera View or edit entry - + Ikusi edo editatu sarrera &New group - + &Talde berria Add a new group - + Gehitu talde berria Change master &key... - + Aldatu &gako nagusia... &Database settings... - + &Datu-basearen ezarpenak... Copy &password - + Kopiatu &pasahitza Perform &Auto-Type @@ -3495,23 +3479,23 @@ We recommend you use the AppImage available on our downloads page. Open &URL - + Ireki &URLa KeePass 1 database... - + KeePass 1 datu-basea... Import a KeePass 1 database - + Inportatu KeePass 1 datu-basea CSV file... - + CSV fitxategia... Import a CSV file - + Inportatu CSV fittxategia Show TOTP... @@ -3523,11 +3507,11 @@ We recommend you use the AppImage available on our downloads page. Check for Updates... - + Egiaztatu eguneraketarik dagoen Share entry - + Partekatu sarrera NOTE: You are using a pre-release version of KeePassXC! @@ -3610,12 +3594,12 @@ Expect some bugs and minor issues, this version is not meant for production use. NewDatabaseWizard Create a new KeePassXC database... - + Sortu KeePassXC datu-basea... Root Root group - + Erroa @@ -3626,52 +3610,52 @@ Expect some bugs and minor issues, this version is not meant for production use. En&cryption Settings - + Zi&fraketa ezarpenak Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + Hemen datu-basearen zifraketa ezarpenak aldatu ditzakezu. Ez kezkatu, geroago ere aldatu ditzakezu datu-basearen ezarpenetan. Advanced Settings - + Ezarpen aurreratuak Simple Settings - + Oinarrizko ezarpenak NewDatabaseWizardPageEncryption Encryption Settings - + Zifraketa ezarpenak Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + Hemen datu-basearen zifraketa ezarpenak aldatu ditzakezu. Ez kezkatu, ondoren ere aldatu ditzakezu datu-basearen ezarpenetan. NewDatabaseWizardPageMasterKey Database Master Key - + Datu-basearen gako nagusia A master key known only to you protects your database. - + Zuk bakarrik ezagutzen duzun gako nagusiak zure datu-basea babesten du. NewDatabaseWizardPageMetaData General Database Information - + Datu-basearen informazio orokorra Please fill in the display name and an optional description for your new database: - + Mesedez jarri izena eta deskribapena (aukeran) datu-base berrirako: @@ -3781,7 +3765,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Confirm password: - + Berretsi pasahitza: Password @@ -3789,19 +3773,15 @@ Expect some bugs and minor issues, this version is not meant for production use. <p>A password is the primary method for securing your database.</p><p>Good passwords are long and unique. KeePassXC can generate one for you.</p> - - - - Password cannot be empty. - + <p>Pasahitza da datu-basea babesteko metodo nagusia.</p><p>Pasahitz onak luzeak eta bakarrak dira. KeePassXC-ek bat sor dezake zuretzat.</p> Passwords do not match. - + Pasahitzak ez datoz bat. Generate master password - + Sortu pasahitz nagusia @@ -4043,14 +4023,14 @@ Expect some bugs and minor issues, this version is not meant for production use. QFileDialog Select - + Aukeratu QMessageBox Overwrite - + Gainditzi Delete @@ -4058,11 +4038,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Move - + Mugitu Empty - + Hutsik Remove @@ -4078,14 +4058,14 @@ Expect some bugs and minor issues, this version is not meant for production use. Merge - + Bateratu QObject Database not opened - + Datubasea ez dago irekita Database hash not available @@ -4129,7 +4109,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Unknown error - + Errore ezezaguna Add a new entry to a database. @@ -4352,7 +4332,7 @@ Available commands: Browser Integration - + Integrazioa nabigatzaileekin YubiKey[%1] Challenge Response - Slot %2 - %3 @@ -4578,11 +4558,11 @@ Available commands: File %1 does not exist. - + %1 fitxategia ez da existitzen. Unable to open file %1. - + Ezin izan da %1 fitxategia ireki. Error while reading the database: @@ -4821,6 +4801,10 @@ Available commands: Database password: + + Cannot create new group + + QtIOCompressor @@ -4978,31 +4962,31 @@ Available commands: SettingsWidgetKeeShare Active - + Martxan Allow export - + Baimendu esportatzea Allow import - + Baimendu inportatzea Own certificate - + Ziurtagari propioa Fingerprint: - + Hatz-marka: Certificate: - + Ziurtagiria: Signer - + Sinatzailea Key: @@ -5018,23 +5002,23 @@ Available commands: Export - + Esportatu Imported certificates - + Inportatutako ziurtagariak Trust - + Fidatu Ask - + Galdetu Untrust - + Mesfidatu Remove @@ -5046,27 +5030,27 @@ Available commands: Status - + Egoera Fingerprint - + Hatz-marka Certificate - + Ziurtagiria Trusted - + Fidagarria Untrusted - + Ez-fidagarria Unknown - + Ezezaguna key.share @@ -5087,16 +5071,15 @@ Available commands: Exporting changed certificate - + Aldatutako ziurtagiria esportatzen The exported certificate is not the same as the one in use. Do you want to export the current certificate? - %1.%2 - Template for KeeShare key file - + Signer: + Sinatzailea: @@ -5113,10 +5096,6 @@ Available commands: Import from container with certificate - - Do you want to trust %1 with the fingerprint of %2 from %3 - - Not this time @@ -5193,14 +5172,6 @@ Available commands: Could not write export container (%1) - - Could not embed signature (%1) - - - - Could not embed database (%1) - - Overwriting unsigned share container is not supported - export prevented @@ -5225,6 +5196,34 @@ Available commands: Export to %1 + + Do you want to trust %1 with the fingerprint of %2 from %3? + + + + Multiple import source path to %1 in %2 + + + + Conflicting export target path %1 in %2 + + + + Could not embed signature: Could not open file to write (%1) + + + + Could not embed signature: Could not write file (%1) + + + + Could not embed database: Could not open file to write (%1) + + + + Could not embed database: Could not write file (%1) + + TotpDialog @@ -5321,11 +5320,11 @@ Available commands: UpdateCheckDialog Checking for updates - + Eguneraketak bilatzen Checking for updates... - + Eguneraketak bilatzen... Close @@ -5333,39 +5332,39 @@ Available commands: Update Error! - + Eguneraketa errorea! An error occurred in retrieving update information. - + Errore bat gertatu da eguneraketa informazioa eskuratzean. Please try again later. - + Mesedez, saiatu berriro geroago. Software Update - + Software eguneraketa A new version of KeePassXC is available! - + KeePassXC-ren bertsio berri bat dago eskuragarri! KeePassXC %1 is now available — you have %2. - + KeePassXC %1 eskuragarri dago — orain %2 daukazu. Download it at keepassxc.org - + Deskargatu keepassxc.org webgunetik You're up-to-date! - + Egunean zaude! KeePassXC %1 is currently the newest version available - + KeePassXC %1 eskuragarri dagoen azken bertsioa da @@ -5380,7 +5379,7 @@ Available commands: Open existing database - + Ireki datu-basea Import from KeePass 1 @@ -5392,11 +5391,11 @@ Available commands: Recent databases - + Azken datu-baseak Welcome to KeePassXC %1 - + Ongi etorri KeePassXC %1 -era diff --git a/share/translations/keepassx_fr.ts b/share/translations/keepassx_fr.ts index bf305ac4bb..7d6e726c85 100644 --- a/share/translations/keepassx_fr.ts +++ b/share/translations/keepassx_fr.ts @@ -611,7 +611,7 @@ Veuillez sélectionner la base de donnée souhaitée pour enregistrer les identi Due to Snap sandboxing, you must run a script to enable browser integration.<br />You can obtain this script from %1 - + À cause du mécanisme de sandboxing Snap, vous devez lancer un script pour activer l'intégration du navigateur.<br />Vous pouvez obtenir ce script depuis %1 Please see special instructions for browser extension use below @@ -710,7 +710,7 @@ Voulez-vous créer ce groupe ? Your KeePassXC-Browser settings need to be moved into the database settings. This is necessary to maintain your current browser connections. Would you like to migrate your existing settings now? - + Vos réglages pour KeePassXC-Browser doivent être intégrés dans les réglages de la base de données. Ceci est nécessaire pour maintenir vos connexions actuelles avec le navigateur ouvertes. Souhaitez-vous effectuer la migration de vos réglages maintenant ? @@ -874,7 +874,7 @@ Would you like to migrate your existing settings now? Key not transformed. This is a bug, please report it to the developers! - + La clé n'a pas été transformée. Ceci est un bogue, pouvez-vous s'il vous plaît le signaler aux développeurs ? @@ -1071,7 +1071,7 @@ Cela peut empêcher la connexion avec l'extension de navigateur. Do you really want forget all site-specific settings on every entry? Permissions to access entries will be revoked. - + Êtes-vous sûr de vouloir effacer les préférences de site pour toutes les entrées ? Les permissions d'accès aux entrées seront révoquées. Removing stored permissions… @@ -1099,12 +1099,13 @@ Permissions to access entries will be revoked. Move KeePassHTTP attributes to custom data - + Déplacer les attributs KeePassHTTP vers les données personnalisées Do you really want to move all legacy browser integration data to the latest standard? This is necessary to maintain compatibility with the browser plugin. - + Voulez-vous convertir toutes les anciennes données d'intégration au navigateur en version plus récente ? +Ceci est nécessaire pour assurer la compatibilité de l'extension. @@ -1528,7 +1529,7 @@ Voulez-vous fusionner vos changements ? Do you really want to delete %n entry(s) for good? - Voulez-vous vraiment supprimer définitivement %1 entrée ?Voulez-vous vraiment supprimer définitivement %1 entrées ? + Voulez-vous vraiment supprimer définitivement %n entrée ?Voulez-vous vraiment supprimer définitivement %n entrées ? Delete entry(s)? @@ -3463,7 +3464,7 @@ Nous recommandons l'utilisation de l'AppImage disponible sur notre pag &New database... - &Ńouvelle base de données... + &Nouvelle base de données... Create a new database @@ -4513,7 +4514,7 @@ Commandes disponibles : Multi-word extra bits %1 - + Octets additionnels mots multiples %1 Type: Bruteforce @@ -4561,7 +4562,7 @@ Commandes disponibles : Type: Dict+Leet(Rep) - + Type : Dictionnaire + Leet (rep) Type: User Words(Rep) @@ -4569,7 +4570,7 @@ Commandes disponibles : Type: User+Leet(Rep) - + Type : Utilisateur + Leet (rep) Type: Repeated(Rep) @@ -4597,7 +4598,7 @@ Commandes disponibles : *** Password length (%1) != sum of length of parts (%2) *** - + *** Longueur du mot de passe (%1) != longueurs additionnées des morceaux (%2) *** Failed to load key file %1: %2 @@ -4933,11 +4934,11 @@ Commandes disponibles : Search terms are as follows: [modifiers][field:]["]term["] - + Les termes de recherche sont construits comme suit : [modificateurs][champ:]["]terme["] Every search term must match (ie, logical AND) - + Tous les termes doivent correspondre (ET logique) Modifiers @@ -4961,15 +4962,15 @@ Commandes disponibles : Term Wildcards - + Caractères spéciaux match anything - + correspond à n'importe quel caractère match one - + correspond à un seul caractère logical OR @@ -5105,7 +5106,7 @@ Commandes disponibles : key.share Filetype for KeeShare key - + cle.share KeeShare key file @@ -5121,15 +5122,15 @@ Commandes disponibles : Exporting changed certificate - + Exportation des certificats modifiés The exported certificate is not the same as the one in use. Do you want to export the current certificate? - + Le certificat exporté est différent de celui en cours d'utilisation. Voulez-vous exporter le certificat actuel ? Signer: - + Signataire : @@ -5140,7 +5141,7 @@ Commandes disponibles : We cannot verify the source of the shared container because it is not signed. Do you really want to import from %1? - + Nous ne pouvons vérifier la source du conteneur partagé car celui-ci n'est pas signé. Êtes-vous sûr de vouloir importer depuis %1 ? Import from container with certificate @@ -5176,7 +5177,7 @@ Commandes disponibles : Signed share container are not supported - import prevented - + Conteneur de partage signé non pris en charge - importation annulée File is not readable @@ -5184,15 +5185,15 @@ Commandes disponibles : Invalid sharing container - + Conteneur de partage invalide Untrusted import prevented - + Importation non sécurisée annulée Successful signed import - + Importation signée réussie Unexpected error @@ -5200,11 +5201,11 @@ Commandes disponibles : Unsigned share container are not supported - import prevented - + Conteneur de partage non signé non pris en charge - importation annulée Successful unsigned import - + Importation non signée réussie File does not exist @@ -5212,27 +5213,27 @@ Commandes disponibles : Unknown share container type - + Type de conteneur de partage non reconnu Overwriting signed share container is not supported - export prevented - + Remplacement de conteneur de partage signé non pris en charge - exportation annulée Could not write export container (%1) - + Impossible d'exporter le conteneur (%1) Overwriting unsigned share container is not supported - export prevented - + Remplacement de conteneur non signé non pris en charge - exportation annulée Could not write export container - + Impossible d'exporter le conteneur Unexpected export error occurred - + Une erreur inattendue est survenue lors de l'exportation Export to %1 failed (%2) @@ -5248,31 +5249,31 @@ Commandes disponibles : Do you want to trust %1 with the fingerprint of %2 from %3? - + Voulez-vous autoriser %1 avec l'empreinte de %2 à %3 ? {1 ?} {2 ?} Multiple import source path to %1 in %2 - + Chemin source d'importation multiple de %1 dans %2 Conflicting export target path %1 in %2 - + Conflit du chemin cible d'exportation %1 dans %2 Could not embed signature: Could not open file to write (%1) - + Impossible d'intégrer la signature : le fichier (%1) n'a pas pu être ouvert en écriture Could not embed signature: Could not write file (%1) - + Impossible d'intégrer la signature : problème d'écriture dans le fichier (%1) Could not embed database: Could not open file to write (%1) - + Impossible d'intégrer la base de données : le fichier (%1) n'a pas pu être ouvert en écriture Could not embed database: Could not write file (%1) - + Impossible d'intégrer la base de données : problème d'écriture dans le fichier (%1) diff --git a/share/translations/keepassx_it.ts b/share/translations/keepassx_it.ts index e5a1558cc0..b56e7c2bc3 100644 --- a/share/translations/keepassx_it.ts +++ b/share/translations/keepassx_it.ts @@ -54,7 +54,7 @@ Use OpenSSH for Windows instead of Pageant - + Usa OpenSSH per Windows al posto di Pageant @@ -442,7 +442,8 @@ Seleziona se vuoi consentire l'accesso. You have multiple databases open. Please select the correct database for saving credentials. - + C'è più di un database aperto +Selezionare il database corretto dove salvare le credenziali @@ -1341,7 +1342,9 @@ Se continui con questo numero, il tuo database potrebbe essere decifrato molto f WARNING! You have not set a password. Using a database without a password is strongly discouraged! Are you sure you want to continue without a password? - + ATTENZIONE! Non è stata impostata una password. Utilizzare un database senza password è fortemente sconsigliato! + +Siete sicuri di voler continuare senza password? Unknown error @@ -1547,7 +1550,8 @@ Vuoi salvare le modifiche? Database was modified. Save changes? - + Il database è stato modificato. +Salvare le modifiche? Save changes? @@ -3532,7 +3536,7 @@ Si consiglia di utilizzare l'AppImage disponibile sulla nostra pagina di do Check for Updates... - + Controllo aggiornamenti... Share entry @@ -3545,15 +3549,15 @@ Expect some bugs and minor issues, this version is not meant for production use. Check for updates on startup? - + Controllare gli aggiornamenti all'avvio? Would you like KeePassXC to check for updates on startup? - + Volete che KeePassXC controlli eventuali aggiornamenti all'avvio? You can always check for updates manually from the application menu. - + È sempre possibile controllare gli aggiornamenti manualmente tramite i menu dell'applicazione. @@ -3619,7 +3623,7 @@ Expect some bugs and minor issues, this version is not meant for production use. NewDatabaseWizard Create a new KeePassXC database... - + Creazione di un nuovo database KeePassXC... Root @@ -3639,7 +3643,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + Qui è possibile modificare le impostazioni di crittaggio del database. È sempre possibile modificarli dopo nelle impostazioni del database. Advanced Settings @@ -3658,7 +3662,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + Qui è possibile modificare le impostazioni di crittaggio del database. È sempre possibile modificarli dopo nelle impostazioni del database. @@ -3669,7 +3673,7 @@ Expect some bugs and minor issues, this version is not meant for production use. A master key known only to you protects your database. - + Una password principale segreta protegge il vostro database. @@ -3798,7 +3802,7 @@ Expect some bugs and minor issues, this version is not meant for production use. <p>A password is the primary method for securing your database.</p><p>Good passwords are long and unique. KeePassXC can generate one for you.</p> - + <p>Una password è il metodo principale per mantenere sicuro il vostro database.</p><p>Una buona password dev'essere lunga ed unica. KeePassXC può generarne una per voi.</p> Passwords do not match. @@ -3958,7 +3962,7 @@ Expect some bugs and minor issues, this version is not meant for production use. {[( - + {[( Punctuation @@ -3966,7 +3970,7 @@ Expect some bugs and minor issues, this version is not meant for production use. .,:; - + .,:; Quotes @@ -3974,7 +3978,7 @@ Expect some bugs and minor issues, this version is not meant for production use. " ' - + " ' Math @@ -3982,7 +3986,7 @@ Expect some bugs and minor issues, this version is not meant for production use. <*+!?= - + <*+!?= Dashes @@ -3990,7 +3994,7 @@ Expect some bugs and minor issues, this version is not meant for production use. \_|-/ - + \_|-/ Logograms @@ -3998,7 +4002,7 @@ Expect some bugs and minor issues, this version is not meant for production use. #$%&&@^`~ - + #$%&&@^`~ Switch to simple mode @@ -4041,21 +4045,21 @@ Expect some bugs and minor issues, this version is not meant for production use. QApplication KeeShare - + KeeShare QFileDialog Select - + Seleziona QMessageBox Overwrite - + Sovrascrivi Delete @@ -4063,11 +4067,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Move - + Sposta Empty - + Vuoto Remove @@ -4075,7 +4079,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Skip - + Salta Disable @@ -4083,7 +4087,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Merge - + Incorpora @@ -4743,19 +4747,19 @@ Comandi disponibili: Create a new database. - + Crea un nuovo database. File %1 already exists. - + Il file %1 esiste già. Loading the key file failed - + Caricamento del key-file fallito. No key is set. Aborting database creation. - + Chiave non impostata. Annullamento creazione database. Failed to save the database: %1. @@ -4763,7 +4767,7 @@ Comandi disponibili: Successfully created new database. - + Nuovo database creato con successo. Insert password to encrypt database (Press enter to leave blank): diff --git a/share/translations/keepassx_pt_BR.ts b/share/translations/keepassx_pt_BR.ts index a7a7e47321..2e2fd4ac7b 100644 --- a/share/translations/keepassx_pt_BR.ts +++ b/share/translations/keepassx_pt_BR.ts @@ -491,7 +491,7 @@ Por favor, selecione o banco de dados correto para salvar as credenciais. Re&quest to unlock the database if it is locked - Pe&dir para desbloquear a base de dados se estiver bloqueada + Pe&dir para desbloquear a banco de dados se estiver bloqueado Only entries with the same scheme (http://, https://, ...) are returned. @@ -1063,7 +1063,7 @@ Isso pode impedir a conexão com o plugin do navegador. Successfully removed %n encryption key(s) from KeePassXC settings. - + Removido com sucesso% n chave (s) criptográficas das configurações do KeePassXC.Removido com sucesso% n chave (s) criptográficas das configurações do KeePassXC. Forget all site-specific settings on entries @@ -1097,7 +1097,7 @@ Permissões para acessar entradas serão revogadas. The active database does not contain an entry with permissions. - A base de dados ativa não contém uma entrada com permissões. + O banco de dados ativo não contém uma entrada com permissões. Move KeePassHTTP attributes to custom data @@ -1239,7 +1239,7 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!< thread(s) Threads for parallel execution (KDF settings) - + processo(s)processo(s) %1 ms @@ -1489,11 +1489,11 @@ Este é definitivamente um bug, por favor denuncie para os desenvolvedores. No current database. - Nenhuma base de dados atual. + Nenhuma banco de dados atual. No source database, nothing to do. - Nenhuma base de dados de origem, nada a fazer. + Nenhuma banco de dados de origem, nada a fazer. Search Results (%1) @@ -1509,7 +1509,7 @@ Este é definitivamente um bug, por favor denuncie para os desenvolvedores. The database file has changed. Do you want to load the changes? - A base de dados foi alterada. Deseja carregar as alterações? + O banco de dados foi alterado. Deseja carregar as alterações? Merge Request @@ -1531,7 +1531,7 @@ Você deseja combinar suas alterações? Do you really want to delete %n entry(s) for good? - + Você realmente quer apagar %n entrada(s) para o bem?Você realmente quer apagar %n entrada(s) para o bem? Delete entry(s)? @@ -1609,7 +1609,7 @@ Deseja desabilitar salvamento seguro e tentar novamente? Entry "%1" has %2 reference(s). Do you want to overwrite references with values, skip this entry, or delete anyway? - + A entrada "%1" tem %2 referência(s). Deseja substituir referências por valores, ignorar essa entrada ou excluir mesmo assim?A entrada "%1" tem %2 referência(s). Deseja substituir referências por valores, ignorar essa entrada ou excluir mesmo assim? Delete group @@ -2078,15 +2078,15 @@ Deseja desabilitar salvamento seguro e tentar novamente? The export container %1 is already referenced. - + O contêiner de exportado %1 já é referenciado. The import container %1 is already imported. - + O contêiner de importado %1 já foi importado. The container %1 imported and export by different groups. - + O contêiner %1 importado e exportado por diferentes grupos. @@ -2168,7 +2168,7 @@ Deseja desabilitar salvamento seguro e tentar novamente? Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security - + Dica: você pode habilitar o DuckDuckGo como um reserva em Ferramentas> Configurações> Segurança Select Image(s) @@ -2184,7 +2184,7 @@ Deseja desabilitar salvamento seguro e tentar novamente? %n icon(s) already exist in the database - + %n ícone(s) já existe no banco de dados%n ícone(s) já existe no banco de dados The following icon(s) failed: @@ -2192,7 +2192,7 @@ Deseja desabilitar salvamento seguro e tentar novamente? This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it? - + Este ícone é usado por %n entrada(s) e será substituído pelo ícone padrão. Tem certeza de que deseja excluí-lo?Este ícone é usado por %n entrada(s) e será substituído pelo ícone padrão. Tem certeza de que deseja excluí-lo? @@ -2593,7 +2593,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Wrong key or database file is corrupt. - Chave errada ou base de dados corrompida. + Chave errada ou banco de dados corrompido. missing database headers @@ -2601,7 +2601,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Header doesn't match hash - + Cabeçalho não corresponde ao hash Invalid header id size @@ -2833,7 +2833,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Unable to parse UUID: %1 - + Não é possível analisar o UUID: %1 Failed to read database file. @@ -3036,7 +3036,7 @@ Linha %2, coluna %3 Wrong key or database file is corrupt. - Chave errada ou base de dados corrompida. + Chave errada ou banco de dados corrompido. Key transformation failed @@ -3225,7 +3225,7 @@ Linha %2, coluna %3 <p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p> - + <p>Você pode adicionar um arquivo de chave contendo bytes aleatórios para segurança adicional.</p><p>Você deve mantê-lo em segredo e nunca perdê-lo ou você será bloqueado!</p> Legacy key file format @@ -3236,12 +3236,16 @@ Linha %2, coluna %3 unsupported in the future. Please go to the master key settings and generate a new key file. - + Você está usando um formato de arquivo de chave antigo que pode ficar +sem suporte no futuro. + +Por favor, vá para as configurações da chave mestra e gere um novo arquivo de chave. Error loading the key file '%1' Message: %2 - + Erro ao carregar o arquivo de chave '%1' +Mensagem: %2 Key files @@ -3261,7 +3265,7 @@ Message: %2 Unable to create key file: %1 - + Não foi possível criar arquivo de chave: %1 Select a key file @@ -3308,11 +3312,11 @@ Message: %2 &Save database - &Salvar base de dados + &Salvar banco de dados &Close database - &Fechar base de dados + &Fechar banco de dados &Delete entry @@ -3360,7 +3364,7 @@ Message: %2 &Lock databases - &Trancar base de dados + &Trancar banco de dados &Title @@ -3591,11 +3595,11 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Adding backup for older target %1 [%2] - + Adicionando backup para o alvo mais antigo %1 [%2] Adding backup for older source %1 [%2] - + Adicionando backup para fonte mais antiga %1 [%2] Reapplying older target entry on top of newer source %1 [%2] @@ -3607,7 +3611,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Synchronizing from newer source %1 [%2] - + Sincronizando de uma fonte mais nova %1 [%2] Synchronizing from older source %1 [%2] @@ -3619,7 +3623,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Deleting orphan %1 [%2] - + Excluindo órfã %1 [%2] Changed deleted objects @@ -3937,7 +3941,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç ExtendedASCII - + ASCIIEstendido Switch to advanced mode @@ -3957,7 +3961,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Lower Case Letters A to F - + Letras minúsculas de A a F a-z @@ -4033,7 +4037,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Add non-hex letters to "do not include" list - + Adicionar letras não hexadecimais à lista "não incluir" Hex @@ -4041,7 +4045,7 @@ Espere alguns bugs e problemas menores, esta versão não é para uso em produç Excluded characters: "0", "1", "l", "I", "O", "|", "﹒" - + Caracteres excluídos: "0", "1", "l", "I", "O", "|", "﹒" Word Co&unt: @@ -4415,11 +4419,11 @@ Comandos disponíveis: Invalid value for password length %1. - + Valor inválido para o tamanho da senha %1. Could not create entry with path %1. - + Não foi possível criar uma entrada com o caminho %1. Enter password for new entry: @@ -4427,15 +4431,15 @@ Comandos disponíveis: Writing the database failed %1. - + Gravação do banco de dados falhou %1. Successfully added entry %1. - + Entrada adicionada com sucesso %1. Copy the current TOTP to the clipboard. - + Copie o TOTP atual para a área de transferência. Invalid timeout value %1. @@ -4516,11 +4520,11 @@ Comandos disponíveis: Type: Bruteforce - + Tipo: Força Bruta Type: Dictionary - + Tipo: Dicionário Type: Dict+Leet @@ -4540,15 +4544,15 @@ Comandos disponíveis: Type: Sequence - + Tipo: Sequência Type: Spatial - + Tipo: Espacial Type: Date - + Tipo: Data Type: Bruteforce(Rep) @@ -4596,11 +4600,11 @@ Comandos disponíveis: *** Password length (%1) != sum of length of parts (%2) *** - + *** Comprimento da senha (%1) != soma do comprimento das partes (%2) *** Failed to load key file %1: %2 - + Falha ao carregar o arquivo de chave %1: %2 File %1 does not exist. @@ -4613,16 +4617,18 @@ Comandos disponíveis: Error while reading the database: %1 - + Erro ao ler o banco de dados: +%1 Error while parsing the database: %1 - + Erro ao analisar o banco de dados: +%1 Length of the generated password - + Comprimento da senha gerada Use lowercase characters @@ -4646,11 +4652,11 @@ Comandos disponíveis: Exclude character set - + Excluir conjunto de caracteres chars - + caracteres Exclude similar looking characters @@ -4666,7 +4672,7 @@ Comandos disponíveis: Cannot find group %1. - + Não foi possível encontrar o grupo %1. Error reading merge file: @@ -4691,19 +4697,19 @@ Comandos disponíveis: Show the entry's current TOTP. - + Mostrar o TOTP atual da entrada. ERROR: unknown attribute %1. - + ERRO: atributo desconhecido %1. No program defined for clipboard manipulation - + Nenhum programa definido para manipulação da área de transferência Unable to start program %1 - + Não é possível iniciar o programa %1 file empty @@ -5268,7 +5274,7 @@ Comandos disponíveis: Could not embed database: Could not write file (%1) - + Não foi possível incorporar o banco de dados: não foi possível gravar o arquivo (%1) diff --git a/share/translations/keepassx_ru.ts b/share/translations/keepassx_ru.ts index fbada2f717..b3fab3d249 100644 --- a/share/translations/keepassx_ru.ts +++ b/share/translations/keepassx_ru.ts @@ -15,7 +15,7 @@ KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. - KeePassXC распространяется на условиях универсальной общественной лицензии GNU (GPL) версии 2 или 3 (на ваше усмотрение). + KeePassXC распространяется на условиях универсальной общедоступной лицензии GNU (GPL) версии 2 или 3 (на ваше усмотрение). Contributors @@ -124,7 +124,7 @@ Minimize window at application startup - Сворачивать окно при запуске приложения + Запускать приложение в свёрнутом виде File Management @@ -148,7 +148,7 @@ Don't mark database as modified for non-data changes (e.g., expanding groups) - Не помечать базу данных изменённой при действиях, не связанных с изменением данных (например при распахивании групп) + Не помечать базу данных изменённой при действиях, не изменяющих данные (например при раскрытии групп) Automatically reload the database when modified externally @@ -192,7 +192,7 @@ Hide window to system tray when minimized - При сворачивании скрывать окно в область уведомлений + Сворачивать окно в область уведомлений Language @@ -216,7 +216,7 @@ Global Auto-Type shortcut - Комбинация клавиш для глобального автоввода + Клавиши для глобального автоввода Auto-Type typing delay @@ -237,11 +237,11 @@ Include pre-releases when checking for updates - Включать предварительные версии при проверке обновлений + Включать бета-версии при проверке обновлений Movable toolbar - Передвижная панель инструментов + Перемещаемая панель инструментов Button style @@ -265,7 +265,7 @@ Lock databases after inactivity of - Блокировать базу данных при отсутствии активности в течение + Блокировать базу данных при неактивности в течение min @@ -273,7 +273,7 @@ Forget TouchID after inactivity of - Забыть TouchID после неактивности + Забывать TouchID после неактивности Convenience @@ -285,19 +285,19 @@ Forget TouchID when session is locked or lid is closed - Забыть TouchID, когда сеанс заблокирован или крышка закрыта + Забывать TouchID при блокировке сеанса или закрытии крышки ноутбука Lock databases after minimizing the window - Блокировать базу данных при сворачивании окна + Блокировать базы данных при сворачивании окна Re-lock previously locked database after performing Auto-Type - Заблокировать базу данных после автоввода + Блокировать ранее заблокированную БД после автоввода Don't require password repeat when it is visible - Не требовать повторный ввод пароля, когда он показывается + Не требовать повторный ввод пароля, когда он отображается Don't hide passwords when editing them @@ -313,7 +313,7 @@ Hide entry notes by default - Скрыть примечания записи по умолчанию + По умолчанию скрывать примечания записи Privacy @@ -321,7 +321,7 @@ Use DuckDuckGo as fallback for downloading website icons - Использовать DuckDuckGo как резервный источник для скачивания значков сайта + Использовать DuckDuckGo как резервный источник для загрузки значков сайта @@ -429,11 +429,11 @@ Please select whether you want to allow access. BrowserEntrySaveDialog KeePassXC-Browser Save Entry - KeePassXC-Browser сохранить запись + KeePassXC-Browser - сохранить запись Ok - Ok + OK Cancel @@ -443,7 +443,7 @@ Please select whether you want to allow access. You have multiple databases open. Please select the correct database for saving credentials. У вас открыто несколько баз данных. -Пожалуйста, выберите нужную базу данных для сохранения учетных данных. +Выберите нужную базу для сохранения учётных данных. @@ -503,7 +503,7 @@ Please select the correct database for saving credentials. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Возвращать при поиске по URL только лучшие совпадения, а не все записи для домена. + При поиске по URL возвращать только лучшие совпадения, а не все записи для домена. &Return only best-matching credentials @@ -594,7 +594,7 @@ Please select the correct database for saving credentials. <b>Warning</b>, the keepassxc-proxy application was not found!<br />Please check the KeePassXC installation directory or confirm the custom path in advanced options.<br />Browser integration WILL NOT WORK without the proxy application.<br />Expected Path: - <b>Предупреждение</b>, приложение keepassxc-proxy не найдено! <br /> Проверьте каталог установки KeePassXC или установите пользовательский путь в расширенные настройках. <br />Интеграция браузера не будет работы без прокси приложения. <br />Ожидаемый путь: + <b>Внимание!</b> Не найдено приложение keepassxc-proxy! <br /> Проверьте папку установки KeePassXC или задайте свой путь в расширенных настройках. <br />Интеграция в браузеры не будет работать без прокси-приложения. <br />Ожидаемый путь: Executable Files @@ -611,15 +611,15 @@ Please select the correct database for saving credentials. Due to Snap sandboxing, you must run a script to enable browser integration.<br />You can obtain this script from %1 - Из-за того, что Snap - это песочница, Вы должны запустить скрипт, чтобы разрешить браузерную интеграцию.<br />Вы можете получить этот скрипт с %1 + Так как Snap - песочница, нужно запустить скрипт, чтобы разрешить интеграцию в браузеры.<br />Этот скрипт можно получить тут: %1 Please see special instructions for browser extension use below - Пожалуйста, смотрите особые инструкции по использованию расширения браузера ниже + Ознакомьтесь ниже с особыми инструкциями по использованию расширения браузера KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3 - KeePassXC-Browser необходим для работы интеграции браузера. <br />Скачайте его для %1 и %2. %3 + KeePassXC-Browser необходим для интеграции в браузер. <br />Скачайте его для %1 и %2. %3 @@ -676,11 +676,11 @@ Do you want to overwrite it? Successfully converted attributes from %1 entry(s). Moved %2 keys to custom data. Успешно преобразованы атрибуты из %1 записи(ей). -%2 ключей перемещены в пользовательские данные. +Перемещено ключей в пользовательские данные: %2. Successfully moved %n keys to custom data. - Успешно переехал %n ключи пользовательских данных.Успешно переехал %n ключи пользовательских данных.Успешно переехал %n ключи пользовательских данных.Успешно перемещено %n ключей в пользовательские данные. + Успешно переехал %n ключи пользовательских данных.Успешно переехал %n ключи пользовательских данных.Успешно переехал %n ключи пользовательских данных.Успешно перемещено ключей в пользовательские данные: %n. KeePassXC: No entry with KeePassHTTP attributes found! @@ -688,11 +688,11 @@ Moved %2 keys to custom data. The active database does not contain an entry with KeePassHTTP attributes. - Активная база данных не содержит запись с атрибутами KeePassHTTP. + В активной базе данных нет записи с атрибутами KeePassHTTP. KeePassXC: Legacy browser integration settings detected - KeePassXC: Устаревшая интеграция с браузером обнаружена + KeePassXC: Обнаружена устаревшая интеграция с браузером KeePassXC: Create a new group @@ -702,17 +702,17 @@ Moved %2 keys to custom data. A request for creating a new group "%1" has been received. Do you want to create this group? - Был получен запрос для создания новой группы "%1". -Вы хотите создать эту группу? + Получен запрос на создание новой группы "%1". +Создать эту группу? Your KeePassXC-Browser settings need to be moved into the database settings. This is necessary to maintain your current browser connections. Would you like to migrate your existing settings now? - Нужно переместить Ваши настройки KeePassXC-Browser в настройки базы данных. -Это необходимо, чтобы поддерживать Ваши текущие соединения браузера. -Желаете ли Вы мигрировать Ваши существующие настройки сейчас? + Ваши настройки KeePassXC-Browser требуется переместить в настройки базы данных. +Это необходимо, чтобы поддерживать текущие подключения браузера. +Хотите перенести настройки сейчас? @@ -754,7 +754,7 @@ Would you like to migrate your existing settings now? Codec - Кодировка + Кодек Text is qualified by @@ -806,7 +806,7 @@ Would you like to migrate your existing settings now? Empty fieldname %1 - Пустое имя поле %1 + Пустое имя поля %1 column %1 @@ -814,16 +814,16 @@ Would you like to migrate your existing settings now? Error(s) detected in CSV file! - В CSV-файле обнаруженны ошибки! + Ошибки в CSV-файле! [%n more message(s) skipped] - [%n больше сообщений пропущен][%n больше сообщений пропущен][%n больше сообщений пропущен][%n сообщений пропущено] + [%n больше сообщений пропущен][%n больше сообщений пропущен][%n больше сообщений пропущен][пропущено сообщений: %n] CSV import: writer has errors: %1 - Импорт CSV: запись была с ошибками: %1 + Импорт CSV: запись с ошибками - %1 @@ -839,7 +839,7 @@ Would you like to migrate your existing settings now? %n byte(s) - %n байт(ов)%n байт(ов)%n байт(ов)%n байт(ов) + %n байт(ов)%n байт(ов)%n байт(ов)%n байт %n row(s) @@ -859,7 +859,7 @@ Would you like to migrate your existing settings now? Unable to open file %1. - Не удается открыть файл %1. + Невозможно открыть файл %1. Error while reading the database: %1 @@ -871,11 +871,11 @@ Would you like to migrate your existing settings now? File cannot be written as it is opened in read-only mode. - Файл не может быть перезаписан, т.к. он открыт в режиме "только для чтения". + Файл не может быть перезаписан - он открыт в режиме "только для чтения". Key not transformed. This is a bug, please report it to the developers! - Ключ не преобразован. Это ошибка, пожалуйста, сообщите о нём разработчикам! + Ключ не преобразован. Это ошибка, сообщите о ней разработчикам! @@ -947,13 +947,13 @@ Please consider generating a new key file. Unable to open the database: %1 - Не удается открыть базу данных: + Невозможно открыть базу данных: %1 Can't open key file: %1 - Не удается открыть ключевой файл: + Невозможно открыть ключевой файл: %1 @@ -980,7 +980,7 @@ Please consider generating a new key file. Master Key - Мастер ключ + Мастер-ключ Encryption Settings @@ -988,7 +988,7 @@ Please consider generating a new key file. Browser Integration - Интеграция с браузером + Интеграция с браузерами @@ -1003,7 +1003,7 @@ Please consider generating a new key file. Forg&et all site-specific settings on entries - Забыть все сайтоспецифические настройки записей + Забыть все настройки записей для конкретных сайтов Move KeePassHTTP attributes to KeePassXC-Browser &custom data @@ -1011,7 +1011,7 @@ Please consider generating a new key file. Stored keys - Сохраненные ключи + Сохранённые ключи Remove @@ -1025,7 +1025,7 @@ Please consider generating a new key file. Do you really want to delete the selected key? This may prevent connection to the browser plugin. Вы действительно хотите удалить выбранный ключ? -Это может воспрепятствовать соединению с плагином браузера. +Это может помешать подключению к плагину браузера. Key @@ -1046,8 +1046,8 @@ This may prevent connection to the browser plugin. Do you really want to disconnect all browsers? This may prevent connection to the browser plugin. - Вы действительно хотите отсоединить все браузеры? -Это может воспрепятствовать соединению с плагином браузера. + Вы действительно хотите отключить все браузеры? +Это может помешать подключению к плагину браузера. KeePassXC: No keys found @@ -1055,7 +1055,7 @@ This may prevent connection to the browser plugin. No shared encryption keys found in KeePassXC settings. - Не найдены разделяемые секретные ключи в настройках KeePassXC. + В настройках KeePassXC нет общих ключей шифрования. KeePassXC: Removed keys from database @@ -1063,17 +1063,17 @@ This may prevent connection to the browser plugin. Successfully removed %n encryption key(s) from KeePassXC settings. - Успешно удалён %n ключ шифрования из настроек KeePassXC.Успешно удалёны %n ключа шифрования из настроек KeePassXC.Успешно удалёны %n ключей шифрования из настроек KeePassXC.Успешно удалён(ы) %n ключ(ей) шифрования из настроек KeePassXC. + Успешно удалён %n ключ шифрования из настроек KeePassXC.Успешно удалёны %n ключа шифрования из настроек KeePassXC.Успешно удалёны %n ключей шифрования из настроек KeePassXC.Успешно удалено ключей шифрования из настроек KeePassXC: %n. Forget all site-specific settings on entries - Забыть все сайтоспецифические настройки записей + Забыть все настройки записей для конкретных сайтов Do you really want forget all site-specific settings on every entry? Permissions to access entries will be revoked. Вы действительно хотите забыть все настройки сайта для каждой записи? -Разрешения на доступ к записи будет отменено. +Разрешения на доступ к записям будут отменены. Removing stored permissions… @@ -1089,7 +1089,7 @@ Permissions to access entries will be revoked. Successfully removed permissions from %n entry(s). - Успешно удалено разрешение от %n записи.Успешно удалены разрешения от %n записей.Успешно удалены разрешения от %n записей.Успешно удалены разрешения от %n записей. + Успешно удалено разрешение от %n записи.Успешно удалены разрешения от %n записей.Успешно удалены разрешения от %n записей.Успешно удалены разрешения из %n шт. записей. KeePassXC: No entry with permissions found! @@ -1097,16 +1097,16 @@ Permissions to access entries will be revoked. The active database does not contain an entry with permissions. - Активная база данных не содержит записей с разрешениями. + В активной базе данных нет записей с разрешениями. Move KeePassHTTP attributes to custom data - Переместить аттрибуты KeePassHTTP в пользовательские данные + Переместить атрибуты KeePassHTTP в пользовательские данные Do you really want to move all legacy browser integration data to the latest standard? This is necessary to maintain compatibility with the browser plugin. - Вы действительно хотите привести все устаревшие данные интеграции браузера к новейшему стандарту? + Вы действительно хотите перевести все устаревшие данные интеграции браузера в новый стандарт? Это необходимо для поддержания совместимости с плагином браузера. @@ -1130,7 +1130,7 @@ This is necessary to maintain compatibility with the browser plugin. Transform rounds: - Раундов преобразования: + Циклов преобразования: Benchmark 1-second delay @@ -1150,7 +1150,7 @@ This is necessary to maintain compatibility with the browser plugin. ?? s - ?? с + ?? сек Change @@ -1162,11 +1162,11 @@ This is necessary to maintain compatibility with the browser plugin. 5 s - 5 s + 5 сек Higher values offer more protection, but opening the database will take longer. - Более высокие значения дают более высокий уровень защиты, но открытие базы данных займет больше времени. + Чем больше значение, тем сильнее защита, но дольше открывается база данных. Database format: @@ -1174,7 +1174,7 @@ This is necessary to maintain compatibility with the browser plugin. This is only important if you need to use your database with other programs. - Это важно только если Вам нужно использовать вашу базу данных с другими программами. + Это важно, только если нужно использовать базу данных с другими программами. KDBX 4.0 (recommended) @@ -1192,15 +1192,15 @@ This is necessary to maintain compatibility with the browser plugin. Number of rounds too high Key transformation rounds - Слишком много раундов + Слишком много циклов You are using a very high number of key transform rounds with Argon2. If you keep this number, your database may take hours or days (or even longer) to open! - Слишком большое число раундов преобразования ключа Argon2. + Слишком много циклов преобразования ключа Argon2. -Если оставить это значение, открытие базы данных может занять часы, дни или даже больше! +Если оставить это значение, то база данных может открываться часы, дни или даже дольше! Understood, keep number @@ -1213,13 +1213,13 @@ If you keep this number, your database may take hours or days (or even longer) t Number of rounds too low Key transformation rounds - Слишком мало раундов + Слишком мало циклов You are using a very low number of key transform rounds with AES-KDF. If you keep this number, your database may be too easy to crack! - Слишком мало раундов преобразования ключа AES-KDF. + Слишком мало циклов преобразования ключа AES-KDF. Если оставить это значение, базу данных можно будет слишком легко взломать! @@ -1249,7 +1249,7 @@ If you keep this number, your database may be too easy to crack! %1 s seconds - %1 s%1 s%1 s%1 c + %1 s%1 s%1 s%1 cек @@ -1260,7 +1260,7 @@ If you keep this number, your database may be too easy to crack! Database name: - Название базы данных: + Имя базы данных: Database description: @@ -1343,7 +1343,7 @@ If you keep this number, your database may be too easy to crack! You must add at least one encryption key to secure your database! - Вы должны добавить по крайней мере один ключ шифрования, чтобы обезопасить вашу базу данных! + Нужно добавить хотя бы один ключ шифрования, чтобы обезопасить базу данных! No password set @@ -1353,9 +1353,9 @@ If you keep this number, your database may be too easy to crack! WARNING! You have not set a password. Using a database without a password is strongly discouraged! Are you sure you want to continue without a password? - ВНИМАНИЕ! Вы не установили пароль. Мы настоятельно отговариваем Вас от использования базы данных без пароля! + ВНИМАНИЕ! Вы не установили пароль. Категорически НЕ рекомендуется использовать базу данных без пароля! -Вы уверены, что хотите продолжить без пароля? +Вы действительно хотите продолжить без пароля? Unknown error @@ -1363,7 +1363,7 @@ Are you sure you want to continue without a password? Failed to change master key - Не удалось сменить мастер ключ + Не удалось изменить мастер-ключ @@ -1422,8 +1422,8 @@ Are you sure you want to continue without a password? The created database has no key or KDF, refusing to save it. This is definitely a bug, please report it to the developers. - Созданная база данных не имеет ключа или ФФК, отказываюсь сохранять её. -Это определённо баг, пожалуйста, сообщите о нём разработчикам. + У созданной базы данных нет ключа или ФФК, сохранение невозможно. +Это определённо ошибка, сообщите о ней разработчикам. The database file does not exist or is not accessible. @@ -1431,7 +1431,7 @@ This is definitely a bug, please report it to the developers. Select CSV file - Выберать CSV-файл + Выберите CSV-файл New Database @@ -1440,17 +1440,17 @@ This is definitely a bug, please report it to the developers. %1 [New Database] Database tab name modifier - %1 [Новая база данных] + %1 [новая база данных] %1 [Locked] Database tab name modifier - %1 [Заблокировано] + %1 [заблокировано] %1 [Read-only] Database tab name modifier - %1 [Только для чтения] + %1 [только для чтения] @@ -1461,7 +1461,7 @@ This is definitely a bug, please report it to the developers. Do you really want to delete the entry "%1" for good? - Удалить навсегда запись «%1»? + Удалить запись «%1» окончательно? Do you really want to move entry "%1" to the recycle bin? @@ -1485,7 +1485,7 @@ This is definitely a bug, please report it to the developers. Do you really want to delete the group "%1" for good? - Удалить навсегда группу «%1»? + Удалить группу «%1» окончательно? No current database. @@ -1531,15 +1531,15 @@ Do you want to merge your changes? Do you really want to delete %n entry(s) for good? - Вы действительно хотите удалить %n запись насовсем?Вы действительно хотите удалить %n записи насовсем?Вы действительно хотите удалить %n записей насовсем?Вы действительно хотите удалить %n запись(ей) насовсем? + Вы действительно хотите удалить %n запись насовсем?Вы действительно хотите удалить %n записи насовсем?Вы действительно хотите удалить %n записей насовсем?Вы действительно хотите окончательно удалить записи (%n шт.)? Delete entry(s)? - Удалить запись?Удалить записи?Удалить записи?Удалить запись(и)? + Удалить запись?Удалить записи?Удалить записи?Удалить записи? Move entry(s) to recycle bin? - Переместить запись в корзину?Переместить записи в корзину?Переместить записи в корзину?Переместить запись(и) в корзину? + Переместить запись в корзину?Переместить записи в корзину?Переместить записи в корзину?Переместить записи в корзину? File opened in read only mode. @@ -1551,7 +1551,7 @@ Do you want to merge your changes? You are editing an entry. Discard changes and lock anyway? - Вы редактируете запись. Сбросить изменения и заблокировать в любом случае? + Вы сейчас редактируете запись. Отменить изменения и всё равно заблокировать? "%1" was modified. @@ -1572,7 +1572,7 @@ Save changes? Could not open the new database file while attempting to autoreload. Error: %1 - Не удалось открыть новый файл базы данных во время попытки автоматической перезагрузки. + Не удалось открыть новый файл базы данных при попытке автоматически загрузить повторно. Ошибка: %1 @@ -1609,7 +1609,7 @@ Disable safe saves and try again? Entry "%1" has %2 reference(s). Do you want to overwrite references with values, skip this entry, or delete anyway? - Запись "%1" имеет %2 ссылку. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?Запись "%1" имеет %2 ссылки. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?Запись "%1" имеет %2 ссылок. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?Запись "%1" имеет %2 ссылку(ки, ок). Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае? + Запись "%1" имеет %2 ссылку. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?Запись "%1" имеет %2 ссылки. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?Запись "%1" имеет %2 ссылок. Вы хотите переписать ссылки значениями, пропустить эту запись или удалить в любом случае?У записи "%1" есть ссылки (%2 шт.). Хотите перезаписать ссылки значениями, пропустить эту запись или всё равно её удалить? Delete group @@ -1625,11 +1625,11 @@ Disable safe saves and try again? Successfully merged the database files. - Слияние файлов баз данных прошло успешно. + Файлы баз данных успешно объединены. Database was not modified by merge operation. - База данных не была изменена операцией слияния. + База данных не была изменена операцией объединения. Shared group... @@ -1676,15 +1676,15 @@ Disable safe saves and try again? Select private key - Выберите частный ключ + Выберите закрытый (личный) ключ File too large to be a private key - Слишком большой файл для частного ключа + Слишком большой файл для закрытого ключа Failed to open private key - Не удалось открыть частный ключ + Не удалось открыть личный ключ Entry history @@ -1696,7 +1696,7 @@ Disable safe saves and try again? Edit entry - Редактировать запись + Изменить запись Different passwords supplied. @@ -1736,7 +1736,7 @@ Disable safe saves and try again? Entry has unsaved changes - Запись имеет не сохранённые изменения + В записи есть несохранённые изменения New attribute %1 @@ -1744,7 +1744,7 @@ Disable safe saves and try again? [PROTECTED] Press reveal to view or edit - [Защищён] Нажмите для открытия просмотра или правки + [Защищено] Нажмите 'Показать' для просмотра или правки %n year(s) @@ -1779,7 +1779,7 @@ Disable safe saves and try again? Reveal - Открытие + Показать Attachments @@ -1935,7 +1935,7 @@ Disable safe saves and try again? Private key - Частный ключ + Закрытый (личный) ключ External file @@ -1960,7 +1960,7 @@ Disable safe saves and try again? Require user confirmation when this key is used - Требовать подтверждения при использовании этого ключа + Требовать подтверждение при использовании этого ключа @@ -2038,11 +2038,11 @@ Disable safe saves and try again? Your KeePassXC version does not support sharing your container type. Please use %1. - Ваша версия KeePassXC не поддерживает разделение вашего типа контейнера. Пожалуйста, используйте %1. + Эта версия KeePassXC не поддерживает совместное использование контейнера такого типа. Используйте %1. Database sharing is disabled - Разделение базы данных отключено + Совместное использование базы данных отключено Database export is disabled @@ -2062,11 +2062,11 @@ Disable safe saves and try again? Select import source - Выбрать источник для импорта + Выберите источник импорта Select export target - Выбрать цель для экспорта + Выберите место экспорта Select import/export file @@ -2156,7 +2156,7 @@ Disable safe saves and try again? Custom icon already exists - Пользовательский значок уже существует + Свой значок уже существует Confirm Delete @@ -2164,11 +2164,11 @@ Disable safe saves and try again? Custom icon successfully downloaded - Пользовательский значок успешно загружен + Свой значок успешно загружен Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security - Совет: Вы можете включить DuckDuckGo в качестве резерва в "Инструменты>Настройки>Безопасность" + * Вы можете включить DuckDuckGo как резерв в "Сервис>Настройки>Безопасность" Select Image(s) @@ -2176,23 +2176,23 @@ Disable safe saves and try again? Successfully loaded %1 of %n icon(s) - Успешно загружен %1 из %n значкаУспешно загружены %1 из %n значковУспешно загружены %1 из %n значковУспешно загружены %1 из %n значков + Успешно загружен %1 из %n значкаУспешно загружены %1 из %n значковУспешно загружены %1 из %n значковУспешно загружено значков: %1 из %n No icons were loaded - Значки не были загружены + Не загружено ни одного значка %n icon(s) already exist in the database - %n значок уже существует в базе данных%n значка уже существуют в базе данных%n значков уже существуют в базе данных%n значков уже существуют в базе данных + %n значок уже существует в базе данных%n значка уже существуют в базе данных%n значков уже существуют в базе данныхЗначков, уже имеющихся в базе данных: %n The following icon(s) failed: - Следующий значок потерпел неудачу:Следующие значки потерпели неудачу:Следующие значки потерпели неудачу:Следующие значки потерпели неудачу: + Следующий значок потерпел неудачу:Следующие значки потерпели неудачу:Следующие значки потерпели неудачу:Ошибки в следующих значках: This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it? - Этот значок используется %n записью и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется %n записями и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется %n записями и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется %n записями и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его? + Этот значок используется %n записью и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется %n записями и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется %n записями и будет замещён значком по умолчанию. Вы уверены, что хотите удалить его?Этот значок используется записями (%n), он будет замещён стандартным значком. Вы действительно хотите его удалить? @@ -2244,7 +2244,7 @@ This may cause the affected plugins to malfunction. Entry %1 - Clone - %1 - Клон + %1 - клон @@ -2286,7 +2286,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Действительно хотите удалить вложения (%n)? + Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Вы действительно хотите удалить вложения (%n шт.)? Save attachments @@ -2295,20 +2295,21 @@ This may cause the affected plugins to malfunction. Unable to create directory: %1 - Не удаётся создать папку: %1 + Невозможно создать папку: +%1 Are you sure you want to overwrite the existing file "%1" with the attachment? - Действительно хотите перезаписать имеющийся файл "%1" с вложением? + Вы действительно хотите перезаписать имеющийся файл "%1" с вложением? Confirm overwrite - Подтвердить перезапись + Подтвердите перезапись Unable to save attachments: %1 - Невозможно сохранить вложение: + Невозможно сохранить вложения: %1 @@ -2325,7 +2326,7 @@ This may cause the affected plugins to malfunction. Confirm remove - Подтвердить удаление + Подтвердите удаление Unable to open file(s): @@ -2333,7 +2334,7 @@ This may cause the affected plugins to malfunction. Не удалось открыть файл: %1Не удалось открыть файлы: %1Не удалось открыть файлы: -%1Не удалось открыть файл(ы): +%1Невозможно открыть файл(ы): %1 @@ -2443,7 +2444,7 @@ This may cause the affected plugins to malfunction. Username - Имя пользователя + Логин Password @@ -2499,7 +2500,7 @@ This may cause the affected plugins to malfunction. [PROTECTED] - [ЗАЩИЩЁННЫЙ] + [ЗАЩИЩЕНО] <b>%1</b>: %2 @@ -2527,7 +2528,7 @@ This may cause the affected plugins to malfunction. Hide Usernames - Скрыть имена пользователей + Скрыть логины Hide Passwords @@ -2543,7 +2544,7 @@ This may cause the affected plugins to malfunction. Reset to defaults - Восстановить значения по умолчанию + Сброс в стандартные значения Attachments (icon) @@ -2559,7 +2560,7 @@ This may cause the affected plugins to malfunction. [empty] group has no children - [пустой] + [пусто] @@ -2592,7 +2593,7 @@ This may cause the affected plugins to malfunction. Unable to issue challenge-response. - Не удалось выполнить вызов-ответ. + Невозможно выполнить вызов-ответ. Wrong key or database file is corrupt. @@ -2604,7 +2605,7 @@ This may cause the affected plugins to malfunction. Header doesn't match hash - Заголовок не соответствует хэшу + Заголовок не соответствует хешу Invalid header id size @@ -2623,7 +2624,7 @@ This may cause the affected plugins to malfunction. Kdbx3Writer Unable to issue challenge-response. - Не удалось выполнить вызов-ответ. + Невозможно выполнить вызов-ответ. Unable to calculate master key @@ -2794,11 +2795,11 @@ This may cause the affected plugins to malfunction. Invalid transform seed size - Недопустимый размер seed для трансформирования + Недопустимый размер seed для преобразования Invalid transform rounds size - Недопустимый размер раунда преобразования + Недопустимый размер цикла преобразования Invalid start bytes size @@ -2855,11 +2856,11 @@ This is a one-way migration. You won't be able to open the imported databas Missing icon uuid or data - Нет значка uuid или данных + Нет UUID значка или данных Missing custom data key or value - Отсутствует ключ пользовательских данных или значение + Нет ключа пользовательских данных или значения Multiple group elements @@ -2867,7 +2868,7 @@ This is a one-way migration. You won't be able to open the imported databas Null group uuid - Значение uuid для NULL группы + UUID для группы NULL Invalid group icon number @@ -2883,19 +2884,19 @@ This is a one-way migration. You won't be able to open the imported databas No group uuid found - Отсутствует групповой uuid + Нет UUID группы Null DeleteObject uuid - Null DeleteObject uuid + UUID DeleteObject Null Missing DeletedObject uuid or time - Нет uuid или времени для DeletedObject + Нет UUID или времени для DeletedObject Null entry uuid - Null uuid для записи + UUID для записи Null Invalid entry icon number @@ -2907,11 +2908,11 @@ This is a one-way migration. You won't be able to open the imported databas No entry uuid found - Нет uuid для записи + Нет UUID для записи History element with different uuid - Элемент истории с отличающимся uuid + Элемент истории с отличающимся UUID Duplicate custom attribute found @@ -2919,7 +2920,7 @@ This is a one-way migration. You won't be able to open the imported databas Entry string key or value missing - Отсутствует ключ или значение записи + Нет ключа или значения записи Duplicate attachment found @@ -2927,11 +2928,11 @@ This is a one-way migration. You won't be able to open the imported databas Entry binary key or value missing - Отсутствует двоичный ключ или значение записи + Нет двоичного ключа или значения записи Auto-type association window or sequence missing - Отсутствует окно или последовательность для автоввода + Нет окна или последовательности для автоввода Invalid bool value @@ -2939,7 +2940,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid date time value - Недопустимое значение даты времени + Недопустимое значение даты/времени Invalid color value @@ -2947,7 +2948,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid color rgb part - Недопустимое значение части цвета rgb + Недопустимое значение части цвета RGB Invalid number value @@ -2955,7 +2956,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid uuid value - Недопустимое значение uuid + Недопустимое значение UUID Unable to decompress binary @@ -3003,7 +3004,7 @@ Line %2, column %3 Unable to read encryption IV IV = Initialization Vector for symmetric cipher - Не удается прочитать шифрования IV + Невозможно прочитать шифрование IV Invalid number of groups @@ -3019,11 +3020,11 @@ Line %2, column %3 Invalid transform seed size - Недопустимый размер seed для трансформирования + Недопустимый размер seed для преобразования Invalid number of transform rounds - Недопустимое количество раундов преобразования + Недопустимое число циклов преобразования Unable to construct group tree @@ -3075,7 +3076,7 @@ Line %2, column %3 Incorrect group expiry time field size - Неверное значение поля времени истечения срока действия группы + Неверный размер поля времени истечения срока действия группы Incorrect group icon field size @@ -3091,15 +3092,15 @@ Line %2, column %3 Missing group id or level - Отсутствует групповой идентификатор или уровень + Нет группового идентификатора или уровня Missing entry field type number - Отсутствует номер типа поля записи + Нет номера типа поля записи Invalid entry field size - Недопустимый размер поля записи + Неверный размер поля записи Read entry field data doesn't match size @@ -3107,7 +3108,7 @@ Line %2, column %3 Invalid entry uuid field size - Недопустимый размер поля uuid записи + Неверный размер поля UUID записи Invalid entry group id field size @@ -3127,7 +3128,7 @@ Line %2, column %3 Invalid entry expiry time field size - Недопустимый размер поля времени для срока действия + Недопустимый размер поля времени срока действия Invalid entry field type @@ -3150,7 +3151,7 @@ Line %2, column %3 Export to - Экспортировать в + Экспорт в Synchronize with @@ -3158,19 +3159,19 @@ Line %2, column %3 Disabled share %1 - Отключённая часть %1 + Отключённый общий ресурс %1 Import from share %1 - Импортировать из части %1 + Импорт из общего ресурса %1 Export to share %1 - Экспортировать в часть %1 + Экспорт в общий ресурс %1 Synchronize with share %1 - Синхронизировать с частью %1 + Синхронизировать с общим ресурсом %1 @@ -3189,7 +3190,7 @@ Line %2, column %3 Key Component set, click to change or remove - Ключевой компонент установлен, щёлкните, чтобы изменить или удалить + Ключевой компонент установлен, нажмите, чтобы изменить или удалить Add %1 @@ -3228,7 +3229,7 @@ Line %2, column %3 <p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p> - <p>Вы можете добавить ключевой файл, содержащий случайные байты, для дополнительной безопасности.</p><p>Вы должны хранить его в секрете и никогда не терять, или Вы будете заблокированы!</p> + <p>Для большей безопасности вы можете добавить ключевой файл со случайными байтами.</p><p>Храните его в надёжном месте и не теряйте, иначе будете заблокированы!</p> Legacy key file format @@ -3239,9 +3240,9 @@ Line %2, column %3 unsupported in the future. Please go to the master key settings and generate a new key file. - Вы используете устаревший формат файла-ключа, который может стать не поддерживаемым в будущем. + Вы используете ключевой файл устаревшего формата, поддержка которого впоследствии может прекратиться. -Пожалуйста, сходите в настройки мастер ключа и сгенерируйте новый файл-ключ. +Сгенерируйте новый ключевой файл в настройках мастер-ключа. Error loading the key file '%1' @@ -3286,7 +3287,7 @@ Message: %2 &Help - Справка + &Справка E&ntries @@ -3298,11 +3299,11 @@ Message: %2 &Tools - &Инструменты + Серв&ис &Quit - &Выход + В&ыход &About @@ -3346,11 +3347,11 @@ Message: %2 Copy &username - Скопировать &имя пользователя + Скопировать лог&ин Copy username to clipboard - Скопировать имя пользователя в буфер обмена + Скопировать логин в буфер обмена Copy password to clipboard @@ -3451,8 +3452,8 @@ This version is not meant for production use. WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard! We recommend you use the AppImage available on our downloads page. - Предупреждение: Ваша версия Qt может привести к сбоям KeePassXC при работе с экранной клавиатурой! -Мы рекомендуем вам использовать AppImage доступны на нашей странице загрузки. + ВНИМАНИЕ: Ваша версия Qt может привести к сбоям KeePassXC при работе с экранной клавиатурой! +Рекомендуем использовать AppImage (см. нашу страницу загрузок). &Import @@ -3476,11 +3477,11 @@ We recommend you use the AppImage available on our downloads page. &Merge from database... - Сое&динить с другой базой данных... + Объе&динить с другой базой данных... Merge from another KDBX database - Соединить с другой базой данных KDBX + Объединить с другой базой данных KDBX &New entry @@ -3492,11 +3493,11 @@ We recommend you use the AppImage available on our downloads page. &Edit entry - &Править запись + Изменить запись View or edit entry - Просмотреть или отредактировать запись + Показать/изменить запись &New group @@ -3508,7 +3509,7 @@ We recommend you use the AppImage available on our downloads page. Change master &key... - Измнить мастер &ключ... + Изменить мастер-&ключ... &Database settings... @@ -3520,7 +3521,7 @@ We recommend you use the AppImage available on our downloads page. Perform &Auto-Type - Осуществить а&втоввод + Выполнить а&втоввод Open &URL @@ -3561,8 +3562,8 @@ We recommend you use the AppImage available on our downloads page. NOTE: You are using a pre-release version of KeePassXC! Expect some bugs and minor issues, this version is not meant for production use. - ЗАМЕТЬТЕ: Вы используете предварительную версию KeePassXC! -Ожидайте некоторые баги и мелкие проблемы, эта версия не подразумевается для производственного использования. + ВНИМАНИЕ: Вы используете бета-версию KeePassXC! +В ней возможны ошибки и небольшие проблемы, она не предназначена для основного применения. Check for updates on startup? @@ -3570,11 +3571,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Would you like KeePassXC to check for updates on startup? - Вы бы хотели, чтобы KeePassXC проверял обновления при запуске? + Хотите проверять обновления KeePassXC при запуске? You can always check for updates manually from the application menu. - Вы всегда можете проверять обновления вручную из меню приложения. + Проверять наличие обновлений можно вручную из меню приложения. @@ -3593,11 +3594,11 @@ Expect some bugs and minor issues, this version is not meant for production use. older entry merged from database "%1" - более старая запись присоединена из базы данных "%1" + более старая запись из базы данных "%1" Adding backup for older target %1 [%2] - Добавление резервной копии для более старой мишени %1 [%2] + Добавление резервной копии для более старой цели %1 [%2] Adding backup for older source %1 [%2] @@ -3625,7 +3626,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Deleting orphan %1 [%2] - Удаление заброшенной %1 [%2] + Удаление "осиротевшей" %1 [%2] Changed deleted objects @@ -3660,7 +3661,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - Здесь Вы можете отрегулировать настройки шифрования базы данных. Не беспокойтесь, вы сможете изменить их позже в настройках базы данных. + Здесь можно настроить параметры шифрования базы данных. Их можно будет изменить позже в настройках базы данных. Advanced Settings @@ -3679,7 +3680,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - Здесь Вы можете отрегулировать настройки шифрования базы данных. Не беспокойтесь, вы сможете изменить их позже в настройках базы данных. + Здесь можно настроить параметры шифрования базы данных. Их можно будет изменить позже в настройках базы данных. @@ -3690,7 +3691,7 @@ Expect some bugs and minor issues, this version is not meant for production use. A master key known only to you protects your database. - Мастер-ключ, известный только Вам, защищает Вашу базу данных. + Известный только вам мастер-ключ служит для защиты базы данных. @@ -3701,7 +3702,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Please fill in the display name and an optional description for your new database: - Пожалуйста, заполните отображаемое имя и опциональное описание Вашей новой базы данных: + Заполните отображаемое имя и, при желании, описание новой базы данных: @@ -3716,7 +3717,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Base64 decoding failed - Не удалось декодировать Base64 + Ошибка декодирования Base64 Key file way too small. @@ -3732,15 +3733,15 @@ Expect some bugs and minor issues, this version is not meant for production use. Failed to read public key. - Не удалось прочитать публичный ключ. + Ошибка чтения открытого (публичного) ключа. Corrupted key file, reading private key failed - Поврежденный ключевой файл, ошибка чтения частного ключа + Ключевой файл повреждён, ошибка чтения закрытого (личного) ключа No private key payload to decrypt - Нет сведений для дешифрования в частном ключе + Нет данных для расшифровки в закрытом (личном) ключе Trying to run KDF without cipher @@ -3760,27 +3761,27 @@ Expect some bugs and minor issues, this version is not meant for production use. Unexpected EOF while reading public key - Неожиданный конец файла при чтении публичного ключа + Неожиданный конец файла при чтении открытого (публичного) ключа Unexpected EOF while reading private key - Неожиданный конец файла при чтении частного ключа + Неожиданный конец файла при чтении закрытого (личного) ключа Can't write public key as it is empty - Невозможно записать публичный ключ, так как он пуст + Невозможно записать открытый (публичный) ключ, так как он пуст Unexpected EOF when writing public key - Неожиданный конец файла при записи публичного ключа + Неожиданный конец файла при записи открытого (публичного) ключа Can't write private key as it is empty - Невозможно записать частный ключ, так как он пуст + Невозможно записать закрытый (личный) ключ, так как он пуст Unexpected EOF when writing private key - Неожиданный конец файла при записи частного ключа + Неожиданный конец файла при записи закрытого (личного) ключа Unsupported key type: %1 @@ -3792,7 +3793,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Cipher IV is too short for MD5 kdf - Слишком короткий Cipher IV для MD5 ФФК + Слишком короткий вектор инициализации (IV) для MD5 ФФК Unknown KDF: %1 @@ -3819,7 +3820,7 @@ Expect some bugs and minor issues, this version is not meant for production use. <p>A password is the primary method for securing your database.</p><p>Good passwords are long and unique. KeePassXC can generate one for you.</p> - <p>Пароль - первичный метод защиты Вашей базы данных.</p><p>Хорошие пароли длинные и уникальные. KeePassXC может сгенерировать его для Вас.</p> + <p>Пароль - это основной метод защиты базы данных.</p><p>Хороший пароль должен быть длинным и уникальным. KeePassXC может сгенерировать его сам.</p> Passwords do not match. @@ -3903,7 +3904,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Copy - Копировать + Скопировать Accept @@ -3947,11 +3948,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Switch to advanced mode - Переключиться в расширенный режим + В расширенный режим Advanced - Дополнительные + Дополнительно Upper Case Letters A to F @@ -3983,7 +3984,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Punctuation - Знаки припенания + Знаки препинания .,:; @@ -4023,7 +4024,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Switch to simple mode - Переключиться в простой режим + В простой режим Simple @@ -4039,7 +4040,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Add non-hex letters to "do not include" list - Добавить не шестнадцатеричные буквы к списку "не включать" + Добавить не-шестнадцатеричные буквы к списку исключений Hex @@ -4055,7 +4056,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Regenerate - Перегенерировать + Создать снова @@ -4084,7 +4085,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Move - Перемещение + Переместить Empty @@ -4119,7 +4120,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Client public key not received - Не получен публичный ключ клиента + Не получен открытый (публичный) ключ клиента Cannot decrypt message @@ -4147,7 +4148,7 @@ Expect some bugs and minor issues, this version is not meant for production use. No URL provided - Отсутствует URL-адрес + Нет URL-адреса No logins found @@ -4175,11 +4176,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Username for the entry. - Имя пользователя для записи. + Логин для записи. username - имя пользователя + логин URL for the entry. @@ -4458,15 +4459,15 @@ Available commands: Entry's current TOTP copied to the clipboard! - Текущий TOTP записи скопирован в буфер обмена! + Текущий TOTP записи скопирован в буфер обмена. Entry's password copied to the clipboard! - Пароль записи скопирован в буфер обмена! + Пароль записи скопирован в буфер обмена. Clearing the clipboard in %1 second(s)... - Очищение буфера обмена через %1 секунду...Очищение буфера обмена через %1 секунды..Очищение буфера обмена через %1 секунд...Очищение буфера обмена через %1 секунд(у, ы)... + Очищение буфера обмена через %1 секунду...Очищение буфера обмена через %1 секунды..Очищение буфера обмена через %1 секунд...Очистка буфера обмена через %1 сек... Clipboard cleared! @@ -4491,7 +4492,7 @@ Available commands: Not changing any field for entry %1. - Не меняются какие-либо поля для записи %1. + Не меняются никакие поля для записи %1. Enter new password for entry: @@ -4499,11 +4500,11 @@ Available commands: Writing the database failed: %1 - Запись базы данных не удалась: %1 + Ошибка записи базы данных: %1 Successfully edited entry %1. - Правка записи прошла успешно %1. + Запись %1 отредактирована. Length %1 @@ -4531,19 +4532,19 @@ Available commands: Type: Dict+Leet - Тип: Словать+Leet + Тип: Словать+замена букв цифрами/знаками Type: User Words - Тип: Пользователь слова + Тип: Пользовательские слова Type: User+Leet - Тип: Пользователь+Leet + Тип: Пользователь+замена букв цифрами/знаками Type: Repeated - Тип: Повторения + Тип: Повторы Type: Sequence @@ -4559,39 +4560,39 @@ Available commands: Type: Bruteforce(Rep) - Тип: Перебор(Повт.) + Тип: Перебор (повт.) Type: Dictionary(Rep) - Тип: Словарь(Повт.) + Тип: Словарь (повт.) Type: Dict+Leet(Rep) - Тип: Словарь+Leet(Повт.) + Тип: Словарь+замена букв цифрами/знаками (повт.) Type: User Words(Rep) - Тип: Пользовательские слова(Повт.) + Тип: Пользовательские слова (повт.) Type: User+Leet(Rep) - Тип: Пользователь+Leet(Повт.) + Тип: Пользователь+замена букв цифрами/знаками (повт.) Type: Repeated(Rep) - Тип: Повторения(Повт.) + Тип: Повторы (повт.) Type: Sequence(Rep) - Тип: Последовательность(Повт.) + Тип: Последовательность (повт.) Type: Spatial(Rep) - Тип: Пространственный(Повт.) + Тип: Пространственный (повт.) Type: Date(Rep) - Тип: Дата(Повт.) + Тип: Дата (повт.) Type: Unknown%1 @@ -4607,7 +4608,7 @@ Available commands: Failed to load key file %1: %2 - Не удалось загрузить файл-ключ %1: %2 + Ошибка загрузки ключевого файла %1: %2 File %1 does not exist. @@ -4615,7 +4616,7 @@ Available commands: Unable to open file %1. - Не удается открыть файл %1. + Невозможно открыть файл %1. Error while reading the database: @@ -4631,11 +4632,11 @@ Available commands: Length of the generated password - Длина сгенерированного пароля + Длина генерируемого пароля Use lowercase characters - Использовать символы в нижнем регистре + Использовать строчные буквы Use uppercase characters @@ -4643,7 +4644,7 @@ Available commands: Use numbers. - Использовать цифры. + Использовать цифры Use special characters @@ -4663,7 +4664,7 @@ Available commands: Exclude similar looking characters - Исключать схоже выглядящие символы + Исключать похожие символы Include characters from every selected group @@ -4671,33 +4672,33 @@ Available commands: Recursively list the elements of the group. - Рекурсивно перечислять элементы группы. + Рекурсивный список элементов группы. Cannot find group %1. - Не удалось найти группу %1. + Невозможно найти группу %1. Error reading merge file: %1 - Ошибка при чтении файла слияния: + Ошибка при чтении объединяемого файла: %1 Unable to save database to file : %1 - Не удалось сохранить базу данных в файл : %1 + Невозможно сохранить базу данных в файл: %1 Unable to save database to file: %1 - Не удалось сохранить базу данных в файл: %1 + Невозможно сохранить базу данных в файл: %1 Successfully recycled entry %1. - Успешно перемещена в корзину запись %1. + Запись %1 перемещена в корзину. Successfully deleted entry %1. - Успешно удалена запись %1. + Запись %1 удалена. Show the entry's current TOTP. @@ -4705,15 +4706,15 @@ Available commands: ERROR: unknown attribute %1. - ОШИБКА: неизвестный атрибут %1. + ОШИБКА: Неизвестный атрибут %1. No program defined for clipboard manipulation - Не задана программа для манипуляции буфером обмена + Не задана программа для управления буфером обмена Unable to start program %1 - Не удалось запустить программу %1 + Невозможно запустить программу %1 file empty @@ -4759,7 +4760,7 @@ Available commands: Message encryption failed. - Шифрование сообщений не удалось. + Ошибка шифрования сообщений. No groups found @@ -4775,31 +4776,31 @@ Available commands: Loading the key file failed - Загрузка файла-ключа не удалась + Ошибка загрузки ключевого файла No key is set. Aborting database creation. - Ключ не установлен. Прерываю создание базы данных. + Не задан ключ. Создание базы данных отменено. Failed to save the database: %1. - Не удалось сохранить базу данных: %1. + Ошибка сохранения базы данных: %1. Successfully created new database. - Успешно создана новая база данных. + Новая база данных успешно создана. Insert password to encrypt database (Press enter to leave blank): - Вставьте пароль, чтобы зашифровать базу данных (нажмите "ввод", чтобы оставить его пустым): + Введите пароль для шифрования базы данных (нажмите Enter, чтобы оставить его пустым): Creating KeyFile %1 failed: %2 - Создание ключевого файла %1 не удалось: %2 + Ошибка создания ключевого файла %1: %2 Loading KeyFile %1 failed: %2 - Загрузка ключевого файла %1 не удалась: %2 + Ошибка загрузки ключевого файла %1: %2 Remove an entry from the database. @@ -4811,7 +4812,7 @@ Available commands: Existing single-instance lock file is invalid. Launching new instance. - Повреждён файл блокировки запуска, запуск нового экземпляра программы. + Повреждён файл блокировки запуска. Запускается новый экземпляр программы. The lock file could not be created. Single-instance mode disabled. @@ -4859,7 +4860,7 @@ Available commands: Cannot create new group - Не удаётся создать новую группу + Невозможно создать новую группу @@ -4908,15 +4909,15 @@ Available commands: No agent running, cannot add identity. - Агент не запущен, не удается добавить идентификатор. + Агент не запущен, невозможно добавить идентификатор. No agent running, cannot remove identity. - Агент не запущен, не удается удалить личность + Агент не запущен, невозможно удалить идентификатор. Agent refused this identity. Possible reasons include: - Агент отклонил учетную запись. Возможные причины: + Идентификатор отклонён агентом. Возможные причины: The key has already been added. @@ -4935,11 +4936,11 @@ Available commands: SearchHelpWidget Search Help - Искать в справке + Поиск в Справке Search terms are as follows: [modifiers][field:]["]term["] - Поисковые выражения выглядят следующим образом: [модификаторы][поле:]["]выражение["] + Поисковые выражения выглядят так: [модификаторы][поле:]["]выражение["] Every search term must match (ie, logical AND) @@ -4959,7 +4960,7 @@ Available commands: use regex in term - использовать регулярные выражения в поисковых + использовать регулярные выражения Fields @@ -5002,7 +5003,7 @@ Available commands: Search Help - Искать в справке + Поиск в Справке Search (%1)... @@ -5011,7 +5012,7 @@ Available commands: Case sensitive - Чувствительно к регистру + Учитывать регистр @@ -5030,7 +5031,7 @@ Available commands: Own certificate - Собственный сертификат + Свой сертификат Fingerprint: @@ -5042,7 +5043,7 @@ Available commands: Signer - Подписчик + Подписант Key: @@ -5131,11 +5132,11 @@ Available commands: The exported certificate is not the same as the one in use. Do you want to export the current certificate? - Экспортированный сертификат не такой же, как сертификат, который используется. Вы хотите экспортировать текущий сертификат? + Экспортированный сертификат отличается от используемого. Хотите экспортировать текущий сертификат? Signer: - Подписавшийся: + Подписант: @@ -5146,7 +5147,7 @@ Available commands: We cannot verify the source of the shared container because it is not signed. Do you really want to import from %1? - Мы не можем проверить источник коллективного контейнера, потому что он не подписан. Вы действительно хотите импортировать из %1? + Невозможно проверить источник общего контейнера, потому что он не подписан. Вы действительно хотите выполнить импорт из %1? Import from container with certificate @@ -5154,7 +5155,7 @@ Available commands: Not this time - Не в этот раз + Не сейчас Never @@ -5166,15 +5167,15 @@ Available commands: Just this time - Только в этот раз + Только сейчас Import from %1 failed (%2) - Импорт из %1 не удался (%2) + Ошибка импорта из %1 (%2) Import from %1 successful (%2) - Импорт из %1 успешен (%2) + Импорт из %1 выполнен (%2) Imported from %1 @@ -5182,35 +5183,35 @@ Available commands: Signed share container are not supported - import prevented - Контейнер с подписанным ресурсом не поддерживается - импорт запрещен + Общий контейнер с подписью не поддерживается - импорт не выполнен File is not readable - Файл не читабелен + Файл не читается Invalid sharing container - Неверный контейнер для обмена + Неверный общий контейнер Untrusted import prevented - Ненадежный импорт предотвращен + Ненадёжный импорт не выполнен Successful signed import - Успешный подписанный импорт + Подписанный импорт выполнен Unexpected error - Неожиданная ошибка + Неизвестная ошибка Unsigned share container are not supported - import prevented - Контейнер без подписи не поддерживается - импорт запрещен + Контейнер без подписи не поддерживается - импорт не выполнен Successful unsigned import - Успешный импорт без подписи + Импорт без подписи выполнен File does not exist @@ -5222,23 +5223,23 @@ Available commands: Overwriting signed share container is not supported - export prevented - Перезапись подписанного контейнера общего ресурса не поддерживается - экспорт запрещен + Перезапись подписанного общего контейнера не поддерживается - экспорт не выполнен Could not write export container (%1) - Не удалось записать Экспорт контейнера (%1) + Ошибка записи экспортируемого контейнера (%1) Overwriting unsigned share container is not supported - export prevented - Перезапись неподписанного общего ресурса не поддерживается - экспорт запрещен + Перезапись неподписанного общего контейнера не поддерживается - экспорт не выполнен Could not write export container - Не удалось записать экспорта контейнера + Ошибка записи экспортируемого контейнера Unexpected export error occurred - Произошла непредвиденная ошибка экспорта + Неизвестная ошибка экспорта Export to %1 failed (%2) @@ -5246,7 +5247,7 @@ Available commands: Export to %1 successful (%2) - Экспорт в %1 завершен (%2) + Экспорт в %1 выполнен (%2) Export to %1 @@ -5254,7 +5255,7 @@ Available commands: Do you want to trust %1 with the fingerprint of %2 from %3? - Вы хотите довериться %1 с отпечатком %2 из %3? {1 ?} {2 ?} + Доверять %1 с отпечатком %2 из %3? {1 ?} {2 ?} Multiple import source path to %1 in %2 @@ -5266,19 +5267,19 @@ Available commands: Could not embed signature: Could not open file to write (%1) - Не удалось встроить подпись: Не удалось открыть файл для записи (%1) + Не удалось встроить подпись: невозможно открыть файл для записи (%1) Could not embed signature: Could not write file (%1) - Не удалось встроить подпись: Не удалось записать файл (%1) + Не удалось встроить подпись: невозможно записать файл (%1) Could not embed database: Could not open file to write (%1) - Не удалось встроить базу данных: Не удалось открыть файл для записи (%1) + Не удалось встроить базу данных: невозможно открыть файл для записи (%1) Could not embed database: Could not write file (%1) - Не удалось встроить базу данных: Не удалось записать файл (%1) + Не удалось встроить базу данных: невозможно записать файл (%1) @@ -5293,31 +5294,31 @@ Available commands: Copy - Копировать + Скопировать Expires in <b>%n</b> second(s) - Истекает через <b>%n</b> секундуИстекает через <b>%n</b> секундыИстекает через <b>%n</b> секундИстекает через <b>%n</b> секунд(у) + Истекает через <b>%n</b> секундуИстекает через <b>%n</b> секундыИстекает через <b>%n</b> секундИстекает через <b>%n</b> сек TotpExportSettingsDialog Copy - Копировать + Скопировать NOTE: These TOTP settings are custom and may not work with other authenticators. TOTP QR code dialog warning - Примечание: Эти параметры TOTP пользовательские и могут не работать с другими средства проверки подлинности. + * Эти параметры TOTP - пользовательские, они могут не работать с другими средствами проверки подлинности. There was an error creating the QR code. - Произошла ошибка при создании QR кода. + Ошибка при создании QR-кода. Closing in %1 seconds. - Закрытие через %1 секунд. + Закрытие через %1 сек. @@ -5340,11 +5341,11 @@ Available commands: Use custom settings - Использовать пользовательские параметры + Использовать особые настройки Custom Settings - Пользовательские настройки + Особые настройки Time step: @@ -5392,15 +5393,15 @@ Available commands: An error occurred in retrieving update information. - Возникла ошибка при извлечении информации об обновлении. + Ошибка при получении информации об обновлении. Please try again later. - Пожалуйста, попытайтесь снова позже. + Повторите попытку позже. Software Update - Обновление программного обеспечения + Обновление ПО A new version of KeePassXC is available! @@ -5408,7 +5409,7 @@ Available commands: KeePassXC %1 is now available — you have %2. - Сейчас доступна KeePassXC %1 — у Вас %2. + Доступна KeePassXC версии %1. У вас — %2. Download it at keepassxc.org @@ -5416,11 +5417,11 @@ Available commands: You're up-to-date! - У Вас всё самое свежее! + У вас самая новая версия! KeePassXC %1 is currently the newest version available - KeePassXC %1 на текущий момент является самой новой доступной версией + На данный момент KeePassXC %1 — самая новая версия @@ -5462,19 +5463,19 @@ Available commands: YubiKey Challenge-Response - YubiKey вызов-ответ + Вызов-ответ YubiKey <p>If you own a <a href="https://www.yubico.com/">YubiKey</a>, you can use it for additional security.</p><p>The YubiKey requires one of its slots to be programmed as <a href="https://www.yubico.com/products/services-software/personalization-tools/challenge-response/">HMAC-SHA1 Challenge-Response</a>.</p> - <p>Если Вы владееете <a href="https://www.yubico.com/">YubiKey</a>, Вы можете использовать его для дополнительной безопасности.</p><p>YubiKey требует, чтобы один из его слотов был запрограммирован как <a href="https://www.yubico.com/products/services-software/personalization-tools/challenge-response/"> вызов-ответ HMAC-SHA1</a>.</p> + <p>Если у вас есть <a href="https://www.yubico.com/">YubiKey</a>, его можно использовать для дополнительной безопасности.</p><p>YubiKey требует, чтобы один из его слотов был запрограммирован как <a href="https://www.yubico.com/products/services-software/personalization-tools/challenge-response/"> вызов-ответ HMAC-SHA1</a>.</p> No YubiKey detected, please ensure it's plugged in. - YubiKey не обнаружен, пожалуйста убедитесь, что он подключен. + YubiKey не обнаружен. Убедитесь, что он подключён. No YubiKey inserted. - YubiKey не подключен. + YubiKey не подключён. \ No newline at end of file diff --git a/share/translations/keepassx_sv.ts b/share/translations/keepassx_sv.ts index 059932ecd5..3dcdf1c6eb 100644 --- a/share/translations/keepassx_sv.ts +++ b/share/translations/keepassx_sv.ts @@ -39,7 +39,7 @@ Project Maintainers: - Projekt ansvariga: + Projektansvariga: Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. @@ -220,7 +220,7 @@ Auto-Type typing delay - Auto-skriv fördröjning + Fördröjning för auto-skriv ms @@ -765,7 +765,7 @@ Would you like to migrate your existing settings now? Number of headers line to discard - Antal av rubrik rader att kasta bort + Antal rubrikrader att kasta bort Consider '\' an escape character @@ -1258,7 +1258,7 @@ If you keep this number, your database may be too easy to crack! Max. history items: - Maxantal historik poster: + Maxantal historikposter: Max. history size: @@ -3856,7 +3856,7 @@ Expect some bugs and minor issues, this version is not meant for production use. Word Separator: - Ord separerare: + Ordseparerare: Copy @@ -4008,11 +4008,11 @@ Expect some bugs and minor issues, this version is not meant for production use. Word Co&unt: - + &Antal ord: Regenerate - + Regenerera diff --git a/share/translations/keepassx_tr.ts b/share/translations/keepassx_tr.ts index cb8db274b1..18b248c6f5 100644 --- a/share/translations/keepassx_tr.ts +++ b/share/translations/keepassx_tr.ts @@ -73,7 +73,7 @@ Access error for config file %1 - %1 yapılandırma dosyası için erişim hatası + Yapılandırma dosyası erişim hatası %1 Icon only diff --git a/share/translations/keepassx_uk.ts b/share/translations/keepassx_uk.ts index 6eb5f6eec7..bcc711b134 100644 --- a/share/translations/keepassx_uk.ts +++ b/share/translations/keepassx_uk.ts @@ -27,11 +27,11 @@ Debug Info - Інформація щодо зневадження + Зневаджувальна інформація Include the following information whenever you report a bug: - Коли Ви повідомляєте про ваду, завжди долучайте таку інформацію: + Повідомляючи про проблему, завжди долучайте наступну інформацію: Copy to clipboard @@ -61,7 +61,7 @@ ApplicationSettingsWidget Application Settings - Налаштування застосунку + Налаштування програми General @@ -100,7 +100,7 @@ ApplicationSettingsWidgetGeneral Basic Settings - Базове налаштування + Основні налаштування Startup @@ -116,7 +116,7 @@ Remember last key files and security dongles - Пам'ятати останні файли ключів і механізми захисту + Пам'ятати останні файли ключів та апаратні ключі Load previous databases on startup @@ -172,7 +172,7 @@ General - Загальне + Загальні Hide toolbar (icons) @@ -1425,7 +1425,7 @@ Are you sure you want to continue without a password? The created database has no key or KDF, refusing to save it. This is definitely a bug, please report it to the developers. Створене сховище не має ані ключа, ані ФОК, і тому не може бути збереженим. -Це напевно вада у програмі. Будь ласка, повідомте про це розробникам. +Це певно є вадою програми, будь ласка, повідомте про це розробникам. The database file does not exist or is not accessible. @@ -3564,8 +3564,8 @@ We recommend you use the AppImage available on our downloads page. NOTE: You are using a pre-release version of KeePassXC! Expect some bugs and minor issues, this version is not meant for production use. - <b>Примітка</b>: Ви використовуєте попередню версію KeePassXC! -Зважайте на можливість деяких вади та незначних проблем, ця версія не призначена для повсякденного користування. + <b>Примітка</b>: Ви використовуєте попередній випуск KeePassXC! +Зважайте на ймовірні помилки та незначні проблеми, ця версія не призначена для повсякденного користування. Check for updates on startup? diff --git a/share/translations/keepassx_zh_CN.ts b/share/translations/keepassx_zh_CN.ts index d1b17aae03..e9ed56c456 100644 --- a/share/translations/keepassx_zh_CN.ts +++ b/share/translations/keepassx_zh_CN.ts @@ -4672,7 +4672,7 @@ Available commands: Cannot find group %1. - 找不到组%1。 + 找不到群组%1。 Error reading merge file: @@ -4760,7 +4760,7 @@ Available commands: No groups found - 未找到组 + 未找到群组 Create a new database. diff --git a/share/translations/keepassx_zh_TW.ts b/share/translations/keepassx_zh_TW.ts index 7c5b5deb0a..f5dfd589f7 100644 --- a/share/translations/keepassx_zh_TW.ts +++ b/share/translations/keepassx_zh_TW.ts @@ -54,7 +54,7 @@ Use OpenSSH for Windows instead of Pageant - + 使用 OpenSSH for Windows 而不是 Pageant @@ -93,7 +93,7 @@ Follow style - + 遵照系統樣式 @@ -132,11 +132,11 @@ Safely save database files (may be incompatible with Dropbox, etc) - + 安全儲存資料庫檔案 (可能與 Dropbox 等服務不相容) Backup database file before saving - + 儲存資料庫檔案前先備份 Automatically save after every change @@ -168,7 +168,7 @@ Hide the entry preview panel - + 隱藏預覽項目區域 General @@ -176,11 +176,11 @@ Hide toolbar (icons) - + 隱藏工具列 (圖示) Minimize instead of app exit - + 以最小化取代關閉程式 Show a system tray icon @@ -220,7 +220,7 @@ Auto-Type typing delay - + 自動輸入按鍵延遲 ms @@ -229,23 +229,23 @@ Auto-Type start delay - + 自動輸入啟動延遲 Check for updates at application startup - + 程式啟動時檢查更新 Include pre-releases when checking for updates - + 檢查更新時包括預先發行版本 Movable toolbar - + 可移動的工具列 Button style - + 按鈕樣式 @@ -269,11 +269,11 @@ min - + 分鐘 Forget TouchID after inactivity of - + 遺忘 TouchID 當閒置 Convenience @@ -285,7 +285,7 @@ Forget TouchID when session is locked or lid is closed - + 當工作階段鎖定或蓋上螢幕時遺忘 TouchID Lock databases after minimizing the window @@ -293,7 +293,7 @@ Re-lock previously locked database after performing Auto-Type - + 自動輸入後,將原本鎖定的資料庫重新鎖定 Don't require password repeat when it is visible @@ -301,15 +301,15 @@ Don't hide passwords when editing them - + 編輯時不隱藏密碼 Don't use placeholder for empty password fields - + 不於空白密碼欄位處填入替代字符 Hide passwords in the entry preview panel - + 在項目預覽區內隱藏密碼 Hide entry notes by default @@ -429,11 +429,11 @@ Please select whether you want to allow access. BrowserEntrySaveDialog KeePassXC-Browser Save Entry - + KeePassXC-Browser 瀏覽器擴充功能儲存項目 Ok - + 確定 Cancel @@ -442,7 +442,8 @@ Please select whether you want to allow access. You have multiple databases open. Please select the correct database for saving credentials. - + 您開啟了多個資料庫。 +請選擇您想要儲存憑證的資料庫。 @@ -589,11 +590,11 @@ Please select the correct database for saving credentials. &Tor Browser - + &Tor 瀏覽器 <b>Warning</b>, the keepassxc-proxy application was not found!<br />Please check the KeePassXC installation directory or confirm the custom path in advanced options.<br />Browser integration WILL NOT WORK without the proxy application.<br />Expected Path: - + <b>警告</b>,找不到 keepassxc-proxy 應用程式!<br />請檢查 KeePassXC 安裝目錄,或在進階選項中確認自定路徑。<br />缺少此應用程式瀏覽器整合將無法運作。<br />預期的路徑: Executable Files @@ -606,7 +607,7 @@ Please select the correct database for saving credentials. Do not ask permission for HTTP &Basic Auth An extra HTTP Basic Auth setting - + 不確認 HTTP 權限 Due to Snap sandboxing, you must run a script to enable browser integration.<br />You can obtain this script from %1 @@ -618,7 +619,7 @@ Please select the correct database for saving credentials. KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3 - + 需要 KeePassXC-Browser 瀏覽器擴充功能才能使用瀏覽器整合。為 %1 及 %2 下載。%3 @@ -883,7 +884,7 @@ Would you like to migrate your existing settings now? DatabaseOpenWidget Enter master key - 輸入主金鑰 + 輸入主密碼 Key File: @@ -972,7 +973,7 @@ Please consider generating a new key file. Master Key - + 主密碼 Encryption Settings @@ -987,7 +988,7 @@ Please consider generating a new key file. DatabaseSettingsWidgetBrowser KeePassXC-Browser settings - + KeePassXC-Browser 瀏覽器擴充功能設定 &Disconnect all browsers @@ -995,7 +996,7 @@ Please consider generating a new key file. Forg&et all site-specific settings on entries - + 遺忘目前項目中所有站台相關的設定 (&e) Move KeePassHTTP attributes to KeePassXC-Browser &custom data @@ -1032,7 +1033,7 @@ This may prevent connection to the browser plugin. Disconnect all browsers - + 與所有瀏覽器中斷連線 Do you really want to disconnect all browsers? @@ -1291,7 +1292,7 @@ If you keep this number, your database may be too easy to crack! DatabaseSettingsWidgetKeeShare Sharing - + 分享 Breadcrumb @@ -1299,11 +1300,11 @@ If you keep this number, your database may be too easy to crack! Type - + 種類 Path - + 路徑 Last Signer @@ -1311,12 +1312,12 @@ If you keep this number, your database may be too easy to crack! Certificates - + 憑證 > Breadcrumb separator - + > @@ -1335,13 +1336,15 @@ If you keep this number, your database may be too easy to crack! No password set - + 沒有設定密碼 WARNING! You have not set a password. Using a database without a password is strongly discouraged! Are you sure you want to continue without a password? - + 警告!您尚未設定密碼。誠心建議不要使用不含密碼的資料庫檔案! + +您確定還是要在無密碼的情形下繼續? Unknown error @@ -1349,7 +1352,7 @@ Are you sure you want to continue without a password? Failed to change master key - + 更改主密碼失敗 @@ -1403,7 +1406,7 @@ Are you sure you want to continue without a password? Database creation error - + 資料庫建立錯誤 The created database has no key or KDF, refusing to save it. @@ -1425,17 +1428,17 @@ This is definitely a bug, please report it to the developers. %1 [New Database] Database tab name modifier - + %1 [新的資料庫] %1 [Locked] Database tab name modifier - + %1 [已鎖定] %1 [Read-only] Database tab name modifier - + %1 [唯讀] @@ -1532,7 +1535,7 @@ Do you want to merge your changes? Lock Database? - + 鎖定資料庫? You are editing an entry. Discard changes and lock anyway? @@ -1547,7 +1550,8 @@ Save changes? Database was modified. Save changes? - + 資料庫已修改。 +儲存變更? Save changes? @@ -1560,7 +1564,7 @@ Error: %1 Disable safe saves? - 關閉安全存檔? + 關閉安全存檔? KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. @@ -2565,7 +2569,7 @@ This may cause the affected plugins to malfunction. Kdbx3Reader Unable to calculate master key - 無法計算主金鑰 + 無法計算主密碼 Unable to issue challenge-response. @@ -2604,7 +2608,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - 無法計算主金鑰 + 無法計算主密碼 @@ -2615,7 +2619,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - 無法計算主金鑰 + 無法計算主密碼 Invalid header checksum size @@ -2743,7 +2747,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - 無法計算主金鑰 + 無法計算主密碼 Failed to serialize KDF parameters variant map @@ -3010,7 +3014,7 @@ Line %2, column %3 Unable to calculate master key - 無法計算主金鑰 + 無法計算主密碼 Wrong key or database file is corrupt. @@ -3174,17 +3178,17 @@ Line %2, column %3 Change %1 Change a key component - + 更改%1 Remove %1 Remove a key component - + 移除%1 %1 set, click to change or remove Change or remove a key component - + %1已設定,點選以更改或移除 @@ -3214,7 +3218,9 @@ Line %2, column %3 unsupported in the future. Please go to the master key settings and generate a new key file. - + 你正在使用未來將不再支援的舊式金鑰檔案格式。 + +請至主密碼設定產生新的金鑰。 Error loading the key file '%1' @@ -3414,11 +3420,11 @@ This version is not meant for production use. &Donate - + 捐贈 (&D) Report a &bug - + 回報錯誤 (&b) WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard! @@ -3427,75 +3433,75 @@ We recommend you use the AppImage available on our downloads page. &Import - + 匯入 (&I) Copy att&ribute... - + 複製屬性 (&r)… TOTP... - + 基於時間的一次性密碼算法… &New database... - + 新增資料庫(&N)… Create a new database - + 建立新資料庫 &Merge from database... - + 與資料庫合併(&M)… Merge from another KDBX database - + 與其他 KDBX 資料庫合併 &New entry - + 新增項目(&N) Add a new entry - + 添加新項目 &Edit entry - + 編輯項目(&E) View or edit entry - + 檢視或編輯項目 &New group - + 新增群組 (&N) Add a new group - + 添加新群組 Change master &key... - + 更改主密碼(&k)… &Database settings... - + 資料庫設定(&D)… Copy &password - + 複製密碼(&p) Perform &Auto-Type - + 進行自動輸入 (&A) Open &URL - + 開啟網址(&U) KeePass 1 database... @@ -3515,7 +3521,7 @@ We recommend you use the AppImage available on our downloads page. Show TOTP... - + 顯示 TOTP… Show TOTP QR Code... @@ -3626,11 +3632,11 @@ Expect some bugs and minor issues, this version is not meant for production use. En&cryption Settings - + 加密設定 (&c) Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + 你可以在這裡調整資料庫加密設定。別擔心,你之後還能在資料庫設定中變更。 Advanced Settings @@ -3649,18 +3655,18 @@ Expect some bugs and minor issues, this version is not meant for production use. Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. - + 你可以在這裡調整資料庫加密設定。別擔心,你之後還能在資料庫設定中變更。 NewDatabaseWizardPageMasterKey Database Master Key - + 資料庫主密碼 A master key known only to you protects your database. - + 只有你知道的密碼才能保護你的資料庫。 @@ -5047,7 +5053,7 @@ Available commands: Path - + 路徑 Status @@ -5361,11 +5367,11 @@ Available commands: Please try again later. - + 請稍後再試。 Software Update - + 軟體更新 A new version of KeePassXC is available! @@ -5377,7 +5383,7 @@ Available commands: Download it at keepassxc.org - + 在 keepassxc.org 下載 You're up-to-date! diff --git a/snap/local/launchers/README.md b/snap/local/launchers/README.md new file mode 100644 index 0000000000..334fbbdcc5 --- /dev/null +++ b/snap/local/launchers/README.md @@ -0,0 +1,11 @@ +# /snap/local/launchers +Here are the launchers, or wrapper programs to deal with some runtime-fixable problems for the snapped applications, like setting proper environmental variables in snap. + +In convention launchers are named _something_-launch, for dealing certain problem with _something_, and usually can be called in a stacked manner to consolidate their modifications. + +```yaml +apps: + _app_name_: + command: foo-launch bar-launch _app_command_ +``` + diff --git a/snap/local/launchers/gtk3-env-launch b/snap/local/launchers/gtk3-env-launch new file mode 100755 index 0000000000..f017e86116 --- /dev/null +++ b/snap/local/launchers/gtk3-env-launch @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# This is the maintainence launcher for the snap, make necessary runtime environment changes to make the snap work here. You may also insert security confinement/deprecation/obsoletion notice of the snap here. + +set \ + -o errexit \ + -o errtrace \ + -o nounset \ + -o pipefail + +# gtk-common-themes support +export QT_QPA_PLATFORMTHEME=gtk3 + +# Finally run the next part of the command chain +exec "${@}" diff --git a/snapcraft.yaml b/snap/snapcraft.yaml similarity index 74% rename from snapcraft.yaml rename to snap/snapcraft.yaml index fdeef77661..c509d4c552 100644 --- a/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: keepassxc -version: 2.4.1 +version: 2.4.2 grade: stable summary: Community-driven port of the Windows application “KeePass Password Safe” description: | @@ -9,16 +9,28 @@ description: | confinement: strict base: core18 -plugs: - icon-themes: # fix mouse cursor theme +plugs: # plugs for theming, font settings, cursor and to use gtk3 file chooser + gtk-3-themes: + interface: content + target: $SNAP/data-dir/themes + default-provider: gtk-common-themes:gtk-3-themes + icon-themes: interface: content target: $SNAP/data-dir/icons - default-provider: gtk-common-themes + default-provider: gtk-common-themes:icon-themes + sound-themes: + interface: content + target: $SNAP/data-dir/sounds + default-provider: gtk-common-themes:sounds-themes apps: keepassxc: - command: desktop-launch keepassxc - plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb, wayland, desktop-legacy] + adapter: full + command: usr/bin/keepassxc -style fusion + command-chain: + - bin/desktop-launch + - bin/gtk3-env-launch + plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb, wayland, desktop-legacy, desktop] desktop: usr/share/applications/org.keepassxc.KeePassXC.desktop environment: DISABLE_WAYLAND: 1 @@ -68,12 +80,12 @@ parts: - libxtst6 - libqt5x11extras5 - libqt5svg5 - - libqrencode3 + - try: [libqrencode3, libqrencode4] - libqt5concurrent5 - libquazip5-1 - libusb-1.0-0 - qtwayland5 - - qt5-style-plugins # for mouse cursor theme fix + - qt5-gtk-platformtheme # for theming, font settings, cursor and to use gtk3 file chooser override-build: | snapcraftctl build sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.KeePassXC.desktop @@ -82,7 +94,15 @@ parts: stage: - -opt after: [desktop-qt5] - + + launchers: # custom launcher to set QT_QPA_PLATFORMTHEME=gtk3 correctly + source: snap/local/launchers + plugin: dump + organize: + '*': bin/ + stage: + - -bin/README.* + desktop-qt5: source: https://github.com/ubuntu/snapcraft-desktop-helpers.git source-subdir: qt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8eb681e38..f142f36800 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,9 +16,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -configure_file(config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h) -configure_file(git-info.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/git-info.h) - find_library(ZXCVBN_LIBRARIES zxcvbn) if(NOT ZXCVBN_LIBRARIES) add_library(zxcvbn STATIC zxcvbn/zxcvbn.c) @@ -27,6 +24,7 @@ if(NOT ZXCVBN_LIBRARIES) endif(NOT ZXCVBN_LIBRARIES) set(keepassx_SOURCES + core/Alloc.cpp core/AutoTypeAssociations.cpp core/AutoTypeMatch.cpp core/Compare.cpp @@ -167,7 +165,8 @@ if(APPLE) core/ScreenLockListenerMac.cpp core/MacPasteboard.cpp gui/macutils/MacUtils.cpp - gui/macutils/AppKitImpl.mm) + gui/macutils/AppKitImpl.mm + gui/macutils/AppKit.h) endif() if(UNIX AND NOT APPLE) set(keepassx_SOURCES @@ -192,8 +191,7 @@ add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing") add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)") add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser") add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent") -add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare") -add_feature_info(KeeShare-Secure WITH_XC_KEESHARE_SECURE "Sharing integration with KeeShare with secure sources") +add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)") add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response") add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking") if(APPLE) @@ -254,8 +252,13 @@ endif() if(WITH_XC_TOUCHID) list(APPEND keepassx_SOURCES touchid/TouchID.mm) + # TODO: Remove -Wno-error once deprecation warnings have been resolved. + set_source_files_properties(touchid/TouchID.mm PROPERTY COMPILE_FLAGS "-Wno-old-style-cast -Wno-error") endif() +configure_file(config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h) +configure_file(git-info.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/git-info.h) + add_library(autotype STATIC ${autotype_SOURCES}) target_link_libraries(autotype Qt5::Core Qt5::Widgets) @@ -270,6 +273,7 @@ target_link_libraries(keepassx_core Qt5::Concurrent Qt5::Network Qt5::Widgets + ${sodium_LIBRARY_RELEASE} ${YUBIKEY_LIBRARIES} ${ZXCVBN_LIBRARIES} ${ARGON2_LIBRARIES} @@ -410,25 +414,19 @@ if(MINGW) install(CODE "set(gp_tool \"objdump\")" COMPONENT Runtime) - include(DeployQt4) - install_qt4_executable(${PROGNAME}.exe) - - # install Qt5 plugins - set(PLUGINS_DIR ${Qt5_PREFIX}/share/qt5/plugins) - install(FILES - ${PLUGINS_DIR}/platforms/qwindows$<$:d>.dll - ${PLUGINS_DIR}/platforms/qdirect2d$<$:d>.dll - DESTINATION "platforms") - install(FILES ${PLUGINS_DIR}/styles/qwindowsvistastyle$<$:d>.dll DESTINATION "styles") - install(FILES ${PLUGINS_DIR}/platforminputcontexts/qtvirtualkeyboardplugin$<$:d>.dll DESTINATION "platforminputcontexts") - install(FILES ${PLUGINS_DIR}/iconengines/qsvgicon$<$:d>.dll DESTINATION "iconengines") - install(FILES - ${PLUGINS_DIR}/imageformats/qgif$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qicns$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qico$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qjpeg$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qwebp$<$:d>.dll - DESTINATION "imageformats") + # Deploy all 3rd party library dependencies first + install(CODE "include(BundleUtilities) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${PROGNAME}.exe\" \"\" \"\")" + COMPONENT Runtime) + + # Use windeployqt.exe to setup Qt dependencies + set(WINDEPLOYQT_MODE "--release") + if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") + set(WINDEPLOYQT_MODE "--debug") + endif() + + install(CODE "execute_process(COMMAND ${WINDEPLOYQT_EXE} ${PROGNAME}.exe ${WINDEPLOYQT_MODE} WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX} OUTPUT_QUIET)" + COMPONENT Runtime) # install CA cert chains install(FILES ${Qt5_PREFIX}/ssl/certs/ca-bundle.crt DESTINATION "ssl/certs") diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 0f772d8d3e..20fabea883 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -47,7 +47,7 @@ AutoType::AutoType(QObject* parent, bool test) , m_pluginLoader(new QPluginLoader(this)) , m_plugin(nullptr) , m_executor(nullptr) - , m_windowFromGlobal(0) + , m_windowForGlobal(0) { // prevent crash when the plugin has unresolved symbols m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint); @@ -90,7 +90,7 @@ void AutoType::loadPlugin(const QString& pluginPath) if (m_plugin) { if (m_plugin->isAvailable()) { m_executor = m_plugin->createExecutor(); - connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered())); + connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType())); } else { unloadPlugin(); } @@ -222,6 +222,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt())); + // Used only for selected entry auto-type if (!window) { window = m_plugin->activeWindow(); } @@ -240,6 +241,9 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c QCoreApplication::processEvents(QEventLoop::AllEvents, 10); } + m_windowForGlobal = 0; + m_windowTitleForGlobal.clear(); + // emit signal only if autotype performed correctly emit autotypePerformed(); @@ -264,6 +268,13 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow) executeAutoTypeActions(entry, hideWindow, sequences.first()); } +void AutoType::startGlobalAutoType() +{ + m_windowForGlobal = m_plugin->activeWindow(); + m_windowTitleForGlobal = m_plugin->activeWindowTitle(); + emit globalAutoTypeTriggered(); +} + /** * Global Autotype entry-point function * Perform global Auto-Type on the active window @@ -278,9 +289,7 @@ void AutoType::performGlobalAutoType(const QList>& dbLi return; } - QString windowTitle = m_plugin->activeWindowTitle(); - - if (windowTitle.isEmpty()) { + if (m_windowTitleForGlobal.isEmpty()) { m_inGlobalAutoTypeDialog.unlock(); return; } @@ -290,7 +299,7 @@ void AutoType::performGlobalAutoType(const QList>& dbLi for (const auto& db : dbList) { const QList dbEntries = db->rootGroup()->entriesRecursive(); for (Entry* entry : dbEntries) { - const QSet sequences = autoTypeSequences(entry, windowTitle).toSet(); + const QSet sequences = autoTypeSequences(entry, m_windowTitleForGlobal).toSet(); for (const QString& sequence : sequences) { if (!sequence.isEmpty()) { matchList << AutoTypeMatch(entry, sequence); @@ -304,8 +313,9 @@ void AutoType::performGlobalAutoType(const QList>& dbLi auto* msgBox = new QMessageBox(); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setWindowTitle(tr("Auto-Type - KeePassXC")); - msgBox->setText( - tr("Couldn't find an entry that matches the window title:").append("\n\n").append(windowTitle)); + msgBox->setText(tr("Couldn't find an entry that matches the window title:") + .append("\n\n") + .append(m_windowTitleForGlobal)); msgBox->setIcon(QMessageBox::Information); msgBox->setStandardButtons(QMessageBox::Ok); msgBox->show(); @@ -316,10 +326,9 @@ void AutoType::performGlobalAutoType(const QList>& dbLi m_inGlobalAutoTypeDialog.unlock(); emit autotypeRejected(); } else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) { - executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence); + executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence, m_windowForGlobal); m_inGlobalAutoTypeDialog.unlock(); } else { - m_windowFromGlobal = m_plugin->activeWindow(); auto* selectDialog = new AutoTypeSelectDialog(); // connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex @@ -327,11 +336,12 @@ void AutoType::performGlobalAutoType(const QList>& dbLi connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal())); selectDialog->setMatchList(matchList); -#if defined(Q_OS_MACOS) +#ifdef Q_OS_MACOS m_plugin->raiseOwnWindow(); - Tools::wait(500); + Tools::wait(200); #endif selectDialog->show(); + selectDialog->raise(); // necessary when the main window is minimized selectDialog->activateWindow(); } @@ -339,8 +349,8 @@ void AutoType::performGlobalAutoType(const QList>& dbLi void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match) { - m_plugin->raiseWindow(m_windowFromGlobal); - executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal); + m_plugin->raiseWindow(m_windowForGlobal); + executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowForGlobal); // make sure the mutex is definitely locked before we unlock it Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock()); @@ -353,6 +363,8 @@ void AutoType::autoTypeRejectedFromGlobal() // so make sure the mutex is locked before we try unlocking it Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock()); m_inGlobalAutoTypeDialog.unlock(); + m_windowForGlobal = 0; + m_windowTitleForGlobal.clear(); emit autotypeRejected(); } diff --git a/src/autotype/AutoType.h b/src/autotype/AutoType.h index f58a1c0c1e..ad8607529e 100644 --- a/src/autotype/AutoType.h +++ b/src/autotype/AutoType.h @@ -62,18 +62,19 @@ public slots: void raiseWindow(); signals: - void globalShortcutTriggered(); + void globalAutoTypeTriggered(); void autotypePerformed(); void autotypeRejected(); private slots: + void startGlobalAutoType(); void performAutoTypeFromGlobal(AutoTypeMatch match); void autoTypeRejectedFromGlobal(); void unloadPlugin(); private: explicit AutoType(QObject* parent = nullptr, bool test = false); - ~AutoType(); + ~AutoType() override; void loadPlugin(const QString& pluginPath); void executeAutoTypeActions(const Entry* entry, QWidget* hideWindow = nullptr, @@ -94,9 +95,11 @@ private slots: QPluginLoader* m_pluginLoader; AutoTypePlatformInterface* m_plugin; AutoTypeExecutor* m_executor; - WId m_windowFromGlobal; static AutoType* m_instance; + QString m_windowTitleForGlobal; + WId m_windowForGlobal; + Q_DISABLE_COPY(AutoType) }; diff --git a/src/autotype/mac/CMakeLists.txt b/src/autotype/mac/CMakeLists.txt index f1c5387f34..7427450a10 100644 --- a/src/autotype/mac/CMakeLists.txt +++ b/src/autotype/mac/CMakeLists.txt @@ -1,10 +1,6 @@ set(autotype_mac_SOURCES AutoTypeMac.cpp) -set(autotype_mac_mm_SOURCES - ${CMAKE_SOURCE_DIR}/src/gui/macutils/AppKitImpl.mm - ${CMAKE_SOURCE_DIR}/src/gui/macutils/MacUtils.cpp) - -add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES} ${autotype_mac_mm_SOURCES}) +add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES}) set_target_properties(keepassx-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon") target_link_libraries(keepassx-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets) diff --git a/src/autotype/test/AutoTypeTest.cpp b/src/autotype/test/AutoTypeTest.cpp index 9a1b650130..225698e628 100644 --- a/src/autotype/test/AutoTypeTest.cpp +++ b/src/autotype/test/AutoTypeTest.cpp @@ -68,6 +68,11 @@ AutoTypeExecutor* AutoTypePlatformTest::createExecutor() return new AutoTypeExecutorTest(this); } +void AutoTypePlatformTest::triggerGlobalAutoType() +{ + emit globalShortcutTriggered(); +} + void AutoTypePlatformTest::setActiveWindowTitle(const QString& title) { m_activeWindowTitle = title; diff --git a/src/autotype/test/AutoTypeTest.h b/src/autotype/test/AutoTypeTest.h index 87d19491ae..ef19c1dd76 100644 --- a/src/autotype/test/AutoTypeTest.h +++ b/src/autotype/test/AutoTypeTest.h @@ -48,6 +48,7 @@ class AutoTypePlatformTest : public QObject, public AutoTypePlatformInterface, p bool raiseOwnWindow() override; #endif + void triggerGlobalAutoType() override; void setActiveWindowTitle(const QString& title) override; QString actionChars() override; diff --git a/src/autotype/test/AutoTypeTestInterface.h b/src/autotype/test/AutoTypeTestInterface.h index 7681f2ecb8..4595b0eb80 100644 --- a/src/autotype/test/AutoTypeTestInterface.h +++ b/src/autotype/test/AutoTypeTestInterface.h @@ -26,6 +26,7 @@ class AutoTypeTestInterface virtual ~AutoTypeTestInterface() { } + virtual void triggerGlobalAutoType() = 0; virtual void setActiveWindowTitle(const QString& title) = 0; virtual QString actionChars() = 0; diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 5351709393..3c8df5ffc2 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -94,6 +94,8 @@ class BrowserAction : public QObject QString m_publicKey; QString m_secretKey; bool m_associated; + + friend class TestBrowser; }; #endif // BROWSERACTION_H diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index 9eecc63f9a..e212fc6b48 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -120,6 +120,7 @@ void BrowserOptionDialog::loadSettings() m_ui->useCustomProxy->setChecked(settings->useCustomProxy()); m_ui->customProxyLocation->setText(settings->customProxyLocation()); m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath()); + m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials()); m_ui->chromeSupport->setChecked(settings->chromeSupport()); m_ui->chromiumSupport->setChecked(settings->chromiumSupport()); m_ui->firefoxSupport->setChecked(settings->firefoxSupport()); @@ -176,6 +177,7 @@ void BrowserOptionDialog::saveSettings() settings->setCustomProxyLocation(m_ui->customProxyLocation->text()); settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked()); + settings->setAllowExpiredCredentials(m_ui->allowExpiredCredentials->isChecked()); settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked()); settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked()); settings->setHttpAuthPermission(m_ui->httpAuthPermission->isChecked()); diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 50fd9d205c..0229649f13 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -219,6 +219,16 @@ + + + + Returns expired credentials. String [expired] is added to the title. + + + &Allow returning expired credentials. + + + diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 9c06c24875..3916b99ad9 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -172,9 +172,9 @@ QJsonArray BrowserService::getChildrenFromGroup(Group* group) return groupList; } -QJsonObject BrowserService::getDatabaseGroups() +QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer& selectedDb) { - auto db = getDatabase(); + auto db = selectedDb ? selectedDb : getDatabase(); if (!db) { return {}; } @@ -296,6 +296,7 @@ QString BrowserService::storeKey(const QString& key) do { QInputDialog keyDialog; + connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &keyDialog, SLOT(reject())); keyDialog.setWindowTitle(tr("KeePassXC: New key association request")); keyDialog.setLabelText(tr("You have received an association request for the above key.\n\n" "If you would like to allow it access to your KeePassXC database,\n" @@ -310,7 +311,7 @@ QString BrowserService::storeKey(const QString& key) id = keyDialog.textValue(); - if (ok != QDialog::Accepted || id.isEmpty()) { + if (ok != QDialog::Accepted || id.isEmpty() || !isDatabaseOpened()) { hideWindow(); return {}; } @@ -406,6 +407,11 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, return QJsonArray(); } + // Ensure that database is not locked when the popup was visible + if (!isDatabaseOpened()) { + return QJsonArray(); + } + // Sort results pwEntries = sortEntries(pwEntries, host, submitUrl); @@ -447,11 +453,6 @@ void BrowserService::addEntry(const QString& id, return; } - auto* addEntryGroup = findCreateAddEntryGroup(db); - if (!addEntryGroup) { - return; - } - auto* entry = new Entry(); entry->setUuid(QUuid::createUuid()); entry->setTitle(QUrl(url).host()); @@ -459,16 +460,19 @@ void BrowserService::addEntry(const QString& id, entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON); entry->setUsername(login); entry->setPassword(password); - entry->setGroup(addEntryGroup); // Select a group for the entry if (!group.isEmpty()) { if (db->rootGroup()) { auto selectedGroup = db->rootGroup()->findGroupByUuid(Tools::hexToUuid(groupUuid)); - if (selectedGroup && selectedGroup->name() == group) { + if (selectedGroup) { entry->setGroup(selectedGroup); + } else { + entry->setGroup(getDefaultEntryGroup(db)); } } + } else { + entry->setGroup(getDefaultEntryGroup(db)); } const QString host = QUrl(url).host(); @@ -760,6 +764,7 @@ bool BrowserService::confirmEntries(QList& pwEntriesToConfirm, m_dialogActive = true; BrowserAccessControlDialog accessControlDialog; + connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &accessControlDialog, SLOT(reject())); accessControlDialog.setUrl(url); accessControlDialog.setItems(pwEntriesToConfirm); @@ -811,6 +816,10 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry) res["totp"] = entry->totp(); } + if (entry->isExpired()) { + res["expired"] = "true"; + } + if (browserSettings()->supportKphFields()) { const EntryAttributes* attr = entry->attributes(); QJsonArray stringFields; @@ -834,7 +843,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri return Unknown; } if (entry->isExpired()) { - return Denied; + return browserSettings()->allowExpiredCredentials() ? Allowed : Denied; } if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) { return Allowed; @@ -848,7 +857,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri return Unknown; } -Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer& selectedDb) +Group* BrowserService::getDefaultEntryGroup(const QSharedPointer& selectedDb) { auto db = selectedDb ? selectedDb : getDatabase(); if (!db) { @@ -861,7 +870,7 @@ Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer& s } const QString groupName = - QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); // TODO: setting to decide where new keys are created + QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); for (auto* g : rootGroup->groupsRecursive(true)) { if (g->name() == groupName && !g->isRecycled()) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index a8f04262ff..77d94b6bd6 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -44,7 +44,7 @@ class BrowserService : public QObject bool openDatabase(bool triggerUnlock); QString getDatabaseRootUuid(); QString getDatabaseRecycleBinUuid(); - QJsonObject getDatabaseGroups(); + QJsonObject getDatabaseGroups(const QSharedPointer& selectedDb = {}); QJsonObject createNewGroup(const QString& groupName); QString getKey(const QString& id); void addEntry(const QString& id, @@ -114,7 +114,7 @@ public slots: const QString& realm); QJsonObject prepareEntry(const Entry* entry); Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); - Group* findCreateAddEntryGroup(const QSharedPointer& selectedDb = {}); + Group* getDefaultEntryGroup(const QSharedPointer& selectedDb = {}); int sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const; bool matchUrlScheme(const QString& url); @@ -135,6 +135,8 @@ public slots: bool m_bringToFrontRequested; WindowState m_prevWindowState; QUuid m_keepassBrowserUUID; + + friend class TestBrowser; }; #endif // BROWSERSERVICE_H diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index dd74dc1cb6..f44c5e8021 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -194,6 +194,16 @@ void BrowserSettings::setUpdateBinaryPath(bool enabled) config()->set("Browser/UpdateBinaryPath", enabled); } +bool BrowserSettings::allowExpiredCredentials() +{ + return config()->get("Browser/AllowExpiredCredentials", false).toBool(); +} + +void BrowserSettings::setAllowExpiredCredentials(bool enabled) +{ + config()->set("Browser/AllowExpiredCredentials", enabled); +} + bool BrowserSettings::chromeSupport() { return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index ba74ff53e5..b47e928668 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -64,6 +64,8 @@ class BrowserSettings void setCustomProxyLocation(const QString& location); bool updateBinaryPath(); void setUpdateBinaryPath(bool enabled); + bool allowExpiredCredentials(); + void setAllowExpiredCredentials(bool enabled); bool chromeSupport(); void setChromeSupport(bool enabled); bool chromiumSupport(); diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index 10189d931c..7e813eb5bf 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -16,7 +16,6 @@ if(WITH_XC_BROWSER) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - find_package(sodium 1.0.12 REQUIRED) set(keepassxcbrowser_SOURCES BrowserAccessControlDialog.cpp @@ -33,5 +32,5 @@ if(WITH_XC_BROWSER) Variant.cpp) add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES}) - target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network sodium) + target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${sodium_LIBRARY_RELEASE}) endif() diff --git a/src/browser/NativeMessagingBase.cpp b/src/browser/NativeMessagingBase.cpp index a6b8d97c0e..33592ce3a3 100644 --- a/src/browser/NativeMessagingBase.cpp +++ b/src/browser/NativeMessagingBase.cpp @@ -19,6 +19,8 @@ #include "NativeMessagingBase.h" #include +#include "config-keepassx.h" + #if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) #include #include @@ -138,7 +140,7 @@ QString NativeMessagingBase::getLocalServerPath() const { const QString serverPath = "/kpxc_server"; #if defined(KEEPASSXC_DIST_SNAP) - return QProcessEnvironment::systemEnvironment().value("SNAP_COMMON") + serverPath; + return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverPath; #elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // Use XDG_RUNTIME_DIR instead of /tmp if it's available QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 395b849195..975d549e5f 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -84,7 +84,7 @@ int Add::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli add"); + errorTextStream << parser.helpText().replace("[options]", "add [options]"); return EXIT_FAILURE; } diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index c3f97a2cdc..2f4a7275e3 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -38,6 +38,7 @@ target_link_libraries(keepassxc-cli keepassx_core Qt5::Core ${GCRYPT_LIBRARIES} + ${sodium_LIBRARY_RELEASE} ${ARGON2_LIBRARIES} ${GPGERROR_LIBRARIES} ${ZLIB_LIBRARIES} diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 31b421de6c..e1e74c682a 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -63,7 +63,7 @@ int Clip::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2 && args.size() != 3) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli clip"); + errorTextStream << parser.helpText().replace("[options]", "clip [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Create.cpp b/src/cli/Create.cpp index b8c094f900..80dcb5691a 100644 --- a/src/cli/Create.cpp +++ b/src/cli/Create.cpp @@ -70,7 +70,7 @@ int Create::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() < 1) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli create"); + out << parser.helpText().replace("[options]", "create [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index f113473446..c663cfc393 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -58,7 +58,7 @@ int Diceware::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + errorTextStream << parser.helpText().replace("[options]", "diceware [options]"); return EXIT_FAILURE; } @@ -78,7 +78,7 @@ int Diceware::execute(const QStringList& arguments) } if (!dicewareGenerator.isValid()) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + outputTextStream << parser.helpText().replace("[options]", "diceware [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index 76e996c981..59cedd7c92 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -88,7 +88,7 @@ int Edit::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); + errorTextStream << parser.helpText().replace("[options]", "edit [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Estimate.cpp b/src/cli/Estimate.cpp index 7064963f4f..c278b50f3f 100644 --- a/src/cli/Estimate.cpp +++ b/src/cli/Estimate.cpp @@ -171,7 +171,7 @@ int Estimate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() > 1) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate"); + errorTextStream << parser.helpText().replace("[options]", "estimate [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 729687fe31..2e4e6f9cd9 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -57,7 +57,7 @@ int Extract::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 1) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract"); + errorTextStream << parser.helpText().replace("[options]", "extract [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Generate.cpp b/src/cli/Generate.cpp index 5f0ad98ac7..e8ca90275d 100644 --- a/src/cli/Generate.cpp +++ b/src/cli/Generate.cpp @@ -84,7 +84,7 @@ int Generate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + errorTextStream << parser.helpText().replace("[options]", "generate [options]"); return EXIT_FAILURE; } @@ -128,7 +128,7 @@ int Generate::execute(const QStringList& arguments) passwordGenerator.setExcludedChars(parser.value(exclude)); if (!passwordGenerator.isValid()) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + errorTextStream << parser.helpText().replace("[options]", "generate [options]"); return EXIT_FAILURE; } diff --git a/src/cli/List.cpp b/src/cli/List.cpp index ebf7bfda1b..52797470cc 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -59,7 +59,7 @@ int List::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 1 && args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli ls"); + errorTextStream << parser.helpText().replace("[options]", "ls [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index 81bbdd55d0..af5f24196a 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -56,7 +56,7 @@ int Locate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli locate"); + errorTextStream << parser.helpText().replace("[options]", "locate [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index a7357394f7..2356f5d3ac 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -69,7 +69,7 @@ int Merge::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli merge"); + errorTextStream << parser.helpText().replace("[options]", "merge [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 07da23b7b4..bb2374e9aa 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -58,7 +58,7 @@ int Remove::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); + errorTextStream << parser.helpText().replace("[options]", "rm [options]"); return EXIT_FAILURE; } diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index d16fbfe3c5..3abccd79c6 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -69,7 +69,7 @@ int Show::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli show"); + errorTextStream << parser.helpText().replace("[options]", "show [options]"); return EXIT_FAILURE; } diff --git a/src/cli/TextStream.cpp b/src/cli/TextStream.cpp index d75cb74a95..938fd62924 100644 --- a/src/cli/TextStream.cpp +++ b/src/cli/TextStream.cpp @@ -19,6 +19,9 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif TextStream::TextStream() { @@ -59,12 +62,26 @@ void TextStream::detectCodec() { QString codecName = "UTF-8"; auto env = QProcessEnvironment::systemEnvironment(); + #ifdef Q_OS_WIN - if (!env.contains("SHELL")) { - // native shell (no Msys or cygwin) + WINBOOL success = false; +#ifdef CP_UTF8 + success = SetConsoleOutputCP(CP_UTF8); +#endif + if (!success && !env.contains("SHELL")) { + // Fall back to cp850 if this is Windows without CP_UTF8 and we + // are running in a native shell (i.e., no Msys or Cygwin). codecName = "Windows-850"; } +#else + if (env.contains("LANG") && !env.value("LANG").isEmpty() && env.value("LANG") != "C") { + // Only override codec if LANG is set, otherwise Qt will assume + // US-ASCII, which is almost always wrong and results in + // Unicode passwords being displayed as question marks. + codecName = QTextCodec::codecForLocale()->name(); + } #endif + codecName = env.value("ENCODING_OVERRIDE", codecName); auto* codec = QTextCodec::codecForName(codecName.toLatin1()); if (codec) { diff --git a/src/core/Alloc.cpp b/src/core/Alloc.cpp new file mode 100644 index 0000000000..a33b561962 --- /dev/null +++ b/src/core/Alloc.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * 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 . + */ + +#include +#include +#include +#ifdef Q_OS_MACOS +#include +#else +#include +#endif + +#if defined(NDEBUG) && !defined(__cpp_sized_deallocation) +#warning "KeePassXC is being compiled without sized deallocation support. Deletes may be slow." +#endif + +/** + * Custom sized delete operator which securely zeroes out allocated + * memory before freeing it (requires C++14 sized deallocation support). + */ +void operator delete(void* ptr, std::size_t size) noexcept +{ + if (!ptr) { + return; + } + + sodium_memzero(ptr, size); + std::free(ptr); +} + +void operator delete[](void* ptr, std::size_t size) noexcept +{ + ::operator delete(ptr, size); +} + +/** + * Custom delete operator which securely zeroes out + * allocated memory before freeing it. + */ +void operator delete(void* ptr) noexcept +{ + if (!ptr) { + return; + } + +#if defined(Q_OS_WIN) + ::operator delete(ptr, _msize(ptr)); +#elif defined(Q_OS_MACOS) + ::operator delete(ptr, malloc_size(ptr)); +#elif defined(Q_OS_UNIX) + ::operator delete(ptr, malloc_usable_size(ptr)); +#else + // whatever OS this is, give up and simply free stuff + std::free(ptr); +#endif +} + +void operator delete[](void* ptr) noexcept +{ + ::operator delete(ptr); +} + +/** + * Custom insecure delete operator that does not zero out memory before + * freeing a buffer. Can be used for better performance. + */ +void operator delete(void* ptr, bool) noexcept +{ + std::free(ptr); +} + +void operator delete[](void* ptr, bool) noexcept +{ + ::operator delete(ptr, false); +} diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index a06bf74c1d..2d1a3e0878 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -85,6 +85,12 @@ namespace Bootstrap bootstrap(); MessageBox::initializeButtonDefs(); +#ifdef KEEPASSXC_DIST_SNAP + // snap: force fallback theme to avoid using system theme (gtk integration) + // with missing actions just like on Windows and macOS + QIcon::setThemeSearchPaths(QStringList() << ":/icons"); +#endif + #ifdef Q_OS_MACOS // Don't show menu icons on OSX QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); diff --git a/src/core/CustomData.cpp b/src/core/CustomData.cpp index 86adae158c..f009176a0d 100644 --- a/src/core/CustomData.cpp +++ b/src/core/CustomData.cpp @@ -16,9 +16,12 @@ */ #include "CustomData.h" +#include "Clock.h" #include "core/Global.h" +const QString CustomData::LastModified = "_LAST_MODIFIED"; + CustomData::CustomData(QObject* parent) : QObject(parent) { @@ -60,6 +63,7 @@ void CustomData::set(const QString& key, const QString& value) if (addAttribute || changeValue) { m_data.insert(key, value); + updateLastModified(); emit customDataModified(); } @@ -74,6 +78,7 @@ void CustomData::remove(const QString& key) m_data.remove(key); + updateLastModified(); emit removed(key); emit customDataModified(); } @@ -94,6 +99,7 @@ void CustomData::rename(const QString& oldKey, const QString& newKey) m_data.remove(oldKey); m_data.insert(newKey, data); + updateLastModified(); emit customDataModified(); emit renamed(oldKey, newKey); } @@ -108,9 +114,19 @@ void CustomData::copyDataFrom(const CustomData* other) m_data = other->m_data; + updateLastModified(); emit reset(); emit customDataModified(); } + +QDateTime CustomData::getLastModified() const +{ + if (m_data.contains(LastModified)) { + return Clock::parse(m_data.value(LastModified)); + } + return {}; +} + bool CustomData::operator==(const CustomData& other) const { return (m_data == other.m_data); @@ -152,3 +168,13 @@ int CustomData::dataSize() const } return size; } + +void CustomData::updateLastModified() +{ + if (m_data.size() == 1 && m_data.contains(LastModified)) { + m_data.remove(LastModified); + return; + } + + m_data.insert(LastModified, Clock::currentDateTimeUtc().toString()); +} diff --git a/src/core/CustomData.h b/src/core/CustomData.h index d085c94095..126d4d84e6 100644 --- a/src/core/CustomData.h +++ b/src/core/CustomData.h @@ -42,9 +42,12 @@ class CustomData : public QObject int size() const; int dataSize() const; void copyDataFrom(const CustomData* other); + QDateTime getLastModified() const; bool operator==(const CustomData& other) const; bool operator!=(const CustomData& other) const; + static const QString LastModified; + signals: void customDataModified(); void aboutToBeAdded(const QString& key); @@ -55,6 +58,10 @@ class CustomData : public QObject void renamed(const QString& oldKey, const QString& newKey); void aboutToBeReset(); void reset(); + void lastModified(); + +private slots: + void updateLastModified(); private: QHash m_data; diff --git a/src/core/Merger.cpp b/src/core/Merger.cpp index c732483888..dcbed250f8 100644 --- a/src/core/Merger.cpp +++ b/src/core/Merger.cpp @@ -609,9 +609,6 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context) // TODO HNH: missing handling of recycle bin, names, templates for groups and entries, // public data (entries of newer dict override keys of older dict - ignoring // their own age - it is enough if one entry of the whole dict is newer) => possible lost update - // TODO HNH: CustomData is merged with entries of the new customData overwrite entries - // of the older CustomData - the dict with the newest entry is considered - // newer regardless of the age of the other entries => possible lost update ChangeList changes; auto* sourceMetadata = context.m_sourceDb->metadata(); auto* targetMetadata = context.m_targetDb->metadata(); @@ -624,5 +621,32 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context) changes << tr("Adding missing icon %1").arg(QString::fromLatin1(customIconId.toRfc4122().toHex())); } } + + // Merge Custom Data if source is newer + const auto targetCustomDataModificationTime = sourceMetadata->customData()->getLastModified(); + const auto sourceCustomDataModificationTime = targetMetadata->customData()->getLastModified(); + if (!targetMetadata->customData()->contains(CustomData::LastModified) || + (targetCustomDataModificationTime.isValid() && sourceCustomDataModificationTime.isValid() && + targetCustomDataModificationTime > sourceCustomDataModificationTime)) { + const auto sourceCustomDataKeys = sourceMetadata->customData()->keys(); + const auto targetCustomDataKeys = targetMetadata->customData()->keys(); + + // Check missing keys from source. Remove those from target + for (const auto& key : targetCustomDataKeys) { + if (!sourceMetadata->customData()->contains(key)) { + auto value = targetMetadata->customData()->value(key); + targetMetadata->customData()->remove(key); + changes << tr("Removed custom data %1 [%2]").arg(key, value); + } + } + + // Transfer new/existing keys + for (const auto& key : sourceCustomDataKeys) { + auto value = sourceMetadata->customData()->value(key); + targetMetadata->customData()->set(key, value); + changes << tr("Adding custom data %1 [%2]").arg(key, value); + } + } + return changes; } diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index 6448c391a2..ff1ee71e7d 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -195,7 +195,7 @@ QPixmap Metadata::customIconScaledPixmap(const QUuid& uuid) const QPixmapCache::Key& cacheKey = m_customIconScaledCacheKeys[uuid]; if (!QPixmapCache::find(cacheKey, &pixmap)) { - QImage image = m_customIcons.value(uuid).scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QImage image = m_customIcons.value(uuid).scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); pixmap = QPixmap::fromImage(image); cacheKey = QPixmapCache::insert(pixmap); } diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp index 595dadfa1d..95de3ce91f 100644 --- a/src/core/Translator.cpp +++ b/src/core/Translator.cpp @@ -34,13 +34,14 @@ */ void Translator::installTranslators() { + QLocale locale; QString language = config()->get("GUI/Language").toString(); - if (language == "system" || language.isEmpty()) { - language = QLocale::system().name(); - } - if (language == "en") { + if (!language.isEmpty() && language != "system") { // use actual English translation instead of the English locale source language - language = "en_US"; + if (language == "en") { + language = "en_US"; + } + locale = QLocale(language); } const QStringList paths = { @@ -51,11 +52,12 @@ void Translator::installTranslators() bool translationsLoaded = false; for (const QString& path : paths) { - translationsLoaded |= installTranslator(language, path) || installTranslator("en_US", path); + translationsLoaded |= installTranslator(locale, path) || installTranslator(QLocale("en_US"), path); if (!installQtTranslator(language, path)) { - installQtTranslator("en", path); + installQtTranslator(QLocale("en"), path); } } + if (!translationsLoaded) { // couldn't load configured language or fallback qWarning("Couldn't load translations."); @@ -114,10 +116,10 @@ QList> Translator::availableLanguages() * @param path local search path * @return true on success */ -bool Translator::installTranslator(const QString& language, const QString& path) +bool Translator::installTranslator(const QLocale& locale, const QString& path) { QScopedPointer translator(new QTranslator(qApp)); - if (translator->load(QString("keepassx_%1").arg(language), path)) { + if (translator->load(locale, "keepassx_", "", path)) { return QCoreApplication::installTranslator(translator.take()); } return false; @@ -131,13 +133,12 @@ bool Translator::installTranslator(const QString& language, const QString& path) * @param path local search path * @return true on success */ -bool Translator::installQtTranslator(const QString& language, const QString& path) +bool Translator::installQtTranslator(const QLocale& locale, const QString& path) { QScopedPointer qtTranslator(new QTranslator(qApp)); - if (qtTranslator->load(QString("qtbase_%1").arg(language), path)) { + if (qtTranslator->load(locale, "qtbase_", "", path)) { return QCoreApplication::installTranslator(qtTranslator.take()); - } else if (qtTranslator->load(QString("qtbase_%1").arg(language), - QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + } else if (qtTranslator->load(locale, "qtbase_", "", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { return QCoreApplication::installTranslator(qtTranslator.take()); } return false; diff --git a/src/core/Translator.h b/src/core/Translator.h index cf62f48e4d..cfc49d7105 100644 --- a/src/core/Translator.h +++ b/src/core/Translator.h @@ -20,6 +20,7 @@ #include #include +#include class Translator { @@ -28,8 +29,8 @@ class Translator static QList> availableLanguages(); private: - static bool installTranslator(const QString& language, const QString& path); - static bool installQtTranslator(const QString& language, const QString& path); + static bool installTranslator(const QLocale& locale, const QString& path); + static bool installQtTranslator(const QLocale& locale, const QString& path); }; #endif // KEEPASSX_TRANSLATOR_H diff --git a/src/crypto/kdf/Argon2Kdf.cpp b/src/crypto/kdf/Argon2Kdf.cpp index 2ae81a6b3e..0d449b5b52 100644 --- a/src/crypto/kdf/Argon2Kdf.cpp +++ b/src/crypto/kdf/Argon2Kdf.cpp @@ -35,7 +35,7 @@ Argon2Kdf::Argon2Kdf() , m_memory(1 << 16) , m_parallelism(static_cast(QThread::idealThreadCount())) { - m_rounds = 1; + m_rounds = 10; } quint32 Argon2Kdf::version() const diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp index 4bb0202b10..d0914a04ed 100644 --- a/src/format/Kdbx4Reader.cpp +++ b/src/format/Kdbx4Reader.cpp @@ -35,7 +35,7 @@ bool Kdbx4Reader::readDatabaseImpl(QIODevice* device, { Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4); - m_binaryPoolInverse.clear(); + m_binaryPool.clear(); if (hasError()) { return false; @@ -273,11 +273,7 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device) return false; } auto data = fieldData.mid(1); - if (m_binaryPoolInverse.contains(data)) { - qWarning("Skipping duplicate binary record"); - break; - } - m_binaryPoolInverse.insert(data, QString::number(m_binaryPoolInverse.size())); + m_binaryPool.insert(QString::number(m_binaryPool.size()), data); break; } } @@ -422,17 +418,5 @@ QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device) */ QHash Kdbx4Reader::binaryPool() const { - QHash binaryPool; - for (auto it = m_binaryPoolInverse.cbegin(); it != m_binaryPoolInverse.cend(); ++it) { - binaryPool.insert(it.value(), it.key()); - } - return binaryPool; -} - -/** - * @return mapping from binary data to attachment keys - */ -QHash Kdbx4Reader::binaryPoolInverse() const -{ - return m_binaryPoolInverse; + return m_binaryPool; } diff --git a/src/format/Kdbx4Reader.h b/src/format/Kdbx4Reader.h index 3afb5bf69a..fc73a1fbd6 100644 --- a/src/format/Kdbx4Reader.h +++ b/src/format/Kdbx4Reader.h @@ -34,7 +34,6 @@ class Kdbx4Reader : public KdbxReader const QByteArray& headerData, QSharedPointer key, Database* db) override; - QHash binaryPoolInverse() const; QHash binaryPool() const; protected: @@ -44,7 +43,7 @@ class Kdbx4Reader : public KdbxReader bool readInnerHeaderField(QIODevice* device); QVariantMap readVariantMap(QIODevice* device); - QHash m_binaryPoolInverse; + QHash m_binaryPool; }; #endif // KEEPASSX_KDBX4READER_H diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 22a49dece6..2461230c86 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -64,6 +64,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) , m_globalAutoTypeModifiers(Qt::NoModifier) { setHeadline(tr("Application Settings")); + showApplyButton(false); m_secUi->setupUi(m_secWidget); m_generalUi->setupUi(m_generalWidget); @@ -75,7 +76,6 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) } connect(this, SIGNAL(accepted()), SLOT(saveSettings())); - connect(this, SIGNAL(apply()), SLOT(saveSettings())); connect(this, SIGNAL(rejected()), SLOT(reject())); // clang-format off diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 344c2b81c9..bf5cce2d30 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -212,7 +212,7 @@ - Use DuckDuckGo as fallback for downloading website icons + Use DuckDuckGo service to download website icons diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 313bfabb1b..92d706b6e5 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -59,10 +59,14 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged())); connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*))); - connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType())); + connect(autoType(), SIGNAL(globalAutoTypeTriggered()), SLOT(performGlobalAutoType())); connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase())); connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase())); // clang-format on + +#ifdef Q_OS_MACOS + connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases())); +#endif } DatabaseTabWidget::~DatabaseTabWidget() @@ -558,7 +562,7 @@ void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, #ifdef Q_OS_MACOS if (intent == DatabaseOpenDialog::Intent::AutoType || intent == DatabaseOpenDialog::Intent::Browser) { macUtils()->raiseOwnWindow(); - Tools::wait(500); + Tools::wait(200); } #endif diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 8cfc408154..51a0d7d81b 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -174,7 +174,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged())); connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged())); connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode))); - connect(m_previewView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString))); + connect(m_previewView, SIGNAL(errorOccurred(QString)), SLOT(showErrorMessage(QString))); + connect(m_previewView, SIGNAL(entryUrlActivated(Entry*)), SLOT(openUrlForEntry(Entry*))); connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged())); connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*))); connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged())); @@ -190,7 +191,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) connect(m_keepass1OpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool))); connect(m_csvImportWizard, SIGNAL(importFinished(bool)), SLOT(csvImportFinished(bool))); connect(m_fileWatcher.data(), SIGNAL(fileChanged()), this, SLOT(reloadDatabaseFile())); - connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); + connect(this, SIGNAL(currentChanged(int)), SLOT(emitCurrentModeChanged())); // clang-format on connectDatabaseSignals(); @@ -652,6 +653,10 @@ void DatabaseWidget::openUrl() void DatabaseWidget::openUrlForEntry(Entry* entry) { Q_ASSERT(entry); + if (!entry) { + return; + } + QString cmdString = entry->resolveMultiplePlaceholders(entry->url()); if (cmdString.startsWith("cmd://")) { // check if decision to execute command was stored @@ -695,9 +700,9 @@ void DatabaseWidget::openUrlForEntry(Entry* entry) } } } else { - QString urlString = entry->webUrl(); - if (!urlString.isEmpty()) { - QDesktopServices::openUrl(urlString); + QUrl url = QUrl(entry->url()); + if (!url.isEmpty()) { + QDesktopServices::openUrl(url); } } } @@ -782,6 +787,9 @@ void DatabaseWidget::switchToMainView(bool previousDialogAccepted) } m_newParent = nullptr; + } else { + // Workaround: ensure entries are focused so search doesn't reset + m_entryView->setFocus(); } setCurrentWidget(m_mainWidget); @@ -1158,9 +1166,10 @@ void DatabaseWidget::onDatabaseModified() { if (!m_blockAutoSave && config()->get("AutoSaveAfterEveryChange").toBool()) { save(); + } else { + // Only block once, then reset + m_blockAutoSave = false; } - - m_blockAutoSave = false; } QString DatabaseWidget::getCurrentSearch() @@ -1258,11 +1267,13 @@ bool DatabaseWidget::lock() } if (m_db->isModified()) { + bool saved = false; + // Attempt to save on exit, but don't block locking if it fails if (config()->get("AutoSaveOnExit").toBool()) { - if (!save()) { - return false; - } - } else { + saved = save(); + } + + if (!saved) { QString msg; if (!m_db->metadata()->name().toHtmlEscaped().isEmpty()) { msg = tr("\"%1\" was modified.\nSave changes?").arg(m_db->metadata()->name().toHtmlEscaped()); @@ -1521,11 +1532,14 @@ bool DatabaseWidget::save() return true; } + // Read-only and new databases ask for filename if (m_db->isReadOnly() || m_db->filePath().isEmpty()) { return saveAs(); } + // Prevent recursions and infinite save loops blockAutoReload(true); + m_blockAutoSave = true; ++m_saveAttempts; // TODO: Make this async, but lock out the database widget to prevent re-entrance @@ -1536,6 +1550,7 @@ bool DatabaseWidget::save() if (ok) { m_saveAttempts = 0; + m_blockAutoSave = false; return true; } diff --git a/src/gui/DialogyWidget.cpp b/src/gui/DialogyWidget.cpp index 858d2949b0..597bcc59de 100644 --- a/src/gui/DialogyWidget.cpp +++ b/src/gui/DialogyWidget.cpp @@ -35,7 +35,8 @@ void DialogyWidget::keyPressEvent(QKeyEvent* e) } } else #endif - if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { + if (!e->modifiers() || e->modifiers() == Qt::ControlModifier + || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 242ae4542f..dcc5160a32 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -197,8 +197,6 @@ void EditWidgetIcons::downloadFavicon() QString fullyQualifiedDomain = m_url.host(); - m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico")); - // Determine if host portion of URL is an IP address by resolving it and // searching for a match with the returned address(es). bool hostIsIp = false; @@ -209,32 +207,35 @@ void EditWidgetIcons::downloadFavicon() } } + // Determine the second-level domain, if available + QString secondLevelDomain; if (!hostIsIp) { - QString secondLevelDomain = getSecondLevelDomain(m_url); - - // Attempt to simply load the favicon.ico file - if (fullyQualifiedDomain != secondLevelDomain) { - m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico")); - } + secondLevelDomain = getSecondLevelDomain(m_url); } - // Try to use alternative fallback URL, if enabled + // Start with the "fallback" url (if enabled) to try to get the best favicon if (config()->get("security/IconDownloadFallback", false).toBool()) { QUrl fallbackUrl = QUrl("https://icons.duckduckgo.com"); fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(fullyQualifiedDomain) + ".ico"); - m_urlsToTry.append(fallbackUrl); - if (!hostIsIp) { - QString secondLevelDomain = getSecondLevelDomain(m_url); - - if (fullyQualifiedDomain != secondLevelDomain) { - fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(secondLevelDomain) + ".ico"); - m_urlsToTry.append(fallbackUrl); - } + // Also try a direct pull of the second-level domain (if possible) + if (!hostIsIp && fullyQualifiedDomain != secondLevelDomain) { + fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(secondLevelDomain) + ".ico"); + m_urlsToTry.append(fallbackUrl); } } + // Add a direct pull of the website's own favicon.ico file + m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico")); + + // Also try a direct pull of the second-level domain (if possible) + if (!hostIsIp && fullyQualifiedDomain != secondLevelDomain) { + m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico")); + } + + // Use the first URL to start the download process + // If a favicon is not found, the next URL will be tried startFetchFavicon(m_urlsToTry.takeFirst()); #endif } @@ -277,7 +278,7 @@ void EditWidgetIcons::fetchFinished() if (!image.isNull()) { if (!addCustomIcon(image)) { emit messageEditEntry(tr("Custom icon already exists"), MessageWidget::Information); - } else if (!this->isVisible()) { + } else if (!isVisible()) { // Show confirmation message if triggered from Entry tab download button emit messageEditEntry(tr("Custom icon successfully downloaded"), MessageWidget::Positive); } @@ -289,7 +290,7 @@ void EditWidgetIcons::fetchFinished() if (!fallbackEnabled) { emit messageEditEntry( tr("Unable to fetch favicon.") + "\n" - + tr("Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security"), + + tr("You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security"), MessageWidget::Error); } else { emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error); diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index 0fd8826c87..c90d0aa670 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -54,11 +54,13 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent) m_ui->entryAttachmentsWidget->setReadOnly(true); m_ui->entryAttachmentsWidget->setButtonsVisible(false); + connect(m_ui->entryUrlLabel, SIGNAL(linkActivated(QString)), SLOT(openEntryUrl())); + connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool))); connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide())); connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool))); connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); - connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel())); + connect(&m_totpTimer, SIGNAL(timeout()), SLOT(updateTotpLabel())); // Group m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close")); @@ -197,11 +199,12 @@ void EntryPreviewWidget::updateEntryGeneralTab() } m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl()); - const QString url = m_currentEntry->webUrl(); + const QString url = m_currentEntry->url(); if (!url.isEmpty()) { // URL is well formed and can be opened in a browser m_ui->entryUrlLabel->setUrl(url); m_ui->entryUrlLabel->setCursor(Qt::PointingHandCursor); + m_ui->entryUrlLabel->setOpenExternalLinks(false); } else { m_ui->entryUrlLabel->setUrl({}); m_ui->entryUrlLabel->setCursor(Qt::ArrowCursor); @@ -327,6 +330,13 @@ void EntryPreviewWidget::updateTabIndexes() m_selectedTabGroup = m_ui->groupTabWidget->currentIndex(); } +void EntryPreviewWidget::openEntryUrl() +{ + if (m_currentEntry) { + emit entryUrlActivated(m_currentEntry); + } +} + void EntryPreviewWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled) { const int tabIndex = tabWidget->indexOf(widget); diff --git a/src/gui/EntryPreviewWidget.h b/src/gui/EntryPreviewWidget.h index 5bfd9dbd65..6a50c0febe 100644 --- a/src/gui/EntryPreviewWidget.h +++ b/src/gui/EntryPreviewWidget.h @@ -43,6 +43,7 @@ public slots: signals: void errorOccurred(const QString& error); + void entryUrlActivated(Entry* entry); private slots: void updateEntryHeaderLine(); @@ -63,6 +64,7 @@ private slots: void updateTotpLabel(); void updateTabIndexes(); + void openEntryUrl(); private: void setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 077ee796ef..334d4fed14 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -41,6 +41,10 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" +#ifdef Q_OS_MACOS +#include "macutils/MacUtils.h" +#endif + #ifdef WITH_XC_UPDATECHECK #include "gui/MessageBox.h" #include "gui/UpdateCheckDialog.h" @@ -135,6 +139,7 @@ MainWindow::MainWindow() , m_trayIcon(nullptr) , m_appExitCalled(false) , m_appExiting(false) + , m_lastFocusOutTime(0) { g_MainWindow = this; @@ -248,6 +253,9 @@ MainWindow::MainWindow() m_ui->actionEntryCopyURL->setShortcutVisibleInContextMenu(true); #endif + connect(m_ui->menuEntries, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock())); + connect(m_ui->menuGroups, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock())); + // Control window state new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(showMinimized())); new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow())); @@ -370,6 +378,9 @@ MainWindow::MainWindow() #ifdef Q_OS_MACOS setUnifiedTitleAndToolBarOnMac(true); + if (macUtils()->isDarkMode()) { + setStyleSheet("QToolButton {color:white;}"); + } #endif #ifdef WITH_XC_UPDATECHECK @@ -396,6 +407,12 @@ MainWindow::MainWindow() connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock())); #endif + // Tray Icon setup + connect(Application::instance(), SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*))); + m_trayIconTriggerReason = QSystemTrayIcon::Unknown; + m_trayIconTriggerTimer.setSingleShot(true); + connect(&m_trayIconTriggerTimer, SIGNAL(timeout()), SLOT(processTrayIconTrigger())); + updateTrayIcon(); if (config()->hasAccessError()) { @@ -529,8 +546,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) switch (mode) { case DatabaseWidget::Mode::ViewMode: { // bool inSearch = dbWidget->isInSearchMode(); - bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && dbWidget->currentEntryHasFocus(); - bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && dbWidget->currentEntryHasFocus(); + bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && (m_contextMenuFocusLock || dbWidget->currentEntryHasFocus()); + bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && (m_contextMenuFocusLock || dbWidget->currentEntryHasFocus()); bool groupSelected = dbWidget->isGroupSelected(); bool recycleBinSelected = dbWidget->isRecycleBinSelected(); @@ -857,7 +874,9 @@ void MainWindow::closeEvent(QCloseEvent* event) return; } - if (config()->get("GUI/MinimizeOnClose").toBool() && !m_appExitCalled) { + // Don't ignore close event when the app is hidden to tray. + // This can occur when the OS issues close events on shutdown. + if (config()->get("GUI/MinimizeOnClose").toBool() && !isHidden() && !m_appExitCalled) { event->ignore(); hideWindow(); return; @@ -912,7 +931,7 @@ bool MainWindow::saveLastDatabases() } QStringList openDatabases; - for (int i=0; i < m_ui->tabWidget->count(); ++i) { + for (int i = 0; i < m_ui->tabWidget->count(); ++i) { auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i); openDatabases.append(dbWidget->database()->filePath()); } @@ -936,6 +955,8 @@ void MainWindow::updateTrayIcon() QAction* actionToggle = new QAction(tr("Toggle window"), menu); menu->addAction(actionToggle); + menu->addAction(m_ui->actionLockDatabases); + #ifdef Q_OS_MACOS QAction* actionQuit = new QAction(tr("Quit KeePassXC"), menu); menu->addAction(actionQuit); @@ -969,13 +990,20 @@ void MainWindow::updateTrayIcon() } } +void MainWindow::releaseContextFocusLock() +{ + m_contextMenuFocusLock = false; +} + void MainWindow::showEntryContextMenu(const QPoint& globalPos) { + m_contextMenuFocusLock = true; m_ui->menuEntries->popup(globalPos); } void MainWindow::showGroupContextMenu(const QPoint& globalPos) { + m_contextMenuFocusLock = true; m_ui->menuGroups->popup(globalPos); } @@ -1029,10 +1057,38 @@ void MainWindow::applySettingsChanges() updateTrayIcon(); } +void MainWindow::focusWindowChanged(QWindow* focusWindow) +{ + if (focusWindow != windowHandle()) { + m_lastFocusOutTime = Clock::currentSecondsSinceEpoch(); + } +} + void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::MiddleClick) { + if (!m_trayIconTriggerTimer.isActive()) { + m_trayIconTriggerTimer.start(150); + } + // Overcome Qt bug https://bugreports.qt.io/browse/QTBUG-69698 + // Store last issued tray icon activation reason to properly + // capture doubleclick events + m_trayIconTriggerReason = reason; +} + +void MainWindow::processTrayIconTrigger() +{ + if (m_trayIconTriggerReason == QSystemTrayIcon::DoubleClick) { + // Always toggle window on double click toggleWindow(); + } else if (m_trayIconTriggerReason == QSystemTrayIcon::Trigger + || m_trayIconTriggerReason == QSystemTrayIcon::MiddleClick) { + // On single/middle click focus the window if it is not hidden + // and did not have focus less than a second ago, otherwise toggle + if (isHidden() || (Clock::currentSecondsSinceEpoch() - m_lastFocusOutTime) <= 1) { + toggleWindow(); + } else { + bringToFront(); + } } } diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 5a72d6f027..95405f475d 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -85,6 +85,7 @@ private slots: void showAboutDialog(); void showUpdateCheckStartup(); void showUpdateCheckDialog(); + void focusWindowChanged(QWindow* focusWindow); void hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested); void openDonateUrl(); void openBugReportUrl(); @@ -107,6 +108,7 @@ private slots: void showGroupContextMenu(const QPoint& globalPos); void applySettingsChanges(); void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); + void processTrayIconTrigger(); void lockDatabasesAfterInactivity(); void forgetTouchIDAfterInactivity(); void handleScreenLock(); @@ -115,6 +117,7 @@ private slots: void selectPreviousDatabaseTab(); void togglePasswordsHidden(); void toggleUsernamesHidden(); + void releaseContextFocusLock(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); @@ -146,6 +149,10 @@ private slots: bool m_appExitCalled; bool m_appExiting; + bool m_contextMenuFocusLock; + uint m_lastFocusOutTime; + QTimer m_trayIconTriggerTimer; + QSystemTrayIcon::ActivationReason m_trayIconTriggerReason; }; /** diff --git a/src/gui/SearchHelpWidget.ui b/src/gui/SearchHelpWidget.ui index daa3a851e2..e4d77b1a0e 100644 --- a/src/gui/SearchHelpWidget.ui +++ b/src/gui/SearchHelpWidget.ui @@ -16,9 +16,6 @@ false - - #SearchHelpWidget { background-color: #ffffff } - QFrame::Box diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 063f8da2c1..323bf22fc1 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -152,11 +152,6 @@ void EditEntryWidget::setupMain() m_mainUi->expirePresets->setMenu(createPresetsMenu()); connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*))); - QAction* action = new QAction(this); - action->setShortcut(Qt::CTRL | Qt::Key_Return); - connect(action, SIGNAL(triggered()), this, SLOT(commitEntry())); - this->addAction(action); - m_mainUi->passwordGenerator->hide(); m_mainUi->passwordGenerator->reset(); } @@ -285,7 +280,6 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(updateFaviconButtonEnable(QString))); #endif connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setModified())); - connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setModified())); connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(setModified())); connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setModified())); @@ -968,6 +962,7 @@ void EditEntryWidget::cancel() m_entry->setIcon(Entry::DefaultIconNumber); } + bool accepted = false; if (isModified()) { auto result = MessageBox::question(this, QString(), @@ -975,18 +970,17 @@ void EditEntryWidget::cancel() MessageBox::Cancel | MessageBox::Save | MessageBox::Discard, MessageBox::Cancel); if (result == MessageBox::Cancel) { - m_mainUi->passwordGenerator->reset(); return; - } - if (result == MessageBox::Save) { - commitEntry(); - setModified(false); + } else if (result == MessageBox::Save) { + accepted = true; + if (!commitEntry()) { + return; + } } } clear(); - - emit editFinished(!isModified()); + emit editFinished(accepted); } void EditEntryWidget::clear() @@ -1111,8 +1105,9 @@ void EditEntryWidget::updateCurrentAttribute() void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected) { - // Block signals to prevent extra calls + // Block signals to prevent modified being set m_advancedUi->protectAttributeButton->blockSignals(true); + m_advancedUi->attributesEdit->blockSignals(true); if (index.isValid()) { QString key = m_attributesModel->keyByIndex(index); @@ -1143,6 +1138,7 @@ void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected) } m_advancedUi->protectAttributeButton->blockSignals(false); + m_advancedUi->attributesEdit->blockSignals(false); } void EditEntryWidget::protectCurrentAttribute(bool state) diff --git a/src/gui/entry/EntryAttachmentsWidget.cpp b/src/gui/entry/EntryAttachmentsWidget.cpp index 810b4bde5a..9610cc05b1 100644 --- a/src/gui/entry/EntryAttachmentsWidget.cpp +++ b/src/gui/entry/EntryAttachmentsWidget.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include #include "EntryAttachmentsModel.h" +#include "config-keepassx.h" #include "core/Config.h" #include "core/EntryAttachments.h" #include "core/Tools.h" @@ -324,7 +326,12 @@ bool EntryAttachmentsWidget::openAttachment(const QModelIndex& index, QString& e const QByteArray attachmentData = m_entryAttachments->value(filename); // tmp file will be removed once the database (or the application) has been closed +#ifdef KEEPASSXC_DIST_SNAP + const QString tmpFileTemplate = + QString("%1/XXXXXX.%2").arg(QProcessEnvironment::systemEnvironment().value("SNAP_USER_DATA"), filename); +#else const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename)); +#endif QScopedPointer tmpFile(new QTemporaryFile(tmpFileTemplate, this)); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 0a620a6872..cd7896b061 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -139,17 +139,18 @@ void EntryView::keyPressEvent(QKeyEvent* event) } int last = m_model->rowCount() - 1; + if (last > 0) { + if (event->key() == Qt::Key_Up && currentIndex().row() == 0) { + QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0)); + setCurrentEntry(m_model->entryFromIndex(index)); + return; + } - if (event->key() == Qt::Key_Up && currentIndex().row() == 0) { - QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0)); - setCurrentEntry(m_model->entryFromIndex(index)); - return; - } - - if (event->key() == Qt::Key_Down && currentIndex().row() == last) { - QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); - setCurrentEntry(m_model->entryFromIndex(index)); - return; + if (event->key() == Qt::Key_Down && currentIndex().row() == last) { + QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); + setCurrentEntry(m_model->entryFromIndex(index)); + return; + } } QTreeView::keyPressEvent(event); diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp index fe83a943e1..051f23d4ba 100644 --- a/src/gui/group/EditGroupWidget.cpp +++ b/src/gui/group/EditGroupWidget.cpp @@ -227,6 +227,9 @@ void EditGroupWidget::cancel() tr("Entry has unsaved changes"), MessageBox::Cancel | MessageBox::Save | MessageBox::Discard, MessageBox::Cancel); + if (result == MessageBox::Cancel) { + return; + } if (result == MessageBox::Save) { apply(); setModified(false); diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index 7c3c91355b..fa70233517 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -72,6 +72,12 @@ void GroupView::dragMoveEvent(QDragMoveEvent* event) } } +void GroupView::focusInEvent(QFocusEvent* event) +{ + emitGroupChanged(); + QTreeView::focusInEvent(event); +} + Group* GroupView::currentGroup() { if (currentIndex() == QModelIndex()) { diff --git a/src/gui/group/GroupView.h b/src/gui/group/GroupView.h index 61f7b3bac9..609ab0e033 100644 --- a/src/gui/group/GroupView.h +++ b/src/gui/group/GroupView.h @@ -47,6 +47,7 @@ private slots: protected: void dragMoveEvent(QDragMoveEvent* event) override; + void focusInEvent(QFocusEvent* event) override; private: void recInitExpanded(Group* group); diff --git a/src/gui/macutils/AppKit.h b/src/gui/macutils/AppKit.h index cdb822ffc7..da81f69135 100644 --- a/src/gui/macutils/AppKit.h +++ b/src/gui/macutils/AppKit.h @@ -19,14 +19,15 @@ #ifndef KEEPASSX_APPKIT_H #define KEEPASSX_APPKIT_H +#include #include -extern "C" { - -class AppKit +class AppKit : public QObject { + Q_OBJECT + public: - AppKit(); + AppKit(QObject* parent = nullptr); ~AppKit(); pid_t lastActiveProcessId(); @@ -37,10 +38,11 @@ class AppKit bool isHidden(pid_t pid); bool isDarkMode(); +signals: + void lockDatabases(); + private: void *self; }; -} // extern "C" - #endif // KEEPASSX_APPKIT_H diff --git a/src/gui/macutils/AppKitImpl.h b/src/gui/macutils/AppKitImpl.h index 3bf2d20efc..ca2506794b 100644 --- a/src/gui/macutils/AppKitImpl.h +++ b/src/gui/macutils/AppKitImpl.h @@ -22,6 +22,10 @@ #import @interface AppKitImpl : NSObject +{ + AppKit *m_appkit; +} +- (id) initWithObject:(AppKit *)appkit; @property (strong) NSRunningApplication *lastActiveApplication; @@ -31,5 +35,6 @@ - (bool) hideProcess:(pid_t) pid; - (bool) isHidden:(pid_t) pid; - (bool) isDarkMode; +- (void) userSwitchHandler:(NSNotification*) notification; @end diff --git a/src/gui/macutils/AppKitImpl.mm b/src/gui/macutils/AppKitImpl.mm index cd709df27e..4165e0d5e2 100644 --- a/src/gui/macutils/AppKitImpl.mm +++ b/src/gui/macutils/AppKitImpl.mm @@ -22,19 +22,22 @@ @implementation AppKitImpl -AppKit::AppKit() +- (id) initWithObject:(AppKit *)appkit { - self = [[AppKitImpl alloc] init]; - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast(self) + self = [super init]; + if (self) { + m_appkit = appkit; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast(self) selector:@selector(didDeactivateApplicationObserver:) name:NSWorkspaceDidDeactivateApplicationNotification object:nil]; -} - -AppKit::~AppKit() -{ - [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast(self)]; - [static_cast(self) dealloc]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast(self) + selector:@selector(userSwitchHandler:) + name:NSWorkspaceSessionDidResignActiveNotification + object:nil]; + } + return self; } // @@ -104,10 +107,34 @@ - (bool) isDarkMode && NSOrderedSame == [style caseInsensitiveCompare:@"dark"] ); } +// +// Notification for user switch +// +- (void) userSwitchHandler:(NSNotification*) notification +{ + if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification] && m_appkit) + { + emit m_appkit->lockDatabases(); + } +} + +@end + // // ------------------------- C++ Trampolines ------------------------- // +AppKit::AppKit(QObject* parent) : QObject(parent) +{ + self = [[AppKitImpl alloc] initWithObject:this]; +} + +AppKit::~AppKit() +{ + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast(self)]; + [static_cast(self) dealloc]; +} + pid_t AppKit::lastActiveProcessId() { return [static_cast(self) lastActiveApplication].processIdentifier; @@ -142,5 +169,3 @@ - (bool) isDarkMode { return [static_cast(self) isDarkMode]; } - -@end diff --git a/src/gui/macutils/MacUtils.cpp b/src/gui/macutils/MacUtils.cpp index c362fe1bd8..654923c31c 100644 --- a/src/gui/macutils/MacUtils.cpp +++ b/src/gui/macutils/MacUtils.cpp @@ -24,7 +24,7 @@ MacUtils* MacUtils::m_instance = nullptr; MacUtils::MacUtils(QObject* parent) : QObject(parent) , m_appkit(new AppKit()) { - + connect(m_appkit.data(), SIGNAL(lockDatabases()), SIGNAL(lockDatabases())); } MacUtils::~MacUtils() diff --git a/src/gui/macutils/MacUtils.h b/src/gui/macutils/MacUtils.h index 39a06bd84d..49644795e1 100644 --- a/src/gui/macutils/MacUtils.h +++ b/src/gui/macutils/MacUtils.h @@ -39,14 +39,16 @@ class MacUtils : public QObject bool isHidden(); bool isDarkMode(); +signals: + void lockDatabases(); + private: explicit MacUtils(QObject* parent = nullptr); ~MacUtils(); private: - std::unique_ptr m_appkit; + QScopedPointer m_appkit; static MacUtils* m_instance; - void* self; Q_DISABLE_COPY(MacUtils) }; diff --git a/src/keeshare/CMakeLists.txt b/src/keeshare/CMakeLists.txt index 14aa17b996..d791d3be64 100644 --- a/src/keeshare/CMakeLists.txt +++ b/src/keeshare/CMakeLists.txt @@ -1,4 +1,6 @@ if(WITH_XC_KEESHARE) + set(WITH_XC_KEESHARE_INSECURE ON PARENT_SCOPE) + include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) set(keeshare_SOURCES @@ -15,9 +17,19 @@ if(WITH_XC_KEESHARE) ) add_library(keeshare STATIC ${keeshare_SOURCES}) - if(WITH_XC_KEESHARE_SECURE) - target_link_libraries(keeshare Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${QUAZIP_LIBRARIES} ${crypto_ssh_LIB}) + target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB}) + + # Try to find libquazip5, if found, enable secure sharing + find_package(QuaZip) + if(QUAZIP_FOUND) + set(WITH_XC_KEESHARE_SECURE ON PARENT_SCOPE) + target_include_directories(keeshare SYSTEM PRIVATE ${QUAZIP_INCLUDE_DIR}) + target_link_libraries(keeshare PRIVATE ${QUAZIP_LIBRARIES}) else() - target_link_libraries(keeshare Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB}) + set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE) + message(STATUS "KeeShare: Secure container support is DISABLED; quazip library not found") endif() -endif() +else(WITH_XC_KEESHARE) + set(WITH_XC_KEESHARE_INSECURE OFF PARENT_SCOPE) + set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE) +endif(WITH_XC_KEESHARE) diff --git a/src/keeshare/ShareObserver.cpp b/src/keeshare/ShareObserver.cpp index 33f5ed1f63..644f1c157d 100644 --- a/src/keeshare/ShareObserver.cpp +++ b/src/keeshare/ShareObserver.cpp @@ -46,8 +46,8 @@ #include #if defined(WITH_XC_KEESHARE_SECURE) -#include -#include +#include +#include #endif namespace diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp index 9d1e8f50fb..da25ef4aef 100644 --- a/src/keys/FileKey.cpp +++ b/src/keys/FileKey.cpp @@ -18,19 +18,35 @@ #include "FileKey.h" -#include - #include "core/Tools.h" #include "crypto/CryptoHash.h" #include "crypto/Random.h" +#include + +#include +#include +#include +#include + QUuid FileKey::UUID("a584cbc4-c9b4-437e-81bb-362ca9709273"); +constexpr int FileKey::SHA256_SIZE; + FileKey::FileKey() : Key(UUID) + , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) { } +FileKey::~FileKey() +{ + if (m_key) { + gcry_free(m_key); + m_key = nullptr; + } +} + /** * Read key file from device while trying to detect its file format. * @@ -148,7 +164,10 @@ bool FileKey::load(const QString& fileName, QString* errorMsg) */ QByteArray FileKey::rawKey() const { - return m_key; + if (!m_key) { + return {}; + } + return QByteArray::fromRawData(m_key, SHA256_SIZE); } /** @@ -223,12 +242,15 @@ bool FileKey::loadXml(QIODevice* device) } } + bool ok = false; if (!xmlReader.error() && correctMeta && !data.isEmpty()) { - m_key = data; - return true; + std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size())); + ok = true; } - return false; + sodium_memzero(data.data(), static_cast(data.capacity())); + + return ok; } /** @@ -293,7 +315,8 @@ bool FileKey::loadBinary(QIODevice* device) if (!Tools::readAllFromDevice(device, data) || data.size() != 32) { return false; } else { - m_key = data; + std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size())); + sodium_memzero(data.data(), static_cast(data.capacity())); return true; } } @@ -321,12 +344,15 @@ bool FileKey::loadHex(QIODevice* device) } QByteArray key = QByteArray::fromHex(data); + sodium_memzero(data.data(), static_cast(data.capacity())); if (key.size() != 32) { return false; } - m_key = key; + std::memcpy(m_key, key.data(), std::min(SHA256_SIZE, key.size())); + sodium_memzero(key.data(), static_cast(key.capacity())); + return true; } @@ -348,7 +374,9 @@ bool FileKey::loadHashed(QIODevice* device) cryptoHash.addData(buffer); } while (!buffer.isEmpty()); - m_key = cryptoHash.result(); + auto result = cryptoHash.result(); + std::memcpy(m_key, result.data(), std::min(SHA256_SIZE, result.size())); + sodium_memzero(result.data(), static_cast(result.capacity())); return true; } diff --git a/src/keys/FileKey.h b/src/keys/FileKey.h index d7486467b3..290a04af05 100644 --- a/src/keys/FileKey.h +++ b/src/keys/FileKey.h @@ -40,6 +40,7 @@ class FileKey : public Key }; FileKey(); + ~FileKey() override; bool load(QIODevice* device); bool load(const QString& fileName, QString* errorMsg = nullptr); QByteArray rawKey() const override; @@ -48,6 +49,8 @@ class FileKey : public Key static bool create(const QString& fileName, QString* errorMsg = nullptr, int size = 128); private: + static constexpr int SHA256_SIZE = 32; + bool loadXml(QIODevice* device); bool loadXmlMeta(QXmlStreamReader& xmlReader); QByteArray loadXmlKey(QXmlStreamReader& xmlReader); @@ -55,7 +58,7 @@ class FileKey : public Key bool loadHex(QIODevice* device); bool loadHashed(QIODevice* device); - QByteArray m_key; + char* m_key = nullptr; Type m_type = None; }; diff --git a/src/keys/PasswordKey.cpp b/src/keys/PasswordKey.cpp index 35ecb99897..2d0416af8f 100644 --- a/src/keys/PasswordKey.cpp +++ b/src/keys/PasswordKey.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Felix Geyer + * Copyright (C) 2019 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,35 +16,51 @@ */ #include "PasswordKey.h" +#include "core/Tools.h" #include "crypto/CryptoHash.h" +#include +#include +#include QUuid PasswordKey::UUID("77e90411-303a-43f2-b773-853b05635ead"); +constexpr int PasswordKey::SHA256_SIZE; + PasswordKey::PasswordKey() : Key(UUID) + , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) { } PasswordKey::PasswordKey(const QString& password) : Key(UUID) + , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) { setPassword(password); } +PasswordKey::~PasswordKey() +{ + if (m_key) { + gcry_free(m_key); + m_key = nullptr; + } +} + QSharedPointer PasswordKey::fromRawKey(const QByteArray& rawKey) { auto result = QSharedPointer::create(); - result->m_key = rawKey; + std::memcpy(result->m_key, rawKey.data(), std::min(SHA256_SIZE, rawKey.size())); return result; } QByteArray PasswordKey::rawKey() const { - return m_key; + return QByteArray::fromRawData(m_key, SHA256_SIZE); } void PasswordKey::setPassword(const QString& password) { - m_key = CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256); + std::memcpy(m_key, CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256).data(), SHA256_SIZE); } diff --git a/src/keys/PasswordKey.h b/src/keys/PasswordKey.h index 68ab79895e..4408cabcf3 100644 --- a/src/keys/PasswordKey.h +++ b/src/keys/PasswordKey.h @@ -30,13 +30,16 @@ class PasswordKey : public Key PasswordKey(); explicit PasswordKey(const QString& password); + ~PasswordKey() override; QByteArray rawKey() const override; void setPassword(const QString& password); static QSharedPointer fromRawKey(const QByteArray& rawKey); private: - QByteArray m_key; + static constexpr int SHA256_SIZE = 32; + + char* m_key = nullptr; }; #endif // KEEPASSX_PASSWORDKEY_H diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index f9cbe31747..759d8d1bc8 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -1,6 +1,6 @@ /* + * Copyright (C) 2019 KeePassXC Team * Copyright (C) 2014 Kyle Manna - * Copyright (C) 2017 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +32,10 @@ #include #include +#include +#include +#include + QUuid YkChallengeResponseKey::UUID("e092495c-e77d-498b-84a1-05ae0d955508"); YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) @@ -45,9 +49,18 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) } } +YkChallengeResponseKey::~YkChallengeResponseKey() +{ + if (m_key) { + gcry_free(m_key); + m_keySize = 0; + m_key = nullptr; + } +} + QByteArray YkChallengeResponseKey::rawKey() const { - return m_key; + return QByteArray::fromRawData(m_key, static_cast(m_keySize)); } /** @@ -67,14 +80,22 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned int emit userInteractionRequired(); } + QByteArray key; auto result = AsyncTask::runAndWaitForFuture( - [this, challenge]() { return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); }); + [this, challenge, &key]() { return YubiKey::instance()->challenge(m_slot, true, challenge, key); }); if (m_blocking) { emit userConfirmed(); } if (result == YubiKey::SUCCESS) { + if (m_key) { + gcry_free(m_key); + } + m_keySize = static_cast(key.size()); + m_key = static_cast(gcry_malloc_secure(m_keySize)); + std::memcpy(m_key, key.data(), m_keySize); + sodium_memzero(key.data(), static_cast(key.capacity())); return true; } } while (retries > 0); diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index b8467e7a62..5f7c40e721 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2019 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +32,7 @@ class YkChallengeResponseKey : public QObject, public ChallengeResponseKey static QUuid UUID; explicit YkChallengeResponseKey(int slot = -1, bool blocking = false); + ~YkChallengeResponseKey() override; QByteArray rawKey() const override; bool challenge(const QByteArray& challenge) override; @@ -52,7 +53,8 @@ class YkChallengeResponseKey : public QObject, public ChallengeResponseKey void userConfirmed(); private: - QByteArray m_key; + char* m_key = nullptr; + std::size_t m_keySize = 0; int m_slot; bool m_blocking; }; diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index ff645dadb4..bdbfa3b740 100755 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -18,12 +18,13 @@ if(WITH_XC_BROWSER) include_directories(${BROWSER_SOURCE_DIR}) set(proxy_SOURCES + ../core/Alloc.cpp keepassxc-proxy.cpp ${BROWSER_SOURCE_DIR}/NativeMessagingBase.cpp NativeMessagingHost.cpp) add_library(proxy STATIC ${proxy_SOURCES}) - target_link_libraries(proxy Qt5::Core Qt5::Network) + target_link_libraries(proxy Qt5::Core Qt5::Network ${sodium_LIBRARY_RELEASE}) add_executable(keepassxc-proxy keepassxc-proxy.cpp) target_link_libraries(keepassxc-proxy proxy) diff --git a/src/totp/totp.cpp b/src/totp/totp.cpp index 7188288a18..ceb163c04c 100644 --- a/src/totp/totp.cpp +++ b/src/totp/totp.cpp @@ -128,9 +128,9 @@ QString Totp::writeSettings(const QSharedPointer& settings, auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1") .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)), username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)), - QString(Base32::sanitizeInput(settings->key.toLatin1()))) - .arg(settings->step) - .arg(settings->digits); + QString(Base32::sanitizeInput(settings->key.toLatin1())), + QString::number(settings->step), + QString::number(settings->digits)); if (!settings->encoder.name.isEmpty()) { urlstring.append("&encoder=").append(settings->encoder.name); diff --git a/src/touchid/TouchID.mm b/src/touchid/TouchID.mm index 9ef72189b2..7df5ad556b 100644 --- a/src/touchid/TouchID.mm +++ b/src/touchid/TouchID.mm @@ -15,6 +15,7 @@ inline void debug(const char* message, ...) { + Q_UNUSED(message); // qWarning(...); } @@ -258,6 +259,7 @@ inline QString hash(const QString& value) NSString* authMessage = msg.toNSString(); // autoreleased [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:authMessage reply:^(BOOL success, NSError* error) { + Q_UNUSED(error); result = success ? kTouchIDResultAllowed : kTouchIDResultFailed; CFRunLoopWakeUp(CFRunLoopGetCurrent()); }]; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bab810cea1..54670f2acb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -220,6 +220,11 @@ add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp add_unit_test(NAME testtools SOURCES TestTools.cpp LIBS ${TEST_LIBRARIES}) +if(WITH_XC_BROWSER) +add_unit_test(NAME testbrowser SOURCES TestBrowser.cpp + LIBS ${TEST_LIBRARIES}) +endif() + if(WITH_GUI_TESTS) # CLI clip tests need X environment on Linux diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 9cc22f8287..69ae955b34 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -157,6 +157,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch() void TestAutoType::testGlobalAutoTypeWithOneMatch() { m_test->setActiveWindowTitle("custom window"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password())); @@ -167,6 +168,7 @@ void TestAutoType::testGlobalAutoTypeTitleMatch() config()->set("AutoTypeEntryTitleMatch", true); m_test->setActiveWindowTitle("An Entry Title!"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter))); @@ -177,6 +179,7 @@ void TestAutoType::testGlobalAutoTypeUrlMatch() config()->set("AutoTypeEntryTitleMatch", true); m_test->setActiveWindowTitle("Dummy - http://example.org/ - "); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter))); @@ -187,6 +190,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch() config()->set("AutoTypeEntryTitleMatch", true); m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - "); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter))); @@ -195,6 +199,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch() void TestAutoType::testGlobalAutoTypeTitleMatchDisabled() { m_test->setActiveWindowTitle("An Entry Title!"); + m_test->triggerGlobalAutoType(); MessageBox::setNextAnswer(MessageBox::Ok); m_autoType->performGlobalAutoType(m_dbList); @@ -205,58 +210,68 @@ void TestAutoType::testGlobalAutoTypeRegExp() { // substring matches are ok m_test->setActiveWindowTitle("lorem REGEX1 ipsum"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("regex1")); m_test->clearActions(); // should be case-insensitive m_test->setActiveWindowTitle("lorem regex1 ipsum"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("regex1")); m_test->clearActions(); // exact match m_test->setActiveWindowTitle("REGEX2"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("regex2")); m_test->clearActions(); // a bit more complicated regex m_test->setActiveWindowTitle("REGEX3-R2D2"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("regex3")); m_test->clearActions(); // with custom attributes m_test->setActiveWindowTitle("CustomAttr1"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute")); m_test->clearActions(); // with (non uppercase) undefined custom attributes m_test->setActiveWindowTitle("CustomAttr2"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("")); m_test->clearActions(); // with mixedcase default attributes m_test->setActiveWindowTitle("CustomAttr3"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("custom_attr")); m_test->clearActions(); // with resolve placeholders in window association title m_test->setActiveWindowTitle("AttrValueFirst"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("custom_attr_first")); m_test->clearActions(); m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second")); m_test->clearActions(); m_test->setActiveWindowTitle("lorem AttrValueThird ipsum"); + m_test->triggerGlobalAutoType(); m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("custom_attr_third")); m_test->clearActions(); diff --git a/tests/TestBrowser.cpp b/tests/TestBrowser.cpp new file mode 100644 index 0000000000..b2ec6d6dd5 --- /dev/null +++ b/tests/TestBrowser.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * 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 . + */ + +#include "TestBrowser.h" +#include "TestGlobal.h" +#include "crypto/Crypto.h" +#include "sodium/crypto_box.h" +#include "browser/BrowserSettings.h" +#include + +QTEST_GUILESS_MAIN(TestBrowser) + +const QString PUBLICKEY = "UIIPObeoya1G8g1M5omgyoPR/j1mR1HlYHu0wHCgMhA="; +const QString SECRETKEY = "B8ei4ZjQJkWzZU2SK/tBsrYRwp+6ztEMf5GFQV+i0yI="; +const QString SERVERPUBLICKEY = "lKnbLhrVCOqzEjuNoUz1xj9EZlz8xeO4miZBvLrUPVQ="; +const QString SERVERSECRETKEY = "tbPQcghxfOgbmsnEqG2qMIj1W2+nh+lOJcNsHncaz1Q="; +const QString NONCE = "zBKdvTjL5bgWaKMCTut/8soM/uoMrFoZ"; +const QString CLIENTID = "testClient"; + +void TestBrowser::initTestCase() +{ + QVERIFY(Crypto::init()); + m_browserService.reset(new BrowserService(nullptr)); + m_browserAction.reset(new BrowserAction(*m_browserService.data())); +} + +void TestBrowser::cleanupTestCase() +{ + +} + +/** + * Tests for BrowserAction + */ + +void TestBrowser::testChangePublicKeys() +{ + QJsonObject json; + json["action"] = "change-public-keys"; + json["publicKey"] = PUBLICKEY; + json["nonce"] = NONCE; + + auto response = m_browserAction->handleAction(json); + QCOMPARE(response["action"].toString(), QString("change-public-keys")); + QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false); + QCOMPARE(response["success"].toString(), QString("true")); +} + +void TestBrowser::testEncryptMessage() +{ + QJsonObject message; + message["action"] = "test-action"; + + m_browserAction->m_publicKey = SERVERPUBLICKEY; + m_browserAction->m_secretKey = SERVERSECRETKEY; + m_browserAction->m_clientPublicKey = PUBLICKEY; + auto encrypted = m_browserAction->encryptMessage(message, NONCE); + + QCOMPARE(encrypted, QString("+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP")); +} + +void TestBrowser::testDecryptMessage() +{ + QString message = "+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP"; + m_browserAction->m_publicKey = SERVERPUBLICKEY; + m_browserAction->m_secretKey = SERVERSECRETKEY; + m_browserAction->m_clientPublicKey = PUBLICKEY; + auto decrypted = m_browserAction->decryptMessage(message, NONCE); + + QCOMPARE(decrypted["action"].toString(), QString("test-action")); +} + +void TestBrowser::testGetBase64FromKey() +{ + unsigned char pk[crypto_box_PUBLICKEYBYTES]; + + for (unsigned int i = 0; i < crypto_box_PUBLICKEYBYTES; ++i) { + pk[i] = i; + } + + auto response = m_browserAction->getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES); + QCOMPARE(response, QString("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=")); +} + +void TestBrowser::testIncrementNonce() +{ + auto result = m_browserAction->incrementNonce(NONCE); + QCOMPARE(result, QString("zRKdvTjL5bgWaKMCTut/8soM/uoMrFoZ")); +} + +/** + * Tests for BrowserService + */ +void TestBrowser::testBaseDomain() +{ + QString url1 = "https://another.example.co.uk"; + QString url2 = "https://www.example.com"; + QString url3 = "http://test.net"; + QString url4 = "http://so.many.subdomains.co.jp"; + + QString res1 = m_browserService->baseDomain(url1); + QString res2 = m_browserService->baseDomain(url2); + QString res3 = m_browserService->baseDomain(url3); + QString res4 = m_browserService->baseDomain(url4); + + QCOMPARE(res1, QString("example.co.uk")); + QCOMPARE(res2, QString("example.com")); + QCOMPARE(res3, QString("test.net")); + QCOMPARE(res4, QString("subdomains.co.jp")); +} + +void TestBrowser::testSortPriority() +{ + QString host = "github.com"; + QString submitUrl = "https://github.com/session"; + QString baseSubmitUrl = "https://github.com"; + + QScopedPointer entry1(new Entry()); + QScopedPointer entry2(new Entry()); + QScopedPointer entry3(new Entry()); + QScopedPointer entry4(new Entry()); + QScopedPointer entry5(new Entry()); + QScopedPointer entry6(new Entry()); + QScopedPointer entry7(new Entry()); + QScopedPointer entry8(new Entry()); + QScopedPointer entry9(new Entry()); + QScopedPointer entry10(new Entry()); + + entry1->setUrl("https://github.com/login"); + entry2->setUrl("https://github.com/login"); + entry3->setUrl("https://github.com/"); + entry4->setUrl("github.com/login"); + entry5->setUrl("http://github.com"); + entry6->setUrl("http://github.com/login"); + entry7->setUrl("github.com"); + entry8->setUrl("github.com/login"); + entry9->setUrl("https://github"); + entry10->setUrl("github.com"); + + // The extension uses the submitUrl as default for comparison + auto res1 = m_browserService->sortPriority(entry1.data(), host, "https://github.com/login", baseSubmitUrl); + auto res2 = m_browserService->sortPriority(entry2.data(), host, submitUrl, baseSubmitUrl); + auto res3 = m_browserService->sortPriority(entry3.data(), host, submitUrl, baseSubmitUrl); + auto res4 = m_browserService->sortPriority(entry4.data(), host, submitUrl, baseSubmitUrl); + auto res5 = m_browserService->sortPriority(entry5.data(), host, submitUrl, baseSubmitUrl); + auto res6 = m_browserService->sortPriority(entry6.data(), host, submitUrl, baseSubmitUrl); + auto res7 = m_browserService->sortPriority(entry7.data(), host, submitUrl, baseSubmitUrl); + auto res8 = m_browserService->sortPriority(entry8.data(), host, submitUrl, baseSubmitUrl); + auto res9 = m_browserService->sortPriority(entry9.data(), host, submitUrl, baseSubmitUrl); + auto res10 = m_browserService->sortPriority(entry10.data(), host, submitUrl, baseSubmitUrl); + + QCOMPARE(res1, 100); + QCOMPARE(res2, 40); + QCOMPARE(res3, 90); + QCOMPARE(res4, 0); + QCOMPARE(res5, 0); + QCOMPARE(res6, 0); + QCOMPARE(res7, 0); + QCOMPARE(res8, 0); + QCOMPARE(res9, 90); + QCOMPARE(res10, 0); +} + +void TestBrowser::testSearchEntries() +{ + auto db = QSharedPointer::create(); + auto* root = db->rootGroup(); + + QList urls; + urls.push_back("https://github.com/login_page"); + urls.push_back("https://github.com/login"); + urls.push_back("https://github.com/"); + urls.push_back("github.com/login"); + urls.push_back("http://github.com"); + urls.push_back("http://github.com/login"); + urls.push_back("github.com"); + urls.push_back("github.com/login"); + urls.push_back("https://github"); + urls.push_back("github.com"); + + for (int i = 0; i < urls.length(); ++i) { + auto entry = new Entry(); + entry->setGroup(root); + entry->beginUpdate(); + entry->setUrl(urls[i]); + entry->setUsername(QString("User %1").arg(i)); + entry->endUpdate(); + } + + browserSettings()->setMatchUrlScheme(false); + auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url + QCOMPARE(result.length(), 7); + QCOMPARE(result[0]->url(), QString("https://github.com/login_page")); + QCOMPARE(result[1]->url(), QString("https://github.com/login")); + QCOMPARE(result[2]->url(), QString("https://github.com/")); + QCOMPARE(result[3]->url(), QString("http://github.com")); + QCOMPARE(result[4]->url(), QString("http://github.com/login")); + QCOMPARE(result[5]->url(), QString("github.com")); + QCOMPARE(result[6]->url(), QString("github.com")) ; + + // With matching there should be only 5 results + browserSettings()->setMatchUrlScheme(true); + result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url + QCOMPARE(result.length(), 5); + QCOMPARE(result[0]->url(), QString("https://github.com/login_page")); + QCOMPARE(result[1]->url(), QString("https://github.com/login")); + QCOMPARE(result[2]->url(), QString("https://github.com/")); + QCOMPARE(result[3]->url(), QString("github.com")); + QCOMPARE(result[4]->url(), QString("github.com")); +} + +void TestBrowser::testSearchEntriesWithPort() +{ + auto db = QSharedPointer::create(); + auto* root = db->rootGroup(); + + QList urls; + urls.push_back("http://127.0.0.1:443"); + urls.push_back("http://127.0.0.1:80"); + + for (int i = 0; i < urls.length(); ++i) { + auto entry = new Entry(); + entry->setGroup(root); + entry->beginUpdate(); + entry->setUrl(urls[i]); + entry->setUsername(QString("User %1").arg(i)); + entry->endUpdate(); + } + + auto result = m_browserService->searchEntries(db, "127.0.0.1", "http://127.0.0.1:443"); // db, hostname, url + QCOMPARE(result.length(), 1); + QCOMPARE(result[0]->url(), QString("http://127.0.0.1:443")); +} + +void TestBrowser::testSortEntries() +{ + auto db = QSharedPointer::create(); + auto* root = db->rootGroup(); + + QList urls; + urls.push_back("https://github.com/login_page"); + urls.push_back("https://github.com/login"); + urls.push_back("https://github.com/"); + urls.push_back("github.com/login"); + urls.push_back("http://github.com"); + urls.push_back("http://github.com/login"); + urls.push_back("github.com"); + urls.push_back("github.com/login"); + urls.push_back("https://github"); + urls.push_back("github.com"); + + QList entries; + for (int i = 0; i < urls.length(); ++i) { + auto entry = new Entry(); + entry->setGroup(root); + entry->beginUpdate(); + entry->setUrl(urls[i]); + entry->setUsername(QString("User %1").arg(i)); + entry->endUpdate(); + entries.push_back(entry); + } + + browserSettings()->setBestMatchOnly(false); + auto result = m_browserService->sortEntries(entries, "github.com", "https://github.com/session"); // entries, host, submitUrl + QCOMPARE(result.size(), 10); + QCOMPARE(result[0]->username(), QString("User 2")); + QCOMPARE(result[0]->url(), QString("https://github.com/")); + QCOMPARE(result[1]->username(), QString("User 8")); + QCOMPARE(result[1]->url(), QString("https://github")); + QCOMPARE(result[2]->username(), QString("User 0")); + QCOMPARE(result[2]->url(), QString("https://github.com/login_page")); + QCOMPARE(result[3]->username(), QString("User 1")); + QCOMPARE(result[3]->url(), QString("https://github.com/login")); +} + +void TestBrowser::testGetDatabaseGroups() +{ + auto db = QSharedPointer::create(); + auto* root = db->rootGroup(); + + QScopedPointer group1(new Group()); + group1->setParent(root); + group1->setName("group1"); + + QScopedPointer group2(new Group()); + group2->setParent(root); + group2->setName("group2"); + + QScopedPointer group3(new Group()); + group3->setParent(root); + group3->setName("group3"); + + QScopedPointer group2_1(new Group()); + group2_1->setParent(group2.data()); + group2_1->setName("group2_1"); + + QScopedPointer group2_2(new Group()); + group2_2->setParent(group2.data()); + group2_2->setName("group2_2"); + + QScopedPointer group2_1_1(new Group()); + group2_1_1->setParent(group2_1.data()); + group2_1_1->setName("group2_1_1"); + + auto result = m_browserService->getDatabaseGroups(db); + QCOMPARE(result.length(), 1); + + auto groups = result["groups"].toArray(); + auto first = groups.at(0); + auto children = first.toObject()["children"].toArray(); + QCOMPARE(first.toObject()["name"].toString(), QString("Root")); + QCOMPARE(children.size(), 3); + + auto firstChild = children.at(0); + auto secondChild = children.at(1); + auto thirdChild = children.at(2); + QCOMPARE(firstChild.toObject()["name"].toString(), QString("group1")); + QCOMPARE(secondChild.toObject()["name"].toString(), QString("group2")); + QCOMPARE(thirdChild.toObject()["name"].toString(), QString("group3")); + + auto childrenOfSecond = secondChild.toObject()["children"].toArray(); + auto firstOfCOS = childrenOfSecond.at(0); + auto secondOfCOS = childrenOfSecond.at(1); + QCOMPARE(firstOfCOS.toObject()["name"].toString(), QString("group2_1")); + QCOMPARE(secondOfCOS.toObject()["name"].toString(), QString("group2_2")); + + auto lastChildren = firstOfCOS.toObject()["children"].toArray(); + auto lastChild = lastChildren.at(0); + QCOMPARE(lastChild.toObject()["name"].toString(), QString("group2_1_1")); + } diff --git a/tests/TestBrowser.h b/tests/TestBrowser.h new file mode 100644 index 0000000000..0b939b0772 --- /dev/null +++ b/tests/TestBrowser.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_TESTBROWSER_H +#define KEEPASSXC_TESTBROWSER_H + +#include + +#include "browser/BrowserAction.h" +#include "browser/BrowserService.h" +#include "core/Group.h" + +class TestBrowser : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testChangePublicKeys(); + void testEncryptMessage(); + void testDecryptMessage(); + void testGetBase64FromKey(); + void testIncrementNonce(); + + void testBaseDomain(); + void testSortPriority(); + void testSearchEntries(); + void testSearchEntriesWithPort(); + void testSortEntries(); + void testGetDatabaseGroups(); + +private: + QScopedPointer m_browserAction; + QScopedPointer m_browserService; +}; + +#endif // KEEPASSXC_TESTBROWSER_H diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp index fe4679da59..88352d8252 100644 --- a/tests/TestKdbx4.cpp +++ b/tests/TestKdbx4.cpp @@ -186,8 +186,10 @@ void TestKdbx4::testFormat400Upgrade() QCOMPARE(reader.version(), expectedVersion); QCOMPARE(targetDb->cipher(), cipherUuid); - QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData()); - QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData()); + QCOMPARE(targetDb->metadata()->customData()->value("CustomPublicData"), + sourceDb->metadata()->customData()->value("CustomPublicData")); + QCOMPARE(targetDb->rootGroup()->customData()->value("CustomGroupData"), + sourceDb->rootGroup()->customData()->value("CustomGroupData")); } // clang-format off @@ -346,20 +348,22 @@ void TestKdbx4::testCustomData() const QString customDataKey2 = "CD2"; const QString customData1 = "abcäöü"; const QString customData2 = "Hello World"; - const int dataSize = customDataKey1.toUtf8().size() + customDataKey1.toUtf8().size() + customData1.toUtf8().size() - + customData2.toUtf8().size(); // test custom database data db.metadata()->customData()->set(customDataKey1, customData1); db.metadata()->customData()->set(customDataKey2, customData2); - QCOMPARE(db.metadata()->customData()->size(), 2); + auto lastModified = db.metadata()->customData()->value(CustomData::LastModified); + const int dataSize = customDataKey1.toUtf8().size() + customDataKey1.toUtf8().size() + customData1.toUtf8().size() + + customData2.toUtf8().size() + lastModified.toUtf8().size() + + CustomData::LastModified.toUtf8().size(); + QCOMPARE(db.metadata()->customData()->size(), 3); QCOMPARE(db.metadata()->customData()->dataSize(), dataSize); // test custom root group data Group* root = db.rootGroup(); root->customData()->set(customDataKey1, customData1); root->customData()->set(customDataKey2, customData2); - QCOMPARE(root->customData()->size(), 2); + QCOMPARE(root->customData()->size(), 3); QCOMPARE(root->customData()->dataSize(), dataSize); // test copied custom group data @@ -378,9 +382,9 @@ void TestKdbx4::testCustomData() // test custom data deletion entry->customData()->set("additional item", "foobar"); - QCOMPARE(entry->customData()->size(), 3); + QCOMPARE(entry->customData()->size(), 4); entry->customData()->remove("additional item"); - QCOMPARE(entry->customData()->size(), 2); + QCOMPARE(entry->customData()->size(), 3); QCOMPARE(entry->customData()->dataSize(), dataSize); // test custom data on cloned groups diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp index 03eae32efa..4d9aef2110 100644 --- a/tests/TestMerge.cpp +++ b/tests/TestMerge.cpp @@ -1164,6 +1164,65 @@ void TestMerge::testMetadata() // will be used - exception is the target has no recycle bin activated } +void TestMerge::testCustomdata() +{ + QScopedPointer dbDestination(new Database()); + QScopedPointer dbSource(createTestDatabase()); + QScopedPointer dbDestination2(new Database()); + QScopedPointer dbSource2(createTestDatabase()); + + m_clock->advanceSecond(1); + + dbDestination->metadata()->customData()->set("toBeDeleted", "value"); + dbDestination->metadata()->customData()->set("key3", "oldValue"); + + dbSource2->metadata()->customData()->set("key1", "value1"); + dbSource2->metadata()->customData()->set("key2", "value2"); + dbSource2->metadata()->customData()->set("key3", "newValue"); + dbSource2->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]"); + + m_clock->advanceSecond(1); + + dbSource->metadata()->customData()->set("key1", "value1"); + dbSource->metadata()->customData()->set("key2", "value2"); + dbSource->metadata()->customData()->set("key3", "newValue"); + dbSource->metadata()->customData()->set("Browser", "n'8=3W@L^6d->d.]St_>]"); + + dbDestination2->metadata()->customData()->set("notToBeDeleted", "value"); + dbDestination2->metadata()->customData()->set("key3", "oldValue"); + + // Sanity check. + QVERIFY(!dbSource->metadata()->customData()->isEmpty()); + QVERIFY(!dbSource2->metadata()->customData()->isEmpty()); + + m_clock->advanceSecond(1); + + Merger merger(dbSource.data(), dbDestination.data()); + merger.merge(); + + Merger merger2(dbSource2.data(), dbDestination2.data()); + merger2.merge(); + + // Source is newer, data should be merged + QVERIFY(!dbDestination->metadata()->customData()->isEmpty()); + QVERIFY(dbDestination->metadata()->customData()->contains("key1")); + QVERIFY(dbDestination->metadata()->customData()->contains("key2")); + QVERIFY(dbDestination->metadata()->customData()->contains("Browser")); + QVERIFY(!dbDestination->metadata()->customData()->contains("toBeDeleted")); + QCOMPARE(dbDestination->metadata()->customData()->value("key1"), QString("value1")); + QCOMPARE(dbDestination->metadata()->customData()->value("key2"), QString("value2")); + QCOMPARE(dbDestination->metadata()->customData()->value("Browser"), QString("n'8=3W@L^6d->d.]St_>]")); + QCOMPARE(dbDestination->metadata()->customData()->value("key3"), QString("newValue")); // Old value should be replaced + + // Target is newer, no data is merged + QVERIFY(!dbDestination2->metadata()->customData()->isEmpty()); + QVERIFY(!dbDestination2->metadata()->customData()->contains("key1")); + QVERIFY(!dbDestination2->metadata()->customData()->contains("key2")); + QVERIFY(!dbDestination2->metadata()->customData()->contains("Browser")); + QVERIFY(dbDestination2->metadata()->customData()->contains("notToBeDeleted")); + QCOMPARE(dbDestination2->metadata()->customData()->value("key3"), QString("oldValue")); // Old value should not be replaced +} + void TestMerge::testDeletedEntry() { QScopedPointer dbDestination(createTestDatabase()); diff --git a/tests/TestMerge.h b/tests/TestMerge.h index 357f852621..15f67ca793 100644 --- a/tests/TestMerge.h +++ b/tests/TestMerge.h @@ -59,6 +59,7 @@ private slots: void testMergeCustomIcons(); void testMergeDuplicateCustomIcons(); void testMetadata(); + void testCustomdata(); void testDeletedEntry(); void testDeletedGroup(); void testDeletedRevertedEntry(); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 63e36fbaae..0db2a5dfbd 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -1286,20 +1286,29 @@ void TestGui::testTrayRestoreHide() QSKIP("QSystemTrayIcon::isSystemTrayAvailable() = false, skipping tray restore/hide test..."); } + m_mainWindow->hideWindow(); + QVERIFY(!m_mainWindow->isVisible()); + auto* trayIcon = m_mainWindow->findChild(); - QVERIFY(m_mainWindow->isVisible()); + QVERIFY(trayIcon); trayIcon->activated(QSystemTrayIcon::Trigger); - QTRY_VERIFY(!m_mainWindow->isVisible()); + QTRY_VERIFY(m_mainWindow->isVisible()); trayIcon->activated(QSystemTrayIcon::Trigger); + QTRY_VERIFY(!m_mainWindow->isVisible()); + + trayIcon->activated(QSystemTrayIcon::MiddleClick); QTRY_VERIFY(m_mainWindow->isVisible()); - trayIcon->activated(QSystemTrayIcon::Trigger); + trayIcon->activated(QSystemTrayIcon::MiddleClick); QTRY_VERIFY(!m_mainWindow->isVisible()); - trayIcon->activated(QSystemTrayIcon::Trigger); + trayIcon->activated(QSystemTrayIcon::DoubleClick); QTRY_VERIFY(m_mainWindow->isVisible()); + + trayIcon->activated(QSystemTrayIcon::DoubleClick); + QTRY_VERIFY(!m_mainWindow->isVisible()); } int TestGui::addCannedEntries()