diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-06-14 14:59:43 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-06-14 15:51:56 +0200 |
commit | 5941fdf6d65ecf29fc46dab000987041f5f4a8d8 (patch) | |
tree | 07a04edf9079955aa725ea24014106dcb6616911 /framework/src/domain/composercontroller.cpp | |
parent | 8809ffea6390e593b1d83f69cc50dfe8d7dadad0 (diff) | |
download | kube-5941fdf6d65ecf29fc46dab000987041f5f4a8d8.tar.gz kube-5941fdf6d65ecf29fc46dab000987041f5f4a8d8.zip |
Attachment support in the composer
Diffstat (limited to 'framework/src/domain/composercontroller.cpp')
-rw-r--r-- | framework/src/domain/composercontroller.cpp | 151 |
1 files changed, 148 insertions, 3 deletions
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index cbc43afe..7c495543 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp | |||
@@ -27,6 +27,10 @@ | |||
27 | #include <QSortFilterProxyModel> | 27 | #include <QSortFilterProxyModel> |
28 | #include <QList> | 28 | #include <QList> |
29 | #include <QDebug> | 29 | #include <QDebug> |
30 | #include <QMimeDatabase> | ||
31 | #include <QUrlQuery> | ||
32 | #include <QFileInfo> | ||
33 | #include <QFile> | ||
30 | #include <sink/store.h> | 34 | #include <sink/store.h> |
31 | #include <sink/log.h> | 35 | #include <sink/log.h> |
32 | 36 | ||
@@ -84,8 +88,16 @@ ComposerController::ComposerController() | |||
84 | mIdentitySelector{new IdentitySelector{*this}}, | 88 | mIdentitySelector{new IdentitySelector{*this}}, |
85 | mToModel{new QStringListModel}, | 89 | mToModel{new QStringListModel}, |
86 | mCcModel{new QStringListModel}, | 90 | mCcModel{new QStringListModel}, |
87 | mBccModel{new QStringListModel} | 91 | mBccModel{new QStringListModel}, |
92 | mAttachmentModel{new QStandardItemModel} | ||
88 | { | 93 | { |
94 | mAttachmentModel->setItemRoleNames({{NameRole, "name"}, | ||
95 | {FilenameRole, "filename"}, | ||
96 | {ContentRole, "content"}, | ||
97 | {MimeTypeRole, "mimetype"}, | ||
98 | {DescriptionRole, "description"}, | ||
99 | {IconNameRole, "iconName"}, | ||
100 | {InlineRole, "inline"}}); | ||
89 | updateSaveAsDraftAction(); | 101 | updateSaveAsDraftAction(); |
90 | // mSendAction->monitorProperty<To>(); | 102 | // mSendAction->monitorProperty<To>(); |
91 | // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{ | 103 | // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{ |
@@ -175,6 +187,46 @@ void ComposerController::removeBcc(const QString &s) | |||
175 | updateSendAction(); | 187 | updateSendAction(); |
176 | } | 188 | } |
177 | 189 | ||
190 | QAbstractItemModel *ComposerController::attachmentModel() const | ||
191 | { | ||
192 | return mAttachmentModel.data(); | ||
193 | } | ||
194 | |||
195 | void ComposerController::addAttachment(const QUrl &url) | ||
196 | { | ||
197 | QMimeDatabase db; | ||
198 | auto mimeType = db.mimeTypeForUrl(url); | ||
199 | if (mimeType.name() == QLatin1String("inode/directory")) { | ||
200 | qWarning() << "Can't deal with directories yet."; | ||
201 | } else { | ||
202 | if (!url.isLocalFile()) { | ||
203 | qWarning() << "Cannot attach remote file: " << url; | ||
204 | return; | ||
205 | } | ||
206 | |||
207 | QFileInfo fileInfo(url.toLocalFile()); | ||
208 | if (!fileInfo.exists()) { | ||
209 | qWarning() << "The file doesn't exist: " << url; | ||
210 | } | ||
211 | |||
212 | QFile file{fileInfo.filePath()}; | ||
213 | file.open(QIODevice::ReadOnly); | ||
214 | const auto data = file.readAll(); | ||
215 | auto item = new QStandardItem; | ||
216 | item->setData(fileInfo.fileName(), NameRole); | ||
217 | item->setData(mimeType.name().toLatin1(), MimeTypeRole); | ||
218 | item->setData(fileInfo.fileName(), FilenameRole); | ||
219 | item->setData(false, InlineRole); | ||
220 | item->setData(mimeType.iconName(), IconNameRole); | ||
221 | item->setData(data, ContentRole); | ||
222 | mAttachmentModel->appendRow(item); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | void ComposerController::removeAttachment(const QString &s) | ||
227 | { | ||
228 | } | ||
229 | |||
178 | Completer *ComposerController::recipientCompleter() const | 230 | Completer *ComposerController::recipientCompleter() const |
179 | { | 231 | { |
180 | return mRecipientCompleter.data(); | 232 | return mRecipientCompleter.data(); |
@@ -215,6 +267,56 @@ static QStringList getStringListFromAddresses(const QString &s) | |||
215 | return list; | 267 | return list; |
216 | } | 268 | } |
217 | 269 | ||
270 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | ||
271 | { | ||
272 | auto item = new QStandardItem; | ||
273 | |||
274 | if (partToAttach->contentType()->mimeType() == "multipart/digest" || | ||
275 | partToAttach->contentType()->mimeType() == "message/rfc822") { | ||
276 | // if it is a digest or a full message, use the encodedContent() of the attachment, | ||
277 | // which already has the proper headers | ||
278 | item->setData(partToAttach->encodedContent(), ContentRole); | ||
279 | } else { | ||
280 | item->setData(partToAttach->decodedContent(), ContentRole); | ||
281 | } | ||
282 | item->setData(partToAttach->contentType()->mimeType(), MimeTypeRole); | ||
283 | |||
284 | QMimeDatabase db; | ||
285 | auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType()); | ||
286 | item->setData(mimeType.iconName(), IconNameRole); | ||
287 | |||
288 | if (partToAttach->contentDescription(false)) { | ||
289 | item->setData(partToAttach->contentDescription()->asUnicodeString(), DescriptionRole); | ||
290 | } | ||
291 | QString name; | ||
292 | QString filename; | ||
293 | if (partToAttach->contentType(false)) { | ||
294 | if (partToAttach->contentType()->hasParameter(QStringLiteral("name"))) { | ||
295 | name = partToAttach->contentType()->parameter(QStringLiteral("name")); | ||
296 | } | ||
297 | } | ||
298 | if (partToAttach->contentDisposition(false)) { | ||
299 | filename = partToAttach->contentDisposition()->filename(); | ||
300 | item->setData(partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline, InlineRole); | ||
301 | } | ||
302 | |||
303 | if (name.isEmpty() && !filename.isEmpty()) { | ||
304 | name = filename; | ||
305 | } | ||
306 | if (filename.isEmpty() && !name.isEmpty()) { | ||
307 | filename = name; | ||
308 | } | ||
309 | |||
310 | if (!filename.isEmpty()) { | ||
311 | item->setData(filename, FilenameRole); | ||
312 | } | ||
313 | if (!name.isEmpty()) { | ||
314 | item->setData(name, NameRole); | ||
315 | } | ||
316 | |||
317 | mAttachmentModel->appendRow(item); | ||
318 | } | ||
319 | |||
218 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) | 320 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) |
219 | { | 321 | { |
220 | mToModel->setStringList(getStringListFromAddresses(msg->to(true)->asUnicodeString())); | 322 | mToModel->setStringList(getStringListFromAddresses(msg->to(true)->asUnicodeString())); |
@@ -223,6 +325,12 @@ void ComposerController::setMessage(const KMime::Message::Ptr &msg) | |||
223 | 325 | ||
224 | setSubject(msg->subject(true)->asUnicodeString()); | 326 | setSubject(msg->subject(true)->asUnicodeString()); |
225 | setBody(msg->body()); | 327 | setBody(msg->body()); |
328 | |||
329 | //TODO use ObjecTreeParser to get encrypted attachments as well | ||
330 | foreach (const auto &att, msg->attachments()) { | ||
331 | addAttachmentPart(att); | ||
332 | } | ||
333 | |||
226 | setExistingMessage(msg); | 334 | setExistingMessage(msg); |
227 | } | 335 | } |
228 | 336 | ||
@@ -236,7 +344,6 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
236 | Store::fetchOne<Mail>(query).then([this, loadAsDraft](const Mail &mail) { | 344 | Store::fetchOne<Mail>(query).then([this, loadAsDraft](const Mail &mail) { |
237 | setExistingMail(mail); | 345 | setExistingMail(mail); |
238 | 346 | ||
239 | //TODO this should probably happen as reaction to the property being set. | ||
240 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); | 347 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); |
241 | if (!mailData.isEmpty()) { | 348 | if (!mailData.isEmpty()) { |
242 | KMime::Message::Ptr mail(new KMime::Message); | 349 | KMime::Message::Ptr mail(new KMime::Message); |
@@ -285,7 +392,6 @@ KMime::Message::Ptr ComposerController::assembleMessage() | |||
285 | mail->from(true)->addAddress(getIdentity()); | 392 | mail->from(true)->addAddress(getIdentity()); |
286 | 393 | ||
287 | mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); | 394 | mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); |
288 | mail->setBody(getBody().toUtf8()); | ||
289 | if (!mail->messageID()) { | 395 | if (!mail->messageID()) { |
290 | mail->messageID(true)->generate("org.kde.kube"); | 396 | mail->messageID(true)->generate("org.kde.kube"); |
291 | } | 397 | } |
@@ -293,6 +399,45 @@ KMime::Message::Ptr ComposerController::assembleMessage() | |||
293 | mail->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); | 399 | mail->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); |
294 | } | 400 | } |
295 | 401 | ||
402 | auto root = mAttachmentModel->invisibleRootItem(); | ||
403 | if (root->hasChildren()) { | ||
404 | mail->contentType(true)->setMimeType("multipart/mixed"); | ||
405 | mail->contentType()->setBoundary(KMime::multiPartBoundary()); | ||
406 | mail->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit); | ||
407 | mail->setPreamble("This is a multi-part message in MIME format.\n"); | ||
408 | for (int row = 0; row < root->rowCount(); row++) { | ||
409 | auto item = root->child(row, 0); | ||
410 | const auto name = item->data(NameRole).toString(); | ||
411 | const auto filename = item->data(FilenameRole).toString(); | ||
412 | const auto mimeType = item->data(MimeTypeRole).toByteArray(); | ||
413 | const auto isInline = item->data(InlineRole).toBool(); | ||
414 | const auto content = item->data(ContentRole).toByteArray(); | ||
415 | |||
416 | KMime::Content *part = new KMime::Content; | ||
417 | part->contentDisposition(true)->setFilename(filename); | ||
418 | if (isInline) { | ||
419 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDinline); | ||
420 | } else { | ||
421 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); | ||
422 | } | ||
423 | part->contentType(true)->setMimeType(mimeType); | ||
424 | part->contentType(true)->setName(name, "utf-8"); | ||
425 | //Just always encode attachments base64 so it's safe for binary data | ||
426 | part->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); | ||
427 | part->setBody(content); | ||
428 | |||
429 | mail->addContent(part); | ||
430 | |||
431 | auto mainMessage = new KMime::Content; | ||
432 | mainMessage->setBody(getBody().toUtf8()); | ||
433 | mainMessage->contentType(true)->setMimeType("text/plain"); | ||
434 | mail->addContent(mainMessage); | ||
435 | } | ||
436 | } else { | ||
437 | //FIXME same implementation as above for attachments | ||
438 | mail->setBody(getBody().toUtf8()); | ||
439 | } | ||
440 | |||
296 | mail->assemble(); | 441 | mail->assemble(); |
297 | return mail; | 442 | return mail; |
298 | } | 443 | } |