diff options
Diffstat (limited to 'framework/src')
-rw-r--r-- | framework/src/domain/composercontroller.cpp | 66 | ||||
-rw-r--r-- | framework/src/domain/composercontroller.h | 13 | ||||
-rw-r--r-- | framework/src/domain/mime/mailtemplates.cpp | 36 | ||||
-rw-r--r-- | framework/src/domain/mime/mailtemplates.h | 1 | ||||
-rw-r--r-- | framework/src/domain/mime/tests/mailtemplatetest.cpp | 24 |
5 files changed, 109 insertions, 31 deletions
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index 60e955ea..4bfc34ab 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp | |||
@@ -272,14 +272,8 @@ static QStringList getStringListFromAddresses(const KMime::Types::Mailbox::List | |||
272 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | 272 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) |
273 | { | 273 | { |
274 | QVariantMap map; | 274 | QVariantMap map; |
275 | if (partToAttach->contentType()->mimeType() == "multipart/digest" || | 275 | // May need special care for the multipart/digest MIME type |
276 | partToAttach->contentType()->mimeType() == "message/rfc822") { | 276 | map.insert("content", partToAttach->decodedContent()); |
277 | // if it is a digest or a full message, use the encodedContent() of the attachment, | ||
278 | // which already has the proper headers | ||
279 | map.insert("content", partToAttach->encodedContent()); | ||
280 | } else { | ||
281 | map.insert("content", partToAttach->decodedContent()); | ||
282 | } | ||
283 | map.insert("mimetype", partToAttach->contentType()->mimeType()); | 277 | map.insert("mimetype", partToAttach->contentType()->mimeType()); |
284 | 278 | ||
285 | QMimeDatabase db; | 279 | QMimeDatabase db; |
@@ -337,7 +331,40 @@ void ComposerController::setMessage(const KMime::Message::Ptr &msg) | |||
337 | setExistingMessage(msg); | 331 | setExistingMessage(msg); |
338 | } | 332 | } |
339 | 333 | ||
340 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | 334 | void ComposerController::loadDraft(const QVariant &message) { |
335 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
336 | mRemoveDraft = true; | ||
337 | setMessage(mail); | ||
338 | }); | ||
339 | } | ||
340 | |||
341 | void ComposerController::loadReply(const QVariant &message) { | ||
342 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
343 | //Find all personal email addresses to exclude from reply | ||
344 | KMime::Types::AddrSpecList me; | ||
345 | auto list = static_cast<IdentitySelector*>(mIdentitySelector.data())->getAllAddresses(); | ||
346 | for (const auto &a : list) { | ||
347 | KMime::Types::Mailbox mb; | ||
348 | mb.setAddress(a); | ||
349 | me << mb.addrSpec(); | ||
350 | } | ||
351 | |||
352 | MailTemplates::reply(mail, [this] (const KMime::Message::Ptr &reply) { | ||
353 | //We assume reply | ||
354 | setMessage(reply); | ||
355 | }, me); | ||
356 | }); | ||
357 | } | ||
358 | |||
359 | void ComposerController::loadForward(const QVariant &message) { | ||
360 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
361 | MailTemplates::forward(mail, [this] (const KMime::Message::Ptr &fwdMessage) { | ||
362 | setMessage(fwdMessage); | ||
363 | }); | ||
364 | }); | ||
365 | } | ||
366 | |||
367 | void ComposerController::loadMessage(const QVariant &message, std::function<void(const KMime::Message::Ptr&)> callback) | ||
341 | { | 368 | { |
342 | using namespace Sink; | 369 | using namespace Sink; |
343 | using namespace Sink::ApplicationDomain; | 370 | using namespace Sink::ApplicationDomain; |
@@ -346,8 +373,7 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
346 | Q_ASSERT(msg); | 373 | Q_ASSERT(msg); |
347 | Query query(*msg); | 374 | Query query(*msg); |
348 | query.request<Mail::MimeMessage>(); | 375 | query.request<Mail::MimeMessage>(); |
349 | Store::fetchOne<Mail>(query).then([this, loadAsDraft](const Mail &mail) { | 376 | Store::fetchOne<Mail>(query).then([this, callback](const Mail &mail) { |
350 | mRemoveDraft = loadAsDraft; | ||
351 | setExistingMail(mail); | 377 | setExistingMail(mail); |
352 | 378 | ||
353 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); | 379 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); |
@@ -355,23 +381,7 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
355 | KMime::Message::Ptr mail(new KMime::Message); | 381 | KMime::Message::Ptr mail(new KMime::Message); |
356 | mail->setContent(mailData); | 382 | mail->setContent(mailData); |
357 | mail->parse(); | 383 | mail->parse(); |
358 | if (loadAsDraft) { | 384 | callback(mail); |
359 | setMessage(mail); | ||
360 | } else { | ||
361 | //Find all personal email addresses to exclude from reply | ||
362 | KMime::Types::AddrSpecList me; | ||
363 | auto list = static_cast<IdentitySelector*>(mIdentitySelector.data())->getAllAddresses(); | ||
364 | for (const auto &a : list) { | ||
365 | KMime::Types::Mailbox mb; | ||
366 | mb.setAddress(a); | ||
367 | me << mb.addrSpec(); | ||
368 | } | ||
369 | |||
370 | MailTemplates::reply(mail, [this] (const KMime::Message::Ptr &reply) { | ||
371 | //We assume reply | ||
372 | setMessage(reply); | ||
373 | }, me); | ||
374 | } | ||
375 | } else { | 385 | } else { |
376 | qWarning() << "Retrieved empty message"; | 386 | qWarning() << "Retrieved empty message"; |
377 | } | 387 | } |
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h index 0ace365b..ac83dfd3 100644 --- a/framework/src/domain/composercontroller.h +++ b/framework/src/domain/composercontroller.h | |||
@@ -81,12 +81,21 @@ class ComposerController : public Kube::Controller | |||
81 | KUBE_CONTROLLER_ACTION(saveAsDraft) | 81 | KUBE_CONTROLLER_ACTION(saveAsDraft) |
82 | 82 | ||
83 | public: | 83 | public: |
84 | enum LoadType { | ||
85 | Draft, | ||
86 | Reply, | ||
87 | Forward, | ||
88 | }; | ||
89 | Q_ENUMS(LoadType); | ||
90 | |||
84 | explicit ComposerController(); | 91 | explicit ComposerController(); |
85 | 92 | ||
86 | Completer *recipientCompleter() const; | 93 | Completer *recipientCompleter() const; |
87 | Selector *identitySelector() const; | 94 | Selector *identitySelector() const; |
88 | 95 | ||
89 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); | 96 | Q_INVOKABLE void loadDraft(const QVariant &message); |
97 | Q_INVOKABLE void loadReply(const QVariant &message); | ||
98 | Q_INVOKABLE void loadForward(const QVariant &message); | ||
90 | 99 | ||
91 | public slots: | 100 | public slots: |
92 | virtual void clear() Q_DECL_OVERRIDE; | 101 | virtual void clear() Q_DECL_OVERRIDE; |
@@ -95,6 +104,8 @@ private slots: | |||
95 | void findPersonalKey(); | 104 | void findPersonalKey(); |
96 | 105 | ||
97 | private: | 106 | private: |
107 | void loadMessage(const QVariant &message, std::function<void(const KMime::Message::Ptr&)> callback); | ||
108 | |||
98 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); | 109 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); |
99 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 110 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
100 | void addAttachmentPart(KMime::Content *partToAttach); | 111 | void addAttachmentPart(KMime::Content *partToAttach); |
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index 9af15e91..8e644b34 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp | |||
@@ -864,6 +864,34 @@ void MailTemplates::reply(const KMime::Message::Ptr &origMsg, const std::functio | |||
864 | }); | 864 | }); |
865 | } | 865 | } |
866 | 866 | ||
867 | void MailTemplates::forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback) | ||
868 | { | ||
869 | KMime::Message::Ptr wrapperMsg(new KMime::Message); | ||
870 | |||
871 | wrapperMsg->to()->clear(); | ||
872 | wrapperMsg->cc()->clear(); | ||
873 | |||
874 | wrapperMsg->subject()->fromUnicodeString(forwardSubject(origMsg->subject()->asUnicodeString()), "utf-8"); | ||
875 | |||
876 | const QByteArray refStr = getRefStr(origMsg); | ||
877 | if (!refStr.isEmpty()) { | ||
878 | wrapperMsg->references()->fromUnicodeString(QString::fromLocal8Bit(refStr), "utf-8"); | ||
879 | } | ||
880 | |||
881 | KMime::Content* fwdAttachment = new KMime::Content; | ||
882 | |||
883 | fwdAttachment->contentDisposition()->setDisposition(KMime::Headers::CDinline); | ||
884 | fwdAttachment->contentType()->setMimeType("message/rfc822"); | ||
885 | fwdAttachment->contentDisposition()->setFilename(origMsg->subject()->asUnicodeString() + ".eml"); | ||
886 | // The mail was parsed in loadMessage before, so no need to assemble it | ||
887 | fwdAttachment->setBody(origMsg->encodedContent()); | ||
888 | |||
889 | wrapperMsg->addContent(fwdAttachment); | ||
890 | wrapperMsg->assemble(); | ||
891 | |||
892 | callback(wrapperMsg); | ||
893 | } | ||
894 | |||
867 | QString MailTemplates::plaintextContent(const KMime::Message::Ptr &msg) | 895 | QString MailTemplates::plaintextContent(const KMime::Message::Ptr &msg) |
868 | { | 896 | { |
869 | MimeTreeParser::ObjectTreeParser otp; | 897 | MimeTreeParser::ObjectTreeParser otp; |
@@ -899,10 +927,14 @@ static KMime::Content *createAttachmentPart(const QByteArray &content, const QSt | |||
899 | } else { | 927 | } else { |
900 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); | 928 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); |
901 | } | 929 | } |
930 | |||
902 | part->contentType(true)->setMimeType(mimeType); | 931 | part->contentType(true)->setMimeType(mimeType); |
903 | part->contentType(true)->setName(name, "utf-8"); | 932 | part->contentType(true)->setName(name, "utf-8"); |
904 | //Just always encode attachments base64 so it's safe for binary data | 933 | // Just always encode attachments base64 so it's safe for binary data, |
905 | part->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); | 934 | // except when it's another message |
935 | if(mimeType != "message/rfc822") { | ||
936 | part->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); | ||
937 | } | ||
906 | part->setBody(content); | 938 | part->setBody(content); |
907 | return part; | 939 | return part; |
908 | } | 940 | } |
diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h index 21efb5a0..9447e169 100644 --- a/framework/src/domain/mime/mailtemplates.h +++ b/framework/src/domain/mime/mailtemplates.h | |||
@@ -35,6 +35,7 @@ struct Attachment { | |||
35 | namespace MailTemplates | 35 | namespace MailTemplates |
36 | { | 36 | { |
37 | void reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback, const KMime::Types::AddrSpecList &me = {}); | 37 | void reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback, const KMime::Types::AddrSpecList &me = {}); |
38 | void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback); | ||
38 | QString plaintextContent(const KMime::Message::Ptr &origMsg); | 39 | QString plaintextContent(const KMime::Message::Ptr &origMsg); |
39 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); | 40 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); |
40 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<GpgME::Key> &signingKeys = {}, const std::vector<GpgME::Key> &encryptionKeys = {}); | 41 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<GpgME::Key> &signingKeys = {}, const std::vector<GpgME::Key> &encryptionKeys = {}); |
diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp index 302fae95..8d044608 100644 --- a/framework/src/domain/mime/tests/mailtemplatetest.cpp +++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp | |||
@@ -224,6 +224,30 @@ private slots: | |||
224 | QCOMPARE(result->cc()->addresses(), l); | 224 | QCOMPARE(result->cc()->addresses(), l); |
225 | } | 225 | } |
226 | 226 | ||
227 | void testForwardAsAttachment() | ||
228 | { | ||
229 | auto msg = readMail("plaintext.mbox"); | ||
230 | KMime::Message::Ptr result; | ||
231 | MailTemplates::forward(msg, [&] (const KMime::Message::Ptr &r) { | ||
232 | result = r; | ||
233 | }); | ||
234 | QTRY_VERIFY(result); | ||
235 | QCOMPARE(result->subject(false)->asUnicodeString(), {"FW: A random subject with alternative contenttype"}); | ||
236 | QCOMPARE(result->to()->addresses(), {}); | ||
237 | QCOMPARE(result->cc()->addresses(), {}); | ||
238 | |||
239 | auto attachments = result->attachments(); | ||
240 | QCOMPARE(attachments.size(), 1); | ||
241 | auto attachment = attachments[0]; | ||
242 | QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline); | ||
243 | QCOMPARE(attachment->contentDisposition(false)->filename(), {"A random subject with alternative contenttype.eml"}); | ||
244 | QVERIFY(attachment->bodyIsMessage()); | ||
245 | |||
246 | attachment->parse(); | ||
247 | auto origMsg = attachment->bodyAsMessage(); | ||
248 | QCOMPARE(origMsg->subject(false)->asUnicodeString(), {"A random subject with alternative contenttype"}); | ||
249 | } | ||
250 | |||
227 | void testCreatePlainMail() | 251 | void testCreatePlainMail() |
228 | { | 252 | { |
229 | QStringList to = {{"to@example.org"}}; | 253 | QStringList to = {{"to@example.org"}}; |