Messagelib

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

KDE's Doxygen guidelines are available online.