File indexing completed on 2024-06-23 05:13:41
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 commands/genrevokecommand.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik 0006 SPDX-FileContributor: Intevation GmbH 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include <config-kleopatra.h> 0012 0013 #include "genrevokecommand.h" 0014 0015 #include <utils/applicationstate.h> 0016 0017 #include <Libkleo/Formatting> 0018 #include <Libkleo/GnuPG> 0019 0020 #include <gpgme++/key.h> 0021 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 0025 #include <QFile> 0026 #include <QFileDialog> 0027 #include <QProcess> 0028 #include <QTextStream> 0029 0030 #include "command_p.h" 0031 #include "kleopatra_debug.h" 0032 0033 using namespace Kleo; 0034 using namespace Kleo::Commands; 0035 using namespace GpgME; 0036 0037 GenRevokeCommand::GenRevokeCommand(QAbstractItemView *v, KeyListController *c) 0038 : GnuPGProcessCommand(v, c) 0039 { 0040 } 0041 0042 GenRevokeCommand::GenRevokeCommand(KeyListController *c) 0043 : GnuPGProcessCommand(c) 0044 { 0045 } 0046 0047 GenRevokeCommand::GenRevokeCommand(const Key &key) 0048 : GnuPGProcessCommand(key) 0049 { 0050 } 0051 0052 // Fixup the revocation certificate similar to GnuPG 0053 void GenRevokeCommand::postSuccessHook(QWidget *parentWidget) 0054 { 0055 QFile f(mOutputFileName); 0056 0057 if (!f.open(QIODevice::ReadOnly)) { 0058 // Should never happen because in this case we would not have had a success. 0059 KMessageBox::error(parentWidget, QStringLiteral("Failed to access the created output file."), errorCaption()); 0060 return; 0061 } 0062 const QString revCert = QString::fromLocal8Bit(f.readAll()); 0063 f.close(); 0064 0065 if (!f.open(QIODevice::WriteOnly)) { 0066 KMessageBox::error(parentWidget, QStringLiteral("Failed to write to the created output file."), errorCaption()); 0067 return; 0068 } 0069 0070 QTextStream s(&f); 0071 0072 s << i18n("This is a revocation certificate for the OpenPGP key:") << "\n\n" // 0073 << " " << Formatting::prettyNameAndEMail(d->key()) << "\n" // 0074 << "Fingerprint: " << d->key().primaryFingerprint() << "\n\n" 0075 << i18n( 0076 "A revocation certificate is a kind of \"kill switch\" to publicly\n" 0077 "declare that a key shall not anymore be used. It is not possible\n" 0078 "to retract such a revocation certificate once it has been published.") 0079 << "\n\n" 0080 << i18n( 0081 "Use it to revoke this key in case of a compromise or loss of\n" 0082 "the secret key.") 0083 << "\n\n" 0084 << i18n( 0085 "To avoid an accidental use of this file, a colon has been inserted\n" 0086 "before the 5 dashes below. Remove this colon with a text editor\n" 0087 "before importing and publishing this revocation certificate.") 0088 << "\n\n:" << revCert; 0089 s.flush(); 0090 qCDebug(KLEOPATRA_LOG) << "revocation certificate stored as:" << mOutputFileName; 0091 0092 f.close(); 0093 KMessageBox::information(d->parentWidgetOrView(), 0094 i18nc("@info", 0095 "Certificate successfully created.<br><br>" 0096 "Note:<br>To prevent accidental import of the revocation<br>" 0097 "it is required to manually edit the certificate<br>" 0098 "before it can be imported."), 0099 i18nc("@title:window", "Revocation certificate created")); 0100 } 0101 0102 /* Well not much to do with GnuPGProcessCommand anymore I guess.. */ 0103 void GenRevokeCommand::doStart() 0104 { 0105 auto proposedFileName = ApplicationState::lastUsedExportDirectory() + u'/' + QString::fromLatin1(d->key().primaryFingerprint()) + QLatin1StringView{".rev"}; 0106 while (mOutputFileName.isEmpty()) { 0107 mOutputFileName = QFileDialog::getSaveFileName(d->parentWidgetOrView(), 0108 i18n("Generate revocation certificate"), 0109 proposedFileName, 0110 QStringLiteral("%1 (*.rev)").arg(i18n("Revocation Certificates ")), 0111 {}, 0112 QFileDialog::DontConfirmOverwrite); 0113 if (mOutputFileName.isEmpty()) { 0114 d->finished(); 0115 return; 0116 } 0117 if (!mOutputFileName.endsWith(QLatin1StringView(".rev"))) { 0118 mOutputFileName += QLatin1StringView(".rev"); 0119 } 0120 const QFileInfo fi{mOutputFileName}; 0121 if (fi.exists()) { 0122 auto sel = KMessageBox::questionTwoActions(d->parentWidgetOrView(), 0123 xi18n("The file <filename>%1</filename> already exists. Do you wish to overwrite it?", fi.fileName()), 0124 i18nc("@title:window", "Overwrite File?"), 0125 KStandardGuiItem::overwrite(), 0126 KStandardGuiItem::cancel(), 0127 {}, 0128 KMessageBox::Notify | KMessageBox::Dangerous); 0129 if (sel == KMessageBox::ButtonCode::SecondaryAction) { 0130 proposedFileName = mOutputFileName; 0131 mOutputFileName.clear(); 0132 } 0133 } 0134 } 0135 ApplicationState::setLastUsedExportDirectory(mOutputFileName); 0136 0137 auto proc = process(); 0138 // We do custom io 0139 disconnect(m_procReadyReadStdErrConnection); 0140 proc->setReadChannel(QProcess::StandardOutput); 0141 0142 GnuPGProcessCommand::doStart(); 0143 0144 connect(proc, &QProcess::readyReadStandardOutput, this, [proc]() { 0145 while (proc->canReadLine()) { 0146 const QString line = QString::fromUtf8(proc->readLine()).trimmed(); 0147 // Command-fd is a stable interface, while this is all kind of hacky we 0148 // are on a deadline :-/ 0149 if (line == QLatin1StringView("[GNUPG:] GET_BOOL gen_revoke.okay")) { 0150 proc->write("y\n"); 0151 } else if (line == QLatin1StringView("[GNUPG:] GET_LINE ask_revocation_reason.code")) { 0152 proc->write("0\n"); 0153 } else if (line == QLatin1StringView("[GNUPG:] GET_LINE ask_revocation_reason.text")) { 0154 proc->write("\n"); 0155 } else if (line == QLatin1StringView("[GNUPG:] GET_BOOL openfile.overwrite.okay")) { 0156 // We asked before 0157 proc->write("y\n"); 0158 } else if (line == QLatin1StringView("[GNUPG:] GET_BOOL ask_revocation_reason.okay")) { 0159 proc->write("y\n"); 0160 } 0161 } 0162 }); 0163 } 0164 0165 QStringList GenRevokeCommand::arguments() const 0166 { 0167 const Key key = d->key(); 0168 0169 return { 0170 gpgPath(), 0171 QStringLiteral("--command-fd"), 0172 QStringLiteral("0"), 0173 QStringLiteral("--status-fd"), 0174 QStringLiteral("1"), 0175 QStringLiteral("-o"), 0176 mOutputFileName, 0177 QStringLiteral("--gen-revoke"), 0178 QLatin1StringView(key.primaryFingerprint()), 0179 }; 0180 } 0181 0182 QString GenRevokeCommand::errorCaption() const 0183 { 0184 return i18nc("@title:window", "Error creating revocation certificate"); 0185 } 0186 0187 QString GenRevokeCommand::crashExitMessage(const QStringList &) const 0188 { 0189 // We show a success message so a failure is either the user aborted 0190 // or a bug. 0191 qCDebug(KLEOPATRA_LOG) << "Crash exit of GenRevokeCommand"; 0192 return QString(); 0193 } 0194 0195 QString GenRevokeCommand::errorExitMessage(const QStringList &) const 0196 { 0197 // We show a success message so a failure is either the user aborted 0198 // or a bug. 0199 qCDebug(KLEOPATRA_LOG) << "Error exit of GenRevokeCommand"; 0200 return QString(); 0201 } 0202 0203 #include "moc_genrevokecommand.cpp"