mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Merge branch 'release/2.3.0' into develop
This commit is contained in:
commit
b80ddef274
47
.travis.yml
47
.travis.yml
@ -1,47 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
# FIXME : remove when (https://github.com/google/sanitizers/issues/837) is resolved.
|
|
||||||
group: deprecated-2017Q3
|
|
||||||
services: [docker]
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
# - osx
|
|
||||||
|
|
||||||
# Define clang compiler without any frills
|
|
||||||
compiler:
|
|
||||||
- clang
|
|
||||||
- gcc
|
|
||||||
|
|
||||||
env:
|
|
||||||
- CONFIG=Release ASAN_OPTIONS=detect_odr_violation=1
|
|
||||||
- CONFIG=Debug ASAN_OPTIONS=detect_odr_violation=1
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: 3
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake3 libclang-common-3.5-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb libyubikey-dev libykpers-1-dev; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq cmake || brew install cmake; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq qt5 || brew install qt5; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq libgcrypt || brew install libgcrypt; fi
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then CMAKE_ARGS="-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5"; fi
|
|
||||||
- mkdir build && pushd build
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cmake -DCMAKE_BUILD_TYPE=${CONFIG} -DWITH_GUI_TESTS=ON -DWITH_ASAN=ON -DWITH_XC_HTTP=ON -DWITH_XC_AUTOTYPE=ON -DWITH_XC_YUBIKEY=ON -DWITH_XC_SSHAGENT=ON $CMAKE_ARGS ..
|
|
||||||
- make -j2
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ASAN_OPTIONS=${ASAN_OPTIONS}:leak_check_at_exit=0 xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test ARGS+="--output-on-failure"; fi
|
|
||||||
|
|
||||||
# Generate snapcraft build when merging into master/develop branches
|
|
||||||
#after_success:
|
|
||||||
# - popd
|
|
||||||
# - "[[ $DEPLOY = 1 ]] && [[ $CONFIG = Release ]] && [[ $TRAVIS_BRANCH =~ (master|develop) ]] && [[ $TRAVIS_PULL_REQUEST = false ]] \
|
|
||||||
# && docker run -v $(pwd):/cwd snapcore/snapcraft sh -c 'cd /cwd && apt update && snapcraft'"
|
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
|
|
||||||
ENV REBUILD_COUNTER=4
|
ENV REBUILD_COUNTER=5
|
||||||
|
|
||||||
ENV QT5_VERSION=59
|
ENV QT5_VERSION=59
|
||||||
ENV QT5_PPA_VERSION=${QT5_VERSION}2
|
ENV QT5_PPA_VERSION=${QT5_VERSION}2
|
||||||
@ -51,7 +51,8 @@ RUN set -x \
|
|||||||
libxtst-dev \
|
libxtst-dev \
|
||||||
mesa-common-dev \
|
mesa-common-dev \
|
||||||
libyubikey-dev \
|
libyubikey-dev \
|
||||||
libykpers-1-dev
|
libykpers-1-dev \
|
||||||
|
libcurl4-openssl-dev
|
||||||
|
|
||||||
ENV CMAKE_PREFIX_PATH="/opt/qt${QT5_VERSION}/lib/cmake"
|
ENV CMAKE_PREFIX_PATH="/opt/qt${QT5_VERSION}/lib/cmake"
|
||||||
ENV CMAKE_INCLUDE_PATH="/opt/libgcrypt20-18/include:/opt/gpg-error-127/include"
|
ENV CMAKE_INCLUDE_PATH="/opt/libgcrypt20-18/include:/opt/gpg-error-127/include"
|
||||||
|
@ -43,6 +43,7 @@ RUN set -x \
|
|||||||
libgcrypt20-18-dev \
|
libgcrypt20-18-dev \
|
||||||
libargon2-0-dev \
|
libargon2-0-dev \
|
||||||
libsodium-dev \
|
libsodium-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
qt${QT5_VERSION}base \
|
qt${QT5_VERSION}base \
|
||||||
qt${QT5_VERSION}tools \
|
qt${QT5_VERSION}tools \
|
||||||
qt${QT5_VERSION}x11extras \
|
qt${QT5_VERSION}x11extras \
|
||||||
|
@ -42,6 +42,7 @@ parts:
|
|||||||
- libxtst-dev
|
- libxtst-dev
|
||||||
- libyubikey-dev
|
- libyubikey-dev
|
||||||
- libykpers-1-dev
|
- libykpers-1-dev
|
||||||
|
- libcurl4-openssl-dev
|
||||||
- libsodium-dev
|
- libsodium-dev
|
||||||
stage-packages:
|
stage-packages:
|
||||||
- dbus
|
- dbus
|
||||||
|
@ -39,6 +39,7 @@ configure_file(version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h @ONLY)
|
|||||||
set(keepassx_SOURCES
|
set(keepassx_SOURCES
|
||||||
core/AutoTypeAssociations.cpp
|
core/AutoTypeAssociations.cpp
|
||||||
core/AsyncTask.h
|
core/AsyncTask.h
|
||||||
|
core/AutoTypeMatch.cpp
|
||||||
core/Config.cpp
|
core/Config.cpp
|
||||||
core/CsvParser.cpp
|
core/CsvParser.cpp
|
||||||
core/Database.cpp
|
core/Database.cpp
|
||||||
@ -137,6 +138,8 @@ set(keepassx_SOURCES
|
|||||||
gui/csvImport/CsvImportWizard.cpp
|
gui/csvImport/CsvImportWizard.cpp
|
||||||
gui/csvImport/CsvParserModel.cpp
|
gui/csvImport/CsvParserModel.cpp
|
||||||
gui/entry/AutoTypeAssociationsModel.cpp
|
gui/entry/AutoTypeAssociationsModel.cpp
|
||||||
|
gui/entry/AutoTypeMatchModel.cpp
|
||||||
|
gui/entry/AutoTypeMatchView.cpp
|
||||||
gui/entry/EditEntryWidget.cpp
|
gui/entry/EditEntryWidget.cpp
|
||||||
gui/entry/EditEntryWidget_p.h
|
gui/entry/EditEntryWidget_p.h
|
||||||
gui/entry/EntryAttachmentsModel.cpp
|
gui/entry/EntryAttachmentsModel.cpp
|
||||||
@ -196,13 +199,9 @@ add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with C
|
|||||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||||
|
|
||||||
if(WITH_XC_HTTP)
|
add_subdirectory(http)
|
||||||
add_subdirectory(http)
|
|
||||||
set(keepasshttp_LIB keepasshttp)
|
|
||||||
endif()
|
|
||||||
if(WITH_XC_NETWORKING)
|
if(WITH_XC_NETWORKING)
|
||||||
add_subdirectory(http/qhttp)
|
find_package(CURL REQUIRED)
|
||||||
set(keepassxcnetwork_LIB qhttp Qt5::Network)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
||||||
@ -248,23 +247,21 @@ endif()
|
|||||||
add_library(autotype STATIC ${autotype_SOURCES})
|
add_library(autotype STATIC ${autotype_SOURCES})
|
||||||
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
|
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
|
||||||
|
|
||||||
set(autotype_LIB autotype)
|
|
||||||
|
|
||||||
add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
||||||
|
|
||||||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
||||||
target_link_libraries(keepassx_core
|
target_link_libraries(keepassx_core
|
||||||
|
autotype
|
||||||
|
${keepassxchttp_LIB}
|
||||||
${keepassxcbrowser_LIB}
|
${keepassxcbrowser_LIB}
|
||||||
${keepasshttp_LIB}
|
|
||||||
${keepassxcnetwork_LIB}
|
|
||||||
${autotype_LIB}
|
|
||||||
${sshagent_LIB}
|
${sshagent_LIB}
|
||||||
${YUBIKEY_LIBRARIES}
|
|
||||||
${ZXCVBN_LIBRARIES}
|
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::Network
|
Qt5::Network
|
||||||
Qt5::Concurrent
|
Qt5::Concurrent
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
|
${CURL_LIBRARIES}
|
||||||
|
${YUBIKEY_LIBRARIES}
|
||||||
|
${ZXCVBN_LIBRARIES}
|
||||||
${ARGON2_LIBRARIES}
|
${ARGON2_LIBRARIES}
|
||||||
${GCRYPT_LIBRARIES}
|
${GCRYPT_LIBRARIES}
|
||||||
${GPGERROR_LIBRARIES}
|
${GPGERROR_LIBRARIES}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "autotype/AutoTypePlatformPlugin.h"
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
#include "autotype/AutoTypeSelectDialog.h"
|
#include "autotype/AutoTypeSelectDialog.h"
|
||||||
#include "autotype/WildcardMatcher.h"
|
#include "autotype/WildcardMatcher.h"
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
#include "core/Entry.h"
|
#include "core/Entry.h"
|
||||||
@ -40,7 +41,6 @@ AutoType* AutoType::m_instance = nullptr;
|
|||||||
|
|
||||||
AutoType::AutoType(QObject* parent, bool test)
|
AutoType::AutoType(QObject* parent, bool test)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_inAutoType(false)
|
|
||||||
, m_autoTypeDelay(0)
|
, m_autoTypeDelay(0)
|
||||||
, m_currentGlobalKey(static_cast<Qt::Key>(0))
|
, m_currentGlobalKey(static_cast<Qt::Key>(0))
|
||||||
, m_currentGlobalModifiers(0)
|
, m_currentGlobalModifiers(0)
|
||||||
@ -102,6 +102,19 @@ void AutoType::loadPlugin(const QString& pluginPath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoType::unloadPlugin()
|
||||||
|
{
|
||||||
|
if (m_executor) {
|
||||||
|
delete m_executor;
|
||||||
|
m_executor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_plugin) {
|
||||||
|
m_plugin->unload();
|
||||||
|
m_plugin = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AutoType* AutoType::instance()
|
AutoType* AutoType::instance()
|
||||||
{
|
{
|
||||||
if (!m_instance) {
|
if (!m_instance) {
|
||||||
@ -127,32 +140,76 @@ QStringList AutoType::windowTitles()
|
|||||||
return m_plugin->windowTitles();
|
return m_plugin->windowTitles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window)
|
void AutoType::resetInAutoType()
|
||||||
{
|
{
|
||||||
if (m_inAutoType || !m_plugin) {
|
m_inAutoType.unlock();
|
||||||
|
|
||||||
|
emit autotypeRejected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoType::raiseWindow()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
m_plugin->raiseOwnWindow();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
Q_ASSERT(key);
|
||||||
|
Q_ASSERT(modifiers);
|
||||||
|
|
||||||
|
if (!m_plugin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != m_currentGlobalKey || modifiers != m_currentGlobalModifiers) {
|
||||||
|
if (m_currentGlobalKey && m_currentGlobalModifiers) {
|
||||||
|
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_plugin->registerGlobalShortcut(key, modifiers)) {
|
||||||
|
m_currentGlobalKey = key;
|
||||||
|
m_currentGlobalModifiers = modifiers;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoType::unregisterGlobalShortcut()
|
||||||
|
{
|
||||||
|
if (m_plugin && m_currentGlobalKey && m_currentGlobalModifiers) {
|
||||||
|
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoType::callEventFilter(void* event)
|
||||||
|
{
|
||||||
|
if (!m_plugin) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_plugin->platformEventFilter(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core Autotype function that will execute actions
|
||||||
|
*/
|
||||||
|
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
|
||||||
|
{
|
||||||
|
// no edit to the sequence beyond this point
|
||||||
|
if (!verifyAutoTypeSyntax(sequence)) {
|
||||||
|
emit autotypeRejected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_inAutoType = true;
|
|
||||||
|
|
||||||
QString sequence;
|
|
||||||
if (customSequence.isEmpty()) {
|
|
||||||
sequence = autoTypeSequence(entry);
|
|
||||||
} else {
|
|
||||||
sequence = customSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkSyntax(sequence)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence.replace("{{}", "{LEFTBRACE}");
|
|
||||||
sequence.replace("{}}", "{RIGHTBRACE}");
|
|
||||||
|
|
||||||
QList<AutoTypeAction*> actions;
|
QList<AutoTypeAction*> actions;
|
||||||
ListDeleter<AutoTypeAction*> actionsDeleter(&actions);
|
ListDeleter<AutoTypeAction*> actionsDeleter(&actions);
|
||||||
|
|
||||||
if (!parseActions(sequence, entry, actions)) {
|
if (!parseActions(sequence, entry, actions)) {
|
||||||
m_inAutoType = false; // TODO: make this automatic
|
emit autotypeRejected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,19 +232,49 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
for (AutoTypeAction* action : asConst(actions)) {
|
for (AutoTypeAction* action : asConst(actions)) {
|
||||||
if (m_plugin->activeWindow() != window) {
|
if (m_plugin->activeWindow() != window) {
|
||||||
qWarning("Active window changed, interrupting auto-type.");
|
qWarning("Active window changed, interrupting auto-type.");
|
||||||
break;
|
emit autotypeRejected();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action->accept(m_executor);
|
action->accept(m_executor);
|
||||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inAutoType = false;
|
// emit signal only if autotype performed correctly
|
||||||
|
emit autotypePerformed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single Autotype entry-point function
|
||||||
|
* Perfom autotype sequence in the active window
|
||||||
|
*/
|
||||||
|
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||||
|
{
|
||||||
|
if (!m_plugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> sequences = autoTypeSequences(entry);
|
||||||
|
if (sequences.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_inAutoType.tryLock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
||||||
|
|
||||||
|
m_inAutoType.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global Autotype entry-point funcion
|
||||||
|
* Perform global autotype on the active window
|
||||||
|
*/
|
||||||
void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||||
{
|
{
|
||||||
if (m_inAutoType || !m_plugin) {
|
if (!m_plugin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,38 +284,42 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inAutoType = true;
|
if (!m_inAutoType.tryLock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QList<Entry*> entryList;
|
QList<AutoTypeMatch> matchList;
|
||||||
QHash<Entry*, QString> sequenceHash;
|
|
||||||
|
|
||||||
for (Database* db : dbList) {
|
for (Database* db : dbList) {
|
||||||
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
||||||
for (Entry* entry : dbEntries) {
|
for (Entry* entry : dbEntries) {
|
||||||
QString sequence = autoTypeSequence(entry, windowTitle);
|
const QSet<QString> sequences = autoTypeSequences(entry, windowTitle).toSet();
|
||||||
if (!sequence.isEmpty()) {
|
for (const QString& sequence : sequences) {
|
||||||
entryList << entry;
|
if (!sequence.isEmpty()) {
|
||||||
sequenceHash.insert(entry, sequence);
|
matchList << AutoTypeMatch(entry, sequence);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entryList.isEmpty()) {
|
if (matchList.isEmpty()) {
|
||||||
m_inAutoType = false;
|
m_inAutoType.unlock();
|
||||||
QString message = tr("Couldn't find an entry that matches the window title:");
|
QString message = tr("Couldn't find an entry that matches the window title:");
|
||||||
message.append("\n\n");
|
message.append("\n\n");
|
||||||
message.append(windowTitle);
|
message.append(windowTitle);
|
||||||
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message);
|
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message);
|
||||||
} else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
|
||||||
m_inAutoType = false;
|
emit autotypeRejected();
|
||||||
performAutoType(entryList.first(), nullptr, sequenceHash[entryList.first()]);
|
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
||||||
|
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
|
||||||
|
m_inAutoType.unlock();
|
||||||
} else {
|
} else {
|
||||||
m_windowFromGlobal = m_plugin->activeWindow();
|
m_windowFromGlobal = m_plugin->activeWindow();
|
||||||
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog();
|
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog();
|
||||||
connect(
|
connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)),
|
||||||
selectDialog, SIGNAL(entryActivated(Entry*, QString)), SLOT(performAutoTypeFromGlobal(Entry*, QString)));
|
SLOT(performAutoTypeFromGlobal(AutoTypeMatch)));
|
||||||
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
||||||
selectDialog->setEntries(entryList, sequenceHash);
|
selectDialog->setMatchList(matchList);
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
m_plugin->raiseOwnWindow();
|
m_plugin->raiseOwnWindow();
|
||||||
Tools::wait(500);
|
Tools::wait(500);
|
||||||
@ -239,92 +330,31 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoType::performAutoTypeFromGlobal(Entry* entry, const QString& sequence)
|
void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_inAutoType);
|
// We don't care about the result here, the mutex should already be locked. Now it's locked for sure
|
||||||
|
m_inAutoType.tryLock();
|
||||||
|
|
||||||
m_plugin->raiseWindow(m_windowFromGlobal);
|
m_plugin->raiseWindow(m_windowFromGlobal);
|
||||||
|
|
||||||
m_inAutoType = false;
|
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
|
||||||
|
|
||||||
performAutoType(entry, nullptr, sequence, m_windowFromGlobal);
|
m_inAutoType.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoType::resetInAutoType()
|
/**
|
||||||
{
|
* Parse an autotype sequence and resolve its Template/command inside as AutoTypeActions
|
||||||
Q_ASSERT(m_inAutoType);
|
*/
|
||||||
|
bool AutoType::parseActions(const QString& actionSequence, const Entry* entry, QList<AutoTypeAction*>& actions)
|
||||||
m_inAutoType = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoType::raiseWindow()
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
m_plugin->raiseOwnWindow();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoType::unloadPlugin()
|
|
||||||
{
|
|
||||||
if (m_executor) {
|
|
||||||
delete m_executor;
|
|
||||||
m_executor = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_plugin) {
|
|
||||||
m_plugin->unload();
|
|
||||||
m_plugin = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
|
||||||
{
|
|
||||||
Q_ASSERT(key);
|
|
||||||
Q_ASSERT(modifiers);
|
|
||||||
|
|
||||||
if (!m_plugin) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key != m_currentGlobalKey || modifiers != m_currentGlobalModifiers) {
|
|
||||||
if (m_currentGlobalKey && m_currentGlobalModifiers) {
|
|
||||||
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_plugin->registerGlobalShortcut(key, modifiers)) {
|
|
||||||
m_currentGlobalKey = key;
|
|
||||||
m_currentGlobalModifiers = modifiers;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoType::unregisterGlobalShortcut()
|
|
||||||
{
|
|
||||||
if (m_plugin && m_currentGlobalKey && m_currentGlobalModifiers) {
|
|
||||||
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int AutoType::callEventFilter(void* event)
|
|
||||||
{
|
|
||||||
if (!m_plugin) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_plugin->platformEventFilter(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions)
|
|
||||||
{
|
{
|
||||||
QString tmpl;
|
QString tmpl;
|
||||||
bool inTmpl = false;
|
bool inTmpl = false;
|
||||||
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
|
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
|
||||||
|
|
||||||
|
QString sequence = actionSequence;
|
||||||
|
sequence.replace("{{}", "{LEFTBRACE}");
|
||||||
|
sequence.replace("{}}", "{RIGHTBRACE}");
|
||||||
|
|
||||||
for (const QChar& ch : sequence) {
|
for (const QChar& ch : sequence) {
|
||||||
if (inTmpl) {
|
if (inTmpl) {
|
||||||
if (ch == '{') {
|
if (ch == '{') {
|
||||||
@ -363,6 +393,9 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<A
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an autotype Template/command to its AutoTypeAction that will be executed by the plugin executor
|
||||||
|
*/
|
||||||
QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry)
|
QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry)
|
||||||
{
|
{
|
||||||
QString tmplName = tmpl;
|
QString tmplName = tmpl;
|
||||||
@ -506,80 +539,65 @@ QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, c
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitle)
|
/**
|
||||||
|
* Retrive the autotype sequences matches for a given windowTitle
|
||||||
|
* This returns a list with priority ordering. If you don't want duplicates call .toSet() on it.
|
||||||
|
*/
|
||||||
|
QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle)
|
||||||
{
|
{
|
||||||
|
QList<QString> sequenceList;
|
||||||
|
|
||||||
if (!entry->autoTypeEnabled()) {
|
if (!entry->autoTypeEnabled()) {
|
||||||
return QString();
|
return sequenceList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool enableSet = false;
|
const Group* group = entry->group();
|
||||||
QString sequence;
|
do {
|
||||||
|
if (group->autoTypeEnabled() == Group::Disable) {
|
||||||
|
return sequenceList;
|
||||||
|
} else if (group->autoTypeEnabled() == Group::Enable) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
group = group->parentGroup();
|
||||||
|
|
||||||
|
} while (group);
|
||||||
|
|
||||||
if (!windowTitle.isEmpty()) {
|
if (!windowTitle.isEmpty()) {
|
||||||
bool match = false;
|
|
||||||
const QList<AutoTypeAssociations::Association> assocList = entry->autoTypeAssociations()->getAll();
|
const QList<AutoTypeAssociations::Association> assocList = entry->autoTypeAssociations()->getAll();
|
||||||
for (const AutoTypeAssociations::Association& assoc : assocList) {
|
for (const AutoTypeAssociations::Association& assoc : assocList) {
|
||||||
const QString window = entry->resolveMultiplePlaceholders(assoc.window);
|
const QString window = entry->resolveMultiplePlaceholders(assoc.window);
|
||||||
if (windowMatches(windowTitle, window)) {
|
if (windowMatches(windowTitle, window)) {
|
||||||
if (!assoc.sequence.isEmpty()) {
|
if (!assoc.sequence.isEmpty()) {
|
||||||
sequence = assoc.sequence;
|
sequenceList.append(assoc.sequence);
|
||||||
} else {
|
} else {
|
||||||
sequence = entry->defaultAutoTypeSequence();
|
sequenceList.append(entry->effectiveAutoTypeSequence());
|
||||||
}
|
}
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match && config()->get("AutoTypeEntryTitleMatch").toBool() &&
|
if (config()->get("AutoTypeEntryTitleMatch").toBool() &&
|
||||||
windowMatchesTitle(windowTitle, entry->resolvePlaceholder(entry->title()))) {
|
windowMatchesTitle(windowTitle, entry->resolvePlaceholder(entry->title()))) {
|
||||||
sequence = entry->defaultAutoTypeSequence();
|
sequenceList.append(entry->effectiveAutoTypeSequence());
|
||||||
match = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match && config()->get("AutoTypeEntryURLMatch").toBool() &&
|
if (config()->get("AutoTypeEntryURLMatch").toBool() &&
|
||||||
windowMatchesUrl(windowTitle, entry->resolvePlaceholder(entry->url()))) {
|
windowMatchesUrl(windowTitle, entry->resolvePlaceholder(entry->url()))) {
|
||||||
sequence = entry->defaultAutoTypeSequence();
|
sequenceList.append(entry->effectiveAutoTypeSequence());
|
||||||
match = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match) {
|
if (sequenceList.isEmpty()) {
|
||||||
return QString();
|
return sequenceList;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sequence = entry->defaultAutoTypeSequence();
|
sequenceList.append(entry->effectiveAutoTypeSequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Group* group = entry->group();
|
return sequenceList;
|
||||||
do {
|
|
||||||
if (!enableSet) {
|
|
||||||
if (group->autoTypeEnabled() == Group::Disable) {
|
|
||||||
return QString();
|
|
||||||
} else if (group->autoTypeEnabled() == Group::Enable) {
|
|
||||||
enableSet = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sequence.isEmpty()) {
|
|
||||||
sequence = group->defaultAutoTypeSequence();
|
|
||||||
}
|
|
||||||
|
|
||||||
group = group->parentGroup();
|
|
||||||
} while (group && (!enableSet || sequence.isEmpty()));
|
|
||||||
|
|
||||||
if (sequence.isEmpty() && (!entry->resolvePlaceholder(entry->username()).isEmpty() ||
|
|
||||||
!entry->resolvePlaceholder(entry->password()).isEmpty())) {
|
|
||||||
if (entry->resolvePlaceholder(entry->username()).isEmpty()) {
|
|
||||||
sequence = "{PASSWORD}{ENTER}";
|
|
||||||
} else if (entry->resolvePlaceholder(entry->password()).isEmpty()) {
|
|
||||||
sequence = "{USERNAME}{ENTER}";
|
|
||||||
} else {
|
|
||||||
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sequence;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a window title matches a pattern
|
||||||
|
*/
|
||||||
bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern)
|
bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern)
|
||||||
{
|
{
|
||||||
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
|
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
|
||||||
@ -590,11 +608,19 @@ bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a window title matches an entry Title
|
||||||
|
* The entry title should be Spr-compiled by the caller
|
||||||
|
*/
|
||||||
bool AutoType::windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle)
|
bool AutoType::windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle)
|
||||||
{
|
{
|
||||||
return !resolvedTitle.isEmpty() && windowTitle.contains(resolvedTitle, Qt::CaseInsensitive);
|
return !resolvedTitle.isEmpty() && windowTitle.contains(resolvedTitle, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a window title matches an entry URL
|
||||||
|
* The entry URL should be Spr-compiled by the caller
|
||||||
|
*/
|
||||||
bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl)
|
bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl)
|
||||||
{
|
{
|
||||||
if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) {
|
if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) {
|
||||||
@ -609,6 +635,9 @@ bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resol
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the overall syntax of an autotype sequence is fine
|
||||||
|
*/
|
||||||
bool AutoType::checkSyntax(const QString& string)
|
bool AutoType::checkSyntax(const QString& string)
|
||||||
{
|
{
|
||||||
QString allowRepetition = "(?:\\s\\d+)?";
|
QString allowRepetition = "(?:\\s\\d+)?";
|
||||||
@ -634,6 +663,9 @@ bool AutoType::checkSyntax(const QString& string)
|
|||||||
return match.hasMatch();
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks an autotype sequence for high delay
|
||||||
|
*/
|
||||||
bool AutoType::checkHighDelay(const QString& string)
|
bool AutoType::checkHighDelay(const QString& string)
|
||||||
{
|
{
|
||||||
// 5 digit numbers(10 seconds) are too much
|
// 5 digit numbers(10 seconds) are too much
|
||||||
@ -642,6 +674,9 @@ bool AutoType::checkHighDelay(const QString& string)
|
|||||||
return match.hasMatch();
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks an autotype sequence for slow keypress
|
||||||
|
*/
|
||||||
bool AutoType::checkSlowKeypress(const QString& string)
|
bool AutoType::checkSlowKeypress(const QString& string)
|
||||||
{
|
{
|
||||||
// 3 digit numbers(100 milliseconds) are too much
|
// 3 digit numbers(100 milliseconds) are too much
|
||||||
@ -650,6 +685,9 @@ bool AutoType::checkSlowKeypress(const QString& string)
|
|||||||
return match.hasMatch();
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks an autotype sequence for high repetition command
|
||||||
|
*/
|
||||||
bool AutoType::checkHighRepetition(const QString& string)
|
bool AutoType::checkHighRepetition(const QString& string)
|
||||||
{
|
{
|
||||||
// 3 digit numbers are too much
|
// 3 digit numbers are too much
|
||||||
@ -658,6 +696,9 @@ bool AutoType::checkHighRepetition(const QString& string)
|
|||||||
return match.hasMatch();
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if the syntax of an autotype sequence is correct and doesn't have silly parameters
|
||||||
|
*/
|
||||||
bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
|
bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
|
||||||
{
|
{
|
||||||
if (!AutoType::checkSyntax(sequence)) {
|
if (!AutoType::checkSyntax(sequence)) {
|
||||||
@ -691,12 +732,3 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window)
|
|
||||||
{
|
|
||||||
auto sequence = entry->effectiveAutoTypeSequence();
|
|
||||||
if (verifyAutoTypeSyntax(sequence)) {
|
|
||||||
executeAutoTypeActions(entry, hideWindow, customSequence, window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
@ -22,6 +22,9 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
|
|
||||||
class AutoTypeAction;
|
class AutoTypeAction;
|
||||||
class AutoTypeExecutor;
|
class AutoTypeExecutor;
|
||||||
@ -36,10 +39,6 @@ class AutoType : public QObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
QStringList windowTitles();
|
QStringList windowTitles();
|
||||||
void executeAutoTypeActions(const Entry* entry,
|
|
||||||
QWidget* hideWindow = nullptr,
|
|
||||||
const QString& customSequence = QString(),
|
|
||||||
WId window = 0);
|
|
||||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
void unregisterGlobalShortcut();
|
void unregisterGlobalShortcut();
|
||||||
int callEventFilter(void* event);
|
int callEventFilter(void* event);
|
||||||
@ -49,9 +48,7 @@ public:
|
|||||||
static bool checkHighDelay(const QString& string);
|
static bool checkHighDelay(const QString& string);
|
||||||
static bool verifyAutoTypeSyntax(const QString& sequence);
|
static bool verifyAutoTypeSyntax(const QString& sequence);
|
||||||
void performAutoType(const Entry* entry,
|
void performAutoType(const Entry* entry,
|
||||||
QWidget* hideWindow = nullptr,
|
QWidget* hideWindow = nullptr);
|
||||||
const QString& customSequence = QString(),
|
|
||||||
WId window = 0);
|
|
||||||
|
|
||||||
inline bool isAvailable()
|
inline bool isAvailable()
|
||||||
{
|
{
|
||||||
@ -67,9 +64,11 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void globalShortcutTriggered();
|
void globalShortcutTriggered();
|
||||||
|
void autotypePerformed();
|
||||||
|
void autotypeRejected();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void performAutoTypeFromGlobal(Entry* entry, const QString& sequence);
|
void performAutoTypeFromGlobal(AutoTypeMatch match);
|
||||||
void resetInAutoType();
|
void resetInAutoType();
|
||||||
void unloadPlugin();
|
void unloadPlugin();
|
||||||
|
|
||||||
@ -77,14 +76,18 @@ private:
|
|||||||
explicit AutoType(QObject* parent = nullptr, bool test = false);
|
explicit AutoType(QObject* parent = nullptr, bool test = false);
|
||||||
~AutoType();
|
~AutoType();
|
||||||
void loadPlugin(const QString& pluginPath);
|
void loadPlugin(const QString& pluginPath);
|
||||||
|
void executeAutoTypeActions(const Entry* entry,
|
||||||
|
QWidget* hideWindow = nullptr,
|
||||||
|
const QString& customSequence = QString(),
|
||||||
|
WId window = 0);
|
||||||
bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
|
bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
|
||||||
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
|
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
|
||||||
QString autoTypeSequence(const Entry* entry, const QString& windowTitle = QString());
|
QList<QString> autoTypeSequences(const Entry* entry, const QString& windowTitle = QString());
|
||||||
bool windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle);
|
bool windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle);
|
||||||
bool windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl);
|
bool windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl);
|
||||||
bool windowMatches(const QString& windowTitle, const QString& windowPattern);
|
bool windowMatches(const QString& windowTitle, const QString& windowPattern);
|
||||||
|
|
||||||
bool m_inAutoType;
|
QMutex m_inAutoType;
|
||||||
int m_autoTypeDelay;
|
int m_autoTypeDelay;
|
||||||
Qt::Key m_currentGlobalKey;
|
Qt::Key m_currentGlobalKey;
|
||||||
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QChar>
|
#include <QChar>
|
||||||
#include <Qt>
|
#include <Qt>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "core/Global.h"
|
#include "core/Global.h"
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -25,14 +26,16 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "autotype/AutoTypeSelectView.h"
|
#include "autotype/AutoTypeSelectView.h"
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/FilePath.h"
|
#include "core/FilePath.h"
|
||||||
#include "gui/entry/EntryModel.h"
|
#include "gui/entry/AutoTypeMatchModel.h"
|
||||||
|
|
||||||
AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_view(new AutoTypeSelectView(this))
|
, m_view(new AutoTypeSelectView(this))
|
||||||
, m_entryActivatedEmitted(false)
|
, m_matchActivatedEmitted(false)
|
||||||
|
, m_rejected(false)
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
// Places the window on the active (virtual) desktop instead of where the main window is.
|
// Places the window on the active (virtual) desktop instead of where the main window is.
|
||||||
@ -42,7 +45,7 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
|||||||
setWindowIcon(filePath()->applicationIcon());
|
setWindowIcon(filePath()->applicationIcon());
|
||||||
|
|
||||||
QRect screenGeometry = QApplication::desktop()->availableGeometry(QCursor::pos());
|
QRect screenGeometry = QApplication::desktop()->availableGeometry(QCursor::pos());
|
||||||
QSize size = config()->get("GUI/AutoTypeSelectDialogSize", QSize(400, 250)).toSize();
|
QSize size = config()->get("GUI/AutoTypeSelectDialogSize", QSize(600, 250)).toSize();
|
||||||
size.setWidth(qMin(size.width(), screenGeometry.width()));
|
size.setWidth(qMin(size.width(), screenGeometry.width()));
|
||||||
size.setHeight(qMin(size.height(), screenGeometry.height()));
|
size.setHeight(qMin(size.height(), screenGeometry.height()));
|
||||||
resize(size);
|
resize(size);
|
||||||
@ -56,10 +59,10 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
|||||||
QLabel* descriptionLabel = new QLabel(tr("Select entry to Auto-Type:"), this);
|
QLabel* descriptionLabel = new QLabel(tr("Select entry to Auto-Type:"), this);
|
||||||
layout->addWidget(descriptionLabel);
|
layout->addWidget(descriptionLabel);
|
||||||
|
|
||||||
connect(m_view, SIGNAL(activated(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
|
connect(m_view, SIGNAL(activated(QModelIndex)), SLOT(emitMatchActivated(QModelIndex)));
|
||||||
connect(m_view, SIGNAL(clicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
|
connect(m_view, SIGNAL(clicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex)));
|
||||||
|
connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(matchRemoved()));
|
||||||
connect(m_view, SIGNAL(rejected()), SLOT(reject()));
|
connect(m_view, SIGNAL(rejected()), SLOT(reject()));
|
||||||
connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(entryRemoved()));
|
|
||||||
layout->addWidget(m_view);
|
layout->addWidget(m_view);
|
||||||
|
|
||||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||||
@ -67,10 +70,9 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
|||||||
layout->addWidget(buttonBox);
|
layout->addWidget(buttonBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeSelectDialog::setEntries(const QList<Entry*>& entries, const QHash<Entry*, QString>& sequences)
|
void AutoTypeSelectDialog::setMatchList(const QList<AutoTypeMatch>& matchList)
|
||||||
{
|
{
|
||||||
m_sequences = sequences;
|
m_view->setMatchList(matchList);
|
||||||
m_view->setEntryList(entries);
|
|
||||||
|
|
||||||
m_view->header()->resizeSections(QHeaderView::ResizeToContents);
|
m_view->header()->resizeSections(QHeaderView::ResizeToContents);
|
||||||
}
|
}
|
||||||
@ -82,21 +84,32 @@ void AutoTypeSelectDialog::done(int r)
|
|||||||
QDialog::done(r);
|
QDialog::done(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeSelectDialog::emitEntryActivated(const QModelIndex& index)
|
void AutoTypeSelectDialog::reject()
|
||||||
{
|
{
|
||||||
// make sure we don't emit the signal twice when both activated() and clicked() are triggered
|
m_rejected = true;
|
||||||
if (m_entryActivatedEmitted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_entryActivatedEmitted = true;
|
|
||||||
|
|
||||||
Entry* entry = m_view->entryFromIndex(index);
|
QDialog::reject();
|
||||||
accept();
|
|
||||||
emit entryActivated(entry, m_sequences[entry]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeSelectDialog::entryRemoved()
|
void AutoTypeSelectDialog::emitMatchActivated(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
|
// make sure we don't emit the signal twice when both activated() and clicked() are triggered
|
||||||
|
if (m_matchActivatedEmitted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_matchActivatedEmitted = true;
|
||||||
|
|
||||||
|
AutoTypeMatch match = m_view->matchFromIndex(index);
|
||||||
|
accept();
|
||||||
|
emit matchActivated(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeSelectDialog::matchRemoved()
|
||||||
|
{
|
||||||
|
if (m_rejected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_view->model()->rowCount() == 0) {
|
if (m_view->model()->rowCount() == 0) {
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,9 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
|
|
||||||
class AutoTypeSelectView;
|
class AutoTypeSelectView;
|
||||||
class Entry;
|
|
||||||
|
|
||||||
class AutoTypeSelectDialog : public QDialog
|
class AutoTypeSelectDialog : public QDialog
|
||||||
{
|
{
|
||||||
@ -31,22 +32,23 @@ class AutoTypeSelectDialog : public QDialog
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AutoTypeSelectDialog(QWidget* parent = nullptr);
|
explicit AutoTypeSelectDialog(QWidget* parent = nullptr);
|
||||||
void setEntries(const QList<Entry*>& entries, const QHash<Entry*, QString>& sequences);
|
void setMatchList(const QList<AutoTypeMatch>& matchList);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void entryActivated(Entry* entry, const QString& sequence);
|
void matchActivated(AutoTypeMatch match);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void done(int r) override;
|
void done(int r) override;
|
||||||
|
void reject() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void emitEntryActivated(const QModelIndex& index);
|
void emitMatchActivated(const QModelIndex& index);
|
||||||
void entryRemoved();
|
void matchRemoved();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AutoTypeSelectView* const m_view;
|
AutoTypeSelectView* const m_view;
|
||||||
QHash<Entry*, QString> m_sequences;
|
bool m_matchActivatedEmitted;
|
||||||
bool m_entryActivatedEmitted;
|
bool m_rejected;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_AUTOTYPESELECTDIALOG_H
|
#endif // KEEPASSX_AUTOTYPESELECTDIALOG_H
|
||||||
|
@ -21,15 +21,12 @@
|
|||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
||||||
AutoTypeSelectView::AutoTypeSelectView(QWidget* parent)
|
AutoTypeSelectView::AutoTypeSelectView(QWidget* parent)
|
||||||
: EntryView(parent)
|
: AutoTypeMatchView(parent)
|
||||||
{
|
{
|
||||||
hideColumn(3);
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
setAllColumnsShowFocus(true);
|
setAllColumnsShowFocus(true);
|
||||||
setDragEnabled(false);
|
|
||||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
|
||||||
|
|
||||||
connect(model(), SIGNAL(modelReset()), SLOT(selectFirstEntry()));
|
connect(model(), SIGNAL(modelReset()), SLOT(selectFirstMatch()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeSelectView::mouseMoveEvent(QMouseEvent* event)
|
void AutoTypeSelectView::mouseMoveEvent(QMouseEvent* event)
|
||||||
@ -44,10 +41,10 @@ void AutoTypeSelectView::mouseMoveEvent(QMouseEvent* event)
|
|||||||
unsetCursor();
|
unsetCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
EntryView::mouseMoveEvent(event);
|
AutoTypeMatchView::mouseMoveEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeSelectView::selectFirstEntry()
|
void AutoTypeSelectView::selectFirstMatch()
|
||||||
{
|
{
|
||||||
QModelIndex index = model()->index(0, 0);
|
QModelIndex index = model()->index(0, 0);
|
||||||
|
|
||||||
|
@ -18,11 +18,9 @@
|
|||||||
#ifndef KEEPASSX_AUTOTYPESELECTVIEW_H
|
#ifndef KEEPASSX_AUTOTYPESELECTVIEW_H
|
||||||
#define KEEPASSX_AUTOTYPESELECTVIEW_H
|
#define KEEPASSX_AUTOTYPESELECTVIEW_H
|
||||||
|
|
||||||
#include "gui/entry/EntryView.h"
|
#include "gui/entry/AutoTypeMatchView.h"
|
||||||
|
|
||||||
class Entry;
|
class AutoTypeSelectView : public AutoTypeMatchView
|
||||||
|
|
||||||
class AutoTypeSelectView : public EntryView
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -34,7 +32,7 @@ protected:
|
|||||||
void keyReleaseEvent(QKeyEvent* e) override;
|
void keyReleaseEvent(QKeyEvent* e) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void selectFirstEntry();
|
void selectFirstMatch();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void rejected();
|
void rejected();
|
||||||
|
@ -211,7 +211,7 @@ void BrowserSettings::setVivaldiSupport(bool enabled) {
|
|||||||
|
|
||||||
bool BrowserSettings::passwordUseNumbers()
|
bool BrowserSettings::passwordUseNumbers()
|
||||||
{
|
{
|
||||||
return config()->get("generator/Numbers", true).toBool();
|
return config()->get("generator/Numbers", PasswordGenerator::DefaultNumbers).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordUseNumbers(bool useNumbers)
|
void BrowserSettings::setPasswordUseNumbers(bool useNumbers)
|
||||||
@ -221,7 +221,7 @@ void BrowserSettings::setPasswordUseNumbers(bool useNumbers)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordUseLowercase()
|
bool BrowserSettings::passwordUseLowercase()
|
||||||
{
|
{
|
||||||
return config()->get("generator/LowerCase", true).toBool();
|
return config()->get("generator/LowerCase", PasswordGenerator::DefaultLower).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordUseLowercase(bool useLowercase)
|
void BrowserSettings::setPasswordUseLowercase(bool useLowercase)
|
||||||
@ -231,7 +231,7 @@ void BrowserSettings::setPasswordUseLowercase(bool useLowercase)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordUseUppercase()
|
bool BrowserSettings::passwordUseUppercase()
|
||||||
{
|
{
|
||||||
return config()->get("generator/UpperCase", true).toBool();
|
return config()->get("generator/UpperCase", PasswordGenerator::DefaultUpper).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordUseUppercase(bool useUppercase)
|
void BrowserSettings::setPasswordUseUppercase(bool useUppercase)
|
||||||
@ -241,7 +241,7 @@ void BrowserSettings::setPasswordUseUppercase(bool useUppercase)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordUseSpecial()
|
bool BrowserSettings::passwordUseSpecial()
|
||||||
{
|
{
|
||||||
return config()->get("generator/SpecialChars", false).toBool();
|
return config()->get("generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordUseSpecial(bool useSpecial)
|
void BrowserSettings::setPasswordUseSpecial(bool useSpecial)
|
||||||
@ -251,7 +251,7 @@ void BrowserSettings::setPasswordUseSpecial(bool useSpecial)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordUseEASCII()
|
bool BrowserSettings::passwordUseEASCII()
|
||||||
{
|
{
|
||||||
return config()->get("generator/EASCII", false).toBool();
|
return config()->get("generator/EASCII", PasswordGenerator::DefaultEASCII).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordUseEASCII(bool useEASCII)
|
void BrowserSettings::setPasswordUseEASCII(bool useEASCII)
|
||||||
@ -261,7 +261,7 @@ void BrowserSettings::setPasswordUseEASCII(bool useEASCII)
|
|||||||
|
|
||||||
int BrowserSettings::passPhraseWordCount()
|
int BrowserSettings::passPhraseWordCount()
|
||||||
{
|
{
|
||||||
return config()->get("generator/WordCount", 6).toInt();
|
return config()->get("generator/WordCount", PassphraseGenerator::DefaultWordCount).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPassPhraseWordCount(int wordCount)
|
void BrowserSettings::setPassPhraseWordCount(int wordCount)
|
||||||
@ -271,7 +271,7 @@ void BrowserSettings::setPassPhraseWordCount(int wordCount)
|
|||||||
|
|
||||||
QString BrowserSettings::passPhraseWordSeparator()
|
QString BrowserSettings::passPhraseWordSeparator()
|
||||||
{
|
{
|
||||||
return config()->get("generator/WordSeparator", " ").toString();
|
return config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPassPhraseWordSeparator(QString separator)
|
void BrowserSettings::setPassPhraseWordSeparator(QString separator)
|
||||||
@ -291,7 +291,7 @@ void BrowserSettings::setGeneratorType(int type)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordEveryGroup()
|
bool BrowserSettings::passwordEveryGroup()
|
||||||
{
|
{
|
||||||
return config()->get("generator/EnsureEvery", true).toBool();
|
return config()->get("generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordEveryGroup(bool everyGroup)
|
void BrowserSettings::setPasswordEveryGroup(bool everyGroup)
|
||||||
@ -301,7 +301,7 @@ void BrowserSettings::setPasswordEveryGroup(bool everyGroup)
|
|||||||
|
|
||||||
bool BrowserSettings::passwordExcludeAlike()
|
bool BrowserSettings::passwordExcludeAlike()
|
||||||
{
|
{
|
||||||
return config()->get("generator/ExcludeAlike", true).toBool();
|
return config()->get("generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordExcludeAlike(bool excludeAlike)
|
void BrowserSettings::setPasswordExcludeAlike(bool excludeAlike)
|
||||||
@ -311,7 +311,7 @@ void BrowserSettings::setPasswordExcludeAlike(bool excludeAlike)
|
|||||||
|
|
||||||
int BrowserSettings::passwordLength()
|
int BrowserSettings::passwordLength()
|
||||||
{
|
{
|
||||||
return config()->get("generator/Length", 20).toInt();
|
return config()->get("generator/Length", PasswordGenerator::DefaultLength).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setPasswordLength(int length)
|
void BrowserSettings::setPasswordLength(int length)
|
||||||
|
@ -31,15 +31,15 @@
|
|||||||
|
|
||||||
Add::Add()
|
Add::Add()
|
||||||
{
|
{
|
||||||
this->name = QString("add");
|
name = QString("add");
|
||||||
this->description = QObject::tr("Add a new entry to a database.");
|
description = QObject::tr("Add a new entry to a database.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Add::~Add()
|
Add::~Add()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Add::execute(QStringList arguments)
|
int Add::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
|
|
||||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
@ -133,8 +133,8 @@ int Add::execute(QStringList arguments)
|
|||||||
passwordGenerator.setLength(passwordLength.toInt());
|
passwordGenerator.setLength(passwordLength.toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordGenerator.setCharClasses(PasswordGenerator::LowerLetters | PasswordGenerator::UpperLetters |
|
passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset);
|
||||||
PasswordGenerator::Numbers);
|
passwordGenerator.setFlags(PasswordGenerator::DefaultFlags);
|
||||||
QString password = passwordGenerator.generatePassword();
|
QString password = passwordGenerator.generatePassword();
|
||||||
entry->setPassword(password);
|
entry->setPassword(password);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class Add : public Command
|
|||||||
public:
|
public:
|
||||||
Add();
|
Add();
|
||||||
~Add();
|
~Add();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_ADD_H
|
#endif // KEEPASSXC_ADD_H
|
||||||
|
@ -20,12 +20,16 @@ set(cli_SOURCES
|
|||||||
Clip.h
|
Clip.h
|
||||||
Command.cpp
|
Command.cpp
|
||||||
Command.h
|
Command.h
|
||||||
|
Diceware.cpp
|
||||||
|
Diceware.h
|
||||||
Edit.cpp
|
Edit.cpp
|
||||||
Edit.h
|
Edit.h
|
||||||
Estimate.cpp
|
Estimate.cpp
|
||||||
Estimate.h
|
Estimate.h
|
||||||
Extract.cpp
|
Extract.cpp
|
||||||
Extract.h
|
Extract.h
|
||||||
|
Generate.cpp
|
||||||
|
Generate.h
|
||||||
List.cpp
|
List.cpp
|
||||||
List.h
|
List.h
|
||||||
Locate.cpp
|
Locate.cpp
|
||||||
|
@ -32,15 +32,15 @@
|
|||||||
|
|
||||||
Clip::Clip()
|
Clip::Clip()
|
||||||
{
|
{
|
||||||
this->name = QString("clip");
|
name = QString("clip");
|
||||||
this->description = QObject::tr("Copy an entry's password to the clipboard.");
|
description = QObject::tr("Copy an entry's password to the clipboard.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Clip::~Clip()
|
Clip::~Clip()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Clip::execute(QStringList arguments)
|
int Clip::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
|
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
|
@ -25,7 +25,7 @@ class Clip : public Command
|
|||||||
public:
|
public:
|
||||||
Clip();
|
Clip();
|
||||||
~Clip();
|
~Clip();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
int clipEntry(Database* database, QString entryPath, QString timeout);
|
int clipEntry(Database* database, QString entryPath, QString timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,9 +24,11 @@
|
|||||||
|
|
||||||
#include "Add.h"
|
#include "Add.h"
|
||||||
#include "Clip.h"
|
#include "Clip.h"
|
||||||
|
#include "Diceware.h"
|
||||||
#include "Edit.h"
|
#include "Edit.h"
|
||||||
#include "Estimate.h"
|
#include "Estimate.h"
|
||||||
#include "Extract.h"
|
#include "Extract.h"
|
||||||
|
#include "Generate.h"
|
||||||
#include "List.h"
|
#include "List.h"
|
||||||
#include "Locate.h"
|
#include "Locate.h"
|
||||||
#include "Merge.h"
|
#include "Merge.h"
|
||||||
@ -39,7 +41,7 @@ Command::~Command()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Command::execute(QStringList)
|
int Command::execute(const QStringList&)
|
||||||
{
|
{
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@ -61,9 +63,11 @@ void populateCommands()
|
|||||||
if (commands.isEmpty()) {
|
if (commands.isEmpty()) {
|
||||||
commands.insert(QString("add"), new Add());
|
commands.insert(QString("add"), new Add());
|
||||||
commands.insert(QString("clip"), new Clip());
|
commands.insert(QString("clip"), new Clip());
|
||||||
|
commands.insert(QString("diceware"), new Diceware());
|
||||||
commands.insert(QString("edit"), new Edit());
|
commands.insert(QString("edit"), new Edit());
|
||||||
commands.insert(QString("estimate"), new Estimate());
|
commands.insert(QString("estimate"), new Estimate());
|
||||||
commands.insert(QString("extract"), new Extract());
|
commands.insert(QString("extract"), new Extract());
|
||||||
|
commands.insert(QString("generate"), new Generate());
|
||||||
commands.insert(QString("locate"), new Locate());
|
commands.insert(QString("locate"), new Locate());
|
||||||
commands.insert(QString("ls"), new List());
|
commands.insert(QString("ls"), new List());
|
||||||
commands.insert(QString("merge"), new Merge());
|
commands.insert(QString("merge"), new Merge());
|
||||||
|
@ -29,7 +29,7 @@ class Command
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Command();
|
virtual ~Command();
|
||||||
virtual int execute(QStringList arguments);
|
virtual int execute(const QStringList& arguments);
|
||||||
QString name;
|
QString name;
|
||||||
QString description;
|
QString description;
|
||||||
QString getDescriptionLine();
|
QString getDescriptionLine();
|
||||||
|
86
src/cli/Diceware.cpp
Normal file
86
src/cli/Diceware.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Diceware.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include "core/PassphraseGenerator.h"
|
||||||
|
|
||||||
|
Diceware::Diceware()
|
||||||
|
{
|
||||||
|
name = QString("diceware");
|
||||||
|
description = QObject::tr("Generate a new random diceware passphrase.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Diceware::~Diceware()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Diceware::execute(const QStringList& arguments)
|
||||||
|
{
|
||||||
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
|
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription(this->description);
|
||||||
|
QCommandLineOption words(QStringList() << "W" << "words",
|
||||||
|
QObject::tr("Word count for the diceware passphrase."),
|
||||||
|
QObject::tr("count"));
|
||||||
|
parser.addOption(words);
|
||||||
|
QCommandLineOption wordlistFile(QStringList() << "w"
|
||||||
|
<< "word-list",
|
||||||
|
QObject::tr("Wordlist for the diceware generator.\n[Default: EFF English]"),
|
||||||
|
QObject::tr("path"));
|
||||||
|
parser.addOption(wordlistFile);
|
||||||
|
parser.process(arguments);
|
||||||
|
|
||||||
|
const QStringList args = parser.positionalArguments();
|
||||||
|
if (!args.isEmpty()) {
|
||||||
|
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassphraseGenerator dicewareGenerator;
|
||||||
|
|
||||||
|
if (parser.value(words).isEmpty()) {
|
||||||
|
dicewareGenerator.setWordCount(PassphraseGenerator::DefaultWordCount);
|
||||||
|
} else {
|
||||||
|
int wordcount = parser.value(words).toInt();
|
||||||
|
dicewareGenerator.setWordCount(wordcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parser.value(wordlistFile).isEmpty()) {
|
||||||
|
dicewareGenerator.setWordList(parser.value(wordlistFile));
|
||||||
|
} else {
|
||||||
|
dicewareGenerator.setDefaultWordList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dicewareGenerator.isValid()) {
|
||||||
|
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString password = dicewareGenerator.generatePassphrase();
|
||||||
|
outputTextStream << password << endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
31
src/cli/Diceware.h
Normal file
31
src/cli/Diceware.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSXC_DICEWARE_H
|
||||||
|
#define KEEPASSXC_DICEWARE_H
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class Diceware : public Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Diceware();
|
||||||
|
~Diceware();
|
||||||
|
int execute(const QStringList& arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSXC_DICEWARE_H
|
@ -31,15 +31,15 @@
|
|||||||
|
|
||||||
Edit::Edit()
|
Edit::Edit()
|
||||||
{
|
{
|
||||||
this->name = QString("edit");
|
name = QString("edit");
|
||||||
this->description = QObject::tr("Edit an entry.");
|
description = QObject::tr("Edit an entry.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Edit::~Edit()
|
Edit::~Edit()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Edit::execute(QStringList arguments)
|
int Edit::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
|
|
||||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
@ -149,8 +149,8 @@ int Edit::execute(QStringList arguments)
|
|||||||
passwordGenerator.setLength(passwordLength.toInt());
|
passwordGenerator.setLength(passwordLength.toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordGenerator.setCharClasses(PasswordGenerator::LowerLetters | PasswordGenerator::UpperLetters |
|
passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset);
|
||||||
PasswordGenerator::Numbers);
|
passwordGenerator.setFlags(PasswordGenerator::DefaultFlags);
|
||||||
QString password = passwordGenerator.generatePassword();
|
QString password = passwordGenerator.generatePassword();
|
||||||
entry->setPassword(password);
|
entry->setPassword(password);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class Edit : public Command
|
|||||||
public:
|
public:
|
||||||
Edit();
|
Edit();
|
||||||
~Edit();
|
~Edit();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_EDIT_H
|
#endif // KEEPASSXC_EDIT_H
|
||||||
|
@ -34,8 +34,8 @@
|
|||||||
|
|
||||||
Estimate::Estimate()
|
Estimate::Estimate()
|
||||||
{
|
{
|
||||||
this->name = QString("estimate");
|
name = QString("estimate");
|
||||||
this->description = QObject::tr("Estimate the entropy of a password.");
|
description = QObject::tr("Estimate the entropy of a password.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Estimate::~Estimate()
|
Estimate::~Estimate()
|
||||||
@ -138,7 +138,7 @@ static void estimate(const char* pwd, bool advanced)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Estimate::execute(QStringList arguments)
|
int Estimate::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||||
|
@ -25,7 +25,7 @@ class Estimate : public Command
|
|||||||
public:
|
public:
|
||||||
Estimate();
|
Estimate();
|
||||||
~Estimate();
|
~Estimate();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_ESTIMATE_H
|
#endif // KEEPASSXC_ESTIMATE_H
|
||||||
|
@ -33,15 +33,15 @@
|
|||||||
|
|
||||||
Extract::Extract()
|
Extract::Extract()
|
||||||
{
|
{
|
||||||
this->name = QString("extract");
|
name = QString("extract");
|
||||||
this->description = QObject::tr("Extract and print the content of a database.");
|
description = QObject::tr("Extract and print the content of a database.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Extract::~Extract()
|
Extract::~Extract()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Extract::execute(QStringList arguments)
|
int Extract::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
QTextStream errorTextStream(stderr);
|
QTextStream errorTextStream(stderr);
|
||||||
|
@ -25,7 +25,7 @@ class Extract : public Command
|
|||||||
public:
|
public:
|
||||||
Extract();
|
Extract();
|
||||||
~Extract();
|
~Extract();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_EXTRACT_H
|
#endif // KEEPASSXC_EXTRACT_H
|
||||||
|
111
src/cli/Generate.cpp
Normal file
111
src/cli/Generate.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Generate.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include "core/PasswordGenerator.h"
|
||||||
|
|
||||||
|
Generate::Generate()
|
||||||
|
{
|
||||||
|
name = QString("generate");
|
||||||
|
description = QObject::tr("Generate a new random password.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Generate::~Generate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Generate::execute(const QStringList& arguments)
|
||||||
|
{
|
||||||
|
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||||
|
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription(this->description);
|
||||||
|
QCommandLineOption len(QStringList() << "L" << "length",
|
||||||
|
QObject::tr("Length of the generated password."),
|
||||||
|
QObject::tr("length"));
|
||||||
|
parser.addOption(len);
|
||||||
|
QCommandLineOption lower(QStringList() << "l",
|
||||||
|
QObject::tr("Use lowercase characters in the generated password."));
|
||||||
|
parser.addOption(lower);
|
||||||
|
QCommandLineOption upper(QStringList() << "u",
|
||||||
|
QObject::tr("Use uppercase characters in the generated password."));
|
||||||
|
parser.addOption(upper);
|
||||||
|
QCommandLineOption numeric(QStringList() << "n",
|
||||||
|
QObject::tr("Use numbers in the generated password."));
|
||||||
|
parser.addOption(numeric);
|
||||||
|
QCommandLineOption special(QStringList() << "s",
|
||||||
|
QObject::tr("Use special characters in the generated password."));
|
||||||
|
parser.addOption(special);
|
||||||
|
QCommandLineOption extended(QStringList() << "e",
|
||||||
|
QObject::tr("Use extended ASCII in the generated password."));
|
||||||
|
parser.addOption(extended);
|
||||||
|
parser.process(arguments);
|
||||||
|
|
||||||
|
const QStringList args = parser.positionalArguments();
|
||||||
|
if (!args.isEmpty()) {
|
||||||
|
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordGenerator passwordGenerator;
|
||||||
|
|
||||||
|
if (parser.value(len).isEmpty()) {
|
||||||
|
passwordGenerator.setLength(PasswordGenerator::DefaultLength);
|
||||||
|
} else {
|
||||||
|
int length = parser.value(len).toInt();
|
||||||
|
passwordGenerator.setLength(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordGenerator::CharClasses classes = 0x0;
|
||||||
|
|
||||||
|
if (parser.isSet(lower)) {
|
||||||
|
classes |= PasswordGenerator::LowerLetters;
|
||||||
|
}
|
||||||
|
if (parser.isSet(upper)) {
|
||||||
|
classes |= PasswordGenerator::UpperLetters;
|
||||||
|
}
|
||||||
|
if (parser.isSet(numeric)) {
|
||||||
|
classes |= PasswordGenerator::Numbers;
|
||||||
|
}
|
||||||
|
if (parser.isSet(special)) {
|
||||||
|
classes |= PasswordGenerator::SpecialCharacters;
|
||||||
|
}
|
||||||
|
if (parser.isSet(extended)) {
|
||||||
|
classes |= PasswordGenerator::EASCII;
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordGenerator.setCharClasses(classes);
|
||||||
|
passwordGenerator.setFlags(PasswordGenerator::DefaultFlags);
|
||||||
|
|
||||||
|
if (!passwordGenerator.isValid()) {
|
||||||
|
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString password = passwordGenerator.generatePassword();
|
||||||
|
outputTextStream << password << endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
31
src/cli/Generate.h
Normal file
31
src/cli/Generate.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSXC_GENERATE_H
|
||||||
|
#define KEEPASSXC_GENERATE_H
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class Generate : public Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Generate();
|
||||||
|
~Generate();
|
||||||
|
int execute(const QStringList& arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSXC_GENERATE_H
|
@ -29,15 +29,15 @@
|
|||||||
|
|
||||||
List::List()
|
List::List()
|
||||||
{
|
{
|
||||||
this->name = QString("ls");
|
name = QString("ls");
|
||||||
this->description = QObject::tr("List database entries.");
|
description = QObject::tr("List database entries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
List::~List()
|
List::~List()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int List::execute(QStringList arguments)
|
int List::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class List : public Command
|
|||||||
public:
|
public:
|
||||||
List();
|
List();
|
||||||
~List();
|
~List();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
int listGroup(Database* database, QString groupPath = QString(""));
|
int listGroup(Database* database, QString groupPath = QString(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,15 +31,15 @@
|
|||||||
|
|
||||||
Locate::Locate()
|
Locate::Locate()
|
||||||
{
|
{
|
||||||
this->name = QString("locate");
|
name = QString("locate");
|
||||||
this->description = QObject::tr("Find entries quickly.");
|
description = QObject::tr("Find entries quickly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Locate::~Locate()
|
Locate::~Locate()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Locate::execute(QStringList arguments)
|
int Locate::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
|
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
|
@ -25,7 +25,7 @@ class Locate : public Command
|
|||||||
public:
|
public:
|
||||||
Locate();
|
Locate();
|
||||||
~Locate();
|
~Locate();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
int locateEntry(Database* database, QString searchTerm);
|
int locateEntry(Database* database, QString searchTerm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,15 +26,15 @@
|
|||||||
|
|
||||||
Merge::Merge()
|
Merge::Merge()
|
||||||
{
|
{
|
||||||
this->name = QString("merge");
|
name = QString("merge");
|
||||||
this->description = QObject::tr("Merge two databases.");
|
description = QObject::tr("Merge two databases.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Merge::~Merge()
|
Merge::~Merge()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Merge::execute(QStringList arguments)
|
int Merge::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class Merge : public Command
|
|||||||
public:
|
public:
|
||||||
Merge();
|
Merge();
|
||||||
~Merge();
|
~Merge();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_MERGE_H
|
#endif // KEEPASSXC_MERGE_H
|
||||||
|
@ -34,15 +34,15 @@
|
|||||||
|
|
||||||
Remove::Remove()
|
Remove::Remove()
|
||||||
{
|
{
|
||||||
this->name = QString("rm");
|
name = QString("rm");
|
||||||
this->description = QString("Remove an entry from the database.");
|
description = QString("Remove an entry from the database.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Remove::~Remove()
|
Remove::~Remove()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Remove::execute(QStringList arguments)
|
int Remove::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class Remove : public Command
|
|||||||
public:
|
public:
|
||||||
Remove();
|
Remove();
|
||||||
~Remove();
|
~Remove();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
int removeEntry(Database* database, QString databasePath, QString entryPath);
|
int removeEntry(Database* database, QString databasePath, QString entryPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,15 +29,15 @@
|
|||||||
|
|
||||||
Show::Show()
|
Show::Show()
|
||||||
{
|
{
|
||||||
this->name = QString("show");
|
name = QString("show");
|
||||||
this->description = QObject::tr("Show an entry's information.");
|
description = QObject::tr("Show an entry's information.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Show::~Show()
|
Show::~Show()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Show::execute(QStringList arguments)
|
int Show::execute(const QStringList& arguments)
|
||||||
{
|
{
|
||||||
QTextStream out(stdout);
|
QTextStream out(stdout);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class Show : public Command
|
|||||||
public:
|
public:
|
||||||
Show();
|
Show();
|
||||||
~Show();
|
~Show();
|
||||||
int execute(QStringList arguments);
|
int execute(const QStringList& arguments);
|
||||||
int showEntry(Database* database, QStringList attributes, QString entryPath);
|
int showEntry(Database* database, QStringList attributes, QString entryPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ QString Utils::getPassword()
|
|||||||
* A valid and running event loop is needed to use the global QClipboard,
|
* A valid and running event loop is needed to use the global QClipboard,
|
||||||
* so we need to use this from the CLI.
|
* so we need to use this from the CLI.
|
||||||
*/
|
*/
|
||||||
int Utils::clipText(QString text)
|
int Utils::clipText(const QString& text)
|
||||||
{
|
{
|
||||||
|
|
||||||
QString programName = "";
|
QString programName = "";
|
||||||
|
@ -25,7 +25,7 @@ class Utils
|
|||||||
public:
|
public:
|
||||||
static void setStdinEcho(bool enable);
|
static void setStdinEcho(bool enable);
|
||||||
static QString getPassword();
|
static QString getPassword();
|
||||||
static int clipText(QString text);
|
static int clipText(const QString& text);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_UTILS_H
|
#endif // KEEPASSXC_UTILS_H
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.TH KEEPASSXC-CLI 1 "Aug 22, 2017"
|
.TH KEEPASSXC-CLI 1 "Jan 19, 2018"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager.
|
keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager.
|
||||||
@ -19,6 +19,9 @@ Adds a new entry to a database. A password can be generated (\fI-g\fP option), o
|
|||||||
.IP "clip [options] <database> <entry> [timeout]"
|
.IP "clip [options] <database> <entry> [timeout]"
|
||||||
Copies the password of a database entry to the clipboard. If multiple entries with the same name exist in different groups, only the password for the first one is going to be copied. For copying the password of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. Optionally, a timeout in seconds can be specified to automatically clear the clipboard.
|
Copies the password of a database entry to the clipboard. If multiple entries with the same name exist in different groups, only the password for the first one is going to be copied. For copying the password of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. Optionally, a timeout in seconds can be specified to automatically clear the clipboard.
|
||||||
|
|
||||||
|
.IP "diceware [options]"
|
||||||
|
Generate a random diceware passphrase.
|
||||||
|
|
||||||
.IP "edit [options] <database> <entry>"
|
.IP "edit [options] <database> <entry>"
|
||||||
Edits a database entry. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option).
|
Edits a database entry. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option).
|
||||||
|
|
||||||
@ -28,6 +31,9 @@ Estimates the entropy of a password. The password to estimate can be provided as
|
|||||||
.IP "extract [options] <database>"
|
.IP "extract [options] <database>"
|
||||||
Extracts and prints the contents of a database to standard output in XML format.
|
Extracts and prints the contents of a database to standard output in XML format.
|
||||||
|
|
||||||
|
.IP "generate [options]"
|
||||||
|
Generate a random password.
|
||||||
|
|
||||||
.IP "locate [options] <database> <term>"
|
.IP "locate [options] <database> <term>"
|
||||||
Locates all the entries that match a specific search term in a database.
|
Locates all the entries that match a specific search term in a database.
|
||||||
|
|
||||||
@ -104,6 +110,39 @@ with each attribute shown one-per-line in the given order. If no attributes are
|
|||||||
specified, a summary of the default attributes is given.
|
specified, a summary of the default attributes is given.
|
||||||
|
|
||||||
|
|
||||||
|
.SS "Diceware options"
|
||||||
|
|
||||||
|
.IP "-W, --words <count>"
|
||||||
|
Desired number of words for the generated passphrase. [Default: 7]
|
||||||
|
|
||||||
|
.IP "-w, --word-list <path>"
|
||||||
|
Path of the wordlist for the diceware generator. The wordlist must have > 1000 words,
|
||||||
|
otherwise the program will fail. If the wordlist has < 4000 words a warning will
|
||||||
|
be printed to STDERR.
|
||||||
|
|
||||||
|
|
||||||
|
.SS "Generate options"
|
||||||
|
|
||||||
|
.IP "-L, --length <length>"
|
||||||
|
Desired length for the generated password. [Default: 16]
|
||||||
|
|
||||||
|
.IP "-l"
|
||||||
|
Use lowercase characters for the generated password. [Default: Enabled]
|
||||||
|
|
||||||
|
.IP "-u"
|
||||||
|
Use uppercase characters for the generated password. [Default: Enabled]
|
||||||
|
|
||||||
|
.IP "-n"
|
||||||
|
Use numbers characters for the generated password. [Default: Enabled]
|
||||||
|
|
||||||
|
.IP "-s"
|
||||||
|
Use special characters for the generated password. [Default: Disabled]
|
||||||
|
|
||||||
|
.IP "-e"
|
||||||
|
Use extended ASCII characters for the generated password. [Default: Disabled]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
||||||
|
|
||||||
|
39
src/core/AutoTypeMatch.cpp
Normal file
39
src/core/AutoTypeMatch.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoTypeMatch.h"
|
||||||
|
|
||||||
|
AutoTypeMatch::AutoTypeMatch()
|
||||||
|
: entry(nullptr),
|
||||||
|
sequence()
|
||||||
|
{}
|
||||||
|
|
||||||
|
AutoTypeMatch::AutoTypeMatch(Entry* entry, QString sequence)
|
||||||
|
: entry(entry),
|
||||||
|
sequence(sequence)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool AutoTypeMatch::operator==(const AutoTypeMatch& other) const
|
||||||
|
{
|
||||||
|
return entry == other.entry && sequence == other.sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoTypeMatch::operator!=(const AutoTypeMatch& other) const
|
||||||
|
{
|
||||||
|
return entry != other.entry || sequence != other.sequence;
|
||||||
|
}
|
41
src/core/AutoTypeMatch.h
Normal file
41
src/core/AutoTypeMatch.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEMATCH_H
|
||||||
|
#define KEEPASSX_AUTOTYPEMATCH_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
struct AutoTypeMatch
|
||||||
|
{
|
||||||
|
Entry* entry;
|
||||||
|
QString sequence;
|
||||||
|
|
||||||
|
AutoTypeMatch();
|
||||||
|
AutoTypeMatch(Entry* entry, QString sequence);
|
||||||
|
|
||||||
|
bool operator==(const AutoTypeMatch& other) const;
|
||||||
|
bool operator!=(const AutoTypeMatch& other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_TYPEINFO(AutoTypeMatch, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEMATCH_H
|
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
const int Entry::DefaultIconNumber = 0;
|
const int Entry::DefaultIconNumber = 0;
|
||||||
const int Entry::ResolveMaximumDepth = 10;
|
const int Entry::ResolveMaximumDepth = 10;
|
||||||
|
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
|
||||||
|
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
|
||||||
|
|
||||||
|
|
||||||
Entry::Entry()
|
Entry::Entry()
|
||||||
@ -218,30 +220,37 @@ QString Entry::defaultAutoTypeSequence() const
|
|||||||
return m_data.defaultAutoTypeSequence;
|
return m_data.defaultAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the effective sequence that will be injected
|
||||||
|
* This function return an empty string if a parent group has autotype disabled or if the entry has no parent
|
||||||
|
*/
|
||||||
QString Entry::effectiveAutoTypeSequence() const
|
QString Entry::effectiveAutoTypeSequence() const
|
||||||
{
|
{
|
||||||
|
if (!autoTypeEnabled()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Group* parent = group();
|
||||||
|
if (!parent) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sequence = parent->effectiveAutoTypeSequence();
|
||||||
|
if (sequence.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_data.defaultAutoTypeSequence.isEmpty()) {
|
if (!m_data.defaultAutoTypeSequence.isEmpty()) {
|
||||||
return m_data.defaultAutoTypeSequence;
|
return m_data.defaultAutoTypeSequence;
|
||||||
}
|
}
|
||||||
QString sequence;
|
|
||||||
|
|
||||||
const Group* grp = group();
|
if (sequence == Group::RootAutoTypeSequence && (!username().isEmpty() || !password().isEmpty())) {
|
||||||
if(grp) {
|
|
||||||
sequence = grp->effectiveAutoTypeSequence();
|
|
||||||
} else {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sequence.isEmpty() && (!username().isEmpty() || !password().isEmpty())) {
|
|
||||||
if (username().isEmpty()) {
|
if (username().isEmpty()) {
|
||||||
sequence = "{PASSWORD}{ENTER}";
|
return AutoTypeSequencePassword;
|
||||||
}
|
} else if (password().isEmpty()) {
|
||||||
else if (password().isEmpty()) {
|
return AutoTypeSequenceUsername;
|
||||||
sequence = "{USERNAME}{ENTER}";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
|
||||||
}
|
}
|
||||||
|
return Group::RootAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sequence;
|
return sequence;
|
||||||
|
@ -85,6 +85,7 @@ public:
|
|||||||
int autoTypeObfuscation() const;
|
int autoTypeObfuscation() const;
|
||||||
QString defaultAutoTypeSequence() const;
|
QString defaultAutoTypeSequence() const;
|
||||||
QString effectiveAutoTypeSequence() const;
|
QString effectiveAutoTypeSequence() const;
|
||||||
|
QString effectiveNewAutoTypeSequence() const;
|
||||||
AutoTypeAssociations* autoTypeAssociations();
|
AutoTypeAssociations* autoTypeAssociations();
|
||||||
const AutoTypeAssociations* autoTypeAssociations() const;
|
const AutoTypeAssociations* autoTypeAssociations() const;
|
||||||
QString title() const;
|
QString title() const;
|
||||||
@ -109,6 +110,8 @@ public:
|
|||||||
|
|
||||||
static const int DefaultIconNumber;
|
static const int DefaultIconNumber;
|
||||||
static const int ResolveMaximumDepth;
|
static const int ResolveMaximumDepth;
|
||||||
|
static const QString AutoTypeSequenceUsername;
|
||||||
|
static const QString AutoTypeSequencePassword;
|
||||||
|
|
||||||
void setUuid(const Uuid& uuid);
|
void setUuid(const Uuid& uuid);
|
||||||
void setIcon(int iconNumber);
|
void setIcon(int iconNumber);
|
||||||
|
@ -91,6 +91,11 @@ QString FilePath::pluginPath(const QString& name)
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FilePath::wordlistPath(const QString& name)
|
||||||
|
{
|
||||||
|
return dataPath("wordlists/" + name);
|
||||||
|
}
|
||||||
|
|
||||||
QIcon FilePath::applicationIcon()
|
QIcon FilePath::applicationIcon()
|
||||||
{
|
{
|
||||||
bool darkIcon = useDarkIcon();
|
bool darkIcon = useDarkIcon();
|
||||||
|
@ -27,6 +27,7 @@ class FilePath
|
|||||||
public:
|
public:
|
||||||
QString dataPath(const QString& name);
|
QString dataPath(const QString& name);
|
||||||
QString pluginPath(const QString& name);
|
QString pluginPath(const QString& name);
|
||||||
|
QString wordlistPath(const QString& name);
|
||||||
QIcon applicationIcon();
|
QIcon applicationIcon();
|
||||||
QIcon trayIconLocked();
|
QIcon trayIconLocked();
|
||||||
QIcon trayIconUnlocked();
|
QIcon trayIconUnlocked();
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
const int Group::DefaultIconNumber = 48;
|
const int Group::DefaultIconNumber = 48;
|
||||||
const int Group::RecycleBinIconNumber = 43;
|
const int Group::RecycleBinIconNumber = 43;
|
||||||
|
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
||||||
|
|
||||||
Group::CloneFlags Group::DefaultCloneFlags = static_cast<Group::CloneFlags>(
|
Group::CloneFlags Group::DefaultCloneFlags = static_cast<Group::CloneFlags>(
|
||||||
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
|
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
|
||||||
@ -192,6 +193,10 @@ QString Group::defaultAutoTypeSequence() const
|
|||||||
return m_data.defaultAutoTypeSequence;
|
return m_data.defaultAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the effective sequence that will be injected
|
||||||
|
* This function return an empty string if the current group or any parent has autotype disabled
|
||||||
|
*/
|
||||||
QString Group::effectiveAutoTypeSequence() const
|
QString Group::effectiveAutoTypeSequence() const
|
||||||
{
|
{
|
||||||
QString sequence;
|
QString sequence;
|
||||||
@ -207,7 +212,7 @@ QString Group::effectiveAutoTypeSequence() const
|
|||||||
} while (group && sequence.isEmpty());
|
} while (group && sequence.isEmpty());
|
||||||
|
|
||||||
if (sequence.isEmpty()) {
|
if (sequence.isEmpty()) {
|
||||||
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
sequence = RootAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sequence;
|
return sequence;
|
||||||
|
@ -88,6 +88,7 @@ public:
|
|||||||
static const int RecycleBinIconNumber;
|
static const int RecycleBinIconNumber;
|
||||||
static CloneFlags DefaultCloneFlags;
|
static CloneFlags DefaultCloneFlags;
|
||||||
static Entry::CloneFlags DefaultEntryCloneFlags;
|
static Entry::CloneFlags DefaultEntryCloneFlags;
|
||||||
|
static const QString RootAutoTypeSequence;
|
||||||
|
|
||||||
Group* findChildByName(const QString& name);
|
Group* findChildByName(const QString& name);
|
||||||
Group* findChildByUuid(const Uuid& uuid);
|
Group* findChildByUuid(const Uuid& uuid);
|
||||||
|
@ -17,29 +17,31 @@
|
|||||||
|
|
||||||
#include "PassphraseGenerator.h"
|
#include "PassphraseGenerator.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <cmath>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
#include "crypto/Random.h"
|
#include "crypto/Random.h"
|
||||||
#include "core/FilePath.h"
|
#include "core/FilePath.h"
|
||||||
|
|
||||||
|
const char* PassphraseGenerator::DefaultSeparator = " ";
|
||||||
|
const char* PassphraseGenerator::DefaultWordList = "eff_large.wordlist";
|
||||||
|
|
||||||
PassphraseGenerator::PassphraseGenerator()
|
PassphraseGenerator::PassphraseGenerator()
|
||||||
: m_wordCount(0)
|
: m_wordCount(0)
|
||||||
, m_separator(' ')
|
, m_separator(PassphraseGenerator::DefaultSeparator)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double PassphraseGenerator::calculateEntropy(QString passphrase)
|
double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
||||||
{
|
{
|
||||||
Q_UNUSED(passphrase);
|
Q_UNUSED(passphrase);
|
||||||
|
|
||||||
if (m_wordlist.size() == 0) {
|
if (m_wordlist.isEmpty()) {
|
||||||
return 0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return log(m_wordlist.size()) / log(2.0) * m_wordCount;
|
return std::log2(m_wordlist.size()) * m_wordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordCount(int wordCount)
|
void PassphraseGenerator::setWordCount(int wordCount)
|
||||||
@ -48,12 +50,12 @@ void PassphraseGenerator::setWordCount(int wordCount)
|
|||||||
m_wordCount = wordCount;
|
m_wordCount = wordCount;
|
||||||
} else {
|
} else {
|
||||||
// safe default if something goes wrong
|
// safe default if something goes wrong
|
||||||
m_wordCount = 7;
|
m_wordCount = DefaultWordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordList(QString path)
|
void PassphraseGenerator::setWordList(const QString& path)
|
||||||
{
|
{
|
||||||
m_wordlist.clear();
|
m_wordlist.clear();
|
||||||
|
|
||||||
@ -76,11 +78,11 @@ void PassphraseGenerator::setWordList(QString path)
|
|||||||
|
|
||||||
void PassphraseGenerator::setDefaultWordList()
|
void PassphraseGenerator::setDefaultWordList()
|
||||||
{
|
{
|
||||||
const QString path = filePath()->dataPath("wordlists/eff_large.wordlist");
|
const QString path = filePath()->wordlistPath(PassphraseGenerator::DefaultWordList);
|
||||||
setWordList(path);
|
setWordList(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordSeparator(QString separator) {
|
void PassphraseGenerator::setWordSeparator(const QString& separator) {
|
||||||
m_separator = separator;
|
m_separator = separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +96,8 @@ QString PassphraseGenerator::generatePassphrase() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
QStringList words;
|
QStringList words;
|
||||||
for (int i = 0; i < m_wordCount; i++) {
|
for (int i = 0; i < m_wordCount; ++i) {
|
||||||
int wordIndex = randomGen()->randomUInt(m_wordlist.length());
|
int wordIndex = randomGen()->randomUInt(static_cast<quint32>(m_wordlist.length()));
|
||||||
words.append(m_wordlist.at(wordIndex));
|
words.append(m_wordlist.at(wordIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,9 +110,5 @@ bool PassphraseGenerator::isValid() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_wordlist.size() < 1000) {
|
return m_wordlist.size() >= 1000;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
@ -26,22 +26,25 @@ class PassphraseGenerator
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PassphraseGenerator();
|
PassphraseGenerator();
|
||||||
|
Q_DISABLE_COPY(PassphraseGenerator)
|
||||||
|
|
||||||
double calculateEntropy(QString passphrase);
|
double calculateEntropy(const QString& passphrase);
|
||||||
void setWordCount(int wordCount);
|
void setWordCount(int wordCount);
|
||||||
void setWordList(QString path);
|
void setWordList(const QString& path);
|
||||||
void setDefaultWordList();
|
void setDefaultWordList();
|
||||||
void setWordSeparator(QString separator);
|
void setWordSeparator(const QString& separator);
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
QString generatePassphrase() const;
|
QString generatePassphrase() const;
|
||||||
|
|
||||||
|
static constexpr int DefaultWordCount = 7;
|
||||||
|
static const char* DefaultSeparator;
|
||||||
|
static const char* DefaultWordList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_wordCount;
|
int m_wordCount;
|
||||||
QString m_separator;
|
QString m_separator;
|
||||||
QVector<QString> m_wordlist;
|
QVector<QString> m_wordlist;
|
||||||
|
|
||||||
Q_DISABLE_COPY(PassphraseGenerator)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_PASSPHRASEGENERATOR_H
|
#endif // KEEPASSX_PASSPHRASEGENERATOR_H
|
||||||
|
@ -35,11 +35,19 @@ double PasswordGenerator::calculateEntropy(QString password)
|
|||||||
|
|
||||||
void PasswordGenerator::setLength(int length)
|
void PasswordGenerator::setLength(int length)
|
||||||
{
|
{
|
||||||
|
if (length <= 0) {
|
||||||
|
m_length = DefaultLength;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_length = length;
|
m_length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasswordGenerator::setCharClasses(const CharClasses& classes)
|
void PasswordGenerator::setCharClasses(const CharClasses& classes)
|
||||||
{
|
{
|
||||||
|
if (classes == 0) {
|
||||||
|
m_classes = DefaultCharset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_classes = classes;
|
m_classes = classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +34,16 @@ public:
|
|||||||
UpperLetters = 0x2,
|
UpperLetters = 0x2,
|
||||||
Numbers = 0x4,
|
Numbers = 0x4,
|
||||||
SpecialCharacters = 0x8,
|
SpecialCharacters = 0x8,
|
||||||
EASCII = 0x10
|
EASCII = 0x10,
|
||||||
|
DefaultCharset = LowerLetters | UpperLetters | Numbers
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(CharClasses, CharClass)
|
Q_DECLARE_FLAGS(CharClasses, CharClass)
|
||||||
|
|
||||||
enum GeneratorFlag
|
enum GeneratorFlag
|
||||||
{
|
{
|
||||||
ExcludeLookAlike = 0x1,
|
ExcludeLookAlike = 0x1,
|
||||||
CharFromEveryGroup = 0x2
|
CharFromEveryGroup = 0x2,
|
||||||
|
DefaultFlags = ExcludeLookAlike | CharFromEveryGroup
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag)
|
Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag)
|
||||||
|
|
||||||
@ -59,6 +61,13 @@ public:
|
|||||||
int getbits() const;
|
int getbits() const;
|
||||||
|
|
||||||
static const int DefaultLength = 16;
|
static const int DefaultLength = 16;
|
||||||
|
static constexpr bool DefaultLower = (DefaultCharset & LowerLetters) != 0;
|
||||||
|
static constexpr bool DefaultUpper = (DefaultCharset & UpperLetters) != 0;
|
||||||
|
static constexpr bool DefaultNumbers = (DefaultCharset & Numbers) != 0;
|
||||||
|
static constexpr bool DefaultSpecial = (DefaultCharset & SpecialCharacters) != 0;
|
||||||
|
static constexpr bool DefaultEASCII = (DefaultCharset & EASCII) != 0;
|
||||||
|
static constexpr bool DefaultLookAlike = (DefaultFlags & ExcludeLookAlike) != 0;
|
||||||
|
static constexpr bool DefaultFromEveryGroup = (DefaultFlags & CharFromEveryGroup) != 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<PasswordGroup> passwordGroups() const;
|
QVector<PasswordGroup> passwordGroups() const;
|
||||||
|
@ -57,6 +57,7 @@ bool SymmetricCipher::isInitalized() const
|
|||||||
SymmetricCipherBackend* SymmetricCipher::createBackend(Algorithm algo, Mode mode, Direction direction)
|
SymmetricCipherBackend* SymmetricCipher::createBackend(Algorithm algo, Mode mode, Direction direction)
|
||||||
{
|
{
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
|
case Aes128:
|
||||||
case Aes256:
|
case Aes256:
|
||||||
case Twofish:
|
case Twofish:
|
||||||
case Salsa20:
|
case Salsa20:
|
||||||
|
@ -31,6 +31,7 @@ class SymmetricCipher
|
|||||||
public:
|
public:
|
||||||
enum Algorithm
|
enum Algorithm
|
||||||
{
|
{
|
||||||
|
Aes128,
|
||||||
Aes256,
|
Aes256,
|
||||||
Twofish,
|
Twofish,
|
||||||
Salsa20,
|
Salsa20,
|
||||||
|
@ -37,6 +37,9 @@ SymmetricCipherGcrypt::~SymmetricCipherGcrypt()
|
|||||||
int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo)
|
int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo)
|
||||||
{
|
{
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
|
case SymmetricCipher::Aes128:
|
||||||
|
return GCRY_CIPHER_AES128;
|
||||||
|
|
||||||
case SymmetricCipher::Aes256:
|
case SymmetricCipher::Aes256:
|
||||||
return GCRY_CIPHER_AES256;
|
return GCRY_CIPHER_AES256;
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ const int DatabaseTabWidget::LastDatabasesCount = 5;
|
|||||||
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
||||||
: QTabWidget(parent)
|
: QTabWidget(parent)
|
||||||
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
|
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
|
||||||
|
, m_dbPendingLock(nullptr)
|
||||||
{
|
{
|
||||||
DragTabBar* tabBar = new DragTabBar(this);
|
DragTabBar* tabBar = new DragTabBar(this);
|
||||||
setTabBar(tabBar);
|
setTabBar(tabBar);
|
||||||
@ -63,6 +64,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
|||||||
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
|
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
|
||||||
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
|
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
|
||||||
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
||||||
|
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseTabWidget::~DatabaseTabWidget()
|
DatabaseTabWidget::~DatabaseTabWidget()
|
||||||
@ -737,6 +739,27 @@ void DatabaseTabWidget::lockDatabases()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function relock the pending database when autotype has been performed successfully
|
||||||
|
* A database is marked as pending when it's unlocked after a global Auto-Type invocation
|
||||||
|
*/
|
||||||
|
void DatabaseTabWidget::relockPendingDatabase()
|
||||||
|
{
|
||||||
|
if (!m_dbPendingLock || !config()->get("security/relockautotype").toBool()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_dbPendingLock->currentMode() == DatabaseWidget::LockedMode || !m_dbPendingLock->dbHasKey()) {
|
||||||
|
m_dbPendingLock = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dbPendingLock->lock();
|
||||||
|
|
||||||
|
emit databaseLocked(m_dbPendingLock);
|
||||||
|
m_dbPendingLock = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void DatabaseTabWidget::modified()
|
void DatabaseTabWidget::modified()
|
||||||
{
|
{
|
||||||
Q_ASSERT(qobject_cast<Database*>(sender()));
|
Q_ASSERT(qobject_cast<Database*>(sender()));
|
||||||
@ -827,6 +850,7 @@ void DatabaseTabWidget::performGlobalAutoType()
|
|||||||
if (unlockedDatabases.size() > 0) {
|
if (unlockedDatabases.size() > 0) {
|
||||||
autoType()->performGlobalAutoType(unlockedDatabases);
|
autoType()->performGlobalAutoType(unlockedDatabases);
|
||||||
} else if (m_dbList.size() > 0){
|
} else if (m_dbList.size() > 0){
|
||||||
indexDatabaseManagerStruct(0).dbWidget->showUnlockDialog();
|
m_dbPendingLock = indexDatabaseManagerStruct(0).dbWidget;
|
||||||
|
m_dbPendingLock->showUnlockDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ public slots:
|
|||||||
bool isModified(int index = -1);
|
bool isModified(int index = -1);
|
||||||
void performGlobalAutoType();
|
void performGlobalAutoType();
|
||||||
void lockDatabases();
|
void lockDatabases();
|
||||||
|
void relockPendingDatabase();
|
||||||
QString databasePath(int index = -1);
|
QString databasePath(int index = -1);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -117,6 +118,7 @@ private:
|
|||||||
|
|
||||||
QHash<Database*, DatabaseManagerStruct> m_dbList;
|
QHash<Database*, DatabaseManagerStruct> m_dbList;
|
||||||
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
|
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
|
||||||
|
QPointer<DatabaseWidget> m_dbPendingLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_DATABASETABWIDGET_H
|
#endif // KEEPASSX_DATABASETABWIDGET_H
|
||||||
|
@ -31,10 +31,9 @@
|
|||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
|
|
||||||
#ifdef WITH_XC_NETWORKING
|
#ifdef WITH_XC_NETWORKING
|
||||||
#include "http/qhttp/qhttpclient.hpp"
|
#include <curl/curl.h>
|
||||||
#include "http/qhttp/qhttpclientresponse.hpp"
|
#include "core/AsyncTask.h"
|
||||||
|
#undef MessageBox
|
||||||
using namespace qhttp::client;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
IconStruct::IconStruct()
|
IconStruct::IconStruct()
|
||||||
@ -49,10 +48,6 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
|||||||
, m_database(nullptr)
|
, m_database(nullptr)
|
||||||
, m_defaultIconModel(new DefaultIconModel(this))
|
, m_defaultIconModel(new DefaultIconModel(this))
|
||||||
, m_customIconModel(new CustomIconModel(this))
|
, m_customIconModel(new CustomIconModel(this))
|
||||||
#ifdef WITH_XC_NETWORKING
|
|
||||||
, m_fallbackToGoogle(true)
|
|
||||||
, m_redirectCount(0)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
@ -88,17 +83,14 @@ IconStruct EditWidgetIcons::state()
|
|||||||
QModelIndex index = m_ui->defaultIconsView->currentIndex();
|
QModelIndex index = m_ui->defaultIconsView->currentIndex();
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
iconStruct.number = index.row();
|
iconStruct.number = index.row();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
QModelIndex index = m_ui->customIconsView->currentIndex();
|
QModelIndex index = m_ui->customIconsView->currentIndex();
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
iconStruct.uuid = m_customIconModel->uuidFromIndex(m_ui->customIconsView->currentIndex());
|
iconStruct.uuid = m_customIconModel->uuidFromIndex(m_ui->customIconsView->currentIndex());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
iconStruct.number = -1;
|
iconStruct.number = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,14 +121,12 @@ void EditWidgetIcons::load(const Uuid& currentUuid, Database* database, const Ic
|
|||||||
int iconNumber = iconStruct.number;
|
int iconNumber = iconStruct.number;
|
||||||
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(iconNumber, 0));
|
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(iconNumber, 0));
|
||||||
m_ui->defaultIconsRadio->setChecked(true);
|
m_ui->defaultIconsRadio->setChecked(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
QModelIndex index = m_customIconModel->indexFromUuid(iconUuid);
|
QModelIndex index = m_customIconModel->indexFromUuid(iconUuid);
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
m_ui->customIconsView->setCurrentIndex(index);
|
m_ui->customIconsView->setCurrentIndex(index);
|
||||||
m_ui->customIconsRadio->setChecked(true);
|
m_ui->customIconsRadio->setChecked(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
|
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
|
||||||
m_ui->defaultIconsRadio->setChecked(true);
|
m_ui->defaultIconsRadio->setChecked(true);
|
||||||
}
|
}
|
||||||
@ -148,7 +138,6 @@ void EditWidgetIcons::setUrl(const QString& url)
|
|||||||
#ifdef WITH_XC_NETWORKING
|
#ifdef WITH_XC_NETWORKING
|
||||||
m_url = url;
|
m_url = url;
|
||||||
m_ui->faviconButton->setVisible(!url.isEmpty());
|
m_ui->faviconButton->setVisible(!url.isEmpty());
|
||||||
resetFaviconDownload();
|
|
||||||
#else
|
#else
|
||||||
Q_UNUSED(url);
|
Q_UNUSED(url);
|
||||||
m_ui->faviconButton->setVisible(false);
|
m_ui->faviconButton->setVisible(false);
|
||||||
@ -158,107 +147,75 @@ void EditWidgetIcons::setUrl(const QString& url)
|
|||||||
void EditWidgetIcons::downloadFavicon()
|
void EditWidgetIcons::downloadFavicon()
|
||||||
{
|
{
|
||||||
#ifdef WITH_XC_NETWORKING
|
#ifdef WITH_XC_NETWORKING
|
||||||
|
m_ui->faviconButton->setDisabled(true);
|
||||||
|
|
||||||
QUrl url = QUrl(m_url);
|
QUrl url = QUrl(m_url);
|
||||||
url.setPath("/favicon.ico");
|
url.setPath("/favicon.ico");
|
||||||
fetchFavicon(url);
|
// Attempt to simply load the favicon.ico file
|
||||||
|
QImage image = fetchFavicon(url);
|
||||||
|
if (!image.isNull()) {
|
||||||
|
addCustomIcon(image);
|
||||||
|
} else if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) {
|
||||||
|
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons");
|
||||||
|
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(url.host()));
|
||||||
|
// Attempt to load favicon from Google
|
||||||
|
image = fetchFavicon(faviconUrl);
|
||||||
|
if (!image.isNull()) {
|
||||||
|
addCustomIcon(image);
|
||||||
|
} else {
|
||||||
|
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" +
|
||||||
|
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"),
|
||||||
|
MessageWidget::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->faviconButton->setDisabled(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_XC_NETWORKING
|
#ifdef WITH_XC_NETWORKING
|
||||||
void EditWidgetIcons::fetchFavicon(const QUrl& url)
|
namespace {
|
||||||
|
std::size_t writeCurlResponse(char* ptr, std::size_t size, std::size_t nmemb, void* data)
|
||||||
{
|
{
|
||||||
if (nullptr == m_httpClient) {
|
QByteArray* response = static_cast<QByteArray*>(data);
|
||||||
m_httpClient = new QHttpClient(this);
|
std::size_t realsize = size * nmemb;
|
||||||
}
|
response->append(ptr, realsize);
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool requestMade = m_httpClient->request(qhttp::EHTTP_GET, url, [this, url](QHttpResponse* response) {
|
QImage EditWidgetIcons::fetchFavicon(const QUrl& url)
|
||||||
if (m_database == nullptr) {
|
{
|
||||||
return;
|
QImage image;
|
||||||
}
|
CURL* curl = curl_easy_init();
|
||||||
|
if (curl) {
|
||||||
|
QByteArray imagedata;
|
||||||
|
QByteArray baUrl = url.url().toLatin1();
|
||||||
|
|
||||||
response->collectData();
|
curl_easy_setopt(curl, CURLOPT_URL, baUrl.data());
|
||||||
response->onEnd([this, response, &url]() {
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
|
||||||
int status = response->status();
|
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||||
if (200 == status) {
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
QImage image;
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
|
||||||
image.loadFromData(response->collectedData());
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &imagedata);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCurlResponse);
|
||||||
|
|
||||||
if (!image.isNull()) {
|
// Perform the request in another thread
|
||||||
addCustomIcon(image);
|
CURLcode result = AsyncTask::runAndWaitForFuture([curl]() {
|
||||||
resetFaviconDownload();
|
return curl_easy_perform(curl);
|
||||||
} else {
|
|
||||||
fetchFaviconFromGoogle(url.host());
|
|
||||||
}
|
|
||||||
} else if (301 == status || 302 == status) {
|
|
||||||
// Check if server has sent a redirect
|
|
||||||
QUrl possibleRedirectUrl(response->headers().value("location", ""));
|
|
||||||
if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != m_redirectUrl && m_redirectCount < 3) {
|
|
||||||
resetFaviconDownload(false);
|
|
||||||
m_redirectUrl = possibleRedirectUrl;
|
|
||||||
++m_redirectCount;
|
|
||||||
fetchFavicon(m_redirectUrl);
|
|
||||||
} else {
|
|
||||||
// website is trying to redirect to itself or
|
|
||||||
// maximum number of redirects has been reached, fall back to Google
|
|
||||||
fetchFaviconFromGoogle(url.host());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fetchFaviconFromGoogle(url.host());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (!requestMade) {
|
if (result == CURLE_OK) {
|
||||||
resetFaviconDownload();
|
image.loadFromData(imagedata);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_httpClient->setConnectingTimeOut(5000, [this]() {
|
|
||||||
QUrl tempurl = QUrl(m_url);
|
|
||||||
if (tempurl.scheme() == "http") {
|
|
||||||
resetFaviconDownload();
|
|
||||||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" +
|
|
||||||
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"),
|
|
||||||
MessageWidget::Error);
|
|
||||||
} else {
|
|
||||||
tempurl.setScheme("http");
|
|
||||||
m_url = tempurl.url();
|
|
||||||
tempurl.setPath("/favicon.ico");
|
|
||||||
fetchFavicon(tempurl);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
m_ui->faviconButton->setDisabled(true);
|
curl_easy_cleanup(curl);
|
||||||
}
|
|
||||||
|
|
||||||
void EditWidgetIcons::fetchFaviconFromGoogle(const QString& domain)
|
|
||||||
{
|
|
||||||
if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool() && m_fallbackToGoogle) {
|
|
||||||
resetFaviconDownload();
|
|
||||||
m_fallbackToGoogle = false;
|
|
||||||
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons");
|
|
||||||
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(domain));
|
|
||||||
fetchFavicon(faviconUrl);
|
|
||||||
} else {
|
|
||||||
resetFaviconDownload();
|
|
||||||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditWidgetIcons::resetFaviconDownload(bool clearRedirect)
|
|
||||||
{
|
|
||||||
if (clearRedirect) {
|
|
||||||
m_redirectUrl.clear();
|
|
||||||
m_redirectCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullptr != m_httpClient) {
|
return image;
|
||||||
m_httpClient->deleteLater();
|
|
||||||
m_httpClient = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_fallbackToGoogle = true;
|
|
||||||
m_ui->faviconButton->setDisabled(false);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -281,7 +238,7 @@ void EditWidgetIcons::addCustomIconFromFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWidgetIcons::addCustomIcon(const QImage &icon)
|
void EditWidgetIcons::addCustomIcon(const QImage& icon)
|
||||||
{
|
{
|
||||||
if (m_database) {
|
if (m_database) {
|
||||||
Uuid uuid = m_database->metadata()->findCustomIcon(icon);
|
Uuid uuid = m_database->metadata()->findCustomIcon(icon);
|
||||||
@ -392,8 +349,7 @@ void EditWidgetIcons::updateWidgetsDefaultIcons(bool check)
|
|||||||
QModelIndex index = m_ui->defaultIconsView->currentIndex();
|
QModelIndex index = m_ui->defaultIconsView->currentIndex();
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
|
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
m_ui->defaultIconsView->setCurrentIndex(index);
|
m_ui->defaultIconsView->setCurrentIndex(index);
|
||||||
}
|
}
|
||||||
m_ui->customIconsView->selectionModel()->clearSelection();
|
m_ui->customIconsView->selectionModel()->clearSelection();
|
||||||
@ -408,8 +364,7 @@ void EditWidgetIcons::updateWidgetsCustomIcons(bool check)
|
|||||||
QModelIndex index = m_ui->customIconsView->currentIndex();
|
QModelIndex index = m_ui->customIconsView->currentIndex();
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
m_ui->customIconsView->setCurrentIndex(m_customIconModel->index(0, 0));
|
m_ui->customIconsView->setCurrentIndex(m_customIconModel->index(0, 0));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
m_ui->customIconsView->setCurrentIndex(index);
|
m_ui->customIconsView->setCurrentIndex(index);
|
||||||
}
|
}
|
||||||
m_ui->defaultIconsView->selectionModel()->clearSelection();
|
m_ui->defaultIconsView->selectionModel()->clearSelection();
|
||||||
|
@ -32,14 +32,6 @@ class Database;
|
|||||||
class DefaultIconModel;
|
class DefaultIconModel;
|
||||||
class CustomIconModel;
|
class CustomIconModel;
|
||||||
|
|
||||||
#ifdef WITH_XC_NETWORKING
|
|
||||||
namespace qhttp {
|
|
||||||
namespace client {
|
|
||||||
class QHttpClient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class EditWidgetIcons;
|
class EditWidgetIcons;
|
||||||
}
|
}
|
||||||
@ -74,9 +66,7 @@ signals:
|
|||||||
private slots:
|
private slots:
|
||||||
void downloadFavicon();
|
void downloadFavicon();
|
||||||
#ifdef WITH_XC_NETWORKING
|
#ifdef WITH_XC_NETWORKING
|
||||||
void fetchFavicon(const QUrl& url);
|
QImage fetchFavicon(const QUrl& url);
|
||||||
void fetchFaviconFromGoogle(const QString& domain);
|
|
||||||
void resetFaviconDownload(bool clearRedirect = true);
|
|
||||||
#endif
|
#endif
|
||||||
void addCustomIconFromFile();
|
void addCustomIconFromFile();
|
||||||
void addCustomIcon(const QImage& icon);
|
void addCustomIcon(const QImage& icon);
|
||||||
@ -93,12 +83,6 @@ private:
|
|||||||
QString m_url;
|
QString m_url;
|
||||||
DefaultIconModel* const m_defaultIconModel;
|
DefaultIconModel* const m_defaultIconModel;
|
||||||
CustomIconModel* const m_customIconModel;
|
CustomIconModel* const m_customIconModel;
|
||||||
#ifdef WITH_XC_NETWORKING
|
|
||||||
QUrl m_redirectUrl;
|
|
||||||
bool m_fallbackToGoogle;
|
|
||||||
unsigned short m_redirectCount;
|
|
||||||
qhttp::client::QHttpClient* m_httpClient = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Q_DISABLE_COPY(EditWidgetIcons)
|
Q_DISABLE_COPY(EditWidgetIcons)
|
||||||
};
|
};
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
class HttpPlugin: public ISettingsPage
|
class HttpPlugin: public ISettingsPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HttpPlugin(DatabaseTabWidget * tabWidget)
|
HttpPlugin(DatabaseTabWidget* tabWidget)
|
||||||
{
|
{
|
||||||
m_service = new Service(tabWidget);
|
m_service = new Service(tabWidget);
|
||||||
}
|
}
|
||||||
@ -88,18 +88,18 @@ public:
|
|||||||
|
|
||||||
QWidget * createWidget() override
|
QWidget * createWidget() override
|
||||||
{
|
{
|
||||||
OptionDialog * dlg = new OptionDialog();
|
OptionDialog* dlg = new OptionDialog();
|
||||||
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys()));
|
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys()));
|
||||||
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions()));
|
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions()));
|
||||||
return dlg;
|
return dlg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadSettings(QWidget * widget) override
|
void loadSettings(QWidget* widget) override
|
||||||
{
|
{
|
||||||
qobject_cast<OptionDialog*>(widget)->loadSettings();
|
qobject_cast<OptionDialog*>(widget)->loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveSettings(QWidget * widget) override
|
void saveSettings(QWidget* widget) override
|
||||||
{
|
{
|
||||||
qobject_cast<OptionDialog*>(widget)->saveSettings();
|
qobject_cast<OptionDialog*>(widget)->saveSettings();
|
||||||
if (HttpSettings::isEnabled())
|
if (HttpSettings::isEnabled())
|
||||||
@ -108,7 +108,7 @@ public:
|
|||||||
m_service->stop();
|
m_service->stop();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Service *m_service;
|
Service* m_service;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -68,9 +68,9 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set default separator to Space
|
// set default separator to Space
|
||||||
m_ui->editWordSeparator->setText(" ");
|
m_ui->editWordSeparator->setText(PassphraseGenerator::DefaultSeparator);
|
||||||
|
|
||||||
QDir path(filePath()->dataPath("wordlists/"));
|
QDir path(filePath()->wordlistPath(""));
|
||||||
QStringList files = path.entryList(QDir::Files);
|
QStringList files = path.entryList(QDir::Files);
|
||||||
m_ui->comboBoxWordList->addItems(files);
|
m_ui->comboBoxWordList->addItems(files);
|
||||||
if (files.size() > 1) {
|
if (files.size() > 1) {
|
||||||
@ -93,19 +93,19 @@ PasswordGeneratorWidget::~PasswordGeneratorWidget()
|
|||||||
void PasswordGeneratorWidget::loadSettings()
|
void PasswordGeneratorWidget::loadSettings()
|
||||||
{
|
{
|
||||||
// Password config
|
// Password config
|
||||||
m_ui->checkBoxLower->setChecked(config()->get("generator/LowerCase", true).toBool());
|
m_ui->checkBoxLower->setChecked(config()->get("generator/LowerCase", PasswordGenerator::DefaultLower).toBool());
|
||||||
m_ui->checkBoxUpper->setChecked(config()->get("generator/UpperCase", true).toBool());
|
m_ui->checkBoxUpper->setChecked(config()->get("generator/UpperCase", PasswordGenerator::DefaultUpper).toBool());
|
||||||
m_ui->checkBoxNumbers->setChecked(config()->get("generator/Numbers", true).toBool());
|
m_ui->checkBoxNumbers->setChecked(config()->get("generator/Numbers", PasswordGenerator::DefaultNumbers).toBool());
|
||||||
m_ui->checkBoxSpecialChars->setChecked(config()->get("generator/SpecialChars", false).toBool());
|
m_ui->checkBoxSpecialChars->setChecked(config()->get("generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool());
|
||||||
m_ui->checkBoxExtASCII->setChecked(config()->get("generator/EASCII", false).toBool());
|
m_ui->checkBoxExtASCII->setChecked(config()->get("generator/EASCII", PasswordGenerator::DefaultEASCII).toBool());
|
||||||
m_ui->checkBoxExcludeAlike->setChecked(config()->get("generator/ExcludeAlike", true).toBool());
|
m_ui->checkBoxExcludeAlike->setChecked(config()->get("generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool());
|
||||||
m_ui->checkBoxEnsureEvery->setChecked(config()->get("generator/EnsureEvery", true).toBool());
|
m_ui->checkBoxEnsureEvery->setChecked(config()->get("generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool());
|
||||||
m_ui->spinBoxLength->setValue(config()->get("generator/Length", PasswordGenerator::DefaultLength).toInt());
|
m_ui->spinBoxLength->setValue(config()->get("generator/Length", PasswordGenerator::DefaultLength).toInt());
|
||||||
|
|
||||||
// Diceware config
|
// Diceware config
|
||||||
m_ui->spinBoxWordCount->setValue(config()->get("generator/WordCount", 6).toInt());
|
m_ui->spinBoxWordCount->setValue(config()->get("generator/WordCount", PassphraseGenerator::DefaultWordCount).toInt());
|
||||||
m_ui->editWordSeparator->setText(config()->get("generator/WordSeparator", " ").toString());
|
m_ui->editWordSeparator->setText(config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString());
|
||||||
m_ui->comboBoxWordList->setCurrentText(config()->get("generator/WordList", "eff_large.wordlist").toString());
|
m_ui->comboBoxWordList->setCurrentText(config()->get("generator/WordList", PassphraseGenerator::DefaultWordList).toString());
|
||||||
|
|
||||||
// Password or diceware?
|
// Password or diceware?
|
||||||
m_ui->tabWidget->setCurrentIndex(config()->get("generator/Type", 0).toInt());
|
m_ui->tabWidget->setCurrentIndex(config()->get("generator/Type", 0).toInt());
|
||||||
@ -394,7 +394,7 @@ void PasswordGeneratorWidget::updateGenerator()
|
|||||||
|
|
||||||
m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value());
|
m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value());
|
||||||
if (!m_ui->comboBoxWordList->currentText().isEmpty()) {
|
if (!m_ui->comboBoxWordList->currentText().isEmpty()) {
|
||||||
QString path = filePath()->dataPath("wordlists/" + m_ui->comboBoxWordList->currentText());
|
QString path = filePath()->wordlistPath(m_ui->comboBoxWordList->currentText());
|
||||||
m_dicewareGenerator->setWordList(path);
|
m_dicewareGenerator->setWordList(path);
|
||||||
}
|
}
|
||||||
m_dicewareGenerator->setWordSeparator(m_ui->editWordSeparator->text());
|
m_dicewareGenerator->setWordSeparator(m_ui->editWordSeparator->text());
|
||||||
|
@ -161,6 +161,7 @@ void SettingsWidget::loadSettings()
|
|||||||
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
|
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
|
||||||
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool());
|
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool());
|
||||||
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool());
|
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool());
|
||||||
|
m_secUi->relockDatabaseAutoTypeCheckBox->setChecked(config()->get("security/relockautotype").toBool());
|
||||||
m_secUi->fallbackToGoogle->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
|
m_secUi->fallbackToGoogle->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
|
||||||
|
|
||||||
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
|
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
|
||||||
@ -233,6 +234,7 @@ void SettingsWidget::saveSettings()
|
|||||||
config()->set("security/lockdatabaseidlesec", m_secUi->lockDatabaseIdleSpinBox->value());
|
config()->set("security/lockdatabaseidlesec", m_secUi->lockDatabaseIdleSpinBox->value());
|
||||||
config()->set("security/lockdatabaseminimize", m_secUi->lockDatabaseMinimizeCheckBox->isChecked());
|
config()->set("security/lockdatabaseminimize", m_secUi->lockDatabaseMinimizeCheckBox->isChecked());
|
||||||
config()->set("security/lockdatabasescreenlock", m_secUi->lockDatabaseOnScreenLockCheckBox->isChecked());
|
config()->set("security/lockdatabasescreenlock", m_secUi->lockDatabaseOnScreenLockCheckBox->isChecked());
|
||||||
|
config()->set("security/relockautotype", m_secUi->relockDatabaseAutoTypeCheckBox->isChecked());
|
||||||
config()->set("security/IconDownloadFallbackToGoogle", m_secUi->fallbackToGoogle->isChecked());
|
config()->set("security/IconDownloadFallbackToGoogle", m_secUi->fallbackToGoogle->isChecked());
|
||||||
|
|
||||||
config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked());
|
config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked());
|
||||||
|
@ -122,6 +122,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QCheckBox" name="relockDatabaseAutoTypeCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Re-lock previously locked database after performing Auto-Type</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="passwordRepeatCheckBox">
|
<widget class="QCheckBox" name="passwordRepeatCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
201
src/gui/entry/AutoTypeMatchModel.cpp
Normal file
201
src/gui/entry/AutoTypeMatchModel.cpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoTypeMatchModel.h"
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
|
||||||
|
#include "core/DatabaseIcons.h"
|
||||||
|
#include "core/Entry.h"
|
||||||
|
#include "core/Global.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
|
|
||||||
|
AutoTypeMatchModel::AutoTypeMatchModel(QObject* parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeMatch AutoTypeMatchModel::matchFromIndex(const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(index.isValid() && index.row() < m_matches.size());
|
||||||
|
return m_matches.at(index.row());
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex AutoTypeMatchModel::indexFromMatch(AutoTypeMatch match) const
|
||||||
|
{
|
||||||
|
int row = m_matches.indexOf(match);
|
||||||
|
Q_ASSERT(row != -1);
|
||||||
|
return index(row, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::setMatchList(const QList<AutoTypeMatch>& matches)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
severConnections();
|
||||||
|
|
||||||
|
m_allGroups.clear();
|
||||||
|
m_matches = matches;
|
||||||
|
|
||||||
|
QSet<Database*> databases;
|
||||||
|
|
||||||
|
for (AutoTypeMatch& match : m_matches) {
|
||||||
|
databases.insert(match.entry->group()->database());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Database* db : asConst(databases)) {
|
||||||
|
Q_ASSERT(db);
|
||||||
|
for (const Group* group : db->rootGroup()->groupsRecursive(true)) {
|
||||||
|
m_allGroups.append(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db->metadata()->recycleBin()) {
|
||||||
|
m_allGroups.removeOne(db->metadata()->recycleBin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Group* group : asConst(m_allGroups)) {
|
||||||
|
makeConnections(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypeMatchModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_matches.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypeMatchModel::columnCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant AutoTypeMatchModel::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeMatch match = matchFromIndex(index);
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
QString result;
|
||||||
|
switch (index.column()) {
|
||||||
|
case ParentGroup:
|
||||||
|
if (match.entry->group()) {
|
||||||
|
return match.entry->group()->name();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Title:
|
||||||
|
return match.entry->resolveMultiplePlaceholders(match.entry->title());
|
||||||
|
case Username:
|
||||||
|
return match.entry->resolveMultiplePlaceholders(match.entry->username());
|
||||||
|
case Sequence:
|
||||||
|
return match.sequence;
|
||||||
|
}
|
||||||
|
} else if (role == Qt::DecorationRole) {
|
||||||
|
switch (index.column()) {
|
||||||
|
case ParentGroup:
|
||||||
|
if (match.entry->group()) {
|
||||||
|
return match.entry->group()->iconScaledPixmap();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Title:
|
||||||
|
if (match.entry->isExpired()) {
|
||||||
|
return databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex);
|
||||||
|
} else {
|
||||||
|
return match.entry->iconScaledPixmap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (role == Qt::FontRole) {
|
||||||
|
QFont font;
|
||||||
|
if (match.entry->isExpired()) {
|
||||||
|
font.setStrikeOut(true);
|
||||||
|
}
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant AutoTypeMatchModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||||
|
switch (section) {
|
||||||
|
case ParentGroup:
|
||||||
|
return tr("Group");
|
||||||
|
case Title:
|
||||||
|
return tr("Title");
|
||||||
|
case Username:
|
||||||
|
return tr("Username");
|
||||||
|
case Sequence:
|
||||||
|
return tr("Sequence");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::entryDataChanged(Entry* entry)
|
||||||
|
{
|
||||||
|
for (int row = 0; row < m_matches.size(); ++row) {
|
||||||
|
AutoTypeMatch match = m_matches[row];
|
||||||
|
if (match.entry == entry) {
|
||||||
|
emit dataChanged(index(row, 0), index(row, columnCount()-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::entryAboutToRemove(Entry* entry)
|
||||||
|
{
|
||||||
|
for (int row = 0; row < m_matches.size(); ++row) {
|
||||||
|
AutoTypeMatch match = m_matches[row];
|
||||||
|
if (match.entry == entry) {
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_matches.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
--row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::entryRemoved()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::severConnections()
|
||||||
|
{
|
||||||
|
for (const Group* group : asConst(m_allGroups)) {
|
||||||
|
disconnect(group, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchModel::makeConnections(const Group* group)
|
||||||
|
{
|
||||||
|
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*)));
|
||||||
|
connect(group, SIGNAL(entryRemoved(Entry*)), SLOT(entryRemoved()));
|
||||||
|
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
|
||||||
|
}
|
66
src/gui/entry/AutoTypeMatchModel.h
Normal file
66
src/gui/entry/AutoTypeMatchModel.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEMATCHMODEL_H
|
||||||
|
#define KEEPASSX_AUTOTYPEMATCHMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
class Group;
|
||||||
|
|
||||||
|
class AutoTypeMatchModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ModelColumn
|
||||||
|
{
|
||||||
|
ParentGroup = 0,
|
||||||
|
Title = 1,
|
||||||
|
Username = 2,
|
||||||
|
Sequence = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AutoTypeMatchModel(QObject* parent = nullptr);
|
||||||
|
AutoTypeMatch matchFromIndex(const QModelIndex& index) const;
|
||||||
|
QModelIndex indexFromMatch(AutoTypeMatch match) const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
void setMatchList(const QList<AutoTypeMatch>& matches);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void entryAboutToRemove(Entry* entry);
|
||||||
|
void entryRemoved();
|
||||||
|
void entryDataChanged(Entry* entry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void severConnections();
|
||||||
|
void makeConnections(const Group* group);
|
||||||
|
|
||||||
|
QList<AutoTypeMatch> m_matches;
|
||||||
|
QList<const Group*> m_allGroups;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEMATCHMODEL_H
|
112
src/gui/entry/AutoTypeMatchView.cpp
Normal file
112
src/gui/entry/AutoTypeMatchView.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoTypeMatchView.h"
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
#include "gui/SortFilterHideProxyModel.h"
|
||||||
|
|
||||||
|
AutoTypeMatchView::AutoTypeMatchView(QWidget* parent)
|
||||||
|
: QTreeView(parent)
|
||||||
|
, m_model(new AutoTypeMatchModel(this))
|
||||||
|
, m_sortModel(new SortFilterHideProxyModel(this))
|
||||||
|
{
|
||||||
|
m_sortModel->setSourceModel(m_model);
|
||||||
|
m_sortModel->setDynamicSortFilter(true);
|
||||||
|
m_sortModel->setSortLocaleAware(true);
|
||||||
|
m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
QTreeView::setModel(m_sortModel);
|
||||||
|
|
||||||
|
setUniformRowHeights(true);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setAlternatingRowColors(true);
|
||||||
|
setDragEnabled(false);
|
||||||
|
setSortingEnabled(true);
|
||||||
|
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
header()->setDefaultSectionSize(150);
|
||||||
|
|
||||||
|
connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex)));
|
||||||
|
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(matchSelectionChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
||||||
|
emitMatchActivated(currentIndex());
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// Pressing return does not emit the QTreeView::activated signal on mac os
|
||||||
|
emit activated(currentIndex());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeView::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchView::setMatchList(const QList<AutoTypeMatch>& matches)
|
||||||
|
{
|
||||||
|
m_model->setMatchList(matches);
|
||||||
|
for (int i = 0; i < m_model->columnCount(); ++i) {
|
||||||
|
resizeColumnToContents(i);
|
||||||
|
if (columnWidth(i) > 250) {
|
||||||
|
setColumnWidth(i, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFirstMatchActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchView::setFirstMatchActive()
|
||||||
|
{
|
||||||
|
if (m_model->rowCount() > 0) {
|
||||||
|
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
|
||||||
|
setCurrentMatch(m_model->matchFromIndex(index));
|
||||||
|
} else {
|
||||||
|
emit matchSelectionChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchView::emitMatchActivated(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
AutoTypeMatch match = matchFromIndex(index);
|
||||||
|
|
||||||
|
emit matchActivated(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeMatch AutoTypeMatchView::currentMatch()
|
||||||
|
{
|
||||||
|
QModelIndexList list = selectionModel()->selectedRows();
|
||||||
|
if (list.size() == 1) {
|
||||||
|
return m_model->matchFromIndex(m_sortModel->mapToSource(list.first()));
|
||||||
|
}
|
||||||
|
return AutoTypeMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeMatchView::setCurrentMatch(AutoTypeMatch match)
|
||||||
|
{
|
||||||
|
selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromMatch(match)),
|
||||||
|
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeMatch AutoTypeMatchView::matchFromIndex(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (index.isValid()) {
|
||||||
|
return m_model->matchFromIndex(m_sortModel->mapToSource(index));
|
||||||
|
}
|
||||||
|
return AutoTypeMatch();
|
||||||
|
}
|
57
src/gui/entry/AutoTypeMatchView.h
Normal file
57
src/gui/entry/AutoTypeMatchView.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEMATCHVIEW_H
|
||||||
|
#define KEEPASSX_AUTOTYPEMATCHVIEW_H
|
||||||
|
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
#include "core/AutoTypeMatch.h"
|
||||||
|
|
||||||
|
#include "gui/entry/AutoTypeMatchModel.h"
|
||||||
|
|
||||||
|
class SortFilterHideProxyModel;
|
||||||
|
|
||||||
|
class AutoTypeMatchView : public QTreeView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AutoTypeMatchView(QWidget* parent = nullptr);
|
||||||
|
AutoTypeMatch currentMatch();
|
||||||
|
void setCurrentMatch(AutoTypeMatch match);
|
||||||
|
AutoTypeMatch matchFromIndex(const QModelIndex& index);
|
||||||
|
void setMatchList(const QList<AutoTypeMatch>& matches);
|
||||||
|
void setFirstMatchActive();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void matchActivated(AutoTypeMatch match);
|
||||||
|
void matchSelectionChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void emitMatchActivated(const QModelIndex& index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoTypeMatchModel* const m_model;
|
||||||
|
SortFilterHideProxyModel* const m_sortModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEMATCHVIEW_H
|
@ -170,8 +170,6 @@ void EditEntryWidget::setupAutoType()
|
|||||||
|
|
||||||
m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->inheritSequenceButton);
|
m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->inheritSequenceButton);
|
||||||
m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->customSequenceButton);
|
m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->customSequenceButton);
|
||||||
m_autoTypeWindowSequenceGroup->addButton(m_autoTypeUi->defaultWindowSequenceButton);
|
|
||||||
m_autoTypeWindowSequenceGroup->addButton(m_autoTypeUi->customWindowSequenceButton);
|
|
||||||
m_autoTypeAssocModel->setAutoTypeAssociations(m_autoTypeAssoc);
|
m_autoTypeAssocModel->setAutoTypeAssociations(m_autoTypeAssoc);
|
||||||
m_autoTypeUi->assocView->setModel(m_autoTypeAssocModel);
|
m_autoTypeUi->assocView->setModel(m_autoTypeAssocModel);
|
||||||
m_autoTypeUi->assocView->setColumnHidden(1, true);
|
m_autoTypeUi->assocView->setColumnHidden(1, true);
|
||||||
@ -190,8 +188,6 @@ void EditEntryWidget::setupAutoType()
|
|||||||
connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc()));
|
connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc()));
|
||||||
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)),
|
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)),
|
||||||
SLOT(applyCurrentAssoc()));
|
SLOT(applyCurrentAssoc()));
|
||||||
connect(m_autoTypeUi->defaultWindowSequenceButton, SIGNAL(toggled(bool)),
|
|
||||||
SLOT(applyCurrentAssoc()));
|
|
||||||
connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)),
|
connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)),
|
||||||
SLOT(applyCurrentAssoc()));
|
SLOT(applyCurrentAssoc()));
|
||||||
}
|
}
|
||||||
@ -644,7 +640,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
|||||||
}
|
}
|
||||||
m_autoTypeUi->sequenceEdit->setText(entry->effectiveAutoTypeSequence());
|
m_autoTypeUi->sequenceEdit->setText(entry->effectiveAutoTypeSequence());
|
||||||
m_autoTypeUi->windowTitleCombo->lineEdit()->clear();
|
m_autoTypeUi->windowTitleCombo->lineEdit()->clear();
|
||||||
m_autoTypeUi->defaultWindowSequenceButton->setChecked(true);
|
m_autoTypeUi->customWindowSequenceButton->setChecked(false);
|
||||||
m_autoTypeUi->windowSequenceEdit->setText("");
|
m_autoTypeUi->windowSequenceEdit->setText("");
|
||||||
m_autoTypeAssoc->copyDataFrom(entry->autoTypeAssociations());
|
m_autoTypeAssoc->copyDataFrom(entry->autoTypeAssociations());
|
||||||
m_autoTypeAssocModel->setEntry(entry);
|
m_autoTypeAssocModel->setEntry(entry);
|
||||||
@ -998,7 +994,6 @@ void EditEntryWidget::updateAutoTypeEnabled()
|
|||||||
|
|
||||||
m_autoTypeUi->windowTitleLabel->setEnabled(autoTypeEnabled && validIndex);
|
m_autoTypeUi->windowTitleLabel->setEnabled(autoTypeEnabled && validIndex);
|
||||||
m_autoTypeUi->windowTitleCombo->setEnabled(autoTypeEnabled && validIndex);
|
m_autoTypeUi->windowTitleCombo->setEnabled(autoTypeEnabled && validIndex);
|
||||||
m_autoTypeUi->defaultWindowSequenceButton->setEnabled(!m_history && autoTypeEnabled && validIndex);
|
|
||||||
m_autoTypeUi->customWindowSequenceButton->setEnabled(!m_history && autoTypeEnabled && validIndex);
|
m_autoTypeUi->customWindowSequenceButton->setEnabled(!m_history && autoTypeEnabled && validIndex);
|
||||||
m_autoTypeUi->windowSequenceEdit->setEnabled(autoTypeEnabled && validIndex
|
m_autoTypeUi->windowSequenceEdit->setEnabled(autoTypeEnabled && validIndex
|
||||||
&& m_autoTypeUi->customWindowSequenceButton->isChecked());
|
&& m_autoTypeUi->customWindowSequenceButton->isChecked());
|
||||||
@ -1029,16 +1024,15 @@ void EditEntryWidget::loadCurrentAssoc(const QModelIndex& current)
|
|||||||
AutoTypeAssociations::Association assoc = m_autoTypeAssoc->get(current.row());
|
AutoTypeAssociations::Association assoc = m_autoTypeAssoc->get(current.row());
|
||||||
m_autoTypeUi->windowTitleCombo->setEditText(assoc.window);
|
m_autoTypeUi->windowTitleCombo->setEditText(assoc.window);
|
||||||
if (assoc.sequence.isEmpty()) {
|
if (assoc.sequence.isEmpty()) {
|
||||||
m_autoTypeUi->defaultWindowSequenceButton->setChecked(true);
|
m_autoTypeUi->customWindowSequenceButton->setChecked(false);
|
||||||
}
|
m_autoTypeUi->windowSequenceEdit->setText(m_entry->effectiveAutoTypeSequence());
|
||||||
else {
|
} else {
|
||||||
m_autoTypeUi->customWindowSequenceButton->setChecked(true);
|
m_autoTypeUi->customWindowSequenceButton->setChecked(true);
|
||||||
|
m_autoTypeUi->windowSequenceEdit->setText(assoc.sequence);
|
||||||
}
|
}
|
||||||
m_autoTypeUi->windowSequenceEdit->setText(assoc.sequence);
|
|
||||||
|
|
||||||
updateAutoTypeEnabled();
|
updateAutoTypeEnabled();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
clearCurrentAssoc();
|
clearCurrentAssoc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,7 +1041,7 @@ void EditEntryWidget::clearCurrentAssoc()
|
|||||||
{
|
{
|
||||||
m_autoTypeUi->windowTitleCombo->setEditText("");
|
m_autoTypeUi->windowTitleCombo->setEditText("");
|
||||||
|
|
||||||
m_autoTypeUi->defaultWindowSequenceButton->setChecked(true);
|
m_autoTypeUi->customWindowSequenceButton->setChecked(false);
|
||||||
m_autoTypeUi->windowSequenceEdit->setText("");
|
m_autoTypeUi->windowSequenceEdit->setText("");
|
||||||
|
|
||||||
updateAutoTypeEnabled();
|
updateAutoTypeEnabled();
|
||||||
|
@ -203,16 +203,9 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="defaultWindowSequenceButton">
|
<widget class="QCheckBox" name="customWindowSequenceButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use default se&quence</string>
|
<string>Use a specific sequence for this association:</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="customWindowSequenceButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Set custo&m sequence:</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -277,7 +270,6 @@
|
|||||||
<tabstop>sequenceEdit</tabstop>
|
<tabstop>sequenceEdit</tabstop>
|
||||||
<tabstop>assocView</tabstop>
|
<tabstop>assocView</tabstop>
|
||||||
<tabstop>windowTitleCombo</tabstop>
|
<tabstop>windowTitleCombo</tabstop>
|
||||||
<tabstop>defaultWindowSequenceButton</tabstop>
|
|
||||||
<tabstop>customWindowSequenceButton</tabstop>
|
<tabstop>customWindowSequenceButton</tabstop>
|
||||||
<tabstop>windowSequenceEdit</tabstop>
|
<tabstop>windowSequenceEdit</tabstop>
|
||||||
<tabstop>assocAddButton</tabstop>
|
<tabstop>assocAddButton</tabstop>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
if(WITH_XC_HTTP)
|
if(WITH_XC_HTTP)
|
||||||
|
add_subdirectory(qhttp)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
set(keepasshttp_SOURCES
|
set(keepasshttp_SOURCES
|
||||||
@ -13,5 +15,7 @@ if(WITH_XC_HTTP)
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(keepasshttp STATIC ${keepasshttp_SOURCES})
|
add_library(keepasshttp STATIC ${keepasshttp_SOURCES})
|
||||||
target_link_libraries(keepasshttp qhttp Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network)
|
target_link_libraries(keepasshttp PUBLIC qhttp Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network)
|
||||||
|
|
||||||
|
set(keepassxchttp_LIB keepasshttp PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
|
@ -48,13 +48,14 @@ HttpPasswordGeneratorWidget::~HttpPasswordGeneratorWidget()
|
|||||||
|
|
||||||
void HttpPasswordGeneratorWidget::loadSettings()
|
void HttpPasswordGeneratorWidget::loadSettings()
|
||||||
{
|
{
|
||||||
m_ui->checkBoxLower->setChecked(config()->get("Http/generator/LowerCase", true).toBool());
|
m_ui->checkBoxLower->setChecked(config()->get("Http/generator/LowerCase", PasswordGenerator::DefaultLower).toBool());
|
||||||
m_ui->checkBoxUpper->setChecked(config()->get("Http/generator/UpperCase", true).toBool());
|
m_ui->checkBoxUpper->setChecked(config()->get("Http/generator/UpperCase", PasswordGenerator::DefaultUpper).toBool());
|
||||||
m_ui->checkBoxNumbers->setChecked(config()->get("Http/generator/Numbers", true).toBool());
|
m_ui->checkBoxNumbers->setChecked(config()->get("Http/generator/Numbers", PasswordGenerator::DefaultNumbers).toBool());
|
||||||
m_ui->checkBoxSpecialChars->setChecked(config()->get("Http/generator/SpecialChars", false).toBool());
|
m_ui->checkBoxSpecialChars->setChecked(config()->get("Http/generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool());
|
||||||
|
m_ui->checkBoxSpecialChars->setChecked(config()->get("Http/generator/EASCII", PasswordGenerator::DefaultEASCII).toBool());
|
||||||
|
|
||||||
m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", true).toBool());
|
m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool());
|
||||||
m_ui->checkBoxEnsureEvery->setChecked(config()->get("Http/generator/EnsureEvery", true).toBool());
|
m_ui->checkBoxEnsureEvery->setChecked(config()->get("Http/generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool());
|
||||||
|
|
||||||
m_ui->spinBoxLength->setValue(config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt());
|
m_ui->spinBoxLength->setValue(config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt());
|
||||||
}
|
}
|
||||||
@ -65,6 +66,7 @@ void HttpPasswordGeneratorWidget::saveSettings()
|
|||||||
config()->set("Http/generator/UpperCase", m_ui->checkBoxUpper->isChecked());
|
config()->set("Http/generator/UpperCase", m_ui->checkBoxUpper->isChecked());
|
||||||
config()->set("Http/generator/Numbers", m_ui->checkBoxNumbers->isChecked());
|
config()->set("Http/generator/Numbers", m_ui->checkBoxNumbers->isChecked());
|
||||||
config()->set("Http/generator/SpecialChars", m_ui->checkBoxSpecialChars->isChecked());
|
config()->set("Http/generator/SpecialChars", m_ui->checkBoxSpecialChars->isChecked());
|
||||||
|
config()->set("Http/generator/EASCII", m_ui->checkBoxExtASCII->isChecked());
|
||||||
|
|
||||||
config()->set("Http/generator/ExcludeAlike", m_ui->checkBoxExcludeAlike->isChecked());
|
config()->set("Http/generator/ExcludeAlike", m_ui->checkBoxExcludeAlike->isChecked());
|
||||||
config()->set("Http/generator/EnsureEvery", m_ui->checkBoxEnsureEvery->isChecked());
|
config()->set("Http/generator/EnsureEvery", m_ui->checkBoxEnsureEvery->isChecked());
|
||||||
@ -120,6 +122,10 @@ PasswordGenerator::CharClasses HttpPasswordGeneratorWidget::charClasses()
|
|||||||
classes |= PasswordGenerator::SpecialCharacters;
|
classes |= PasswordGenerator::SpecialCharacters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_ui->checkBoxExtASCII->isChecked()) {
|
||||||
|
classes |= PasswordGenerator::EASCII;
|
||||||
|
}
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +142,22 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="checkBoxExtASCII">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Extended ASCII</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Extended ASCII</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">optionButtons</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -145,7 +145,7 @@ void HttpSettings::setHttpPort(int port)
|
|||||||
|
|
||||||
bool HttpSettings::passwordUseNumbers()
|
bool HttpSettings::passwordUseNumbers()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/Numbers", true).toBool();
|
return config()->get("Http/generator/Numbers", PasswordGenerator::DefaultNumbers).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordUseNumbers(bool useNumbers)
|
void HttpSettings::setPasswordUseNumbers(bool useNumbers)
|
||||||
@ -155,7 +155,7 @@ void HttpSettings::setPasswordUseNumbers(bool useNumbers)
|
|||||||
|
|
||||||
bool HttpSettings::passwordUseLowercase()
|
bool HttpSettings::passwordUseLowercase()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/LowerCase", true).toBool();
|
return config()->get("Http/generator/LowerCase", PasswordGenerator::DefaultLower).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordUseLowercase(bool useLowercase)
|
void HttpSettings::setPasswordUseLowercase(bool useLowercase)
|
||||||
@ -165,7 +165,7 @@ void HttpSettings::setPasswordUseLowercase(bool useLowercase)
|
|||||||
|
|
||||||
bool HttpSettings::passwordUseUppercase()
|
bool HttpSettings::passwordUseUppercase()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/UpperCase", true).toBool();
|
return config()->get("Http/generator/UpperCase", PasswordGenerator::DefaultUpper).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordUseUppercase(bool useUppercase)
|
void HttpSettings::setPasswordUseUppercase(bool useUppercase)
|
||||||
@ -175,7 +175,7 @@ void HttpSettings::setPasswordUseUppercase(bool useUppercase)
|
|||||||
|
|
||||||
bool HttpSettings::passwordUseSpecial()
|
bool HttpSettings::passwordUseSpecial()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/SpecialChars", false).toBool();
|
return config()->get("Http/generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordUseSpecial(bool useSpecial)
|
void HttpSettings::setPasswordUseSpecial(bool useSpecial)
|
||||||
@ -183,9 +183,19 @@ void HttpSettings::setPasswordUseSpecial(bool useSpecial)
|
|||||||
config()->set("Http/generator/SpecialChars", useSpecial);
|
config()->set("Http/generator/SpecialChars", useSpecial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HttpSettings::passwordUseEASCII()
|
||||||
|
{
|
||||||
|
return config()->get("Http/generator/EASCII", PasswordGenerator::DefaultEASCII).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpSettings::setPasswordUseEASCII(bool useExtended)
|
||||||
|
{
|
||||||
|
config()->set("Http/generator/EASCII", useExtended);
|
||||||
|
}
|
||||||
|
|
||||||
bool HttpSettings::passwordEveryGroup()
|
bool HttpSettings::passwordEveryGroup()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/EnsureEvery", true).toBool();
|
return config()->get("Http/generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordEveryGroup(bool everyGroup)
|
void HttpSettings::setPasswordEveryGroup(bool everyGroup)
|
||||||
@ -195,7 +205,7 @@ void HttpSettings::setPasswordEveryGroup(bool everyGroup)
|
|||||||
|
|
||||||
bool HttpSettings::passwordExcludeAlike()
|
bool HttpSettings::passwordExcludeAlike()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/ExcludeAlike", true).toBool();
|
return config()->get("Http/generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordExcludeAlike(bool excludeAlike)
|
void HttpSettings::setPasswordExcludeAlike(bool excludeAlike)
|
||||||
@ -205,7 +215,7 @@ void HttpSettings::setPasswordExcludeAlike(bool excludeAlike)
|
|||||||
|
|
||||||
int HttpSettings::passwordLength()
|
int HttpSettings::passwordLength()
|
||||||
{
|
{
|
||||||
return config()->get("Http/generator/Length", 20).toInt();
|
return config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSettings::setPasswordLength(int length)
|
void HttpSettings::setPasswordLength(int length)
|
||||||
@ -217,14 +227,21 @@ void HttpSettings::setPasswordLength(int length)
|
|||||||
PasswordGenerator::CharClasses HttpSettings::passwordCharClasses()
|
PasswordGenerator::CharClasses HttpSettings::passwordCharClasses()
|
||||||
{
|
{
|
||||||
PasswordGenerator::CharClasses classes;
|
PasswordGenerator::CharClasses classes;
|
||||||
if (passwordUseLowercase())
|
if (passwordUseLowercase()) {
|
||||||
classes |= PasswordGenerator::LowerLetters;
|
classes |= PasswordGenerator::LowerLetters;
|
||||||
if (passwordUseUppercase())
|
}
|
||||||
|
if (passwordUseUppercase()) {
|
||||||
classes |= PasswordGenerator::UpperLetters;
|
classes |= PasswordGenerator::UpperLetters;
|
||||||
if (passwordUseNumbers())
|
}
|
||||||
|
if (passwordUseNumbers()) {
|
||||||
classes |= PasswordGenerator::Numbers;
|
classes |= PasswordGenerator::Numbers;
|
||||||
if (passwordUseSpecial())
|
}
|
||||||
|
if (passwordUseSpecial()) {
|
||||||
classes |= PasswordGenerator::SpecialCharacters;
|
classes |= PasswordGenerator::SpecialCharacters;
|
||||||
|
}
|
||||||
|
if (passwordUseEASCII()) {
|
||||||
|
classes |= PasswordGenerator::EASCII;
|
||||||
|
}
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ public:
|
|||||||
static void setPasswordUseUppercase(bool useUppercase);
|
static void setPasswordUseUppercase(bool useUppercase);
|
||||||
static bool passwordUseSpecial();
|
static bool passwordUseSpecial();
|
||||||
static void setPasswordUseSpecial(bool useSpecial);
|
static void setPasswordUseSpecial(bool useSpecial);
|
||||||
|
static bool passwordUseEASCII();
|
||||||
|
static void setPasswordUseEASCII(bool useExtended);
|
||||||
static bool passwordEveryGroup();
|
static bool passwordEveryGroup();
|
||||||
static void setPasswordEveryGroup(bool everyGroup);
|
static void setPasswordEveryGroup(bool everyGroup);
|
||||||
static bool passwordExcludeAlike();
|
static bool passwordExcludeAlike();
|
||||||
|
@ -204,9 +204,10 @@ bool OpenSSHKey::parsePEM(const QByteArray& in, QByteArray& out)
|
|||||||
rows.removeFirst();
|
rows.removeFirst();
|
||||||
} while (!rows.isEmpty());
|
} while (!rows.isEmpty());
|
||||||
|
|
||||||
if (pemOptions.contains("Proc-Type")) {
|
if (pemOptions.value("Proc-Type").compare("4,encrypted", Qt::CaseInsensitive) == 0) {
|
||||||
m_error = tr("Encrypted keys are not yet supported");
|
m_kdfName = "md5";
|
||||||
return false;
|
m_cipherName = pemOptions.value("DEK-Info").section(",", 0, 0);
|
||||||
|
m_cipherIV = QByteArray::fromHex(pemOptions.value("DEK-Info").section(",", 1, 1).toLatin1());
|
||||||
}
|
}
|
||||||
|
|
||||||
out = QByteArray::fromBase64(rows.join("").toLatin1());
|
out = QByteArray::fromBase64(rows.join("").toLatin1());
|
||||||
@ -278,7 +279,7 @@ bool OpenSSHKey::parse(const QByteArray& in)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_error = tr("Unsupported key type: %s").arg(m_privateType);
|
m_error = tr("Unsupported key type: %1").arg(m_privateType);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,12 +309,14 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cipherName == "aes256-cbc") {
|
if (m_cipherName.compare("aes-128-cbc", Qt::CaseInsensitive) == 0) {
|
||||||
|
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||||
|
} else if (m_cipherName == "aes256-cbc") {
|
||||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||||
} else if (m_cipherName == "aes256-ctr") {
|
} else if (m_cipherName == "aes256-ctr") {
|
||||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt));
|
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt));
|
||||||
} else if (m_cipherName != "none") {
|
} else if (m_cipherName != "none") {
|
||||||
m_error = tr("Unknown cipher: %s").arg(m_cipherName);
|
m_error = tr("Unknown cipher: %1").arg(m_cipherName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,8 +358,23 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
|||||||
m_error = cipher->errorString();
|
m_error = cipher->errorString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (m_kdfName == "md5") {
|
||||||
|
if (m_cipherIV.length() < 8) {
|
||||||
|
m_error = tr("Cipher IV is too short for MD5 kdf");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
|
hash.addData(passphrase.toUtf8());
|
||||||
|
hash.addData(m_cipherIV.data(), 8);
|
||||||
|
QByteArray keyData = hash.result();
|
||||||
|
|
||||||
|
if (!cipher->init(keyData, m_cipherIV)) {
|
||||||
|
m_error = cipher->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (m_kdfName != "none") {
|
} else if (m_kdfName != "none") {
|
||||||
m_error = tr("Unknown KDF: %s").arg(m_kdfName);
|
m_error = tr("Unknown KDF: %1").arg(m_kdfName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,14 +391,14 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
|||||||
|
|
||||||
if (m_privateType == TYPE_DSA) {
|
if (m_privateType == TYPE_DSA) {
|
||||||
if (!ASN1Key::parseDSA(rawPrivateData, *this)) {
|
if (!ASN1Key::parseDSA(rawPrivateData, *this)) {
|
||||||
m_error = tr("Reading DSA private key failed, only unencrypted keys are supported at this time");
|
m_error = tr("Decryption failed, wrong passphrase?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (m_privateType == TYPE_RSA) {
|
} else if (m_privateType == TYPE_RSA) {
|
||||||
if (!ASN1Key::parseRSA(rawPrivateData, *this)) {
|
if (!ASN1Key::parseRSA(rawPrivateData, *this)) {
|
||||||
m_error = tr("Reading RSA private key failed, only unencrypted keys are supported at this time");
|
m_error = tr("Decryption failed, wrong passphrase?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +420,7 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
|||||||
return readPrivate(keyStream);
|
return readPrivate(keyStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_error = tr("Unsupported key type: %s").arg(m_privateType);
|
m_error = tr("Unsupported key type: %1").arg(m_privateType);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +443,7 @@ bool OpenSSHKey::readPublic(BinaryStream& stream)
|
|||||||
} else if (m_type == "ssh-ed25519") {
|
} else if (m_type == "ssh-ed25519") {
|
||||||
keyParts = 1;
|
keyParts = 1;
|
||||||
} else {
|
} else {
|
||||||
m_error = tr("Unknown key type: %s").arg(m_type);
|
m_error = tr("Unknown key type: %1").arg(m_type);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,7 +480,7 @@ bool OpenSSHKey::readPrivate(BinaryStream& stream)
|
|||||||
} else if (m_type == "ssh-ed25519") {
|
} else if (m_type == "ssh-ed25519") {
|
||||||
keyParts = 2;
|
keyParts = 2;
|
||||||
} else {
|
} else {
|
||||||
m_error = tr("Unknown key type: %s").arg(m_type);
|
m_error = tr("Unknown key type: %1").arg(m_type);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ private:
|
|||||||
|
|
||||||
QString m_type;
|
QString m_type;
|
||||||
QString m_cipherName;
|
QString m_cipherName;
|
||||||
|
QByteArray m_cipherIV;
|
||||||
QString m_kdfName;
|
QString m_kdfName;
|
||||||
QByteArray m_kdfOptions;
|
QByteArray m_kdfOptions;
|
||||||
QByteArray m_rawPrivateData;
|
QByteArray m_rawPrivateData;
|
||||||
|
@ -136,7 +136,7 @@ void TestAutoType::testInternal()
|
|||||||
QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
|
QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestAutoType::testAutoTypeWithoutSequence()
|
void TestAutoType::testSingleAutoType()
|
||||||
{
|
{
|
||||||
m_autoType->performAutoType(m_entry1, nullptr);
|
m_autoType->performAutoType(m_entry1, nullptr);
|
||||||
|
|
||||||
@ -147,17 +147,6 @@ void TestAutoType::testAutoTypeWithoutSequence()
|
|||||||
.arg(m_test->keyToString(Qt::Key_Enter)));
|
.arg(m_test->keyToString(Qt::Key_Enter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestAutoType::testAutoTypeWithSequence()
|
|
||||||
{
|
|
||||||
m_autoType->performAutoType(m_entry1, nullptr, "{Username}abc{PaSsWoRd}");
|
|
||||||
|
|
||||||
QCOMPARE(m_test->actionCount(), 15);
|
|
||||||
QCOMPARE(m_test->actionChars(),
|
|
||||||
QString("%1abc%2")
|
|
||||||
.arg(m_entry1->username())
|
|
||||||
.arg(m_entry1->password()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestAutoType::testGlobalAutoTypeWithNoMatch()
|
void TestAutoType::testGlobalAutoTypeWithNoMatch()
|
||||||
{
|
{
|
||||||
m_test->setActiveWindowTitle("nomatch");
|
m_test->setActiveWindowTitle("nomatch");
|
||||||
@ -310,4 +299,82 @@ void TestAutoType::testAutoTypeSyntaxChecks()
|
|||||||
QCOMPARE(true, AutoType::checkHighRepetition("{LEFT 50000000}"));
|
QCOMPARE(true, AutoType::checkHighRepetition("{LEFT 50000000}"));
|
||||||
QCOMPARE(false, AutoType::checkHighRepetition("{SPACE 10}{TAB 3}{RIGHT 50}"));
|
QCOMPARE(false, AutoType::checkHighRepetition("{SPACE 10}{TAB 3}{RIGHT 50}"));
|
||||||
QCOMPARE(false, AutoType::checkHighRepetition("{delay 5000000000}"));
|
QCOMPARE(false, AutoType::checkHighRepetition("{delay 5000000000}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAutoType::testAutoTypeEffectiveSequences()
|
||||||
|
{
|
||||||
|
QString defaultSequence("{USERNAME}{TAB}{PASSWORD}{ENTER}");
|
||||||
|
QString sequenceG1("{TEST_GROUP1}");
|
||||||
|
QString sequenceG3("{TEST_GROUP3}");
|
||||||
|
QString sequenceE2("{TEST_ENTRY2}");
|
||||||
|
QString sequenceDisabled("{TEST_DISABLED}");
|
||||||
|
QString sequenceOrphan("{TEST_ORPHAN}");
|
||||||
|
|
||||||
|
QScopedPointer<Database> db(new Database());
|
||||||
|
QPointer<Group> rootGroup = db->rootGroup();
|
||||||
|
|
||||||
|
// Group with autotype enabled and custom default sequence
|
||||||
|
QPointer<Group> group1 = new Group();
|
||||||
|
group1->setParent(rootGroup);
|
||||||
|
group1->setDefaultAutoTypeSequence(sequenceG1);
|
||||||
|
|
||||||
|
// Child group with inherit
|
||||||
|
QPointer<Group> group2 = new Group();
|
||||||
|
group2->setParent(group1);
|
||||||
|
|
||||||
|
// Group with autotype disabled and custom default sequence
|
||||||
|
QPointer<Group> group3 = new Group();
|
||||||
|
group3->setParent(group1);
|
||||||
|
group3->setAutoTypeEnabled(Group::Disable);
|
||||||
|
group3->setDefaultAutoTypeSequence(sequenceG3);
|
||||||
|
|
||||||
|
QCOMPARE(rootGroup->defaultAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(rootGroup->effectiveAutoTypeSequence(), defaultSequence);
|
||||||
|
QCOMPARE(group1->defaultAutoTypeSequence(), sequenceG1);
|
||||||
|
QCOMPARE(group1->effectiveAutoTypeSequence(), sequenceG1);
|
||||||
|
QCOMPARE(group2->defaultAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(group2->effectiveAutoTypeSequence(), sequenceG1);
|
||||||
|
QCOMPARE(group3->defaultAutoTypeSequence(), sequenceG3);
|
||||||
|
QCOMPARE(group3->effectiveAutoTypeSequence(), QString());
|
||||||
|
|
||||||
|
// Entry from root group
|
||||||
|
QPointer<Entry> entry1 = new Entry();
|
||||||
|
entry1->setGroup(rootGroup);
|
||||||
|
|
||||||
|
// Entry with custom default sequence
|
||||||
|
QPointer<Entry> entry2 = new Entry();
|
||||||
|
entry2->setDefaultAutoTypeSequence(sequenceE2);
|
||||||
|
entry2->setGroup(rootGroup);
|
||||||
|
|
||||||
|
// Entry from enabled child group
|
||||||
|
QPointer<Entry> entry3 = new Entry();
|
||||||
|
entry3->setGroup(group2);
|
||||||
|
|
||||||
|
// Entry from disabled group
|
||||||
|
QPointer<Entry> entry4 = new Entry();
|
||||||
|
entry4->setDefaultAutoTypeSequence(sequenceDisabled);
|
||||||
|
entry4->setGroup(group3);
|
||||||
|
|
||||||
|
// Entry from enabled group with disabled autotype
|
||||||
|
QPointer<Entry> entry5 = new Entry();
|
||||||
|
entry5->setGroup(group2);
|
||||||
|
entry5->setDefaultAutoTypeSequence(sequenceDisabled);
|
||||||
|
entry5->setAutoTypeEnabled(false);
|
||||||
|
|
||||||
|
// Entry with no parent
|
||||||
|
QScopedPointer<Entry> entry6(new Entry());
|
||||||
|
entry6->setDefaultAutoTypeSequence(sequenceOrphan);
|
||||||
|
|
||||||
|
QCOMPARE(entry1->defaultAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(entry1->effectiveAutoTypeSequence(), defaultSequence);
|
||||||
|
QCOMPARE(entry2->defaultAutoTypeSequence(), sequenceE2);
|
||||||
|
QCOMPARE(entry2->effectiveAutoTypeSequence(), sequenceE2);
|
||||||
|
QCOMPARE(entry3->defaultAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(entry3->effectiveAutoTypeSequence(), sequenceG1);
|
||||||
|
QCOMPARE(entry4->defaultAutoTypeSequence(), sequenceDisabled);
|
||||||
|
QCOMPARE(entry4->effectiveAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(entry5->defaultAutoTypeSequence(), sequenceDisabled);
|
||||||
|
QCOMPARE(entry5->effectiveAutoTypeSequence(), QString());
|
||||||
|
QCOMPARE(entry6->defaultAutoTypeSequence(), sequenceOrphan);
|
||||||
|
QCOMPARE(entry6->effectiveAutoTypeSequence(), QString());
|
||||||
}
|
}
|
@ -38,8 +38,7 @@ private slots:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
void testInternal();
|
void testInternal();
|
||||||
void testAutoTypeWithoutSequence();
|
void testSingleAutoType();
|
||||||
void testAutoTypeWithSequence();
|
|
||||||
void testGlobalAutoTypeWithNoMatch();
|
void testGlobalAutoTypeWithNoMatch();
|
||||||
void testGlobalAutoTypeWithOneMatch();
|
void testGlobalAutoTypeWithOneMatch();
|
||||||
void testGlobalAutoTypeTitleMatch();
|
void testGlobalAutoTypeTitleMatch();
|
||||||
@ -48,6 +47,7 @@ private slots:
|
|||||||
void testGlobalAutoTypeTitleMatchDisabled();
|
void testGlobalAutoTypeTitleMatchDisabled();
|
||||||
void testGlobalAutoTypeRegExp();
|
void testGlobalAutoTypeRegExp();
|
||||||
void testAutoTypeSyntaxChecks();
|
void testAutoTypeSyntaxChecks();
|
||||||
|
void testAutoTypeEffectiveSequences();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AutoTypePlatformInterface* m_platform;
|
AutoTypePlatformInterface* m_platform;
|
||||||
|
@ -90,6 +90,54 @@ void TestOpenSSHKey::testParseDSA()
|
|||||||
QCOMPARE(key.fingerprint(), QString("SHA256:tbbNuLN1hja8JNASDTlLOZQsbTlJDzJlz/oAGK3sX18"));
|
QCOMPARE(key.fingerprint(), QString("SHA256:tbbNuLN1hja8JNASDTlLOZQsbTlJDzJlz/oAGK3sX18"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestOpenSSHKey::testDecryptAES128CBC()
|
||||||
|
{
|
||||||
|
const QString keyString = QString(
|
||||||
|
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||||
|
"Proc-Type: 4,ENCRYPTED\n"
|
||||||
|
"DEK-Info: AES-128-CBC,804E4D214D1263FF94E3743FE799DBB4\n"
|
||||||
|
"\n"
|
||||||
|
"lM9TDfOTbiRhaGGDh7Hn+rqw8CCWcYBZYu7smyYLdnWKXKPmbne8CQFZBAS1FJwZ\n"
|
||||||
|
"6Mj6n075yFGyzN9/OfeqKiUA4adlbwLbGwB+yyKsC2FlsvRIEr4hup02WWM47vHj\n"
|
||||||
|
"DS4TRmNkE7MKFLhpNCyt5OGGM45s+/lwVTw51K0Hm99TBd72IrX4jfY9ZxAVbL3l\n"
|
||||||
|
"aTohL8x6oOTe7q318QgJoFi+DjJhDWLGLLJ7fBqD2imz2fmrY4j8Jpw2sDe1rj82\n"
|
||||||
|
"gMqqNG3FrfN0S4uYlWYH5pAh+BUcB1UdmTU/rV5wJMK1oUytmZv/J2+X/0k3Y93F\n"
|
||||||
|
"aw6JWOy28OizW+TQXvv8gREWsp5PEclqUZhhGQbVbCQCiDOxg+xiXNySdRH1IqjR\n"
|
||||||
|
"zQiKgD4SPzkxQekExPaIQT/KutWZdMNYybEqooCx8YyeDoN31z7Wa2rv6OulOn/j\n"
|
||||||
|
"wJFvyd2PT/6brHKI4ky8RYroDf4FbVYKfyEW5CSAg2OyL/tY/kSPgy/k0WT7fDwq\n"
|
||||||
|
"dPSuYM9yeWNL6kAhDqDOv8+s3xvOVEljktBvQvItQwVLmHszC3E2AcnaxzdblKPu\n"
|
||||||
|
"e3+mBT80NXHjERK2ht+/9JYseK1ujNbNAaG8SbKfU3FF0VlyJ0QW6TuIEdpNnymT\n"
|
||||||
|
"0fm0cDfKNaoeJIFnBRZhgIOJAic9DM0cTe/vSG69DaUYsaQPp36al7Fbux3GpFHS\n"
|
||||||
|
"OtJEySYGro/6zvJ9dDIEfIGZjA3RaMt6+DuyJZXQdT2RNXa9j60xW7dXh0En4n82\n"
|
||||||
|
"JUKTxYhDPLS5c8BzpJqoopxpKwElmrJ7Y3xpd6z2vIlD8ftuZrkk6siTMNQ2s7MI\n"
|
||||||
|
"Xl332O+0H4k7uSfczHPOOw36TFhNjGQAP0b7O+0/RVG0ttOIoAn7ZkX3nfdbtG5B\n"
|
||||||
|
"DWKvDaopvrcC2/scQ5uLUnqnBiGw1XiYpdg5ang7knHNzHZAIekVaYYZigpCAKp+\n"
|
||||||
|
"OtoaDeUEzqFhYVmF8ad1fgvC9ZUsuxS4XUHCKl0H6CJcvW9MJPVbveqYoK+j9qKd\n"
|
||||||
|
"iMIkQBP1kE2rzGZVGUkZTpM9LVD9nP0nsbr6E8BatFcNgRirsg2BTJglNpXlCmY6\n"
|
||||||
|
"ldzJ/ELBbzoXIn+0wTGai0o4eBPx55baef69JfPuZqEB9pLNE+mHstrqIwcfqYu4\n"
|
||||||
|
"M+Vzun1QshRMj9a1PVkIHfs1fLeebI4QCHO0vJlc9K4iYPM4rsDNO3YaAgGRuARS\n"
|
||||||
|
"f3McGiGFxkv5zxe8i05ZBnn+exE77jpRKxd223jAMe2wu4WiFB7ZVo4Db6b5Oo2T\n"
|
||||||
|
"TPh3VuY7TNMEKkcUi+mGLKjroocQ5j8WQYlfnyOaTalUVQDzOTNb67QIIoiszR0U\n"
|
||||||
|
"+AXGyxHj0QtotZFoPME+AbS9Zqy3SgSOuIzPBPU5zS4uoKNdD5NPE5YAuafCjsDy\n"
|
||||||
|
"MT4DVy+cPOQYUK022S7T2nsA1btmvUvD5LL2Mc8VuKsWOn/7FKZua6OCfipt6oX0\n"
|
||||||
|
"1tzYrw0/ALK+CIdVdYIiPPfxGZkr+JSLOOg7u50tpmen9GzxgNTv63miygwUAIDF\n"
|
||||||
|
"u0GbQwOueoA453/N75FcXOgrbqTdivyadUbRP+l7YJk/SfIytyJMOigejp+Z1lzF\n"
|
||||||
|
"-----END RSA PRIVATE KEY-----\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
const QByteArray keyData = keyString.toLatin1();
|
||||||
|
|
||||||
|
OpenSSHKey key;
|
||||||
|
QVERIFY(key.parse(keyData));
|
||||||
|
QVERIFY(key.encrypted());
|
||||||
|
QCOMPARE(key.cipherName(), QString("AES-128-CBC"));
|
||||||
|
QVERIFY(!key.openPrivateKey("incorrectpassphrase"));
|
||||||
|
QVERIFY(key.openPrivateKey("correctpassphrase"));
|
||||||
|
QCOMPARE(key.type(), QString("ssh-rsa"));
|
||||||
|
QCOMPARE(key.comment(), QString(""));
|
||||||
|
QCOMPARE(key.fingerprint(), QString("SHA256:1Hsebt2WWnmc72FERsUOgvaajIGHkrMONxXylcmk87U"));
|
||||||
|
}
|
||||||
|
|
||||||
void TestOpenSSHKey::testParseRSA()
|
void TestOpenSSHKey::testParseRSA()
|
||||||
{
|
{
|
||||||
const QString keyString = QString(
|
const QString keyString = QString(
|
||||||
|
@ -31,6 +31,7 @@ private slots:
|
|||||||
void testParse();
|
void testParse();
|
||||||
void testParseDSA();
|
void testParseDSA();
|
||||||
void testParseRSA();
|
void testParseRSA();
|
||||||
|
void testDecryptAES128CBC();
|
||||||
void testDecryptAES256CBC();
|
void testDecryptAES256CBC();
|
||||||
void testDecryptAES256CTR();
|
void testDecryptAES256CTR();
|
||||||
};
|
};
|
||||||
|
@ -32,6 +32,90 @@ void TestSymmetricCipher::initTestCase()
|
|||||||
QVERIFY(Crypto::init());
|
QVERIFY(Crypto::init());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSymmetricCipher::testAes128CbcEncryption()
|
||||||
|
{
|
||||||
|
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
|
||||||
|
QByteArray key = QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c");
|
||||||
|
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
|
||||||
|
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||||
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
|
QByteArray cipherText = QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d");
|
||||||
|
cipherText.append(QByteArray::fromHex("5086cb9b507219ee95db113a917678b2"));
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.blockSize(), 16);
|
||||||
|
QCOMPARE(cipher.process(plainText, &ok), cipherText);
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
|
QBuffer buffer;
|
||||||
|
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc,
|
||||||
|
SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(stream.init(key, iv));
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
QVERIFY(stream.open(QIODevice::WriteOnly));
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
|
||||||
|
buffer.reset();
|
||||||
|
buffer.buffer().clear();
|
||||||
|
QCOMPARE(stream.write(plainText.left(16)), qint64(16));
|
||||||
|
QCOMPARE(buffer.data(), cipherText.left(16));
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
// make sure padding is written
|
||||||
|
QCOMPARE(buffer.data().size(), 32);
|
||||||
|
|
||||||
|
buffer.reset();
|
||||||
|
buffer.buffer().clear();
|
||||||
|
QCOMPARE(stream.write(plainText.left(10)), qint64(10));
|
||||||
|
QVERIFY(buffer.data().isEmpty());
|
||||||
|
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
buffer.reset();
|
||||||
|
buffer.buffer().clear();
|
||||||
|
QCOMPARE(stream.write(plainText.left(10)), qint64(10));
|
||||||
|
stream.close();
|
||||||
|
QCOMPARE(buffer.data().size(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSymmetricCipher::testAes128CbcDecryption()
|
||||||
|
{
|
||||||
|
QByteArray key = QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c");
|
||||||
|
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
|
||||||
|
QByteArray cipherText = QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d");
|
||||||
|
cipherText.append(QByteArray::fromHex("5086cb9b507219ee95db113a917678b2"));
|
||||||
|
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||||
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.blockSize(), 16);
|
||||||
|
QCOMPARE(cipher.process(cipherText, &ok), plainText);
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
|
// padded with 16 0x10 bytes
|
||||||
|
QByteArray cipherTextPadded = cipherText + QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538");
|
||||||
|
QBuffer buffer(&cipherTextPadded);
|
||||||
|
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc,
|
||||||
|
SymmetricCipher::Decrypt);
|
||||||
|
QVERIFY(stream.init(key, iv));
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
QVERIFY(stream.open(QIODevice::ReadOnly));
|
||||||
|
|
||||||
|
QCOMPARE(stream.read(10), plainText.left(10));
|
||||||
|
buffer.reset();
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
QCOMPARE(stream.read(20), plainText.left(20));
|
||||||
|
buffer.reset();
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
QCOMPARE(stream.read(16), plainText.left(16));
|
||||||
|
buffer.reset();
|
||||||
|
QVERIFY(stream.reset());
|
||||||
|
QCOMPARE(stream.read(100), plainText);
|
||||||
|
}
|
||||||
|
|
||||||
void TestSymmetricCipher::testAes256CbcEncryption()
|
void TestSymmetricCipher::testAes256CbcEncryption()
|
||||||
{
|
{
|
||||||
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
@ -27,6 +27,8 @@ class TestSymmetricCipher : public QObject
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
void testAes128CbcEncryption();
|
||||||
|
void testAes128CbcDecryption();
|
||||||
void testAes256CbcEncryption();
|
void testAes256CbcEncryption();
|
||||||
void testAes256CbcDecryption();
|
void testAes256CbcDecryption();
|
||||||
void testAes256CtrEncryption();
|
void testAes256CtrEncryption();
|
||||||
|
Loading…
Reference in New Issue
Block a user