Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/databasekey')
-rw-r--r--src/gui/databasekey/KeyComponentWidget.cpp201
-rw-r--r--src/gui/databasekey/KeyComponentWidget.h137
-rw-r--r--src/gui/databasekey/KeyComponentWidget.ui226
-rw-r--r--src/gui/databasekey/KeyFileEditWidget.cpp148
-rw-r--r--src/gui/databasekey/KeyFileEditWidget.h57
-rw-r--r--src/gui/databasekey/KeyFileEditWidget.ui94
-rw-r--r--src/gui/databasekey/PasswordEditWidget.cpp114
-rw-r--r--src/gui/databasekey/PasswordEditWidget.h57
-rw-r--r--src/gui/databasekey/PasswordEditWidget.ui100
-rw-r--r--src/gui/databasekey/YubiKeyEditWidget.cpp141
-rw-r--r--src/gui/databasekey/YubiKeyEditWidget.h57
-rw-r--r--src/gui/databasekey/YubiKeyEditWidget.ui96
12 files changed, 1428 insertions, 0 deletions
diff --git a/src/gui/databasekey/KeyComponentWidget.cpp b/src/gui/databasekey/KeyComponentWidget.cpp
new file mode 100644
index 000000000..769cbab95
--- /dev/null
+++ b/src/gui/databasekey/KeyComponentWidget.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "KeyComponentWidget.h"
+#include "ui_KeyComponentWidget.h"
+
+#include <QStackedWidget>
+#include <QTimer>
+
+KeyComponentWidget::KeyComponentWidget(QWidget* parent)
+ : KeyComponentWidget({}, parent)
+{
+}
+
+KeyComponentWidget::KeyComponentWidget(const QString& name, QWidget* parent)
+ : QWidget(parent)
+ , m_ui(new Ui::KeyComponentWidget())
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->addButton, SIGNAL(clicked(bool)), SIGNAL(componentAddRequested()));
+ connect(m_ui->changeButton, SIGNAL(clicked(bool)), SIGNAL(componentEditRequested()));
+ connect(m_ui->removeButton, SIGNAL(clicked(bool)), SIGNAL(componentRemovalRequested()));
+ connect(m_ui->cancelButton, SIGNAL(clicked(bool)), SLOT(cancelEdit()));
+
+ connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(resetComponentEditWidget()));
+
+ connect(this, SIGNAL(nameChanged(QString)), SLOT(updateComponentName(QString)));
+ connect(this, SIGNAL(descriptionChanged(QString)), SLOT(updateComponentDescription(QString)));
+ connect(this, SIGNAL(componentAddRequested()), SLOT(doAdd()));
+ connect(this, SIGNAL(componentEditRequested()), SLOT(doEdit()));
+ connect(this, SIGNAL(componentRemovalRequested()), SLOT(doRemove()));
+ connect(this, SIGNAL(componentAddChanged(bool)), SLOT(updateAddStatus(bool)));
+
+ bool prev = blockSignals(true);
+ setComponentName(name);
+ blockSignals(prev);
+
+ prev = m_ui->stackedWidget->blockSignals(true);
+ m_ui->stackedWidget->setCurrentIndex(Page::AddNew);
+ m_ui->stackedWidget->blockSignals(prev);
+}
+
+KeyComponentWidget::~KeyComponentWidget()
+{
+}
+
+/**
+ * @param name display name for the key component
+ */
+void KeyComponentWidget::setComponentName(const QString& name)
+{
+ if (name == m_componentName) {
+ return;
+ }
+
+ m_componentName = name;
+ emit nameChanged(name);
+}
+
+/**
+ * @return The key component's display name
+ */
+QString KeyComponentWidget::componentName() const
+{
+ return m_componentName;
+}
+
+void KeyComponentWidget::setComponentDescription(const QString& description)
+{
+ if (description == m_componentDescription) {
+ return;
+ }
+
+ m_componentDescription = description;
+ emit descriptionChanged(description);
+}
+
+QString KeyComponentWidget::componentDescription() const
+{
+ return m_componentDescription;
+}
+
+void KeyComponentWidget::setComponentAdded(bool added)
+{
+ if (m_isComponentAdded == added) {
+ return;
+ }
+
+ m_isComponentAdded = added;
+ emit componentAddChanged(added);
+}
+
+bool KeyComponentWidget::componentAdded() const
+{
+ return m_isComponentAdded;
+}
+
+void KeyComponentWidget::changeVisiblePage(KeyComponentWidget::Page page)
+{
+ m_previousPage = static_cast<Page>(m_ui->stackedWidget->currentIndex());
+ m_ui->stackedWidget->setCurrentIndex(page);
+}
+
+KeyComponentWidget::Page KeyComponentWidget::visiblePage() const
+{
+ return static_cast<Page>(m_ui->stackedWidget->currentIndex());
+}
+
+void KeyComponentWidget::updateComponentName(const QString& name)
+{
+ m_ui->groupBox->setTitle(name);
+ m_ui->addButton->setText(tr("Add %1", "Add a key component").arg(name));
+ m_ui->changeButton->setText(tr("Change %1", "Change a key component").arg(name));
+ m_ui->removeButton->setText(tr("Remove %1", "Remove a key component").arg(name));
+ m_ui->changeOrRemoveLabel->setText(
+ tr("%1 set, click to change or remove", "Change or remove a key component").arg(name));
+}
+
+void KeyComponentWidget::updateComponentDescription(const QString& description)
+{
+ m_ui->componentDescription->setText(description);
+}
+
+void KeyComponentWidget::updateAddStatus(bool added)
+{
+ if (added) {
+ m_ui->stackedWidget->setCurrentIndex(Page::LeaveOrRemove);
+ } else {
+ m_ui->stackedWidget->setCurrentIndex(Page::AddNew);
+ }
+}
+
+void KeyComponentWidget::doAdd()
+{
+ changeVisiblePage(Page::Edit);
+}
+
+void KeyComponentWidget::doEdit()
+{
+ changeVisiblePage(Page::Edit);
+}
+
+void KeyComponentWidget::doRemove()
+{
+ changeVisiblePage(Page::AddNew);
+}
+
+void KeyComponentWidget::cancelEdit()
+{
+ m_ui->stackedWidget->setCurrentIndex(m_previousPage);
+ emit editCanceled();
+}
+
+void KeyComponentWidget::showEvent(QShowEvent* event)
+{
+ resetComponentEditWidget();
+ QWidget::showEvent(event);
+}
+
+void KeyComponentWidget::resetComponentEditWidget()
+{
+ if (!m_componentWidget || static_cast<Page>(m_ui->stackedWidget->currentIndex()) == Page::Edit) {
+ if (m_componentWidget) {
+ delete m_componentWidget;
+ }
+
+ m_componentWidget = componentEditWidget();
+ m_ui->componentWidgetLayout->addWidget(m_componentWidget);
+ initComponentEditWidget(m_componentWidget);
+ }
+
+ QTimer::singleShot(0, this, SLOT(updateSize()));
+}
+
+void KeyComponentWidget::updateSize()
+{
+ for (int i = 0; i < m_ui->stackedWidget->count(); ++i) {
+ if (m_ui->stackedWidget->currentIndex() == i) {
+ m_ui->stackedWidget->widget(i)->setSizePolicy(
+ m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Preferred);
+ } else {
+ m_ui->stackedWidget->widget(i)->setSizePolicy(
+ m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Ignored);
+ }
+ }
+}
diff --git a/src/gui/databasekey/KeyComponentWidget.h b/src/gui/databasekey/KeyComponentWidget.h
new file mode 100644
index 000000000..b73f881fc
--- /dev/null
+++ b/src/gui/databasekey/KeyComponentWidget.h
@@ -0,0 +1,137 @@
+/*
+ * 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_KEYCOMPONENTWIDGET_H
+#define KEEPASSXC_KEYCOMPONENTWIDGET_H
+
+#include <QPointer>
+#include <QScopedPointer>
+#include <QWidget>
+
+namespace Ui
+{
+ class KeyComponentWidget;
+}
+class CompositeKey;
+class QStackedWidget;
+
+class KeyComponentWidget : public QWidget
+{
+ Q_OBJECT
+ // clang-format off
+ Q_PROPERTY(QString componentName READ m_componentName READ componentName
+ WRITE setComponentName NOTIFY nameChanged)
+ Q_PROPERTY(QString componentDescription READ m_componentDescription READ componentDescription
+ WRITE setComponentDescription NOTIFY descriptionChanged)
+ Q_PROPERTY(bool componentAdded READ m_isComponentAdded READ componentAdded
+ WRITE setComponentAdded NOTIFY componentAddChanged)
+ // clang-format on
+
+public:
+ enum Page
+ {
+ AddNew = 0,
+ Edit = 1,
+ LeaveOrRemove = 2
+ };
+
+ explicit KeyComponentWidget(QWidget* parent = nullptr);
+ explicit KeyComponentWidget(const QString& name, QWidget* parent = nullptr);
+ Q_DISABLE_COPY(KeyComponentWidget);
+ ~KeyComponentWidget() override;
+
+ /**
+ * Add the new key component to the given \link CompositeKey.
+ * A caller should always check first with \link validate() if
+ * the new key data is actually valid before adding it to a CompositeKey.
+ *
+ * @param key CompositeKey to add new key to
+ * @return true if added successfully
+ */
+ virtual bool addToCompositeKey(QSharedPointer<CompositeKey> key) = 0;
+
+ /**
+ * Validate key component data to check if key component
+ * may be added to a CompositeKey.
+ *
+ * @param errorMessage error message in case data is not valid
+ * @return true if data is valid
+ */
+ virtual bool validate(QString& errorMessage) const = 0;
+
+ void setComponentName(const QString& name);
+ QString componentName() const;
+ void setComponentDescription(const QString& name);
+ QString componentDescription() const;
+ void setComponentAdded(bool added);
+ bool componentAdded() const;
+ void changeVisiblePage(Page page);
+ Page visiblePage() const;
+
+protected:
+ /**
+ * Construct and return an instance of the key component edit widget
+ * which is to be inserted into the component management UI.
+ * Since previous widgets will be destroyed, every successive call to
+ * this function must return a new widget.
+ *
+ * @return edit widget instance
+ */
+ virtual QWidget* componentEditWidget() = 0;
+
+ /**
+ * Initialize the key component widget created by \link componentEditWidget().
+ * This method is called every time the component edit widget is shown.
+ *
+ * @param widget pointer to the widget
+ */
+ virtual void initComponentEditWidget(QWidget* widget) = 0;
+
+signals:
+ void nameChanged(const QString& newName);
+ void descriptionChanged(const QString& newDescription);
+ void componentAddChanged(bool added);
+ void componentAddRequested();
+ void componentEditRequested();
+ void editCanceled();
+ void componentRemovalRequested();
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
+private slots:
+ void updateComponentName(const QString& name);
+ void updateComponentDescription(const QString& decription);
+ void updateAddStatus(bool added);
+ void doAdd();
+ void doEdit();
+ void doRemove();
+ void cancelEdit();
+ void resetComponentEditWidget();
+ void updateSize();
+
+private:
+ bool m_isComponentAdded = false;
+ Page m_previousPage = Page::AddNew;
+ QString m_componentName;
+ QString m_componentDescription;
+ QPointer<QWidget> m_componentWidget;
+
+ const QScopedPointer<Ui::KeyComponentWidget> m_ui;
+};
+
+#endif // KEEPASSXC_KEYCOMPONENTWIDGET_H
diff --git a/src/gui/databasekey/KeyComponentWidget.ui b/src/gui/databasekey/KeyComponentWidget.ui
new file mode 100644
index 000000000..5036655e0
--- /dev/null
+++ b/src/gui/databasekey/KeyComponentWidget.ui
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>KeyComponentWidget</class>
+ <widget class="QWidget" name="KeyComponentWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>354</width>
+ <height>106</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QGroupBox { font-weight: bold; }</string>
+ </property>
+ <property name="title">
+ <string>Key Component</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <widget class="QWidget" name="addPage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="componentDescription">
+ <property name="text">
+ <string>Key Component Description</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addButton">
+ <property name="text">
+ <string notr="true">Add Key Component</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="editPage">
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="componentWidgetContainer" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="componentWidgetLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="changeOrRemovePage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="changeOrRemoveLabel">
+ <property name="text">
+ <string>Key Component set, click to change or remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="changeButton">
+ <property name="text">
+ <string notr="true">Change Key Component</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeButton">
+ <property name="text">
+ <string notr="true">Remove Key Component</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/databasekey/KeyFileEditWidget.cpp b/src/gui/databasekey/KeyFileEditWidget.cpp
new file mode 100644
index 000000000..e0486ae07
--- /dev/null
+++ b/src/gui/databasekey/KeyFileEditWidget.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 "KeyFileEditWidget.h"
+#include "ui_KeyFileEditWidget.h"
+#include <gui/dbsettings/DatabaseSettingsWidget.h>
+
+#include "gui/FileDialog.h"
+#include "gui/MainWindow.h"
+#include "gui/MessageBox.h"
+#include "keys/CompositeKey.h"
+#include "keys/FileKey.h"
+
+KeyFileEditWidget::KeyFileEditWidget(DatabaseSettingsWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::KeyFileEditWidget())
+ , m_parent(parent)
+{
+ setComponentName(tr("Key File"));
+ setComponentDescription(tr("<p>You can add a key file containing random bytes for additional security.</p>"
+ "<p>You must keep it secret and never lose it or you will be locked out!</p>"));
+}
+
+KeyFileEditWidget::~KeyFileEditWidget()
+{
+}
+
+bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ auto fileKey = QSharedPointer<FileKey>::create();
+ QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ if (!fileKey->load(fileKeyName, nullptr)) {
+ return false;
+ }
+
+ if (fileKey->type() != FileKey::Hashed) {
+ QMessageBox::warning(getMainWindow(),
+ tr("Legacy key file format"),
+ tr("You are using a legacy key file format which may become\n"
+ "unsupported in the future.\n\n"
+ "Generate a new key file in the database security settings."),
+ QMessageBox::Ok);
+ }
+
+ key->addKey(fileKey);
+ return true;
+}
+
+bool KeyFileEditWidget::validate(QString& errorMessage) const
+{
+ FileKey fileKey;
+ QString fileKeyError;
+ QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ if (!fileKey.load(fileKeyName, &fileKeyError)) {
+ errorMessage = tr("Error loading the key file '%1'\nMessage: %2").arg(fileKeyName, fileKeyError);
+ return false;
+ }
+ return true;
+}
+
+QWidget* KeyFileEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+
+ connect(m_compUi->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile()));
+ connect(m_compUi->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile()));
+
+ return m_compEditWidget;
+}
+
+void KeyFileEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->keyFileCombo->setFocus();
+}
+
+void KeyFileEditWidget::createKeyFile()
+{
+ Q_ASSERT(m_compEditWidget);
+ if (!m_compEditWidget) {
+ return;
+ }
+ QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
+ QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters);
+
+ if (!fileName.isEmpty()) {
+ QString errorMsg;
+ bool created = FileKey::create(fileName, &errorMsg);
+ if (!created) {
+ MessageBox::critical(getMainWindow(),
+ tr("Error creating key file"),
+ tr("Unable to create key file: %1").arg(errorMsg),
+ QMessageBox::Button::Ok);
+ } else {
+ m_compUi->keyFileCombo->setEditText(fileName);
+ }
+ }
+}
+
+void KeyFileEditWidget::browseKeyFile()
+{
+ Q_ASSERT(m_compEditWidget);
+ if (!m_compEditWidget) {
+ return;
+ }
+ QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
+ QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters);
+
+ if (QFileInfo(fileName).canonicalFilePath() == m_parent->getDatabase()->canonicalFilePath()) {
+ MessageBox::critical(getMainWindow(),
+ tr("Invalid Key File"),
+ tr("You cannot use the current database as its own keyfile. Please choose a different "
+ "file or generate a new key file."));
+ return;
+ } else if (fileName.endsWith(".kdbx", Qt::CaseInsensitive)) {
+ auto response =
+ MessageBox::warning(getMainWindow(),
+ tr("Suspicious Key File"),
+ tr("The chosen key file looks like a password database file. A key file must be a "
+ "static file that never changes or you will lose access to your database "
+ "forever.\nAre you sure you want to continue with this file?"),
+ MessageBox::Continue | MessageBox::Cancel,
+ MessageBox::Cancel);
+ if (response != MessageBox::Continue) {
+ return;
+ }
+ }
+
+ if (!fileName.isEmpty()) {
+ m_compUi->keyFileCombo->setEditText(fileName);
+ }
+}
diff --git a/src/gui/databasekey/KeyFileEditWidget.h b/src/gui/databasekey/KeyFileEditWidget.h
new file mode 100644
index 000000000..dd414e133
--- /dev/null
+++ b/src/gui/databasekey/KeyFileEditWidget.h
@@ -0,0 +1,57 @@
+/*
+ * 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_KEYFILEEDITWIDGET_H
+#define KEEPASSXC_KEYFILEEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+namespace Ui
+{
+ class KeyFileEditWidget;
+}
+
+class DatabaseSettingsWidget;
+
+class KeyFileEditWidget : public KeyComponentWidget
+{
+ Q_OBJECT
+
+public:
+ explicit KeyFileEditWidget(DatabaseSettingsWidget* parent);
+ Q_DISABLE_COPY(KeyFileEditWidget);
+ ~KeyFileEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+
+private slots:
+ void createKeyFile();
+ void browseKeyFile();
+
+private:
+ const QScopedPointer<Ui::KeyFileEditWidget> m_compUi;
+ QPointer<QWidget> m_compEditWidget;
+ const QPointer<DatabaseSettingsWidget> m_parent;
+};
+
+#endif // KEEPASSXC_KEYFILEEDITWIDGET_H
diff --git a/src/gui/databasekey/KeyFileEditWidget.ui b/src/gui/databasekey/KeyFileEditWidget.ui
new file mode 100644
index 000000000..088995dc8
--- /dev/null
+++ b/src/gui/databasekey/KeyFileEditWidget.ui
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>KeyFileEditWidget</class>
+ <widget class="QWidget" name="KeyFileEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>370</width>
+ <height>76</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="keyFileCombo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="accessibleName">
+ <string>Key file selection</string>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="browseKeyFileButton">
+ <property name="accessibleName">
+ <string>Browse for key file</string>
+ </property>
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="createKeyFileButton">
+ <property name="accessibleName">
+ <string>Generate a new key file</string>
+ </property>
+ <property name="text">
+ <string>Generate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <italic>true</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Note: Do not use a file that may change as that will prevent you from unlocking your database!</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/databasekey/PasswordEditWidget.cpp b/src/gui/databasekey/PasswordEditWidget.cpp
new file mode 100644
index 000000000..2d355cc10
--- /dev/null
+++ b/src/gui/databasekey/PasswordEditWidget.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "PasswordEditWidget.h"
+#include "ui_PasswordEditWidget.h"
+
+#include "core/Resources.h"
+#include "gui/PasswordGeneratorWidget.h"
+#include "keys/CompositeKey.h"
+#include "keys/PasswordKey.h"
+
+#include <QDialog>
+
+PasswordEditWidget::PasswordEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::PasswordEditWidget())
+{
+ setComponentName(tr("Password"));
+ setComponentDescription(tr("<p>A password is the primary method for securing your database.</p>"
+ "<p>Good passwords are long and unique. KeePassXC can generate one for you.</p>"));
+}
+
+PasswordEditWidget::~PasswordEditWidget()
+{
+}
+
+bool PasswordEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ QString pw = m_compUi->enterPasswordEdit->text();
+ if (!pw.isEmpty()) {
+ key->addKey(QSharedPointer<PasswordKey>::create(pw));
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @param visible changed password visibility state
+ */
+void PasswordEditWidget::setPasswordVisible(bool visible)
+{
+ m_compUi->enterPasswordEdit->setShowPassword(visible);
+}
+
+/**
+ * @return password visibility state
+ */
+bool PasswordEditWidget::isPasswordVisible() const
+{
+ return m_compUi->enterPasswordEdit->isPasswordVisible();
+}
+
+bool PasswordEditWidget::isEmpty() const
+{
+ return (visiblePage() == Page::Edit) && m_compUi->enterPasswordEdit->text().isEmpty();
+}
+
+QWidget* PasswordEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+ m_compUi->enterPasswordEdit->enablePasswordGenerator();
+ m_compUi->enterPasswordEdit->setRepeatPartner(m_compUi->repeatPasswordEdit);
+ return m_compEditWidget;
+}
+
+void PasswordEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->enterPasswordEdit->setFocus();
+}
+
+void PasswordEditWidget::hideEvent(QHideEvent* event)
+{
+ if (!isVisible() && m_compUi->enterPasswordEdit) {
+ m_compUi->enterPasswordEdit->setText("");
+ m_compUi->repeatPasswordEdit->setText("");
+ }
+
+ QWidget::hideEvent(event);
+}
+
+bool PasswordEditWidget::validate(QString& errorMessage) const
+{
+ if (m_compUi->enterPasswordEdit->text() != m_compUi->repeatPasswordEdit->text()) {
+ errorMessage = tr("Passwords do not match.");
+ return false;
+ }
+
+ return true;
+}
+
+void PasswordEditWidget::setPassword(const QString& password)
+{
+ Q_ASSERT(m_compEditWidget);
+
+ m_compUi->enterPasswordEdit->setText(password);
+ m_compUi->repeatPasswordEdit->setText(password);
+}
diff --git a/src/gui/databasekey/PasswordEditWidget.h b/src/gui/databasekey/PasswordEditWidget.h
new file mode 100644
index 000000000..802219451
--- /dev/null
+++ b/src/gui/databasekey/PasswordEditWidget.h
@@ -0,0 +1,57 @@
+/*
+ * 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_PASSWORDEDITWIDGET_H
+#define KEEPASSXC_PASSWORDEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+namespace Ui
+{
+ class PasswordEditWidget;
+}
+
+class PasswordEditWidget : public KeyComponentWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PasswordEditWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(PasswordEditWidget);
+ ~PasswordEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
+ void setPasswordVisible(bool visible);
+ bool isPasswordVisible() const;
+ bool isEmpty() const;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+ void hideEvent(QHideEvent* event) override;
+
+private slots:
+ void setPassword(const QString& password);
+
+private:
+ const QScopedPointer<Ui::PasswordEditWidget> m_compUi;
+ QPointer<QWidget> m_compEditWidget;
+};
+
+#endif // KEEPASSXC_PASSWORDEDITWIDGET_H
diff --git a/src/gui/databasekey/PasswordEditWidget.ui b/src/gui/databasekey/PasswordEditWidget.ui
new file mode 100644
index 000000000..d8382ed94
--- /dev/null
+++ b/src/gui/databasekey/PasswordEditWidget.ui
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordEditWidget</class>
+ <widget class="QWidget" name="PasswordEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>571</width>
+ <height>78</height>
+ </rect>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="enterPasswordLabel">
+ <property name="text">
+ <string>Enter password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="PasswordEdit" name="enterPasswordEdit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="accessibleName">
+ <string>Password field</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="repeatPasswordLabel">
+ <property name="text">
+ <string>Confirm password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="PasswordEdit" name="repeatPasswordEdit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="accessibleName">
+ <string>Repeat password field</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>PasswordEdit</class>
+ <extends>QLineEdit</extends>
+ <header>gui/PasswordEdit.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>enterPasswordEdit</tabstop>
+ <tabstop>repeatPasswordEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/databasekey/YubiKeyEditWidget.cpp b/src/gui/databasekey/YubiKeyEditWidget.cpp
new file mode 100644
index 000000000..855bd7099
--- /dev/null
+++ b/src/gui/databasekey/YubiKeyEditWidget.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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 "YubiKeyEditWidget.h"
+#include "ui_YubiKeyEditWidget.h"
+
+#include "config-keepassx.h"
+#include "core/AsyncTask.h"
+#include "gui/MainWindow.h"
+#include "gui/MessageBox.h"
+#include "keys/CompositeKey.h"
+#include "keys/YkChallengeResponseKey.h"
+
+YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::YubiKeyEditWidget())
+{
+ setComponentName(tr("YubiKey Challenge-Response"));
+ setComponentDescription(
+ tr("<p>If you own a <a href=\"https://www.yubico.com/\">YubiKey</a>, you can use it "
+ "for additional security.</p><p>The YubiKey requires one of its slots to be programmed as "
+ "<a href=\"https://www.yubico.com/products/services-software/personalization-tools/challenge-response/\">"
+ "HMAC-SHA1 Challenge-Response</a>.</p>"));
+
+ connect(YubiKey::instance(), SIGNAL(detectComplete(bool)), SLOT(hardwareKeyResponse(bool)), Qt::QueuedConnection);
+}
+
+YubiKeyEditWidget::~YubiKeyEditWidget()
+{
+}
+
+bool YubiKeyEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ if (!m_isDetected || !m_compEditWidget) {
+ return false;
+ }
+
+ int selectionIndex = m_compUi->comboChallengeResponse->currentIndex();
+ auto slot = m_compUi->comboChallengeResponse->itemData(selectionIndex).value<YubiKeySlot>();
+ key->addChallengeResponseKey(QSharedPointer<YkChallengeResponseKey>::create(slot));
+ return true;
+}
+
+bool YubiKeyEditWidget::validate(QString& errorMessage) const
+{
+ if (!m_isDetected) {
+ errorMessage = tr("Could not find any hardware keys!");
+ return false;
+ }
+
+ // Perform a test challenge response
+ int selectionIndex = m_compUi->comboChallengeResponse->currentIndex();
+ auto slot = m_compUi->comboChallengeResponse->itemData(selectionIndex).value<YubiKeySlot>();
+ bool valid = AsyncTask::runAndWaitForFuture([&slot] { return YubiKey::instance()->testChallenge(slot); });
+ if (!valid) {
+ errorMessage = tr("Selected hardware key slot does not support challenge-response!");
+ }
+ return valid;
+}
+
+QWidget* YubiKeyEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+
+ QSizePolicy sp = m_compUi->yubikeyProgress->sizePolicy();
+ sp.setRetainSizeWhenHidden(true);
+ m_compUi->yubikeyProgress->setSizePolicy(sp);
+ m_compUi->yubikeyProgress->setVisible(false);
+
+#ifdef WITH_XC_YUBIKEY
+ connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey()));
+ pollYubikey();
+#endif
+
+ return m_compEditWidget;
+}
+
+void YubiKeyEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->comboChallengeResponse->setFocus();
+}
+
+void YubiKeyEditWidget::pollYubikey()
+{
+#ifdef WITH_XC_YUBIKEY
+ if (!m_compEditWidget) {
+ return;
+ }
+
+ m_isDetected = false;
+ m_compUi->comboChallengeResponse->clear();
+ m_compUi->comboChallengeResponse->addItem(tr("Detecting hardware keys…"));
+ m_compUi->buttonRedetectYubikey->setEnabled(false);
+ m_compUi->comboChallengeResponse->setEnabled(false);
+ m_compUi->yubikeyProgress->setVisible(true);
+
+ YubiKey::instance()->findValidKeys();
+#endif
+}
+
+void YubiKeyEditWidget::hardwareKeyResponse(bool found)
+{
+ if (!m_compEditWidget) {
+ return;
+ }
+
+ m_compUi->comboChallengeResponse->clear();
+ m_compUi->buttonRedetectYubikey->setEnabled(true);
+ m_compUi->yubikeyProgress->setVisible(false);
+
+ if (!found) {
+ m_compUi->comboChallengeResponse->addItem(tr("No hardware keys detected"));
+ m_isDetected = false;
+ return;
+ }
+
+ for (auto& slot : YubiKey::instance()->foundKeys()) {
+ // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
+ m_compUi->comboChallengeResponse->addItem(YubiKey::instance()->getDisplayName(slot), QVariant::fromValue(slot));
+ }
+
+ m_isDetected = true;
+ m_compUi->comboChallengeResponse->setEnabled(true);
+}
diff --git a/src/gui/databasekey/YubiKeyEditWidget.h b/src/gui/databasekey/YubiKeyEditWidget.h
new file mode 100644
index 000000000..d5be0a683
--- /dev/null
+++ b/src/gui/databasekey/YubiKeyEditWidget.h
@@ -0,0 +1,57 @@
+/*
+ * 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_YUBIKEYEDITWIDGET_H
+#define KEEPASSXC_YUBIKEYEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+namespace Ui
+{
+ class YubiKeyEditWidget;
+}
+
+class YkChallengeResponseKey;
+
+class YubiKeyEditWidget : public KeyComponentWidget
+{
+ Q_OBJECT
+
+public:
+ explicit YubiKeyEditWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(YubiKeyEditWidget);
+ ~YubiKeyEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+
+private slots:
+ void hardwareKeyResponse(bool found);
+ void pollYubikey();
+
+private:
+ const QScopedPointer<Ui::YubiKeyEditWidget> m_compUi;
+ QPointer<QWidget> m_compEditWidget;
+ bool m_isDetected = false;
+};
+
+#endif // KEEPASSXC_YUBIKEYEDITWIDGET_H
diff --git a/src/gui/databasekey/YubiKeyEditWidget.ui b/src/gui/databasekey/YubiKeyEditWidget.ui
new file mode 100644
index 000000000..fa150084b
--- /dev/null
+++ b/src/gui/databasekey/YubiKeyEditWidget.ui
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>YubiKeyEditWidget</class>
+ <widget class="QWidget" name="YubiKeyEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>381</width>
+ <height>64</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="buttonRedetectYubikey">
+ <property name="accessibleName">
+ <string>Refresh hardware tokens</string>
+ </property>
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="comboChallengeResponse">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="accessibleName">
+ <string>Hardware key slot selection</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QProgressBar" name="yubikeyProgress">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>2</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="textVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>comboChallengeResponse</tabstop>
+ <tabstop>buttonRedetectYubikey</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>