Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 52fe5f16b..d13b18925 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,394 +1,395 @@
add_subdirectory(icons)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if (NOT DISABLE_KWATCHGNUPG)
add_subdirectory(kwatchgnupg)
endif()
add_subdirectory(libkleopatraclient)
add_subdirectory(conf)
add_subdirectory(kconf_update)
if(WIN32)
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp)
set(_kleopatra_extra_SRCS
utils/gnupg-registry.c
selftest/registrycheck.cpp
utils/windowsprocessdevice.cpp
utils/userinfo_win.cpp
)
else()
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp)
set(_kleopatra_extra_SRCS)
endif()
set(_kleopatra_uiserver_SRCS
uiserver/sessiondata.cpp
uiserver/uiserver.cpp
${_kleopatra_extra_uiserver_SRCS}
uiserver/assuanserverconnection.cpp
uiserver/echocommand.cpp
uiserver/decryptverifycommandemailbase.cpp
uiserver/decryptverifycommandfilesbase.cpp
uiserver/signcommand.cpp
uiserver/signencryptfilescommand.cpp
uiserver/prepencryptcommand.cpp
uiserver/prepsigncommand.cpp
uiserver/encryptcommand.cpp
uiserver/selectcertificatecommand.cpp
uiserver/importfilescommand.cpp
uiserver/createchecksumscommand.cpp
uiserver/verifychecksumscommand.cpp
selftest/uiservercheck.cpp
)
if(ASSUAN2_FOUND)
include_directories(${ASSUAN2_INCLUDES})
set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES})
else()
include_directories(${ASSUAN_INCLUDES})
if(WIN32)
set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES})
else()
set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES})
endif()
endif()
if(HAVE_GPG_ERR_SOURCE_KLEO)
add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
else()
add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
endif()
ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
if("${Gpgmepp_VERSION}" VERSION_GREATER_EQUAL "1.14.1")
set(_kleopatra_deviceinfowatcher_files
smartcard/deviceinfowatcher.cpp
)
else()
set(_kleopatra_deviceinfowatcher_files)
endif()
set(_kleopatra_SRCS
utils/gui-helper.cpp
utils/filedialog.cpp
utils/kdpipeiodevice.cpp
utils/headerview.cpp
utils/scrollarea.cpp
utils/dragqueen.cpp
utils/multivalidator.cpp
utils/systemtrayicon.cpp
utils/hex.cpp
utils/path-helper.cpp
utils/input.cpp
utils/output.cpp
utils/validation.cpp
utils/wsastarter.cpp
utils/iodevicelogger.cpp
utils/log.cpp
utils/action_data.cpp
utils/types.cpp
utils/archivedefinition.cpp
utils/auditlog.cpp
utils/clipboardmenu.cpp
utils/kuniqueservice.cpp
utils/tags.cpp
utils/writecertassuantransaction.cpp
utils/keyparameters.cpp
utils/userinfo.cpp
selftest/selftest.cpp
selftest/enginecheck.cpp
selftest/gpgconfcheck.cpp
selftest/gpgagentcheck.cpp
selftest/libkleopatrarccheck.cpp
selftest/compliancecheck.cpp
${_kleopatra_extra_SRCS}
view/htmllabel.cpp
view/keylistcontroller.cpp
view/keytreeview.cpp
view/searchbar.cpp
view/smartcardwidget.cpp
view/openpgpkeycardwidget.cpp
view/padwidget.cpp
view/pgpcardwidget.cpp
view/pivcardwidget.cpp
view/p15cardwidget.cpp
view/netkeywidget.cpp
view/nullpinwidget.cpp
view/tabwidget.cpp
view/keycacheoverlay.cpp
view/urllabel.cpp
view/waitwidget.cpp
view/welcomewidget.cpp
dialogs/certificateselectiondialog.cpp
dialogs/certifywidget.cpp
dialogs/expirydialog.cpp
dialogs/lookupcertificatesdialog.cpp
dialogs/ownertrustdialog.cpp
dialogs/selftestdialog.cpp
dialogs/certifycertificatedialog.cpp
dialogs/revokecertificationwidget.cpp
dialogs/revokecertificationdialog.cpp
dialogs/adduseriddialog.cpp
dialogs/addemaildialog.cpp
dialogs/deletecertificatesdialog.cpp
dialogs/setinitialpindialog.cpp
dialogs/certificatedetailsdialog.cpp
dialogs/certificatedetailswidget.cpp
dialogs/trustchainwidget.cpp
dialogs/weboftrustwidget.cpp
dialogs/weboftrustdialog.cpp
dialogs/exportdialog.cpp
dialogs/subkeyswidget.cpp
dialogs/gencardkeydialog.cpp
dialogs/updatenotification.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/certificatedetailsinputwidget.cpp
dialogs/createcsrforcardkeydialog.cpp
dialogs/groupdetailsdialog.cpp
dialogs/editgroupdialog.cpp
crypto/controller.cpp
crypto/certificateresolver.cpp
crypto/sender.cpp
crypto/recipient.cpp
crypto/task.cpp
crypto/taskcollection.cpp
crypto/decryptverifytask.cpp
crypto/decryptverifyemailcontroller.cpp
crypto/decryptverifyfilescontroller.cpp
crypto/autodecryptverifyfilescontroller.cpp
crypto/encryptemailtask.cpp
crypto/encryptemailcontroller.cpp
crypto/newsignencryptemailcontroller.cpp
crypto/signencrypttask.cpp
crypto/signencryptfilescontroller.cpp
crypto/signemailtask.cpp
crypto/signemailcontroller.cpp
crypto/createchecksumscontroller.cpp
crypto/verifychecksumscontroller.cpp
crypto/gui/wizard.cpp
crypto/gui/wizardpage.cpp
crypto/gui/certificateselectionline.cpp
crypto/gui/certificatelineedit.cpp
crypto/gui/signingcertificateselectionwidget.cpp
crypto/gui/signingcertificateselectiondialog.cpp
crypto/gui/resultitemwidget.cpp
crypto/gui/resultlistwidget.cpp
crypto/gui/resultpage.cpp
crypto/gui/newresultpage.cpp
crypto/gui/signencryptfileswizard.cpp
crypto/gui/signencryptemailconflictdialog.cpp
crypto/gui/decryptverifyoperationwidget.cpp
crypto/gui/decryptverifyfileswizard.cpp
crypto/gui/decryptverifyfilesdialog.cpp
crypto/gui/objectspage.cpp
crypto/gui/resolverecipientspage.cpp
crypto/gui/signerresolvepage.cpp
crypto/gui/encryptemailwizard.cpp
crypto/gui/signemailwizard.cpp
crypto/gui/signencryptwidget.cpp
crypto/gui/signencryptwizard.cpp
crypto/gui/unknownrecipientwidget.cpp
crypto/gui/verifychecksumsdialog.cpp
commands/command.cpp
commands/gnupgprocesscommand.cpp
commands/detailscommand.cpp
commands/exportcertificatecommand.cpp
+ commands/exportgroupscommand.cpp
commands/importcertificatescommand.cpp
commands/importcertificatefromfilecommand.cpp
commands/importcertificatefromclipboardcommand.cpp
commands/importcertificatefromdatacommand.cpp
commands/lookupcertificatescommand.cpp
commands/reloadkeyscommand.cpp
commands/refreshx509certscommand.cpp
commands/refreshopenpgpcertscommand.cpp
commands/deletecertificatescommand.cpp
commands/decryptverifyfilescommand.cpp
commands/signencryptfilescommand.cpp
commands/signencryptfoldercommand.cpp
commands/encryptclipboardcommand.cpp
commands/signclipboardcommand.cpp
commands/decryptverifyclipboardcommand.cpp
commands/clearcrlcachecommand.cpp
commands/dumpcrlcachecommand.cpp
commands/dumpcertificatecommand.cpp
commands/importcrlcommand.cpp
commands/changeexpirycommand.cpp
commands/changeownertrustcommand.cpp
commands/changeroottrustcommand.cpp
commands/changepassphrasecommand.cpp
commands/certifycertificatecommand.cpp
commands/revokecertificationcommand.cpp
commands/selftestcommand.cpp
commands/exportsecretkeycommand.cpp
commands/exportopenpgpcertstoservercommand.cpp
commands/adduseridcommand.cpp
commands/newcertificatecommand.cpp
commands/setinitialpincommand.cpp
commands/learncardkeyscommand.cpp
commands/checksumcreatefilescommand.cpp
commands/checksumverifyfilescommand.cpp
commands/exportpaperkeycommand.cpp
commands/importpaperkeycommand.cpp
commands/genrevokecommand.cpp
commands/keytocardcommand.cpp
commands/cardcommand.cpp
commands/pivgeneratecardkeycommand.cpp
commands/changepincommand.cpp
commands/authenticatepivcardapplicationcommand.cpp
commands/setpivcardapplicationadministrationkeycommand.cpp
commands/certificatetopivcardcommand.cpp
commands/importcertificatefrompivcardcommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createcsrforcardkeycommand.cpp
commands/listreaderscommand.cpp
${_kleopatra_uiserver_files}
conf/configuredialog.cpp
conf/groupsconfigdialog.cpp
conf/groupsconfigpage.cpp
conf/groupsconfigwidget.cpp
newcertificatewizard/listwidget.cpp
newcertificatewizard/newcertificatewizard.cpp
smartcard/readerstatus.cpp
smartcard/card.cpp
smartcard/openpgpcard.cpp
smartcard/netkeycard.cpp
smartcard/pivcard.cpp
smartcard/p15card.cpp
smartcard/keypairinfo.cpp
smartcard/utils.cpp
${_kleopatra_deviceinfowatcher_files}
accessibility/accessiblerichtextlabel.cpp
accessibility/accessiblewidgetfactory.cpp
aboutdata.cpp
systrayicon.cpp
kleopatraapplication.cpp
mainwindow.cpp
main.cpp
kleopatra.qrc
)
if(WIN32)
configure_file (versioninfo.rc.in versioninfo.rc)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS})
endif()
set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS})
ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra
DESCRIPTION "kleopatra (kleopatra)"
OLD_CATEGORY_NAMES log_kleopatra
EXPORT KLEOPATRA
)
if(KLEO_MODEL_TEST)
add_definitions(-DKLEO_MODEL_TEST)
set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp)
endif()
ki18n_wrap_ui(_kleopatra_SRCS
dialogs/lookupcertificatesdialog.ui
dialogs/ownertrustdialog.ui
dialogs/selectchecklevelwidget.ui
dialogs/selftestdialog.ui
dialogs/adduseriddialog.ui
dialogs/setinitialpindialog.ui
dialogs/trustchainwidget.ui
dialogs/subkeyswidget.ui
newcertificatewizard/listwidget.ui
newcertificatewizard/chooseprotocolpage.ui
newcertificatewizard/enterdetailspage.ui
newcertificatewizard/keycreationpage.ui
newcertificatewizard/resultpage.ui
newcertificatewizard/advancedsettingsdialog.ui
)
kconfig_add_kcfg_files(_kleopatra_SRCS
kcfg/tooltippreferences.kcfgc
kcfg/emailoperationspreferences.kcfgc
kcfg/fileoperationspreferences.kcfgc
kcfg/smimevalidationpreferences.kcfgc
kcfg/tagspreferences.kcfgc
kcfg/settings.kcfgc
)
file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png")
ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS})
add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS})
# For the ConfigureDialog & KCMs
target_link_libraries(kleopatra_bin kcm_kleopatra_static)
#if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
# set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON)
#endif()
set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra)
if (WIN32)
set(_kleopatra_platform_libs "secur32")
endif ()
target_link_libraries(kleopatra_bin
Gpgmepp
QGpgme
${_kleopatra_extra_libs}
KF5::Libkleo
KF5::Mime
KF5::I18n
KF5::XmlGui
KF5::IconThemes
KF5::WindowSystem
KF5::CoreAddons
KF5::ItemModels
KF5::Crash
Qt::Network
Qt::PrintSupport # Printing secret keys
${_kleopatra_uiserver_extra_libs}
${_kleopatra_dbusaddons_libs}
kleopatraclientcore
${_kleopatra_platform_libs}
)
install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(
PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop
DESTINATION ${KDE_INSTALL_APPDIR}
)
install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
install(
PROGRAMS data/kleopatra_signencryptfiles.desktop
data/kleopatra_signencryptfolders.desktop
data/kleopatra_decryptverifyfiles.desktop
data/kleopatra_decryptverifyfolders.desktop
DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus
)
diff --git a/src/commands/exportgroupscommand.cpp b/src/commands/exportgroupscommand.cpp
new file mode 100644
index 000000000..4ba8bb0e3
--- /dev/null
+++ b/src/commands/exportgroupscommand.cpp
@@ -0,0 +1,300 @@
+/* -*- mode: c++; c-basic-offset:4 -*-
+ exportgroupscommand.cpp
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include <config-kleopatra.h>
+
+#include "exportgroupscommand.h"
+#include "command_p.h"
+
+#include "utils/filedialog.h"
+
+#include <Libkleo/Algorithm>
+#include <Libkleo/KeyGroup>
+#include <Libkleo/KeyGroupConfig>
+#include <Libkleo/KeyHelpers>
+
+#include <KConfigGroup>
+#include <KLocalizedString>
+#include <KSharedConfig>
+
+#include <QGpgME/Protocol>
+#include <QGpgME/ExportJob>
+
+#include <QFileInfo>
+#include <QStandardPaths>
+
+#include <memory>
+#include <vector>
+
+using namespace Kleo;
+using namespace GpgME;
+using namespace QGpgME;
+
+namespace
+{
+
+static const QString certificateGroupFileExtension{QLatin1String{".kgrp"}};
+
+QString getLastUsedExportDirectory()
+{
+ KConfigGroup config{KSharedConfig::openConfig(), "ExportDialog"};
+ return config.readEntry("LastDirectory", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
+}
+
+void updateLastUsedExportDirectory(const QString &path)
+{
+ KConfigGroup config{KSharedConfig::openConfig(), "ExportDialog"};
+ config.writeEntry("LastDirectory", QFileInfo{path}.absolutePath());
+}
+
+QString proposeFilename(const std::vector<KeyGroup> &groups)
+{
+ QString filename;
+
+ filename = getLastUsedExportDirectory() + QLatin1Char{'/'};
+ if (groups.size() == 1) {
+ filename += groups.front().name().replace(QLatin1Char{'/'}, QLatin1Char{'_'});
+ } else {
+ filename += i18nc("A generic filename for exported certificate groups", "certificate groups");
+ }
+
+ return filename + certificateGroupFileExtension;
+}
+
+QString requestFilename(QWidget *parent, const std::vector<KeyGroup> &groups)
+{
+ const QString proposedFilename = proposeFilename(groups);
+
+ auto filename = FileDialog::getSaveFileNameEx(
+ parent,
+ i18ncp("@title:window", "Export Certificate Group", "Export Certificate Groups", groups.size()),
+ QStringLiteral("imp"),
+ proposedFilename,
+ i18nc("filename filter like Certificate Groups (*.foo)", "Certificate Groups (*%1)", certificateGroupFileExtension));
+ if (!filename.isEmpty()) {
+ const QFileInfo fi{filename};
+ if (fi.suffix().isEmpty()) {
+ filename += certificateGroupFileExtension;
+ }
+ updateLastUsedExportDirectory(filename);
+ }
+
+ return filename;
+}
+
+}
+
+class ExportGroupsCommand::Private : public Command::Private
+{
+ friend class ::ExportGroupsCommand;
+ ExportGroupsCommand *q_func() const
+ {
+ return static_cast<ExportGroupsCommand *>(q);
+ }
+public:
+ explicit Private(ExportGroupsCommand *qq);
+ ~Private() override;
+
+ void start();
+
+ void exportGroups();
+ bool startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys);
+
+ void onExportJobResult(const QGpgME::Job *job, const GpgME::Error &err, const QByteArray &keyData);
+
+ void cancelJobs();
+ void showError(const GpgME::Error &err);
+
+ void finishedIfLastJob();
+
+private:
+ std::vector<KeyGroup> groups;
+ QString filename;
+ std::vector<QPointer<QGpgME::Job>> exportJobs;
+};
+
+ExportGroupsCommand::Private *ExportGroupsCommand::d_func()
+{
+ return static_cast<Private *>(d.get());
+}
+const ExportGroupsCommand::Private *ExportGroupsCommand::d_func() const
+{
+ return static_cast<const Private *>(d.get());
+}
+
+#define d d_func()
+#define q q_func()
+
+ExportGroupsCommand::Private::Private(ExportGroupsCommand *qq)
+ : Command::Private(qq)
+{
+}
+
+ExportGroupsCommand::Private::~Private() = default;
+
+void ExportGroupsCommand::Private::start()
+{
+ if (groups.empty()) {
+ finished();
+ return;
+ }
+
+ filename = requestFilename(parentWidgetOrView(), groups);
+ if (filename.isEmpty()) {
+ canceled();
+ return;
+ }
+
+ const auto groupKeys = std::accumulate(std::begin(groups), std::end(groups),
+ KeyGroup::Keys{},
+ [](auto &allKeys, const auto &group) {
+ const auto keys = group.keys();
+ allKeys.insert(std::begin(keys), std::end(keys));
+ return allKeys;
+ });
+
+ std::vector<Key> openpgpKeys;
+ std::vector<Key> cmsKeys;
+ std::partition_copy(std::begin(groupKeys), std::end(groupKeys),
+ std::back_inserter(openpgpKeys),
+ std::back_inserter(cmsKeys),
+ [](const GpgME::Key &key) {
+ return key.protocol() == GpgME::OpenPGP;
+ });
+
+ // remove/overwrite existing file
+ if (QFile::exists(filename) && !QFile::remove(filename)) {
+ error(xi18n("Cannot overwrite existing <filename>%1</filename>.", filename),
+ i18nc("@title:window", "Export Failed"));
+ finished();
+ return;
+ }
+ exportGroups();
+ if (!openpgpKeys.empty()) {
+ if (!startExportJob(GpgME::OpenPGP, openpgpKeys)) {
+ finished();
+ return;
+ }
+ }
+ if (!cmsKeys.empty()) {
+ if (!startExportJob(GpgME::CMS, cmsKeys)) {
+ finishedIfLastJob();
+ }
+ }
+}
+
+void ExportGroupsCommand::Private::exportGroups()
+{
+ KeyGroupConfig config{filename};
+ config.writeGroups(groups);
+}
+
+bool ExportGroupsCommand::Private::startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys)
+{
+ const QGpgME::Protocol *const backend = (protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
+ Q_ASSERT(backend);
+ std::unique_ptr<ExportJob> jobOwner(backend->publicKeyExportJob(/*armor=*/ true));
+ auto job = jobOwner.get();
+ Q_ASSERT(job);
+
+ connect(job, &ExportJob::result,
+ q, [this, job](const GpgME::Error &err, const QByteArray &keyData) {
+ onExportJobResult(job, err, keyData);
+ });
+ connect(job, &Job::progress,
+ q, &Command::progress);
+
+ const GpgME::Error err = job->start(Kleo::getFingerprints(keys));
+ if (err) {
+ showError(err);
+ return false;
+ }
+ Q_EMIT q->info(i18n("Exporting certificate groups..."));
+
+ exportJobs.push_back(jobOwner.release());
+ return true;
+}
+
+void ExportGroupsCommand::Private::onExportJobResult(const QGpgME::Job *job, const GpgME::Error &err, const QByteArray &keyData)
+{
+ Q_ASSERT(Kleo::contains(exportJobs, job));
+ exportJobs.erase(std::remove(exportJobs.begin(), exportJobs.end(), job), exportJobs.end());
+
+ if (err) {
+ showError(err);
+ finishedIfLastJob();
+ return;
+ }
+
+ QFile f{filename};
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Append)) {
+ error(xi18n("Cannot open file <filename>%1</filename> for writing.", filename),
+ i18nc("@title:window", "Export Failed"));
+ finishedIfLastJob();
+ return;
+ }
+
+ const auto bytesWritten = f.write(keyData);
+ if (bytesWritten != keyData.size()) {
+ error(xi18n("Writing certificates to file <filename>%1</filename> failed.", filename),
+ i18nc("@title:window", "Export Failed"));
+ }
+
+ finishedIfLastJob();
+}
+
+void ExportGroupsCommand::Private::showError(const GpgME::Error &err)
+{
+ error(xi18n("<para>An error occurred during the export:</para>"
+ "<para><message>%1</message></para>",
+ QString::fromLocal8Bit(err.asString())),
+ i18nc("@title:window", "Export Failed"));
+}
+
+void ExportGroupsCommand::Private::finishedIfLastJob()
+{
+ if (exportJobs.size() == 0) {
+ finished();
+ }
+}
+
+void ExportGroupsCommand::Private::cancelJobs()
+{
+ std::for_each(std::cbegin(exportJobs), std::cend(exportJobs),
+ [](const auto &job) {
+ if (job) {
+ job->slotCancel();
+ }
+ });
+ exportJobs.clear();
+}
+
+ExportGroupsCommand::ExportGroupsCommand(const std::vector<KeyGroup> &groups)
+ : Command{new Private{this}}
+{
+ d->groups = groups;
+}
+
+ExportGroupsCommand::~ExportGroupsCommand() = default;
+
+void ExportGroupsCommand::doStart()
+{
+ d->start();
+}
+
+void ExportGroupsCommand::doCancel()
+{
+ d->cancelJobs();
+}
+
+#undef d
+#undef q
+
+#include "moc_exportgroupscommand.cpp"
diff --git a/src/commands/exportgroupscommand.h b/src/commands/exportgroupscommand.h
new file mode 100644
index 000000000..513c75c0e
--- /dev/null
+++ b/src/commands/exportgroupscommand.h
@@ -0,0 +1,35 @@
+/* -*- mode: c++; c-basic-offset:4 -*-
+ exportgroupscommand.h
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#pragma once
+
+#include "command.h"
+
+namespace Kleo
+{
+class KeyGroup;
+
+class ExportGroupsCommand : public Command
+{
+ Q_OBJECT
+public:
+ explicit ExportGroupsCommand(const std::vector<KeyGroup> &groups);
+ ~ExportGroupsCommand() override;
+
+private:
+ void doStart() override;
+ void doCancel() override;
+
+private:
+ class Private;
+ inline Private *d_func();
+ inline const Private *d_func() const;
+};
+}
diff --git a/src/commands/importcertificatefromfilecommand.cpp b/src/commands/importcertificatefromfilecommand.cpp
index e7e3f6dbe..d9384795c 100644
--- a/src/commands/importcertificatefromfilecommand.cpp
+++ b/src/commands/importcertificatefromfilecommand.cpp
@@ -1,170 +1,170 @@
/* -*- mode: c++; c-basic-offset:4 -*-
importcertificatefromfilecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "importcertificatefromfilecommand.h"
#include "importcertificatescommand_p.h"
#include "utils/filedialog.h"
#include <QGpgME/Protocol>
#include <QGpgME/ImportJob>
#include <Libkleo/Classify>
#include <gpgme++/global.h>
#include <gpgme++/importresult.h>
#include <KLocalizedString>
#include <KConfigGroup>
#include <QFile>
#include <QString>
#include <QWidget>
#include <QFileInfo>
#include <QDir>
#include <KSharedConfig>
#include <memory>
using namespace GpgME;
using namespace Kleo;
using namespace QGpgME;
class ImportCertificateFromFileCommand::Private : public ImportCertificatesCommand::Private
{
friend class ::ImportCertificateFromFileCommand;
ImportCertificateFromFileCommand *q_func() const
{
return static_cast<ImportCertificateFromFileCommand *>(q);
}
public:
explicit Private(ImportCertificateFromFileCommand *qq, KeyListController *c);
~Private() override;
bool ensureHaveFile();
private:
QStringList files;
};
ImportCertificateFromFileCommand::Private *ImportCertificateFromFileCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ImportCertificateFromFileCommand::Private *ImportCertificateFromFileCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
ImportCertificateFromFileCommand::Private::Private(ImportCertificateFromFileCommand *qq, KeyListController *c)
: ImportCertificatesCommand::Private(qq, c),
files()
{
}
ImportCertificateFromFileCommand::Private::~Private() {}
#define d d_func()
#define q q_func()
ImportCertificateFromFileCommand::ImportCertificateFromFileCommand()
: ImportCertificatesCommand(new Private(this, nullptr))
{
}
ImportCertificateFromFileCommand::ImportCertificateFromFileCommand(KeyListController *p)
: ImportCertificatesCommand(new Private(this, p))
{
}
ImportCertificateFromFileCommand::ImportCertificateFromFileCommand(QAbstractItemView *v, KeyListController *p)
: ImportCertificatesCommand(v, new Private(this, p))
{
}
ImportCertificateFromFileCommand::ImportCertificateFromFileCommand(const QStringList &files, KeyListController *p)
: ImportCertificatesCommand(new Private(this, p))
{
d->files = files;
}
ImportCertificateFromFileCommand::ImportCertificateFromFileCommand(const QStringList &files, QAbstractItemView *v, KeyListController *p)
: ImportCertificatesCommand(v, new Private(this, p))
{
d->files = files;
}
ImportCertificateFromFileCommand::~ImportCertificateFromFileCommand() {}
void ImportCertificateFromFileCommand::setFiles(const QStringList &files)
{
d->files = files;
}
void ImportCertificateFromFileCommand::doStart()
{
if (!d->ensureHaveFile()) {
Q_EMIT canceled();
d->finished();
return;
}
//TODO: use KIO here
d->setWaitForMoreJobs(true);
for (const QString &fn : std::as_const(d->files)) {
QFile in(fn);
if (!in.open(QIODevice::ReadOnly)) {
d->error(i18n("Could not open file %1 for reading: %2", in.fileName(), in.errorString()), i18n("Certificate Import Failed"));
d->importResult({fn, GpgME::UnknownProtocol, ImportType::Local, ImportResult{}});
continue;
}
const auto data = in.readAll();
d->startImport(GpgME::OpenPGP, data, fn);
d->startImport(GpgME::CMS, data, fn);
d->importGroupsFromFile(fn);
}
d->setWaitForMoreJobs(false);
}
static QStringList get_file_name(QWidget *parent)
{
- const QString certificateFilter = i18n("Certificates") + QLatin1String(" (*.asc *.cer *.cert *.crt *.der *.pem *.gpg *.p7c *.p12 *.pfx *.pgp)");
+ const QString certificateFilter = i18n("Certificates") + QLatin1String(" (*.asc *.cer *.cert *.crt *.der *.pem *.gpg *.p7c *.p12 *.pfx *.pgp *.kgrp)");
const QString anyFilesFilter = i18n("Any files") + QLatin1String(" (*)");
QString previousDir;
if (const KSharedConfig::Ptr config = KSharedConfig::openConfig()) {
const KConfigGroup group(config, "Import Certificate");
previousDir = group.readPathEntry("last-open-file-directory", QDir::homePath());
}
const QStringList files = Kleo::FileDialog::getOpenFileNames(parent, i18n("Select Certificate File"), previousDir, certificateFilter + QLatin1String(";;") + anyFilesFilter);
if (!files.empty())
if (const KSharedConfig::Ptr config = KSharedConfig::openConfig()) {
KConfigGroup group(config, "Import Certificate");
group.writePathEntry("last-open-file-directory", QFileInfo(files.front()).path());
}
return files;
}
bool ImportCertificateFromFileCommand::Private::ensureHaveFile()
{
if (files.empty()) {
files = get_file_name(parentWidgetOrView());
}
return !files.empty();
}
#undef d
#undef q
diff --git a/src/conf/groupsconfigwidget.cpp b/src/conf/groupsconfigwidget.cpp
index eaf98df3b..2b56eb218 100644
--- a/src/conf/groupsconfigwidget.cpp
+++ b/src/conf/groupsconfigwidget.cpp
@@ -1,278 +1,304 @@
/*
conf/groupsconfigwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "groupsconfigwidget.h"
+#include "commands/exportgroupscommand.h"
#include "dialogs/editgroupdialog.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <KLocalizedString>
#include <KRandom>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
Q_DECLARE_METATYPE(KeyGroup)
namespace
{
class ProxyModel : public AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent = nullptr)
: AbstractKeyListSortFilterProxyModel(parent)
{
}
~ProxyModel() override = default;
ProxyModel *clone() const override
{
// compiler-generated copy ctor is fine!
return new ProxyModel(*this);
}
};
}
class GroupsConfigWidget::Private
{
friend class ::Kleo::GroupsConfigWidget;
GroupsConfigWidget *const q;
struct {
QLineEdit *groupsFilter = nullptr;
QListView *groupsList = nullptr;
QPushButton *newButton = nullptr;
QPushButton *editButton = nullptr;
QPushButton *deleteButton = nullptr;
+ QPushButton *exportButton = nullptr;
} ui;
AbstractKeyListModel *groupsModel = nullptr;
ProxyModel *groupsFilterModel = nullptr;
public:
Private(GroupsConfigWidget *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
auto groupsLayout = new QGridLayout();
groupsLayout->setColumnStretch(0, 1);
groupsLayout->setRowStretch(1, 1);
ui.groupsFilter = new QLineEdit();
ui.groupsFilter->setClearButtonEnabled(true);
ui.groupsFilter->setPlaceholderText(i18nc("Placeholder text", "Search..."));
groupsLayout->addWidget(ui.groupsFilter, 0, 0);
groupsModel = AbstractKeyListModel::createFlatKeyListModel(q);
groupsFilterModel = new ProxyModel(q);
groupsFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setFilterKeyColumn(KeyList::Summary);
groupsFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setSourceModel(groupsModel);
groupsFilterModel->sort(KeyList::Summary, Qt::AscendingOrder);
ui.groupsList = new QListView();
ui.groupsList->setModel(groupsFilterModel);
ui.groupsList->setModelColumn(KeyList::Summary);
ui.groupsList->setSelectionBehavior(QAbstractItemView::SelectRows);
ui.groupsList->setSelectionMode(QAbstractItemView::SingleSelection);
groupsLayout->addWidget(ui.groupsList, 1, 0);
auto groupsButtonLayout = new QVBoxLayout();
ui.newButton = new QPushButton(i18n("New"));
groupsButtonLayout->addWidget(ui.newButton);
ui.editButton = new QPushButton(i18n("Edit"));
ui.editButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.editButton);
ui.deleteButton = new QPushButton(i18n("Delete"));
ui.deleteButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.deleteButton);
+ ui.exportButton = new QPushButton{i18nc("@action::button", "Export")};
+ ui.exportButton->setEnabled(false);
+ groupsButtonLayout->addWidget(ui.exportButton);
+
groupsButtonLayout->addStretch(1);
groupsLayout->addLayout(groupsButtonLayout, 1, 1);
mainLayout->addLayout(groupsLayout, /*stretch=*/ 1);
connect(ui.groupsFilter, &QLineEdit::textChanged, groupsFilterModel, &QSortFilterProxyModel::setFilterFixedString);
connect(ui.groupsList->selectionModel(), &QItemSelectionModel::selectionChanged,
q, [this] () { selectionChanged(); });
connect(ui.groupsList, &QListView::doubleClicked,
q, [this] (const QModelIndex &index) { editGroup(index); });
connect(ui.newButton, &QPushButton::clicked, q, [this] () { addGroup(); });
connect(ui.editButton, &QPushButton::clicked, q, [this] () { editGroup(); });
connect(ui.deleteButton, &QPushButton::clicked, q, [this] () { deleteGroup(); });
+ connect(ui.exportButton, &QPushButton::clicked, q, [this] () { exportGroup(); });
}
~Private()
{
}
private:
QModelIndex selectedIndex()
{
const QModelIndexList selected = ui.groupsList->selectionModel()->selectedRows();
return selected.empty() ? QModelIndex() : selected[0];
}
KeyGroup getGroup(const QModelIndex &index)
{
return index.isValid() ? ui.groupsList->model()->data(index, KeyList::GroupRole).value<KeyGroup>() : KeyGroup();
}
void selectionChanged()
{
const KeyGroup selectedGroup = getGroup(selectedIndex());
const bool selectedGroupIsEditable = !selectedGroup.isNull() && !selectedGroup.isImmutable();
ui.editButton->setEnabled(selectedGroupIsEditable);
ui.deleteButton->setEnabled(selectedGroupIsEditable);
+ ui.exportButton->setEnabled(!selectedGroup.isNull());
}
KeyGroup showEditGroupDialog(KeyGroup group, const QString &windowTitle, EditGroupDialog::FocusWidget focusWidget)
{
auto dialog = std::make_unique<EditGroupDialog>(q);
dialog->setWindowTitle(windowTitle);
dialog->setGroupName(group.name());
const KeyGroup::Keys &keys = group.keys();
dialog->setGroupKeys(std::vector<GpgME::Key>(keys.cbegin(), keys.cend()));
dialog->setInitialFocus(focusWidget);
const int result = dialog->exec();
if (result == QDialog::Rejected) {
return KeyGroup();
}
group.setName(dialog->groupName());
group.setKeys(dialog->groupKeys());
return group;
}
void addGroup()
{
const KeyGroup::Id newId = KRandom::randomString(8);
KeyGroup group = KeyGroup(newId, i18nc("default name for new group of keys", "New Group"), {}, KeyGroup::ApplicationConfig);
group.setIsImmutable(false);
const KeyGroup newGroup = showEditGroupDialog(
group, i18nc("@title:window a group of keys", "New Group"), EditGroupDialog::GroupName);
if (newGroup.isNull()) {
return;
}
const QModelIndex newIndex = groupsModel->addGroup(newGroup);
if (!newIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "Adding group to model failed";
return;
}
Q_EMIT q->changed();
}
void editGroup(const QModelIndex &index = QModelIndex())
{
const QModelIndex groupIndex = index.isValid() ? index : selectedIndex();
if (!groupIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
const KeyGroup group = getGroup(groupIndex);
if (group.isNull()) {
qCDebug(KLEOPATRA_LOG) << "selected group is null";
return;
}
if (group.isImmutable()) {
qCDebug(KLEOPATRA_LOG) << "selected group is immutable";
return;
}
const KeyGroup updatedGroup = showEditGroupDialog(
group, i18nc("@title:window a group of keys", "Edit Group"), EditGroupDialog::KeysFilter);
if (updatedGroup.isNull()) {
return;
}
const bool success = ui.groupsList->model()->setData(groupIndex, QVariant::fromValue(updatedGroup));
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Updating group in model failed";
return;
}
Q_EMIT q->changed();
}
void deleteGroup()
{
const QModelIndex groupIndex = selectedIndex();
if (!groupIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
const KeyGroup group = getGroup(groupIndex);
if (group.isNull()) {
qCDebug(KLEOPATRA_LOG) << "selected group is null";
return;
}
const bool success = groupsModel->removeGroup(group);
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Removing group from model failed";
return;
}
Q_EMIT q->changed();
}
+
+ void exportGroup()
+ {
+ const QModelIndex groupIndex = selectedIndex();
+ if (!groupIndex.isValid()) {
+ qCDebug(KLEOPATRA_LOG) << "selection is empty";
+ return;
+ }
+ const KeyGroup group = getGroup(groupIndex);
+ if (group.isNull()) {
+ qCDebug(KLEOPATRA_LOG) << "selected group is null";
+ return;
+ }
+
+ // execute export group command
+ auto cmd = new ExportGroupsCommand({group});
+ cmd->start();
+ }
};
GroupsConfigWidget::GroupsConfigWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
GroupsConfigWidget::~GroupsConfigWidget() = default;
void GroupsConfigWidget::setGroups(const std::vector<KeyGroup> &groups)
{
d->groupsModel->setGroups(groups);
}
std::vector<KeyGroup> GroupsConfigWidget::groups() const
{
std::vector<KeyGroup> result;
result.reserve(d->groupsModel->rowCount());
for (int row = 0; row < d->groupsModel->rowCount(); ++row) {
const QModelIndex index = d->groupsModel->index(row, 0);
result.push_back(d->groupsModel->group(index));
}
return result;
}
#include "groupsconfigwidget.moc"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:52 PM (11 h, 7 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d8/4e/d543c9333a4364d535e7c07ca331

Event Timeline