1 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
3 This file is part of the Trojita Qt IMAP e-mail client,
4 http://trojita.flaska.net/
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "MessageComposer.h"
26 #include <QMimeDatabase>
29 #include "Common/Application.h"
30 #include "Composer/ComposerAttachments.h"
31 #include "Imap/Encoders.h"
32 #include "Imap/Model/ItemRoles.h"
33 #include "Imap/Model/Model.h"
34 #include "Imap/Model/Utils.h"
35 #include "UiUtils/IconLoader.h"
38 static QString xTrojitaAttachmentList
= QStringLiteral("application/x-trojita-attachments-list");
39 static QString xTrojitaMessageList
= QStringLiteral("application/x-trojita-message-list");
40 static QString xTrojitaImapPart
= QStringLiteral("application/x-trojita-imap-part");
45 MessageComposer::MessageComposer(Imap::Mailbox::Model
*model
, QObject
*parent
) :
46 QAbstractListModel(parent
), m_model(model
), m_shouldPreload(false), m_reportTrojitaVersions(true)
50 MessageComposer::~MessageComposer()
52 qDeleteAll(m_attachments
);
55 int MessageComposer::rowCount(const QModelIndex
&parent
) const
57 return parent
.isValid() ? 0 : m_attachments
.size();
60 QVariant
MessageComposer::data(const QModelIndex
&index
, int role
) const
62 if (!index
.isValid() || index
.column() != 0 || index
.row() < 0 || index
.row() >= m_attachments
.size())
67 return m_attachments
[index
.row()]->caption();
69 return m_attachments
[index
.row()]->tooltip();
70 case Qt::DecorationRole
:
72 // This is more or less copy-pasted from Gui/AttachmentView.cpp. Unfortunately, sharing the implementation
73 // is not trivial due to the way how the static libraries are currently built.
74 QMimeType mimeType
= QMimeDatabase().mimeTypeForName(QString::fromUtf8(m_attachments
[index
.row()]->mimeType()));
75 if (mimeType
.isValid() && !mimeType
.isDefault()) {
76 return QIcon::fromTheme(mimeType
.iconName(), UiUtils::loadIcon(QStringLiteral("mail-attachment")));
78 return UiUtils::loadIcon(QStringLiteral("mail-attachment"));
81 case Imap::Mailbox::RoleAttachmentContentDispositionMode
:
82 return static_cast<int>(m_attachments
[index
.row()]->contentDispositionMode());
87 Qt::DropActions
MessageComposer::supportedDropActions() const
89 return Qt::CopyAction
| Qt::MoveAction
| Qt::LinkAction
;
92 Qt::ItemFlags
MessageComposer::flags(const QModelIndex
&index
) const
94 Qt::ItemFlags f
= QAbstractListModel::flags(index
);
96 if (index
.isValid()) {
97 f
|= Qt::ItemIsDragEnabled
;
99 f
|= Qt::ItemIsDropEnabled
;
103 QMimeData
*MessageComposer::mimeData(const QModelIndexList
&indexes
) const
105 QByteArray encodedData
;
106 QDataStream
stream(&encodedData
, QIODevice::WriteOnly
);
107 stream
.setVersion(QDataStream::Qt_4_6
);
109 QList
<AttachmentItem
*> items
;
110 Q_FOREACH(const QModelIndex
&index
, indexes
) {
111 if (index
.model() != this || !index
.isValid() || index
.column() != 0 || index
.parent().isValid())
113 if (index
.row() < 0 || index
.row() >= m_attachments
.size())
115 items
<< m_attachments
[index
.row()];
121 stream
<< items
.size();
122 Q_FOREACH(const AttachmentItem
*attachment
, items
) {
123 attachment
->asDroppableMimeData(stream
);
125 QMimeData
*res
= new QMimeData();
126 res
->setData(xTrojitaAttachmentList
, encodedData
);
130 bool MessageComposer::dropMimeData(const QMimeData
*data
, Qt::DropAction action
, int row
, int column
, const QModelIndex
&parent
)
132 if (action
== Qt::IgnoreAction
)
143 // FIXME: would be cool to support attachment reshuffling and to respect the desired drop position
146 if (data
->hasFormat(xTrojitaAttachmentList
)) {
147 QByteArray encodedData
= data
->data(xTrojitaAttachmentList
);
148 QDataStream
stream(&encodedData
, QIODevice::ReadOnly
);
149 return dropAttachmentList(stream
);
150 } else if (data
->hasFormat(xTrojitaMessageList
)) {
151 QByteArray encodedData
= data
->data(xTrojitaMessageList
);
152 QDataStream
stream(&encodedData
, QIODevice::ReadOnly
);
153 return dropImapMessage(stream
);
154 } else if (data
->hasFormat(xTrojitaImapPart
)) {
155 QByteArray encodedData
= data
->data(xTrojitaImapPart
);
156 QDataStream
stream(&encodedData
, QIODevice::ReadOnly
);
157 return dropImapPart(stream
);
158 } else if (data
->hasUrls()) {
159 bool attached
= false;
160 QList
<QUrl
> urls
= data
->urls();
161 foreach (const QUrl
&url
, urls
) {
162 if (url
.isLocalFile()) {
163 // Careful here -- we definitely don't want the boolean evaluation shortcuts taking effect!
164 // At the same time, any file being recognized and attached is enough to "satisfy" the drop
165 attached
= addFileAttachment(url
.path()) || attached
;
174 /** @short Container wrapper which calls qDeleteAll on all items which remain in the list at the time of destruction */
175 template <typename T
>
176 class WillDeleteAll
{
184 /** @short Handle a drag-and-drop of a list of attachments */
185 bool MessageComposer::dropAttachmentList(QDataStream
&stream
)
187 stream
.setVersion(QDataStream::Qt_4_6
);
188 if (stream
.atEnd()) {
189 qDebug() << "drag-and-drop: cannot decode data: end of stream";
194 if (stream
.status() != QDataStream::Ok
) {
195 qDebug() << "drag-and-drop: stream failed:" << stream
.status();
199 qDebug() << "drag-and-drop: invalid number of items";
203 // A crude RAII here; there are many places where the validation might fail even though we have already allocated memory
204 WillDeleteAll
<QList
<AttachmentItem
*>> items
;
206 for (int i
= 0; i
< num
; ++i
) {
211 case AttachmentItem::ATTACHMENT_IMAP_MESSAGE
:
216 stream
>> mailbox
>> uidValidity
>> uids
;
217 if (!validateDropImapMessage(stream
, mailbox
, uidValidity
, uids
))
219 if (uids
.size() != 1) {
220 qDebug() << "drag-and-drop: malformed data for a single message in a mixed list: too many UIDs";
224 items
.d
<< new ImapMessageAttachmentItem(m_model
, mailbox
, uidValidity
, uids
.front());
225 } catch (Imap::UnknownMessageIndex
&) {
232 case AttachmentItem::ATTACHMENT_IMAP_PART
:
237 QByteArray trojitaPath
;
238 if (!validateDropImapPart(stream
, mailbox
, uidValidity
, uid
, trojitaPath
))
241 items
.d
<< new ImapPartAttachmentItem(m_model
, mailbox
, uidValidity
, uid
, trojitaPath
);
242 } catch (Imap::UnknownMessageIndex
&) {
249 case AttachmentItem::ATTACHMENT_FILE
:
253 items
.d
<< new FileAttachmentItem(fileName
);
258 qDebug() << "drag-and-drop: invalid kind of attachment";
263 beginInsertRows(QModelIndex(), m_attachments
.size(), m_attachments
.size() + items
.d
.size() - 1);
264 Q_FOREACH(AttachmentItem
*attachment
, items
.d
) {
266 attachment
->preload();
267 m_attachments
<< attachment
;
275 /** @short Check that the data representing a list of messages is correct */
276 bool MessageComposer::validateDropImapMessage(QDataStream
&stream
, QString
&mailbox
, uint
&uidValidity
, QList
<uint
> &uids
) const
278 if (stream
.status() != QDataStream::Ok
) {
279 qDebug() << "drag-and-drop: stream failed:" << stream
.status();
283 Imap::Mailbox::TreeItemMailbox
*mboxPtr
= m_model
->findMailboxByName(mailbox
);
285 qDebug() << "drag-and-drop: mailbox not found";
289 if (uids
.size() < 1) {
290 qDebug() << "drag-and-drop: no UIDs passed";
294 qDebug() << "drag-and-drop: invalid UIDVALIDITY";
301 /** @short Handle a drag-and-drop of a list of messages */
302 bool MessageComposer::dropImapMessage(QDataStream
&stream
)
304 stream
.setVersion(QDataStream::Qt_4_6
);
305 if (stream
.atEnd()) {
306 qDebug() << "drag-and-drop: cannot decode data: end of stream";
312 stream
>> mailbox
>> uidValidity
>> uids
;
313 if (!validateDropImapMessage(stream
, mailbox
, uidValidity
, uids
))
315 if (!stream
.atEnd()) {
316 qDebug() << "drag-and-drop: cannot decode data: too much data";
320 WillDeleteAll
<QList
<AttachmentItem
*>> items
;
321 Q_FOREACH(const uint uid
, uids
) {
323 items
.d
<< new ImapMessageAttachmentItem(m_model
, mailbox
, uidValidity
, uid
);
324 } catch (Imap::UnknownMessageIndex
&) {
327 items
.d
.last()->setContentDispositionMode(CDN_INLINE
);
329 beginInsertRows(QModelIndex(), m_attachments
.size(), m_attachments
.size() + uids
.size() - 1);
330 Q_FOREACH(AttachmentItem
*attachment
, items
.d
) {
332 attachment
->preload();
333 m_attachments
<< attachment
;
341 /** @short Check that the data representing a single message part are correct */
342 bool MessageComposer::validateDropImapPart(QDataStream
&stream
, QString
&mailbox
, uint
&uidValidity
, uint
&uid
, QByteArray
&trojitaPath
) const
344 stream
>> mailbox
>> uidValidity
>> uid
>> trojitaPath
;
345 if (stream
.status() != QDataStream::Ok
) {
346 qDebug() << "drag-and-drop: stream failed:" << stream
.status();
349 Imap::Mailbox::TreeItemMailbox
*mboxPtr
= m_model
->findMailboxByName(mailbox
);
351 qDebug() << "drag-and-drop: mailbox not found";
355 if (!uidValidity
|| !uid
|| trojitaPath
.isEmpty()) {
356 qDebug() << "drag-and-drop: invalid data";
362 /** @short Handle a drag-adn-drop of a list of message parts */
363 bool MessageComposer::dropImapPart(QDataStream
&stream
)
365 stream
.setVersion(QDataStream::Qt_4_6
);
366 if (stream
.atEnd()) {
367 qDebug() << "drag-and-drop: cannot decode data: end of stream";
373 QByteArray trojitaPath
;
374 if (!validateDropImapPart(stream
, mailbox
, uidValidity
, uid
, trojitaPath
))
376 if (!stream
.atEnd()) {
377 qDebug() << "drag-and-drop: cannot decode data: too much data";
381 AttachmentItem
*item
;
383 item
= new ImapPartAttachmentItem(m_model
, mailbox
, uidValidity
, uid
, trojitaPath
);
384 } catch (Imap::UnknownMessageIndex
&) {
388 beginInsertRows(QModelIndex(), m_attachments
.size(), m_attachments
.size());
389 m_attachments
<< item
;
391 m_attachments
.back()->preload();
397 QStringList
MessageComposer::mimeTypes() const
399 return QStringList() << xTrojitaMessageList
<< xTrojitaImapPart
<< xTrojitaAttachmentList
<< QStringLiteral("text/uri-list");
402 void MessageComposer::setFrom(const Imap::Message::MailAddress
&from
)
407 void MessageComposer::setRecipients(const QList
<QPair
<Composer::RecipientKind
, Imap::Message::MailAddress
> > &recipients
)
409 m_recipients
= recipients
;
412 /** @short Set the value for the In-Reply-To header as per RFC 5322, section 3.6.4
414 The expected values to be passed here do *not* contain the angle brackets. This is in accordance with
415 the very last sentence of that section which says that the angle brackets are not part of the msg-id.
417 void MessageComposer::setInReplyTo(const QList
<QByteArray
> &inReplyTo
)
419 m_inReplyTo
= inReplyTo
;
422 /** @short Set the value for the References header as per RFC 5322, section 3.6.4
426 void MessageComposer::setReferences(const QList
<QByteArray
> &references
)
428 m_references
= references
;
431 void MessageComposer::setTimestamp(const QDateTime
×tamp
)
433 m_timestamp
= timestamp
;
436 void MessageComposer::setSubject(const QString
&subject
)
441 void MessageComposer::setOrganization(const QString
&organization
)
443 m_organization
= organization
;
446 void MessageComposer::setText(const QString
&text
)
451 bool MessageComposer::isReadyForSerialization() const
453 Q_FOREACH(const AttachmentItem
*attachment
, m_attachments
) {
454 if (!attachment
->isAvailableLocally())
460 QByteArray
MessageComposer::generateMessageId(const Imap::Message::MailAddress
&sender
)
462 if (sender
.host
.isEmpty()) {
463 // There's no usable domain, let's just bail out of here
466 return QUuid::createUuid().toByteArray().replace("{", "").replace("}", "") + "@" + sender
.host
.toUtf8();
469 /** @short Generate a random enough MIME boundary */
470 QByteArray
MessageComposer::generateMimeBoundary()
472 // Usage of "=_" is recommended by RFC2045 as it's guaranteed to never occur in a quoted-printable source
473 return QByteArray("trojita=_") + QUuid::createUuid().toByteArray().replace("{", "").replace("}", "");
476 QByteArray
MessageComposer::encodeHeaderField(const QString
&text
)
478 /* This encodes an "unstructured" header field */
479 return Imap::encodeRFC2047StringWithAsciiPrefix(text
);
484 /** @short Write a list of recipients into an output buffer */
485 static void processListOfRecipientsIntoHeader(const QByteArray
&prefix
, const QList
<QByteArray
> &addresses
, QByteArray
&out
)
487 // Qt and STL are different, it looks like we cannot easily use something as simple as the ostream_iterator here :(
488 if (!addresses
.isEmpty()) {
490 for (int i
= 0; i
< addresses
.size() - 1; ++i
)
491 out
.append(addresses
[i
]).append(",\r\n ");
492 out
.append(addresses
.last()).append("\r\n");
498 void MessageComposer::writeCommonMessageBeginning(QIODevice
*target
, const QByteArray boundary
) const
501 target
->write(QByteArray("From: ").append(m_from
.asMailHeader()).append("\r\n"));
504 // Got to group the headers so that both of (To, Cc) are present at most once
505 QList
<QByteArray
> rcptTo
, rcptCc
;
506 for (auto it
= m_recipients
.begin(); it
!= m_recipients
.end(); ++it
) {
508 case Composer::ADDRESS_TO
:
509 rcptTo
<< it
->second
.asMailHeader();
511 case Composer::ADDRESS_CC
:
512 rcptCc
<< it
->second
.asMailHeader();
514 case Composer::ADDRESS_BCC
:
516 case Composer::ADDRESS_FROM
:
517 case Composer::ADDRESS_SENDER
:
518 case Composer::ADDRESS_REPLY_TO
:
519 // These should never ever be produced by Trojita for now
525 QByteArray recipientHeaders
;
526 processListOfRecipientsIntoHeader("To: ", rcptTo
, recipientHeaders
);
527 processListOfRecipientsIntoHeader("Cc: ", rcptCc
, recipientHeaders
);
528 target
->write(recipientHeaders
);
530 // Other message metadata
531 target
->write(encodeHeaderField(QLatin1String("Subject: ") + m_subject
) + "\r\n" +
532 "Date: " + Imap::dateTimeToRfc2822(m_timestamp
).toUtf8() + "\r\n" +
533 "MIME-Version: 1.0\r\n");
534 QByteArray messageId
= generateMessageId(m_from
);
535 if (!messageId
.isEmpty()) {
536 target
->write("Message-ID: <" + messageId
+ ">\r\n");
538 writeHeaderWithMsgIds(target
, "In-Reply-To", m_inReplyTo
);
539 writeHeaderWithMsgIds(target
, "References", m_references
);
540 if (!m_organization
.isEmpty()) {
541 target
->write(encodeHeaderField(QLatin1String("Organization: ") + m_organization
) + "\r\n");
543 if (m_reportTrojitaVersions
) {
544 target
->write(QString::fromUtf8("User-Agent: Trojita/%1; %2\r\n").arg(
545 Common::Application::version
, Imap::Mailbox::systemPlatformVersion()).toUtf8());
547 target
->write("User-Agent: Trojita\r\n");
550 // Headers depending on actual message body data
551 if (!m_attachments
.isEmpty()) {
552 target
->write("Content-Type: multipart/mixed;\r\n\tboundary=\"" + boundary
+ "\"\r\n"
553 "\r\nThis is a multipart/mixed message in MIME format.\r\n\r\n"
554 "--" + boundary
+ "\r\n");
557 target
->write("Content-Type: text/plain; charset=utf-8; format=flowed\r\n"
558 "Content-Transfer-Encoding: quoted-printable\r\n"
560 target
->write(Imap::quotedPrintableEncode(Imap::wrapFormatFlowed(m_text
).toUtf8()));
563 /** @short Write a header consisting of a list of message-ids
565 Empty headers will not be produced, and the result is wrapped at an appropriate length.
567 The header name must not contain the colon, it is added automatically.
569 void MessageComposer::writeHeaderWithMsgIds(QIODevice
*target
, const QByteArray
&headerName
,
570 const QList
<QByteArray
> &messageIds
) const
572 if (messageIds
.isEmpty())
575 target
->write(headerName
+ ":");
576 int charCount
= headerName
.length() + 1;
577 for (int i
= 0; i
< messageIds
.size(); ++i
) {
578 // Wrapping shall happen at 78 columns, three bytes are eaten by "space < >"
579 if (i
!= 0 && charCount
!= 0 && charCount
+ messageIds
[i
].length() > 78 - 3) {
580 // got to wrap the header to respect a reasonably small line size
582 target
->write("\r\n");
584 // and now just append one more item
585 target
->write(" <" + messageIds
[i
] + ">");
586 charCount
+= messageIds
[i
].length() + 3;
588 target
->write("\r\n");
591 bool MessageComposer::writeAttachmentHeader(QIODevice
*target
, QString
*errorMessage
, const AttachmentItem
*attachment
, const QByteArray
&boundary
) const
593 if (!attachment
->isAvailableLocally() && attachment
->imapUrl().isEmpty()) {
594 *errorMessage
= tr("Attachment %1 is not available").arg(attachment
->caption());
597 target
->write("\r\n--" + boundary
+ "\r\n"
598 "Content-Type: " + attachment
->mimeType() + "\r\n");
599 target
->write(attachment
->contentDispositionHeader());
601 switch (attachment
->suggestedCTE()) {
602 case AttachmentItem::CTE_BASE64
:
603 target
->write("Content-Transfer-Encoding: base64\r\n");
605 case AttachmentItem::CTE_7BIT
:
606 target
->write("Content-Transfer-Encoding: 7bit\r\n");
608 case AttachmentItem::CTE_8BIT
:
609 target
->write("Content-Transfer-Encoding: 8bit\r\n");
611 case AttachmentItem::CTE_BINARY
:
612 target
->write("Content-Transfer-Encoding: binary\r\n");
616 target
->write("\r\n");
620 bool MessageComposer::writeAttachmentBody(QIODevice
*target
, QString
*errorMessage
, const AttachmentItem
*attachment
) const
622 if (!attachment
->isAvailableLocally()) {
623 *errorMessage
= tr("Attachment %1 is not available").arg(attachment
->caption());
626 QSharedPointer
<QIODevice
> io
= attachment
->rawData();
628 *errorMessage
= tr("Attachment %1 disappeared").arg(attachment
->caption());
631 while (!io
->atEnd()) {
632 switch (attachment
->suggestedCTE()) {
633 case AttachmentItem::CTE_BASE64
:
634 // Base64 maps 6bit chunks into a single byte. Output shall have no more than 76 characters per line
635 // (not counting the CRLF pair).
636 target
->write(io
->read(76*6/8).toBase64() + "\r\n");
639 target
->write(io
->readAll());
645 bool MessageComposer::asRawMessage(QIODevice
*target
, QString
*errorMessage
) const
647 // We don't bother with checking that our boundary is not present in the individual parts. That's arguably wrong,
648 // but we don't have much choice if we ever plan to use CATENATE. It also looks like this is exactly how other MUAs
649 // oeprate as well, so let's just join the universal dontcareism here.
650 QByteArray
boundary(generateMimeBoundary());
652 writeCommonMessageBeginning(target
, boundary
);
654 if (!m_attachments
.isEmpty()) {
655 Q_FOREACH(const AttachmentItem
*attachment
, m_attachments
) {
656 if (!writeAttachmentHeader(target
, errorMessage
, attachment
, boundary
))
658 if (!writeAttachmentBody(target
, errorMessage
, attachment
))
661 target
->write("\r\n--" + boundary
+ "--\r\n");
666 bool MessageComposer::asCatenateData(QList
<Imap::Mailbox::CatenatePair
> &target
, QString
*errorMessage
) const
668 using namespace Imap::Mailbox
;
670 QByteArray
boundary(generateMimeBoundary());
671 target
.append(qMakePair(CATENATE_TEXT
, QByteArray()));
673 // write the initial data
675 QBuffer
io(&target
.back().second
);
676 io
.open(QIODevice::ReadWrite
);
677 writeCommonMessageBeginning(&io
, boundary
);
680 if (!m_attachments
.isEmpty()) {
681 Q_FOREACH(const AttachmentItem
*attachment
, m_attachments
) {
682 if (target
.back().first
!= CATENATE_TEXT
) {
683 target
.append(qMakePair(CATENATE_TEXT
, QByteArray()));
685 QBuffer
io(&target
.back().second
);
686 io
.open(QIODevice::Append
);
688 if (!writeAttachmentHeader(&io
, errorMessage
, attachment
, boundary
))
691 QByteArray url
= attachment
->imapUrl();
693 // Cannot use CATENATE here
694 if (!writeAttachmentBody(&io
, errorMessage
, attachment
))
697 target
.append(qMakePair(CATENATE_URL
, url
));
700 if (target
.back().first
!= CATENATE_TEXT
) {
701 target
.append(qMakePair(CATENATE_TEXT
, QByteArray()));
703 QBuffer
io(&target
.back().second
);
704 io
.open(QIODevice::Append
);
705 io
.write("\r\n--" + boundary
+ "--\r\n");
710 QDateTime
MessageComposer::timestamp() const
715 QList
<QByteArray
> MessageComposer::inReplyTo() const
720 QList
<QByteArray
> MessageComposer::references() const
725 QByteArray
MessageComposer::rawFromAddress() const
727 return m_from
.asSMTPMailbox();
730 QList
<QByteArray
> MessageComposer::rawRecipientAddresses() const
732 QList
<QByteArray
> res
;
734 for (auto it
= m_recipients
.begin(); it
!= m_recipients
.end(); ++it
) {
735 res
<< it
->second
.asSMTPMailbox();
741 bool MessageComposer::addFileAttachment(const QString
&path
)
743 beginInsertRows(QModelIndex(), m_attachments
.size(), m_attachments
.size());
744 QScopedPointer
<AttachmentItem
> attachment(new FileAttachmentItem(path
));
745 if (!attachment
->isAvailableLocally())
748 attachment
->preload();
749 m_attachments
<< attachment
.take();
754 void MessageComposer::removeAttachment(const QModelIndex
&index
)
756 if (!index
.isValid() || index
.column() != 0 || index
.row() < 0 || index
.row() >= m_attachments
.size())
759 beginRemoveRows(QModelIndex(), index
.row(), index
.row());
760 delete m_attachments
.takeAt(index
.row());
764 void MessageComposer::setAttachmentName(const QModelIndex
&index
, const QString
&newName
)
766 if (!index
.isValid() || index
.column() != 0 || index
.row() < 0 || index
.row() >= m_attachments
.size())
769 if (m_attachments
[index
.row()]->setPreferredFileName(newName
))
770 emit
dataChanged(index
, index
);
773 void MessageComposer::setAttachmentContentDisposition(const QModelIndex
&index
, const ContentDisposition disposition
)
775 if (!index
.isValid() || index
.column() != 0 || index
.row() < 0 || index
.row() >= m_attachments
.size())
778 if (m_attachments
[index
.row()]->setContentDispositionMode(disposition
))
779 emit
dataChanged(index
, index
);
782 void MessageComposer::setPreloadEnabled(const bool preload
)
784 m_shouldPreload
= preload
;
787 void MessageComposer::setReplyingToMessage(const QModelIndex
&index
)
789 m_replyingTo
= index
;
792 QModelIndex
MessageComposer::replyingToMessage() const
797 QModelIndex
MessageComposer::forwardingMessage() const
802 void MessageComposer::prepareForwarding(const QModelIndex
&index
, const ForwardMode mode
)
804 m_forwarding
= index
;
807 case Composer::ForwardMode::FORWARD_AS_ATTACHMENT
:
809 beginInsertRows(QModelIndex(), m_attachments
.size(), m_attachments
.size());
810 QString mailbox
= m_forwarding
.data(Imap::Mailbox::RoleMailboxName
).toString();
811 uint uidValidity
= m_forwarding
.data(Imap::Mailbox::RoleMailboxUidValidity
).toUInt();
812 uint uid
= m_forwarding
.data(Imap::Mailbox::RoleMessageUid
).toUInt();
813 QScopedPointer
<AttachmentItem
> attachment(new ImapMessageAttachmentItem(m_model
, mailbox
, uidValidity
, uid
));
814 if (m_shouldPreload
) {
815 attachment
->preload();
817 attachment
->setContentDispositionMode(CDN_INLINE
);
818 m_attachments
<< attachment
.take();
825 void MessageComposer::setReportTrojitaVersions(const bool reportVersions
)
827 m_reportTrojitaVersions
= reportVersions
;