Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 90d1bf941..c9732c6b7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,153 +1,151 @@
set(kleopatra_version 3.1.3)
# The following is for Windows. Keep in line with kleopatra_version.
set(kleopatra_fileversion 3,1,3,0)
cmake_minimum_required(VERSION 3.0)
project(kleopatra VERSION ${kleopatra_version})
# Add version suffix for internal usage of the version string
set(kleopatra_version ${kleopatra_version}${KLEOPATRA_VERSION_SUFFIX})
option(FORCE_DISABLE_KCMUTILS "Force building Kleopatra without KCMUtils. Doing this will disable configuration KCM Plugins. [default=OFF]" OFF)
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF5_VERSION "5.50.0")
set(KMIME_VERSION "5.9.40")
set(LIBKLEO_VERSION "5.9.40")
set(QT_REQUIRED_VERSION "5.9.0")
set(GPGME_REQUIRED_VERSION "1.8.0")
find_package(ECM ${KF5_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
include(ECMOptionalAddSubdirectory)
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMAddTests)
include(ECMMarkNonGuiExecutable)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
include(CMakePackageConfigHelpers)
include(FeatureSummary)
include(CheckFunctionExists)
include(ECMGeneratePriFile)
include(KDEInstallDirs)
include(KDECMakeSettings)
-include(KDECompilerSettings NO_POLICY_SCOPE)
+include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
include(ECMCoverageOption)
# Find KF5 packages
if (NOT FORCE_DISABLE_KCMUTILS)
find_package(KF5KCMUtils ${KF5_VERSION} CONFIG REQUIRED)
endif()
find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5CoreAddons ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5IconThemes ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5XmlGui ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5DocTools ${KF5_VERSION} CONFIG)
find_package(KF5Crash ${KF5_VERSION} REQUIRED)
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Documentation tools" TYPE OPTIONAL PURPOSE "Required to generate Kleopatra documentation.")
# Optional packages
if (WIN32)
# Only a replacement available for Windows so this
# is required on other platforms.
find_package(KF5DBusAddons ${KF5_VERSION} CONFIG)
set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus"
PURPOSE "DBus session integration"
URL "http://inqlude.org/libraries/kdbusaddons.html"
TYPE OPTIONAL)
else()
find_package(KF5DBusAddons ${KF5_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF5::DBusAddons)
endif()
set(HAVE_QDBUS ${Qt5DBus_FOUND})
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_VERSION} CONFIG REQUIRED)
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(Assuan2 REQUIRED)
set(HAVE_KCMUTILS ${KF5KCMUtils_FOUND})
find_package(Boost 1.34.0 REQUIRED)
find_path(Boost_TOPOLOGICAL_SORT_DIR NAMES boost/graph/topological_sort.hpp PATHS ${Boost_INCLUDE_DIRS})
if(NOT Boost_TOPOLOGICAL_SORT_DIR)
message(FATAL_ERROR "The Boost Topological_sort header was NOT found. Should be part of Boost graph module.")
endif()
set(kleopatra_release FALSE)
if(NOT kleopatra_release)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra
OUTPUT_VARIABLE Kleopatra_WC_REVISION)
string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}")
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%ci ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra
OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE)
string(REGEX REPLACE " [-0-9:+ ]*\n" "" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}")
set(kleopatra_version "${kleopatra_version}-git${Kleopatra_WC_REVISION} (${Kleopatra_WC_LAST_CHANGED_DATE})")
endif()
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h)
include (ConfigureChecks.cmake)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${Boost_INCLUDE_DIR}
${ASSUAN2_INCLUDES}
)
add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS)
-add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
kde_enable_exceptions()
add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
-add_definitions(-DQT_USE_QSTRINGBUILDER)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_subdirectory(pics)
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(autotests)
endif()
install( FILES kleopatra.renamecategories kleopatra.categories DESTINATION ${KDE_INSTALL_CONFDIR} )
if(KF5DocTools_FOUND)
add_subdirectory(doc)
endif()
diff --git a/src/commands/command_p.h b/src/commands/command_p.h
index d9017d195..d9acbaaeb 100644
--- a/src/commands/command_p.h
+++ b/src/commands/command_p.h
@@ -1,144 +1,144 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/command_p.h
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#ifndef __KLEOPATRA_COMMANDS_COMMAND_P_H__
#define __KLEOPATRA_COMMANDS_COMMAND_P_H__
#include "command.h"
#include "view/keylistcontroller.h"
#include <Libkleo/KeyListModel>
#include <KMessageBox>
#include <QAbstractItemView>
#include <QPointer>
#include <QList>
#include <QModelIndex>
#include <gpgme++/key.h>
#include <algorithm>
#include <iterator>
class Kleo::Command::Private
{
friend class ::Kleo::Command;
protected:
Command *const q;
public:
explicit Private(Command *qq, KeyListController *controller);
virtual ~Private();
QAbstractItemView *view() const
{
return view_;
}
QWidget *parentWidgetOrView() const
{
if (parentWidget_) {
return parentWidget_;
} else {
return view_;
}
}
KeyListModelInterface *model() const
{
- return view_ ? dynamic_cast<KeyListModelInterface *>(view_->model()) : 0;
+ return view_ ? dynamic_cast<KeyListModelInterface *>(view_->model()) : nullptr;
}
KeyListController *controller() const
{
return controller_;
}
QList<QModelIndex> indexes() const
{
QList<QModelIndex> result;
std::copy(indexes_.begin(), indexes_.end(), std::back_inserter(result));
return result;
}
GpgME::Key key() const
{
return keys_.empty() ? model() && !indexes_.empty() ? model()->key(indexes_.front()) : GpgME::Key::null : keys_.front();
}
std::vector<GpgME::Key> keys() const
{
return keys_.empty() ? model() ? model()->keys(indexes()) : std::vector<GpgME::Key>() : keys_;
}
void finished()
{
emit q->finished();
if (autoDelete) {
q->deleteLater();
}
}
void canceled()
{
emit q->canceled();
finished();
}
void error(const QString &text, const QString &caption = QString(), KMessageBox::Options options = KMessageBox::Notify) const
{
if (parentWId) {
KMessageBox::errorWId(parentWId, text, caption, options);
} else {
KMessageBox::error(parentWidgetOrView(), text, caption, options);
}
}
void information(const QString &text, const QString &caption = QString(), const QString &dontShowAgainName = QString(), KMessageBox::Options options = KMessageBox::Notify) const
{
if (parentWId) {
KMessageBox::informationWId(parentWId, text, caption, dontShowAgainName, options);
} else {
KMessageBox::information(parentWidgetOrView(), text, caption, dontShowAgainName, options);
}
}
void applyWindowID(QWidget *w) const
{
return q->applyWindowID(w);
}
private:
bool autoDelete : 1;
bool warnWhenRunningAtShutdown : 1;
std::vector<GpgME::Key> keys_;
QList<QPersistentModelIndex> indexes_;
QPointer<QAbstractItemView> view_;
QPointer<QWidget> parentWidget_;
WId parentWId;
QPointer<KeyListController> controller_;
};
#endif /* __KLEOPATRA_COMMANDS_COMMAND_P_H__ */
diff --git a/src/commands/exportpaperkeycommand.cpp b/src/commands/exportpaperkeycommand.cpp
index d25eeeaaf..5e0f53a6f 100644
--- a/src/commands/exportpaperkeycommand.cpp
+++ b/src/commands/exportpaperkeycommand.cpp
@@ -1,149 +1,149 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportpaperkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "exportpaperkeycommand.h"
#include <utils/gnupg-helper.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <KMessageBox>
#include <QProcess>
#include <QPrinter>
#include <QPrintDialog>
#include <QTextDocument>
#include <QFontDatabase>
#include "kleopatra_debug.h"
#include "command_p.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
ExportPaperKeyCommand::ExportPaperKeyCommand(QAbstractItemView *v, KeyListController *c) :
GnuPGProcessCommand(v, c),
mParent(v)
{
connect(&mPkProc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &ExportPaperKeyCommand::pkProcFinished);
mPkProc.setProgram(paperKeyInstallPath());
mPkProc.setArguments(QStringList() << QStringLiteral("--output-type=base16"));
process()->setStandardOutputProcess(&mPkProc);
qCDebug(KLEOPATRA_LOG) << "Starting PaperKey process.";
mPkProc.start();
setAutoDelete(false);
}
QStringList ExportPaperKeyCommand::arguments() const
{
const Key key = d->key();
QStringList result;
result << gpgPath() << QStringLiteral("--batch");
result << QStringLiteral("--export-secret-key");
result << QLatin1String(key.primaryFingerprint());
return result;
}
bool ExportPaperKeyCommand::preStartHook(QWidget *parent) const
{
if (paperKeyInstallPath().isNull()) {
KMessageBox::sorry(parent, xi18nc("@info", "<para><application>Kleopatra</application> uses "
"<application>PaperKey</application> to create a minimized and"
" printable version of your secret key.</para>"
"<para>Please make sure it is installed.</para>"),
i18nc("@title", "Failed to find PaperKey executable."));
return false;
}
return true;
}
void ExportPaperKeyCommand::pkProcFinished(int code, QProcess::ExitStatus status)
{
qCDebug(KLEOPATRA_LOG) << "Paperkey export finished: " << code << "status: " << status;
if (status == QProcess::CrashExit || code) {
qCDebug(KLEOPATRA_LOG) << "Aborting because paperkey failed";
deleteLater();
return;
}
QPrinter printer;
const Key key = d->key();
- printer.setDocName(QStringLiteral("0x%1-sec").arg(key.shortKeyID()));
+ printer.setDocName(QStringLiteral("0x%1-sec").arg(QString::fromLatin1(key.shortKeyID())));
QPrintDialog printDialog(&printer, mParent);
printDialog.setWindowTitle(i18nc("@title", "Print secret key"));
if (printDialog.exec() != QDialog::Accepted) {
qCDebug(KLEOPATRA_LOG) << "Printing aborted.";
deleteLater();
return;
}
- QTextDocument doc(mPkProc.readAllStandardOutput());
+ QTextDocument doc(QString::fromLatin1(mPkProc.readAllStandardOutput()));
doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
doc.print(&printer);
deleteLater();
}
QString ExportPaperKeyCommand::errorCaption() const
{
return i18nc("@title:window", "Error printing secret key");
}
QString ExportPaperKeyCommand::crashExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>The GPG process that tried to export the secret key "
"ended prematurely because of an unexpected error.</para>"
"<para>Please check the output of <icode>%1</icode> for details.</para>",
args.join(QLatin1Char(' ')));
}
QString ExportPaperKeyCommand::errorExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>An error occurred while trying to export the secret key.</para> "
"<para>The output from <command>%1</command> was: <message>%2</message></para>",
args[0], errorString());
}
diff --git a/src/commands/genrevokecommand.cpp b/src/commands/genrevokecommand.cpp
index d8f8edfb3..0d8d3f1ee 100644
--- a/src/commands/genrevokecommand.cpp
+++ b/src/commands/genrevokecommand.cpp
@@ -1,213 +1,213 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/genrevokecommand.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "genrevokecommand.h"
#include <utils/gnupg-helper.h>
#include <Libkleo/Formatting>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <KMessageBox>
#include <QProcess>
#include <QTextStream>
#include <QFile>
#include <QFileDialog>
#include "kleopatra_debug.h"
#include "command_p.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
GenRevokeCommand::GenRevokeCommand(QAbstractItemView *v, KeyListController *c) :
GnuPGProcessCommand(v, c)
{
}
GenRevokeCommand::GenRevokeCommand(KeyListController *c)
: GnuPGProcessCommand(c)
{
}
GenRevokeCommand::GenRevokeCommand(const Key &key)
: GnuPGProcessCommand(key)
{
}
// Fixup the revocation certificate similar to GnuPG
void GenRevokeCommand::postSuccessHook(QWidget *parentWidget)
{
QFile f(mOutputFileName);
if (!f.open(QIODevice::ReadOnly)) {
// Should never happen because in this case we would not have had a success.
KMessageBox::error(parentWidget, errorCaption(),
QStringLiteral("Failed to access the created output file."));
return;
}
- const QString revCert = f.readAll();
+ const QString revCert = QString::fromLocal8Bit(f.readAll());
f.close();
if (!f.open(QIODevice::WriteOnly)) {
KMessageBox::error(parentWidget, errorCaption(),
QStringLiteral("Failed to write to the created output file."));
return;
}
QTextStream s(&f);
s << i18n("This is a revocation certificate for the OpenPGP key:")
<< "\n\n " << Formatting::prettyNameAndEMail(d->key())
<< "\n Fingerprint: " << d->key().primaryFingerprint() << "\n\n"
<< i18n("A revocation certificate is a kind of \"kill switch\" to publicly\n"
"declare that a key shall not anymore be used. It is not possible\n"
"to retract such a revocation certificate once it has been published.")
<< "\n\n"
<< i18n("Use it to revoke this key in case of a compromise or loss of\n"
"the secret key.")
<< "\n\n"
<< i18n("To avoid an accidental use of this file, a colon has been inserted\n"
"before the 5 dashes below. Remove this colon with a text editor\n"
"before importing and publishing this revocation certificate.")
<< "\n\n:"
<< revCert;
s.flush();
qCDebug(KLEOPATRA_LOG) << "revocation certificate stored as:" << mOutputFileName;
f.close();
KMessageBox::information(d->parentWidgetOrView(),
i18nc("@info", "Certificate successfully created.<br><br>"
"Note:<br>To prevent accidental import of the revocation<br>"
"it is required to manually edit the certificate<br>"
"before it can be imported."),
i18n("Revocation certificate created"));
}
/* Well not much to do with GnuPGProcessCommand anymore I guess.. */
void GenRevokeCommand::doStart()
{
while (mOutputFileName.isEmpty()) {
mOutputFileName = QFileDialog::getSaveFileName(d->parentWidgetOrView(), i18n("Generate revocation certificate"),
QString(),
QStringLiteral("%1 (*.rev)").arg(i18n("Revocation Certificates ")));
if (mOutputFileName.isEmpty()) {
d->finished();
return;
}
if (!mOutputFileName.endsWith(QLatin1String(".rev"))) {
mOutputFileName += QLatin1String(".rev");
}
if (QFileInfo(mOutputFileName).exists()) {
auto sel = KMessageBox::questionYesNo(d->parentWidgetOrView(), i18n("The file <b>%1</b> already exists.\n"
"Overwrite?", mOutputFileName),
i18n("Overwrite Existing File?"));
if (sel == KMessageBox::No) {
mOutputFileName = QString();
}
}
}
auto proc = process();
// We do custom io
disconnect(proc, SIGNAL(readyReadStandardError()),
this, SLOT(slotProcessReadyReadStandardError()));
proc->setReadChannel(QProcess::StandardOutput);
GnuPGProcessCommand::doStart();
connect(proc, &QProcess::readyReadStandardOutput,
this, [proc] () {
while (proc->canReadLine()) {
const QString line = QString::fromUtf8(proc->readLine()).trimmed();
// Command-fd is a stable interface, while this is all kind of hacky we
// are on a deadline :-/
if (line == QStringLiteral("[GNUPG:] GET_BOOL gen_revoke.okay")) {
proc->write("y\n");
} else if (line == QStringLiteral("[GNUPG:] GET_LINE ask_revocation_reason.code")) {
proc->write("0\n");
} else if (line == QStringLiteral("[GNUPG:] GET_LINE ask_revocation_reason.text")) {
proc->write("\n");
} else if (line == QStringLiteral("[GNUPG:] GET_BOOL openfile.overwrite.okay")) {
// We asked before
proc->write("y\n");
} else if (line == QStringLiteral("[GNUPG:] GET_BOOL ask_revocation_reason.okay")) {
proc->write("y\n");
}
}
});
}
QStringList GenRevokeCommand::arguments() const
{
const Key key = d->key();
QStringList result;
result << gpgPath() << QStringLiteral("--command-fd") << QStringLiteral("0") << QStringLiteral("--status-fd") << QStringLiteral("1")
<< QStringLiteral("-o") << mOutputFileName
<< QStringLiteral("--gen-revoke")
<< QLatin1String(key.primaryFingerprint());
return result;
}
QString GenRevokeCommand::errorCaption() const
{
return i18nc("@title:window", "Error creating revocation certificate");
}
QString GenRevokeCommand::crashExitMessage(const QStringList &) const
{
// We show a success message so a failure is either the user aborted
// or a bug.
qCDebug(KLEOPATRA_LOG) << "Crash exit of GenRevokeCommand";
return QString();
}
QString GenRevokeCommand::errorExitMessage(const QStringList &) const
{
// We show a success message so a failure is either the user aborted
// or a bug.
qCDebug(KLEOPATRA_LOG) << "Error exit of GenRevokeCommand";
return QString();
}
diff --git a/src/commands/importpaperkeycommand.cpp b/src/commands/importpaperkeycommand.cpp
index 7e2f430e4..03f587928 100644
--- a/src/commands/importpaperkeycommand.cpp
+++ b/src/commands/importpaperkeycommand.cpp
@@ -1,247 +1,247 @@
/* commands/importperkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "importpaperkeycommand.h"
#include <utils/gnupg-helper.h>
#include <gpgme++/key.h>
#include <gpgme++/importresult.h>
#include <QGpgME/Protocol>
#include <QGpgME/ImportJob>
#include <QGpgME/ExportJob>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
#include <KMessageBox>
#include <QFileDialog>
#include <QTextStream>
#include "kleopatra_debug.h"
#include "command_p.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
ImportPaperKeyCommand::ImportPaperKeyCommand(const GpgME::Key &k) :
GnuPGProcessCommand(k)
{
}
QStringList ImportPaperKeyCommand::arguments() const
{
const Key key = d->key();
QStringList result;
result << paperKeyInstallPath() << QStringLiteral("--pubring")
<< mTmpDir.path() + QStringLiteral("/pubkey.gpg")
<< QStringLiteral("--secrets")
<< mTmpDir.path() + QStringLiteral("/secrets.txt")
<< QStringLiteral("--output")
<< mTmpDir.path() + QStringLiteral("/seckey.gpg");
return result;
}
void ImportPaperKeyCommand::exportResult(const GpgME::Error &err, const QByteArray &data)
{
if (err) {
- d->error(err.asString(), errorCaption());
+ d->error(QString::fromUtf8(err.asString()), errorCaption());
d->finished();
return;
}
if (!mTmpDir.isValid()) {
// Should not happen so no i18n
d->error(QStringLiteral("Failed to get temporary directory"), errorCaption());
qCWarning(KLEOPATRA_LOG) << "Failed to get temporary dir";
d->finished();
return;
}
const QString fileName = mTmpDir.path() + QStringLiteral("/pubkey.gpg");
QFile f(fileName);
if (!f.open(QIODevice::WriteOnly)) {
d->error(QStringLiteral("Failed to create temporary file"), errorCaption());
qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file";
d->finished();
return;
}
f.write(data);
f.close();
// Copy and sanitize input a bit
QFile input(mFileName);
if (!input.open(QIODevice::ReadOnly)) {
d->error(xi18n("Cannot open <filename>%1</filename> for reading.", mFileName), errorCaption());
d->finished();
return;
}
const QString outName = mTmpDir.path() + QStringLiteral("/secrets.txt");
QFile out(outName);
if (!out.open(QIODevice::WriteOnly)) {
// Should not happen
d->error(QStringLiteral("Failed to create temporary file"), errorCaption());
qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file for writing";
d->finished();
return;
}
QTextStream in(&input);
while (!in.atEnd()) {
// Paperkey is picky, tabs may not be part. Neither may be empty lines.
const QString line = in.readLine().trimmed().replace(QLatin1Char('\t'), QStringLiteral(" ")) +
QLatin1Char('\n');
out.write(line.toUtf8());
}
input.close();
out.close();
GnuPGProcessCommand::doStart();
}
void ImportPaperKeyCommand::postSuccessHook(QWidget *)
{
qCDebug(KLEOPATRA_LOG) << "Paperkey secrets restore finished successfully.";
QFile secKey(mTmpDir.path() + QStringLiteral("/seckey.gpg"));
if (!secKey.open(QIODevice::ReadOnly)) {
d->error(QStringLiteral("Failed to open temporary secret"), errorCaption());
qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file";
finished();
return;
}
auto data = secKey.readAll();
secKey.close();
auto importjob = QGpgME::openpgp()->importJob();
auto result = importjob->exec(data);
delete importjob;
if (result.error()) {
- d->error(result.error().asString(), errorCaption());
+ d->error(QString::fromUtf8(result.error().asString()), errorCaption());
finished();
return;
}
if (!result.numSecretKeysImported() ||
(result.numSecretKeysUnchanged() == result.numSecretKeysImported())) {
d->error(i18n("Failed to restore any secret keys."), errorCaption());
finished();
return;
}
// Refresh the key after success
KeyCache::mutableInstance()->reload(OpenPGP);
finished();
d->information(xi18nc("@info", "Successfully restored the secret key parts from <filename>%1</filename>",
mFileName));
return;
}
void ImportPaperKeyCommand::doStart()
{
if (paperKeyInstallPath().isNull()) {
KMessageBox::sorry(d->parentWidgetOrView(),
xi18nc("@info", "<para><application>Kleopatra</application> uses "
"<application>PaperKey</application> to import your "
"text backup.</para>"
"<para>Please make sure it is installed.</para>"),
i18nc("@title", "Failed to find PaperKey executable."));
return;
}
mFileName = QFileDialog::getOpenFileName(d->parentWidgetOrView(), i18n("Select input file"),
QString(),
QStringLiteral("%1 (*.txt)").arg(i18n("Paper backup"))
#ifdef Q_OS_WIN
/* For whatever reason at least with Qt 5.6.1 the native file dialog crashes in
* my (aheinecke) Windows 10 environment when invoked here.
* In other places it works, with the same arguments as in other places (e.g. import)
* it works. But not here. Maybe it's our (gpg4win) build? But why did it only
* crash here?
*
* It does not crash immediately, the program flow continues for a while before it
* crashes so this is hard to debug.
*
* There are some reports about this
* QTBUG-33119 QTBUG-41416 where different people describe "bugs" but they
* describe them differntly also not really reproducible.
* Anyway this works for now and for such an exotic feature its good enough for now.
*/
, 0, QFileDialog::DontUseNativeDialog
#endif
);
if (mFileName.isEmpty()) {
d->finished();
return;
}
auto exportJob = QGpgME::openpgp()->publicKeyExportJob();
// Do not change to new style connect without testing on
// Windows / mingw first for compatibility please.
connect(exportJob, SIGNAL(result(GpgME::Error,QByteArray)),
this, SLOT(exportResult(GpgME::Error,QByteArray)));
exportJob->start(QStringList() << QLatin1String(d->key().primaryFingerprint()));
}
QString ImportPaperKeyCommand::errorCaption() const
{
return i18nc("@title:window", "Error importing secret key");
}
QString ImportPaperKeyCommand::crashExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>The GPG process that tried to restore the secret key "
"ended prematurely because of an unexpected error.</para>"
"<para>Please check the output of <icode>%1</icode> for details.</para>",
args.join(QLatin1Char(' ')));
}
QString ImportPaperKeyCommand::errorExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>An error occurred while trying to restore the secret key.</para> "
"<para>The output from <command>%1</command> was:</para>"
"<para><message>%2</message></para>",
args[0], errorString());
}
QString ImportPaperKeyCommand::successMessage(const QStringList &) const
{
return QString();
}
diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp
index 2b831984b..ac3a37d86 100644
--- a/src/commands/keytocardcommand.cpp
+++ b/src/commands/keytocardcommand.cpp
@@ -1,269 +1,269 @@
/* commands/setinitialpincommand.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "keytocardcommand.h"
#include "kleopatra_debug.h"
#include "command_p.h"
#include "smartcard/readerstatus.h"
#include "smartcard/openpgpcard.h"
#include <QInputDialog>
#include <QDateTime>
#include <QStringList>
#include <KLocalizedString>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION > 0x10801
# define GPGME_SUBKEY_HAS_KEYGRIP
#endif
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
bool KeyToCardCommand::supported()
{
#ifdef GPGME_SUBKEY_HAS_KEYGRIP
return true;
#else
return false;
#endif
}
class KeyToCardCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::KeyToCardCommand;
KeyToCardCommand *q_func() const
{
return static_cast<KeyToCardCommand *>(q);
}
public:
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &key, const std::string &serialno);
~Private();
private:
void start()
{
// Check if we need to ask the user for the slot
if ((mKey.canSign() || mKey.canCertify()) && !mKey.canEncrypt() && !mKey.canAuthenticate()) {
// Signing only
slotDetermined(1);
return;
}
if (mKey.canEncrypt() && !(mKey.canSign() || mKey.canCertify()) && !mKey.canAuthenticate()) {
// Encrypt only
slotDetermined(2);
return;
}
if (mKey.canAuthenticate() && !(mKey.canSign() || mKey.canCertify()) && !mKey.canEncrypt()) {
// Auth only
slotDetermined(3);
return;
}
// Multiple uses, ask user.
QStringList options;
if (mKey.canSign() || mKey.canCertify()) {
options << i18n("Signature") + QStringLiteral(" (1)");
}
if (mKey.canEncrypt()) {
options << i18n("Encryption") + QStringLiteral(" (2)");
}
if (mKey.canAuthenticate()) {
options << i18n("Authentication") + QStringLiteral(" (3)");
}
dialog = std::shared_ptr<QInputDialog> (new QInputDialog(parentWidgetOrView()));
dialog->setComboBoxItems(options);
connect(dialog.get(), &QDialog::rejected, q_func(), [this] () {finished();});
connect(dialog.get(), &QInputDialog::textValueSelected, q_func(), [this] (const QString &text) {
slotDetermined(text.at(text.size() - 1).digitValue());
});
}
void slotDetermined(int slot)
{
// Check if we need to do the overwrite warning.
const auto cards = ReaderStatus::instance()->getCards();
qDebug() << "slot determined" << slot;
bool cardFound = false;
std::string existingKey;
QString encKeyWarning;
for (const auto &card: cards) {
if (card->serialNumber() == mSerial) {
const auto pgpCard = dynamic_cast<SmartCard::OpenPGPCard*>(card.get());
Q_ASSERT(pgpCard);
cardFound = true;
if (slot == 1) {
existingKey = pgpCard->sigFpr();
break;
}
if (slot == 2) {
existingKey = pgpCard->encFpr();
encKeyWarning = i18n("It will no longer be possible to decrypt past communication "
"encrypted for the existing key.");
break;
}
if (slot == 3) {
existingKey = pgpCard->authFpr();
break;
}
break;
}
}
if (!cardFound) {
error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(mSerial)));
finished();
return;
}
if (!existingKey.empty()) {
if (KMessageBox::warningContinueCancel(parentWidgetOrView(), i18nc("@info",
"<p>This card already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
"<p>If there is no backup the existing key will be irrecoverably lost.</p>") +
i18n("The existing key has the fingerprint:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
encKeyWarning,
i18nc("@title:window", "Overwrite existing key"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous)
!= KMessageBox::Continue) {
finished();
return;
}
}
// Now do the deed
const auto time = QDateTime::fromTime_t(mKey.creationTime());
const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss"));
#ifdef GPGME_SUBKEY_HAS_KEYGRIP
- const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 OPENPGP.%3 %4").arg(mKey.keyGrip())
+ const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 OPENPGP.%3 %4").arg(QString::fromLatin1(mKey.keyGrip()))
.arg(QString::fromStdString(mSerial))
.arg(slot)
.arg(timestamp);
ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), q_func(), "keyToCardDone");
#else
finished();
#endif
}
private:
std::shared_ptr<QInputDialog> dialog;
std::string mSerial;
GpgME::Subkey mKey;
};
KeyToCardCommand::Private *KeyToCardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const KeyToCardCommand::Private *KeyToCardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
void KeyToCardCommand::keyToCardDone(const GpgME::Error &err)
{
if (err) {
d->error(i18nc("@info",
- "Moving the key to the card failed: %1", err.asString()),
+ "Moving the key to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
/* TODO DELETE_KEY is too strong, because it also deletes the stub
* of the secret key. I could not find out how GnuPG does this. Question
* to GnuPG Developers is pending an answer
if (KMessageBox::questionYesNo(d->parentWidgetOrView(),
i18n("Do you want to delete the key on this computer?"),
i18nc("@title:window",
"Key transferred to card")) == KMessageBox::Yes) {
const QString cmd = QStringLiteral("DELETE_KEY --force %1").arg(d->mKey.keyGrip());
// Using readerstatus is a bit overkill but it's an easy way to talk to the agent.
ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), this, "deleteDone");
}
*/
KMessageBox::information(d->parentWidgetOrView(),
i18n("Successfully copied the key to the card."),
i18nc("@title", "Success"));
}
d->finished();
}
void KeyToCardCommand::deleteDone(const GpgME::Error &err)
{
if (err) {
- d->error(i18nc("@info", "Failed to delete the key: %1", err.asString()),
+ d->error(i18nc("@info", "Failed to delete the key: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
}
d->finished();
}
KeyToCardCommand::Private::Private(KeyToCardCommand *qq,
const GpgME::Subkey &key,
const std::string &serialno)
: Command::Private(qq, nullptr),
dialog(),
mSerial(serialno),
mKey(key)
{
}
KeyToCardCommand::Private::~Private() {}
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialno)
: Command(new Private(this, key, serialno))
{
}
KeyToCardCommand::~KeyToCardCommand() {}
void KeyToCardCommand::doStart()
{
d->start();
}
void KeyToCardCommand::doCancel()
{
if (d->dialog) {
d->dialog->close();
}
}
#undef q_func
#undef d_func
diff --git a/src/crypto/certificateresolver.cpp b/src/crypto/certificateresolver.cpp
index 3489ea26b..fc8069b28 100644
--- a/src/crypto/certificateresolver.cpp
+++ b/src/crypto/certificateresolver.cpp
@@ -1,286 +1,286 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/certificateresolver.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "certificateresolver.h"
#include <Libkleo/KeyCache>
#include <gpgme++/key.h>
#include <KConfig>
#include <KConfigGroup>
#include <QRegularExpression>
#include <QByteArray>
#include <QHash>
#include <QSet>
#include <algorithm>
#include <iterator>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
using namespace KMime::HeaderParsing;
std::vector< std::vector<Key> > CertificateResolver::resolveRecipients(const std::vector<Mailbox> &recipients, Protocol proto)
{
std::vector< std::vector<Key> > result;
std::transform(recipients.begin(), recipients.end(),
std::back_inserter(result),
[proto](const Mailbox &recipient) {
return resolveRecipient(recipient, proto);
});
return result;
}
std::vector<Key> CertificateResolver::resolveRecipient(const Mailbox &recipient, Protocol proto)
{
- std::vector<Key> result = KeyCache::instance()->findByEMailAddress(recipient.address());
+ std::vector<Key> result = KeyCache::instance()->findByEMailAddress(recipient.address().constData());
auto end = std::remove_if(result.begin(), result.end(),
[](const Key &key) { return key.canEncrypt(); });
if (proto != UnknownProtocol)
end = std::remove_if(result.begin(), end,
[proto](const Key &key) { return key.protocol() != proto; });
result.erase(end, result.end());
return result;
}
std::vector< std::vector<Key> > CertificateResolver::resolveSigners(const std::vector<Mailbox> &signers, Protocol proto)
{
std::vector< std::vector<Key> > result;
std::transform(signers.begin(), signers.end(),
std::back_inserter(result),
[proto](const Mailbox &signer) {
return resolveSigner(signer, proto);
});
return result;
}
std::vector<Key> CertificateResolver::resolveSigner(const Mailbox &signer, Protocol proto)
{
- std::vector<Key> result = KeyCache::instance()->findByEMailAddress(signer.address());
+ std::vector<Key> result = KeyCache::instance()->findByEMailAddress(signer.address().constData());
auto end = std::remove_if(result.begin(), result.end(),
[](const Key &key) { return key.hasSecret(); });
end = std::remove_if(result.begin(), end,
[](const Key &key) { return key.canReallySign(); });
if (proto != UnknownProtocol)
end = std::remove_if(result.begin(), end,
[proto](const Key &key) { return key.protocol() != proto; });
result.erase(end, result.end());
return result;
}
class KConfigBasedRecipientPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedRecipientPreferences;
KConfigBasedRecipientPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QHash<QByteArray, QByteArray> pgpPrefs;
mutable QHash<QByteArray, QByteArray> cmsPrefs;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedRecipientPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false)
{
Q_ASSERT(m_config);
}
KConfigBasedRecipientPreferences::Private::~Private()
{
writePrefs();
}
void KConfigBasedRecipientPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
const QSet<QByteArray> keys = pgpPrefs.keys().toSet() + cmsPrefs.keys().toSet();
int n = 0;
for (const QByteArray &i : keys) {
KConfigGroup group(m_config, QStringLiteral("EncryptionPreference_%1").arg(n++));
group.writeEntry("email", i);
const QByteArray pgp = pgpPrefs.value(i);
if (!pgp.isEmpty()) {
group.writeEntry("pgpCertificate", pgp);
}
const QByteArray cms = cmsPrefs.value(i);
if (!cms.isEmpty()) {
group.writeEntry("cmsCertificate", cms);
}
}
m_config->sync();
m_dirty = false;
}
void KConfigBasedRecipientPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const QStringList groups = m_config->groupList().filter(QRegularExpression(QStringLiteral("^EncryptionPreference_\\d+$")));
for (const QString &i : groups) {
const KConfigGroup group(m_config, i);
const QByteArray id = group.readEntry("email", QByteArray());
if (id.isEmpty()) {
continue;
}
pgpPrefs.insert(id, group.readEntry("pgpCertificate", QByteArray()));
cmsPrefs.insert(id, group.readEntry("cmsCertificate", QByteArray()));
}
m_parsed = true;
}
KConfigBasedRecipientPreferences::KConfigBasedRecipientPreferences(const KSharedConfigPtr &config) : d(new Private(config, this))
{
}
KConfigBasedRecipientPreferences::~KConfigBasedRecipientPreferences()
{
d->writePrefs();
}
Key KConfigBasedRecipientPreferences::preferredCertificate(const Mailbox &recipient, Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsPrefs : d->pgpPrefs).value(recipient.address());
- return KeyCache::instance()->findByKeyIDOrFingerprint(keyId);
+ return KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData());
}
void KConfigBasedRecipientPreferences::setPreferredCertificate(const Mailbox &recipient, Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
if (!recipient.hasAddress()) {
return;
}
(protocol == CMS ? d->cmsPrefs : d->pgpPrefs).insert(recipient.address(), certificate.keyID());
d->m_dirty = true;
}
class KConfigBasedSigningPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedSigningPreferences;
KConfigBasedSigningPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QByteArray pgpSigningCertificate;
mutable QByteArray cmsSigningCertificate;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedSigningPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false)
{
Q_ASSERT(m_config);
}
void KConfigBasedSigningPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const KConfigGroup group(m_config, "SigningPreferences");
pgpSigningCertificate = group.readEntry("pgpSigningCertificate", QByteArray());
cmsSigningCertificate = group.readEntry("cmsSigningCertificate", QByteArray());
m_parsed = true;
}
void KConfigBasedSigningPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
KConfigGroup group(m_config, "SigningPreferences");
group.writeEntry("pgpSigningCertificate", pgpSigningCertificate);
group.writeEntry("cmsSigningCertificate", cmsSigningCertificate);
m_config->sync();
m_dirty = false;
}
KConfigBasedSigningPreferences::Private::~Private()
{
writePrefs();
}
KConfigBasedSigningPreferences::KConfigBasedSigningPreferences(const KSharedConfigPtr &config) : d(new Private(config, this))
{
}
KConfigBasedSigningPreferences::~KConfigBasedSigningPreferences()
{
d->writePrefs();
}
Key KConfigBasedSigningPreferences::preferredCertificate(Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate);
- const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(keyId);
+ const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData());
return key.hasSecret() ? key : Key::null;
}
void KConfigBasedSigningPreferences::setPreferredCertificate(Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
(protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate) = certificate.keyID();
d->m_dirty = true;
}
diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp
index 48ec0e48b..db142d56f 100644
--- a/src/crypto/signencryptfilescontroller.cpp
+++ b/src/crypto/signencryptfilescontroller.cpp
@@ -1,726 +1,726 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencryptfilescontroller.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 Klarälvdalens Datakonsult AB
2017 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "signencryptfilescontroller.h"
#include "signencrypttask.h"
#include "certificateresolver.h"
#include "crypto/gui/signencryptfileswizard.h"
#include "crypto/taskcollection.h"
#include "fileoperationspreferences.h"
#include "utils/input.h"
#include "utils/output.h"
#include "utils/kleo_assert.h"
#include "utils/archivedefinition.h"
#include "utils/path-helper.h"
#include <Libkleo/Exception>
#include <Libkleo/Classify>
#include <kmime/kmime_header_parsing.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QPointer>
#include <QTimer>
#include <QFileInfo>
#include <QDir>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
class SignEncryptFilesController::Private
{
friend class ::Kleo::Crypto::SignEncryptFilesController;
SignEncryptFilesController *const q;
public:
explicit Private(SignEncryptFilesController *qq);
~Private();
private:
void slotWizardOperationPrepared();
void slotWizardCanceled();
private:
void ensureWizardCreated();
void ensureWizardVisible();
void updateWizardMode();
void cancelAllTasks();
void reportError(int err, const QString &details)
{
q->setLastError(err, details);
q->emitDoneOrError();
}
void schedule();
std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto);
static void assertValidOperation(unsigned int);
static QString titleForOperation(unsigned int op);
private:
std::vector< std::shared_ptr<SignEncryptTask> > runnable, completed;
std::shared_ptr<SignEncryptTask> cms, openpgp;
QPointer<SignEncryptFilesWizard> wizard;
QStringList files;
unsigned int operation;
Protocol protocol;
};
SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq)
: q(qq),
runnable(),
cms(),
openpgp(),
wizard(),
files(),
operation(SignAllowed | EncryptAllowed | ArchiveAllowed),
protocol(UnknownProtocol)
{
}
SignEncryptFilesController::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
}
QString SignEncryptFilesController::Private::titleForOperation(unsigned int op)
{
const bool signDisallowed = (op & SignMask) == SignDisallowed;
const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed;
const bool archiveSelected = (op & ArchiveMask) == ArchiveForced;
kleo_assert(!signDisallowed || !encryptDisallowed);
if (!signDisallowed && encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Sign Files");
} else {
return i18n("Sign Files");
}
}
if (signDisallowed && !encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Encrypt Files");
} else {
return i18n("Encrypt Files");
}
}
if (archiveSelected) {
return i18n("Archive and Sign/Encrypt Files");
} else {
return i18n("Sign/Encrypt Files");
}
}
SignEncryptFilesController::SignEncryptFilesController(QObject *p)
: Controller(p), d(new Private(this))
{
}
SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
: Controller(ctx, p), d(new Private(this))
{
}
SignEncryptFilesController::~SignEncryptFilesController()
{
qCDebug(KLEOPATRA_LOG);
if (d->wizard && !d->wizard->isVisible()) {
delete d->wizard;
}
//d->wizard->close(); ### ?
}
void SignEncryptFilesController::setProtocol(Protocol proto)
{
kleo_assert(d->protocol == UnknownProtocol ||
d->protocol == proto);
d->protocol = proto;
d->ensureWizardCreated();
}
Protocol SignEncryptFilesController::protocol() const
{
return d->protocol;
}
// static
void SignEncryptFilesController::Private::assertValidOperation(unsigned int op)
{
kleo_assert((op & SignMask) == SignDisallowed ||
(op & SignMask) == SignAllowed ||
(op & SignMask) == SignSelected);
kleo_assert((op & EncryptMask) == EncryptDisallowed ||
(op & EncryptMask) == EncryptAllowed ||
(op & EncryptMask) == EncryptSelected);
kleo_assert((op & ArchiveMask) == ArchiveDisallowed ||
(op & ArchiveMask) == ArchiveAllowed ||
(op & ArchiveMask) == ArchiveForced);
kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0);
}
void SignEncryptFilesController::setOperationMode(unsigned int mode)
{
Private::assertValidOperation(mode);
d->operation = mode;
d->updateWizardMode();
}
void SignEncryptFilesController::Private::updateWizardMode()
{
if (!wizard) {
return;
}
wizard->setWindowTitle(titleForOperation(operation));
const unsigned int signOp = (operation & SignMask);
const unsigned int encrOp = (operation & EncryptMask);
const unsigned int archOp = (operation & ArchiveMask);
if (signOp == SignDisallowed) {
wizard->setSigningUserMutable(false);
wizard->setSigningPreset(false);
} else {
wizard->setSigningUserMutable(true);
wizard->setSigningPreset(signOp == SignSelected);
}
if (encrOp == EncryptDisallowed) {
wizard->setEncryptionPreset(false);
wizard->setEncryptionUserMutable(false);
} else {
wizard->setEncryptionUserMutable(true);
wizard->setEncryptionPreset(false);
wizard->setEncryptionPreset(encrOp == EncryptSelected);
}
wizard->setArchiveForced(archOp == ArchiveForced);
wizard->setArchiveMutable(archOp == ArchiveAllowed);
}
unsigned int SignEncryptFilesController::operationMode() const
{
return d->operation;
}
static const char *extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
{
unsigned int cls = pgp ? Class::OpenPGP : Class::CMS;
if (encrypt) {
cls |= Class::CipherText;
} else if (sign) {
cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature;
}
cls |= ascii ? Class::Ascii : Class::Binary;
const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
if (const char *const ext = outputFileExtension(cls, usePGPFileExt)) {
return ext;
} else {
return "out";
}
}
static std::shared_ptr<ArchiveDefinition> getDefaultAd()
{
std::vector<std::shared_ptr<ArchiveDefinition> > ads = ArchiveDefinition::getArchiveDefinitions();
Q_ASSERT(!ads.empty());
std::shared_ptr<ArchiveDefinition> ad = ads.front();
const FileOperationsPreferences prefs;
Q_FOREACH (const std::shared_ptr<ArchiveDefinition> toCheck, ads) {
if (toCheck->id() == prefs.archiveCommand()) {
ad = toCheck;
break;
}
}
return ad;
}
static QMap <int, QString> buildOutputNames(const QStringList &files, const bool archive)
{
QMap <int, QString> nameMap;
// Build the default names for the wizard.
QString baseNameCms;
QString baseNamePgp;
const QFileInfo firstFile(files.first());
if (archive) {
QString baseName;
baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(files.size() > 1 ?
i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") :
firstFile.baseName());
const auto ad = getDefaultAd();
baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.');
baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.');
} else {
baseNameCms = baseNamePgp = files.first() + QLatin1Char('.');
}
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
- nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + extension(false, true, false, ascii, true));
- nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + extension(false, false, true, ascii, false));
- nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + extension(true, true, true, ascii, false));
- nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + extension(true, false, true, ascii, false));
- nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + extension(true, true, false, ascii, true));
+ nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + QString::fromLatin1(extension(false, true, false, ascii, true)));
+ nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + QString::fromLatin1(extension(false, false, true, ascii, false)));
+ nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + QString::fromLatin1(extension(true, true, true, ascii, false)));
+ nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + QString::fromLatin1(extension(true, false, true, ascii, false)));
+ nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + QString::fromLatin1(extension(true, true, false, ascii, true)));
nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files));
return nameMap;
}
static QMap <int, QString> buildOutputNamesForDir(const QString &file, const QMap <int, QString> orig)
{
QMap <int, QString> ret;
const QString dir = orig.value(SignEncryptFilesWizard::Directory);
if (dir.isEmpty()) {
return orig;
}
// Build the default names for the wizard.
const QFileInfo fi(file);
const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.');
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
- ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + extension(false, true, false, ascii, true));
- ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + extension(false, false, true, ascii, false));
- ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + extension(true, true, true, ascii, false));
- ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + extension(true, false, true, ascii, false));
- ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + extension(true, true, false, ascii, true));
+ ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + QString::fromLatin1(extension(false, true, false, ascii, true)));
+ ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + QString::fromLatin1(extension(false, false, true, ascii, false)));
+ ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + QString::fromLatin1(extension(true, true, true, ascii, false)));
+ ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + QString::fromLatin1(extension(true, false, true, ascii, false)));
+ ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + QString::fromLatin1(extension(true, true, false, ascii, true)));
return ret;
}
void SignEncryptFilesController::setFiles(const QStringList &files)
{
kleo_assert(!files.empty());
d->files = files;
bool archive = false;
if (files.size() > 1) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
archive = true;
}
for (const auto &file: files) {
if (QFileInfo(file).isDir()) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
archive = true;
break;
}
}
d->ensureWizardCreated();
d->wizard->setOutputNames(buildOutputNames(files, archive));
}
void SignEncryptFilesController::Private::slotWizardCanceled()
{
qCDebug(KLEOPATRA_LOG);
reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
}
void SignEncryptFilesController::start()
{
d->ensureWizardVisible();
}
static std::shared_ptr<SignEncryptTask>
createSignEncryptTaskForFileInfo(const QFileInfo &fi, bool ascii,
const std::vector<Key> &recipients, const std::vector<Key> &signers,
const QString &outputName, bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(true);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
task->setDetachedSignature(false);
} else {
task->setEncrypt(false);
}
task->setEncryptSymmetric(symmetric);
const QString input = fi.absoluteFilePath();
task->setInputFileName(input);
task->setInput(Input::createFromFile(input));
task->setOutputFileName(outputName);
return task;
}
static std::shared_ptr<SignEncryptTask>
createArchiveSignEncryptTaskForFiles(const QStringList &files,
const std::shared_ptr<ArchiveDefinition> &ad, bool pgp, bool ascii,
const std::vector<Key> &recipients, const std::vector<Key> &signers,
const QString& outputName, bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
task->setEncryptSymmetric(symmetric);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(false);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
} else {
task->setEncrypt(false);
}
kleo_assert(ad);
const Protocol proto = pgp ? OpenPGP : CMS;
task->setInputFileNames(files);
task->setInput(ad->createInputFromPackCommand(proto, files));
task->setOutputFileName(outputName);
return task;
}
static std::vector< std::shared_ptr<SignEncryptTask> >
createSignEncryptTasksForFileInfo(const QFileInfo &fi, bool ascii, const std::vector<Key> &pgpRecipients, const std::vector<Key> &pgpSigners,
const std::vector<Key> &cmsRecipients, const std::vector<Key> &cmsSigners, const QMap<int, QString> outputNames,
bool symmetric)
{
std::vector< std::shared_ptr<SignEncryptTask> > result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
// Symmetric encryption is only supported for PGP
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric)&& !pgpSigners.empty()) {
outKind = SignEncryptFilesWizard::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesWizard::EncryptedPGP;
} else {
outKind = SignEncryptFilesWizard::SignaturePGP;
}
result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
// There is no combined sign / encrypt in gpgsm so we create one sign task
// and one encrypt task. Which leaves us with the age old dilemma, encrypt
// then sign, or sign then encrypt. Ugly.
if (!cmsSigners.empty()) {
result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(),
cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS],
false));
}
if (!cmsRecipients.empty()) {
result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients,
std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS],
false));
}
}
return result;
}
static std::vector< std::shared_ptr<SignEncryptTask> >
createArchiveSignEncryptTasksForFiles(const QStringList &files, const std::shared_ptr<ArchiveDefinition> &ad,
bool ascii, const std::vector<Key> &pgpRecipients,
const std::vector<Key> &pgpSigners, const std::vector<Key> &cmsRecipients, const std::vector<Key> &cmsSigners,
const QMap<int, QString> outputNames, bool symmetric)
{
std::vector< std::shared_ptr<SignEncryptTask> > result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
outKind = SignEncryptFilesWizard::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesWizard::EncryptedPGP;
} else {
outKind = SignEncryptFilesWizard::SignaturePGP;
}
result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
if (!cmsSigners.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii,
std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS],
false));
}
if (!cmsRecipients.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii,
cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS],
false));
}
}
return result;
}
void SignEncryptFilesController::Private::slotWizardOperationPrepared()
{
try {
kleo_assert(wizard);
kleo_assert(!files.empty());
const bool archive = (wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) ||
((operation & ArchiveMask) == ArchiveForced);
const QVector<Key> recipients = wizard->resolvedRecipients();
const QVector<Key> signers = wizard->resolvedSigners();
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
QVector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners;
Q_FOREACH (const Key k, recipients) {
if (k.protocol() == GpgME::OpenPGP) {
pgpRecipients << k;
} else {
cmsRecipients << k;
}
}
Q_FOREACH (Key k, signers) {
if (k.protocol() == GpgME::OpenPGP) {
pgpSigners << k;
} else {
cmsSigners << k;
}
}
std::vector< std::shared_ptr<SignEncryptTask> > tasks;
if (!archive) {
tasks.reserve(files.size());
}
if (archive) {
tasks = createArchiveSignEncryptTasksForFiles(files,
getDefaultAd(),
ascii,
pgpRecipients.toStdVector(),
pgpSigners.toStdVector(),
cmsRecipients.toStdVector(),
cmsSigners.toStdVector(),
wizard->outputNames(),
wizard->encryptSymmetric());
} else {
Q_FOREACH (const QString &file, files) {
const std::vector< std::shared_ptr<SignEncryptTask> > created =
createSignEncryptTasksForFileInfo(QFileInfo(file), ascii,
pgpRecipients.toStdVector(),
pgpSigners.toStdVector(),
cmsRecipients.toStdVector(),
cmsSigners.toStdVector(),
buildOutputNamesForDir(file, wizard->outputNames()),
wizard->encryptSymmetric());
tasks.insert(tasks.end(), created.begin(), created.end());
}
}
const std::shared_ptr<OverwritePolicy> overwritePolicy(new OverwritePolicy(wizard));
Q_FOREACH (const std::shared_ptr<SignEncryptTask> &i, tasks) {
i->setOverwritePolicy(overwritePolicy);
}
kleo_assert(runnable.empty());
runnable.swap(tasks);
Q_FOREACH (const std::shared_ptr<Task> &task, runnable) {
q->connectTask(task);
}
std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task> > tmp;
std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp));
coll->setTasks(tmp);
wizard->setTaskCollection(coll);
QTimer::singleShot(0, q, SLOT(schedule()));
} catch (const Kleo::Exception &e) {
reportError(e.error().encodedError(), e.message());
} catch (const std::exception &e) {
reportError(gpg_error(GPG_ERR_UNEXPECTED),
i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1",
QString::fromLocal8Bit(e.what())));
} catch (...) {
reportError(gpg_error(GPG_ERR_UNEXPECTED),
i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared"));
}
}
void SignEncryptFilesController::Private::schedule()
{
if (!cms)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) {
t->start();
cms = t;
}
if (!openpgp)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) {
t->start();
openpgp = t;
}
if (!cms && !openpgp) {
kleo_assert(runnable.empty());
q->emitDoneOrError();
}
}
std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto)
{
const auto it = std::find_if(runnable.begin(), runnable.end(),
[proto](const std::shared_ptr<Task> &task) { return task->protocol() == proto; });
if (it == runnable.end()) {
return std::shared_ptr<SignEncryptTask>();
}
const std::shared_ptr<SignEncryptTask> result = *it;
runnable.erase(it);
return result;
}
void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
{
Q_UNUSED(result)
Q_ASSERT(task);
// We could just delete the tasks here, but we can't use
// Qt::QueuedConnection here (we need sender()) and other slots
// might not yet have executed. Therefore, we push completed tasks
// into a burial container
if (task == d->cms.get()) {
d->completed.push_back(d->cms);
d->cms.reset();
} else if (task == d->openpgp.get()) {
d->completed.push_back(d->openpgp);
d->openpgp.reset();
}
QTimer::singleShot(0, this, SLOT(schedule()));
}
void SignEncryptFilesController::cancel()
{
qCDebug(KLEOPATRA_LOG);
try {
if (d->wizard) {
d->wizard->close();
}
d->cancelAllTasks();
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
}
}
void SignEncryptFilesController::Private::cancelAllTasks()
{
// we just kill all runnable tasks - this will not result in
// signal emissions.
runnable.clear();
// a cancel() will result in a call to
if (cms) {
cms->cancel();
}
if (openpgp) {
openpgp->cancel();
}
}
void SignEncryptFilesController::Private::ensureWizardCreated()
{
if (wizard) {
return;
}
std::unique_ptr<SignEncryptFilesWizard> w(new SignEncryptFilesWizard);
w->setAttribute(Qt::WA_DeleteOnClose);
connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection);
connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
wizard = w.release();
updateWizardMode();
}
void SignEncryptFilesController::Private::ensureWizardVisible()
{
ensureWizardCreated();
q->bringToForeground(wizard);
}
#include "moc_signencryptfilescontroller.cpp"
diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp
index 41f328de0..7838a94af 100644
--- a/src/dialogs/certificatedetailswidget.cpp
+++ b/src/dialogs/certificatedetailswidget.cpp
@@ -1,628 +1,628 @@
/* Copyright (c) 2016 Klarälvdalens Datakonsult AB
2017 Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "certificatedetailswidget.h"
#include "ui_certificatedetailswidget.h"
#include "kleopatra_debug.h"
#include "exportdialog.h"
#include "trustchainwidget.h"
#include "subkeyswidget.h"
#include "weboftrustdialog.h"
#include "commands/changepassphrasecommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/certifycertificatecommand.h"
#include "commands/adduseridcommand.h"
#include "commands/genrevokecommand.h"
#include "commands/detailscommand.h"
#include "commands/dumpcertificatecommand.h"
#include <libkleo/formatting.h>
#include <libkleo/dn.h>
#include <libkleo/keycache.h>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <gpgme++/tofuinfo.h>
#include <QGpgME/Protocol>
#include <QGpgME/KeyListJob>
#include <QToolTip>
#include <QDateTime>
#include <QDialogButtonBox>
#include <QMenu>
#include <KConfigGroup>
#include <KSharedConfig>
#define HIDE_ROW(row) \
ui.row->setVisible(false); \
ui.row##Lbl->setVisible(false);
Q_DECLARE_METATYPE(GpgME::UserID)
using namespace Kleo;
class CertificateDetailsWidget::Private
{
public:
Private(CertificateDetailsWidget *parent)
: q(parent)
{}
void setupCommonProperties();
void setupPGPProperties();
void setupSMIMEProperties();
void revokeUID(const GpgME::UserID &uid);
void genRevokeCert();
void certifyClicked();
void webOfTrustClicked();
void exportClicked();
void addUserID();
void changePassphrase();
void changeExpiration();
void keysMayHaveChanged();
void showTrustChainDialog();
void showMoreDetails();
void publishCertificate();
void userIDTableContextMenuRequested(const QPoint &p);
QString tofuTooltipString(const GpgME::UserID &uid) const;
void smimeLinkActivated(const QString &link);
void setUpdatedKey(const GpgME::Key &key);
void keyListDone(const GpgME::KeyListResult &,
const std::vector<GpgME::Key> &, const QString &,
const GpgME::Error &);
Ui::CertificateDetailsWidget ui;
GpgME::Key key;
bool updateInProgress;
private:
CertificateDetailsWidget *q;
};
void CertificateDetailsWidget::Private::setupCommonProperties()
{
// TODO: Enable once implemented
HIDE_ROW(publishing)
const bool hasSecret = key.hasSecret();
const bool isOpenPGP = key.protocol() == GpgME::OpenPGP;
// TODO: Enable once implemented
const bool canRevokeUID = false; // isOpenPGP && hasSecret
ui.changePassphraseBtn->setVisible(hasSecret);
ui.genRevokeBtn->setVisible(isOpenPGP && hasSecret);
ui.certifyBtn->setVisible(isOpenPGP && !hasSecret);
ui.changeExpirationBtn->setVisible(isOpenPGP && hasSecret);
ui.addUserIDBtn->setVisible(hasSecret);
ui.webOfTrustBtn->setVisible(isOpenPGP);
ui.hboxLayout_1->addStretch(1);
ui.validFrom->setText(Kleo::Formatting::creationDateString(key));
const QString expiry = Kleo::Formatting::expirationDateString(key);
ui.expires->setText(expiry.isEmpty() ? i18nc("Expires", "never") : expiry);
ui.type->setText(Kleo::Formatting::type(key));
ui.fingerprint->setText(Formatting::prettyID(key.primaryFingerprint()));
if (Kleo::Formatting::complianceMode().isEmpty()) {
HIDE_ROW(compliance)
} else {
ui.complianceLbl->setText(Kleo::Formatting::complianceStringForKey(key));
}
ui.userIDTable->clear();
QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level") };
if (canRevokeUID) {
headers << QString();
}
ui.userIDTable->setColumnCount(headers.count());
ui.userIDTable->setColumnWidth(0, 200);
ui.userIDTable->setColumnWidth(1, 200);
ui.userIDTable->setHeaderLabels(headers);
const auto uids = key.userIDs();
for (unsigned int i = 0; i < uids.size(); ++i) {
const auto &uid = uids[i];
auto item = new QTreeWidgetItem;
const QString toolTip = tofuTooltipString(uid);
item->setData(0, Qt::UserRole, QVariant::fromValue(uid));
auto pMail = Kleo::Formatting::prettyEMail(uid);
auto pName = Kleo::Formatting::prettyName(uid);
if (!isOpenPGP && pMail.isEmpty() && !pName.isEmpty()) {
// S/MIME UserIDs are sometimes split, with one userID
// containing the name another the Mail, we merge these
// UID's into a single item.
if (i + 1 < uids.size()) {
pMail = Kleo::Formatting::prettyEMail(uids[i + 1]);
// skip next uid
++i;
}
}
if (!isOpenPGP && pMail.isEmpty() && pName.isEmpty()) {
// S/MIME certificates sometimes contain urls where both
// name and mail is empty. In that case we print whatever
// the uid is as name.
//
// Can be ugly like (3:uri24:http://ca.intevation.org), but
// this is better then showing an empty entry.
- pName = uid.id();
+ pName = QString::fromLatin1(uid.id());
}
item->setData(0, Qt::DisplayRole, pMail);
item->setData(0, Qt::ToolTipRole, toolTip);
item->setData(1, Qt::DisplayRole, pName);
item->setData(1, Qt::ToolTipRole, toolTip);
QIcon trustIcon;
if (updateInProgress) {
trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question"));
item->setData(2, Qt::DisplayRole, i18n("Updating..."));
} else {
switch (uid.validity()) {
case GpgME::UserID::Unknown:
case GpgME::UserID::Undefined:
trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question"));
break;
case GpgME::UserID::Never:
trustIcon = QIcon::fromTheme(QStringLiteral("emblem-error"));
break;
case GpgME::UserID::Marginal:
trustIcon = QIcon::fromTheme(QStringLiteral("emblem-warning"));
break;
case GpgME::UserID::Full:
case GpgME::UserID::Ultimate:
trustIcon = QIcon::fromTheme(QStringLiteral("emblem-success"));
break;
}
item->setData(2, Qt::DisplayRole, Kleo::Formatting::validityShort(uid));
}
item->setData(2, Qt::DecorationRole, trustIcon);
item->setData(2, Qt::ToolTipRole, toolTip);
ui.userIDTable->addTopLevelItem(item);
if (canRevokeUID) {
auto button = new QPushButton;
button->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete")));
button->setToolTip(i18n("Revoke this User ID"));
button->setMaximumWidth(32);
QObject::connect(button, &QPushButton::clicked,
q, [this, uid]() { revokeUID(uid); });
ui.userIDTable->setItemWidget(item, 4, button);
}
}
}
void CertificateDetailsWidget::Private::revokeUID(const GpgME::UserID &uid)
{
Q_UNUSED(uid);
qCWarning(KLEOPATRA_LOG) << "Revoking UserID is not implemented. How did you even get here?!?!";
}
void CertificateDetailsWidget::Private::changeExpiration()
{
auto cmd = new Kleo::Commands::ChangeExpiryCommand(key);
QObject::connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished,
q, [this]() {
ui.changeExpirationBtn->setEnabled(true);
});
ui.changeExpirationBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::changePassphrase()
{
auto cmd = new Kleo::Commands::ChangePassphraseCommand(key);
QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished,
q, [this, cmd]() {
ui.changePassphraseBtn->setEnabled(true);
});
ui.changePassphraseBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::genRevokeCert()
{
auto cmd = new Kleo::Commands::GenRevokeCommand(key);
QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished,
q, [this, cmd]() {
ui.genRevokeBtn->setEnabled(true);
});
ui.genRevokeBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::certifyClicked()
{
auto cmd = new Kleo::Commands::CertifyCertificateCommand(key);
QObject::connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished,
q, [this, cmd]() {
ui.certifyBtn->setEnabled(true);
});
ui.certifyBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::webOfTrustClicked()
{
QScopedPointer<WebOfTrustDialog> dlg(new WebOfTrustDialog(q));
dlg->setKey(key);
dlg->exec();
}
void CertificateDetailsWidget::Private::exportClicked()
{
QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
dlg->setKey(key);
dlg->exec();
}
void CertificateDetailsWidget::Private::addUserID()
{
auto cmd = new Kleo::Commands::AddUserIDCommand(key);
QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished,
q, [this, cmd]() {
ui.addUserIDBtn->setEnabled(true);
});
ui.addUserIDBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::keysMayHaveChanged()
{
auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
if (!newKey.isNull()) {
setUpdatedKey(newKey);
}
}
void CertificateDetailsWidget::Private::showTrustChainDialog()
{
QScopedPointer<TrustChainDialog> dlg(new TrustChainDialog(q));
dlg->setKey(key);
dlg->exec();
}
void CertificateDetailsWidget::Private::publishCertificate()
{
qCWarning(KLEOPATRA_LOG) << "publishCertificateis not implemented.";
//TODO
}
void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QPoint &p)
{
auto item = ui.userIDTable->itemAt(p);
if (!item) {
return;
}
const auto userID = item->data(0, Qt::UserRole).value<GpgME::UserID>();
QMenu *menu = new QMenu(q);
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")),
i18n("Certify ..."),
q, [this, userID]() {
auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID);
ui.userIDTable->setEnabled(false);
connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished,
q, [this]() { ui.userIDTable->setEnabled(true); });
cmd->start();
});
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
menu->popup(ui.userIDTable->viewport()->mapToGlobal(p));
}
void CertificateDetailsWidget::Private::showMoreDetails()
{
ui.moreDetailsBtn->setEnabled(false);
if (key.protocol() == GpgME::CMS) {
auto cmd = new Kleo::Commands::DumpCertificateCommand(key);
connect(cmd, &Kleo::Commands::DumpCertificateCommand::finished,
q, [this]() {
ui.moreDetailsBtn->setEnabled(true);
});
cmd->setUseDialog(true);
cmd->start();
} else {
QScopedPointer<SubKeysDialog> dlg(new SubKeysDialog(q));
dlg->setKey(key);
dlg->exec();
ui.moreDetailsBtn->setEnabled(true);
}
}
QString CertificateDetailsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const
{
const auto tofu = uid.tofuInfo();
if (tofu.isNull()) {
return QString();
}
QString html = QStringLiteral("<table border=\"0\" cell-padding=\"5\">");
const auto appendRow = [&html](const QString &lbl, const QString &val) {
html += QStringLiteral("<tr>"
"<th style=\"text-align: right; padding-right: 5px; white-space: nowrap;\">%1:</th>"
"<td style=\"white-space: nowrap;\">%2</td>"
"</tr>")
.arg(lbl, val);
};
const auto appendHeader = [this, &html](const QString &hdr) {
html += QStringLiteral("<tr><th colspan=\"2\" style=\"background-color: %1; color: %2\">%3</th></tr>")
.arg(q->palette().highlight().color().name(),
q->palette().highlightedText().color().name(),
hdr);
};
const auto dateTime = [](long ts) {
return ts == 0 ? i18n("never") : QDateTime::fromTime_t(ts).toString(Qt::SystemLocaleShortDate);
};
appendHeader(i18n("Signing"));
appendRow(i18n("First message"), dateTime(tofu.signFirst()));
appendRow(i18n("Last message"), dateTime(tofu.signLast()));
appendRow(i18n("Message count"), QString::number(tofu.signCount()));
appendHeader(i18n("Encryption"));
appendRow(i18n("First message"), dateTime(tofu.encrFirst()));
appendRow(i18n("Last message"), dateTime(tofu.encrLast()));
appendRow(i18n("Message count"), QString::number(tofu.encrCount()));
html += QStringLiteral("</table>");
// Make sure the tooltip string is different for each UserID, even if the
// data are the same, otherwise the tooltip is not updated and moved when
// user moves mouse from one row to another.
html += QStringLiteral("<!-- %1 //-->").arg(QString::fromUtf8(uid.id()));
return html;
}
void CertificateDetailsWidget::Private::setupPGPProperties()
{
HIDE_ROW(smimeOwner)
HIDE_ROW(smimeIssuer)
ui.smimeRelatedAddresses->setVisible(false);
ui.trustChainDetailsBtn->setVisible(false);
ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested,
q, [this](const QPoint &p) { userIDTableContextMenuRequested(p); });
}
static QString formatDNToolTip(const Kleo::DN &dn)
{
QString html = QStringLiteral("<table border=\"0\" cell-spacing=15>");
const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) {
const QString val = dn[attr];
if (!val.isEmpty()) {
html += QStringLiteral(
"<tr><th style=\"text-align: left; white-space: nowrap\">%1:</th>"
"<td style=\"white-space: nowrap\">%2</td>"
"</tr>").arg(lbl, val);
}
};
appendRow(i18n("Common Name"), QStringLiteral("CN"));
appendRow(i18n("Organization"), QStringLiteral("O"));
appendRow(i18n("Street"), QStringLiteral("STREET"));
appendRow(i18n("City"), QStringLiteral("L"));
appendRow(i18n("State"), QStringLiteral("ST"));
appendRow(i18n("Country"), QStringLiteral("C"));
html += QStringLiteral("</table>");
return html;
}
void CertificateDetailsWidget::Private::setupSMIMEProperties()
{
HIDE_ROW(publishing)
const auto ownerId = key.userID(0);
const Kleo::DN dn(ownerId.id());
const QString cn = dn[QStringLiteral("CN")];
const QString o = dn[QStringLiteral("O")];
const QString dnEmail = dn[QStringLiteral("EMAIL")];
const QString name = cn.isEmpty() ? dnEmail : cn;
QString owner;
if (name.isEmpty()) {
owner = dn.dn();
} else if (o.isEmpty()) {
owner = name;
} else {
owner = i18nc("<name> of <company>", "%1 of %2", name, o);
}
ui.smimeOwner->setText(owner);
ui.smimeOwner->setTextInteractionFlags(Qt::TextBrowserInteraction);
const Kleo::DN issuerDN(key.issuerName());
const QString issuerCN = issuerDN[QStringLiteral("CN")];
- const QString issuer = issuerCN.isEmpty() ? key.issuerName() : issuerCN;
+ const QString issuer = issuerCN.isEmpty() ? QString::fromUtf8(key.issuerName()) : issuerCN;
ui.smimeIssuer->setText(QStringLiteral("<a href=\"#issuerDetails\">%1</a>").arg(issuer));
ui.smimeIssuer->setToolTip(formatDNToolTip(issuerDN));
ui.smimeOwner->setToolTip(formatDNToolTip(dn));
}
void CertificateDetailsWidget::Private::smimeLinkActivated(const QString &link)
{
if (link == QLatin1String("#issuerDetails")) {
const auto parentKey = KeyCache::instance()->findIssuers(key, KeyCache::NoOption);
if (!parentKey.size()) {
return;
}
auto cmd = new Kleo::Commands::DetailsCommand(parentKey[0], nullptr);
cmd->setParentWidget(q);
cmd->start();
return;
}
qCWarning(KLEOPATRA_LOG) << "Unknown link activated:" << link;
}
CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
d->ui.setupUi(this);
connect(d->ui.addUserIDBtn, &QPushButton::clicked,
this, [this]() { d->addUserID(); });
connect(d->ui.changePassphraseBtn, &QPushButton::clicked,
this, [this]() { d->changePassphrase(); });
connect(d->ui.genRevokeBtn, &QPushButton::clicked,
this, [this]() { d->genRevokeCert(); });
connect(d->ui.changeExpirationBtn, &QPushButton::clicked,
this, [this]() { d->changeExpiration(); });
connect(d->ui.smimeOwner, &QLabel::linkActivated,
this, [this](const QString &link) { d->smimeLinkActivated(link); });
connect(d->ui.smimeIssuer, &QLabel::linkActivated,
this, [this](const QString &link) { d->smimeLinkActivated(link); });
connect(d->ui.trustChainDetailsBtn, &QPushButton::pressed,
this, [this]() { d->showTrustChainDialog(); });
connect(d->ui.moreDetailsBtn, &QPushButton::pressed,
this, [this]() { d->showMoreDetails(); });
connect(d->ui.publishing, &QPushButton::pressed,
this, [this]() { d->publishCertificate(); });
connect(d->ui.certifyBtn, &QPushButton::clicked,
this, [this]() { d->certifyClicked(); });
connect(d->ui.webOfTrustBtn, &QPushButton::clicked,
this, [this]() { d->webOfTrustClicked(); });
connect(d->ui.exportBtn, &QPushButton::clicked,
this, [this]() { d->exportClicked(); });
connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged,
this, [this]() { d->keysMayHaveChanged(); });
}
CertificateDetailsWidget::~CertificateDetailsWidget()
{
}
void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &,
const std::vector<GpgME::Key> &keys,
const QString &,
const GpgME::Error &) {
updateInProgress = false;
if (keys.size() != 1) {
qCWarning(KLEOPATRA_LOG) << "Invalid keylist result in update.";
return;
}
// As we listen for keysmayhavechanged we get the update
// after updating the keycache.
KeyCache::mutableInstance()->insert(keys);
}
void CertificateDetailsWidget::Private::setUpdatedKey(const GpgME::Key &k)
{
key = k;
setupCommonProperties();
if (key.protocol() == GpgME::OpenPGP) {
setupPGPProperties();
} else {
setupSMIMEProperties();
}
}
void CertificateDetailsWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() == GpgME::CMS) {
// For everything but S/MIME this should be quick
// and we don't need to show another status.
d->updateInProgress = true;
}
d->setUpdatedKey(key);
// Run a keylistjob with full details (TOFU / Validate)
QGpgME::KeyListJob *job = key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp()->keyListJob(false, true, true) :
QGpgME::smime()->keyListJob(false, true, true);
auto ctx = QGpgME::Job::context(job);
ctx->addKeyListMode(GpgME::WithTofu);
// Windows QGpgME new style connect problem makes this necessary.
connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)),
this, SLOT(keyListDone(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)));
- job->start(QStringList() << key.primaryFingerprint(), key.hasSecret());
+ job->start(QStringList() << QLatin1String(key.primaryFingerprint()), key.hasSecret());
}
GpgME::Key CertificateDetailsWidget::key() const
{
return d->key;
}
CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18n("Certificate Details"));
auto l = new QVBoxLayout(this);
l->addWidget(new CertificateDetailsWidget(this));
auto bbox = new QDialogButtonBox(this);
auto btn = bbox->addButton(QDialogButtonBox::Close);
connect(btn, &QPushButton::pressed, this, &QDialog::accept);
l->addWidget(bbox);
readConfig();
}
CertificateDetailsDialog::~CertificateDetailsDialog()
{
writeConfig();
}
void CertificateDetailsDialog::readConfig()
{
KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog");
const QSize size = dialog.readEntry("Size", QSize(730, 280));
if (size.isValid()) {
resize(size);
}
}
void CertificateDetailsDialog::writeConfig()
{
KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog");
dialog.writeEntry("Size", size());
dialog.sync();
}
void CertificateDetailsDialog::setKey(const GpgME::Key &key)
{
auto w = findChild<CertificateDetailsWidget*>();
Q_ASSERT(w);
w->setKey(key);
}
GpgME::Key CertificateDetailsDialog::key() const
{
auto w = findChild<CertificateDetailsWidget*>();
Q_ASSERT(w);
return w->key();
}
#include "moc_certificatedetailswidget.cpp"
diff --git a/src/dialogs/exportdialog.cpp b/src/dialogs/exportdialog.cpp
index bffa23225..910460b55 100644
--- a/src/dialogs/exportdialog.cpp
+++ b/src/dialogs/exportdialog.cpp
@@ -1,201 +1,201 @@
/* Copyright (c) 2017 Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "exportdialog.h"
#include "kleopatra_debug.h"
#include "view/waitwidget.h"
#include <QDialog>
#include <QDialogButtonBox>
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <gpgme++/key.h>
#include <QGpgME/Protocol>
#include <QGpgME/ExportJob>
#include <KLocalizedString>
#include <KSharedConfig>
#include <KConfigGroup>
#include <Libkleo/Formatting>
using namespace Kleo;
class ExportWidget::Private
{
public:
Private(ExportWidget *qq)
: q(qq)
{}
void setupUi();
GpgME::Key key;
QTextEdit *textEdit;
WaitWidget *waitWidget;
private:
ExportWidget *q;
};
void ExportWidget::Private::setupUi()
{
auto vlay = new QVBoxLayout(q);
textEdit = new QTextEdit;
textEdit->setVisible(false);
textEdit->setReadOnly(true);
- auto fixedFont = QFont("Monospace");
+ auto fixedFont = QFont(QStringLiteral("Monospace"));
fixedFont.setStyleHint(QFont::TypeWriter);
textEdit->setFont(fixedFont);
textEdit->setReadOnly(true);
vlay->addWidget(textEdit);
waitWidget = new WaitWidget;
waitWidget->setText(i18n("Exporting ..."));
vlay->addWidget(waitWidget);
}
ExportWidget::ExportWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
d->setupUi();
}
ExportWidget::~ExportWidget()
{
}
static QString injectComments(const GpgME::Key &key, const QByteArray &data)
{
QString ret = QString::fromUtf8(data);
if (key.protocol() != GpgME::OpenPGP) {
return ret;
}
auto overView = Formatting::toolTip(key, Formatting::Fingerprint |
Formatting::UserIDs |
Formatting::Issuer |
Formatting::Subject |
Formatting::ExpiryDates |
Formatting::CertificateType |
Formatting::CertificateUsage);
// Fixup the HTML coming from the toolTip for our own format.
overView.remove(QLatin1String("<tr><th>"));
overView.replace(QLatin1String("</th><td>"), QLatin1String("\t"));
overView.replace(QLatin1String("</td></tr>"), QLatin1String("\n"));
overView.remove(QLatin1String("<table border=\"0\">"));
overView.remove(QLatin1String("\n</table>"));
overView.replace(QLatin1String("&lt;"), QLatin1String("<"));
overView.replace(QLatin1String("&gt;"), QLatin1String(">"));
auto overViewLines = overView.split(QLatin1Char('\n'));
// Format comments so that they fit for RFC 4880
auto comments = QString::fromLatin1("Comment: ");
comments += overViewLines.join(QStringLiteral("\nComment: ")) + QLatin1Char('\n');
ret.insert(37 /* -----BEGIN PGP PUBLIC KEY BLOCK-----\n */, comments);
return ret;
}
void ExportWidget::exportResult(const GpgME::Error &err, const QByteArray &data)
{
d->waitWidget->setVisible(false);
d->textEdit->setVisible(true);
if (err) {
/* Should not happen. But well,.. */
d->textEdit->setText(i18nc("%1 is error message", "Failed to export: '%1'", QString::fromLatin1(err.asString())));
}
d->textEdit->setText(injectComments(d->key, data));
}
void ExportWidget::setKey(const GpgME::Key &key)
{
d->waitWidget->setVisible(true);
d->textEdit->setVisible(false);
d->key = key;
auto protocol = key.protocol() == GpgME::CMS ?
QGpgME::smime() : QGpgME::openpgp();
auto job = protocol->publicKeyExportJob(true);
/* New style connect does not work on Windows. */
connect(job, SIGNAL(result(GpgME::Error,QByteArray)),
this, SLOT(exportResult(GpgME::Error,QByteArray)));
- job->start(QStringList() << key.primaryFingerprint());
+ job->start(QStringList() << QLatin1String(key.primaryFingerprint()));
}
GpgME::Key ExportWidget::key() const
{
return d->key;
}
ExportDialog::ExportDialog(QWidget *parent)
: QDialog(parent),
mWidget(new ExportWidget(this))
{
KConfigGroup dialog(KSharedConfig::openConfig(), "ExportDialog");
const auto size = dialog.readEntry("Size", QSize(600, 800));
if (size.isValid()) {
resize(size);
}
setWindowTitle(i18n("Export..."));
auto l = new QVBoxLayout(this);
l->addWidget(mWidget);
auto bbox = new QDialogButtonBox(this);
auto btn = bbox->addButton(QDialogButtonBox::Close);
connect(btn, &QPushButton::pressed, this, &QDialog::accept);
l->addWidget(bbox);
}
ExportDialog::~ExportDialog()
{
KConfigGroup dialog(KSharedConfig::openConfig(), "ExportDialog");
dialog.writeEntry("Size", size());
dialog.sync();
}
void ExportDialog::setKey(const GpgME::Key &key)
{
mWidget->setKey(key);
}
GpgME::Key ExportDialog::key() const
{
return mWidget->key();
}
diff --git a/src/libkleopatraclient/core/CMakeLists.txt b/src/libkleopatraclient/core/CMakeLists.txt
index ff97d1a9d..7e701f3df 100644
--- a/src/libkleopatraclient/core/CMakeLists.txt
+++ b/src/libkleopatraclient/core/CMakeLists.txt
@@ -1,61 +1,60 @@
add_definitions(
-D_ASSUAN_ONLY_GPG_ERRORS
- -DQT_NO_CAST_FROM_ASCII -DQT_NO_KEYWORDS -DQT_NO_CAST_TO_ASCII
)
if(WIN32)
set(_kleopatraclientcore_extra_SRCS ../../utils/gnupg-registry.c)
else()
set(_kleopatraclientcore_extra_SRCS)
endif()
ecm_qt_declare_logging_category(_kleopatraclientcore_common_SRCS HEADER libkleopatraclientcore_debug.h IDENTIFIER LIBKLEOPATRACLIENTCORE_LOG CATEGORY_NAME org.kde.pim.libkleopatraclientcore)
add_library(kleopatraclientcore
${_kleopatraclientcore_extra_SRCS}
initialization.cpp
command.cpp
selectcertificatecommand.cpp
signencryptfilescommand.cpp
decryptverifyfilescommand.cpp
libkleopatraclientcore_debug.cpp
${_kleopatraclientcore_common_SRCS}
)
generate_export_header(kleopatraclientcore BASE_NAME kleopatraclientcore)
set_target_properties(kleopatraclientcore PROPERTIES
VERSION ${libkleopatraclient_version}
SOVERSION ${libkleopatraclient_soversion}
)
if(WIN32)
if(ASSUAN2_FOUND)
target_link_libraries(kleopatraclientcore
${ASSUAN2_LIBRARIES}
ws2_32
)
else()
target_link_libraries(kleopatraclientcore
${ASSUAN_VANILLA_LIBRARIES}
ws2_32
)
endif()
else()
if(ASSUAN2_FOUND)
target_link_libraries(kleopatraclientcore
${ASSUAN2_LIBRARIES}
)
else()
target_link_libraries(kleopatraclientcore
${ASSUAN_PTHREAD_LIBRARIES}
)
endif()
endif()
target_link_libraries(kleopatraclientcore Qt5::Widgets KF5::I18n)
install(TARGETS kleopatraclientcore ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/src/libkleopatraclient/gui/CMakeLists.txt b/src/libkleopatraclient/gui/CMakeLists.txt
index 114e31351..faebd89f2 100644
--- a/src/libkleopatraclient/gui/CMakeLists.txt
+++ b/src/libkleopatraclient/gui/CMakeLists.txt
@@ -1,16 +1,15 @@
-add_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_KEYWORDS -DQT_NO_CAST_TO_ASCII)
add_library(kleopatraclientgui certificaterequester.cpp)
generate_export_header(kleopatraclientgui BASE_NAME kleopatraclientgui)
target_link_libraries(kleopatraclientgui
kleopatraclientcore
KF5::I18n
)
set_target_properties(kleopatraclientgui PROPERTIES
VERSION ${libkleopatraclient_version}
SOVERSION ${libkleopatraclient_soversion}
)
install(TARGETS kleopatraclientgui ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/src/newcertificatewizard/newcertificatewizard.cpp b/src/newcertificatewizard/newcertificatewizard.cpp
index 7cd38c622..7d440d8b7 100644
--- a/src/newcertificatewizard/newcertificatewizard.cpp
+++ b/src/newcertificatewizard/newcertificatewizard.cpp
@@ -1,2049 +1,2049 @@
/* -*- mode: c++; c-basic-offset:4 -*-
newcertificatewizard/newcertificatewizard.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2008 Klarälvdalens Datakonsult AB
2016, 2017 by Bundesamt für Sicherheit in der Informationstechnik
Software engineering by Intevation GmbH
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "newcertificatewizard.h"
#include "ui_chooseprotocolpage.h"
#include "ui_enterdetailspage.h"
#include "ui_overviewpage.h"
#include "ui_keycreationpage.h"
#include "ui_resultpage.h"
#include "ui_advancedsettingsdialog.h"
#include <commands/exportsecretkeycommand.h>
#include <commands/exportopenpgpcertstoservercommand.h>
#include <commands/exportcertificatecommand.h>
#include <utils/validation.h>
#include <utils/filedialog.h>
#include "utils/gnupg-helper.h"
#include <Libkleo/Stl_Util>
#include <Libkleo/Dn>
#include <Libkleo/OidMap>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
#include <QGpgME/CryptoConfig>
#include <gpgme++/global.h>
#include <gpgme++/gpgmepp_version.h>
#include <gpgme++/keygenerationresult.h>
#include <gpgme++/context.h>
#include <KConfigGroup>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QTemporaryDir>
#include <KMessageBox>
#include <QIcon>
#include <QRegExpValidator>
#include <QLineEdit>
#include <QMetaProperty>
#include <QDir>
#include <QFile>
#include <QUrl>
#include <QDesktopServices>
#include <QUrlQuery>
#include <algorithm>
#include <KSharedConfig>
#include <KEMailSettings>
#include <QLocale>
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
using namespace Kleo::Commands;
using namespace GpgME;
static const char RSA_KEYSIZES_ENTRY[] = "RSAKeySizes";
static const char DSA_KEYSIZES_ENTRY[] = "DSAKeySizes";
static const char ELG_KEYSIZES_ENTRY[] = "ELGKeySizes";
static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels";
static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels";
static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels";
static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType";
static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType";
// This should come from gpgme in the future
// For now we only support the basic 2.1 curves and check
// for GnuPG 2.1. The whole subkey / usage generation needs
// new api and a reworked dialog. (ah 10.3.16)
// EDDSA should be supported, too.
static const QStringList curveNames {
{ QStringLiteral("brainpoolP256r1") },
{ QStringLiteral("brainpoolP384r1") },
{ QStringLiteral("brainpoolP512r1") },
{ QStringLiteral("NIST P-256") },
{ QStringLiteral("NIST P-384") },
{ QStringLiteral("NIST P-521") },
};
static void set_tab_order(const QList<QWidget *> &wl)
{
kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), &QWidget::setTabOrder);
}
enum KeyAlgo { RSA, DSA, ELG, ECDSA, ECDH, EDDSA };
static bool is_algo(Subkey::PubkeyAlgo algo, KeyAlgo what)
{
switch (algo) {
case Subkey::AlgoRSA:
case Subkey::AlgoRSA_E:
case Subkey::AlgoRSA_S:
return what == RSA;
case Subkey::AlgoELG_E:
case Subkey::AlgoELG:
return what == ELG;
case Subkey::AlgoDSA:
return what == DSA;
case Subkey::AlgoECDSA:
return what == ECDSA;
case Subkey::AlgoECDH:
return what == ECDH;
case Subkey::AlgoEDDSA:
return what == EDDSA;
default:
break;
}
return false;
}
static bool is_rsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), RSA);
}
static bool is_dsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), DSA);
}
static bool is_elg(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ELG);
}
static bool is_ecdsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDSA);
}
static bool is_eddsa(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), EDDSA);
}
static bool is_ecdh(unsigned int algo)
{
return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDH);
}
static void force_set_checked(QAbstractButton *b, bool on)
{
// work around Qt bug (tested: 4.1.4, 4.2.3, 4.3.4)
const bool autoExclusive = b->autoExclusive();
b->setAutoExclusive(false);
b->setChecked(b->isEnabled() && on);
b->setAutoExclusive(autoExclusive);
}
static void set_keysize(QComboBox *cb, unsigned int strength)
{
if (!cb) {
return;
}
const int idx = cb->findData(static_cast<int>(strength));
if (idx < 0) {
qCWarning(KLEOPATRA_LOG) << "keysize " << strength << " not allowed";
}
cb->setCurrentIndex(idx);
}
static unsigned int get_keysize(const QComboBox *cb)
{
if (!cb) {
return 0;
}
const int idx = cb->currentIndex();
if (idx < 0) {
return 0;
}
return cb->itemData(idx).toInt();
}
static void set_curve(QComboBox *cb, const QString &curve)
{
if (!cb) {
return;
}
const int idx = cb->findText(curve);
if (idx < 0) {
// Can't happen as we don't have them configurable.
qCWarning(KLEOPATRA_LOG) << "curve " << curve << " not allowed";
}
cb->setCurrentIndex(idx);
}
static QString get_curve(const QComboBox *cb)
{
if (!cb) {
return QString();
}
return cb->currentText();
}
// Extract the algo information from default_pubkey_algo format
//
// and put it into the return values size, algo and curve.
//
// Values look like:
// RSA-2048
// rsa2048/cert,sign+rsa2048/enc
// brainpoolP256r1+brainpoolP256r1
static void parseAlgoString(const QString &algoString, int *size, Subkey::PubkeyAlgo *algo, QString &curve)
{
const auto split = algoString.split(QLatin1Char('/'));
bool isEncrypt = split.size() == 2 && split[1].contains(QStringLiteral("enc"));
// Normalize
const auto lowered = split[0].toLower().remove(QLatin1Char('-'));
if (!algo || !size) {
return;
}
*algo = Subkey::AlgoUnknown;
if (lowered.startsWith(QLatin1String("rsa"))) {
*algo = Subkey::AlgoRSA;
} else if (lowered.startsWith(QLatin1String("dsa"))) {
*algo = Subkey::AlgoDSA;
} else if (lowered.startsWith(QLatin1String("elg"))) {
*algo = Subkey::AlgoELG;
}
if (*algo != Subkey::AlgoUnknown) {
bool ok;
*size = lowered.right(lowered.size() - 3).toInt(&ok);
if (!ok) {
qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered;
*size = 2048;
}
return;
}
// Now the ECC Algorithms
if (lowered.startsWith(QLatin1String("ed25519"))) {
// Special handling for this as technically
// this is a cv25519 curve used for EDDSA
curve = split[0];
*algo = Subkey::AlgoEDDSA;
return;
}
if (lowered.startsWith(QLatin1String("cv25519")) ||
lowered.startsWith(QLatin1String("nist")) ||
lowered.startsWith(QLatin1String("brainpool")) ||
lowered.startsWith(QLatin1String("secp"))) {
curve = split[0];
*algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA;
return;
}
qCWarning(KLEOPATRA_LOG) << "Failed to parse default_pubkey_algo:" << algoString;
}
Q_DECLARE_METATYPE(GpgME::Subkey::PubkeyAlgo)
namespace Kleo
{
namespace NewCertificateUi
{
class WizardPage : public QWizardPage
{
Q_OBJECT
protected:
explicit WizardPage(QWidget *parent = nullptr)
: QWizardPage(parent) {}
NewCertificateWizard *wizard() const
{
Q_ASSERT(static_cast<NewCertificateWizard *>(QWizardPage::wizard()) == qobject_cast<NewCertificateWizard *>(QWizardPage::wizard()));
return static_cast<NewCertificateWizard *>(QWizardPage::wizard());
}
QAbstractButton *button(QWizard::WizardButton button) const
{
return QWizardPage::wizard() ? QWizardPage::wizard()->button(button) : nullptr;
}
bool isButtonVisible(QWizard::WizardButton button) const
{
if (const QAbstractButton *const b = this->button(button)) {
return b->isVisible();
} else {
return false;
}
}
QDir tmpDir() const;
protected Q_SLOTS:
void setButtonVisible(QWizard::WizardButton button, bool visible)
{
if (QAbstractButton *const b = this->button(button)) {
b->setVisible(visible);
}
}
protected:
#define FIELD(type, name) type name() const { return field( QLatin1String(#name) ).value<type>(); }
FIELD(bool, pgp)
FIELD(bool, signingAllowed)
FIELD(bool, encryptionAllowed)
FIELD(bool, certificationAllowed)
FIELD(bool, authenticationAllowed)
FIELD(QString, name)
FIELD(QString, email)
FIELD(QString, dn)
FIELD(Subkey::PubkeyAlgo, keyType)
FIELD(int, keyStrength)
FIELD(QString, keyCurve)
FIELD(Subkey::PubkeyAlgo, subkeyType)
FIELD(int, subkeyStrength)
FIELD(QString, subkeyCurve)
FIELD(QDate, expiryDate)
FIELD(QStringList, additionalUserIDs)
FIELD(QStringList, additionalEMailAddresses)
FIELD(QStringList, dnsNames)
FIELD(QStringList, uris)
FIELD(QString, url)
FIELD(QString, error)
FIELD(QString, result)
FIELD(QString, fingerprint)
#undef FIELD
};
} // namespace NewCertificateUi
} // namespace Kleo
using namespace Kleo::NewCertificateUi;
namespace
{
class AdvancedSettingsDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(QStringList additionalUserIDs READ additionalUserIDs WRITE setAdditionalUserIDs)
Q_PROPERTY(QStringList additionalEMailAddresses READ additionalEMailAddresses WRITE setAdditionalEMailAddresses)
Q_PROPERTY(QStringList dnsNames READ dnsNames WRITE setDnsNames)
Q_PROPERTY(QStringList uris READ uris WRITE setUris)
Q_PROPERTY(uint keyStrength READ keyStrength WRITE setKeyStrength)
Q_PROPERTY(Subkey::PubkeyAlgo keyType READ keyType WRITE setKeyType)
Q_PROPERTY(QString keyCurve READ keyCurve WRITE setKeyCurve)
Q_PROPERTY(uint subkeyStrength READ subkeyStrength WRITE setSubkeyStrength)
Q_PROPERTY(QString subkeyCurve READ subkeyCurve WRITE setSubkeyCurve)
Q_PROPERTY(Subkey::PubkeyAlgo subkeyType READ subkeyType WRITE setSubkeyType)
Q_PROPERTY(bool signingAllowed READ signingAllowed WRITE setSigningAllowed)
Q_PROPERTY(bool encryptionAllowed READ encryptionAllowed WRITE setEncryptionAllowed)
Q_PROPERTY(bool certificationAllowed READ certificationAllowed WRITE setCertificationAllowed)
Q_PROPERTY(bool authenticationAllowed READ authenticationAllowed WRITE setAuthenticationAllowed)
Q_PROPERTY(QDate expiryDate READ expiryDate WRITE setExpiryDate)
public:
explicit AdvancedSettingsDialog(QWidget *parent = nullptr)
: QDialog(parent),
protocol(UnknownProtocol),
pgpDefaultAlgorithm(Subkey::AlgoELG_E),
cmsDefaultAlgorithm(Subkey::AlgoRSA),
keyTypeImmutable(false),
ui(),
mECCSupported(engineIsVersion(2, 1, 0)),
mEdDSASupported(engineIsVersion(2, 1, 15))
{
qRegisterMetaType<Subkey::PubkeyAlgo>("Subkey::PubkeyAlgo");
ui.setupUi(this);
const QDate today = QDate::currentDate();
ui.expiryDE->setMinimumDate(today);
ui.expiryDE->setDate(today.addYears(2));
ui.expiryCB->setChecked(true);
ui.emailLW->setDefaultValue(i18n("new email"));
ui.dnsLW->setDefaultValue(i18n("new dns name"));
ui.uriLW->setDefaultValue(i18n("new uri"));
fillKeySizeComboBoxen();
}
void setProtocol(GpgME::Protocol proto)
{
if (protocol == proto) {
return;
}
protocol = proto;
loadDefaultKeyType();
}
void setAdditionalUserIDs(const QStringList &items)
{
ui.uidLW->setItems(items);
}
QStringList additionalUserIDs() const
{
return ui.uidLW->items();
}
void setAdditionalEMailAddresses(const QStringList &items)
{
ui.emailLW->setItems(items);
}
QStringList additionalEMailAddresses() const
{
return ui.emailLW->items();
}
void setDnsNames(const QStringList &items)
{
ui.dnsLW->setItems(items);
}
QStringList dnsNames() const
{
return ui.dnsLW->items();
}
void setUris(const QStringList &items)
{
ui.uriLW->setItems(items);
}
QStringList uris() const
{
return ui.uriLW->items();
}
void setKeyStrength(unsigned int strength)
{
set_keysize(ui.rsaKeyStrengthCB, strength);
set_keysize(ui.dsaKeyStrengthCB, strength);
}
unsigned int keyStrength() const
{
return
ui.dsaRB->isChecked() ? get_keysize(ui.dsaKeyStrengthCB) :
ui.rsaRB->isChecked() ? get_keysize(ui.rsaKeyStrengthCB) : 0;
}
void setKeyType(Subkey::PubkeyAlgo algo)
{
QRadioButton *const rb =
is_rsa(algo) ? ui.rsaRB :
is_dsa(algo) ? ui.dsaRB :
is_ecdsa(algo) || is_eddsa(algo) ? ui.ecdsaRB : nullptr;
if (rb) {
rb->setChecked(true);
}
}
Subkey::PubkeyAlgo keyType() const
{
return
ui.dsaRB->isChecked() ? Subkey::AlgoDSA :
ui.rsaRB->isChecked() ? Subkey::AlgoRSA :
ui.ecdsaRB->isChecked() ?
ui.ecdsaKeyCurvesCB->currentText() == QStringLiteral("ed25519") ? Subkey::AlgoEDDSA :
Subkey::AlgoECDSA :
Subkey::AlgoUnknown;
}
void setKeyCurve(const QString &curve)
{
set_curve(ui.ecdsaKeyCurvesCB, curve);
}
QString keyCurve() const
{
return get_curve(ui.ecdsaKeyCurvesCB);
}
void setSubkeyType(Subkey::PubkeyAlgo algo)
{
ui.elgCB->setChecked(is_elg(algo));
ui.rsaSubCB->setChecked(is_rsa(algo));
ui.ecdhCB->setChecked(is_ecdh(algo));
}
Subkey::PubkeyAlgo subkeyType() const
{
if (ui.elgCB->isChecked()) {
return Subkey::AlgoELG_E;
} else if (ui.rsaSubCB->isChecked()) {
return Subkey::AlgoRSA;
} else if (ui.ecdhCB->isChecked()) {
return Subkey::AlgoECDH;
}
return Subkey::AlgoUnknown;
}
void setSubkeyCurve(const QString &curve)
{
set_curve(ui.ecdhKeyCurvesCB, curve);
}
QString subkeyCurve() const
{
return get_curve(ui.ecdhKeyCurvesCB);
}
void setSubkeyStrength(unsigned int strength)
{
if (subkeyType() == Subkey::AlgoRSA) {
set_keysize(ui.rsaKeyStrengthSubCB, strength);
} else {
set_keysize(ui.elgKeyStrengthCB, strength);
}
}
unsigned int subkeyStrength() const
{
if (subkeyType() == Subkey::AlgoRSA) {
return get_keysize(ui.rsaKeyStrengthSubCB);
}
return get_keysize(ui.elgKeyStrengthCB);
}
void setSigningAllowed(bool on)
{
ui.signingCB->setChecked(on);
}
bool signingAllowed() const
{
return ui.signingCB->isChecked();
}
void setEncryptionAllowed(bool on)
{
ui.encryptionCB->setChecked(on);
}
bool encryptionAllowed() const
{
return ui.encryptionCB->isChecked();
}
void setCertificationAllowed(bool on)
{
ui.certificationCB->setChecked(on);
}
bool certificationAllowed() const
{
return ui.certificationCB->isChecked();
}
void setAuthenticationAllowed(bool on)
{
ui.authenticationCB->setChecked(on);
}
bool authenticationAllowed() const
{
return ui.authenticationCB->isChecked();
}
void setExpiryDate(const QDate &date)
{
if (date.isValid()) {
ui.expiryDE->setDate(date);
} else {
ui.expiryCB->setChecked(false);
}
}
QDate expiryDate() const
{
return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate();
}
Q_SIGNALS:
void changed();
private Q_SLOTS:
void slotKeyMaterialSelectionChanged()
{
const unsigned int algo = keyType();
const unsigned int sk_algo = subkeyType();
if (protocol == OpenPGP) {
if (!keyTypeImmutable) {
ui.elgCB->setEnabled(is_dsa(algo));
ui.rsaSubCB->setEnabled(is_rsa(algo));
ui.ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo));
if (sender() == ui.dsaRB || sender() == ui.rsaRB || sender() == ui.ecdsaRB) {
ui.elgCB->setChecked(is_dsa(algo));
ui.ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo));
ui.rsaSubCB->setChecked(is_rsa(algo));
}
if (is_rsa(algo)) {
ui.encryptionCB->setEnabled(true);
ui.encryptionCB->setChecked(true);
ui.signingCB->setEnabled(true);
ui.signingCB->setChecked(true);
ui.authenticationCB->setEnabled(true);
if (is_rsa(sk_algo)) {
ui.encryptionCB->setEnabled(false);
ui.encryptionCB->setChecked(true);
} else {
ui.encryptionCB->setEnabled(true);
}
} else if (is_dsa(algo)) {
ui.encryptionCB->setEnabled(false);
if (is_elg(sk_algo)) {
ui.encryptionCB->setChecked(true);
} else {
ui.encryptionCB->setChecked(false);
}
} else if (is_ecdsa(algo) || is_eddsa(algo)) {
ui.signingCB->setEnabled(true);
ui.signingCB->setChecked(true);
ui.authenticationCB->setEnabled(true);
ui.encryptionCB->setEnabled(false);
ui.encryptionCB->setChecked(is_ecdh(sk_algo));
}
}
} else {
//assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all
}
}
void slotSigningAllowedToggled(bool on)
{
if (!on && protocol == CMS && !encryptionAllowed()) {
setEncryptionAllowed(true);
}
}
void slotEncryptionAllowedToggled(bool on)
{
if (!on && protocol == CMS && !signingAllowed()) {
setSigningAllowed(true);
}
}
private:
void fillKeySizeComboBoxen();
void loadDefaultKeyType();
void loadDefaultGnuPGKeyType();
void updateWidgetVisibility();
private:
GpgME::Protocol protocol;
unsigned int pgpDefaultAlgorithm;
unsigned int cmsDefaultAlgorithm;
bool keyTypeImmutable;
Ui_AdvancedSettingsDialog ui;
bool mECCSupported;
bool mEdDSASupported;
};
class ChooseProtocolPage : public WizardPage
{
Q_OBJECT
public:
explicit ChooseProtocolPage(QWidget *p = nullptr)
: WizardPage(p),
initialized(false),
ui()
{
ui.setupUi(this);
registerField(QStringLiteral("pgp"), ui.pgpCLB);
}
void setProtocol(Protocol proto)
{
if (proto == OpenPGP) {
ui.pgpCLB->setChecked(true);
} else if (proto == CMS) {
ui.x509CLB->setChecked(true);
} else {
force_set_checked(ui.pgpCLB, false);
force_set_checked(ui.x509CLB, false);
}
}
Protocol protocol() const
{
return
ui.pgpCLB->isChecked() ? OpenPGP :
ui.x509CLB->isChecked() ? CMS : UnknownProtocol;
}
void initializePage() override {
if (!initialized)
{
connect(ui.pgpCLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
connect(ui.x509CLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
}
initialized = true;
}
bool isComplete() const override
{
return protocol() != UnknownProtocol;
}
private:
bool initialized : 1;
Ui_ChooseProtocolPage ui;
};
struct Line {
QString attr;
QString label;
QString regex;
QLineEdit *edit;
};
class EnterDetailsPage : public WizardPage
{
Q_OBJECT
public:
explicit EnterDetailsPage(QWidget *p = nullptr)
: WizardPage(p), dialog(this), ui()
{
ui.setupUi(this);
// set errorLB to have a fixed height of two lines:
ui.errorLB->setText(QStringLiteral("2<br>1"));
ui.errorLB->setFixedHeight(ui.errorLB->minimumSizeHint().height());
ui.errorLB->clear();
connect(ui.resultLE, &QLineEdit::textChanged,
this, &QWizardPage::completeChanged);
// The email doesn't necessarily show up in ui.resultLE:
connect(ui.emailLE, &QLineEdit::textChanged,
this, &QWizardPage::completeChanged);
connect(ui.addEmailToDnCB, &QAbstractButton::toggled,
this, &EnterDetailsPage::slotUpdateResultLabel);
registerDialogPropertiesAsFields();
registerField(QStringLiteral("dn"), ui.resultLE);
registerField(QStringLiteral("name"), ui.nameLE);
registerField(QStringLiteral("email"), ui.emailLE);
updateForm();
}
bool isComplete() const override;
void initializePage() override {
updateForm();
dialog.setProtocol(pgp() ? OpenPGP : CMS);
}
void cleanupPage() override {
saveValues();
}
private:
void updateForm();
void clearForm();
void saveValues();
void registerDialogPropertiesAsFields();
private:
QString pgpUserID() const;
QString cmsDN() const;
private Q_SLOTS:
void slotAdvancedSettingsClicked();
void slotUpdateResultLabel()
{
ui.resultLE->setText(pgp() ? pgpUserID() : cmsDN());
}
private:
QVector<Line> lineList;
QList<QWidget *> dynamicWidgets;
QMap<QString, QString> savedValues;
AdvancedSettingsDialog dialog;
Ui_EnterDetailsPage ui;
};
class OverviewPage : public WizardPage
{
Q_OBJECT
public:
explicit OverviewPage(QWidget *p = nullptr)
: WizardPage(p), ui()
{
ui.setupUi(this);
setCommitPage(true);
setButtonText(QWizard::CommitButton, i18nc("@action", "Create"));
}
void initializePage() override {
slotShowDetails();
}
private Q_SLOTS:
void slotShowDetails()
{
ui.textBrowser->setHtml(i18nFormatGnupgKeyParms(ui.showAllDetailsCB->isChecked()));
}
private:
QStringList i18nKeyUsages() const;
QStringList i18nSubkeyUsages() const;
QStringList i18nCombinedKeyUsages() const;
QString i18nFormatGnupgKeyParms(bool details) const;
private:
Ui_OverviewPage ui;
};
class KeyCreationPage : public WizardPage
{
Q_OBJECT
public:
explicit KeyCreationPage(QWidget *p = nullptr)
: WizardPage(p),
ui()
{
ui.setupUi(this);
}
bool isComplete() const override
{
return !job;
}
void initializePage() override {
startJob();
}
private:
void startJob()
{
const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime();
if (!proto) {
return;
}
QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob();
if (!j) {
return;
}
connect(j, SIGNAL(result(GpgME::KeyGenerationResult,QByteArray,QString)),
this, SLOT(slotResult(GpgME::KeyGenerationResult,QByteArray,QString)));
if (const Error err = j->start(createGnupgKeyParms()))
setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1",
QString::fromLocal8Bit(err.asString())));
else {
job = j;
}
}
QStringList keyUsages() const;
QStringList subkeyUsages() const;
QString createGnupgKeyParms() const;
private Q_SLOTS:
void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog)
{
Q_UNUSED(auditLog);
if (result.error().code() || (pgp() && !result.fingerprint())) {
setField(QStringLiteral("error"), result.error().isCanceled()
? i18n("Operation canceled.")
: i18n("Could not create key pair: %1",
QString::fromLocal8Bit(result.error().asString())));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else if (pgp()) {
setField(QStringLiteral("error"), QString());
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), i18n("Key pair created successfully.\n"
"Fingerprint: %1", QLatin1String(result.fingerprint())));
} else {
QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10")));
if (!file.open(QIODevice::WriteOnly)) {
setField(QStringLiteral("error"), i18n("Could not write output file %1: %2",
file.fileName(), file.errorString()));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else {
file.write(request);
setField(QStringLiteral("error"), QString());
setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString());
setField(QStringLiteral("result"), i18n("Key pair created successfully."));
}
}
// Ensure that we have the key in the keycache
if (pgp() && !result.error().code() && result.fingerprint()) {
auto ctx = Context::createForProtocol(OpenPGP);
if (ctx) {
// Check is pretty useless something very buggy in that case.
Error e;
const auto key = ctx->key(result.fingerprint(), e, true);
if (!key.isNull()) {
KeyCache::mutableInstance()->insert(key);
} else {
qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key.";
}
delete ctx;
}
}
setField(QStringLiteral("fingerprint"), result.fingerprint() ?
QString::fromLatin1(result.fingerprint()) : QString());
job = nullptr;
Q_EMIT completeChanged();
QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
}
private:
QPointer<QGpgME::KeyGenerationJob> job;
Ui_KeyCreationPage ui;
};
class ResultPage : public WizardPage
{
Q_OBJECT
public:
explicit ResultPage(QWidget *p = nullptr)
: WizardPage(p),
initialized(false),
successfullyCreatedSigningCertificate(false),
successfullyCreatedEncryptionCertificate(false),
ui()
{
ui.setupUi(this);
ui.dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64));
registerField(QStringLiteral("error"), ui.errorTB, "plainText");
registerField(QStringLiteral("result"), ui.resultTB, "plainText");
registerField(QStringLiteral("url"), ui.dragQueen, "url");
// hidden field, since QWizard can't deal with non-widget-backed fields...
QLineEdit *le = new QLineEdit(this);
le->hide();
registerField(QStringLiteral("fingerprint"), le);
}
void initializePage() override {
const bool error = isError();
if (error)
{
setTitle(i18nc("@title", "Key Creation Failed"));
setSubTitle(i18n("Key pair creation failed. Please find details about the failure below."));
} else {
setTitle(i18nc("@title", "Key Pair Successfully Created"));
setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below."));
}
ui.resultTB ->setVisible(!error);
ui.errorTB ->setVisible(error);
ui.dragQueen ->setVisible(!error &&!pgp());
ui.restartWizardPB ->setVisible(error);
ui.nextStepsGB ->setVisible(!error);
ui.saveRequestToFilePB ->setVisible(!pgp());
ui.makeBackupPB ->setVisible(pgp());
ui.createRevocationRequestPB->setVisible(pgp() &&false); // not implemented
ui.sendCertificateByEMailPB ->setVisible(pgp());
ui.sendRequestByEMailPB ->setVisible(!pgp());
ui.uploadToKeyserverPB ->setVisible(pgp());
if (!error && !pgp())
{
if (signingAllowed() && !encryptionAllowed()) {
successfullyCreatedSigningCertificate = true;
} else if (!signingAllowed() && encryptionAllowed()) {
successfullyCreatedEncryptionCertificate = true;
} else {
successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true;
}
}
ui.createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate &&!successfullyCreatedSigningCertificate);
ui.createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate &&!successfullyCreatedEncryptionCertificate);
setButtonVisible(QWizard::CancelButton, error);
if (!initialized)
connect(ui.restartWizardPB, &QAbstractButton::clicked,
wizard(), &QWizard::restart);
initialized = true;
}
void cleanupPage() override {
setButtonVisible(QWizard::CancelButton, true);
}
bool isError() const
{
return !ui.errorTB->document()->isEmpty();
}
bool isComplete() const override
{
return !isError();
}
private:
Key key() const
{
return KeyCache::instance()->findByFingerprint(fingerprint().toLatin1().constData());
}
private Q_SLOTS:
void slotSaveRequestToFile()
{
QString fileName = FileDialog::getSaveFileName(this, i18nc("@title", "Save Request"),
QStringLiteral("imp"), i18n("PKCS#10 Requests (*.p10)"));
if (fileName.isEmpty()) {
return;
}
if (!fileName.endsWith(QLatin1String(".p10"), Qt::CaseInsensitive)) {
fileName += QLatin1String(".p10");
}
QFile src(QUrl(url()).toLocalFile());
if (!src.copy(fileName))
KMessageBox::error(this,
xi18nc("@info",
"Could not copy temporary file <filename>%1</filename> "
"to file <filename>%2</filename>: <message>%3</message>",
src.fileName(), fileName, src.errorString()),
i18nc("@title", "Error Saving Request"));
else
KMessageBox::information(this,
xi18nc("@info",
"<para>Successfully wrote request to <filename>%1</filename>.</para>"
"<para>You should now send the request to the Certification Authority (CA).</para>",
fileName),
i18nc("@title", "Request Saved"));
}
void slotSendRequestByEMail()
{
if (pgp()) {
return;
}
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
invokeMailer(config.readEntry("CAEmailAddress"), // to
i18n("Please process this certificate."), // subject
i18n("Please process this certificate and inform the sender about the location to fetch the resulting certificate.\n\nThanks,\n"), // body
QUrl(url()).toLocalFile()); // attachment
}
void slotSendCertificateByEMail()
{
if (!pgp() || exportCertificateCommand) {
return;
}
ExportCertificateCommand *cmd = new ExportCertificateCommand(key());
connect(cmd, &ExportCertificateCommand::finished, this, &ResultPage::slotSendCertificateByEMailContinuation);
cmd->setOpenPGPFileName(tmpDir().absoluteFilePath(fingerprint() + QLatin1String(".asc")));
cmd->start();
exportCertificateCommand = cmd;
}
void slotSendCertificateByEMailContinuation()
{
if (!exportCertificateCommand) {
return;
}
// ### better error handling?
const QString fileName = exportCertificateCommand->openPGPFileName();
qCDebug(KLEOPATRA_LOG) << "fileName" << fileName;
exportCertificateCommand = nullptr;
if (fileName.isEmpty()) {
return;
}
invokeMailer(QString(), // to
i18n("My new public OpenPGP key"), // subject
i18n("Please find attached my new public OpenPGP key."), // body
fileName);
}
QByteArray ol_quote(QByteArray str)
{
#ifdef Q_OS_WIN
return "\"\"" + str.replace('"', "\\\"") + "\"\"";
//return '"' + str.replace( '"', "\\\"" ) + '"';
#else
return str;
#endif
}
void invokeMailer(const QString &to, const QString &subject, const QString &body, const QString &attachment)
{
qCDebug(KLEOPATRA_LOG) << "to:" << to << "subject:" << subject
<< "body:" << body << "attachment:" << attachment;
// RFC 2368 says body's linebreaks need to be encoded as
// "%0D%0A", so normalize body to CRLF:
//body.replace(QLatin1Char('\n'), QStringLiteral("\r\n")).remove(QStringLiteral("\r\r"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("subject"), subject);
query.addQueryItem(QStringLiteral("body"), body);
if (!attachment.isEmpty()) {
query.addQueryItem(QStringLiteral("attach"), attachment);
}
QUrl url;
url.setScheme(QStringLiteral("mailto"));
url.setQuery(query);
qCDebug(KLEOPATRA_LOG) << "openUrl" << url;
QDesktopServices::openUrl(url);
KMessageBox::information(this,
xi18nc("@info",
"<para><application>Kleopatra</application> tried to send a mail via your default mail client.</para>"
"<para>Some mail clients are known not to support attachments when invoked this way.</para>"
"<para>If your mail client does not have an attachment, then drag the <application>Kleopatra</application> icon and drop it on the message compose window of your mail client.</para>"
"<para>If that does not work, either, save the request to a file, and then attach that.</para>"),
i18nc("@title", "Sending Mail"),
QStringLiteral("newcertificatewizard-mailto-troubles"));
}
void slotUploadCertificateToDirectoryServer()
{
if (pgp()) {
(new ExportOpenPGPCertsToServerCommand(key()))->start();
}
}
void slotBackupCertificate()
{
if (pgp()) {
(new ExportSecretKeyCommand(key()))->start();
}
}
void slotCreateRevocationRequest()
{
}
void slotCreateSigningCertificate()
{
if (successfullyCreatedSigningCertificate) {
return;
}
toggleSignEncryptAndRestart();
}
void slotCreateEncryptionCertificate()
{
if (successfullyCreatedEncryptionCertificate) {
return;
}
toggleSignEncryptAndRestart();
}
private:
void toggleSignEncryptAndRestart()
{
if (!wizard()) {
return;
}
if (KMessageBox::warningContinueCancel(
this,
i18nc("@info",
"This operation will delete the certification request. "
"Please make sure that you have sent or saved it before proceeding."),
i18nc("@title", "Certification Request About To Be Deleted")) != KMessageBox::Continue) {
return;
}
const bool sign = signingAllowed();
const bool encr = encryptionAllowed();
setField(QStringLiteral("signingAllowed"), !sign);
setField(QStringLiteral("encryptionAllowed"), !encr);
// restart and skip to Overview Page:
wizard()->restart();
for (int i = wizard()->currentId(); i < NewCertificateWizard::OverviewPageId; ++i) {
wizard()->next();
}
}
private:
bool initialized : 1;
bool successfullyCreatedSigningCertificate : 1;
bool successfullyCreatedEncryptionCertificate : 1;
QPointer<ExportCertificateCommand> exportCertificateCommand;
Ui_ResultPage ui;
};
}
class NewCertificateWizard::Private
{
friend class ::Kleo::NewCertificateWizard;
friend class ::Kleo::NewCertificateUi::WizardPage;
NewCertificateWizard *const q;
public:
explicit Private(NewCertificateWizard *qq)
: q(qq),
tmp(QDir::temp().absoluteFilePath(QStringLiteral("kleo-"))),
ui(q)
{
q->setWindowTitle(i18nc("@title", "Key Pair Creation Wizard"));
}
private:
QTemporaryDir tmp;
struct Ui {
ChooseProtocolPage chooseProtocolPage;
EnterDetailsPage enterDetailsPage;
OverviewPage overviewPage;
KeyCreationPage keyCreationPage;
ResultPage resultPage;
explicit Ui(NewCertificateWizard *q)
: chooseProtocolPage(q),
enterDetailsPage(q),
overviewPage(q),
keyCreationPage(q),
resultPage(q)
{
KDAB_SET_OBJECT_NAME(chooseProtocolPage);
KDAB_SET_OBJECT_NAME(enterDetailsPage);
KDAB_SET_OBJECT_NAME(overviewPage);
KDAB_SET_OBJECT_NAME(keyCreationPage);
KDAB_SET_OBJECT_NAME(resultPage);
q->setOptions(DisabledBackButtonOnLastPage);
q->setPage(ChooseProtocolPageId, &chooseProtocolPage);
q->setPage(EnterDetailsPageId, &enterDetailsPage);
q->setPage(OverviewPageId, &overviewPage);
q->setPage(KeyCreationPageId, &keyCreationPage);
q->setPage(ResultPageId, &resultPage);
q->setStartId(ChooseProtocolPageId);
}
} ui;
};
NewCertificateWizard::NewCertificateWizard(QWidget *p)
: QWizard(p), d(new Private(this))
{
}
NewCertificateWizard::~NewCertificateWizard() {}
void NewCertificateWizard::setProtocol(Protocol proto)
{
d->ui.chooseProtocolPage.setProtocol(proto);
setStartId(proto == UnknownProtocol ? ChooseProtocolPageId : EnterDetailsPageId);
}
Protocol NewCertificateWizard::protocol() const
{
return d->ui.chooseProtocolPage.protocol();
}
static QString pgpLabel(const QString &attr)
{
if (attr == QLatin1String("NAME")) {
return i18n("Name");
}
if (attr == QLatin1String("EMAIL")) {
return i18n("EMail");
}
return QString();
}
static QString attributeLabel(const QString &attr, bool pgp)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = pgp ? pgpLabel(attr) : Kleo::DNAttributeMapper::instance()->name2label(attr);
if (!label.isEmpty())
if (pgp) {
return label;
} else
return i18nc("Format string for the labels in the \"Your Personal Data\" page",
"%1 (%2)", label, attr);
else {
return attr;
}
}
#if 0
//Not used anywhere
static QString attributeLabelWithColor(const QString &attr, bool pgp)
{
const QString result = attributeLabel(attr, pgp);
if (result.isEmpty()) {
return QString();
} else {
return result + ':';
}
}
#endif
static QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
static const char *oidForAttributeName(const QString &attr)
{
QByteArray attrUtf8 = attr.toUtf8();
for (unsigned int i = 0; i < numOidMaps; ++i)
- if (qstricmp(attrUtf8, oidmap[i].name) == 0) {
+ if (qstricmp(attrUtf8.constData(), oidmap[i].name) == 0) {
return oidmap[i].oid;
}
return nullptr;
}
QDir WizardPage::tmpDir() const
{
return wizard() ? QDir(wizard()->d->tmp.path()) : QDir::home();
}
void EnterDetailsPage::registerDialogPropertiesAsFields()
{
const QMetaObject *const mo = dialog.metaObject();
for (unsigned int i = mo->propertyOffset(), end = i + mo->propertyCount(); i != end; ++i) {
const QMetaProperty mp = mo->property(i);
if (mp.isValid()) {
registerField(QLatin1String(mp.name()), &dialog, mp.name(), SIGNAL(accepted()));
}
}
}
void EnterDetailsPage::saveValues()
{
for (const Line &line : qAsConst(lineList)) {
savedValues[ attributeFromKey(line.attr) ] = line.edit->text().trimmed();
}
}
void EnterDetailsPage::clearForm()
{
qDeleteAll(dynamicWidgets);
dynamicWidgets.clear();
lineList.clear();
ui.nameLE->hide();
ui.nameLE->clear();
ui.nameLB->hide();
ui.nameRequiredLB->hide();
ui.emailLE->hide();
ui.emailLE->clear();
ui.emailLB->hide();
ui.emailRequiredLB->hide();
ui.addEmailToDnCB->hide();
}
static int row_index_of(QWidget *w, QGridLayout *l)
{
const int idx = l->indexOf(w);
int r, c, rs, cs;
l->getItemPosition(idx, &r, &c, &rs, &cs);
return r;
}
static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required)
{
Q_ASSERT(l);
Q_ASSERT(row >= 0);
Q_ASSERT(row < l->rowCount());
QLabel *lb = qobject_cast<QLabel *>(l->itemAtPosition(row, 0)->widget());
Q_ASSERT(lb);
QLineEdit *le = qobject_cast<QLineEdit *>(l->itemAtPosition(row, 1)->widget());
Q_ASSERT(le);
lb->setBuddy(le); // For better accessibility
QLabel *reqLB = qobject_cast<QLabel *>(l->itemAtPosition(row, 2)->widget());
Q_ASSERT(reqLB);
lb->setText(i18nc("interpunctation for labels", "%1:", label));
le->setText(preset);
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
delete le->validator();
if (validator) {
if (!validator->parent()) {
validator->setParent(le);
}
le->setValidator(validator);
}
le->setReadOnly(readonly && le->hasAcceptableInput());
lb->show();
le->show();
reqLB->show();
return le;
}
static int add_row(QGridLayout *l, QList<QWidget *> *wl)
{
Q_ASSERT(l);
Q_ASSERT(wl);
const int row = l->rowCount();
QWidget *w1, *w2, *w3;
l->addWidget(w1 = new QLabel(l->parentWidget()), row, 0);
l->addWidget(w2 = new QLineEdit(l->parentWidget()), row, 1);
l->addWidget(w3 = new QLabel(l->parentWidget()), row, 2);
wl->push_back(w1);
wl->push_back(w2);
wl->push_back(w3);
return row;
}
void EnterDetailsPage::updateForm()
{
clearForm();
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
QStringList attrOrder = config.readEntry(pgp() ? "OpenPGPAttributeOrder" : "DNAttributeOrder", QStringList());
if (attrOrder.empty()) {
if (pgp()) {
attrOrder << QStringLiteral("NAME") << QStringLiteral("EMAIL");
} else {
attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O!") << QStringLiteral("C!") << QStringLiteral("EMAIL!");
}
}
QList<QWidget *> widgets;
widgets.push_back(ui.nameLE);
widgets.push_back(ui.emailLE);
QMap<int, Line> lines;
Q_FOREACH (const QString &rawKey, attrOrder) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString preset = savedValues.value(attr, config.readEntry(attr, QString()));
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1String("_label"),
attributeLabel(attr, pgp()));
const QString regex = config.readEntry(attr + QLatin1String("_regex"));
int row;
bool known = true;
QValidator *validator = nullptr;
if (attr == QLatin1String("EMAIL")) {
row = row_index_of(ui.emailLE, ui.gridLayout);
validator = regex.isEmpty() ? Validation::email() : Validation::email(QRegExp(regex));
if (!pgp()) {
ui.addEmailToDnCB->show();
}
} else if (attr == QLatin1String("NAME") || attr == QLatin1String("CN")) {
if ((pgp() && attr == QLatin1String("CN")) || (!pgp() && attr == QLatin1String("NAME"))) {
continue;
}
if (pgp()) {
validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(QRegExp(regex));
}
row = row_index_of(ui.nameLE, ui.gridLayout);
} else {
known = false;
row = add_row(ui.gridLayout, &dynamicWidgets);
}
if (!validator && !regex.isEmpty()) {
validator = new QRegExpValidator(QRegExp(regex), nullptr);
}
QLineEdit *le = adjust_row(ui.gridLayout, row, label, preset, validator, readonly, required);
const Line line = { key, label, regex, le };
lines[row] = line;
if (!known) {
widgets.push_back(le);
}
// don't connect twice:
disconnect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
connect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
}
// create lineList in visual order, so requirementsAreMet()
// complains from top to bottom:
lineList.reserve(lines.count());
std::copy(lines.cbegin(), lines.cend(), std::back_inserter(lineList));
widgets.push_back(ui.resultLE);
widgets.push_back(ui.addEmailToDnCB);
widgets.push_back(ui.advancedPB);
const KEMailSettings e;
if (ui.nameLE->text().isEmpty()) {
ui.nameLE->setText(e.getSetting(KEMailSettings::RealName));
}
if (ui.emailLE->text().isEmpty()) {
ui.emailLE->setText(e.getSetting(KEMailSettings::EmailAddress));
}
set_tab_order(widgets);
}
QString EnterDetailsPage::cmsDN() const
{
DN dn;
for (QVector<Line>::const_iterator it = lineList.begin(), end = lineList.end(); it != end; ++it) {
const QString text = it->edit->text().trimmed();
if (text.isEmpty()) {
continue;
}
QString attr = attributeFromKey(it->attr);
if (attr == QLatin1String("EMAIL") && !ui.addEmailToDnCB->isChecked()) {
continue;
}
if (const char *const oid = oidForAttributeName(attr)) {
attr = QString::fromUtf8(oid);
}
dn.append(DN::Attribute(attr, text));
}
return dn.dn();
}
QString EnterDetailsPage::pgpUserID() const
{
return Formatting::prettyNameAndEMail(OpenPGP, QString(),
ui.nameLE->text().trimmed(),
ui.emailLE->text().trimmed(),
QString());
}
static bool has_intermediate_input(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return v && v->validate(text, pos) == QValidator::Intermediate;
}
static bool requirementsAreMet(const QVector<Line> &list, QString &error)
{
bool allEmpty = true;
for (const Line &line : list) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
const QString key = line.attr;
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\":";
if (le->text().trimmed().isEmpty()) {
if (key.endsWith(QLatin1Char('!'))) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
}
} else if (has_intermediate_input(le)) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else
error = xi18nc("@info", "<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
return false;
} else {
allEmpty = false;
}
}
// Ensure that at least one value is acceptable
return !allEmpty;
}
bool EnterDetailsPage::isComplete() const
{
QString error;
const bool ok = requirementsAreMet(lineList, error);
ui.errorLB->setText(error);
return ok;
}
void EnterDetailsPage::slotAdvancedSettingsClicked()
{
dialog.exec();
}
QStringList KeyCreationPage::keyUsages() const
{
QStringList usages;
if (signingAllowed()) {
usages << QStringLiteral("sign");
}
if (encryptionAllowed() && !is_ecdh(subkeyType()) &&
!is_dsa(keyType()) && !is_rsa(subkeyType())) {
usages << QStringLiteral("encrypt");
}
if (0) // not needed in pgp (implied) and not supported in cms
if (certificationAllowed()) {
usages << QStringLiteral("certify");
}
if (authenticationAllowed()) {
usages << QStringLiteral("auth");
}
return usages;
}
QStringList OverviewPage::i18nKeyUsages() const
{
QStringList usages;
if (signingAllowed()) {
usages << i18n("Sign");
}
if (encryptionAllowed() && !is_ecdh(subkeyType()) &&
!is_dsa(keyType()) && !is_rsa(subkeyType())) {
usages << i18n("Encrypt");
}
if (0) // not needed in pgp (implied) and not supported in cms
if (certificationAllowed()) {
usages << i18n("Certify");
}
if (authenticationAllowed()) {
usages << i18n("Authenticate");
}
return usages;
}
QStringList KeyCreationPage::subkeyUsages() const
{
QStringList usages;
if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) ||
is_ecdh(subkeyType()))) {
Q_ASSERT(subkeyType());
usages << QStringLiteral("encrypt");
}
return usages;
}
QStringList OverviewPage::i18nSubkeyUsages() const
{
QStringList usages;
if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) ||
is_ecdh(subkeyType()))) {
Q_ASSERT(subkeyType());
usages << i18n("Encrypt");
}
return usages;
}
QStringList OverviewPage::i18nCombinedKeyUsages() const
{
return i18nSubkeyUsages() + i18nKeyUsages();
}
namespace
{
template <typename T = QString>
struct Row {
QString key;
T value;
Row(const QString &k, const T &v) : key(k), value(v) {}
};
template <typename T>
QTextStream &operator<<(QTextStream &s, const Row<T> &row)
{
if (row.key.isEmpty()) {
return s;
} else {
return s << "<tr><td>" << row.key << "</td><td>" << row.value << "</td></tr>";
}
}
}
QString OverviewPage::i18nFormatGnupgKeyParms(bool details) const
{
QString result;
QTextStream s(&result);
s << "<table>";
if (pgp()) {
if (!name().isEmpty()) {
s << Row< >(i18n("Name:"), name());
}
}
if (!email().isEmpty()) {
s << Row< >(i18n("Email Address:"), email());
}
if (!pgp()) {
s << Row< >(i18n("Subject-DN:"), DN(dn()).dn(QStringLiteral(",<br>")));
}
if (details) {
s << Row< >(i18n("Key Type:"), QLatin1String(Subkey::publicKeyAlgorithmAsString(keyType())));
if (is_ecdsa(keyType()) || is_eddsa(keyType())) {
s << Row< >(i18n("Key Curve:"), keyCurve());
} else if (const unsigned int strength = keyStrength()) {
s << Row< >(i18n("Key Strength:"), i18np("1 bit", "%1 bits", strength));
} else {
s << Row< >(i18n("Key Strength:"), i18n("default"));
}
s << Row< >(i18n("Usage:"), i18nCombinedKeyUsages().join(i18nc("separator for key usages", ",&nbsp;")));
if (const Subkey::PubkeyAlgo subkey = subkeyType()) {
s << Row< >(i18n("Subkey Type:"), QLatin1String(Subkey::publicKeyAlgorithmAsString(subkey)));
if (is_ecdh(subkeyType())) {
s << Row< >(i18n("Key Curve:"), subkeyCurve());
} else if (const unsigned int strength = subkeyStrength()) {
s << Row< >(i18n("Subkey Strength:"), i18np("1 bit", "%1 bits", strength));
} else {
s << Row< >(i18n("Subkey Strength:"), i18n("default"));
}
s << Row< >(i18n("Subkey Usage:"), i18nSubkeyUsages().join(i18nc("separator for key usages", ",&nbsp;")));
}
}
if (pgp() && details && expiryDate().isValid()) {
s << Row< >(i18n("Valid Until:"), QLocale().toString(expiryDate()));
}
if (!pgp() && details) {
Q_FOREACH (const QString &email, additionalEMailAddresses()) {
s << Row< >(i18n("Add. Email Address:"), email);
}
Q_FOREACH (const QString &dns, dnsNames()) {
s << Row< >(i18n("DNS Name:"), dns);
}
Q_FOREACH (const QString &uri, uris()) {
s << Row< >(i18n("URI:"), uri);
}
}
return result;
}
static QString encode_dns(const QString &dns)
{
return QLatin1String(QUrl::toAce(dns));
}
static QString encode_email(const QString &email)
{
const int at = email.lastIndexOf(QLatin1Char('@'));
if (at < 0) {
return email;
}
return email.left(at + 1) + encode_dns(email.mid(at + 1));
}
QString KeyCreationPage::createGnupgKeyParms() const
{
QString result;
QTextStream s(&result);
s << "<GnupgKeyParms format=\"internal\">" << endl;
if (pgp()) {
s << "%ask-passphrase" << endl;
}
s << "key-type: " << Subkey::publicKeyAlgorithmAsString(keyType()) << endl;
if (is_ecdsa(keyType()) || is_eddsa(keyType())) {
s << "key-curve: " << keyCurve() << endl;
} else if (const unsigned int strength = keyStrength()) {
s << "key-length: " << strength << endl;
}
s << "key-usage: " << keyUsages().join(QLatin1Char(' ')) << endl;
if (const Subkey::PubkeyAlgo subkey = subkeyType()) {
s << "subkey-type: " << Subkey::publicKeyAlgorithmAsString(subkey) << endl;
if (is_ecdh(subkeyType())) {
s << "subkey-curve: " << subkeyCurve() << endl;
} else if (const unsigned int strength = subkeyStrength()) {
s << "subkey-length: " << strength << endl;
}
s << "subkey-usage: " << subkeyUsages().join(QLatin1Char(' ')) << endl;
}
if (pgp() && expiryDate().isValid()) {
s << "expire-date: " << expiryDate().toString(Qt::ISODate) << endl;
}
if (pgp()) {
if (!name().isEmpty()) {
s << "name-real: " << name() << endl;
}
if (!email().isEmpty()) {
s << "name-email: " << email() << endl;
}
} else {
s << "name-dn: " << dn() << endl;
s << "name-email: " << encode_email(email()) << endl;
Q_FOREACH (const QString &email, additionalEMailAddresses()) {
s << "name-email: " << encode_email(email) << endl;
}
Q_FOREACH (const QString &dns, dnsNames()) {
s << "name-dns: " << encode_dns(dns) << endl;
}
Q_FOREACH (const QString &uri, uris()) {
s << "name-uri: " << uri << endl;
}
}
s << "</GnupgKeyParms>" << endl;
qCDebug(KLEOPATRA_LOG) << '\n' << result;
return result;
}
static void fill_combobox(QComboBox &cb, const QList<int> &sizes, const QStringList &labels)
{
cb.clear();
for (int i = 0, end = sizes.size(); i != end; ++i) {
cb.addItem(i < labels.size() && !labels[i].trimmed().isEmpty()
? sizes[i] < 0
? i18ncp("%2: some admin-supplied text, %1: key size in bits", "%2 (1 bit; default)", "%2 (%1 bits; default)", -sizes[i], labels[i].trimmed())
: i18ncp("%2: some admin-supplied text, %1: key size in bits", "%2 (1 bit)", "%2 (%1 bits)", sizes[i], labels[i].trimmed())
: sizes[i] < 0
? i18ncp("%1: key size in bits", "1 bit (default)", "%1 bits (default)", -sizes[i])
: i18ncp("%1: key size in bits", "1 bit", "%1 bits", sizes[i]),
std::abs(sizes[i]));
if (sizes[i] < 0) {
cb.setCurrentIndex(cb.count() - 1);
}
}
}
void AdvancedSettingsDialog::fillKeySizeComboBoxen()
{
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
const QList<int> rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList<int>() << -2048 << 3072 << 4096);
const QList<int> dsaKeySizes = config.readEntry(DSA_KEYSIZES_ENTRY, QList<int>() << -2048);
const QList<int> elgKeySizes = config.readEntry(ELG_KEYSIZES_ENTRY, QList<int>() << -2048 << 3072 << 4096);
const QStringList rsaKeySizeLabels = config.readEntry(RSA_KEYSIZE_LABELS_ENTRY, QStringList());
const QStringList dsaKeySizeLabels = config.readEntry(DSA_KEYSIZE_LABELS_ENTRY, QStringList());
const QStringList elgKeySizeLabels = config.readEntry(ELG_KEYSIZE_LABELS_ENTRY, QStringList());
fill_combobox(*ui.rsaKeyStrengthCB, rsaKeySizes, rsaKeySizeLabels);
fill_combobox(*ui.rsaKeyStrengthSubCB, rsaKeySizes, rsaKeySizeLabels);
fill_combobox(*ui.dsaKeyStrengthCB, dsaKeySizes, dsaKeySizeLabels);
fill_combobox(*ui.elgKeyStrengthCB, elgKeySizes, elgKeySizeLabels);
if (mEdDSASupported) {
// If supported we recommend cv25519
ui.ecdsaKeyCurvesCB->addItem(QStringLiteral("ed25519"));
ui.ecdhKeyCurvesCB->addItem(QStringLiteral("cv25519"));
}
ui.ecdhKeyCurvesCB->addItems(curveNames);
ui.ecdsaKeyCurvesCB->addItems(curveNames);
}
// Try to load the default key type from GnuPG
void AdvancedSettingsDialog::loadDefaultGnuPGKeyType()
{
const auto conf = QGpgME::cryptoConfig();
if (!conf) {
qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
return;
}
const auto entry = conf->entry(protocol == CMS ? QStringLiteral("gpgsm") : QStringLiteral("gpg"),
QStringLiteral("Configuration"),
QStringLiteral("default_pubkey_algo"));
if (!entry) {
qCDebug(KLEOPATRA_LOG) << "GnuPG does not have default key type. Fallback to RSA";
setKeyType(Subkey::AlgoRSA);
setSubkeyType(Subkey::AlgoRSA);
return;
}
qCDebug(KLEOPATRA_LOG) << "Have default key type: " << entry->stringValue();
// Format is <primarytype>[/usage]+<subkeytype>[/usage]
const auto split = entry->stringValue().split(QLatin1Char('+'));
int size = 0;
Subkey::PubkeyAlgo algo = Subkey::AlgoUnknown;
QString curve;
parseAlgoString(split[0], &size, &algo, curve);
if (algo == Subkey::AlgoUnknown) {
setSubkeyType(Subkey::AlgoRSA);
return;
}
setKeyType(algo);
if (is_rsa(algo) || is_elg(algo) || is_dsa(algo)) {
setKeyStrength(size);
} else {
setKeyCurve(curve);
}
if (split.size() == 2) {
auto algoString = split[1];
// If it has no usage we assume encrypt subkey
if (!algoString.contains(QLatin1Char('/'))) {
algoString += QStringLiteral("/enc");
}
parseAlgoString(algoString, &size, &algo, curve);
if (algo == Subkey::AlgoUnknown) {
setSubkeyType(Subkey::AlgoRSA);
return;
}
setSubkeyType(algo);
if (is_rsa(algo) || is_elg(algo)) {
setSubkeyStrength(size);
} else {
setSubkeyCurve(curve);
}
}
}
void AdvancedSettingsDialog::loadDefaultKeyType()
{
if (protocol != CMS && protocol != OpenPGP) {
return;
}
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
const QString entry = protocol == CMS ? QLatin1String(CMS_KEY_TYPE_ENTRY) : QLatin1String(PGP_KEY_TYPE_ENTRY);
const QString keyType = config.readEntry(entry).trimmed().toUpper();
if (protocol == OpenPGP && keyType == QLatin1String("DSA")) {
setKeyType(Subkey::AlgoDSA);
setSubkeyType(Subkey::AlgoUnknown);
} else if (protocol == OpenPGP && keyType == QLatin1String("DSA+ELG")) {
setKeyType(Subkey::AlgoDSA);
setSubkeyType(Subkey::AlgoELG_E);
#if GPGMEPP_VERSION > 0x10800
// GPGME 1.8.0 has a bug that makes the gpgconf engine
// return garbage so we don't load it for this
} else if (keyType.isEmpty() && engineIsVersion(2, 1, 17)) {
loadDefaultGnuPGKeyType();
#endif
} else {
if (!keyType.isEmpty() && keyType != QLatin1String("RSA"))
qCWarning(KLEOPATRA_LOG) << "invalid value \"" << qPrintable(keyType)
<< "\" for entry \"[CertificateCreationWizard]"
<< qPrintable(entry) << "\"";
setKeyType(Subkey::AlgoRSA);
setSubkeyType(Subkey::AlgoRSA);
}
keyTypeImmutable = config.isEntryImmutable(entry);
updateWidgetVisibility();
}
void AdvancedSettingsDialog::updateWidgetVisibility()
{
// Personal Details Page
if (protocol == OpenPGP) { // ### hide until multi-uid is implemented
if (ui.tabWidget->indexOf(ui.personalTab) != -1) {
ui.tabWidget->removeTab(ui.tabWidget->indexOf(ui.personalTab));
}
} else {
if (ui.tabWidget->indexOf(ui.personalTab) == -1) {
ui.tabWidget->addTab(ui.personalTab, tr2i18n("Personal Details", nullptr));
}
}
ui.uidGB->setVisible(protocol == OpenPGP);
ui.uidGB->setEnabled(false);
ui.uidGB->setToolTip(i18nc("@info:tooltip", "Adding more than one User ID is not yet implemented."));
ui.emailGB->setVisible(protocol == CMS);
ui.dnsGB->setVisible(protocol == CMS);
ui.uriGB->setVisible(protocol == CMS);
ui.ecdhCB->setVisible(mECCSupported);
ui.ecdhKeyCurvesCB->setVisible(mECCSupported);
ui.ecdsaKeyCurvesCB->setVisible(mECCSupported);
ui.ecdsaRB->setVisible(mECCSupported);
if (mEdDSASupported) {
// We use the same radio button for EdDSA as we use for
// ECDSA GnuPG does the same and this is really super technical
// land.
ui.ecdsaRB->setText(QStringLiteral("ECDSA/EdDSA"));
}
bool deVsHack = Kleo::gpgComplianceP("de-vs");
if (deVsHack) {
// GnuPG Provides no API to query which keys are compliant for
// a mode. If we request a different one it will error out so
// we have to remove the options.
//
// Does anyone want to use NIST anyway?
int i;
while ((i = ui.ecdsaKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 ||
(i = ui.ecdsaKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
ui.ecdsaKeyCurvesCB->removeItem(i);
}
while ((i = ui.ecdhKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 ||
(i = ui.ecdhKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
ui.ecdhKeyCurvesCB->removeItem(i);
}
}
// Technical Details Page
if (keyTypeImmutable) {
ui.rsaRB->setEnabled(false);
ui.rsaSubCB->setEnabled(false);
ui.dsaRB->setEnabled(false);
ui.elgCB->setEnabled(false);
ui.ecdsaRB->setEnabled(false);
ui.ecdhCB->setEnabled(false);
} else {
ui.rsaRB->setEnabled(true);
ui.rsaSubCB->setEnabled(protocol == OpenPGP);
ui.dsaRB->setEnabled(protocol == OpenPGP && !deVsHack);
ui.elgCB->setEnabled(protocol == OpenPGP && !deVsHack);
ui.ecdsaRB->setEnabled(protocol == OpenPGP);
ui.ecdhCB->setEnabled(protocol == OpenPGP);
}
ui.certificationCB->setVisible(protocol == OpenPGP); // gpgsm limitation?
ui.authenticationCB->setVisible(protocol == OpenPGP);
if (protocol == OpenPGP) { // pgp keys must have certify capability
ui.certificationCB->setChecked(true);
ui.certificationCB->setEnabled(false);
}
if (protocol == CMS) {
ui.encryptionCB->setEnabled(true);
ui.rsaSubCB->setChecked(false);
ui.rsaKeyStrengthSubCB->setEnabled(false);
}
ui.expiryDE->setVisible(protocol == OpenPGP);
ui.expiryCB->setVisible(protocol == OpenPGP);
slotKeyMaterialSelectionChanged();
}
#include "newcertificatewizard.moc"
diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp
index 322d3c3e8..af0cdfd7b 100644
--- a/src/smartcard/readerstatus.cpp
+++ b/src/smartcard/readerstatus.cpp
@@ -1,682 +1,682 @@
/* -*- mode: c++; c-basic-offset:4 -*-
smartcard/readerstatus.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009 Klarälvdalens Datakonsult AB
Kleopatra 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 of the License, or
(at your option) any later version.
Kleopatra 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "readerstatus.h"
#include <utils/gnupg-helper.h>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/Stl_Util>
#include <gpgme++/context.h>
#include <gpgme++/defaultassuantransaction.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include "openpgpcard.h"
#include "netkeycard.h"
#include <QStringList>
#include <QFileInfo>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QPointer>
#include <memory>
#include <vector>
#include <set>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdlib>
#include "utils/kdtoolsglobal.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace GpgME;
static ReaderStatus *self = nullptr;
static const char *flags[] = {
"NOCARD",
"PRESENT",
"ACTIVE",
"USABLE",
};
static_assert(sizeof flags / sizeof * flags == Card::_NumScdStates, "");
static const char *prettyFlags[] = {
"NoCard",
"CardPresent",
"CardActive",
"CardUsable",
"CardError",
};
static_assert(sizeof prettyFlags / sizeof * prettyFlags == Card::NumStates, "");
#if 0
We need this once we have support for multiple readers in scdaemons
interface.
static unsigned int parseFileName(const QString &fileName, bool *ok)
{
QRegExp rx(QLatin1String("reader_(\\d+)\\.status"));
if (ok) {
*ok = false;
}
if (rx.exactMatch(QFileInfo(fileName).fileName())) {
return rx.cap(1).toUInt(ok, 10);
}
return 0;
}
#endif
Q_DECLARE_METATYPE(GpgME::Error)
namespace
{
static QDebug operator<<(QDebug s, const std::vector< std::pair<std::string, std::string> > &v)
{
typedef std::pair<std::string, std::string> pair;
s << '(';
for (const pair &p : v) {
s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << endl;
}
return s << ')';
}
static const char *app_types[] = {
"_", // will hopefully never be used as an app-type :)
"openpgp",
"nks",
"p15",
"dinsig",
"geldkarte",
};
static_assert(sizeof app_types / sizeof * app_types == Card::NumAppTypes, "");
static Card::AppType parse_app_type(const std::string &s)
{
qCDebug(KLEOPATRA_LOG) << "parse_app_type(" << s.c_str() << ")";
const char **it = std::find_if(std::begin(app_types), std::end(app_types),
[&s](const char *type) {
return ::strcasecmp(s.c_str(), type) == 0;
});
if (it == std::end(app_types)) {
qCDebug(KLEOPATRA_LOG) << "App type not found";
return Card::UnknownApplication;
}
return static_cast<Card::AppType>(it - std::begin(app_types));
}
static int parse_app_version(const std::string &s)
{
return std::atoi(s.c_str());
}
static Card::PinState parse_pin_state(const QString &s)
{
bool ok;
int i = s.toInt(&ok);
if (!ok) {
qCDebug(KLEOPATRA_LOG) << "Failed to parse pin state" << s;
return Card::UnknownPinState;
}
switch (i) {
case -4: return Card::NullPin;
case -3: return Card::PinBlocked;
case -2: return Card::NoPin;
case -1: return Card::UnknownPinState;
default:
if (i < 0) {
return Card::UnknownPinState;
} else {
return Card::PinOk;
}
}
}
static std::unique_ptr<DefaultAssuanTransaction> gpgagent_transact(std::shared_ptr<Context> &gpgAgent, const char *command, Error &err)
{
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << ")";
err = gpgAgent->assuanTransact(command);
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << "):" << QString::fromLocal8Bit(err.asString());
if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) {
qCDebug(KLEOPATRA_LOG) << "Assuan problem, killing context";
gpgAgent.reset();
}
return std::unique_ptr<DefaultAssuanTransaction>();
}
std::unique_ptr<AssuanTransaction> t = gpgAgent->takeLastAssuanTransaction();
return std::unique_ptr<DefaultAssuanTransaction>(dynamic_cast<DefaultAssuanTransaction*>(t.release()));
}
const std::vector< std::pair<std::string, std::string> > gpgagent_statuslines(std::shared_ptr<Context> gpgAgent, const char *what, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_transact(gpgAgent, what, err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): got" << t->statusLines();
return t->statusLines();
} else {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): t == NULL";
return std::vector<std::pair<std::string, std::string> >();
}
}
static const std::string gpgagent_status(const std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
const auto lines = gpgagent_statuslines (gpgAgent, what, err);
// The status is only the last attribute
// e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO
// it would only be FOO
const char *p = strrchr(what, ' ');
const char *needle = (p + 1) ? (p + 1) : what;
for (const auto &pair: lines) {
if (pair.first == needle) {
return pair.second;
}
}
return std::string();
}
static const std::string scd_getattr_status(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
std::string cmd = "SCD GETATTR ";
cmd += what;
return gpgagent_status(gpgAgent, cmd.c_str(), err);
}
static void handle_openpgp_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto ret = new OpenPGPCard();
ret->setSerialNumber(ci->serialNumber());
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code()) {
ci->setStatus(Card::CardError);
return;
}
ret->setKeyPairInfo(info);
ci.reset(ret);
}
static void handle_netkey_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto nkCard = new NetKeyCard();
nkCard->setSerialNumber(ci->serialNumber());
ci.reset(nkCard);
ci->setAppVersion(parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err)));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "NKS-VERSION resulted in error" << err.asString();
ci->setErrorMsg(QStringLiteral ("NKS-VERSION failed: ") + QString::fromUtf8(err.asString()));
return;
}
if (ci->appVersion() != 3) {
qCDebug(KLEOPATRA_LOG) << "not a NetKey v3 card, giving up. Version:" << ci->appVersion();
ci->setErrorMsg(QStringLiteral("NetKey v%1 cards are not supported.").arg(ci->appVersion()));
return;
}
// the following only works for NKS v3...
const auto chvStatus = QString::fromStdString(
scd_getattr_status(gpg_agent, "CHV-STATUS", err)).split(QStringLiteral(" "));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "no CHV-STATUS" << err.asString();
ci->setErrorMsg(QStringLiteral ("CHV-Status failed: ") + QString::fromUtf8(err.asString()));
return;
}
std::vector<Card::PinState> states;
// CHV Status for NKS v3 is
// Pin1 (Normal pin) Pin2 (Normal PUK)
// SigG1 SigG PUK.
int num = 0;
for (const auto &state: chvStatus) {
const auto parsed = parse_pin_state (state);
states.push_back(parsed);
if (parsed == Card::NullPin) {
if (num == 0) {
ci->setHasNullPin(true);
}
}
++num;
}
nkCard->setPinStates(states);
// check for keys to learn:
const std::unique_ptr<DefaultAssuanTransaction> result = gpgagent_transact(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code() || !result.get()) {
if (err) {
- ci->setErrorMsg(err.asString());
+ ci->setErrorMsg(QString::fromLatin1(err.asString()));
} else {
ci->setErrorMsg(QStringLiteral("Invalid internal state. No result."));
}
return;
}
const std::vector<std::string> keyPairInfos = result->statusLine("KEYPAIRINFO");
if (keyPairInfos.empty()) {
return;
}
nkCard->setKeyPairInfo(keyPairInfos);
}
static std::shared_ptr<Card> get_card_status(unsigned int slot, std::shared_ptr<Context> &gpg_agent)
{
qCDebug(KLEOPATRA_LOG) << "get_card_status(" << slot << ',' << gpg_agent.get() << ')';
auto ci = std::shared_ptr<Card> (new Card());
if (slot != 0 || !gpg_agent) {
// In the future scdaemon should support multiple slots but
// not yet (2.1.18)
return ci;
}
Error err;
ci->setSerialNumber(gpgagent_status(gpg_agent, "SCD SERIALNO", err));
if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
ci->setStatus(Card::NoCard);
return ci;
}
if (err.code()) {
ci->setStatus(Card::CardError);
return ci;
}
ci->setStatus(Card::CardPresent);
const auto verbatimType = scd_getattr_status(gpg_agent, "APPTYPE", err);
ci->setAppType(parse_app_type(verbatimType));
if (err.code()) {
return ci;
}
// Handle different card types
if (ci->appType() == Card::NksApplication) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found Netkey card" << ci->serialNumber().c_str() << "end";
handle_netkey_card(ci, gpg_agent);
return ci;
} else if (ci->appType() == Card::OpenPGPApplication) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found OpenPGP card" << ci->serialNumber().c_str() << "end";
handle_openpgp_card(ci, gpg_agent);
return ci;
} else {
qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << verbatimType.c_str();
return ci;
}
return ci;
}
static std::vector<std::shared_ptr<Card> > update_cardinfo(std::shared_ptr<Context> &gpgAgent)
{
// Multiple smartcard readers are only supported internally by gnupg
// but not by scdaemon (Status gnupg 2.1.18)
// We still pretend that there can be multiple cards inserted
// at once but we don't handle it yet.
const auto ci = get_card_status(0, gpgAgent);
return std::vector<std::shared_ptr<Card> >(1, ci);
}
} // namespace
struct Transaction {
QByteArray command;
QPointer<QObject> receiver;
const char *slot;
};
static const Transaction updateTransaction = { "__update__", nullptr, nullptr };
static const Transaction quitTransaction = { "__quit__", nullptr, nullptr };
namespace
{
class ReaderStatusThread : public QThread
{
Q_OBJECT
public:
explicit ReaderStatusThread(QObject *parent = nullptr)
: QThread(parent),
m_gnupgHomePath(Kleo::gnupgHomeDirectory()),
m_transactions(1, updateTransaction) // force initial scan
{
connect(this, &ReaderStatusThread::oneTransactionFinished,
this, &ReaderStatusThread::slotOneTransactionFinished);
}
std::vector<std::shared_ptr<Card> > cardInfos() const
{
const QMutexLocker locker(&m_mutex);
return m_cardInfos;
}
Card::Status cardStatus(unsigned int slot) const
{
const QMutexLocker locker(&m_mutex);
if (slot < m_cardInfos.size()) {
return m_cardInfos[slot]->status();
} else {
return Card::NoCard;
}
}
void addTransaction(const Transaction &t)
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_back(t);
m_waitForTransactions.wakeOne();
}
Q_SIGNALS:
void anyCardHasNullPinChanged(bool);
void anyCardCanLearnKeysChanged(bool);
void cardChanged(unsigned int);
void oneTransactionFinished(GpgME::Error err);
public Q_SLOTS:
void ping()
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()";
addTransaction(updateTransaction);
}
void stop()
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_front(quitTransaction);
m_waitForTransactions.wakeOne();
}
private Q_SLOTS:
void slotOneTransactionFinished(GpgME::Error err)
{
std::list<Transaction> ft;
KDAB_SYNCHRONIZED(m_mutex)
ft.splice(ft.begin(), m_finishedTransactions);
Q_FOREACH (const Transaction &t, ft)
if (t.receiver && t.slot && *t.slot) {
QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, err));
}
}
private:
void run() override {
while (true) {
std::shared_ptr<Context> gpgAgent;
QByteArray command;
bool nullSlot = false;
std::list<Transaction> item;
std::vector<std::shared_ptr<Card> > oldCards;
Error err;
std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
if (err.code() == GPG_ERR_NOT_SUPPORTED) {
return;
}
gpgAgent = std::shared_ptr<Context>(c.release());
KDAB_SYNCHRONIZED(m_mutex) {
while (m_transactions.empty()) {
// go to sleep waiting for more work:
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: waiting for commands";
m_waitForTransactions.wait(&m_mutex);
}
// splice off the first transaction without
// copying, so we own it without really importing
// it into this thread (the QPointer isn't
// thread-safe):
item.splice(item.end(),
m_transactions, m_transactions.begin());
// make local copies of the interesting stuff so
// we can release the mutex again:
command = item.front().command;
nullSlot = !item.front().slot;
oldCards = m_cardInfos;
}
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot;
// now, let's see what we got:
if (nullSlot && command == quitTransaction.command) {
return; // quit
}
if ((nullSlot && command == updateTransaction.command)) {
std::vector<std::shared_ptr<Card> > newCards = update_cardinfo(gpgAgent);
newCards.resize(std::max(newCards.size(), oldCards.size()));
oldCards.resize(std::max(newCards.size(), oldCards.size()));
KDAB_SYNCHRONIZED(m_mutex)
m_cardInfos = newCards;
std::vector<std::shared_ptr<Card> >::const_iterator
nit = newCards.begin(), nend = newCards.end(),
oit = oldCards.begin(), oend = oldCards.end();
unsigned int idx = 0;
bool anyLC = false;
bool anyNP = false;
bool anyError = false;
while (nit != nend && oit != oend) {
const auto optr = (*oit).get();
const auto nptr = (*nit).get();
if ((optr && !nptr) || (!optr && nptr) || (optr && nptr && *optr != *nptr)) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: slot" << idx << ": card Changed";
Q_EMIT cardChanged(idx);
}
if ((*nit)->canLearnKeys()) {
anyLC = true;
}
if ((*nit)->hasNullPin()) {
anyNP = true;
}
if ((*nit)->status() == Card::CardError) {
anyError = true;
}
++nit;
++oit;
++idx;
}
Q_EMIT anyCardHasNullPinChanged(anyNP);
Q_EMIT anyCardCanLearnKeysChanged(anyLC);
if (anyError) {
gpgAgent.reset();
}
} else {
GpgME::Error err;
(void)gpgagent_transact(gpgAgent, command.constData(), err);
KDAB_SYNCHRONIZED(m_mutex)
// splice 'item' into m_finishedTransactions:
m_finishedTransactions.splice(m_finishedTransactions.end(), item);
Q_EMIT oneTransactionFinished(err);
}
}
}
private:
mutable QMutex m_mutex;
QWaitCondition m_waitForTransactions;
const QString m_gnupgHomePath;
// protected by m_mutex:
std::vector<std::shared_ptr<Card> > m_cardInfos;
std::list<Transaction> m_transactions, m_finishedTransactions;
};
}
class ReaderStatus::Private : ReaderStatusThread
{
friend class Kleo::SmartCard::ReaderStatus;
ReaderStatus *const q;
public:
explicit Private(ReaderStatus *qq)
: ReaderStatusThread(qq),
q(qq),
watcher()
{
KDAB_SET_OBJECT_NAME(watcher);
qRegisterMetaType<Card::Status>("Kleo::SmartCard::Card::Status");
qRegisterMetaType<GpgME::Error>("GpgME::Error");
watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status")));
watcher.addPath(Kleo::gnupgHomeDirectory());
watcher.setDelay(100);
connect(this, &::ReaderStatusThread::cardChanged,
q, &ReaderStatus::cardChanged);
connect(this, &::ReaderStatusThread::anyCardHasNullPinChanged,
q, &ReaderStatus::anyCardHasNullPinChanged);
connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged,
q, &ReaderStatus::anyCardCanLearnKeysChanged);
connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::ping);
}
~Private()
{
stop();
if (!wait(100)) {
terminate();
wait();
}
}
private:
bool anyCardHasNullPinImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->hasNullPin(); });
}
bool anyCardCanLearnKeysImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->canLearnKeys(); });
}
private:
FileSystemWatcher watcher;
};
ReaderStatus::ReaderStatus(QObject *parent)
: QObject(parent), d(new Private(this))
{
self = this;
}
ReaderStatus::~ReaderStatus()
{
self = nullptr;
}
// slot
void ReaderStatus::startMonitoring()
{
d->start();
}
// static
ReaderStatus *ReaderStatus::mutableInstance()
{
return self;
}
// static
const ReaderStatus *ReaderStatus::instance()
{
return self;
}
Card::Status ReaderStatus::cardStatus(unsigned int slot) const
{
return d->cardStatus(slot);
}
bool ReaderStatus::anyCardHasNullPin() const
{
return d->anyCardHasNullPinImpl();
}
bool ReaderStatus::anyCardCanLearnKeys() const
{
return d->anyCardCanLearnKeysImpl();
}
std::vector<Card::PinState> ReaderStatus::pinStates(unsigned int slot) const
{
const auto ci = d->cardInfos();
if (slot < ci.size()) {
return ci[slot]->pinStates();
} else {
return std::vector<Card::PinState>();
}
}
void ReaderStatus::startSimpleTransaction(const QByteArray &command, QObject *receiver, const char *slot)
{
const Transaction t = { command, receiver, slot };
d->addTransaction(t);
}
void ReaderStatus::updateStatus()
{
d->ping();
}
std::vector <std::shared_ptr<Card> > ReaderStatus::getCards() const
{
return d->cardInfos();
}
#include "readerstatus.moc"
diff --git a/src/utils/pimpl_ptr.h b/src/utils/pimpl_ptr.h
index 2d49e0237..80fc09f09 100644
--- a/src/utils/pimpl_ptr.h
+++ b/src/utils/pimpl_ptr.h
@@ -1,86 +1,86 @@
/****************************************************************************
** Copyright (C) 2001-2007 Klarälvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Tools library.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid commercial KD Tools licenses may use this file in
** accordance with the KD Tools Commercial License Agreement provided with
** the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/
#ifndef __KDTOOLSCORE__PIMPL_PTR_H__
#define __KDTOOLSCORE__PIMPL_PTR_H__
#include <utils/kdtoolsglobal.h>
namespace kdtools
{
template <typename T>
class pimpl_ptr
{
KDAB_DISABLE_COPY(pimpl_ptr);
T *d;
public:
pimpl_ptr() : d(new T) {}
explicit pimpl_ptr(T *t) : d(t) {}
~pimpl_ptr()
{
delete d;
- d = 0;
+ d = nullptr;
}
T *get()
{
return d;
}
const T *get() const
{
return d;
}
T *operator->()
{
return get();
}
const T *operator->() const
{
return get();
}
T &operator*()
{
return *get();
}
const T &operator*() const
{
return *get();
}
KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR(get())
};
// these are not implemented, so's we can catch their use at
// link-time. Leaving them undeclared would open up a comparison
// via operator unspecified-bool-type().
template <typename T, typename S>
void operator==(const pimpl_ptr<T> &, const pimpl_ptr<S> &);
template <typename T, typename S>
void operator!=(const pimpl_ptr<T> &, const pimpl_ptr<S> &);
} // namespace kdtools
#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 20, 11:37 PM (1 d, 15 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a7/cb/caffb1c241feb41ae72b43a83c6d

Event Timeline