diff --git a/messageviewer/src/viewer/messagepart.h b/messageviewer/src/viewer/messagepart.h --- a/messageviewer/src/viewer/messagepart.h +++ b/messageviewer/src/viewer/messagepart.h @@ -164,6 +164,22 @@ const QString &mMsg; }; +// Make sure the whole content is relative, so that nothing is painted over the header +// if a malicious message uses absolute positioning. +// Also force word wrapping, which is useful for printing, see https://issues.kolab.org/issue3992. +class RootBlock : public HTMLBlock +{ +public: + RootBlock(MessageViewer::HtmlWriter *writer); + virtual ~RootBlock(); + +private: + void internalEnter(); + void internalExit(); + + HtmlWriter* mWriter; +}; + class MessagePart : public Interface::MessagePart { public: @@ -181,16 +197,20 @@ PartMetaData *partMetaData(); + /* only a function that should be removed if the refactoring is over */ + virtual void fix() const; + virtual void copyContentFrom() const; + protected: void parseInternal(KMime::Content *node, bool onlyOneMimePart); - void renderInternalHtml() const; - void copyContentFrom() const; + void renderInternalHtml( bool decorate ) const; QString renderInternalText() const; HTMLBlock::Ptr attachmentBlock() const; QString mText; ObjectTreeParser *mOtp; ObjectTreeParser *mSubOtp; + MessagePart::Ptr mSubMessagePart; PartMetaData mMetaData; KMime::Content *mAttachmentNode; }; @@ -222,15 +242,23 @@ QString text() const Q_DECL_OVERRIDE; void html(bool decorate) Q_DECL_OVERRIDE; - void appendMessagePart(const MessagePart::Ptr &messagePart); + void setIsRoot(bool root); + bool isRoot() const; - const QVector &messageParts() const; + void appendMessagePart(const Interface::MessagePart::Ptr &messagePart); + + const QVector &messageParts() const; + + void fix() const Q_DECL_OVERRIDE; + void copyContentFrom() const Q_DECL_OVERRIDE; protected: - void htmlInternal(bool decorate); + HTMLBlock::Ptr rootBlock() const; + void htmlInternal(bool decorate); private: - QVector mBlocks; + QVector mBlocks; + bool mRoot; }; enum IconType { @@ -252,6 +280,7 @@ KMMsgEncryptionState encryptionState() const; bool decryptMessage() const; + private: void parseContent(); KMime::Content *mNode; @@ -273,8 +302,7 @@ QString text() const Q_DECL_OVERRIDE; void html(bool decorate) Q_DECL_OVERRIDE; - /* only a function that should be removed if the refactiring is over */ - void fix(); + void fix() const Q_DECL_OVERRIDE; private: KMime::Content* mNode; @@ -294,7 +322,9 @@ void html(bool decorate) Q_DECL_OVERRIDE; void setViewHtml(bool html); - bool viewHtml(); + bool viewHtml() const; + void fix() const Q_DECL_OVERRIDE; + void copyContentFrom() const Q_DECL_OVERRIDE; private: KMime::Content* mTextNode; KMime::Content* mHTMLNode; @@ -331,6 +361,8 @@ QString text() const Q_DECL_OVERRIDE; void html(bool decorate) Q_DECL_OVERRIDE; + void copyContentFrom() const Q_DECL_OVERRIDE; + void fix() const Q_DECL_OVERRIDE; private: const KMime::Message::Ptr mMessage; KMime::Content *mNode; diff --git a/messageviewer/src/viewer/messagepart.cpp b/messageviewer/src/viewer/messagepart.cpp --- a/messageviewer/src/viewer/messagepart.cpp +++ b/messageviewer/src/viewer/messagepart.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -366,6 +365,39 @@ mWriter->queue(QStringLiteral("\n")); } +RootBlock::RootBlock(HtmlWriter* writer) + : HTMLBlock() + , mWriter(writer) +{ + internalEnter(); +} + +RootBlock::~RootBlock() +{ + internalExit(); +} + +void RootBlock::internalEnter() +{ + if (!mWriter || entered) { + return; + } + entered = true; + + mWriter->queue(QStringLiteral("
\n")); +} + +void RootBlock::internalExit() +{ + if (!entered) { + return; + } + + entered = false; + + mWriter->queue(QStringLiteral("
\n")); +} + //------MessagePart----------------------- MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text) @@ -380,7 +412,6 @@ MessagePart::~MessagePart() { if (mSubOtp) { - delete mSubOtp->htmlWriter(); delete mSubOtp; mSubOtp = Q_NULLPTR; } @@ -437,81 +468,127 @@ { mSubOtp = new ObjectTreeParser(mOtp, onlyOneMimePart); mSubOtp->setAllowAsync(mOtp->allowAsync()); - if (mOtp->htmlWriter()) { - mSubOtp->mHtmlWriter = new QueueHtmlWriter(mOtp->htmlWriter()); - } - mSubOtp->parseObjectTreeInternal(node); + mSubMessagePart = mSubOtp->parseObjectTreeInternal(node); } -void MessagePart::renderInternalHtml() const +void MessagePart::renderInternalHtml(bool decorate) const { - if (mSubOtp && mOtp->htmlWriter()) { - static_cast(mSubOtp->htmlWriter())->replay(); + if (mSubMessagePart) { + mSubMessagePart->html(decorate); } } void MessagePart::copyContentFrom() const { - if (mSubOtp) { - mOtp->copyContentFrom(mSubOtp); + if (mSubMessagePart) { + mSubMessagePart->copyContentFrom(); + if (mSubOtp) { + mOtp->copyContentFrom(mSubOtp); + } } } QString MessagePart::renderInternalText() const { - if (!mSubOtp) { + if (!mSubMessagePart) { return QString(); } - return mSubOtp->plainTextContent(); + return mSubMessagePart->text(); +} + +void MessagePart::fix() const +{ + if (mSubMessagePart) { + mSubMessagePart->fix(); + } } //-----MessagePartList---------------------- MessagePartList::MessagePartList(ObjectTreeParser* otp) : MessagePart(otp, QString()) + , mRoot(false) { } MessagePartList::~MessagePartList() { } -void MessagePartList::appendMessagePart(const MessagePart::Ptr& messagePart) +void MessagePartList::setIsRoot(bool root) +{ + mRoot = root; +} + +bool MessagePartList::isRoot() const +{ + return mRoot; +} + +HTMLBlock::Ptr MessagePartList::rootBlock() const +{ + if (mOtp->htmlWriter() && isRoot()) { + return HTMLBlock::Ptr(new RootBlock(mOtp->htmlWriter())); + } + return HTMLBlock::Ptr(); +} + +void MessagePartList::appendMessagePart(const Interface::MessagePart::Ptr& messagePart) { mBlocks.append(messagePart); } -const QVector& MessagePartList::messageParts() const +const QVector& MessagePartList::messageParts() const { return mBlocks; } - void MessagePartList::html(bool decorate) { MessageViewer::HtmlWriter *writer = mOtp->htmlWriter(); + const HTMLBlock::Ptr rBlock(rootBlock()); const HTMLBlock::Ptr aBlock(attachmentBlock()); htmlInternal(decorate); } void MessagePartList::htmlInternal(bool decorate) { - foreach (const MessagePart::Ptr &mp, mBlocks) { + foreach (const auto &mp, mBlocks) { mp->html(decorate); } } QString MessagePartList::text() const { QString text; - foreach (const MessagePart::Ptr &mp, mBlocks) { + foreach (const auto &mp, mBlocks) { text += mp->text(); } return text; } +void MessagePartList::fix() const +{ + foreach (const auto &mp, mBlocks) { + const auto m = mp.dynamicCast(); + if (m) { + m->fix(); + } + } +} + +void MessagePartList::copyContentFrom() const +{ + foreach (const auto &mp, mBlocks) { + const auto m = mp.dynamicCast(); + if (m) { + m->copyContentFrom(); + } + } +} + //-----TextMessageBlock---------------------- TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage, IconType asIcon) @@ -599,10 +676,11 @@ continue; } - const PartMetaData *messagePart(messageParts().last()->partMetaData()); + const auto mp = messageParts().last().staticCast(); + const PartMetaData *messagePart(mp->partMetaData()); if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { - messageParts().last()->setText(aCodec->toUnicode(block.text())); + mp->setText(aCodec->toUnicode(block.text())); } if (messagePart->isEncrypted) { @@ -674,15 +752,14 @@ { } -void HtmlMessagePart::fix() +void HtmlMessagePart::fix() const { mOtp->mHtmlContent += mBodyHTML; mOtp->mHtmlContentCharset = mCharset; } void HtmlMessagePart::html(bool decorate) { - fix(); MessageViewer::HtmlWriter *writer = mOtp->htmlWriter(); if (!writer) { return; @@ -768,10 +845,8 @@ void MimeMessagePart::html(bool decorate) { - copyContentFrom(); const HTMLBlock::Ptr aBlock(attachmentBlock()); - - renderInternalHtml(); + renderInternalHtml(decorate); } QString MimeMessagePart::text() const @@ -811,7 +886,7 @@ mViewHtml = html; } -bool AlternativeMessagePart::viewHtml() +bool AlternativeMessagePart::viewHtml() const { return mViewHtml; } @@ -821,21 +896,12 @@ MessageViewer::HtmlWriter *writer = mOtp->htmlWriter(); if (!writer) { - // If there is no HTML writer, process both the HTML and the plain text nodes, as we're collecting - // the plainTextContent and the htmlContent - if (mTextPart) { - mTextPart->copyContentFrom(); - } - if (mHTMLPart) { - mHTMLPart->copyContentFrom(); - } return; } const HTMLBlock::Ptr aBlock(attachmentBlock()); if (viewHtml() && mHTMLPart) { - mHTMLPart->copyContentFrom(); mHTMLPart->html(decorate); } else if (mTextNode) { mTextPart->html(decorate); @@ -850,6 +916,28 @@ return QString(); } +void AlternativeMessagePart::fix() const +{ + if (mTextPart) { + mTextPart->fix(); + } + + if (viewHtml() && mHTMLPart) { + mHTMLPart->fix(); + } +} + +void AlternativeMessagePart::copyContentFrom() const +{ + if (mTextPart) { + mTextPart->copyContentFrom(); + } + + if (viewHtml() && mHTMLPart) { + mHTMLPart->copyContentFrom(); + } +} + //-----CertMessageBlock---------------------- CertMessagePart::CertMessagePart(ObjectTreeParser* otp, KMime::Content* node, const Kleo::CryptoBackend::Protocol *cryptoProto, bool autoImport) @@ -1071,13 +1159,9 @@ void CryptoMessagePart::html(bool decorate) { - bool hideErrors = false; MessageViewer::HtmlWriter *writer = mOtp->htmlWriter(); - //TODO: still the following part should not be here - copyContentFrom(); - if (!writer) { return; } @@ -1110,7 +1194,7 @@ } } else if (mNode) { const CryptoBlock block(mOtp, &mMetaData, mCryptoProto, mFromAddress, mNode); - renderInternalHtml(); + renderInternalHtml(decorate); } else { MessagePart::html(decorate); } @@ -1149,7 +1233,7 @@ void EncapsulatedRfc822MessagePart::html(bool decorate) { Q_UNUSED(decorate) - if (!mSubOtp) { + if (!mSubMessagePart) { return; } @@ -1163,12 +1247,20 @@ const CryptoBlock block(mOtp, &mMetaData, Q_NULLPTR, mMessage->from()->asUnicodeString(), mMessage.data()); writer->queue(mOtp->mSource->createMessageHeader(mMessage.data())); - renderInternalHtml(); + renderInternalHtml(decorate); mOtp->nodeHelper()->setPartMetaData(mNode, mMetaData); } QString EncapsulatedRfc822MessagePart::text() const { return renderInternalText(); } + +void EncapsulatedRfc822MessagePart::copyContentFrom() const +{ +} + +void EncapsulatedRfc822MessagePart::fix() const +{ +} diff --git a/messageviewer/src/viewer/objecttreeparser.h b/messageviewer/src/viewer/objecttreeparser.h --- a/messageviewer/src/viewer/objecttreeparser.h +++ b/messageviewer/src/viewer/objecttreeparser.h @@ -364,7 +364,7 @@ * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the * top-level content. */ - void parseObjectTreeInternal(KMime::Content *node); + MessagePart::Ptr parseObjectTreeInternal(KMime::Content *node); MessagePart::Ptr defaultHandling(KMime::Content *node, ProcessResult &result); diff --git a/messageviewer/src/viewer/objecttreeparser.cpp b/messageviewer/src/viewer/objecttreeparser.cpp --- a/messageviewer/src/viewer/objecttreeparser.cpp +++ b/messageviewer/src/viewer/objecttreeparser.cpp @@ -281,18 +281,24 @@ void ObjectTreeParser::parseObjectTree(KMime::Content *node) { mTopLevelContent = node; - parseObjectTreeInternal(node); + const auto mp = parseObjectTreeInternal(node); + + if (mp) { + mp->fix(); + mp->copyContentFrom(); + mp->html(false); + } } void ObjectTreeParser::setPrinting(bool printing) { mPrinting = printing; } -void ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node) +MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node) { if (!node) { - return; + return MessagePart::Ptr(); } // reset pending async jobs state (we'll rediscover pending jobs as we go) @@ -310,13 +316,9 @@ mNodeHelper->setNodeUnprocessed(node, true); } - // Make sure the whole content is relative, so that nothing is painted over the header - // if a malicious message uses absolute positioning. - // Also force word wrapping, which is useful for printing, see https://issues.kolab.org/issue3992. - bool isRoot = node->isTopLevel(); - if (isRoot && htmlWriter()) { - htmlWriter()->queue(QStringLiteral("
\n")); - } + const bool isRoot = node->isTopLevel(); + MessagePartList::Ptr mpl(new MessagePartList(this)); + mpl->setIsRoot(isRoot); for (; node; node = MessageCore::NodeHelper::nextSibling(node)) { if (mNodeHelper->nodeProcessed(node)) { @@ -359,7 +361,7 @@ if (const auto mp = dynamic_cast(result.data())) { mp->setAttachmentFlag(node); - mp->html(false); + mpl->appendMessagePart(result); bRendered = true; break; } else if (dynamic_cast(result.data())) { @@ -371,7 +373,7 @@ const auto mp = defaultHandling(node, processResult); if (mp) { mp->setAttachmentFlag(node); - mp->html(false); + mpl->appendMessagePart(mp); } bRendered = true; break; @@ -387,7 +389,7 @@ const auto mp = defaultHandling(node, processResult); if (mp) { mp->setAttachmentFlag(node); - mp->html(false); + mpl->appendMessagePart(mp); } } mNodeHelper->setNodeProcessed(node, false); @@ -400,9 +402,7 @@ } } - if (isRoot && htmlWriter()) { - htmlWriter()->queue(QStringLiteral("
\n")); - } + return mpl; } MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node, ProcessResult &result)