8#include "job/signjob.h"
10#include "contentjobbase_p.h"
11#include "job/protectedheadersjob.h"
12#include "utils/util_p.h"
14#include <QGpgME/Protocol>
15#include <QGpgME/SignJob>
18#include "messagecomposer_debug.h"
19#include <KMime/Content>
20#include <KMime/Headers>
21#include <KMime/Message>
22#include <Libkleo/Formatting>
24#include <gpgme++/encryptionresult.h>
25#include <gpgme++/global.h>
26#include <gpgme++/signingresult.h>
30class MessageComposer::SignJobPrivate :
public ContentJobBasePrivate
33 SignJobPrivate(SignJob *qq)
34 : ContentJobBasePrivate(qq)
38 KMime::Content *content =
nullptr;
39 KMime::Message *skeletonMessage =
nullptr;
40 std::vector<GpgME::Key> signers;
41 Kleo::CryptoMessageFormat format;
43 bool protectedHeaders =
true;
46 [[nodiscard]]
bool binaryHint(Kleo::CryptoMessageFormat f)
49 case Kleo::SMIMEFormat:
50 case Kleo::SMIMEOpaqueFormat:
53 case Kleo::OpenPGPMIMEFormat:
54 case Kleo::InlineOpenPGPFormat:
59 [[nodiscard]] GpgME::SignatureMode signingMode(Kleo::CryptoMessageFormat f)
62 case Kleo::SMIMEOpaqueFormat:
63 return GpgME::NormalSignatureMode;
64 case Kleo::InlineOpenPGPFormat:
65 return GpgME::Clearsigned;
67 case Kleo::SMIMEFormat:
68 case Kleo::OpenPGPMIMEFormat:
69 return GpgME::Detached;
73 Q_DECLARE_PUBLIC(SignJob)
76SignJob::SignJob(
QObject *parent)
81SignJob::~SignJob() =
default;
90void SignJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
95 Q_ASSERT(format == Kleo::OpenPGPMIMEFormat || format == Kleo::InlineOpenPGPFormat || format == Kleo::SMIMEFormat || format == Kleo::SMIMEOpaqueFormat);
99void SignJob::setSigningKeys(
const std::vector<GpgME::Key> &signers)
103 d->signers = signers;
106void SignJob::setSkeletonMessage(KMime::Message *skeletonMessage)
110 d->skeletonMessage = skeletonMessage;
113void SignJob::setProtectedHeaders(
bool protectedHeaders)
117 d->protectedHeaders = protectedHeaders;
120KMime::Content *SignJob::origContent()
127void SignJob::doStart()
130 Q_ASSERT(d->resultContent ==
nullptr);
132 if (d->protectedHeaders && d->skeletonMessage && d->format & Kleo::OpenPGPMIMEFormat) {
133 auto pJob =
new ProtectedHeadersJob;
134 pJob->setContent(d->content);
135 pJob->setSkeletonMessage(d->skeletonMessage);
136 pJob->setObvoscate(
false);
141 d->content = pJob->content();
149void SignJob::slotResult(KJob *job)
152 ContentJobBase::slotResult(job);
156 auto pjob =
static_cast<ProtectedHeadersJob *
>(
subjobs().last());
160 pjob->setContent(cjob->content());
164 ContentJobBase::slotResult(job);
167void SignJob::process()
170 Q_ASSERT(d->resultContent ==
nullptr);
175 Q_ASSERT(d->subjobContents.size() == 1);
176 d->content = d->subjobContents.constFirst();
181 const QGpgME::Protocol *proto =
nullptr;
182 if (d->format & Kleo::AnyOpenPGP) {
183 proto = QGpgME::openpgp();
184 }
else if (d->format & Kleo::AnySMIME) {
185 proto = QGpgME::smime();
190 qCDebug(MESSAGECOMPOSER_LOG) <<
"creating signJob from:" << proto->name() << proto->displayName();
192 QByteArray signature;
194 d->content->assemble();
199 if (d->format & Kleo::InlineOpenPGPFormat) {
201 }
else if (!(d->format & Kleo::SMIMEOpaqueFormat)) {
207 const auto cte = d->content->contentTransferEncoding(
false);
208 const auto encoding = cte ? cte->encoding() : KMime::Headers::CE7Bit;
209 if ((encoding == KMime::Headers::CEquPr || encoding == KMime::Headers::CE7Bit) && !d->content->contentType(
false)) {
210 QByteArray body = d->content->encodedBody();
211 bool changed =
false;
212 constexpr auto search = std::to_array<QByteArrayView>({
"From ",
"from ",
"-"});
213 constexpr auto replacements = std::to_array<QByteArrayView>({
"From=20",
"from=20",
"=2D"});
215 if (encoding == KMime::Headers::CE7Bit) {
216 for (
size_t i = 0, total = search.size(); i < total; ++i) {
217 const auto pos = body.
indexOf(search[i]);
218 if (pos == 0 || (pos > 0 && body.
at(pos - 1) ==
'\n')) {
224 d->content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
225 d->content->assemble();
226 body = d->content->encodedBody();
230 for (
size_t i = 0, total = search.size(); i < total; ++i) {
231 const auto pos = body.
indexOf(search[i]);
232 if (pos == 0 || (pos > 0 && body.
at(pos - 1) ==
'\n')) {
234 body.
replace(pos, search[i].size(), replacements[i]);
239 qCDebug(MESSAGECOMPOSER_LOG) <<
"Content changed";
240 d->content->setEncodedBody(body);
244 content = KMime::LFtoCRLF(d->content->encodedContent());
246 content = d->content->encodedContent();
249 QGpgME::SignJob *job(proto->signJob(!d->binaryHint(d->format), d->format == Kleo::InlineOpenPGPFormat));
252 &QGpgME::SignJob::result,
254 [
this, d](
const GpgME::SigningResult &
result,
const QByteArray &signature,
const QString &auditLogAsHtml,
const GpgME::Error &auditLogError) {
255 Q_UNUSED(auditLogAsHtml)
256 Q_UNUSED(auditLogError)
257 if (
result.error().code()) {
258 qCDebug(MESSAGECOMPOSER_LOG) <<
"signing failed:" << Kleo::Formatting::errorAsString(
result.error());
266 QByteArray signatureHashAlgo =
result.createdSignature(0).hashAlgorithmAsString();
267 d->resultContent = MessageComposer::Util::composeHeadersAndBody(d->content, signature, d->format,
true, signatureHashAlgo);
281#include "moc_signjob.cpp"
const QList< KJob * > & subjobs() const
void setErrorText(const QString &errorText)
void setError(int errorCode)
virtual Q_SCRIPTABLE void start()=0
KJob(QObject *parent=nullptr)
The ContentJobBase class.
virtual void doStart()
Reimplement to do additional stuff before processing children, such as adding more subjobs.
bool appendSubjob(ContentJobBase *job)
This is meant to be used instead of KCompositeJob::addSubjob(), making it possible to add subjobs fro...
KMime::Content * content() const
Get the resulting KMime::Content that the ContentJobBase has generated.
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
char at(qsizetype i) const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)