Compare commits
42 Commits
2.0-alpha5
...
2.0-alpha6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43a1d54bba | ||
|
|
2963752585 | ||
|
|
1decdc6c11 | ||
|
|
6659745e2b | ||
|
|
1e2d1a1b17 | ||
|
|
f300ca5b7b | ||
|
|
b9370c6e79 | ||
|
|
54bb7462f6 | ||
|
|
4a08101a60 | ||
|
|
809be5f89e | ||
|
|
bf9a755bea | ||
|
|
34b82da9aa | ||
|
|
e0c59395da | ||
|
|
b07de47e35 | ||
|
|
586de64293 | ||
|
|
9321943e64 | ||
|
|
c00550078c | ||
|
|
2190260a68 | ||
|
|
678c4a8ece | ||
|
|
c2781274a0 | ||
|
|
bc14898b13 | ||
|
|
b6497d9245 | ||
|
|
79d827ffa4 | ||
|
|
811eb2714f | ||
|
|
a914b837a1 | ||
|
|
04c9332a99 | ||
|
|
97f374a189 | ||
|
|
6f3648d63e | ||
|
|
350cf4b00a | ||
|
|
3af2307468 | ||
|
|
29c997e1bc | ||
|
|
223c5a1651 | ||
|
|
8b437821a2 | ||
|
|
4ded95a060 | ||
|
|
0b6b149351 | ||
|
|
eee909e948 | ||
|
|
8c7e655274 | ||
|
|
8e86437e89 | ||
|
|
a8edad1e27 | ||
|
|
41162ea2e8 | ||
|
|
0f91e0d141 | ||
|
|
6a50a76466 |
14
CHANGELOG
@@ -1,3 +1,16 @@
|
||||
2.0 Alpha 6 (2014-04-06)
|
||||
=========================
|
||||
|
||||
- Add option to lock databases after user inactivity [#62]
|
||||
- Add compatibility with libgcrypt 1.6 [#129]
|
||||
- Display passwords in monospace font [#51]
|
||||
- Use an icon for the button that shows/masks passwords [#38]
|
||||
- Add an option to show passwords by default [#93]
|
||||
- Improve password generator design [#122]
|
||||
- On Linux link .kdbx files with KeePassX
|
||||
- Remember window size [#154]
|
||||
- Disallow global auto-typing when the database is locked
|
||||
|
||||
2.0 Alpha 5 (2013-12-20)
|
||||
=========================
|
||||
|
||||
@@ -42,7 +55,6 @@
|
||||
- Sortable entry view
|
||||
- Support building Mac OS X bundles
|
||||
|
||||
|
||||
2.0 Alpha 1 (2012-05-07)
|
||||
=========================
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ option(WITH_GUI_TESTS "Enable building of GUI tests" OFF)
|
||||
option(WITH_LTO "Enable Link Time Optimization (LTO)" OFF)
|
||||
option(WITH_CXX11 "Build with the C++ 11 standard" OFF)
|
||||
|
||||
set(KEEPASSX_VERSION "2.0 alpha 5")
|
||||
set(KEEPASSX_VERSION_NUM "1.9.84")
|
||||
set(KEEPASSX_VERSION "2.0 alpha 6")
|
||||
set(KEEPASSX_VERSION_NUM "1.9.85")
|
||||
|
||||
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(CMAKE_COMPILER_IS_CLANG 1)
|
||||
@@ -167,6 +167,10 @@ find_package(Qt4 4.6.0 REQUIRED ${QT_REQUIRED_MODULES})
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
find_package(Gcrypt REQUIRED)
|
||||
if(NOT (${GCRYPT_VERSION_STRING} VERSION_LESS "1.6.0"))
|
||||
message(STATUS "Gcrypt ${GCRYPT_VERSION_STRING} supports the SALSA20 cipher")
|
||||
set(GCRYPT_HAS_SALSA20 1)
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
|
||||
5
COPYING
@@ -25,6 +25,8 @@ Copyright: 2010-2012, Felix Geyer <debfx@fobos.de>
|
||||
2012, Intel Corporation
|
||||
2012, Nokia Corporation and/or its subsidiary(-ies)
|
||||
2000-2008, Tom Sato <VEF00200@nifty.ne.jp>
|
||||
2013, Laszlo Papp <lpapp@kde.org>
|
||||
2013, David Faure <faure@kde.org>
|
||||
License: GPL-2 or GPL-3
|
||||
|
||||
Files: cmake/GNUInstallDirs.cmake
|
||||
@@ -134,10 +136,13 @@ Files: share/icons/application/*/actions/application-exit.png
|
||||
share/icons/application/*/actions/document-save-as.png
|
||||
share/icons/application/*/actions/edit-clear-locationbar-ltr.png
|
||||
share/icons/application/*/actions/edit-clear-locationbar-rtl.png
|
||||
share/icons/application/*/actions/password-copy.png
|
||||
share/icons/application/*/actions/password-show-*.png
|
||||
share/icons/application/*/actions/system-search.png
|
||||
share/icons/application/*/status/dialog-error.png
|
||||
share/icons/application/*/status/dialog-information.png
|
||||
share/icons/application/*/status/dialog-warning.png
|
||||
share/icons/application/*/status/username-copy.png
|
||||
share/icons/svg/*.svgz
|
||||
Copyright: 2007, Nuno Pinheiro <nuno@oxygen-icons.org>
|
||||
2007, David Vignoni <david@icon-king.com>
|
||||
|
||||
@@ -17,7 +17,15 @@ find_path(GCRYPT_INCLUDE_DIR gcrypt.h)
|
||||
|
||||
find_library(GCRYPT_LIBRARIES gcrypt)
|
||||
|
||||
mark_as_advanced(GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIR)
|
||||
|
||||
if(GCRYPT_INCLUDE_DIR AND EXISTS "${GCRYPT_INCLUDE_DIR}/gcrypt.h")
|
||||
file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" GCRYPT_H REGEX "^#define GCRYPT_VERSION \"[^\"]*\"$")
|
||||
string(REGEX REPLACE "^.*GCRYPT_VERSION \"([0-9]+).*$" "\\1" GCRYPT_VERSION_MAJOR "${GCRYPT_H}")
|
||||
string(REGEX REPLACE "^.*GCRYPT_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" GCRYPT_VERSION_MINOR "${GCRYPT_H}")
|
||||
string(REGEX REPLACE "^.*GCRYPT_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" GCRYPT_VERSION_PATCH "${GCRYPT_H}")
|
||||
set(GCRYPT_VERSION_STRING "${GCRYPT_VERSION_MAJOR}.${GCRYPT_VERSION_MINOR}.${GCRYPT_VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Gcrypt DEFAULT_MSG GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIR)
|
||||
|
||||
@@ -20,7 +20,10 @@ install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database)
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(DIRECTORY icons/application/ DESTINATION share/icons/hicolor
|
||||
FILES_MATCHING PATTERN "keepassx.png" PATTERN "keepassx.svgz")
|
||||
install(DIRECTORY icons/application/ DESTINATION share/icons/hicolor
|
||||
FILES_MATCHING PATTERN "application-x-keepassx.png" PATTERN "application-x-keepassx.svgz")
|
||||
install(FILES linux/keepassx.desktop DESTINATION share/applications)
|
||||
install(FILES linux/keepassx.xml DESTINATION share/mime/packages)
|
||||
endif(UNIX AND NOT APPLE)
|
||||
|
||||
if(APPLE)
|
||||
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
BIN
share/icons/application/16x16/actions/password-copy.png
Normal file
|
After Width: | Height: | Size: 670 B |
BIN
share/icons/application/16x16/actions/password-show-off.png
Normal file
|
After Width: | Height: | Size: 783 B |
BIN
share/icons/application/16x16/actions/password-show-on.png
Normal file
|
After Width: | Height: | Size: 838 B |
BIN
share/icons/application/16x16/actions/username-copy.png
Normal file
|
After Width: | Height: | Size: 766 B |
|
After Width: | Height: | Size: 711 B |
BIN
share/icons/application/22x22/actions/document-open.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
share/icons/application/22x22/actions/document-save.png
Normal file
|
After Width: | Height: | Size: 798 B |
BIN
share/icons/application/22x22/actions/password-copy.png
Normal file
|
After Width: | Height: | Size: 966 B |
BIN
share/icons/application/22x22/actions/username-copy.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
BIN
share/icons/svg/application-x-keepassx-16.svgz
Normal file
BIN
share/icons/svg/application-x-keepassx.svgz
Normal file
BIN
share/icons/svg/password-copy.svgz
Normal file
BIN
share/icons/svg/username-copy.svgz
Normal file
8
share/linux/keepassx.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
<glob pattern="*.kdbx"/>
|
||||
<icon name="application-x-keepassx"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -27,7 +27,6 @@ set(keepassx_SOURCES
|
||||
autotype/WildcardMatcher.cpp
|
||||
autotype/WindowSelectComboBox.cpp
|
||||
autotype/test/AutoTypeTestInterface.h
|
||||
core/ArgumentParser.cpp
|
||||
core/AutoTypeAssociations.cpp
|
||||
core/Config.cpp
|
||||
core/Database.cpp
|
||||
@@ -39,6 +38,7 @@ set(keepassx_SOURCES
|
||||
core/FilePath.cpp
|
||||
core/Global.h
|
||||
core/Group.cpp
|
||||
core/InactivityTimer.cpp
|
||||
core/ListDeleter.h
|
||||
core/Metadata.cpp
|
||||
core/PasswordGenerator.cpp
|
||||
@@ -49,18 +49,14 @@ set(keepassx_SOURCES
|
||||
core/TimeInfo.cpp
|
||||
core/Tools.cpp
|
||||
core/Uuid.cpp
|
||||
core/qcommandlineoption.cpp
|
||||
core/qcommandlineparser.cpp
|
||||
crypto/Crypto.cpp
|
||||
crypto/CryptoHash.cpp
|
||||
crypto/Random.cpp
|
||||
crypto/salsa20/ecrypt-config.h
|
||||
crypto/salsa20/ecrypt-machine.h
|
||||
crypto/salsa20/ecrypt-portable.h
|
||||
crypto/salsa20/ecrypt-sync.h
|
||||
crypto/salsa20/salsa20.c
|
||||
crypto/SymmetricCipher.cpp
|
||||
crypto/SymmetricCipherBackend.h
|
||||
crypto/SymmetricCipherGcrypt.cpp
|
||||
crypto/SymmetricCipherSalsa20.cpp
|
||||
format/KeePass1.h
|
||||
format/KeePass1Reader.cpp
|
||||
format/KeePass2.h
|
||||
@@ -88,7 +84,9 @@ set(keepassx_SOURCES
|
||||
gui/LineEdit.cpp
|
||||
gui/MainWindow.cpp
|
||||
gui/MessageBox.cpp
|
||||
gui/PasswordEdit.cpp
|
||||
gui/PasswordGeneratorWidget.cpp
|
||||
gui/PasswordComboBox.cpp
|
||||
gui/SettingsWidget.cpp
|
||||
gui/SortFilterHideProxyModel.cpp
|
||||
gui/UnlockDatabaseWidget.cpp
|
||||
@@ -116,6 +114,18 @@ set(keepassx_SOURCES
|
||||
streams/SymmetricCipherStream.cpp
|
||||
)
|
||||
|
||||
if(NOT GCRYPT_HAS_SALSA20)
|
||||
set(keepassx_SOURCES
|
||||
${keepassx_SOURCES}
|
||||
crypto/salsa20/ecrypt-config.h
|
||||
crypto/salsa20/ecrypt-machine.h
|
||||
crypto/salsa20/ecrypt-portable.h
|
||||
crypto/salsa20/ecrypt-sync.h
|
||||
crypto/salsa20/salsa20.c
|
||||
crypto/SymmetricCipherSalsa20.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(keepassx_SOURCES_MAINEXE
|
||||
main.cpp
|
||||
)
|
||||
@@ -133,6 +143,7 @@ set(keepassx_MOC
|
||||
core/EntryAttachments.h
|
||||
core/EntryAttributes.h
|
||||
core/Group.h
|
||||
core/InactivityTimer.h
|
||||
core/Metadata.h
|
||||
core/qsavefile.h
|
||||
gui/AboutDialog.h
|
||||
@@ -152,7 +163,9 @@ set(keepassx_MOC
|
||||
gui/KeePass1OpenWidget.h
|
||||
gui/LineEdit.h
|
||||
gui/MainWindow.h
|
||||
gui/PasswordEdit.h
|
||||
gui/PasswordGeneratorWidget.h
|
||||
gui/PasswordComboBox.h
|
||||
gui/SettingsWidget.h
|
||||
gui/SortFilterHideProxyModel.h
|
||||
gui/UnlockDatabaseWidget.h
|
||||
|
||||
@@ -40,6 +40,7 @@ AutoType::AutoType(QObject* parent, bool test)
|
||||
, m_pluginLoader(new QPluginLoader(this))
|
||||
, m_plugin(Q_NULLPTR)
|
||||
, m_executor(Q_NULLPTR)
|
||||
, m_windowFromGlobal(0)
|
||||
{
|
||||
// prevent crash when the plugin has unresolved symbols
|
||||
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
|
||||
|
||||
@@ -46,7 +46,8 @@ AutoTypePlatformX11::AutoTypePlatformX11()
|
||||
|
||||
m_keysymTable = Q_NULLPTR;
|
||||
m_xkb = Q_NULLPTR;
|
||||
m_specialCharacterKeycode = 0;
|
||||
m_remapKeycode = 0;
|
||||
m_currentRemapKeysym = NoSymbol;
|
||||
m_modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
|
||||
|
||||
m_loaded = true;
|
||||
@@ -440,11 +441,13 @@ void AutoTypePlatformX11::updateKeymap()
|
||||
&m_keysymPerKeycode);
|
||||
|
||||
/* determine the keycode to use for remapped keys */
|
||||
if (m_specialCharacterKeycode == 0) {
|
||||
inx = (m_remapKeycode - m_minKeycode) * m_keysymPerKeycode;
|
||||
if (m_remapKeycode == 0 || !isRemapKeycodeValid()) {
|
||||
for (keycode = m_minKeycode; keycode <= m_maxKeycode; keycode++) {
|
||||
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
|
||||
if (m_keysymTable[inx] == NoSymbol) {
|
||||
m_specialCharacterKeycode = keycode;
|
||||
m_remapKeycode = keycode;
|
||||
m_currentRemapKeysym = NoSymbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -465,6 +468,18 @@ void AutoTypePlatformX11::updateKeymap()
|
||||
XFreeModifiermap(modifiers);
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::isRemapKeycodeValid()
|
||||
{
|
||||
int baseKeycode = (m_remapKeycode - m_minKeycode) * m_keysymPerKeycode;
|
||||
for (int i = 0; i < m_keysymPerKeycode; i++) {
|
||||
if (m_keysymTable[baseKeycode + i] == m_currentRemapKeysym) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AutoTypePlatformX11::startCatchXErrors()
|
||||
{
|
||||
Q_ASSERT(!m_catchXErrors);
|
||||
@@ -505,14 +520,17 @@ int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error)
|
||||
*/
|
||||
int AutoTypePlatformX11::AddKeysym(KeySym keysym)
|
||||
{
|
||||
if (m_specialCharacterKeycode == 0) {
|
||||
if (m_remapKeycode == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inx = (m_specialCharacterKeycode - m_minKeycode) * m_keysymPerKeycode;
|
||||
int inx = (m_remapKeycode- m_minKeycode) * m_keysymPerKeycode;
|
||||
m_keysymTable[inx] = keysym;
|
||||
XChangeKeyboardMapping(m_dpy, m_specialCharacterKeycode, m_keysymPerKeycode, &m_keysymTable[inx], 1);
|
||||
m_currentRemapKeysym = keysym;
|
||||
|
||||
XChangeKeyboardMapping(m_dpy, m_remapKeycode, m_keysymPerKeycode, &m_keysymTable[inx], 1);
|
||||
XFlush(m_dpy);
|
||||
updateKeymap();
|
||||
|
||||
/* Xlib needs some time until the mapping is distributed to
|
||||
all clients */
|
||||
@@ -521,7 +539,7 @@ int AutoTypePlatformX11::AddKeysym(KeySym keysym)
|
||||
ts.tv_nsec = 10 * 1000 * 1000;
|
||||
nanosleep(&ts, Q_NULLPTR);
|
||||
|
||||
return m_specialCharacterKeycode;
|
||||
return m_remapKeycode;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -565,29 +583,42 @@ void AutoTypePlatformX11::SendModifier(XKeyEvent *event, unsigned int mask, int
|
||||
* keysym.
|
||||
*/
|
||||
int AutoTypePlatformX11::GetKeycode(KeySym keysym, unsigned int *mask)
|
||||
{
|
||||
int keycode = XKeysymToKeycode(m_dpy, keysym);
|
||||
|
||||
if (keycode && keysymModifiers(keysym, keycode, mask)) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
/* no modifier matches => resort to remapping */
|
||||
keycode = AddKeysym(keysym);
|
||||
if (keycode && keysymModifiers(keysym, keycode, mask)) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
*mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::keysymModifiers(KeySym keysym, int keycode, unsigned int *mask)
|
||||
{
|
||||
int shift, mod;
|
||||
unsigned int mods_rtrn;
|
||||
KeySym keysym_rtrn;
|
||||
int keycode;
|
||||
|
||||
keycode = XKeysymToKeycode(m_dpy, keysym);
|
||||
|
||||
/* determine whether there is a combination of the modifiers
|
||||
(Mod1-Mod5) with or without shift which returns keysym */
|
||||
for (shift = 0; shift < 2; shift ++) {
|
||||
for (mod = ControlMapIndex; mod <= Mod5MapIndex; mod ++) {
|
||||
KeySym keysym_rtrn;
|
||||
*mask = (mod == ControlMapIndex) ? shift : shift | (1 << mod);
|
||||
XkbTranslateKeyCode(m_xkb, keycode, *mask, &mods_rtrn, &keysym_rtrn);
|
||||
if (keysym_rtrn == keysym) {
|
||||
return keycode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no modifier matches => resort to remapping */
|
||||
*mask = 0;
|
||||
return AddKeysym(keysym);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -609,6 +640,8 @@ void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym)
|
||||
return;
|
||||
}
|
||||
|
||||
XGetInputFocus(m_dpy, &cur_focus, &revert_to);
|
||||
|
||||
event.display = m_dpy;
|
||||
event.window = cur_focus;
|
||||
event.root = m_rootWindow;
|
||||
@@ -627,7 +660,6 @@ void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym)
|
||||
|
||||
XQueryPointer(m_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
|
||||
saved_mask = mask;
|
||||
XGetInputFocus(m_dpy, &cur_focus, &revert_to);
|
||||
|
||||
/* determine keycode and mask for the given keysym */
|
||||
keycode = GetKeycode(keysym, &mask);
|
||||
|
||||
@@ -72,11 +72,13 @@ private:
|
||||
static int x11ErrorHandler(Display* display, XErrorEvent* error);
|
||||
|
||||
void updateKeymap();
|
||||
bool isRemapKeycodeValid();
|
||||
int AddKeysym(KeySym keysym);
|
||||
void AddModifier(KeySym keysym);
|
||||
void SendEvent(XKeyEvent* event, int event_type);
|
||||
void SendModifier(XKeyEvent *event, unsigned int mask, int event_type);
|
||||
int GetKeycode(KeySym keysym, unsigned int *mask);
|
||||
bool keysymModifiers(KeySym keysym, int keycode, unsigned int *mask);
|
||||
|
||||
static int MyErrorHandler(Display* my_dpy, XErrorEvent* event);
|
||||
|
||||
@@ -106,8 +108,9 @@ private:
|
||||
int m_minKeycode;
|
||||
int m_maxKeycode;
|
||||
int m_keysymPerKeycode;
|
||||
/* dedicated 'special character' keycode */
|
||||
int m_specialCharacterKeycode;
|
||||
/* dedicated keycode for remapped keys */
|
||||
unsigned int m_remapKeycode;
|
||||
KeySym m_currentRemapKeysym;
|
||||
KeyCode m_modifier_keycode[N_MOD_INDICES];
|
||||
bool m_loaded;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* config-keepassx.h. Generated by cmake from config-keepassx.h.cmake */
|
||||
|
||||
#ifndef KEEPASSX_CONFIG_H
|
||||
#define KEEPASSX_CONFIG_H
|
||||
#ifndef KEEPASSX_CONFIG_KEEPASSX_H
|
||||
#define KEEPASSX_CONFIG_KEEPASSX_H
|
||||
|
||||
#define KEEPASSX_VERSION "${KEEPASSX_VERSION}"
|
||||
|
||||
@@ -13,4 +13,6 @@
|
||||
#cmakedefine HAVE_RLIMIT_CORE 1
|
||||
#cmakedefine HAVE_PT_DENY_ATTACH 1
|
||||
|
||||
#endif // KEEPASSX_CONFIG_H
|
||||
#cmakedefine GCRYPT_HAS_SALSA20
|
||||
|
||||
#endif // KEEPASSX_CONFIG_KEEPASSX_H
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 "ArgumentParser.h"
|
||||
|
||||
const QStringList ArgumentParser::ArgumentKeys = QStringList() << "password" << "config" << "filename";
|
||||
|
||||
QHash<QString, QString> ArgumentParser::parseArguments(const QStringList& args)
|
||||
{
|
||||
QHash<QString, QString> argumentMap;
|
||||
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
if (args[i].startsWith("--")) {
|
||||
parseOption(i, argumentMap, args);
|
||||
}
|
||||
else if (!args[i].startsWith("-")) {
|
||||
argumentMap.insert("filename", args[i]);
|
||||
}
|
||||
else {
|
||||
qWarning("Unknown argument \"%s\"", qPrintable(args[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return argumentMap;
|
||||
}
|
||||
|
||||
void ArgumentParser::parseOption(int& i, QHash<QString, QString>& argumentMap, const QStringList& args)
|
||||
{
|
||||
if (args.size() < (i + 2)) {
|
||||
qWarning("No value given for option \"%s\"", qPrintable(args[i]));
|
||||
return;
|
||||
}
|
||||
|
||||
QString argument(args[i].mid(2));
|
||||
if (ArgumentKeys.contains(argument)) {
|
||||
argumentMap.insert(argument, args[i + 1]);
|
||||
}
|
||||
else {
|
||||
qWarning("Unknown option \"%s\" with value \"%s\"", qPrintable(args[i]), qPrintable(args[i+1]));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ Config::Config(QObject* parent)
|
||||
userPath += "/keepassx/";
|
||||
#else
|
||||
userPath = QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
|
||||
userPath += "/keepassx/";
|
||||
// storageLocation() appends the application name ("/keepassx/") to the end
|
||||
#endif
|
||||
|
||||
userPath += "keepassx2.ini";
|
||||
@@ -96,6 +96,9 @@ void Config::init(const QString& fileName)
|
||||
m_defaults.insert("MinimizeOnCopy", false);
|
||||
m_defaults.insert("security/clearclipboard", true);
|
||||
m_defaults.insert("security/clearclipboardtimeout", 10);
|
||||
m_defaults.insert("security/lockdatabaseidle", false);
|
||||
m_defaults.insert("security/lockdatabaseidlesec", 10);
|
||||
m_defaults.insert("security/passwordscleartext", false);
|
||||
}
|
||||
|
||||
Config* Config::instance()
|
||||
|
||||
@@ -30,6 +30,7 @@ Entry::Entry()
|
||||
, m_attachments(new EntryAttachments(this))
|
||||
, m_autoTypeAssociations(new AutoTypeAssociations(this))
|
||||
, m_tmpHistoryItem(Q_NULLPTR)
|
||||
, m_modifiedSinceBegin(false)
|
||||
, m_updateTimeinfo(true)
|
||||
{
|
||||
m_data.iconNumber = DefaultIconNumber;
|
||||
|
||||
@@ -117,6 +117,51 @@ QIcon FilePath::icon(const QString& category, const QString& name, bool fromThem
|
||||
return icon;
|
||||
}
|
||||
|
||||
QIcon FilePath::onOffIcon(const QString& category, const QString& name)
|
||||
{
|
||||
QString combinedName = category + "/" + name;
|
||||
QString cacheName = "onoff/" + combinedName;
|
||||
|
||||
QIcon icon = m_iconCache.value(cacheName);
|
||||
|
||||
if (!icon.isNull()) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
QIcon::State state;
|
||||
QString stateName;
|
||||
|
||||
if (i == 0) {
|
||||
state = QIcon::Off;
|
||||
stateName = "off";
|
||||
}
|
||||
else {
|
||||
state = QIcon::On;
|
||||
stateName = "on";
|
||||
}
|
||||
|
||||
QList<int> pngSizes;
|
||||
pngSizes << 16 << 22 << 24 << 32 << 48 << 64 << 128;
|
||||
QString filename;
|
||||
Q_FOREACH (int size, pngSizes) {
|
||||
filename = QString("%1/icons/application/%2x%2/%3-%4.png").arg(m_dataPath, QString::number(size),
|
||||
combinedName, stateName);
|
||||
if (QFile::exists(filename)) {
|
||||
icon.addFile(filename, QSize(size, size), QIcon::Normal, state);
|
||||
}
|
||||
}
|
||||
filename = QString("%1/icons/application/scalable/%3-%4.svgz").arg(m_dataPath, combinedName, stateName);
|
||||
if (QFile::exists(filename)) {
|
||||
icon.addFile(filename, QSize(), QIcon::Normal, state);
|
||||
}
|
||||
}
|
||||
|
||||
m_iconCache.insert(cacheName, icon);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
FilePath::FilePath()
|
||||
{
|
||||
if (false) {
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
QString pluginPath(const QString& name);
|
||||
QIcon applicationIcon();
|
||||
QIcon icon(const QString& category, const QString& name, bool fromTheme = true);
|
||||
QIcon onOffIcon(const QString& category, const QString& name);
|
||||
|
||||
static FilePath* instance();
|
||||
|
||||
|
||||
73
src/core/InactivityTimer.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 "InactivityTimer.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
|
||||
InactivityTimer::InactivityTimer(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_timer(new QTimer(this))
|
||||
, m_active(false)
|
||||
{
|
||||
m_timer->setSingleShot(true);
|
||||
connect(m_timer, SIGNAL(timeout()), SLOT(timeout()));
|
||||
}
|
||||
|
||||
void InactivityTimer::setInactivityTimeout(int inactivityTimeout)
|
||||
{
|
||||
Q_ASSERT(inactivityTimeout > 0);
|
||||
|
||||
m_timer->setInterval(inactivityTimeout);
|
||||
}
|
||||
|
||||
void InactivityTimer::activate()
|
||||
{
|
||||
if (!m_active) {
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
m_active = true;
|
||||
m_timer->start();
|
||||
}
|
||||
|
||||
void InactivityTimer::deactivate()
|
||||
{
|
||||
qApp->removeEventFilter(this);
|
||||
m_active = false;
|
||||
m_timer->stop();
|
||||
}
|
||||
|
||||
bool InactivityTimer::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
const QEvent::Type type = event->type();
|
||||
|
||||
if ( (type >= QEvent::MouseButtonPress && type <= QEvent::KeyRelease)
|
||||
|| (type >= QEvent::HoverEnter && type <= QEvent::HoverMove)
|
||||
|| (type == QEvent::Wheel) ) {
|
||||
m_timer->start();
|
||||
}
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void InactivityTimer::timeout()
|
||||
{
|
||||
if (m_active && !m_timer->isActive()) {
|
||||
Q_EMIT inactivityDetected();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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
|
||||
@@ -15,28 +15,37 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTARGUMENTPARSER_H
|
||||
#define KEEPASSX_TESTARGUMENTPARSER_H
|
||||
#ifndef KEEPASSX_INACTIVITYTIMER_H
|
||||
#define KEEPASSX_INACTIVITYTIMER_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
class TestArgumentParser : public QObject
|
||||
#include "core/Global.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
class InactivityTimer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InactivityTimer(QObject* parent = Q_NULLPTR);
|
||||
void setInactivityTimeout(int inactivityTimeout);
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
Q_SIGNALS:
|
||||
void inactivityDetected();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void testNoArguments();
|
||||
void testMissingOptionValue();
|
||||
void testUnknownArgument();
|
||||
void testFilename();
|
||||
void testMultipleArguments();
|
||||
void testFilenameWithoutOption();
|
||||
void timeout();
|
||||
|
||||
private:
|
||||
void parse(const QStringList& arguments);
|
||||
|
||||
QHash<QString, QString> argumentMap;
|
||||
QTimer* m_timer;
|
||||
bool m_active;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTARGUMENTPARSER_H
|
||||
#endif // KEEPASSX_INACTIVITYTIMER_H
|
||||
@@ -19,15 +19,33 @@
|
||||
|
||||
#include "crypto/Random.h"
|
||||
|
||||
PasswordGenerator* PasswordGenerator::m_instance = Q_NULLPTR;
|
||||
|
||||
QString PasswordGenerator::generatePassword(int length,
|
||||
const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags)
|
||||
PasswordGenerator::PasswordGenerator()
|
||||
: m_length(0)
|
||||
, m_classes(0)
|
||||
, m_flags(0)
|
||||
{
|
||||
Q_ASSERT(isValidCombination(length, classes, flags));
|
||||
}
|
||||
|
||||
QVector<PasswordGroup> groups = passwordGroups(classes, flags);
|
||||
void PasswordGenerator::setLength(int length)
|
||||
{
|
||||
m_length = length;
|
||||
}
|
||||
|
||||
void PasswordGenerator::setCharClasses(const CharClasses& classes)
|
||||
{
|
||||
m_classes = classes;
|
||||
}
|
||||
|
||||
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
|
||||
{
|
||||
m_flags = flags;
|
||||
}
|
||||
|
||||
QString PasswordGenerator::generatePassword() const
|
||||
{
|
||||
Q_ASSERT(isValid());
|
||||
|
||||
QVector<PasswordGroup> groups = passwordGroups();
|
||||
|
||||
QVector<QChar> passwordChars;
|
||||
Q_FOREACH (const PasswordGroup& group, groups) {
|
||||
@@ -38,14 +56,14 @@ QString PasswordGenerator::generatePassword(int length,
|
||||
|
||||
QString password;
|
||||
|
||||
if (flags & CharFromEveryGroup) {
|
||||
if (m_flags & CharFromEveryGroup) {
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
int pos = randomGen()->randomUInt(groups[i].size());
|
||||
|
||||
password.append(groups[i][pos]);
|
||||
}
|
||||
|
||||
for (int i = groups.size(); i < length; i++) {
|
||||
for (int i = groups.size(); i < m_length; i++) {
|
||||
int pos = randomGen()->randomUInt(passwordChars.size());
|
||||
|
||||
password.append(passwordChars[pos]);
|
||||
@@ -61,7 +79,7 @@ QString PasswordGenerator::generatePassword(int length,
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int i = 0; i < m_length; i++) {
|
||||
int pos = randomGen()->randomUInt(passwordChars.size());
|
||||
|
||||
password.append(passwordChars[pos]);
|
||||
@@ -71,34 +89,31 @@ QString PasswordGenerator::generatePassword(int length,
|
||||
return password;
|
||||
}
|
||||
|
||||
bool PasswordGenerator::isValidCombination(int length,
|
||||
const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags)
|
||||
bool PasswordGenerator::isValid() const
|
||||
{
|
||||
if (classes == 0) {
|
||||
if (m_classes == 0) {
|
||||
return false;
|
||||
}
|
||||
else if (length == 0) {
|
||||
else if (m_length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & CharFromEveryGroup) && (length < numCharClasses(classes))) {
|
||||
if ((m_flags & CharFromEveryGroup) && (m_length < numCharClasses())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags)
|
||||
QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
|
||||
{
|
||||
QVector<PasswordGroup> passwordGroups;
|
||||
|
||||
if (classes & LowerLetters) {
|
||||
if (m_classes & LowerLetters) {
|
||||
PasswordGroup group;
|
||||
|
||||
for (int i = 97; i < (97 + 26); i++) {
|
||||
if ((flags & ExcludeLookAlike) && (i == 108)) { // "l"
|
||||
if ((m_flags & ExcludeLookAlike) && (i == 108)) { // "l"
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -107,11 +122,11 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
|
||||
|
||||
passwordGroups.append(group);
|
||||
}
|
||||
if (classes & UpperLetters) {
|
||||
if (m_classes & UpperLetters) {
|
||||
PasswordGroup group;
|
||||
|
||||
for (int i = 65; i < (65 + 26); i++) {
|
||||
if ((flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O"
|
||||
if ((m_flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O"
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -120,11 +135,11 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
|
||||
|
||||
passwordGroups.append(group);
|
||||
}
|
||||
if (classes & Numbers) {
|
||||
if (m_classes & Numbers) {
|
||||
PasswordGroup group;
|
||||
|
||||
for (int i = 48; i < (48 + 10); i++) {
|
||||
if ((flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1"
|
||||
if ((m_flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1"
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -133,7 +148,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
|
||||
|
||||
passwordGroups.append(group);
|
||||
}
|
||||
if (classes & SpecialCharacters) {
|
||||
if (m_classes & SpecialCharacters) {
|
||||
PasswordGroup group;
|
||||
|
||||
for (int i = 33; i <= 47; i++) {
|
||||
@@ -149,7 +164,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
|
||||
}
|
||||
|
||||
for (int i = 123; i <= 126; i++) {
|
||||
if ((flags & ExcludeLookAlike) && (i == 124)) { // "|"
|
||||
if ((m_flags & ExcludeLookAlike) && (i == 124)) { // "|"
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -162,35 +177,22 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups(const PasswordGenerator
|
||||
return passwordGroups;
|
||||
}
|
||||
|
||||
int PasswordGenerator::numCharClasses(const PasswordGenerator::CharClasses& classes)
|
||||
int PasswordGenerator::numCharClasses() const
|
||||
{
|
||||
int numClasses = 0;
|
||||
|
||||
if (classes & LowerLetters) {
|
||||
if (m_classes & LowerLetters) {
|
||||
numClasses++;
|
||||
}
|
||||
if (classes & UpperLetters) {
|
||||
if (m_classes & UpperLetters) {
|
||||
numClasses++;
|
||||
}
|
||||
if (classes & Numbers) {
|
||||
if (m_classes & Numbers) {
|
||||
numClasses++;
|
||||
}
|
||||
if (classes & SpecialCharacters) {
|
||||
if (m_classes & SpecialCharacters) {
|
||||
numClasses++;
|
||||
}
|
||||
|
||||
return numClasses;
|
||||
}
|
||||
|
||||
PasswordGenerator* PasswordGenerator::instance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new PasswordGenerator();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
PasswordGenerator::PasswordGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -45,29 +45,28 @@ public:
|
||||
};
|
||||
Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag)
|
||||
|
||||
QString generatePassword(int length, const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags);
|
||||
bool isValidCombination(int length, const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags);
|
||||
|
||||
static PasswordGenerator* instance();
|
||||
|
||||
private:
|
||||
public:
|
||||
PasswordGenerator();
|
||||
|
||||
QVector<PasswordGroup> passwordGroups(const PasswordGenerator::CharClasses& classes,
|
||||
const PasswordGenerator::GeneratorFlags& flags);
|
||||
int numCharClasses(const PasswordGenerator::CharClasses& classes);
|
||||
void setLength(int length);
|
||||
void setCharClasses(const CharClasses& classes);
|
||||
void setFlags(const GeneratorFlags& flags);
|
||||
|
||||
static PasswordGenerator* m_instance;
|
||||
bool isValid() const;
|
||||
|
||||
QString generatePassword() const;
|
||||
|
||||
private:
|
||||
QVector<PasswordGroup> passwordGroups() const;
|
||||
int numCharClasses() const;
|
||||
|
||||
int m_length;
|
||||
CharClasses m_classes;
|
||||
GeneratorFlags m_flags;
|
||||
|
||||
Q_DISABLE_COPY(PasswordGenerator)
|
||||
};
|
||||
|
||||
inline PasswordGenerator* passwordGenerator() {
|
||||
return PasswordGenerator::instance();
|
||||
}
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses)
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::GeneratorFlags)
|
||||
|
||||
305
src/core/qcommandlineoption.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
|
||||
** Copyright (C) 2013 David Faure <faure@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcommandlineoption.h"
|
||||
|
||||
#include <QSet>
|
||||
|
||||
class QCommandLineOptionPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
inline QCommandLineOptionPrivate()
|
||||
{ }
|
||||
|
||||
void setNames(const QStringList &nameList);
|
||||
|
||||
//! The list of names used for this option.
|
||||
QStringList names;
|
||||
|
||||
//! The documentation name for the value, if one is expected
|
||||
//! Example: "-o <file>" means valueName == "file"
|
||||
QString valueName;
|
||||
|
||||
//! The description used for this option.
|
||||
QString description;
|
||||
|
||||
//! The list of default values used for this option.
|
||||
QStringList defaultValues;
|
||||
};
|
||||
|
||||
/*!
|
||||
\since 5.2
|
||||
\class QCommandLineOption
|
||||
\brief The QCommandLineOption class defines a possible command-line option.
|
||||
\inmodule QtCore
|
||||
\ingroup shared
|
||||
\ingroup tools
|
||||
|
||||
This class is used to describe an option on the command line. It allows
|
||||
different ways of defining the same option with multiple aliases possible.
|
||||
It is also used to describe how the option is used - it may be a flag (e.g. \c{-v})
|
||||
or take an argument (e.g. \c{-o file}).
|
||||
|
||||
Examples:
|
||||
\snippet code/src_corelib_tools_qcommandlineoption.cpp 0
|
||||
|
||||
\sa QCommandLineParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a command line option object with the given arguments.
|
||||
|
||||
The name of the option is set to \a name.
|
||||
The name can be either short or long. If the name is one character in
|
||||
length, it is considered a short name. Option names must not be empty,
|
||||
must not start with a dash or a slash character, must not contain a \c{=}
|
||||
and cannot be repeated.
|
||||
|
||||
The description is set to \a description. It is customary to add a "."
|
||||
at the end of the description.
|
||||
|
||||
In addition, the \a valueName can be set if the option expects a value.
|
||||
The default value for the option is set to \a defaultValue.
|
||||
|
||||
\sa setDescription(), setValueName(), setDefaultValues()
|
||||
*/
|
||||
QCommandLineOption::QCommandLineOption(const QString &name, const QString &description,
|
||||
const QString &valueName,
|
||||
const QString &defaultValue)
|
||||
: d(new QCommandLineOptionPrivate)
|
||||
{
|
||||
d->setNames(QStringList(name));
|
||||
setValueName(valueName);
|
||||
setDescription(description);
|
||||
setDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a command line option object with the given arguments.
|
||||
|
||||
This overload allows to set multiple names for the option, for instance
|
||||
\c{o} and \c{output}.
|
||||
|
||||
The names of the option are set to \a names.
|
||||
The names can be either short or long. Any name in the list that is one
|
||||
character in length is a short name. Option names must not be empty,
|
||||
must not start with a dash or a slash character, must not contain a \c{=}
|
||||
and cannot be repeated.
|
||||
|
||||
The description is set to \a description. It is customary to add a "."
|
||||
at the end of the description.
|
||||
|
||||
In addition, the \a valueName can be set if the option expects a value.
|
||||
The default value for the option is set to \a defaultValue.
|
||||
|
||||
\sa setDescription(), setValueName(), setDefaultValues()
|
||||
*/
|
||||
QCommandLineOption::QCommandLineOption(const QStringList &names, const QString &description,
|
||||
const QString &valueName,
|
||||
const QString &defaultValue)
|
||||
: d(new QCommandLineOptionPrivate)
|
||||
{
|
||||
d->setNames(names);
|
||||
setValueName(valueName);
|
||||
setDescription(description);
|
||||
setDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QCommandLineOption object that is a copy of the QCommandLineOption
|
||||
object \a other.
|
||||
|
||||
\sa operator=()
|
||||
*/
|
||||
QCommandLineOption::QCommandLineOption(const QCommandLineOption &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the command line option object.
|
||||
*/
|
||||
QCommandLineOption::~QCommandLineOption()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes a copy of the \a other object and assigns it to this QCommandLineOption
|
||||
object.
|
||||
*/
|
||||
QCommandLineOption &QCommandLineOption::operator=(const QCommandLineOption &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the names set for this option.
|
||||
*/
|
||||
QStringList QCommandLineOption::names() const
|
||||
{
|
||||
return d->names;
|
||||
}
|
||||
|
||||
void QCommandLineOptionPrivate::setNames(const QStringList &nameList)
|
||||
{
|
||||
QStringList newNames;
|
||||
if (nameList.isEmpty())
|
||||
qWarning("QCommandLineOption: Options must have at least one name");
|
||||
Q_FOREACH (const QString &name, nameList) {
|
||||
if (name.isEmpty()) {
|
||||
qWarning("QCommandLineOption: Option names cannot be empty");
|
||||
} else {
|
||||
const QChar c = name.at(0);
|
||||
if (c == QLatin1Char('-'))
|
||||
qWarning("QCommandLineOption: Option names cannot start with a '-'");
|
||||
else if (c == QLatin1Char('/'))
|
||||
qWarning("QCommandLineOption: Option names cannot start with a '/'");
|
||||
else if (name.contains(QLatin1Char('=')))
|
||||
qWarning("QCommandLineOption: Option names cannot contain a '='");
|
||||
else
|
||||
newNames.append(name);
|
||||
}
|
||||
}
|
||||
// commit
|
||||
names = newNames;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the name of the expected value, for the documentation, to \a valueName.
|
||||
|
||||
Options without a value assigned have a boolean-like behavior:
|
||||
either the user specifies --option or they don't.
|
||||
|
||||
Options with a value assigned need to set a name for the expected value,
|
||||
for the documentation of the option in the help output. An option with names \c{o} and \c{output},
|
||||
and a value name of \c{file} will appear as \c{-o, --output <file>}.
|
||||
|
||||
Call QCommandLineParser::argument() if you expect the option to be present
|
||||
only once, and QCommandLineParser::arguments() if you expect that option
|
||||
to be present multiple times.
|
||||
|
||||
\sa valueName()
|
||||
*/
|
||||
void QCommandLineOption::setValueName(const QString &valueName)
|
||||
{
|
||||
d->valueName = valueName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the name of the expected value.
|
||||
|
||||
If empty, the option doesn't take a value.
|
||||
|
||||
\sa setValueName()
|
||||
*/
|
||||
QString QCommandLineOption::valueName() const
|
||||
{
|
||||
return d->valueName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the description used for this option to \a description.
|
||||
|
||||
It is customary to add a "." at the end of the description.
|
||||
|
||||
The description is used by QCommandLineParser::showHelp().
|
||||
|
||||
\sa description()
|
||||
*/
|
||||
void QCommandLineOption::setDescription(const QString &description)
|
||||
{
|
||||
d->description = description;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the description set for this option.
|
||||
|
||||
\sa setDescription()
|
||||
*/
|
||||
QString QCommandLineOption::description() const
|
||||
{
|
||||
return d->description;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the default value used for this option to \a defaultValue.
|
||||
|
||||
The default value is used if the user of the application does not specify
|
||||
the option on the command line.
|
||||
|
||||
If \a defaultValue is empty, the option has no default values.
|
||||
|
||||
\sa defaultValues() setDefaultValues()
|
||||
*/
|
||||
void QCommandLineOption::setDefaultValue(const QString &defaultValue)
|
||||
{
|
||||
QStringList newDefaultValues;
|
||||
if (!defaultValue.isEmpty()) {
|
||||
newDefaultValues << defaultValue;
|
||||
}
|
||||
// commit:
|
||||
d->defaultValues = newDefaultValues;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the list of default values used for this option to \a defaultValues.
|
||||
|
||||
The default values are used if the user of the application does not specify
|
||||
the option on the command line.
|
||||
|
||||
\sa defaultValues() setDefaultValue()
|
||||
*/
|
||||
void QCommandLineOption::setDefaultValues(const QStringList &defaultValues)
|
||||
{
|
||||
d->defaultValues = defaultValues;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the default values set for this option.
|
||||
|
||||
\sa setDefaultValues()
|
||||
*/
|
||||
QStringList QCommandLineOption::defaultValues() const
|
||||
{
|
||||
return d->defaultValues;
|
||||
}
|
||||
81
src/core/qcommandlineoption.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOMMANDLINEOPTION_H
|
||||
#define QCOMMANDLINEOPTION_H
|
||||
|
||||
#include <QStringList>
|
||||
#include <QSharedData>
|
||||
|
||||
class QCommandLineOptionPrivate;
|
||||
|
||||
class QCommandLineOption
|
||||
{
|
||||
public:
|
||||
explicit QCommandLineOption(const QString &name, const QString &description = QString(),
|
||||
const QString &valueName = QString(),
|
||||
const QString &defaultValue = QString());
|
||||
explicit QCommandLineOption(const QStringList &names, const QString &description = QString(),
|
||||
const QString &valueName = QString(),
|
||||
const QString &defaultValue = QString());
|
||||
QCommandLineOption(const QCommandLineOption &other);
|
||||
|
||||
~QCommandLineOption();
|
||||
|
||||
QCommandLineOption &operator=(const QCommandLineOption &other);
|
||||
|
||||
QStringList names() const;
|
||||
|
||||
void setValueName(const QString &name);
|
||||
QString valueName() const;
|
||||
|
||||
void setDescription(const QString &description);
|
||||
QString description() const;
|
||||
|
||||
void setDefaultValue(const QString &defaultValue);
|
||||
void setDefaultValues(const QStringList &defaultValues);
|
||||
QStringList defaultValues() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QCommandLineOptionPrivate> d;
|
||||
};
|
||||
|
||||
#endif // QCOMMANDLINEOPTION_H
|
||||
944
src/core/qcommandlineparser.cpp
Normal file
@@ -0,0 +1,944 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
|
||||
** Copyright (C) 2013 David Faure <faure@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcommandlineparser.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHash>
|
||||
#include <QVector>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef QHash<QString, int> NameHash_t;
|
||||
|
||||
class QCommandLineParserPrivate
|
||||
{
|
||||
public:
|
||||
inline QCommandLineParserPrivate()
|
||||
: singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions),
|
||||
builtinVersionOption(false),
|
||||
builtinHelpOption(false),
|
||||
needsParsing(true)
|
||||
{ }
|
||||
|
||||
bool parse(const QStringList &args);
|
||||
void checkParsed(const char *method);
|
||||
QStringList aliases(const QString &name) const;
|
||||
QString helpText() const;
|
||||
bool registerFoundOption(const QString &optionName);
|
||||
bool parseOptionValue(const QString &optionName, const QString &argument,
|
||||
QStringList::const_iterator *argumentIterator,
|
||||
QStringList::const_iterator argsEnd);
|
||||
|
||||
//! Error text set when parse() returns false
|
||||
QString errorText;
|
||||
|
||||
//! The command line options used for parsing
|
||||
QList<QCommandLineOption> commandLineOptionList;
|
||||
|
||||
//! Hash mapping option names to their offsets in commandLineOptionList and optionArgumentList.
|
||||
NameHash_t nameHash;
|
||||
|
||||
//! Option values found (only for options with a value)
|
||||
QHash<int, QStringList> optionValuesHash;
|
||||
|
||||
//! Names of options found on the command line.
|
||||
QStringList optionNames;
|
||||
|
||||
//! Arguments which did not belong to any option.
|
||||
QStringList positionalArgumentList;
|
||||
|
||||
//! Names of options which were unknown.
|
||||
QStringList unknownOptionNames;
|
||||
|
||||
//! Application description
|
||||
QString description;
|
||||
|
||||
//! Documentation for positional arguments
|
||||
struct PositionalArgumentDefinition
|
||||
{
|
||||
QString name;
|
||||
QString description;
|
||||
QString syntax;
|
||||
};
|
||||
QVector<PositionalArgumentDefinition> positionalArgumentDefinitions;
|
||||
|
||||
//! The parsing mode for "-abc"
|
||||
QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode;
|
||||
|
||||
//! Whether addVersionOption was called
|
||||
bool builtinVersionOption;
|
||||
|
||||
//! Whether addHelpOption was called
|
||||
bool builtinHelpOption;
|
||||
|
||||
//! True if parse() needs to be called
|
||||
bool needsParsing;
|
||||
};
|
||||
|
||||
QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
|
||||
{
|
||||
const NameHash_t::const_iterator it = nameHash.find(optionName);
|
||||
if (it == nameHash.end()) {
|
||||
qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
|
||||
return QStringList();
|
||||
}
|
||||
return commandLineOptionList.at(*it).names();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.2
|
||||
\class QCommandLineParser
|
||||
\inmodule QtCore
|
||||
\ingroup tools
|
||||
|
||||
\brief The QCommandLineParser class provides a means for handling the
|
||||
command line options.
|
||||
|
||||
QCoreApplication provides the command-line arguments as a simple list of strings.
|
||||
QCommandLineParser provides the ability to define a set of options, parse the
|
||||
command-line arguments, and store which options have actually been used, as
|
||||
well as option values.
|
||||
|
||||
Any argument that isn't an option (i.e. doesn't start with a \c{-}) is stored
|
||||
as a "positional argument".
|
||||
|
||||
The parser handles short names, long names, more than one name for the same
|
||||
option, and option values.
|
||||
|
||||
Options on the command line are recognized as starting with a single or
|
||||
double \c{-} character(s).
|
||||
The option \c{-} (single dash alone) is a special case, often meaning standard
|
||||
input, and not treated as an option. The parser will treat everything after the
|
||||
option \c{--} (double dash) as positional arguments.
|
||||
|
||||
Short options are single letters. The option \c{v} would be specified by
|
||||
passing \c{-v} on the command line. In the default parsing mode, short options
|
||||
can be written in a compact form, for instance \c{-abc} is equivalent to \c{-a -b -c}.
|
||||
The parsing mode for can be set to ParseAsLongOptions, in which case \c{-abc}
|
||||
will be parsed as the long option \c{abc}.
|
||||
|
||||
Long options are more than one letter long and cannot be compacted together.
|
||||
The long option \c{verbose} would be passed as \c{--verbose} or \c{-verbose}.
|
||||
|
||||
Passing values to options can be done using the assignment operator: \c{-v=value}
|
||||
\c{--verbose=value}, or a space: \c{-v value} \c{--verbose value}, i.e. the next
|
||||
argument is used as value (even if it starts with a \c{-}).
|
||||
|
||||
The parser does not support optional values - if an option is set to
|
||||
require a value, one must be present. If such an option is placed last
|
||||
and has no value, the option will be treated as if it had not been
|
||||
specified.
|
||||
|
||||
The parser does not automatically support negating or disabling long options
|
||||
by using the format \c{--disable-option} or \c{--no-option}. However, it is
|
||||
possible to handle this case explicitly by making an option with \c{no-option}
|
||||
as one of its names, and handling the option explicitly.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
|
||||
|
||||
Known limitation: the parsing of Qt options inside QCoreApplication and subclasses
|
||||
happens before QCommandLineParser exists, so it can't take it into account. This
|
||||
means any option value that looks like a builtin Qt option, will be treated by
|
||||
QCoreApplication as a builtin Qt option. Example: \c{--profile -reverse} will
|
||||
lead to QGuiApplication seeing the -reverse option set, and removing it from
|
||||
QCoreApplication::arguments() before QCommandLineParser defines the \c{profile}
|
||||
option and parses the command line.
|
||||
|
||||
\sa QCommandLineOption, QCoreApplication
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a command line parser object.
|
||||
*/
|
||||
QCommandLineParser::QCommandLineParser()
|
||||
: d(new QCommandLineParserPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the command line parser object.
|
||||
*/
|
||||
QCommandLineParser::~QCommandLineParser()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QCommandLineParser::SingleDashWordOptionMode
|
||||
|
||||
This enum describes the way the parser interprets command-line
|
||||
options that use a single dash followed by multiple letters, as as \c{-abc}.
|
||||
|
||||
\value ParseAsCompactedShortOptions \c{-abc} is interpreted as \c{-a -b -c},
|
||||
i.e. as three short options that have been compacted on the command-line,
|
||||
if none of the options take a value. If \c{a} takes a value, then it
|
||||
is interpreted as \c{-a bc}, i.e. the short option \c{a} followed by the value \c{bc}.
|
||||
This is typically used in tools that behave like compilers, in order
|
||||
to handle options such as \c{-DDEFINE=VALUE} or \c{-I/include/path}.
|
||||
This is the default parsing mode. New applications are recommended to
|
||||
use this mode.
|
||||
|
||||
\value ParseAsLongOptions \c{-abc} is interpreted as \c{--abc},
|
||||
i.e. as the long option named \c{abc}. This is how Qt's own tools
|
||||
(uic, rcc...) have always been parsing arguments. This mode should be
|
||||
used for preserving compatibility in applications that were parsing
|
||||
arguments in such a way.
|
||||
|
||||
\sa setSingleDashWordOptionMode()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Sets the parsing mode to \a singleDashWordOptionMode.
|
||||
This must be called before process() or parse().
|
||||
*/
|
||||
void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
|
||||
{
|
||||
d->singleDashWordOptionMode = singleDashWordOptionMode;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the option \a option to look for while parsing.
|
||||
|
||||
Returns \c true if adding the option was successful; otherwise returns \c false.
|
||||
|
||||
Adding the option fails if there is no name attached to the option, or
|
||||
the option has a name that clashes with an option name added before.
|
||||
*/
|
||||
bool QCommandLineParser::addOption(const QCommandLineOption &option)
|
||||
{
|
||||
QStringList optionNames = option.names();
|
||||
|
||||
if (!optionNames.isEmpty()) {
|
||||
Q_FOREACH (const QString &name, optionNames) {
|
||||
if (d->nameHash.contains(name))
|
||||
return false;
|
||||
}
|
||||
|
||||
d->commandLineOptionList.append(option);
|
||||
|
||||
const int offset = d->commandLineOptionList.size() - 1;
|
||||
Q_FOREACH (const QString &name, optionNames)
|
||||
d->nameHash.insert(name, offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the \c{-v} / \c{--version} option, which displays the version string of the application.
|
||||
|
||||
This option is handled automatically by QCommandLineParser.
|
||||
|
||||
You can set the actual version string by using QCoreApplication::setApplicationVersion().
|
||||
|
||||
Returns the option instance, which can be used to call isSet().
|
||||
*/
|
||||
QCommandLineOption QCommandLineParser::addVersionOption()
|
||||
{
|
||||
QCommandLineOption opt(QStringList() << "v" << "version", tr("Displays version information."));
|
||||
addOption(opt);
|
||||
d->builtinVersionOption = true;
|
||||
return opt;
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the help option (\c{-h}, \c{--help} and \c{-?} on Windows)
|
||||
This option is handled automatically by QCommandLineParser.
|
||||
|
||||
Remember to use setApplicationDescription to set the application description,
|
||||
which will be displayed when this option is used.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
|
||||
|
||||
Returns the option instance, which can be used to call isSet().
|
||||
*/
|
||||
QCommandLineOption QCommandLineParser::addHelpOption()
|
||||
{
|
||||
QCommandLineOption opt(QStringList()
|
||||
#ifdef Q_OS_WIN
|
||||
<< "?"
|
||||
#endif
|
||||
<< "h"
|
||||
<< "help", tr("Displays this help."));
|
||||
addOption(opt);
|
||||
d->builtinHelpOption = true;
|
||||
return opt;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the application \a description shown by helpText().
|
||||
*/
|
||||
void QCommandLineParser::setApplicationDescription(const QString &description)
|
||||
{
|
||||
d->description = description;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the application description set in setApplicationDescription().
|
||||
*/
|
||||
QString QCommandLineParser::applicationDescription() const
|
||||
{
|
||||
return d->description;
|
||||
}
|
||||
|
||||
/*!
|
||||
Defines an additional argument to the application, for the benefit of the help text.
|
||||
|
||||
The argument \a name and \a description will appear under the \c{Arguments:} section
|
||||
of the help. If \a syntax is specified, it will be appended to the Usage line, otherwise
|
||||
the \a name will be appended.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser.cpp 2
|
||||
|
||||
\sa addHelpOption(), helpText()
|
||||
*/
|
||||
void QCommandLineParser::addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
|
||||
{
|
||||
QCommandLineParserPrivate::PositionalArgumentDefinition arg;
|
||||
arg.name = name;
|
||||
arg.description = description;
|
||||
arg.syntax = syntax.isEmpty() ? name : syntax;
|
||||
d->positionalArgumentDefinitions.append(arg);
|
||||
}
|
||||
|
||||
/*!
|
||||
Clears the definitions of additional arguments from the help text.
|
||||
|
||||
This is only needed for the special case of tools which support multiple commands
|
||||
with different options. Once the actual command has been identified, the options
|
||||
for this command can be defined, and the help text for the command can be adjusted
|
||||
accordingly.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser.cpp 3
|
||||
*/
|
||||
void QCommandLineParser::clearPositionalArguments()
|
||||
{
|
||||
d->positionalArgumentDefinitions.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Parses the command line \a arguments.
|
||||
|
||||
Most programs don't need to call this, a simple call to process() is enough.
|
||||
|
||||
parse() is more low-level, and only does the parsing. The application will have to
|
||||
take care of the error handling, using errorText() if parse() returns \c false.
|
||||
This can be useful for instance to show a graphical error message in graphical programs.
|
||||
|
||||
Calling parse() instead of process() can also be useful in order to ignore unknown
|
||||
options temporarily, because more option definitions will be provided later on
|
||||
(depending on one of the arguments), before calling process().
|
||||
|
||||
Don't forget that \a arguments must start with the name of the executable (ignored, though).
|
||||
|
||||
Returns \c false in case of a parse error (unknown option or missing value); returns \c true otherwise.
|
||||
|
||||
\sa process()
|
||||
*/
|
||||
bool QCommandLineParser::parse(const QStringList &arguments)
|
||||
{
|
||||
return d->parse(arguments);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a translated error text for the user.
|
||||
This should only be called when parse() returns \c false.
|
||||
*/
|
||||
QString QCommandLineParser::errorText() const
|
||||
{
|
||||
if (!d->errorText.isEmpty())
|
||||
return d->errorText;
|
||||
if (d->unknownOptionNames.count() == 1)
|
||||
return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
|
||||
if (d->unknownOptionNames.count() > 1)
|
||||
return tr("Unknown options: %1.").arg(d->unknownOptionNames.join(", "));
|
||||
return QString();
|
||||
}
|
||||
|
||||
/*!
|
||||
Processes the command line \a arguments.
|
||||
|
||||
In addition to parsing the options (like parse()), this function also handles the builtin
|
||||
options and handles errors.
|
||||
|
||||
The builtin options are \c{--version} if addVersionOption was called and \c{--help} if addHelpOption was called.
|
||||
|
||||
When invoking one of these options, or when an error happens (for instance an unknown option was
|
||||
passed), the current process will then stop, using the exit() function.
|
||||
|
||||
\sa QCoreApplication::arguments(), parse()
|
||||
*/
|
||||
void QCommandLineParser::process(const QStringList &arguments)
|
||||
{
|
||||
if (!d->parse(arguments)) {
|
||||
fprintf(stderr, "%s\n", qPrintable(errorText()));
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (d->builtinVersionOption && isSet("version")) {
|
||||
printf("%s %s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(QCoreApplication::applicationVersion()));
|
||||
::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (d->builtinHelpOption && isSet("help"))
|
||||
showHelp(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
The command line is obtained from the QCoreApplication instance \a app.
|
||||
*/
|
||||
void QCommandLineParser::process(const QCoreApplication &app)
|
||||
{
|
||||
// QCoreApplication::arguments() is static, but the app instance must exist so we require it as parameter
|
||||
Q_UNUSED(app);
|
||||
process(QCoreApplication::arguments());
|
||||
}
|
||||
|
||||
void QCommandLineParserPrivate::checkParsed(const char *method)
|
||||
{
|
||||
if (needsParsing)
|
||||
qWarning("QCommandLineParser: call process() or parse() before %s", method);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Looks up the option \a optionName (found on the command line) and register it as found.
|
||||
Returns \c true on success.
|
||||
*/
|
||||
bool QCommandLineParserPrivate::registerFoundOption(const QString &optionName)
|
||||
{
|
||||
if (nameHash.contains(optionName)) {
|
||||
optionNames.append(optionName);
|
||||
return true;
|
||||
} else {
|
||||
unknownOptionNames.append(optionName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\brief Parse the value for a given option, if it was defined to expect one.
|
||||
|
||||
The value is taken from the next argument, or after the equal sign in \a argument.
|
||||
|
||||
\param optionName the short option name
|
||||
\param argument the argument from the command line currently parsed. Only used for -k=value parsing.
|
||||
\param argumentIterator iterator to the currently parsed argument. Incremented if the next argument contains the value.
|
||||
\param argsEnd args.end(), to check if ++argumentIterator goes out of bounds
|
||||
Returns \c true on success.
|
||||
*/
|
||||
bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, const QString &argument,
|
||||
QStringList::const_iterator *argumentIterator, QStringList::const_iterator argsEnd)
|
||||
{
|
||||
const QLatin1Char assignChar('=');
|
||||
const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
|
||||
if (nameHashIt != nameHash.constEnd()) {
|
||||
const int assignPos = argument.indexOf(assignChar);
|
||||
const NameHash_t::mapped_type optionOffset = *nameHashIt;
|
||||
const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
|
||||
if (withValue) {
|
||||
if (assignPos == -1) {
|
||||
++(*argumentIterator);
|
||||
if (*argumentIterator == argsEnd) {
|
||||
errorText = QCommandLineParser::tr("Missing value after '%1'.").arg(argument);
|
||||
return false;
|
||||
}
|
||||
optionValuesHash[optionOffset].append(*(*argumentIterator));
|
||||
} else {
|
||||
optionValuesHash[optionOffset].append(argument.mid(assignPos + 1));
|
||||
}
|
||||
} else {
|
||||
if (assignPos != -1) {
|
||||
errorText = QCommandLineParser::tr("Unexpected value after '%1'.").arg(argument.left(assignPos));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Parse the list of arguments \a args, and fills in
|
||||
optionNames, optionValuesHash, unknownOptionNames, positionalArguments, and errorText.
|
||||
|
||||
Any results from a previous parse operation are removed.
|
||||
|
||||
The parser will not look for further options once it encounters the option
|
||||
\c{--}; this does not include when \c{--} follows an option that requires a value.
|
||||
*/
|
||||
bool QCommandLineParserPrivate::parse(const QStringList &args)
|
||||
{
|
||||
needsParsing = false;
|
||||
bool error = false;
|
||||
|
||||
const QString doubleDashString("--");
|
||||
const QLatin1Char dashChar('-');
|
||||
const QLatin1Char assignChar('=');
|
||||
|
||||
bool doubleDashFound = false;
|
||||
errorText.clear();
|
||||
positionalArgumentList.clear();
|
||||
optionNames.clear();
|
||||
unknownOptionNames.clear();
|
||||
optionValuesHash.clear();
|
||||
|
||||
if (args.isEmpty()) {
|
||||
qWarning("QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList::const_iterator argumentIterator = args.begin();
|
||||
++argumentIterator; // skip executable name
|
||||
|
||||
for (; argumentIterator != args.end() ; ++argumentIterator) {
|
||||
QString argument = *argumentIterator;
|
||||
|
||||
if (doubleDashFound) {
|
||||
positionalArgumentList.append(argument);
|
||||
} else if (argument.startsWith(doubleDashString)) {
|
||||
if (argument.length() > 2) {
|
||||
QString optionName = argument.mid(2).section(assignChar, 0, 0);
|
||||
if (registerFoundOption(optionName)) {
|
||||
if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
|
||||
error = true;
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
} else {
|
||||
doubleDashFound = true;
|
||||
}
|
||||
} else if (argument.startsWith(dashChar)) {
|
||||
if (argument.size() == 1) { // single dash ("stdin")
|
||||
positionalArgumentList.append(argument);
|
||||
continue;
|
||||
}
|
||||
switch (singleDashWordOptionMode) {
|
||||
case QCommandLineParser::ParseAsCompactedShortOptions:
|
||||
{
|
||||
QString optionName;
|
||||
bool valueFound = false;
|
||||
for (int pos = 1 ; pos < argument.size(); ++pos) {
|
||||
optionName = argument.mid(pos, 1);
|
||||
if (!registerFoundOption(optionName)) {
|
||||
error = true;
|
||||
} else {
|
||||
const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
|
||||
Q_ASSERT(nameHashIt != nameHash.constEnd()); // checked by registerFoundOption
|
||||
const NameHash_t::mapped_type optionOffset = *nameHashIt;
|
||||
const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
|
||||
if (withValue) {
|
||||
if (pos + 1 < argument.size()) {
|
||||
if (argument.at(pos + 1) == assignChar)
|
||||
++pos;
|
||||
optionValuesHash[optionOffset].append(argument.mid(pos + 1));
|
||||
valueFound = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (pos + 1 < argument.size() && argument.at(pos + 1) == assignChar)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!valueFound && !parseOptionValue(optionName, argument, &argumentIterator, args.end()))
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
case QCommandLineParser::ParseAsLongOptions:
|
||||
{
|
||||
const QString optionName = argument.mid(1).section(assignChar, 0, 0);
|
||||
if (registerFoundOption(optionName)) {
|
||||
if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
|
||||
error = true;
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
positionalArgumentList.append(argument);
|
||||
}
|
||||
if (argumentIterator == args.end())
|
||||
break;
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
/*!
|
||||
Checks whether the option \a name was passed to the application.
|
||||
|
||||
Returns \c true if the option \a name was set, false otherwise.
|
||||
|
||||
The name provided can be any long or short name of any option that was
|
||||
added with \c addOption(). All the options names are treated as being
|
||||
equivalent. If the name is not recognized or that option was not present,
|
||||
false is returned.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser.cpp 0
|
||||
*/
|
||||
|
||||
bool QCommandLineParser::isSet(const QString &name) const
|
||||
{
|
||||
d->checkParsed("isSet");
|
||||
if (d->optionNames.contains(name))
|
||||
return true;
|
||||
const QStringList aliases = d->aliases(name);
|
||||
Q_FOREACH (const QString &optionName, d->optionNames) {
|
||||
if (aliases.contains(optionName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the option value found for the given option name \a optionName, or
|
||||
an empty string if not found.
|
||||
|
||||
The name provided can be any long or short name of any option that was
|
||||
added with \c addOption(). All the option names are treated as being
|
||||
equivalent. If the name is not recognized or that option was not present, an
|
||||
empty string is returned.
|
||||
|
||||
For options found by the parser, the last value found for
|
||||
that option is returned. If the option wasn't specified on the command line,
|
||||
the default value is returned.
|
||||
|
||||
An empty string is returned if the option does not take a value.
|
||||
|
||||
\sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
|
||||
*/
|
||||
|
||||
QString QCommandLineParser::value(const QString &optionName) const
|
||||
{
|
||||
d->checkParsed("value");
|
||||
const QStringList valueList = values(optionName);
|
||||
|
||||
if (!valueList.isEmpty())
|
||||
return valueList.last();
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of option values found for the given option name \a
|
||||
optionName, or an empty list if not found.
|
||||
|
||||
The name provided can be any long or short name of any option that was
|
||||
added with \c addOption(). All the options names are treated as being
|
||||
equivalent. If the name is not recognized or that option was not present, an
|
||||
empty list is returned.
|
||||
|
||||
For options found by the parser, the list will contain an entry for
|
||||
each time the option was encountered by the parser. If the option wasn't
|
||||
specified on the command line, the default values are returned.
|
||||
|
||||
An empty list is returned if the option does not take a value.
|
||||
|
||||
\sa value(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
|
||||
*/
|
||||
|
||||
QStringList QCommandLineParser::values(const QString &optionName) const
|
||||
{
|
||||
d->checkParsed("values");
|
||||
const NameHash_t::const_iterator it = d->nameHash.constFind(optionName);
|
||||
if (it != d->nameHash.constEnd()) {
|
||||
const int optionOffset = *it;
|
||||
QStringList values = d->optionValuesHash.value(optionOffset);
|
||||
if (values.isEmpty())
|
||||
values = d->commandLineOptionList.at(optionOffset).defaultValues();
|
||||
return values;
|
||||
}
|
||||
|
||||
qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
Checks whether the \a option was passed to the application.
|
||||
|
||||
Returns \c true if the \a option was set, false otherwise.
|
||||
|
||||
This is the recommended way to check for options with no values.
|
||||
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qcommandlineparser.cpp 1
|
||||
*/
|
||||
bool QCommandLineParser::isSet(const QCommandLineOption &option) const
|
||||
{
|
||||
// option.names() might be empty if the constructor failed
|
||||
return !option.names().isEmpty() && isSet(option.names().first());
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
Returns the option value found for the given \a option, or
|
||||
an empty string if not found.
|
||||
|
||||
For options found by the parser, the last value found for
|
||||
that option is returned. If the option wasn't specified on the command line,
|
||||
the default value is returned.
|
||||
|
||||
An empty string is returned if the option does not take a value.
|
||||
|
||||
\sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
|
||||
*/
|
||||
QString QCommandLineParser::value(const QCommandLineOption &option) const
|
||||
{
|
||||
return value(option.names().first());
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
Returns a list of option values found for the given \a option,
|
||||
or an empty list if not found.
|
||||
|
||||
For options found by the parser, the list will contain an entry for
|
||||
each time the option was encountered by the parser. If the option wasn't
|
||||
specified on the command line, the default values are returned.
|
||||
|
||||
An empty list is returned if the option does not take a value.
|
||||
|
||||
\sa value(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
|
||||
*/
|
||||
QStringList QCommandLineParser::values(const QCommandLineOption &option) const
|
||||
{
|
||||
return values(option.names().first());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of positional arguments.
|
||||
|
||||
These are all of the arguments that were not recognized as part of an
|
||||
option.
|
||||
*/
|
||||
|
||||
QStringList QCommandLineParser::positionalArguments() const
|
||||
{
|
||||
d->checkParsed("positionalArguments");
|
||||
return d->positionalArgumentList;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of option names that were found.
|
||||
|
||||
This returns a list of all the recognized option names found by the
|
||||
parser, in the order in which they were found. For any long options
|
||||
that were in the form {--option=value}, the value part will have been
|
||||
dropped.
|
||||
|
||||
The names in this list do not include the preceding dash characters.
|
||||
Names may appear more than once in this list if they were encountered
|
||||
more than once by the parser.
|
||||
|
||||
Any entry in the list can be used with \c value() or with
|
||||
\c values() to get any relevant option values.
|
||||
*/
|
||||
|
||||
QStringList QCommandLineParser::optionNames() const
|
||||
{
|
||||
d->checkParsed("optionNames");
|
||||
return d->optionNames;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of unknown option names.
|
||||
|
||||
This list will include both long an short name options that were not
|
||||
recognized. For any long options that were in the form {--option=value},
|
||||
the value part will have been dropped and only the long name is added.
|
||||
|
||||
The names in this list do not include the preceding dash characters.
|
||||
Names may appear more than once in this list if they were encountered
|
||||
more than once by the parser.
|
||||
|
||||
\sa optionNames()
|
||||
*/
|
||||
|
||||
QStringList QCommandLineParser::unknownOptionNames() const
|
||||
{
|
||||
d->checkParsed("unknownOptionNames");
|
||||
return d->unknownOptionNames;
|
||||
}
|
||||
|
||||
/*!
|
||||
Displays the help information, and exits the application.
|
||||
This is automatically triggered by the --help option, but can also
|
||||
be used to display the help when the user is not invoking the
|
||||
application correctly.
|
||||
The exit code is set to \a exitCode. It should be set to 0 if the
|
||||
user requested to see the help, and to any other value in case of
|
||||
an error.
|
||||
|
||||
\sa helpText()
|
||||
*/
|
||||
void QCommandLineParser::showHelp(int exitCode)
|
||||
{
|
||||
fprintf(stdout, "%s", qPrintable(d->helpText()));
|
||||
::exit(exitCode);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a string containing the complete help information.
|
||||
|
||||
\sa showHelp()
|
||||
*/
|
||||
QString QCommandLineParser::helpText() const
|
||||
{
|
||||
return d->helpText();
|
||||
}
|
||||
|
||||
static QString wrapText(const QString &names, int longestOptionNameString, const QString &description)
|
||||
{
|
||||
const QLatin1Char nl('\n');
|
||||
QString text = QString(" ") + names.leftJustified(longestOptionNameString) + QLatin1Char(' ');
|
||||
const int indent = text.length();
|
||||
int lineStart = 0;
|
||||
int lastBreakable = -1;
|
||||
const int max = 79 - indent;
|
||||
int x = 0;
|
||||
const int len = description.length();
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
++x;
|
||||
const QChar c = description.at(i);
|
||||
if (c.isSpace())
|
||||
lastBreakable = i;
|
||||
|
||||
int breakAt = -1;
|
||||
int nextLineStart = -1;
|
||||
if (x > max && lastBreakable != -1) {
|
||||
// time to break and we know where
|
||||
breakAt = lastBreakable;
|
||||
nextLineStart = lastBreakable + 1;
|
||||
} else if ((x > max - 1 && lastBreakable == -1) || i == len - 1) {
|
||||
// time to break but found nowhere [-> break here], or end of last line
|
||||
breakAt = i + 1;
|
||||
nextLineStart = breakAt;
|
||||
} else if (c == nl) {
|
||||
// forced break
|
||||
breakAt = i;
|
||||
nextLineStart = i + 1;
|
||||
}
|
||||
|
||||
if (breakAt != -1) {
|
||||
const int numChars = breakAt - lineStart;
|
||||
//qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
|
||||
if (lineStart > 0)
|
||||
text += QString(indent, QLatin1Char(' '));
|
||||
text += description.mid(lineStart, numChars) + nl;
|
||||
x = 0;
|
||||
lastBreakable = -1;
|
||||
lineStart = nextLineStart;
|
||||
if (lineStart < len && description.at(lineStart).isSpace())
|
||||
++lineStart; // don't start a line with a space
|
||||
i = lineStart;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
QString QCommandLineParserPrivate::helpText() const
|
||||
{
|
||||
const QLatin1Char nl('\n');
|
||||
QString text;
|
||||
const QString exeName = QCoreApplication::instance()->arguments().first();
|
||||
QString usage = exeName;
|
||||
if (!commandLineOptionList.isEmpty()) {
|
||||
usage += QLatin1Char(' ');
|
||||
usage += QCommandLineParser::tr("[options]");
|
||||
}
|
||||
Q_FOREACH (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
|
||||
usage += QLatin1Char(' ');
|
||||
usage += arg.syntax;
|
||||
}
|
||||
text += QCommandLineParser::tr("Usage: %1").arg(usage) + nl;
|
||||
if (!description.isEmpty())
|
||||
text += description + nl;
|
||||
text += nl;
|
||||
if (!commandLineOptionList.isEmpty())
|
||||
text += QCommandLineParser::tr("Options:") + nl;
|
||||
QStringList optionNameList;
|
||||
int longestOptionNameString = 0;
|
||||
Q_FOREACH (const QCommandLineOption &option, commandLineOptionList) {
|
||||
QStringList optionNames;
|
||||
Q_FOREACH (const QString &optionName, option.names()) {
|
||||
if (optionName.length() == 1)
|
||||
optionNames.append(QLatin1Char('-') + optionName);
|
||||
else
|
||||
optionNames.append(QString("--") + optionName);
|
||||
}
|
||||
QString optionNamesString = optionNames.join(", ");
|
||||
if (!option.valueName().isEmpty())
|
||||
optionNamesString += QString(" <") + option.valueName() + QLatin1Char('>');
|
||||
optionNameList.append(optionNamesString);
|
||||
longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
|
||||
}
|
||||
++longestOptionNameString;
|
||||
for (int i = 0; i < commandLineOptionList.count(); ++i) {
|
||||
const QCommandLineOption &option = commandLineOptionList.at(i);
|
||||
text += wrapText(optionNameList.at(i), longestOptionNameString, option.description());
|
||||
}
|
||||
if (!positionalArgumentDefinitions.isEmpty()) {
|
||||
if (!commandLineOptionList.isEmpty())
|
||||
text += nl;
|
||||
text += QCommandLineParser::tr("Arguments:") + nl;
|
||||
Q_FOREACH (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
|
||||
text += wrapText(arg.name, longestOptionNameString, arg.description);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
102
src/core/qcommandlineparser.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOMMANDLINEPARSER_H
|
||||
#define QCOMMANDLINEPARSER_H
|
||||
|
||||
#include <QStringList>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "qcommandlineoption.h"
|
||||
|
||||
class QCommandLineParserPrivate;
|
||||
class QCoreApplication;
|
||||
|
||||
class QCommandLineParser
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QCommandLineParser)
|
||||
public:
|
||||
QCommandLineParser();
|
||||
~QCommandLineParser();
|
||||
|
||||
enum SingleDashWordOptionMode {
|
||||
ParseAsCompactedShortOptions,
|
||||
ParseAsLongOptions
|
||||
};
|
||||
void setSingleDashWordOptionMode(SingleDashWordOptionMode parsingMode);
|
||||
|
||||
bool addOption(const QCommandLineOption &commandLineOption);
|
||||
|
||||
QCommandLineOption addVersionOption();
|
||||
QCommandLineOption addHelpOption();
|
||||
void setApplicationDescription(const QString &description);
|
||||
QString applicationDescription() const;
|
||||
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax = QString());
|
||||
void clearPositionalArguments();
|
||||
|
||||
void process(const QStringList &arguments);
|
||||
void process(const QCoreApplication &app);
|
||||
|
||||
bool parse(const QStringList &arguments);
|
||||
QString errorText() const;
|
||||
|
||||
bool isSet(const QString &name) const;
|
||||
QString value(const QString &name) const;
|
||||
QStringList values(const QString &name) const;
|
||||
|
||||
bool isSet(const QCommandLineOption &option) const;
|
||||
QString value(const QCommandLineOption &option) const;
|
||||
QStringList values(const QCommandLineOption &option) const;
|
||||
|
||||
QStringList positionalArguments() const;
|
||||
QStringList optionNames() const;
|
||||
QStringList unknownOptionNames() const;
|
||||
|
||||
void showHelp(int exitCode = 0);
|
||||
QString helpText() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QCommandLineParser)
|
||||
|
||||
QCommandLineParserPrivate * const d;
|
||||
};
|
||||
|
||||
#endif // QCOMMANDLINEPARSER_H
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
bool Crypto::m_initalized(false);
|
||||
|
||||
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
|
||||
static int gcry_qt_mutex_init(void** p_sys)
|
||||
{
|
||||
*p_sys = new QMutex();
|
||||
@@ -57,6 +58,7 @@ static const struct gcry_thread_cbs gcry_threads_qt =
|
||||
gcry_qt_mutex_unlock,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
#endif
|
||||
|
||||
Crypto::Crypto()
|
||||
{
|
||||
@@ -69,7 +71,10 @@ void Crypto::init()
|
||||
return;
|
||||
}
|
||||
|
||||
// libgcrypt >= 1.6 doesn't allow custom thread callbacks anymore.
|
||||
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
|
||||
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_qt);
|
||||
#endif
|
||||
gcry_check_version(0);
|
||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "SymmetricCipher.h"
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "crypto/SymmetricCipherGcrypt.h"
|
||||
#include "crypto/SymmetricCipherSalsa20.h"
|
||||
|
||||
@@ -24,7 +25,6 @@ SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCiphe
|
||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
|
||||
: m_backend(createBackend(algo, mode, direction))
|
||||
{
|
||||
m_backend->init();
|
||||
m_backend->setKey(key);
|
||||
m_backend->setIv(iv);
|
||||
}
|
||||
@@ -39,10 +39,15 @@ SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorith
|
||||
switch (algo) {
|
||||
case SymmetricCipher::Aes256:
|
||||
case SymmetricCipher::Twofish:
|
||||
#if defined(GCRYPT_HAS_SALSA20)
|
||||
case SymmetricCipher::Salsa20:
|
||||
#endif
|
||||
return new SymmetricCipherGcrypt(algo, mode, direction);
|
||||
|
||||
#if !defined(GCRYPT_HAS_SALSA20)
|
||||
case SymmetricCipher::Salsa20:
|
||||
return new SymmetricCipherSalsa20(algo, mode, direction);
|
||||
#endif
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
|
||||
@@ -24,7 +24,6 @@ class SymmetricCipherBackend
|
||||
{
|
||||
public:
|
||||
virtual ~SymmetricCipherBackend() {}
|
||||
virtual void init() = 0;
|
||||
virtual void setKey(const QByteArray& key) = 0;
|
||||
virtual void setIv(const QByteArray& iv) = 0;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "SymmetricCipherGcrypt.h"
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||
@@ -27,6 +28,16 @@ SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, Sy
|
||||
, m_blockSize(-1)
|
||||
{
|
||||
Q_ASSERT(Crypto::initalized());
|
||||
|
||||
gcry_error_t error;
|
||||
|
||||
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
||||
Q_ASSERT(error == 0); // TODO: real error checking
|
||||
|
||||
size_t blockSizeT;
|
||||
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, Q_NULLPTR, &blockSizeT);
|
||||
Q_ASSERT(error == 0);
|
||||
m_blockSize = blockSizeT;
|
||||
}
|
||||
|
||||
SymmetricCipherGcrypt::~SymmetricCipherGcrypt()
|
||||
@@ -43,6 +54,11 @@ int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo)
|
||||
case SymmetricCipher::Twofish:
|
||||
return GCRY_CIPHER_TWOFISH;
|
||||
|
||||
#ifdef GCRYPT_HAS_SALSA20
|
||||
case SymmetricCipher::Salsa20:
|
||||
return GCRY_CIPHER_SALSA20;
|
||||
#endif
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
@@ -58,25 +74,15 @@ int SymmetricCipherGcrypt::gcryptMode(SymmetricCipher::Mode mode)
|
||||
case SymmetricCipher::Cbc:
|
||||
return GCRY_CIPHER_MODE_CBC;
|
||||
|
||||
case SymmetricCipher::Stream:
|
||||
return GCRY_CIPHER_MODE_STREAM;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SymmetricCipherGcrypt::init()
|
||||
{
|
||||
gcry_error_t error;
|
||||
|
||||
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
||||
Q_ASSERT(error == 0); // TODO: real error checking
|
||||
|
||||
size_t blockSizeT;
|
||||
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, Q_NULLPTR, &blockSizeT);
|
||||
Q_ASSERT(error == 0);
|
||||
m_blockSize = blockSizeT;
|
||||
}
|
||||
|
||||
void SymmetricCipherGcrypt::setKey(const QByteArray& key)
|
||||
{
|
||||
m_key = key;
|
||||
@@ -134,15 +140,18 @@ void SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds)
|
||||
|
||||
gcry_error_t error;
|
||||
|
||||
char* rawData = data.data();
|
||||
int size = data.size();
|
||||
|
||||
if (m_direction == SymmetricCipher::Decrypt) {
|
||||
for (quint64 i = 0; i != rounds; ++i) {
|
||||
error = gcry_cipher_decrypt(m_ctx, data.data(), data.size(), Q_NULLPTR, 0);
|
||||
error = gcry_cipher_decrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
||||
Q_ASSERT(error == 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (quint64 i = 0; i != rounds; ++i) {
|
||||
error = gcry_cipher_encrypt(m_ctx, data.data(), data.size(), Q_NULLPTR, 0);
|
||||
error = gcry_cipher_encrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
||||
Q_ASSERT(error == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ public:
|
||||
SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||
SymmetricCipher::Direction direction);
|
||||
~SymmetricCipherGcrypt();
|
||||
void init();
|
||||
void setKey(const QByteArray& key);
|
||||
void setIv(const QByteArray& iv);
|
||||
|
||||
|
||||
@@ -33,10 +33,6 @@ SymmetricCipherSalsa20::~SymmetricCipherSalsa20()
|
||||
{
|
||||
}
|
||||
|
||||
void SymmetricCipherSalsa20::init()
|
||||
{
|
||||
}
|
||||
|
||||
void SymmetricCipherSalsa20::setKey(const QByteArray& key)
|
||||
{
|
||||
Q_ASSERT((key.size() == 16) || (key.size() == 32));
|
||||
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
void setAlgorithm(SymmetricCipher::Algorithm algo);
|
||||
void setMode(SymmetricCipher::Mode mode);
|
||||
void setDirection(SymmetricCipher::Direction direction);
|
||||
void init();
|
||||
void setKey(const QByteArray& key);
|
||||
void setIv(const QByteArray& iv);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "ChangeMasterKeyWidget.h"
|
||||
#include "ui_ChangeMasterKeyWidget.h"
|
||||
|
||||
#include "core/FilePath.h"
|
||||
#include "keys/FileKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "gui/FileDialog.h"
|
||||
@@ -31,7 +32,9 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent)
|
||||
|
||||
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey()));
|
||||
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(togglePassword(bool)));
|
||||
m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
|
||||
connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->enterPasswordEdit, SLOT(setShowPassword(bool)));
|
||||
m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit);
|
||||
connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile()));
|
||||
connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile()));
|
||||
}
|
||||
@@ -40,12 +43,6 @@ ChangeMasterKeyWidget::~ChangeMasterKeyWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void ChangeMasterKeyWidget::togglePassword(bool checked)
|
||||
{
|
||||
m_ui->enterPasswordEdit->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
m_ui->repeatPasswordEdit->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void ChangeMasterKeyWidget::createKeyFile()
|
||||
{
|
||||
QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
|
||||
@@ -81,7 +78,7 @@ void ChangeMasterKeyWidget::clearForms()
|
||||
m_ui->enterPasswordEdit->setText("");
|
||||
m_ui->repeatPasswordEdit->setText("");
|
||||
m_ui->keyFileGroup->setChecked(false);
|
||||
m_ui->togglePasswordButton->setChecked(true);
|
||||
m_ui->togglePasswordButton->setChecked(false);
|
||||
// TODO: clear m_ui->keyFileCombo
|
||||
|
||||
m_ui->enterPasswordEdit->setFocus();
|
||||
|
||||
@@ -45,7 +45,6 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void generateKey();
|
||||
void reject();
|
||||
void togglePassword(bool checked);
|
||||
void createKeyFile();
|
||||
void browseKeyFile();
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="enterPasswordEdit">
|
||||
<widget class="PasswordEdit" name="enterPasswordEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
@@ -60,15 +60,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="togglePasswordButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -81,7 +75,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="repeatPasswordEdit">
|
||||
<widget class="PasswordEdit" name="repeatPasswordEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
@@ -151,6 +145,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PasswordEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/PasswordEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>passwordGroup</tabstop>
|
||||
<tabstop>enterPasswordEdit</tabstop>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "gui/FileDialog.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
@@ -40,7 +41,9 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
|
||||
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), SLOT(togglePassword(bool)));
|
||||
m_ui->buttonTogglePassword->setIcon(filePath()->onOffIcon("actions", "password-show"));
|
||||
connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)),
|
||||
m_ui->editPassword, SLOT(setShowPassword(bool)));
|
||||
connect(m_ui->buttonBrowseFile, SIGNAL(clicked()), SLOT(browseKeyFile()));
|
||||
|
||||
connect(m_ui->editPassword, SIGNAL(textChanged(QString)), SLOT(activatePassword()));
|
||||
@@ -155,11 +158,6 @@ void DatabaseOpenWidget::reject()
|
||||
Q_EMIT editFinished(false);
|
||||
}
|
||||
|
||||
void DatabaseOpenWidget::togglePassword(bool checked)
|
||||
{
|
||||
m_ui->editPassword->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void DatabaseOpenWidget::activatePassword()
|
||||
{
|
||||
m_ui->checkPassword->setChecked(true);
|
||||
|
||||
@@ -52,7 +52,6 @@ protected Q_SLOTS:
|
||||
void reject();
|
||||
|
||||
private Q_SLOTS:
|
||||
void togglePassword(bool checked);
|
||||
void activatePassword();
|
||||
void activateKeyFile();
|
||||
void setOkButtonEnabled();
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="editPassword">
|
||||
<widget class="PasswordEdit" name="editPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
@@ -80,15 +80,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonTogglePassword">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -144,6 +138,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PasswordEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/PasswordEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>checkPassword</tabstop>
|
||||
<tabstop>editPassword</tabstop>
|
||||
|
||||
@@ -597,5 +597,17 @@ void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb)
|
||||
|
||||
void DatabaseTabWidget::performGlobalAutoType()
|
||||
{
|
||||
autoType()->performGlobalAutoType(m_dbList.keys());
|
||||
QList<Database*> unlockedDatabases;
|
||||
|
||||
QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
DatabaseWidget::Mode mode = i.value().dbWidget->currentMode();
|
||||
|
||||
if (mode != DatabaseWidget::LockedMode) {
|
||||
unlockedDatabases.append(i.key());
|
||||
}
|
||||
}
|
||||
|
||||
autoType()->performGlobalAutoType(unlockedDatabases);
|
||||
}
|
||||
|
||||
@@ -591,12 +591,24 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod
|
||||
|
||||
void DatabaseWidget::switchToEntryEdit()
|
||||
{
|
||||
switchToEntryEdit(m_entryView->currentEntry(), false);
|
||||
Entry* entry = m_entryView->currentEntry();
|
||||
Q_ASSERT(entry);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
switchToEntryEdit(entry, false);
|
||||
}
|
||||
|
||||
void DatabaseWidget::switchToGroupEdit()
|
||||
{
|
||||
switchToGroupEdit(m_groupView->currentGroup(), false);
|
||||
Group* group = m_groupView->currentGroup();
|
||||
Q_ASSERT(group);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
switchToGroupEdit(group, false);
|
||||
}
|
||||
|
||||
void DatabaseWidget::switchToMasterKeyChange()
|
||||
|
||||
@@ -60,11 +60,15 @@ void DialogyWidget::keyPressEvent(QKeyEvent* e)
|
||||
|
||||
bool DialogyWidget::clickButton(QDialogButtonBox::StandardButton standardButton)
|
||||
{
|
||||
QPushButton* pb = qobject_cast<QPushButton*>(focusWidget());
|
||||
QPushButton* pb;
|
||||
|
||||
if (standardButton == QDialogButtonBox::Ok) {
|
||||
pb = qobject_cast<QPushButton*>(focusWidget());
|
||||
if (pb && pb->isVisible() && pb->isEnabled() && pb->hasFocus()) {
|
||||
pb->click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QDialogButtonBox*> buttonBoxes = findChildren<QDialogButtonBox*>();
|
||||
for (int i = 0; i < buttonBoxes.size(); ++i) {
|
||||
|
||||
@@ -35,6 +35,7 @@ IconStruct::IconStruct()
|
||||
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetIcons())
|
||||
, m_database(Q_NULLPTR)
|
||||
, m_defaultIconModel(new DefaultIconModel(this))
|
||||
, m_customIconModel(new CustomIconModel(this))
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/InactivityTimer.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "gui/AboutDialog.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
@@ -39,6 +40,8 @@ MainWindow::MainWindow()
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
restoreGeometry(config()->get("window/Geometry").toByteArray());
|
||||
|
||||
setWindowIcon(filePath()->applicationIcon());
|
||||
QAction* toggleViewAction = m_ui->toolBar->toggleViewAction();
|
||||
toggleViewAction->setText(tr("Show toolbar"));
|
||||
@@ -66,6 +69,11 @@ MainWindow::MainWindow()
|
||||
autoType()->registerGlobalShortcut(globalAutoTypeKey, globalAutoTypeModifiers);
|
||||
}
|
||||
|
||||
m_inactivityTimer = new InactivityTimer(this);
|
||||
connect(m_inactivityTimer, SIGNAL(inactivityDetected()),
|
||||
m_ui->tabWidget, SLOT(lockDatabases()));
|
||||
applySettingsChanges();
|
||||
|
||||
setShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O);
|
||||
setShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S);
|
||||
setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs);
|
||||
@@ -101,6 +109,8 @@ MainWindow::MainWindow()
|
||||
m_ui->actionEntryEdit->setIcon(filePath()->icon("actions", "entry-edit", false));
|
||||
m_ui->actionEntryDelete->setIcon(filePath()->icon("actions", "entry-delete", false));
|
||||
m_ui->actionEntryAutoType->setIcon(filePath()->icon("actions", "auto-type", false));
|
||||
m_ui->actionEntryCopyUsername->setIcon(filePath()->icon("actions", "username-copy", false));
|
||||
m_ui->actionEntryCopyPassword->setIcon(filePath()->icon("actions", "password-copy", false));
|
||||
|
||||
m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false));
|
||||
m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false));
|
||||
@@ -134,6 +144,7 @@ MainWindow::MainWindow()
|
||||
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
|
||||
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
|
||||
connect(m_ui->settingsWidget, SIGNAL(editFinished(bool)), SLOT(switchToDatabases()));
|
||||
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges()));
|
||||
|
||||
connect(m_ui->actionDatabaseNew, SIGNAL(triggered()), m_ui->tabWidget,
|
||||
SLOT(newDatabase()));
|
||||
@@ -217,7 +228,9 @@ void MainWindow::updateCopyAttributesMenu()
|
||||
if (!dbWidget) {
|
||||
return;
|
||||
}
|
||||
if (!dbWidget->entryView()->isSingleEntrySelected()) {
|
||||
|
||||
Entry* entry = dbWidget->entryView()->currentEntry();
|
||||
if (!entry || !dbWidget->entryView()->isSingleEntrySelected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -226,8 +239,6 @@ void MainWindow::updateCopyAttributesMenu()
|
||||
delete actions[i];
|
||||
}
|
||||
|
||||
Entry* entry = dbWidget->entryView()->currentEntry();
|
||||
|
||||
Q_FOREACH (const QString& key, entry->attributes()->customKeys()) {
|
||||
QAction* action = m_ui->menuEntryCopyAttribute->addAction(key);
|
||||
m_copyAdditionalAttributeActions->addAction(action);
|
||||
@@ -405,6 +416,26 @@ void MainWindow::databaseTabChanged(int tabIndex)
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
bool accept = saveLastDatabases();
|
||||
|
||||
if (accept) {
|
||||
saveWindowInformation();
|
||||
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::saveWindowInformation()
|
||||
{
|
||||
config()->set("window/Geometry", saveGeometry());
|
||||
}
|
||||
|
||||
bool MainWindow::saveLastDatabases()
|
||||
{
|
||||
bool accept;
|
||||
m_openDatabases.clear();
|
||||
bool openPreviousDatabasesOnStartup = config()->get("OpenPreviousDatabasesOnStartup").toBool();
|
||||
|
||||
@@ -414,10 +445,10 @@ void MainWindow::closeEvent(QCloseEvent* event)
|
||||
}
|
||||
|
||||
if (!m_ui->tabWidget->closeAllDatabases()) {
|
||||
event->ignore();
|
||||
accept = false;
|
||||
}
|
||||
else {
|
||||
event->accept();
|
||||
accept = true;
|
||||
}
|
||||
|
||||
if (openPreviousDatabasesOnStartup) {
|
||||
@@ -425,6 +456,8 @@ void MainWindow::closeEvent(QCloseEvent* event)
|
||||
this, SLOT(rememberOpenDatabases(QString)));
|
||||
config()->set("LastOpenedDatabases", m_openDatabases);
|
||||
}
|
||||
|
||||
return accept;
|
||||
}
|
||||
|
||||
void MainWindow::showEntryContextMenu(const QPoint& globalPos)
|
||||
@@ -456,3 +489,19 @@ void MainWindow::rememberOpenDatabases(const QString& filePath)
|
||||
{
|
||||
m_openDatabases.append(filePath);
|
||||
}
|
||||
|
||||
void MainWindow::applySettingsChanges()
|
||||
{
|
||||
int timeout = config()->get("security/lockdatabaseidlesec").toInt() * 1000;
|
||||
if (timeout <= 0) {
|
||||
timeout = 60;
|
||||
}
|
||||
|
||||
m_inactivityTimer->setInactivityTimeout(timeout);
|
||||
if (config()->get("security/lockdatabaseidle").toBool()) {
|
||||
m_inactivityTimer->activate();
|
||||
}
|
||||
else {
|
||||
m_inactivityTimer->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class InactivityTimer;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -58,18 +60,23 @@ private Q_SLOTS:
|
||||
void showGroupContextMenu(const QPoint& globalPos);
|
||||
void saveToolbarState(bool value);
|
||||
void rememberOpenDatabases(const QString& filePath);
|
||||
void applySettingsChanges();
|
||||
|
||||
private:
|
||||
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
|
||||
|
||||
static const QString BaseWindowTitle;
|
||||
|
||||
void saveWindowInformation();
|
||||
bool saveLastDatabases();
|
||||
|
||||
const QScopedPointer<Ui::MainWindow> m_ui;
|
||||
SignalMultiplexer m_actionMultiplexer;
|
||||
QAction* m_clearHistoryAction;
|
||||
QActionGroup* m_lastDatabasesActions;
|
||||
QActionGroup* m_copyAdditionalAttributeActions;
|
||||
QStringList m_openDatabases;
|
||||
InactivityTimer* m_inactivityTimer;
|
||||
|
||||
Q_DISABLE_COPY(MainWindow)
|
||||
};
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
<addaction name="menuEntryCopyAttribute"/>
|
||||
<addaction name="actionEntryAutoType"/>
|
||||
<addaction name="actionEntryOpenUrl"/>
|
||||
<addaction name="actionSearch"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuGroups">
|
||||
<property name="title">
|
||||
@@ -165,7 +166,6 @@
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionDatabaseNew"/>
|
||||
<addaction name="actionDatabaseOpen"/>
|
||||
<addaction name="actionDatabaseSave"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -173,6 +173,9 @@
|
||||
<addaction name="actionEntryEdit"/>
|
||||
<addaction name="actionEntryDelete"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEntryCopyUsername"/>
|
||||
<addaction name="actionEntryCopyPassword"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLockDatabases"/>
|
||||
<addaction name="actionSearch"/>
|
||||
</widget>
|
||||
@@ -318,6 +321,9 @@
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy username to clipboard</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyPassword">
|
||||
<property name="enabled">
|
||||
@@ -326,6 +332,9 @@
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy password to clipboard</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="text">
|
||||
|
||||
92
src/gui/PasswordComboBox.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Michael Curtis <michael@moltenmercury.org>
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 "PasswordComboBox.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "core/PasswordGenerator.h"
|
||||
|
||||
PasswordComboBox::PasswordComboBox(QWidget* parent)
|
||||
: QComboBox(parent)
|
||||
, m_generator(Q_NULLPTR)
|
||||
, m_alternatives(10)
|
||||
{
|
||||
setEditable(true);
|
||||
setEcho(false);
|
||||
}
|
||||
|
||||
PasswordComboBox::~PasswordComboBox()
|
||||
{
|
||||
}
|
||||
|
||||
void PasswordComboBox::setEcho(bool echo)
|
||||
{
|
||||
lineEdit()->setEchoMode(echo ? QLineEdit::Normal : QLineEdit::Password);
|
||||
|
||||
QString current = currentText();
|
||||
|
||||
if (echo) {
|
||||
// add fake item to show visual indication that a popup is available
|
||||
addItem("");
|
||||
|
||||
setStyleSheet("QComboBox { font-family: monospace; }");
|
||||
}
|
||||
else {
|
||||
// clear items so the combobox indicates that no popup menu is available
|
||||
clear();
|
||||
|
||||
setStyleSheet("QComboBox { font-family: initial; }");
|
||||
}
|
||||
|
||||
setEditText(current);
|
||||
}
|
||||
|
||||
void PasswordComboBox::setGenerator(PasswordGenerator* generator)
|
||||
{
|
||||
m_generator = generator;
|
||||
}
|
||||
|
||||
void PasswordComboBox::setNumberAlternatives(int alternatives)
|
||||
{
|
||||
m_alternatives = alternatives;
|
||||
}
|
||||
|
||||
void PasswordComboBox::showPopup()
|
||||
{
|
||||
// no point in showing a bunch of hidden passwords
|
||||
if (lineEdit()->echoMode() == QLineEdit::Password) {
|
||||
hidePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
// keep existing password as the first item in the popup
|
||||
QString current = currentText();
|
||||
clear();
|
||||
addItem(current);
|
||||
|
||||
if (m_generator && m_generator->isValid()) {
|
||||
for (int alternative = 0; alternative < m_alternatives; alternative++) {
|
||||
QString password = m_generator->generatePassword();
|
||||
|
||||
addItem(password);
|
||||
}
|
||||
}
|
||||
|
||||
QComboBox::showPopup();
|
||||
}
|
||||
48
src/gui/PasswordComboBox.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Michael Curtis <michael@moltenmercury.org>
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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_PASSWORDCOMBOBOX_H
|
||||
#define KEEPASSX_PASSWORDCOMBOBOX_H
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
#include "core/Global.h"
|
||||
|
||||
class PasswordGenerator;
|
||||
|
||||
class PasswordComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PasswordComboBox(QWidget* parent = Q_NULLPTR);
|
||||
~PasswordComboBox();
|
||||
|
||||
void setGenerator(PasswordGenerator* generator);
|
||||
void setNumberAlternatives(int alternatives);
|
||||
void showPopup();
|
||||
|
||||
public Q_SLOTS:
|
||||
void setEcho(bool echo);
|
||||
|
||||
private:
|
||||
PasswordGenerator* m_generator;
|
||||
int m_alternatives;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_PASSWORDCOMBOBOX_H
|
||||
75
src/gui/PasswordEdit.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 "PasswordEdit.h"
|
||||
|
||||
#include "core/Global.h"
|
||||
|
||||
const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15);
|
||||
const QColor PasswordEdit::ErrorColor = QColor(255, 125, 125);
|
||||
|
||||
PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
: QLineEdit(parent)
|
||||
, m_basePasswordEdit(Q_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
void PasswordEdit::enableVerifyMode(PasswordEdit* basePasswordEdit)
|
||||
{
|
||||
m_basePasswordEdit = basePasswordEdit;
|
||||
|
||||
updateStylesheet();
|
||||
connect(m_basePasswordEdit, SIGNAL(textChanged(QString)), SLOT(updateStylesheet()));
|
||||
connect(this, SIGNAL(textChanged(QString)), SLOT(updateStylesheet()));
|
||||
|
||||
connect(m_basePasswordEdit, SIGNAL(showPasswordChanged(bool)), SLOT(setShowPassword(bool)));
|
||||
}
|
||||
|
||||
void PasswordEdit::setShowPassword(bool show)
|
||||
{
|
||||
setEchoMode(show ? QLineEdit::Normal : QLineEdit::Password);
|
||||
updateStylesheet();
|
||||
Q_EMIT showPasswordChanged(show);
|
||||
}
|
||||
|
||||
bool PasswordEdit::passwordsEqual() const
|
||||
{
|
||||
return text() == m_basePasswordEdit->text();
|
||||
}
|
||||
|
||||
void PasswordEdit::updateStylesheet()
|
||||
{
|
||||
QString stylesheet("QLineEdit { ");
|
||||
|
||||
if (echoMode() == QLineEdit::Normal) {
|
||||
stylesheet.append("font-family: monospace; ");
|
||||
}
|
||||
|
||||
if (m_basePasswordEdit && !passwordsEqual()) {
|
||||
stylesheet.append("background: %1; ");
|
||||
|
||||
if (m_basePasswordEdit->text().startsWith(text())) {
|
||||
stylesheet = stylesheet.arg(CorrectSoFarColor.name());
|
||||
}
|
||||
else {
|
||||
stylesheet = stylesheet.arg(ErrorColor.name());
|
||||
}
|
||||
}
|
||||
|
||||
stylesheet.append("}");
|
||||
setStyleSheet(stylesheet);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Florian Geyer <blueice@fobos.de>
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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
|
||||
@@ -15,23 +15,37 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_ARGUMENTPARSER_H
|
||||
#define KEEPASSX_ARGUMENTPARSER_H
|
||||
#ifndef KEEPASSX_PASSWORDEDIT_H
|
||||
#define KEEPASSX_PASSWORDEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "core/Global.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
|
||||
class ArgumentParser
|
||||
class PasswordEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QHash<QString, QString> parseArguments(const QStringList& args);
|
||||
static const QColor CorrectSoFarColor;
|
||||
static const QColor ErrorColor;
|
||||
|
||||
explicit PasswordEdit(QWidget* parent = Q_NULLPTR);
|
||||
void enableVerifyMode(PasswordEdit* baseEdit);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setShowPassword(bool show);
|
||||
|
||||
Q_SIGNALS:
|
||||
void showPasswordChanged(bool show);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateStylesheet();
|
||||
|
||||
private:
|
||||
static void parseOption(int& i, QHash<QString, QString>& argumentMap, const QStringList& args);
|
||||
bool passwordsEqual() const;
|
||||
|
||||
static const QStringList ArgumentKeys;
|
||||
PasswordEdit* m_basePasswordEdit;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_ARGUMENTPARSER_H
|
||||
#endif // KEEPASSX_PASSWORDEDIT_H
|
||||
@@ -18,22 +18,36 @@
|
||||
#include "PasswordGeneratorWidget.h"
|
||||
#include "ui_PasswordGeneratorWidget.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/PasswordGenerator.h"
|
||||
#include "core/FilePath.h"
|
||||
|
||||
PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_updatingSpinBox(false)
|
||||
, m_generator(new PasswordGenerator())
|
||||
, m_ui(new Ui::PasswordGeneratorWidget())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updateApplyEnabled(QString)));
|
||||
connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(togglePassword(bool)));
|
||||
connect(m_ui->buttonGenerate, SIGNAL(clicked()), SLOT(generatePassword()));
|
||||
m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
|
||||
|
||||
connect(m_ui->editNewPassword->lineEdit(), SIGNAL(textChanged(QString)), SLOT(updateApplyEnabled(QString)));
|
||||
connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->editNewPassword, SLOT(setEcho(bool)));
|
||||
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(emitNewPassword()));
|
||||
connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(saveSettings()));
|
||||
|
||||
reset();
|
||||
connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(sliderMoved()));
|
||||
connect(m_ui->spinBoxLength, SIGNAL(valueChanged(int)), SLOT(spinBoxChanged()));
|
||||
|
||||
connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator()));
|
||||
|
||||
m_ui->editNewPassword->setGenerator(m_generator.data());
|
||||
|
||||
loadSettings();
|
||||
reset();
|
||||
}
|
||||
|
||||
PasswordGeneratorWidget::~PasswordGeneratorWidget()
|
||||
@@ -68,8 +82,10 @@ void PasswordGeneratorWidget::saveSettings()
|
||||
|
||||
void PasswordGeneratorWidget::reset()
|
||||
{
|
||||
m_ui->editNewPassword->setText("");
|
||||
m_ui->togglePasswordButton->setChecked(true);
|
||||
m_ui->editNewPassword->lineEdit()->setText("");
|
||||
m_ui->togglePasswordButton->setChecked(config()->get("security/passwordscleartext").toBool());
|
||||
|
||||
updateGenerator();
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::updateApplyEnabled(const QString& password)
|
||||
@@ -77,29 +93,32 @@ void PasswordGeneratorWidget::updateApplyEnabled(const QString& password)
|
||||
m_ui->buttonApply->setEnabled(!password.isEmpty());
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::togglePassword(bool checked)
|
||||
void PasswordGeneratorWidget::emitNewPassword()
|
||||
{
|
||||
m_ui->editNewPassword->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
Q_EMIT newPassword(m_ui->editNewPassword->lineEdit()->text());
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::generatePassword()
|
||||
void PasswordGeneratorWidget::sliderMoved()
|
||||
{
|
||||
int length = m_ui->spinBoxLength->value();
|
||||
PasswordGenerator::CharClasses classes = charClasses();
|
||||
PasswordGenerator::GeneratorFlags flags = generatorFlags();
|
||||
|
||||
if (!passwordGenerator()->isValidCombination(length, classes, flags)) {
|
||||
// TODO: display error
|
||||
if (m_updatingSpinBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString password = passwordGenerator()->generatePassword(length, classes, flags);
|
||||
m_ui->editNewPassword->setText(password);
|
||||
m_ui->spinBoxLength->setValue(m_ui->sliderLength->value());
|
||||
|
||||
updateGenerator();
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::emitNewPassword()
|
||||
void PasswordGeneratorWidget::spinBoxChanged()
|
||||
{
|
||||
Q_EMIT newPassword(m_ui->editNewPassword->text());
|
||||
// Interlock so that we don't update twice - this causes issues as the spinbox can go higher than slider
|
||||
m_updatingSpinBox = true;
|
||||
|
||||
m_ui->sliderLength->setValue(m_ui->spinBoxLength->value());
|
||||
|
||||
m_updatingSpinBox = false;
|
||||
|
||||
updateGenerator();
|
||||
}
|
||||
|
||||
PasswordGenerator::CharClasses PasswordGeneratorWidget::charClasses()
|
||||
@@ -139,3 +158,15 @@ PasswordGenerator::GeneratorFlags PasswordGeneratorWidget::generatorFlags()
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::updateGenerator()
|
||||
{
|
||||
m_generator->setLength(m_ui->spinBoxLength->value());
|
||||
m_generator->setCharClasses(charClasses());
|
||||
m_generator->setFlags(generatorFlags());
|
||||
|
||||
if (m_generator->isValid()) {
|
||||
QString password = m_generator->generatePassword();
|
||||
m_ui->editNewPassword->setEditText(password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define KEEPASSX_PASSWORDGENERATORWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "core/Global.h"
|
||||
#include "core/PasswordGenerator.h"
|
||||
@@ -27,6 +28,8 @@ namespace Ui {
|
||||
class PasswordGeneratorWidget;
|
||||
}
|
||||
|
||||
class PasswordGenerator;
|
||||
|
||||
class PasswordGeneratorWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -42,15 +45,21 @@ Q_SIGNALS:
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateApplyEnabled(const QString& password);
|
||||
void togglePassword(bool checked);
|
||||
void generatePassword();
|
||||
|
||||
void emitNewPassword();
|
||||
void saveSettings();
|
||||
void sliderMoved();
|
||||
void spinBoxChanged();
|
||||
|
||||
void updateGenerator();
|
||||
|
||||
private:
|
||||
bool m_updatingSpinBox;
|
||||
|
||||
PasswordGenerator::CharClasses charClasses();
|
||||
PasswordGenerator::GeneratorFlags generatorFlags();
|
||||
|
||||
const QScopedPointer<PasswordGenerator> m_generator;
|
||||
const QScopedPointer<Ui::PasswordGeneratorWidget> m_ui;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,93 +6,69 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>468</width>
|
||||
<height>298</height>
|
||||
<width>434</width>
|
||||
<height>250</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelGroups">
|
||||
<property name="text">
|
||||
<string>Use the following password groups:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxLower">
|
||||
<widget class="QLabel" name="labelNewPassword">
|
||||
<property name="text">
|
||||
<string>Lower letters</string>
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="checkBoxNumbers">
|
||||
<property name="text">
|
||||
<string>Numbers</string>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="PasswordComboBox" name="editNewPassword">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="togglePasswordButton">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxUpper">
|
||||
<widget class="QLabel" name="labelLength">
|
||||
<property name="text">
|
||||
<string>Upper letters</string>
|
||||
<string>Length:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="checkBoxSpecialChars">
|
||||
<property name="text">
|
||||
<string>Special characters</string>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QSlider" name="sliderLength">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxExcludeAlike">
|
||||
<property name="text">
|
||||
<string>Exclude look-alike characters</string>
|
||||
<property name="maximum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxEnsureEvery">
|
||||
<property name="text">
|
||||
<string>Ensure that the password contains characters from every group</string>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelLength">
|
||||
<property name="text">
|
||||
<string>Length:</string>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -106,8 +82,84 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Character Types</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QToolButton" name="checkBoxUpper">
|
||||
<property name="toolTip">
|
||||
<string>Upper Case Letters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>A-Z</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="checkBoxLower">
|
||||
<property name="toolTip">
|
||||
<string>Lower Case Letters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>a-z</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="checkBoxNumbers">
|
||||
<property name="toolTip">
|
||||
<string>Numbers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0-9</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="checkBoxSpecialChars">
|
||||
<property name="toolTip">
|
||||
<string>Special Characters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>/*_& ...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
@@ -121,41 +173,43 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxExcludeAlike">
|
||||
<property name="text">
|
||||
<string>Exclude look-alike characters</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxEnsureEvery">
|
||||
<property name="text">
|
||||
<string>Ensure that the password contains characters from every group</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">optionButtons</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelNewPassword">
|
||||
<property name="text">
|
||||
<string>New password:</string>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="editNewPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="togglePasswordButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonGenerate">
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonApply">
|
||||
@@ -163,7 +217,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
<string>Accept</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -171,19 +225,33 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PasswordComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header location="global">gui/PasswordComboBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>editNewPassword</tabstop>
|
||||
<tabstop>togglePasswordButton</tabstop>
|
||||
<tabstop>sliderLength</tabstop>
|
||||
<tabstop>spinBoxLength</tabstop>
|
||||
<tabstop>checkBoxUpper</tabstop>
|
||||
<tabstop>checkBoxLower</tabstop>
|
||||
<tabstop>checkBoxNumbers</tabstop>
|
||||
<tabstop>checkBoxUpper</tabstop>
|
||||
<tabstop>checkBoxSpecialChars</tabstop>
|
||||
<tabstop>checkBoxExcludeAlike</tabstop>
|
||||
<tabstop>checkBoxEnsureEvery</tabstop>
|
||||
<tabstop>spinBoxLength</tabstop>
|
||||
<tabstop>editNewPassword</tabstop>
|
||||
<tabstop>togglePasswordButton</tabstop>
|
||||
<tabstop>buttonGenerate</tabstop>
|
||||
<tabstop>buttonApply</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="optionButtons">
|
||||
<property name="exclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</buttongroup>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -28,6 +28,8 @@ SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
, m_generalWidget(new QWidget())
|
||||
, m_secUi(new Ui::SettingsWidgetSecurity())
|
||||
, m_generalUi(new Ui::SettingsWidgetGeneral())
|
||||
, m_globalAutoTypeKey(static_cast<Qt::Key>(0))
|
||||
, m_globalAutoTypeModifiers(Qt::NoModifier)
|
||||
{
|
||||
setHeadline(tr("Application Settings"));
|
||||
|
||||
@@ -47,6 +49,8 @@ SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
|
||||
connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)),
|
||||
m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool)));
|
||||
connect(m_secUi->lockDatabaseIdleCheckBox, SIGNAL(toggled(bool)),
|
||||
m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool)));
|
||||
}
|
||||
|
||||
SettingsWidget::~SettingsWidget()
|
||||
@@ -74,24 +78,38 @@ void SettingsWidget::loadSettings()
|
||||
m_secUi->clearClipboardCheckBox->setChecked(config()->get("security/clearclipboard").toBool());
|
||||
m_secUi->clearClipboardSpinBox->setValue(config()->get("security/clearclipboardtimeout").toInt());
|
||||
|
||||
m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get("security/lockdatabaseidle").toBool());
|
||||
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
|
||||
|
||||
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
|
||||
|
||||
setCurrentRow(0);
|
||||
}
|
||||
|
||||
void SettingsWidget::saveSettings()
|
||||
{
|
||||
config()->set("RememberLastDatabases", m_generalUi->rememberLastDatabasesCheckBox->isChecked());
|
||||
config()->set("OpenPreviousDatabasesOnStartup", m_generalUi->openPreviousDatabasesOnStartupCheckBox->isChecked());
|
||||
config()->set("ModifiedOnExpandedStateChanges", m_generalUi->modifiedExpandedChangedCheckBox->isChecked());
|
||||
config()->set("AutoSaveAfterEveryChange", m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked());
|
||||
config()->set("OpenPreviousDatabasesOnStartup",
|
||||
m_generalUi->openPreviousDatabasesOnStartupCheckBox->isChecked());
|
||||
config()->set("ModifiedOnExpandedStateChanges",
|
||||
m_generalUi->modifiedExpandedChangedCheckBox->isChecked());
|
||||
config()->set("AutoSaveAfterEveryChange",
|
||||
m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked());
|
||||
config()->set("AutoSaveOnExit", m_generalUi->autoSaveOnExitCheckBox->isChecked());
|
||||
config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked());
|
||||
if (autoType()->isAvailable()) {
|
||||
config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key());
|
||||
config()->set("GlobalAutoTypeModifiers", static_cast<int>(m_generalUi->autoTypeShortcutWidget->modifiers()));
|
||||
config()->set("GlobalAutoTypeModifiers",
|
||||
static_cast<int>(m_generalUi->autoTypeShortcutWidget->modifiers()));
|
||||
}
|
||||
config()->set("security/clearclipboard", m_secUi->clearClipboardCheckBox->isChecked());
|
||||
config()->set("security/clearclipboardtimeout", m_secUi->clearClipboardSpinBox->value());
|
||||
|
||||
config()->set("security/lockdatabaseidle", m_secUi->lockDatabaseIdleCheckBox->isChecked());
|
||||
config()->set("security/lockdatabaseidlesec", m_secUi->lockDatabaseIdleSpinBox->value());
|
||||
|
||||
config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked());
|
||||
|
||||
Q_EMIT editFinished(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
<property name="text">
|
||||
<string>Clear clipboard after</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="clearClipboardSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> sec</string>
|
||||
</property>
|
||||
@@ -34,6 +34,36 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="lockDatabaseIdleCheckBox">
|
||||
<property name="text">
|
||||
<string>Lock databases after inactivity of</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="lockDatabaseIdleSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> sec</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="passwordCleartextCheckBox">
|
||||
<property name="text">
|
||||
<string>Show passwords in cleartext by default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/Config.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/TimeDelta.h"
|
||||
#include "core/Tools.h"
|
||||
@@ -80,19 +81,16 @@ EditEntryWidget::~EditEntryWidget()
|
||||
{
|
||||
}
|
||||
|
||||
const QColor EditEntryWidget::CorrectSoFarColor = QColor(255, 205, 15);
|
||||
const QColor EditEntryWidget::ErrorColor = QColor(255, 125, 125);
|
||||
|
||||
void EditEntryWidget::setupMain()
|
||||
{
|
||||
m_mainUi->setupUi(m_mainWidget);
|
||||
add(tr("Entry"), m_mainWidget);
|
||||
|
||||
connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(togglePassword(bool)));
|
||||
m_mainUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
|
||||
connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), m_mainUi->passwordEdit, SLOT(setShowPassword(bool)));
|
||||
connect(m_mainUi->tooglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool)));
|
||||
connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool)));
|
||||
connect(m_mainUi->passwordEdit, SIGNAL(textEdited(QString)), SLOT(setPasswordCheckColors()));
|
||||
connect(m_mainUi->passwordRepeatEdit, SIGNAL(textEdited(QString)), SLOT(setPasswordCheckColors()));
|
||||
m_mainUi->passwordRepeatEdit->enableVerifyMode(m_mainUi->passwordEdit);
|
||||
connect(m_mainUi->passwordGenerator, SIGNAL(newPassword(QString)), SLOT(setGeneratedPassword(QString)));
|
||||
|
||||
m_mainUi->expirePresets->setMenu(createPresetsMenu());
|
||||
@@ -307,11 +305,10 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
||||
m_mainUi->urlEdit->setText(entry->url());
|
||||
m_mainUi->passwordEdit->setText(entry->password());
|
||||
m_mainUi->passwordRepeatEdit->setText(entry->password());
|
||||
setPasswordCheckColors();
|
||||
m_mainUi->expireCheck->setChecked(entry->timeInfo().expires());
|
||||
m_mainUi->expireDatePicker->setDateTime(entry->timeInfo().expiryTime().toLocalTime());
|
||||
m_mainUi->expirePresets->setEnabled(!m_history);
|
||||
m_mainUi->togglePasswordButton->setChecked(true);
|
||||
m_mainUi->togglePasswordButton->setChecked(config()->get("security/passwordscleartext").toBool());
|
||||
|
||||
m_mainUi->notesEdit->setPlainText(entry->notes());
|
||||
|
||||
@@ -478,12 +475,6 @@ void EditEntryWidget::cancel()
|
||||
Q_EMIT editFinished(false);
|
||||
}
|
||||
|
||||
void EditEntryWidget::togglePassword(bool checked)
|
||||
{
|
||||
m_mainUi->passwordEdit->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
m_mainUi->passwordRepeatEdit->setEchoMode(checked ? QLineEdit::Password : QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void EditEntryWidget::togglePasswordGeneratorButton(bool checked)
|
||||
{
|
||||
m_mainUi->passwordGenerator->setVisible(checked);
|
||||
@@ -494,25 +485,6 @@ bool EditEntryWidget::passwordsEqual()
|
||||
return m_mainUi->passwordEdit->text() == m_mainUi->passwordRepeatEdit->text();
|
||||
}
|
||||
|
||||
void EditEntryWidget::setPasswordCheckColors()
|
||||
{
|
||||
if (passwordsEqual()) {
|
||||
m_mainUi->passwordRepeatEdit->setStyleSheet("");
|
||||
}
|
||||
else {
|
||||
QString stylesheet = "QLineEdit { background: %1; }";
|
||||
|
||||
if (m_mainUi->passwordEdit->text().startsWith(m_mainUi->passwordRepeatEdit->text())) {
|
||||
stylesheet = stylesheet.arg(CorrectSoFarColor.name());
|
||||
}
|
||||
else {
|
||||
stylesheet = stylesheet.arg(ErrorColor.name());
|
||||
}
|
||||
|
||||
m_mainUi->passwordRepeatEdit->setStyleSheet(stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::setGeneratedPassword(const QString& password)
|
||||
{
|
||||
m_mainUi->passwordEdit->setText(password);
|
||||
|
||||
@@ -58,9 +58,6 @@ public:
|
||||
void loadEntry(Entry* entry, bool create, bool history, const QString& parentName,
|
||||
Database* database);
|
||||
|
||||
static const QColor CorrectSoFarColor;
|
||||
static const QColor ErrorColor;
|
||||
|
||||
void createPresetsMenu(QMenu* expirePresetsMenu);
|
||||
QString entryTitle() const;
|
||||
|
||||
@@ -71,9 +68,7 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void saveEntry();
|
||||
void cancel();
|
||||
void togglePassword(bool checked);
|
||||
void togglePasswordGeneratorButton(bool checked);
|
||||
void setPasswordCheckColors();
|
||||
void setGeneratedPassword(const QString& password);
|
||||
void insertAttribute();
|
||||
void editCurrentAttribute();
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordEdit">
|
||||
<widget class="PasswordEdit" name="passwordEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
@@ -52,15 +52,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="togglePasswordButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -75,7 +69,7 @@
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordRepeatEdit">
|
||||
<widget class="PasswordEdit" name="passwordRepeatEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
@@ -93,10 +87,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="urlEdit"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="urlLabel">
|
||||
<property name="text">
|
||||
<string>URL:</string>
|
||||
@@ -104,7 +95,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="PasswordGeneratorWidget" name="passwordGenerator" native="true"/>
|
||||
<widget class="QLineEdit" name="urlEdit"/>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="expireCheck">
|
||||
@@ -151,6 +142,9 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="PasswordGeneratorWidget" name="passwordGenerator" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
@@ -160,6 +154,11 @@
|
||||
<header>gui/PasswordGeneratorWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PasswordEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/PasswordEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>titleEdit</tabstop>
|
||||
|
||||
43
src/main.cpp
@@ -17,8 +17,9 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "core/ArgumentParser.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Config.h"
|
||||
#include "core/qcommandlineparser.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "gui/Application.h"
|
||||
@@ -31,16 +32,38 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
|
||||
Application app(argc, argv);
|
||||
// don't set applicationName or organizationName as that changes
|
||||
// QDesktopServices::storageLocation()
|
||||
Application::setApplicationName("keepassx");
|
||||
Application::setApplicationVersion(KEEPASSX_VERSION);
|
||||
// don't set organizationName as that changes the return value of
|
||||
// QDesktopServices::storageLocation(QDesktopServices::DataLocation)
|
||||
|
||||
Crypto::init();
|
||||
|
||||
const QStringList args = app.arguments();
|
||||
QHash<QString, QString> argumentMap = ArgumentParser::parseArguments(args);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassX - cross-platform password manager"));
|
||||
parser.addPositionalArgument("filename", QCoreApplication::translate("main", "filename of the password database to open (*.kdbx)"));
|
||||
|
||||
if (!argumentMap.value("config").isEmpty()) {
|
||||
Config::createConfigFromFile(argumentMap.value("config"));
|
||||
QCommandLineOption configOption("config",
|
||||
QCoreApplication::translate("main", "path to a custom config file"),
|
||||
"config");
|
||||
QCommandLineOption passwordOption("password",
|
||||
QCoreApplication::translate("main", "password of the database (DANGEROUS!)"),
|
||||
"password");
|
||||
QCommandLineOption keyfileOption("keyfile",
|
||||
QCoreApplication::translate("main", "key file of the database"),
|
||||
"keyfile");
|
||||
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addOption(configOption);
|
||||
parser.addOption(passwordOption);
|
||||
parser.addOption(keyfileOption);
|
||||
|
||||
parser.process(app);
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
||||
if (parser.isSet(configOption)) {
|
||||
Config::createConfigFromFile(parser.value(configOption));
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
@@ -53,9 +76,11 @@ int main(int argc, char** argv)
|
||||
|
||||
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
||||
|
||||
QString filename(argumentMap.value("filename"));
|
||||
if (!args.isEmpty()) {
|
||||
QString filename = args[0];
|
||||
if (!filename.isEmpty() && QFile::exists(filename)) {
|
||||
mainWindow.openDatabase(filename, argumentMap.value("password"), QString());
|
||||
mainWindow.openDatabase(filename, parser.value(passwordOption), parser.value(keyfileOption));
|
||||
}
|
||||
}
|
||||
|
||||
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
||||
|
||||
@@ -159,7 +159,7 @@ set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON)
|
||||
add_unit_test(NAME testentry SOURCES TestEntry.cpp MOCS TestEntry.h
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testargumentparser SOURCES TestArgumentParser.cpp MOCS TestArgumentParser.h
|
||||
add_unit_test(NAME testqcommandlineparser SOURCES TestQCommandLineParser.cpp MOCS TestQCommandLineParser.h
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testrandom SOURCES TestRandom.cpp MOCS TestRandom.h
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 "TestArgumentParser.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "tests.h"
|
||||
#include "core/ArgumentParser.h"
|
||||
|
||||
// TODO: test qWarning with own message handler?
|
||||
|
||||
void TestArgumentParser::testNoArguments()
|
||||
{
|
||||
parse(QStringList());
|
||||
|
||||
QVERIFY(argumentMap.isEmpty());
|
||||
}
|
||||
|
||||
void TestArgumentParser::testMissingOptionValue()
|
||||
{
|
||||
parse(QStringList() << "--foo");
|
||||
|
||||
QVERIFY(argumentMap.isEmpty());
|
||||
}
|
||||
|
||||
void TestArgumentParser::testUnknownArgument()
|
||||
{
|
||||
parse(QStringList() << "--foo" << "bar");
|
||||
|
||||
QVERIFY(argumentMap.isEmpty());
|
||||
}
|
||||
|
||||
void TestArgumentParser::testFilename()
|
||||
{
|
||||
parse(QStringList() << "--filename" << "foo");
|
||||
|
||||
QCOMPARE(argumentMap.size(), 1);
|
||||
QCOMPARE(argumentMap.value("filename"), QString("foo"));
|
||||
}
|
||||
|
||||
void TestArgumentParser::testMultipleArguments()
|
||||
{
|
||||
parse(QStringList() << "--config" << "myconfig.ini" << "--filename" << "myfilename"
|
||||
<< "--password" << "mypassword");
|
||||
|
||||
QCOMPARE(argumentMap.size(), 3);
|
||||
QCOMPARE(argumentMap.value("config"), QString("myconfig.ini"));
|
||||
QCOMPARE(argumentMap.value("filename"), QString("myfilename"));
|
||||
QCOMPARE(argumentMap.value("password"), QString("mypassword"));
|
||||
}
|
||||
|
||||
void TestArgumentParser::testFilenameWithoutOption()
|
||||
{
|
||||
parse(QStringList() << "foo");
|
||||
|
||||
QCOMPARE(argumentMap.size(), 1);
|
||||
QCOMPARE(argumentMap.value("filename"), QString("foo"));
|
||||
}
|
||||
|
||||
void TestArgumentParser::parse(const QStringList& arguments)
|
||||
{
|
||||
argumentMap = ArgumentParser::parseArguments(QStringList() << "keepassx" << arguments);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestArgumentParser)
|
||||
@@ -165,4 +165,24 @@ void TestKeys::testFileKeyError()
|
||||
errorMsg = "";
|
||||
}
|
||||
|
||||
void TestKeys::benchmarkTransformKey()
|
||||
{
|
||||
QByteArray env = qgetenv("BENCHMARK");
|
||||
|
||||
if (env.isEmpty() || env == "0" || env == "no") {
|
||||
QSKIP("Benchmark skipped. Set env variable BENCHMARK=1 to enable.", SkipAll);
|
||||
}
|
||||
|
||||
PasswordKey pwKey;
|
||||
pwKey.setPassword("password");
|
||||
CompositeKey compositeKey;
|
||||
compositeKey.addKey(pwKey);
|
||||
|
||||
QByteArray seed(32, '\x4B');
|
||||
|
||||
QBENCHMARK {
|
||||
compositeKey.transform(seed, 1e6);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKeys)
|
||||
|
||||
@@ -31,6 +31,7 @@ private Q_SLOTS:
|
||||
void testFileKey_data();
|
||||
void testCreateFileKey();
|
||||
void testFileKeyError();
|
||||
void benchmarkTransformKey();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTKEYS_H
|
||||
|
||||
416
tests/TestQCommandLineParser.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 David Faure <faure@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "TestQCommandLineParser.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
#include "tests.h"
|
||||
#include "core/qcommandlineparser.h"
|
||||
|
||||
Q_DECLARE_METATYPE(char**)
|
||||
|
||||
static char *empty_argv[] = { 0 };
|
||||
static int empty_argc = 1;
|
||||
|
||||
void TestQCommandLineParser::initTestCase()
|
||||
{
|
||||
Q_ASSERT(!empty_argv[0]);
|
||||
empty_argv[0] = const_cast<char*>("TestQCommandLineParser");
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QCommandLineParser::SingleDashWordOptionMode)
|
||||
|
||||
void TestQCommandLineParser::parsingModes_data()
|
||||
{
|
||||
QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
|
||||
|
||||
QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions;
|
||||
QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions;
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testInvalidOptions()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineOption: Option names cannot start with a '-'");
|
||||
parser.addOption(QCommandLineOption("-v", "Displays version information."));
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testPositionalArguments()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "file.txt"));
|
||||
QCOMPARE(parser.positionalArguments(), QStringList() << "file.txt");
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testBooleanOption_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("args");
|
||||
QTest::addColumn<QStringList>("expectedOptionNames");
|
||||
QTest::addColumn<bool>("expectedIsSet");
|
||||
|
||||
QTest::newRow("set") << (QStringList() << "TestQCommandLineParser" << "-b") << (QStringList() << "b") << true;
|
||||
QTest::newRow("unset") << (QStringList() << "TestQCommandLineParser") << QStringList() << false;
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testBooleanOption()
|
||||
{
|
||||
QFETCH(QStringList, args);
|
||||
QFETCH(QStringList, expectedOptionNames);
|
||||
QFETCH(bool, expectedIsSet);
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QVERIFY(parser.addOption(QCommandLineOption("b", "a boolean option")));
|
||||
QVERIFY(parser.parse(args));
|
||||
QCOMPARE(parser.optionNames(), expectedOptionNames);
|
||||
QCOMPARE(parser.isSet("b"), expectedIsSet);
|
||||
QCOMPARE(parser.values("b"), QStringList());
|
||||
QCOMPARE(parser.positionalArguments(), QStringList());
|
||||
// Should warn on typos
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\"");
|
||||
QVERIFY(!parser.isSet("c"));
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testMultipleNames_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("args");
|
||||
QTest::addColumn<QStringList>("expectedOptionNames");
|
||||
|
||||
QTest::newRow("short") << (QStringList() << "TestQCommandLineParser" << "-v") << (QStringList() << "v");
|
||||
QTest::newRow("long") << (QStringList() << "TestQCommandLineParser" << "--version") << (QStringList() << "version");
|
||||
QTest::newRow("not_set") << (QStringList() << "TestQCommandLineParser") << QStringList();
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testMultipleNames()
|
||||
{
|
||||
QFETCH(QStringList, args);
|
||||
QFETCH(QStringList, expectedOptionNames);
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineOption option(QStringList() << "v" << "version", "Show version information");
|
||||
QCOMPARE(option.names(), QStringList() << "v" << "version");
|
||||
QCommandLineParser parser;
|
||||
QVERIFY(parser.addOption(option));
|
||||
QVERIFY(parser.parse(args));
|
||||
QCOMPARE(parser.optionNames(), expectedOptionNames);
|
||||
const bool expectedIsSet = !expectedOptionNames.isEmpty();
|
||||
QCOMPARE(parser.isSet("v"), expectedIsSet);
|
||||
QCOMPARE(parser.isSet("version"), expectedIsSet);
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testSingleValueOption_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("args");
|
||||
QTest::addColumn<QStringList>("defaults");
|
||||
QTest::addColumn<bool>("expectedIsSet");
|
||||
|
||||
QTest::newRow("short") << (QStringList() << "tst" << "-s" << "oxygen") << QStringList() << true;
|
||||
QTest::newRow("long") << (QStringList() << "tst" << "--style" << "oxygen") << QStringList() << true;
|
||||
QTest::newRow("longequal") << (QStringList() << "tst" << "--style=oxygen") << QStringList() << true;
|
||||
QTest::newRow("default") << (QStringList() << "tst") << (QStringList() << "oxygen") << false;
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testSingleValueOption()
|
||||
{
|
||||
QFETCH(QStringList, args);
|
||||
QFETCH(QStringList, defaults);
|
||||
QFETCH(bool, expectedIsSet);
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption option(QStringList() << "s" << "style", "style name", "styleName");
|
||||
option.setDefaultValues(defaults);
|
||||
QVERIFY(parser.addOption(option));
|
||||
for (int mode = 0; mode < 2; ++mode) {
|
||||
parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(mode));
|
||||
QVERIFY(parser.parse(args));
|
||||
QCOMPARE(parser.isSet("s"), expectedIsSet);
|
||||
QCOMPARE(parser.isSet("style"), expectedIsSet);
|
||||
QCOMPARE(parser.isSet(option), expectedIsSet);
|
||||
QCOMPARE(parser.value("s"), QString("oxygen"));
|
||||
QCOMPARE(parser.value("style"), QString("oxygen"));
|
||||
QCOMPARE(parser.values("s"), QStringList() << "oxygen");
|
||||
QCOMPARE(parser.values("style"), QStringList() << "oxygen");
|
||||
QCOMPARE(parser.values(option), QStringList() << "oxygen");
|
||||
QCOMPARE(parser.positionalArguments(), QStringList());
|
||||
}
|
||||
// Should warn on typos
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\"");
|
||||
QVERIFY(parser.values("c").isEmpty());
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testValueNotSet()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
// Not set, no default value
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption option(QStringList() << "s" << "style", "style name");
|
||||
option.setValueName("styleName");
|
||||
QVERIFY(parser.addOption(option));
|
||||
QVERIFY(parser.parse(QStringList() << "tst"));
|
||||
QCOMPARE(parser.optionNames(), QStringList());
|
||||
QVERIFY(!parser.isSet("s"));
|
||||
QVERIFY(!parser.isSet("style"));
|
||||
QCOMPARE(parser.value("s"), QString());
|
||||
QCOMPARE(parser.value("style"), QString());
|
||||
QCOMPARE(parser.values("s"), QStringList());
|
||||
QCOMPARE(parser.values("style"), QStringList());
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testMultipleValuesOption()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineOption option("param", "Pass parameter to the backend.");
|
||||
option.setValueName("key=value");
|
||||
QCommandLineParser parser;
|
||||
QVERIFY(parser.addOption(option));
|
||||
{
|
||||
QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1"));
|
||||
QVERIFY(parser.isSet("param"));
|
||||
QCOMPARE(parser.values("param"), QStringList() << "key1=value1");
|
||||
QCOMPARE(parser.value("param"), QString("key1=value1"));
|
||||
}
|
||||
{
|
||||
QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1" << "--param" << "key2=value2"));
|
||||
QVERIFY(parser.isSet("param"));
|
||||
QCOMPARE(parser.values("param"), QStringList() << "key1=value1" << "key2=value2");
|
||||
QCOMPARE(parser.value("param"), QString("key2=value2"));
|
||||
}
|
||||
|
||||
QString expected =
|
||||
"Usage: TestQCommandLineParser [options]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --param <key=value> Pass parameter to the backend.\n";
|
||||
|
||||
const QString exeName = QCoreApplication::instance()->arguments().first(); // e.g. debug\tst_qcommandlineparser.exe on Windows
|
||||
expected.replace("TestQCommandLineParser", exeName);
|
||||
QCOMPARE(parser.helpText(), expected);
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testUnknownOptionErrorHandling_data()
|
||||
{
|
||||
QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
|
||||
QTest::addColumn<QStringList>("args");
|
||||
QTest::addColumn<QStringList>("expectedUnknownOptionNames");
|
||||
QTest::addColumn<QString>("expectedErrorText");
|
||||
|
||||
const QStringList args_hello = QStringList() << "TestQCommandLineParser" << "--hello";
|
||||
const QString error_hello("Unknown option 'hello'.");
|
||||
QTest::newRow("unknown_name_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_hello << QStringList("hello") << error_hello;
|
||||
QTest::newRow("unknown_name_long") << QCommandLineParser::ParseAsLongOptions << args_hello << QStringList("hello") << error_hello;
|
||||
|
||||
const QStringList args_value = QStringList() << "TestQCommandLineParser" << "-b=1";
|
||||
QTest::newRow("bool_with_value_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
|
||||
QTest::newRow("bool_with_value_long") << QCommandLineParser::ParseAsLongOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
|
||||
|
||||
const QStringList args_dash_long = QStringList() << "TestQCommandLineParser" << "-bool";
|
||||
const QString error_bool("Unknown options: o, o, l.");
|
||||
QTest::newRow("unknown_name_long_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_dash_long << (QStringList() << "o" << "o" << "l") << error_bool;
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testUnknownOptionErrorHandling()
|
||||
{
|
||||
QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
|
||||
QFETCH(QStringList, args);
|
||||
QFETCH(QStringList, expectedUnknownOptionNames);
|
||||
QFETCH(QString, expectedErrorText);
|
||||
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
parser.setSingleDashWordOptionMode(parsingMode);
|
||||
QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "b" << "bool", "a boolean option")));
|
||||
QCOMPARE(parser.parse(args), expectedErrorText.isEmpty());
|
||||
QCOMPARE(parser.unknownOptionNames(), expectedUnknownOptionNames);
|
||||
QCOMPARE(parser.errorText(), expectedErrorText);
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testDoubleDash_data()
|
||||
{
|
||||
parsingModes_data();
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testDoubleDash()
|
||||
{
|
||||
QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
|
||||
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
parser.addOption(QCommandLineOption(QStringList() << "o" << "output", "Output file", "filename"));
|
||||
parser.setSingleDashWordOptionMode(parsingMode);
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "--output" << "foo"));
|
||||
QCOMPARE(parser.value("output"), QString("foo"));
|
||||
QCOMPARE(parser.positionalArguments(), QStringList());
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "--" << "--output" << "bar" << "-b" << "bleh"));
|
||||
QCOMPARE(parser.value("output"), QString());
|
||||
QCOMPARE(parser.positionalArguments(), QStringList() << "--output" << "bar" << "-b" << "bleh");
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testDefaultValue()
|
||||
{
|
||||
QCommandLineOption opt("name", "desc",
|
||||
"valueName", "default");
|
||||
QCOMPARE(opt.defaultValues(), QStringList("default"));
|
||||
opt.setDefaultValue("");
|
||||
QCOMPARE(opt.defaultValues(), QStringList());
|
||||
opt.setDefaultValue("default");
|
||||
QCOMPARE(opt.defaultValues(), QStringList("default"));
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testProcessNotCalled()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QVERIFY(parser.addOption(QCommandLineOption("b", "a boolean option")));
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before isSet");
|
||||
QVERIFY(!parser.isSet("b"));
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before values");
|
||||
QCOMPARE(parser.values("b"), QStringList());
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testEmptyArgsList()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
|
||||
QVERIFY(!parser.parse(QStringList())); // invalid call, argv[0] is missing
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testMissingOptionValue()
|
||||
{
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
parser.addOption(QCommandLineOption("option", "An option", "value"));
|
||||
QVERIFY(!parser.parse(QStringList() << "argv0" << "--option")); // the user forgot to pass a value for --option
|
||||
QCOMPARE(parser.value("option"), QString());
|
||||
QCOMPARE(parser.errorText(), QString("Missing value after '--option'."));
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testStdinArgument_data()
|
||||
{
|
||||
parsingModes_data();
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testStdinArgument()
|
||||
{
|
||||
QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
|
||||
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
parser.setSingleDashWordOptionMode(parsingMode);
|
||||
parser.addOption(QCommandLineOption(QStringList() << "i" << "input", "Input file.", "filename"));
|
||||
parser.addOption(QCommandLineOption("b", "Boolean option."));
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "--input" << "-"));
|
||||
QCOMPARE(parser.value("input"), QString("-"));
|
||||
QCOMPARE(parser.positionalArguments(), QStringList());
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "--input" << "-" << "-b" << "arg"));
|
||||
QCOMPARE(parser.value("input"), QString("-"));
|
||||
QVERIFY(parser.isSet("b"));
|
||||
QCOMPARE(parser.positionalArguments(), QStringList() << "arg");
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
|
||||
QVERIFY(parser.parse(QStringList() << "TestQCommandLineParser" << "-"));
|
||||
QCOMPARE(parser.value("input"), QString());
|
||||
QVERIFY(!parser.isSet("b"));
|
||||
QCOMPARE(parser.positionalArguments(), QStringList() << "-");
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testSingleDashWordOptionModes_data()
|
||||
{
|
||||
QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
|
||||
QTest::addColumn<QStringList>("commandLine");
|
||||
QTest::addColumn<QStringList>("expectedOptionNames");
|
||||
QTest::addColumn<QStringList>("expectedOptionValues");
|
||||
|
||||
QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc" << "val")
|
||||
<< (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
|
||||
QTest::newRow("collapsed_with_equalsign_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc=val")
|
||||
<< (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
|
||||
QTest::newRow("collapsed_explicit_longoption") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("--nn")
|
||||
<< QStringList("nn") << QStringList();
|
||||
QTest::newRow("collapsed_longoption_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "--abc" << "val")
|
||||
<< QStringList("abc") << QStringList("val");
|
||||
QTest::newRow("compiler") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("-cab")
|
||||
<< QStringList("c") << QStringList("ab");
|
||||
QTest::newRow("compiler_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
|
||||
<< QStringList("c") << QStringList("val");
|
||||
|
||||
QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc" << "val")
|
||||
<< QStringList("abc") << QStringList("val");
|
||||
QTest::newRow("implicitlylong_equal") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc=val")
|
||||
<< QStringList("abc") << QStringList("val");
|
||||
QTest::newRow("implicitlylong_longoption") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--nn")
|
||||
<< QStringList("nn") << QStringList();
|
||||
QTest::newRow("implicitlylong_longoption_value") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--abc" << "val")
|
||||
<< QStringList("abc") << QStringList("val");
|
||||
QTest::newRow("implicitlylong_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
|
||||
<< QStringList("c") << QStringList("val");
|
||||
}
|
||||
|
||||
void TestQCommandLineParser::testSingleDashWordOptionModes()
|
||||
{
|
||||
QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
|
||||
QFETCH(QStringList, commandLine);
|
||||
QFETCH(QStringList, expectedOptionNames);
|
||||
QFETCH(QStringList, expectedOptionValues);
|
||||
|
||||
commandLine.prepend("TestQCommandLineParser");
|
||||
|
||||
QCoreApplication app(empty_argc, empty_argv);
|
||||
QCommandLineParser parser;
|
||||
parser.setSingleDashWordOptionMode(parsingMode);
|
||||
parser.addOption(QCommandLineOption("a", "a option."));
|
||||
parser.addOption(QCommandLineOption("b", "b option."));
|
||||
parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", "c option.", "value"));
|
||||
parser.addOption(QCommandLineOption("nn", "nn option."));
|
||||
QVERIFY(parser.parse(commandLine));
|
||||
QCOMPARE(parser.optionNames(), expectedOptionNames);
|
||||
for (int i = 0; i < expectedOptionValues.count(); ++i)
|
||||
QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i));
|
||||
QCOMPARE(parser.unknownOptionNames(), QStringList());
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestQCommandLineParser)
|
||||
82
tests/TestQCommandLineParser.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 David Faure <faure@kde.org>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef KEEPASSX_TESTQCOMMANDLINEPARSER_H
|
||||
#define KEEPASSX_TESTQCOMMANDLINEPARSER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestQCommandLineParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
private Q_SLOTS:
|
||||
void parsingModes_data();
|
||||
|
||||
// In-process tests
|
||||
void testInvalidOptions();
|
||||
void testPositionalArguments();
|
||||
void testBooleanOption_data();
|
||||
void testBooleanOption();
|
||||
void testMultipleNames_data();
|
||||
void testMultipleNames();
|
||||
void testSingleValueOption_data();
|
||||
void testSingleValueOption();
|
||||
void testValueNotSet();
|
||||
void testMultipleValuesOption();
|
||||
void testUnknownOptionErrorHandling_data();
|
||||
void testUnknownOptionErrorHandling();
|
||||
void testDoubleDash_data();
|
||||
void testDoubleDash();
|
||||
void testDefaultValue();
|
||||
void testProcessNotCalled();
|
||||
void testEmptyArgsList();
|
||||
void testMissingOptionValue();
|
||||
void testStdinArgument_data();
|
||||
void testStdinArgument();
|
||||
void testSingleDashWordOptionModes_data();
|
||||
void testSingleDashWordOptionModes();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTQCOMMANDLINEPARSER_H
|
||||