File indexing completed on 2024-05-26 05:28:09

0001 /* Copyright (C) 2014 - 2015 Stephan Platz <trojita@paalsteek.de>
0002    Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org>
0003 
0004    This file is part of the Trojita Qt IMAP e-mail client,
0005    http://trojita.flaska.net/
0006 
0007    This program is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU General Public License as
0009    published by the Free Software Foundation; either version 2 of
0010    the License or (at your option) version 3 or any later version
0011    accepted by the membership of KDE e.V. (or its successor approved
0012    by the membership of KDE e.V.), which shall act as a proxy
0013    defined in Section 14 of version 3 of the license.
0014 
0015    This program is distributed in the hope that it will be useful,
0016    but WITHOUT ANY WARRANTY; without even the implied warranty of
0017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018    GNU General Public License for more details.
0019 
0020    You should have received a copy of the GNU General Public License
0021    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0022 */
0023 
0024 #include <QtGui/QFont>
0025 
0026 #include "MessagePart.h"
0027 
0028 #include "MessageModel.h"
0029 #include "Cryptography/PartReplacer.h"
0030 #include "Common/MetaTypes.h"
0031 #include "Imap/Encoders.h"
0032 #include "Imap/Model/MailboxTree.h"
0033 #include "Imap/Model/ItemRoles.h"
0034 #include "UiUtils/Formatting.h"
0035 
0036 namespace Cryptography {
0037 
0038 #define PART_FETCH_CHILDREN(MODEL) const_cast<MessagePart *>(this)->fetchChildren(const_cast<MessageModel *>(MODEL))
0039 
0040 MessagePart::MessagePart(MessagePart *parent, const int row)
0041     : m_childrenState(FetchingState::NONE)
0042     , m_parent(parent)
0043     , m_children()
0044     , m_row(row)
0045 {
0046 }
0047 
0048 MessagePart::~MessagePart()
0049 {
0050 }
0051 
0052 MessagePart *MessagePart::parent() const
0053 {
0054     return m_parent;
0055 }
0056 
0057 int MessagePart::row() const
0058 {
0059     return m_row;
0060 }
0061 
0062 int MessagePart::rowCount(MessageModel *model) const
0063 {
0064     PART_FETCH_CHILDREN(model);
0065     return m_children.size();
0066 }
0067 
0068 int MessagePart::columnCount(MessageModel *model) const
0069 {
0070     PART_FETCH_CHILDREN(model);
0071     return 1;
0072 }
0073 
0074 MessagePart *MessagePart::child(MessageModel *model, int row, int column) const
0075 {
0076     PART_FETCH_CHILDREN(model);
0077     if (row == 0) {
0078         switch (column) {
0079         case Imap::Mailbox::TreeItem::OFFSET_HEADER:
0080             return m_headerPart.get();
0081         case Imap::Mailbox::TreeItem::OFFSET_TEXT:
0082             return m_textPart.get();
0083         case Imap::Mailbox::TreeItem::OFFSET_MIME:
0084             return m_mimePart.get();
0085         case Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS:
0086             return m_rawPart.get();
0087         }
0088     }
0089     if (column != 0) {
0090         return nullptr;
0091     }
0092 
0093     PART_FETCH_CHILDREN(model);
0094     auto it = m_children.find(row);
0095     return it == m_children.end() ? nullptr : it->second.get();
0096 }
0097 
0098 void MessagePart::setSpecialParts(Ptr headerPart, Ptr textPart, Ptr mimePart, Ptr rawPart)
0099 {
0100     Q_ASSERT(!m_headerPart);
0101     Q_ASSERT(!m_textPart);
0102     Q_ASSERT(!m_mimePart);
0103     Q_ASSERT(!m_rawPart);
0104     m_headerPart = std::move(headerPart);
0105     m_textPart = std::move(textPart);
0106     m_mimePart = std::move(mimePart);
0107     m_rawPart = std::move(rawPart);
0108 }
0109 
0110 #ifdef MIME_TREE_DEBUG
0111 QDebug operator<<(QDebug dbg, const MessagePart &part)
0112 {
0113     return dbg << part.dump(0).data();
0114 }
0115 
0116 QByteArray MessagePart::dump(const int nestingLevel) const
0117 {
0118     const QByteArray spacing(nestingLevel, ' ');
0119     QByteArray res = spacing + QByteArray::number(row()) + ": " + dumpLocalInfo() +
0120             " (0x" + QByteArray::number(reinterpret_cast<const qulonglong>(this), 16) + ", " +
0121             data(Imap::Mailbox::RolePartMimeType).toByteArray() + ", " +
0122             data(Imap::Mailbox::RolePartPathToPart).toByteArray()
0123             + ") {\n";
0124     for (const auto &child: m_children) {
0125         if (child.second->parent() != this) {
0126             res += spacing + "  v-- next child has wrong parent "
0127                              "0x" + QByteArray::number(reinterpret_cast<const qulonglong>(child.second->parent()), 16) + " --v \n";
0128         }
0129         res += child.second->dump(nestingLevel + 2);
0130     }
0131     res += spacing + "}\n";
0132     return res;
0133 }
0134 
0135 QByteArray MessagePart::dumpLocalInfo() const
0136 {
0137     return "MessagePart";
0138 }
0139 #endif
0140 
0141 TopLevelMessage::TopLevelMessage(const QModelIndex &messageRoot, MessageModel *model)
0142     : MessagePart(nullptr, 0)
0143     , m_root(messageRoot)
0144     , m_model(model)
0145 {
0146     Q_ASSERT(m_root.isValid());
0147     m_model->m_map[m_root] = this;
0148 }
0149 
0150 TopLevelMessage::~TopLevelMessage()
0151 {
0152     for (auto it = m_model->m_map.begin(); it != m_model->m_map.end(); /* nothing */) {
0153         if (*it == this) {
0154             it = m_model->m_map.erase(it);
0155         } else {
0156             ++it;
0157         }
0158     }
0159 }
0160 
0161 QVariant TopLevelMessage::data(int role) const
0162 {
0163     return m_root.data(role);
0164 }
0165 
0166 void TopLevelMessage::fetchChildren(MessageModel *model)
0167 {
0168     // This is where the magic happens -- this class is just a dumb, fake node, so we're inserting something real below.
0169     // The stuff below still points to the message root, *not* to an actual message part, and everybody is happy that way.
0170 
0171     if (m_childrenState == FetchingState::NONE) {
0172         // trigger a fetch within the original model
0173         m_root.model()->rowCount(m_root);
0174 
0175         if (m_root.model()->index(0, 0, m_root).isValid()) {
0176             // OK, we can do it synchronously.
0177             // Note that we are *not* guarding this based on a RoleIsFetched because that thing might not be true
0178             // when the rowsInserted() is called.
0179             m_children[0] = Ptr(new ProxyMessagePart(this, 0, m_root, model));
0180             m_childrenState = FetchingState::DONE;
0181         } else {
0182             // The upstream model is loading its stuff, okay, let's do it asynchronously.
0183             m_childrenState = FetchingState::LOADING;
0184             model->m_insertRows = model->connect(model->m_message.model(), &QAbstractItemModel::rowsInserted,
0185                                                  model, [this, model](const QModelIndex &idx) {
0186                 if (idx == model->m_message) {
0187                     model->disconnect(model->m_insertRows);
0188                     if (!m_root.isValid()) {
0189                         return;
0190                     }
0191                     Q_ASSERT(m_root.isValid());
0192                     m_childrenState = MessagePart::FetchingState::NONE;
0193                     model->beginInsertRows(QModelIndex(), 0, 0);
0194                     fetchChildren(model);
0195                     model->endInsertRows();
0196                 }
0197             });
0198         }
0199     }
0200 }
0201 
0202 #ifdef MIME_TREE_DEBUG
0203 QByteArray TopLevelMessage::dumpLocalInfo() const
0204 {
0205     return "TopLevelMessage";
0206 }
0207 #endif
0208 
0209 
0210 ProxyMessagePart::ProxyMessagePart(MessagePart *parent, const int row, const QModelIndex &sourceIndex, MessageModel *model)
0211     : MessagePart(parent, row)
0212     , m_sourceIndex(sourceIndex)
0213     , m_model(model)
0214 {
0215     m_model->m_map[sourceIndex] = this;
0216 }
0217 
0218 ProxyMessagePart::~ProxyMessagePart()
0219 {
0220     for (auto it = m_model->m_map.begin(); it != m_model->m_map.end(); /* nothing */) {
0221         if (*it == this) {
0222             it = m_model->m_map.erase(it);
0223         } else {
0224             ++it;
0225         }
0226     }
0227 }
0228 
0229 QVariant ProxyMessagePart::data(int role) const
0230 {
0231     if (auto msg = dynamic_cast<TopLevelMessage *>(parent())) {
0232         switch (role) {
0233         case Qt::DisplayRole:
0234             return QStringLiteral("[fake message root: UID %1]").arg(
0235                         msg->data(Imap::Mailbox::RoleMessageUid).toString());
0236         }
0237     }
0238     return m_sourceIndex.data(role);
0239 }
0240 
0241 void ProxyMessagePart::fetchChildren(MessageModel *model)
0242 {
0243     QModelIndex index = parent() ? QModelIndex(m_sourceIndex) : model->message();
0244     if (!index.isValid()) {
0245         return;
0246     }
0247     switch (m_childrenState) {
0248     case FetchingState::DONE:
0249     case FetchingState::LOADING:
0250     case FetchingState::UNAVAILABLE:
0251         return;
0252     case FetchingState::NONE:
0253         break;
0254     }
0255 
0256     const QAbstractItemModel *indexModel = index.model();
0257     auto headerIdx = indexModel->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, index);
0258     auto textIdx = indexModel->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, index);
0259     auto mimeIdx = indexModel->index(0, Imap::Mailbox::TreeItem::OFFSET_MIME, index);
0260     auto rawIdx = indexModel->index(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS, index);
0261 
0262     setSpecialParts(
0263                 std::unique_ptr<ProxyMessagePart>(headerIdx.isValid() ? new ProxyMessagePart(this, 0, headerIdx, model) : nullptr),
0264                 std::unique_ptr<ProxyMessagePart>(textIdx.isValid() ? new ProxyMessagePart(this, 0, textIdx, model) : nullptr),
0265                 std::unique_ptr<ProxyMessagePart>(mimeIdx.isValid() ? new ProxyMessagePart(this, 0, mimeIdx, model) : nullptr),
0266                 std::unique_ptr<ProxyMessagePart>(rawIdx.isValid() ? new ProxyMessagePart(this, 0, rawIdx, model) : nullptr)
0267                 );
0268 
0269     m_childrenState = FetchingState::LOADING;
0270     int row = 0;
0271     while (true) {
0272         auto childIndex = index.model()->index(row, 0, index);
0273         if (!childIndex.isValid()) {
0274             break;
0275         }
0276 
0277         auto part = Ptr(new ProxyMessagePart(this, row, childIndex, model));
0278 
0279         // Find out if something wants to override our choice
0280         for (const auto &module: model->m_partHandlers) {
0281             part = module->createPart(model, this, std::move(part), childIndex, model->createIndex(this->row(), 0, this));
0282         }
0283 
0284         m_children[row] = std::move(part);
0285         ++row;
0286     }
0287     m_childrenState = FetchingState::DONE;
0288 }
0289 
0290 #ifdef MIME_TREE_DEBUG
0291 QByteArray ProxyMessagePart::dumpLocalInfo() const
0292 {
0293     return "ProxyMessagePart";
0294 }
0295 #endif
0296 
0297 
0298 LocalMessagePart::LocalMessagePart(MessagePart *parent, const int row, const QByteArray &mimetype)
0299     : MessagePart(parent, row)
0300     , m_localState(FetchingState::NONE)
0301     , m_hdrListPostNo(false)
0302     , m_mimetype(mimetype)
0303     , m_octets(0)
0304 {
0305 }
0306 
0307 LocalMessagePart::~LocalMessagePart()
0308 {
0309 }
0310 
0311 void LocalMessagePart::fetchChildren(MessageModel *model)
0312 {
0313     return;
0314 }
0315 
0316 void LocalMessagePart::setChild(int row, Ptr part)
0317 {
0318     Q_ASSERT((int)m_children.size() >= row);
0319     Q_ASSERT(part);
0320     Q_ASSERT(row == part->row());
0321     m_children[row] = std::move(part);
0322 }
0323 
0324 void LocalMessagePart::setData(const QByteArray &data)
0325 {
0326     m_data = data;
0327     m_localState = FetchingState::DONE;
0328 }
0329 
0330 void LocalMessagePart::setCharset(const QByteArray &charset)
0331 {
0332     m_charset = charset;
0333 }
0334 
0335 void LocalMessagePart::setContentFormat(const QByteArray &format)
0336 {
0337     m_contentFormat = format;
0338 }
0339 
0340 void LocalMessagePart::setDelSp(const QByteArray &delSp)
0341 {
0342     m_delSp = delSp;
0343 }
0344 
0345 void LocalMessagePart::setFilename(const QString &filename)
0346 {
0347     m_filename = filename;
0348 }
0349 
0350 void LocalMessagePart::setTransferEncoding(const QByteArray &transferEncoding)
0351 {
0352     m_transferEncoding = transferEncoding;
0353 }
0354 
0355 void LocalMessagePart::setBodyFldId(const QByteArray &bodyFldId)
0356 {
0357     m_bodyFldId = bodyFldId;
0358 }
0359 
0360 void LocalMessagePart::setBodyDisposition(const QByteArray &bodyDisposition)
0361 {
0362     m_bodyDisposition = bodyDisposition;
0363 }
0364 
0365 void LocalMessagePart::setMultipartRelatedStartPart(const QByteArray &startPart)
0366 {
0367     m_multipartRelatedStartPart = startPart;
0368 }
0369 
0370 void LocalMessagePart::setOctets(uint octets)
0371 {
0372     m_octets = octets;
0373 }
0374 
0375 void LocalMessagePart::setEnvelope(std::unique_ptr<Imap::Message::Envelope> envelope)
0376 {
0377     m_envelope = std::move(envelope);
0378 }
0379 
0380 void LocalMessagePart::setHdrReferences(const QList<QByteArray> &references)
0381 {
0382     m_hdrReferences = references;
0383 }
0384 
0385 void LocalMessagePart::setHdrListPost(const QList<QUrl> &listPost)
0386 {
0387     m_hdrListPost = listPost;
0388 }
0389 
0390 void LocalMessagePart::setHdrListPostNo(const bool listPostNo)
0391 {
0392     m_hdrListPostNo = listPostNo;
0393 }
0394 
0395 void LocalMessagePart::setBodyFldParam(const QMap<QByteArray, QByteArray> &bodyFldParam)
0396 {
0397     m_bodyFldParam = bodyFldParam;
0398 }
0399 
0400 bool LocalMessagePart::isTopLevelMultipart() const
0401 {
0402     return m_mimetype.startsWith("multipart/") && (!parent()->parent() || parent()->data(Imap::Mailbox::RolePartMimeType).toByteArray().startsWith("message/"));
0403 }
0404 
0405 QByteArray LocalMessagePart::partId() const
0406 {
0407     if (isTopLevelMultipart())
0408         return QByteArray();
0409 
0410     QByteArray id = QByteArray::number(row() + 1);
0411     if (parent() && !parent()->data(Imap::Mailbox::RolePartId).toByteArray().isEmpty())
0412         id = parent()->data(Imap::Mailbox::RolePartId).toByteArray() + '.' + id;
0413 
0414     return id;
0415 }
0416 
0417 QByteArray LocalMessagePart::pathToPart() const {
0418     QByteArray parentPath;
0419     if (parent()) {
0420         parentPath = parent()->data(Imap::Mailbox::RolePartPathToPart).toByteArray();
0421     }
0422 
0423     return parentPath + '/' + QByteArray::number(row());
0424 }
0425 
0426 QVariant LocalMessagePart::data(int role) const
0427 {
0428     // The first three roles are for debugging purposes only
0429     switch (role) {
0430     case Qt::DisplayRole:
0431         if (isTopLevelMultipart()) {
0432             return QString::fromUtf8(m_mimetype);
0433         } else {
0434             return QString(QString::fromUtf8(partId()) + QStringLiteral(": ") + QString::fromUtf8(m_mimetype));
0435         }
0436     case Qt::ToolTipRole:
0437         if (m_mimetype == "message/rfc822") {
0438             Q_ASSERT(m_envelope);
0439             QString buf;
0440             QTextStream stream(&buf);
0441             stream << *m_envelope;
0442             return UiUtils::Formatting::htmlEscaped(buf);
0443         } else {
0444             return m_octets > 10000 ? QStringLiteral("%1 bytes of data").arg(m_octets) : QString::fromUtf8(m_data);
0445         }
0446     case Qt::FontRole:
0447     {
0448         QFont f;
0449         f.setItalic(true);
0450         return f;
0451     }
0452     case Imap::Mailbox::RoleIsFetched:
0453         return m_localState == FetchingState::DONE;
0454     case Imap::Mailbox::RoleMessageEnvelope:
0455         if (m_envelope) {
0456             return QVariant::fromValue<Imap::Message::Envelope>(*m_envelope);
0457         } else {
0458             return QVariant();
0459         }
0460     case Imap::Mailbox::RoleMessageHeaderReferences:
0461         return QVariant::fromValue(m_hdrReferences);
0462     case Imap::Mailbox::RoleMessageHeaderListPost:
0463     {
0464         QVariantList res;
0465         Q_FOREACH(const QUrl &url, m_hdrListPost)
0466             res << url;
0467         return res;
0468     }
0469     case Imap::Mailbox::RoleMessageHeaderListPostNo:
0470         return m_hdrListPostNo;
0471     case Imap::Mailbox::RoleIsUnavailable:
0472         return m_localState == FetchingState::UNAVAILABLE;
0473     case Imap::Mailbox::RolePartData:
0474         return m_data;
0475     case Imap::Mailbox::RolePartUnicodeText:
0476         if (m_mimetype.startsWith("text/")) {
0477             return Imap::decodeByteArray(m_data, m_charset);
0478         } else {
0479             return QVariant();
0480         }
0481     case Imap::Mailbox::RolePartMimeType:
0482         return m_mimetype;
0483     case Imap::Mailbox::RolePartCharset:
0484         return m_charset;
0485     case Imap::Mailbox::RolePartContentFormat:
0486         return m_contentFormat;
0487     case Imap::Mailbox::RolePartContentDelSp:
0488         return m_delSp;
0489     case Imap::Mailbox::RolePartTransferEncoding:
0490         return m_transferEncoding;
0491     case Imap::Mailbox::RolePartBodyFldId:
0492         return m_bodyFldId;
0493     case Imap::Mailbox::RolePartBodyDisposition:
0494         return m_bodyDisposition;
0495     case Imap::Mailbox::RolePartFileName:
0496         return m_filename;
0497     case Imap::Mailbox::RolePartOctets:
0498         return m_octets;
0499     case Imap::Mailbox::RolePartId:
0500         return partId();
0501     case Imap::Mailbox::RolePartPathToPart:
0502         return pathToPart();
0503     case Imap::Mailbox::RolePartMultipartRelatedMainCid:
0504         if (m_multipartRelatedStartPart.isEmpty()) {
0505             return m_multipartRelatedStartPart;
0506         } else {
0507             return QVariant();
0508         }
0509     case Imap::Mailbox::RolePartIsTopLevelMultipart:
0510         return isTopLevelMultipart();
0511     case Imap::Mailbox::RolePartForceFetchFromCache:
0512         return QVariant(); // Nothing to do here
0513     case Imap::Mailbox::RolePartBufferPtr:
0514         return QVariant::fromValue(const_cast<QByteArray*>(&m_data));
0515     case Imap::Mailbox::RoleMessageDate:
0516         return m_envelope ? m_envelope->date : QDateTime();
0517     case Imap::Mailbox::RoleMessageSubject:
0518         return m_envelope ? m_envelope->subject : QString();
0519     case Imap::Mailbox::RoleMessageFrom:
0520         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->from) : QVariant();
0521     case Imap::Mailbox::RoleMessageSender:
0522         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->sender) : QVariant();
0523     case Imap::Mailbox::RoleMessageReplyTo:
0524         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->replyTo) : QVariant();
0525     case Imap::Mailbox::RoleMessageTo:
0526         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->to) : QVariant();
0527     case Imap::Mailbox::RoleMessageCc:
0528         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->cc) : QVariant();
0529     case Imap::Mailbox::RoleMessageBcc:
0530         return m_envelope ? Imap::Mailbox::TreeItemMessage::addresListToQVariant(m_envelope->bcc) : QVariant();
0531     case Imap::Mailbox::RoleMessageMessageId:
0532         return m_envelope ? m_envelope->messageId : QByteArray();
0533     case Imap::Mailbox::RoleMessageInReplyTo:
0534         return m_envelope ? QVariant::fromValue(m_envelope->inReplyTo) : QVariant();
0535     case Imap::Mailbox::RoleMessageFlags:
0536         return QVariant();
0537     case Imap::Mailbox::RolePartBodyFldParam:
0538         return QVariant::fromValue<decltype(m_bodyFldParam)>(m_bodyFldParam);
0539     default:
0540         break;
0541     }
0542 
0543     return QVariant();
0544 }
0545 
0546 #ifdef MIME_TREE_DEBUG
0547 QByteArray LocalMessagePart::dumpLocalInfo() const
0548 {
0549     return "LocalMessagePart";
0550 }
0551 #endif
0552 
0553 
0554 }