Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5af84d156..d155a5ad8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,612 +1,614 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(icons)
add_subdirectory(mimetypes)
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
selftest/registrycheck.cpp selftest/registrycheck.h
utils/gnupg-registry.c
utils/userinfo_win.cpp
utils/windowsprocessdevice.cpp utils/windowsprocessdevice.h
versioninfo.rc
)
else()
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp)
set(_kleopatra_extra_SRCS)
endif()
set(_kleopatra_uiserver_SRCS
${_kleopatra_extra_uiserver_SRCS}
selftest/uiservercheck.cpp selftest/uiservercheck.h
uiserver/assuanserverconnection.cpp uiserver/assuanserverconnection.h
uiserver/createchecksumscommand.cpp uiserver/createchecksumscommand.h
uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandemailbase.h
uiserver/decryptverifycommandfilesbase.cpp uiserver/decryptverifycommandfilesbase.h
uiserver/echocommand.cpp uiserver/echocommand.h
uiserver/encryptcommand.cpp uiserver/encryptcommand.h
uiserver/importfilescommand.cpp uiserver/importfilescommand.h
uiserver/prepencryptcommand.cpp uiserver/prepencryptcommand.h
uiserver/prepsigncommand.cpp uiserver/prepsigncommand.h
uiserver/selectcertificatecommand.cpp
uiserver/sessiondata.cpp uiserver/sessiondata.h
uiserver/signcommand.cpp uiserver/signcommand.h
uiserver/signencryptfilescommand.cpp
uiserver/uiserver.cpp
uiserver/verifychecksumscommand.cpp uiserver/verifychecksumscommand.h
)
set(_kleopatra_uiserver_extra_libs LibAssuan::LibAssuan LibGpgError::LibGpgError)
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()
if(KF5IdentityManagement_FOUND AND KF5MailTransport_FOUND AND KF5MailTransportAkonadi_FOUND)
set(_kleopatra_mail_libs
KF5::IdentityManagement # Export OpenPGP keys using WKS
KF5::MailTransport
KF5::MailTransportAkonadi
)
add_definitions(-DMAILAKONADI_ENABLED)
endif()
ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
set(_kleopatra_SRCS
${_kleopatra_extra_SRCS}
accessibility/accessiblelink.cpp
accessibility/accessiblelink_p.h
accessibility/accessiblerichtextlabel.cpp
accessibility/accessiblerichtextlabel_p.h
accessibility/accessiblevaluelabel.cpp
accessibility/accessiblevaluelabel_p.h
accessibility/accessiblewidgetfactory.cpp
accessibility/accessiblewidgetfactory.h
commands/adduseridcommand.cpp
commands/adduseridcommand.h
commands/authenticatepivcardapplicationcommand.cpp
commands/authenticatepivcardapplicationcommand.h
commands/cardcommand.cpp
commands/cardcommand.h
commands/certificatetopivcardcommand.cpp
commands/certificatetopivcardcommand.h
commands/certifycertificatecommand.cpp
commands/certifycertificatecommand.h
commands/changeexpirycommand.cpp
commands/changeexpirycommand.h
commands/changeownertrustcommand.cpp
commands/changeownertrustcommand.h
commands/changepassphrasecommand.cpp
commands/changepassphrasecommand.h
commands/changepincommand.cpp
commands/changepincommand.h
commands/changeroottrustcommand.cpp
commands/changeroottrustcommand.h
commands/checksumcreatefilescommand.cpp
commands/checksumcreatefilescommand.h
commands/checksumverifyfilescommand.cpp
commands/checksumverifyfilescommand.h
commands/clearcrlcachecommand.cpp
commands/clearcrlcachecommand.h
commands/command.cpp
commands/command.h
commands/createcsrforcardkeycommand.cpp
commands/createcsrforcardkeycommand.h
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.h
commands/decryptverifyclipboardcommand.cpp
commands/decryptverifyclipboardcommand.h
commands/decryptverifyfilescommand.cpp
commands/decryptverifyfilescommand.h
commands/deletecertificatescommand.cpp
commands/deletecertificatescommand.h
commands/detailscommand.cpp
commands/detailscommand.h
commands/dumpcertificatecommand.cpp
commands/dumpcertificatecommand.h
commands/dumpcrlcachecommand.cpp
commands/dumpcrlcachecommand.h
commands/encryptclipboardcommand.cpp
commands/encryptclipboardcommand.h
commands/exportcertificatecommand.cpp
commands/exportcertificatecommand.h
commands/exportgroupscommand.cpp
commands/exportgroupscommand.h
commands/exportopenpgpcertstoservercommand.cpp
commands/exportopenpgpcertstoservercommand.h
commands/exportopenpgpcerttoprovidercommand.cpp
commands/exportopenpgpcerttoprovidercommand.h
commands/exportpaperkeycommand.cpp
commands/exportpaperkeycommand.h
commands/exportsecretkeycommand.cpp
commands/exportsecretkeycommand.h
commands/exportsecretkeycommand_old.cpp
commands/exportsecretkeycommand_old.h
commands/exportsecretsubkeycommand.cpp
commands/exportsecretsubkeycommand.h
commands/genrevokecommand.cpp
commands/genrevokecommand.h
commands/gnupgprocesscommand.cpp
commands/gnupgprocesscommand.h
commands/importcertificatefromclipboardcommand.cpp
commands/importcertificatefromclipboardcommand.h
commands/importcertificatefromdatacommand.cpp
commands/importcertificatefromdatacommand.h
commands/importcertificatefromfilecommand.cpp
commands/importcertificatefromfilecommand.h
commands/importcertificatefromkeyservercommand.cpp
commands/importcertificatefromkeyservercommand.h
commands/importcertificatefrompivcardcommand.cpp
commands/importcertificatefrompivcardcommand.h
commands/importcertificatescommand.cpp
commands/importcertificatescommand.h
commands/importcrlcommand.cpp
commands/importcrlcommand.h
commands/importpaperkeycommand.cpp
commands/importpaperkeycommand.h
commands/keytocardcommand.cpp
commands/keytocardcommand.h
commands/learncardkeyscommand.cpp
commands/learncardkeyscommand.h
commands/lookupcertificatescommand.cpp
commands/lookupcertificatescommand.h
commands/newcertificatesigningrequestcommand.cpp
commands/newcertificatesigningrequestcommand.h
commands/newopenpgpcertificatecommand.cpp
commands/newopenpgpcertificatecommand.h
commands/openpgpgeneratecardkeycommand.cpp
commands/openpgpgeneratecardkeycommand.h
commands/pivgeneratecardkeycommand.cpp
commands/pivgeneratecardkeycommand.h
commands/refreshcertificatecommand.cpp
commands/refreshcertificatecommand.h
commands/refreshopenpgpcertscommand.cpp
commands/refreshopenpgpcertscommand.h
commands/refreshx509certscommand.cpp
commands/refreshx509certscommand.h
commands/reloadkeyscommand.cpp
commands/reloadkeyscommand.h
commands/revokecertificationcommand.cpp
commands/revokecertificationcommand.h
commands/revokekeycommand.cpp
commands/revokekeycommand.h
commands/revokeuseridcommand.cpp
commands/revokeuseridcommand.h
commands/selftestcommand.cpp
commands/selftestcommand.h
commands/setinitialpincommand.cpp
commands/setinitialpincommand.h
commands/setpivcardapplicationadministrationkeycommand.cpp
commands/setpivcardapplicationadministrationkeycommand.h
commands/setprimaryuseridcommand.cpp
commands/setprimaryuseridcommand.h
commands/signclipboardcommand.cpp
commands/signclipboardcommand.h
commands/signencryptfilescommand.cpp
commands/signencryptfilescommand.h
commands/signencryptfoldercommand.cpp
commands/signencryptfoldercommand.h
conf/configuredialog.cpp
conf/configuredialog.h
conf/groupsconfigdialog.cpp
conf/groupsconfigdialog.h
conf/groupsconfigpage.cpp
conf/groupsconfigpage.h
conf/groupsconfigwidget.cpp
conf/groupsconfigwidget.h
crypto/autodecryptverifyfilescontroller.cpp
crypto/autodecryptverifyfilescontroller.h
crypto/certificateresolver.cpp
crypto/certificateresolver.h
crypto/checksumsutils_p.cpp
crypto/checksumsutils_p.h
crypto/controller.cpp
crypto/controller.h
crypto/createchecksumscontroller.cpp
crypto/createchecksumscontroller.h
crypto/decryptverifyemailcontroller.cpp
crypto/decryptverifyemailcontroller.h
crypto/decryptverifyfilescontroller.cpp
crypto/decryptverifyfilescontroller.h
crypto/decryptverifytask.cpp
crypto/decryptverifytask.h
crypto/encryptemailcontroller.cpp
crypto/encryptemailcontroller.h
crypto/encryptemailtask.cpp
crypto/encryptemailtask.h
crypto/gui/certificatelineedit.cpp
crypto/gui/certificatelineedit.h
crypto/gui/certificateselectionline.cpp
crypto/gui/certificateselectionline.h
crypto/gui/decryptverifyfilesdialog.cpp
crypto/gui/decryptverifyfilesdialog.h
crypto/gui/decryptverifyfileswizard.cpp
crypto/gui/decryptverifyfileswizard.h
crypto/gui/decryptverifyoperationwidget.cpp
crypto/gui/decryptverifyoperationwidget.h
crypto/gui/encryptemailwizard.cpp
crypto/gui/encryptemailwizard.h
crypto/gui/newresultpage.cpp
crypto/gui/newresultpage.h
crypto/gui/objectspage.cpp
crypto/gui/objectspage.h
crypto/gui/resolverecipientspage.cpp
crypto/gui/resolverecipientspage.h
crypto/gui/resultitemwidget.cpp
crypto/gui/resultitemwidget.h
crypto/gui/resultlistwidget.cpp
crypto/gui/resultlistwidget.h
crypto/gui/resultpage.cpp
crypto/gui/resultpage.h
crypto/gui/signemailwizard.cpp
crypto/gui/signemailwizard.h
crypto/gui/signencryptemailconflictdialog.cpp
crypto/gui/signencryptemailconflictdialog.h
crypto/gui/signencryptfileswizard.cpp
crypto/gui/signencryptfileswizard.h
crypto/gui/signencryptwidget.cpp
crypto/gui/signencryptwidget.h
crypto/gui/signencryptwizard.cpp
crypto/gui/signencryptwizard.h
crypto/gui/signerresolvepage.cpp
crypto/gui/signerresolvepage.h
crypto/gui/signingcertificateselectiondialog.cpp
crypto/gui/signingcertificateselectiondialog.h
crypto/gui/signingcertificateselectionwidget.cpp
crypto/gui/signingcertificateselectionwidget.h
crypto/gui/unknownrecipientwidget.cpp
crypto/gui/unknownrecipientwidget.h
crypto/gui/verifychecksumsdialog.cpp
crypto/gui/verifychecksumsdialog.h
crypto/gui/wizard.cpp
crypto/gui/wizard.h
crypto/gui/wizardpage.cpp
crypto/gui/wizardpage.h
crypto/newsignencryptemailcontroller.cpp
crypto/newsignencryptemailcontroller.h
crypto/recipient.cpp
crypto/recipient.h
crypto/sender.cpp
crypto/sender.h
crypto/signemailcontroller.cpp
crypto/signemailcontroller.h
crypto/signemailtask.cpp
crypto/signemailtask.h
crypto/signencryptfilescontroller.cpp
crypto/signencryptfilescontroller.h
crypto/signencrypttask.cpp
crypto/signencrypttask.h
crypto/task.cpp
crypto/task.h
crypto/taskcollection.cpp
crypto/taskcollection.h
crypto/verifychecksumscontroller.cpp
crypto/verifychecksumscontroller.h
dialogs/adduseriddialog.cpp
dialogs/adduseriddialog.h
dialogs/certificatedetailsdialog.cpp
dialogs/certificatedetailsdialog.h
dialogs/certificatedetailsinputwidget.cpp
dialogs/certificatedetailsinputwidget.h
dialogs/certificatedetailswidget.cpp
dialogs/certificatedetailswidget.h
dialogs/certificateselectiondialog.cpp
dialogs/certificateselectiondialog.h
dialogs/certifycertificatedialog.cpp
dialogs/certifycertificatedialog.h
dialogs/certifywidget.cpp
dialogs/certifywidget.h
dialogs/createcsrforcardkeydialog.cpp
dialogs/createcsrforcardkeydialog.h
dialogs/deletecertificatesdialog.cpp
dialogs/deletecertificatesdialog.h
dialogs/editgroupdialog.cpp
dialogs/editgroupdialog.h
dialogs/expirydialog.cpp
dialogs/expirydialog.h
dialogs/exportdialog.cpp
dialogs/exportdialog.h
dialogs/gencardkeydialog.cpp
dialogs/gencardkeydialog.h
dialogs/groupdetailsdialog.cpp
dialogs/groupdetailsdialog.h
dialogs/lookupcertificatesdialog.cpp
dialogs/lookupcertificatesdialog.h
dialogs/nameandemailwidget.cpp
dialogs/nameandemailwidget.h
dialogs/newopenpgpcertificatedetailsdialog.cpp
dialogs/newopenpgpcertificatedetailsdialog.h
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.h
dialogs/revokekeydialog.cpp
dialogs/revokekeydialog.h
dialogs/selftestdialog.cpp
dialogs/selftestdialog.h
dialogs/setinitialpindialog.cpp
dialogs/setinitialpindialog.h
dialogs/subkeyswidget.cpp
dialogs/subkeyswidget.h
dialogs/trustchainwidget.cpp
dialogs/trustchainwidget.h
dialogs/updatenotification.cpp
dialogs/updatenotification.h
dialogs/weboftrustdialog.cpp
dialogs/weboftrustdialog.h
dialogs/weboftrustwidget.cpp
dialogs/weboftrustwidget.h
interfaces/anchorprovider.h
interfaces/focusfirstchild.h
newcertificatewizard/advancedsettingsdialog.cpp
newcertificatewizard/advancedsettingsdialog_p.h
newcertificatewizard/enterdetailspage.cpp
newcertificatewizard/enterdetailspage_p.h
newcertificatewizard/keyalgo.cpp
newcertificatewizard/keyalgo_p.h
newcertificatewizard/keycreationpage.cpp
newcertificatewizard/keycreationpage_p.h
newcertificatewizard/listwidget.cpp
newcertificatewizard/listwidget.h
newcertificatewizard/newcertificatewizard.cpp
newcertificatewizard/newcertificatewizard.h
newcertificatewizard/resultpage.cpp
newcertificatewizard/resultpage_p.h
newcertificatewizard/wizardpage.cpp
newcertificatewizard/wizardpage_p.h
selftest/compliancecheck.cpp
selftest/compliancecheck.h
selftest/enginecheck.cpp
selftest/enginecheck.h
selftest/gpgagentcheck.cpp
selftest/gpgagentcheck.h
selftest/gpgconfcheck.cpp
selftest/gpgconfcheck.h
selftest/libkleopatrarccheck.cpp
selftest/libkleopatrarccheck.h
selftest/selftest.cpp
selftest/selftest.h
smartcard/algorithminfo.h
smartcard/card.cpp
smartcard/card.h
smartcard/deviceinfowatcher.cpp
smartcard/deviceinfowatcher.h
smartcard/keypairinfo.cpp
smartcard/keypairinfo.h
smartcard/netkeycard.cpp
smartcard/netkeycard.h
smartcard/openpgpcard.cpp
smartcard/openpgpcard.h
smartcard/p15card.cpp
smartcard/p15card.h
smartcard/pivcard.cpp
smartcard/pivcard.h
smartcard/readerstatus.cpp
smartcard/readerstatus.h
smartcard/utils.cpp
smartcard/utils.h
utils/accessibility.cpp
utils/accessibility.h
utils/action_data.cpp
utils/action_data.h
utils/applicationstate.cpp
utils/applicationstate.h
utils/archivedefinition.cpp
utils/archivedefinition.h
utils/clipboardmenu.cpp
utils/clipboardmenu.h
utils/debug-helpers.cpp
utils/debug-helpers.h
utils/dragqueen.cpp
utils/dragqueen.h
utils/email.cpp
utils/email.h
utils/emptypassphraseprovider.cpp
utils/emptypassphraseprovider.h
utils/filedialog.cpp
utils/filedialog.h
utils/gui-helper.cpp
utils/gui-helper.h
utils/headerview.cpp
utils/headerview.h
utils/input.cpp
utils/input.h
utils/iodevicelogger.cpp
utils/iodevicelogger.h
utils/kdpipeiodevice.cpp
utils/kdpipeiodevice.h
utils/keyparameters.cpp
utils/keyparameters.h
utils/keys.cpp
utils/keys.h
utils/kuniqueservice.cpp
utils/kuniqueservice.h
utils/log.cpp
utils/log.h
utils/memory-helpers.h
utils/multivalidator.cpp
utils/multivalidator.h
utils/output.cpp
utils/output.h
+ utils/overwritedialog.cpp
+ utils/overwritedialog.h
utils/path-helper.cpp
utils/path-helper.h
utils/scrollarea.cpp
utils/scrollarea.h
utils/systemtrayicon.cpp
utils/systemtrayicon.h
utils/tags.cpp
utils/tags.h
utils/types.cpp
utils/types.h
utils/userinfo.cpp
utils/userinfo.h
utils/validation.cpp
utils/validation.h
utils/writecertassuantransaction.cpp
utils/writecertassuantransaction.h
utils/wsastarter.cpp
utils/wsastarter.h
view/anchorcache.cpp
view/anchorcache_p.h
view/errorlabel.cpp
view/errorlabel.h
view/formtextinput.cpp
view/formtextinput.h
view/htmllabel.cpp
view/htmllabel.h
view/infofield.cpp
view/infofield.h
view/keycacheoverlay.cpp
view/keycacheoverlay.h
view/keylistcontroller.cpp
view/keylistcontroller.h
view/keytreeview.cpp
view/keytreeview.h
view/netkeywidget.cpp
view/netkeywidget.h
view/nullpinwidget.cpp
view/nullpinwidget.h
view/openpgpkeycardwidget.cpp
view/openpgpkeycardwidget.h
view/p15cardwidget.cpp
view/p15cardwidget.h
view/padwidget.cpp
view/padwidget.h
view/pgpcardwidget.cpp
view/pgpcardwidget.h
view/pivcardwidget.cpp
view/pivcardwidget.h
view/searchbar.cpp
view/searchbar.h
view/smartcardwidget.cpp
view/smartcardwidget.h
view/tabwidget.cpp
view/tabwidget.h
view/urllabel.cpp
view/urllabel.h
view/waitwidget.cpp
view/waitwidget.h
view/welcomewidget.cpp
view/welcomewidget.h
aboutdata.cpp
aboutdata.h
kleopatra.qrc
kleopatraapplication.cpp
kleopatraapplication.h
main.cpp
mainwindow.cpp
mainwindow.h
systrayicon.cpp
systrayicon.h
)
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 conf/kleopageconfigdialog.h ${_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/setinitialpindialog.ui
dialogs/trustchainwidget.ui
newcertificatewizard/listwidget.ui
)
kconfig_add_kcfg_files(_kleopatra_SRCS
kcfg/emailoperationspreferences.kcfgc
kcfg/fileoperationspreferences.kcfgc
kcfg/settings.kcfgc
kcfg/smimevalidationpreferences.kcfgc
kcfg/tagspreferences.kcfgc
kcfg/tooltippreferences.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
${_kleopatra_extra_libs}
KF5::Libkleo
KF5::Mime
KF${KF_MAJOR_VERSION}::I18n
KF${KF_MAJOR_VERSION}::XmlGui
KF${KF_MAJOR_VERSION}::IconThemes
KF${KF_MAJOR_VERSION}::WindowSystem
KF${KF_MAJOR_VERSION}::CoreAddons
KF${KF_MAJOR_VERSION}::ItemModels
KF${KF_MAJOR_VERSION}::Crash
KF${KF_MAJOR_VERSION}::Codecs
${_kleopatra_mail_libs}
Qt::Network
Qt::PrintSupport # Printing secret keys
${_kleopatra_uiserver_extra_libs}
${_kleopatra_dbusaddons_libs}
kleopatraclientcore
${_kleopatra_platform_libs}
)
if (QT_MAJOR_VERSION STREQUAL "6")
target_link_libraries(kleopatra_bin QGpgmeQt6)
else()
target_link_libraries(kleopatra_bin QGpgme)
endif()
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/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp
index 75f4e4d3f..0d5bbbcf6 100644
--- a/src/crypto/signencrypttask.cpp
+++ b/src/crypto/signencrypttask.cpp
@@ -1,812 +1,814 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencrypttask.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 "signencrypttask.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/path-helper.h>
#include <utils/kleo_assert.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <Libkleo/KleoException>
#include <QGpgME/Protocol>
#include <QGpgME/SignJob>
#include <QGpgME/SignEncryptJob>
#include <QGpgME/EncryptJob>
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
#include <QGpgME/EncryptArchiveJob>
#include <QGpgME/SignArchiveJob>
#include <QGpgME/SignEncryptArchiveJob>
#endif
#include <gpgme++/signingresult.h>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QFileInfo>
#include <QPointer>
#include <QTextDocument> // for Qt::escape
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted)
{
return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2",
input.toHtmlEscaped(),
outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped());
}
class ErrorResult : public Task::Result
{
public:
ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog)
: Task::Result(), m_sign(sign), m_encrypt(encrypt), m_error(err), m_errString(errStr), m_inputLabel(input), m_outputLabel(output), m_auditLog(auditLog) {}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override
{
return m_error;
}
QString errorString() const override
{
return m_errString;
}
VisualCode code() const override
{
return NeutralError;
}
AuditLogEntry auditLog() const override
{
return m_auditLog;
}
private:
const bool m_sign;
const bool m_encrypt;
const Error m_error;
const QString m_errString;
const QString m_inputLabel;
const QString m_outputLabel;
const AuditLogEntry m_auditLog;
};
namespace
{
struct LabelAndError
{
QString label;
QString errorString;
};
}
class SignEncryptFilesResult : public Task::Result
{
public:
SignEncryptFilesResult(const SigningResult &sr, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_sresult.isNull());
}
SignEncryptFilesResult(const EncryptionResult &er, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_eresult(er),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_eresult.isNull());
}
SignEncryptFilesResult(const SigningResult &sr, const EncryptionResult &er, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_eresult(er),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull());
}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
private:
const SigningResult m_sresult;
const EncryptionResult m_eresult;
const LabelAndError m_input;
const LabelAndError m_output;
const bool m_outputCreated;
const AuditLogEntry m_auditLog;
};
static QString makeSigningOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Signing canceled.");
}
if (err) {
return i18n("Signing failed.");
}
return i18n("Signing succeeded.");
}
static QString makeResultOverview(const SigningResult &result)
{
return makeSigningOverview(result.error());
}
static QString makeEncryptionOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Encryption canceled.");
}
if (err) {
return i18n("Encryption failed.");
}
return i18n("Encryption succeeded.");
}
static QString makeResultOverview(const EncryptionResult &result)
{
return makeEncryptionOverview(result.error());
}
static QString makeResultOverview(const SigningResult &sr, const EncryptionResult &er)
{
if (er.isNull() && sr.isNull()) {
return QString();
}
if (er.isNull()) {
return makeResultOverview(sr);
}
if (sr.isNull()) {
return makeResultOverview(er);
}
if (sr.error().isCanceled() || sr.error()) {
return makeResultOverview(sr);
}
if (er.error().isCanceled() || er.error()) {
return makeResultOverview(er);
}
return i18n("Signing and encryption succeeded.");
}
static QString escape(QString s)
{
s = s.toHtmlEscaped();
s.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
return s;
}
static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return QString();
}
static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return i18n(" Encryption succeeded.");
}
}
QString ErrorResult::overview() const
{
Q_ASSERT(m_error || m_error.isCanceled());
Q_ASSERT(m_sign || m_encrypt);
const QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true);
const bool canceled = m_error.isCanceled();
if (m_sign && m_encrypt) {
return canceled ? i18n("%1: <b>Sign/encrypt canceled.</b>", label) : i18n(" %1: Sign/encrypt failed.", label);
}
return i18nc("label: result. Example: foo -> foo.gpg: Encryption failed.", "%1: <b>%2</b>", label,
m_sign ? makeSigningOverview(m_error) : makeEncryptionOverview(m_error));
}
QString ErrorResult::details() const
{
return m_errString;
}
class SignEncryptTask::Private
{
friend class ::Kleo::Crypto::SignEncryptTask;
SignEncryptTask *const q;
public:
explicit Private(SignEncryptTask *qq);
private:
void startSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startSignEncryptArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto);
#endif
std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog);
private:
void slotResult(const SigningResult &);
void slotResult(const SigningResult &, const EncryptionResult &);
void slotResult(const EncryptionResult &);
void slotResult(const QGpgME::Job *, const SigningResult &, const EncryptionResult &);
private:
std::shared_ptr<Input> input;
std::shared_ptr<Output> output;
QStringList inputFileNames;
QString outputFileName;
std::vector<Key> signers;
std::vector<Key> recipients;
bool sign : 1;
bool encrypt : 1;
bool detached : 1;
bool symmetric: 1;
bool clearsign: 1;
bool archive : 1;
QPointer<QGpgME::Job> job;
std::shared_ptr<OverwritePolicy> m_overwritePolicy;
};
SignEncryptTask::Private::Private(SignEncryptTask *qq)
: q{qq}
, sign{true}
, encrypt{true}
, detached{false}
, clearsign{false}
, archive{false}
, m_overwritePolicy{new OverwritePolicy{nullptr}}
{
q->setAsciiArmor(true);
}
std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, q->label(), output ? output->label() : QString{}, auditLog));
}
SignEncryptTask::SignEncryptTask(QObject *p)
: Task(p), d(new Private(this))
{
}
SignEncryptTask::~SignEncryptTask() {}
void SignEncryptTask::setInputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->inputFileNames = QStringList(fileName);
}
void SignEncryptTask::setInputFileNames(const QStringList &fileNames)
{
kleo_assert(!d->job);
kleo_assert(!fileNames.empty());
d->inputFileNames = fileNames;
}
void SignEncryptTask::setInput(const std::shared_ptr<Input> &input)
{
kleo_assert(!d->job);
kleo_assert(input);
d->input = input;
}
void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output)
{
kleo_assert(!d->job);
kleo_assert(output);
d->output = output;
}
void SignEncryptTask::setOutputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->outputFileName = fileName;
}
void SignEncryptTask::setSigners(const std::vector<Key> &signers)
{
kleo_assert(!d->job);
d->signers = signers;
}
void SignEncryptTask::setRecipients(const std::vector<Key> &recipients)
{
kleo_assert(!d->job);
d->recipients = recipients;
}
void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy)
{
kleo_assert(!d->job);
d->m_overwritePolicy = policy;
}
void SignEncryptTask::setSign(bool sign)
{
kleo_assert(!d->job);
d->sign = sign;
}
void SignEncryptTask::setEncrypt(bool encrypt)
{
kleo_assert(!d->job);
d->encrypt = encrypt;
}
void SignEncryptTask::setDetachedSignature(bool detached)
{
kleo_assert(!d->job);
d->detached = detached;
}
void SignEncryptTask::setEncryptSymmetric(bool symmetric)
{
kleo_assert(!d->job);
d->symmetric = symmetric;
}
void SignEncryptTask::setClearsign(bool clearsign)
{
kleo_assert(!d->job);
d->clearsign = clearsign;
}
void SignEncryptTask::setCreateArchive(bool archive)
{
kleo_assert(!d->job);
d->archive = archive;
}
Protocol SignEncryptTask::protocol() const
{
if (d->sign && !d->signers.empty()) {
return d->signers.front().protocol();
}
if (d->encrypt || d->symmetric) {
if (!d->recipients.empty()) {
return d->recipients.front().protocol();
} else {
return GpgME::OpenPGP; // symmetric OpenPGP encryption
}
}
throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL),
i18n("Cannot determine protocol for task"));
}
QString SignEncryptTask::label() const
{
if (d->input) {
return d->input->label();
} else if (!d->inputFileNames.empty()) {
const auto firstFile = QFileInfo{d->inputFileNames.front()}.fileName();
return d->inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile);
}
return {};
}
QString SignEncryptTask::tag() const
{
return Formatting::displayName(protocol());
}
unsigned long long SignEncryptTask::inputSize() const
{
return d->input ? d->input->size() : 0U;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
}
#endif
void SignEncryptTask::doStart()
{
kleo_assert(!d->job);
if (d->sign) {
kleo_assert(!d->signers.empty());
if (d->archive) {
kleo_assert(!d->detached && !d->clearsign);
}
}
if (!d->output) {
d->output = Output::createFromFile(d->outputFileName, d->m_overwritePolicy);
}
+ // release grab on policy, so that output can infer multiple outputs from use count > 1
+ d->m_overwritePolicy.reset();
const auto proto = protocol();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->archive && archiveJobsCanBeUsed(proto)) {
d->startSignEncryptArchiveJob(proto);
} else
#endif
{
d->startSignEncryptJob(proto);
}
}
void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto)
{
kleo_assert(input);
if (encrypt || symmetric) {
Context::EncryptionFlags flags = Context::AlwaysTrust;
if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
if (sign) {
std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
#endif
job->start(signers, recipients,
input->ioDevice(), output->ioDevice(), flags);
this->job = job.release();
} else {
std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
#endif
job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
this->job = job.release();
}
} else if (sign) {
std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto);
kleo_assert(job.get());
kleo_assert(! (detached && clearsign));
job->start(signers,
input->ioDevice(), output->ioDevice(),
detached ? GpgME::Detached : clearsign ?
GpgME::Clearsigned : GpgME::NormalSignatureMode);
this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
void SignEncryptTask::cancel()
{
if (d->job) {
d->job->slotCancel();
}
}
std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signJob.get());
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(signJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
#else
connect(signJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
connect(signJob.get(), SIGNAL(result(GpgME::SigningResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult)));
return signJob;
}
std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signEncryptJob.get());
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(signEncryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
#else
connect(signEncryptJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
connect(signEncryptJob.get(), SIGNAL(result(GpgME::SigningResult,GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult,GpgME::EncryptionResult)));
return signEncryptJob;
}
std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(encryptJob.get());
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(encryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
#else
connect(encryptJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
q->setProgress(processed, total);
});
#endif
connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::EncryptionResult)));
return encryptJob;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto)
{
kleo_assert(!input);
const auto baseDirectory = heuristicBaseDirectory(inputFileNames);
if (baseDirectory.isEmpty()) {
throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n'))));
}
qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory;
const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames);
const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()};
qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths;
if (encrypt || symmetric) {
Context::EncryptionFlags flags = Context::AlwaysTrust;
if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
if (sign) {
std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(signers, recipients, relativePaths, output->ioDevice(), flags);
this->job = job.release();
} else {
std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(recipients, relativePaths, output->ioDevice(), flags);
this->job = job.release();
}
} else if (sign) {
std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(signers, relativePaths, output->ioDevice());
this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor()));
auto job = signJob.get();
kleo_assert(job);
connect(job, &QGpgME::SignArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
connect(job, &QGpgME::SignArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult) {
slotResult(job, signResult, EncryptionResult{});
});
return signJob;
}
std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor()));
auto job = signEncryptJob.get();
kleo_assert(job);
connect(job, &QGpgME::SignEncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
connect(job, &QGpgME::SignEncryptArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) {
slotResult(job, signResult, encryptResult);
});
return signEncryptJob;
}
std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor()));
auto job = encryptJob.get();
kleo_assert(job);
connect(job, &QGpgME::EncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) {
slotResult(job, SigningResult{}, encryptResult);
});
return encryptJob;
}
#endif
void SignEncryptTask::Private::slotResult(const SigningResult &result)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), result, EncryptionResult{});
}
void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), sresult, eresult);
}
void SignEncryptTask::Private::slotResult(const EncryptionResult &result)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), SigningResult{}, result);
}
void SignEncryptTask::Private::slotResult(const QGpgME::Job *job, const SigningResult &sresult, const EncryptionResult &eresult)
{
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
output->cancel();
q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO),
i18n("Input error: %1", escape( input->errorString())),
auditLog));
return;
} else if (sresult.error().code() || eresult.error().code()) {
output->cancel();
} else {
try {
kleo_assert(!sresult.isNull() || !eresult.isNull());
output->finalize();
outputCreated = true;
if (input) {
input->finalize();
}
} catch (const GpgME::Exception &e) {
q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
}
}
const LabelAndError inputInfo{q->label(), input ? input->errorString() : QString{}};
const LabelAndError outputInfo{output->label(), output->errorString()};
q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, inputInfo, outputInfo, outputCreated, auditLog)));
}
QString SignEncryptFilesResult::overview() const
{
const QString files = formatInputOutputLabel(m_input.label, m_output.label, !m_outputCreated);
return files + QLatin1String(": ") + makeOverview(makeResultOverview(m_sresult, m_eresult));
}
QString SignEncryptFilesResult::details() const
{
return errorString();
}
GpgME::Error SignEncryptFilesResult::error() const
{
if (m_sresult.error().code()) {
return m_sresult.error();
}
if (m_eresult.error().code()) {
return m_eresult.error();
}
return {};
}
QString SignEncryptFilesResult::errorString() const
{
const bool sign = !m_sresult.isNull();
const bool encrypt = !m_eresult.isNull();
kleo_assert(sign || encrypt);
if (sign && encrypt) {
return
m_sresult.error().code() ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) :
m_eresult.error().code() ? makeResultDetails(m_eresult, m_input.errorString, m_output.errorString) :
QString();
}
return
sign ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) :
/*else*/ makeResultDetails(m_eresult, m_input.errorString, m_output.errorString);
}
Task::Result::VisualCode SignEncryptFilesResult::code() const
{
if (m_sresult.error().isCanceled() || m_eresult.error().isCanceled()) {
return Warning;
}
return (m_sresult.error().code() || m_eresult.error().code()) ? NeutralError : NeutralSuccess;
}
AuditLogEntry SignEncryptFilesResult::auditLog() const
{
return m_auditLog;
}
#include "moc_signencrypttask.cpp"
diff --git a/src/utils/output.cpp b/src/utils/output.cpp
index 1ad7ea7f0..5d29ace9e 100644
--- a/src/utils/output.cpp
+++ b/src/utils/output.cpp
@@ -1,761 +1,817 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/output.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 "output.h"
#include "input_p.h"
#include "detail_p.h"
#include "kleo_assert.h"
#include "kdpipeiodevice.h"
#include "log.h"
#include "cached.h"
+#include "overwritedialog.h"
#include <Libkleo/KleoException>
+#include <KFileUtils>
#include <KLocalizedString>
#include <KMessageBox>
+
#include "kleopatra_debug.h"
#include <QFileInfo>
#include <QTemporaryFile>
#include <QString>
#include <QClipboard>
#include <QApplication>
#include <QBuffer>
#include <QPointer>
#include <QWidget>
#include <QDir>
#include <QProcess>
#include <QTimer>
+#include <QUrl>
#ifdef Q_OS_WIN
# include <windows.h>
#endif
#include <cerrno>
using namespace Kleo;
using namespace Kleo::_detail;
static const int PROCESS_MAX_RUNTIME_TIMEOUT = -1; // no timeout
static const int PROCESS_TERMINATE_TIMEOUT = 5 * 1000; // 5s
class OverwritePolicy::Private
{
public:
Private(QWidget *p, OverwritePolicy::Policy pol) : policy(pol), widget(p) {}
OverwritePolicy::Policy policy;
QWidget *widget;
};
OverwritePolicy::OverwritePolicy(QWidget *parent, Policy initialPolicy) : d(new Private(parent, initialPolicy))
{
}
OverwritePolicy::~OverwritePolicy() {}
OverwritePolicy::Policy OverwritePolicy::policy() const
{
return d->policy;
}
void OverwritePolicy::setPolicy(Policy policy)
{
d->policy = policy;
}
QWidget *OverwritePolicy::parentWidget() const
{
return d->widget;
}
namespace
{
class TemporaryFile : public QTemporaryFile
{
public:
using QTemporaryFile::QTemporaryFile;
void close() override {
if (isOpen())
{
m_oldFileName = fileName();
}
QTemporaryFile::close();
}
bool openNonInheritable()
{
if (!QTemporaryFile::open()) {
return false;
}
#if defined(Q_OS_WIN)
//QTemporaryFile (tested with 4.3.3) creates the file handle as inheritable.
//The handle is then inherited by gpgsm, which prevents deletion of the temp file
//in FileOutput::doFinalize()
//There are no inheritable handles under wince
return SetHandleInformation((HANDLE)_get_osfhandle(handle()), HANDLE_FLAG_INHERIT, 0);
#endif
return true;
}
QString oldFileName() const
{
return m_oldFileName;
}
private:
QString m_oldFileName;
};
template <typename T_IODevice>
struct inhibit_close : T_IODevice {
explicit inhibit_close() : T_IODevice() {}
template <typename T1>
explicit inhibit_close(T1 &t1) : T_IODevice(t1) {}
/* reimp */ void close() override {}
void reallyClose()
{
T_IODevice::close();
}
};
template <typename T_IODevice>
struct redirect_close : T_IODevice {
explicit redirect_close() : T_IODevice(), m_closed(false) {}
template <typename T1>
explicit redirect_close(T1 &t1) : T_IODevice(t1), m_closed(false) {}
/* reimp */ void close() override
{
this->closeWriteChannel();
m_closed = true;
}
bool isClosed() const
{
return m_closed;
}
private:
bool m_closed;
};
class FileOutput;
class OutputInput : public InputImplBase
{
public:
OutputInput(const std::shared_ptr<FileOutput> &output);
unsigned int classification() const override
{
return 0;
}
void outputFinalized()
{
if (!m_ioDevice->open(QIODevice::ReadOnly)) {
qCCritical(KLEOPATRA_LOG) << "Failed to open file for reading";
}
}
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_ioDevice;
}
unsigned long long size() const override
{
return 0;
}
private:
std::shared_ptr<FileOutput> m_output;
mutable std::shared_ptr<QIODevice> m_ioDevice = nullptr;
};
class OutputImplBase : public Output
{
public:
OutputImplBase()
: Output(),
m_defaultLabel(),
m_customLabel(),
m_errorString(),
m_isFinalized(false),
m_isFinalizing(false),
m_cancelPending(false),
m_canceled(false),
m_binaryOpt(false)
{
}
QString label() const override
{
return m_customLabel.isEmpty() ? m_defaultLabel : m_customLabel;
}
void setLabel(const QString &label) override {
m_customLabel = label;
}
void setDefaultLabel(const QString &l)
{
m_defaultLabel = l;
}
void setBinaryOpt(bool value) override {
m_binaryOpt = value;
}
bool binaryOpt() const override
{
return m_binaryOpt;
}
QString errorString() const override
{
if (m_errorString.dirty()) {
m_errorString = doErrorString();
}
return m_errorString;
}
bool isFinalized() const override
{
return m_isFinalized;
}
void finalize() override {
qCDebug(KLEOPATRA_LOG) << this;
if (m_isFinalized || m_isFinalizing)
{
return;
}
m_isFinalizing = true;
try {
doFinalize();
} catch (...)
{
m_isFinalizing = false;
throw;
}
m_isFinalizing = false;
m_isFinalized = true;
if (m_cancelPending)
{
cancel();
}
}
void cancel() override {
qCDebug(KLEOPATRA_LOG) << this;
if (m_isFinalizing)
{
m_cancelPending = true;
} else if (!m_canceled)
{
m_isFinalizing = true;
try {
doCancel();
} catch (...) {}
m_isFinalizing = false;
m_isFinalized = false;
m_canceled = true;
}
}
private:
virtual QString doErrorString() const
{
if (std::shared_ptr<QIODevice> io = ioDevice()) {
return io->errorString();
} else {
return i18n("No output device");
}
}
virtual void doFinalize() = 0;
virtual void doCancel() = 0;
private:
QString m_defaultLabel;
QString m_customLabel;
mutable cached<QString> m_errorString;
bool m_isFinalized : 1;
bool m_isFinalizing : 1;
bool m_cancelPending : 1;
bool m_canceled : 1;
bool m_binaryOpt : 1;
};
class PipeOutput : public OutputImplBase
{
public:
explicit PipeOutput(assuan_fd_t fd);
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_io;
}
void doFinalize() override {
m_io->reallyClose();
}
void doCancel() override {
doFinalize();
}
private:
std::shared_ptr< inhibit_close<KDPipeIODevice> > m_io;
};
class ProcessStdInOutput : public OutputImplBase
{
public:
explicit ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd);
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_proc;
}
void doFinalize() override {
/*
Make sure the data is written in the output here. If this
is not done the output will be written in small chunks
trough the eventloop causing an unnecessary delay before
the process has even a chance to work and finish.
This delay is mainly noticeable on Windows where it can
take ~30 seconds to write out a 10MB file in the 512 byte
chunks gpgme serves. */
qCDebug(KLEOPATRA_LOG) << "Waiting for " << m_proc->bytesToWrite()
<< " Bytes to be written";
while (m_proc->waitForBytesWritten(PROCESS_MAX_RUNTIME_TIMEOUT));
if (!m_proc->isClosed())
{
m_proc->close();
}
m_proc->waitForFinished(PROCESS_MAX_RUNTIME_TIMEOUT);
}
bool failed() const override
{
if (!m_proc) {
return false;
}
return !(m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0);
}
void doCancel() override {
m_proc->terminate();
QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, m_proc.get(), &QProcess::kill);
}
QString label() const override;
private:
QString doErrorString() const override;
private:
const QString m_command;
const QStringList m_arguments;
const std::shared_ptr< redirect_close<QProcess> > m_proc;
};
class FileOutput : public OutputImplBase
{
public:
explicit FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy);
~FileOutput() override
{
qCDebug(KLEOPATRA_LOG) << this;
}
QString label() const override
{
return QFileInfo(m_fileName).fileName();
}
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_tmpFile;
}
void doFinalize() override;
void doCancel() override {
qCDebug(KLEOPATRA_LOG) << this;
}
QString fileName() const override
{
return m_fileName;
}
void attachInput(const std::shared_ptr<OutputInput> &input)
{
m_attachedInput = std::weak_ptr<OutputInput>(input);
}
private:
- bool obtainOverwritePermission();
+ // returns the file name to write to or an empty string if overwriting was declined
+ QString obtainOverwritePermission(const QString &fileName);
private:
- const QString m_fileName;
+ QString m_fileName;
std::shared_ptr< TemporaryFile > m_tmpFile;
const std::shared_ptr<OverwritePolicy> m_policy;
std::weak_ptr<OutputInput> m_attachedInput;
};
#ifndef QT_NO_CLIPBOARD
class ClipboardOutput : public OutputImplBase
{
public:
explicit ClipboardOutput(QClipboard::Mode mode);
QString label() const override;
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_buffer;
}
void doFinalize() override;
void doCancel() override {}
private:
QString doErrorString() const override
{
return QString();
}
private:
const QClipboard::Mode m_mode;
std::shared_ptr<QBuffer> m_buffer;
};
#endif // QT_NO_CLIPBOARD
class ByteArrayOutput: public OutputImplBase
{
public:
explicit ByteArrayOutput(QByteArray *data):
m_buffer(std::shared_ptr<QBuffer>(new QBuffer(data)))
{
if (!m_buffer->open(QIODevice::WriteOnly))
throw Exception(gpg_error(GPG_ERR_EIO),
QStringLiteral("Could not open bytearray for writing?!"));
}
QString label() const override
{
return m_label;
}
void setLabel(const QString &label) override
{
m_label = label;
}
std::shared_ptr<QIODevice> ioDevice() const override
{
return m_buffer;
}
void doFinalize() override
{
m_buffer->close();
}
void doCancel() override
{
m_buffer->close();
}
private:
QString doErrorString() const override
{
return QString();
}
private:
QString m_label;
std::shared_ptr<QBuffer> m_buffer;
};
}
std::shared_ptr<Output> Output::createFromPipeDevice(assuan_fd_t fd, const QString &label)
{
std::shared_ptr<PipeOutput> po(new PipeOutput(fd));
po->setDefaultLabel(label);
return po;
}
PipeOutput::PipeOutput(assuan_fd_t fd)
: OutputImplBase(),
m_io(new inhibit_close<KDPipeIODevice>)
{
errno = 0;
if (!m_io->open(fd, QIODevice::WriteOnly))
throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
i18n("Could not open FD %1 for writing",
assuanFD2int(fd)));
}
std::shared_ptr<Output> Output::createFromFile(const QString &fileName, bool forceOverwrite)
{
- return createFromFile(fileName, std::shared_ptr<OverwritePolicy>(new OverwritePolicy(nullptr, forceOverwrite ? OverwritePolicy::Allow : OverwritePolicy::Deny)));
-
+ return createFromFile(fileName, std::shared_ptr<OverwritePolicy>(new OverwritePolicy(nullptr, forceOverwrite ? OverwritePolicy::Overwrite : OverwritePolicy::Skip)));
}
+
std::shared_ptr<Output> Output::createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
{
std::shared_ptr<FileOutput> fo(new FileOutput(fileName, policy));
qCDebug(KLEOPATRA_LOG) << fo.get();
return fo;
}
FileOutput::FileOutput(const QString &fileName, const std::shared_ptr<OverwritePolicy> &policy)
: OutputImplBase(),
m_fileName(fileName),
m_tmpFile(new TemporaryFile(fileName)),
m_policy(policy)
{
Q_ASSERT(m_policy);
errno = 0;
if (!m_tmpFile->openNonInheritable())
throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
i18n("Could not create temporary file for output \"%1\"", fileName));
}
-bool FileOutput::obtainOverwritePermission()
+static QString suggestFileName(const QString &fileName)
+{
+ const QFileInfo fileInfo{fileName};
+ const QString path = fileInfo.absolutePath();
+ const QString newFileName = KFileUtils::suggestName(QUrl::fromLocalFile(path), fileInfo.fileName());
+ return path + QLatin1Char{'/'} + newFileName;
+}
+
+QString FileOutput::obtainOverwritePermission(const QString &fileName)
{
- if (m_policy->policy() != OverwritePolicy::Ask) {
- return m_policy->policy() == OverwritePolicy::Allow;
+ switch (m_policy->policy()) {
+ case OverwritePolicy::Ask:
+ break;
+ case OverwritePolicy::Overwrite:
+ return fileName;
+ case OverwritePolicy::Rename: {
+ return suggestFileName(fileName);
}
- const int sel = KMessageBox::questionTwoActionsCancel(m_policy->parentWidget(),
- i18n("The file <b>%1</b> already exists.\n"
- "Overwrite?",
- m_fileName),
- i18n("Overwrite Existing File?"),
- KStandardGuiItem::overwrite(),
- KGuiItem(i18n("Overwrite All")),
- KStandardGuiItem::cancel());
- if (sel == KMessageBox::ButtonCode::SecondaryAction) { // Overwrite All
- m_policy->setPolicy(OverwritePolicy::Allow);
+ case OverwritePolicy::Skip:
+ return {};
+ case OverwritePolicy::Cancel:
+ qCDebug(KLEOPATRA_LOG) << __func__ << "Error: Called although user canceled operation";
+ return {};
}
- return sel == KMessageBox::ButtonCode::PrimaryAction || sel == KMessageBox::ButtonCode::SecondaryAction;
+
+ qCDebug(KLEOPATRA_LOG) << __func__ << "m_policy.use_count():" << m_policy.use_count();
+ OverwriteDialog::Options options = OverwriteDialog::AllowRename;
+ if (m_policy.use_count() > 1) {
+ options |= OverwriteDialog::MultipleItems | OverwriteDialog::AllowSkip;
+ }
+ OverwriteDialog dialog{m_policy->parentWidget(),
+ i18nc("@title:window", "File Already Exists"),
+ fileName,
+ options};
+ const auto result = static_cast<OverwriteDialog::Result>(dialog.exec());
+ qCDebug(KLEOPATRA_LOG) << __func__ << "result:" << static_cast<int>(result);
+ switch (result) {
+ case OverwriteDialog::Cancel:
+ m_policy->setPolicy(OverwritePolicy::Cancel);
+ return {};
+ case OverwriteDialog::AutoSkip:
+ m_policy->setPolicy(OverwritePolicy::Skip);
+ [[fallthrough]];
+ case OverwriteDialog::Skip:
+ return {};
+ case OverwriteDialog::OverwriteAll:
+ m_policy->setPolicy(OverwritePolicy::Overwrite);
+ [[fallthrough]];
+ case OverwriteDialog::Overwrite:
+ return fileName;
+ case OverwriteDialog::Rename:
+ return dialog.newFileName();
+ case OverwriteDialog::AutoRename: {
+ m_policy->setPolicy(OverwritePolicy::Rename);
+ return suggestFileName(fileName);
+ }
+ default:
+ qCDebug(KLEOPATRA_LOG) << __func__ << "unexpected result:" << result;
+ };
+ return {};
}
void FileOutput::doFinalize()
{
qCDebug(KLEOPATRA_LOG) << this;
struct Remover {
QString file;
~Remover()
{
if (QFile::exists(file)) {
QFile::remove(file);
}
}
} remover;
kleo_assert(m_tmpFile);
if (m_tmpFile->isOpen()) {
m_tmpFile->close();
}
QString tmpFileName = remover.file = m_tmpFile->oldFileName();
m_tmpFile->setAutoRemove(false);
QPointer<QObject> guard = m_tmpFile.get();
m_tmpFile.reset(); // really close the file - needed on Windows for renaming :/
kleo_assert(!guard); // if this triggers, we need to audit for holder of std::shared_ptr<QIODevice>s.
const QFileInfo fi(tmpFileName);
bool qtbug83365_workaround = false;
if (!fi.exists()) {
/* QT Bug 83365 since qt 5.13 causes the filename of temporary files
* in UNC path name directories (unmounted samba shares) to start with
* UNC/ instead of the // that Qt can actually handle for things like
* rename and remove. So if we can't find our temporary file we try
* to workaround that bug. */
qCDebug(KLEOPATRA_LOG) << "failure to find " << tmpFileName;
if (tmpFileName.startsWith(QLatin1String("UNC"))) {
tmpFileName.replace(0, strlen("UNC"), QLatin1Char('/'));
qtbug83365_workaround = true;
}
const QFileInfo fi2(tmpFileName);
if (!fi2.exists()) {
throw Exception(gpg_error(GPG_ERR_EIO),
QStringLiteral("Could not find temporary file \"%1\".").arg(tmpFileName));
}
}
- qCDebug(KLEOPATRA_LOG) << this << " renaming " << tmpFileName << "->" << m_fileName;
+ qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName;
if (QFile::rename(tmpFileName, m_fileName)) {
qCDebug(KLEOPATRA_LOG) << this << "succeeded";
if (!m_attachedInput.expired()) {
m_attachedInput.lock()->outputFinalized();
}
return;
}
qCDebug(KLEOPATRA_LOG) << this << "failed";
- if (!obtainOverwritePermission())
- throw Exception(gpg_error(GPG_ERR_CANCELED),
- i18n("Overwriting declined"));
-
- qCDebug(KLEOPATRA_LOG) << this << "going to overwrite" << m_fileName;
-
- if (!QFile::remove(m_fileName))
- throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
- i18n("Could not remove file \"%1\" for overwriting.", m_fileName));
+ {
+ const auto newFileName = obtainOverwritePermission(m_fileName);
+ if (newFileName.isEmpty()) {
+ throw Exception(gpg_error(GPG_ERR_CANCELED),
+ i18n("Overwriting declined"));
+ }
+ if (newFileName == m_fileName) {
+ qCDebug(KLEOPATRA_LOG) << this << "going to remove file for overwriting" << m_fileName;
+ if (!QFile::remove(m_fileName)) {
+ throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
+ xi18nc("@info", "Could not remove file <filename>%1</filename> for overwriting.", m_fileName));
+ }
+ qCDebug(KLEOPATRA_LOG) << this << "removing file to overwrite succeeded";
+ } else {
+ m_fileName = newFileName;
+ }
+ }
- qCDebug(KLEOPATRA_LOG) << this << "succeeded, renaming " << tmpFileName << "->" << m_fileName;
+ qCDebug(KLEOPATRA_LOG) << this << "renaming" << tmpFileName << "->" << m_fileName;
if (QFile::rename(tmpFileName, m_fileName)) {
qCDebug(KLEOPATRA_LOG) << this << "succeeded";
if (!m_attachedInput.expired()) {
m_attachedInput.lock()->outputFinalized();
}
if (qtbug83365_workaround) {
QFile::remove(tmpFileName);
}
return;
}
qCDebug(KLEOPATRA_LOG) << this << "failed";
throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
i18n(R"(Could not rename file "%1" to "%2")",
tmpFileName, m_fileName));
}
std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command)
{
return std::shared_ptr<Output>(new ProcessStdInOutput(command, QStringList(), QDir::current()));
}
std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args)
{
return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, QDir::current()));
}
std::shared_ptr<Output> Output::createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &wd)
{
return std::shared_ptr<Output>(new ProcessStdInOutput(command, args, wd));
}
ProcessStdInOutput::ProcessStdInOutput(const QString &cmd, const QStringList &args, const QDir &wd)
: OutputImplBase(),
m_command(cmd),
m_arguments(args),
m_proc(new redirect_close<QProcess>)
{
qCDebug(KLEOPATRA_LOG) << "cd" << wd.absolutePath() << '\n' << cmd << args;
if (cmd.isEmpty())
throw Exception(gpg_error(GPG_ERR_INV_ARG),
i18n("Command not specified"));
m_proc->setWorkingDirectory(wd.absolutePath());
m_proc->start(cmd, args);
m_proc->setReadChannel(QProcess::StandardError);
if (!m_proc->waitForStarted())
throw Exception(gpg_error(GPG_ERR_EIO),
i18n("Could not start %1 process: %2", cmd, m_proc->errorString()));
}
QString ProcessStdInOutput::label() const
{
if (!m_proc) {
return OutputImplBase::label();
}
// output max. 3 arguments
const QString cmdline = (QStringList(m_command) + m_arguments.mid(0, 3)).join(QLatin1Char(' '));
if (m_arguments.size() > 3) {
return i18nc("e.g. \"Input to tar xf - file1 ...\"", "Input to %1 ...", cmdline);
} else {
return i18nc("e.g. \"Input to tar xf - file\"", "Input to %1", cmdline);
}
}
QString ProcessStdInOutput::doErrorString() const
{
kleo_assert(m_proc);
if (m_proc->exitStatus() == QProcess::NormalExit && m_proc->exitCode() == 0) {
return QString();
}
if (m_proc->error() == QProcess::UnknownError)
return i18n("Error while running %1: %2", m_command,
QString::fromLocal8Bit(m_proc->readAllStandardError().trimmed().constData()));
else {
return i18n("Failed to execute %1: %2", m_command, m_proc->errorString());
}
}
#ifndef QT_NO_CLIPBOARD
std::shared_ptr<Output> Output::createFromClipboard()
{
return std::shared_ptr<Output>(new ClipboardOutput(QClipboard::Clipboard));
}
ClipboardOutput::ClipboardOutput(QClipboard::Mode mode)
: OutputImplBase(),
m_mode(mode),
m_buffer(new QBuffer)
{
errno = 0;
if (!m_buffer->open(QIODevice::WriteOnly))
throw Exception(errno ? gpg_error_from_errno(errno) : gpg_error(GPG_ERR_EIO),
i18n("Could not write to clipboard"));
}
QString ClipboardOutput::label() const
{
switch (m_mode) {
case QClipboard::Clipboard:
return i18n("Clipboard");
case QClipboard::FindBuffer:
return i18n("Find buffer");
case QClipboard::Selection:
return i18n("Selection");
}
return QString();
}
void ClipboardOutput::doFinalize()
{
if (m_buffer->isOpen()) {
m_buffer->close();
}
if (QClipboard *const cb = QApplication::clipboard()) {
cb->setText(QString::fromUtf8(m_buffer->data()));
} else
throw Exception(gpg_error(GPG_ERR_EIO),
i18n("Could not find clipboard"));
}
#endif // QT_NO_CLIPBOARD
Output::~Output() {}
OutputInput::OutputInput(const std::shared_ptr<FileOutput> &output)
: m_output(output)
, m_ioDevice(new QFile(output->fileName()))
{
}
std::shared_ptr<Input> Input::createFromOutput(const std::shared_ptr<Output> &output)
{
if (auto fo = std::dynamic_pointer_cast<FileOutput>(output)) {
auto input = std::shared_ptr<OutputInput>(new OutputInput(fo));
fo->attachInput(input);
return input;
} else {
return {};
}
}
std::shared_ptr<Output> Output::createFromByteArray(QByteArray *data, const QString &label)
{
auto ret = std::shared_ptr<ByteArrayOutput>(new ByteArrayOutput(data));
ret->setLabel(label);
return ret;
}
diff --git a/src/utils/output.h b/src/utils/output.h
index 9fee4747b..8feced6f0 100644
--- a/src/utils/output.h
+++ b/src/utils/output.h
@@ -1,81 +1,83 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/output.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <assuan.h> // for assuan_fd_t
#include <utils/pimpl_ptr.h>
#include <QString>
#include <QStringList>
#include <memory>
class QIODevice;
class QDir;
class QWidget;
namespace Kleo
{
class OverwritePolicy
{
public:
enum Policy {
- Allow,
- Deny,
- Ask
+ Ask,
+ Overwrite,
+ Rename,
+ Skip,
+ Cancel,
};
explicit OverwritePolicy(QWidget *parent, Policy initialPolicy = Ask);
~OverwritePolicy();
Policy policy() const;
void setPolicy(Policy);
QWidget *parentWidget() const;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
class Output
{
public:
virtual ~Output();
virtual void setLabel(const QString &label) = 0;
virtual QString label() const = 0;
virtual std::shared_ptr<QIODevice> ioDevice() const = 0;
virtual QString errorString() const = 0;
virtual bool isFinalized() const = 0;
virtual void finalize() = 0;
virtual void cancel() = 0;
virtual bool binaryOpt() const = 0;
virtual void setBinaryOpt(bool value) = 0;
/** Whether or not the output failed. */
virtual bool failed() const { return false; }
virtual QString fileName() const { return {}; }
static std::shared_ptr<Output> createFromFile(const QString &fileName, const std::shared_ptr<OverwritePolicy> &);
static std::shared_ptr<Output> createFromFile(const QString &fileName, bool forceOverwrite);
static std::shared_ptr<Output> createFromPipeDevice(assuan_fd_t fd, const QString &label);
static std::shared_ptr<Output> createFromProcessStdIn(const QString &command);
static std::shared_ptr<Output> createFromProcessStdIn(const QString &command, const QStringList &args);
static std::shared_ptr<Output> createFromProcessStdIn(const QString &command, const QStringList &args, const QDir &workingDirectory);
#ifndef QT_NO_CLIPBOARD
static std::shared_ptr<Output> createFromClipboard();
#endif
static std::shared_ptr<Output> createFromByteArray(QByteArray *data, const QString &label);
};
}
diff --git a/src/utils/overwritedialog.cpp b/src/utils/overwritedialog.cpp
new file mode 100644
index 000000000..7f711280b
--- /dev/null
+++ b/src/utils/overwritedialog.cpp
@@ -0,0 +1,311 @@
+/* utils/overwritedialog.cpp
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2023 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 "overwritedialog.h"
+
+#include <QFileInfo>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMimeDatabase>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include <KFileUtils>
+#include <KGuiItem>
+#include <KLocalizedString>
+#include <KMessageBox>
+#include <KSeparator>
+#include <KStandardGuiItem>
+
+using namespace Kleo;
+
+class OverwriteDialog::Private
+{
+ Kleo::OverwriteDialog *q;
+
+public:
+ Private(const QString &filePath, Kleo::OverwriteDialog *qq)
+ : q{qq}
+ , fileInfo{filePath}
+ {
+ }
+
+ void setRenameBoxText(const QString &fileName);
+ void enableRenameButton(const QString &newName);
+ void suggestNewNamePressed();
+ QString newFileName() const;
+
+ void renamePressed();
+ void renameAllPressed();
+ void skipPressed();
+ void skipAllPressed();
+ void overwritePressed();
+ void overwriteAllPressed();
+ void cancelPressed();
+
+ void done(Result result)
+ {
+ q->done(static_cast<int>(result));
+ }
+
+ QLineEdit *newNameEdit = nullptr;
+ QPushButton *suggestNewNameBtn = nullptr;
+ QPushButton *renameBtn = nullptr;
+ QPushButton *renameAllBtn = nullptr;
+ QPushButton *skipBtn = nullptr;
+ QPushButton *skipAllBtn = nullptr;
+ QPushButton *overwriteBtn = nullptr;
+ QPushButton *overwriteAllBtn = nullptr;
+ QPushButton *cancelBtn = nullptr;
+ QFileInfo fileInfo;
+};
+
+void OverwriteDialog::Private::setRenameBoxText(const QString &fileName)
+{
+ // sets the text in file name line edit box, selecting the filename (but not the extension if there is one).
+ QMimeDatabase db;
+ const auto extension = db.suffixForFileName(fileName);
+ newNameEdit->setText(fileName);
+
+ if (!extension.isEmpty()) {
+ const int selectionLength = fileName.length() - extension.length() - 1;
+ newNameEdit->setSelection(0, selectionLength);
+ } else {
+ newNameEdit->selectAll();
+ }
+}
+
+void OverwriteDialog::Private::enableRenameButton(const QString &newName)
+{
+ if (!newName.isEmpty() && (newName != fileInfo.fileName())) {
+ renameBtn->setEnabled(true);
+ renameBtn->setDefault(true);
+
+ if (renameAllBtn) {
+ renameAllBtn->setEnabled(false);
+ }
+ if (overwriteBtn) {
+ overwriteBtn->setEnabled(false);
+ }
+ if (overwriteAllBtn) {
+ overwriteAllBtn->setEnabled(false);
+ }
+ } else {
+ renameBtn->setEnabled(false);
+
+ if (renameAllBtn) {
+ renameAllBtn->setEnabled(true);
+ }
+ if (overwriteBtn) {
+ overwriteBtn->setEnabled(true);
+ }
+ if (overwriteAllBtn) {
+ overwriteAllBtn->setEnabled(true);
+ }
+ }
+}
+
+void OverwriteDialog::Private::suggestNewNamePressed()
+{
+ if (!newNameEdit->text().isEmpty()) {
+ setRenameBoxText(KFileUtils::suggestName(QUrl::fromLocalFile(fileInfo.absolutePath()), newNameEdit->text()));
+ } else {
+ setRenameBoxText(KFileUtils::suggestName(QUrl::fromLocalFile(fileInfo.absolutePath()), fileInfo.fileName()));
+ }
+}
+
+QString OverwriteDialog::Private::newFileName() const
+{
+ return fileInfo.path() + QLatin1Char{'/'} + newNameEdit->text();
+}
+
+void OverwriteDialog::Private::renamePressed()
+{
+ if (newNameEdit->text().isEmpty()) {
+ return;
+ }
+ const auto fileName = newFileName();
+ if (QFileInfo::exists(fileName)) {
+ KMessageBox::error(q, xi18nc("@info", "The file <filename>%1</filename> already exists. Please enter a different file name.", fileName));
+ return;
+ }
+ done(Result::Rename);
+}
+
+void OverwriteDialog::Private::renameAllPressed()
+{
+ done(Result::AutoRename);
+}
+
+void OverwriteDialog::Private::skipPressed()
+{
+ done(Result::Skip);
+}
+
+void OverwriteDialog::Private::skipAllPressed()
+{
+ done(Result::AutoSkip);
+}
+
+void OverwriteDialog::Private::overwritePressed()
+{
+ done(Result::Overwrite);
+}
+
+void OverwriteDialog::Private::overwriteAllPressed()
+{
+ done(Result::OverwriteAll);
+}
+
+void OverwriteDialog::Private::cancelPressed()
+{
+ done(Result::Cancel);
+}
+
+OverwriteDialog::OverwriteDialog(QWidget *parent, const QString &title, const QString &fileName, Options options)
+ : QDialog{parent}
+ , d{new Private{fileName, this}}
+{
+ setObjectName(QStringLiteral("Kleo::OverwriteDialog"));
+
+ setWindowTitle(title);
+
+ auto mainLayout = new QVBoxLayout{this};
+ mainLayout->addStrut(400); // makes dlg at least that wide
+
+ mainLayout->addWidget(new QLabel{xi18nc("@info", "The file <filename>%1</filename> already exists.", fileName), this});
+
+ if (options & AllowRename) {
+ mainLayout->addSpacing(15);
+
+ auto label = new QLabel{i18nc("@label", "Rename:"), this};
+ mainLayout->addWidget(label);
+
+ auto hbox = new QHBoxLayout;
+
+ d->newNameEdit = new QLineEdit{this};
+ label->setBuddy(d->newNameEdit);
+ hbox->addWidget(d->newNameEdit);
+
+ d->suggestNewNameBtn = new QPushButton{i18nc("@action:button", "Suggest New Name"), this};
+ d->suggestNewNameBtn->setToolTip(i18nc("@info:tooltip", "Suggest a file name that does not already exist."));
+ hbox->addWidget(d->suggestNewNameBtn);
+
+ mainLayout->addLayout(hbox);
+ }
+
+ mainLayout->addWidget(new KSeparator{this});
+
+ auto buttonLayout = new QHBoxLayout;
+
+ if (options & AllowRename) {
+ d->renameBtn = new QPushButton{i18nc("@action:button", "Rename"), this};
+ d->renameBtn->setToolTip(i18nc("@info:tooltip", "Save the file with the given name."));
+ d->renameBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
+ d->renameBtn->setEnabled(false);
+ buttonLayout->addWidget(d->renameBtn);
+
+ if (options & MultipleItems) {
+ d->renameAllBtn = new QPushButton{i18nc("@action:button", "Rename All"), this};
+ d->renameAllBtn->setIcon(d->renameBtn->icon());
+ d->renameAllBtn->setToolTip(
+ i18nc("@info:tooltip", "Automatically save all files that would overwrite an already existing file with a different name."));
+ buttonLayout->addWidget(d->renameAllBtn);
+ }
+ }
+
+ if ((options & AllowSkip) && (options & MultipleItems)) {
+ d->skipBtn = new QPushButton{i18nc("@action:button", "Skip"), this};
+ d->skipBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-next-skip")));
+ d->skipBtn->setToolTip(i18nc("@info:tooltip", "Do not write this file, skip to the next one instead."));
+ buttonLayout->addWidget(d->skipBtn);
+
+ d->skipAllBtn = new QPushButton{i18nc("@action:button", "Skip All"), this};
+ d->skipAllBtn->setIcon(d->skipBtn->icon());
+ d->skipAllBtn->setToolTip(i18nc("@info:tooltip", "Do not write this file and any other files that would overwrite an already existing file."));
+ buttonLayout->addWidget(d->skipAllBtn);
+ }
+
+ d->overwriteBtn = new QPushButton{i18nc("@action:button", "Overwrite"), this};
+ d->overwriteBtn->setIcon(KStandardGuiItem::overwrite().icon());
+ d->overwriteBtn->setToolTip(i18nc("@info:tooltip", "Overwrite the existing file."));
+ buttonLayout->addWidget(d->overwriteBtn);
+
+ if (options & MultipleItems) {
+ d->overwriteAllBtn = new QPushButton{i18nc("@action:button", "Overwrite All"), this};
+ d->overwriteAllBtn->setIcon(d->overwriteBtn->icon());
+ d->overwriteAllBtn->setToolTip(i18nc("@info:tooltip", "Overwrite the existing file and any other files that already exist."));
+ buttonLayout->addWidget(d->overwriteAllBtn);
+ }
+
+ d->cancelBtn = new QPushButton{this};
+ KGuiItem::assign(d->cancelBtn, KStandardGuiItem::cancel());
+ d->cancelBtn->setDefault(true);
+ buttonLayout->addWidget(d->cancelBtn);
+
+ mainLayout->addLayout(buttonLayout);
+
+ if (d->newNameEdit) {
+ d->setRenameBoxText(d->fileInfo.fileName());
+ connect(d->newNameEdit, &QLineEdit::textChanged, this, [this](const QString &text) {
+ d->enableRenameButton(text);
+ });
+ connect(d->suggestNewNameBtn, &QAbstractButton::clicked, this, [this]() {
+ d->suggestNewNamePressed();
+ });
+ }
+ if (d->renameBtn) {
+ connect(d->renameBtn, &QAbstractButton::clicked, this, [this]() {
+ d->renamePressed();
+ });
+ }
+ if (d->renameAllBtn) {
+ connect(d->renameAllBtn, &QAbstractButton::clicked, this, [this]() {
+ d->renameAllPressed();
+ });
+ }
+ if (d->skipBtn) {
+ connect(d->skipBtn, &QAbstractButton::clicked, this, [this]() {
+ d->skipPressed();
+ });
+ connect(d->skipAllBtn, &QAbstractButton::clicked, this, [this]() {
+ d->skipAllPressed();
+ });
+ }
+ connect(d->overwriteBtn, &QAbstractButton::clicked, this, [this]() {
+ d->overwritePressed();
+ });
+ if (d->overwriteAllBtn) {
+ connect(d->overwriteAllBtn, &QAbstractButton::clicked, this, [this]() {
+ d->overwriteAllPressed();
+ });
+ }
+ connect(d->cancelBtn, &QAbstractButton::clicked, this, [this]() {
+ d->cancelPressed();
+ });
+
+ if (d->newNameEdit) {
+ d->newNameEdit->setFocus();
+ }
+
+ resize(sizeHint());
+}
+
+OverwriteDialog::~OverwriteDialog() = default;
+
+QString OverwriteDialog::newFileName() const
+{
+ if (result() == Result::Rename) {
+ return d->newFileName();
+ }
+ return {};
+}
diff --git a/src/utils/overwritedialog.h b/src/utils/overwritedialog.h
new file mode 100644
index 000000000..93cfbbf26
--- /dev/null
+++ b/src/utils/overwritedialog.h
@@ -0,0 +1,80 @@
+/* utils/overwritedialog.h
+
+ This file is part of Kleopatra, the KDE keymanager
+ SPDX-FileCopyrightText: 2023 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+#pragma once
+
+#include <QDialog>
+
+#include <memory>
+
+namespace Kleo
+{
+
+/**
+ * @class Kleo::OverwriteDialog overwritedialog.h <overwritedialog.h>
+ *
+ * This dialog can be shown when you realize that a file you want to write
+ * already exists and you want to offer the user the choice to either Rename,
+ * Overwrite, or Skip.
+ */
+class OverwriteDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * @see Options
+ */
+ enum Option {
+ AllowRename = 1, ///< Allow the user to enter a different file name.
+ AllowSkip = 2, ///< Offer a "Skip" button, to skip other files too. Requires MultipleItems.
+ MultipleItems =
+ 4, ///< Set if the current operation concerns multiple files, so it makes sense to offer buttons that apply the user's choice to all files/folders.
+ };
+ /**
+ * Stores a combination of #Option values.
+ */
+ Q_DECLARE_FLAGS(Options, Option)
+
+ enum Result {
+ Cancel = 0, // = QDialog::Rejected
+ Overwrite = 1,
+ OverwriteAll = 2,
+ Rename = 3,
+ AutoRename = 4,
+ Skip = 5,
+ AutoSkip = 6,
+ };
+
+ /**
+ * Construct an "overwrite" dialog to let the user know that the file @p fileName is about to be overwritten.
+ *
+ * @param parent parent widget
+ * @param title the title for the dialog
+ * @param fileName the path of the file that already exists
+ * @param options parameters for the dialog (which buttons to show...),
+ */
+ OverwriteDialog(QWidget *parent, const QString &title, const QString &fileName, Options options);
+
+ ~OverwriteDialog() override;
+
+ /**
+ * Returns the new file name to use if the user selected the Rename option.
+ * Otherwise, returns an empty string.
+ *
+ * @return the new file name or an empty string
+ */
+ QString newFileName() const;
+
+private:
+ class Private;
+ std::unique_ptr<Private> const d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(OverwriteDialog::Options)
+
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 25, 3:00 PM (1 d, 14 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d9/c7/087d66e22bfdd76ebe81a174487d

Event Timeline