File indexing completed on 2024-11-24 04:52:57

0001 /* Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org>
0002 
0003    This file is part of the Trojita Qt IMAP e-mail client,
0004    http://trojita.flaska.net/
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU General Public License as
0008    published by the Free Software Foundation; either version 2 of
0009    the License or (at your option) version 3 or any later version
0010    accepted by the membership of KDE e.V. (or its successor approved
0011    by the membership of KDE e.V.), which shall act as a proxy
0012    defined in Section 14 of version 3 of the license.
0013 
0014    This program is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017    GNU General Public License for more details.
0018 
0019    You should have received a copy of the GNU General Public License
0020    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #include <cstdint> // workaround broken mimetic header
0024 #include <mimetic/mimetic.h>
0025 #include <QBrush>
0026 #include <QFont>
0027 #include "Common/InvokeMethod.h"
0028 #include "Cryptography/LocalMimeParser.h"
0029 #include "Cryptography/MessageModel.h"
0030 #include "Cryptography/MimeticUtils.h"
0031 #include "Imap/Model/ItemRoles.h"
0032 #include "Imap/Model/MailboxTree.h"
0033 
0034 namespace Cryptography {
0035 
0036 LocalMimeMessageParser::LocalMimeMessageParser()
0037     : PartReplacer()
0038 {
0039 }
0040 
0041 LocalMimeMessageParser::~LocalMimeMessageParser()
0042 {
0043 }
0044 
0045 MessagePart::Ptr LocalMimeMessageParser::createPart(MessageModel *model, MessagePart *parentPart, MessagePart::Ptr original,
0046                                                     const QModelIndex &sourceItemIndex, const QModelIndex &proxyParentIndex)
0047 {
0048     auto mimeType = sourceItemIndex.data(Imap::Mailbox::RolePartMimeType).toByteArray();
0049     if (mimeType == "message/rfc822") {
0050         return MessagePart::Ptr(new LocallyParsedMimePart(model, parentPart, std::move(original),
0051                                                           sourceItemIndex, proxyParentIndex));
0052     }
0053     return original;
0054 }
0055 
0056 LocallyParsedMimePart::LocallyParsedMimePart(MessageModel *model, MessagePart *parentPart, Ptr originalPart,
0057                                              const QModelIndex &sourceItemIndex, const QModelIndex &proxyParentIndex)
0058     : QObject(0)
0059     , LocalMessagePart(parentPart, originalPart->row(), sourceItemIndex.data(Imap::Mailbox::RolePartMimeType).toByteArray())
0060     , m_model(model)
0061     , m_sourceHeaderIndex(sourceItemIndex.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, sourceItemIndex))
0062     , m_sourceTextIndex(sourceItemIndex.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, sourceItemIndex))
0063     , m_proxyParentIndex(proxyParentIndex)
0064 {
0065     Q_ASSERT(m_proxyParentIndex.isValid());
0066     Q_ASSERT(m_proxyParentIndex.model() == model);
0067     Q_ASSERT(m_sourceHeaderIndex.isValid());
0068     Q_ASSERT(m_sourceHeaderIndex.model() != model);
0069     Q_ASSERT(m_sourceTextIndex.isValid());
0070     Q_ASSERT(m_sourceTextIndex.model() != model);
0071     connect(m_sourceHeaderIndex.model(), &QAbstractItemModel::dataChanged, this, &LocallyParsedMimePart::messageMaybeAvailable);
0072 
0073     // request the data
0074     m_sourceHeaderIndex.data(Imap::Mailbox::RolePartData);
0075     m_sourceTextIndex.data(Imap::Mailbox::RolePartData);
0076     m_localState = FetchingState::LOADING;
0077     // ...and speculatively check if they're already there
0078     CALL_LATER(this, messageMaybeAvailable, Q_ARG(QModelIndex, m_sourceHeaderIndex), Q_ARG(QModelIndex, m_sourceHeaderIndex));
0079 }
0080 
0081 void LocallyParsedMimePart::messageMaybeAvailable(const QModelIndex &topLeft, const QModelIndex &bottomRight)
0082 {
0083     Q_ASSERT(m_children.empty());
0084     Q_ASSERT(topLeft == bottomRight);
0085     Q_ASSERT(m_sourceHeaderIndex.isValid() == m_sourceTextIndex.isValid());
0086 
0087     if (!m_sourceHeaderIndex.isValid()) {
0088         if (auto qaim = qobject_cast<QAbstractItemModel *>(sender())) {
0089             disconnect(qaim, &QAbstractItemModel::dataChanged, this, &LocallyParsedMimePart::messageMaybeAvailable);
0090         }
0091         m_localState = FetchingState::UNAVAILABLE;
0092         return;
0093     }
0094     Q_ASSERT(m_proxyParentIndex.isValid());
0095 
0096     if (topLeft != m_sourceHeaderIndex && topLeft != m_sourceTextIndex) {
0097         return;
0098     }
0099 
0100     if (m_sourceHeaderIndex.data(Imap::Mailbox::RoleIsFetched).toBool() && m_sourceTextIndex.data(Imap::Mailbox::RoleIsFetched).toBool()) {
0101         disconnect(m_sourceHeaderIndex.model(), &QAbstractItemModel::dataChanged, this, &LocallyParsedMimePart::messageMaybeAvailable);
0102         Q_ASSERT(m_localState == FetchingState::LOADING);
0103 
0104         // This part is rather fugly because we do not really have access to the full MIME plaintext of the "entire attachment",
0105         // including its *own* MIME headers such as Content-Type. In other words, the knowledge that the item's Content-Type
0106         // happens to be "message/rfc822" is communicated to us through an out-of-band channel, the IMAP's BODYSTRUCTURE.
0107         //
0108         // We're simply reconstructing this piece of information by adding a hand-crafted header.
0109         const QByteArray header = QByteArrayLiteral("Content-Type: ") + m_mimetype + QByteArrayLiteral("\r\n\r\n");
0110         const QByteArray data = header +
0111                 m_sourceHeaderIndex.data(Imap::Mailbox::RolePartData).toByteArray() +
0112                 m_sourceTextIndex.data(Imap::Mailbox::RolePartData).toByteArray();
0113         auto part = MimeticUtils::mimeEntityToPart(mimetic::MimeEntity(data.begin(), data.end()), this, row());
0114         auto rawPart = dynamic_cast<LocalMessagePart *>(part.get());
0115         Q_ASSERT(rawPart);
0116 
0117         // Do not store our artificial header, though!
0118         rawPart->setData(data.mid(header.length()));
0119         m_localState = FetchingState::DONE;
0120 
0121         m_model->replaceMeWithSubtree(m_proxyParentIndex, this, std::move(part));
0122         //m_model->insertSubtree(m_model->index(row(), 0, m_proxyParentIndex), std::move(part));
0123     }
0124 }
0125 
0126 QVariant LocallyParsedMimePart::data(int role) const
0127 {
0128     switch (role) {
0129     case Qt::FontRole:
0130     {
0131         QFont f;
0132         f.setItalic(true);
0133         return f;
0134     }
0135     case Qt::BackgroundRole:
0136         return QBrush(QColor(0x80, 0x80, 0xff));
0137     default:
0138         return LocalMessagePart::data(role);
0139     }
0140 }
0141 
0142 #ifdef MIME_TREE_DEBUG
0143 QByteArray LocallyParsedMimePart::dumpLocalInfo() const
0144 {
0145     return "LocallyParsedMimePart";
0146 }
0147 #endif
0148 
0149 
0150 }