9#include "composerjob.h"
11#include "imagescaling/imagescaling.h"
12#include "imagescaling/imagescalingutils.h"
13#include "job/attachmentjob.h"
14#include "job/autocryptheadersjob.h"
15#include "job/encryptjob.h"
16#include "job/itipjob.h"
17#include "job/jobbase_p.h"
18#include "job/maintextjob.h"
19#include "job/multipartjob.h"
20#include "job/signencryptjob.h"
21#include "job/signjob.h"
22#include "job/skeletonmessagejob.h"
23#include "job/transparentjob.h"
24#include "part/globalpart.h"
25#include "part/infopart.h"
26#include "part/itippart.h"
27#include "part/textpart.h"
29#include "messagecomposer_debug.h"
30#include <KLocalizedString>
35class MessageComposer::ComposerJobPrivate :
public JobBasePrivate
38 explicit ComposerJobPrivate(ComposerJob *qq)
43 ~ComposerJobPrivate()
override
45 delete skeletonMessage;
52 [[nodiscard]] QList<ContentJobBase *> createEncryptJobs(ContentJobBase *contentJob,
bool sign);
53 void contentJobFinished(KJob *job);
54 void composeWithLateAttachments(KMime::Message *headers,
55 KMime::Content *content,
57 const std::vector<GpgME::Key> &keys,
58 const QStringList &recipients);
59 void attachmentsFinished(KJob *job);
61 void composeFinalStep(KMime::Content *headers, KMime::Content *content);
64 QList<QPair<QStringList, std::vector<GpgME::Key>>> encData;
65 GpgME::Key senderEncryptionKey;
66 std::vector<GpgME::Key> signers;
71 QList<KMime::Message::Ptr> resultMessages;
73 Kleo::CryptoMessageFormat format;
76 GlobalPart *globalPart =
nullptr;
77 InfoPart *infoPart =
nullptr;
78 TextPart *textPart =
nullptr;
79 ItipPart *itipPart =
nullptr;
82 KMime::Message *skeletonMessage =
nullptr;
85 bool finished =
false;
88 bool noCrypto =
false;
89 bool autoSaving =
false;
90 bool autocryptEnabled =
false;
91 Q_DECLARE_PUBLIC(ComposerJob)
94void ComposerJobPrivate::init()
100 globalPart =
new GlobalPart(q);
101 infoPart =
new InfoPart(q);
106void ComposerJobPrivate::doStart()
113void ComposerJobPrivate::composeStep1()
118 auto skeletonJob =
new SkeletonMessageJob(infoPart, globalPart, q);
125 Q_ASSERT(skeletonMessage ==
nullptr);
126 skeletonMessage = skeletonJob->message();
127 Q_ASSERT(skeletonMessage);
128 skeletonMessage->assemble();
132 q->addSubjob(skeletonJob);
133 skeletonJob->start();
136void ComposerJobPrivate::composeStep2()
140 ContentJobBase *mainJob =
nullptr;
141 ContentJobBase *mainContentJob =
nullptr;
142 Q_ASSERT(textPart || itipPart);
143 if (textPart && !itipPart) {
144 mainContentJob =
new MainTextJob(textPart, q);
145 }
else if (!textPart && itipPart) {
146 mainContentJob =
new ItipJob(itipPart, q);
149 Q_ASSERT(!textPart || !itipPart);
152 if ((sign || encrypt) && format & Kleo::InlineOpenPGPFormat) {
153 qCDebug(MESSAGECOMPOSER_LOG) <<
"sending to sign/enc inline job!";
158 const QList<ContentJobBase *> jobs = createEncryptJobs(mainContentJob, sign);
159 for (ContentJobBase *subJob : jobs) {
160 if (attachmentParts.isEmpty()) {
164 auto multipartJob =
new MultipartJob(q);
165 multipartJob->setMultipartSubtype(
"mixed");
166 multipartJob->appendSubjob(subJob);
168 multipartJob->appendSubjob(
new AttachmentJob(part));
170 mainJob = multipartJob;
173 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
174 q->addSubjob(mainJob);
177 auto subJob =
new SignJob(q);
178 subJob->setSigningKeys(signers);
179 subJob->setCryptoMessageFormat(format);
180 subJob->appendSubjob(mainContentJob);
182 if (attachmentParts.isEmpty()) {
186 auto multipartJob =
new MultipartJob(q);
187 multipartJob->setMultipartSubtype(
"mixed");
188 multipartJob->appendSubjob(subJob);
190 multipartJob->appendSubjob(
new AttachmentJob(part));
192 mainJob = multipartJob;
194 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
195 q->addSubjob(mainJob);
201 qCDebug(MESSAGECOMPOSER_LOG) <<
"main job is null";
206 if (attachmentParts.isEmpty()) {
208 mainJob = mainContentJob;
211 QMutableListIterator<AttachmentPart::Ptr> iter(attachmentParts);
212 while (iter.hasNext()) {
214 qCDebug(MESSAGECOMPOSER_LOG) <<
"Checking attachment crypto policy... signed: " << part->isSigned() <<
" isEncrypted : " << part->isEncrypted();
215 if (!noCrypto && !autoSaving && (sign != part->isSigned() || encrypt != part->isEncrypted())) {
216 qCDebug(MESSAGECOMPOSER_LOG) <<
"got attachment with different crypto policy!";
217 lateAttachmentParts.append(part);
221 auto multipartJob =
new MultipartJob(q);
222 multipartJob->setMultipartSubtype(
"mixed");
223 multipartJob->appendSubjob(mainContentJob);
225 multipartJob->appendSubjob(
new AttachmentJob(part));
227 mainJob = multipartJob;
230 if (autocryptEnabled) {
231 auto autocryptJob =
new AutocryptHeadersJob();
232 autocryptJob->setSkeletonMessage(skeletonMessage);
233 autocryptJob->setGnupgHome(gnupgHome);
234 autocryptJob->appendSubjob(mainJob);
235 autocryptJob->setSenderKey(senderEncryptionKey);
236 if (encrypt && format & Kleo::OpenPGPMIMEFormat) {
237 qDebug() <<
"Add gossip: " << encData[0].first.size() << encData[0].second.size();
238 if (encData[0].first.size() > 1 && encData[0].second.size() > 2) {
239 autocryptJob->setGossipKeys(encData[0].second);
242 mainJob = autocryptJob;
246 auto sJob =
new SignJob(q);
247 sJob->setCryptoMessageFormat(format);
248 sJob->setSigningKeys(signers);
249 sJob->appendSubjob(mainJob);
250 sJob->setSkeletonMessage(skeletonMessage);
255 const auto lstJob = createEncryptJobs(mainJob,
false);
256 for (ContentJobBase *job : lstJob) {
257 auto eJob =
dynamic_cast<EncryptJob *
>(job);
260 eJob->setProtectedHeaders(
false);
262 QObject::connect(job, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
267 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
268 q->addSubjob(mainJob);
274QList<ContentJobBase *> ComposerJobPrivate::createEncryptJobs(
ContentJobBase *contentJob,
bool sign)
278 QList<ContentJobBase *> jobs;
283 qCDebug(MESSAGECOMPOSER_LOG) <<
"starting enc jobs";
284 qCDebug(MESSAGECOMPOSER_LOG) <<
"format:" << format;
285 qCDebug(MESSAGECOMPOSER_LOG) <<
"enc data:" << encData.size();
287 if (encData.isEmpty()) {
288 q->setErrorText(
i18n(
"No key data for recipients found."));
289 q->setError(ComposerJob::IncompleteError);
294 const int encDataSize = encData.size();
296 for (
int i = 0; i < encDataSize; ++i) {
297 QPair<QStringList, std::vector<GpgME::Key>> recipients = encData[i];
298 qCDebug(MESSAGECOMPOSER_LOG) <<
"got first list of recipients:" << recipients.first;
299 ContentJobBase *subJob =
nullptr;
301 auto seJob =
new SignEncryptJob(q);
303 seJob->setCryptoMessageFormat(format);
304 seJob->setSigningKeys(signers);
305 seJob->setEncryptionKeys(recipients.second);
306 seJob->setRecipients(recipients.first);
307 seJob->setSkeletonMessage(skeletonMessage);
311 auto eJob =
new EncryptJob(q);
312 eJob->setCryptoMessageFormat(format);
313 eJob->setEncryptionKeys(recipients.second);
314 eJob->setRecipients(recipients.first);
315 eJob->setSkeletonMessage(skeletonMessage);
316 eJob->setGnupgHome(gnupgHome);
319 qCDebug(MESSAGECOMPOSER_LOG) <<
"subJob" << subJob;
323 qCDebug(MESSAGECOMPOSER_LOG) << jobs.
size();
327void ComposerJobPrivate::contentJobFinished(KJob *job)
332 qCDebug(MESSAGECOMPOSER_LOG) <<
"composing final message";
334 KMime::Message *headers =
nullptr;
335 KMime::Content *resultContent =
nullptr;
336 std::vector<GpgME::Key> keys;
337 QStringList recipients;
339 Q_ASSERT(
dynamic_cast<ContentJobBase *
>(job) ==
static_cast<ContentJobBase *
>(job));
340 auto contentJob =
static_cast<ContentJobBase *
>(job);
344 if (encData.size() > 1) {
345 Q_ASSERT(
dynamic_cast<MessageComposer::AbstractEncryptJob *
>(job));
346 auto eJob =
dynamic_cast<MessageComposer::AbstractEncryptJob *
>(job);
348 keys = eJob->encryptionKeys();
349 recipients = eJob->recipients();
351 resultContent = contentJob->
content();
352 headers =
new KMime::Message;
353 headers->
setHeader(skeletonMessage->from());
354 headers->
setHeader(skeletonMessage->to());
355 headers->
setHeader(skeletonMessage->cc());
356 headers->
setHeader(skeletonMessage->subject());
357 headers->
setHeader(skeletonMessage->date());
358 headers->
setHeader(skeletonMessage->messageID());
360 auto realTo =
new KMime::Headers::Generic(
"X-KMail-EncBccRecipients");
361 realTo->fromUnicodeString(eJob->recipients().join(QLatin1Char(
'%')));
363 qCDebug(MESSAGECOMPOSER_LOG) <<
"got one of multiple messages sending to:" << realTo->asUnicodeString();
364 qCDebug(MESSAGECOMPOSER_LOG) <<
"sending to recipients:" << recipients;
368 if (!encData.isEmpty()) {
369 const auto firstElement = encData.at(0);
370 qCDebug(MESSAGECOMPOSER_LOG) <<
"setting enc data:" << firstElement.first <<
"with num keys:" << firstElement.second.size();
371 keys = firstElement.second;
372 recipients = firstElement.
first;
375 headers = skeletonMessage;
376 resultContent = contentJob->
content();
379 if (lateAttachmentParts.isEmpty()) {
380 composeFinalStep(headers, resultContent);
382 composeWithLateAttachments(headers, resultContent, lateAttachmentParts, keys, recipients);
386void ComposerJobPrivate::composeWithLateAttachments(KMime::Message *headers,
387 KMime::Content *content,
389 const std::vector<GpgME::Key> &keys,
390 const QStringList &recipients)
394 auto multiJob =
new MultipartJob(q);
395 multiJob->setMultipartSubtype(
"mixed");
398 auto tJob =
new MessageComposer::TransparentJob(q);
399 tJob->setContent(content);
400 multiJob->appendSubjob(tJob);
401 multiJob->setExtraContent(headers);
403 qCDebug(MESSAGECOMPOSER_LOG) <<
"attachment encr key size:" << keys.size() <<
" recipients: " << recipients;
407 auto attachJob =
new AttachmentJob(attachment, q);
409 qCDebug(MESSAGECOMPOSER_LOG) <<
"got a late attachment";
410 if (attachment->isSigned() && format) {
411 qCDebug(MESSAGECOMPOSER_LOG) <<
"adding signjob for late attachment";
412 auto sJob =
new SignJob(q);
413 sJob->setContent(
nullptr);
414 sJob->setCryptoMessageFormat(format);
415 sJob->setSigningKeys(signers);
417 sJob->appendSubjob(attachJob);
418 if (attachment->isEncrypted()) {
419 qCDebug(MESSAGECOMPOSER_LOG) <<
"adding sign + encrypt job for late attachment";
420 auto eJob =
new EncryptJob(q);
421 eJob->setCryptoMessageFormat(format);
422 eJob->setEncryptionKeys(keys);
423 eJob->setRecipients(recipients);
425 eJob->appendSubjob(sJob);
427 multiJob->appendSubjob(eJob);
429 qCDebug(MESSAGECOMPOSER_LOG) <<
"Just signing late attachment";
430 multiJob->appendSubjob(sJob);
432 }
else if (attachment->isEncrypted() && format) {
433 qCDebug(MESSAGECOMPOSER_LOG) <<
"just encrypting late attachment";
434 auto eJob =
new EncryptJob(q);
435 eJob->setCryptoMessageFormat(format);
436 eJob->setEncryptionKeys(keys);
437 eJob->setRecipients(recipients);
439 eJob->appendSubjob(attachJob);
440 multiJob->appendSubjob(eJob);
442 qCDebug(MESSAGECOMPOSER_LOG) <<
"attaching plain non-crypto attachment";
443 auto attachSecondJob =
new AttachmentJob(attachment, q);
444 multiJob->appendSubjob(attachSecondJob);
448 QObject::connect(multiJob, SIGNAL(finished(KJob *)), q, SLOT(attachmentsFinished(KJob *)));
450 q->addSubjob(multiJob);
454void ComposerJobPrivate::attachmentsFinished(KJob *job)
459 qCDebug(MESSAGECOMPOSER_LOG) <<
"composing final message with late attachments";
461 Q_ASSERT(
dynamic_cast<ContentJobBase *
>(job));
462 auto contentJob =
static_cast<ContentJobBase *
>(job);
464 KMime::Content *content = contentJob->
content();
467 composeFinalStep(headers, content);
470void ComposerJobPrivate::composeFinalStep(KMime::Content *headers, KMime::Content *content)
479 resultMessage->setContent(allData);
480 resultMessage->parse();
481 resultMessages.append(resultMessage);
484ComposerJob::ComposerJob(QObject *parent)
485 :
JobBase(*new ComposerJobPrivate(this), parent)
491ComposerJob::~ComposerJob() =
default;
495 Q_D(
const ComposerJob);
496 Q_ASSERT(d->finished);
498 return d->resultMessages;
503 Q_D(
const ComposerJob);
504 return d->globalPart;
507InfoPart *ComposerJob::infoPart()
const
509 Q_D(
const ComposerJob);
513TextPart *ComposerJob::textPart()
const
515 Q_D(
const ComposerJob);
517 auto *self =
const_cast<ComposerJob *
>(
this);
518 self->d_func()->textPart =
new TextPart(self);
523void ComposerJob::clearTextPart()
527 d->textPart =
nullptr;
530ItipPart *ComposerJob::itipPart()
const
532 Q_D(
const ComposerJob);
534 auto *self =
const_cast<ComposerJob *
>(
this);
535 self->d_func()->itipPart =
new ItipPart(self);
540void ComposerJob::clearItipPart()
544 d->itipPart =
nullptr;
549 Q_D(
const ComposerJob);
550 return d->attachmentParts;
556 Q_ASSERT(!d->started);
557 Q_ASSERT(!d->attachmentParts.contains(part));
558 if (autoresizeImage) {
559 MessageComposer::Utils resizeUtils;
560 if (resizeUtils.resizeImage(part)) {
561 MessageComposer::ImageScaling autoResizeJob;
562 autoResizeJob.setName(part->name());
563 autoResizeJob.setMimetype(part->mimeType());
567 part->setMimeType(autoResizeJob.
mimetype());
568 part->setName(autoResizeJob.generateNewName());
569 resizeUtils.changeFileName(part);
574 d->attachmentParts.append(part);
580 addAttachmentPart(part, autoresizeImage);
587 Q_ASSERT(!d->started);
588 const int numberOfElements = d->attachmentParts.removeAll(part);
589 if (numberOfElements <= 0) {
590 qCCritical(MESSAGECOMPOSER_LOG) <<
"Unknown attachment part" << part.
data();
596void ComposerJob::setSignAndEncrypt(
const bool doSign,
const bool doEncrypt)
600 d->encrypt = doEncrypt;
603void ComposerJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
610void ComposerJob::setSigningKeys(
const std::vector<GpgME::Key> &signers)
614 d->signers = signers;
617void ComposerJob::setEncryptionKeys(
const QList<QPair<QStringList, std::vector<GpgME::Key>>> &encData)
621 d->encData = encData;
624void ComposerJob::setNoCrypto(
bool noCrypto)
628 d->noCrypto = noCrypto;
631void ComposerJob::setAutocryptEnabled(
bool autocryptEnabled)
635 d->autocryptEnabled = autocryptEnabled;
638void ComposerJob::setSenderEncryptionKey(
const GpgME::Key &senderKey)
642 d->senderEncryptionKey = senderKey;
645void ComposerJob::setGnupgHome(
const QString &path)
652QString ComposerJob::gnupgHome()
const
654 Q_D(
const ComposerJob);
659bool ComposerJob::finished()
const
661 Q_D(
const ComposerJob);
666bool ComposerJob::autoSave()
const
668 Q_D(
const ComposerJob);
670 return d->autoSaving;
677 d->autoSaving = isAutoSave;
680void ComposerJob::start()
686void ComposerJob::slotResult(
KJob *job)
697#include "moc_composerjob.cpp"
QSharedPointer< AttachmentPart > Ptr
Defines a pointer to an attachment object.
QList< Ptr > List
Defines a list of pointers to attachment objects.
virtual void slotResult(KJob *job)
QByteArray encodedContent(bool useCrLf=false) const
void setHeader(Headers::Base *h)
QSharedPointer< Message > Ptr
void setAutoSave(bool isAutoSave)
Sets if this message being composed is an auto-saved message if so, might need different handling,...
The ContentJobBase class.
void start() override
Starts processing this ContentJobBase asynchronously.
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.
KMime::Content * extraContent() const
Get extra content that was previously added.
bool resizeImage()
resizeImage
QByteArray imageArray() const
imageArray
QByteArray mimetype() const
mimetype
bool loadImageFromData(const QByteArray &data)
loadImageFromData
The InfoPart class contains the message header.
A dummy abstract class defining some errors pertaining to the Composer.
A class that encapsulates an attachment.
QString i18n(const char *text, const TYPE &arg...)
QString path(const QString &relativePath)
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
void append(QList< T > &&value)
void reserve(qsizetype size)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)