1 /*
2   SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
3 
4   SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "job/singlepartjob.h"
8 
9 #include "composer/composer.h"
10 #include "contentjobbase_p.h"
11 #include "part/globalpart.h"
12 #include "utils/util.h"
13 
14 #include "messagecomposer_debug.h"
15 #include <KLocalizedString>
16 
17 #include <KMime/Content>
18 #include <KMime/Headers>
19 
20 using namespace MessageComposer;
21 
22 class MessageComposer::SinglepartJobPrivate : public ContentJobBasePrivate
23 {
24 public:
SinglepartJobPrivate(SinglepartJob * qq)25     SinglepartJobPrivate(SinglepartJob *qq)
26         : ContentJobBasePrivate(qq)
27     {
28     }
29 
30     bool chooseCTE();
31 
32     QByteArray data;
33     KMime::Headers::ContentDescription *contentDescription = nullptr;
34     KMime::Headers::ContentDisposition *contentDisposition = nullptr;
35     KMime::Headers::ContentID *contentID = nullptr;
36     KMime::Headers::ContentTransferEncoding *contentTransferEncoding = nullptr;
37     KMime::Headers::ContentType *contentType = nullptr;
38 
39     Q_DECLARE_PUBLIC(SinglepartJob)
40 };
41 
chooseCTE()42 bool SinglepartJobPrivate::chooseCTE()
43 {
44     Q_Q(SinglepartJob);
45 
46     auto allowed = KMime::encodingsForData(data);
47 
48     if (!q->globalPart()->is8BitAllowed()) {
49         allowed.removeAll(KMime::Headers::CE8Bit);
50     }
51 
52 #if 0 // TODO signing
53       // In the following cases only QP and Base64 are allowed:
54       // - the buffer will be OpenPGP/MIME signed and it contains trailing
55       //   whitespace (cf. RFC 3156)
56       // - a line starts with "From "
57     if ((willBeSigned && cf.hasTrailingWhitespace())
58         || cf.hasLeadingFrom()) {
59         ret.removeAll(DwMime::kCte8bit);
60         ret.removeAll(DwMime::kCte7bit);
61     }
62 #endif
63 
64     if (contentTransferEncoding) {
65         // Specific CTE set.  Check that our data fits in it.
66         if (!allowed.contains(contentTransferEncoding->encoding())) {
67             q->setError(JobBase::BugError);
68             q->setErrorText(
69                 i18n("%1 Content-Transfer-Encoding cannot correctly encode this message.", KMime::nameForEncoding(contentTransferEncoding->encoding())));
70             return false;
71             // TODO improve error message in case 8bit is requested but not allowed.
72         }
73     } else {
74         // No specific CTE set.  Choose the best one.
75         Q_ASSERT(!allowed.isEmpty());
76         contentTransferEncoding = new KMime::Headers::ContentTransferEncoding;
77         contentTransferEncoding->setEncoding(allowed.first());
78     }
79     qCDebug(MESSAGECOMPOSER_LOG) << "Settled on encoding" << KMime::nameForEncoding(contentTransferEncoding->encoding());
80     return true;
81 }
82 
SinglepartJob(QObject * parent)83 SinglepartJob::SinglepartJob(QObject *parent)
84     : ContentJobBase(*new SinglepartJobPrivate(this), parent)
85 {
86 }
87 
~SinglepartJob()88 SinglepartJob::~SinglepartJob()
89 {
90 }
91 
data() const92 QByteArray SinglepartJob::data() const
93 {
94     Q_D(const SinglepartJob);
95     return d->data;
96 }
97 
setData(const QByteArray & data)98 void SinglepartJob::setData(const QByteArray &data)
99 {
100     Q_D(SinglepartJob);
101     d->data = data;
102 }
103 
contentDescription()104 KMime::Headers::ContentDescription *SinglepartJob::contentDescription()
105 {
106     Q_D(SinglepartJob);
107     if (!d->contentDescription) {
108         d->contentDescription = new KMime::Headers::ContentDescription;
109     }
110     return d->contentDescription;
111 }
112 
contentDisposition()113 KMime::Headers::ContentDisposition *SinglepartJob::contentDisposition()
114 {
115     Q_D(SinglepartJob);
116     if (!d->contentDisposition) {
117         d->contentDisposition = new KMime::Headers::ContentDisposition;
118     }
119     return d->contentDisposition;
120 }
121 
contentID()122 KMime::Headers::ContentID *SinglepartJob::contentID()
123 {
124     Q_D(SinglepartJob);
125     if (!d->contentID) {
126         d->contentID = new KMime::Headers::ContentID;
127     }
128     return d->contentID;
129 }
130 
contentTransferEncoding()131 KMime::Headers::ContentTransferEncoding *SinglepartJob::contentTransferEncoding()
132 {
133     Q_D(SinglepartJob);
134     if (!d->contentTransferEncoding) {
135         d->contentTransferEncoding = new KMime::Headers::ContentTransferEncoding;
136     }
137     return d->contentTransferEncoding;
138 }
139 
contentType()140 KMime::Headers::ContentType *SinglepartJob::contentType()
141 {
142     Q_D(SinglepartJob);
143     if (!d->contentType) {
144         d->contentType = new KMime::Headers::ContentType;
145     }
146     return d->contentType;
147 }
148 
process()149 void SinglepartJob::process()
150 {
151     Q_D(SinglepartJob);
152     Q_ASSERT(d->resultContent == nullptr); // Not processed before.
153     d->resultContent = new KMime::Content;
154 
155     if (!d->chooseCTE()) {
156         Q_ASSERT(error());
157         emitResult();
158         return;
159     }
160 
161     // Set headers.
162     if (d->contentDescription) {
163         d->resultContent->setHeader(d->contentDescription);
164     }
165     if (d->contentDisposition) {
166         d->resultContent->setHeader(d->contentDisposition);
167     }
168     if (d->contentID) {
169         d->resultContent->setHeader(d->contentID);
170     }
171     Q_ASSERT(d->contentTransferEncoding); // chooseCTE() created it if it didn't exist.
172     {
173         d->resultContent->setHeader(d->contentTransferEncoding);
174     }
175     if (d->contentType) {
176         d->resultContent->setHeader(d->contentType);
177     }
178 
179     // Set data.
180     d->resultContent->setBody(d->data);
181 
182     emitResult();
183 }
184