Messagelib

composer.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
3 SPDX-FileCopyrightText: 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
4 SPDX-FileCopyrightText: 2009 Leo Franchi <lfranchi@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "composer.h"
10
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"
28
29#include "messagecomposer_debug.h"
30#include <KLocalizedString>
31
32using namespace MessageComposer;
34
35class MessageComposer::ComposerPrivate : public JobBasePrivate
36{
37public:
38 explicit ComposerPrivate(Composer *qq)
39 : JobBasePrivate(qq)
40 {
41 }
42
43 ~ComposerPrivate() override
44 {
45 delete skeletonMessage;
46 }
47
48 void init();
49 void doStart(); // slot
50 void composeStep1();
51 void composeStep2();
52 [[nodiscard]] QList<ContentJobBase *> createEncryptJobs(ContentJobBase *contentJob, bool sign);
53 void contentJobFinished(KJob *job); // slot
54 void composeWithLateAttachments(KMime::Message *headers,
55 KMime::Content *content,
56 const AttachmentPart::List &parts,
57 const std::vector<GpgME::Key> &keys,
58 const QStringList &recipients);
59 void attachmentsFinished(KJob *job); // slot
60
61 void composeFinalStep(KMime::Content *headers, KMime::Content *content);
62
63 QString gnupgHome;
65 GpgME::Key senderEncryptionKey;
66 std::vector<GpgME::Key> signers;
67 AttachmentPart::List attachmentParts;
68 // attachments with different sign/encrypt settings from
69 // main message body. added at the end of the process
70 AttachmentPart::List lateAttachmentParts;
71 QList<KMime::Message::Ptr> resultMessages;
72
73 Kleo::CryptoMessageFormat format;
74
75 // Stuff that the application plays with.
76 GlobalPart *globalPart = nullptr;
77 InfoPart *infoPart = nullptr;
78 TextPart *textPart = nullptr;
79 ItipPart *itipPart = nullptr;
80
81 // Stuff that we play with.
82 KMime::Message *skeletonMessage = nullptr;
83
84 bool started = false;
85 bool finished = false;
86 bool sign = false;
87 bool encrypt = false;
88 bool noCrypto = false;
89 bool autoSaving = false;
90 bool autocryptEnabled = false;
91 Q_DECLARE_PUBLIC(Composer)
92};
93
94void ComposerPrivate::init()
95{
96 Q_Q(Composer);
97
98 // We cannot create these in ComposerPrivate's constructor, because
99 // their parent q is not fully constructed at that time.
100 globalPart = new GlobalPart(q);
101 infoPart = new InfoPart(q);
102 textPart = nullptr;
103 itipPart = nullptr;
104}
105
106void ComposerPrivate::doStart()
107{
108 Q_ASSERT(!started);
109 started = true;
110 composeStep1();
111}
112
113void ComposerPrivate::composeStep1()
114{
115 Q_Q(Composer);
116
117 // Create skeleton message (containing headers only; no content).
118 auto skeletonJob = new SkeletonMessageJob(infoPart, globalPart, q);
119 QObject::connect(skeletonJob, &SkeletonMessageJob::finished, q, [this, skeletonJob](KJob *job) {
120 if (job->error()) {
121 return; // KCompositeJob takes care of the error.
122 }
123
124 // SkeletonMessageJob is a special job creating a Message instead of a Content.
125 Q_ASSERT(skeletonMessage == nullptr);
126 skeletonMessage = skeletonJob->message();
127 Q_ASSERT(skeletonMessage);
128 skeletonMessage->assemble();
129
130 composeStep2();
131 });
132 q->addSubjob(skeletonJob);
133 skeletonJob->start();
134}
135
136void ComposerPrivate::composeStep2()
137{
138 Q_Q(Composer);
139
140 ContentJobBase *mainJob = nullptr;
141 ContentJobBase *mainContentJob = nullptr;
142 Q_ASSERT(textPart || itipPart); // At least one must be present, otherwise it's a useless message
143 if (textPart && !itipPart) {
144 mainContentJob = new MainTextJob(textPart, q);
145 } else if (!textPart && itipPart) {
146 mainContentJob = new ItipJob(itipPart, q);
147 } else {
148 // Combination of both text and itip parts not supported right now
149 Q_ASSERT(!textPart || !itipPart);
150 }
151
152 if ((sign || encrypt) && format & Kleo::InlineOpenPGPFormat) { // needs custom handling --- one SignEncryptJob by itself
153 qCDebug(MESSAGECOMPOSER_LOG) << "sending to sign/enc inline job!";
154
155 if (encrypt) {
156 // TODO: fix Inline PGP with encrypted attachments
157
158 const QList<ContentJobBase *> jobs = createEncryptJobs(mainContentJob, sign);
159 for (ContentJobBase *subJob : jobs) {
160 if (attachmentParts.isEmpty()) {
161 // We have no attachments. Use the content given by the mainContentJob
162 mainJob = subJob;
163 } else {
164 auto multipartJob = new MultipartJob(q);
165 multipartJob->setMultipartSubtype("mixed");
166 multipartJob->appendSubjob(subJob);
167 for (const AttachmentPart::Ptr &part : std::as_const(attachmentParts)) {
168 multipartJob->appendSubjob(new AttachmentJob(part));
169 }
170 mainJob = multipartJob;
171 }
172
173 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
174 q->addSubjob(mainJob);
175 }
176 } else {
177 auto subJob = new SignJob(q);
178 subJob->setSigningKeys(signers);
179 subJob->setCryptoMessageFormat(format);
180 subJob->appendSubjob(mainContentJob);
181
182 if (attachmentParts.isEmpty()) {
183 // We have no attachments. Use the content given by the mainContentJob.
184 mainJob = subJob;
185 } else {
186 auto multipartJob = new MultipartJob(q);
187 multipartJob->setMultipartSubtype("mixed");
188 multipartJob->appendSubjob(subJob);
189 for (const AttachmentPart::Ptr &part : std::as_const(attachmentParts)) {
190 multipartJob->appendSubjob(new AttachmentJob(part));
191 }
192 mainJob = multipartJob;
193 }
194 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
195 q->addSubjob(mainJob);
196 }
197
198 if (mainJob) {
199 mainJob->start();
200 } else {
201 qCDebug(MESSAGECOMPOSER_LOG) << "main job is null";
202 }
203 return;
204 }
205
206 if (attachmentParts.isEmpty()) {
207 // We have no attachments. Use the content given by the mainContentJob
208 mainJob = mainContentJob;
209 } else {
210 // We have attachments. Create a multipart/mixed content.
211 QMutableListIterator<AttachmentPart::Ptr> iter(attachmentParts);
212 while (iter.hasNext()) {
213 AttachmentPart::Ptr part = iter.next();
214 qCDebug(MESSAGECOMPOSER_LOG) << "Checking attachment crypto policy... signed: " << part->isSigned() << " isEncrypted : " << part->isEncrypted();
215 if (!noCrypto && !autoSaving && (sign != part->isSigned() || encrypt != part->isEncrypted())) { // different policy
216 qCDebug(MESSAGECOMPOSER_LOG) << "got attachment with different crypto policy!";
217 lateAttachmentParts.append(part);
218 iter.remove();
219 }
220 }
221 auto multipartJob = new MultipartJob(q);
222 multipartJob->setMultipartSubtype("mixed");
223 multipartJob->appendSubjob(mainContentJob);
224 for (const AttachmentPart::Ptr &part : std::as_const(attachmentParts)) {
225 multipartJob->appendSubjob(new AttachmentJob(part));
226 }
227 mainJob = multipartJob;
228 }
229
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);
240 }
241 }
242 mainJob = autocryptJob;
243 }
244
245 if (sign) {
246 auto sJob = new SignJob(q);
247 sJob->setCryptoMessageFormat(format);
248 sJob->setSigningKeys(signers);
249 sJob->appendSubjob(mainJob);
250 sJob->setSkeletonMessage(skeletonMessage);
251 mainJob = sJob;
252 }
253
254 if (encrypt) {
255 const auto lstJob = createEncryptJobs(mainJob, false);
256 for (ContentJobBase *job : lstJob) {
257 auto eJob = dynamic_cast<EncryptJob *>(job);
258 if (eJob && sign) {
259 // When doing Encrypt and Sign move headers only in the signed part
260 eJob->setProtectedHeaders(false);
261 }
262 QObject::connect(job, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
263 q->addSubjob(job);
264 mainJob = job; // start only last EncryptJob
265 }
266 } else {
267 QObject::connect(mainJob, SIGNAL(finished(KJob *)), q, SLOT(contentJobFinished(KJob *)));
268 q->addSubjob(mainJob);
269 }
270
271 mainJob->start();
272}
273
274QList<ContentJobBase *> ComposerPrivate::createEncryptJobs(ContentJobBase *contentJob, bool sign)
275{
276 Q_Q(Composer);
277
279
280 // each SplitInfo holds a list of recipients/keys, if there is more than
281 // one item in it then it means there are secondary recipients that need
282 // different messages w/ clean headers
283 qCDebug(MESSAGECOMPOSER_LOG) << "starting enc jobs";
284 qCDebug(MESSAGECOMPOSER_LOG) << "format:" << format;
285 qCDebug(MESSAGECOMPOSER_LOG) << "enc data:" << encData.size();
286
287 if (encData.isEmpty()) { // no key data! bail!
288 q->setErrorText(i18n("No key data for recipients found."));
289 q->setError(Composer::IncompleteError);
290 q->emitResult();
291 return jobs;
292 }
293
294 const int encDataSize = encData.size();
295 jobs.reserve(encDataSize);
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;
300 if (sign) {
301 auto seJob = new SignEncryptJob(q);
302
303 seJob->setCryptoMessageFormat(format);
304 seJob->setSigningKeys(signers);
305 seJob->setEncryptionKeys(recipients.second);
306 seJob->setRecipients(recipients.first);
307 seJob->setSkeletonMessage(skeletonMessage);
308
309 subJob = seJob;
310 } else {
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);
317 subJob = eJob;
318 }
319 qCDebug(MESSAGECOMPOSER_LOG) << "subJob" << subJob;
320 subJob->appendSubjob(contentJob);
321 jobs.append(subJob);
322 }
323 qCDebug(MESSAGECOMPOSER_LOG) << jobs.size();
324 return jobs;
325}
326
327void ComposerPrivate::contentJobFinished(KJob *job)
328{
329 if (job->error()) {
330 return; // KCompositeJob takes care of the error.
331 }
332 qCDebug(MESSAGECOMPOSER_LOG) << "composing final message";
333
334 KMime::Message *headers = nullptr;
335 KMime::Content *resultContent = nullptr;
336 std::vector<GpgME::Key> keys;
337 QStringList recipients;
338
339 Q_ASSERT(dynamic_cast<ContentJobBase *>(job) == static_cast<ContentJobBase *>(job));
340 auto contentJob = static_cast<ContentJobBase *>(job);
341
342 // create the final headers and body,
343 // taking into account secondary recipients for encryption
344 if (encData.size() > 1) { // crypto job with secondary recipients..
345 Q_ASSERT(dynamic_cast<MessageComposer::AbstractEncryptJob *>(job)); // we need to get the recipients for this job
346 auto eJob = dynamic_cast<MessageComposer::AbstractEncryptJob *>(job);
347
348 keys = eJob->encryptionKeys();
349 recipients = eJob->recipients();
350
351 resultContent = contentJob->content(); // content() comes from superclass
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());
359
360 auto realTo = new KMime::Headers::Generic("X-KMail-EncBccRecipients");
361 realTo->fromUnicodeString(eJob->recipients().join(QLatin1Char('%')), "utf-8");
362
363 qCDebug(MESSAGECOMPOSER_LOG) << "got one of multiple messages sending to:" << realTo->asUnicodeString();
364 qCDebug(MESSAGECOMPOSER_LOG) << "sending to recipients:" << recipients;
365 headers->setHeader(realTo);
366 headers->assemble();
367 } else { // just use the saved headers from before
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;
373 }
374
375 headers = skeletonMessage;
376 resultContent = contentJob->content();
377 }
378
379 if (lateAttachmentParts.isEmpty()) {
380 composeFinalStep(headers, resultContent);
381 } else {
382 composeWithLateAttachments(headers, resultContent, lateAttachmentParts, keys, recipients);
383 }
384}
385
386void ComposerPrivate::composeWithLateAttachments(KMime::Message *headers,
387 KMime::Content *content,
388 const AttachmentPart::List &parts,
389 const std::vector<GpgME::Key> &keys,
390 const QStringList &recipients)
391{
392 Q_Q(Composer);
393
394 auto multiJob = new MultipartJob(q);
395 multiJob->setMultipartSubtype("mixed");
396
397 // wrap the content into a job for the multijob to handle it
398 auto tJob = new MessageComposer::TransparentJob(q);
399 tJob->setContent(content);
400 multiJob->appendSubjob(tJob);
401 multiJob->setExtraContent(headers);
402
403 qCDebug(MESSAGECOMPOSER_LOG) << "attachment encr key size:" << keys.size() << " recipients: " << recipients;
404
405 // operate correctly on each attachment that has a different crypto policy than body.
406 for (const AttachmentPart::Ptr &attachment : std::as_const(parts)) {
407 auto attachJob = new AttachmentJob(attachment, q);
408
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);
416
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);
424
425 eJob->appendSubjob(sJob);
426
427 multiJob->appendSubjob(eJob);
428 } else {
429 qCDebug(MESSAGECOMPOSER_LOG) << "Just signing late attachment";
430 multiJob->appendSubjob(sJob);
431 }
432 } else if (attachment->isEncrypted() && format) { // only encryption
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);
438
439 eJob->appendSubjob(attachJob);
440 multiJob->appendSubjob(eJob);
441 } else {
442 qCDebug(MESSAGECOMPOSER_LOG) << "attaching plain non-crypto attachment";
443 auto attachSecondJob = new AttachmentJob(attachment, q);
444 multiJob->appendSubjob(attachSecondJob);
445 }
446 }
447
448 QObject::connect(multiJob, SIGNAL(finished(KJob *)), q, SLOT(attachmentsFinished(KJob *)));
449
450 q->addSubjob(multiJob);
451 multiJob->start();
452}
453
454void ComposerPrivate::attachmentsFinished(KJob *job)
455{
456 if (job->error()) {
457 return; // KCompositeJob takes care of the error.
458 }
459 qCDebug(MESSAGECOMPOSER_LOG) << "composing final message with late attachments";
460
461 Q_ASSERT(dynamic_cast<ContentJobBase *>(job));
462 auto contentJob = static_cast<ContentJobBase *>(job);
463
464 KMime::Content *content = contentJob->content();
465 KMime::Content *headers = contentJob->extraContent();
466
467 composeFinalStep(headers, content);
468}
469
470void ComposerPrivate::composeFinalStep(KMime::Content *headers, KMime::Content *content)
471{
472 content->assemble();
473
474 const QByteArray allData = headers->head() + content->encodedContent();
475
476 delete content;
477
478 KMime::Message::Ptr resultMessage(new KMime::Message);
479 resultMessage->setContent(allData);
480 resultMessage->parse(); // Not strictly necessary.
481 resultMessages.append(resultMessage);
482}
483
484Composer::Composer(QObject *parent)
485 : JobBase(*new ComposerPrivate(this), parent)
486{
487 Q_D(Composer);
488 d->init();
489}
490
491Composer::~Composer() = default;
492
493QList<KMime::Message::Ptr> Composer::resultMessages() const
494{
495 Q_D(const Composer);
496 Q_ASSERT(d->finished);
497 Q_ASSERT(!error());
498 return d->resultMessages;
499}
500
501GlobalPart *Composer::globalPart() const
502{
503 Q_D(const Composer);
504 return d->globalPart;
505}
506
507InfoPart *Composer::infoPart() const
508{
509 Q_D(const Composer);
510 return d->infoPart;
511}
512
513TextPart *Composer::textPart() const
514{
515 Q_D(const Composer);
516 if (!d->textPart) {
517 auto *self = const_cast<Composer *>(this);
518 self->d_func()->textPart = new TextPart(self);
519 }
520 return d->textPart;
521}
522
523void Composer::clearTextPart()
524{
525 Q_D(Composer);
526 delete d->textPart;
527 d->textPart = nullptr;
528}
529
530ItipPart *Composer::itipPart() const
531{
532 Q_D(const Composer);
533 if (!d->itipPart) {
534 auto *self = const_cast<Composer *>(this);
535 self->d_func()->itipPart = new ItipPart(self);
536 }
537 return d->itipPart;
538}
539
540void Composer::clearItipPart()
541{
542 Q_D(Composer);
543 delete d->itipPart;
544 d->itipPart = nullptr;
545}
546
547AttachmentPart::List Composer::attachmentParts() const
548{
549 Q_D(const Composer);
550 return d->attachmentParts;
551}
552
553void Composer::addAttachmentPart(AttachmentPart::Ptr part, bool autoresizeImage)
554{
555 Q_D(Composer);
556 Q_ASSERT(!d->started);
557 Q_ASSERT(!d->attachmentParts.contains(part));
558 if (autoresizeImage) {
559 MessageComposer::Utils resizeUtils;
560 if (resizeUtils.resizeImage(part)) {
562 autoResizeJob.setName(part->name());
563 autoResizeJob.setMimetype(part->mimeType());
564 if (autoResizeJob.loadImageFromData(part->data())) {
565 if (autoResizeJob.resizeImage()) {
566 part->setData(autoResizeJob.imageArray());
567 part->setMimeType(autoResizeJob.mimetype());
568 part->setName(autoResizeJob.generateNewName());
569 resizeUtils.changeFileName(part);
570 }
571 }
572 }
573 }
574 d->attachmentParts.append(part);
575}
576
577void Composer::addAttachmentParts(const AttachmentPart::List &parts, bool autoresizeImage)
578{
579 for (const AttachmentPart::Ptr &part : parts) {
580 addAttachmentPart(part, autoresizeImage);
581 }
582}
583
584void Composer::removeAttachmentPart(AttachmentPart::Ptr part)
585{
586 Q_D(Composer);
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();
591 Q_ASSERT(false);
592 return;
593 }
594}
595
596void Composer::setSignAndEncrypt(const bool doSign, const bool doEncrypt)
597{
598 Q_D(Composer);
599 d->sign = doSign;
600 d->encrypt = doEncrypt;
601}
602
603void Composer::setMessageCryptoFormat(Kleo::CryptoMessageFormat format)
604{
605 Q_D(Composer);
606
607 d->format = format;
608}
609
610void Composer::setSigningKeys(const std::vector<GpgME::Key> &signers)
611{
612 Q_D(Composer);
613
614 d->signers = signers;
615}
616
617void Composer::setEncryptionKeys(const QList<QPair<QStringList, std::vector<GpgME::Key>>> &encData)
618{
619 Q_D(Composer);
620
621 d->encData = encData;
622}
623
624void Composer::setNoCrypto(bool noCrypto)
625{
626 Q_D(Composer);
627
628 d->noCrypto = noCrypto;
629}
630
631void Composer::setAutocryptEnabled(bool autocryptEnabled)
632{
633 Q_D(Composer);
634
635 d->autocryptEnabled = autocryptEnabled;
636}
637
638void Composer::setSenderEncryptionKey(const GpgME::Key &senderKey)
639{
640 Q_D(Composer);
641
642 d->senderEncryptionKey = senderKey;
643}
644
645void Composer::setGnupgHome(const QString &path)
646{
647 Q_D(Composer);
648
649 d->gnupgHome = path;
650}
651
652QString Composer::gnupgHome() const
653{
654 Q_D(const Composer);
655
656 return d->gnupgHome;
657}
658
659bool Composer::finished() const
660{
661 Q_D(const Composer);
662
663 return d->finished;
664}
665
666bool Composer::autoSave() const
667{
668 Q_D(const Composer);
669
670 return d->autoSaving;
671}
672
673void Composer::setAutoSave(bool isAutoSave)
674{
675 Q_D(Composer);
676
677 d->autoSaving = isAutoSave;
678}
679
680void Composer::start()
681{
682 Q_D(Composer);
683 d->doStart();
684}
685
686void Composer::slotResult(KJob *job)
687{
688 Q_D(Composer);
690
691 if (!hasSubjobs()) {
692 d->finished = true;
693 emitResult();
694 }
695}
696
697#include "moc_composer.cpp"
bool hasSubjobs() const
virtual void slotResult(KJob *job)
void emitResult()
int error() const
void finished(KJob *job)
void setHeader(Headers::Base *h)
QByteArray head() const
QByteArray encodedContent(bool useCrLf=false)
KMime::Headers::Cc * cc(bool create=true)
KMime::Headers::To * to(bool create=true)
KMime::Headers::MessageID * messageID(bool create=true)
KMime::Headers::Subject * subject(bool create=true)
KMime::Headers::Date * date(bool create=true)
KMime::Headers::From * from(bool create=true)
The AttachmentJob class.
The Composer class.
Definition composer.h:35
void setAutoSave(bool isAutoSave)
Sets if this message being composed is an auto-saved message if so, might need different handling,...
Definition composer.cpp:673
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.
Encrypt the contents of a message .
Definition encryptjob.h:34
The GlobalPart class.
Definition globalpart.h:20
The ImageScaling class.
The InfoPart class contains the message header.
Definition infopart.h:22
The ItipJob class.
Definition itipjob.h:20
The ItipPart class.
Definition itippart.h:21
A dummy abstract class defining some errors pertaining to the Composer.
Definition jobbase.h:25
The MainTextJob class.
Definition maintextjob.h:20
The MultipartJob class.
Signs and encrypt the contents of a message.
Signs the contents of a message.
Definition signjob.h:32
A message containing only the headers...
The TextPart class.
Definition textpart.h:21
A job that just wraps some KMime::Content into a job object for use as a subjob in another job.
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)
const_reference at(qsizetype i) const const
T & first()
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.