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 
32 using namespace MessageComposer;
34 
35 class MessageComposer::ComposerPrivate : public JobBasePrivate
36 {
37 public:
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 
94 void 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 
106 void ComposerPrivate::doStart()
107 {
108  Q_ASSERT(!started);
109  started = true;
110  composeStep1();
111 }
112 
113 void 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 
136 void 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 
274 QList<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 
327 void 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 
386 void 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 
454 void 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 
470 void 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 
484 Composer::Composer(QObject *parent)
485  : JobBase(*new ComposerPrivate(this), parent)
486 {
487  Q_D(Composer);
488  d->init();
489 }
490 
491 Composer::~Composer() = default;
492 
493 QList<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 
501 GlobalPart *Composer::globalPart() const
502 {
503  Q_D(const Composer);
504  return d->globalPart;
505 }
506 
507 InfoPart *Composer::infoPart() const
508 {
509  Q_D(const Composer);
510  return d->infoPart;
511 }
512 
513 TextPart *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 
523 void Composer::clearTextPart()
524 {
525  Q_D(Composer);
526  delete d->textPart;
527  d->textPart = nullptr;
528 }
529 
530 ItipPart *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 
540 void Composer::clearItipPart()
541 {
542  Q_D(Composer);
543  delete d->itipPart;
544  d->itipPart = nullptr;
545 }
546 
547 AttachmentPart::List Composer::attachmentParts() const
548 {
549  Q_D(const Composer);
550  return d->attachmentParts;
551 }
552 
553 void 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)) {
561  MessageComposer::ImageScaling autoResizeJob;
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 
577 void Composer::addAttachmentParts(const AttachmentPart::List &parts, bool autoresizeImage)
578 {
579  for (const AttachmentPart::Ptr &part : parts) {
580  addAttachmentPart(part, autoresizeImage);
581  }
582 }
583 
584 void 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 
596 void Composer::setSignAndEncrypt(const bool doSign, const bool doEncrypt)
597 {
598  Q_D(Composer);
599  d->sign = doSign;
600  d->encrypt = doEncrypt;
601 }
602 
603 void Composer::setMessageCryptoFormat(Kleo::CryptoMessageFormat format)
604 {
605  Q_D(Composer);
606 
607  d->format = format;
608 }
609 
610 void Composer::setSigningKeys(const std::vector<GpgME::Key> &signers)
611 {
612  Q_D(Composer);
613 
614  d->signers = signers;
615 }
616 
617 void Composer::setEncryptionKeys(const QList<QPair<QStringList, std::vector<GpgME::Key>>> &encData)
618 {
619  Q_D(Composer);
620 
621  d->encData = encData;
622 }
623 
624 void Composer::setNoCrypto(bool noCrypto)
625 {
626  Q_D(Composer);
627 
628  d->noCrypto = noCrypto;
629 }
630 
631 void Composer::setAutocryptEnabled(bool autocryptEnabled)
632 {
633  Q_D(Composer);
634 
635  d->autocryptEnabled = autocryptEnabled;
636 }
637 
638 void Composer::setSenderEncryptionKey(const GpgME::Key &senderKey)
639 {
640  Q_D(Composer);
641 
642  d->senderEncryptionKey = senderKey;
643 }
644 
645 void Composer::setGnupgHome(const QString &path)
646 {
647  Q_D(Composer);
648 
649  d->gnupgHome = path;
650 }
651 
652 QString Composer::gnupgHome() const
653 {
654  Q_D(const Composer);
655 
656  return d->gnupgHome;
657 }
658 
659 bool Composer::finished() const
660 {
661  Q_D(const Composer);
662 
663  return d->finished;
664 }
665 
666 bool Composer::autoSave() const
667 {
668  Q_D(const Composer);
669 
670  return d->autoSaving;
671 }
672 
673 void Composer::setAutoSave(bool isAutoSave)
674 {
675  Q_D(Composer);
676 
677  d->autoSaving = isAutoSave;
678 }
679 
680 void Composer::start()
681 {
682  Q_D(Composer);
683  d->doStart();
684 }
685 
686 void Composer::slotResult(KJob *job)
687 {
688  Q_D(Composer);
689  JobBase::slotResult(job);
690 
691  if (!hasSubjobs()) {
692  d->finished = true;
693  emitResult();
694  }
695 }
696 
697 #include "moc_composer.cpp"
void append(const T &value)
T & first()
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
The ItipJob class.
Definition: itipjob.h:19
void finished(KJob *job)
The AttachmentJob class.
Definition: attachmentjob.h:21
bool appendSubjob(ContentJobBase *job)
This is meant to be used instead of KCompositeJob::addSubjob(), making it possible to add subjobs fro...
A class that encapsulates an attachment.
void start() override
Starts processing this ContentJobBase asynchronously.
Q_SCRIPTABLE Q_NOREPLY void start()
The Composer class.
Definition: composer.h:34
A dummy abstract class defining some errors pertaining to the Composer.
Definition: jobbase.h:24
Signs the contents of a message.
Definition: signjob.h:31
Signs and encrypt the contents of a message.
bool resizeImage()
resizeImage
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
The GlobalPart class.
Definition: globalpart.h:19
void reserve(int alloc)
int size() const const
A message containing only the headers...
Encrypt the contents of a message .
Definition: encryptjob.h:33
QString i18n(const char *text, const TYPE &arg...)
void setHeader(Headers::Base *h)
bool loadImageFromData(const QByteArray &data)
loadImageFromData
QByteArray encodedContent(bool useCrLf=false)
KMime::Content * content() const
Get the resulting KMime::Content that the ContentJobBase has generated.
The ItipPart class.
Definition: itippart.h:20
QByteArray mimetype() const
mimetype
QByteArray head() const
The InfoPart class contains the message header.
Definition: infopart.h:21
The MainTextJob class.
Definition: maintextjob.h:19
KMime::Content * extraContent() const
Get extra content that was previously added.
QByteArray imageArray() const
imageArray
The MultipartJob class.
Definition: multipartjob.h:19
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
The ImageScaling class.
Definition: imagescaling.h:20
The TextPart class.
Definition: textpart.h:20
QString path(const QString &relativePath)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
The ContentJobBase class.
A job that just wraps some KMime::Content into a job object for use as a subjob in another job.
int error() const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:55:19 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.