Compare commits

..

59 Commits
2.2.1 ... 2.2.3

Author SHA1 Message Date
Jonathan White
cf94610f46 Release 2.2.3
- 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]
2017-12-11 21:01:14 -05:00
Jonathan White
c7836f1157 Update translations 2017-12-11 21:01:00 -05:00
Jonathan White
228843b010 Version bump to 2.2.3 and changelog 2017-12-11 20:59:41 -05:00
thez3ro
8a71c680ac resize message-close to 16x16 pixels 2017-12-05 22:03:55 +01:00
Michal Kaptur
0ff75e7a88 Fixed memory leaks in non-gui tests
Fixed 2 memory leaks in production code and a few in testcases. As a
result leak_check_at_exit ASAN option does not need to turned off for
non-gui tests.
Smart pointers should be used elsewhere for consistency, but the sooner
this fixes are delivered, the lesser memory leaks are introduced.
2017-11-27 23:36:09 +01:00
Janek Bevendorff
b20918b60e Add update information to AppImage and use KeePassXC PPA for YubiKey libs, resolves #1157 2017-11-26 18:18:34 +01:00
Janek Bevendorff
8905fe5a54 Use Consolas on Windows for PasswordEdit instead of the default Courier New, resolves #1226 2017-11-26 17:53:15 +01:00
Jonathan White
e17b3d24bf Corrected database corruption when locked [#1113] 2017-11-23 21:32:56 +01:00
TheZ3ro
7ef61b47e3 Merge pull request #1141 from keepassxreboot/hotfix/apply-button-save
Fix apply button not saving new entries
2017-11-06 20:27:15 +01:00
Jonathan White
4e7f2c6a4f Fix apply button not saving new entries 2017-10-28 09:23:45 -04:00
Janek Bevendorff
83fd387f2f Fix desktop integration 2017-10-24 17:58:27 +02:00
Janek Bevendorff
cd6aac9acf Remove libdbus and libsystemd blacklisting 2017-10-24 17:58:27 +02:00
Janek Bevendorff
05d02b702a Restrict global menu fix to Qt < 5.9.0, resolves #691 2017-10-24 17:58:27 +02:00
Janek Bevendorff
f6933a8868 Ensure that YubiKey is only polled once, even if showEvent() is called twice 2017-10-24 17:58:27 +02:00
Janek Bevendorff
51b42bc7b8 Move back to Ubuntu 14.04, but with custom libyubikey and libykpers-1 builds, resolves #1114 2017-10-24 17:58:27 +02:00
Janek Bevendorff
6d46717cfc Release 2.2.2
- Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]
- Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]
- Added AppStream description [#1082]
- Improved TOTP compatibility and added new Base32 implementation [#1069]
- Fixed error handling when processing invalid cipher stream [#1099]
- Fixed double warning display when opening a database [#1037]
- Fixed unlocking databases with --pw-stdin [#1087]
- Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]
- Fixed transform seed not being regenerated when saving the database [#1068]
- Fixed only one YubiKey slot being polled [#1048]
- Corrected an issue with entry icons while merging [#1008]
- Corrected desktop and tray icons in Snap package [#1030]
- Fixed screen lock and Google fallback settings [#1029]
2017-10-22 00:28:08 +02:00
Janek Bevendorff
3088371631 Update translations 2017-10-22 00:28:04 +02:00
Janek Bevendorff
880a8da68a Bump version to 2.2.2 and update changelog 2017-10-22 00:27:26 +02:00
Janek Bevendorff
e2c6f50108 Support starting keepassxc-cli via 'cli' argument 2017-10-21 22:05:13 +02:00
Janek Bevendorff
51ed7a59da Fix Exec replacement 2017-10-21 22:05:13 +02:00
Janek Bevendorff
94ea91ec63 Switch from Ubuntu 14.04 to CentOS 7 2017-10-21 22:05:13 +02:00
Janek Bevendorff
76ac8dda54 Fix missing semicolon 2017-10-21 22:05:13 +02:00
Peter Nirschl
a895729b9e 🐛 Fix result propagation in SymmetricCipherGcrypt::process
The boolean 'ok' has always been set to true, regardless of possible errors.
2017-10-21 20:00:28 +02:00
Janek Bevendorff
aba2acb062 Use QSharedPointer 2017-10-21 14:19:00 +02:00
Louis-Bertrand Varin
a1aad5d165 Using QScopedPointer 2017-10-21 14:19:00 +02:00
Louis-Bertrand Varin
0fe06b3fbb Fix double warning display for database open 2017-10-21 14:19:00 +02:00
Adolfo E. García
86cd2c09a4 Improve readability of code 2017-10-21 13:15:02 +02:00
Adolfo E. García
f1d99dd0ed Improve testing of Base32 2017-10-21 13:15:02 +02:00
Adolfo E. García
c731f8e5c0 Fix bug in Base32::decode and add bounds check
The bug that was fixed, was affecting how the number of bytes of decoded data was  calculated and thus, even though it didn't truncate the result, it was causing the array to be resized unnecessarily.
2017-10-21 13:15:02 +02:00
Adolfo E. García
24f560aaa2 Replace Optional with QVariant 2017-10-21 13:15:02 +02:00
Adolfo E. García
905e104ba2 Revert formatting change 2017-10-21 13:15:02 +02:00
Adolfo E. García
d21ae0f94a Fix for loop's variable increment style 2017-10-21 13:15:02 +02:00
Adolfo E. García
95b0ad15ef Fix code style 2017-10-21 13:15:02 +02:00
Adolfo E. García
19eb6a8a60 Add new Base32 implementation 2017-10-21 13:15:02 +02:00
Jonathan White
85f652290b Add signtool function (Windows Only) to release-tool script 2017-10-20 18:12:10 -04:00
thez3ro
feb6baad05 add KeePassHTTP-Connector 2017-10-20 21:59:15 +02:00
Jonathan White
37aa4f0257 Unlock previously opened databases with --pw-stdin 2017-10-20 01:21:32 +02:00
Janek Bevendorff
d3208bddb0 Enhance lupdate-qt5 check 2017-10-19 21:24:31 +02:00
Janek Bevendorff
eb977b8021 Check AppStream info file before merging releases 2017-10-19 21:24:31 +02:00
Janek Bevendorff
b30eb59791 Extend desktop file and name it according to the freedesktop specification 2017-10-19 21:24:31 +02:00
Janek Bevendorff
72a6c34450 Add AppStream AppData file, resolves #1066 2017-10-19 21:24:31 +02:00
Janek Bevendorff
ec8c99c3b2 Fix remaining uses of KEEPASSXC_SNAP_BUILD 2017-10-19 15:52:46 +02:00
Janek Bevendorff
6d433fecef Set KEEPASSXC_DIST_TYPE when building AppImages and Snaps 2017-10-19 15:52:46 +02:00
Janek Bevendorff
bae9dbc407 Add distribution information to debug info screen 2017-10-19 15:52:46 +02:00
Janek Bevendorff
1cbbcc0d87 Allow starting AppImage with custom extended QT_PLUGIN_PATH, resolves #1077 2017-10-19 15:52:46 +02:00
Janek Bevendorff
c0182df05e Add CLion build directories 2017-10-19 15:52:46 +02:00
Jonathan White
7d6baf53fa Merge pull request #1047 from aidanhs/aphs-fix-dockerfile
Fix builds in Docker
2017-10-18 23:05:32 -04:00
rockihack
c6f83b9ca6 Fix: Regenerate transform seed and transform master key on save. 2017-10-18 23:04:37 -04:00
Jonathan White
3bc8a79b9b Both slots on Yubikey are now polled for challenge/response 2017-10-09 09:39:47 -04:00
Aidan Hobson Sayers
84508e91da Disable apparmor to use fuse
Necessary for an Ubuntu 16.04, Docker 17.09.0-ce host
See https://github.com/moby/moby/issues/9448#issuecomment-289950103
2017-10-07 23:37:39 +01:00
Aidan Hobson Sayers
38cbb42b58 Use the renamed QT ppa (with packages named the same) 2017-10-07 23:37:34 +01:00
Louis-Bertrand Varin
2e81751131 Backporting #1008 2017-10-07 11:03:24 -04:00
Jonathan White
157f1134bf Select custom icon radio button after successful download 2017-10-07 08:49:14 -04:00
Jonathan White
4277364e91 Fixed cast warning 2017-10-07 07:36:25 -04:00
frostasm
4e903e4c50 Fix memory leak in DatabaseTabWidget::openDatabase function 2017-10-05 18:03:01 -04:00
Jonathan White
5098866413 Rewrote resolveUrl function to conform to various test cases 2017-10-03 18:40:32 -04:00
Jonathan White
91d746c5c0 Corrected issues with desktop and tray icons in snap build 2017-10-03 15:43:52 -04:00
rockihack
e53754d202 Fix build on mac os with qt 5.9.1 2017-10-03 15:01:54 -04:00
thez3ro
eb21eeb214 Fix GoogleFallback/Screnlock settings 2017-10-03 17:37:26 +02:00
93 changed files with 2332 additions and 1016 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
CMakeLists.txt.*
build*/
cmake-build-*/
release*/
.idea/
*.iml

View File

@@ -15,8 +15,8 @@ compiler:
- gcc
env:
- CONFIG=Release ASAN_OPTIONS=detect_odr_violation=1:leak_check_at_exit=0
- CONFIG=Debug 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
git:
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 ..
- make -j2
- 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
# Generate snapcraft build when merging into master/develop branches

View File

@@ -34,6 +34,7 @@ fi
APP="$1"
LOWERAPP="$(echo "$APP" | tr '[:upper:]' '[:lower:]')"
VERSION="$2"
export ARCH=x86_64
mkdir -p $APP.AppDir
wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh
@@ -42,6 +43,8 @@ wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./func
LIB_DIR=./usr/lib
if [ -d ./usr/lib/x86_64-linux-gnu ]; then
LIB_DIR=./usr/lib/x86_64-linux-gnu
elif [ -d ./usr/lib64 ]; then
LIB_DIR=./usr/lib64
fi
cd $APP.AppDir
@@ -51,7 +54,7 @@ rm -R ./usr/local
rmdir ./opt 2> /dev/null
# bundle Qt platform plugins and themes
QXCB_PLUGIN="$(find /usr/lib -name 'libqxcb.so' 2> /dev/null)"
QXCB_PLUGIN="$(find /usr/lib* -name 'libqxcb.so' 2> /dev/null)"
if [ "$QXCB_PLUGIN" == "" ]; then
QXCB_PLUGIN="$(find /opt/qt*/plugins -name 'libqxcb.so' 2> /dev/null)"
fi
@@ -63,33 +66,34 @@ get_apprun
copy_deps
delete_blacklisted
# remove dbus and systemd libs as they are not blacklisted
find . -name libdbus-1.so.3 -exec rm {} \;
find . -name libsystemd.so.0 -exec rm {} \;
get_desktop
get_icon
cat << EOF > ./usr/bin/keepassxc_env
#!/usr/bin/env bash
#export QT_QPA_PLATFORMTHEME=gtk2
export LD_LIBRARY_PATH="..$(dirname ${QT_PLUGIN_PATH})/lib:\${LD_LIBRARY_PATH}"
export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}"
export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}:\${KPXC_QT_PLUGIN_PATH}"
# unset XDG_DATA_DIRS to make tray icon work in Ubuntu Unity
# see https://github.com/probonopd/AppImageKit/issues/351
# see https://github.com/AppImage/AppImageKit/issues/351
unset XDG_DATA_DIRS
exec keepassxc "\$@"
if [ "\${1}" == "cli" ]; then
shift
exec keepassxc-cli "\$@"
else
exec keepassxc "\$@"
fi
EOF
chmod +x ./usr/bin/keepassxc_env
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' keepassxc.desktop
get_desktopintegration $LOWERAPP
GLIBC_NEEDED=$(glibc_needed)
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.${APP}.desktop
get_desktopintegration "org.${LOWERAPP}.${APP}"
cd ..
generate_type2_appimage
GLIBC_NEEDED=$(glibc_needed)
NO_GLIBC_VERSION=true
mv ../out/*.AppImage ..
rmdir ../out > /dev/null 2>&1
generate_type2_appimage -u "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-${ARCH}.AppImage.zsync"
mv ../out/*.AppImage* ../
rm -rf ../out

View File

@@ -1,3 +1,30 @@
2.2.3 (2017-12-11)
=========================
- 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)
=========================
- Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]
- Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]
- Added AppStream description [#1082]
- Improved TOTP compatibility and added new Base32 implementation [#1069]
- Fixed error handling when processing invalid cipher stream [#1099]
- Fixed double warning display when opening a database [#1037]
- Fixed unlocking databases with --pw-stdin [#1087]
- Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]
- Fixed transform seed not being regenerated when saving the database [#1068]
- Fixed only one YubiKey slot being polled [#1048]
- Corrected an issue with entry icons while merging [#1008]
- Corrected desktop and tray icons in Snap package [#1030]
- Fixed screen lock and Google fallback settings [#1029]
2.2.1 (2017-10-01)
=========================

View File

@@ -49,9 +49,21 @@ set(CMAKE_AUTOUIC ON)
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "2")
set(KEEPASSXC_VERSION_PATCH "1")
set(KEEPASSXC_VERSION_PATCH "3")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
# Distribution info
set(KEEPASSXC_DIST True)
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution type")
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other)
if(KEEPASSXC_DIST_TYPE STREQUAL "Snap")
set(KEEPASSXC_DIST_SNAP True)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage")
set(KEEPASSXC_DIST_APPIMAGE True)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other")
unset(KEEPASSXC_DIST)
endif()
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1)
endif()

View File

@@ -214,10 +214,6 @@ Files: share/icons/database/C65_W.png
Copyright: none
License: public-domain
Files: src/crypto/salsa20/*
Copyright: none
License: public-domain
Files: src/streams/qtiocompressor.*
src/streams/QtIOCompressor
tests/modeltest.*
@@ -241,8 +237,3 @@ Files: src/gui/KMessageWidget.h
Copyright: 2011 Aurélien Gâteau <agateau@kde.org>
2014 Dominik Haumann <dhaumann@kde.org>
License: LGPL-2.1
Files: src/totp/base32.cpp
src/totp/base32.h
Copyright: 2010 Google Inc.
License: Apache 2.0

View File

@@ -16,47 +16,48 @@
FROM ubuntu:14.04
RUN set -x \
&& apt-get update \
&& apt-get install --yes software-properties-common
ENV QT5_VERSION=59
ENV QT5_PPA_VERSION=${QT5_VERSION}2
RUN set -x \
&& add-apt-repository ppa:george-edison55/cmake-3.x
ENV QT_VERSION=qt59
&& apt-get update -y \
&& apt-get -y install software-properties-common
RUN set -x \
&& add-apt-repository --yes ppa:beineri/opt-${QT_VERSION}-trusty
&& add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \
&& add-apt-repository ppa:phoerious/keepassxc
RUN set -x \
&& apt-get update \
&& apt-get install --yes \
&& apt-get update -y \
&& apt-get upgrade -y
# build and runtime dependencies
RUN set -x \
&& apt-get install -y \
cmake3 \
g++ \
cmake \
libgcrypt20-dev \
${QT_VERSION}base \
${QT_VERSION}tools \
${QT_VERSION}x11extras \
qt${QT5_VERSION}base \
qt${QT5_VERSION}tools \
qt${QT5_VERSION}x11extras \
zlib1g-dev \
libxi-dev \
libxtst-dev \
zlib1g-dev \
mesa-common-dev \
libyubikey-dev \
libykpers-1-dev \
xvfb \
wget \
file \
fuse \
python
libykpers-1-dev
ENV CMAKE_PREFIX_PATH=/opt/qt${QT5_VERSION}/lib/cmake
ENV LD_LIBRARY_PATH=/opt/qt${QT5_VERSION}/lib
RUN set -x \
&& apt-get install --yes mesa-common-dev
&& echo /opt/qt${QT_VERSION}/lib > /etc/ld.so.conf.d/qt${QT5_VERSION}.conf
# AppImage dependencies
RUN set -x \
&& apt-get install -y \
libfuse2 \
wget
VOLUME /keepassxc/src
VOLUME /keepassxc/out
WORKDIR /keepassxc
ENV CMAKE_PREFIX_PATH=/opt/${QT_VERSION}/lib/cmake
ENV LD_LIBRARY_PATH=/opt/${QT_VERSION}/lib
RUN set -x \
&& echo /opt/${QT_VERSION}/lib > /etc/ld.so.conf.d/${QT_VERSION}.conf

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -14,7 +14,7 @@ KeePass Cross-platform Community Edition
- Using website favicons as entry icons
- Merging of databases
- Automatic reload when the database changed on disk
- KeePassHTTP support for use with [PassIFox](https://addons.mozilla.org/en-us/firefox/addon/passifox/) in Mozilla Firefox and [chromeIPass](https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae) in Google Chrome or Chromium, and [passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari.
- 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.
- Many bug fixes
For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document.

View File

@@ -119,9 +119,11 @@ EOF
Sign previously compiled release packages
Options:
-f, --files Files to sign (required)
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
-h, --help Show this help
-f, --files Files to sign (required)
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
--signtool Specify the signtool executable (default: 'signtool')
--signtool-key Provide a key to be used with signtool (for Windows EXE)
-h, --help Show this help
EOF
fi
}
@@ -257,19 +259,18 @@ checkChangeLog() {
grep -qPzo "${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n=+\n" CHANGELOG
if [ $? -ne 0 ]; then
exitError "CHANGELOG does not contain any information about the '${RELEASE_NAME}' release!"
exitError "'CHANGELOG' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
checkTransifexCommandExists() {
command -v tx > /dev/null
if [ 0 -ne $? ]; then
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'"
checkAppStreamInfo() {
if [ ! -f share/linux/org.keepassxc.appdata.xml ]; then
exitError "No AppStream info file found!"
fi
command -v lupdate-qt5 > /dev/null
if [ 0 -ne $? ]; then
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
grep -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.appdata.xml
if [ $? -ne 0 ]; then
exitError "'share/linux/org.keepassxc.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
@@ -281,7 +282,24 @@ checkSnapcraft() {
grep -qPzo "version: ${RELEASE_NAME}" snapcraft.yaml
if [ $? -ne 0 ]; then
exitError "snapcraft.yaml has not been updated to the '${RELEASE_NAME}' release!"
exitError "'snapcraft.yaml' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
checkTransifexCommandExists() {
command -v tx > /dev/null
if [ 0 -ne $? ]; then
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'"
fi
}
checkQt5LUpdateExists() {
command -v lupdate > /dev/null
if [ 0 -eq $? ] && ! $(lupdate -version | grep -q "lupdate version 5\."); then
command -v lupdate-qt5 > /dev/null
if [ 0 -ne $? ]; then
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
fi
fi
}
@@ -296,6 +314,7 @@ performChecks() {
logInfo "Validating toolset and repository..."
checkTransifexCommandExists
checkQt5LUpdateExists
checkGitRepository
checkReleaseDoesNotExist
checkWorkingTreeClean
@@ -309,6 +328,7 @@ performChecks() {
checkVersionInCMake
checkChangeLog
checkAppStreamInfo
checkSnapcraft
logInfo "\e[1m\e[32mAll checks passed!\e[0m"
@@ -598,7 +618,8 @@ build() {
# Building on Linux without Docker container
logInfo "Configuring build..."
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR"
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
-DKEEPASSXC_DIST_TYPE=AppImage "$SRC_DIR"
logInfo "Compiling sources..."
make $MAKE_OPTIONS
@@ -615,7 +636,7 @@ build() {
logInfo "Launching Docker container to compile sources..."
docker run --name "$DOCKER_CONTAINER_NAME" --rm \
--cap-add SYS_ADMIN --device /dev/fuse \
--cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \
-e "CC=${CC}" -e "CXX=${CXX}" \
-v "$(realpath "$SRC_DIR"):/keepassxc/src:ro" \
-v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \
@@ -644,6 +665,8 @@ build() {
# -----------------------------------------------------------------------
sign() {
SIGN_FILES=()
SIGNTOOL="signtool"
SIGNTOOL_KEY=""
while [ $# -ge 1 ]; do
local arg="$1"
@@ -658,6 +681,14 @@ sign() {
GPG_KEY="$2"
shift ;;
--signtool)
SIGNTOOL="$2"
shift ;;
--signtool-key)
SIGNTOOL_KEY="$2"
shift ;;
-h|--help)
printUsage "sign"
exit ;;
@@ -676,12 +707,29 @@ sign() {
exit 1
fi
if [[ -n "$SIGNTOOL_KEY" && ! -f "$SIGNTOOL_KEY" ]]; then
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
exitError "File '${f}' does not exist!"
fi
logInfo "Signing file '${f}'..."
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
logInfo "Signing file '${f}' using release key..."
gpg --output "${f}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$f"
if [ 0 -ne $? ]; then

View File

@@ -30,7 +30,8 @@ if(UNIX AND NOT APPLE)
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz"
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE)
install(FILES linux/keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES linux/org.keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES linux/org.keepassxc.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages)
endif(UNIX AND NOT APPLE)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 360 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="application/x-keepass2">
<comment>KeePass 2 database</comment>
<comment>KeePass 2 Database</comment>
<glob pattern="*.kdbx"/>
<icon name="application-x-keepassxc"/>
</mime-type>

View File

@@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-<li>Copyright 2017 KeePassXC Team <team@keepassxc.org> -->
<component type="desktop-application">
<id>org.keepassxc</id>
<name>KeePassXC</name>
<metadata_license>CC-BY-3.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<icon type="stock">keepassxc</icon>
<url type="homepage">https://keepassxc.org</url>
<mimetypes>
<mimetype>application/x-keepass2</mimetype>
</mimetypes>
<summary>Community-driven port of the Windows application “KeePass Password Safe”</summary>
<description>
<p>
KeePassXC is an application for people with extremely high demands on secure
personal data management. It has a light interface, is cross-platform and
published under the terms of the GNU General Public License.
</p>
</description>
<launchable type="desktop-id">org.keepassxc.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://keepassxc.org/images/screenshots/linux/screen_001.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_002.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_003.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_004.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_005.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_006.png</image>
</screenshot>
<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>
</screenshots>
<releases>
<release version="2.2.3" date="2017-12-11">
<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">
<description>
<ul>
<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>Added AppStream description [#1082]</li>
<li>Improved TOTP compatibility and added new Base32 implementation [#1069]</li>
<li>Fixed error handling when processing invalid cipher stream [#1099]</li>
<li>Fixed double warning display when opening a database [#1037]</li>
<li>Fixed unlocking databases with --pw-stdin [#1087]</li>
<li>Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]</li>
<li>Fixed transform seed not being regenerated when saving the database [#1068]</li>
<li>Fixed only one YubiKey slot being polled [#1048]</li>
<li>Corrected an issue with entry icons while merging [#1008]</li>
<li>Corrected desktop and tray icons in Snap package [#1030]</li>
<li>Fixed screen lock and Google fallback settings [#1029]</li>
</ul>
</description>
</release>
<release version="2.2.1" date="2017-10-01">
<description>
<ul>
<li>Corrected multiple snap issues [#934, #1011]</li>
<li>Corrected multiple custom icon issues [#708, #719, #994]</li>
<li>Corrected multiple Yubikey issues [#880]</li>
<li>Fixed single instance preventing load on occasion [#997]</li>
<li>Keep entry history when merging databases [#970]</li>
<li>Prevent data loss if passwords were mismatched [#1007]</li>
<li>Fixed crash after merge [#941]</li>
<li>Added configurable auto-type default delay [#703]</li>
<li>Unlock database dialog window comes to front [#663]</li>
<li>Translation and compiling fixes</li>
</ul>
</description>
</release>
<release version="2.2.0" date="2017-06-23">
<description>
<ul>
<li>Added YubiKey 2FA integration for unlocking databases [#127]</li>
<li>Added TOTP support [#519]</li>
<li>Added CSV import tool [#146, #490]</li>
<li>Added KeePassXC CLI tool [#254]</li>
<li>Added diceware password generator [#373]</li>
<li>Added support for entry references [#370, #378]</li>
<li>Added support for Twofish encryption [#167]</li>
<li>Enabled DEP and ASLR for in-memory protection [#371]</li>
<li>Enabled single instance mode [#510]</li>
<li>Enabled portable mode [#645]</li>
<li>Enabled database lock on screensaver and session lock [#545]</li>
<li>Redesigned welcome screen with common features and recent databases [#292]</li>
<li>Multiple updates to search behavior [#168, #213, #374, #471, #603, #654]</li>
<li>Added auto-type fields {CLEARFIELD}, {SPACE}, {{}, {}} [#267, #427, #480]</li>
<li>Fixed auto-type errors on Linux [#550]</li>
<li>Prompt user prior to executing a cmd:// URL [#235]</li>
<li>Entry attributes can be protected (hidden) [#220]</li>
<li>Added extended ascii to password generator [#538]</li>
<li>Added new database icon to toolbar [#289]</li>
<li>Added context menu entry to empty recycle bin in databases [#520]</li>
<li>Added "apply" button to entry and group edit windows [#624]</li>
<li>Added macOS tray icon and enabled minimize on close [#583]</li>
<li>Fixed issues with unclean shutdowns [#170, #580]</li>
<li>Changed keyboard shortcut to create new database to CTRL+SHIFT+N [#515]</li>
<li>Compare window title to entry URLs [#556]</li>
<li>Implemented inline error messages [#162]</li>
<li>Ignore group expansion and other minor changes when making database "dirty" [#464]</li>
<li>Updated license and copyright information on souce files [#632]</li>
<li>Added contributors list to about dialog [#629]</li>
</ul>
</description>
</release>
<release version="2.1.4" date="2017-04-09">
<description>
<ul>
<li>Bumped KeePassHTTP version to 1.8.4.2</li>
<li>KeePassHTTP confirmation window comes to foreground [#466]</li>
</ul>
</description>
</release>
<release version="2.1.3" date="2017-03-03">
<description>
<ul>
<li>Fix possible overflow in zxcvbn library [#363]</li>
<li>Revert HiDPI setting to avoid problems on laptop screens [#332]</li>
<li>Set file meta properties in Windows executable [#330]</li>
<li>Suppress error message when auto-reloading a locked database [#345]</li>
<li>Improve usability of question dialog when database is already locked by a different instance [#346]</li>
<li>Fix compiler warnings in QHttp library [#351]</li>
<li>Use unified toolbar on Mac OS X [#361]</li>
<li>Fix an issue on X11 where the main window would be raised instead of closed on Alt+F4 [#362]</li>
</ul>
</description>
</release>
<release version="2.1.2" date="2017-02-17">
<description>
<ul>
<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>Prevent Qt from degrading Wifi network performance on certain platforms [#318]</li>
<li>Visually refine user interface on OS X and other platforms [#299]</li>
<li>Remove unusable tray icon setting on OS X [#293]</li>
<li>Fix compositing glitches on Ubuntu and prevent flashing when minimizing to the tray at startup [#307]</li>
<li>Fix AppImage tray icon on Ubuntu [#277, #273]</li>
<li>Fix global menu disappearing after restoring KeePassXC from the tray on Ubuntu [#276]</li>
<li>Fix result order in entry search [#320]</li>
<li>Enable HiDPI scaling on supported platforms [#315]</li>
<li>Remove empty directories from installation target [#282]</li>
</ul>
</description>
</release>
<release version="2.1.1" date="2017-02-06">
<description>
<ul>
<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>Corrected crashes in favicon download and password generator [#233, #226]</li>
<li>Increase font size of password meter [#228]</li>
<li>Fixed compatibility with Qt 5.8 [#211]</li>
<li>Use consistent button heights in password generator [#229]</li>
</ul>
</description>
</release>
<release version="2.1.0" date="2017-01-22">
<description>
<ul>
<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>Support autotype on Windows and OS X [#42, #60, #63]</li>
<li>Add delay feature to autotype [#76, #77]</li>
<li>Add password strength meter [#84, #92]</li>
<li>Add option for automatically locking the database when minimizing the window [#57]</li>
<li>Add feature to download favicons and use them as entry icons [#30]</li>
<li>Automatically reload and merge database when the file changed on disk [#22, #33, #93]</li>
<li>Add tool for merging two databases [#22, #47, #143]</li>
<li>Add --pw-stdin commandline option to unlock the database by providing a password on STDIN [#54]</li>
<li>Add utility script for reading the database password from KWallet [#55]</li>
<li>Fix some KeePassHTTP settings not being remembered [#34, #65]</li>
<li>Make search box persistent [#15, #67, #157]</li>
<li>Enhance search feature by scoping the search to selected group [#16, #118]</li>
<li>Improve interaction between search field and entry list [#131, #141]</li>
<li>Add stand-alone password-generator [#18, #92]</li>
<li>Don't require password repetition when password is visible [#27, #92]</li>
<li>Add support for entry attributes in autotype sequences [#107]</li>
<li>Always focus password field when opening the database unlock widget [#116, #117]</li>
<li>Fix compilation errors on various platforms [#53, #126, #130]</li>
<li>Restructure and improve kdbx-extract utility [#160]</li>
</ul>
</description>
</release>
</releases>
</component>

View File

@@ -1,13 +1,16 @@
[Desktop Entry]
Name=KeePassXC
GenericName=Community Password Manager
GenericName=Password Manager
GenericName[de]=Passwortverwaltung
GenericName[es]=Gestor de contraseñas
GenericName[fr]=Gestionnaire de mot de passe
GenericName[ru]=менеджер паролей
Comment=Community-driven port of the Windows application KeePass Password Safe
Exec=keepassxc %f
TryExec=keepassxc
Icon=keepassxc
Terminal=false
Type=Application
Categories=Qt;Utility;
Version=1.0
Categories=Utility;Security;Qt;
MimeType=application/x-keepass2;

View File

@@ -67,6 +67,10 @@ Jádro systému: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>K hlášení chyby vždy připojte následující údaje:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribuce: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1095,7 +1099,7 @@ Chcete ji přesto otevřít?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Tato vlastní ikona už existuje</translation>
</message>
</context>
<context>
@@ -1318,6 +1322,17 @@ Můžete ho importovat pomocí Databáze → Importovat databázi ve formátu Ke
Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otevřít ve staré verzi KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nedaří se vyvolat výzva-odpověď.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nedaří se spočítat hlavní klíč</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1338,7 +1353,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Existující uzamykací soubor, zajišťující spuštění pouze jedné instance, není platný. Spouští se nová instance.</translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Kerne: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inkludér følgende information når du indrapporterer en fejl:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1313,6 +1317,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Kan ikke beregne hovednøgle</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Geben Sie folgende Informationen an, wenn Sie einen Bug melden:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribution: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -567,7 +571,7 @@ Sie können sie speichern oder die Sperre freigeben.</translation>
Do you want to save the database before locking it?
Otherwise your changes are lost.</source>
<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>
</message>
<message>
@@ -1092,7 +1096,7 @@ Möchten Sie diese dennoch öffnen?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Es gibt bereits ein eigenes Symbol </translation>
</message>
</context>
<context>
@@ -1315,6 +1319,17 @@ Zum Importieren gehen Sie auf Datenbank &gt; &apos;KeePass 1-Datenbank importier
Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann später nicht mehr mit der alten KeePassX-Version 0.4 geöffnet werden.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Fehler beim Ausführen des Challenge-Response-Verfahrens</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Berechnung des Hauptschlüssels gescheitert</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1335,7 +1350,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Vorhandene einmal-Sperrdatei ist ungültig. Starte neuen Vorgang. </translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Συμπεριλάβετε τις ακόλουθες πληροφορίες όποτε αναφέρετε κάποιο σφάλμα:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1311,6 +1315,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Σε θέση να υπολογίσει το κύριο κλειδί</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -66,6 +66,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1309,6 +1313,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Núcleo: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Incluya la información siguiente cuando informe sobre un error:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribución: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -246,7 +250,7 @@ Por favor seleccione si desea autorizar su acceso.</translation>
</message>
<message>
<source>Codec</source>
<translation>Codificación</translation>
<translation>Códec</translation>
</message>
<message>
<source>Text is qualified by</source>
@@ -298,11 +302,11 @@ Por favor seleccione si desea autorizar su acceso.</translation>
</message>
<message>
<source>Original data: </source>
<translation>Datos originales:</translation>
<translation>Dato original:</translation>
</message>
<message>
<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>
<source> more messages skipped]</source>
@@ -724,7 +728,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Merge Request</source>
<translation>Solicitud de unión</translation>
<translation>Solicitud de Unión</translation>
</message>
<message>
<source>The database file has changed and you have unsaved changes.Do you want to merge your changes?</source>
@@ -740,7 +744,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<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>
</context>
<context>
@@ -791,7 +795,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Select file</source>
<translation>Seleccionar archivo llave</translation>
<translation>Seleccionar archivo</translation>
</message>
<message>
<source>Unable to open file</source>
@@ -813,11 +817,11 @@ Do you want to open it anyway?</source>
</message>
<message numerus="yes">
<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 numerus="yes">
<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>
<source>1 year</source>
@@ -965,7 +969,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Presets</source>
<translation>Predeterminado</translation>
<translation>Programar</translation>
</message>
<message>
<source>Notes:</source>
@@ -1094,7 +1098,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>El icono personalizado ya existe</translation>
</message>
</context>
<context>
@@ -1317,6 +1321,17 @@ Puede importarla haciendo clic en Base de datos &gt; &apos;Importar base de dato
Esta migración es en único sentido. No podrá abrir la base de datos importada con la vieja versión 0.4 de KeePassX.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>No se pudo hacer el desafío/respuesta:</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>No se puede calcular la clave maestra</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1337,7 +1352,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada
</message>
<message>
<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>
</context>
<context>
@@ -1468,7 +1483,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada
</message>
<message>
<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>
<source>Copy &amp;TOTP</source>

View File

@@ -65,6 +65,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1300,6 +1304,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Ezin izan da gako nagusia kalkulatu</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Sisällytä seuraavat tiedot aina kun ilmoitat ongelmasta:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1314,6 +1318,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Pääavaimen laskeminen ei onnistu</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -29,27 +29,27 @@
</message>
<message>
<source>Revision: %1</source>
<translation>Revision: %1</translation>
<translation>Révision : %1</translation>
</message>
<message>
<source>Libraries:</source>
<translation>Bibliothèques:</translation>
<translation>Bibliothèques :</translation>
</message>
<message>
<source>Operating system: %1
CPU architecture: %2
Kernel: %3 %4</source>
<translation>Système d&apos;exploitation: %1
Architecture CPU: %2
Kernel: %3 %4</translation>
<translation>Système d&apos;exploitation : %1
Architecture CPU : %2
Kernel : %3 %4</translation>
</message>
<message>
<source>Enabled extensions:</source>
<translation>Extensions activées:</translation>
<translation>Extensions activées :</translation>
</message>
<message>
<source>Report bugs at: &lt;a href=&quot;https://github.com/keepassxreboot/keepassxc/issues&quot; style=&quot;text-decoration: underline;&quot;&gt;https://github.com&lt;/a&gt;</source>
<translation>Signaler les bugs sur: &lt;a href=&quot;https://github.com/keepassxreboot/keepassxc/issues&quot; style=&quot;text-decoration: underline;&quot;&gt;https://github.com&lt;/a&gt;</translation>
<translation>Signaler les bugs sur : &lt;a href=&quot;https://github.com/keepassxreboot/keepassxc/issues&quot; style=&quot;text-decoration: underline;&quot;&gt;https://github.com&lt;/a&gt;</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
@@ -57,7 +57,7 @@ Kernel: %3 %4</translation>
</message>
<message>
<source>Project Maintainers:</source>
<translation>Mainteneurs du projet:</translation>
<translation>Mainteneurs du projet :</translation>
</message>
<message>
<source>&lt;a href=&quot;https://github.com/keepassxreboot/keepassxc/graphs/contributors&quot;&gt;See Contributions on GitHub&lt;/a&gt;</source>
@@ -65,7 +65,11 @@ Kernel: %3 %4</translation>
</message>
<message>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclure l&apos;information suivante lorsque vous signaler un bug:</translation>
<translation>Inclure l&apos;information suivante lorsque vous signaler un bug :</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribution : %1</translation>
</message>
</context>
<context>
@@ -298,7 +302,7 @@ Veuillez sélectionner si vous souhaitez autoriser laccès.</translation>
</message>
<message>
<source>Original data: </source>
<translation>Données originales:</translation>
<translation>Données originales :</translation>
</message>
<message>
<source>Error(s) detected in CSV file !</source>
@@ -393,7 +397,7 @@ Veuillez sélectionner si vous souhaitez autoriser laccès.</translation>
</message>
<message>
<source>Challenge Response:</source>
<translation>Challenge-réponse:</translation>
<translation>Challenge-réponse :</translation>
</message>
</context>
<context>
@@ -473,15 +477,15 @@ Vous pouvez maintenant la sauvegarder.</translation>
</message>
<message>
<source>AES: 256 Bit (default)</source>
<translation>AES: 256 Bits (par défault)</translation>
<translation>AES : 256 Bits (par défaut)</translation>
</message>
<message>
<source>Twofish: 256 Bit</source>
<translation>Twofish: 256 bits</translation>
<translation>Twofish : 256 bits</translation>
</message>
<message>
<source>Algorithm:</source>
<translation>Algorithme:</translation>
<translation>Algorithme :</translation>
</message>
</context>
<context>
@@ -733,7 +737,7 @@ Voulez vous l&apos;ouvrir quand même ?</translation>
</message>
<message>
<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&apos;un rafraîchissement automatique de l&apos;actuelle est en cours.</translation>
<translation>La nouvelle base de données ne peut être ouverte pendant qu&apos;un rafraîchissement automatique de l&apos;actuelle est en cours.</translation>
</message>
<message>
<source>Empty recycle bin?</source>
@@ -1091,11 +1095,11 @@ Voulez vous l&apos;ouvrir quand même ?</translation>
</message>
<message>
<source>Hint: You can enable Google as a fallback under Tools&gt;Settings&gt;Security</source>
<translation>Astuce: Vous pouvez activer Google en tant que repli sous Outils&gt;Paramètres&gt;Sécurité</translation>
<translation>Astuce : Vous pouvez activer Google en tant que repli sous Outils&gt;Paramètres&gt;Sécurité</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>L&apos;icône personnalisée existe déjà</translation>
</message>
</context>
<context>
@@ -1171,7 +1175,7 @@ Voulez vous l&apos;ouvrir quand même ?</translation>
<message>
<source>Ref: </source>
<comment>Reference abbreviation</comment>
<translation>Réf:</translation>
<translation>Réf : </translation>
</message>
</context>
<context>
@@ -1318,6 +1322,17 @@ Vous pouvez l&apos;importer en cliquant sur Base de données&gt;&apos;Importer u
Il s&apos;agit d&apos;une migration à sens unique. Vous ne pourrez pas ouvrir la base de données importée avec l&apos;ancienne version de KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Impossible de lancer une challenge-réponse.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Impossible de calculer la clé maître</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1338,7 +1353,7 @@ Il s&apos;agit d&apos;une migration à sens unique. Vous ne pourrez pas ouvrir l
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Le fichier de verrouillage de linstance unique existant nest pas valide. Lancement d&apos;une nouvelle instance.</translation>
</message>
</context>
<context>
@@ -1632,11 +1647,11 @@ Il s&apos;agit d&apos;une migration à sens unique. Vous ne pourrez pas ouvrir l
</message>
<message>
<source>HTTP Port:</source>
<translation>Port HTTP:</translation>
<translation>Port HTTP :</translation>
</message>
<message>
<source>Default port: 19455</source>
<translation>Port par défaut: 19455</translation>
<translation>Port par défaut : 19455</translation>
</message>
<message>
<source>Re&amp;quest to unlock the database if it is locked</source>
@@ -1666,7 +1681,7 @@ Restauration du port 19455 par défaut.</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>&amp; Retourne les champs avancés de type chaîne qui commencent par &quot;KPH:&quot;</translation>
<translation>&amp; Retourne les champs avancés de type chaîne qui commencent par &quot;KPH :&quot;</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
@@ -1759,7 +1774,7 @@ Ne les changez que si vous savez ce que vous faites.</translation>
</message>
<message>
<source>&amp;Length:</source>
<translation>&amp;Longueur:</translation>
<translation>&amp;Longueur :</translation>
</message>
<message>
<source>Pick characters from every group</source>
@@ -1779,11 +1794,11 @@ Ne les changez que si vous savez ce que vous faites.</translation>
</message>
<message>
<source>Entropy: %1 bit</source>
<translation>Entropie: %1 bit</translation>
<translation>Entropie : %1 bit</translation>
</message>
<message>
<source>Password Quality: %1</source>
<translation>Qualité du mot de passe: %1</translation>
<translation>Qualité du mot de passe : %1</translation>
</message>
<message>
<source>Poor</source>
@@ -1815,15 +1830,15 @@ Ne les changez que si vous savez ce que vous faites.</translation>
</message>
<message>
<source>Wordlist:</source>
<translation>Liste de mots:</translation>
<translation>Liste de mots :</translation>
</message>
<message>
<source>Word Count:</source>
<translation>Nombre de mots:</translation>
<translation>Nombre de mots :</translation>
</message>
<message>
<source>Word Separator:</source>
<translation>Séparateur de mot:</translation>
<translation>Séparateur de mot :</translation>
</message>
<message>
<source>Copy</source>
@@ -2004,7 +2019,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une qui
</message>
<message>
<source>KeePassXC: New key association request</source>
<translation>KeePassXC: nouvelle demande d&apos;association</translation>
<translation>KeePassXC : nouvelle demande d&apos;association</translation>
</message>
<message>
<source>You have received an association request for the above key.
@@ -2016,23 +2031,23 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>KeePassXC: Overwrite existing key?</source>
<translation>KeePassXC: Écraser la clé existante ?</translation>
<translation>KeePassXC : Écraser la clé existante ?</translation>
</message>
<message>
<source>KeePassXC: Update Entry</source>
<translation>KeePassXC: Mettre à jour l&apos;entrée</translation>
<translation>KeePassXC : Mettre à jour l&apos;entrée</translation>
</message>
<message>
<source>KeePassXC: Database locked!</source>
<translation>KeePassXC: Base de données verrouillée !</translation>
<translation>KeePassXC : Base de données verrouillée !</translation>
</message>
<message>
<source>KeePassXC: Removed keys from database</source>
<translation>KeePassXC: Les clés ont é effacées de la base de donnée</translation>
<translation>KeePassXC : Les clés ont é effacées de la base de donnée</translation>
</message>
<message>
<source>KeePassXC: No keys found</source>
<translation>KeePassXC: Aucune clé trouvée</translation>
<translation>KeePassXC : Aucune clé trouvée</translation>
</message>
<message>
<source>KeePassXC: Settings not available!</source>
@@ -2040,11 +2055,11 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>KeePassXC: Removed permissions</source>
<translation>KeePassXC: Permissions retirées</translation>
<translation>KeePassXC : Permissions retirées</translation>
</message>
<message>
<source>KeePassXC: No entry with permissions found!</source>
<translation>KeePassXC: Aucune entrée avec permissions trouvée !</translation>
<translation>KeePassXC : Aucune entrée avec permissions trouvée !</translation>
</message>
</context>
<context>
@@ -2130,7 +2145,7 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>Don&apos;t mark database as modified for non-data changes (e.g., expanding groups)</source>
<translation>Ne pas indiquer la base de données comme modifiée pour les changements hors-données (par exemple: groupes développés)</translation>
<translation>Ne pas indiquer la base de données comme modifiée pour les changements hors-données (par exemple : groupes développés)</translation>
</message>
<message>
<source>Auto-Type</source>
@@ -2212,7 +2227,7 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>Key:</source>
<translation>Clé:</translation>
<translation>Clé :</translation>
</message>
<message>
<source>Use custom settings</source>
@@ -2220,11 +2235,11 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>Note: Change these settings only if you know what you are doing.</source>
<translation>Attention: modifiez ces paramètres seulement si vous savez ce que vous faites.</translation>
<translation>Attention : modifiez ces paramètres seulement si vous savez ce que vous faites.</translation>
</message>
<message>
<source>Time step:</source>
<translation>Période de temps:</translation>
<translation>Période de temps :</translation>
</message>
<message>
<source>8 digits</source>
@@ -2236,7 +2251,7 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>Code size:</source>
<translation>Taille du code:</translation>
<translation>Taille du code :</translation>
</message>
<message>
<source> sec</source>
@@ -2360,7 +2375,7 @@ attribuez lui un nom unique pour l&apos;identifier et acceptez la.</translation>
</message>
<message>
<source>Path of the group to list. Default is /</source>
<translation>Chemin du groupe à lister. Par défaut: /</translation>
<translation>Chemin du groupe à lister. Par défaut : /</translation>
</message>
<message>
<source>Print the UUIDs of the entries and groups.</source>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Minden hibajelentésnél legyenek mellékelve ezek az információk:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Disztribúció: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1094,7 +1098,7 @@ Mindenképp megnyitja?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Az egyéni ikon már létezik</translation>
</message>
</context>
<context>
@@ -1317,6 +1321,17 @@ Be lehet importálni az Adatbázis &gt; „KeePass 1 adatbázis importálása…
Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a régi KeePassX 0.4 verzióval.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nem lehet kiutalni a kihívás-választ.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nem lehet kiszámítani a mesterkulcsot</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1337,7 +1352,7 @@ Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>A meglévő egypéldányos zárolási fájl érvénytelen. Új példány indítása.</translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Sertakan informasi berikut setiap Anda melaporkan bug:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribusi: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1317,6 +1321,17 @@ Anda bisa mengimpornya dengan mengklik Basis Data &gt; &apos;Impor basis data Ke
Ini adalah migrasi satu arah. Anda tidak akan bisa membuka basis data yang diimpor dengan versi lama KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Tidak bisa mengkalkulasi kunci utama</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,10 @@ CPU アーキテクチャ: %2
<source>Include the following information whenever you report a bug:</source>
<translation>:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1094,7 +1098,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>カスタムアイコンは既に存在します</translation>
</message>
</context>
<context>
@@ -1317,6 +1321,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1337,7 +1352,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>

View File

@@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1307,6 +1311,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Басты парольді есептеу мүмкін емес</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ CPU 아키텍처: %2
<source>Include the following information whenever you report a bug:</source>
<translation> :</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1092,7 +1096,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>사용자 정의 아이콘이 이미 존재함</translation>
</message>
</context>
<context>
@@ -1315,6 +1319,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
. KeePassX 0.4 .</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation> .</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation> </translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1335,7 +1350,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation> . .</translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Branduolys: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Pranešdami apie klaidą, visuomet pateikite ir š informaciją:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Platinimas: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1094,7 +1098,7 @@ Ar vis tiek norite ją atverti?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Tinkinta piktograma jau yra</translation>
</message>
</context>
<context>
@@ -1317,6 +1321,17 @@ Jūs galite ją importuoti, nuspausdami Duomenų bazė &gt; &quot;Importuoti Kee
Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų bazės, naudodami senąją KeePassX 0.4 versija.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nepavyko išduoti iššūkio atsakymo.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nepavyko apskaičiuoti pagrindinio rakto</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1337,7 +1352,7 @@ Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Esamas vieno egzemplioriaus užrakto failas yra neteisingas. Paleidžiamas naujas egzempliorius.</translation>
</message>
</context>
<context>

View File

@@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1310,6 +1314,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Niet mogelijk om hoofdsleutel te berekenen</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Jądro: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Uwzględnij następujące informacje, gdy zgłaszasz błąd:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Dystrybucja: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1095,7 +1099,7 @@ Czy chcesz ją otworzyć mimo to?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Ikona niestandardowa już istnieje</translation>
</message>
</context>
<context>
@@ -1318,6 +1322,17 @@ Możesz zaimportować ją przez wybranie Baza danych &gt; &apos;Importuj bazę d
Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej bazy danych za pomocą starej wersji KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nie można wywołać wyzwania-odpowiedzi.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nie mogę wyliczyć głównego klucza</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1338,7 +1353,7 @@ Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej baz
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Istniejący plik blokady pojedynczego wystąpienia jest nieprawidłowy. Uruchamianie nowego wystąpienia.</translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclua as informações abaixo quando reportar um erro:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1314,6 +1318,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Não foi possível calcular a chave mestre</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclua as seguintes informações sempre que reportar um erro:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribuição: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1095,7 +1099,7 @@ Ainda assim deseja abrir a base de dados?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Já existe um ícone personalizado</translation>
</message>
</context>
<context>
@@ -1318,6 +1322,17 @@ Pode importá-lo clicando em Base de dados - &gt; &apos;Importar base de dados d
Esta é uma migração unidirecional. Não será possível abrir a base de dados importada com a versão 0.4 do KeePassX.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Incapaz de emitir a pergunta de segurança.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Impossível de calcular a chave-mestre</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1338,7 +1353,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>O ficheiro de bloqueio da instância única é inválido. A iniciar nova instância.</translation>
</message>
</context>
<context>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Включите следующую информацию, когда сообщаете об ошибке:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Дистрибутив: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -215,7 +219,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Replace username and password with references</source>
<translation>Заменить имя пользователя и пароль к ссылкам</translation>
<translation>Использовать ссылки для имени пользователя и пароля</translation>
</message>
<message>
<source>Copy history</source>
@@ -223,7 +227,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Append &apos; - Clone&apos; to title</source>
<translation>Добавить&apos; - Клонировать&apos; в заголовок</translation>
<translation>Добавить к названию « - клон»</translation>
</message>
</context>
<context>
@@ -575,7 +579,7 @@ Otherwise your changes are lost.</source>
<message>
<source>&quot;%1&quot; is in edit mode.
Discard changes and close anyway?</source>
<translation>«%1» в режиме редактирования.
<translation>«%1» в режиме правки.
Отменить изменения и всё равно закрыть?</translation>
</message>
<message>
@@ -737,7 +741,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Empty recycle bin?</source>
<translation>Корзина пустая?</translation>
<translation>Очистить корзину?</translation>
</message>
<message>
<source>Are you sure you want to permanently delete everything from your recycle bin?</source>
@@ -780,7 +784,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Edit entry</source>
<translation>Редактировать запись</translation>
<translation>Править запись</translation>
</message>
<message>
<source>Different passwords supplied.</source>
@@ -834,7 +838,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>[PROTECTED] Press reveal to view or edit</source>
<translation>[Защищён] Нажмите для открытия просмотра или редактирования</translation>
<translation>[Защищён] Нажмите для открытия просмотра или правки</translation>
</message>
<message>
<source>Are you sure you want to remove this attachment?</source>
@@ -849,7 +853,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Add</source>
<translation>Добавить</translation>
<translation>Создать</translation>
</message>
<message>
<source>Remove</source>
@@ -942,7 +946,7 @@ Do you want to open it anyway?</source>
<name>EditEntryWidgetMain</name>
<message>
<source>Title:</source>
<translation>Заголовок:</translation>
<translation>Название:</translation>
</message>
<message>
<source>Username:</source>
@@ -993,7 +997,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Edit group</source>
<translation>Редактировать группу</translation>
<translation>Править группу</translation>
</message>
<message>
<source>Enable</source>
@@ -1091,22 +1095,22 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Hint: You can enable Google as a fallback under Tools&gt;Settings&gt;Security</source>
<translation>Подсказка: вы можете включить Google в качестве резервного копирования в меню «Инструменты»&gt; «Настройки»&gt; «Безопасность»</translation>
<translation>Подсказка: в качестве резервного варианта для получения значков сайтов возможно использовать Google. Включите этот параметр в меню «Инструменты» -&gt; «Настройки» -&gt; «Безопасность»</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Пользовательский значок уже существует</translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
<message>
<source>Created:</source>
<translation>Создано:</translation>
<translation>Создание:</translation>
</message>
<message>
<source>Modified:</source>
<translation>Изменено:</translation>
<translation>Изменение:</translation>
</message>
<message>
<source>Accessed:</source>
@@ -1121,7 +1125,7 @@ Do you want to open it anyway?</source>
<name>Entry</name>
<message>
<source> - Clone</source>
<translation> - Клонировать</translation>
<translation> - клон</translation>
</message>
</context>
<context>
@@ -1139,7 +1143,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Title</source>
<translation>Заголовок</translation>
<translation>Имя записи</translation>
</message>
<message>
<source>Username</source>
@@ -1158,7 +1162,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Title</source>
<translation>Заголовок</translation>
<translation>Имя записи</translation>
</message>
<message>
<source>Username</source>
@@ -1318,6 +1322,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
Это одностороннее перемещение. Вы не сможете открыть импортированную базу данных на старой версии KeePassX 0,4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Не удалось выполнить запрос ответа.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Невозможно вычислить мастер-пароль</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -1338,7 +1353,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Запускается новый экземпляр программы, т.к. файл блокировки запуска повреждён.</translation>
</message>
</context>
<context>
@@ -1437,11 +1452,11 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>&amp;Add new entry</source>
<translation>&amp;Добавить новую запись</translation>
<translation>&amp;Создать запись</translation>
</message>
<message>
<source>&amp;View/Edit entry</source>
<translation>&amp;Посмотреть/редактировать запись</translation>
<translation>&amp;Открыть/править запись</translation>
</message>
<message>
<source>&amp;Delete entry</source>
@@ -1449,11 +1464,11 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>&amp;Add new group</source>
<translation>&amp;Добавить новую группу</translation>
<translation>&amp;Создать группу</translation>
</message>
<message>
<source>&amp;Edit group</source>
<translation>&amp;Редактировать группу</translation>
<translation>&amp;Править группу</translation>
</message>
<message>
<source>&amp;Delete group</source>
@@ -1509,7 +1524,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>&amp;Title</source>
<translation>&amp;Заголовок</translation>
<translation>&amp;Имя записи</translation>
</message>
<message>
<source>&amp;URL</source>
@@ -1541,7 +1556,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>Empty recycle bin</source>
<translation>Корзина пустая</translation>
<translation>Очистить корзину</translation>
</message>
<message>
<source>Access error for config file %1</source>
@@ -1686,15 +1701,15 @@ Using default port 19455.</source>
</message>
<message>
<source>&amp;Return only best matching entries</source>
<translation>&amp;Возврат только наиболее совпадающих записей</translation>
<translation>&amp;Показывать только лучшие совпадения</translation>
</message>
<message>
<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>
<source>&amp;Match URL schemes</source>
<translation>&amp;Совпадения схем адресов</translation>
<translation>&amp;Проверять протокол</translation>
</message>
<message>
<source>Password Generator</source>
@@ -1864,7 +1879,7 @@ Change them only if you know what you are doing.</source>
</message>
<message>
<source>Title</source>
<translation>Заголовок</translation>
<translation>Имя записи</translation>
</message>
<message>
<source>Username</source>
@@ -2070,7 +2085,7 @@ give it a unique name to identify and accept it.</source>
<name>SettingsWidgetGeneral</name>
<message>
<source>Remember last databases</source>
<translation>Помнить последнюю базу данных</translation>
<translation>Запоминать последнюю базу данных</translation>
</message>
<message>
<source>Automatically save on exit</source>
@@ -2102,7 +2117,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Hide window to system tray when minimized</source>
<translation>При сворачивании прятать окно в системный лоток</translation>
<translation>При сворачивании скрывать окно в системный лоток</translation>
</message>
<message>
<source>Load previous databases on startup</source>
@@ -2114,7 +2129,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Hide window to system tray instead of app exit</source>
<translation>Прятать окно в системный лоток вместо выхода</translation>
<translation>Скрывать окно в системный лоток вместо выхода</translation>
</message>
<message>
<source>Minimize window at application startup</source>
@@ -2126,11 +2141,11 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Remember last key files and security dongles</source>
<translation>Помнить последние ключевые файлы и ключи безопасности</translation>
<translation>Запоминать последние использованные файлы ключей и устройства</translation>
</message>
<message>
<source>Don&apos;t mark database as modified for non-data changes (e.g., expanding groups)</source>
<translation>Не помечать базу данных как измененную без изменения данных (например, для расширения групп)</translation>
<translation>Не помечать базу данных изменённой при действиях, не связанных с изменением данных (например, при распахивании групп)</translation>
</message>
<message>
<source>Auto-Type</source>
@@ -2138,7 +2153,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Use entry title and URL to match windows for global Auto-Type</source>
<translation>Использовать URL и заголовок записи при сопоставлении окон для глобального автоввода</translation>
<translation>Использовать для поиска URL и название записи</translation>
</message>
<message>
<source>Always ask before performing Auto-Type</source>
@@ -2169,7 +2184,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Lock databases after inactivity of</source>
<translation>Заблокировать базу данных после неактивности длительностью</translation>
<translation>Блокировать базу данных при отсутствии активности длительностью</translation>
</message>
<message>
<source>Show passwords in cleartext by default</source>
@@ -2177,7 +2192,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Lock databases after minimizing the window</source>
<translation>Блокировать базу данных после сворачивания окна</translation>
<translation>Блокировать базу данных при сворачивания окна</translation>
</message>
<message>
<source>Don&apos;t require password repeat when it is visible</source>
@@ -2201,7 +2216,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Use Google as fallback for downloading website icons</source>
<translation>Использовать Google как резерв для загрузки значков веб-сайтов</translation>
<translation>Использовать Google в качестве резервного варианта для получения значков веб-сайтов</translation>
</message>
</context>
<context>

View File

@@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1305,6 +1309,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Izračun glavnega ključa ni uspel</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -24,7 +24,8 @@
<message>
<source>Version %1
</source>
<translation type="unfinished"/>
<translation>Version %1
</translation>
</message>
<message>
<source>Revision: %1</source>
@@ -64,6 +65,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -196,7 +201,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Empty password</source>
<translation type="unfinished"/>
<translation>Tomt lösenord</translation>
</message>
<message>
<source>Changing master key failed: no YubiKey inserted.</source>
@@ -254,7 +259,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Comments start with</source>
<translation type="unfinished"/>
<translation>Kommentarer inleds med</translation>
</message>
<message>
<source>First record has field names</source>
@@ -282,7 +287,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Empty fieldname </source>
<translation type="unfinished"/>
<translation>Tomt fältnamn </translation>
</message>
<message>
<source>column </source>
@@ -337,7 +342,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source> rows, </source>
<translation type="unfinished"/>
<translation> rader, </translation>
</message>
<message>
<source> columns</source>
@@ -622,7 +627,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Open CSV file</source>
<translation type="unfinished"/>
<translation>Öppna CSV fil</translation>
</message>
</context>
<context>
@@ -677,7 +682,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Searching...</source>
<translation type="unfinished"/>
<translation>Söker...</translation>
</message>
<message>
<source>No current database.</source>
@@ -689,11 +694,11 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Search Results (%1)</source>
<translation type="unfinished"/>
<translation>Sökresultat (%1)</translation>
</message>
<message>
<source>No Results</source>
<translation type="unfinished"/>
<translation>Inget resultat</translation>
</message>
<message>
<source>Execute command?</source>
@@ -818,7 +823,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Confirm Remove</source>
<translation type="unfinished"/>
<translation>Bekräfta borttagning</translation>
</message>
<message>
<source>Are you sure you want to remove this attribute?</source>
@@ -865,7 +870,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Protect</source>
<translation type="unfinished"/>
<translation>Skydda</translation>
</message>
<message>
<source>Reveal</source>
@@ -1307,6 +1312,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Kunde inte räkna nu master-nyckeln</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ MİB mimarisi: %2
<source>Include the following information whenever you report a bug:</source>
<translation>Bir hata bildirirken şu bilgileri ekleyin:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1313,6 +1317,17 @@ Veri tabanı &gt; &apos;KeePass1 veri tabanı içe aktar...&apos;a tıklayarak i
Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski KeePassX 0.4 sürümüyle açamayacaksınız.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Ana anahtar hesaplanamıyor</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Коли Ви повідомляєте про ваду, завжди долучайте таку інформацію:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1318,6 +1322,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
Перетворення можливе лише в одному напрямку. Ви не зможете відкрити імпортоване сховище старою версією KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Неможливо видати виклик-відповідь.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Неможливо вирахувати головний ключ</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ CPU 架构:%2
<source>Include the following information whenever you report a bug:</source>
<translation> bug </translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -1316,6 +1320,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation> Bug </translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>%1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -192,7 +196,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Cha&amp;llenge Response</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<source>Refresh</source>
@@ -204,7 +208,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Changing master key failed: no YubiKey inserted.</source>
<translation type="unfinished"/>
<translation> YubiKey</translation>
</message>
</context>
<context>
@@ -393,7 +397,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Challenge Response:</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>
@@ -1094,7 +1098,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>自訂圖示已經存在</translation>
</message>
</context>
<context>
@@ -1317,6 +1321,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@@ -2125,7 +2140,7 @@ give it a unique name to identify and accept it.</source>
</message>
<message>
<source>Remember last key files and security dongles</source>
<translation></translation>
<translation></translation>
</message>
<message>
<source>Don&apos;t mark database as modified for non-data changes (e.g., expanding groups)</source>

View File

@@ -1,18 +1,18 @@
name: keepassxc
version: 2.2.1
version: 2.2.3
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: |
KeePassXC is an application for people with extremely high demands on secure
personal data management. It has a light interface, is cross platform and
is published under the terms of the GNU General Public License.
personal data management. It has a light interface, is cross-platform and
published under the terms of the GNU General Public License.
confinement: strict
apps:
keepassxc:
command: desktop-launch keepassxc
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb]
desktop: share/applications/keepassxc.desktop
desktop: usr/share/applications/org.keepassxc.desktop
cli:
command: keepassxc-cli
plugs: [gsettings, home, removable-media, raw-usb]
@@ -23,6 +23,8 @@ parts:
plugin: cmake
configflags:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/usr
- -DKEEPASSXC_DIST_TYPE=Snap
- -DWITH_TESTS=OFF
- -DWITH_XC_AUTOTYPE=ON
- -DWITH_XC_HTTP=ON
@@ -39,6 +41,8 @@ parts:
- libxtst-dev
- libyubikey-dev
- libykpers-1-dev
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
after: [desktop-qt5]
# Redefine desktop-qt5 stage packages to work with Ubuntu 17.04
@@ -55,10 +59,12 @@ parts:
- locales-all
# 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
plugin: dump
organize:
data/xdg-open: bin/xdg-open
source-depth: 1
plugin: nil
install: |
install -D -t $SNAPCRAFT_PART_INSTALL/usr/bin/ data/xdg-open
stage-packages:
- dbus

View File

@@ -59,6 +59,8 @@ set(keepassx_SOURCES
core/Tools.cpp
core/Translator.cpp
core/Uuid.cpp
core/Base32.h
core/Base32.cpp
cli/PasswordInput.cpp
cli/PasswordInput.h
crypto/Crypto.cpp
@@ -138,8 +140,6 @@ set(keepassx_SOURCES
streams/qtiocompressor.cpp
streams/StoreDataStream.cpp
streams/SymmetricCipherStream.cpp
totp/base32.h
totp/base32.cpp
totp/totp.h
totp/totp.cpp
)

View File

@@ -16,6 +16,11 @@
#cmakedefine WITH_XC_AUTOTYPE
#cmakedefine WITH_XC_YUBIKEY
#cmakedefine KEEPASSXC_DIST
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
#cmakedefine KEEPASSXC_DIST_SNAP
#cmakedefine KEEPASSXC_DIST_APPIMAGE
#cmakedefine HAVE_PR_SET_DUMPABLE 1
#cmakedefine HAVE_RLIMIT_CORE 1
#cmakedefine HAVE_PT_DENY_ATTACH 1

290
src/core/Base32.cpp Normal file
View File

@@ -0,0 +1,290 @@
/*
* 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/>.
*/
/* Conforms to RFC 4648. For details, see: https://tools.ietf.org/html/rfc4648
* Use the functions Base32::addPadding/1, Base32::removePadding/1 or
* Base32::sanitizeInput/1 to fix input or output for a particular
* applications (e.g. to use with Google Authenticator).
*/
#include "Base32.h"
constexpr quint64 MASK_40BIT = quint64(0xF8) << 32;
constexpr quint64 MASK_35BIT = quint64(0x7C0000000);
constexpr quint64 MASK_25BIT = quint64(0x1F00000);
constexpr quint64 MASK_20BIT = quint64(0xF8000);
constexpr quint64 MASK_10BIT = quint64(0x3E0);
constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
constexpr quint8 ALPH_POS_2 = 26;
constexpr quint8 ASCII_2 = static_cast<quint8>('2');
constexpr quint8 ASCII_7 = static_cast<quint8>('7');
constexpr quint8 ASCII_A = static_cast<quint8>('A');
constexpr quint8 ASCII_Z = static_cast<quint8>('Z');
constexpr quint8 ASCII_a = static_cast<quint8>('a');
constexpr quint8 ASCII_z = static_cast<quint8>('z');
constexpr quint8 ASCII_EQ = static_cast<quint8>('=');
QVariant Base32::decode(const QByteArray& encodedData)
{
if (encodedData.size() <= 0) {
return QVariant::fromValue(QByteArray(""));
}
if (encodedData.size() % 8 != 0) {
return QVariant();
}
int nPads = 0;
for (int i = -1; i > -7; --i) {
if ('=' == encodedData[encodedData.size() + i])
++nPads;
}
int specialOffset;
int nSpecialBytes;
switch (nPads) { // in {0, 1, 3, 4, 6}
case 1:
nSpecialBytes = 4;
specialOffset = 3;
break;
case 3:
nSpecialBytes = 3;
specialOffset = 1;
break;
case 4:
nSpecialBytes = 2;
specialOffset = 4;
break;
case 6:
nSpecialBytes = 1;
specialOffset = 2;
break;
default:
nSpecialBytes = 0;
specialOffset = 0;
}
Q_ASSERT(encodedData.size() > 0);
const int nQuanta = encodedData.size() / 8;
const int nBytes = nSpecialBytes > 0 ? (nQuanta - 1) * 5 + nSpecialBytes : nQuanta * 5;
QByteArray data(nBytes, Qt::Uninitialized);
int i = 0;
int o = 0;
while (i < encodedData.size()) {
quint64 quantum = 0;
int nQuantumBytes = 5;
for (int n = 0; n < 8; ++n) {
quint8 ch = static_cast<quint8>(encodedData[i++]);
if ((ASCII_A <= ch && ch <= ASCII_Z) || (ASCII_a <= ch && ch <= ASCII_z)) {
ch -= ASCII_A;
if (ch >= ALPH_POS_2)
ch -= ASCII_a - ASCII_A;
} else {
if (ASCII_2 <= ch && ch <= ASCII_7) {
ch -= ASCII_2;
ch += ALPH_POS_2;
} else {
if (ASCII_EQ == ch) {
if (i == encodedData.size()) {
// finished with special quantum
quantum >>= specialOffset;
nQuantumBytes = nSpecialBytes;
}
continue;
} else {
// illegal character
return QVariant();
}
}
}
quantum <<= 5;
quantum |= ch;
}
const int offset = (nQuantumBytes - 1) * 8;
quint64 mask = quint64(0xFF) << offset;
for (int n = offset; n >= 0 && o < nBytes; n -= 8) {
data[o++] = static_cast<char>((quantum & mask) >> n);
mask >>= 8;
}
}
Q_ASSERT(encodedData.size() == i);
Q_ASSERT(nBytes == o);
return QVariant::fromValue(data);
}
QByteArray Base32::encode(const QByteArray& data)
{
if (data.size() < 1) {
return QByteArray();
}
const int nBits = data.size() * 8;
const int rBits = nBits % 40; // in {0, 8, 16, 24, 32}
const int nQuanta = nBits / 40 + (rBits > 0 ? 1 : 0);
const int nBytes = nQuanta * 8;
QByteArray encodedData(nQuanta * 8, Qt::Uninitialized);
int i = 0;
int o = 0;
int n;
quint64 mask;
quint64 quantum;
// 40-bits of input per input group
while (i + 5 <= data.size()) {
quantum = 0;
for (n = 32; n >= 0; n -= 8) {
quantum |= (static_cast<quint64>(data[i++]) << n);
}
mask = MASK_40BIT;
int index;
for (n = 35; n >= 0; n -= 5) {
index = (quantum & mask) >> n;
encodedData[o++] = alphabet[index];
mask >>= 5;
}
}
// < 40-bits of input at final input group
if (i < data.size()) {
Q_ASSERT(rBits > 0);
quantum = 0;
for (n = rBits - 8; n >= 0; n -= 8)
quantum |= static_cast<quint64>(data[i++]) << n;
switch (rBits) {
case 8: // expand to 10 bits
quantum <<= 2;
mask = MASK_10BIT;
n = 5;
break;
case 16: // expand to 20 bits
quantum <<= 4;
mask = MASK_20BIT;
n = 15;
break;
case 24: // expand to 25 bits
quantum <<= 1;
mask = MASK_25BIT;
n = 20;
break;
default: // expand to 35 bits
Q_ASSERT(rBits == 32);
quantum <<= 3;
mask = MASK_35BIT;
n = 30;
}
while (n >= 0) {
int index = (quantum & mask) >> n;
encodedData[o++] = alphabet[index];
mask >>= 5;
n -= 5;
}
// add pad characters
while (o < encodedData.size()) {
encodedData[o++] = '=';
}
}
Q_ASSERT(data.size() == i);
Q_ASSERT(nBytes == o);
return encodedData;
}
QByteArray Base32::addPadding(const QByteArray& encodedData)
{
if (encodedData.size() <= 0 || encodedData.size() % 8 == 0) {
return encodedData;
}
const int rBytes = encodedData.size() % 8;
// rBytes must be a member of {2, 4, 5, 7}
if (1 == rBytes || 3 == rBytes || 6 == rBytes) {
return encodedData;
}
QByteArray newEncodedData(encodedData);
for (int nPads = 8 - rBytes; nPads > 0; --nPads) {
newEncodedData.append('=');
}
return newEncodedData;
}
QByteArray Base32::removePadding(const QByteArray& encodedData)
{
if (encodedData.size() <= 0 || encodedData.size() % 8 != 0) {
return encodedData; // return same bad input
}
int nPads = 0;
for (int i = -1; i > -7; --i) {
if ('=' == encodedData[encodedData.size() + i]) {
++nPads;
}
}
QByteArray newEncodedData(encodedData);
newEncodedData.remove(encodedData.size() - nPads, nPads);
newEncodedData.resize(encodedData.size() - nPads);
return newEncodedData;
}
QByteArray Base32::sanitizeInput(const QByteArray& encodedData)
{
if (encodedData.size() <= 0) {
return encodedData;
}
QByteArray newEncodedData(encodedData.size(), Qt::Uninitialized);
int i = 0;
for (auto ch : encodedData) {
switch (ch) {
case '0':
newEncodedData[i++] = 'O';
break;
case '1':
newEncodedData[i++] = 'L';
break;
case '8':
newEncodedData[i++] = 'B';
break;
default:
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('2' <= ch && ch <= '7')) {
newEncodedData[i++] = ch;
}
}
}
newEncodedData.resize(i);
return addPadding(newEncodedData);
}

42
src/core/Base32.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* 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/>.
*/
/* Conforms to RFC 4648. For details, see: https://tools.ietf.org/html/rfc4648
* Use the functions Base32::addPadding/1, Base32::removePadding/1 or
* Base32::sanitizeInput/1 to fix input or output for a particular
* applications (e.g. to use with Google Authenticator).
*/
#ifndef BASE32_H
#define BASE32_H
#include <QByteArray>
#include <QVariant>
#include <QtCore/qglobal.h>
class Base32
{
public:
Base32() = default;
Q_REQUIRED_RESULT static QVariant decode(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray encode(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray addPadding(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray removePadding(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray sanitizeInput(const QByteArray&);
};
#endif // BASE32_H

View File

@@ -257,6 +257,25 @@ bool Database::hasKey() const
return m_data.hasKey;
}
bool Database::transformKeyWithSeed(const QByteArray& transformSeed)
{
Q_ASSERT(hasKey());
bool ok;
QString errorString;
QByteArray transformedMasterKey =
m_data.key.transform(transformSeed, transformRounds(), &ok, &errorString);
if (!ok) {
return false;
}
m_data.transformSeed = transformSeed;
m_data.transformedMasterKey = transformedMasterKey;
return true;
}
bool Database::verifyKey(const CompositeKey& key) const
{
Q_ASSERT(hasKey());
@@ -328,6 +347,15 @@ void Database::emptyRecycleBin()
void Database::merge(const Database* other)
{
m_rootGroup->merge(other->rootGroup());
for (Uuid customIconId : other->metadata()->customIcons().keys()) {
QImage customIcon = other->metadata()->customIcon(customIconId);
if (!this->metadata()->containsCustomIcon(customIconId)) {
qDebug("Adding custom icon %s to database.", qPrintable(customIconId.toHex()));
this->metadata()->addCustomIcon(customIconId, customIcon);
}
}
emit modified();
}

View File

@@ -106,6 +106,7 @@ public:
*/
bool setKey(const CompositeKey& key);
bool hasKey() const;
bool transformKeyWithSeed(const QByteArray& transformSeed);
bool verifyKey(const CompositeKey& key) const;
void recycleEntry(Entry* entry);
void recycleGroup(Group* group);

View File

@@ -801,28 +801,31 @@ QString Entry::resolvePlaceholder(const QString& str) const
QString Entry::resolveUrl(const QString& url) const
{
#ifdef WITH_XC_HTTP
QString newUrl = url;
if (!url.contains("://")) {
if (!url.isEmpty() && !url.contains("://")) {
// URL doesn't have a protocol, add https by default
newUrl.prepend("https://");
}
QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid()) {
if (tempUrl.scheme() == "cmd") {
// URL is a cmd, hopefully the second argument is an URL
QStringList cmd = newUrl.split(" ");
if (cmd.size() > 1) {
return resolveUrl(cmd[1].remove("'").remove("\""));
if (newUrl.startsWith("cmd://")) {
QStringList cmdList = newUrl.split(" ");
for (int i=1; i < cmdList.size(); ++i) {
// Don't pass arguments to the resolveUrl function (they look like URL's)
if (!cmdList[i].startsWith("-") && !cmdList[i].startsWith("/")) {
return resolveUrl(cmdList[i].remove(QRegExp("'|\"")));
}
} else if (tempUrl.scheme() == "http" || tempUrl.scheme() == "https") {
// URL is nice
return tempUrl.url();
}
// No URL in this command
return QString("");
}
#else
Q_UNUSED(url);
#endif
// Validate the URL
QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid() && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https")) {
return tempUrl.url();
}
// No valid http URL's found
return QString("");
}

View File

@@ -91,17 +91,29 @@ QString FilePath::pluginPath(const QString& name)
QIcon FilePath::applicationIcon()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc", false);
#else
return icon("apps", "keepassxc");
#endif
}
QIcon FilePath::trayIconLocked()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc-locked", false);
#else
return icon("apps", "keepassxc-locked");
#endif
}
QIcon FilePath::trayIconUnlocked()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc-unlocked", false);
#else
return icon("apps", "keepassxc-unlocked");
#endif
}
QIcon FilePath::icon(const QString& category, const QString& name, bool fromTheme)

View File

@@ -454,7 +454,7 @@ void Metadata::copyCustomIcons(const QSet<Uuid>& iconList, const Metadata* other
QByteArray Metadata::hashImage(const QImage& image)
{
auto data = QByteArray((char*)image.bits(), image.byteCount());
auto data = QByteArray(reinterpret_cast<const char*>(image.bits()), image.byteCount());
return QCryptographicHash::hash(data, QCryptographicHash::Md5);
}

View File

@@ -59,11 +59,11 @@ public:
return m_backend->process(data, ok);
}
inline bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT {
Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data) {
return m_backend->processInPlace(data);
}
inline bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT {
Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data, quint64 rounds) {
Q_ASSERT(rounds > 0);
return m_backend->processInPlace(data, rounds);
}

View File

@@ -29,8 +29,8 @@ public:
virtual bool setIv(const QByteArray& iv) = 0;
virtual QByteArray process(const QByteArray& data, bool* ok) = 0;
virtual bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT = 0;
virtual bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT = 0;
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data) = 0;
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0;
virtual bool reset() = 0;
virtual int blockSize() const = 0;

View File

@@ -86,6 +86,8 @@ bool SymmetricCipherGcrypt::init()
gcry_error_t error;
if(m_ctx != nullptr)
gcry_cipher_close(m_ctx);
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
if (error != 0) {
setErrorString(error);
@@ -148,9 +150,10 @@ QByteArray SymmetricCipherGcrypt::process(const QByteArray& data, bool* ok)
if (error != 0) {
setErrorString(error);
*ok = false;
} else {
*ok = true;
}
*ok = true;
return result;
}

View File

@@ -35,8 +35,8 @@ public:
bool setIv(const QByteArray& iv);
QByteArray process(const QByteArray& data, bool* ok);
bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT;
bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT;
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data);
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data, quint64 rounds);
bool reset();
int blockSize() const;

View File

@@ -29,7 +29,7 @@ public:
bool init(const QByteArray& key);
QByteArray randomBytes(int size, bool* ok);
QByteArray process(const QByteArray& data, bool* ok);
bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT;
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data);
QString errorString() const;
private:

View File

@@ -1,5 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -18,36 +19,29 @@
#include "KeePass2Repair.h"
#include <QBuffer>
#include <QScopedPointer>
#include <QRegExp>
#include "format/KeePass2RandomStream.h"
#include "format/KeePass2Reader.h"
#include "format/KeePass2XmlReader.h"
KeePass2Repair::KeePass2Repair()
: m_db(nullptr)
KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
{
}
KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
{
m_db = nullptr;
m_errorStr.clear();
KeePass2Reader reader;
reader.setSaveXml(true);
Database* db = reader.readDatabase(device, key, true);
QScopedPointer<Database> db(reader.readDatabase(device, key, true));
if (!reader.hasError()) {
delete db;
return NothingTodo;
return qMakePair(NothingTodo, nullptr);
}
QByteArray xmlData = reader.xmlData();
if (!db || xmlData.isEmpty()) {
delete db;
m_errorStr = reader.errorString();
return UnableToOpen;
return qMakePair(UnableToOpen, nullptr);
}
bool repairAction = false;
@@ -59,8 +53,7 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
&& encodingRegExp.cap(1).compare("utf8", Qt::CaseInsensitive) != 0)
{
// database is not utf-8 encoded, we don't support repairing that
delete db;
return RepairFailed;
return qMakePair(RepairFailed, nullptr);
}
}
@@ -75,8 +68,7 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
if (!repairAction) {
// we were unable to find the problem
delete db;
return RepairFailed;
return qMakePair(RepairFailed, nullptr);
}
KeePass2RandomStream randomStream;
@@ -84,23 +76,16 @@ KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, c
KeePass2XmlReader xmlReader;
QBuffer buffer(&xmlData);
buffer.open(QIODevice::ReadOnly);
xmlReader.readDatabase(&buffer, db, &randomStream);
xmlReader.readDatabase(&buffer, db.data(), &randomStream);
if (xmlReader.hasError()) {
delete db;
return RepairFailed;
return qMakePair(RepairFailed, nullptr);
}
else {
m_db = db;
return RepairSuccess;
return qMakePair(RepairSuccess, db.take());
}
}
Database* KeePass2Repair::database() const
{
return m_db;
}
QString KeePass2Repair::errorString() const
{
return m_errorStr;

View File

@@ -1,5 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -20,6 +21,7 @@
#include <QCoreApplication>
#include <QIODevice>
#include <QPair>
#include "core/Database.h"
#include "keys/CompositeKey.h"
@@ -36,14 +38,12 @@ public:
RepairSuccess,
RepairFailed
};
using RepairOutcome = QPair<RepairResult, Database*>;
KeePass2Repair();
RepairResult repairDatabase(QIODevice* device, const CompositeKey& key);
Database* database() const;
RepairOutcome repairDatabase(QIODevice* device, const CompositeKey& key);
QString errorString() const;
private:
Database* m_db;
QString m_errorStr;
};

View File

@@ -45,6 +45,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
m_error = false;
m_errorStr.clear();
QByteArray transformSeed = randomGen()->randomArray(32);
QByteArray masterSeed = randomGen()->randomArray(32);
QByteArray encryptionIV = randomGen()->randomArray(16);
QByteArray protectedStreamKey = randomGen()->randomArray(32);
@@ -52,7 +53,12 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
QByteArray endOfHeader = "\r\n\r\n";
if (db->challengeMasterSeed(masterSeed) == false) {
raiseError("Unable to issue challenge-response.");
raiseError(tr("Unable to issue challenge-response."));
return;
}
if (!db->transformKeyWithSeed(transformSeed)) {
raiseError(tr("Unable to calculate master key"));
return;
}

View File

@@ -18,6 +18,8 @@
#ifndef KEEPASSX_KEEPASS2WRITER_H
#define KEEPASSX_KEEPASS2WRITER_H
#include <QCoreApplication>
#include "format/KeePass2.h"
#include "keys/CompositeKey.h"
@@ -26,6 +28,8 @@ class QIODevice;
class KeePass2Writer
{
Q_DECLARE_TR_FUNCTIONS(KeePass2Writer)
public:
KeePass2Writer();
void writeDatabase(QIODevice* device, Database* db);

View File

@@ -55,10 +55,14 @@ AboutDialog::AboutDialog(QWidget* parent)
QString debugInfo = "KeePassXC - ";
debugInfo.append(tr("Version %1\n").arg(KEEPASSX_VERSION));
if (!commitHash.isEmpty()) {
debugInfo.append(tr("Revision: %1").arg(commitHash).append("\n\n"));
debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n"));
}
debugInfo.append(QString("%1\n- Qt %2\n- %3\n\n")
#ifdef KEEPASSXC_DIST
debugInfo.append(tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n"));
#endif
debugInfo.append("\n").append(QString("%1\n- Qt %2\n- %3\n\n")
.arg(tr("Libraries:"))
.arg(QString::fromLocal8Bit(qVersion()))
.arg(Crypto::backendVersion()));

View File

@@ -70,9 +70,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey()));
connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse()));
connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
#else
m_ui->checkChallengeResponse->setVisible(false);
m_ui->buttonRedetectYubikey->setVisible(false);
@@ -98,7 +95,27 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event)
m_ui->editPassword->setFocus();
#ifdef WITH_XC_YUBIKEY
pollYubikey();
// showEvent() may be called twice, so make sure we are only polling once
if (!m_yubiKeyBeingPolled) {
connect(YubiKey::instance(), SIGNAL(detected(int, bool)), SLOT(yubikeyDetected(int, bool)),
Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(detectComplete()), SLOT(yubikeyDetectComplete()), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
pollYubikey();
m_yubiKeyBeingPolled = true;
}
#endif
}
void DatabaseOpenWidget::hideEvent(QHideEvent* event)
{
DialogyWidget::hideEvent(event);
#ifdef WITH_XC_YUBIKEY
// Don't listen to any Yubikey events if we are hidden
disconnect(YubiKey::instance(), 0, this, 0);
m_yubiKeyBeingPolled = false;
#endif
}
@@ -151,7 +168,10 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile)
void DatabaseOpenWidget::openDatabase()
{
KeePass2Reader reader;
CompositeKey masterKey = databaseKey();
QSharedPointer<CompositeKey> masterKey = databaseKey();
if (masterKey.isNull()) {
return;
}
QFile file(m_filename);
if (!file.open(QIODevice::ReadOnly)) {
@@ -163,7 +183,7 @@ void DatabaseOpenWidget::openDatabase()
delete m_db;
}
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
m_db = reader.readDatabase(&file, masterKey);
m_db = reader.readDatabase(&file, *masterKey);
QApplication::restoreOverrideCursor();
if (m_db) {
@@ -171,20 +191,19 @@ void DatabaseOpenWidget::openDatabase()
m_ui->messageWidget->animatedHide();
}
emit editFinished(true);
}
else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.")
.append("\n").append(reader.errorString()), MessageWidget::Error);
} else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(reader.errorString()),
MessageWidget::Error);
m_ui->editPassword->clear();
}
}
CompositeKey DatabaseOpenWidget::databaseKey()
QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
{
CompositeKey masterKey;
auto masterKey = QSharedPointer<CompositeKey>::create();
if (m_ui->checkPassword->isChecked()) {
masterKey.addKey(PasswordKey(m_ui->editPassword->text()));
masterKey->addKey(PasswordKey(m_ui->editPassword->text()));
}
QHash<QString, QVariant> lastKeyFiles = config()->get("LastKeyFiles").toHash();
@@ -195,11 +214,11 @@ CompositeKey DatabaseOpenWidget::databaseKey()
QString keyFilename = m_ui->comboKeyFile->currentText();
QString errorMsg;
if (!key.load(keyFilename, &errorMsg)) {
m_ui->messageWidget->showMessage(tr("Can't open key file").append(":\n")
.append(errorMsg), MessageWidget::Error);
return CompositeKey();
m_ui->messageWidget->showMessage(tr("Can't open key file").append(":\n").append(errorMsg),
MessageWidget::Error);
return QSharedPointer<CompositeKey>();
}
masterKey.addKey(key);
masterKey->addKey(key);
lastKeyFiles[m_filename] = keyFilename;
} else {
lastKeyFiles.remove(m_filename);
@@ -226,9 +245,9 @@ CompositeKey DatabaseOpenWidget::databaseKey()
// read blocking mode from LSB and slot index number from second LSB
bool blocking = comboPayload & 1;
int slot = comboPayload >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
masterKey.addChallengeResponseKey(key);
int slot = comboPayload >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
masterKey->addChallengeResponseKey(key);
}
#endif
@@ -283,10 +302,6 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
YkChallengeResponseKey yk(slot, blocking);
// add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
if (config()->get("RememberLastKeyFiles").toBool()) {
QHash<QString, QVariant> lastChallengeResponse = config()->get("LastChallengeResponse").toHash();
@@ -296,8 +311,18 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
}
}
void DatabaseOpenWidget::yubikeyDetectComplete()
{
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
m_yubiKeyBeingPolled = false;
}
void DatabaseOpenWidget::noYubikeyFound()
{
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
m_yubiKeyBeingPolled = false;
}

View File

@@ -51,7 +51,8 @@ signals:
protected:
void showEvent(QShowEvent* event) override;
CompositeKey databaseKey();
void hideEvent(QHideEvent* event) override;
QSharedPointer<CompositeKey> databaseKey();
protected slots:
virtual void openDatabase();
@@ -63,6 +64,7 @@ private slots:
void activateChallengeResponse();
void browseKeyFile();
void yubikeyDetected(int slot, bool blocking);
void yubikeyDetectComplete();
void noYubikeyFound();
protected:
@@ -71,6 +73,7 @@ protected:
QString m_filename;
private:
bool m_yubiKeyBeingPolled = false;
Q_DISABLE_COPY(DatabaseOpenWidget)
};

View File

@@ -1,5 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -69,7 +70,8 @@ void DatabaseRepairWidget::openDatabase()
delete m_db;
}
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();
switch (repairResult) {
@@ -83,7 +85,7 @@ void DatabaseRepairWidget::openDatabase()
emit editFinished(false);
return;
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."));
emit editFinished(true);
return;

View File

@@ -129,7 +129,12 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
while (i.hasNext()) {
i.next();
if (i.value().canonicalFilePath == canonicalFilePath) {
setCurrentIndex(databaseIndex(i.key()));
if (!i.value().dbWidget->dbHasKey() && !(pw.isNull() && keyFile.isEmpty())) {
// If the database is locked and a pw or keyfile is provided, unlock it
i.value().dbWidget->switchToOpenDatabase(i.value().filePath, pw, keyFile);
} else {
setCurrentIndex(databaseIndex(i.key()));
}
return;
}
}
@@ -181,6 +186,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
lockFile->tryLock();
}
} else {
delete lockFile;
return;
}
}
@@ -203,7 +209,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
updateLastDatabases(dbStruct.filePath);
if (!pw.isNull() || !keyFile.isEmpty()) {
if (!(pw.isNull() && keyFile.isEmpty())) {
dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath, pw, keyFile);
}
else {
@@ -292,8 +298,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
if (!saveDatabase(db)) {
return false;
}
}
else {
} else if (dbStruct.dbWidget->currentMode() != DatabaseWidget::LockedMode) {
QMessageBox::StandardButton result =
MessageBox::question(
this, tr("Save changes?"),
@@ -301,10 +306,9 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
if (!saveDatabase(db)) {
return false;
return false;
}
}
else if (result == QMessageBox::Cancel) {
} else if (result == QMessageBox::Cancel) {
return false;
}
}
@@ -349,8 +353,13 @@ bool DatabaseTabWidget::saveDatabase(Database* 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);
QString errorMessage = db->saveToFile(dbStruct.canonicalFilePath);
dbStruct.dbWidget->blockAutoReload(false);
@@ -369,7 +378,6 @@ bool DatabaseTabWidget::saveDatabase(Database* db)
MessageWidget::Error);
return false;
}
} else {
return saveDatabaseAs(db);
}

View File

@@ -1125,7 +1125,7 @@ void DatabaseWidget::onWatchedFileChanged()
void DatabaseWidget::reloadDatabaseFile()
{
if (m_db == nullptr)
if (m_db == nullptr || currentMode() == DatabaseWidget::LockedMode)
return;
if (! config()->get("AutoReloadOnChange").toBool()) {

View File

@@ -302,6 +302,7 @@ void EditWidgetIcons::addCustomIcon(const QImage &icon)
}
// Select the new or existing icon
updateRadioButtonCustomIcons();
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
m_ui->customIconsView->setCurrentIndex(index);
}

View File

@@ -881,7 +881,7 @@ void MainWindow::toggleWindow()
raise();
activateWindow();
#if defined(Q_OS_LINUX) && ! defined(QT_NO_DBUS)
#if defined(Q_OS_LINUX) && ! defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
// re-register global D-Bus menu (needed on Ubuntu with Unity)
// see https://github.com/keepassxreboot/keepassxc/issues/271
// and https://bugreports.qt.io/browse/QTBUG-58723

View File

@@ -32,8 +32,17 @@ PasswordEdit::PasswordEdit(QWidget* parent)
setEchoMode(QLineEdit::Password);
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);
#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", passwordFont.styleName(), passwordFont.pointSize());
const QFont defaultFont;
if (passwordFont != defaultFont) {
passwordFont = consolasFont;
}
#endif
passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110);
setFont(passwordFont);
}

View File

@@ -156,7 +156,7 @@ void SettingsWidget::loadSettings()
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
m_secUi->fallbackToGoogle->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool());

View File

@@ -278,6 +278,7 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const Q
m_database = database;
m_create = create;
m_history = history;
m_saved = false;
if (history) {
setHeadline(QString("%1 > %2").arg(parentName, tr("Entry history")));
@@ -438,6 +439,7 @@ void EditEntryWidget::saveEntry()
}
updateEntryData(m_entry);
m_saved = true;
if (!m_create) {
m_entry->endUpdate();
@@ -510,7 +512,7 @@ void EditEntryWidget::cancel()
clear();
emit editFinished(false);
emit editFinished(m_saved);
}
void EditEntryWidget::clear()

View File

@@ -121,6 +121,7 @@ private:
bool m_create;
bool m_history;
bool m_saved;
const QScopedPointer<Ui::EditEntryWidgetMain> m_mainUi;
const QScopedPointer<Ui::EditEntryWidgetAdvanced> m_advancedUi;
const QScopedPointer<Ui::EditEntryWidgetAutoType> m_autoTypeUi;

View File

@@ -25,6 +25,7 @@
#include <ykdef.h>
#include <ykstatus.h>
#include "core/Tools.h"
#include "core/Global.h"
#include "crypto/Random.h"
@@ -112,23 +113,36 @@ bool YubiKey::deinit()
void YubiKey::detect()
{
if (init()) {
for (int i = 1; i < 3; i++) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
bool found = false;
if (init()) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
// Check slot 1 and 2 for Challenge-Response HMAC capability
for (int i = 1; i <= 2; ++i) {
result = challenge(i, false, rand, resp);
if (result == YubiKey::ALREADY_RUNNING) {
emit alreadyRunning();
return;
} else if (result != YubiKey::ERROR) {
emit detected(i, result == YubiKey::WOULDBLOCK);
return;
if (result == ALREADY_RUNNING) {
// Try this slot again after waiting
Tools::sleep(300);
result = challenge(i, false, rand, resp);
}
if (result != ALREADY_RUNNING && result != ERROR) {
emit detected(i, result == WOULDBLOCK);
found = true;
}
// Wait between slots to let the yubikey settle
Tools::sleep(150);
}
}
emit notFound();
if (!found) {
emit notFound();
} else {
emit detectComplete();
}
}
bool YubiKey::getSerial(unsigned int& serial)
@@ -160,6 +174,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
}
// yk_challenge_response() insists on 64 byte response buffer */
response.clear();
response.resize(64);
/* The challenge sent to the yubikey should always be 64 bytes for

View File

@@ -87,6 +87,11 @@ signals:
*/
void detected(int slot, bool blocking);
/**
* Emitted when detection is complete
*/
void detectComplete();
/**
* Emitted when the YubiKey was challenged and has returned a response.
*/

View File

@@ -1,68 +0,0 @@
// Base32 implementation
// Source: https://github.com/google/google-authenticator-libpam/blob/master/src/base32.c
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
// Modifications Copyright 2017 KeePassXC team <team@keepassxc.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base32.h"
Base32::Base32()
{
}
QByteArray Base32::base32_decode(const QByteArray encoded)
{
QByteArray result;
int buffer = 0;
int bitsLeft = 0;
for (char ch : encoded) {
if (ch == 0 || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-' || ch == '=') {
continue;
}
buffer <<= 5;
// Deal with commonly mistyped characters
if (ch == '0') {
ch = 'O';
} else if (ch == '1') {
ch = 'L';
} else if (ch == '8') {
ch = 'B';
}
// Look up one base32 digit
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
ch = (ch & 0x1F) - 1;
} else if (ch >= '2' && ch <= '7') {
ch -= '2' - 26;
} else {
return QByteArray();
}
buffer |= ch;
bitsLeft += 5;
if (bitsLeft >= 8) {
result.append(static_cast<char> (buffer >> (bitsLeft - 8)));
bitsLeft -= 8;
}
}
return result;
}

View File

@@ -1,34 +0,0 @@
// Base32 implementation
// Source: https://github.com/google/google-authenticator-libpam/blob/master/src/base32.h
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
// Modifications Copyright 2017 KeePassXC team <team@keepassxc.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BASE32_H
#define BASE32_H
#include <QtCore/qglobal.h>
#include <QByteArray>
class Base32
{
public:
Base32();
static QByteArray base32_decode(const QByteArray encoded);
};
#endif //BASE32_H

View File

@@ -17,16 +17,16 @@
*/
#include "totp.h"
#include "base32.h"
#include <cmath>
#include <QtEndian>
#include <QRegExp>
#include <QDateTime>
#include "core/Base32.h"
#include <QCryptographicHash>
#include <QDateTime>
#include <QMessageAuthenticationCode>
#include <QRegExp>
#include <QUrl>
#include <QUrlQuery>
#include <QVariant>
#include <QtEndian>
#include <cmath>
const quint8 QTotp::defaultStep = 30;
const quint8 QTotp::defaultDigits = 6;
@@ -35,7 +35,7 @@ QTotp::QTotp()
{
}
QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
QString QTotp::parseOtpString(QString key, quint8& digits, quint8& step)
{
QUrl url(key);
@@ -58,7 +58,6 @@ QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
step = q_step;
}
} else {
// Compatibility with "KeeOtp" plugin string format
QRegExp rx("key=(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
@@ -93,30 +92,59 @@ QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
return seed;
}
QString QTotp::generateTotp(const QByteArray key, quint64 time,
const quint8 numDigits = defaultDigits, const quint8 step = defaultStep)
QString QTotp::generateTotp(const QByteArray key,
quint64 time,
const quint8 numDigits = defaultDigits,
const quint8 step = defaultStep)
{
quint64 current = qToBigEndian(time / step);
QByteArray secret = Base32::base32_decode(key);
if (secret.isEmpty()) {
QVariant secret = Base32::decode(Base32::sanitizeInput(key));
if (secret.isNull()) {
return "Invalid TOTP secret key";
}
QMessageAuthenticationCode code(QCryptographicHash::Sha1);
code.setKey(secret);
code.setKey(secret.toByteArray());
code.addData(QByteArray(reinterpret_cast<char*>(&current), sizeof(current)));
QByteArray hmac = code.result();
int offset = (hmac[hmac.length() - 1] & 0xf);
// clang-format off
int binary =
((hmac[offset] & 0x7f) << 24)
| ((hmac[offset + 1] & 0xff) << 16)
| ((hmac[offset + 2] & 0xff) << 8)
| (hmac[offset + 3] & 0xff);
// clang-format on
quint32 digitsPower = pow(10, numDigits);
quint64 password = binary % digitsPower;
return QString("%1").arg(password, numDigits, 10, QChar('0'));
}
// See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
QUrl QTotp::generateOtpString(const QString& secret,
const QString& type,
const QString& issuer,
const QString& username,
const QString& algorithm,
const quint8& digits,
const quint8& step)
{
QUrl keyUri;
keyUri.setScheme("otpauth");
keyUri.setHost(type);
keyUri.setPath(QString("/%1:%2").arg(issuer).arg(username));
QUrlQuery parameters;
parameters.addQueryItem("secret", secret);
parameters.addQueryItem("issuer", issuer);
parameters.addQueryItem("algorithm", algorithm);
parameters.addQueryItem("digits", QString::number(digits));
parameters.addQueryItem("period", QString::number(step));
keyUri.setQuery(parameters);
return keyUri;
}

View File

@@ -21,12 +21,21 @@
#include <QtCore/qglobal.h>
class QUrl;
class QTotp
{
public:
QTotp();
static QString parseOtpString(QString rawSecret, quint8 &digits, quint8 &step);
static QString parseOtpString(QString rawSecret, quint8& digits, quint8& step);
static QString generateTotp(const QByteArray key, quint64 time, const quint8 numDigits, const quint8 step);
static QUrl generateOtpString(const QString& secret,
const QString& type,
const QString& issuer,
const QString& username,
const QString& algorithm,
const quint8& digits,
const quint8& step);
static const quint8 defaultStep;
static const quint8 defaultDigits;
};

View File

@@ -161,6 +161,9 @@ add_unit_test(NAME testentry SOURCES TestEntry.cpp
add_unit_test(NAME testtotp SOURCES TestTotp.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testbase32 SOURCES TestBase32.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testcsvparser SOURCES TestCsvParser.cpp
LIBS ${TEST_LIBRARIES})

330
tests/TestBase32.cpp Normal file
View File

@@ -0,0 +1,330 @@
/*
* 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 "TestBase32.h"
#include "core/Base32.h"
#include <QTest>
QTEST_GUILESS_MAIN(TestBase32)
void TestBase32::testDecode()
{
// 3 quanta, all upper case + padding
QByteArray encodedData = "JBSWY3DPEB3W64TMMQXC4LQ=";
QVariant data = Base32::decode(encodedData);
QString expectedData = "Hello world...";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, all upper case
encodedData = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, all lower case
encodedData = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, mixed upper and lower case
encodedData = "Gezdgnbvgy3tQojqgezdGnbvgy3tQojQ";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 1 pad characters
encodedData = "ORSXG5A=";
data = Base32::decode(encodedData);
expectedData = "test";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 3 pad characters
encodedData = "L5PV6===";
data = Base32::decode(encodedData);
expectedData = "___";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 pad characters
encodedData = "MZXW6IDCMFZA====";
data = Base32::decode(encodedData);
expectedData = "foo bar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// six pad characters
encodedData = "MZXW6YTBOI======";
data = Base32::decode(encodedData);
expectedData = "foobar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "IA======";
data = Base32::decode(encodedData);
expectedData = "@";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// error: illegal character
encodedData = "1MZXW6YTBOI=====";
data = Base32::decode(encodedData);
QVERIFY(data.isNull());
// error: missing pad character
encodedData = "MZXW6YTBOI=====";
data = Base32::decode(encodedData);
QVERIFY(data.isNull());
// RFC 4648 test vectors
encodedData = "";
data = Base32::decode(encodedData);
expectedData = "";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MY======";
data = Base32::decode(encodedData);
expectedData = "f";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXQ====";
data = Base32::decode(encodedData);
expectedData = "fo";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6===";
data = Base32::decode(encodedData);
QVERIFY(!data.isNull());
expectedData = "foo";
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YQ=";
data = Base32::decode(encodedData);
expectedData = "foob";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YTB";
expectedData = "fooba";
data = Base32::decode(encodedData);
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YTBOI======";
data = Base32::decode(encodedData);
expectedData = "foobar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
}
void TestBase32::testEncode()
{
QByteArray data = "Hello world...";
QByteArray encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("JBSWY3DPEB3W64TMMQXC4LQ="));
data = "12345678901234567890";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"));
data = "012345678901234567890";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("GAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGA======"));
data = "test";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("ORSXG5A="));
data = "___";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("L5PV6==="));
data = "foo bar";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6IDCMFZA===="));
data = "@";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("IA======"));
// RFC 4648 test vectors
data = "";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray(""));
data = "f";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MY======"));
data = "fo";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXQ===="));
data = "foo";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6==="));
data = "foob";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YQ="));
data = "fooba";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YTB"));
data = "foobar";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YTBOI======"));
}
void TestBase32::testAddPadding()
{
// Empty. Invalid, returns input.
QByteArray data = "";
QByteArray paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// One byte of encoded data. Invalid, returns input.
data = "B";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Two bytes of encoded data.
data = "BB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BB======"));
// Three bytes of encoded data. Invalid, returns input.
data = "BBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Four bytes of encoded data.
data = "BBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBB===="));
// Five bytes of encoded data.
data = "BBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBBB==="));
// Six bytes of encoded data. Invalid, returns input.
data = "BBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Seven bytes of encoded data.
data = "BBBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBBBBB="));
// Eight bytes of encoded data. Valid, but returns same as input.
data = "BBBBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// More than eight bytes (8+5).
data = "AAAAAAAABBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("AAAAAAAABBBBB==="));
}
void TestBase32::testRemovePadding()
{
QByteArray data = "";
QByteArray unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
data = "AAAAAAAABB======";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("AAAAAAAABB"));
data = "BBBB====";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("BBBB"));
data = "AAAAAAAABBBBB===";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("AAAAAAAABBBBB"));
data = "BBBBBBB=";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("BBBBBBB"));
// Invalid: 7 bytes of data. Returns same as input.
data = "IIIIIII";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
// Invalid: more padding than necessary. Returns same as input.
data = "AAAAAAAABBBB=====";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
}
void TestBase32::testSanitizeInput()
{
// sanitize input (white space + missing padding)
QByteArray encodedData = "JBSW Y3DP EB3W 64TM MQXC 4LQA";
auto data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (typo + missing padding)
encodedData = "J8SWY3DPE83W64TMMQXC4LQA";
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (other illegal characters)
encodedData = "J8SWY3D[PE83W64TMMQ]XC!4LQA";
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (NUL character)
encodedData = "J8SWY3DPE83W64TMMQXC4LQA";
encodedData.insert(3, '\0');
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
}

37
tests/TestBase32.h Normal file
View 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 KEEPASSX_TESTBASE32_H
#define KEEPASSX_TESTBASE32_H
#include <QObject>
class Base32;
class TestBase32 : public QObject
{
Q_OBJECT
private slots:
void testEncode();
void testDecode();
void testAddPadding();
void testRemovePadding();
void testSanitizeInput();
};
#endif // KEEPASSX_TESTBASE32_H

View File

@@ -24,17 +24,12 @@ QTEST_GUILESS_MAIN(TestCsvParser)
void TestCsvParser::initTestCase()
{
parser = new CsvParser();
}
void TestCsvParser::cleanupTestCase()
{
delete parser;
parser.reset(new CsvParser());
}
void TestCsvParser::init()
{
file = new QTemporaryFile();
file.reset(new QTemporaryFile());
if (not file->open())
QFAIL("Cannot open file!");
parser->setBackslashSyntax(false);
@@ -51,20 +46,20 @@ void TestCsvParser::cleanup()
/****************** TEST CASES ******************/
void TestCsvParser::testMissingQuote() {
parser->setTextQualifier(':');
QTextStream out(file);
QTextStream out(file.data());
out << "A,B\n:BM,1";
QEXPECT_FAIL("", "Bad format", Continue);
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QWARN(parser->getStatus().toLatin1());
}
void TestCsvParser::testMalformed() {
parser->setTextQualifier(':');
QTextStream out(file);
QTextStream out(file.data());
out << "A,B,C\n:BM::,1,:2:";
QEXPECT_FAIL("", "Bad format", Continue);
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QWARN(parser->getStatus().toLatin1());
}
@@ -72,14 +67,14 @@ void TestCsvParser::testMalformed() {
void TestCsvParser::testBackslashSyntax() {
parser->setBackslashSyntax(true);
parser->setTextQualifier(QChar('X'));
QTextStream out(file);
QTextStream out(file.data());
//attended result: one"\t\"wo
out << "Xone\\\"\\\\t\\\\\\\"w\noX\n"
<< "X13X,X2\\X,X,\"\"3\"X\r"
<< "3,X\"4\"X,,\n"
<< "XX\n"
<< "\\";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.at(0).at(0) == "one\"\\t\\\"w\no");
QVERIFY(t.at(1).at(0) == "13");
@@ -94,10 +89,10 @@ void TestCsvParser::testBackslashSyntax() {
}
void TestCsvParser::testQuoted() {
QTextStream out(file);
QTextStream out(file.data());
out << "ro,w,\"end, of \"\"\"\"\"\"row\"\"\"\"\"\n"
<< "2\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.at(0).at(0) == "ro");
QVERIFY(t.at(0).at(1) == "w");
@@ -107,41 +102,41 @@ void TestCsvParser::testQuoted() {
}
void TestCsvParser::testEmptySimple() {
QTextStream out(file);
QTextStream out(file.data());
out <<"";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 0);
}
void TestCsvParser::testEmptyQuoted() {
QTextStream out(file);
QTextStream out(file.data());
out <<"\"\"";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 0);
}
void TestCsvParser::testEmptyNewline() {
QTextStream out(file);
QTextStream out(file.data());
out <<"\"\n\"";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 0);
}
void TestCsvParser::testEmptyFile()
{
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 0);
}
void TestCsvParser::testNewline()
{
QTextStream out(file);
QTextStream out(file.data());
out << "1,2\n\n\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 1);
QVERIFY(t.at(0).at(0) == "1");
@@ -150,9 +145,9 @@ void TestCsvParser::testNewline()
void TestCsvParser::testCR()
{
QTextStream out(file);
QTextStream out(file.data());
out << "1,2\r3,4";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 2);
QVERIFY(t.at(0).at(0) == "1");
@@ -163,9 +158,9 @@ void TestCsvParser::testCR()
void TestCsvParser::testLF()
{
QTextStream out(file);
QTextStream out(file.data());
out << "1,2\n3,4";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 2);
QVERIFY(t.at(0).at(0) == "1");
@@ -176,9 +171,9 @@ void TestCsvParser::testLF()
void TestCsvParser::testCRLF()
{
QTextStream out(file);
QTextStream out(file.data());
out << "1,2\r\n3,4";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 2);
QVERIFY(t.at(0).at(0) == "1");
@@ -189,13 +184,13 @@ void TestCsvParser::testCRLF()
void TestCsvParser::testComments()
{
QTextStream out(file);
QTextStream out(file.data());
out << " #one\n"
<< " \t # two, three \r\n"
<< " #, sing\t with\r"
<< " #\t me!\n"
<< "useful,text #1!";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 1);
QVERIFY(t.at(0).at(0) == "useful");
@@ -203,21 +198,21 @@ void TestCsvParser::testComments()
}
void TestCsvParser::testColumns() {
QTextStream out(file);
QTextStream out(file.data());
out << "1,2\n"
<< ",,,,,,,,,a\n"
<< "a,b,c,d\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(parser->getCsvCols() == 10);
}
void TestCsvParser::testSimple() {
QTextStream out(file);
QTextStream out(file.data());
out << ",,2\r,2,3\n"
<< "A,,B\"\n"
<< " ,,\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 4);
QVERIFY(t.at(0).at(0) == "");
@@ -236,11 +231,11 @@ void TestCsvParser::testSimple() {
void TestCsvParser::testSeparator() {
parser->setFieldSeparator('\t');
QTextStream out(file);
QTextStream out(file.data());
out << "\t\t2\r\t2\t3\n"
<< "A\t\tB\"\n"
<< " \t\t\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 4);
QVERIFY(t.at(0).at(0) == "");
@@ -260,10 +255,10 @@ void TestCsvParser::testSeparator() {
void TestCsvParser::testMultiline()
{
parser->setTextQualifier(QChar(':'));
QTextStream out(file);
QTextStream out(file.data());
out << ":1\r\n2a::b:,:3\r4:\n"
<< "2\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.at(0).at(0) == "1\n2a:b");
QVERIFY(t.at(0).at(1) == "3\n4");
@@ -281,10 +276,10 @@ void TestCsvParser::testEmptyReparsing()
void TestCsvParser::testReparsing()
{
QTextStream out(file);
QTextStream out(file.data());
out << ":te\r\nxt1:,:te\rxt2:,:end of \"this\n string\":\n"
<< "2\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QEXPECT_FAIL("", "Wrong qualifier", Continue);
@@ -303,10 +298,10 @@ void TestCsvParser::testReparsing()
void TestCsvParser::testQualifier() {
parser->setTextQualifier(QChar('X'));
QTextStream out(file);
QTextStream out(file.data());
out << "X1X,X2XX,X,\"\"3\"\"\"X\r"
<< "3,X\"4\"X,,\n";
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 2);
QVERIFY(t.at(0).at(0) == "1");
@@ -324,10 +319,10 @@ void TestCsvParser::testUnicode() {
//CORRECT QChar g(0x20AC);
//ERROR QChar g("\u20AC");
parser->setFieldSeparator(QChar('A'));
QTextStream out(file);
QTextStream out(file.data());
out << QString("€1A2śA\"3śAż\"Ażac");
QVERIFY(parser->parse(file));
QVERIFY(parser->parse(file.data()));
t = parser->getCsvTable();
QVERIFY(t.size() == 1);
QVERIFY(t.at(0).at(0) == "€1");

View File

@@ -22,6 +22,7 @@
#include <QObject>
#include <QFile>
#include <QTemporaryFile>
#include <QScopedPointer>
#include "core/CsvParser.h"
@@ -37,7 +38,6 @@ private slots:
void init();
void cleanup();
void initTestCase();
void cleanupTestCase();
void testUnicode();
void testLF();
@@ -62,8 +62,8 @@ private slots:
void testColumns();
private:
QTemporaryFile* file;
CsvParser* parser;
QScopedPointer<QTemporaryFile> file;
QScopedPointer<CsvParser> parser;
CsvTable t;
void dumpRow(CsvTable table, int row);
};

View File

@@ -16,6 +16,7 @@
*/
#include "TestEntry.h"
#include "config-keepassx-tests.h"
#include <QTest>
@@ -130,3 +131,30 @@ void TestEntry::testClone()
delete entryOrg;
}
void TestEntry::testResolveUrl()
{
Entry* entry = new Entry();
QString testUrl("www.google.com");
QString testCmd("cmd://firefox " + testUrl);
QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
QString nonHttpUrl("ftp://google.com");
QString noUrl("random text inserted here");
// Test standard URL's
QCOMPARE(entry->resolveUrl(""), QString(""));
QCOMPARE(entry->resolveUrl(testUrl), "https://" + testUrl);
QCOMPARE(entry->resolveUrl("http://" + testUrl), "http://" + testUrl);
// Test cmd:// with no URL
QCOMPARE(entry->resolveUrl("cmd://firefox"), QString(""));
QCOMPARE(entry->resolveUrl("cmd://firefox --no-url"), QString(""));
// Test cmd:// with URL's
QCOMPARE(entry->resolveUrl(testCmd), "https://" + testUrl);
QCOMPARE(entry->resolveUrl(testComplexCmd), "http://" + testUrl);
// Test non-http URL
QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
// Test no URL
QCOMPARE(entry->resolveUrl(noUrl), QString(""));
delete entry;
}

View File

@@ -31,6 +31,7 @@ private slots:
void testHistoryItemDeletion();
void testCopyDataFrom();
void testClone();
void testResolveUrl();
};
#endif // KEEPASSX_TESTENTRY_H

View File

@@ -18,9 +18,10 @@
#include "TestGroup.h"
#include <QPointer>
#include <QSignalSpy>
#include <QDebug>
#include <QPointer>
#include <QScopedPointer>
#include <QSignalSpy>
#include <QTest>
#include "core/Database.h"
@@ -75,6 +76,7 @@ void TestGroup::testParenting()
QCOMPARE(g3->children().size(), 1);
QCOMPARE(g4->children().size(), 0);
QVERIFY(rootGroup->children().at(0) == g1);
QVERIFY(rootGroup->children().at(0) == g1);
QVERIFY(g1->children().at(0) == g2);
QVERIFY(g1->children().at(1) == g3);
@@ -99,7 +101,6 @@ void TestGroup::testParenting()
g3->setIcon(Uuid::random());
g1->setIcon(2);
QCOMPARE(spy.count(), 6);
delete db;
QVERIFY(rootGroup.isNull());
@@ -107,7 +108,6 @@ void TestGroup::testParenting()
QVERIFY(g2.isNull());
QVERIFY(g3.isNull());
QVERIFY(g4.isNull());
delete tmpRoot;
}
@@ -117,18 +117,18 @@ void TestGroup::testSignals()
Database* db2 = new Database();
QPointer<Group> root = db->rootGroup();
QSignalSpy spyAboutToAdd(db, SIGNAL(groupAboutToAdd(Group*,int)));
QSignalSpy spyAboutToAdd(db, SIGNAL(groupAboutToAdd(Group*, int)));
QSignalSpy spyAdded(db, SIGNAL(groupAdded()));
QSignalSpy spyAboutToRemove(db, SIGNAL(groupAboutToRemove(Group*)));
QSignalSpy spyRemoved(db, SIGNAL(groupRemoved()));
QSignalSpy spyAboutToMove(db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
QSignalSpy spyAboutToMove(db, SIGNAL(groupAboutToMove(Group*, Group*, int)));
QSignalSpy spyMoved(db, SIGNAL(groupMoved()));
QSignalSpy spyAboutToAdd2(db2, SIGNAL(groupAboutToAdd(Group*,int)));
QSignalSpy spyAboutToAdd2(db2, SIGNAL(groupAboutToAdd(Group*, int)));
QSignalSpy spyAdded2(db2, SIGNAL(groupAdded()));
QSignalSpy spyAboutToRemove2(db2, SIGNAL(groupAboutToRemove(Group*)));
QSignalSpy spyRemoved2(db2, SIGNAL(groupRemoved()));
QSignalSpy spyAboutToMove2(db2, SIGNAL(groupAboutToMove(Group*,Group*,int)));
QSignalSpy spyAboutToMove2(db2, SIGNAL(groupAboutToMove(Group*, Group*, int)));
QSignalSpy spyMoved2(db2, SIGNAL(groupMoved()));
Group* g1 = new Group();
@@ -251,7 +251,7 @@ void TestGroup::testEntries()
void TestGroup::testDeleteSignals()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
Group* groupRoot = db->rootGroup();
Group* groupChild = new Group();
Group* groupChildChild = new Group();
@@ -260,15 +260,13 @@ void TestGroup::testDeleteSignals()
groupChildChild->setObjectName("groupChildChild");
groupChild->setParent(groupRoot);
groupChildChild->setParent(groupChild);
QSignalSpy spyAboutToRemove(db, SIGNAL(groupAboutToRemove(Group*)));
QSignalSpy spyRemoved(db, SIGNAL(groupRemoved()));
QSignalSpy spyAboutToRemove(db.data(), SIGNAL(groupAboutToRemove(Group*)));
QSignalSpy spyRemoved(db.data(), SIGNAL(groupRemoved()));
delete groupChild;
QVERIFY(groupRoot->children().isEmpty());
QCOMPARE(spyAboutToRemove.count(), 2);
QCOMPARE(spyRemoved.count(), 2);
delete db;
Group* group = new Group();
Entry* entry = new Entry();
@@ -282,7 +280,7 @@ void TestGroup::testDeleteSignals()
QCOMPARE(spyEntryRemoved.count(), 1);
delete group;
Database* db2 = new Database();
QScopedPointer<Database> db2(new Database());
Group* groupRoot2 = db2->rootGroup();
Group* group2 = new Group();
group2->setParent(groupRoot2);
@@ -294,12 +292,11 @@ void TestGroup::testDeleteSignals()
delete group2;
QCOMPARE(spyEntryAboutToRemove2.count(), 1);
QCOMPARE(spyEntryRemoved2.count(), 1);
delete db2;
}
void TestGroup::testCopyCustomIcon()
{
Database* dbSource = new Database();
QScopedPointer<Database> dbSource(new Database());
Uuid groupIconUuid = Uuid::random();
QImage groupIcon(16, 16, QImage::Format_RGB32);
@@ -321,7 +318,7 @@ void TestGroup::testCopyCustomIcon()
entry->setIcon(entryIconUuid);
QCOMPARE(entry->icon(), entryIcon);
Database* dbTarget = new Database();
QScopedPointer<Database> dbTarget(new Database());
group->setParent(dbTarget->rootGroup());
QVERIFY(dbTarget->metadata()->containsCustomIcon(groupIconUuid));
@@ -332,37 +329,34 @@ void TestGroup::testCopyCustomIcon()
QVERIFY(dbTarget->metadata()->containsCustomIcon(entryIconUuid));
QCOMPARE(dbTarget->metadata()->customIcon(entryIconUuid), entryIcon);
QCOMPARE(entry->icon(), entryIcon);
delete dbSource;
delete dbTarget;
}
void TestGroup::testClone()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
Group* originalGroup = new Group();
QScopedPointer<Group> originalGroup(new Group());
originalGroup->setParent(db->rootGroup());
originalGroup->setName("Group");
originalGroup->setIcon(42);
Entry* originalGroupEntry = new Entry();
originalGroupEntry->setGroup(originalGroup);
QScopedPointer<Entry> originalGroupEntry(new Entry());
originalGroupEntry->setGroup(originalGroup.data());
originalGroupEntry->setTitle("GroupEntryOld");
originalGroupEntry->setIcon(43);
originalGroupEntry->beginUpdate();
originalGroupEntry->setTitle("GroupEntry");
originalGroupEntry->endUpdate();
Group* subGroup = new Group();
subGroup->setParent(originalGroup);
QScopedPointer<Group> subGroup(new Group());
subGroup->setParent(originalGroup.data());
subGroup->setName("SubGroup");
Entry* subGroupEntry = new Entry();
subGroupEntry->setGroup(subGroup);
QScopedPointer<Entry> subGroupEntry(new Entry());
subGroupEntry->setGroup(subGroup.data());
subGroupEntry->setTitle("SubGroupEntry");
Group* clonedGroup = originalGroup->clone();
QScopedPointer<Group> clonedGroup(originalGroup->clone());
QVERIFY(!clonedGroup->parentGroup());
QVERIFY(!clonedGroup->database());
QVERIFY(clonedGroup->uuid() != originalGroup->uuid());
@@ -387,19 +381,15 @@ void TestGroup::testClone()
QVERIFY(clonedSubGroupEntry->uuid() != subGroupEntry->uuid());
QCOMPARE(clonedSubGroupEntry->title(), QString("SubGroupEntry"));
Group* clonedGroupKeepUuid = originalGroup->clone(Entry::CloneNoFlags);
QScopedPointer<Group> clonedGroupKeepUuid(originalGroup->clone(Entry::CloneNoFlags));
QCOMPARE(clonedGroupKeepUuid->entries().at(0)->uuid(), originalGroupEntry->uuid());
QCOMPARE(clonedGroupKeepUuid->children().at(0)->entries().at(0)->uuid(), subGroupEntry->uuid());
delete clonedGroup;
delete clonedGroupKeepUuid;
delete db;
}
void TestGroup::testCopyCustomIcons()
{
Database* dbSource = new Database();
Database* dbTarget = new Database();
QScopedPointer<Database> dbSource(new Database());
QScopedPointer<Database> dbTarget(new Database());
QImage iconImage1(1, 1, QImage::Format_RGB32);
iconImage1.setPixel(0, 0, qRgb(1, 2, 3));
@@ -407,20 +397,20 @@ void TestGroup::testCopyCustomIcons()
QImage iconImage2(1, 1, QImage::Format_RGB32);
iconImage2.setPixel(0, 0, qRgb(4, 5, 6));
Group* group1 = new Group();
QScopedPointer<Group> group1(new Group());
group1->setParent(dbSource->rootGroup());
Uuid group1Icon = Uuid::random();
dbSource->metadata()->addCustomIcon(group1Icon, iconImage1);
group1->setIcon(group1Icon);
Group* group2 = new Group();
group2->setParent(group1);
QScopedPointer<Group> group2(new Group());
group2->setParent(group1.data());
Uuid group2Icon = Uuid::random();
dbSource->metadata()->addCustomIcon(group2Icon, iconImage1);
group2->setIcon(group2Icon);
Entry* entry1 = new Entry();
entry1->setGroup(group2);
QScopedPointer<Entry> entry1(new Entry());
entry1->setGroup(group2.data());
Uuid entry1IconOld = Uuid::random();
dbSource->metadata()->addCustomIcon(entry1IconOld, iconImage1);
entry1->setIcon(entry1IconOld);
@@ -447,27 +437,24 @@ void TestGroup::testCopyCustomIcons()
QCOMPARE(metaTarget->customIcon(group1Icon).pixel(0, 0), qRgb(1, 2, 3));
QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6));
delete dbTarget;
delete dbSource;
}
void TestGroup::testMerge()
{
Group* group1 = new Group();
QScopedPointer<Group> group1(new Group());
group1->setName("group 1");
Group* group2 = new Group();
QScopedPointer<Group> group2(new Group());
group2->setName("group 2");
Entry* entry1 = new Entry();
Entry* entry2 = new Entry();
QScopedPointer<Entry> entry1(new Entry());
QScopedPointer<Entry> entry2(new Entry());
entry1->setGroup(group1);
entry1->setGroup(group1.data());
entry1->setUuid(Uuid::random());
entry2->setGroup(group1);
entry2->setGroup(group1.data());
entry2->setUuid(Uuid::random());
group2->merge(group1);
group2->merge(group1.data());
QCOMPARE(group1->entries().size(), 2);
QCOMPARE(group2->entries().size(), 2);
@@ -475,25 +462,22 @@ void TestGroup::testMerge()
void TestGroup::testMergeDatabase()
{
Database* dbSource = createMergeTestDatabase();
Database* dbDest = new Database();
QScopedPointer<Database> dbSource(createMergeTestDatabase());
QScopedPointer<Database> dbDest(new Database());
dbDest->merge(dbSource);
dbDest->merge(dbSource.data());
QCOMPARE(dbDest->rootGroup()->children().size(), 2);
QCOMPARE(dbDest->rootGroup()->children().at(0)->entries().size(), 2);
delete dbDest;
delete dbSource;
}
void TestGroup::testMergeConflict()
{
Database* dbSource = createMergeTestDatabase();
QScopedPointer<Database> dbSource(createMergeTestDatabase());
// test merging updated entries
// falls back to KeepBoth mode
Database* dbCopy = new Database();
QScopedPointer<Database> dbCopy(new Database());
dbCopy->setRootGroup(dbSource->rootGroup()->clone(Entry::CloneNoFlags));
// sanity check
@@ -505,22 +489,19 @@ void TestGroup::testMergeConflict()
updatedTimeInfo.setLastModificationTime(updatedTimeInfo.lastModificationTime().addYears(1));
updatedEntry->setTimeInfo(updatedTimeInfo);
dbCopy->merge(dbSource);
dbCopy->merge(dbSource.data());
// one entry is duplicated because of mode
QCOMPARE(dbCopy->rootGroup()->children().at(0)->entries().size(), 2);
delete dbSource;
delete dbCopy;
}
void TestGroup::testMergeConflictKeepBoth()
{
Database* dbSource = createMergeTestDatabase();
QScopedPointer<Database> dbSource(createMergeTestDatabase());
// test merging updated entries
// falls back to KeepBoth mode
Database* dbCopy = new Database();
QScopedPointer<Database> dbCopy(new Database());
dbCopy->setRootGroup(dbSource->rootGroup()->clone(Entry::CloneNoFlags));
// sanity check
@@ -534,21 +515,18 @@ void TestGroup::testMergeConflictKeepBoth()
dbCopy->rootGroup()->setMergeMode(Group::MergeMode::KeepBoth);
dbCopy->merge(dbSource);
dbCopy->merge(dbSource.data());
// one entry is duplicated because of mode
QCOMPARE(dbCopy->rootGroup()->children().at(0)->entries().size(), 3);
// the older entry was merged from the other db as last in the group
Entry* olderEntry = dbCopy->rootGroup()->children().at(0)->entries().at(2);
QVERIFY2(olderEntry->attributes()->hasKey("merged"), "older entry is marked with an attribute \"merged\"");
delete dbSource;
delete dbCopy;
}
Database* TestGroup::createMergeTestDatabase()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
Group* group1 = new Group();
group1->setName("group 1");
@@ -566,12 +544,12 @@ Database* TestGroup::createMergeTestDatabase()
group1->setParent(db->rootGroup());
group2->setParent(db->rootGroup());
return db;
return db.take();
}
void TestGroup::testFindEntry()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
Entry* entry1 = new Entry();
entry1->setTitle(QString("entry1"));
@@ -642,13 +620,11 @@ void TestGroup::testFindEntry()
// An invalid UUID.
entry = db->rootGroup()->findEntry(QString("febfb01ebcdf9dbd90a3f1579dc"));
QVERIFY(entry == nullptr);
delete db;
}
void TestGroup::testFindGroupByPath()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
Group* group1 = new Group();
group1->setName("group1");
@@ -706,13 +682,11 @@ void TestGroup::testFindGroupByPath()
group = db->rootGroup()->findGroupByPath("invalid");
QVERIFY(group == nullptr);
delete db;
}
void TestGroup::testPrint()
{
Database* db = new Database();
QScopedPointer<Database> db(new Database());
QString output = db->rootGroup()->print();
QCOMPARE(output, QString("[empty]\n"));
@@ -731,7 +705,6 @@ void TestGroup::testPrint()
output = db->rootGroup()->print(true);
QCOMPARE(output, QString("entry1 " + entry1->uuid().toHex() + "\n"));
Group* group1 = new Group();
group1->setName("group1");
@@ -752,5 +725,4 @@ void TestGroup::testPrint()
QVERIFY(output.contains(QString("entry1 " + entry1->uuid().toHex() + "\n")));
QVERIFY(output.contains(QString("group1/ " + group1->uuid().toHex() + "\n")));
QVERIFY(output.contains(QString(" entry2 " + entry2->uuid().toHex() + "\n")));
delete db;
}

View File

@@ -148,13 +148,15 @@ void TestKeePass2Writer::testRepair()
KeePass2Repair repair;
QFile file(brokenDbFilename);
file.open(QIODevice::ReadOnly);
QCOMPARE(repair.repairDatabase(&file, key), KeePass2Repair::RepairSuccess);
Database* dbRepaired = repair.database();
auto result = repair.repairDatabase(&file, key);
QCOMPARE(result.first, KeePass2Repair::RepairSuccess);
Database* dbRepaired = result.second;
QVERIFY(dbRepaired);
QCOMPARE(dbRepaired->rootGroup()->entries().size(), 1);
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->username(), QString("testuser").append(QChar(0x20AC)));
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->password(), QString("testpw"));
delete dbRepaired;
}
void TestKeePass2Writer::cleanupTestCase()

View File

@@ -162,7 +162,7 @@ void TestSymmetricCipher::testTwofish256CbcEncryption()
bool ok;
for (int i = 0; i < keys.size(); ++i) {
cipher.init(keys[i], ivs[i]);
QVERIFY(cipher.init(keys[i], ivs[i]));
QByteArray ptNext = plainTexts[i];
QByteArray ctPrev = ivs[i];
QByteArray ctCur;

View File

@@ -18,15 +18,14 @@
#include "TestTotp.h"
#include <QTest>
#include <QTime>
#include <QDateTime>
#include <QtEndian>
#include <QTest>
#include <QTextCodec>
#include <QTime>
#include <QtEndian>
#include "crypto/Crypto.h"
#include "totp/totp.h"
#include "totp/base32.h"
QTEST_GUILESS_MAIN(TestTotp)
@@ -35,12 +34,13 @@ void TestTotp::initTestCase()
QVERIFY(Crypto::init());
}
void TestTotp::testParseSecret()
{
quint8 digits = 0;
quint8 step = 0;
QString secret = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30";
QString secret = "otpauth://totp/"
"ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm="
"SHA1&digits=6&period=30";
QCOMPARE(QTotp::parseOtpString(secret, digits, step), QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
QCOMPARE(digits, quint8(6));
QCOMPARE(step, quint8(30));
@@ -60,25 +60,6 @@ void TestTotp::testParseSecret()
QCOMPARE(step, quint8(30));
}
void TestTotp::testBase32()
{
QByteArray key = QString("JBSW Y3DP EB3W 64TM MQXC 4LQA").toLatin1();
QByteArray secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("Hello world..."));
key = QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("12345678901234567890"));
key = QString("ORSXG5A=").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("test"));
key = QString("MZXW6YTBOI======").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("foobar"));
}
void TestTotp::testTotpCode()
{
// Test vectors from RFC 6238

View File

@@ -30,7 +30,6 @@ class TestTotp : public QObject
private slots:
void initTestCase();
void testParseSecret();
void testBase32();
void testTotpCode();
};

View File

@@ -332,15 +332,27 @@ void TestGui::testAddEntry()
QTest::keyClicks(passwordRepeatEdit, "something 2");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
// Add entry "something 3"
// Add entry "something 3" using the apply button then click ok
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 3");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
// Add entry "something 4" using the apply button then click cancel
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 4");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Cancel), Qt::LeftButton);
// Add entry "something 5" but click cancel button (does NOT add entry)
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 5");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Cancel), Qt::LeftButton);
QApplication::processEvents();
// Confirm that 4 entries now exist
QTRY_COMPARE(entryView->model()->rowCount(), 4);
// Confirm that 5 entries now exist
QTRY_COMPARE(entryView->model()->rowCount(), 5);
}
void TestGui::testPasswordEntryEntropy()
@@ -513,7 +525,7 @@ void TestGui::testTotp()
void TestGui::testSearch()
{
// Add canned entries for consistent testing
testAddEntry();
Q_UNUSED(addCannedEntries());
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
@@ -629,7 +641,7 @@ void TestGui::testSearch()
void TestGui::testDeleteEntry()
{
// Add canned entries for consistent testing
testAddEntry();
Q_UNUSED(addCannedEntries());
GroupView* groupView = m_dbWidget->findChild<GroupView*>("groupView");
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
@@ -905,6 +917,42 @@ void TestGui::cleanupTestCase()
delete m_mainWindow;
}
int TestGui::addCannedEntries()
{
int entries_added = 0;
// Find buttons
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
QWidget* entryNewWidget = toolBar->widgetForAction(m_mainWindow->findChild<QAction*>("actionEntryNew"));
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
QLineEdit* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
QLineEdit* passwordEdit = editEntryWidget->findChild<QLineEdit*>("passwordEdit");
QLineEdit* passwordRepeatEdit = editEntryWidget->findChild<QLineEdit*>("passwordRepeatEdit");
// Add entry "test" and confirm added
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "test");
QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
++entries_added;
// Add entry "something 2"
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 2");
QTest::keyClicks(passwordEdit, "something 2");
QTest::keyClicks(passwordRepeatEdit, "something 2");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
++entries_added;
// Add entry "something 3"
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 3");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
++entries_added;
return entries_added;
}
void TestGui::checkDatabase(QString dbFileName)
{
if (dbFileName.isEmpty())

View File

@@ -61,6 +61,7 @@ private slots:
void testDatabaseLocking();
private:
int addCannedEntries();
void checkDatabase(QString dbFileName = "");
void triggerAction(const QString& name);
void dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex& targetIndex, int row,