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 }