diff --git a/src/Gui/AttachmentView.cpp b/src/Gui/AttachmentView.cpp index f0bbfd33..99cc1890 100644 --- a/src/Gui/AttachmentView.cpp +++ b/src/Gui/AttachmentView.cpp @@ -1,406 +1,413 @@ /* Copyright (C) 2006 - 2014 Jan Kundrát This file is part of the Trojita Qt IMAP e-mail client, http://trojita.flaska.net/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "AttachmentView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Common/Paths.h" #include "Gui/MessageView.h" // so that the compiler knows it's a QObject #include "Gui/Window.h" #include "Imap/Network/FileDownloadManager.h" #include "Imap/Model/DragAndDrop.h" #include "Imap/Model/MailboxTree.h" #include "Imap/Model/ItemRoles.h" #include "UiUtils/Formatting.h" #include "UiUtils/IconLoader.h" namespace Gui { AttachmentView::AttachmentView(QWidget *parent, Imap::Network::MsgPartNetAccessManager *manager, const QModelIndex &partIndex, MessageView *messageView, QWidget *contentWidget) : QFrame(parent) , m_partIndex(partIndex) , m_messageView(messageView) , m_downloadAttachment(nullptr) , m_openAttachment(nullptr) , m_showHideAttachment(nullptr) , m_showSource(nullptr) , m_netAccess(manager) , m_tmpFile(nullptr) , m_contentWidget(contentWidget) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setFrameStyle(QFrame::NoFrame); setCursor(Qt::OpenHandCursor); setAttribute(Qt::WA_Hover); // not actually required, but styles may assume the parameter and segfault on nullptr deref QStyleOption opt; opt.initFrom(this); const int padding = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this); setContentsMargins(padding, 0, padding, 0); QHBoxLayout *layout = new QHBoxLayout(); layout->setContentsMargins(0,0,0,0); // should be PM_LayoutHorizontalSpacing, but is not implemented by many Qt4 styles -including oxygen- for other conflicts int spacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &opt, this); if (spacing < 0) spacing = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing, &opt, this); layout->setSpacing(0); m_menu = new QMenu(this); m_downloadAttachment = m_menu->addAction(UiUtils::loadIcon(QStringLiteral("document-save-as")), tr("Download")); m_openAttachment = m_menu->addAction(tr("Open Directly")); connect(m_downloadAttachment, &QAction::triggered, this, &AttachmentView::slotDownloadAttachment); connect(m_openAttachment, &QAction::triggered, this, &AttachmentView::slotOpenAttachment); connect(m_menu, &QMenu::aboutToShow, this, &AttachmentView::updateShowHideAttachmentState); if (m_contentWidget) { m_showHideAttachment = m_menu->addAction(UiUtils::loadIcon(QStringLiteral("view-preview")), tr("Show Preview")); m_showHideAttachment->setCheckable(true); m_showHideAttachment->setChecked(!m_contentWidget->isHidden()); connect(m_showHideAttachment, &QAction::triggered, m_contentWidget, &QWidget::setVisible); connect(m_showHideAttachment, &QAction::triggered, this, &AttachmentView::updateShowHideAttachmentState); } if (partIndex.data(Imap::Mailbox::RolePartMimeType).toByteArray() == "message/rfc822") { m_showSource = m_menu->addAction(UiUtils::loadIcon(QStringLiteral("text-x-hex")), tr("Show Message Source")); connect(m_showSource, &QAction::triggered, this, &AttachmentView::showMessageSource); } // Icon on the left m_icon = new QToolButton(this); m_icon->setAttribute(Qt::WA_NoMousePropagation, false); // inform us for DnD m_icon->setAutoRaise(true); m_icon->setIconSize(QSize(22,22)); m_icon->setToolButtonStyle(Qt::ToolButtonIconOnly); m_icon->setPopupMode(QToolButton::MenuButtonPopup); m_icon->setMenu(m_menu); connect(m_icon, &QAbstractButton::pressed, this, &AttachmentView::toggleIconCursor); connect(m_icon, &QAbstractButton::clicked, this, &AttachmentView::showMenuOrPreview); connect(m_icon, &QAbstractButton::released, this, &AttachmentView::toggleIconCursor); m_icon->setCursor(Qt::ArrowCursor); QString mimeDescription = partIndex.data(Imap::Mailbox::RolePartMimeType).toString(); QString rawMime = mimeDescription; QMimeType mimeType = QMimeDatabase().mimeTypeForName(mimeDescription); - if (mimeType.isValid() && !mimeType.isDefault()) { + if (rawMime == QStringLiteral("application/x-trojita-malformed-part-from-imap-response")) { + mimeDescription = QString::fromUtf8(partIndex.data(Imap::Mailbox::RolePartBodyFldParam) + .value() + .value("x-trojita-original-mime-type")); + mimeDescription = tr("IMAP Server error for this part: %1 (%2)").arg( + QMimeDatabase().mimeTypeForName(mimeDescription).comment(), mimeDescription); + m_icon->setIcon(UiUtils::loadIcon(QStringLiteral("emblem-warning"))); + } else if (mimeType.isValid() && !mimeType.isDefault()) { mimeDescription = mimeType.comment(); QIcon icon; if (rawMime == QLatin1String("message/rfc822")) { // Special case for plain e-mail messages. Motivation for this is that most of the OSes ship these icons // with a pixmap which shows something like a sheet of paper as the background. I find it rather dumb // to do this in the context of a MUA where attached messages are pretty common, which is why this special // case is in place. Comments welcome. icon = UiUtils::loadIcon(QStringLiteral("trojita")); } else { icon = QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(mimeType.genericIconName(), UiUtils::loadIcon(QStringLiteral("mail-attachment"))) ); } m_icon->setIcon(icon); } else { m_icon->setIcon(UiUtils::loadIcon(QStringLiteral("mail-attachment"))); } layout->addWidget(m_icon); // space between icon and label layout->addSpacing(spacing); QVBoxLayout *subLayout = new QVBoxLayout; subLayout->setContentsMargins(0,0,0,0); // The file name shall be mouse-selectable m_fileName = new QLabel(this); m_fileName->setTextFormat(Qt::PlainText); m_fileName->setText(partIndex.data(Imap::Mailbox::RolePartFileName).toString()); m_fileName->setTextInteractionFlags(Qt::TextSelectableByMouse); m_fileName->setCursor(Qt::IBeamCursor); m_fileName->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); subLayout->addWidget(m_fileName); // Some metainformation -- the MIME type and the file size QLabel *lbl = new QLabel(tr("%2, %3").arg(mimeDescription, UiUtils::Formatting::prettySize(partIndex.data(Imap::Mailbox::RolePartOctets).toULongLong()))); if (rawMime != mimeDescription) { lbl->setToolTip(rawMime); } QFont f(lbl->font()); f.setItalic(true); if (f.pointSize() > -1) // don't try the below on pixel fonts ever. f.setPointSizeF(f.pointSizeF() * 0.8); lbl->setFont(f); subLayout->addWidget(lbl); layout->addLayout(subLayout); // space between label and arrow layout->addSpacing(spacing); layout->addStretch(100); QVBoxLayout *contentLayout = new QVBoxLayout(this); contentLayout->addLayout(layout); if (m_contentWidget) { contentLayout->addWidget(m_contentWidget); m_contentWidget->setCursor(Qt::ArrowCursor); } updateShowHideAttachmentState(); } void AttachmentView::slotDownloadAttachment() { m_downloadAttachment->setEnabled(false); Imap::Network::FileDownloadManager *manager = new Imap::Network::FileDownloadManager(this, m_netAccess, m_partIndex); connect(manager, &Imap::Network::FileDownloadManager::fileNameRequested, this, &AttachmentView::slotFileNameRequested); connect(manager, &Imap::Network::FileDownloadManager::transferError, m_messageView, &MessageView::transferError); connect(manager, &Imap::Network::FileDownloadManager::transferError, this, &AttachmentView::enableDownloadAgain); connect(manager, &Imap::Network::FileDownloadManager::transferError, manager, &QObject::deleteLater); connect(manager, &Imap::Network::FileDownloadManager::cancelled, this, &AttachmentView::enableDownloadAgain); connect(manager, &Imap::Network::FileDownloadManager::cancelled, manager, &QObject::deleteLater); connect(manager, &Imap::Network::FileDownloadManager::succeeded, this, &AttachmentView::enableDownloadAgain); connect(manager, &Imap::Network::FileDownloadManager::succeeded, manager, &QObject::deleteLater); manager->downloadPart(); } void AttachmentView::slotOpenAttachment() { m_openAttachment->setEnabled(false); Imap::Network::FileDownloadManager *manager = new Imap::Network::FileDownloadManager(this, m_netAccess, m_partIndex); connect(manager, &Imap::Network::FileDownloadManager::fileNameRequested, this, &AttachmentView::slotFileNameRequestedOnOpen); connect(manager, &Imap::Network::FileDownloadManager::transferError, m_messageView, &MessageView::transferError); connect(manager, &Imap::Network::FileDownloadManager::transferError, this, &AttachmentView::onOpenFailed); connect(manager, &Imap::Network::FileDownloadManager::transferError, manager, &QObject::deleteLater); // we aren't connecting to cancelled() as it cannot really happen -- the filename is never empty connect(manager, &Imap::Network::FileDownloadManager::succeeded, this, &AttachmentView::openDownloadedAttachment); connect(manager, &Imap::Network::FileDownloadManager::succeeded, manager, &QObject::deleteLater); manager->downloadPart(); } void AttachmentView::slotFileNameRequestedOnOpen(QString *fileName) { Q_ASSERT(!m_tmpFile); m_tmpFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/trojita-attachment-XXXXXX-") + fileName->replace(QLatin1Char('/'), QLatin1Char('_'))); m_tmpFile->setAutoRemove(false); m_tmpFile->open(); *fileName = m_tmpFile->fileName(); } void AttachmentView::slotFileNameRequested(QString *fileName) { static QDir lastDir = QDir(Common::writablePath(Common::LOCATION_DOWNLOAD)); if (!lastDir.exists()) lastDir = QDir(Common::writablePath(Common::LOCATION_DOWNLOAD)); QString fileLocation = lastDir.filePath(*fileName); *fileName = QFileDialog::getSaveFileName(this, tr("Save Attachment"), fileLocation, QString(), nullptr, QFileDialog::HideNameFilterDetails); if (!fileName->isEmpty()) lastDir = QFileInfo(*fileName).absoluteDir(); } void AttachmentView::enableDownloadAgain() { m_downloadAttachment->setEnabled(true); } void AttachmentView::onOpenFailed() { delete m_tmpFile; m_tmpFile = nullptr; m_openAttachment->setEnabled(true); } void AttachmentView::openDownloadedAttachment() { Q_ASSERT(m_tmpFile); // Make sure that the file is read-only so that the launched application does not attempt to modify it m_tmpFile->setPermissions(QFile::ReadOwner); QDesktopServices::openUrl(QUrl::fromLocalFile(m_tmpFile->fileName())); delete m_tmpFile; m_tmpFile = nullptr; m_openAttachment->setEnabled(true); } bool AttachmentView::previewIsShown() const { return m_contentWidget && m_contentWidget->isVisibleTo(const_cast(this)); } void AttachmentView::updateShowHideAttachmentState() { if (m_showHideAttachment) { m_showHideAttachment->setChecked(previewIsShown()); } } void AttachmentView::showMenuOrPreview() { if (previewIsShown() || !m_contentWidget) { showMenu(); } else { m_showHideAttachment->trigger(); } } void AttachmentView::showMenu() { if (QToolButton *btn = qobject_cast(sender())) { btn->setDown(false); } QPoint p = QCursor::pos(); p.rx() -= m_menu->width()/2; m_menu->popup(p); } void AttachmentView::toggleIconCursor() { if (m_icon->isDown()) m_icon->setCursor(Qt::OpenHandCursor); else m_icon->setCursor(Qt::ArrowCursor); } void AttachmentView::indicateHover() { if (m_menu->isVisible() || rect().contains(mapFromGlobal(QCursor::pos()))) { // WA_UnderMouse is wrong if (!autoFillBackground()) { setAutoFillBackground(true); QPalette pal(palette()); QLinearGradient grad(0,0,0,height()); grad.setColorAt(0, pal.color(backgroundRole())); grad.setColorAt(0.15, pal.color(backgroundRole()).lighter(110)); grad.setColorAt(0.8, pal.color(backgroundRole()).darker(110)); grad.setColorAt(1, pal.color(backgroundRole())); pal.setBrush(backgroundRole(), grad); setPalette(pal); } } else { setAutoFillBackground(false); setPalette(QPalette()); } } void AttachmentView::mousePressEvent(QMouseEvent *event) { event->accept(); if (event->button() == Qt::RightButton) { showMenu(); return; } m_dragStartPos = event->pos(); QFrame::mousePressEvent(event); } void AttachmentView::mouseMoveEvent(QMouseEvent *event) { QFrame::mouseMoveEvent(event); if (!(event->buttons() & Qt::LeftButton)) { return; } if ((m_dragStartPos - event->pos()).manhattanLength() < QApplication::startDragDistance()) return; QMimeData *mimeData = Imap::Mailbox::mimeDataForDragAndDrop(m_partIndex); if (!mimeData) return; event->accept(); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setHotSpot(event->pos()); drag->exec(Qt::CopyAction, Qt::CopyAction); } void AttachmentView::paintEvent(QPaintEvent *event) { QFrame::paintEvent(event); QPainter p(this); const int x = m_icon->geometry().width() + m_fileName->sizeHint().width() + 32; if (x >= rect().width()) return; QLinearGradient grad(x, 0, rect().right(), 0); const QColor c = testAttribute(Qt::WA_UnderMouse) ? palette().color(QPalette::Highlight) : palette().color(backgroundRole()).darker(120); grad.setColorAt(0, palette().color(backgroundRole())); grad.setColorAt(0.5, c); grad.setColorAt(1, palette().color(backgroundRole())); p.setBrush(grad); p.setPen(Qt::NoPen); p.drawRect(x, m_fileName->geometry().center().y(), width(), 1); p.end(); } QString AttachmentView::quoteMe() const { const AbstractPartWidget *widget = dynamic_cast(m_contentWidget); return widget && !m_contentWidget->isHidden() ? widget->quoteMe() : QString(); } bool AttachmentView::searchDialogRequested() { if (AbstractPartWidget *widget = dynamic_cast(m_contentWidget)) return widget->searchDialogRequested(); return false; } #define IMPL_PART_FORWARD_ONE_METHOD(METHOD) \ void AttachmentView::METHOD() \ {\ if (AbstractPartWidget *w = dynamic_cast(m_contentWidget)) \ w->METHOD(); \ } IMPL_PART_FORWARD_ONE_METHOD(reloadContents) IMPL_PART_FORWARD_ONE_METHOD(zoomIn) IMPL_PART_FORWARD_ONE_METHOD(zoomOut) IMPL_PART_FORWARD_ONE_METHOD(zoomOriginal) void AttachmentView::showMessageSource() { auto w = MainWindow::messageSourceWidget(m_partIndex); w->setWindowTitle(tr("Source of Attached Message")); w->show(); } } diff --git a/src/UiUtils/PartWalker_impl.h b/src/UiUtils/PartWalker_impl.h index 87841fd9..3f61562c 100644 --- a/src/UiUtils/PartWalker_impl.h +++ b/src/UiUtils/PartWalker_impl.h @@ -1,219 +1,220 @@ /* Copyright (C) 2006 - 2014 Jan Kundrát This file is part of the Trojita Qt IMAP e-mail client, http://trojita.flaska.net/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "UiUtils/PartWalker.h" #include "Imap/Model/ItemRoles.h" namespace UiUtils { template PartWalker::PartWalker(Imap::Network::MsgPartNetAccessManager *manager, Context context, std::unique_ptr > visitor) : m_manager(manager), m_netWatcher(0), m_context(context) { m_visitor = std::move(visitor); } template Result PartWalker::walk(const QModelIndex &partIndex,int recursionDepth, const UiUtils::PartLoadingOptions loadingMode) { using namespace Imap::Mailbox; Q_ASSERT(partIndex.isValid()); if (recursionDepth > 1000) { return m_visitor->visitError(PartWalker::tr("This message contains too deep nesting of MIME message parts.\n" "To prevent stack exhaustion and your head from exploding, only\n" "the top-most thousand items or so are shown."), 0); } QString mimeType = partIndex.data(Imap::Mailbox::RolePartMimeType).toString().toLower(); bool isMessageRfc822 = mimeType == QLatin1String("message/rfc822"); bool isCompoundMimeType = mimeType.startsWith(QLatin1String("multipart/")) || isMessageRfc822; if (loadingMode & PART_IS_HIDDEN) { return m_visitor->visitLoadablePart(0, m_manager, partIndex, this, recursionDepth + 1, loadingMode | PART_IGNORE_CLICKTHROUGH); } // Check if we are dealing with encrypted data if (mimeType == QLatin1String("multipart/encrypted")) { return m_visitor->visitMultipartEncryptedView(0, this, partIndex, recursionDepth, loadingMode); } // Check whether we can render this MIME type at all QStringList allowedMimeTypes; allowedMimeTypes << QStringLiteral("text/html") << QStringLiteral("text/plain") << QStringLiteral("image/jpeg") << QStringLiteral("image/jpg") << QStringLiteral("image/pjpeg") << QStringLiteral("image/png") << QStringLiteral("image/gif"); bool recognizedMimeType = isCompoundMimeType || allowedMimeTypes.contains(mimeType); bool isDerivedMimeType = false; if (!recognizedMimeType) { // QMimeType's docs say that one shall use inherit() to check for "is this a recognized MIME type". // E.g. text/x-csrc inherits text/plain. QMimeType partType = QMimeDatabase().mimeTypeForName(mimeType); Q_FOREACH(const QString &candidate, allowedMimeTypes) { if (partType.isValid() && !partType.isDefault() && partType.inherits(candidate)) { // Looks like we shall be able to show this recognizedMimeType = true; // If it's a derived MIME type, it makes sense to not block inline display, yet still make it possible to hide it // using the regular attachment controls isDerivedMimeType = true; m_manager->registerMimeTypeTranslation(mimeType, candidate); break; } } } // Check whether it shall be wrapped inside an AttachmentView // From section 2.8 of RFC 2183: "Unrecognized disposition types should be treated as `attachment'." const QByteArray contentDisposition = partIndex.data(Imap::Mailbox::RolePartBodyDisposition).toByteArray().toLower(); const bool isInline = (contentDisposition.isEmpty() || contentDisposition == "inline") && !(loadingMode & PART_IGNORE_INLINE); const bool looksLikeAttachment = !partIndex.data(Imap::Mailbox::RolePartFileName).toString().isEmpty(); + const bool isMimeParsingError = mimeType == QStringLiteral("application/x-trojita-malformed-part-from-imap-response"); const bool wrapInAttachmentView = !(loadingMode & PART_IGNORE_DISPOSITION_ATTACHMENT) - && (looksLikeAttachment || !isInline || !recognizedMimeType || isDerivedMimeType || isMessageRfc822); + && (looksLikeAttachment || !isInline || !recognizedMimeType || isDerivedMimeType || isMessageRfc822 || isMimeParsingError); if (wrapInAttachmentView) { // The problem is that some nasty MUAs (hint hint Thunderbird) would // happily attach a .tar.gz and call it "inline" Result contentView = 0; if (recognizedMimeType) { PartLoadingOptions options = loadingMode | PART_IGNORE_DISPOSITION_ATTACHMENT; if (!isInline) { // The widget will be hidden by default, i.e. the "inline preview" will be deactivated. // If the user clicks that action in the AttachmentView, it makes sense to load the plugin without any further ado, // without requiring an extra clickthrough options |= PART_IS_HIDDEN; } else if (!isCompoundMimeType) { // This is to prevent a clickthrough when the data can be already shown partIndex.data(Imap::Mailbox::RolePartForceFetchFromCache); // This makes sure that clickthrough only affects big parts during "expensive network" mode if ( (m_netWatcher && m_netWatcher->desiredNetworkPolicy() != Imap::Mailbox::NETWORK_EXPENSIVE) || partIndex.data(Imap::Mailbox::RolePartOctets).toULongLong() <= ExpensiveFetchThreshold) { options |= PART_IGNORE_CLICKTHROUGH; } } else { // A compound type -> make sure we disable clickthrough options |= PART_IGNORE_CLICKTHROUGH; } if (m_netWatcher && m_netWatcher->effectiveNetworkPolicy() == Imap::Mailbox::NETWORK_OFFLINE) { // This is to prevent a clickthrough when offline options |= PART_IGNORE_CLICKTHROUGH; } contentView = m_visitor->visitLoadablePart(0, m_manager, partIndex, this, recursionDepth + 1, options); if (!isInline) { m_visitor->applySetHidden(contentView); } } // Previously, we would also hide an attachment if the current policy was "expensive network", the part was too big // and not fetched yet. Arguably, that's a bug -- an item which is marked online shall not be hidden. // Wrapping via a clickthrough is fine, though; the user is clearly informed that this item *should* be visible, // yet the bandwidth is not excessively trashed. return m_visitor->visitAttachmentPart(0, m_manager, partIndex, m_context, contentView); } // Now we know for sure that it's not supposed to be wrapped in an AttachmentView, cool. if (mimeType.startsWith(QLatin1String("multipart/"))) { // it's a compound part if (mimeType == QLatin1String("multipart/alternative")) { return m_visitor->visitMultipartAlternative(0, this, partIndex, recursionDepth, loadingMode); } else if (mimeType == QLatin1String("multipart/signed")) { return m_visitor->visitMultipartSignedView(0, this, partIndex, recursionDepth, loadingMode); } else if (mimeType == QLatin1String("multipart/related")) { // The purpose of this section is to find a text/html e-mail, along with its associated body parts, and hide // everything else than the HTML widget. // At this point, it might be interesting to somehow respect the user's preference about using text/plain // instead of text/html. However, things are a bit complicated; the widget used at this point really wants // to either show just a single part or alternatively all of them in a sequence. // Furthermore, if someone sends a text/plain and a text/html together inside a multipart/related, they're // just wrong. // Let's see if we know what the root part is QModelIndex mainPartIndex; QVariant mainPartCID = partIndex.data(RolePartMultipartRelatedMainCid); if (mainPartCID.isValid()) { mainPartIndex = Imap::Network::MsgPartNetAccessManager::cidToPart(partIndex, mainPartCID.toByteArray()); } if (!mainPartIndex.isValid()) { // The Content-Type-based start parameter was not terribly useful. Let's find the HTML part manually. QModelIndex candidate = partIndex.child(0, 0); while (candidate.isValid()) { if (candidate.data(RolePartMimeType).toString() == QLatin1String("text/html")) { mainPartIndex = candidate; break; } candidate = candidate.sibling(candidate.row() + 1, 0); } } if (mainPartIndex.isValid()) { if (mainPartIndex.data(RolePartMimeType).toString() == QLatin1String("text/html")) { return walk(mainPartIndex, recursionDepth+1, loadingMode); } else { // Sorry, but anything else than text/html is by definition suspicious here. Better than picking some random // choice, let's just show everything. return m_visitor->visitGenericMultipartView(0, this, partIndex, recursionDepth, loadingMode); } } else { // The RFC2387's wording is clear that in absence of an explicit START argument, the first part is the starting one. // On the other hand, I've seen real-world messages whose first part is some utter garbage (an image sent as // application/octet-stream, for example) and some *other* part is an HTML text. In that case (and if we somehow // failed to pick the HTML part by a heuristic), it's better to show everything. return m_visitor->visitGenericMultipartView(0, this, partIndex, recursionDepth, loadingMode); } } else { return m_visitor->visitGenericMultipartView(0, this, partIndex, recursionDepth, loadingMode); } } else if (mimeType == QLatin1String("message/rfc822")) { return m_visitor->visitMessage822View(0, this, partIndex, recursionDepth, loadingMode); } else { partIndex.data(Imap::Mailbox::RolePartForceFetchFromCache); if ((loadingMode & PART_IGNORE_CLICKTHROUGH) || (loadingMode & PART_IGNORE_LOAD_ON_SHOW) || partIndex.data(Imap::Mailbox::RoleIsFetched).toBool() || (m_netWatcher && m_netWatcher->desiredNetworkPolicy() != Imap::Mailbox::NETWORK_EXPENSIVE ) || partIndex.data(Imap::Mailbox::RolePartOctets).toULongLong() < ExpensiveFetchThreshold) { // Show it directly without any fancy wrapping return m_visitor->visitSimplePartView(0, m_manager, partIndex, m_context); } else { return m_visitor->visitLoadablePart(0, m_manager, partIndex, this, recursionDepth + 1, (m_netWatcher && m_netWatcher->effectiveNetworkPolicy() != Imap::Mailbox::NETWORK_OFFLINE) ? loadingMode : loadingMode | PART_IGNORE_CLICKTHROUGH); } } } template Context PartWalker::context() const { return m_context; } template void PartWalker::setNetworkWatcher(Imap::Mailbox::NetworkWatcher *netWatcher) { m_netWatcher = netWatcher; } }