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 }