summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/composercontroller.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-06-14 14:59:43 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-06-14 15:51:56 +0200
commit5941fdf6d65ecf29fc46dab000987041f5f4a8d8 (patch)
tree07a04edf9079955aa725ea24014106dcb6616911 /framework/src/domain/composercontroller.cpp
parent8809ffea6390e593b1d83f69cc50dfe8d7dadad0 (diff)
downloadkube-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.cpp151
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
190QAbstractItemModel *ComposerController::attachmentModel() const
191{
192 return mAttachmentModel.data();
193}
194
195void 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
226void ComposerController::removeAttachment(const QString &s)
227{
228}
229
178Completer *ComposerController::recipientCompleter() const 230Completer *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
270void 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
218void ComposerController::setMessage(const KMime::Message::Ptr &msg) 320void 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}