mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-11 15:29:51 -05:00
keepassxc-browser
This commit is contained in:
parent
a7e6e4ef45
commit
06518c5736
@ -44,6 +44,7 @@ option(WITH_XC_AUTOTYPE "Include Auto-Type." ON)
|
|||||||
option(WITH_XC_HTTP "Include KeePassHTTP and Custom Icon Downloads." OFF)
|
option(WITH_XC_HTTP "Include KeePassHTTP and Custom Icon Downloads." OFF)
|
||||||
option(WITH_XC_YUBIKEY "Include YubiKey support." OFF)
|
option(WITH_XC_YUBIKEY "Include YubiKey support." OFF)
|
||||||
option(WITH_XC_SSHAGENT "Include SSH agent support." OFF)
|
option(WITH_XC_SSHAGENT "Include SSH agent support." OFF)
|
||||||
|
option(WITH_XC_BROWSER "Include browser extension support." OFF)
|
||||||
|
|
||||||
# Process ui files automatically from source files
|
# Process ui files automatically from source files
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
@ -200,11 +201,13 @@ endif()
|
|||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
set(CLI_INSTALL_DIR ".")
|
set(CLI_INSTALL_DIR ".")
|
||||||
|
set(PROXY_INSTALL_DIR ".")
|
||||||
set(BIN_INSTALL_DIR ".")
|
set(BIN_INSTALL_DIR ".")
|
||||||
set(PLUGIN_INSTALL_DIR ".")
|
set(PLUGIN_INSTALL_DIR ".")
|
||||||
set(DATA_INSTALL_DIR "share")
|
set(DATA_INSTALL_DIR "share")
|
||||||
elseif(APPLE AND WITH_APP_BUNDLE)
|
elseif(APPLE AND WITH_APP_BUNDLE)
|
||||||
set(CLI_INSTALL_DIR "/usr/local/bin")
|
set(CLI_INSTALL_DIR "/usr/local/bin")
|
||||||
|
set(PROXY_INSTALL_DIR "/usr/local/bin")
|
||||||
set(BIN_INSTALL_DIR ".")
|
set(BIN_INSTALL_DIR ".")
|
||||||
set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns")
|
set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns")
|
||||||
set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources")
|
set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources")
|
||||||
@ -212,6 +215,7 @@ else()
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
||||||
|
set(PROXY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
||||||
set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
||||||
set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/keepassxc")
|
set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/keepassxc")
|
||||||
set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc")
|
set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc")
|
||||||
|
@ -25,7 +25,8 @@ RUN set -x \
|
|||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \
|
&& add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \
|
||||||
&& add-apt-repository ppa:phoerious/keepassxc
|
&& add-apt-repository ppa:phoerious/keepassxc \
|
||||||
|
&& LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apt-get update -y \
|
&& apt-get update -y \
|
||||||
@ -46,7 +47,8 @@ RUN set -x \
|
|||||||
libxtst-dev \
|
libxtst-dev \
|
||||||
mesa-common-dev \
|
mesa-common-dev \
|
||||||
libyubikey-dev \
|
libyubikey-dev \
|
||||||
libykpers-1-dev
|
libykpers-1-dev \
|
||||||
|
libsodium-dev
|
||||||
|
|
||||||
ENV CMAKE_PREFIX_PATH=/opt/qt${QT5_VERSION}/lib/cmake
|
ENV CMAKE_PREFIX_PATH=/opt/qt${QT5_VERSION}/lib/cmake
|
||||||
ENV LD_LIBRARY_PATH=/opt/qt${QT5_VERSION}/lib
|
ENV LD_LIBRARY_PATH=/opt/qt${QT5_VERSION}/lib
|
||||||
|
12
INSTALL.md
12
INSTALL.md
@ -6,7 +6,7 @@ You can visit the online version of this document at the following link:
|
|||||||
|
|
||||||
https://github.com/keepassxreboot/keepassx/wiki/Install-Instruction-from-Source
|
https://github.com/keepassxreboot/keepassx/wiki/Install-Instruction-from-Source
|
||||||
|
|
||||||
The [KeePassXC QuickStart](./docs/QUICKSTART.md) gets you started using KeePassXC on your
|
The [KeePassXC QuickStart](./docs/QUICKSTART.md) gets you started using KeePassXC on your
|
||||||
Windows, Mac, or Linux computer using the pre-built binaries.
|
Windows, Mac, or Linux computer using the pre-built binaries.
|
||||||
|
|
||||||
Build Dependencies
|
Build Dependencies
|
||||||
@ -25,6 +25,7 @@ The following libraries are required:
|
|||||||
* zlib
|
* zlib
|
||||||
* libmicrohttpd
|
* libmicrohttpd
|
||||||
* libxi, libxtst, qtx11extras (optional for auto-type on X11)
|
* libxi, libxtst, qtx11extras (optional for auto-type on X11)
|
||||||
|
* libsodium (>= 1.0.12, optional for keepassxc-browser support)
|
||||||
|
|
||||||
|
|
||||||
Prepare the Building Environment
|
Prepare the Building Environment
|
||||||
@ -40,7 +41,7 @@ Build Steps
|
|||||||
To compile from source, open a **Terminal (on Linux/MacOS)** or a **MSYS2-MinGW shell (on Windows)**<br/>
|
To compile from source, open a **Terminal (on Linux/MacOS)** or a **MSYS2-MinGW shell (on Windows)**<br/>
|
||||||
**Note:** on Windows make sure you are using a **MINGW shell** by checking the label before the current path
|
**Note:** on Windows make sure you are using a **MINGW shell** by checking the label before the current path
|
||||||
|
|
||||||
First, download the KeePassXC [source tarball](https://keepassxc.org/download#source)
|
First, download the KeePassXC [source tarball](https://keepassxc.org/download#source)
|
||||||
or check out the latest version from our [Git repository](https://github.com/keepassxreboot/keepassxc).
|
or check out the latest version from our [Git repository](https://github.com/keepassxreboot/keepassxc).
|
||||||
|
|
||||||
To clone the project from Git, `cd` to a suitable location and run
|
To clone the project from Git, `cd` to a suitable location and run
|
||||||
@ -66,10 +67,10 @@ cd build
|
|||||||
cmake -DWITH_TESTS=OFF ...and other options - see below...
|
cmake -DWITH_TESTS=OFF ...and other options - see below...
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
These steps place the compiled KeePassXC binary inside the `./build/src/` directory.
|
These steps place the compiled KeePassXC binary inside the `./build/src/` directory.
|
||||||
(Note the cmake notes/options below.)
|
(Note the cmake notes/options below.)
|
||||||
|
|
||||||
**Cmake Notes:**
|
**Cmake Notes:**
|
||||||
|
|
||||||
* Common cmake parameters
|
* Common cmake parameters
|
||||||
|
|
||||||
@ -86,7 +87,8 @@ 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_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON)
|
||||||
-DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and custom icon downloads (default: OFF)
|
-DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and custom icon downloads (default: OFF)
|
||||||
-DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF)
|
-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_TESTS=[ON|OFF] Enable/Disable building of unit tests (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_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_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF)
|
||||||
|
45
README.md
45
README.md
@ -1,19 +1,19 @@
|
|||||||
# <img src="https://keepassxc.org/logo.png" width="40" height="40"/> KeePassXC
|
# <img src="https://keepassxc.org/logo.png" width="40" height="40"/> KeePassXC
|
||||||
[![TeamCity Build Status](https://ci.keepassxc.org/app/rest/builds/buildType:\(id:KeepassXC_TeamCityCi\)/statusIcon?guest=1)](https://ci.keepassxc.org/viewType.html?buildTypeId=KeepassXC_TeamCityCi&guest=1) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc)
|
[![TeamCity Build Status](https://ci.keepassxc.org/app/rest/builds/buildType:\(id:KeepassXC_TeamCityCi\)/statusIcon?guest=1)](https://ci.keepassxc.org/viewType.html?buildTypeId=KeepassXC_TeamCityCi&guest=1) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc)
|
||||||
|
|
||||||
## About KeePassXC
|
## About KeePassXC
|
||||||
[KeePassXC](https://keepassxc.org) is a cross-platform community fork of
|
[KeePassXC](https://keepassxc.org) is a cross-platform community fork of
|
||||||
[KeePassX](https://www.keepassx.org/).
|
[KeePassX](https://www.keepassx.org/).
|
||||||
Our goal is to extend and improve it with new features and bugfixes
|
Our goal is to extend and improve it with new features and bugfixes
|
||||||
to provide a feature-rich, fully cross-platform and modern
|
to provide a feature-rich, fully cross-platform and modern
|
||||||
open-source password manager.
|
open-source password manager.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
The [KeePassXC QuickStart](./docs/QUICKSTART.md) gets you started using
|
The [KeePassXC QuickStart](./docs/QUICKSTART.md) gets you started using
|
||||||
KeePassXC on your Windows, Mac, or Linux computer using pre-compiled binaries
|
KeePassXC on your Windows, Mac, or Linux computer using pre-compiled binaries
|
||||||
from the [downloads page](https://keepassxc.org/download).
|
from the [downloads page](https://keepassxc.org/download).
|
||||||
|
|
||||||
Additionally, individual Linux distributions may ship their own versions,
|
Additionally, individual Linux distributions may ship their own versions,
|
||||||
so please check out your distribution's package list to see if KeePassXC is available.
|
so please check out your distribution's package list to see if KeePassXC is available.
|
||||||
|
|
||||||
## Additional features compared to KeePassX
|
## Additional features compared to KeePassX
|
||||||
@ -29,24 +29,25 @@ so please check out your distribution's package list to see if KeePassXC is avai
|
|||||||
- Using website favicons as entry icons
|
- Using website favicons as entry icons
|
||||||
- Merging of databases
|
- Merging of databases
|
||||||
- Automatic reload when the database changed on disk
|
- Automatic reload when the database changed on disk
|
||||||
- Browser integration with KeePassHTTP-Connector for
|
- Browser integration with KeePassHTTP-Connector for
|
||||||
[Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/) and
|
[Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/) and
|
||||||
[Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb), and
|
[Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb), and
|
||||||
[passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari. [[See note about KeePassHTTP]](#note-about-keepasshttp)
|
[passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari. [[See note about KeePassHTTP]](#Note_about_KeePassHTTP)
|
||||||
|
- Browser integration with keepassxc-browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/iopaggbpplllidnfmcghoonnokmjoicf)
|
||||||
- Many bug fixes
|
- Many bug fixes
|
||||||
|
|
||||||
For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document.
|
For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document.
|
||||||
|
|
||||||
## Building KeePassXC
|
## Building KeePassXC
|
||||||
|
|
||||||
Detailed instructions are available in the [Build and Install](./INSTALL.md)
|
Detailed instructions are available in the [Build and Install](./INSTALL.md)
|
||||||
page or on the [Wiki page](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC).
|
page or on the [Wiki page](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We are always looking for suggestions how to improve our application.
|
We are always looking for suggestions how to improve our application.
|
||||||
If you find any bugs or have an idea for a new feature, please let us know by
|
If you find any bugs or have an idea for a new feature, please let us know by
|
||||||
opening a report in our [issue tracker](https://github.com/keepassxreboot/keepassxc/issues)
|
opening a report in our [issue tracker](https://github.com/keepassxreboot/keepassxc/issues)
|
||||||
on GitHub or join us on IRC on freenode channels #keepassxc or #keepassxc-dev.
|
on GitHub or join us on IRC on freenode channels #keepassxc or #keepassxc-dev.
|
||||||
|
|
||||||
You can of course also directly contribute your own code. We are happy to accept your pull requests.
|
You can of course also directly contribute your own code. We are happy to accept your pull requests.
|
||||||
@ -57,11 +58,11 @@ Please read the [CONTRIBUTING document](.github/CONTRIBUTING.md) for further inf
|
|||||||
The KeePassHTTP protocol is not a highly secure protocol.
|
The KeePassHTTP protocol is not a highly secure protocol.
|
||||||
It has a certain flaw which could allow an attacker to decrypt your passwords
|
It has a certain flaw which could allow an attacker to decrypt your passwords
|
||||||
should they manage to impersonate the web browser extension from a remote address.
|
should they manage to impersonate the web browser extension from a remote address.
|
||||||
<!--intercept communication between a KeePassHTTP server
|
<!--intercept communication between a KeePassHTTP server
|
||||||
and PassIFox/chromeIPass over a network connection -->
|
and PassIFox/chromeIPass over a network connection -->
|
||||||
(See [here](https://github.com/pfn/keepasshttp/issues/258) and [here](https://github.com/keepassxreboot/keepassxc/issues/147)).
|
(See [here](https://github.com/pfn/keepasshttp/issues/258) and [here](https://github.com/keepassxreboot/keepassxc/issues/147)).
|
||||||
|
|
||||||
To minimize the risk, KeePassXC strictly limits communication between itself
|
To minimize the risk, KeePassXC strictly limits communication between itself
|
||||||
and the browser plugin to your local computer (localhost).
|
and the browser plugin to your local computer (localhost).
|
||||||
This makes your passwords quite safe,
|
This makes your passwords quite safe,
|
||||||
but as with all open source software, use it at your own risk!
|
but as with all open source software, use it at your own risk!
|
||||||
|
267
cmake/Findsodium.cmake
Normal file
267
cmake/Findsodium.cmake
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
#
|
||||||
|
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
# Tries to find the local libsodium installation.
|
||||||
|
#
|
||||||
|
# On Windows the sodium_DIR environment variable is used as a default
|
||||||
|
# hint which can be overridden by setting the corresponding cmake variable.
|
||||||
|
#
|
||||||
|
# Once done the following variables will be defined:
|
||||||
|
#
|
||||||
|
# sodium_FOUND
|
||||||
|
# sodium_INCLUDE_DIR
|
||||||
|
# sodium_LIBRARY_DEBUG
|
||||||
|
# sodium_LIBRARY_RELEASE
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Furthermore an imported "sodium" target is created.
|
||||||
|
#
|
||||||
|
|
||||||
|
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
|
||||||
|
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(_GCC_COMPATIBLE 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# static library option
|
||||||
|
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium")
|
||||||
|
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
|
||||||
|
unset(sodium_LIBRARY CACHE)
|
||||||
|
unset(sodium_LIBRARY_DEBUG CACHE)
|
||||||
|
unset(sodium_LIBRARY_RELEASE CACHE)
|
||||||
|
unset(sodium_DLL_DEBUG CACHE)
|
||||||
|
unset(sodium_DLL_RELEASE CACHE)
|
||||||
|
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# UNIX
|
||||||
|
if (UNIX)
|
||||||
|
# import pkg-config
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if (PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(sodium_PKG QUIET libsodium)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(sodium_USE_STATIC_LIBS)
|
||||||
|
set(XPREFIX sodium_PKG_STATIC)
|
||||||
|
else()
|
||||||
|
set(XPREFIX sodium_PKG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(sodium_INCLUDE_DIR sodium.h
|
||||||
|
HINTS ${${XPREFIX}_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES} sodium
|
||||||
|
HINTS ${${XPREFIX}_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES} sodium
|
||||||
|
HINTS ${${XPREFIX}_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Windows
|
||||||
|
elseif (WIN32)
|
||||||
|
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
|
||||||
|
mark_as_advanced(sodium_DIR)
|
||||||
|
|
||||||
|
find_path(sodium_INCLUDE_DIR sodium.h
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES include
|
||||||
|
)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# detect target architecture
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.c" [=[
|
||||||
|
#if defined _M_IX86
|
||||||
|
#error ARCH_VALUE x86_32
|
||||||
|
#elif defined _M_X64
|
||||||
|
#error ARCH_VALUE x86_64
|
||||||
|
#endif
|
||||||
|
#error ARCH_VALUE unknown
|
||||||
|
]=])
|
||||||
|
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.c"
|
||||||
|
OUTPUT_VARIABLE _COMPILATION_LOG
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
|
||||||
|
|
||||||
|
# construct library path
|
||||||
|
if (_TARGET_ARCH STREQUAL "x86_32")
|
||||||
|
string(APPEND _PLATFORM_PATH "Win32")
|
||||||
|
elseif(_TARGET_ARCH STREQUAL "x86_64")
|
||||||
|
string(APPEND _PLATFORM_PATH "x64")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
|
||||||
|
endif()
|
||||||
|
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
|
||||||
|
|
||||||
|
if (MSVC_VERSION LESS 1900)
|
||||||
|
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
|
||||||
|
else()
|
||||||
|
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
|
||||||
|
endif()
|
||||||
|
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
|
||||||
|
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
string(APPEND _PLATFORM_PATH "/static")
|
||||||
|
else()
|
||||||
|
string(APPEND _PLATFORM_PATH "/dynamic")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
|
||||||
|
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
|
||||||
|
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.lib
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.lib
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
if (NOT sodium_USE_STATIC_LIBS)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
|
||||||
|
find_library(sodium_DLL_DEBUG libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_RELEASE libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
elseif(_GCC_COMPATIBLE)
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB _DLL
|
||||||
|
LIST_DIRECTORIES false
|
||||||
|
RELATIVE "${sodium_DIR}/bin"
|
||||||
|
"${sodium_DIR}/bin/libsodium*.dll"
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES bin
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES bin
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# unsupported
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# common stuff
|
||||||
|
|
||||||
|
# extract sodium version
|
||||||
|
if (sodium_INCLUDE_DIR)
|
||||||
|
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
|
||||||
|
if (EXISTS _VERSION_HEADER)
|
||||||
|
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
|
||||||
|
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
|
||||||
|
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
|
||||||
|
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# communicate results
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(sodium
|
||||||
|
REQUIRED_VARS
|
||||||
|
sodium_LIBRARY_RELEASE
|
||||||
|
sodium_LIBRARY_DEBUG
|
||||||
|
sodium_INCLUDE_DIR
|
||||||
|
VERSION_VAR
|
||||||
|
sodium_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
# mark file paths as advanced
|
||||||
|
mark_as_advanced(sodium_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(sodium_LIBRARY_DEBUG)
|
||||||
|
mark_as_advanced(sodium_LIBRARY_RELEASE)
|
||||||
|
if (WIN32)
|
||||||
|
mark_as_advanced(sodium_DLL_DEBUG)
|
||||||
|
mark_as_advanced(sodium_DLL_RELEASE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# create imported target
|
||||||
|
if(sodium_USE_STATIC_LIBS)
|
||||||
|
set(_LIB_TYPE STATIC)
|
||||||
|
else()
|
||||||
|
set(_LIB_TYPE SHARED)
|
||||||
|
endif()
|
||||||
|
add_library(sodium ${_LIB_TYPE} IMPORTED)
|
||||||
|
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
|
||||||
|
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
if (UNIX)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
elseif (WIN32)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
@ -29,6 +29,7 @@ parts:
|
|||||||
- -DWITH_XC_AUTOTYPE=ON
|
- -DWITH_XC_AUTOTYPE=ON
|
||||||
- -DWITH_XC_HTTP=ON
|
- -DWITH_XC_HTTP=ON
|
||||||
- -DWITH_XC_YUBIKEY=ON
|
- -DWITH_XC_YUBIKEY=ON
|
||||||
|
- -DWITH_XC_BROWSER=ON
|
||||||
build-packages:
|
build-packages:
|
||||||
- g++
|
- g++
|
||||||
- libgcrypt20-dev
|
- libgcrypt20-dev
|
||||||
@ -41,6 +42,7 @@ parts:
|
|||||||
- libxtst-dev
|
- libxtst-dev
|
||||||
- libyubikey-dev
|
- libyubikey-dev
|
||||||
- libykpers-1-dev
|
- libykpers-1-dev
|
||||||
|
- libsodium-dev
|
||||||
stage-packages:
|
stage-packages:
|
||||||
- dbus
|
- dbus
|
||||||
- qttranslations5-l10n # common translations
|
- qttranslations5-l10n # common translations
|
||||||
@ -65,4 +67,3 @@ parts:
|
|||||||
- libqt5svg5 # for loading icon themes which are svg
|
- libqt5svg5 # for loading icon themes which are svg
|
||||||
- locales-all
|
- locales-all
|
||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
|
|
||||||
|
@ -182,12 +182,20 @@ add_feature_info(AutoType WITH_XC_AUTOTYPE "Automatic password typing")
|
|||||||
add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with ChromeIPass and PassIFox")
|
add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with ChromeIPass and PassIFox")
|
||||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||||
|
add_feature_info(keepassxc-browser WITH_XC_BROWSER "KeePassXC browser support with keepassxc-browser")
|
||||||
|
|
||||||
add_subdirectory(http)
|
add_subdirectory(http)
|
||||||
if(WITH_XC_HTTP)
|
if(WITH_XC_HTTP)
|
||||||
set(keepasshttp_LIB keepasshttp qhttp Qt5::Network)
|
set(keepasshttp_LIB keepasshttp qhttp Qt5::Network)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
||||||
|
add_subdirectory(browser)
|
||||||
|
add_subdirectory(proxy)
|
||||||
|
if(WITH_XC_BROWSER)
|
||||||
|
set(keepassxcbrowser_LIB keepassxcbrowser)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(autotype)
|
add_subdirectory(autotype)
|
||||||
add_subdirectory(cli)
|
add_subdirectory(cli)
|
||||||
|
|
||||||
@ -231,6 +239,7 @@ add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
|||||||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
||||||
target_link_libraries(keepassx_core
|
target_link_libraries(keepassx_core
|
||||||
${keepasshttp_LIB}
|
${keepasshttp_LIB}
|
||||||
|
${keepassxcbrowser_LIB}
|
||||||
${autotype_LIB}
|
${autotype_LIB}
|
||||||
${sshagent_LIB}
|
${sshagent_LIB}
|
||||||
${YUBIKEY_LIBRARIES}
|
${YUBIKEY_LIBRARIES}
|
||||||
@ -297,6 +306,28 @@ if(APPLE AND WITH_APP_BUNDLE)
|
|||||||
COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app
|
COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
COMMENT "Deploying app bundle")
|
COMMENT "Deploying app bundle")
|
||||||
|
|
||||||
|
if(WITH_XC_BROWSER)
|
||||||
|
set(PROXY_BINARY_DIR "${CMAKE_BINARY_DIR}/src/proxy/keepassxc-proxy")
|
||||||
|
set(PROXY_APP_DIR "${PROGNAME}.app/Contents/MacOS/keepassxc-proxy")
|
||||||
|
add_custom_command(TARGET ${PROGNAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${PROXY_BINARY_DIR} ${PROXY_APP_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
|
COMMENT "Copying keepassxc-proxy inside the application")
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROGNAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore "@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore" ${PROXY_APP_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
|
COMMENT "Changing linking of keepassxc-proxy QtCore")
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROGNAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork "@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork" ${PROXY_APP_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
|
COMMENT "Changing linking of keepassxc-proxy QtNetwork")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS ${PROGNAME}
|
install(TARGETS ${PROGNAME}
|
||||||
|
59
src/browser/BrowserAccessControlDialog.cpp
Executable file
59
src/browser/BrowserAccessControlDialog.cpp
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BrowserAccessControlDialog.h"
|
||||||
|
#include "ui_BrowserAccessControlDialog.h"
|
||||||
|
#include "core/Entry.h"
|
||||||
|
|
||||||
|
BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::BrowserAccessControlDialog())
|
||||||
|
{
|
||||||
|
this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept()));
|
||||||
|
connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserAccessControlDialog::~BrowserAccessControlDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserAccessControlDialog::setUrl(const QString& url)
|
||||||
|
{
|
||||||
|
ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n"
|
||||||
|
"Please select whether you want to allow access.")).arg(QUrl(url).host()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserAccessControlDialog::setItems(const QList<Entry*>& items)
|
||||||
|
{
|
||||||
|
for (Entry* entry : items) {
|
||||||
|
ui->itemsList->addItem(entry->title() + " - " + entry->username());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserAccessControlDialog::remember() const
|
||||||
|
{
|
||||||
|
return ui->rememberDecisionCheckBox->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserAccessControlDialog::setRemember(bool r)
|
||||||
|
{
|
||||||
|
ui->rememberDecisionCheckBox->setChecked(r);
|
||||||
|
}
|
48
src/browser/BrowserAccessControlDialog.h
Executable file
48
src/browser/BrowserAccessControlDialog.h
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERACCESSCONTROLDIALOG_H
|
||||||
|
#define BROWSERACCESSCONTROLDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class BrowserAccessControlDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserAccessControlDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BrowserAccessControlDialog(QWidget* parent = nullptr);
|
||||||
|
~BrowserAccessControlDialog();
|
||||||
|
|
||||||
|
void setUrl(const QString& url);
|
||||||
|
void setItems(const QList<Entry*>& items);
|
||||||
|
bool remember() const;
|
||||||
|
void setRemember(bool r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::BrowserAccessControlDialog> ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERACCESSCONTROLDIALOG_H
|
69
src/browser/BrowserAccessControlDialog.ui
Executable file
69
src/browser/BrowserAccessControlDialog.ui
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>BrowserAccessControlDialog</class>
|
||||||
|
<widget class="QDialog" name="BrowserAccessControlDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>221</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>keepassxc-browser Confirm Access</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="itemsList"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="rememberDecisionCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remember this decision</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="allowButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="denyButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deny</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
546
src/browser/BrowserAction.cpp
Executable file
546
src/browser/BrowserAction.cpp
Executable file
@ -0,0 +1,546 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include "BrowserAction.h"
|
||||||
|
#include "BrowserSettings.h"
|
||||||
|
#include "sodium.h"
|
||||||
|
#include "sodium/crypto_box.h"
|
||||||
|
#include "sodium/randombytes.h"
|
||||||
|
#include "config-keepassx.h"
|
||||||
|
|
||||||
|
BrowserAction::BrowserAction(BrowserService& browserService) :
|
||||||
|
m_mutex(QMutex::Recursive),
|
||||||
|
m_browserService(browserService),
|
||||||
|
m_associated(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::readResponse(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
if (json.isEmpty()) {
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString action = json.value("action").toString();
|
||||||
|
if (action.isEmpty()) {
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !m_browserService.isDatabaseOpened()) {
|
||||||
|
if (m_clientPublicKey.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED);
|
||||||
|
} else if (!m_browserService.openDatabase()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleAction(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Private functions
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleAction(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
QString action = json.value("action").toString();
|
||||||
|
if (action.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.compare("change-public-keys", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleChangePublicKeys(json, action);
|
||||||
|
} else if (action.compare("get-databasehash", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleGetDatabaseHash(json, action);
|
||||||
|
} else if (action.compare("associate", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleAssociate(json, action);
|
||||||
|
} else if (action.compare("test-associate", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleTestAssociate(json, action);
|
||||||
|
} else if (action.compare("get-logins", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleGetLogins(json, action);
|
||||||
|
} else if (action.compare("generate-password", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleGeneratePassword(json, action);
|
||||||
|
} else if (action.compare("set-login", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleSetLogin(json, action);
|
||||||
|
} else if (action.compare("lock-database", Qt::CaseSensitive) == 0) {
|
||||||
|
return handleLockDatabase(json, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action was not recognized
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleChangePublicKeys(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString clientPublicKey = json.value("publicKey").toString();
|
||||||
|
|
||||||
|
if (clientPublicKey.isEmpty() || nonce.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_associated = false;
|
||||||
|
unsigned char pk[crypto_box_PUBLICKEYBYTES];
|
||||||
|
unsigned char sk[crypto_box_SECRETKEYBYTES];
|
||||||
|
crypto_box_keypair(pk, sk);
|
||||||
|
|
||||||
|
const QString publicKey = getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||||
|
const QString secretKey = getBase64FromKey(sk, crypto_box_SECRETKEYBYTES);
|
||||||
|
m_clientPublicKey = clientPublicKey;
|
||||||
|
m_publicKey = publicKey;
|
||||||
|
m_secretKey = secretKey;
|
||||||
|
|
||||||
|
QJsonObject response = buildMessage(incrementNonce(nonce));
|
||||||
|
response["action"] = action;
|
||||||
|
response["publicKey"] = publicKey;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString command = decrypted.value("action").toString();
|
||||||
|
if (!command.isEmpty() && command.compare("get-databasehash", Qt::CaseSensitive) == 0) {
|
||||||
|
QJsonObject message;
|
||||||
|
message["hash"] = hash;
|
||||||
|
message["version"] = KEEPASSX_VERSION;
|
||||||
|
return buildResponse(action, message, incrementNonce(nonce));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString key = decrypted.value("key").toString();
|
||||||
|
if (key.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (key.compare(m_clientPublicKey, Qt::CaseSensitive) == 0) {
|
||||||
|
const QString id = m_browserService.storeKey(key);
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_associated = true;
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
message["hash"] = hash;
|
||||||
|
message["id"] = id;
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString responseKey = decrypted.value("key").toString();
|
||||||
|
const QString id = decrypted.value("id").toString();
|
||||||
|
if (responseKey.isEmpty() || id.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
const QString key = m_browserService.getKey(id);
|
||||||
|
if (key.isEmpty() || key.compare(responseKey, Qt::CaseSensitive) != 0) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_associated = true;
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
message["hash"] = hash;
|
||||||
|
message["id"] = id;
|
||||||
|
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_associated) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString url = decrypted.value("url").toString();
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_NO_URL_PROVIDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString id = decrypted.value("id").toString();
|
||||||
|
const QString submit = decrypted.value("submitUrl").toString();
|
||||||
|
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "");
|
||||||
|
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
return QJsonObject(); // No logins found. Not an error, return an empty JSON object.
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
message["count"] = users.count();
|
||||||
|
message["entries"] = users;
|
||||||
|
message["hash"] = hash;
|
||||||
|
message["id"] = id;
|
||||||
|
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleGeneratePassword(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString password = BrowserSettings::generatePassword();
|
||||||
|
const QString bits = QString::number(BrowserSettings::getbits()); // For some reason this always returns 1140 bits?
|
||||||
|
|
||||||
|
if (nonce.isEmpty() || password.isEmpty()) {
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
QJsonObject passwd;
|
||||||
|
passwd["login"] = QString::number(password.length() * 8); //bits;
|
||||||
|
passwd["password"] = password;
|
||||||
|
arr.append(passwd);
|
||||||
|
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
message["entries"] = arr;
|
||||||
|
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_associated) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString url = decrypted.value("url").toString();
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_NO_URL_PROVIDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString id = decrypted.value("id").toString();
|
||||||
|
const QString login = decrypted.value("login").toString();
|
||||||
|
const QString password = decrypted.value("password").toString();
|
||||||
|
const QString submitUrl = decrypted.value("submitUrl").toString();
|
||||||
|
const QString uuid = decrypted.value("uuid").toString();
|
||||||
|
const QString realm;
|
||||||
|
|
||||||
|
if (uuid.isEmpty()) {
|
||||||
|
m_browserService.addEntry(id, login, password, url, submitUrl, realm);
|
||||||
|
} else {
|
||||||
|
m_browserService.updateEntry(id, uuid, login, password, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
message["count"] = QJsonValue::Null;
|
||||||
|
message["entries"] = QJsonValue::Null;
|
||||||
|
message["error"] = "";
|
||||||
|
message["hash"] = hash;
|
||||||
|
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QString& action)
|
||||||
|
{
|
||||||
|
const QString hash = getDatabaseHash();
|
||||||
|
const QString nonce = json.value("nonce").toString();
|
||||||
|
const QString encrypted = json.value("message").toString();
|
||||||
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce, action);
|
||||||
|
|
||||||
|
if (decrypted.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash.isEmpty()) {
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString command = decrypted.value("action").toString();
|
||||||
|
if (!command.isEmpty() && command.compare("lock-database", Qt::CaseSensitive) == 0) {
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_browserService.lockDatabase();
|
||||||
|
|
||||||
|
const QString newNonce = incrementNonce(nonce);
|
||||||
|
QJsonObject message = buildMessage(newNonce);
|
||||||
|
|
||||||
|
return buildResponse(action, message, newNonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const
|
||||||
|
{
|
||||||
|
QJsonObject response;
|
||||||
|
response["action"] = action;
|
||||||
|
response["errorCode"] = QString::number(errorCode);
|
||||||
|
response["error"] = getErrorMessage(errorCode);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::buildMessage(const QString& nonce) const
|
||||||
|
{
|
||||||
|
QJsonObject message;
|
||||||
|
message["version"] = KEEPASSX_VERSION;
|
||||||
|
message["success"] = "true";
|
||||||
|
message["nonce"] = nonce;
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::buildResponse(const QString& action, const QJsonObject& message, const QString& nonce)
|
||||||
|
{
|
||||||
|
QJsonObject response;
|
||||||
|
response["action"] = action;
|
||||||
|
response["message"] = encryptMessage(message, nonce);
|
||||||
|
response["nonce"] = nonce;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::getErrorMessage(const int errorCode) const
|
||||||
|
{
|
||||||
|
switch (errorCode) {
|
||||||
|
case ERROR_KEEPASS_DATABASE_NOT_OPENED: return "Database not opened";
|
||||||
|
case ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED: return "Database hash not available";
|
||||||
|
case ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED: return "Client public key not received";
|
||||||
|
case ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE: return "Cannot decrypt message";
|
||||||
|
case ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED: return "Timeout or cannot connect to KeePassXC";
|
||||||
|
case ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED: return "Action cancelled or denied";
|
||||||
|
case ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE: return "Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC?";
|
||||||
|
case ERROR_KEEPASS_ASSOCIATION_FAILED: return "KeePassXC association failed, try again";
|
||||||
|
case ERROR_KEEPASS_KEY_CHANGE_FAILED: return "Key change was not successful";
|
||||||
|
case ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED: return "Encryption key is not recognized";
|
||||||
|
case ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND: return "No saved databases found";
|
||||||
|
case ERROR_KEEPASS_INCORRECT_ACTION: return "Incorrect action";
|
||||||
|
case ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED: return "Empty message received";
|
||||||
|
case ERROR_KEEPASS_NO_URL_PROVIDED: return "No URL provided";
|
||||||
|
default: return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::getDatabaseHash()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
QByteArray hash = QCryptographicHash::hash(
|
||||||
|
(m_browserService.getDatabaseRootUuid() + m_browserService.getDatabaseRecycleBinUuid()).toUtf8(),
|
||||||
|
QCryptographicHash::Sha256).toHex();
|
||||||
|
return QString(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::encryptMessage(const QJsonObject& message, const QString& nonce)
|
||||||
|
{
|
||||||
|
if (message.isEmpty() || nonce.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString reply(QJsonDocument(message).toJson());
|
||||||
|
if (!reply.isEmpty()) {
|
||||||
|
return encrypt(reply, nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::decryptMessage(const QString& message, const QString& nonce, const QString& action)
|
||||||
|
{
|
||||||
|
if (message.isEmpty() || nonce.isEmpty()) {
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba = decrypt(message, nonce);
|
||||||
|
if (!ba.isEmpty()) {
|
||||||
|
return getJsonObject(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::encrypt(const QString plaintext, const QString nonce)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
const QByteArray ma = plaintext.toUtf8();
|
||||||
|
const QByteArray na = base64Decode(nonce);
|
||||||
|
const QByteArray ca = base64Decode(m_clientPublicKey);
|
||||||
|
const QByteArray sa = base64Decode(m_secretKey);
|
||||||
|
|
||||||
|
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||||
|
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||||
|
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||||
|
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||||
|
|
||||||
|
std::vector<unsigned char> e;
|
||||||
|
e.resize(max_length);
|
||||||
|
|
||||||
|
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crypto_box_easy(e.data(), m.data(), m.size(), n.data(), ck.data(), sk.data()) == 0) {
|
||||||
|
QByteArray res = getQByteArray(e.data(), (crypto_box_MACBYTES + ma.length()));
|
||||||
|
return res.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray BrowserAction::decrypt(const QString encrypted, const QString nonce)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
const QByteArray ma = base64Decode(encrypted);
|
||||||
|
const QByteArray na = base64Decode(nonce);
|
||||||
|
const QByteArray ca = base64Decode(m_clientPublicKey);
|
||||||
|
const QByteArray sa = base64Decode(m_secretKey);
|
||||||
|
|
||||||
|
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||||
|
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||||
|
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||||
|
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||||
|
|
||||||
|
std::vector<unsigned char> d;
|
||||||
|
d.resize(max_length);
|
||||||
|
|
||||||
|
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crypto_box_open_easy(d.data(), m.data(), ma.length(), n.data(), ck.data(), sk.data()) == 0) {
|
||||||
|
return getQByteArray(d.data(), std::char_traits<char>::length(reinterpret_cast<const char *>(d.data())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::getBase64FromKey(const uchar* array, const uint len)
|
||||||
|
{
|
||||||
|
return getQByteArray(array, len).toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray BrowserAction::getQByteArray(const uchar* array, const uint len) const
|
||||||
|
{
|
||||||
|
QByteArray qba;
|
||||||
|
qba.reserve(len);
|
||||||
|
for (uint i = 0; i < len; ++i) {
|
||||||
|
qba.append(static_cast<char>(array[i]));
|
||||||
|
}
|
||||||
|
return qba;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::getJsonObject(const uchar* pArray, const uint len) const
|
||||||
|
{
|
||||||
|
QByteArray arr = getQByteArray(pArray, len);
|
||||||
|
QJsonParseError err;
|
||||||
|
QJsonDocument doc(QJsonDocument::fromJson(arr, &err));
|
||||||
|
return doc.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserAction::getJsonObject(const QByteArray ba) const
|
||||||
|
{
|
||||||
|
QJsonParseError err;
|
||||||
|
QJsonDocument doc(QJsonDocument::fromJson(ba, &err));
|
||||||
|
return doc.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray BrowserAction::base64Decode(const QString str)
|
||||||
|
{
|
||||||
|
return QByteArray::fromBase64(str.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserAction::incrementNonce(const QString& nonce)
|
||||||
|
{
|
||||||
|
const QByteArray nonceArray = base64Decode(nonce);
|
||||||
|
std::vector<unsigned char> n(nonceArray.cbegin(), nonceArray.cend());
|
||||||
|
|
||||||
|
sodium_increment(n.data(), n.size());
|
||||||
|
return getQByteArray(n.data(), n.size()).toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserAction::removeSharedEncryptionKeys()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_browserService.removeSharedEncryptionKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserAction::removeStoredPermissions()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_browserService.removeStoredPermissions();
|
||||||
|
}
|
97
src/browser/BrowserAction.h
Executable file
97
src/browser/BrowserAction.h
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERACTION_H
|
||||||
|
#define BROWSERACTION_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMutex>
|
||||||
|
#include "BrowserService.h"
|
||||||
|
|
||||||
|
class BrowserAction : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ERROR_KEEPASS_DATABASE_NOT_OPENED = 1,
|
||||||
|
ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED = 2,
|
||||||
|
ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED = 3,
|
||||||
|
ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE = 4,
|
||||||
|
ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED = 5,
|
||||||
|
ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED = 6,
|
||||||
|
ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE = 7,
|
||||||
|
ERROR_KEEPASS_ASSOCIATION_FAILED = 8,
|
||||||
|
ERROR_KEEPASS_KEY_CHANGE_FAILED = 9,
|
||||||
|
ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED = 10,
|
||||||
|
ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11,
|
||||||
|
ERROR_KEEPASS_INCORRECT_ACTION = 12,
|
||||||
|
ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13,
|
||||||
|
ERROR_KEEPASS_NO_URL_PROVIDED = 14
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BrowserAction(BrowserService& browserService);
|
||||||
|
~BrowserAction() = default;
|
||||||
|
|
||||||
|
QJsonObject readResponse(const QJsonObject& json);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void removeSharedEncryptionKeys();
|
||||||
|
void removeStoredPermissions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonObject handleAction(const QJsonObject& json);
|
||||||
|
QJsonObject handleChangePublicKeys(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleGetDatabaseHash(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleAssociate(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleTestAssociate(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleGetLogins(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleGeneratePassword(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleSetLogin(const QJsonObject& json, const QString& action);
|
||||||
|
QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action);
|
||||||
|
|
||||||
|
QJsonObject buildMessage(const QString& nonce) const;
|
||||||
|
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);
|
||||||
|
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
|
||||||
|
QString getErrorMessage(const int errorCode) const;
|
||||||
|
QString getDatabaseHash();
|
||||||
|
|
||||||
|
QString encryptMessage(const QJsonObject& message, const QString& nonce);
|
||||||
|
QJsonObject decryptMessage(const QString& message, const QString& nonce, const QString& action = QString());
|
||||||
|
QString encrypt(const QString plaintext, const QString nonce);
|
||||||
|
QByteArray decrypt(const QString encrypted, const QString nonce);
|
||||||
|
|
||||||
|
QString getBase64FromKey(const uchar* array, const uint len);
|
||||||
|
QByteArray getQByteArray(const uchar* array, const uint len) const;
|
||||||
|
QJsonObject getJsonObject(const uchar* pArray, const uint len) const;
|
||||||
|
QJsonObject getJsonObject(const QByteArray ba) const;
|
||||||
|
QByteArray base64Decode(const QString str);
|
||||||
|
QString incrementNonce(const QString& nonce);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex m_mutex;
|
||||||
|
BrowserService& m_browserService;
|
||||||
|
QString m_clientPublicKey;
|
||||||
|
QString m_publicKey;
|
||||||
|
QString m_secretKey;
|
||||||
|
bool m_associated;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERACTION_H
|
77
src/browser/BrowserClients.cpp
Executable file
77
src/browser/BrowserClients.cpp
Executable file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include "BrowserClients.h"
|
||||||
|
|
||||||
|
BrowserClients::BrowserClients(BrowserService& browserService) :
|
||||||
|
m_mutex(QMutex::Recursive),
|
||||||
|
m_browserService(browserService)
|
||||||
|
{
|
||||||
|
m_clients.reserve(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserClients::readResponse(const QByteArray& arr)
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
const QJsonObject message = byteArrayToJson(arr);
|
||||||
|
const QString clientID = getClientID(message);
|
||||||
|
|
||||||
|
if (!clientID.isEmpty()) {
|
||||||
|
const ClientPtr client = getClient(clientID);
|
||||||
|
if (client->browserAction) {
|
||||||
|
json = client->browserAction->readResponse(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserClients::byteArrayToJson(const QByteArray& arr) const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
QJsonParseError err;
|
||||||
|
QJsonDocument doc(QJsonDocument::fromJson(arr, &err));
|
||||||
|
if (doc.isObject()) {
|
||||||
|
json = doc.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserClients::getClientID(const QJsonObject& json) const
|
||||||
|
{
|
||||||
|
return json["clientID"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserClients::ClientPtr BrowserClients::getClient(const QString& clientID)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
for (const auto &i : m_clients) {
|
||||||
|
if (i->clientID.compare(clientID, Qt::CaseSensitive) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientID not found, create a new client
|
||||||
|
QSharedPointer<BrowserAction> ba = QSharedPointer<BrowserAction>::create(m_browserService);
|
||||||
|
ClientPtr client = ClientPtr::create(clientID, ba);
|
||||||
|
m_clients.push_back(client);
|
||||||
|
return m_clients.back();
|
||||||
|
}
|
56
src/browser/BrowserClients.h
Executable file
56
src/browser/BrowserClients.h
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERCLIENTS_H
|
||||||
|
#define BROWSERCLIENTS_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include "BrowserAction.h"
|
||||||
|
|
||||||
|
class BrowserClients
|
||||||
|
{
|
||||||
|
struct Client {
|
||||||
|
Client(const QString& id, QSharedPointer<BrowserAction> ba) : clientID(id), browserAction(ba) {}
|
||||||
|
QString clientID;
|
||||||
|
QSharedPointer<BrowserAction> browserAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QSharedPointer<Client> ClientPtr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BrowserClients(BrowserService& browserService);
|
||||||
|
~BrowserClients() = default;
|
||||||
|
|
||||||
|
QJsonObject readResponse(const QByteArray& arr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonObject byteArrayToJson(const QByteArray& arr) const;
|
||||||
|
QString getClientID(const QJsonObject& json) const;
|
||||||
|
ClientPtr getClient(const QString& clientID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex m_mutex;
|
||||||
|
QVector<ClientPtr> m_clients;
|
||||||
|
BrowserService& m_browserService;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERCLIENTS_H
|
109
src/browser/BrowserEntryConfig.cpp
Normal file
109
src/browser/BrowserEntryConfig.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BrowserEntryConfig.h"
|
||||||
|
#include <QtCore>
|
||||||
|
#include "core/Entry.h"
|
||||||
|
#include "core/EntryAttributes.h"
|
||||||
|
|
||||||
|
static const char KEEPASSBROWSER_NAME[] = "keepassxc-browser Settings"; //TODO: duplicated string (also in Service.cpp)
|
||||||
|
|
||||||
|
|
||||||
|
BrowserEntryConfig::BrowserEntryConfig(QObject* parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList BrowserEntryConfig::allowedHosts() const
|
||||||
|
{
|
||||||
|
return m_allowedHosts.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::setAllowedHosts(const QStringList& allowedHosts)
|
||||||
|
{
|
||||||
|
m_allowedHosts = allowedHosts.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList BrowserEntryConfig::deniedHosts() const
|
||||||
|
{
|
||||||
|
return m_deniedHosts.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::setDeniedHosts(const QStringList& deniedHosts)
|
||||||
|
{
|
||||||
|
m_deniedHosts = deniedHosts.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserEntryConfig::isAllowed(const QString& host) const
|
||||||
|
{
|
||||||
|
return m_allowedHosts.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::allow(const QString& host)
|
||||||
|
{
|
||||||
|
m_allowedHosts.insert(host);
|
||||||
|
m_deniedHosts.remove(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserEntryConfig::isDenied(const QString& host) const
|
||||||
|
{
|
||||||
|
return m_deniedHosts.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::deny(const QString& host)
|
||||||
|
{
|
||||||
|
m_deniedHosts.insert(host);
|
||||||
|
m_allowedHosts.remove(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserEntryConfig::realm() const
|
||||||
|
{
|
||||||
|
return m_realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::setRealm(const QString& realm)
|
||||||
|
{
|
||||||
|
m_realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserEntryConfig::load(const Entry* entry)
|
||||||
|
{
|
||||||
|
QString s = entry->attributes()->value(KEEPASSBROWSER_NAME);
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(s.toUtf8());
|
||||||
|
if (doc.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap map = doc.object().toVariantMap();
|
||||||
|
for (QVariantMap::const_iterator iter = map.cbegin(); iter != map.cend(); ++iter) {
|
||||||
|
setProperty(iter.key().toLatin1(), iter.value());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserEntryConfig::save(Entry* entry)
|
||||||
|
{
|
||||||
|
QVariantMap v = qo2qv(this);
|
||||||
|
QJsonObject o = QJsonObject::fromVariantMap(v);
|
||||||
|
QByteArray json = QJsonDocument(o).toJson(QJsonDocument::Compact);
|
||||||
|
entry->attributes()->set(KEEPASSBROWSER_NAME, json);
|
||||||
|
}
|
60
src/browser/BrowserEntryConfig.h
Normal file
60
src/browser/BrowserEntryConfig.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERENTRYCONFIG_H
|
||||||
|
#define BROWSERENTRYCONFIG_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QSet>
|
||||||
|
#include "Variant.h"
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
class BrowserEntryConfig : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QStringList Allow READ allowedHosts WRITE setAllowedHosts)
|
||||||
|
Q_PROPERTY(QStringList Deny READ deniedHosts WRITE setDeniedHosts )
|
||||||
|
Q_PROPERTY(QString Realm READ realm WRITE setRealm )
|
||||||
|
|
||||||
|
public:
|
||||||
|
BrowserEntryConfig(QObject* object = 0);
|
||||||
|
|
||||||
|
bool load(const Entry* entry);
|
||||||
|
void save(Entry* entry);
|
||||||
|
bool isAllowed(const QString& host) const;
|
||||||
|
void allow(const QString& host);
|
||||||
|
bool isDenied(const QString& host) const;
|
||||||
|
void deny(const QString& host);
|
||||||
|
QString realm() const;
|
||||||
|
void setRealm(const QString& realm);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList allowedHosts() const;
|
||||||
|
void setAllowedHosts(const QStringList& allowedHosts);
|
||||||
|
QStringList deniedHosts() const;
|
||||||
|
void setDeniedHosts(const QStringList& deniedHosts);
|
||||||
|
|
||||||
|
QSet<QString> m_allowedHosts;
|
||||||
|
QSet<QString> m_deniedHosts;
|
||||||
|
QString m_realm;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERENTRYCONFIG_H
|
104
src/browser/BrowserOptionDialog.cpp
Executable file
104
src/browser/BrowserOptionDialog.cpp
Executable file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BrowserOptionDialog.h"
|
||||||
|
#include "ui_BrowserOptionDialog.h"
|
||||||
|
#include "BrowserSettings.h"
|
||||||
|
#include "core/FilePath.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(new Ui::BrowserOptionDialog())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SIGNAL(removeSharedEncryptionKeys()));
|
||||||
|
connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SIGNAL(removeStoredPermissions()));
|
||||||
|
|
||||||
|
m_ui->warningWidget->showMessage(tr("The following options can be dangerous!\nChange them only if you know what you are doing."), MessageWidget::Warning);
|
||||||
|
m_ui->warningWidget->setIcon(FilePath::instance()->icon("status", "dialog-warning"));
|
||||||
|
m_ui->warningWidget->setCloseButtonVisible(false);
|
||||||
|
|
||||||
|
m_ui->tabWidget->setEnabled(m_ui->enableBrowserSupport->isChecked());
|
||||||
|
connect(m_ui->enableBrowserSupport, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(setEnabled(bool)));
|
||||||
|
|
||||||
|
m_ui->customProxyLocation->setEnabled(m_ui->useCustomProxy->isChecked());
|
||||||
|
connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocation, SLOT(setEnabled(bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserOptionDialog::~BrowserOptionDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserOptionDialog::loadSettings()
|
||||||
|
{
|
||||||
|
BrowserSettings settings;
|
||||||
|
m_ui->enableBrowserSupport->setChecked(settings.isEnabled());
|
||||||
|
|
||||||
|
m_ui->showNotification->setChecked(settings.showNotification());
|
||||||
|
m_ui->bestMatchOnly->setChecked(settings.bestMatchOnly());
|
||||||
|
m_ui->unlockDatabase->setChecked(settings.unlockDatabase());
|
||||||
|
m_ui->matchUrlScheme->setChecked(settings.matchUrlScheme());
|
||||||
|
|
||||||
|
if (settings.sortByUsername()) {
|
||||||
|
m_ui->sortByUsername->setChecked(true);
|
||||||
|
} else {
|
||||||
|
m_ui->sortByTitle->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->alwaysAllowAccess->setChecked(settings.alwaysAllowAccess());
|
||||||
|
m_ui->alwaysAllowUpdate->setChecked(settings.alwaysAllowUpdate());
|
||||||
|
m_ui->searchInAllDatabases->setChecked(settings.searchInAllDatabases());
|
||||||
|
m_ui->supportKphFields->setChecked(settings.supportKphFields());
|
||||||
|
m_ui->supportBrowserProxy->setChecked(settings.supportBrowserProxy());
|
||||||
|
m_ui->useCustomProxy->setChecked(settings.useCustomProxy());
|
||||||
|
m_ui->customProxyLocation->setText(settings.customProxyLocation());
|
||||||
|
m_ui->updateBinaryPath->setChecked(settings.updateBinaryPath());
|
||||||
|
m_ui->chromeSupport->setChecked(settings.chromeSupport());
|
||||||
|
m_ui->chromiumSupport->setChecked(settings.chromiumSupport());
|
||||||
|
m_ui->firefoxSupport->setChecked(settings.firefoxSupport());
|
||||||
|
m_ui->vivaldiSupport->setChecked(settings.vivaldiSupport());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserOptionDialog::saveSettings()
|
||||||
|
{
|
||||||
|
BrowserSettings settings;
|
||||||
|
settings.setEnabled(m_ui->enableBrowserSupport->isChecked());
|
||||||
|
settings.setShowNotification(m_ui->showNotification->isChecked());
|
||||||
|
settings.setBestMatchOnly(m_ui->bestMatchOnly->isChecked());
|
||||||
|
settings.setUnlockDatabase(m_ui->unlockDatabase->isChecked());
|
||||||
|
settings.setMatchUrlScheme(m_ui->matchUrlScheme->isChecked());
|
||||||
|
settings.setSortByUsername(m_ui->sortByUsername->isChecked());
|
||||||
|
|
||||||
|
settings.setSupportBrowserProxy(m_ui->supportBrowserProxy->isChecked());
|
||||||
|
settings.setUseCustomProxy(m_ui->useCustomProxy->isChecked());
|
||||||
|
settings.setCustomProxyLocation(m_ui->customProxyLocation->text());
|
||||||
|
|
||||||
|
settings.setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked());
|
||||||
|
settings.setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked());
|
||||||
|
settings.setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked());
|
||||||
|
settings.setSearchInAllDatabases(m_ui->searchInAllDatabases->isChecked());
|
||||||
|
settings.setSupportKphFields(m_ui->supportKphFields->isChecked());
|
||||||
|
|
||||||
|
settings.setChromeSupport(m_ui->chromeSupport->isChecked());
|
||||||
|
settings.setChromiumSupport(m_ui->chromiumSupport->isChecked());
|
||||||
|
settings.setFirefoxSupport(m_ui->firefoxSupport->isChecked());
|
||||||
|
settings.setVivaldiSupport(m_ui->vivaldiSupport->isChecked());
|
||||||
|
}
|
50
src/browser/BrowserOptionDialog.h
Executable file
50
src/browser/BrowserOptionDialog.h
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSEROPTIONDIALOG_H
|
||||||
|
#define BROWSEROPTIONDIALOG_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class BrowserOptionDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserOptionDialog : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BrowserOptionDialog(QWidget* parent = nullptr);
|
||||||
|
~BrowserOptionDialog();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void loadSettings();
|
||||||
|
void saveSettings();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void removeSharedEncryptionKeys();
|
||||||
|
void removeStoredPermissions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::BrowserOptionDialog> m_ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSEROPTIONDIALOG_H
|
327
src/browser/BrowserOptionDialog.ui
Executable file
327
src/browser/BrowserOptionDialog.ui
Executable file
@ -0,0 +1,327 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>BrowserOptionDialog</class>
|
||||||
|
<widget class="QWidget" name="BrowserOptionDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>577</width>
|
||||||
|
<height>404</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="enableBrowserSupport">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>This is required for accessing your databases with keepassxc-browser</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable KeepassXC browser extension</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>General</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showNotification">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sh&ow a notification when credentials are requested</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="bestMatchOnly">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Only returns the best matches for a specific URL instead of all entries for the whole domain.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Return only best-matching entries</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="unlockDatabase">
|
||||||
|
<property name="text">
|
||||||
|
<string>Re&quest to unlock the database if it is locked</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="matchUrlScheme">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Only entries with the same scheme (http://, https://, ...) are returned.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Match URL schemes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="sortByUsername">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sort matching entries by &username</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="sortByTitle">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sort &matching entries by title</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeSharedEncryptionKeys">
|
||||||
|
<property name="text">
|
||||||
|
<string>R&emove all shared encryption keys from active database</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeStoredPermissions">
|
||||||
|
<property name="text">
|
||||||
|
<string>Re&move all stored permissions from entries in active database</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_3">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Supported browsers</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="browserLabel1">
|
||||||
|
<property name="text">
|
||||||
|
<string>Native messaging requires certain .json files to be installed. Already installed browsers are automatically detected.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="browserLabel2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable KeePassXC native messaging extension for these browsers:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="chromeSupport">
|
||||||
|
<property name="text">
|
||||||
|
<string>Chrome</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="chromiumSupport">
|
||||||
|
<property name="text">
|
||||||
|
<string>Chromium</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="firefoxSupport">
|
||||||
|
<property name="text">
|
||||||
|
<string>Firefox</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="vivaldiSupport">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vivaldi</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Advanced</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="MessageWidget" name="warningWidget" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="alwaysAllowAccess">
|
||||||
|
<property name="text">
|
||||||
|
<string>Always allow &access to entries</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="alwaysAllowUpdate">
|
||||||
|
<property name="text">
|
||||||
|
<string>Always allow &updating entries</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="searchInAllDatabases">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Only the selected database has to be connected with a client.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Searc&h in all opened databases for matching entries</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="supportKphFields">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Automatically creating or updating string fields is not supported.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Return advanced string fields which start with "KPH: "</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="updateBinaryPath">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Update KeePassXC binary path automatically to native messaging scripts on startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="supportBrowserProxy">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Support a proxy application between KeePassXC and browser extension.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Enable support for proxy application between KeePassXC and browser extension</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useCustomProxy">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Use a custom proxy location if you installed a proxy manually.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Use a custom proxy location</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="customProxyLocation">
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>999</number>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>MessageWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>gui/MessageWidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
734
src/browser/BrowserService.cpp
Normal file
734
src/browser/BrowserService.cpp
Normal file
@ -0,0 +1,734 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "BrowserService.h"
|
||||||
|
#include "BrowserSettings.h"
|
||||||
|
#include "BrowserEntryConfig.h"
|
||||||
|
#include "BrowserAccessControlDialog.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "core/EntrySearcher.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
|
#include "core/Uuid.h"
|
||||||
|
#include "core/PasswordGenerator.h"
|
||||||
|
|
||||||
|
|
||||||
|
// de887cc3-0363-43b8-974b-5911b8816224
|
||||||
|
static const unsigned char KEEPASSXCBROWSER_UUID_DATA[] = {
|
||||||
|
0xde, 0x88, 0x7c, 0xc3, 0x03, 0x63, 0x43, 0xb8,
|
||||||
|
0x97, 0x4b, 0x59, 0x11, 0xb8, 0x81, 0x62, 0x24
|
||||||
|
};
|
||||||
|
static const Uuid KEEPASSXCBROWSER_UUID = Uuid(QByteArray::fromRawData(reinterpret_cast<const char *>(KEEPASSXCBROWSER_UUID_DATA), sizeof(KEEPASSXCBROWSER_UUID_DATA)));
|
||||||
|
static const char KEEPASSXCBROWSER_NAME[] = "keepassxc-browser Settings";
|
||||||
|
static const char ASSOCIATE_KEY_PREFIX[] = "Public Key: ";
|
||||||
|
static const char KEEPASSXCBROWSER_GROUP_NAME[] = "keepassxc-browser Passwords";
|
||||||
|
static int KEEPASSXCBROWSER_DEFAULT_ICON = 1;
|
||||||
|
|
||||||
|
BrowserService::BrowserService(DatabaseTabWidget* parent) :
|
||||||
|
m_dbTabWidget(parent),
|
||||||
|
m_dialogActive(false)
|
||||||
|
{
|
||||||
|
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*)));
|
||||||
|
connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*)));
|
||||||
|
connect(m_dbTabWidget, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), this, SLOT(activateDatabaseChanged(DatabaseWidget*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserService::isDatabaseOpened() const
|
||||||
|
{
|
||||||
|
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
||||||
|
if (!dbWidget) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserService::openDatabase()
|
||||||
|
{
|
||||||
|
if (!BrowserSettings::unlockDatabase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
||||||
|
if (!dbWidget) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dbTabWidget->activateWindow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::lockDatabase()
|
||||||
|
{
|
||||||
|
if (thread() != QThread::currentThread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "lockDatabase", Qt::BlockingQueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
||||||
|
if (!dbWidget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode) {
|
||||||
|
dbWidget->lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserService::getDatabaseRootUuid()
|
||||||
|
{
|
||||||
|
Database* db = getDatabase();
|
||||||
|
if (!db) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* rootGroup = db->rootGroup();
|
||||||
|
if (!rootGroup) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootGroup->uuid().toHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserService::getDatabaseRecycleBinUuid()
|
||||||
|
{
|
||||||
|
Database* db = getDatabase();
|
||||||
|
if (!db) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* recycleBin = db->metadata()->recycleBin();
|
||||||
|
if (!recycleBin) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return recycleBin->uuid().toHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* BrowserService::getConfigEntry(bool create)
|
||||||
|
{
|
||||||
|
Entry* entry = nullptr;
|
||||||
|
Database* db = getDatabase();
|
||||||
|
if (!db) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = db->resolveEntry(KEEPASSXCBROWSER_UUID);
|
||||||
|
if (!entry && create) {
|
||||||
|
entry = new Entry();
|
||||||
|
entry->setTitle(QLatin1String(KEEPASSXCBROWSER_NAME));
|
||||||
|
entry->setUuid(KEEPASSXCBROWSER_UUID);
|
||||||
|
entry->setAutoTypeEnabled(false);
|
||||||
|
entry->setGroup(db->rootGroup());
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry && entry->group() == db->metadata()->recycleBin()) {
|
||||||
|
if (!create) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
entry->setGroup(db->rootGroup());
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserService::storeKey(const QString& key)
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
|
||||||
|
if (thread() != QThread::currentThread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "storeKey", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QString, id),
|
||||||
|
Q_ARG(const QString&, key));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* config = getConfigEntry(true);
|
||||||
|
if (!config) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains = false;
|
||||||
|
QMessageBox::StandardButton dialogResult = QMessageBox::No;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bool ok = false;
|
||||||
|
id = QInputDialog::getText(0, tr("KeePassXC: New key association request"),
|
||||||
|
tr("You have received an association "
|
||||||
|
"request for the above key.\n"
|
||||||
|
"If you would like to allow it access "
|
||||||
|
"to your KeePassXC database,\n"
|
||||||
|
"give it a unique name to identify and accept it."),
|
||||||
|
QLineEdit::Normal, QString(), &ok);
|
||||||
|
if (!ok || id.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
contains = config->attributes()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id);
|
||||||
|
dialogResult = QMessageBox::warning(0, tr("KeePassXC: Overwrite existing key?"),
|
||||||
|
tr("A shared encryption key with the name \"%1\" already exists.\nDo you want to overwrite it?").arg(id),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
} while (contains && dialogResult == QMessageBox::No);
|
||||||
|
|
||||||
|
config->attributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserService::getKey(const QString& id)
|
||||||
|
{
|
||||||
|
Entry* config = getConfigEntry();
|
||||||
|
if (!config) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to use KeepassHttpProtocol. Just return a JSON array.
|
||||||
|
QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm)
|
||||||
|
{
|
||||||
|
QJsonArray result;
|
||||||
|
if (thread() != QThread::currentThread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "findMatchingEntries", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QJsonArray, result),
|
||||||
|
Q_ARG(const QString&, id),
|
||||||
|
Q_ARG(const QString&, url),
|
||||||
|
Q_ARG(const QString&, submitUrl),
|
||||||
|
Q_ARG(const QString&, realm));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool alwaysAllowAccess = BrowserSettings::alwaysAllowAccess();
|
||||||
|
const QString host = QUrl(url).host();
|
||||||
|
const QString submitHost = QUrl(submitUrl).host();
|
||||||
|
|
||||||
|
// Check entries for authorization
|
||||||
|
QList<Entry*> pwEntriesToConfirm;
|
||||||
|
QList<Entry*> pwEntries;
|
||||||
|
for (Entry* entry : searchEntries(url)) {
|
||||||
|
switch (checkAccess(entry, host, submitHost, realm)) {
|
||||||
|
case Denied:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case Unknown:
|
||||||
|
if (alwaysAllowAccess) {
|
||||||
|
pwEntries.append(entry);
|
||||||
|
} else {
|
||||||
|
pwEntriesToConfirm.append(entry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Allowed:
|
||||||
|
pwEntries.append(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm entries
|
||||||
|
if (confirmEntries(pwEntriesToConfirm, url, host, submitHost, realm)) {
|
||||||
|
pwEntries.append(pwEntriesToConfirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwEntries.isEmpty()) {
|
||||||
|
return QJsonArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort results
|
||||||
|
pwEntries = sortEntries(pwEntries, host, submitUrl);
|
||||||
|
|
||||||
|
// Fill the list
|
||||||
|
for (Entry* entry : pwEntries) {
|
||||||
|
result << prepareEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::addEntry(const QString&, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm)
|
||||||
|
{
|
||||||
|
Group* group = findCreateAddEntryGroup();
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* entry = new Entry();
|
||||||
|
entry->setUuid(Uuid::random());
|
||||||
|
entry->setTitle(QUrl(url).host());
|
||||||
|
entry->setUrl(url);
|
||||||
|
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
|
||||||
|
entry->setUsername(login);
|
||||||
|
entry->setPassword(password);
|
||||||
|
entry->setGroup(group);
|
||||||
|
|
||||||
|
const QString host = QUrl(url).host();
|
||||||
|
const QString submitHost = QUrl(submitUrl).host();
|
||||||
|
BrowserEntryConfig config;
|
||||||
|
config.allow(host);
|
||||||
|
|
||||||
|
if (!submitHost.isEmpty()) {
|
||||||
|
config.allow(submitHost);
|
||||||
|
}
|
||||||
|
if (!realm.isEmpty()) {
|
||||||
|
config.setRealm(realm);
|
||||||
|
}
|
||||||
|
config.save(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, const QString& url)
|
||||||
|
{
|
||||||
|
if (thread() != QThread::currentThread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "updateEntry", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(const QString&, id),
|
||||||
|
Q_ARG(const QString&, uuid),
|
||||||
|
Q_ARG(const QString&, login),
|
||||||
|
Q_ARG(const QString&, password),
|
||||||
|
Q_ARG(const QString&, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
Database* db = getDatabase();
|
||||||
|
if (!db) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* entry = db->resolveEntry(Uuid::fromHex(uuid));
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString username = entry->username();
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username.compare(login, Qt::CaseSensitive) != 0 || entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
||||||
|
QMessageBox::StandardButton dialogResult = QMessageBox::No;
|
||||||
|
if (!BrowserSettings::alwaysAllowUpdate()) {
|
||||||
|
dialogResult = QMessageBox::warning(0, tr("KeePassXC: Update Entry"),
|
||||||
|
tr("Do you want to update the information in %1 - %2?")
|
||||||
|
.arg(QUrl(url).host()).arg(username),
|
||||||
|
QMessageBox::Yes|QMessageBox::No);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BrowserSettings::alwaysAllowUpdate() || dialogResult == QMessageBox::Yes) {
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->setUsername(login);
|
||||||
|
entry->setPassword(password);
|
||||||
|
entry->endUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Entry*> BrowserService::searchEntries(Database* db, const QString& hostname)
|
||||||
|
{
|
||||||
|
QList<Entry*> entries;
|
||||||
|
Group* rootGroup = db->rootGroup();
|
||||||
|
if (!rootGroup) {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry* entry : EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive)) {
|
||||||
|
QString title = entry->title();
|
||||||
|
QString url = entry->url();
|
||||||
|
|
||||||
|
// Filter to match hostname in Title and Url fields
|
||||||
|
if ((!title.isEmpty() && hostname.contains(title))
|
||||||
|
|| (!url.isEmpty() && hostname.contains(url))
|
||||||
|
|| (matchUrlScheme(title) && hostname.endsWith(QUrl(title).host()))
|
||||||
|
|| (matchUrlScheme(url) && hostname.endsWith(QUrl(url).host())) ) {
|
||||||
|
entries.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Entry*> BrowserService::searchEntries(const QString& text)
|
||||||
|
{
|
||||||
|
// Get the list of databases to search
|
||||||
|
QList<Database*> databases;
|
||||||
|
if (BrowserSettings::searchInAllDatabases()) {
|
||||||
|
const int count = m_dbTabWidget->count();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (DatabaseWidget* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
|
||||||
|
if (Database* db = dbWidget->database()) {
|
||||||
|
databases << db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Database* db = getDatabase()) {
|
||||||
|
databases << db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search entries matching the hostname
|
||||||
|
QString hostname = QUrl(text).host();
|
||||||
|
QList<Entry*> entries;
|
||||||
|
do {
|
||||||
|
for (Database* db : databases) {
|
||||||
|
entries << searchEntries(db, hostname);
|
||||||
|
}
|
||||||
|
} while (entries.isEmpty() && removeFirstDomain(hostname));
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::removeSharedEncryptionKeys()
|
||||||
|
{
|
||||||
|
if (!isDatabaseOpened()) {
|
||||||
|
QMessageBox::critical(0, tr("KeePassXC: Database locked!"),
|
||||||
|
tr("The active database is locked!\n"
|
||||||
|
"Please unlock the selected database or choose another one which is unlocked."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* entry = getConfigEntry();
|
||||||
|
if (!entry) {
|
||||||
|
QMessageBox::information(0, tr("KeePassXC: Settings not available!"),
|
||||||
|
tr("The active database does not contain a settings entry."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList keysToRemove;
|
||||||
|
for (const QString& key : entry->attributes()->keys()) {
|
||||||
|
if (key.startsWith(ASSOCIATE_KEY_PREFIX)) {
|
||||||
|
keysToRemove << key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysToRemove.isEmpty()) {
|
||||||
|
QMessageBox::information(0, tr("KeePassXC: No keys found"),
|
||||||
|
tr("No shared encryption keys found in KeePassXC Settings."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->beginUpdate();
|
||||||
|
for (const QString& key : keysToRemove) {
|
||||||
|
entry->attributes()->remove(key);
|
||||||
|
}
|
||||||
|
entry->endUpdate();
|
||||||
|
|
||||||
|
const int count = keysToRemove.count();
|
||||||
|
QMessageBox::information(0, tr("KeePassXC: Removed keys from database"),
|
||||||
|
tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::removeStoredPermissions()
|
||||||
|
{
|
||||||
|
if (!isDatabaseOpened()) {
|
||||||
|
QMessageBox::critical(0, tr("KeePassXC: Database locked!"),
|
||||||
|
tr("The active database is locked!\n"
|
||||||
|
"Please unlock the selected database or choose another one which is unlocked."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database* db = m_dbTabWidget->currentDatabaseWidget()->database();
|
||||||
|
if (!db) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Entry*> entries = db->rootGroup()->entriesRecursive();
|
||||||
|
|
||||||
|
QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count());
|
||||||
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
|
|
||||||
|
uint counter = 0;
|
||||||
|
for (Entry* entry : entries) {
|
||||||
|
if (progress.wasCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->attributes()->contains(KEEPASSXCBROWSER_NAME)) {
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->attributes()->remove(KEEPASSXCBROWSER_NAME);
|
||||||
|
entry->endUpdate();
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
progress.setValue(progress.value() + 1);
|
||||||
|
}
|
||||||
|
progress.reset();
|
||||||
|
|
||||||
|
if (counter > 0) {
|
||||||
|
QMessageBox::information(0, tr("KeePassXC: Removed permissions"),
|
||||||
|
tr("Successfully removed permissions from %n entry(s).", "", counter),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
} else {
|
||||||
|
QMessageBox::information(0, tr("KeePassXC: No entry with permissions found!"),
|
||||||
|
tr("The active database does not contain an entry with permissions."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& entryUrl)
|
||||||
|
{
|
||||||
|
QUrl url(entryUrl);
|
||||||
|
if (url.scheme().isEmpty()) {
|
||||||
|
url.setScheme("http");
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString submitUrl = url.toString(QUrl::StripTrailingSlash);
|
||||||
|
const QString baseSubmitUrl = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||||
|
|
||||||
|
QMultiMap<int, const Entry*> priorities;
|
||||||
|
for (const Entry* entry : pwEntries) {
|
||||||
|
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl), entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString field = BrowserSettings::sortByTitle() ? "Title" : "UserName";
|
||||||
|
std::sort(pwEntries.begin(), pwEntries.end(), [&priorities, &field](const Entry* left, const Entry* right) {
|
||||||
|
int res = priorities.key(left) - priorities.key(right);
|
||||||
|
if (res == 0) {
|
||||||
|
return QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) < 0;
|
||||||
|
}
|
||||||
|
return res < 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return pwEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm, const QString& url, const QString& host, const QString& submitHost, const QString& realm)
|
||||||
|
{
|
||||||
|
if (pwEntriesToConfirm.isEmpty() || m_dialogActive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dialogActive = true;
|
||||||
|
BrowserAccessControlDialog accessControlDialog;
|
||||||
|
accessControlDialog.setUrl(url);
|
||||||
|
accessControlDialog.setItems(pwEntriesToConfirm);
|
||||||
|
|
||||||
|
int res = accessControlDialog.exec();
|
||||||
|
if (accessControlDialog.remember()) {
|
||||||
|
for (Entry* entry : pwEntriesToConfirm) {
|
||||||
|
BrowserEntryConfig config;
|
||||||
|
config.load(entry);
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
config.allow(host);
|
||||||
|
if (!submitHost.isEmpty() && host != submitHost)
|
||||||
|
config.allow(submitHost);
|
||||||
|
} else if (res == QDialog::Rejected) {
|
||||||
|
config.deny(host);
|
||||||
|
if (!submitHost.isEmpty() && host != submitHost) {
|
||||||
|
config.deny(submitHost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!realm.isEmpty()) {
|
||||||
|
config.setRealm(realm);
|
||||||
|
}
|
||||||
|
config.save(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dialogActive = false;
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BrowserService::prepareEntry(const Entry* entry)
|
||||||
|
{
|
||||||
|
QJsonObject res;
|
||||||
|
res["login"] = entry->resolveMultiplePlaceholders(entry->username());
|
||||||
|
res["password"] = entry->resolveMultiplePlaceholders(entry->password());
|
||||||
|
res["name"] = entry->resolveMultiplePlaceholders(entry->title());
|
||||||
|
res["uuid"] = entry->resolveMultiplePlaceholders(entry->uuid().toHex());
|
||||||
|
|
||||||
|
if (BrowserSettings::supportKphFields()) {
|
||||||
|
const EntryAttributes* attr = entry->attributes();
|
||||||
|
QJsonArray stringFields;
|
||||||
|
for (const QString& key : attr->keys()) {
|
||||||
|
if (key.startsWith(QLatin1String("KPH: "))) {
|
||||||
|
QJsonObject sField;
|
||||||
|
sField[key] = entry->resolveMultiplePlaceholders(attr->value(key));
|
||||||
|
stringFields << sField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res["stringFields"] = stringFields;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserService::Access BrowserService::checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm)
|
||||||
|
{
|
||||||
|
BrowserEntryConfig config;
|
||||||
|
if (!config.load(entry)) {
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) {
|
||||||
|
return Allowed;
|
||||||
|
}
|
||||||
|
if ((config.isDenied(host)) || (!submitHost.isEmpty() && config.isDenied(submitHost))) {
|
||||||
|
return Denied;
|
||||||
|
}
|
||||||
|
if (!realm.isEmpty() && config.realm() != realm) {
|
||||||
|
return Denied;
|
||||||
|
}
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* BrowserService::findCreateAddEntryGroup()
|
||||||
|
{
|
||||||
|
Database* db = getDatabase();
|
||||||
|
if (!db) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* rootGroup = db->rootGroup();
|
||||||
|
if (!rootGroup) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString groupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); //TODO: setting to decide where new keys are created
|
||||||
|
|
||||||
|
for (const Group* g : rootGroup->groupsRecursive(true)) {
|
||||||
|
if (g->name() == groupName) {
|
||||||
|
return db->resolveGroup(g->uuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* group = new Group();
|
||||||
|
group->setUuid(Uuid::random());
|
||||||
|
group->setName(groupName);
|
||||||
|
group->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
|
||||||
|
group->setParent(rootGroup);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowserService::sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const
|
||||||
|
{
|
||||||
|
QUrl url(entry->url());
|
||||||
|
if (url.scheme().isEmpty()) {
|
||||||
|
url.setScheme("http");
|
||||||
|
}
|
||||||
|
const QString entryURL = url.toString(QUrl::StripTrailingSlash);
|
||||||
|
const QString baseEntryURL = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||||
|
|
||||||
|
if (submitUrl == entryURL) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
if (submitUrl.startsWith(entryURL) && entryURL != host && baseSubmitUrl != entryURL) {
|
||||||
|
return 90;
|
||||||
|
}
|
||||||
|
if (submitUrl.startsWith(baseEntryURL) && entryURL != host && baseSubmitUrl != baseEntryURL) {
|
||||||
|
return 80;
|
||||||
|
}
|
||||||
|
if (entryURL == host) {
|
||||||
|
return 70;
|
||||||
|
}
|
||||||
|
if (entryURL == baseSubmitUrl) {
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
if (entryURL.startsWith(submitUrl)) {
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
if (entryURL.startsWith(baseSubmitUrl) && baseSubmitUrl != host) {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
if (submitUrl.startsWith(entryURL)) {
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
if (submitUrl.startsWith(baseEntryURL)) {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
if (entryURL.startsWith(host)) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (host.startsWith(entryURL)) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserService::matchUrlScheme(const QString& url)
|
||||||
|
{
|
||||||
|
QUrl address(url);
|
||||||
|
return !address.scheme().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserService::removeFirstDomain(QString& hostname)
|
||||||
|
{
|
||||||
|
int pos = hostname.indexOf(".");
|
||||||
|
if (pos < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't remove the second-level domain if it's the only one
|
||||||
|
if (hostname.count(".") > 1) {
|
||||||
|
hostname = hostname.mid(pos + 1);
|
||||||
|
return !hostname.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing removed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database* BrowserService::getDatabase()
|
||||||
|
{
|
||||||
|
if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) {
|
||||||
|
if (Database* db = dbWidget->database()) {
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
||||||
|
{
|
||||||
|
if (dbWidget) {
|
||||||
|
emit databaseLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||||
|
{
|
||||||
|
if (dbWidget) {
|
||||||
|
emit databaseUnlocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserService::activateDatabaseChanged(DatabaseWidget* dbWidget)
|
||||||
|
{
|
||||||
|
if (dbWidget) {
|
||||||
|
auto currentMode = dbWidget->currentMode();
|
||||||
|
if (currentMode == DatabaseWidget::ViewMode || currentMode == DatabaseWidget::EditMode) {
|
||||||
|
emit databaseUnlocked();
|
||||||
|
} else {
|
||||||
|
emit databaseLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
src/browser/BrowserService.h
Normal file
82
src/browser/BrowserService.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERSERVICE_H
|
||||||
|
#define BROWSERSERVICE_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QObject>
|
||||||
|
#include "gui/DatabaseTabWidget.h"
|
||||||
|
#include "core/Entry.h"
|
||||||
|
|
||||||
|
enum { max_length = 16*1024 };
|
||||||
|
|
||||||
|
class BrowserService : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BrowserService(DatabaseTabWidget* parent);
|
||||||
|
|
||||||
|
bool isDatabaseOpened() const;
|
||||||
|
bool openDatabase();
|
||||||
|
QString getDatabaseRootUuid();
|
||||||
|
QString getDatabaseRecycleBinUuid();
|
||||||
|
Entry* getConfigEntry(bool create = false);
|
||||||
|
QString getKey(const QString& id);
|
||||||
|
void addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm);
|
||||||
|
QList<Entry*> searchEntries(Database* db, const QString& hostname);
|
||||||
|
QList<Entry*> searchEntries(const QString& text);
|
||||||
|
void removeSharedEncryptionKeys();
|
||||||
|
void removeStoredPermissions();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
QJsonArray findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm);
|
||||||
|
QString storeKey(const QString& key);
|
||||||
|
void updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, const QString& url);
|
||||||
|
void databaseLocked(DatabaseWidget* dbWidget);
|
||||||
|
void databaseUnlocked(DatabaseWidget* dbWidget);
|
||||||
|
void activateDatabaseChanged(DatabaseWidget* dbWidget);
|
||||||
|
void lockDatabase();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void databaseLocked();
|
||||||
|
void databaseUnlocked();
|
||||||
|
void databaseChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Access { Denied, Unknown, Allowed};
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& submitUrl);
|
||||||
|
bool confirmEntries(QList<Entry*>& pwEntriesToConfirm, const QString& url, const QString& host, const QString& submitHost, const QString& realm);
|
||||||
|
QJsonObject prepareEntry(const Entry* entry);
|
||||||
|
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
||||||
|
Group* findCreateAddEntryGroup();
|
||||||
|
int sortPriority(const Entry* entry, const QString &host, const QString& submitUrl, const QString& baseSubmitUrl) const;
|
||||||
|
bool matchUrlScheme(const QString& url);
|
||||||
|
bool removeFirstDomain(QString& hostname);
|
||||||
|
Database* getDatabase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DatabaseTabWidget* const m_dbTabWidget;
|
||||||
|
bool m_dialogActive;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERSERVICE_H
|
377
src/browser/BrowserSettings.cpp
Executable file
377
src/browser/BrowserSettings.cpp
Executable file
@ -0,0 +1,377 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BrowserSettings.h"
|
||||||
|
#include "core/Config.h"
|
||||||
|
|
||||||
|
PasswordGenerator BrowserSettings::m_passwordGenerator;
|
||||||
|
PassphraseGenerator BrowserSettings::m_passPhraseGenerator;
|
||||||
|
HostInstaller BrowserSettings::m_hostInstaller;
|
||||||
|
|
||||||
|
bool BrowserSettings::isEnabled()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/Enabled", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
config()->set("Browser/Enabled", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::showNotification()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/ShowNotification", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setShowNotification(bool showNotification)
|
||||||
|
{
|
||||||
|
config()->set("Browser/ShowNotification", showNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::bestMatchOnly()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/BestMatchOnly", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setBestMatchOnly(bool bestMatchOnly)
|
||||||
|
{
|
||||||
|
config()->set("Browser/BestMatchOnly", bestMatchOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::unlockDatabase()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/UnlockDatabase", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setUnlockDatabase(bool unlockDatabase)
|
||||||
|
{
|
||||||
|
config()->set("Browser/UnlockDatabase", unlockDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::matchUrlScheme()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/MatchUrlScheme", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setMatchUrlScheme(bool matchUrlScheme)
|
||||||
|
{
|
||||||
|
config()->set("Browser/MatchUrlScheme", matchUrlScheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::sortByUsername()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/SortByUsername", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setSortByUsername(bool sortByUsername)
|
||||||
|
{
|
||||||
|
config()->set("Browser/SortByUsername", sortByUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::sortByTitle()
|
||||||
|
{
|
||||||
|
return !sortByUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setSortByTitle(bool sortByUsertitle)
|
||||||
|
{
|
||||||
|
setSortByUsername(!sortByUsertitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::alwaysAllowAccess()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/AlwaysAllowAccess", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setAlwaysAllowAccess(bool alwaysAllowAccess)
|
||||||
|
{
|
||||||
|
config()->set("Browser/AlwaysAllowAccess", alwaysAllowAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::alwaysAllowUpdate()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/AlwaysAllowUpdate", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setAlwaysAllowUpdate(bool alwaysAllowUpdate)
|
||||||
|
{
|
||||||
|
config()->set("Browser/AlwaysAllowUpdate", alwaysAllowUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::searchInAllDatabases()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/SearchInAllDatabases", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setSearchInAllDatabases(bool searchInAllDatabases)
|
||||||
|
{
|
||||||
|
config()->set("Browser/SearchInAllDatabases", searchInAllDatabases);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::supportKphFields()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/SupportKphFields", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setSupportKphFields(bool supportKphFields)
|
||||||
|
{
|
||||||
|
config()->set("Browser/SupportKphFields", supportKphFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::supportBrowserProxy()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/SupportBrowserProxy", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setSupportBrowserProxy(bool enabled)
|
||||||
|
{
|
||||||
|
config()->set("Browser/SupportBrowserProxy", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::useCustomProxy()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/UseCustomProxy", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setUseCustomProxy(bool enabled)
|
||||||
|
{
|
||||||
|
config()->set("Browser/UseCustomProxy", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserSettings::customProxyLocation()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/CustomProxyLocation", " ").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setCustomProxyLocation(QString location)
|
||||||
|
{
|
||||||
|
config()->set("Browser/CustomProxyLocation", location);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::updateBinaryPath()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/UpdateBinaryPath", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setUpdateBinaryPath(bool enabled)
|
||||||
|
{
|
||||||
|
config()->set("Browser/UpdateBinaryPath", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::chromeSupport() {
|
||||||
|
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setChromeSupport(bool enabled) {
|
||||||
|
m_hostInstaller.installBrowser(HostInstaller::SupportedBrowsers::CHROME, enabled, supportBrowserProxy(), customProxyLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::chromiumSupport() {
|
||||||
|
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROMIUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setChromiumSupport(bool enabled) {
|
||||||
|
m_hostInstaller.installBrowser(HostInstaller::SupportedBrowsers::CHROMIUM, enabled, supportBrowserProxy(), customProxyLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::firefoxSupport() {
|
||||||
|
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::FIREFOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setFirefoxSupport(bool enabled) {
|
||||||
|
m_hostInstaller.installBrowser(HostInstaller::SupportedBrowsers::FIREFOX, enabled, supportBrowserProxy(), customProxyLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::vivaldiSupport() {
|
||||||
|
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::VIVALDI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setVivaldiSupport(bool enabled) {
|
||||||
|
m_hostInstaller.installBrowser(HostInstaller::SupportedBrowsers::VIVALDI, enabled, supportBrowserProxy(), customProxyLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordUseNumbers()
|
||||||
|
{
|
||||||
|
return config()->get("generator/Numbers", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordUseNumbers(bool useNumbers)
|
||||||
|
{
|
||||||
|
config()->set("generator/Numbers", useNumbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordUseLowercase()
|
||||||
|
{
|
||||||
|
return config()->get("generator/LowerCase", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordUseLowercase(bool useLowercase)
|
||||||
|
{
|
||||||
|
config()->set("generator/LowerCase", useLowercase);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordUseUppercase()
|
||||||
|
{
|
||||||
|
return config()->get("generator/UpperCase", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordUseUppercase(bool useUppercase)
|
||||||
|
{
|
||||||
|
config()->set("generator/UpperCase", useUppercase);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordUseSpecial()
|
||||||
|
{
|
||||||
|
return config()->get("generator/SpecialChars", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordUseSpecial(bool useSpecial)
|
||||||
|
{
|
||||||
|
config()->set("generator/SpecialChars", useSpecial);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordUseEASCII()
|
||||||
|
{
|
||||||
|
return config()->get("generator/EASCII", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordUseEASCII(bool useEASCII)
|
||||||
|
{
|
||||||
|
config()->set("generator/EASCII", useEASCII);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowserSettings::passPhraseWordCount()
|
||||||
|
{
|
||||||
|
return config()->get("generator/WordCount", 6).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPassPhraseWordCount(int wordCount)
|
||||||
|
{
|
||||||
|
config()->set("generator/WordCount", wordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserSettings::passPhraseWordSeparator()
|
||||||
|
{
|
||||||
|
return config()->get("generator/WordSeparator", " ").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPassPhraseWordSeparator(QString separator)
|
||||||
|
{
|
||||||
|
config()->set("generator/WordSeparator", separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowserSettings::generatorType()
|
||||||
|
{
|
||||||
|
return config()->get("generator/Type", 0).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setGeneratorType(int type)
|
||||||
|
{
|
||||||
|
config()->set("generator/Type", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordEveryGroup()
|
||||||
|
{
|
||||||
|
return config()->get("generator/EnsureEvery", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordEveryGroup(bool everyGroup)
|
||||||
|
{
|
||||||
|
config()->get("generator/EnsureEvery", everyGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::passwordExcludeAlike()
|
||||||
|
{
|
||||||
|
return config()->get("generator/ExcludeAlike", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordExcludeAlike(bool excludeAlike)
|
||||||
|
{
|
||||||
|
config()->set("generator/ExcludeAlike", excludeAlike);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowserSettings::passwordLength()
|
||||||
|
{
|
||||||
|
return config()->get("generator/Length", 20).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setPasswordLength(int length)
|
||||||
|
{
|
||||||
|
config()->set("generator/Length", length);
|
||||||
|
m_passwordGenerator.setLength(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordGenerator::CharClasses BrowserSettings::passwordCharClasses()
|
||||||
|
{
|
||||||
|
PasswordGenerator::CharClasses classes;
|
||||||
|
if (passwordUseLowercase()) {
|
||||||
|
classes |= PasswordGenerator::LowerLetters;
|
||||||
|
}
|
||||||
|
if (passwordUseUppercase()) {
|
||||||
|
classes |= PasswordGenerator::UpperLetters;
|
||||||
|
}
|
||||||
|
if (passwordUseNumbers()) {
|
||||||
|
classes |= PasswordGenerator::Numbers;
|
||||||
|
}
|
||||||
|
if (passwordUseSpecial()) {
|
||||||
|
classes |= PasswordGenerator::SpecialCharacters;
|
||||||
|
}
|
||||||
|
if (passwordUseEASCII()) {
|
||||||
|
classes |= PasswordGenerator::EASCII;
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordGenerator::GeneratorFlags BrowserSettings::passwordGeneratorFlags()
|
||||||
|
{
|
||||||
|
PasswordGenerator::GeneratorFlags flags;
|
||||||
|
if (passwordExcludeAlike()) {
|
||||||
|
flags |= PasswordGenerator::ExcludeLookAlike;
|
||||||
|
}
|
||||||
|
if (passwordEveryGroup()) {
|
||||||
|
flags |= PasswordGenerator::CharFromEveryGroup;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BrowserSettings::generatePassword()
|
||||||
|
{
|
||||||
|
if (generatorType() == 0) {
|
||||||
|
m_passwordGenerator.setLength(passwordLength());
|
||||||
|
m_passwordGenerator.setCharClasses(passwordCharClasses());
|
||||||
|
m_passwordGenerator.setFlags(passwordGeneratorFlags());
|
||||||
|
return m_passwordGenerator.generatePassword();
|
||||||
|
} else {
|
||||||
|
m_passPhraseGenerator.setDefaultWordList();
|
||||||
|
m_passPhraseGenerator.setWordCount(passPhraseWordCount());
|
||||||
|
m_passPhraseGenerator.setWordSeparator(passPhraseWordSeparator());
|
||||||
|
return m_passPhraseGenerator.generatePassphrase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowserSettings::getbits()
|
||||||
|
{
|
||||||
|
return m_passwordGenerator.getbits();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::updateBinaryPaths(QString customProxyLocation)
|
||||||
|
{
|
||||||
|
bool isProxy = supportBrowserProxy();
|
||||||
|
m_hostInstaller.updateBinaryPaths(isProxy, customProxyLocation);
|
||||||
|
}
|
105
src/browser/BrowserSettings.h
Executable file
105
src/browser/BrowserSettings.h
Executable file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROWSERSETTINGS_H
|
||||||
|
#define BROWSERSETTINGS_H
|
||||||
|
|
||||||
|
#include "core/PasswordGenerator.h"
|
||||||
|
#include "core/PassphraseGenerator.h"
|
||||||
|
#include "HostInstaller.h"
|
||||||
|
|
||||||
|
class BrowserSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool isEnabled();
|
||||||
|
static void setEnabled(bool enabled);
|
||||||
|
|
||||||
|
static bool showNotification(); //TODO!!
|
||||||
|
static void setShowNotification(bool showNotification);
|
||||||
|
static bool bestMatchOnly(); //TODO!!
|
||||||
|
static void setBestMatchOnly(bool bestMatchOnly);
|
||||||
|
static bool unlockDatabase(); //TODO!!
|
||||||
|
static void setUnlockDatabase(bool unlockDatabase);
|
||||||
|
static bool matchUrlScheme();
|
||||||
|
static void setMatchUrlScheme(bool matchUrlScheme);
|
||||||
|
static bool sortByUsername();
|
||||||
|
static void setSortByUsername(bool sortByUsername = true);
|
||||||
|
static bool sortByTitle();
|
||||||
|
static void setSortByTitle(bool sortByUsertitle = true);
|
||||||
|
static bool alwaysAllowAccess();
|
||||||
|
static void setAlwaysAllowAccess(bool alwaysAllowAccess);
|
||||||
|
static bool alwaysAllowUpdate();
|
||||||
|
static void setAlwaysAllowUpdate(bool alwaysAllowUpdate);
|
||||||
|
static bool searchInAllDatabases();//TODO!!
|
||||||
|
static void setSearchInAllDatabases(bool searchInAllDatabases);
|
||||||
|
static bool supportKphFields();
|
||||||
|
static void setSupportKphFields(bool supportKphFields);
|
||||||
|
|
||||||
|
static bool supportBrowserProxy();
|
||||||
|
static void setSupportBrowserProxy(bool enabled);
|
||||||
|
static bool useCustomProxy();
|
||||||
|
static void setUseCustomProxy(bool enabled);
|
||||||
|
static QString customProxyLocation();
|
||||||
|
static void setCustomProxyLocation(QString location);
|
||||||
|
static bool updateBinaryPath();
|
||||||
|
static void setUpdateBinaryPath(bool enabled);
|
||||||
|
static bool chromeSupport();
|
||||||
|
static void setChromeSupport(bool enabled);
|
||||||
|
static bool chromiumSupport();
|
||||||
|
static void setChromiumSupport(bool enabled);
|
||||||
|
static bool firefoxSupport();
|
||||||
|
static void setFirefoxSupport(bool enabled);
|
||||||
|
static bool vivaldiSupport();
|
||||||
|
static void setVivaldiSupport(bool enabled);
|
||||||
|
|
||||||
|
static bool passwordUseNumbers();
|
||||||
|
static void setPasswordUseNumbers(bool useNumbers);
|
||||||
|
static bool passwordUseLowercase();
|
||||||
|
static void setPasswordUseLowercase(bool useLowercase);
|
||||||
|
static bool passwordUseUppercase();
|
||||||
|
static void setPasswordUseUppercase(bool useUppercase);
|
||||||
|
static bool passwordUseSpecial();
|
||||||
|
static void setPasswordUseSpecial(bool useSpecial);
|
||||||
|
static bool passwordUseEASCII();
|
||||||
|
static void setPasswordUseEASCII(bool useEASCII);
|
||||||
|
static int passPhraseWordCount();
|
||||||
|
static void setPassPhraseWordCount(int wordCount);
|
||||||
|
static QString passPhraseWordSeparator();
|
||||||
|
static void setPassPhraseWordSeparator(QString separator);
|
||||||
|
static int generatorType();
|
||||||
|
static void setGeneratorType(int type);
|
||||||
|
static bool passwordEveryGroup();
|
||||||
|
static void setPasswordEveryGroup(bool everyGroup);
|
||||||
|
static bool passwordExcludeAlike();
|
||||||
|
static void setPasswordExcludeAlike(bool excludeAlike);
|
||||||
|
static int passwordLength();
|
||||||
|
static void setPasswordLength(int length);
|
||||||
|
static PasswordGenerator::CharClasses passwordCharClasses();
|
||||||
|
static PasswordGenerator::GeneratorFlags passwordGeneratorFlags();
|
||||||
|
static QString generatePassword();
|
||||||
|
static int getbits();
|
||||||
|
static void updateBinaryPaths(QString customProxyLocation = QString());
|
||||||
|
|
||||||
|
private:
|
||||||
|
static PasswordGenerator m_passwordGenerator;
|
||||||
|
static PassphraseGenerator m_passPhraseGenerator;
|
||||||
|
static HostInstaller m_hostInstaller;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BROWSERSETTINGS_H
|
37
src/browser/CMakeLists.txt
Executable file
37
src/browser/CMakeLists.txt
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
BrowserAction.cpp
|
||||||
|
BrowserClients.cpp
|
||||||
|
BrowserEntryConfig.cpp
|
||||||
|
BrowserOptionDialog.cpp
|
||||||
|
BrowserService.cpp
|
||||||
|
BrowserSettings.cpp
|
||||||
|
HostInstaller.cpp
|
||||||
|
NativeMessagingBase.cpp
|
||||||
|
NativeMessagingHost.cpp
|
||||||
|
Variant.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
|
||||||
|
target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network sodium)
|
||||||
|
endif()
|
230
src/browser/HostInstaller.cpp
Normal file
230
src/browser/HostInstaller.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "HostInstaller.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
const QString HostInstaller::HOST_NAME = "org.keepassxc.keepassxc_browser";
|
||||||
|
const QStringList HostInstaller::ALLOWED_ORIGINS = QStringList()
|
||||||
|
<< "chrome-extension://iopaggbpplllidnfmcghoonnokmjoicf/"
|
||||||
|
<< "chrome-extension://fhakpkpdnjecjfceboihdjpfmgajebii/"
|
||||||
|
<< "chrome-extension://jaikbblhommnkeialomogohhdlndpfbi/";
|
||||||
|
|
||||||
|
const QStringList HostInstaller::ALLOWED_EXTENSIONS = QStringList()
|
||||||
|
<< "keepassxc-browser@keepassxc.org";
|
||||||
|
|
||||||
|
#if defined(Q_OS_OSX)
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROME = "/Library/Application Support/Google/Chrome/NativeMessagingHosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROMIUM = "/Library/Application Support/Chromium/NativeMessagingHosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_FIREFOX = "/Library/Application Support/Mozilla/NativeMessagingHosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_VIVALDI = "/Library/Application Support/Vivaldi/NativeMessagingHosts";
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROME = "/.config/google-chrome/NativeMessagingHosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROMIUM = "/.config/chromium/NativeMessagingHosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_FIREFOX = "/.mozilla/native-messaging-hosts";
|
||||||
|
const QString HostInstaller::TARGET_DIR_VIVALDI = "/.config/vivaldi/NativeMessagingHosts";
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROME = "HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\" + HostInstaller::HOST_NAME;
|
||||||
|
const QString HostInstaller::TARGET_DIR_CHROMIUM = "HKEY_CURRENT_USER\\Software\\Chromium\\NativeMessagingHosts\\" + HostInstaller::HOST_NAME;
|
||||||
|
const QString HostInstaller::TARGET_DIR_FIREFOX = "HKEY_CURRENT_USER\\Software\\Mozilla\\NativeMessagingHosts\\" + HostInstaller::HOST_NAME;
|
||||||
|
const QString HostInstaller::TARGET_DIR_VIVALDI = "HKEY_CURRENT_USER\\Software\\Vivaldi\\NativeMessagingHosts\\" + HostInstaller::HOST_NAME;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HostInstaller::HostInstaller()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostInstaller::checkIfInstalled(SupportedBrowsers browser)
|
||||||
|
{
|
||||||
|
QString fileName = getPath(browser);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
|
||||||
|
return registryEntryFound(settings);
|
||||||
|
#else
|
||||||
|
return QFile::exists(fileName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostInstaller::installBrowser(SupportedBrowsers browser, const bool& enabled, const bool& proxy, const QString& location)
|
||||||
|
{
|
||||||
|
if (enabled) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// Create a registry key
|
||||||
|
QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
|
||||||
|
if (!registryEntryFound(settings)) {
|
||||||
|
settings.setValue("Default", getPath(browser));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Always create the script file
|
||||||
|
QJsonObject script = constructFile(browser, proxy, location);
|
||||||
|
if (!saveFile(browser, script)) {
|
||||||
|
QMessageBox::critical(0, tr("KeePassXC: Cannot save file!"),
|
||||||
|
tr("Cannot save the native messaging script file."), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove the script file
|
||||||
|
QString fileName = getPath(browser);
|
||||||
|
QFile::remove(fileName);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// Remove the registry entry
|
||||||
|
QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
|
||||||
|
if (registryEntryFound(settings)) {
|
||||||
|
settings.remove("Default");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostInstaller::updateBinaryPaths(const bool& proxy, const QString& location)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (checkIfInstalled(static_cast<SupportedBrowsers>(i))) {
|
||||||
|
installBrowser(static_cast<SupportedBrowsers>(i), true, proxy, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HostInstaller::getTargetPath(SupportedBrowsers browser) const
|
||||||
|
{
|
||||||
|
switch (browser) {
|
||||||
|
case SupportedBrowsers::CHROME: return HostInstaller::TARGET_DIR_CHROME;
|
||||||
|
case SupportedBrowsers::CHROMIUM: return HostInstaller::TARGET_DIR_CHROMIUM;
|
||||||
|
case SupportedBrowsers::FIREFOX: return HostInstaller::TARGET_DIR_FIREFOX;
|
||||||
|
case SupportedBrowsers::VIVALDI: return HostInstaller::TARGET_DIR_VIVALDI;
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HostInstaller::getBrowserName(SupportedBrowsers browser) const
|
||||||
|
{
|
||||||
|
switch (browser) {
|
||||||
|
case SupportedBrowsers::CHROME: return "chrome";
|
||||||
|
case SupportedBrowsers::CHROMIUM: return "chromium";
|
||||||
|
case SupportedBrowsers::FIREFOX: return "firefox";
|
||||||
|
case SupportedBrowsers::VIVALDI: return "vivaldi";
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HostInstaller::getPath(SupportedBrowsers browser) const
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// If portable settings file exists save the JSON scripts to application folder
|
||||||
|
QString userPath;
|
||||||
|
QString portablePath = QCoreApplication::applicationDirPath() + "/keepassxc.ini";
|
||||||
|
if (QFile::exists(portablePath)) {
|
||||||
|
userPath = QCoreApplication::applicationDirPath();
|
||||||
|
} else {
|
||||||
|
userPath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString winPath = QString("%1/%2_%3.json").arg(userPath, HostInstaller::HOST_NAME, getBrowserName(browser));
|
||||||
|
winPath.replace("/","\\");
|
||||||
|
return winPath;
|
||||||
|
#else
|
||||||
|
QString path = getTargetPath(browser);
|
||||||
|
return QString("%1%2/%3.json").arg(QDir::homePath(), path, HostInstaller::HOST_NAME);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HostInstaller::getInstallDir(SupportedBrowsers browser) const
|
||||||
|
{
|
||||||
|
QString path = getTargetPath(browser);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return QCoreApplication::applicationDirPath();
|
||||||
|
#else
|
||||||
|
return QString("%1%2").arg(QDir::homePath(), path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location)
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
if (proxy) {
|
||||||
|
if (!location.isEmpty()) {
|
||||||
|
path = location;
|
||||||
|
} else {
|
||||||
|
path = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath();
|
||||||
|
path.append("/keepassxc-proxy");
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
path.append(".exe");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path = QFileInfo(QCoreApplication::applicationFilePath()).absoluteFilePath();
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
path.replace("/","\\");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QJsonObject script;
|
||||||
|
script["name"] = HostInstaller::HOST_NAME;
|
||||||
|
script["description"] = "KeePassXC integration with native messaging support";
|
||||||
|
script["path"] = path;
|
||||||
|
script["type"] = "stdio";
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
if (browser == SupportedBrowsers::FIREFOX) {
|
||||||
|
for (const QString extension : HostInstaller::ALLOWED_EXTENSIONS) {
|
||||||
|
arr.append(extension);
|
||||||
|
}
|
||||||
|
script["allowed_extensions"] = arr;
|
||||||
|
} else {
|
||||||
|
for (const QString origin : HostInstaller::ALLOWED_ORIGINS) {
|
||||||
|
arr.append(origin);
|
||||||
|
}
|
||||||
|
script["allowed_origins"] = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostInstaller::registryEntryFound(const QSettings& settings)
|
||||||
|
{
|
||||||
|
return !settings.value("Default").isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostInstaller::saveFile(SupportedBrowsers browser, const QJsonObject& script)
|
||||||
|
{
|
||||||
|
QString path = getPath(browser);
|
||||||
|
QString installDir = getInstallDir(browser);
|
||||||
|
QDir dir(installDir);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
QDir().mkpath(installDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile scriptFile(path);
|
||||||
|
if (!scriptFile.open(QIODevice::WriteOnly)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc(script);
|
||||||
|
qint64 bytesWritten = scriptFile.write(doc.toJson());
|
||||||
|
if (bytesWritten < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
63
src/browser/HostInstaller.h
Normal file
63
src/browser/HostInstaller.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HOSTINSTALLER_H
|
||||||
|
#define HOSTINSTALLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
class HostInstaller : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum SupportedBrowsers : int {
|
||||||
|
CHROME = 0,
|
||||||
|
CHROMIUM = 1,
|
||||||
|
FIREFOX = 2,
|
||||||
|
VIVALDI = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
HostInstaller();
|
||||||
|
bool checkIfInstalled(SupportedBrowsers browser);
|
||||||
|
void installBrowser(SupportedBrowsers browser, const bool& enabled, const bool& proxy = false, const QString& location = "");
|
||||||
|
void updateBinaryPaths(const bool& proxy, const QString& location = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getTargetPath(SupportedBrowsers browser) const;
|
||||||
|
QString getBrowserName(SupportedBrowsers browser) const;
|
||||||
|
QString getPath(SupportedBrowsers browser) const;
|
||||||
|
QString getInstallDir(SupportedBrowsers browser) const;
|
||||||
|
QJsonObject constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location);
|
||||||
|
bool registryEntryFound(const QSettings& settings);
|
||||||
|
bool saveFile(SupportedBrowsers browser, const QJsonObject& script);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QString HOST_NAME;
|
||||||
|
static const QStringList ALLOWED_EXTENSIONS;
|
||||||
|
static const QStringList ALLOWED_ORIGINS;
|
||||||
|
static const QString TARGET_DIR_CHROME;
|
||||||
|
static const QString TARGET_DIR_CHROMIUM;
|
||||||
|
static const QString TARGET_DIR_FIREFOX;
|
||||||
|
static const QString TARGET_DIR_VIVALDI;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HOSTINSTALLER_H
|
141
src/browser/NativeMessagingBase.cpp
Normal file
141
src/browser/NativeMessagingBase.cpp
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "NativeMessagingBase.h"
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/event.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NativeMessagingBase::NativeMessagingBase()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
_setmode(_fileno(stdin), _O_BINARY);
|
||||||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||||||
|
#else
|
||||||
|
m_notifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this));
|
||||||
|
connect(m_notifier.data(), SIGNAL(activated(int)), this, SLOT(newNativeMessage()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingBase::newNativeMessage()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
|
||||||
|
struct kevent ev[1];
|
||||||
|
struct timespec ts = { 5, 0 };
|
||||||
|
|
||||||
|
int fd = kqueue();
|
||||||
|
if (fd == -1) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EV_SET(ev, fileno(stdin), EVFILT_READ, EV_ADD, 0, 0, nullptr);
|
||||||
|
if (kevent(fd, ev, 1, nullptr, 0, &ts) == -1) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = kevent(fd, NULL, 0, ev, 1, &ts);
|
||||||
|
if (ret < 1) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
::close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
int fd = epoll_create(5);
|
||||||
|
struct epoll_event event;
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
event.data.fd = 0;
|
||||||
|
if (epoll_ctl(fd, EPOLL_CTL_ADD, 0, &event) != 0) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epoll_wait(fd, &event, 1, 5000) < 1) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
::close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
readLength();
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
::close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingBase::readNativeMessages()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
quint32 length = 0;
|
||||||
|
while (m_running.load() && !std::cin.eof()) {
|
||||||
|
length = 0;
|
||||||
|
std::cin.read(reinterpret_cast<char*>(&length), 4);
|
||||||
|
readStdIn(length);
|
||||||
|
QThread::msleep(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NativeMessagingBase::jsonToString(const QJsonObject& json) const
|
||||||
|
{
|
||||||
|
return QString(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingBase::sendReply(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
if (!json.isEmpty()) {
|
||||||
|
sendReply(jsonToString(json));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingBase::sendReply(const QString& reply)
|
||||||
|
{
|
||||||
|
if (!reply.isEmpty()) {
|
||||||
|
uint len = reply.length();
|
||||||
|
std::cout << char(((len>>0) & 0xFF)) << char(((len>>8) & 0xFF)) << char(((len>>16) & 0xFF)) << char(((len>>24) & 0xFF));
|
||||||
|
std::cout << reply.toStdString() << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NativeMessagingBase::getLocalServerPath() const
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/kpxc_server";
|
||||||
|
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
||||||
|
// Use XDG_RUNTIME_DIR instead of /tmp/ if it's available
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + "/kpxc_server";
|
||||||
|
return path.isEmpty() ? "/tmp/kpxc_server" : path;
|
||||||
|
#else // Q_OS_MAC and others
|
||||||
|
return "/tmp/kpxc_server";
|
||||||
|
#endif
|
||||||
|
}
|
61
src/browser/NativeMessagingBase.h
Normal file
61
src/browser/NativeMessagingBase.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NATIVEMESSAGINGBASE_H
|
||||||
|
#define NATIVEMESSAGINGBASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QAtomicInteger>
|
||||||
|
#include <iostream>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
class NativeMessagingBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NativeMessagingBase();
|
||||||
|
~NativeMessagingBase() = default;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void newNativeMessage();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void readLength() = 0;
|
||||||
|
virtual void readStdIn(const quint32 length) = 0;
|
||||||
|
void readNativeMessages();
|
||||||
|
QString jsonToString(const QJsonObject& json) const;
|
||||||
|
void sendReply(const QJsonObject& json);
|
||||||
|
void sendReply(const QString& reply);
|
||||||
|
QString getLocalServerPath() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QAtomicInteger<quint8> m_running;
|
||||||
|
QSharedPointer<QSocketNotifier> m_notifier;
|
||||||
|
QFuture<void> m_future;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NATIVEMESSAGINGBASE_H
|
200
src/browser/NativeMessagingHost.cpp
Executable file
200
src/browser/NativeMessagingHost.cpp
Executable file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QtNetwork>
|
||||||
|
#include <iostream>
|
||||||
|
#include "sodium.h"
|
||||||
|
#include "NativeMessagingHost.h"
|
||||||
|
#include "BrowserSettings.h"
|
||||||
|
|
||||||
|
NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent) :
|
||||||
|
NativeMessagingBase(),
|
||||||
|
m_mutex(QMutex::Recursive),
|
||||||
|
m_browserClients(m_browserService),
|
||||||
|
m_browserService(parent)
|
||||||
|
{
|
||||||
|
m_localServer.reset(new QLocalServer(this));
|
||||||
|
m_localServer->setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
m_running.store(false);
|
||||||
|
|
||||||
|
if (BrowserSettings::isEnabled() && !m_running) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(&m_browserService, SIGNAL(databaseLocked()), this, SLOT(databaseLocked()));
|
||||||
|
connect(&m_browserService, SIGNAL(databaseUnlocked()), this, SLOT(databaseUnlocked()));
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeMessagingHost::~NativeMessagingHost()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int NativeMessagingHost::init()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
return sodium_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::run()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_running.load() && init() == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update KeePassXC/keepassxc-proxy binary paths to Native Messaging scripts
|
||||||
|
if (BrowserSettings::updateBinaryPath()) {
|
||||||
|
BrowserSettings::updateBinaryPaths(BrowserSettings::useCustomProxy() ? BrowserSettings::customProxyLocation() : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_running.store(true);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
m_future = QtConcurrent::run(this, static_cast<void(NativeMessagingHost::*)()>(&NativeMessagingHost::readNativeMessages));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (BrowserSettings::supportBrowserProxy()) {
|
||||||
|
QString serverPath = getLocalServerPath();
|
||||||
|
QFile::remove(serverPath);
|
||||||
|
m_localServer->listen(serverPath);
|
||||||
|
connect(m_localServer.data(), SIGNAL(newConnection()), this, SLOT(newLocalConnection()));
|
||||||
|
} else {
|
||||||
|
m_localServer->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::stop()
|
||||||
|
{
|
||||||
|
databaseLocked();
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_socketList.clear();
|
||||||
|
m_running.testAndSetOrdered(true, false);
|
||||||
|
m_future.waitForFinished();
|
||||||
|
m_localServer->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::readLength()
|
||||||
|
{
|
||||||
|
quint32 length = 0;
|
||||||
|
std::cin.read(reinterpret_cast<char*>(&length), 4);
|
||||||
|
if (!std::cin.eof() && length > 0)
|
||||||
|
{
|
||||||
|
readStdIn(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::readStdIn(const quint32 length)
|
||||||
|
{
|
||||||
|
if (length > 0) {
|
||||||
|
QByteArray arr;
|
||||||
|
arr.reserve(length);
|
||||||
|
|
||||||
|
for (quint32 i = 0; i < length; ++i) {
|
||||||
|
arr.append(getchar());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arr.length() > 0) {
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
sendReply(m_browserClients.readResponse(arr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::newLocalConnection()
|
||||||
|
{
|
||||||
|
QLocalSocket* socket = m_localServer->nextPendingConnection();
|
||||||
|
connect(socket, SIGNAL(readyRead()), this, SLOT(newLocalMessage()));
|
||||||
|
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectSocket()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::newLocalMessage()
|
||||||
|
{
|
||||||
|
QLocalSocket* socket = qobject_cast<QLocalSocket*>(QObject::sender());
|
||||||
|
|
||||||
|
if (!socket || socket->bytesAvailable() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray arr = socket->readAll();
|
||||||
|
if (arr.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
if (!m_socketList.contains(socket)) {
|
||||||
|
m_socketList.push_back(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString reply = jsonToString(m_browserClients.readResponse(arr));
|
||||||
|
if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
|
||||||
|
QByteArray arr = reply.toUtf8();
|
||||||
|
socket->write(arr.constData(), arr.length());
|
||||||
|
socket->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::sendReplyToAllClients(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
QString reply = jsonToString(json);
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
for (const auto socket : m_socketList) {
|
||||||
|
if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
|
||||||
|
QByteArray arr = reply.toUtf8();
|
||||||
|
socket->write(arr.constData(), arr.length());
|
||||||
|
socket->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::disconnectSocket()
|
||||||
|
{
|
||||||
|
QLocalSocket* socket(qobject_cast<QLocalSocket*>(QObject::sender()));
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
for (auto s : m_socketList) {
|
||||||
|
if (s == socket) {
|
||||||
|
m_socketList.removeOne(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::removeSharedEncryptionKeys()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_browserService.removeSharedEncryptionKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::removeStoredPermissions()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_browserService.removeStoredPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::databaseLocked()
|
||||||
|
{
|
||||||
|
QJsonObject response;
|
||||||
|
response["action"] = "database-locked";
|
||||||
|
sendReplyToAllClients(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::databaseUnlocked()
|
||||||
|
{
|
||||||
|
QJsonObject response;
|
||||||
|
response["action"] = "database-unlocked";
|
||||||
|
sendReplyToAllClients(response);
|
||||||
|
}
|
67
src/browser/NativeMessagingHost.h
Executable file
67
src/browser/NativeMessagingHost.h
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NATIVEMESSAGINGHOST_H
|
||||||
|
#define NATIVEMESSAGINGHOST_H
|
||||||
|
|
||||||
|
#include "NativeMessagingBase.h"
|
||||||
|
#include "BrowserClients.h"
|
||||||
|
#include "BrowserService.h"
|
||||||
|
#include "gui/DatabaseTabWidget.h"
|
||||||
|
|
||||||
|
class NativeMessagingHost : public NativeMessagingBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
typedef QList<QLocalSocket*> SocketList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NativeMessagingHost(DatabaseTabWidget* parent = 0);
|
||||||
|
~NativeMessagingHost();
|
||||||
|
int init();
|
||||||
|
void run();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void removeSharedEncryptionKeys();
|
||||||
|
void removeStoredPermissions();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readLength();
|
||||||
|
void readStdIn(const quint32 length);
|
||||||
|
void sendReplyToAllClients(const QJsonObject& json);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void databaseLocked();
|
||||||
|
void databaseUnlocked();
|
||||||
|
void newLocalConnection();
|
||||||
|
void newLocalMessage();
|
||||||
|
void disconnectSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex m_mutex;
|
||||||
|
BrowserClients m_browserClients;
|
||||||
|
BrowserService m_browserService;
|
||||||
|
QSharedPointer<QLocalServer> m_localServer;
|
||||||
|
SocketList m_socketList;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NATIVEMESSAGINGHOST_H
|
37
src/browser/Variant.cpp
Normal file
37
src/browser/Variant.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Variant.h"
|
||||||
|
|
||||||
|
QVariantMap qo2qv(const QObject* object, const QStringList& ignoredProperties)
|
||||||
|
{
|
||||||
|
QVariantMap result;
|
||||||
|
const QMetaObject* metaobject = object->metaObject();
|
||||||
|
int count = metaobject->propertyCount();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
QMetaProperty metaproperty = metaobject->property(i);
|
||||||
|
const char* name = metaproperty.name();
|
||||||
|
|
||||||
|
if (ignoredProperties.contains(QLatin1String(name)) || (!metaproperty.isReadable())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant value = object->property(name);
|
||||||
|
result[QLatin1String(name)] = value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
25
src/browser/Variant.h
Normal file
25
src/browser/Variant.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VARIANT_H
|
||||||
|
#define VARIANT_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
QVariantMap qo2qv(const QObject* object, const QStringList& ignoredProperties = QStringList(QString(QLatin1String("objectName"))));
|
||||||
|
|
||||||
|
#endif // VARIANT_H
|
@ -16,6 +16,7 @@
|
|||||||
#cmakedefine WITH_XC_AUTOTYPE
|
#cmakedefine WITH_XC_AUTOTYPE
|
||||||
#cmakedefine WITH_XC_YUBIKEY
|
#cmakedefine WITH_XC_YUBIKEY
|
||||||
#cmakedefine WITH_XC_SSHAGENT
|
#cmakedefine WITH_XC_SSHAGENT
|
||||||
|
#cmakedefine WITH_XC_BROWSER
|
||||||
|
|
||||||
#cmakedefine KEEPASSXC_DIST
|
#cmakedefine KEEPASSXC_DIST
|
||||||
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
|
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
|
||||||
|
@ -28,8 +28,7 @@ PassphraseGenerator::PassphraseGenerator()
|
|||||||
: m_wordCount(0)
|
: m_wordCount(0)
|
||||||
, m_separator(' ')
|
, m_separator(' ')
|
||||||
{
|
{
|
||||||
const QString path = filePath()->dataPath("wordlists/eff_large.wordlist");
|
|
||||||
setWordList(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double PassphraseGenerator::calculateEntropy(QString passphrase)
|
double PassphraseGenerator::calculateEntropy(QString passphrase)
|
||||||
@ -46,12 +45,12 @@ double PassphraseGenerator::calculateEntropy(QString passphrase)
|
|||||||
void PassphraseGenerator::setWordCount(int wordCount)
|
void PassphraseGenerator::setWordCount(int wordCount)
|
||||||
{
|
{
|
||||||
if (wordCount > 0) {
|
if (wordCount > 0) {
|
||||||
m_wordCount = wordCount;
|
m_wordCount = wordCount;
|
||||||
} else {
|
} else {
|
||||||
// safe default if something goes wrong
|
// safe default if something goes wrong
|
||||||
m_wordCount = 7;
|
m_wordCount = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordList(QString path)
|
void PassphraseGenerator::setWordList(QString path)
|
||||||
@ -75,6 +74,12 @@ void PassphraseGenerator::setWordList(QString path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PassphraseGenerator::setDefaultWordList()
|
||||||
|
{
|
||||||
|
const QString path = filePath()->dataPath("wordlists/eff_large.wordlist");
|
||||||
|
setWordList(path);
|
||||||
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordSeparator(QString separator) {
|
void PassphraseGenerator::setWordSeparator(QString separator) {
|
||||||
m_separator = separator;
|
m_separator = separator;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
double calculateEntropy(QString passphrase);
|
double calculateEntropy(QString passphrase);
|
||||||
void setWordCount(int wordCount);
|
void setWordCount(int wordCount);
|
||||||
void setWordList(QString path);
|
void setWordList(QString path);
|
||||||
|
void setDefaultWordList();
|
||||||
void setWordSeparator(QString separator);
|
void setWordSeparator(QString separator);
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
@ -43,4 +44,4 @@ private:
|
|||||||
Q_DISABLE_COPY(PassphraseGenerator)
|
Q_DISABLE_COPY(PassphraseGenerator)
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_PASSPHRASEGENERATOR_H
|
#endif // KEEPASSX_PASSPHRASEGENERATOR_H
|
||||||
|
@ -90,6 +90,9 @@ AboutDialog::AboutDialog(QWidget* parent)
|
|||||||
#ifdef WITH_XC_SSHAGENT
|
#ifdef WITH_XC_SSHAGENT
|
||||||
extensions += "\n- SSH Agent";
|
extensions += "\n- SSH Agent";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_XC_BROWSER
|
||||||
|
extensions += "\n- Native messaging browser extension";
|
||||||
|
#endif
|
||||||
|
|
||||||
if (extensions.isEmpty())
|
if (extensions.isEmpty())
|
||||||
extensions = " None";
|
extensions = " None";
|
||||||
|
@ -54,6 +54,12 @@
|
|||||||
#include "sshagent/SSHAgent.h"
|
#include "sshagent/SSHAgent.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XC_BROWSER
|
||||||
|
#include "browser/NativeMessagingHost.h"
|
||||||
|
#include "browser/BrowserSettings.h"
|
||||||
|
#include "browser/BrowserOptionDialog.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "gui/SettingsWidget.h"
|
#include "gui/SettingsWidget.h"
|
||||||
#include "gui/PasswordGeneratorWidget.h"
|
#include "gui/PasswordGeneratorWidget.h"
|
||||||
|
|
||||||
@ -104,6 +110,54 @@ private:
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XC_BROWSER
|
||||||
|
class BrowserPlugin: public ISettingsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BrowserPlugin(DatabaseTabWidget* tabWidget) {
|
||||||
|
m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget));
|
||||||
|
}
|
||||||
|
|
||||||
|
~BrowserPlugin() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name() override
|
||||||
|
{
|
||||||
|
return QObject::tr("Browser extension with native messaging");
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon icon() override
|
||||||
|
{
|
||||||
|
return FilePath::instance()->icon("apps", "internet-web-browser");
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* createWidget() override {
|
||||||
|
BrowserOptionDialog* dlg = new BrowserOptionDialog();
|
||||||
|
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_nativeMessagingHost.data(), SLOT(removeSharedEncryptionKeys()));
|
||||||
|
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_nativeMessagingHost.data(), SLOT(removeStoredPermissions()));
|
||||||
|
return dlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadSettings(QWidget* widget) override
|
||||||
|
{
|
||||||
|
qobject_cast<BrowserOptionDialog*>(widget)->loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveSettings(QWidget* widget) override
|
||||||
|
{
|
||||||
|
qobject_cast<BrowserOptionDialog*>(widget)->saveSettings();
|
||||||
|
if (BrowserSettings::isEnabled()) {
|
||||||
|
m_nativeMessagingHost->run();
|
||||||
|
} else {
|
||||||
|
m_nativeMessagingHost->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QSharedPointer<NativeMessagingHost> m_nativeMessagingHost;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
const QString MainWindow::BaseWindowTitle = "KeePassXC";
|
const QString MainWindow::BaseWindowTitle = "KeePassXC";
|
||||||
|
|
||||||
MainWindow::MainWindow()
|
MainWindow::MainWindow()
|
||||||
@ -134,6 +188,9 @@ MainWindow::MainWindow()
|
|||||||
SSHAgent::init(this);
|
SSHAgent::init(this);
|
||||||
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
|
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_XC_BROWSER
|
||||||
|
m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget));
|
||||||
|
#endif
|
||||||
|
|
||||||
setWindowIcon(filePath()->applicationIcon());
|
setWindowIcon(filePath()->applicationIcon());
|
||||||
m_ui->globalMessageWidget->setHidden(true);
|
m_ui->globalMessageWidget->setHidden(true);
|
||||||
|
@ -80,7 +80,8 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
m_ui->comboBoxWordList->setVisible(false);
|
m_ui->comboBoxWordList->setVisible(false);
|
||||||
m_ui->labelWordList->setVisible(false);
|
m_ui->labelWordList->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_dicewareGenerator->setDefaultWordList();
|
||||||
loadSettings();
|
loadSettings();
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@ -164,7 +165,7 @@ void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PasswordGeneratorWidget::regeneratePassword()
|
void PasswordGeneratorWidget::regeneratePassword()
|
||||||
{
|
{
|
||||||
if (m_ui->tabWidget->currentIndex() == Password) {
|
if (m_ui->tabWidget->currentIndex() == Password) {
|
||||||
if (m_passwordGenerator->isValid()) {
|
if (m_passwordGenerator->isValid()) {
|
||||||
QString password = m_passwordGenerator->generatePassword();
|
QString password = m_passwordGenerator->generatePassword();
|
||||||
|
15
src/main.cpp
15
src/main.cpp
@ -69,12 +69,18 @@ int main(int argc, char** argv)
|
|||||||
"keyfile");
|
"keyfile");
|
||||||
QCommandLineOption pwstdinOption("pw-stdin",
|
QCommandLineOption pwstdinOption("pw-stdin",
|
||||||
QCoreApplication::translate("main", "read password of the database from stdin"));
|
QCoreApplication::translate("main", "read password of the database from stdin"));
|
||||||
|
// This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method
|
||||||
|
QCommandLineOption parentWindowOption(QStringList() << "pw"
|
||||||
|
<< "parent-window",
|
||||||
|
QCoreApplication::translate("main", "Parent window handle"),
|
||||||
|
"handle");
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
parser.addOption(configOption);
|
parser.addOption(configOption);
|
||||||
parser.addOption(keyfileOption);
|
parser.addOption(keyfileOption);
|
||||||
parser.addOption(pwstdinOption);
|
parser.addOption(pwstdinOption);
|
||||||
|
parser.addOption(parentWindowOption);
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
const QStringList fileNames = parser.positionalArguments();
|
const QStringList fileNames = parser.positionalArguments();
|
||||||
@ -86,7 +92,7 @@ int main(int argc, char** argv)
|
|||||||
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData();
|
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QApplication::setQuitOnLastWindowClosed(false);
|
QApplication::setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
if (!Crypto::init()) {
|
if (!Crypto::init()) {
|
||||||
@ -116,7 +122,7 @@ int main(int argc, char** argv)
|
|||||||
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
|
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
|
||||||
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
||||||
QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection);
|
QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection);
|
||||||
|
|
||||||
// start minimized if configured
|
// start minimized if configured
|
||||||
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
|
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
|
||||||
bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool();
|
bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool();
|
||||||
@ -126,7 +132,7 @@ int main(int argc, char** argv)
|
|||||||
if (!(minimizeOnStartup && minimizeToTray)) {
|
if (!(minimizeOnStartup && minimizeToTray)) {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
||||||
const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList();
|
const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList();
|
||||||
for (const QString& filename: fileNames) {
|
for (const QString& filename: fileNames) {
|
||||||
@ -138,13 +144,12 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
const bool pwstdin = parser.isSet(pwstdinOption);
|
const bool pwstdin = parser.isSet(pwstdinOption);
|
||||||
for (const QString& filename: fileNames) {
|
for (const QString& filename: fileNames) {
|
||||||
if (!filename.isEmpty() && QFile::exists(filename)) {
|
if (!filename.isEmpty() && QFile::exists(filename) && !filename.endsWith(".json", Qt::CaseInsensitive)) {
|
||||||
QString password;
|
QString password;
|
||||||
if (pwstdin) {
|
if (pwstdin) {
|
||||||
static QTextStream in(stdin, QIODevice::ReadOnly);
|
static QTextStream in(stdin, QIODevice::ReadOnly);
|
||||||
password = in.readLine();
|
password = in.readLine();
|
||||||
}
|
}
|
||||||
mainWindow.openDatabase(filename, password, parser.value(keyfileOption));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
src/proxy/CMakeLists.txt
Executable file
34
src/proxy/CMakeLists.txt
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
if(WITH_XC_BROWSER)
|
||||||
|
include_directories(${BROWSER_SOURCE_DIR})
|
||||||
|
|
||||||
|
set(proxy_SOURCES
|
||||||
|
keepassxc-proxy.cpp
|
||||||
|
${BROWSER_SOURCE_DIR}/NativeMessagingBase.cpp
|
||||||
|
NativeMessagingHost.cpp)
|
||||||
|
|
||||||
|
add_library(proxy STATIC ${proxy_SOURCES})
|
||||||
|
target_link_libraries(proxy Qt5::Core Qt5::Network)
|
||||||
|
add_executable(keepassxc-proxy keepassxc-proxy.cpp)
|
||||||
|
target_link_libraries(keepassxc-proxy proxy)
|
||||||
|
|
||||||
|
install(TARGETS keepassxc-proxy
|
||||||
|
BUNDLE DESTINATION . COMPONENT Runtime
|
||||||
|
RUNTIME DESTINATION ${PROXY_INSTALL_DIR} COMPONENT Runtime)
|
||||||
|
|
||||||
|
endif()
|
95
src/proxy/NativeMessagingHost.cpp
Executable file
95
src/proxy/NativeMessagingHost.cpp
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include "NativeMessagingHost.h"
|
||||||
|
|
||||||
|
NativeMessagingHost::NativeMessagingHost() : NativeMessagingBase()
|
||||||
|
{
|
||||||
|
m_localSocket = new QLocalSocket();
|
||||||
|
m_localSocket->connectToServer(getLocalServerPath());
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
m_running.store(true);
|
||||||
|
m_future = QtConcurrent::run(this, static_cast<void(NativeMessagingHost::*)()>(&NativeMessagingHost::readNativeMessages));
|
||||||
|
#endif
|
||||||
|
connect(m_localSocket, SIGNAL(readyRead()), this, SLOT(newLocalMessage()));
|
||||||
|
connect(m_localSocket, SIGNAL(disconnected()), this, SLOT(deleteSocket()));
|
||||||
|
connect(m_localSocket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), this, SLOT(socketStateChanged(QLocalSocket::LocalSocketState)));
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeMessagingHost::~NativeMessagingHost()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
m_future.waitForFinished();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::readLength()
|
||||||
|
{
|
||||||
|
quint32 length = 0;
|
||||||
|
std::cin.read(reinterpret_cast<char*>(&length), 4);
|
||||||
|
if (!std::cin.eof() && length > 0) {
|
||||||
|
readStdIn(length);
|
||||||
|
} else {
|
||||||
|
QCoreApplication::quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::readStdIn(const quint32 length)
|
||||||
|
{
|
||||||
|
if (length > 0) {
|
||||||
|
QByteArray arr;
|
||||||
|
arr.reserve(length);
|
||||||
|
|
||||||
|
for (quint32 i = 0; i < length; ++i) {
|
||||||
|
arr.append(getchar());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arr.length() > 0 && m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState) {
|
||||||
|
m_localSocket->write(arr.constData(), arr.length());
|
||||||
|
m_localSocket->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::newLocalMessage()
|
||||||
|
{
|
||||||
|
if (!m_localSocket || m_localSocket->bytesAvailable() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray arr = m_localSocket->readAll();
|
||||||
|
if (!arr.isEmpty()) {
|
||||||
|
sendReply(arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::deleteSocket()
|
||||||
|
{
|
||||||
|
if (m_notifier) {
|
||||||
|
m_notifier->setEnabled(false);
|
||||||
|
}
|
||||||
|
m_localSocket->deleteLater();
|
||||||
|
QCoreApplication::quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeMessagingHost::socketStateChanged(QLocalSocket::LocalSocketState socketState)
|
||||||
|
{
|
||||||
|
if (socketState == QLocalSocket::UnconnectedState || socketState == QLocalSocket::ClosingState) {
|
||||||
|
m_running.testAndSetOrdered(true, false);
|
||||||
|
}
|
||||||
|
}
|
43
src/proxy/NativeMessagingHost.h
Executable file
43
src/proxy/NativeMessagingHost.h
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NATIVEMESSAGINGHOST_H
|
||||||
|
#define NATIVEMESSAGINGHOST_H
|
||||||
|
|
||||||
|
#include "NativeMessagingBase.h"
|
||||||
|
|
||||||
|
class NativeMessagingHost : public NativeMessagingBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
NativeMessagingHost();
|
||||||
|
~NativeMessagingHost();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void newLocalMessage();
|
||||||
|
void deleteSocket();
|
||||||
|
void socketStateChanged(QLocalSocket::LocalSocketState socketState);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readLength();
|
||||||
|
void readStdIn(const quint32 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLocalSocket* m_localSocket;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NATIVEMESSAGINGHOST_H
|
66
src/proxy/keepassxc-proxy.cpp
Normal file
66
src/proxy/keepassxc-proxy.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <iostream>
|
||||||
|
#include "NativeMessagingHost.h"
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// (C) Gist: https://gist.github.com/azadkuh/a2ac6869661ebd3f8588
|
||||||
|
void ignoreUnixSignals(std::initializer_list<int> ignoreSignals) {
|
||||||
|
for (int sig : ignoreSignals) {
|
||||||
|
signal(sig, SIG_IGN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void catchUnixSignals(std::initializer_list<int> quitSignals) {
|
||||||
|
auto handler = [](int sig) -> void {
|
||||||
|
std::cerr << sig;
|
||||||
|
QCoreApplication::quit();
|
||||||
|
};
|
||||||
|
|
||||||
|
sigset_t blocking_mask;
|
||||||
|
sigemptyset(&blocking_mask);
|
||||||
|
for (auto sig : quitSignals) {
|
||||||
|
sigaddset(&blocking_mask, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = handler;
|
||||||
|
sa.sa_mask = blocking_mask;
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
|
||||||
|
for (auto sig : quitSignals) {
|
||||||
|
sigaction(sig, &sa, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication a(argc, argv);
|
||||||
|
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX)
|
||||||
|
catchUnixSignals({SIGQUIT, SIGINT, SIGTERM, SIGHUP});
|
||||||
|
#endif
|
||||||
|
NativeMessagingHost host;
|
||||||
|
return a.exec();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user