mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-26 15:59:50 -05:00
Merge branch '2.0'
This commit is contained in:
commit
dc7b6623a9
@ -79,10 +79,17 @@ void AutoType::loadPlugin(const QString& pluginPath)
|
||||
QObject* pluginInstance = m_pluginLoader->instance();
|
||||
if (pluginInstance) {
|
||||
m_plugin = qobject_cast<AutoTypePlatformInterface*>(pluginInstance);
|
||||
m_executor = Q_NULLPTR;
|
||||
|
||||
if (m_plugin) {
|
||||
if (m_plugin->isAvailable()) {
|
||||
m_executor = m_plugin->createExecutor();
|
||||
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered()));
|
||||
}
|
||||
else {
|
||||
unloadPlugin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_plugin) {
|
||||
@ -568,7 +575,7 @@ bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPa
|
||||
{
|
||||
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
|
||||
QRegExp regExp(windowPattern.mid(2, windowPattern.size() - 4), Qt::CaseInsensitive, QRegExp::RegExp2);
|
||||
return regExp.exactMatch(windowTitle);
|
||||
return (regExp.indexIn(windowTitle) != -1);
|
||||
}
|
||||
else {
|
||||
return WildcardMatcher(windowTitle).match(windowPattern);
|
||||
|
@ -26,6 +26,7 @@ class AutoTypePlatformInterface
|
||||
{
|
||||
public:
|
||||
virtual ~AutoTypePlatformInterface() {}
|
||||
virtual bool isAvailable() = 0;
|
||||
virtual QStringList windowTitles() = 0;
|
||||
virtual WId activeWindow() = 0;
|
||||
virtual QString activeWindowTitle() = 0;
|
||||
|
@ -17,6 +17,11 @@
|
||||
|
||||
#include "AutoTypeTest.h"
|
||||
|
||||
bool AutoTypePlatformTest::isAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString AutoTypePlatformTest::keyToString(Qt::Key key)
|
||||
{
|
||||
return QString("[Key0x%1]").arg(key, 0, 16);
|
||||
|
@ -35,6 +35,7 @@ class AutoTypePlatformTest : public QObject,
|
||||
public:
|
||||
QString keyToString(Qt::Key key) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isAvailable() Q_DECL_OVERRIDE;
|
||||
QStringList windowTitles() Q_DECL_OVERRIDE;
|
||||
WId activeWindow() Q_DECL_OVERRIDE;
|
||||
QString activeWindowTitle() Q_DECL_OVERRIDE;
|
||||
|
@ -58,6 +58,31 @@ AutoTypePlatformX11::AutoTypePlatformX11()
|
||||
updateKeymap();
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::isAvailable()
|
||||
{
|
||||
int ignore;
|
||||
|
||||
if (!XQueryExtension(m_dpy, "XInputExtension", &ignore, &ignore, &ignore)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!XQueryExtension(m_dpy, "XTEST", &ignore, &ignore, &ignore)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_xkb) {
|
||||
XkbDescPtr kbd = getKeyboard();
|
||||
|
||||
if (!kbd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
XkbFreeKeyboard(kbd, XkbAllComponentsMask, True);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoTypePlatformX11::unload()
|
||||
{
|
||||
// Restore the KeyboardMapping to its original state.
|
||||
@ -185,7 +210,7 @@ int AutoTypePlatformX11::platformEventFilter(void* event)
|
||||
xcb_key_press_event_t* keyPressEvent = static_cast<xcb_key_press_event_t*>(event);
|
||||
if (keyPressEvent->detail == m_currentGlobalKeycode
|
||||
&& (keyPressEvent->state & m_modifierMask) == m_currentGlobalNativeModifiers
|
||||
&& !QApplication::focusWidget()
|
||||
&& (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized())
|
||||
&& m_loaded) {
|
||||
if (type == XCB_KEY_PRESS) {
|
||||
Q_EMIT globalShortcutTriggered();
|
||||
@ -458,21 +483,10 @@ void AutoTypePlatformX11::updateKeymap()
|
||||
int mod_index, mod_key;
|
||||
XModifierKeymap *modifiers;
|
||||
|
||||
if (m_xkb != NULL) XkbFreeKeyboard(m_xkb, XkbAllComponentsMask, True);
|
||||
|
||||
XDeviceInfo* devices;
|
||||
int num_devices;
|
||||
XID keyboard_id = XkbUseCoreKbd;
|
||||
devices = XListInputDevices(m_dpy, &num_devices);
|
||||
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
if (QString(devices[i].name) == "Virtual core XTEST keyboard") {
|
||||
keyboard_id = devices[i].id;
|
||||
break;
|
||||
if (m_xkb) {
|
||||
XkbFreeKeyboard(m_xkb, XkbAllComponentsMask, True);
|
||||
}
|
||||
}
|
||||
|
||||
m_xkb = XkbGetKeyboard(m_dpy, XkbCompatMapMask | XkbGeometryMask, keyboard_id);
|
||||
m_xkb = getKeyboard();
|
||||
|
||||
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
|
||||
if (m_keysymTable != NULL) XFree(m_keysymTable);
|
||||
@ -558,6 +572,27 @@ int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error)
|
||||
return 1;
|
||||
}
|
||||
|
||||
XkbDescPtr AutoTypePlatformX11::getKeyboard()
|
||||
{
|
||||
int num_devices;
|
||||
XID keyboard_id = XkbUseCoreKbd;
|
||||
XDeviceInfo* devices = XListInputDevices(m_dpy, &num_devices);
|
||||
if (!devices) {
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
if (QString(devices[i].name) == "Virtual core XTEST keyboard") {
|
||||
keyboard_id = devices[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XFreeDeviceList(devices);
|
||||
|
||||
return XkbGetKeyboard(m_dpy, XkbCompatMapMask | XkbGeometryMask, keyboard_id);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// The following code is taken from xvkbd 3.0 and has been slightly modified.
|
||||
// --------------------------------------------------------------------------
|
||||
|
@ -42,6 +42,7 @@ class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
|
||||
|
||||
public:
|
||||
AutoTypePlatformX11();
|
||||
bool isAvailable() override;
|
||||
void unload() override;
|
||||
QStringList windowTitles() override;
|
||||
WId activeWindow() override;
|
||||
@ -72,6 +73,7 @@ private:
|
||||
void stopCatchXErrors();
|
||||
static int x11ErrorHandler(Display* display, XErrorEvent* error);
|
||||
|
||||
XkbDescPtr getKeyboard();
|
||||
void updateKeymap();
|
||||
bool isRemapKeycodeValid();
|
||||
int AddKeysym(KeySym keysym);
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
bool Crypto::m_initalized(false);
|
||||
QString Crypto::m_errorStr;
|
||||
QString Crypto::m_backendVersion;
|
||||
|
||||
Crypto::Crypto()
|
||||
{
|
||||
@ -39,7 +40,7 @@ bool Crypto::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
gcry_check_version(0);
|
||||
m_backendVersion = QString::fromLocal8Bit(gcry_check_version(0));
|
||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
|
||||
if (!checkAlgorithms()) {
|
||||
@ -67,6 +68,11 @@ QString Crypto::errorString()
|
||||
return m_errorStr;
|
||||
}
|
||||
|
||||
QString Crypto::backendVersion()
|
||||
{
|
||||
return QString("libgcrypt ").append(m_backendVersion);
|
||||
}
|
||||
|
||||
bool Crypto::backendSelfTest()
|
||||
{
|
||||
return (gcry_control(GCRYCTL_SELFTEST) == 0);
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
static bool initalized();
|
||||
static bool backendSelfTest();
|
||||
static QString errorString();
|
||||
static QString backendVersion();
|
||||
|
||||
private:
|
||||
Crypto();
|
||||
@ -41,6 +42,7 @@ private:
|
||||
|
||||
static bool m_initalized;
|
||||
static QString m_errorStr;
|
||||
static QString m_backendVersion;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_CRYPTO_H
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "config-keepassx.h"
|
||||
#include "version.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
AboutDialog::AboutDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
@ -49,6 +50,12 @@ AboutDialog::AboutDialog(QWidget* parent)
|
||||
m_ui->label_git->setText(labelText);
|
||||
}
|
||||
|
||||
QString libs = QString("%1\n- Qt %2\n- %3")
|
||||
.arg(m_ui->label_libs->text())
|
||||
.arg(QString::fromLocal8Bit(qVersion()))
|
||||
.arg(Crypto::backendVersion());
|
||||
m_ui->label_libs->setText(libs);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||
}
|
||||
|
@ -14,10 +14,20 @@
|
||||
<string>About KeePassX</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel"/>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
@ -36,6 +46,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"><a href="http://www.keepassx.org/">http://www.keepassx.org/</a></string>
|
||||
</property>
|
||||
@ -46,6 +62,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</string>
|
||||
</property>
|
||||
@ -64,6 +86,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_libs">
|
||||
<property name="text">
|
||||
<string>Using:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
|
@ -45,12 +45,18 @@ public:
|
||||
|
||||
Application::Application(int& argc, char** argv)
|
||||
: QApplication(argc, argv)
|
||||
, m_mainWindow(Q_NULLPTR)
|
||||
{
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
||||
installNativeEventFilter(new XcbEventFilter());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::setMainWindow(QWidget* mainWindow)
|
||||
{
|
||||
m_mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
bool Application::event(QEvent* event)
|
||||
{
|
||||
// Handle Apple QFileOpenEvent from finder (double click on .kdbx file)
|
||||
@ -58,6 +64,16 @@ bool Application::event(QEvent* event)
|
||||
Q_EMIT openFile(static_cast<QFileOpenEvent*>(event)->file());
|
||||
return true;
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
// restore main window when clicking on the docker icon
|
||||
else if ((event->type() == QEvent::ApplicationActivate) && m_mainWindow) {
|
||||
m_mainWindow->ensurePolished();
|
||||
m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized);
|
||||
m_mainWindow->show();
|
||||
m_mainWindow->raise();
|
||||
m_mainWindow->activateWindow();
|
||||
}
|
||||
#endif
|
||||
|
||||
return QApplication::event(event);
|
||||
}
|
||||
|
@ -27,11 +27,15 @@ class Application : public QApplication
|
||||
|
||||
public:
|
||||
Application(int& argc, char** argv);
|
||||
void setMainWindow(QWidget* mainWindow);
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void openFile(const QString& filename);
|
||||
|
||||
private:
|
||||
QWidget* m_mainWindow;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_APPLICATION_H
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QShortcut>
|
||||
#include <QTimer>
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
#include "core/Config.h"
|
||||
@ -449,7 +450,7 @@ void MainWindow::changeEvent(QEvent *event)
|
||||
&& isTrayIconEnabled() && config()->get("GUI/MinimizeToTray").toBool())
|
||||
{
|
||||
event->ignore();
|
||||
hide();
|
||||
QTimer::singleShot(0, this, SLOT(hide()));
|
||||
}
|
||||
else {
|
||||
QMainWindow::changeEvent(event);
|
||||
@ -575,10 +576,12 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
|
||||
|
||||
void MainWindow::toggleWindow()
|
||||
{
|
||||
if (QApplication::activeWindow() == this) {
|
||||
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
|
||||
hide();
|
||||
}
|
||||
else {
|
||||
ensurePolished();
|
||||
setWindowState(windowState() & ~Qt::WindowMinimized);
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
@ -597,6 +600,11 @@ void MainWindow::lockDatabasesAfterInactivity()
|
||||
|
||||
bool MainWindow::isTrayIconEnabled() const
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
// systray not useful on OS X
|
||||
return false;
|
||||
#else
|
||||
return config()->get("GUI/ShowTrayIcon").toBool()
|
||||
&& QSystemTrayIcon::isSystemTrayAvailable();
|
||||
#endif
|
||||
}
|
||||
|
@ -41,6 +41,11 @@ SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
|
||||
m_generalUi->autoTypeShortcutWidget->setVisible(autoType()->isAvailable());
|
||||
m_generalUi->autoTypeShortcutLabel->setVisible(autoType()->isAvailable());
|
||||
#ifdef Q_OS_MAC
|
||||
// systray not useful on OS X
|
||||
m_generalUi->systrayShowCheckBox->setVisible(false);
|
||||
m_generalUi->systrayMinimizeToTrayCheckBox->setVisible(false);
|
||||
#endif
|
||||
|
||||
connect(this, SIGNAL(accepted()), SLOT(saveSettings()));
|
||||
connect(this, SIGNAL(rejected()), SLOT(reject()));
|
||||
|
@ -33,5 +33,6 @@ void UnlockDatabaseWidget::clearForms()
|
||||
m_ui->comboKeyFile->clear();
|
||||
m_ui->checkPassword->setChecked(false);
|
||||
m_ui->checkKeyFile->setChecked(false);
|
||||
m_ui->buttonTogglePassword->setChecked(false);
|
||||
m_db = nullptr;
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ int main(int argc, char** argv)
|
||||
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
app.setMainWindow(&mainWindow);
|
||||
|
||||
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
||||
|
||||
|
@ -37,7 +37,6 @@ void TestAutoType::initTestCase()
|
||||
QVERIFY(Crypto::init());
|
||||
Config::createTempFileInstance();
|
||||
AutoType::createTestInstance();
|
||||
config()->set("AutoTypeEntryTitleMatch", false);
|
||||
config()->set("security/autotypeask", false);
|
||||
|
||||
QPluginLoader loader(filePath()->pluginPath("keepassx-autotype-test"));
|
||||
@ -55,6 +54,7 @@ void TestAutoType::initTestCase()
|
||||
|
||||
void TestAutoType::init()
|
||||
{
|
||||
config()->set("AutoTypeEntryTitleMatch", false);
|
||||
m_test->clearActions();
|
||||
|
||||
m_db = new Database();
|
||||
@ -63,11 +63,12 @@ void TestAutoType::init()
|
||||
m_group = new Group();
|
||||
m_db->setRootGroup(m_group);
|
||||
|
||||
AutoTypeAssociations::Association association;
|
||||
|
||||
m_entry1 = new Entry();
|
||||
m_entry1->setGroup(m_group);
|
||||
m_entry1->setUsername("myuser");
|
||||
m_entry1->setPassword("mypass");
|
||||
AutoTypeAssociations::Association association;
|
||||
association.window = "custom window";
|
||||
association.sequence = "{username}association{password}";
|
||||
m_entry1->autoTypeAssociations()->add(association);
|
||||
@ -76,6 +77,19 @@ void TestAutoType::init()
|
||||
m_entry2->setGroup(m_group);
|
||||
m_entry2->setPassword("myuser");
|
||||
m_entry2->setTitle("entry title");
|
||||
|
||||
m_entry3 = new Entry();
|
||||
m_entry3->setGroup(m_group);
|
||||
m_entry3->setPassword("regex");
|
||||
association.window = "//REGEX1//";
|
||||
association.sequence = "regex1";
|
||||
m_entry3->autoTypeAssociations()->add(association);
|
||||
association.window = "//^REGEX2$//";
|
||||
association.sequence = "regex2";
|
||||
m_entry3->autoTypeAssociations()->add(association);
|
||||
association.window = "//^REGEX3-([rd]\\d){2}$//";
|
||||
association.sequence = "regex3";
|
||||
m_entry3->autoTypeAssociations()->add(association);
|
||||
}
|
||||
|
||||
void TestAutoType::cleanup()
|
||||
@ -146,12 +160,36 @@ void TestAutoType::testGlobalAutoTypeTitleMatch()
|
||||
|
||||
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
|
||||
{
|
||||
config()->set("AutoTypeEntryTitleMatch", false);
|
||||
|
||||
m_test->setActiveWindowTitle("An Entry Title!");
|
||||
MessageBox::setNextAnswer(QMessageBox::Ok);
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString());
|
||||
|
||||
}
|
||||
|
||||
void TestAutoType::testGlobalAutoTypeRegExp()
|
||||
{
|
||||
// substring matches are ok
|
||||
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||
m_test->clearActions();
|
||||
|
||||
// should be case-insensitive
|
||||
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||
m_test->clearActions();
|
||||
|
||||
// exact match
|
||||
m_test->setActiveWindowTitle("REGEX2");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
||||
m_test->clearActions();
|
||||
|
||||
// a bit more complicated regex
|
||||
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
||||
m_test->clearActions();
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ private Q_SLOTS:
|
||||
void testGlobalAutoTypeWithOneMatch();
|
||||
void testGlobalAutoTypeTitleMatch();
|
||||
void testGlobalAutoTypeTitleMatchDisabled();
|
||||
void testGlobalAutoTypeRegExp();
|
||||
|
||||
private:
|
||||
AutoTypePlatformInterface* m_platform;
|
||||
@ -53,6 +54,7 @@ private:
|
||||
Group* m_group;
|
||||
Entry* m_entry1;
|
||||
Entry* m_entry2;
|
||||
Entry* m_entry3;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTAUTOTYPE_H
|
||||
|
Loading…
Reference in New Issue
Block a user