Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623421
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
36 KB
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:52 PM (13 h, 40 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d8/4e/d543c9333a4364d535e7c07ca331
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment