mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-23 21:21:08 -05:00
Merge branch 'develop' into issue-1148-msi-package
This commit is contained in:
commit
470ddb4704
@ -15,8 +15,8 @@ compiler:
|
|||||||
- gcc
|
- gcc
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- CONFIG=Release ASAN_OPTIONS=detect_odr_violation=1:leak_check_at_exit=0
|
- CONFIG=Release ASAN_OPTIONS=detect_odr_violation=1
|
||||||
- CONFIG=Debug ASAN_OPTIONS=detect_odr_violation=1:leak_check_at_exit=0
|
- CONFIG=Debug ASAN_OPTIONS=detect_odr_violation=1
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 3
|
depth: 3
|
||||||
@ -37,7 +37,7 @@ script:
|
|||||||
- cmake -DCMAKE_BUILD_TYPE=${CONFIG} -DWITH_GUI_TESTS=ON -DWITH_ASAN=ON -DWITH_XC_HTTP=ON -DWITH_XC_AUTOTYPE=ON -DWITH_XC_YUBIKEY=ON $CMAKE_ARGS ..
|
- cmake -DCMAKE_BUILD_TYPE=${CONFIG} -DWITH_GUI_TESTS=ON -DWITH_ASAN=ON -DWITH_XC_HTTP=ON -DWITH_XC_AUTOTYPE=ON -DWITH_XC_YUBIKEY=ON $CMAKE_ARGS ..
|
||||||
- make -j2
|
- make -j2
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ASAN_OPTIONS=${ASAN_OPTIONS}:leak_check_at_exit=0 xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test ARGS+="--output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test ARGS+="--output-on-failure"; fi
|
||||||
|
|
||||||
# Generate snapcraft build when merging into master/develop branches
|
# Generate snapcraft build when merging into master/develop branches
|
||||||
|
@ -87,14 +87,15 @@ else
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
chmod +x ./usr/bin/keepassxc_env
|
chmod +x ./usr/bin/keepassxc_env
|
||||||
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.desktop
|
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.${APP}.desktop
|
||||||
get_desktopintegration "org.${LOWERAPP}"
|
get_desktopintegration "org.${LOWERAPP}.${APP}"
|
||||||
|
|
||||||
GLIBC_NEEDED=$(glibc_needed)
|
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
generate_type2_appimage
|
GLIBC_NEEDED=$(glibc_needed)
|
||||||
|
NO_GLIBC_VERSION=true
|
||||||
|
|
||||||
mv ../out/*.AppImage ../KeePassXC-${VERSION}-${ARCH}.AppImage
|
generate_type2_appimage -u "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-${ARCH}.AppImage.zsync"
|
||||||
rmdir ../out > /dev/null 2>&1
|
|
||||||
|
mv ../out/*.AppImage* ../
|
||||||
|
rm -rf ../out
|
||||||
|
10
CHANGELOG
10
CHANGELOG
@ -1,3 +1,13 @@
|
|||||||
|
2.2.4 (2017-12-13)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
- Prevent database corruption when locked [#1219]
|
||||||
|
- Fixes apply button not saving new entries [#1141]
|
||||||
|
- Switch to Consolas font on Windows for password edit [#1229]
|
||||||
|
- Multiple fixes to AppImage deployment [#1115, #1228]
|
||||||
|
- Fixes multiple memory leaks [#1213]
|
||||||
|
- Resize message close to 16x16 pixels [#1253]
|
||||||
|
|
||||||
2.2.2 (2017-10-22)
|
2.2.2 (2017-10-22)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
@ -14,16 +14,16 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
|
project(KeePassXC)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
|
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
|
||||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel."
|
"Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel."
|
||||||
FORCE)
|
FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(KeePassXC)
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
# Support Visual Studio Code
|
# Support Visual Studio Code
|
||||||
@ -43,13 +43,14 @@ option(WITH_APP_BUNDLE "Enable Application Bundle for macOS" ON)
|
|||||||
option(WITH_XC_AUTOTYPE "Include Auto-Type." ON)
|
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)
|
||||||
|
|
||||||
# Process ui files automatically from source files
|
# Process ui files automatically from source files
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
set(KEEPASSXC_VERSION_MAJOR "2")
|
set(KEEPASSXC_VERSION_MAJOR "2")
|
||||||
set(KEEPASSXC_VERSION_MINOR "2")
|
set(KEEPASSXC_VERSION_MINOR "2")
|
||||||
set(KEEPASSXC_VERSION_PATCH "2")
|
set(KEEPASSXC_VERSION_PATCH "4")
|
||||||
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
||||||
|
|
||||||
# Distribution info
|
# Distribution info
|
||||||
@ -96,7 +97,7 @@ if(WITH_APP_BUNDLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_gcc_compiler_flags("-fno-common")
|
add_gcc_compiler_flags("-fno-common")
|
||||||
add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long")
|
add_gcc_compiler_flags("-Wall -Werror -Wextra -Wundef -Wpointer-arith -Wno-long-long")
|
||||||
add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute")
|
add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute")
|
||||||
add_gcc_compiler_flags("-fvisibility=hidden")
|
add_gcc_compiler_flags("-fvisibility=hidden")
|
||||||
add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden")
|
add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden")
|
||||||
|
5
COPYING
5
COPYING
@ -55,10 +55,6 @@ Files: cmake/GenerateProductVersion.cmake
|
|||||||
Copyright: 2015 halex2005 <akharlov@gmail.com>
|
Copyright: 2015 halex2005 <akharlov@gmail.com>
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|
||||||
Files: cmake/CodeCoverage.cmake
|
|
||||||
Copyright: 2012 - 2015, Lars Bilke
|
|
||||||
License: BSD-3-clause
|
|
||||||
|
|
||||||
Files: share/icons/application/*/apps/keepassxc.png
|
Files: share/icons/application/*/apps/keepassxc.png
|
||||||
share/icons/application/scalable/apps/keepassxc.svgz
|
share/icons/application/scalable/apps/keepassxc.svgz
|
||||||
share/icons/application/*/apps/keepassxc-dark.png
|
share/icons/application/*/apps/keepassxc-dark.png
|
||||||
@ -179,6 +175,7 @@ Files: share/icons/application/*/actions/application-exit.png
|
|||||||
share/icons/application/*/actions/view-history.png
|
share/icons/application/*/actions/view-history.png
|
||||||
share/icons/application/*/apps/internet-web-browser.png
|
share/icons/application/*/apps/internet-web-browser.png
|
||||||
share/icons/application/*/apps/preferences-desktop-icons.png
|
share/icons/application/*/apps/preferences-desktop-icons.png
|
||||||
|
share/icons/application/*/apps/utilities-terminal.png
|
||||||
share/icons/application/*/categories/preferences-other.png
|
share/icons/application/*/categories/preferences-other.png
|
||||||
share/icons/application/*/status/dialog-error.png
|
share/icons/application/*/status/dialog-error.png
|
||||||
share/icons/application/*/status/dialog-information.png
|
share/icons/application/*/status/dialog-information.png
|
||||||
|
37
Dockerfile
37
Dockerfile
@ -24,7 +24,8 @@ RUN set -x \
|
|||||||
&& apt-get -y install software-properties-common
|
&& apt-get -y install software-properties-common
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apt-get update -y \
|
&& apt-get update -y \
|
||||||
@ -42,7 +43,9 @@ RUN set -x \
|
|||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
libxi-dev \
|
libxi-dev \
|
||||||
libxtst-dev \
|
libxtst-dev \
|
||||||
mesa-common-dev
|
mesa-common-dev \
|
||||||
|
libyubikey-dev \
|
||||||
|
libykpers-1-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
|
||||||
@ -52,34 +55,8 @@ RUN set -x \
|
|||||||
# AppImage dependencies
|
# AppImage dependencies
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
wget \
|
libfuse2 \
|
||||||
libfuse2
|
wget
|
||||||
|
|
||||||
# build libyubikey
|
|
||||||
ENV YUBIKEY_VERSION=1.13
|
|
||||||
RUN set -x \
|
|
||||||
&& wget "https://developers.yubico.com/yubico-c/Releases/libyubikey-${YUBIKEY_VERSION}.tar.gz" \
|
|
||||||
&& tar xf libyubikey-${YUBIKEY_VERSION}.tar.gz \
|
|
||||||
&& cd libyubikey-${YUBIKEY_VERSION} \
|
|
||||||
&& ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu \
|
|
||||||
&& make \
|
|
||||||
&& make install \
|
|
||||||
&& cd .. \
|
|
||||||
&& rm -Rf libyubikey-${YUBIKEY_VERSION}*
|
|
||||||
|
|
||||||
# build libykpers-1
|
|
||||||
ENV YKPERS_VERSION=1.18.0
|
|
||||||
RUN set -x \
|
|
||||||
&& apt-get install -y libusb-dev
|
|
||||||
RUN set -x \
|
|
||||||
&& wget "https://developers.yubico.com/yubikey-personalization/Releases/ykpers-${YKPERS_VERSION}.tar.gz" \
|
|
||||||
&& tar xf ykpers-${YKPERS_VERSION}.tar.gz \
|
|
||||||
&& cd ykpers-${YKPERS_VERSION} \
|
|
||||||
&& ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu \
|
|
||||||
&& make \
|
|
||||||
&& make install \
|
|
||||||
&& cd .. \
|
|
||||||
&& rm -Rf ykpers-${YKPERS_VERSION}*
|
|
||||||
|
|
||||||
VOLUME /keepassxc/src
|
VOLUME /keepassxc/src
|
||||||
VOLUME /keepassxc/out
|
VOLUME /keepassxc/out
|
||||||
|
63
INSTALL.md
63
INSTALL.md
@ -1,11 +1,13 @@
|
|||||||
Install KeePassXC
|
Build and Install KeePassXC
|
||||||
=================
|
=================
|
||||||
|
|
||||||
This document will guide you across the steps to install KeePassXC.
|
This document will guide you through the steps to build and install KeePassXC from source.
|
||||||
You can visit the online version of this document at the following link
|
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
|
||||||
|
Windows, Mac, or Linux computer using the pre-built binaries.
|
||||||
|
|
||||||
Build Dependencies
|
Build Dependencies
|
||||||
==================
|
==================
|
||||||
@ -28,10 +30,9 @@ The following libraries are required:
|
|||||||
Prepare the Building Environment
|
Prepare the Building Environment
|
||||||
================================
|
================================
|
||||||
|
|
||||||
* Building Environment on Linux ==> https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-Linux
|
* [Building Environment on Linux](https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-Linux)
|
||||||
* Building Environment on Windows ==> https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-Windows
|
* [Building Environment on Windows](https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-Windows)
|
||||||
* Building Environment on MacOS ==> https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-OS-X
|
* [Building Environment on MacOS](https://github.com/keepassxreboot/keepassxc/wiki/Set-up-Build-Environment-on-OS-X)
|
||||||
|
|
||||||
|
|
||||||
Build Steps
|
Build Steps
|
||||||
===========
|
===========
|
||||||
@ -39,20 +40,39 @@ 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
|
||||||
|
|
||||||
Navigate to the path you have downloaded KeePassXC and type these commands:
|
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).
|
||||||
|
|
||||||
|
To clone the project from Git, `cd` to a suitable location and run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/keepassxreboot/keepassxc.git
|
||||||
|
```
|
||||||
|
|
||||||
|
This will clone the entire contents of the repository and check out the current `develop` branch.
|
||||||
|
|
||||||
|
To update the project from within the project's folder, you can run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Navigate to the directory where you have downloaded KeePassXC and type these commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
cd directory-where-sources-live
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DWITH_TESTS=OFF
|
cmake -DWITH_TESTS=OFF ...and other options - see below...
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
These steps place the compiled KeePassXC binary inside the `./build/src/` directory.
|
||||||
|
(Note the cmake notes/options below.)
|
||||||
|
|
||||||
**Note:** If you are on MacOS you must add this parameter to **Cmake**, with the Qt version you have installed<br/> `-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake/`
|
**Cmake Notes:**
|
||||||
|
|
||||||
You will have the compiled KeePassXC binary inside the `./build/src/` directory.
|
* Common cmake parameters
|
||||||
|
|
||||||
Common cmake parameters
|
|
||||||
```
|
```
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/local
|
-DCMAKE_INSTALL_PREFIX=/usr/local
|
||||||
-DCMAKE_VERBOSE_MAKEFILE=ON
|
-DCMAKE_VERBOSE_MAKEFILE=ON
|
||||||
@ -60,11 +80,28 @@ Common cmake parameters
|
|||||||
-DWITH_GUI_TESTS=ON
|
-DWITH_GUI_TESTS=ON
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* cmake accepts the following options:
|
||||||
|
|
||||||
|
```
|
||||||
|
-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_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF)
|
||||||
|
|
||||||
|
-DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON)
|
||||||
|
-DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF)
|
||||||
|
-DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF)
|
||||||
|
-DWITH_ASAN=[ON|OFF] Enable/Disable address sanitizer checks (Linux / macOS only) (default: OFF)
|
||||||
|
-DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests (GCC only) (default: OFF)
|
||||||
|
```
|
||||||
|
|
||||||
|
* If you are on MacOS you must add this parameter to **Cmake**, with the Qt version you have installed<br/> `-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake/`
|
||||||
|
|
||||||
|
:exclamation: When building with ASan support on macOS, you need to use `export ASAN_OPTIONS=detect_leaks=0` before running the tests (no LSan support in macOS).
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
To install this binary execute the following:
|
After you have successfully built KeePassXC, install the binary by executing the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo make install
|
sudo make install
|
||||||
|
96
README.md
96
README.md
@ -1,10 +1,20 @@
|
|||||||
# <img src="https://keepassxc.org/logo.png" width="40" height="40"/> KeePassXC [![Travis Build Status](https://travis-ci.org/keepassxreboot/keepassxc.svg?branch=develop)](https://travis-ci.org/keepassxreboot/keepassxc) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc)
|
# <img src="https://keepassxc.org/logo.png" width="40" height="40"/> KeePassXC
|
||||||
|
[![Travis Build Status](https://travis-ci.org/keepassxreboot/keepassxc.svg?branch=develop)](https://travis-ci.org/keepassxreboot/keepassxc) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc)
|
||||||
|
|
||||||
KeePass Cross-platform Community Edition
|
## About KeePassXC
|
||||||
|
[KeePassXC](https://keepassxc.org) is a cross-platform community fork of
|
||||||
|
[KeePassX](https://www.keepassx.org/).
|
||||||
|
Our goal is to extend and improve it with new features and bugfixes
|
||||||
|
to provide a feature-rich, fully cross-platform and modern
|
||||||
|
open-source password manager.
|
||||||
|
|
||||||
## About
|
## Installation
|
||||||
[KeePassXC](https://keepassxc.org) is a community fork of [KeePassX](https://www.keepassx.org/) with the goal to extend and improve it with new features and bugfixes to provide a feature-rich, fully cross-platform and modern open-source password manager.
|
The [KeePassXC QuickStart](./docs/QUICKSTART.md) gets you started using
|
||||||
|
KeePassXC on your Windows, Mac, or Linux computer using pre-compiled binaries
|
||||||
|
from the [downloads page](https://keepassxc.org/download).
|
||||||
|
|
||||||
|
Additionally, individual Linux distributions may ship their own versions,
|
||||||
|
so please check out your distribution's package list to see if KeePassXC is available.
|
||||||
|
|
||||||
## Additional features compared to KeePassX
|
## Additional features compared to KeePassX
|
||||||
- Auto-Type on all three major platforms (Linux, Windows, macOS)
|
- Auto-Type on all three major platforms (Linux, Windows, macOS)
|
||||||
@ -19,67 +29,39 @@ KeePass Cross-platform Community Edition
|
|||||||
- 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
|
||||||
- KeePassHTTP support for use with KeePassHTTP-Connector for [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 [passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari.
|
- Browser integration with KeePassHTTP-Connector for
|
||||||
|
[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
|
||||||
|
[passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari. [[See note about KeePassHTTP]](#Note_about_KeePassHTTP)
|
||||||
- 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.
|
||||||
|
|
||||||
### Note about KeePassHTTP
|
## Building KeePassXC
|
||||||
KeePassHTTP is not a highly secure protocol and has certain flaw which allow an attacker to decrypt your passwords when they manage to intercept communication between a KeePassHTTP server 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)). KeePassXC therefore strictly limits communication between itself and the browser plugin to your local computer. As long as your computer is not compromised, your passwords are fairly safe that way, but use it at your own risk!
|
|
||||||
|
|
||||||
### Installation
|
Detailed instructions are available in the [Build and Install](./INSTALL.md)
|
||||||
Pre-compiled binaries can be found on the [downloads page](https://keepassxc.org/download). Additionally, individual Linux distributions may ship their own versions, so please check out your distribution's package list to see if KeePassXC is available.
|
page or on the [Wiki page](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC).
|
||||||
|
|
||||||
### Building KeePassXC
|
## Contributing
|
||||||
|
|
||||||
*More detailed instructions are available in the INSTALL file or on the [Wiki page](https://github.com/keepassxreboot/keepassxc/wiki/Building-KeePassXC).*
|
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
|
||||||
First, you must 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).
|
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.
|
||||||
To clone the project from Git, `cd` to a suitable location and run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/keepassxreboot/keepassxc.git
|
|
||||||
```
|
|
||||||
|
|
||||||
This will clone the entire contents of the repository and check out the current `develop` branch.
|
|
||||||
|
|
||||||
To update the project from within the project's folder, you can run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git pull
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you have downloaded the source code, you can `cd` into the source code directory, build and install KeePassXC:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -DWITH_TESTS=OFF ..
|
|
||||||
make -j8
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
cmake accepts the following options:
|
|
||||||
|
|
||||||
```
|
|
||||||
-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_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF)
|
|
||||||
|
|
||||||
-DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON)
|
|
||||||
-DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF)
|
|
||||||
-DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF)
|
|
||||||
-DWITH_ASAN=[ON|OFF] Enable/Disable address sanitizer checks (Linux / macOS only) (default: OFF)
|
|
||||||
-DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests (GCC only) (default: OFF)
|
|
||||||
```
|
|
||||||
|
|
||||||
:exclamation: When building with ASan support on macOS, you need to use `export ASAN_OPTIONS=detect_leaks=0` before running the tests (no LSan support in macOS).
|
|
||||||
|
|
||||||
### Contributing
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
Please read the [CONTRIBUTING document](.github/CONTRIBUTING.md) for further information.
|
Please read the [CONTRIBUTING document](.github/CONTRIBUTING.md) for further information.
|
||||||
|
|
||||||
|
### Note about KeePassHTTP
|
||||||
|
The KeePassHTTP protocol is not a highly secure protocol.
|
||||||
|
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.
|
||||||
|
<!--intercept communication between a KeePassHTTP server
|
||||||
|
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)).
|
||||||
|
|
||||||
|
To minimize the risk, KeePassXC strictly limits communication between itself
|
||||||
|
and the browser plugin to your local computer (localhost).
|
||||||
|
This makes your passwords quite safe,
|
||||||
|
but as with all open source software, use it at your own risk!
|
||||||
|
BIN
docs/KeePassXC-Accept-Button.png
Normal file
BIN
docs/KeePassXC-Accept-Button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
BIN
docs/KeePassXC-Confirm.png
Normal file
BIN
docs/KeePassXC-Confirm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
docs/KeePassXC-Connect.png
Normal file
BIN
docs/KeePassXC-Connect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
47
docs/QUICKSTART.md
Normal file
47
docs/QUICKSTART.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Quick Start for KeePassXC
|
||||||
|
|
||||||
|
This procedure gets KeePassXC running on your computer with browser integration,
|
||||||
|
using the pre-built binaries available for [download](https://keepassxc.org/download)
|
||||||
|
from [KeePassXC site](https://keepassxc.org).
|
||||||
|
|
||||||
|
**TL;DR** KeePassXC saves your passwords securely.
|
||||||
|
When you double-click a URL in KeePassXC, it launches your default browser to that URL.
|
||||||
|
With browser integration configured, KeePassXC automatically enters
|
||||||
|
username/password credentials into web page fields.
|
||||||
|
|
||||||
|
## Installing and Starting KeePassXC
|
||||||
|
|
||||||
|
* [Download the native installer](https://keepassxc.org/download) and install
|
||||||
|
KeePassXC for your Windows, macOS, or Linux computer in the usual way for your platform.
|
||||||
|
* Open the KeePassXC application.
|
||||||
|
* Create a new database and give it a master key that's used to unlock the database file.
|
||||||
|
This database holds entries (usernames, passwords, account numbers, notes)
|
||||||
|
for all your websites, programs, etc.
|
||||||
|
* Create a few entries - enter the username, password, URL, and optionally notes about the entry.
|
||||||
|
* KeePassXC securely stores those entries in the database.
|
||||||
|
|
||||||
|
|
||||||
|
## Setting up Browser Integration with KeePassXC
|
||||||
|
|
||||||
|
* *Within KeePassXC*, go to **Tools->Settings** (on macOS, go to **KeePassXC->Preferences**.)
|
||||||
|
* In **Browser Integration**, check **Enable KeePassHTTP server**
|
||||||
|
Leave the other options at their defaults.
|
||||||
|
* *In your default web browser,* install the KeePassHTTP-Connector extension/add-on. Instructions for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/?src=api) or [Chrome](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb?utm_source=chrome-app-launcher-info-dialog)
|
||||||
|
* Click the KeePassXC icon in the upper-right corner. You'll see the dialog below.
|
||||||
|
* Click the blue Connect button to make the browser extension connect to the KeePassXC application.
|
||||||
|
<img src="./KeePassXC-Connect.png" height="200" alt="KeePassXC Connect dialog">
|
||||||
|
|
||||||
|
* *Switch back to KeePassXC.* You'll see a dialog (below) indicating that a request to connect has arrived.
|
||||||
|
* Give the connection a name (perhaps *Keepass-Browsername*, any unique name will suffice) and click OK to accept it.
|
||||||
|
* This one-time operation connects KeePassXC and your browser.
|
||||||
|
<img src="./KeePassXC-Accept-Button.png" height="200" alt="KeePassXC accept connection dialog">
|
||||||
|
|
||||||
|
## Using Browser Integration
|
||||||
|
|
||||||
|
* *Within KeePassXC,* double-click the URL of an entry,
|
||||||
|
or select it and type Ctrl+U (Cmd+U on macOS).
|
||||||
|
* Your browser opens to that URL.
|
||||||
|
* If there are username/password fields on that page, you will see the dialog below.
|
||||||
|
Click *Allow* to confirm that KeePassXC may access the credentials to auto-fill the fields.
|
||||||
|
* Check *Remember this decision* to allow this each time you visit the page.
|
||||||
|
<img src="./KeePassXC-Confirm.png" height="200" alt="KeePassCX Confirm Access dialog">
|
241
release-tool
241
release-tool
@ -50,14 +50,14 @@ printUsage() {
|
|||||||
local cmd
|
local cmd
|
||||||
if [ "" == "$1" ] || [ "help" == "$1" ]; then
|
if [ "" == "$1" ] || [ "help" == "$1" ]; then
|
||||||
cmd="COMMAND"
|
cmd="COMMAND"
|
||||||
elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "sign" == "$1" ]; then
|
elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "gpgsign" == "$1" ] || [ "appsign" == "$1" ]; then
|
||||||
cmd="$1"
|
cmd="$1"
|
||||||
else
|
else
|
||||||
logError "Unknown command: '$1'\n"
|
logError "Unknown command: '$1'\n"
|
||||||
cmd="COMMAND"
|
cmd="COMMAND"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "\e[1mUsage:\e[0m $(basename $0) $cmd [--version x.y.z] [options]\n"
|
printf "\e[1mUsage:\e[0m $(basename $0) $cmd [options]\n"
|
||||||
|
|
||||||
if [ "COMMAND" == "$cmd" ]; then
|
if [ "COMMAND" == "$cmd" ]; then
|
||||||
cat << EOF
|
cat << EOF
|
||||||
@ -66,7 +66,8 @@ Commands:
|
|||||||
check Perform a dry-run check, nothing is changed
|
check Perform a dry-run check, nothing is changed
|
||||||
merge Merge release branch into main branch and create release tags
|
merge Merge release branch into main branch and create release tags
|
||||||
build Build and package binary release from sources
|
build Build and package binary release from sources
|
||||||
sign Sign previously compiled release packages
|
gpgsign Sign previously compiled release packages with GPG
|
||||||
|
appsign Sign binaries with code signing certificates on Windows and macOS
|
||||||
help Show help for the given command
|
help Show help for the given command
|
||||||
EOF
|
EOF
|
||||||
elif [ "merge" == "$cmd" ]; then
|
elif [ "merge" == "$cmd" ]; then
|
||||||
@ -113,16 +114,25 @@ Options:
|
|||||||
-n, --no-source-tarball Don't build source tarball
|
-n, --no-source-tarball Don't build source tarball
|
||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
EOF
|
EOF
|
||||||
elif [ "sign" == "$cmd" ]; then
|
elif [ "gpgsign" == "$cmd" ]; then
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
Sign previously compiled release packages
|
Sign previously compiled release packages with GPG
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-f, --files Files to sign (required)
|
-f, --files Files to sign (required)
|
||||||
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
|
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
|
||||||
--signtool Specify the signtool executable (default: 'signtool')
|
-h, --help Show this help
|
||||||
--signtool-key Provide a key to be used with signtool (for Windows EXE)
|
EOF
|
||||||
|
elif [ "appsign" == "$cmd" ]; then
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Sign binaries with code signing certificates on Windows and macOS
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-f, --files Files to sign (required)
|
||||||
|
-k, --signtool-key Key to be used with signtool (required for Windows EXE)
|
||||||
|
-i, --identity Apple Developer ID to be used with codesign (required for macOS APP and DMG)
|
||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
@ -264,13 +274,13 @@ checkChangeLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAppStreamInfo() {
|
checkAppStreamInfo() {
|
||||||
if [ ! -f share/linux/org.keepassxc.appdata.xml ]; then
|
if [ ! -f share/linux/org.keepassxc.KeePassXC.appdata.xml ]; then
|
||||||
exitError "No AppStream info file found!"
|
exitError "No AppStream info file found!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
grep -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.appdata.xml
|
grep -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.KeePassXC.appdata.xml
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
exitError "'share/linux/org.keepassxc.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
|
exitError "'share/linux/org.keepassxc.KeePassXC.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +299,28 @@ checkSnapcraft() {
|
|||||||
checkTransifexCommandExists() {
|
checkTransifexCommandExists() {
|
||||||
command -v tx > /dev/null
|
command -v tx > /dev/null
|
||||||
if [ 0 -ne $? ]; then
|
if [ 0 -ne $? ]; then
|
||||||
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'"
|
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
checkOsslsigncodeCommandExists() {
|
||||||
|
command -v osslsigncode > /dev/null
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
exitError "osslsigncode command not found on the PATH! Please install it using 'pacman -S mingw-w64-osslsigncode'."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCodesignCommandExists() {
|
||||||
|
command -v codesign > /dev/null
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
exitError "codesign command not found on the PATH! Please check that you have correctly installed Xcode."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCreateDMGCommandExists() {
|
||||||
|
command -v create-dmg > /dev/null
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
exitError "create-dmg command not found on the PATH! Please install it using 'npm install --global create-dmg'."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,19 +696,17 @@ build() {
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
# sign command
|
# gpgsign command
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
sign() {
|
gpgsign() {
|
||||||
SIGN_FILES=()
|
local sign_files=()
|
||||||
SIGNTOOL="signtool"
|
|
||||||
SIGNTOOL_KEY=""
|
|
||||||
|
|
||||||
while [ $# -ge 1 ]; do
|
while [ $# -ge 1 ]; do
|
||||||
local arg="$1"
|
local arg="$1"
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
-f|--files)
|
-f|--files)
|
||||||
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
|
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
|
||||||
SIGN_FILES+=("$2")
|
sign_files+=("$2")
|
||||||
shift
|
shift
|
||||||
done ;;
|
done ;;
|
||||||
|
|
||||||
@ -685,52 +714,27 @@ sign() {
|
|||||||
GPG_KEY="$2"
|
GPG_KEY="$2"
|
||||||
shift ;;
|
shift ;;
|
||||||
|
|
||||||
--signtool)
|
|
||||||
SIGNTOOL="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
--signtool-key)
|
|
||||||
SIGNTOOL_KEY="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-h|--help)
|
-h|--help)
|
||||||
printUsage "sign"
|
printUsage "gpgsign"
|
||||||
exit ;;
|
exit ;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
logError "Unknown option '$arg'\n"
|
logError "Unknown option '$arg'\n"
|
||||||
printUsage "sign"
|
printUsage "gpgsign"
|
||||||
exit 1 ;;
|
exit 1 ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$SIGN_FILES" ]; then
|
if [ -z "${sign_files}" ]; then
|
||||||
logError "Missing arguments, --files is required!\n"
|
logError "Missing arguments, --files is required!\n"
|
||||||
printUsage "sign"
|
printUsage "gpgsign"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$SIGNTOOL_KEY" && ! -f "$SIGNTOOL_KEY" ]]; then
|
for f in "${sign_files[@]}"; do
|
||||||
exitError "Signtool Key was not found!"
|
|
||||||
elif [[ -f "$SIGNTOOL_KEY" && ! -x $(command -v "${SIGNTOOL}") ]]; then
|
|
||||||
exitError "signtool program not found on PATH!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for f in "${SIGN_FILES[@]}"; do
|
|
||||||
if [ ! -f "$f" ]; then
|
if [ ! -f "$f" ]; then
|
||||||
exitError "File '${f}' does not exist!"
|
exitError "File '${f}' does not exist or is not a file!"
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$SIGNTOOL_KEY" && ${f: -4} == '.exe' ]]; then
|
|
||||||
logInfo "Signing file '${f}' using signtool...\n"
|
|
||||||
read -s -p "Signtool Key Password: " password
|
|
||||||
echo
|
|
||||||
"${SIGNTOOL}" sign -f "${SIGNTOOL_KEY}" -p ${password} -v -t http://timestamp.comodoca.com/authenticode ${f}
|
|
||||||
|
|
||||||
if [ 0 -ne $? ]; then
|
|
||||||
exitError "Signing failed!"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
logInfo "Signing file '${f}' using release key..."
|
logInfo "Signing file '${f}' using release key..."
|
||||||
@ -750,6 +754,143 @@ sign() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# appsign command
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
appsign() {
|
||||||
|
local sign_files=()
|
||||||
|
local signtool_key
|
||||||
|
local codesign_identity
|
||||||
|
|
||||||
|
while [ $# -ge 1 ]; do
|
||||||
|
local arg="$1"
|
||||||
|
case "$arg" in
|
||||||
|
-f|--files)
|
||||||
|
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
|
||||||
|
sign_files+=("$2")
|
||||||
|
shift
|
||||||
|
done ;;
|
||||||
|
|
||||||
|
-k|--signtool-key)
|
||||||
|
signtool_key="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-i|--identity)
|
||||||
|
codesign_identity="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-h|--help)
|
||||||
|
printUsage "appsign"
|
||||||
|
exit ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
logError "Unknown option '$arg'\n"
|
||||||
|
printUsage "appsign"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${sign_files}" ]; then
|
||||||
|
logError "Missing arguments, --files is required!\n"
|
||||||
|
printUsage "appsign"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "${sign_files[@]}"; do
|
||||||
|
if [ ! -f "${f}" ]; then
|
||||||
|
exitError "File '${f}' does not exist or is not a file!"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$(uname -s)" == "Darwin" ]; then
|
||||||
|
if [ -z "${codesign_identity}" ]; then
|
||||||
|
logError "Missing arguments, --identity is required on macOS!\n"
|
||||||
|
printUsage "appsign"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkCodesignCommandExists
|
||||||
|
checkCreateDMGCommandExists
|
||||||
|
|
||||||
|
local orig_dir="$(pwd)"
|
||||||
|
for f in "${sign_files[@]}"; do
|
||||||
|
if [[ ${f: -4} == '.dmg' ]]; then
|
||||||
|
logInfo "Unpacking disk image '${f}'..."
|
||||||
|
local tmp_dir="/tmp/KeePassXC_${RANDOM}"
|
||||||
|
mkdir -p ${tmp_dir}/{mnt,app}
|
||||||
|
hdiutil attach -quiet -noautoopen -mountpoint ${tmp_dir}/mnt "${f}"
|
||||||
|
cd ${tmp_dir}
|
||||||
|
cp -a ./mnt/KeePassXC.app ./app
|
||||||
|
hdiutil detach -quiet ${tmp_dir}/mnt
|
||||||
|
|
||||||
|
if [ ! -d ./app/KeePassXC.app ]; then
|
||||||
|
cd "${orig_dir}"
|
||||||
|
exitError "Unpacking failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Signing app using codesign..."
|
||||||
|
codesign --sign "${codesign_identity}" --verbose --deep ./app/KeePassXC.app
|
||||||
|
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
cd "${orig_dir}"
|
||||||
|
exitError "Signing failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Repacking and signing disk image..."
|
||||||
|
create-dmg ./app/KeePassXC.app
|
||||||
|
cd "${orig_dir}"
|
||||||
|
cp -f ${tmp_dir}/KeePassXC-*.dmg "${f}"
|
||||||
|
rm -Rf ${tmp_dir}
|
||||||
|
else
|
||||||
|
logInfo "Skipping non-DMG file '${f}'..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
elif [ "$(uname -o)" == "Msys" ]; then
|
||||||
|
if [ -z "${signtool_key}" ]; then
|
||||||
|
logError "Missing arguments, --signtool-key is required on Windows!\n"
|
||||||
|
printUsage "appsign"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkOsslsigncodeCommandExists
|
||||||
|
|
||||||
|
if [[ ! -f "${signtool_key}" ]]; then
|
||||||
|
exitError "Key file was not found!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -s -p "Key password: " password
|
||||||
|
echo
|
||||||
|
|
||||||
|
for f in "${sign_files[@]}"; do
|
||||||
|
if [[ ${f: -4} == '.exe' ]]; then
|
||||||
|
logInfo "Signing file '${f}' using osslsigncode..."
|
||||||
|
# output a signed exe; we have to use a different name due to osslsigntool limitations
|
||||||
|
osslsigncode sign -pkcs12 "${signtool_key}" -pass "${password}" \
|
||||||
|
-t "http://timestamp.comodoca.com/authenticode" -in "${f}" -out "${f}.signed"
|
||||||
|
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
rm -f "${f}.signed"
|
||||||
|
exitError "Signing failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# overwrite the original exe with the signed exe
|
||||||
|
mv -f "${f}.signed" "${f}"
|
||||||
|
else
|
||||||
|
logInfo "Skipping non-EXE file '${f}'..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
else
|
||||||
|
exitError "Unsupported platform for code signing!\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "All done!"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
# parse global command line
|
# parse global command line
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
@ -762,8 +903,8 @@ if [ "" == "$MODE" ]; then
|
|||||||
elif [ "help" == "$MODE" ]; then
|
elif [ "help" == "$MODE" ]; then
|
||||||
printUsage "$1"
|
printUsage "$1"
|
||||||
exit
|
exit
|
||||||
elif [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] || [ "sign" == "$MODE" ]; then
|
elif [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] || [ "gpgsign" == "$MODE" ] || [ "appsign" == "$MODE" ]; then
|
||||||
$MODE "$@"
|
${MODE} "$@"
|
||||||
else
|
else
|
||||||
printUsage "$MODE"
|
printUsage "$MODE"
|
||||||
fi
|
fi
|
||||||
|
@ -30,8 +30,8 @@ if(UNIX AND NOT APPLE)
|
|||||||
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
|
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
|
||||||
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz"
|
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz"
|
||||||
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE)
|
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE)
|
||||||
install(FILES linux/org.keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
install(FILES linux/org.keepassxc.KeePassXC.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||||
install(FILES linux/org.keepassxc.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
|
install(FILES linux/org.keepassxc.KeePassXC.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
|
||||||
install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages)
|
install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages)
|
||||||
endif(UNIX AND NOT APPLE)
|
endif(UNIX AND NOT APPLE)
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 360 B |
BIN
share/icons/application/32x32/apps/utilities-terminal.png
Normal file
BIN
share/icons/application/32x32/apps/utilities-terminal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 958 B |
BIN
share/icons/svg/utilities-terminal.svgz
Normal file
BIN
share/icons/svg/utilities-terminal.svgz
Normal file
Binary file not shown.
@ -1,5 +1,4 @@
|
|||||||
[General]
|
[General]
|
||||||
ShowToolbar=true
|
|
||||||
RememberLastDatabases=true
|
RememberLastDatabases=true
|
||||||
RememberLastKeyFiles=true
|
RememberLastKeyFiles=true
|
||||||
OpenPreviousDatabasesOnStartup=true
|
OpenPreviousDatabasesOnStartup=true
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--Copyright 2017 KeePassXC Team <team@keepassxc.org> -->
|
<!--Copyright 2017 KeePassXC Team <team@keepassxc.org> -->
|
||||||
<component type="desktop-application">
|
<component type="desktop-application">
|
||||||
<id>org.keepassxc</id>
|
<id>org.keepassxc.KeePassXC.desktop</id>
|
||||||
<name>KeePassXC</name>
|
<name>KeePassXC</name>
|
||||||
<metadata_license>CC-BY-3.0</metadata_license>
|
<metadata_license>CC-BY-3.0</metadata_license>
|
||||||
<project_license>GPL-3.0+</project_license>
|
<project_license>GPL-3.0+</project_license>
|
||||||
<icon type="stock">keepassxc</icon>
|
|
||||||
<url type="homepage">https://keepassxc.org</url>
|
|
||||||
<mimetypes>
|
<mimetypes>
|
||||||
<mimetype>application/x-keepass2</mimetype>
|
<mimetype>application/x-keepass2</mimetype>
|
||||||
</mimetypes>
|
</mimetypes>
|
||||||
<summary>Community-driven port of the Windows application “KeePass Password Safe”</summary>
|
<summary>Community-driven port of the Windows application “KeePass Password Safe”</summary>
|
||||||
|
<developer_name>KeePassXC Team</developer_name>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
KeePassXC is an application for people with extremely high demands on secure
|
KeePassXC is an application for people with extremely high demands on secure
|
||||||
@ -19,56 +18,53 @@
|
|||||||
</p>
|
</p>
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<launchable type="desktop-id">org.keepassxc.desktop</launchable>
|
<launchable type="desktop-id">org.keepassxc.KeePassXC.desktop</launchable>
|
||||||
|
|
||||||
|
<url type="homepage">https://keepassxc.org</url>
|
||||||
|
<url type="bugtracker">https://github.com/keepassxreboot/keepassxc/issues</url>
|
||||||
|
<url type="faq">https://keepassxc.org/docs#faq</url>
|
||||||
|
<url type="help">https://keepassxc.org/docs</url>
|
||||||
|
<url type="translate">https://www.transifex.com/keepassxc/keepassxc</url>
|
||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_001.png</image>
|
<image>https://keepassxc.org/images/screenshots/linux/screen_001.png</image>
|
||||||
|
<caption>Create, Import or Open Databases</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_002.png</image>
|
<image>https://keepassxc.org/images/screenshots/linux/screen_002.png</image>
|
||||||
|
<caption>Organize with Groups and Entries</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_003.png</image>
|
<image>https://keepassxc.org/images/screenshots/linux/screen_003.png</image>
|
||||||
|
<caption>Database Entry</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_004.png</image>
|
<image>https://keepassxc.org/images/screenshots/linux/screen_004.png</image>
|
||||||
</screenshot>
|
<caption>Icon Selection for Entry</caption>
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_005.png</image>
|
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_006.png</image>
|
<image>https://keepassxc.org/images/screenshots/linux/screen_006.png</image>
|
||||||
</screenshot>
|
<caption>Password Generator</caption>
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_007.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_008.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_009.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_010.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_011.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_012.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_013.png</image>
|
|
||||||
</screenshot>
|
|
||||||
<screenshot>
|
|
||||||
<image>https://keepassxc.org/images/screenshots/linux/screen_014.png</image>
|
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.2.4" date="2017-12-13">
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Prevent database corruption when locked [#1219]</li>
|
||||||
|
<li>Fixes apply button not saving new entries [#1141]</li>
|
||||||
|
<li>Switch to Consolas font on Windows for password edit [#1229]</li>
|
||||||
|
<li>Multiple fixes to AppImage deployment [#1115, #1228]</li>
|
||||||
|
<li>Fixes multiple memory leaks [#1213]</li>
|
||||||
|
<li>Resize message close to 16x16 pixels [#1253]</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="2.2.2" date="2017-10-22">
|
<release version="2.2.2" date="2017-10-22">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]</li>
|
<li>Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]</li>
|
||||||
<li>Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]</li>
|
<li>Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]</li>
|
||||||
@ -85,8 +81,10 @@
|
|||||||
<li>Fixed screen lock and Google fallback settings [#1029]</li>
|
<li>Fixed screen lock and Google fallback settings [#1029]</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release><release version="2.2.1" date="2017-10-01">
|
</release>
|
||||||
|
<release version="2.2.1" date="2017-10-01">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Corrected multiple snap issues [#934, #1011]</li>
|
<li>Corrected multiple snap issues [#934, #1011]</li>
|
||||||
<li>Corrected multiple custom icon issues [#708, #719, #994]</li>
|
<li>Corrected multiple custom icon issues [#708, #719, #994]</li>
|
||||||
@ -103,6 +101,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.2.0" date="2017-06-23">
|
<release version="2.2.0" date="2017-06-23">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Added YubiKey 2FA integration for unlocking databases [#127]</li>
|
<li>Added YubiKey 2FA integration for unlocking databases [#127]</li>
|
||||||
<li>Added TOTP support [#519]</li>
|
<li>Added TOTP support [#519]</li>
|
||||||
@ -138,6 +137,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.1.4" date="2017-04-09">
|
<release version="2.1.4" date="2017-04-09">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Bumped KeePassHTTP version to 1.8.4.2</li>
|
<li>Bumped KeePassHTTP version to 1.8.4.2</li>
|
||||||
<li>KeePassHTTP confirmation window comes to foreground [#466]</li>
|
<li>KeePassHTTP confirmation window comes to foreground [#466]</li>
|
||||||
@ -146,6 +146,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.1.3" date="2017-03-03">
|
<release version="2.1.3" date="2017-03-03">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fix possible overflow in zxcvbn library [#363]</li>
|
<li>Fix possible overflow in zxcvbn library [#363]</li>
|
||||||
<li>Revert HiDPI setting to avoid problems on laptop screens [#332]</li>
|
<li>Revert HiDPI setting to avoid problems on laptop screens [#332]</li>
|
||||||
@ -160,6 +161,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.1.2" date="2017-02-17">
|
<release version="2.1.2" date="2017-02-17">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Ask for save location when creating a new database [#302]</li>
|
<li>Ask for save location when creating a new database [#302]</li>
|
||||||
<li>Remove Libmicrohttpd dependency to clean up the code and ensure better OS X compatibility [#317, #265]</li>
|
<li>Remove Libmicrohttpd dependency to clean up the code and ensure better OS X compatibility [#317, #265]</li>
|
||||||
@ -177,6 +179,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.1.1" date="2017-02-06">
|
<release version="2.1.1" date="2017-02-06">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Enabled HTTP plugin build; plugin is disabled by default and limited to localhost [#147]</li>
|
<li>Enabled HTTP plugin build; plugin is disabled by default and limited to localhost [#147]</li>
|
||||||
<li>Escape HTML in dialog boxes [#247]</li>
|
<li>Escape HTML in dialog boxes [#247]</li>
|
||||||
@ -189,6 +192,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="2.1.0" date="2017-01-22">
|
<release version="2.1.0" date="2017-01-22">
|
||||||
<description>
|
<description>
|
||||||
|
<p>Changes included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Show unlock dialog when using autotype on a closed database [#10, #89]</li>
|
<li>Show unlock dialog when using autotype on a closed database [#10, #89]</li>
|
||||||
<li>Show different tray icon when database is locked [#37, #46]</li>
|
<li>Show different tray icon when database is locked [#37, #46]</li>
|
@ -9,6 +9,7 @@ Comment=Community-driven port of the Windows application “KeePass Password Saf
|
|||||||
Exec=keepassxc %f
|
Exec=keepassxc %f
|
||||||
TryExec=keepassxc
|
TryExec=keepassxc
|
||||||
Icon=keepassxc
|
Icon=keepassxc
|
||||||
|
StartupWMClass=keepassxc
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Version=1.0
|
Version=1.0
|
2395
share/translations/keepassx_ca.ts
Normal file
2395
share/translations/keepassx_ca.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -571,7 +571,7 @@ Sie können sie speichern oder die Sperre freigeben.</translation>
|
|||||||
Do you want to save the database before locking it?
|
Do you want to save the database before locking it?
|
||||||
Otherwise your changes are lost.</source>
|
Otherwise your changes are lost.</source>
|
||||||
<translation>Dieses Datenbank wurde geändert.
|
<translation>Dieses Datenbank wurde geändert.
|
||||||
Soll sie gespeichert werden bevor sie gesperrt wirt?
|
Soll sie gespeichert werden bevor sie gesperrt wird?
|
||||||
Anderenfalls gehen Ihre Änderungen verloren.</translation>
|
Anderenfalls gehen Ihre Änderungen verloren.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -69,7 +69,7 @@ Núcleo: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Distribución: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -250,7 +250,7 @@ Por favor seleccione si desea autorizar su acceso.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Codec</source>
|
<source>Codec</source>
|
||||||
<translation>Codificación</translation>
|
<translation>Códec</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Text is qualified by</source>
|
<source>Text is qualified by</source>
|
||||||
@ -302,11 +302,11 @@ Por favor seleccione si desea autorizar su acceso.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Original data: </source>
|
<source>Original data: </source>
|
||||||
<translation>Datos originales:</translation>
|
<translation>Dato original:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error(s) detected in CSV file !</source>
|
<source>Error(s) detected in CSV file !</source>
|
||||||
<translation>¡Se detectaron errores en el archivo CSV!</translation>
|
<translation>¡Error(es) detectado(s) en el archivo CSV!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source> more messages skipped]</source>
|
<source> more messages skipped]</source>
|
||||||
@ -728,7 +728,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Merge Request</source>
|
<source>Merge Request</source>
|
||||||
<translation>Solicitud de unión</translation>
|
<translation>Solicitud de Unión</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The database file has changed and you have unsaved changes.Do you want to merge your changes?</source>
|
<source>The database file has changed and you have unsaved changes.Do you want to merge your changes?</source>
|
||||||
@ -744,7 +744,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Are you sure you want to permanently delete everything from your recycle bin?</source>
|
<source>Are you sure you want to permanently delete everything from your recycle bin?</source>
|
||||||
<translation>¿Está seguro(a) que quiere permanentemente eliminar todo de su papelera de reciclaje?</translation>
|
<translation>¿Está seguro que quiere eliminar permanentemente todo de su papelera de reciclaje?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -795,7 +795,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select file</source>
|
<source>Select file</source>
|
||||||
<translation>Seleccionar archivo llave</translation>
|
<translation>Seleccionar archivo</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to open file</source>
|
<source>Unable to open file</source>
|
||||||
@ -817,11 +817,11 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>%n week(s)</source>
|
<source>%n week(s)</source>
|
||||||
<translation><numerusform>%n semana</numerusform><numerusform>%n semanas</numerusform></translation>
|
<translation><numerusform>%n semana</numerusform><numerusform>%n semana(s)</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>%n month(s)</source>
|
<source>%n month(s)</source>
|
||||||
<translation><numerusform>%n mes</numerusform><numerusform>%n meses</numerusform></translation>
|
<translation><numerusform>%n mes</numerusform><numerusform>%n mes(es)</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>1 year</source>
|
<source>1 year</source>
|
||||||
@ -969,7 +969,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Presets</source>
|
<source>Presets</source>
|
||||||
<translation>Predeterminado</translation>
|
<translation>Programar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Notes:</source>
|
<source>Notes:</source>
|
||||||
@ -1098,7 +1098,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom icon already exists</source>
|
<source>Custom icon already exists</source>
|
||||||
<translation type="unfinished"/>
|
<translation>El icono personalizado ya existe</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1352,7 +1352,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>El archivo de bloqueo de instancia única existente no es válido. Lanzando nueva instancia.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1483,7 +1483,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Timed one-time password</source>
|
<source>Timed one-time password</source>
|
||||||
<translation>Contraseña programada de único uso (TOTP)</translation>
|
<translation>Contraseña programada de único uso</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy &TOTP</source>
|
<source>Copy &TOTP</source>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Revision: %1</source>
|
<source>Revision: %1</source>
|
||||||
<translation>Revision: %1</translation>
|
<translation>Révision : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Libraries:</source>
|
<source>Libraries:</source>
|
||||||
@ -69,7 +69,7 @@ Kernel: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Distribution : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -477,7 +477,7 @@ Vous pouvez maintenant la sauvegarder.</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>AES: 256 Bit (default)</source>
|
<source>AES: 256 Bit (default)</source>
|
||||||
<translation>AES: 256 Bits (par défault)</translation>
|
<translation>AES : 256 Bits (par défaut)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Twofish: 256 Bit</source>
|
<source>Twofish: 256 Bit</source>
|
||||||
@ -737,7 +737,7 @@ Voulez vous l'ouvrir quand même ?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not open the new database file while attempting to autoreload this database.</source>
|
<source>Could not open the new database file while attempting to autoreload this database.</source>
|
||||||
<translation>La nouvelle base de données ne peux être ouverte pendant qu'un rafraîchissement automatique de l'actuelle est en cours.</translation>
|
<translation>La nouvelle base de données ne peut être ouverte pendant qu'un rafraîchissement automatique de l'actuelle est en cours.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Empty recycle bin?</source>
|
<source>Empty recycle bin?</source>
|
||||||
@ -1099,7 +1099,7 @@ Voulez vous l'ouvrir quand même ?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom icon already exists</source>
|
<source>Custom icon already exists</source>
|
||||||
<translation type="unfinished"/>
|
<translation>L'icône personnalisée existe déjà</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1353,7 +1353,7 @@ Il s'agit d'une migration à sens unique. Vous ne pourrez pas ouvrir l
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Le fichier de verrouillage de l’instance unique existant n’est pas valide. Lancement d'une nouvelle instance.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -69,7 +69,7 @@ Kernel: %3 %4</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Distribusi: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -69,7 +69,7 @@ CPU アーキテクチャ: %2
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>配布形式: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -69,7 +69,7 @@ CPU 아키텍처: %2
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>배포판: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1096,7 +1096,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom icon already exists</source>
|
<source>Custom icon already exists</source>
|
||||||
<translation type="unfinished"/>
|
<translation>사용자 정의 아이콘이 이미 존재함</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1350,7 +1350,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>존재하는 단일 인스턴스 잠금 파일이 잘못되었습니다. 새 인스턴스를 실행합니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -69,7 +69,7 @@ Kernel: %3 %4</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Дистрибутив: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -219,7 +219,7 @@ Please select whether you want to allow access.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Replace username and password with references</source>
|
<source>Replace username and password with references</source>
|
||||||
<translation>Заменить имя пользователя и пароль к ссылкам</translation>
|
<translation>Использовать ссылки для имени пользователя и пароля</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy history</source>
|
<source>Copy history</source>
|
||||||
@ -227,7 +227,7 @@ Please select whether you want to allow access.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Append ' - Clone' to title</source>
|
<source>Append ' - Clone' to title</source>
|
||||||
<translation>Добавить' - Клонировать' в заголовок</translation>
|
<translation>Добавить к названию « - клон»</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -579,7 +579,7 @@ Otherwise your changes are lost.</source>
|
|||||||
<message>
|
<message>
|
||||||
<source>"%1" is in edit mode.
|
<source>"%1" is in edit mode.
|
||||||
Discard changes and close anyway?</source>
|
Discard changes and close anyway?</source>
|
||||||
<translation>«%1» в режиме редактирования.
|
<translation>«%1» в режиме правки.
|
||||||
Отменить изменения и всё равно закрыть?</translation>
|
Отменить изменения и всё равно закрыть?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -741,7 +741,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Empty recycle bin?</source>
|
<source>Empty recycle bin?</source>
|
||||||
<translation>Корзина пустая?</translation>
|
<translation>Очистить корзину?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Are you sure you want to permanently delete everything from your recycle bin?</source>
|
<source>Are you sure you want to permanently delete everything from your recycle bin?</source>
|
||||||
@ -784,7 +784,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Edit entry</source>
|
<source>Edit entry</source>
|
||||||
<translation>Редактировать запись</translation>
|
<translation>Править запись</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Different passwords supplied.</source>
|
<source>Different passwords supplied.</source>
|
||||||
@ -838,7 +838,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>[PROTECTED] Press reveal to view or edit</source>
|
<source>[PROTECTED] Press reveal to view or edit</source>
|
||||||
<translation>[Защищён] Нажмите для открытия просмотра или редактирования</translation>
|
<translation>[Защищён] Нажмите для открытия просмотра или правки</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Are you sure you want to remove this attachment?</source>
|
<source>Are you sure you want to remove this attachment?</source>
|
||||||
@ -853,7 +853,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add</source>
|
<source>Add</source>
|
||||||
<translation>Добавить</translation>
|
<translation>Создать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove</source>
|
<source>Remove</source>
|
||||||
@ -946,7 +946,7 @@ Do you want to open it anyway?</source>
|
|||||||
<name>EditEntryWidgetMain</name>
|
<name>EditEntryWidgetMain</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Title:</source>
|
<source>Title:</source>
|
||||||
<translation>Заголовок:</translation>
|
<translation>Название:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Username:</source>
|
<source>Username:</source>
|
||||||
@ -997,7 +997,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Edit group</source>
|
<source>Edit group</source>
|
||||||
<translation>Редактировать группу</translation>
|
<translation>Править группу</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable</source>
|
<source>Enable</source>
|
||||||
@ -1095,22 +1095,22 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hint: You can enable Google as a fallback under Tools>Settings>Security</source>
|
<source>Hint: You can enable Google as a fallback under Tools>Settings>Security</source>
|
||||||
<translation>Подсказка: вы можете включить Google в качестве резервного копирования в меню «Инструменты»> «Настройки»> «Безопасность»</translation>
|
<translation>Подсказка: в качестве резервного варианта для получения значков сайтов возможно использовать Google. Включите этот параметр в меню «Инструменты» -> «Настройки» -> «Безопасность»</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom icon already exists</source>
|
<source>Custom icon already exists</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Пользовательский значок уже существует</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>EditWidgetProperties</name>
|
<name>EditWidgetProperties</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Created:</source>
|
<source>Created:</source>
|
||||||
<translation>Создано:</translation>
|
<translation>Создание:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Modified:</source>
|
<source>Modified:</source>
|
||||||
<translation>Изменено:</translation>
|
<translation>Изменение:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Accessed:</source>
|
<source>Accessed:</source>
|
||||||
@ -1125,7 +1125,7 @@ Do you want to open it anyway?</source>
|
|||||||
<name>Entry</name>
|
<name>Entry</name>
|
||||||
<message>
|
<message>
|
||||||
<source> - Clone</source>
|
<source> - Clone</source>
|
||||||
<translation> - Клонировать</translation>
|
<translation> - клон</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1143,7 +1143,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
<translation>Заголовок</translation>
|
<translation>Имя записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Username</source>
|
<source>Username</source>
|
||||||
@ -1162,7 +1162,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
<translation>Заголовок</translation>
|
<translation>Имя записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Username</source>
|
<source>Username</source>
|
||||||
@ -1353,7 +1353,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Запускается новый экземпляр программы, т.к. файл блокировки запуска повреждён.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1452,11 +1452,11 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Add new entry</source>
|
<source>&Add new entry</source>
|
||||||
<translation>&Добавить новую запись</translation>
|
<translation>&Создать запись</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&View/Edit entry</source>
|
<source>&View/Edit entry</source>
|
||||||
<translation>&Посмотреть/редактировать запись</translation>
|
<translation>&Открыть/править запись</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Delete entry</source>
|
<source>&Delete entry</source>
|
||||||
@ -1464,11 +1464,11 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Add new group</source>
|
<source>&Add new group</source>
|
||||||
<translation>&Добавить новую группу</translation>
|
<translation>&Создать группу</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Edit group</source>
|
<source>&Edit group</source>
|
||||||
<translation>&Редактировать группу</translation>
|
<translation>&Править группу</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Delete group</source>
|
<source>&Delete group</source>
|
||||||
@ -1524,7 +1524,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Title</source>
|
<source>&Title</source>
|
||||||
<translation>&Заголовок</translation>
|
<translation>&Имя записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&URL</source>
|
<source>&URL</source>
|
||||||
@ -1556,7 +1556,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Empty recycle bin</source>
|
<source>Empty recycle bin</source>
|
||||||
<translation>Корзина пустая</translation>
|
<translation>Очистить корзину</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Access error for config file %1</source>
|
<source>Access error for config file %1</source>
|
||||||
@ -1701,15 +1701,15 @@ Using default port 19455.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Return only best matching entries</source>
|
<source>&Return only best matching entries</source>
|
||||||
<translation>&Возврат только наиболее совпадающих записей</translation>
|
<translation>&Показывать только лучшие совпадения</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Only entries with the same scheme (http://, https://, ftp://, ...) are returned.</source>
|
<source>Only entries with the same scheme (http://, https://, ftp://, ...) are returned.</source>
|
||||||
<translation>Возвращаются только записи с той же схемой (http: //, https: //, ftp: //, ...).</translation>
|
<translation>Будут отобраны только записи с совпадающим протоколом (http://, https://, ftp://, ...).</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Match URL schemes</source>
|
<source>&Match URL schemes</source>
|
||||||
<translation>&Совпадения схем адресов</translation>
|
<translation>&Проверять протокол</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password Generator</source>
|
<source>Password Generator</source>
|
||||||
@ -1879,7 +1879,7 @@ Change them only if you know what you are doing.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
<translation>Заголовок</translation>
|
<translation>Имя записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Username</source>
|
<source>Username</source>
|
||||||
@ -2085,7 +2085,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
<name>SettingsWidgetGeneral</name>
|
<name>SettingsWidgetGeneral</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Remember last databases</source>
|
<source>Remember last databases</source>
|
||||||
<translation>Помнить последнюю базу данных</translation>
|
<translation>Запоминать последнюю базу данных</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Automatically save on exit</source>
|
<source>Automatically save on exit</source>
|
||||||
@ -2117,7 +2117,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide window to system tray when minimized</source>
|
<source>Hide window to system tray when minimized</source>
|
||||||
<translation>При сворачивании прятать окно в системный лоток</translation>
|
<translation>При сворачивании скрывать окно в системный лоток</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Load previous databases on startup</source>
|
<source>Load previous databases on startup</source>
|
||||||
@ -2129,7 +2129,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide window to system tray instead of app exit</source>
|
<source>Hide window to system tray instead of app exit</source>
|
||||||
<translation>Прятать окно в системный лоток вместо выхода</translation>
|
<translation>Скрывать окно в системный лоток вместо выхода</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Minimize window at application startup</source>
|
<source>Minimize window at application startup</source>
|
||||||
@ -2141,11 +2141,11 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remember last key files and security dongles</source>
|
<source>Remember last key files and security dongles</source>
|
||||||
<translation>Помнить последние ключевые файлы и ключи безопасности</translation>
|
<translation>Запоминать последние использованные файлы ключей и устройства</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Don't mark database as modified for non-data changes (e.g., expanding groups)</source>
|
<source>Don't mark database as modified for non-data changes (e.g., expanding groups)</source>
|
||||||
<translation>Не помечать базу данных как измененную без изменения данных (например, для расширения групп)</translation>
|
<translation>Не помечать базу данных изменённой при действиях, не связанных с изменением данных (например, при распахивании групп)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto-Type</source>
|
<source>Auto-Type</source>
|
||||||
@ -2153,7 +2153,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use entry title and URL to match windows for global Auto-Type</source>
|
<source>Use entry title and URL to match windows for global Auto-Type</source>
|
||||||
<translation>Использовать URL и заголовок записи при сопоставлении окон для глобального автоввода</translation>
|
<translation>Использовать для поиска URL и название записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Always ask before performing Auto-Type</source>
|
<source>Always ask before performing Auto-Type</source>
|
||||||
@ -2184,7 +2184,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Lock databases after inactivity of</source>
|
<source>Lock databases after inactivity of</source>
|
||||||
<translation>Заблокировать базу данных после неактивности длительностью</translation>
|
<translation>Блокировать базу данных при отсутствии активности длительностью</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show passwords in cleartext by default</source>
|
<source>Show passwords in cleartext by default</source>
|
||||||
@ -2192,7 +2192,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Lock databases after minimizing the window</source>
|
<source>Lock databases after minimizing the window</source>
|
||||||
<translation>Блокировать базу данных после сворачивания окна</translation>
|
<translation>Блокировать базу данных при сворачивания окна</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Don't require password repeat when it is visible</source>
|
<source>Don't require password repeat when it is visible</source>
|
||||||
@ -2216,7 +2216,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use Google as fallback for downloading website icons</source>
|
<source>Use Google as fallback for downloading website icons</source>
|
||||||
<translation>Использовать Google как резерв для загрузки значков веб-сайтов</translation>
|
<translation>Использовать Google в качестве резервного варианта для получения значков веб-сайтов</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -69,7 +69,7 @@ Kernel: %3 %4</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Distribution: %1</source>
|
<source>Distribution: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>散佈:%1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -196,7 +196,7 @@ Please select whether you want to allow access.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cha&llenge Response</source>
|
<source>Cha&llenge Response</source>
|
||||||
<translation type="unfinished"/>
|
<translation>挑戰回應</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Refresh</source>
|
<source>Refresh</source>
|
||||||
@ -208,7 +208,7 @@ Please select whether you want to allow access.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Changing master key failed: no YubiKey inserted.</source>
|
<source>Changing master key failed: no YubiKey inserted.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>挑戰主金鑰失敗:沒有插入 YubiKey</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -397,7 +397,7 @@ Please select whether you want to allow access.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Challenge Response:</source>
|
<source>Challenge Response:</source>
|
||||||
<translation type="unfinished"/>
|
<translation>挑戰驗證:</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1098,7 +1098,7 @@ Do you want to open it anyway?</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Custom icon already exists</source>
|
<source>Custom icon already exists</source>
|
||||||
<translation type="unfinished"/>
|
<translation>自訂圖示已經存在</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -2140,7 +2140,7 @@ give it a unique name to identify and accept it.</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remember last key files and security dongles</source>
|
<source>Remember last key files and security dongles</source>
|
||||||
<translation>記住最近的金鑰檔案與安全加密狗</translation>
|
<translation>記住最近的金鑰檔案與安全鎖</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Don't mark database as modified for non-data changes (e.g., expanding groups)</source>
|
<source>Don't mark database as modified for non-data changes (e.g., expanding groups)</source>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: keepassxc
|
name: keepassxc
|
||||||
version: 2.2.2
|
version: 2.2.4
|
||||||
grade: stable
|
grade: stable
|
||||||
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
||||||
description: |
|
description: |
|
||||||
@ -12,7 +12,7 @@ apps:
|
|||||||
keepassxc:
|
keepassxc:
|
||||||
command: desktop-launch keepassxc
|
command: desktop-launch keepassxc
|
||||||
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb]
|
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb]
|
||||||
desktop: usr/share/applications/org.keepassxc.desktop
|
desktop: usr/share/applications/org.keepassxc.KeePassXC.desktop
|
||||||
cli:
|
cli:
|
||||||
command: keepassxc-cli
|
command: keepassxc-cli
|
||||||
plugs: [gsettings, home, removable-media, raw-usb]
|
plugs: [gsettings, home, removable-media, raw-usb]
|
||||||
@ -41,8 +41,10 @@ parts:
|
|||||||
- libxtst-dev
|
- libxtst-dev
|
||||||
- libyubikey-dev
|
- libyubikey-dev
|
||||||
- libykpers-1-dev
|
- libykpers-1-dev
|
||||||
|
stage-packages:
|
||||||
|
- dbus
|
||||||
install: |
|
install: |
|
||||||
sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.desktop
|
sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.KeePassXC.desktop
|
||||||
after: [desktop-qt5]
|
after: [desktop-qt5]
|
||||||
|
|
||||||
# Redefine desktop-qt5 stage packages to work with Ubuntu 17.04
|
# Redefine desktop-qt5 stage packages to work with Ubuntu 17.04
|
||||||
@ -52,19 +54,12 @@ parts:
|
|||||||
- ttf-ubuntu-font-family
|
- ttf-ubuntu-font-family
|
||||||
- dmz-cursor-theme
|
- dmz-cursor-theme
|
||||||
- light-themes
|
- light-themes
|
||||||
|
- adwaita-icon-theme
|
||||||
|
- gnome-themes-standard
|
||||||
- shared-mime-info
|
- shared-mime-info
|
||||||
- libqt5gui5
|
- libqt5gui5
|
||||||
- libgdk-pixbuf2.0-0
|
- libgdk-pixbuf2.0-0
|
||||||
- libqt5svg5 # for loading icon themes which are svg
|
- libqt5svg5 # for loading icon themes which are svg
|
||||||
- locales-all
|
- locales-all
|
||||||
|
- xdg-user-dirs
|
||||||
|
|
||||||
# Overcome limitation in snapd to support URL loading (CTRL+U)
|
|
||||||
# client needs to install "snapd-xdg-open" on their system
|
|
||||||
snapd-xdg-open:
|
|
||||||
source: https://github.com/ubuntu-core/snapd-xdg-open.git
|
|
||||||
source-depth: 1
|
|
||||||
plugin: nil
|
|
||||||
install: |
|
|
||||||
install -D -t $SNAPCRAFT_PART_INSTALL/usr/bin/ data/xdg-open
|
|
||||||
stage-packages:
|
|
||||||
- dbus
|
|
||||||
|
@ -105,6 +105,7 @@ set(keepassx_SOURCES
|
|||||||
gui/EditWidgetIcons.cpp
|
gui/EditWidgetIcons.cpp
|
||||||
gui/EditWidgetProperties.cpp
|
gui/EditWidgetProperties.cpp
|
||||||
gui/FileDialog.cpp
|
gui/FileDialog.cpp
|
||||||
|
gui/Font.cpp
|
||||||
gui/IconModels.cpp
|
gui/IconModels.cpp
|
||||||
gui/KeePass1OpenWidget.cpp
|
gui/KeePass1OpenWidget.cpp
|
||||||
gui/KMessageWidget.cpp
|
gui/KMessageWidget.cpp
|
||||||
@ -155,6 +156,8 @@ if(APPLE)
|
|||||||
set(keepassx_SOURCES ${keepassx_SOURCES}
|
set(keepassx_SOURCES ${keepassx_SOURCES}
|
||||||
core/ScreenLockListenerMac.h
|
core/ScreenLockListenerMac.h
|
||||||
core/ScreenLockListenerMac.cpp
|
core/ScreenLockListenerMac.cpp
|
||||||
|
core/MacPasteboard.h
|
||||||
|
core/MacPasteboard.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
@ -177,6 +180,7 @@ set(keepassx_SOURCES_MAINEXE
|
|||||||
add_feature_info(AutoType WITH_XC_AUTOTYPE "Automatic password typing")
|
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_subdirectory(http)
|
add_subdirectory(http)
|
||||||
if(WITH_XC_HTTP)
|
if(WITH_XC_HTTP)
|
||||||
@ -186,6 +190,11 @@ endif()
|
|||||||
add_subdirectory(autotype)
|
add_subdirectory(autotype)
|
||||||
add_subdirectory(cli)
|
add_subdirectory(cli)
|
||||||
|
|
||||||
|
add_subdirectory(sshagent)
|
||||||
|
if(WITH_XC_SSHAGENT)
|
||||||
|
set(sshagent_LIB sshagent)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(autotype_SOURCES
|
set(autotype_SOURCES
|
||||||
core/Tools.cpp
|
core/Tools.cpp
|
||||||
autotype/AutoType.cpp
|
autotype/AutoType.cpp
|
||||||
@ -222,6 +231,7 @@ set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUIL
|
|||||||
target_link_libraries(keepassx_core
|
target_link_libraries(keepassx_core
|
||||||
${keepasshttp_LIB}
|
${keepasshttp_LIB}
|
||||||
${autotype_LIB}
|
${autotype_LIB}
|
||||||
|
${sshagent_LIB}
|
||||||
${YUBIKEY_LIBRARIES}
|
${YUBIKEY_LIBRARIES}
|
||||||
${ZXCVBN_LIBRARIES}
|
${ZXCVBN_LIBRARIES}
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
|
@ -256,6 +256,13 @@ void AutoType::resetInAutoType()
|
|||||||
m_inAutoType = false;
|
m_inAutoType = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoType::raiseWindow()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
m_plugin->raiseOwnWindow();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void AutoType::unloadPlugin()
|
void AutoType::unloadPlugin()
|
||||||
{
|
{
|
||||||
if (m_executor) {
|
if (m_executor) {
|
||||||
|
@ -51,6 +51,7 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void performGlobalAutoType(const QList<Database*>& dbList);
|
void performGlobalAutoType(const QList<Database*>& dbList);
|
||||||
|
void raiseWindow();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void globalShortcutTriggered();
|
void globalShortcutTriggered();
|
||||||
|
@ -22,8 +22,8 @@ set(cli_SOURCES
|
|||||||
Command.h
|
Command.h
|
||||||
Edit.cpp
|
Edit.cpp
|
||||||
Edit.h
|
Edit.h
|
||||||
EntropyMeter.cpp
|
Estimate.cpp
|
||||||
EntropyMeter.h
|
Estimate.h
|
||||||
Extract.cpp
|
Extract.cpp
|
||||||
Extract.h
|
Extract.h
|
||||||
List.cpp
|
List.cpp
|
||||||
|
@ -55,9 +55,7 @@ int Clip::execute(QStringList arguments)
|
|||||||
parser.addOption(keyFile);
|
parser.addOption(keyFile);
|
||||||
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip."));
|
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip."));
|
||||||
parser.addPositionalArgument(
|
parser.addPositionalArgument(
|
||||||
"timeout",
|
"timeout", QObject::tr("Timeout in seconds before clearing the clipboard."), QString("[timeout]"));
|
||||||
QObject::tr("Timeout in seconds before clearing the clipboard."),
|
|
||||||
QString("[timeout]"));
|
|
||||||
parser.process(arguments);
|
parser.process(arguments);
|
||||||
|
|
||||||
const QStringList args = parser.positionalArguments();
|
const QStringList args = parser.positionalArguments();
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
|
||||||
#include "Add.h"
|
#include "Add.h"
|
||||||
#include "Edit.h"
|
|
||||||
#include "Clip.h"
|
#include "Clip.h"
|
||||||
#include "EntropyMeter.h"
|
#include "Edit.h"
|
||||||
|
#include "Estimate.h"
|
||||||
#include "Extract.h"
|
#include "Extract.h"
|
||||||
#include "List.h"
|
#include "List.h"
|
||||||
#include "Locate.h"
|
#include "Locate.h"
|
||||||
@ -62,7 +62,7 @@ void populateCommands()
|
|||||||
commands.insert(QString("add"), new Add());
|
commands.insert(QString("add"), new Add());
|
||||||
commands.insert(QString("clip"), new Clip());
|
commands.insert(QString("clip"), new Clip());
|
||||||
commands.insert(QString("edit"), new Edit());
|
commands.insert(QString("edit"), new Edit());
|
||||||
commands.insert(QString("entropy-meter"), new EntropyMeter());
|
commands.insert(QString("estimate"), new Estimate());
|
||||||
commands.insert(QString("extract"), new Extract());
|
commands.insert(QString("extract"), new Extract());
|
||||||
commands.insert(QString("locate"), new Locate());
|
commands.insert(QString("locate"), new Locate());
|
||||||
commands.insert(QString("ls"), new List());
|
commands.insert(QString("ls"), new List());
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "EntropyMeter.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <zxcvbn.h>
|
|
||||||
|
|
||||||
/* For pre-compiled headers under windows */
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
#include "stdafx.h"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EntropyMeter::EntropyMeter()
|
|
||||||
{
|
|
||||||
this->name = QString("entropy-meter");
|
|
||||||
this->description = QObject::tr("Calculate password entropy.");
|
|
||||||
}
|
|
||||||
|
|
||||||
EntropyMeter::~EntropyMeter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void calculate(const char *pwd, int advanced)
|
|
||||||
{
|
|
||||||
double e;
|
|
||||||
int len = strlen(pwd);
|
|
||||||
if (advanced == 0){
|
|
||||||
e = ZxcvbnMatch(pwd, 0, 0);
|
|
||||||
printf("Pass '%s' \tLength %d\tEntropy %.3f\tLog10 %.3f\n", pwd, len, e, e * 0.301029996);
|
|
||||||
} else {
|
|
||||||
int ChkLen;
|
|
||||||
ZxcMatch_t *info, *p;
|
|
||||||
double m = 0.0;
|
|
||||||
e = ZxcvbnMatch(pwd, 0, &info);
|
|
||||||
for(p = info; p; p = p->Next) {
|
|
||||||
m += p->Entrpy;
|
|
||||||
}
|
|
||||||
m = e - m;
|
|
||||||
printf("Pass '%s' \tLength %d\tEntropy %.3f\tLog10 %.3f\n Multi-word extra bits %.1f\n", pwd, len, e, e * 0.301029996, m);
|
|
||||||
p = info;
|
|
||||||
ChkLen = 0;
|
|
||||||
while(p) {
|
|
||||||
int n;
|
|
||||||
switch(static_cast<int>(p->Type))
|
|
||||||
{
|
|
||||||
case BRUTE_MATCH: printf(" Type: Bruteforce "); break;
|
|
||||||
case DICTIONARY_MATCH: printf(" Type: Dictionary "); break;
|
|
||||||
case DICT_LEET_MATCH: printf(" Type: Dict+Leet "); break;
|
|
||||||
case USER_MATCH: printf(" Type: User Words "); break;
|
|
||||||
case USER_LEET_MATCH: printf(" Type: User+Leet "); break;
|
|
||||||
case REPEATS_MATCH: printf(" Type: Repeated "); break;
|
|
||||||
case SEQUENCE_MATCH: printf(" Type: Sequence "); break;
|
|
||||||
case SPATIAL_MATCH: printf(" Type: Spatial "); break;
|
|
||||||
case DATE_MATCH: printf(" Type: Date "); break;
|
|
||||||
case BRUTE_MATCH+MULTIPLE_MATCH: printf(" Type: Bruteforce(Rep)"); break;
|
|
||||||
case DICTIONARY_MATCH+MULTIPLE_MATCH: printf(" Type: Dictionary(Rep)"); break;
|
|
||||||
case DICT_LEET_MATCH+MULTIPLE_MATCH: printf(" Type: Dict+Leet(Rep) "); break;
|
|
||||||
case USER_MATCH+MULTIPLE_MATCH: printf(" Type: User Words(Rep)"); break;
|
|
||||||
case USER_LEET_MATCH+MULTIPLE_MATCH: printf(" Type: User+Leet(Rep) "); break;
|
|
||||||
case REPEATS_MATCH+MULTIPLE_MATCH: printf(" Type: Repeated(Rep) "); break;
|
|
||||||
case SEQUENCE_MATCH+MULTIPLE_MATCH: printf(" Type: Sequence(Rep) "); break;
|
|
||||||
case SPATIAL_MATCH+MULTIPLE_MATCH: printf(" Type: Spatial(Rep) "); break;
|
|
||||||
case DATE_MATCH+MULTIPLE_MATCH: printf(" Type: Date(Rep) "); break;
|
|
||||||
|
|
||||||
default: printf(" Type: Unknown%d ", p->Type); break;
|
|
||||||
}
|
|
||||||
ChkLen += p->Length;
|
|
||||||
printf(" Length %d Entropy %6.3f (%.2f) ", p->Length, p->Entrpy, p->Entrpy * 0.301029996);
|
|
||||||
for(n = 0; n < p->Length; ++n, ++pwd) {
|
|
||||||
printf("%c", *pwd);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
p = p->Next;
|
|
||||||
}
|
|
||||||
ZxcvbnFreeInfo(info);
|
|
||||||
if (ChkLen != len)
|
|
||||||
printf("*** Password length (%d) != sum of length of parts (%d) ***\n", len, ChkLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int EntropyMeter::execute(QStringList arguments)
|
|
||||||
{
|
|
||||||
printf("KeePassXC Entropy Meter, based on zxcvbn-c.\nEnter your password below or pass it as argv\n");
|
|
||||||
printf(" Usage: entropy-meter [-a] [pwd1 pwd2 ...]\n> ");
|
|
||||||
int i, advanced = 0;
|
|
||||||
if (arguments.size() > 1 && arguments.at(1) == "-a")
|
|
||||||
{
|
|
||||||
advanced = 1;
|
|
||||||
arguments.removeAt(1);
|
|
||||||
}
|
|
||||||
i = 1;
|
|
||||||
if (i >= arguments.size())
|
|
||||||
{
|
|
||||||
/* No test passwords on command line, so get them from stdin */
|
|
||||||
char line[500];
|
|
||||||
while(fgets(line, sizeof line, stdin))
|
|
||||||
{
|
|
||||||
/* Drop the trailing newline character */
|
|
||||||
for(i = 0; i < static_cast<int>(sizeof line - 1); ++i)
|
|
||||||
{
|
|
||||||
if (line[i] < ' ')
|
|
||||||
{
|
|
||||||
line[i] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line[0]) {
|
|
||||||
calculate(line,advanced);
|
|
||||||
printf("> ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Do the test passwords on the command line */
|
|
||||||
for(; i < arguments.size(); ++i)
|
|
||||||
{
|
|
||||||
calculate(arguments.at(i).toLatin1(), advanced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
170
src/cli/Estimate.cpp
Normal file
170
src/cli/Estimate.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Estimate.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <zxcvbn.h>
|
||||||
|
|
||||||
|
/* For pre-compiled headers under windows */
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef __MINGW32__
|
||||||
|
#include "stdafx.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Estimate::Estimate()
|
||||||
|
{
|
||||||
|
this->name = QString("estimate");
|
||||||
|
this->description = QObject::tr("Estimate the entropy of a password.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Estimate::~Estimate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void estimate(const char* pwd, bool advanced)
|
||||||
|
{
|
||||||
|
double e;
|
||||||
|
int len = strlen(pwd);
|
||||||
|
if (!advanced) {
|
||||||
|
e = ZxcvbnMatch(pwd, 0, 0);
|
||||||
|
printf("Length %d\tEntropy %.3f\tLog10 %.3f\n", len, e, e * 0.301029996);
|
||||||
|
} else {
|
||||||
|
int ChkLen;
|
||||||
|
ZxcMatch_t *info, *p;
|
||||||
|
double m = 0.0;
|
||||||
|
e = ZxcvbnMatch(pwd, 0, &info);
|
||||||
|
for (p = info; p; p = p->Next) {
|
||||||
|
m += p->Entrpy;
|
||||||
|
}
|
||||||
|
m = e - m;
|
||||||
|
printf("Length %d\tEntropy %.3f\tLog10 %.3f\n Multi-word extra bits %.1f\n", len, e, e * 0.301029996, m);
|
||||||
|
p = info;
|
||||||
|
ChkLen = 0;
|
||||||
|
while (p) {
|
||||||
|
int n;
|
||||||
|
switch (static_cast<int>(p->Type)) {
|
||||||
|
case BRUTE_MATCH:
|
||||||
|
printf(" Type: Bruteforce ");
|
||||||
|
break;
|
||||||
|
case DICTIONARY_MATCH:
|
||||||
|
printf(" Type: Dictionary ");
|
||||||
|
break;
|
||||||
|
case DICT_LEET_MATCH:
|
||||||
|
printf(" Type: Dict+Leet ");
|
||||||
|
break;
|
||||||
|
case USER_MATCH:
|
||||||
|
printf(" Type: User Words ");
|
||||||
|
break;
|
||||||
|
case USER_LEET_MATCH:
|
||||||
|
printf(" Type: User+Leet ");
|
||||||
|
break;
|
||||||
|
case REPEATS_MATCH:
|
||||||
|
printf(" Type: Repeated ");
|
||||||
|
break;
|
||||||
|
case SEQUENCE_MATCH:
|
||||||
|
printf(" Type: Sequence ");
|
||||||
|
break;
|
||||||
|
case SPATIAL_MATCH:
|
||||||
|
printf(" Type: Spatial ");
|
||||||
|
break;
|
||||||
|
case DATE_MATCH:
|
||||||
|
printf(" Type: Date ");
|
||||||
|
break;
|
||||||
|
case BRUTE_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Bruteforce(Rep)");
|
||||||
|
break;
|
||||||
|
case DICTIONARY_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Dictionary(Rep)");
|
||||||
|
break;
|
||||||
|
case DICT_LEET_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Dict+Leet(Rep) ");
|
||||||
|
break;
|
||||||
|
case USER_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: User Words(Rep)");
|
||||||
|
break;
|
||||||
|
case USER_LEET_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: User+Leet(Rep) ");
|
||||||
|
break;
|
||||||
|
case REPEATS_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Repeated(Rep) ");
|
||||||
|
break;
|
||||||
|
case SEQUENCE_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Sequence(Rep) ");
|
||||||
|
break;
|
||||||
|
case SPATIAL_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Spatial(Rep) ");
|
||||||
|
break;
|
||||||
|
case DATE_MATCH + MULTIPLE_MATCH:
|
||||||
|
printf(" Type: Date(Rep) ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf(" Type: Unknown%d ", p->Type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ChkLen += p->Length;
|
||||||
|
printf(" Length %d Entropy %6.3f (%.2f) ", p->Length, p->Entrpy, p->Entrpy * 0.301029996);
|
||||||
|
for (n = 0; n < p->Length; ++n, ++pwd) {
|
||||||
|
printf("%c", *pwd);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
p = p->Next;
|
||||||
|
}
|
||||||
|
ZxcvbnFreeInfo(info);
|
||||||
|
if (ChkLen != len) {
|
||||||
|
printf("*** Password length (%d) != sum of length of parts (%d) ***\n", len, ChkLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Estimate::execute(QStringList arguments)
|
||||||
|
{
|
||||||
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
|
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription(this->description);
|
||||||
|
parser.addPositionalArgument("password", QObject::tr("Password for which to estimate the entropy."), "[password]");
|
||||||
|
QCommandLineOption advancedOption(QStringList() << "a"
|
||||||
|
<< "advanced",
|
||||||
|
QObject::tr("Perform advanced analysis on the password."));
|
||||||
|
parser.addOption(advancedOption);
|
||||||
|
parser.process(arguments);
|
||||||
|
|
||||||
|
const QStringList args = parser.positionalArguments();
|
||||||
|
if (args.size() > 1) {
|
||||||
|
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString password;
|
||||||
|
if (args.size() == 1) {
|
||||||
|
password = args.at(0);
|
||||||
|
} else {
|
||||||
|
password = inputTextStream.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
estimate(password.toLatin1(), parser.isSet(advancedOption));
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
@ -15,17 +15,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef KEEPASSXC_ENTROPYMETER_H
|
#ifndef KEEPASSXC_ESTIMATE_H
|
||||||
#define KEEPASSXC_ENTROPYMETER_H
|
#define KEEPASSXC_ESTIMATE_H
|
||||||
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
|
||||||
class EntropyMeter : public Command
|
class Estimate : public Command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EntropyMeter();
|
Estimate();
|
||||||
~EntropyMeter();
|
~Estimate();
|
||||||
int execute(QStringList arguments);
|
int execute(QStringList arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_ENTROPYMETER_H
|
#endif // KEEPASSXC_ESTIMATE_H
|
@ -85,7 +85,6 @@ int Extract::execute(QStringList arguments)
|
|||||||
compositeKey.addKey(fileKey);
|
compositeKey.addKey(fileKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString databaseFilename = args.at(0);
|
QString databaseFilename = args.at(0);
|
||||||
QFile dbFile(databaseFilename);
|
QFile dbFile(databaseFilename);
|
||||||
if (!dbFile.exists()) {
|
if (!dbFile.exists()) {
|
||||||
|
@ -44,8 +44,7 @@ int List::execute(QStringList arguments)
|
|||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(this->description);
|
parser.setApplicationDescription(this->description);
|
||||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||||
parser.addPositionalArgument(
|
parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), QString("[group]"));
|
||||||
"group", QObject::tr("Path of the group to list. Default is /"), QString("[group]"));
|
|
||||||
QCommandLineOption keyFile(QStringList() << "k"
|
QCommandLineOption keyFile(QStringList() << "k"
|
||||||
<< "key-file",
|
<< "key-file",
|
||||||
QObject::tr("Key file of the database."),
|
QObject::tr("Key file of the database."),
|
||||||
|
@ -83,5 +83,4 @@ int Locate::locateEntry(Database* database, QString searchTerm)
|
|||||||
outputTextStream << result << endl;
|
outputTextStream << result << endl;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,7 @@ int Merge::execute(QStringList arguments)
|
|||||||
parser.addPositionalArgument("database1", QObject::tr("Path of the database to merge into."));
|
parser.addPositionalArgument("database1", QObject::tr("Path of the database to merge into."));
|
||||||
parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from."));
|
parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from."));
|
||||||
|
|
||||||
QCommandLineOption samePasswordOption(
|
QCommandLineOption samePasswordOption(QStringList() << "s"
|
||||||
QStringList() << "s"
|
|
||||||
<< "same-credentials",
|
<< "same-credentials",
|
||||||
QObject::tr("Use the same credentials for both database files."));
|
QObject::tr("Use the same credentials for both database files."));
|
||||||
|
|
||||||
@ -68,7 +67,6 @@ int Merge::execute(QStringList arguments)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Database* db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
Database* db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||||
if (db1 == nullptr) {
|
if (db1 == nullptr) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
@ -49,6 +49,13 @@ int Show::execute(QStringList arguments)
|
|||||||
QObject::tr("Key file of the database."),
|
QObject::tr("Key file of the database."),
|
||||||
QObject::tr("path"));
|
QObject::tr("path"));
|
||||||
parser.addOption(keyFile);
|
parser.addOption(keyFile);
|
||||||
|
QCommandLineOption attributes(QStringList() << "a"
|
||||||
|
<< "attributes",
|
||||||
|
QObject::tr("Names of the attributes to show. "
|
||||||
|
"This option can be specified more than once, with each attribute shown one-per-line in the given order. "
|
||||||
|
"If no attributes are specified, a summary of the default attributes is given."),
|
||||||
|
QObject::tr("attribute"));
|
||||||
|
parser.addOption(attributes);
|
||||||
parser.addPositionalArgument("entry", QObject::tr("Name of the entry to show."));
|
parser.addPositionalArgument("entry", QObject::tr("Name of the entry to show."));
|
||||||
parser.process(arguments);
|
parser.process(arguments);
|
||||||
|
|
||||||
@ -63,10 +70,10 @@ int Show::execute(QStringList arguments)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->showEntry(db, args.at(1));
|
return this->showEntry(db, parser.values(attributes), args.at(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Show::showEntry(Database* database, QString entryPath)
|
int Show::showEntry(Database* database, QStringList attributes, QString entryPath)
|
||||||
{
|
{
|
||||||
|
|
||||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
@ -78,10 +85,24 @@ int Show::showEntry(Database* database, QString entryPath)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
outputTextStream << " title: " << entry->title() << endl;
|
// If no attributes specified, output the default attribute set.
|
||||||
outputTextStream << "username: " << entry->username() << endl;
|
bool showAttributeNames = attributes.isEmpty();
|
||||||
outputTextStream << "password: " << entry->password() << endl;
|
if (attributes.isEmpty()) {
|
||||||
outputTextStream << " URL: " << entry->url() << endl;
|
attributes = EntryAttributes::DefaultAttributes;
|
||||||
outputTextStream << " Notes: " << entry->notes() << endl;
|
}
|
||||||
return EXIT_SUCCESS;
|
|
||||||
|
// Iterate over the attributes and output them line-by-line.
|
||||||
|
bool sawUnknownAttribute = false;
|
||||||
|
for (QString attribute : attributes) {
|
||||||
|
if (!entry->attributes()->contains(attribute)) {
|
||||||
|
sawUnknownAttribute = true;
|
||||||
|
qCritical("ERROR: unknown attribute '%s'.", qPrintable(attribute));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (showAttributeNames) {
|
||||||
|
outputTextStream << attribute << ": ";
|
||||||
|
}
|
||||||
|
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl;
|
||||||
|
}
|
||||||
|
return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public:
|
|||||||
Show();
|
Show();
|
||||||
~Show();
|
~Show();
|
||||||
int execute(QStringList arguments);
|
int execute(QStringList arguments);
|
||||||
int showEntry(Database* database, QString entryPath);
|
int showEntry(Database* database, QStringList attributes, QString entryPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_SHOW_H
|
#endif // KEEPASSXC_SHOW_H
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
|
|
||||||
void Utils::setStdinEcho(bool enable = true)
|
void Utils::setStdinEcho(bool enable = true)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -22,8 +22,8 @@ Copies the password of a database entry to the clipboard. If multiple entries wi
|
|||||||
.IP "edit [options] <database> <entry>"
|
.IP "edit [options] <database> <entry>"
|
||||||
Edits a database entry. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option).
|
Edits a database entry. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option).
|
||||||
|
|
||||||
.IP "entropy-meter [-a pwd1 pwd2 ...]"
|
.IP "estimate [options] [password]"
|
||||||
Calculates the entropy of a single, or multiple passwords specified using the \fI-a\fP option. If no passwords are specified, the program will run in interactive mode and prompt the user to enter a password.
|
Estimates the entropy of a password. The password to estimate can be provided as a positional argument, or using the standard input.
|
||||||
|
|
||||||
.IP "extract [options] <database>"
|
.IP "extract [options] <database>"
|
||||||
Extracts and prints the contents of a database to standard output in XML format.
|
Extracts and prints the contents of a database to standard output in XML format.
|
||||||
@ -90,6 +90,20 @@ Specify the length of the password to generate.
|
|||||||
Specify the title of the entry.
|
Specify the title of the entry.
|
||||||
|
|
||||||
|
|
||||||
|
.SS "Estimate options"
|
||||||
|
|
||||||
|
.IP "-a, --advanced"
|
||||||
|
Perform advanced analysis on the password.
|
||||||
|
|
||||||
|
|
||||||
|
.SS "Show options"
|
||||||
|
|
||||||
|
.IP "-a, --attributes <attribute>..."
|
||||||
|
Names of the attributes to show. This option can be specified more than once,
|
||||||
|
with each attribute shown one-per-line in the given order. If no attributes are
|
||||||
|
specified, a summary of the default attributes is given.
|
||||||
|
|
||||||
|
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#cmakedefine WITH_XC_HTTP
|
#cmakedefine WITH_XC_HTTP
|
||||||
#cmakedefine WITH_XC_AUTOTYPE
|
#cmakedefine WITH_XC_AUTOTYPE
|
||||||
#cmakedefine WITH_XC_YUBIKEY
|
#cmakedefine WITH_XC_YUBIKEY
|
||||||
|
#cmakedefine WITH_XC_SSHAGENT
|
||||||
|
|
||||||
#cmakedefine KEEPASSXC_DIST
|
#cmakedefine KEEPASSXC_DIST
|
||||||
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
|
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
|
||||||
|
@ -52,9 +52,10 @@ QVariant Base32::decode(const QByteArray& encodedData)
|
|||||||
|
|
||||||
int nPads = 0;
|
int nPads = 0;
|
||||||
for (int i = -1; i > -7; --i) {
|
for (int i = -1; i > -7; --i) {
|
||||||
if ('=' == encodedData[encodedData.size() + i])
|
if ('=' == encodedData[encodedData.size() + i]) {
|
||||||
++nPads;
|
++nPads;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int specialOffset;
|
int specialOffset;
|
||||||
int nSpecialBytes;
|
int nSpecialBytes;
|
||||||
@ -95,11 +96,12 @@ QVariant Base32::decode(const QByteArray& encodedData)
|
|||||||
int nQuantumBytes = 5;
|
int nQuantumBytes = 5;
|
||||||
|
|
||||||
for (int n = 0; n < 8; ++n) {
|
for (int n = 0; n < 8; ++n) {
|
||||||
quint8 ch = static_cast<quint8>(encodedData[i++]);
|
auto ch = static_cast<quint8>(encodedData[i++]);
|
||||||
if ((ASCII_A <= ch && ch <= ASCII_Z) || (ASCII_a <= ch && ch <= ASCII_z)) {
|
if ((ASCII_A <= ch && ch <= ASCII_Z) || (ASCII_a <= ch && ch <= ASCII_z)) {
|
||||||
ch -= ASCII_A;
|
ch -= ASCII_A;
|
||||||
if (ch >= ALPH_POS_2)
|
if (ch >= ALPH_POS_2) {
|
||||||
ch -= ASCII_a - ASCII_A;
|
ch -= ASCII_a - ASCII_A;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ASCII_2 <= ch && ch <= ASCII_7) {
|
if (ASCII_2 <= ch && ch <= ASCII_7) {
|
||||||
ch -= ASCII_2;
|
ch -= ASCII_2;
|
||||||
@ -147,7 +149,7 @@ QByteArray Base32::encode(const QByteArray& data)
|
|||||||
const int rBits = nBits % 40; // in {0, 8, 16, 24, 32}
|
const int rBits = nBits % 40; // in {0, 8, 16, 24, 32}
|
||||||
const int nQuanta = nBits / 40 + (rBits > 0 ? 1 : 0);
|
const int nQuanta = nBits / 40 + (rBits > 0 ? 1 : 0);
|
||||||
const int nBytes = nQuanta * 8;
|
const int nBytes = nQuanta * 8;
|
||||||
QByteArray encodedData(nQuanta * 8, Qt::Uninitialized);
|
QByteArray encodedData(nBytes, Qt::Uninitialized);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int o = 0;
|
int o = 0;
|
||||||
@ -166,6 +168,7 @@ QByteArray Base32::encode(const QByteArray& data)
|
|||||||
int index;
|
int index;
|
||||||
for (n = 35; n >= 0; n -= 5) {
|
for (n = 35; n >= 0; n -= 5) {
|
||||||
index = (quantum & mask) >> n;
|
index = (quantum & mask) >> n;
|
||||||
|
Q_ASSERT(0 <= index && index <= 31);
|
||||||
encodedData[o++] = alphabet[index];
|
encodedData[o++] = alphabet[index];
|
||||||
mask >>= 5;
|
mask >>= 5;
|
||||||
}
|
}
|
||||||
@ -173,10 +176,11 @@ QByteArray Base32::encode(const QByteArray& data)
|
|||||||
|
|
||||||
// < 40-bits of input at final input group
|
// < 40-bits of input at final input group
|
||||||
if (i < data.size()) {
|
if (i < data.size()) {
|
||||||
Q_ASSERT(rBits > 0);
|
Q_ASSERT(8 <= rBits && rBits <= 32);
|
||||||
quantum = 0;
|
quantum = 0;
|
||||||
for (n = rBits - 8; n >= 0; n -= 8)
|
for (n = rBits - 8; n >= 0; n -= 8) {
|
||||||
quantum |= static_cast<quint64>(data[i++]) << n;
|
quantum |= static_cast<quint64>(data[i++]) << n;
|
||||||
|
}
|
||||||
|
|
||||||
switch (rBits) {
|
switch (rBits) {
|
||||||
case 8: // expand to 10 bits
|
case 8: // expand to 10 bits
|
||||||
@ -195,7 +199,7 @@ QByteArray Base32::encode(const QByteArray& data)
|
|||||||
n = 20;
|
n = 20;
|
||||||
break;
|
break;
|
||||||
default: // expand to 35 bits
|
default: // expand to 35 bits
|
||||||
Q_ASSERT(rBits == 32);
|
Q_ASSERT(32 == rBits);
|
||||||
quantum <<= 3;
|
quantum <<= 3;
|
||||||
mask = MASK_35BIT;
|
mask = MASK_35BIT;
|
||||||
n = 30;
|
n = 30;
|
||||||
@ -203,6 +207,7 @@ QByteArray Base32::encode(const QByteArray& data)
|
|||||||
|
|
||||||
while (n >= 0) {
|
while (n >= 0) {
|
||||||
int index = (quantum & mask) >> n;
|
int index = (quantum & mask) >> n;
|
||||||
|
Q_ASSERT(0 <= index && index <= 31);
|
||||||
encodedData[o++] = alphabet[index];
|
encodedData[o++] = alphabet[index];
|
||||||
mask >>= 5;
|
mask >>= 5;
|
||||||
n -= 5;
|
n -= 5;
|
||||||
|
@ -114,7 +114,6 @@ void Config::init(const QString& fileName)
|
|||||||
m_defaults.insert("AutoSaveAfterEveryChange", false);
|
m_defaults.insert("AutoSaveAfterEveryChange", false);
|
||||||
m_defaults.insert("AutoReloadOnChange", true);
|
m_defaults.insert("AutoReloadOnChange", true);
|
||||||
m_defaults.insert("AutoSaveOnExit", false);
|
m_defaults.insert("AutoSaveOnExit", false);
|
||||||
m_defaults.insert("ShowToolbar", true);
|
|
||||||
m_defaults.insert("SearchLimitGroup", false);
|
m_defaults.insert("SearchLimitGroup", false);
|
||||||
m_defaults.insert("MinimizeOnCopy", false);
|
m_defaults.insert("MinimizeOnCopy", false);
|
||||||
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
||||||
|
@ -95,10 +95,15 @@ const Metadata* Database::metadata() const
|
|||||||
|
|
||||||
Entry* Database::resolveEntry(const Uuid& uuid)
|
Entry* Database::resolveEntry(const Uuid& uuid)
|
||||||
{
|
{
|
||||||
return recFindEntry(uuid, m_rootGroup);
|
return findEntryRecursive(uuid, m_rootGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceType)
|
||||||
|
{
|
||||||
|
return findEntryRecursive(text, referenceType, m_rootGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* Database::findEntryRecursive(const Uuid& uuid, Group* group)
|
||||||
{
|
{
|
||||||
const QList<Entry*> entryList = group->entries();
|
const QList<Entry*> entryList = group->entries();
|
||||||
for (Entry* entry : entryList) {
|
for (Entry* entry : entryList) {
|
||||||
@ -109,7 +114,57 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
const QList<Group*> children = group->children();
|
const QList<Group*> children = group->children();
|
||||||
for (Group* child : children) {
|
for (Group* child : children) {
|
||||||
Entry* result = recFindEntry(uuid, child);
|
Entry* result = findEntryRecursive(uuid, child);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* Database::findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group)
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(referenceType != EntryReferenceType::Unknown, "Database::findEntryRecursive",
|
||||||
|
"Can't search entry with \"referenceType\" parameter equal to \"Unknown\"");
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
const QList<Entry*> entryList = group->entries();
|
||||||
|
for (Entry* entry : entryList) {
|
||||||
|
switch (referenceType) {
|
||||||
|
case EntryReferenceType::Unknown:
|
||||||
|
return nullptr;
|
||||||
|
case EntryReferenceType::Title:
|
||||||
|
found = entry->title() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::UserName:
|
||||||
|
found = entry->username() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Password:
|
||||||
|
found = entry->password() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Url:
|
||||||
|
found = entry->url() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Notes:
|
||||||
|
found = entry->notes() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Uuid:
|
||||||
|
found = entry->uuid() == Uuid::fromHex(text);
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::CustomAttributes:
|
||||||
|
found = entry->attributes()->containsValue(text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<Group*> children = group->children();
|
||||||
|
for (Group* child : children) {
|
||||||
|
Entry* result = findEntryRecursive(text, referenceType, child);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -120,10 +175,10 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
Group* Database::resolveGroup(const Uuid& uuid)
|
Group* Database::resolveGroup(const Uuid& uuid)
|
||||||
{
|
{
|
||||||
return recFindGroup(uuid, m_rootGroup);
|
return findGroupRecursive(uuid, m_rootGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
Group* Database::recFindGroup(const Uuid& uuid, Group* group)
|
Group* Database::findGroupRecursive(const Uuid& uuid, Group* group)
|
||||||
{
|
{
|
||||||
if (group->uuid() == uuid) {
|
if (group->uuid() == uuid) {
|
||||||
return group;
|
return group;
|
||||||
@ -131,7 +186,7 @@ Group* Database::recFindGroup(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
const QList<Group*> children = group->children();
|
const QList<Group*> children = group->children();
|
||||||
for (Group* child : children) {
|
for (Group* child : children) {
|
||||||
Group* result = recFindGroup(uuid, child);
|
Group* result = findGroupRecursive(uuid, child);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "keys/CompositeKey.h"
|
#include "keys/CompositeKey.h"
|
||||||
|
|
||||||
class Entry;
|
class Entry;
|
||||||
|
enum class EntryReferenceType;
|
||||||
class Group;
|
class Group;
|
||||||
class Metadata;
|
class Metadata;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
@ -81,6 +82,7 @@ public:
|
|||||||
Metadata* metadata();
|
Metadata* metadata();
|
||||||
const Metadata* metadata() const;
|
const Metadata* metadata() const;
|
||||||
Entry* resolveEntry(const Uuid& uuid);
|
Entry* resolveEntry(const Uuid& uuid);
|
||||||
|
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
|
||||||
Group* resolveGroup(const Uuid& uuid);
|
Group* resolveGroup(const Uuid& uuid);
|
||||||
QList<DeletedObject> deletedObjects();
|
QList<DeletedObject> deletedObjects();
|
||||||
void addDeletedObject(const DeletedObject& delObj);
|
void addDeletedObject(const DeletedObject& delObj);
|
||||||
@ -141,8 +143,9 @@ private slots:
|
|||||||
void startModifiedTimer();
|
void startModifiedTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Entry* recFindEntry(const Uuid& uuid, Group* group);
|
Entry* findEntryRecursive(const Uuid& uuid, Group* group);
|
||||||
Group* recFindGroup(const Uuid& uuid, Group* group);
|
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
|
||||||
|
Group* findGroupRecursive(const Uuid& uuid, Group* group);
|
||||||
|
|
||||||
void createRecycleBin();
|
void createRecycleBin();
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
#include "totp/totp.h"
|
#include "totp/totp.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
const int Entry::DefaultIconNumber = 0;
|
const int Entry::DefaultIconNumber = 0;
|
||||||
const int Entry::ResolveMaximumDepth = 10;
|
const int Entry::ResolveMaximumDepth = 10;
|
||||||
|
|
||||||
@ -40,8 +42,8 @@ Entry::Entry()
|
|||||||
m_data.iconNumber = DefaultIconNumber;
|
m_data.iconNumber = DefaultIconNumber;
|
||||||
m_data.autoTypeEnabled = true;
|
m_data.autoTypeEnabled = true;
|
||||||
m_data.autoTypeObfuscation = 0;
|
m_data.autoTypeObfuscation = 0;
|
||||||
m_data.totpStep = QTotp::defaultStep;
|
m_data.totpStep = Totp::defaultStep;
|
||||||
m_data.totpDigits = QTotp::defaultDigits;
|
m_data.totpDigits = Totp::defaultDigits;
|
||||||
|
|
||||||
connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
|
connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
|
||||||
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
|
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
|
||||||
@ -90,6 +92,29 @@ void Entry::setUpdateTimeinfo(bool value)
|
|||||||
m_updateTimeinfo = value;
|
m_updateTimeinfo = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntryReferenceType Entry::referenceType(const QString& referenceStr)
|
||||||
|
{
|
||||||
|
const QString referenceLowerStr = referenceStr.toLower();
|
||||||
|
EntryReferenceType result = EntryReferenceType::Unknown;
|
||||||
|
if (referenceLowerStr == QLatin1String("t")) {
|
||||||
|
result = EntryReferenceType::Title;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("u")) {
|
||||||
|
result = EntryReferenceType::UserName;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("p")) {
|
||||||
|
result = EntryReferenceType::Password;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("a")) {
|
||||||
|
result = EntryReferenceType::Url;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("n")) {
|
||||||
|
result = EntryReferenceType::Notes;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("i")) {
|
||||||
|
result = EntryReferenceType::Uuid;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("o")) {
|
||||||
|
result = EntryReferenceType::CustomAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Uuid Entry::uuid() const
|
Uuid Entry::uuid() const
|
||||||
{
|
{
|
||||||
return m_uuid;
|
return m_uuid;
|
||||||
@ -315,7 +340,7 @@ QString Entry::totp() const
|
|||||||
if (hasTotp()) {
|
if (hasTotp()) {
|
||||||
QString seed = totpSeed();
|
QString seed = totpSeed();
|
||||||
quint64 time = QDateTime::currentDateTime().toTime_t();
|
quint64 time = QDateTime::currentDateTime().toTime_t();
|
||||||
QString output = QTotp::generateTotp(seed.toLatin1(), time, m_data.totpDigits, m_data.totpStep);
|
QString output = Totp::generateTotp(seed.toLatin1(), time, m_data.totpDigits, m_data.totpStep);
|
||||||
|
|
||||||
return QString(output);
|
return QString(output);
|
||||||
} else {
|
} else {
|
||||||
@ -326,18 +351,30 @@ QString Entry::totp() const
|
|||||||
void Entry::setTotp(const QString& seed, quint8& step, quint8& digits)
|
void Entry::setTotp(const QString& seed, quint8& step, quint8& digits)
|
||||||
{
|
{
|
||||||
if (step == 0) {
|
if (step == 0) {
|
||||||
step = QTotp::defaultStep;
|
step = Totp::defaultStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (digits == 0) {
|
if (digits == 0) {
|
||||||
digits = QTotp::defaultDigits;
|
digits = Totp::defaultDigits;
|
||||||
}
|
}
|
||||||
|
QString data;
|
||||||
|
|
||||||
|
const Totp::Encoder & enc = Totp::encoders.value(digits, Totp::defaultEncoder);
|
||||||
|
|
||||||
if (m_attributes->hasKey("otp")) {
|
if (m_attributes->hasKey("otp")) {
|
||||||
m_attributes->set("otp", QString("key=%1&step=%2&size=%3").arg(seed).arg(step).arg(digits), true);
|
data = QString("key=%1&step=%2&size=%3").arg(seed).arg(step).arg(enc.digits == 0 ? digits : enc.digits);
|
||||||
|
if (!enc.name.isEmpty()) {
|
||||||
|
data.append("&enocder=").append(enc.name);
|
||||||
|
}
|
||||||
|
m_attributes->set("otp", data, true);
|
||||||
} else {
|
} else {
|
||||||
m_attributes->set("TOTP Seed", seed, true);
|
m_attributes->set("TOTP Seed", seed, true);
|
||||||
m_attributes->set("TOTP Settings", QString("%1;%2").arg(step).arg(digits));
|
if (!enc.shortName.isEmpty()) {
|
||||||
|
data = QString("%1;%2").arg(step).arg(enc.shortName);
|
||||||
|
} else {
|
||||||
|
data = QString("%1;%2").arg(step).arg(digits);
|
||||||
|
}
|
||||||
|
m_attributes->set("TOTP Settings", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,19 +388,24 @@ QString Entry::totpSeed() const
|
|||||||
secret = m_attributes->value("TOTP Seed");
|
secret = m_attributes->value("TOTP Seed");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data.totpDigits = QTotp::defaultDigits;
|
m_data.totpDigits = Totp::defaultDigits;
|
||||||
m_data.totpStep = QTotp::defaultStep;
|
m_data.totpStep = Totp::defaultStep;
|
||||||
|
|
||||||
if (m_attributes->hasKey("TOTP Settings")) {
|
if (m_attributes->hasKey("TOTP Settings")) {
|
||||||
QRegExp rx("(\\d+);(\\d)", Qt::CaseInsensitive, QRegExp::RegExp);
|
// this regex must be kept in sync with the set of allowed short names Totp::shortNameToEncoder
|
||||||
int pos = rx.indexIn(m_attributes->value("TOTP Settings"));
|
QRegularExpression rx(QString("(\\d+);((?:\\d+)|S)"));
|
||||||
if (pos > -1) {
|
QRegularExpressionMatch m = rx.match(m_attributes->value("TOTP Settings"));
|
||||||
m_data.totpStep = rx.cap(1).toUInt();
|
if (m.hasMatch()) {
|
||||||
m_data.totpDigits = rx.cap(2).toUInt();
|
m_data.totpStep = m.captured(1).toUInt();
|
||||||
|
if (Totp::shortNameToEncoder.contains(m.captured(2))) {
|
||||||
|
m_data.totpDigits = Totp::shortNameToEncoder[m.captured(2)];
|
||||||
|
} else {
|
||||||
|
m_data.totpDigits = m.captured(2).toUInt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QTotp::parseOtpString(secret, m_data.totpDigits, m_data.totpStep);
|
return Totp::parseOtpString(secret, m_data.totpDigits, m_data.totpStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint8 Entry::totpStep() const
|
quint8 Entry::totpStep() const
|
||||||
@ -709,10 +751,7 @@ QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDe
|
|||||||
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
|
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
|
||||||
switch (typeOfPlaceholder) {
|
switch (typeOfPlaceholder) {
|
||||||
case PlaceholderType::NotPlaceholder:
|
case PlaceholderType::NotPlaceholder:
|
||||||
return placeholder;
|
|
||||||
case PlaceholderType::Unknown:
|
case PlaceholderType::Unknown:
|
||||||
qWarning("Can't resolve placeholder %s for entry with uuid %s", qPrintable(placeholder),
|
|
||||||
qPrintable(uuid().toHex()));
|
|
||||||
return placeholder;
|
return placeholder;
|
||||||
case PlaceholderType::Title:
|
case PlaceholderType::Title:
|
||||||
return title();
|
return title();
|
||||||
@ -743,43 +782,62 @@ QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDe
|
|||||||
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
||||||
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
|
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
|
||||||
}
|
}
|
||||||
case PlaceholderType::Reference: {
|
case PlaceholderType::Reference:
|
||||||
// resolving references in format: {REF:<WantedField>@I:<uuid of referenced entry>}
|
return resolveReferencePlaceholderRecursive(placeholder, maxDepth);
|
||||||
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing,
|
}
|
||||||
// but supporting lookups of standard fields and references by UUID only
|
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
|
||||||
|
{
|
||||||
|
// resolving references in format: {REF:<WantedField>@<SearchIn>:<SearchText>}
|
||||||
|
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing
|
||||||
|
|
||||||
|
QRegularExpressionMatch match = EntryAttributes::matchReference(placeholder);
|
||||||
|
if (!match.hasMatch()) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
QString result;
|
QString result;
|
||||||
QRegExp* referenceRegExp = m_attributes->referenceRegExp();
|
const QString searchIn = match.captured(EntryAttributes::SearchInGroupName);
|
||||||
if (referenceRegExp->indexIn(placeholder) != -1) {
|
const QString searchText = match.captured(EntryAttributes::SearchTextGroupName);
|
||||||
constexpr int wantedFieldIndex = 1;
|
|
||||||
constexpr int referencedUuidIndex = 2;
|
const EntryReferenceType searchInType = Entry::referenceType(searchIn);
|
||||||
const Uuid referencedUuid(QByteArray::fromHex(referenceRegExp->cap(referencedUuidIndex).toLatin1()));
|
const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
|
||||||
const Entry* refEntry = m_group->database()->resolveEntry(referencedUuid);
|
|
||||||
if (refEntry) {
|
if (refEntry) {
|
||||||
const QString wantedField = referenceRegExp->cap(wantedFieldIndex).toLower();
|
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);
|
||||||
if (wantedField == "t") {
|
result = refEntry->referenceFieldValue(Entry::referenceType(wantedField));
|
||||||
result = refEntry->title();
|
|
||||||
} else if (wantedField == "u") {
|
|
||||||
result = refEntry->username();
|
|
||||||
} else if (wantedField == "p") {
|
|
||||||
result = refEntry->password();
|
|
||||||
} else if (wantedField == "a") {
|
|
||||||
result = refEntry->url();
|
|
||||||
} else if (wantedField == "n") {
|
|
||||||
result = refEntry->notes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Referencing fields of other entries only works with standard fields, not with custom user strings.
|
// Referencing fields of other entries only works with standard fields, not with custom user strings.
|
||||||
// If you want to reference a custom user string, you need to place a redirection in a standard field
|
// If you want to reference a custom user string, you need to place a redirection in a standard field
|
||||||
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
|
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
|
||||||
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return placeholder;
|
QString Entry::referenceFieldValue(EntryReferenceType referenceType) const
|
||||||
|
{
|
||||||
|
switch (referenceType) {
|
||||||
|
case EntryReferenceType::Title:
|
||||||
|
return title();
|
||||||
|
case EntryReferenceType::UserName:
|
||||||
|
return username();
|
||||||
|
case EntryReferenceType::Password:
|
||||||
|
return password();
|
||||||
|
case EntryReferenceType::Url:
|
||||||
|
return url();
|
||||||
|
case EntryReferenceType::Notes:
|
||||||
|
return notes();
|
||||||
|
case EntryReferenceType::Uuid:
|
||||||
|
return uuid().toHex();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Group* Entry::group()
|
Group* Entry::group()
|
||||||
|
@ -36,6 +36,17 @@
|
|||||||
class Database;
|
class Database;
|
||||||
class Group;
|
class Group;
|
||||||
|
|
||||||
|
enum class EntryReferenceType {
|
||||||
|
Unknown,
|
||||||
|
Title,
|
||||||
|
UserName,
|
||||||
|
Password,
|
||||||
|
Url,
|
||||||
|
Notes,
|
||||||
|
Uuid,
|
||||||
|
CustomAttributes
|
||||||
|
};
|
||||||
|
|
||||||
struct EntryData
|
struct EntryData
|
||||||
{
|
{
|
||||||
int iconNumber;
|
int iconNumber;
|
||||||
@ -203,6 +214,10 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
QString resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const;
|
QString resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const;
|
||||||
QString resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
QString resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
||||||
|
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
||||||
|
QString referenceFieldValue(EntryReferenceType referenceType) const;
|
||||||
|
|
||||||
|
static EntryReferenceType referenceType(const QString& referenceStr);
|
||||||
|
|
||||||
const Database* database() const;
|
const Database* database() const;
|
||||||
template <class T> bool set(T& property, const T& value);
|
template <class T> bool set(T& property, const T& value);
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include "EntryAttachments.h"
|
#include "EntryAttachments.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
EntryAttachments::EntryAttachments(QObject* parent)
|
EntryAttachments::EntryAttachments(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
@ -71,7 +73,8 @@ void EntryAttachments::set(const QString& key, const QByteArray& value)
|
|||||||
void EntryAttachments::remove(const QString& key)
|
void EntryAttachments::remove(const QString& key)
|
||||||
{
|
{
|
||||||
if (!m_attachments.contains(key)) {
|
if (!m_attachments.contains(key)) {
|
||||||
Q_ASSERT(false);
|
Q_ASSERT_X(false, "EntryAttachments::remove",
|
||||||
|
qPrintable(QString("Can't find attachment for key %1").arg(key)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +86,31 @@ void EntryAttachments::remove(const QString& key)
|
|||||||
emit modified();
|
emit modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntryAttachments::remove(const QStringList& keys)
|
||||||
|
{
|
||||||
|
if (keys.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isModified = false;
|
||||||
|
for (const QString &key: keys) {
|
||||||
|
if (!m_attachments.contains(key)) {
|
||||||
|
Q_ASSERT_X(false, "EntryAttachments::remove",
|
||||||
|
qPrintable(QString("Can't find attachment for key %1").arg(key)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
isModified = true;
|
||||||
|
emit aboutToBeRemoved(key);
|
||||||
|
m_attachments.remove(key);
|
||||||
|
emit removed(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isModified) {
|
||||||
|
emit modified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntryAttachments::clear()
|
void EntryAttachments::clear()
|
||||||
{
|
{
|
||||||
if (m_attachments.isEmpty()) {
|
if (m_attachments.isEmpty()) {
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
class QStringList;
|
||||||
|
|
||||||
class EntryAttachments : public QObject
|
class EntryAttachments : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -33,6 +35,7 @@ public:
|
|||||||
QByteArray value(const QString& key) const;
|
QByteArray value(const QString& key) const;
|
||||||
void set(const QString& key, const QByteArray& value);
|
void set(const QString& key, const QByteArray& value);
|
||||||
void remove(const QString& key);
|
void remove(const QString& key);
|
||||||
|
void remove(const QStringList& keys);
|
||||||
void clear();
|
void clear();
|
||||||
void copyDataFrom(const EntryAttachments* other);
|
void copyDataFrom(const EntryAttachments* other);
|
||||||
bool operator==(const EntryAttachments& other) const;
|
bool operator==(const EntryAttachments& other) const;
|
||||||
|
@ -25,11 +25,15 @@ const QString EntryAttributes::URLKey = "URL";
|
|||||||
const QString EntryAttributes::NotesKey = "Notes";
|
const QString EntryAttributes::NotesKey = "Notes";
|
||||||
const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey
|
const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey
|
||||||
<< PasswordKey << URLKey << NotesKey);
|
<< PasswordKey << URLKey << NotesKey);
|
||||||
|
|
||||||
|
const QString EntryAttributes::WantedFieldGroupName = "WantedField";
|
||||||
|
const QString EntryAttributes::SearchInGroupName = "SearchIn";
|
||||||
|
const QString EntryAttributes::SearchTextGroupName = "SearchText";
|
||||||
|
|
||||||
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
|
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
|
||||||
|
|
||||||
EntryAttributes::EntryAttributes(QObject* parent)
|
EntryAttributes::EntryAttributes(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_referenceRegExp("\\{REF:([TUPAN])@I:([^}]+)\\}", Qt::CaseInsensitive, QRegExp::RegExp2)
|
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
@ -66,6 +70,11 @@ bool EntryAttributes::contains(const QString &key) const
|
|||||||
return m_attributes.contains(key);
|
return m_attributes.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntryAttributes::containsValue(const QString& value) const
|
||||||
|
{
|
||||||
|
return m_attributes.values().contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
bool EntryAttributes::isProtected(const QString& key) const
|
bool EntryAttributes::isProtected(const QString& key) const
|
||||||
{
|
{
|
||||||
return m_protectedAttributes.contains(key);
|
return m_protectedAttributes.contains(key);
|
||||||
@ -78,16 +87,8 @@ bool EntryAttributes::isReference(const QString& key) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString data = value(key);
|
const QString data = value(key);
|
||||||
if (m_referenceRegExp.indexIn(data) != -1) {
|
return matchReference(data).hasMatch();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRegExp* EntryAttributes::referenceRegExp()
|
|
||||||
{
|
|
||||||
return &m_referenceRegExp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntryAttributes::set(const QString& key, const QString& value, bool protect)
|
void EntryAttributes::set(const QString& key, const QString& value, bool protect)
|
||||||
@ -258,6 +259,15 @@ bool EntryAttributes::operator!=(const EntryAttributes& other) const
|
|||||||
|| m_protectedAttributes != other.m_protectedAttributes);
|
|| m_protectedAttributes != other.m_protectedAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRegularExpressionMatch EntryAttributes::matchReference(const QString& text)
|
||||||
|
{
|
||||||
|
static QRegularExpression referenceRegExp(
|
||||||
|
"\\{REF:(?<WantedField>[TUPANI])@(?<SearchIn>[TUPANIO]):(?<SearchText>[^}]+)\\}",
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
|
return referenceRegExp.match(text);
|
||||||
|
}
|
||||||
|
|
||||||
void EntryAttributes::clear()
|
void EntryAttributes::clear()
|
||||||
{
|
{
|
||||||
emit aboutToBeReset();
|
emit aboutToBeReset();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
@ -35,9 +36,9 @@ public:
|
|||||||
QList<QString> customKeys();
|
QList<QString> customKeys();
|
||||||
QString value(const QString& key) const;
|
QString value(const QString& key) const;
|
||||||
bool contains(const QString& key) const;
|
bool contains(const QString& key) const;
|
||||||
|
bool containsValue(const QString& value) const;
|
||||||
bool isProtected(const QString& key) const;
|
bool isProtected(const QString& key) const;
|
||||||
bool isReference(const QString& key) const;
|
bool isReference(const QString& key) const;
|
||||||
QRegExp* referenceRegExp();
|
|
||||||
void set(const QString& key, const QString& value, bool protect = false);
|
void set(const QString& key, const QString& value, bool protect = false);
|
||||||
void remove(const QString& key);
|
void remove(const QString& key);
|
||||||
void rename(const QString& oldKey, const QString& newKey);
|
void rename(const QString& oldKey, const QString& newKey);
|
||||||
@ -49,6 +50,8 @@ public:
|
|||||||
bool operator==(const EntryAttributes& other) const;
|
bool operator==(const EntryAttributes& other) const;
|
||||||
bool operator!=(const EntryAttributes& other) const;
|
bool operator!=(const EntryAttributes& other) const;
|
||||||
|
|
||||||
|
static QRegularExpressionMatch matchReference(const QString& text);
|
||||||
|
|
||||||
static const QString TitleKey;
|
static const QString TitleKey;
|
||||||
static const QString UserNameKey;
|
static const QString UserNameKey;
|
||||||
static const QString PasswordKey;
|
static const QString PasswordKey;
|
||||||
@ -58,6 +61,10 @@ public:
|
|||||||
static const QString RememberCmdExecAttr;
|
static const QString RememberCmdExecAttr;
|
||||||
static bool isDefaultAttribute(const QString& key);
|
static bool isDefaultAttribute(const QString& key);
|
||||||
|
|
||||||
|
static const QString WantedFieldGroupName;
|
||||||
|
static const QString SearchInGroupName;
|
||||||
|
static const QString SearchTextGroupName;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void modified();
|
void modified();
|
||||||
void defaultKeyModified();
|
void defaultKeyModified();
|
||||||
@ -74,7 +81,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QMap<QString, QString> m_attributes;
|
QMap<QString, QString> m_attributes;
|
||||||
QSet<QString> m_protectedAttributes;
|
QSet<QString> m_protectedAttributes;
|
||||||
QRegExp m_referenceRegExp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_ENTRYATTRIBUTES_H
|
#endif // KEEPASSX_ENTRYATTRIBUTES_H
|
||||||
|
@ -26,6 +26,11 @@
|
|||||||
const int Group::DefaultIconNumber = 48;
|
const int Group::DefaultIconNumber = 48;
|
||||||
const int Group::RecycleBinIconNumber = 43;
|
const int Group::RecycleBinIconNumber = 43;
|
||||||
|
|
||||||
|
Group::CloneFlags Group::DefaultCloneFlags = static_cast<Group::CloneFlags>(
|
||||||
|
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
|
||||||
|
Entry::CloneFlags Group::DefaultEntryCloneFlags = static_cast<Entry::CloneFlags>(
|
||||||
|
Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
|
||||||
|
|
||||||
Group::Group()
|
Group::Group()
|
||||||
: m_updateTimeinfo(true)
|
: m_updateTimeinfo(true)
|
||||||
{
|
{
|
||||||
@ -77,8 +82,7 @@ template <class P, class V> inline bool Group::set(P& property, const V& value)
|
|||||||
updateTimeinfo();
|
updateTimeinfo();
|
||||||
emit modified();
|
emit modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -682,38 +686,59 @@ void Group::merge(const Group* other)
|
|||||||
|
|
||||||
Entry* existingEntry = rootGroup->findEntryByUuid(entry->uuid());
|
Entry* existingEntry = rootGroup->findEntryByUuid(entry->uuid());
|
||||||
|
|
||||||
// This entry does not exist at all. Create it.
|
|
||||||
if (!existingEntry) {
|
if (!existingEntry) {
|
||||||
|
// This entry does not exist at all. Create it.
|
||||||
qDebug("New entry %s detected. Creating it.", qPrintable(entry->title()));
|
qDebug("New entry %s detected. Creating it.", qPrintable(entry->title()));
|
||||||
entry->clone(Entry::CloneIncludeHistory)->setGroup(this);
|
entry->clone(Entry::CloneIncludeHistory)->setGroup(this);
|
||||||
// Entry is already present in the database. Update it.
|
|
||||||
} else {
|
} else {
|
||||||
|
// Entry is already present in the database. Update it.
|
||||||
bool locationChanged = existingEntry->timeInfo().locationChanged() < entry->timeInfo().locationChanged();
|
bool locationChanged = existingEntry->timeInfo().locationChanged() < entry->timeInfo().locationChanged();
|
||||||
if (locationChanged && existingEntry->group() != this) {
|
if (locationChanged && existingEntry->group() != this) {
|
||||||
existingEntry->setGroup(this);
|
existingEntry->setGroup(this);
|
||||||
qDebug("Location changed for entry %s. Updating it", qPrintable(existingEntry->title()));
|
qDebug("Location changed for entry %s. Updating it", qPrintable(existingEntry->title()));
|
||||||
}
|
}
|
||||||
resolveConflict(existingEntry, entry);
|
resolveEntryConflict(existingEntry, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge groups recursively
|
// merge groups recursively
|
||||||
const QList<Group*> dbChildren = other->children();
|
const QList<Group*> dbChildren = other->children();
|
||||||
for (Group* group : dbChildren) {
|
for (Group* group : dbChildren) {
|
||||||
// groups are searched by name instead of uuid
|
|
||||||
if (findChildByName(group->name())) {
|
Group* existingGroup = rootGroup->findChildByUuid(group->uuid());
|
||||||
findChildByName(group->name())->merge(group);
|
|
||||||
} else {
|
if (!existingGroup) {
|
||||||
qDebug("New group %s detected. Creating it.", qPrintable(group->name()));
|
qDebug("New group %s detected. Creating it.", qPrintable(group->name()));
|
||||||
Group* newGroup = group->clone(Entry::CloneNoFlags, true);
|
Group* newGroup = group->clone(Entry::CloneNoFlags, Group::CloneNoFlags);
|
||||||
newGroup->setParent(this);
|
newGroup->setParent(this);
|
||||||
newGroup->merge(group);
|
newGroup->merge(group);
|
||||||
|
} else {
|
||||||
|
bool locationChanged = existingGroup->timeInfo().locationChanged() < group->timeInfo().locationChanged();
|
||||||
|
if (locationChanged && existingGroup->parent() != this) {
|
||||||
|
existingGroup->setParent(this);
|
||||||
|
qDebug("Location changed for group %s. Updating it", qPrintable(existingGroup->name()));
|
||||||
}
|
}
|
||||||
|
resolveGroupConflict(existingGroup, group);
|
||||||
|
existingGroup->merge(group);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit modified();
|
emit modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Group* Group::findChildByUuid(const Uuid& uuid)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!uuid.isNull());
|
||||||
|
for (Group* group : groupsRecursive(true)) {
|
||||||
|
if (group->uuid() == uuid) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Group* Group::findChildByName(const QString& name)
|
Group* Group::findChildByName(const QString& name)
|
||||||
{
|
{
|
||||||
for (Group* group : asConst(m_children)) {
|
for (Group* group : asConst(m_children)) {
|
||||||
@ -725,16 +750,21 @@ Group* Group::findChildByName(const QString& name)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Group* Group::clone(Entry::CloneFlags entryFlags, bool shallow) const
|
Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags) const
|
||||||
{
|
{
|
||||||
Group* clonedGroup = new Group();
|
Group* clonedGroup = new Group();
|
||||||
|
|
||||||
clonedGroup->setUpdateTimeinfo(false);
|
clonedGroup->setUpdateTimeinfo(false);
|
||||||
|
|
||||||
|
if (groupFlags & Group::CloneNewUuid) {
|
||||||
clonedGroup->setUuid(Uuid::random());
|
clonedGroup->setUuid(Uuid::random());
|
||||||
|
} else {
|
||||||
|
clonedGroup->setUuid(this->uuid());
|
||||||
|
}
|
||||||
|
|
||||||
clonedGroup->m_data = m_data;
|
clonedGroup->m_data = m_data;
|
||||||
|
|
||||||
if (!shallow) {
|
if (groupFlags & Group::CloneIncludeEntries) {
|
||||||
const QList<Entry*> entryList = entries();
|
const QList<Entry*> entryList = entries();
|
||||||
for (Entry* entry : entryList) {
|
for (Entry* entry : entryList) {
|
||||||
Entry* clonedEntry = entry->clone(entryFlags);
|
Entry* clonedEntry = entry->clone(entryFlags);
|
||||||
@ -743,18 +773,20 @@ Group* Group::clone(Entry::CloneFlags entryFlags, bool shallow) const
|
|||||||
|
|
||||||
const QList<Group*> childrenGroups = children();
|
const QList<Group*> childrenGroups = children();
|
||||||
for (Group* groupChild : childrenGroups) {
|
for (Group* groupChild : childrenGroups) {
|
||||||
Group* clonedGroupChild = groupChild->clone(entryFlags);
|
Group* clonedGroupChild = groupChild->clone(entryFlags, groupFlags);
|
||||||
clonedGroupChild->setParent(clonedGroup);
|
clonedGroupChild->setParent(clonedGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clonedGroup->setUpdateTimeinfo(true);
|
clonedGroup->setUpdateTimeinfo(true);
|
||||||
|
if (groupFlags & Group::CloneResetTimeInfo) {
|
||||||
|
|
||||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||||
clonedGroup->m_data.timeInfo.setCreationTime(now);
|
clonedGroup->m_data.timeInfo.setCreationTime(now);
|
||||||
clonedGroup->m_data.timeInfo.setLastModificationTime(now);
|
clonedGroup->m_data.timeInfo.setLastModificationTime(now);
|
||||||
clonedGroup->m_data.timeInfo.setLastAccessTime(now);
|
clonedGroup->m_data.timeInfo.setLastAccessTime(now);
|
||||||
clonedGroup->m_data.timeInfo.setLocationChanged(now);
|
clonedGroup->m_data.timeInfo.setLocationChanged(now);
|
||||||
|
}
|
||||||
|
|
||||||
return clonedGroup;
|
return clonedGroup;
|
||||||
}
|
}
|
||||||
@ -908,7 +940,7 @@ bool Group::resolveAutoTypeEnabled() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Group::resolveConflict(Entry* existingEntry, Entry* otherEntry)
|
void Group::resolveEntryConflict(Entry* existingEntry, Entry* otherEntry)
|
||||||
{
|
{
|
||||||
const QDateTime timeExisting = existingEntry->timeInfo().lastModificationTime();
|
const QDateTime timeExisting = existingEntry->timeInfo().lastModificationTime();
|
||||||
const QDateTime timeOther = otherEntry->timeInfo().lastModificationTime();
|
const QDateTime timeOther = otherEntry->timeInfo().lastModificationTime();
|
||||||
@ -946,6 +978,26 @@ void Group::resolveConflict(Entry* existingEntry, Entry* otherEntry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Group::resolveGroupConflict(Group* existingGroup, Group* otherGroup)
|
||||||
|
{
|
||||||
|
const QDateTime timeExisting = existingGroup->timeInfo().lastModificationTime();
|
||||||
|
const QDateTime timeOther = otherGroup->timeInfo().lastModificationTime();
|
||||||
|
|
||||||
|
// only if the other group is newer, update the existing one.
|
||||||
|
if (timeExisting < timeOther) {
|
||||||
|
qDebug("Updating group %s.", qPrintable(existingGroup->name()));
|
||||||
|
existingGroup->setName(otherGroup->name());
|
||||||
|
existingGroup->setNotes(otherGroup->notes());
|
||||||
|
if (otherGroup->iconNumber() == 0) {
|
||||||
|
existingGroup->setIcon(otherGroup->iconUuid());
|
||||||
|
} else {
|
||||||
|
existingGroup->setIcon(otherGroup->iconNumber());
|
||||||
|
}
|
||||||
|
existingGroup->setExpiryTime(otherGroup->timeInfo().expiryTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QStringList Group::locate(QString locateTerm, QString currentPath)
|
QStringList Group::locate(QString locateTerm, QString currentPath)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!locateTerm.isNull());
|
Q_ASSERT(!locateTerm.isNull());
|
||||||
|
@ -37,6 +37,14 @@ public:
|
|||||||
enum TriState { Inherit, Enable, Disable };
|
enum TriState { Inherit, Enable, Disable };
|
||||||
enum MergeMode { ModeInherit, KeepBoth, KeepNewer, KeepExisting };
|
enum MergeMode { ModeInherit, KeepBoth, KeepNewer, KeepExisting };
|
||||||
|
|
||||||
|
enum CloneFlag {
|
||||||
|
CloneNoFlags = 0,
|
||||||
|
CloneNewUuid = 1, // generate a random uuid for the clone
|
||||||
|
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
|
||||||
|
CloneIncludeEntries = 4, // clone the group entries
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
|
||||||
|
|
||||||
struct GroupData
|
struct GroupData
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
@ -78,8 +86,11 @@ public:
|
|||||||
|
|
||||||
static const int DefaultIconNumber;
|
static const int DefaultIconNumber;
|
||||||
static const int RecycleBinIconNumber;
|
static const int RecycleBinIconNumber;
|
||||||
|
static CloneFlags DefaultCloneFlags;
|
||||||
|
static Entry::CloneFlags DefaultEntryCloneFlags;
|
||||||
|
|
||||||
Group* findChildByName(const QString& name);
|
Group* findChildByName(const QString& name);
|
||||||
|
Group* findChildByUuid(const Uuid& uuid);
|
||||||
Entry* findEntry(QString entryId);
|
Entry* findEntry(QString entryId);
|
||||||
Entry* findEntryByUuid(const Uuid& uuid);
|
Entry* findEntryByUuid(const Uuid& uuid);
|
||||||
Entry* findEntryByPath(QString entryPath, QString basePath = QString(""));
|
Entry* findEntryByPath(QString entryPath, QString basePath = QString(""));
|
||||||
@ -119,14 +130,13 @@ public:
|
|||||||
QList<Group*> groupsRecursive(bool includeSelf);
|
QList<Group*> groupsRecursive(bool includeSelf);
|
||||||
QSet<Uuid> customIconsRecursive() const;
|
QSet<Uuid> customIconsRecursive() const;
|
||||||
/**
|
/**
|
||||||
* Creates a duplicate of this group including all child entries and groups (if not shallow).
|
* Creates a duplicate of this group.
|
||||||
* The exceptions are that the returned group doesn't have a parent group
|
|
||||||
* and all TimeInfo attributes are set to the current time.
|
|
||||||
* Note that you need to copy the custom icons manually when inserting the
|
* Note that you need to copy the custom icons manually when inserting the
|
||||||
* new group into another database.
|
* new group into another database.
|
||||||
*/
|
*/
|
||||||
Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo,
|
Group* clone(Entry::CloneFlags entryFlags = DefaultEntryCloneFlags,
|
||||||
bool shallow = false) const;
|
CloneFlags groupFlags = DefaultCloneFlags) const;
|
||||||
|
|
||||||
void copyDataFrom(const Group* other);
|
void copyDataFrom(const Group* other);
|
||||||
void merge(const Group* other);
|
void merge(const Group* other);
|
||||||
QString print(bool recursive = false, int depth = 0);
|
QString print(bool recursive = false, int depth = 0);
|
||||||
@ -160,7 +170,8 @@ private:
|
|||||||
void removeEntry(Entry* entry);
|
void removeEntry(Entry* entry);
|
||||||
void setParent(Database* db);
|
void setParent(Database* db);
|
||||||
void markOlderEntry(Entry* entry);
|
void markOlderEntry(Entry* entry);
|
||||||
void resolveConflict(Entry* existingEntry, Entry* otherEntry);
|
void resolveEntryConflict(Entry* existingEntry, Entry* otherEntry);
|
||||||
|
void resolveGroupConflict(Group* existingGroup, Group* otherGroup);
|
||||||
|
|
||||||
void recSetDatabase(Database* db);
|
void recSetDatabase(Database* db);
|
||||||
void cleanupParent();
|
void cleanupParent();
|
||||||
@ -183,4 +194,6 @@ private:
|
|||||||
friend void Entry::setGroup(Group* group);
|
friend void Entry::setGroup(Group* group);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Group::CloneFlags)
|
||||||
|
|
||||||
#endif // KEEPASSX_GROUP_H
|
#endif // KEEPASSX_GROUP_H
|
||||||
|
94
src/core/MacPasteboard.cpp
Normal file
94
src/core/MacPasteboard.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MacPasteboard.h"
|
||||||
|
|
||||||
|
QString MacPasteboard::convertorName() { return QLatin1String("MacPasteboard"); }
|
||||||
|
|
||||||
|
QString MacPasteboard::flavorFor(const QString& mimetype) {
|
||||||
|
if (mimetype == QLatin1String("text/plain")) {
|
||||||
|
return QLatin1String("public.utf8-plain-text");
|
||||||
|
} else if (mimetype == QLatin1String("application/x-nspasteboard-concealed-type")) {
|
||||||
|
return QLatin1String("org.nspasteboard.ConcealedType");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = mimetype.indexOf(QLatin1String("charset="));
|
||||||
|
|
||||||
|
if (i >= 0) {
|
||||||
|
QString cs(mimetype.mid(i + 8).toLower());
|
||||||
|
i = cs.indexOf(QLatin1Char(';'));
|
||||||
|
|
||||||
|
if (i >= 0) {
|
||||||
|
cs = cs.left(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cs == QLatin1String("system")) {
|
||||||
|
return QLatin1String("public.utf8-plain-text");
|
||||||
|
} else if (cs == QLatin1String("iso-10646-ucs-2") ||
|
||||||
|
cs == QLatin1String("utf16")) {
|
||||||
|
return QLatin1String("public.utf16-plain-text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MacPasteboard::mimeFor(QString flavor) {
|
||||||
|
if (flavor == QLatin1String("public.utf8-plain-text"))
|
||||||
|
return QLatin1String("text/plain");
|
||||||
|
if (flavor == QLatin1String("org.nspasteboard.ConcealedType"))
|
||||||
|
return QLatin1String("application/x-nspasteboard-concealed-type");
|
||||||
|
if (flavor == QLatin1String("public.utf16-plain-text"))
|
||||||
|
return QLatin1String("text/plain;charset=utf16");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacPasteboard::canConvert(const QString& mimetype, QString flavor) {
|
||||||
|
Q_UNUSED(mimetype);
|
||||||
|
Q_UNUSED(flavor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MacPasteboard::convertToMime(const QString& mimetype, QList<QByteArray> data, QString flavor) {
|
||||||
|
if (data.count() > 1)
|
||||||
|
qWarning("QMime::convertToMime: Cannot handle multiple member data");
|
||||||
|
const QByteArray& firstData = data.first();
|
||||||
|
QVariant ret;
|
||||||
|
if (flavor == QLatin1String("public.utf8-plain-text")) {
|
||||||
|
ret = QString::fromUtf8(firstData);
|
||||||
|
} else if (flavor == QLatin1String("org.nspasteboard.ConcealedType")) {
|
||||||
|
ret = QString::fromUtf8(firstData);
|
||||||
|
} else if (flavor == QLatin1String("public.utf16-plain-text")) {
|
||||||
|
ret = QTextCodec::codecForName("UTF-16")->toUnicode(firstData);
|
||||||
|
} else {
|
||||||
|
qWarning("QMime::convertToMime: unhandled mimetype: %s",
|
||||||
|
qPrintable(mimetype));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> MacPasteboard::convertFromMime(const QString&, QVariant data, QString flavor) {
|
||||||
|
QList<QByteArray> ret;
|
||||||
|
QString string = data.toString();
|
||||||
|
if (flavor == QLatin1String("public.utf8-plain-text"))
|
||||||
|
ret.append(string.toUtf8());
|
||||||
|
else if (flavor == QLatin1String("org.nspasteboard.ConcealedType"))
|
||||||
|
ret.append(string.toUtf8());
|
||||||
|
else if (flavor == QLatin1String("public.utf16-plain-text"))
|
||||||
|
ret.append(QTextCodec::codecForName("UTF-16")->fromUnicode(string));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
37
src/core/MacPasteboard.h
Normal file
37
src/core/MacPasteboard.h
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 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 KEEPASSXC_MACPASTEBOARD_H
|
||||||
|
#define KEEPASSXC_MACPASTEBOARD_H
|
||||||
|
|
||||||
|
#include <QMacPasteboardMime>
|
||||||
|
#include <QTextCodec>
|
||||||
|
|
||||||
|
class MacPasteboard : public QMacPasteboardMime
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MacPasteboard() : QMacPasteboardMime(MIME_ALL) {}
|
||||||
|
|
||||||
|
QString convertorName() override;
|
||||||
|
bool canConvert(const QString &mime, QString flav) override;
|
||||||
|
QString mimeFor(QString flav) override;
|
||||||
|
QString flavorFor(const QString &mime) override;
|
||||||
|
QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) override;
|
||||||
|
QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSXC_MACPASTEBOARD_H
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OPTIONAL_H
|
|
||||||
#define OPTIONAL_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This utility class is for providing basic support for an option type.
|
|
||||||
* It can be replaced by std::optional (C++17) or
|
|
||||||
* std::experimental::optional (C++11) when they become fully supported
|
|
||||||
* by all the compilers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Optional
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
// None
|
|
||||||
Optional() :
|
|
||||||
m_hasValue(false),
|
|
||||||
m_value()
|
|
||||||
{ };
|
|
||||||
|
|
||||||
// Some T
|
|
||||||
Optional(const T& value) :
|
|
||||||
m_hasValue(true),
|
|
||||||
m_value(value)
|
|
||||||
{ };
|
|
||||||
|
|
||||||
// Copy
|
|
||||||
Optional(const Optional& other) :
|
|
||||||
m_hasValue(other.m_hasValue),
|
|
||||||
m_value(other.m_value)
|
|
||||||
{ };
|
|
||||||
|
|
||||||
const Optional& operator=(const Optional& other)
|
|
||||||
{
|
|
||||||
m_hasValue = other.m_hasValue;
|
|
||||||
m_value = other.m_value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Optional& other) const
|
|
||||||
{
|
|
||||||
if(m_hasValue)
|
|
||||||
return other.m_hasValue && m_value == other.m_value;
|
|
||||||
else
|
|
||||||
return !other.m_hasValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Optional& other) const
|
|
||||||
{
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasValue() const
|
|
||||||
{
|
|
||||||
return m_hasValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
T valueOr(const T& other) const
|
|
||||||
{
|
|
||||||
return m_hasValue ? m_value : other;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional static makeOptional(const T& value)
|
|
||||||
{
|
|
||||||
return Optional(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_hasValue;
|
|
||||||
T m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // OPTIONAL_H
|
|
@ -36,6 +36,14 @@ ScreenLockListenerDBus::ScreenLockListenerDBus(QWidget *parent):
|
|||||||
this, //receiver
|
this, //receiver
|
||||||
SLOT(freedesktopScreenSaver(bool)));
|
SLOT(freedesktopScreenSaver(bool)));
|
||||||
|
|
||||||
|
sessionBus.connect(
|
||||||
|
"org.gnome.ScreenSaver", // service
|
||||||
|
"/org/gnome/ScreenSaver", // path
|
||||||
|
"org.gnome.ScreenSaver", // interface
|
||||||
|
"ActiveChanged", // signal name
|
||||||
|
this, //receiver
|
||||||
|
SLOT(freedesktopScreenSaver(bool)));
|
||||||
|
|
||||||
sessionBus.connect(
|
sessionBus.connect(
|
||||||
"org.gnome.SessionManager", // service
|
"org.gnome.SessionManager", // service
|
||||||
"/org/gnome/SessionManager/Presence", // path
|
"/org/gnome/SessionManager/Presence", // path
|
||||||
|
@ -74,6 +74,11 @@ bool SymmetricCipher::reset()
|
|||||||
return m_backend->reset();
|
return m_backend->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SymmetricCipher::keySize() const
|
||||||
|
{
|
||||||
|
return m_backend->keySize();
|
||||||
|
}
|
||||||
|
|
||||||
int SymmetricCipher::blockSize() const
|
int SymmetricCipher::blockSize() const
|
||||||
{
|
{
|
||||||
return m_backend->blockSize();
|
return m_backend->blockSize();
|
||||||
|
@ -38,6 +38,7 @@ public:
|
|||||||
enum Mode
|
enum Mode
|
||||||
{
|
{
|
||||||
Cbc,
|
Cbc,
|
||||||
|
Ctr,
|
||||||
Ecb,
|
Ecb,
|
||||||
Stream
|
Stream
|
||||||
};
|
};
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool reset();
|
bool reset();
|
||||||
|
int keySize() const;
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0;
|
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0;
|
||||||
|
|
||||||
virtual bool reset() = 0;
|
virtual bool reset() = 0;
|
||||||
|
virtual int keySize() const = 0;
|
||||||
virtual int blockSize() const = 0;
|
virtual int blockSize() const = 0;
|
||||||
|
|
||||||
virtual QString errorString() const = 0;
|
virtual QString errorString() const = 0;
|
||||||
|
@ -26,7 +26,6 @@ SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, Sy
|
|||||||
, m_algo(gcryptAlgo(algo))
|
, m_algo(gcryptAlgo(algo))
|
||||||
, m_mode(gcryptMode(mode))
|
, m_mode(gcryptMode(mode))
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
, m_blockSize(-1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +61,9 @@ int SymmetricCipherGcrypt::gcryptMode(SymmetricCipher::Mode mode)
|
|||||||
case SymmetricCipher::Cbc:
|
case SymmetricCipher::Cbc:
|
||||||
return GCRY_CIPHER_MODE_CBC;
|
return GCRY_CIPHER_MODE_CBC;
|
||||||
|
|
||||||
|
case SymmetricCipher::Ctr:
|
||||||
|
return GCRY_CIPHER_MODE_CTR;
|
||||||
|
|
||||||
case SymmetricCipher::Stream:
|
case SymmetricCipher::Stream:
|
||||||
return GCRY_CIPHER_MODE_STREAM;
|
return GCRY_CIPHER_MODE_STREAM;
|
||||||
|
|
||||||
@ -86,20 +88,14 @@ bool SymmetricCipherGcrypt::init()
|
|||||||
|
|
||||||
gcry_error_t error;
|
gcry_error_t error;
|
||||||
|
|
||||||
|
if(m_ctx != nullptr)
|
||||||
|
gcry_cipher_close(m_ctx);
|
||||||
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
setErrorString(error);
|
setErrorString(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t blockSizeT;
|
|
||||||
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, nullptr, &blockSizeT);
|
|
||||||
if (error != 0) {
|
|
||||||
setErrorString(error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_blockSize = blockSizeT;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +115,13 @@ bool SymmetricCipherGcrypt::setKey(const QByteArray& key)
|
|||||||
bool SymmetricCipherGcrypt::setIv(const QByteArray& iv)
|
bool SymmetricCipherGcrypt::setIv(const QByteArray& iv)
|
||||||
{
|
{
|
||||||
m_iv = iv;
|
m_iv = iv;
|
||||||
gcry_error_t error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
gcry_error_t error;
|
||||||
|
|
||||||
|
if (m_mode == GCRY_CIPHER_MODE_CTR) {
|
||||||
|
error = gcry_cipher_setctr(m_ctx, m_iv.constData(), m_iv.size());
|
||||||
|
} else {
|
||||||
|
error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
||||||
|
}
|
||||||
|
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
setErrorString(error);
|
setErrorString(error);
|
||||||
@ -228,9 +230,28 @@ bool SymmetricCipherGcrypt::reset()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SymmetricCipherGcrypt::keySize() const
|
||||||
|
{
|
||||||
|
gcry_error_t error;
|
||||||
|
size_t keySizeT;
|
||||||
|
|
||||||
|
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_KEYLEN, nullptr, &keySizeT);
|
||||||
|
if (error != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return keySizeT;
|
||||||
|
}
|
||||||
|
|
||||||
int SymmetricCipherGcrypt::blockSize() const
|
int SymmetricCipherGcrypt::blockSize() const
|
||||||
{
|
{
|
||||||
return m_blockSize;
|
gcry_error_t error;
|
||||||
|
size_t blockSizeT;
|
||||||
|
|
||||||
|
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, nullptr, &blockSizeT);
|
||||||
|
if (error != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return blockSizeT;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SymmetricCipherGcrypt::errorString() const
|
QString SymmetricCipherGcrypt::errorString() const
|
||||||
|
@ -39,6 +39,7 @@ public:
|
|||||||
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data, quint64 rounds);
|
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data, quint64 rounds);
|
||||||
|
|
||||||
bool reset();
|
bool reset();
|
||||||
|
int keySize() const;
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
|
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
@ -54,7 +55,6 @@ private:
|
|||||||
const SymmetricCipher::Direction m_direction;
|
const SymmetricCipher::Direction m_direction;
|
||||||
QByteArray m_key;
|
QByteArray m_key;
|
||||||
QByteArray m_iv;
|
QByteArray m_iv;
|
||||||
int m_blockSize;
|
|
||||||
QString m_errorString;
|
QString m_errorString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,36 +19,29 @@
|
|||||||
#include "KeePass2Repair.h"
|
#include "KeePass2Repair.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
#include <QScopedPointer>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
|
|
||||||
#include "format/KeePass2RandomStream.h"
|
#include "format/KeePass2RandomStream.h"
|
||||||
#include "format/KeePass2Reader.h"
|
#include "format/KeePass2Reader.h"
|
||||||
#include "format/KeePass2XmlReader.h"
|
#include "format/KeePass2XmlReader.h"
|
||||||
|
|
||||||
KeePass2Repair::KeePass2Repair()
|
KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
|
||||||
: m_db(nullptr)
|
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
|
|
||||||
{
|
|
||||||
m_db = nullptr;
|
|
||||||
m_errorStr.clear();
|
m_errorStr.clear();
|
||||||
|
|
||||||
KeePass2Reader reader;
|
KeePass2Reader reader;
|
||||||
reader.setSaveXml(true);
|
reader.setSaveXml(true);
|
||||||
|
|
||||||
Database* db = reader.readDatabase(device, key, true);
|
QScopedPointer<Database> db(reader.readDatabase(device, key, true));
|
||||||
if (!reader.hasError()) {
|
if (!reader.hasError()) {
|
||||||
delete db;
|
return qMakePair(NothingTodo, nullptr);
|
||||||
return NothingTodo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray xmlData = reader.xmlData();
|
QByteArray xmlData = reader.xmlData();
|
||||||
if (!db || xmlData.isEmpty()) {
|
if (!db || xmlData.isEmpty()) {
|
||||||
delete db;
|
|
||||||
m_errorStr = reader.errorString();
|
m_errorStr = reader.errorString();
|
||||||
return UnableToOpen;
|
return qMakePair(UnableToOpen, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool repairAction = false;
|
bool repairAction = false;
|
||||||
@ -59,8 +53,7 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
|
|||||||
&& encodingRegExp.cap(1).compare("utf8", Qt::CaseInsensitive) != 0)
|
&& encodingRegExp.cap(1).compare("utf8", Qt::CaseInsensitive) != 0)
|
||||||
{
|
{
|
||||||
// database is not utf-8 encoded, we don't support repairing that
|
// database is not utf-8 encoded, we don't support repairing that
|
||||||
delete db;
|
return qMakePair(RepairFailed, nullptr);
|
||||||
return RepairFailed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +68,7 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
|
|||||||
|
|
||||||
if (!repairAction) {
|
if (!repairAction) {
|
||||||
// we were unable to find the problem
|
// we were unable to find the problem
|
||||||
delete db;
|
return qMakePair(RepairFailed, nullptr);
|
||||||
return RepairFailed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream;
|
KeePass2RandomStream randomStream;
|
||||||
@ -84,23 +76,16 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
|
|||||||
KeePass2XmlReader xmlReader;
|
KeePass2XmlReader xmlReader;
|
||||||
QBuffer buffer(&xmlData);
|
QBuffer buffer(&xmlData);
|
||||||
buffer.open(QIODevice::ReadOnly);
|
buffer.open(QIODevice::ReadOnly);
|
||||||
xmlReader.readDatabase(&buffer, db, &randomStream);
|
xmlReader.readDatabase(&buffer, db.data(), &randomStream);
|
||||||
|
|
||||||
if (xmlReader.hasError()) {
|
if (xmlReader.hasError()) {
|
||||||
delete db;
|
return qMakePair(RepairFailed, nullptr);
|
||||||
return RepairFailed;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_db = db;
|
return qMakePair(RepairSuccess, db.take());
|
||||||
return RepairSuccess;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Database* KeePass2Repair::database() const
|
|
||||||
{
|
|
||||||
return m_db;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString KeePass2Repair::errorString() const
|
QString KeePass2Repair::errorString() const
|
||||||
{
|
{
|
||||||
return m_errorStr;
|
return m_errorStr;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -20,6 +21,7 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
#include "keys/CompositeKey.h"
|
#include "keys/CompositeKey.h"
|
||||||
@ -36,14 +38,12 @@ public:
|
|||||||
RepairSuccess,
|
RepairSuccess,
|
||||||
RepairFailed
|
RepairFailed
|
||||||
};
|
};
|
||||||
|
using RepairOutcome = QPair<RepairResult, Database*>;
|
||||||
|
|
||||||
KeePass2Repair();
|
RepairOutcome repairDatabase(QIODevice* device, const CompositeKey& key);
|
||||||
RepairResult repairDatabase(QIODevice* device, const CompositeKey& key);
|
|
||||||
Database* database() const;
|
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Database* m_db;
|
|
||||||
QString m_errorStr;
|
QString m_errorStr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,6 +87,9 @@ AboutDialog::AboutDialog(QWidget* parent)
|
|||||||
#ifdef WITH_XC_YUBIKEY
|
#ifdef WITH_XC_YUBIKEY
|
||||||
extensions += "\n- YubiKey";
|
extensions += "\n- YubiKey";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_XC_SSHAGENT
|
||||||
|
extensions += "\n- SSH Agent";
|
||||||
|
#endif
|
||||||
|
|
||||||
if (extensions.isEmpty())
|
if (extensions.isEmpty())
|
||||||
extensions = " None";
|
extensions = " None";
|
||||||
|
@ -28,6 +28,9 @@ Clipboard* Clipboard::m_instance(nullptr);
|
|||||||
Clipboard::Clipboard(QObject* parent)
|
Clipboard::Clipboard(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_timer(new QTimer(this))
|
, m_timer(new QTimer(this))
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
, m_pasteboard(new MacPasteboard)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
m_timer->setSingleShot(true);
|
m_timer->setSingleShot(true);
|
||||||
connect(m_timer, SIGNAL(timeout()), SLOT(clearClipboard()));
|
connect(m_timer, SIGNAL(timeout()), SLOT(clearClipboard()));
|
||||||
@ -38,10 +41,17 @@ void Clipboard::setText(const QString& text)
|
|||||||
{
|
{
|
||||||
QClipboard* clipboard = QApplication::clipboard();
|
QClipboard* clipboard = QApplication::clipboard();
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QMimeData* mime = new QMimeData;
|
||||||
|
mime->setText(text);
|
||||||
|
mime->setData("application/x-nspasteboard-concealed-type", text.toUtf8());
|
||||||
|
clipboard->setMimeData(mime, QClipboard::Clipboard);
|
||||||
|
#else
|
||||||
clipboard->setText(text, QClipboard::Clipboard);
|
clipboard->setText(text, QClipboard::Clipboard);
|
||||||
if (clipboard->supportsSelection()) {
|
if (clipboard->supportsSelection()) {
|
||||||
clipboard->setText(text, QClipboard::Selection);
|
clipboard->setText(text, QClipboard::Selection);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (config()->get("security/clearclipboard").toBool()) {
|
if (config()->get("security/clearclipboard").toBool()) {
|
||||||
int timeout = config()->get("security/clearclipboardtimeout").toInt();
|
int timeout = config()->get("security/clearclipboardtimeout").toInt();
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
#define KEEPASSX_CLIPBOARD_H
|
#define KEEPASSX_CLIPBOARD_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "core/MacPasteboard.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
@ -43,6 +46,9 @@ private:
|
|||||||
static Clipboard* m_instance;
|
static Clipboard* m_instance;
|
||||||
|
|
||||||
QTimer* m_timer;
|
QTimer* m_timer;
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QScopedPointer<MacPasteboard> m_pasteboard;
|
||||||
|
#endif
|
||||||
QString m_lastCopied;
|
QString m_lastCopied;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,6 +277,9 @@ void DatabaseOpenWidget::activateChallengeResponse()
|
|||||||
void DatabaseOpenWidget::browseKeyFile()
|
void DatabaseOpenWidget::browseKeyFile()
|
||||||
{
|
{
|
||||||
QString filters = QString("%1 (*);;%2 (*.key)").arg(tr("All files"), tr("Key files"));
|
QString filters = QString("%1 (*);;%2 (*.key)").arg(tr("All files"), tr("Key files"));
|
||||||
|
if (!config()->get("RememberLastKeyFiles").toBool()) {
|
||||||
|
fileDialog()->setNextForgetDialog();
|
||||||
|
}
|
||||||
QString filename = fileDialog()->getOpenFileName(this, tr("Select key file"), QString(), filters);
|
QString filename = fileDialog()->getOpenFileName(this, tr("Select key file"), QString(), filters);
|
||||||
|
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -69,7 +70,8 @@ void DatabaseRepairWidget::openDatabase()
|
|||||||
delete m_db;
|
delete m_db;
|
||||||
}
|
}
|
||||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
KeePass2Repair::RepairResult repairResult = repair.repairDatabase(&file, masterKey);
|
auto repairOutcome = repair.repairDatabase(&file, masterKey);
|
||||||
|
KeePass2Repair::RepairResult repairResult = repairOutcome.first;
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
switch (repairResult) {
|
switch (repairResult) {
|
||||||
@ -83,7 +85,7 @@ void DatabaseRepairWidget::openDatabase()
|
|||||||
emit editFinished(false);
|
emit editFinished(false);
|
||||||
return;
|
return;
|
||||||
case KeePass2Repair::RepairSuccess:
|
case KeePass2Repair::RepairSuccess:
|
||||||
m_db = repair.database();
|
m_db = repairOutcome.second;
|
||||||
MessageBox::warning(this, tr("Success"), tr("The database has been successfully repaired\nYou can now save it."));
|
MessageBox::warning(this, tr("Success"), tr("The database has been successfully repaired\nYou can now save it."));
|
||||||
emit editFinished(true);
|
emit editFinished(true);
|
||||||
return;
|
return;
|
||||||
|
@ -298,8 +298,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
|
|||||||
if (!saveDatabase(db)) {
|
if (!saveDatabase(db)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} else if (dbStruct.dbWidget->currentMode() != DatabaseWidget::LockedMode) {
|
||||||
else {
|
|
||||||
QMessageBox::StandardButton result =
|
QMessageBox::StandardButton result =
|
||||||
MessageBox::question(
|
MessageBox::question(
|
||||||
this, tr("Save changes?"),
|
this, tr("Save changes?"),
|
||||||
@ -309,8 +308,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
|
|||||||
if (!saveDatabase(db)) {
|
if (!saveDatabase(db)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} else if (result == QMessageBox::Cancel) {
|
||||||
else if (result == QMessageBox::Cancel) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,8 +353,13 @@ bool DatabaseTabWidget::saveDatabase(Database* db)
|
|||||||
{
|
{
|
||||||
DatabaseManagerStruct& dbStruct = m_dbList[db];
|
DatabaseManagerStruct& dbStruct = m_dbList[db];
|
||||||
|
|
||||||
if (dbStruct.saveToFilename) {
|
if (dbStruct.dbWidget->currentMode() == DatabaseWidget::LockedMode) {
|
||||||
|
// Never allow saving a locked database; it causes corruption
|
||||||
|
// We return true since a save is not required
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbStruct.saveToFilename) {
|
||||||
dbStruct.dbWidget->blockAutoReload(true);
|
dbStruct.dbWidget->blockAutoReload(true);
|
||||||
QString errorMessage = db->saveToFile(dbStruct.canonicalFilePath);
|
QString errorMessage = db->saveToFile(dbStruct.canonicalFilePath);
|
||||||
dbStruct.dbWidget->blockAutoReload(false);
|
dbStruct.dbWidget->blockAutoReload(false);
|
||||||
@ -375,7 +378,6 @@ bool DatabaseTabWidget::saveDatabase(Database* db)
|
|||||||
MessageWidget::Error);
|
MessageWidget::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return saveDatabaseAs(db);
|
return saveDatabaseAs(db);
|
||||||
}
|
}
|
||||||
@ -622,6 +624,36 @@ void DatabaseTabWidget::updateTabNameFromDbWidgetSender()
|
|||||||
|
|
||||||
DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
|
DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
|
||||||
updateTabName(databaseFromDatabaseWidget(dbWidget));
|
updateTabName(databaseFromDatabaseWidget(dbWidget));
|
||||||
|
|
||||||
|
Database* db = dbWidget->database();
|
||||||
|
Group *autoload = db->rootGroup()->findChildByName("AutoOpen");
|
||||||
|
if (autoload) {
|
||||||
|
const DatabaseManagerStruct& dbStruct = m_dbList.value(db);
|
||||||
|
QFileInfo dbpath(dbStruct.canonicalFilePath);
|
||||||
|
QDir dbFolder(dbpath.canonicalPath());
|
||||||
|
for (auto entry : autoload->entries()) {
|
||||||
|
if (entry->url().isEmpty() || entry->password().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QFileInfo filepath;
|
||||||
|
if (entry->url().startsWith("file://")) {
|
||||||
|
QUrl url(entry->url());
|
||||||
|
filepath.setFile(url.toLocalFile());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filepath.setFile(entry->url());
|
||||||
|
if (filepath.isRelative()) {
|
||||||
|
filepath.setFile(dbFolder, entry->url());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filepath.isFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
openDatabase(filepath.canonicalFilePath(), entry->password(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DatabaseTabWidget::databaseIndex(Database* db)
|
int DatabaseTabWidget::databaseIndex(Database* db)
|
||||||
|
@ -57,12 +57,19 @@
|
|||||||
#include "gui/group/EditGroupWidget.h"
|
#include "gui/group/EditGroupWidget.h"
|
||||||
#include "gui/group/GroupView.h"
|
#include "gui/group/GroupView.h"
|
||||||
|
|
||||||
|
#include "config-keepassx.h"
|
||||||
|
|
||||||
|
#ifdef WITH_XC_SSHAGENT
|
||||||
|
#include "sshagent/SSHAgent.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
||||||
: QStackedWidget(parent)
|
: QStackedWidget(parent)
|
||||||
, m_db(db)
|
, m_db(db)
|
||||||
, m_newGroup(nullptr)
|
, m_newGroup(nullptr)
|
||||||
, m_newEntry(nullptr)
|
, m_newEntry(nullptr)
|
||||||
, m_newParent(nullptr)
|
, m_newParent(nullptr)
|
||||||
|
, m_importingCsv(false)
|
||||||
{
|
{
|
||||||
m_mainWidget = new QWidget(this);
|
m_mainWidget = new QWidget(this);
|
||||||
|
|
||||||
@ -73,15 +80,15 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
QLayout* layout = new QHBoxLayout();
|
QLayout* layout = new QHBoxLayout();
|
||||||
mainLayout->addWidget(m_messageWidget);
|
mainLayout->addWidget(m_messageWidget);
|
||||||
mainLayout->addLayout(layout);
|
mainLayout->addLayout(layout);
|
||||||
m_splitter = new QSplitter(m_mainWidget);
|
m_mainSplitter = new QSplitter(m_mainWidget);
|
||||||
m_splitter->setChildrenCollapsible(false);
|
m_mainSplitter->setChildrenCollapsible(false);
|
||||||
m_detailSplitter = new QSplitter(m_mainWidget);
|
m_detailSplitter = new QSplitter(m_mainWidget);
|
||||||
m_detailSplitter->setOrientation(Qt::Vertical);
|
m_detailSplitter->setOrientation(Qt::Vertical);
|
||||||
m_detailSplitter->setChildrenCollapsible(true);
|
m_detailSplitter->setChildrenCollapsible(true);
|
||||||
|
|
||||||
QWidget* rightHandSideWidget = new QWidget(m_splitter);
|
QWidget* rightHandSideWidget = new QWidget(m_mainSplitter);
|
||||||
|
|
||||||
m_groupView = new GroupView(db, m_splitter);
|
m_groupView = new GroupView(db, m_mainSplitter);
|
||||||
m_groupView->setObjectName("groupView");
|
m_groupView->setObjectName("groupView");
|
||||||
m_groupView->setContextMenuPolicy(Qt::CustomContextMenu);
|
m_groupView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)),
|
connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)),
|
||||||
@ -122,13 +129,13 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
|
|
||||||
setTabOrder(m_entryView, m_groupView);
|
setTabOrder(m_entryView, m_groupView);
|
||||||
|
|
||||||
m_splitter->addWidget(m_groupView);
|
m_mainSplitter->addWidget(m_groupView);
|
||||||
m_splitter->addWidget(rightHandSideWidget);
|
m_mainSplitter->addWidget(rightHandSideWidget);
|
||||||
|
|
||||||
m_splitter->setStretchFactor(0, 30);
|
m_mainSplitter->setStretchFactor(0, 30);
|
||||||
m_splitter->setStretchFactor(1, 70);
|
m_mainSplitter->setStretchFactor(1, 70);
|
||||||
|
|
||||||
layout->addWidget(m_splitter);
|
layout->addWidget(m_mainSplitter);
|
||||||
m_mainWidget->setLayout(mainLayout);
|
m_mainWidget->setLayout(mainLayout);
|
||||||
|
|
||||||
m_editEntryWidget = new EditEntryWidget();
|
m_editEntryWidget = new EditEntryWidget();
|
||||||
@ -137,13 +144,14 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
m_editGroupWidget = new EditGroupWidget();
|
m_editGroupWidget = new EditGroupWidget();
|
||||||
m_editGroupWidget->setObjectName("editGroupWidget");
|
m_editGroupWidget->setObjectName("editGroupWidget");
|
||||||
m_changeMasterKeyWidget = new ChangeMasterKeyWidget();
|
m_changeMasterKeyWidget = new ChangeMasterKeyWidget();
|
||||||
|
m_changeMasterKeyWidget->setObjectName("changeMasterKeyWidget");
|
||||||
m_changeMasterKeyWidget->headlineLabel()->setText(tr("Change master key"));
|
m_changeMasterKeyWidget->headlineLabel()->setText(tr("Change master key"));
|
||||||
m_csvImportWizard = new CsvImportWizard();
|
|
||||||
m_csvImportWizard->setObjectName("csvImportWizard");
|
|
||||||
QFont headlineLabelFont = m_changeMasterKeyWidget->headlineLabel()->font();
|
QFont headlineLabelFont = m_changeMasterKeyWidget->headlineLabel()->font();
|
||||||
headlineLabelFont.setBold(true);
|
headlineLabelFont.setBold(true);
|
||||||
headlineLabelFont.setPointSize(headlineLabelFont.pointSize() + 2);
|
headlineLabelFont.setPointSize(headlineLabelFont.pointSize() + 2);
|
||||||
m_changeMasterKeyWidget->headlineLabel()->setFont(headlineLabelFont);
|
m_changeMasterKeyWidget->headlineLabel()->setFont(headlineLabelFont);
|
||||||
|
m_csvImportWizard = new CsvImportWizard();
|
||||||
|
m_csvImportWizard->setObjectName("csvImportWizard");
|
||||||
m_databaseSettingsWidget = new DatabaseSettingsWidget();
|
m_databaseSettingsWidget = new DatabaseSettingsWidget();
|
||||||
m_databaseSettingsWidget->setObjectName("databaseSettingsWidget");
|
m_databaseSettingsWidget->setObjectName("databaseSettingsWidget");
|
||||||
m_databaseOpenWidget = new DatabaseOpenWidget();
|
m_databaseOpenWidget = new DatabaseOpenWidget();
|
||||||
@ -168,7 +176,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
addWidget(m_keepass1OpenWidget);
|
addWidget(m_keepass1OpenWidget);
|
||||||
addWidget(m_unlockDatabaseWidget);
|
addWidget(m_unlockDatabaseWidget);
|
||||||
|
|
||||||
connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged()));
|
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
|
||||||
|
connect(m_detailSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(detailSplitterSizesChanged()));
|
||||||
connect(m_entryView->header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(entryColumnSizesChanged()));
|
connect(m_entryView->header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(entryColumnSizesChanged()));
|
||||||
connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*)));
|
connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*)));
|
||||||
connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged()));
|
connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged()));
|
||||||
@ -207,6 +216,13 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
m_searchCaseSensitive = false;
|
m_searchCaseSensitive = false;
|
||||||
m_searchLimitGroup = config()->get("SearchLimitGroup", false).toBool();
|
m_searchLimitGroup = config()->get("SearchLimitGroup", false).toBool();
|
||||||
|
|
||||||
|
#ifdef WITH_XC_SSHAGENT
|
||||||
|
if (config()->get("SSHAgent", false).toBool()) {
|
||||||
|
connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), SSHAgent::instance(), SLOT(databaseModeChanged(DatabaseWidget::Mode)));
|
||||||
|
connect(this, SIGNAL(closeRequest()), SSHAgent::instance(), SLOT(databaseModeChanged()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
setCurrentWidget(m_mainWidget);
|
setCurrentWidget(m_mainWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,14 +267,24 @@ bool DatabaseWidget::isEditWidgetModified() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> DatabaseWidget::splitterSizes() const
|
QList<int> DatabaseWidget::mainSplitterSizes() const
|
||||||
{
|
{
|
||||||
return m_splitter->sizes();
|
return m_mainSplitter->sizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::setSplitterSizes(const QList<int>& sizes)
|
void DatabaseWidget::setMainSplitterSizes(const QList<int>& sizes)
|
||||||
{
|
{
|
||||||
m_splitter->setSizes(sizes);
|
m_mainSplitter->setSizes(sizes);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<int> DatabaseWidget::detailSplitterSizes() const
|
||||||
|
{
|
||||||
|
return m_detailSplitter->sizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setDetailSplitterSizes(const QList<int> &sizes)
|
||||||
|
{
|
||||||
|
m_detailSplitter->setSizes(sizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> DatabaseWidget::entryHeaderViewSizes() const
|
QList<int> DatabaseWidget::entryHeaderViewSizes() const
|
||||||
@ -391,6 +417,8 @@ void DatabaseWidget::setupTotp()
|
|||||||
setupTotpDialog->setSeed(currentEntry->totpSeed());
|
setupTotpDialog->setSeed(currentEntry->totpSeed());
|
||||||
setupTotpDialog->setStep(currentEntry->totpStep());
|
setupTotpDialog->setStep(currentEntry->totpStep());
|
||||||
setupTotpDialog->setDigits(currentEntry->totpDigits());
|
setupTotpDialog->setDigits(currentEntry->totpDigits());
|
||||||
|
// now that all settings are set, decide whether it's default, steam or custom
|
||||||
|
setupTotpDialog->setSettings(currentEntry->totpDigits());
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTotpDialog->open();
|
setupTotpDialog->open();
|
||||||
@ -771,6 +799,12 @@ void DatabaseWidget::switchToGroupEdit(Group* group, bool create)
|
|||||||
|
|
||||||
void DatabaseWidget::updateMasterKey(bool accepted)
|
void DatabaseWidget::updateMasterKey(bool accepted)
|
||||||
{
|
{
|
||||||
|
if (m_importingCsv) {
|
||||||
|
setCurrentWidget(m_csvImportWizard);
|
||||||
|
m_csvImportWizard->keyFinished(accepted, m_changeMasterKeyWidget->newMasterKey());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (accepted) {
|
if (accepted) {
|
||||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
bool result = m_db->setKey(m_changeMasterKeyWidget->newMasterKey());
|
bool result = m_db->setKey(m_changeMasterKeyWidget->newMasterKey());
|
||||||
@ -904,6 +938,7 @@ void DatabaseWidget::switchToMasterKeyChange(bool disableCancel)
|
|||||||
m_changeMasterKeyWidget->clearForms();
|
m_changeMasterKeyWidget->clearForms();
|
||||||
m_changeMasterKeyWidget->setCancelEnabled(!disableCancel);
|
m_changeMasterKeyWidget->setCancelEnabled(!disableCancel);
|
||||||
setCurrentWidget(m_changeMasterKeyWidget);
|
setCurrentWidget(m_changeMasterKeyWidget);
|
||||||
|
m_importingCsv = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::switchToDatabaseSettings()
|
void DatabaseWidget::switchToDatabaseSettings()
|
||||||
@ -915,8 +950,13 @@ void DatabaseWidget::switchToDatabaseSettings()
|
|||||||
void DatabaseWidget::switchToOpenDatabase(const QString& fileName)
|
void DatabaseWidget::switchToOpenDatabase(const QString& fileName)
|
||||||
{
|
{
|
||||||
updateFilename(fileName);
|
updateFilename(fileName);
|
||||||
|
if (m_databaseOpenWidget) {
|
||||||
m_databaseOpenWidget->load(fileName);
|
m_databaseOpenWidget->load(fileName);
|
||||||
setCurrentWidget(m_databaseOpenWidget);
|
setCurrentWidget(m_databaseOpenWidget);
|
||||||
|
} else if (m_unlockDatabaseWidget) {
|
||||||
|
m_unlockDatabaseWidget->load(fileName);
|
||||||
|
setCurrentWidget(m_unlockDatabaseWidget);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::switchToOpenDatabase(const QString& fileName, const QString& password,
|
void DatabaseWidget::switchToOpenDatabase(const QString& fileName, const QString& password,
|
||||||
@ -924,15 +964,21 @@ void DatabaseWidget::switchToOpenDatabase(const QString& fileName, const QString
|
|||||||
{
|
{
|
||||||
updateFilename(fileName);
|
updateFilename(fileName);
|
||||||
switchToOpenDatabase(fileName);
|
switchToOpenDatabase(fileName);
|
||||||
|
if (m_databaseOpenWidget) {
|
||||||
m_databaseOpenWidget->enterKey(password, keyFile);
|
m_databaseOpenWidget->enterKey(password, keyFile);
|
||||||
|
} else if (m_unlockDatabaseWidget) {
|
||||||
|
m_unlockDatabaseWidget->enterKey(password, keyFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::switchToImportCsv(const QString& fileName)
|
void DatabaseWidget::switchToImportCsv(const QString& fileName)
|
||||||
{
|
{
|
||||||
updateFilename(fileName);
|
updateFilename(fileName);
|
||||||
switchToMasterKeyChange();
|
|
||||||
m_csvImportWizard->load(fileName, m_db);
|
m_csvImportWizard->load(fileName, m_db);
|
||||||
setCurrentWidget(m_csvImportWizard);
|
m_changeMasterKeyWidget->clearForms();
|
||||||
|
m_changeMasterKeyWidget->setCancelEnabled(false);
|
||||||
|
setCurrentWidget(m_changeMasterKeyWidget);
|
||||||
|
m_importingCsv = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::switchToOpenMergeDatabase(const QString& fileName)
|
void DatabaseWidget::switchToOpenMergeDatabase(const QString& fileName)
|
||||||
@ -1169,7 +1215,7 @@ void DatabaseWidget::onWatchedFileChanged()
|
|||||||
|
|
||||||
void DatabaseWidget::reloadDatabaseFile()
|
void DatabaseWidget::reloadDatabaseFile()
|
||||||
{
|
{
|
||||||
if (m_db == nullptr) {
|
if (!m_db || currentMode() == DatabaseWidget::LockedMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,7 +1247,7 @@ void DatabaseWidget::reloadDatabaseFile()
|
|||||||
if (m_databaseModified) {
|
if (m_databaseModified) {
|
||||||
// Ask if we want to merge changes into new database
|
// Ask if we want to merge changes into new database
|
||||||
QMessageBox::StandardButton mb = MessageBox::question(this, tr("Merge Request"),
|
QMessageBox::StandardButton mb = MessageBox::question(this, tr("Merge Request"),
|
||||||
tr("The database file has changed and you have unsaved changes."
|
tr("The database file has changed and you have unsaved changes.\n"
|
||||||
"Do you want to merge your changes?"),
|
"Do you want to merge your changes?"),
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
@ -1365,6 +1411,12 @@ void DatabaseWidget::showUnlockDialog()
|
|||||||
{
|
{
|
||||||
m_unlockDatabaseDialog->clearForms();
|
m_unlockDatabaseDialog->clearForms();
|
||||||
m_unlockDatabaseDialog->setDBFilename(m_filename);
|
m_unlockDatabaseDialog->setDBFilename(m_filename);
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
autoType()->raiseWindow();
|
||||||
|
Tools::wait(500);
|
||||||
|
#endif
|
||||||
|
|
||||||
m_unlockDatabaseDialog->show();
|
m_unlockDatabaseDialog->show();
|
||||||
m_unlockDatabaseDialog->activateWindow();
|
m_unlockDatabaseDialog->activateWindow();
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,10 @@ public:
|
|||||||
bool isGroupSelected() const;
|
bool isGroupSelected() const;
|
||||||
bool isInEditMode() const;
|
bool isInEditMode() const;
|
||||||
bool isEditWidgetModified() const;
|
bool isEditWidgetModified() const;
|
||||||
QList<int> splitterSizes() const;
|
QList<int> mainSplitterSizes() const;
|
||||||
void setSplitterSizes(const QList<int>& sizes);
|
void setMainSplitterSizes(const QList<int>& sizes);
|
||||||
|
QList<int> detailSplitterSizes() const;
|
||||||
|
void setDetailSplitterSizes(const QList<int>& sizes);
|
||||||
QList<int> entryHeaderViewSizes() const;
|
QList<int> entryHeaderViewSizes() const;
|
||||||
void setEntryViewHeaderSizes(const QList<int>& sizes);
|
void setEntryViewHeaderSizes(const QList<int>& sizes);
|
||||||
void clearAllWidgets();
|
void clearAllWidgets();
|
||||||
@ -123,7 +125,8 @@ signals:
|
|||||||
void listModeActivated();
|
void listModeActivated();
|
||||||
void searchModeAboutToActivate();
|
void searchModeAboutToActivate();
|
||||||
void searchModeActivated();
|
void searchModeActivated();
|
||||||
void splitterSizesChanged();
|
void mainSplitterSizesChanged();
|
||||||
|
void detailSplitterSizesChanged();
|
||||||
void entryColumnSizesChanged();
|
void entryColumnSizesChanged();
|
||||||
void updateSearch(QString text);
|
void updateSearch(QString text);
|
||||||
|
|
||||||
@ -214,7 +217,7 @@ private:
|
|||||||
KeePass1OpenWidget* m_keepass1OpenWidget;
|
KeePass1OpenWidget* m_keepass1OpenWidget;
|
||||||
UnlockDatabaseWidget* m_unlockDatabaseWidget;
|
UnlockDatabaseWidget* m_unlockDatabaseWidget;
|
||||||
UnlockDatabaseDialog* m_unlockDatabaseDialog;
|
UnlockDatabaseDialog* m_unlockDatabaseDialog;
|
||||||
QSplitter* m_splitter;
|
QSplitter* m_mainSplitter;
|
||||||
QSplitter* m_detailSplitter;
|
QSplitter* m_detailSplitter;
|
||||||
GroupView* m_groupView;
|
GroupView* m_groupView;
|
||||||
EntryView* m_entryView;
|
EntryView* m_entryView;
|
||||||
@ -233,6 +236,9 @@ private:
|
|||||||
bool m_searchCaseSensitive;
|
bool m_searchCaseSensitive;
|
||||||
bool m_searchLimitGroup;
|
bool m_searchLimitGroup;
|
||||||
|
|
||||||
|
// CSV import state
|
||||||
|
bool m_importingCsv;
|
||||||
|
|
||||||
// Autoreload
|
// Autoreload
|
||||||
QFileSystemWatcher m_fileWatcher;
|
QFileSystemWatcher m_fileWatcher;
|
||||||
QTimer m_fileWatchTimer;
|
QTimer m_fileWatchTimer;
|
||||||
|
@ -25,14 +25,16 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent)
|
|||||||
, m_activeDbWidget(nullptr)
|
, m_activeDbWidget(nullptr)
|
||||||
, m_blockUpdates(false)
|
, m_blockUpdates(false)
|
||||||
{
|
{
|
||||||
m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState"));
|
m_mainSplitterSizes = variantToIntList(config()->get("GUI/SplitterState"));
|
||||||
|
m_detailSplitterSizes = variantToIntList(config()->get("GUI/DetailSplitterState"));
|
||||||
m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes"));
|
m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes"));
|
||||||
m_columnSizesSearch = variantToIntList(config()->get("GUI/EntrySearchColumnSizes"));
|
m_columnSizesSearch = variantToIntList(config()->get("GUI/EntrySearchColumnSizes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseWidgetStateSync::~DatabaseWidgetStateSync()
|
DatabaseWidgetStateSync::~DatabaseWidgetStateSync()
|
||||||
{
|
{
|
||||||
config()->set("GUI/SplitterState", intListToVariant(m_splitterSizes));
|
config()->set("GUI/SplitterState", intListToVariant(m_mainSplitterSizes));
|
||||||
|
config()->set("GUI/DetailSplitterState", intListToVariant(m_detailSplitterSizes));
|
||||||
config()->set("GUI/EntryListColumnSizes", intListToVariant(m_columnSizesList));
|
config()->set("GUI/EntryListColumnSizes", intListToVariant(m_columnSizesList));
|
||||||
config()->set("GUI/EntrySearchColumnSizes", intListToVariant(m_columnSizesSearch));
|
config()->set("GUI/EntrySearchColumnSizes", intListToVariant(m_columnSizesSearch));
|
||||||
}
|
}
|
||||||
@ -48,17 +50,25 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
|
|||||||
if (m_activeDbWidget) {
|
if (m_activeDbWidget) {
|
||||||
m_blockUpdates = true;
|
m_blockUpdates = true;
|
||||||
|
|
||||||
if (!m_splitterSizes.isEmpty())
|
if (!m_mainSplitterSizes.isEmpty()) {
|
||||||
m_activeDbWidget->setSplitterSizes(m_splitterSizes);
|
m_activeDbWidget->setMainSplitterSizes(m_mainSplitterSizes);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_activeDbWidget->isInSearchMode())
|
if (!m_detailSplitterSizes.isEmpty()) {
|
||||||
|
m_activeDbWidget->setDetailSplitterSizes(m_detailSplitterSizes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_activeDbWidget->isInSearchMode()) {
|
||||||
restoreSearchView();
|
restoreSearchView();
|
||||||
else
|
} else {
|
||||||
restoreListView();
|
restoreListView();
|
||||||
|
}
|
||||||
|
|
||||||
m_blockUpdates = false;
|
m_blockUpdates = false;
|
||||||
|
|
||||||
connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()),
|
connect(m_activeDbWidget, SIGNAL(mainSplitterSizesChanged()),
|
||||||
|
SLOT(updateSplitterSizes()));
|
||||||
|
connect(m_activeDbWidget, SIGNAL(detailSplitterSizesChanged()),
|
||||||
SLOT(updateSplitterSizes()));
|
SLOT(updateSplitterSizes()));
|
||||||
connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()),
|
connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()),
|
||||||
SLOT(updateColumnSizes()));
|
SLOT(updateColumnSizes()));
|
||||||
@ -102,7 +112,8 @@ void DatabaseWidgetStateSync::updateSplitterSizes()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_splitterSizes = m_activeDbWidget->splitterSizes();
|
m_mainSplitterSizes = m_activeDbWidget->mainSplitterSizes();
|
||||||
|
m_detailSplitterSizes = m_activeDbWidget->detailSplitterSizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidgetStateSync::updateColumnSizes()
|
void DatabaseWidgetStateSync::updateColumnSizes()
|
||||||
|
@ -46,7 +46,8 @@ private:
|
|||||||
DatabaseWidget* m_activeDbWidget;
|
DatabaseWidget* m_activeDbWidget;
|
||||||
|
|
||||||
bool m_blockUpdates;
|
bool m_blockUpdates;
|
||||||
QList<int> m_splitterSizes;
|
QList<int> m_mainSplitterSizes;
|
||||||
|
QList<int> m_detailSplitterSizes;
|
||||||
QList<int> m_columnSizesList;
|
QList<int> m_columnSizesList;
|
||||||
QList<int> m_columnSizesSearch;
|
QList<int> m_columnSizesSearch;
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "ui_DetailsWidget.h"
|
#include "ui_DetailsWidget.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/FilePath.h"
|
#include "core/FilePath.h"
|
||||||
@ -33,6 +34,7 @@ DetailsWidget::DetailsWidget(QWidget* parent)
|
|||||||
, m_locked(false)
|
, m_locked(false)
|
||||||
, m_currentEntry(nullptr)
|
, m_currentEntry(nullptr)
|
||||||
, m_currentGroup(nullptr)
|
, m_currentGroup(nullptr)
|
||||||
|
, m_timer(nullptr)
|
||||||
, m_attributesWidget(nullptr)
|
, m_attributesWidget(nullptr)
|
||||||
, m_autotypeWidget(nullptr)
|
, m_autotypeWidget(nullptr)
|
||||||
, m_selectedTabEntry(0)
|
, m_selectedTabEntry(0)
|
||||||
@ -108,7 +110,8 @@ void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
|
|||||||
m_ui->usernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username()));
|
m_ui->usernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username()));
|
||||||
|
|
||||||
if (!config()->get("security/hidepassworddetails").toBool()) {
|
if (!config()->get("security/hidepassworddetails").toBool()) {
|
||||||
m_ui->passwordLabel->setText(shortPassword(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password())));
|
m_ui->passwordLabel->setText(
|
||||||
|
shortPassword(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password())));
|
||||||
m_ui->passwordLabel->setToolTip(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()));
|
m_ui->passwordLabel->setToolTip(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()));
|
||||||
} else {
|
} else {
|
||||||
m_ui->passwordLabel->setText("****");
|
m_ui->passwordLabel->setText("****");
|
||||||
@ -136,14 +139,16 @@ void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_currentEntry->hasTotp()) {
|
if (m_currentEntry->hasTotp()) {
|
||||||
m_ui->totpButton->show();
|
|
||||||
updateTotp();
|
|
||||||
|
|
||||||
m_step = m_currentEntry->totpStep();
|
m_step = m_currentEntry->totpStep();
|
||||||
|
|
||||||
|
if (nullptr != m_timer) {
|
||||||
|
m_timer->stop();
|
||||||
|
}
|
||||||
m_timer = new QTimer(this);
|
m_timer = new QTimer(this);
|
||||||
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateTotp()));
|
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateTotp()));
|
||||||
|
updateTotp();
|
||||||
m_timer->start(m_step * 10);
|
m_timer->start(m_step * 10);
|
||||||
|
m_ui->totpButton->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notes = m_currentEntry->notes();
|
QString notes = m_currentEntry->notes();
|
||||||
@ -212,7 +217,6 @@ void DetailsWidget::getSelectedGroup(Group* selectedGroup)
|
|||||||
|
|
||||||
m_ui->tabWidget->setTabEnabled(GroupNotesTab, false);
|
m_ui->tabWidget->setTabEnabled(GroupNotesTab, false);
|
||||||
|
|
||||||
|
|
||||||
m_ui->totpButton->hide();
|
m_ui->totpButton->hide();
|
||||||
m_ui->totpWidget->hide();
|
m_ui->totpWidget->hide();
|
||||||
|
|
||||||
@ -263,14 +267,14 @@ void DetailsWidget::getSelectedGroup(Group* selectedGroup)
|
|||||||
|
|
||||||
void DetailsWidget::updateTotp()
|
void DetailsWidget::updateTotp()
|
||||||
{
|
{
|
||||||
if (m_locked) {
|
if (!m_locked) {
|
||||||
m_timer->stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QString totpCode = m_currentEntry->totp();
|
QString totpCode = m_currentEntry->totp();
|
||||||
QString firstHalf = totpCode.left(totpCode.size() / 2);
|
QString firstHalf = totpCode.left(totpCode.size() / 2);
|
||||||
QString secondHalf = totpCode.right(totpCode.size()/2);
|
QString secondHalf = totpCode.mid(totpCode.size() / 2);
|
||||||
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
|
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
|
||||||
|
} else if (nullptr != m_timer) {
|
||||||
|
m_timer->stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailsWidget::showTotp(bool visible)
|
void DetailsWidget::showTotp(bool visible)
|
||||||
@ -326,7 +330,8 @@ void DetailsWidget::setDatabaseMode(DatabaseWidget::Mode mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailsWidget::updateTabIndex(int index) {
|
void DetailsWidget::updateTabIndex(int index)
|
||||||
|
{
|
||||||
if (m_ui->stackedWidget->currentIndex() == GroupPreview) {
|
if (m_ui->stackedWidget->currentIndex() == GroupPreview) {
|
||||||
m_selectedTabGroup = index;
|
m_selectedTabGroup = index;
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
#ifndef KEEPASSX_DETAILSWIDGET_H
|
#ifndef KEEPASSX_DETAILSWIDGET_H
|
||||||
#define KEEPASSX_DETAILSWIDGET_H
|
#define KEEPASSX_DETAILSWIDGET_H
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "gui/DatabaseWidget.h"
|
#include "gui/DatabaseWidget.h"
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class DetailsWidget;
|
class DetailsWidget;
|
||||||
|
@ -6,22 +6,10 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>600</width>
|
||||||
<height>200</height>
|
<height>200</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0">
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0">
|
||||||
@ -36,12 +24,6 @@
|
|||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
@ -123,7 +105,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="closeButton">
|
<widget class="QToolButton" name="closeButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Generate TOTP Token</string>
|
<string>Close</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
@ -166,85 +148,19 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="entryPage">
|
<widget class="QWidget" name="entryPage">
|
||||||
<layout class="QGridLayout" name="gridLayout" columnstretch="1,3">
|
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,1">
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QLabel" name="usernameLabel">
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="passwordLabel"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLabel" name="urlLabel">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="cursor">
|
|
||||||
<cursorShape>PointingHandCursor</cursorShape>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="expirationLabel_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Expiration</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="urlLabel_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>9</pointsize>
|
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>URL</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLabel" name="expirationLabel"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="passwordLabel_2">
|
<widget class="QLabel" name="passwordLabel_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -267,6 +183,102 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QLabel" name="expirationLabel"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="urlLabel_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>9</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>URL</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QLabel" name="urlLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="cursor">
|
||||||
|
<cursorShape>PointingHandCursor</cursorShape>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="expirationLabel_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Expiration</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel" name="passwordLabel"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
<widget class="QLabel" name="usernameLabel_2">
|
<widget class="QLabel" name="usernameLabel_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -291,41 +303,11 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLabel" name="usernameLabel">
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="groupPage">
|
<widget class="QWidget" name="groupPage">
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,3">
|
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,1">
|
||||||
<item row="2" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLabel" name="groupExpirationLabel">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="autotypeLabel_2">
|
<widget class="QLabel" name="autotypeLabel_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -347,7 +329,30 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="2" column="2">
|
||||||
|
<widget class="QLabel" name="groupExpirationLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
<widget class="QLabel" name="searchingLabel">
|
<widget class="QLabel" name="searchingLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -357,7 +362,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="searchingLabel_2">
|
<widget class="QLabel" name="searchingLabel_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -379,7 +384,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="2">
|
||||||
<widget class="QLabel" name="autotypeLabel">
|
<widget class="QLabel" name="autotypeLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -389,7 +394,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QLabel" name="groupExpirationLabel_2">
|
<widget class="QLabel" name="groupExpirationLabel_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
@ -411,15 +416,18 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="0" column="0">
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="horizontalSpacer_3">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
@ -434,27 +442,9 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Attributes</string>
|
<string>Attributes</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item row="0" column="1">
|
<item>
|
||||||
<widget class="QTextEdit" name="attributesEdit">
|
<widget class="QTextEdit" name="attributesEdit">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>80</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>80</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -469,27 +459,9 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Notes</string>
|
<string>Notes</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item row="0" column="1">
|
<item>
|
||||||
<widget class="QTextEdit" name="notesEdit">
|
<widget class="QTextEdit" name="notesEdit">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>80</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>80</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -504,8 +476,8 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Autotype</string>
|
<string>Autotype</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QTreeWidget" name="autotypeTree">
|
<widget class="QTreeWidget" name="autotypeTree">
|
||||||
<property name="frameShadow">
|
<property name="frameShadow">
|
||||||
<enum>QFrame::Sunken</enum>
|
<enum>QFrame::Sunken</enum>
|
||||||
|
@ -27,7 +27,7 @@ QString FileDialog::getOpenFileName(QWidget* parent, const QString& caption, QSt
|
|||||||
{
|
{
|
||||||
if (!m_nextFileName.isEmpty()) {
|
if (!m_nextFileName.isEmpty()) {
|
||||||
QString result = m_nextFileName;
|
QString result = m_nextFileName;
|
||||||
m_nextFileName = "";
|
m_nextFileName.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -43,11 +43,37 @@ QString FileDialog::getOpenFileName(QWidget* parent, const QString& caption, QSt
|
|||||||
parent->activateWindow();
|
parent->activateWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.isEmpty()) {
|
saveLastDir(result);
|
||||||
config()->set("LastDir", QFileInfo(result).absolutePath());
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
QStringList FileDialog::getOpenFileNames(QWidget *parent, const QString &caption, QString dir,
|
||||||
|
const QString &filter, QString *selectedFilter,
|
||||||
|
QFileDialog::Options options)
|
||||||
|
{
|
||||||
|
if (!m_nextFileNames.isEmpty()) {
|
||||||
|
QStringList results = m_nextFileNames;
|
||||||
|
m_nextFileNames.clear();
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
dir = config()->get("LastDir").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList results = QFileDialog::getOpenFileNames(parent, caption, dir, filter,
|
||||||
|
selectedFilter, options);
|
||||||
|
|
||||||
|
// on Mac OS X the focus is lost after closing the native dialog
|
||||||
|
if (parent) {
|
||||||
|
parent->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!results.isEmpty()) {
|
||||||
|
saveLastDir(results[0]);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +83,7 @@ QString FileDialog::getSaveFileName(QWidget* parent, const QString& caption, QSt
|
|||||||
{
|
{
|
||||||
if (!m_nextFileName.isEmpty()) {
|
if (!m_nextFileName.isEmpty()) {
|
||||||
QString result = m_nextFileName;
|
QString result = m_nextFileName;
|
||||||
m_nextFileName = "";
|
m_nextFileName.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -95,12 +121,34 @@ QString FileDialog::getSaveFileName(QWidget* parent, const QString& caption, QSt
|
|||||||
parent->activateWindow();
|
parent->activateWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.isEmpty()) {
|
saveLastDir(result);
|
||||||
config()->set("LastDir", QFileInfo(result).absolutePath());
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FileDialog::getExistingDirectory(QWidget *parent, const QString &caption, QString dir,
|
||||||
|
QFileDialog::Options options)
|
||||||
|
{
|
||||||
|
if (!m_nextDirName.isEmpty()) {
|
||||||
|
QString result = m_nextDirName;
|
||||||
|
m_nextDirName.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
dir = config()->get("LastDir").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = QFileDialog::getExistingDirectory(parent, caption, dir, options);
|
||||||
|
|
||||||
|
// on Mac OS X the focus is lost after closing the native dialog
|
||||||
|
if (parent) {
|
||||||
|
parent->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLastDir(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileDialog::setNextFileName(const QString& fileName)
|
void FileDialog::setNextFileName(const QString& fileName)
|
||||||
@ -108,10 +156,33 @@ void FileDialog::setNextFileName(const QString& fileName)
|
|||||||
m_nextFileName = fileName;
|
m_nextFileName = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileDialog::setNextFileNames(const QStringList &fileNames)
|
||||||
|
{
|
||||||
|
m_nextFileNames = fileNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::setNextDirName(const QString &dirName)
|
||||||
|
{
|
||||||
|
m_nextDirName = dirName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::setNextForgetDialog()
|
||||||
|
{
|
||||||
|
m_forgetLastDir = true;
|
||||||
|
}
|
||||||
|
|
||||||
FileDialog::FileDialog()
|
FileDialog::FileDialog()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileDialog::saveLastDir(QString dir) {
|
||||||
|
if (!dir.isEmpty() && !m_forgetLastDir) {
|
||||||
|
config()->set("LastDir", QFileInfo(dir).absolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_forgetLastDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
FileDialog* FileDialog::instance()
|
FileDialog* FileDialog::instance()
|
||||||
{
|
{
|
||||||
if (!m_instance) {
|
if (!m_instance) {
|
||||||
|
@ -26,22 +26,35 @@ public:
|
|||||||
QString getOpenFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
QString getOpenFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||||
QString dir = QString(), const QString& filter = QString(),
|
QString dir = QString(), const QString& filter = QString(),
|
||||||
QString* selectedFilter = nullptr, QFileDialog::Options options = 0);
|
QString* selectedFilter = nullptr, QFileDialog::Options options = 0);
|
||||||
|
QStringList getOpenFileNames(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||||
|
QString dir = QString(), const QString& filter = QString(),
|
||||||
|
QString* selectedFilter = nullptr, QFileDialog::Options options = 0);
|
||||||
QString getSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
QString getSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||||
QString dir = QString(), const QString& filter = QString(),
|
QString dir = QString(), const QString& filter = QString(),
|
||||||
QString* selectedFilter = nullptr, QFileDialog::Options options = 0,
|
QString* selectedFilter = nullptr, QFileDialog::Options options = 0,
|
||||||
const QString& defaultExtension = QString());
|
const QString& defaultExtension = QString());
|
||||||
|
QString getExistingDirectory(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||||
|
QString dir = QString(), QFileDialog::Options options = QFileDialog::ShowDirsOnly);
|
||||||
|
|
||||||
|
void setNextForgetDialog();
|
||||||
/**
|
/**
|
||||||
* Sets the result of the next get* method call.
|
* Sets the result of the next get* method call.
|
||||||
* Use only for testing.
|
* Use only for testing.
|
||||||
*/
|
*/
|
||||||
void setNextFileName(const QString& fileName);
|
void setNextFileName(const QString& fileName);
|
||||||
|
void setNextFileNames(const QStringList& fileNames);
|
||||||
|
void setNextDirName(const QString& dirName);
|
||||||
|
|
||||||
static FileDialog* instance();
|
static FileDialog* instance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileDialog();
|
FileDialog();
|
||||||
QString m_nextFileName;
|
QString m_nextFileName;
|
||||||
|
QStringList m_nextFileNames;
|
||||||
|
QString m_nextDirName;
|
||||||
|
bool m_forgetLastDir = false;
|
||||||
|
|
||||||
|
void saveLastDir(QString);
|
||||||
|
|
||||||
static FileDialog* m_instance;
|
static FileDialog* m_instance;
|
||||||
|
|
||||||
|
36
src/gui/Font.cpp
Normal file
36
src/gui/Font.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Font.h"
|
||||||
|
|
||||||
|
#include <QFontDatabase>
|
||||||
|
|
||||||
|
QFont Font::fixedFont()
|
||||||
|
{
|
||||||
|
QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// try to use Consolas on Windows, because the default Courier New has too many similar characters
|
||||||
|
QFont consolasFont = QFontDatabase().font("Consolas", fixedFont.styleName(), fixedFont.pointSize());
|
||||||
|
const QFont defaultFont;
|
||||||
|
if (fixedFont != defaultFont) {
|
||||||
|
fixedFont = consolasFont;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return fixedFont;
|
||||||
|
}
|
31
src/gui/Font.h
Normal file
31
src/gui/Font.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 KEEPASSX_FONT_H
|
||||||
|
#define KEEPASSX_FONT_H
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
|
||||||
|
class Font
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QFont fixedFont();
|
||||||
|
private:
|
||||||
|
Font() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_FONT_H
|
@ -49,6 +49,11 @@
|
|||||||
#include "http/OptionDialog.h"
|
#include "http/OptionDialog.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XC_SSHAGENT
|
||||||
|
#include "sshagent/AgentSettingsPage.h"
|
||||||
|
#include "sshagent/SSHAgent.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "gui/SettingsWidget.h"
|
#include "gui/SettingsWidget.h"
|
||||||
#include "gui/PasswordGeneratorWidget.h"
|
#include "gui/PasswordGeneratorWidget.h"
|
||||||
|
|
||||||
@ -109,6 +114,8 @@ MainWindow::MainWindow()
|
|||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
|
||||||
|
|
||||||
// Setup the search widget in the toolbar
|
// Setup the search widget in the toolbar
|
||||||
SearchWidget *search = new SearchWidget();
|
SearchWidget *search = new SearchWidget();
|
||||||
search->connectSignals(m_actionMultiplexer);
|
search->connectSignals(m_actionMultiplexer);
|
||||||
@ -121,6 +128,10 @@ MainWindow::MainWindow()
|
|||||||
#ifdef WITH_XC_HTTP
|
#ifdef WITH_XC_HTTP
|
||||||
m_ui->settingsWidget->addSettingsPage(new HttpPlugin(m_ui->tabWidget));
|
m_ui->settingsWidget->addSettingsPage(new HttpPlugin(m_ui->tabWidget));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_XC_SSHAGENT
|
||||||
|
SSHAgent::init(this);
|
||||||
|
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
|
||||||
|
#endif
|
||||||
|
|
||||||
setWindowIcon(filePath()->applicationIcon());
|
setWindowIcon(filePath()->applicationIcon());
|
||||||
m_ui->globalMessageWidget->setHidden(true);
|
m_ui->globalMessageWidget->setHidden(true);
|
||||||
@ -727,8 +738,10 @@ void MainWindow::changeEvent(QEvent* event)
|
|||||||
|
|
||||||
void MainWindow::saveWindowInformation()
|
void MainWindow::saveWindowInformation()
|
||||||
{
|
{
|
||||||
|
if (isVisible()) {
|
||||||
config()->set("GUI/MainWindowGeometry", saveGeometry());
|
config()->set("GUI/MainWindowGeometry", saveGeometry());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::saveLastDatabases()
|
bool MainWindow::saveLastDatabases()
|
||||||
{
|
{
|
||||||
@ -811,11 +824,6 @@ void MainWindow::showGroupContextMenu(const QPoint& globalPos)
|
|||||||
m_ui->menuGroups->popup(globalPos);
|
m_ui->menuGroups->popup(globalPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::saveToolbarState(bool value)
|
|
||||||
{
|
|
||||||
config()->set("ShowToolbar", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
|
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
|
||||||
{
|
{
|
||||||
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
||||||
@ -858,6 +866,7 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
|
|||||||
|
|
||||||
void MainWindow::hideWindow()
|
void MainWindow::hideWindow()
|
||||||
{
|
{
|
||||||
|
saveWindowInformation();
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
setWindowState(windowState() | Qt::WindowMinimized);
|
setWindowState(windowState() | Qt::WindowMinimized);
|
||||||
#endif
|
#endif
|
||||||
|
@ -87,7 +87,6 @@ private slots:
|
|||||||
void updateCopyAttributesMenu();
|
void updateCopyAttributesMenu();
|
||||||
void showEntryContextMenu(const QPoint& globalPos);
|
void showEntryContextMenu(const QPoint& globalPos);
|
||||||
void showGroupContextMenu(const QPoint& globalPos);
|
void showGroupContextMenu(const QPoint& globalPos);
|
||||||
void saveToolbarState(bool value);
|
|
||||||
void rememberOpenDatabases(const QString& filePath);
|
void rememberOpenDatabases(const QString& filePath);
|
||||||
void applySettingsChanges();
|
void applySettingsChanges();
|
||||||
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
|
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
|
||||||
|
@ -557,7 +557,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionGroupEmptyRecycleBin">
|
<action name="actionGroupEmptyRecycleBin">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Empty recycle bin</string>
|
<string>E&mpty recycle bin</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="visible">
|
<property name="visible">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
#include "PasswordEdit.h"
|
#include "PasswordEdit.h"
|
||||||
|
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
|
#include "gui/Font.h"
|
||||||
#include <QFontDatabase>
|
|
||||||
|
|
||||||
const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15);
|
const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15);
|
||||||
const QColor PasswordEdit::ErrorColor = QColor(255, 125, 125);
|
const QColor PasswordEdit::ErrorColor = QColor(255, 125, 125);
|
||||||
@ -32,8 +31,8 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
|||||||
setEchoMode(QLineEdit::Password);
|
setEchoMode(QLineEdit::Password);
|
||||||
updateStylesheet();
|
updateStylesheet();
|
||||||
|
|
||||||
// set font to system monospace font and increase letter spacing
|
// use a monospace font for the password field
|
||||||
QFont passwordFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
QFont passwordFont = Font::fixedFont();
|
||||||
passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110);
|
passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110);
|
||||||
setFont(passwordFont);
|
setFont(passwordFont);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/PasswordGenerator.h"
|
#include "core/PasswordGenerator.h"
|
||||||
@ -149,6 +150,19 @@ void PasswordGeneratorWidget::setStandaloneMode(bool standalone)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e)
|
||||||
|
{
|
||||||
|
if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
|
||||||
|
if (e->key() == Qt::Key_Escape && m_standalone == true) {
|
||||||
|
emit dialogTerminated();
|
||||||
|
} else {
|
||||||
|
e->ignore();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e->ignore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PasswordGeneratorWidget::regeneratePassword()
|
void PasswordGeneratorWidget::regeneratePassword()
|
||||||
{
|
{
|
||||||
if (m_ui->tabWidget->currentIndex() == Password) {
|
if (m_ui->tabWidget->currentIndex() == Password) {
|
||||||
|
@ -81,6 +81,9 @@ private:
|
|||||||
const QScopedPointer<PasswordGenerator> m_passwordGenerator;
|
const QScopedPointer<PasswordGenerator> m_passwordGenerator;
|
||||||
const QScopedPointer<PassphraseGenerator> m_dicewareGenerator;
|
const QScopedPointer<PassphraseGenerator> m_dicewareGenerator;
|
||||||
const QScopedPointer<Ui::PasswordGeneratorWidget> m_ui;
|
const QScopedPointer<Ui::PasswordGeneratorWidget> m_ui;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* e) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_PASSWORDGENERATORWIDGET_H
|
#endif // KEEPASSX_PASSWORDGENERATORWIDGET_H
|
||||||
|
@ -318,7 +318,7 @@ QProgressBar::chunk {
|
|||||||
<string>Extended ASCII</string>
|
<string>Extended ASCII</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true">Extended ASCII</string>
|
<string>Extended ASCII</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -241,6 +241,7 @@ void SettingsWidget::saveSettings()
|
|||||||
|
|
||||||
if (!config()->get("RememberLastKeyFiles").toBool()) {
|
if (!config()->get("RememberLastKeyFiles").toBool()) {
|
||||||
config()->set("LastKeyFiles", QVariant());
|
config()->set("LastKeyFiles", QVariant());
|
||||||
|
config()->set("LastDir", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ExtraPage& page: asConst(m_extraPages)) {
|
for (const ExtraPage& page: asConst(m_extraPages)) {
|
||||||
|
@ -35,7 +35,9 @@ SetupTotpDialog::SetupTotpDialog(DatabaseWidget* parent, Entry* entry)
|
|||||||
|
|
||||||
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
|
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||||
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(setupTotp()));
|
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(setupTotp()));
|
||||||
connect(m_ui->customSettingsCheckBox, SIGNAL(toggled(bool)), SLOT(toggleCustom(bool)));
|
connect(m_ui->radioDefault, SIGNAL(toggled(bool)), SLOT(toggleDefault(bool)));
|
||||||
|
connect(m_ui->radioSteam, SIGNAL(toggled(bool)), SLOT(toggleSteam(bool)));
|
||||||
|
connect(m_ui->radioCustom, SIGNAL(toggled(bool)), SLOT(toggleCustom(bool)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,19 +45,37 @@ void SetupTotpDialog::setupTotp()
|
|||||||
{
|
{
|
||||||
quint8 digits;
|
quint8 digits;
|
||||||
|
|
||||||
if (m_ui->radio8Digits->isChecked()) {
|
if (m_ui->radioSteam->isChecked()) {
|
||||||
|
digits = Totp::ENCODER_STEAM;
|
||||||
|
} else if (m_ui->radio8Digits->isChecked()) {
|
||||||
digits = 8;
|
digits = 8;
|
||||||
} else {
|
} else {
|
||||||
digits = 6;
|
digits = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint8 step = m_ui->stepSpinBox->value();
|
quint8 step = m_ui->stepSpinBox->value();
|
||||||
QString seed = QTotp::parseOtpString(m_ui->seedEdit->text(), digits, step);
|
QString seed = Totp::parseOtpString(m_ui->seedEdit->text(), digits, step);
|
||||||
m_entry->setTotp(seed, step, digits);
|
m_entry->setTotp(seed, step, digits);
|
||||||
emit m_parent->entrySelectionChanged();
|
emit m_parent->entrySelectionChanged();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupTotpDialog::toggleDefault(bool status)
|
||||||
|
{
|
||||||
|
if (status) {
|
||||||
|
setStep(Totp::defaultStep);
|
||||||
|
setDigits(Totp::defaultDigits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupTotpDialog::toggleSteam(bool status)
|
||||||
|
{
|
||||||
|
if (status) {
|
||||||
|
setStep(Totp::defaultStep);
|
||||||
|
setDigits(Totp::ENCODER_STEAM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SetupTotpDialog::toggleCustom(bool status)
|
void SetupTotpDialog::toggleCustom(bool status)
|
||||||
{
|
{
|
||||||
m_ui->digitsLabel->setEnabled(status);
|
m_ui->digitsLabel->setEnabled(status);
|
||||||
@ -72,13 +92,25 @@ void SetupTotpDialog::setSeed(QString value)
|
|||||||
m_ui->seedEdit->setText(value);
|
m_ui->seedEdit->setText(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupTotpDialog::setSettings(quint8 digits) {
|
||||||
|
quint8 step = m_ui->stepSpinBox->value();
|
||||||
|
|
||||||
|
bool isDefault = ((step == Totp::defaultStep) &&
|
||||||
|
(digits == Totp::defaultDigits));
|
||||||
|
bool isSteam = (digits == Totp::ENCODER_STEAM);
|
||||||
|
|
||||||
|
if (isSteam) {
|
||||||
|
m_ui->radioSteam->setChecked(true);
|
||||||
|
} else if (isDefault) {
|
||||||
|
m_ui->radioDefault->setChecked(true);
|
||||||
|
} else {
|
||||||
|
m_ui->radioCustom->setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SetupTotpDialog::setStep(quint8 step)
|
void SetupTotpDialog::setStep(quint8 step)
|
||||||
{
|
{
|
||||||
m_ui->stepSpinBox->setValue(step);
|
m_ui->stepSpinBox->setValue(step);
|
||||||
|
|
||||||
if (step != QTotp::defaultStep) {
|
|
||||||
m_ui->customSettingsCheckBox->setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupTotpDialog::setDigits(quint8 digits)
|
void SetupTotpDialog::setDigits(quint8 digits)
|
||||||
@ -90,12 +122,7 @@ void SetupTotpDialog::setDigits(quint8 digits)
|
|||||||
m_ui->radio6Digits->setChecked(true);
|
m_ui->radio6Digits->setChecked(true);
|
||||||
m_ui->radio8Digits->setChecked(false);
|
m_ui->radio8Digits->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (digits != QTotp::defaultDigits) {
|
|
||||||
m_ui->customSettingsCheckBox->setChecked(true);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SetupTotpDialog::~SetupTotpDialog()
|
SetupTotpDialog::~SetupTotpDialog()
|
||||||
{
|
{
|
||||||
|
@ -39,8 +39,11 @@ public:
|
|||||||
void setSeed(QString value);
|
void setSeed(QString value);
|
||||||
void setStep(quint8 step);
|
void setStep(quint8 step);
|
||||||
void setDigits(quint8 digits);
|
void setDigits(quint8 digits);
|
||||||
|
void setSettings(quint8 digits);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void toggleDefault(bool status);
|
||||||
|
void toggleSteam(bool status);
|
||||||
void toggleCustom(bool status);
|
void toggleCustom(bool status);
|
||||||
void setupTotp();
|
void setupTotp();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>282</width>
|
<width>282</width>
|
||||||
<height>257</height>
|
<height>364</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -29,12 +29,39 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="customSettingsCheckBox">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioDefault">
|
||||||
|
<property name="text">
|
||||||
|
<string>Default RFC 6238 token settings</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">settingsButtonGroup</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioSteam">
|
||||||
|
<property name="text">
|
||||||
|
<string>Steam token settings</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">settingsButtonGroup</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioCustom">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use custom settings</string>
|
<string>Use custom settings</string>
|
||||||
</property>
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">settingsButtonGroup</string>
|
||||||
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -134,4 +161,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
<buttongroups>
|
||||||
|
<buttongroup name="settingsButtonGroup"/>
|
||||||
|
</buttongroups>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -88,7 +88,7 @@ void TotpDialog::updateTotp()
|
|||||||
{
|
{
|
||||||
QString totpCode = m_entry->totp();
|
QString totpCode = m_entry->totp();
|
||||||
QString firstHalf = totpCode.left(totpCode.size() / 2);
|
QString firstHalf = totpCode.left(totpCode.size() / 2);
|
||||||
QString secondHalf = totpCode.right(totpCode.size()/2);
|
QString secondHalf = totpCode.mid(totpCode.size() / 2);
|
||||||
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
|
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user