summaryrefslogtreecommitdiffstats
path: root/framework/src
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
parent8809ffea6390e593b1d83f69cc50dfe8d7dadad0 (diff)
downloadkube-5941fdf6d65ecf29fc46dab000987041f5f4a8d8.tar.gz
kube-5941fdf6d65ecf29fc46dab000987041f5f4a8d8.zip
Attachment support in the composer
Diffstat (limited to 'framework/src')
-rw-r--r--framework/src/domain/composercontroller.cpp151
-rw-r--r--framework/src/domain/composercontroller.h17
2 files changed, 165 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}
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h
index 87349d0c..de154b66 100644
--- a/framework/src/domain/composercontroller.h
+++ b/framework/src/domain/composercontroller.h
@@ -24,6 +24,7 @@
24#include <QString> 24#include <QString>
25#include <QStringList> 25#include <QStringList>
26#include <QStringListModel> 26#include <QStringListModel>
27#include <QStandardItemModel>
27#include <QVariant> 28#include <QVariant>
28#include <sink/applicationdomaintype.h> 29#include <sink/applicationdomaintype.h>
29#include <KMime/Message> 30#include <KMime/Message>
@@ -66,6 +67,7 @@ class ComposerController : public Kube::Controller
66 Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT) 67 Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT)
67 Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT) 68 Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT)
68 Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT) 69 Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT)
70 Q_PROPERTY (QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT)
69 71
70 KUBE_CONTROLLER_ACTION(send) 72 KUBE_CONTROLLER_ACTION(send)
71 KUBE_CONTROLLER_ACTION(saveAsDraft) 73 KUBE_CONTROLLER_ACTION(saveAsDraft)
@@ -81,6 +83,7 @@ public:
81 QAbstractItemModel *toModel() const; 83 QAbstractItemModel *toModel() const;
82 QAbstractItemModel *ccModel() const; 84 QAbstractItemModel *ccModel() const;
83 QAbstractItemModel *bccModel() const; 85 QAbstractItemModel *bccModel() const;
86 QAbstractItemModel *attachmentModel() const;
84 87
85 Q_INVOKABLE void addTo(const QString &); 88 Q_INVOKABLE void addTo(const QString &);
86 Q_INVOKABLE void removeTo(const QString &); 89 Q_INVOKABLE void removeTo(const QString &);
@@ -88,6 +91,8 @@ public:
88 Q_INVOKABLE void removeCc(const QString &); 91 Q_INVOKABLE void removeCc(const QString &);
89 Q_INVOKABLE void addBcc(const QString &); 92 Q_INVOKABLE void addBcc(const QString &);
90 Q_INVOKABLE void removeBcc(const QString &); 93 Q_INVOKABLE void removeBcc(const QString &);
94 Q_INVOKABLE void addAttachment(const QUrl &);
95 Q_INVOKABLE void removeAttachment(const QString &);
91 96
92public slots: 97public slots:
93 virtual void clear() Q_DECL_OVERRIDE; 98 virtual void clear() Q_DECL_OVERRIDE;
@@ -97,8 +102,19 @@ private slots:
97 void updateSaveAsDraftAction(); 102 void updateSaveAsDraftAction();
98 103
99private: 104private:
105 enum AttachmentRoles {
106 NameRole = Qt::UserRole + 1,
107 FilenameRole,
108 ContentRole,
109 MimeTypeRole,
110 DescriptionRole,
111 InlineRole,
112 IconNameRole
113 };
114
100 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); 115 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName);
101 void setMessage(const QSharedPointer<KMime::Message> &msg); 116 void setMessage(const QSharedPointer<KMime::Message> &msg);
117 void addAttachmentPart(KMime::Content *partToAttach);
102 KMime::Message::Ptr assembleMessage(); 118 KMime::Message::Ptr assembleMessage();
103 119
104 QScopedPointer<Completer> mRecipientCompleter; 120 QScopedPointer<Completer> mRecipientCompleter;
@@ -106,4 +122,5 @@ private:
106 QScopedPointer<QStringListModel> mToModel; 122 QScopedPointer<QStringListModel> mToModel;
107 QScopedPointer<QStringListModel> mCcModel; 123 QScopedPointer<QStringListModel> mCcModel;
108 QScopedPointer<QStringListModel> mBccModel; 124 QScopedPointer<QStringListModel> mBccModel;
125 QScopedPointer<QStandardItemModel> mAttachmentModel;
109}; 126};