From b968ea8ed364238c57c3e74cf2c122cb897cfbea Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 23 May 2017 19:13:13 +0200 Subject: Builds but doesn't link, no formatters yet --- framework/src/domain/mailtemplates.cpp | 2 +- framework/src/domain/mimetreeparser/CMakeLists.txt | 8 +- framework/src/domain/mimetreeparser/interface.cpp | 6 +- .../src/domain/mimetreeparser/objecttreesource.cpp | 8 +- .../src/domain/mimetreeparser/objecttreesource.h | 2 +- .../src/domain/mimetreeparser/otp/CMakeLists.txt | 180 +++ .../mimetreeparser/otp/attachmentstrategy.cpp | 343 +++++ .../domain/mimetreeparser/otp/attachmentstrategy.h | 86 ++ .../otp/attachmenttemporaryfilesdirs.cpp | 108 ++ .../otp/attachmenttemporaryfilesdirs.h | 57 + .../src/domain/mimetreeparser/otp/bodypart.cpp | 41 + framework/src/domain/mimetreeparser/otp/bodypart.h | 209 +++ .../mimetreeparser/otp/bodypartformatter.cpp | 147 +++ .../domain/mimetreeparser/otp/bodypartformatter.h | 149 +++ .../otp/bodypartformatterbasefactory.cpp | 208 +++ .../otp/bodypartformatterbasefactory.h | 85 ++ .../otp/bodypartformatterbasefactory_p.h | 57 + .../mimetreeparser/otp/cryptobodypartmemento.cpp | 56 + .../mimetreeparser/otp/cryptobodypartmemento.h | 75 ++ .../src/domain/mimetreeparser/otp/cryptohelper.cpp | 150 +++ .../src/domain/mimetreeparser/otp/cryptohelper.h | 62 + .../otp/decryptverifybodypartmemento.cpp | 86 ++ .../otp/decryptverifybodypartmemento.h | 81 ++ framework/src/domain/mimetreeparser/otp/enums.h | 54 + .../domain/mimetreeparser/otp/filehtmlwriter.cpp | 119 ++ .../src/domain/mimetreeparser/otp/filehtmlwriter.h | 70 + .../src/domain/mimetreeparser/otp/htmlwriter.cpp | 40 + .../src/domain/mimetreeparser/otp/htmlwriter.h | 125 ++ .../src/domain/mimetreeparser/otp/messagepart.cpp | 1352 ++++++++++++++++++++ .../src/domain/mimetreeparser/otp/messagepart.h | 422 ++++++ .../mimetreeparser/otp/messagepartrenderer.cpp | 23 + .../mimetreeparser/otp/messagepartrenderer.h | 43 + .../mimetreeparser/otp/mimetreeparser_debug.cpp | 3 + .../mimetreeparser/otp/mimetreeparser_debug.h | 4 + .../src/domain/mimetreeparser/otp/nodehelper.cpp | 1069 ++++++++++++++++ .../src/domain/mimetreeparser/otp/nodehelper.h | 290 +++++ .../domain/mimetreeparser/otp/objecttreeparser.cpp | 495 +++++++ .../domain/mimetreeparser/otp/objecttreeparser.h | 406 ++++++ .../domain/mimetreeparser/otp/objecttreesource.cpp | 28 + .../domain/mimetreeparser/otp/objecttreesource.h | 109 ++ .../src/domain/mimetreeparser/otp/partmetadata.h | 67 + .../domain/mimetreeparser/otp/partnodebodypart.cpp | 125 ++ .../domain/mimetreeparser/otp/partnodebodypart.h | 108 ++ .../mimetreeparser/otp/qgpgmejobexecutor.cpp | 158 +++ .../domain/mimetreeparser/otp/qgpgmejobexecutor.h | 86 ++ .../domain/mimetreeparser/otp/queuehtmlwriter.cpp | 136 ++ .../domain/mimetreeparser/otp/queuehtmlwriter.h | 75 ++ framework/src/domain/mimetreeparser/otp/util.cpp | 136 ++ framework/src/domain/mimetreeparser/otp/util.h | 67 + framework/src/domain/mimetreeparser/otp/utils.cpp | 70 + framework/src/domain/mimetreeparser/otp/utils.h | 42 + .../otp/verifydetachedbodypartmemento.cpp | 177 +++ .../otp/verifydetachedbodypartmemento.h | 87 ++ .../otp/verifyopaquebodypartmemento.cpp | 179 +++ .../otp/verifyopaquebodypartmemento.h | 93 ++ .../src/domain/mimetreeparser/stringhtmlwriter.h | 2 +- .../src/domain/mimetreeparser/tests/CMakeLists.txt | 18 +- framework/src/domain/objecttreesource.cpp | 8 +- framework/src/domain/objecttreesource.h | 2 +- framework/src/domain/stringhtmlwriter.h | 2 +- 60 files changed, 8468 insertions(+), 28 deletions(-) create mode 100644 framework/src/domain/mimetreeparser/otp/CMakeLists.txt create mode 100644 framework/src/domain/mimetreeparser/otp/attachmentstrategy.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/attachmentstrategy.h create mode 100644 framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.h create mode 100644 framework/src/domain/mimetreeparser/otp/bodypart.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/bodypart.h create mode 100644 framework/src/domain/mimetreeparser/otp/bodypartformatter.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/bodypartformatter.h create mode 100644 framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.h create mode 100644 framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory_p.h create mode 100644 framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.h create mode 100644 framework/src/domain/mimetreeparser/otp/cryptohelper.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/cryptohelper.h create mode 100644 framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.h create mode 100644 framework/src/domain/mimetreeparser/otp/enums.h create mode 100644 framework/src/domain/mimetreeparser/otp/filehtmlwriter.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/filehtmlwriter.h create mode 100644 framework/src/domain/mimetreeparser/otp/htmlwriter.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/htmlwriter.h create mode 100644 framework/src/domain/mimetreeparser/otp/messagepart.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/messagepart.h create mode 100644 framework/src/domain/mimetreeparser/otp/messagepartrenderer.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/messagepartrenderer.h create mode 100644 framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.h create mode 100644 framework/src/domain/mimetreeparser/otp/nodehelper.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/nodehelper.h create mode 100644 framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/objecttreeparser.h create mode 100644 framework/src/domain/mimetreeparser/otp/objecttreesource.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/objecttreesource.h create mode 100644 framework/src/domain/mimetreeparser/otp/partmetadata.h create mode 100644 framework/src/domain/mimetreeparser/otp/partnodebodypart.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/partnodebodypart.h create mode 100644 framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.h create mode 100644 framework/src/domain/mimetreeparser/otp/queuehtmlwriter.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/queuehtmlwriter.h create mode 100644 framework/src/domain/mimetreeparser/otp/util.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/util.h create mode 100644 framework/src/domain/mimetreeparser/otp/utils.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/utils.h create mode 100644 framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.h create mode 100644 framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.cpp create mode 100644 framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.h (limited to 'framework/src/domain') diff --git a/framework/src/domain/mailtemplates.cpp b/framework/src/domain/mailtemplates.cpp index 43e169de..254dbba3 100644 --- a/framework/src/domain/mailtemplates.cpp +++ b/framework/src/domain/mailtemplates.cpp @@ -37,7 +37,7 @@ #include "stringhtmlwriter.h" #include "objecttreesource.h" -#include +#include namespace KMime { namespace Types { diff --git a/framework/src/domain/mimetreeparser/CMakeLists.txt b/framework/src/domain/mimetreeparser/CMakeLists.txt index 64da2656..517fb7e5 100644 --- a/framework/src/domain/mimetreeparser/CMakeLists.txt +++ b/framework/src/domain/mimetreeparser/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(.) + set(mimetreeparser_SRCS interface.cpp objecttreesource.cpp @@ -7,9 +9,9 @@ set(mimetreeparser_SRCS add_library(mimetreeparser SHARED ${mimetreeparser_SRCS}) qt5_use_modules(mimetreeparser Core Gui) -target_link_libraries(mimetreeparser KF5::Mime KF5::MimeTreeParser) +target_link_libraries(mimetreeparser KF5::Mime kube_otp) -install(TARGETS mimetreeparser - DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS mimetreeparser DESTINATION ${LIB_INSTALL_DIR}) add_subdirectory(tests) +add_subdirectory(otp) diff --git a/framework/src/domain/mimetreeparser/interface.cpp b/framework/src/domain/mimetreeparser/interface.cpp index 663a2013..b8556336 100644 --- a/framework/src/domain/mimetreeparser/interface.cpp +++ b/framework/src/domain/mimetreeparser/interface.cpp @@ -29,9 +29,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/framework/src/domain/mimetreeparser/objecttreesource.cpp b/framework/src/domain/mimetreeparser/objecttreesource.cpp index 567f3516..186fdf80 100644 --- a/framework/src/domain/mimetreeparser/objecttreesource.cpp +++ b/framework/src/domain/mimetreeparser/objecttreesource.cpp @@ -19,10 +19,10 @@ #include "objecttreesource.h" -#include -#include -#include -#include +#include +#include +#include +#include class ObjectSourcePrivate { diff --git a/framework/src/domain/mimetreeparser/objecttreesource.h b/framework/src/domain/mimetreeparser/objecttreesource.h index 93812dc3..2167e06f 100644 --- a/framework/src/domain/mimetreeparser/objecttreesource.h +++ b/framework/src/domain/mimetreeparser/objecttreesource.h @@ -20,7 +20,7 @@ #ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H #define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H -#include +#include class QString; diff --git a/framework/src/domain/mimetreeparser/otp/CMakeLists.txt b/framework/src/domain/mimetreeparser/otp/CMakeLists.txt new file mode 100644 index 00000000..fcbe574f --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/CMakeLists.txt @@ -0,0 +1,180 @@ +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) +add_definitions("-fvisibility=default") + +find_package(Qt5 COMPONENTS REQUIRED Core Gui) +find_package(KF5Mime "4.87.0" CONFIG REQUIRED) +find_package(QGpgme CONFIG REQUIRED) +find_package(KF5Codecs CONFIG REQUIRED) +find_package(KF5Package CONFIG REQUIRED) +find_package(KF5I18n CONFIG REQUIRED) + +#add_definitions(-DTRANSLATION_DOMAIN=\"libmimetreeparser\") + +# target_include_directories does not handle empty include paths +include_directories(${GPGME_INCLUDES}) + +set(libmimetreeparser_main_SRCS + objecttreeparser.cpp + #bodyformatter/applicationpgpencrypted.cpp + #bodyformatter/applicationpkcs7mime.cpp + #bodyformatter/mailman.cpp + #bodyformatter/multipartalternative.cpp + #bodyformatter/multipartencrypted.cpp + #bodyformatter/multipartmixed.cpp + #bodyformatter/multipartsigned.cpp + #bodyformatter/textplain.cpp + #bodyformatter/texthtml.cpp + #bodyformatter/utils.cpp + + #Interfaces + bodypartformatter.cpp + objecttreesource.cpp + bodypart.cpp + htmlwriter.cpp + messagepartrenderer.cpp + + #bodypartformatter.cpp + bodypartformatterbasefactory.cpp + cryptohelper.cpp + nodehelper.cpp + messagepart.cpp + utils.cpp + partnodebodypart.cpp + #Mementos + cryptobodypartmemento.cpp + decryptverifybodypartmemento.cpp + verifydetachedbodypartmemento.cpp + verifyopaquebodypartmemento.cpp + #Stuff + mimetreeparser_debug.cpp + qgpgmejobexecutor.cpp + util.cpp + attachmentstrategy.cpp + #HTML Writer + queuehtmlwriter.cpp + # htmlwriter/filehtmlwriter.cpp + attachmenttemporaryfilesdirs.cpp + ) + +#ecm_generate_headers(MimeTreeParser_Camelcaseviewer_HEADERS +# HEADER_NAMES +# AttachmentStrategy +# BodyPartFormatterBaseFactory +# Enums +# MessagePart +# NodeHelper +# ObjectTreeParser +# PartMetaData +# PartNodeBodyPart +# REQUIRED_HEADERS MimeTreeParser_viewer_HEADERS +# PREFIX MimeTreeParser +# RELATIVE viewer +# ) +# +#ecm_generate_headers(MimeTreeParser_Camelcaseutils_HEADERS +# HEADER_NAMES +# Util +# REQUIRED_HEADERS MimeTreeParser_utils_HEADERS +# PREFIX MimeTreeParser +# RELATIVE utils +# ) +# +#ecm_generate_headers(MimeTreeParser_Camelcaseinterfaces_HEADERS +# HEADER_NAMES +# BodyPartFormatter +# BodyPart +# HtmlWriter +# MessagePartRenderer +# ObjectTreeSource +# REQUIRED_HEADERS MimeTreeParser_interfaces_HEADERS +# PREFIX MimeTreeParser +# RELATIVE interfaces +# ) +# +#ecm_generate_headers(MimeTreeParser_Camelcasehtmlwriter_HEADERS +# HEADER_NAMES +# FileHtmlWriter +# QueueHtmlWriter +# REQUIRED_HEADERS MimeTreeParser_htmlwriter_HEADERS +# PREFIX MimeTreeParser +# RELATIVE htmlwriter +# ) +# +#ecm_generate_headers(MimeTreeParser_Camelcasetemporaryfile_HEADERS +# HEADER_NAMES +# AttachmentTemporaryFilesDirs +# REQUIRED_HEADERS MimeTreeParser_temporaryfile_HEADERS +# PREFIX MimeTreeParser +# RELATIVE temporaryfile +# ) + +#install(FILES +# ${MimeTreeParser_Camelcasehtmlwriter_HEADERS} +# ${MimeTreeParser_Camelcaseutils_HEADERS} +# ${MimeTreeParser_Camelcaseinterfaces_HEADERS} +# ${MimeTreeParser_Camelcaseviewer_HEADERS} +# ${MimeTreeParser_Camelcasetemporaryfile_HEADERS} +# DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/MimeTreeParser +# COMPONENT Devel +# ) +# +#install(FILES +# ${MimeTreeParser_htmlwriter_HEADERS} +# ${MimeTreeParser_utils_HEADERS} +# ${MimeTreeParser_interfaces_HEADERS} +# ${MimeTreeParser_viewer_HEADERS} +# ${MimeTreeParser_temporaryfile_HEADERS} +# ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_export.h +# ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_debug.h +# +# DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/mimetreeparser +# COMPONENT Devel +# ) +# +#ecm_generate_pri_file(BASE_NAME MimeTreeParser +# LIB_NAME KF5MimeTreeParser +# FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/MimeTreeParser +# ) +# +#install(FILES +# ${PRI_FILENAME} +# DESTINATION ${ECM_MKSPECS_INSTALL_DIR} +# ) + +set(libmimetreeparser_SRCS + ${libmimetreeparser_main_SRCS} + ) + +#ecm_qt_declare_logging_category(libmimetreeparser_SRCS HEADER mimetreeparser_debug.h IDENTIFIER MIMETREEPARSER_LOG CATEGORY_NAME org.kde.pim.mimetreeparser) + +add_library(kube_otp ${libmimetreeparser_SRCS}) + +#generate_export_header(KF5MimeTreeParser BASE_NAME mimetreeparser) + +#set(mimetreeparser_LINK_LIBRARIES +# ) + +target_link_libraries(kube_otp + PRIVATE + QGpgme + KF5::Codecs + KF5::I18n + KF5::Mime + Qt5::Gui + ) +install(TARGETS kube_otp DESTINATION ${LIB_INSTALL_DIR}) + +#install(TARGETS +# KF5MimeTreeParser +# EXPORT KF5MimeTreeParserTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} +# ) +# +#set_target_properties(KF5MimeTreeParser PROPERTIES +# VERSION ${MIMETREEPARSER_VERSION_STRING} +# SOVERSION ${MIMETREEPARSER_SOVERSION} +# EXPORT_NAME MimeTreeParser +# ) +# +#target_include_directories(KF5MimeTreeParser INTERFACE "$") +# diff --git a/framework/src/domain/mimetreeparser/otp/attachmentstrategy.cpp b/framework/src/domain/mimetreeparser/otp/attachmentstrategy.cpp new file mode 100644 index 00000000..5ea21133 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/attachmentstrategy.cpp @@ -0,0 +1,343 @@ +/* -*- c++ -*- + attachmentstrategy.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "attachmentstrategy.h" + +#include "nodehelper.h" +#include "util.h" + +#include + +#include + +#include "mimetreeparser_debug.h" + +using namespace MimeTreeParser; + +static AttachmentStrategy::Display smartDisplay(KMime::Content *node) +{ + const auto cd = node->contentDisposition(false); + + if (cd && cd->disposition() == KMime::Headers::CDinline) + // explict "inline" disposition: + { + return AttachmentStrategy::Inline; + } + if (cd && cd->disposition() == KMime::Headers::CDattachment) + // explicit "attachment" disposition: + { + return AttachmentStrategy::AsIcon; + } + + const auto ct = node->contentType(false); + if (ct && ct->isText() && ct->name().trimmed().isEmpty() && + (!cd || cd->filename().trimmed().isEmpty())) + // text/* w/o filename parameter: + { + return AttachmentStrategy::Inline; + } + return AttachmentStrategy::AsIcon; +} + +// +// IconicAttachmentStrategy: +// show everything but the first text/plain body as icons +// + +class IconicAttachmentStrategy : public AttachmentStrategy +{ + friend class AttachmentStrategy; +protected: + IconicAttachmentStrategy() : AttachmentStrategy() {} + virtual ~IconicAttachmentStrategy() {} + +public: + const char *name() const Q_DECL_OVERRIDE + { + return "iconic"; + } + + bool inlineNestedMessages() const Q_DECL_OVERRIDE + { + return false; + } + Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE + { + if (node->contentType()->isText() && + node->contentDisposition()->filename().trimmed().isEmpty() && + node->contentType()->name().trimmed().isEmpty()) + // text/* w/o filename parameter: + { + return Inline; + } + return AsIcon; + } +}; + +// +// SmartAttachmentStrategy: +// in addition to Iconic, show all body parts +// with content-disposition == "inline" and +// all text parts without a filename or name parameter inline +// + +class SmartAttachmentStrategy : public AttachmentStrategy +{ + friend class AttachmentStrategy; +protected: + SmartAttachmentStrategy() : AttachmentStrategy() {} + virtual ~SmartAttachmentStrategy() {} + +public: + const char *name() const Q_DECL_OVERRIDE + { + return "smart"; + } + + bool inlineNestedMessages() const Q_DECL_OVERRIDE + { + return true; + } + Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE + { + return smartDisplay(node); + } +}; + +// +// InlinedAttachmentStrategy: +// show everything possible inline +// + +class InlinedAttachmentStrategy : public AttachmentStrategy +{ + friend class AttachmentStrategy; +protected: + InlinedAttachmentStrategy() : AttachmentStrategy() {} + virtual ~InlinedAttachmentStrategy() {} + +public: + const char *name() const Q_DECL_OVERRIDE + { + return "inlined"; + } + + bool inlineNestedMessages() const Q_DECL_OVERRIDE + { + return true; + } + Display defaultDisplay(KMime::Content *) const Q_DECL_OVERRIDE + { + return Inline; + } +}; + +// +// HiddenAttachmentStrategy +// show nothing except the first text/plain body part _at all_ +// + +class HiddenAttachmentStrategy : public AttachmentStrategy +{ + friend class AttachmentStrategy; +protected: + HiddenAttachmentStrategy() : AttachmentStrategy() {} + virtual ~HiddenAttachmentStrategy() {} + +public: + const char *name() const Q_DECL_OVERRIDE + { + return "hidden"; + } + + bool inlineNestedMessages() const Q_DECL_OVERRIDE + { + return false; + } + Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE + { + if (node->contentType()->isText() && + node->contentDisposition()->filename().trimmed().isEmpty() && + node->contentType()->name().trimmed().isEmpty()) + // text/* w/o filename parameter: + { + return Inline; + } + if (!node->parent()) { + return Inline; + } + + if (node->parent() && node->parent()->contentType()->isMultipart() && + node->parent()->contentType()->subType() == "related") { + return Inline; + } + + return None; + } +}; + +class HeaderOnlyAttachmentStrategy : public AttachmentStrategy +{ + friend class AttachmentStrategy; +protected: + HeaderOnlyAttachmentStrategy() : AttachmentStrategy() {} + virtual ~HeaderOnlyAttachmentStrategy() {} + +public: + const char *name() const Q_DECL_OVERRIDE + { + return "headerOnly"; + } + + bool inlineNestedMessages() const Q_DECL_OVERRIDE + { + return true; + } + + Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE + { + if (NodeHelper::isInEncapsulatedMessage(node)) { + return smartDisplay(node); + } + + if (!Util::labelForContent(node).isEmpty() && QIcon::hasThemeIcon(Util::iconNameForContent(node)) && ! Util::isTypeBlacklisted(node)) { + return None; + } + return smartDisplay(node); + } + + bool requiresAttachmentListInHeader() const Q_DECL_OVERRIDE + { + return true; + } +}; + +// +// AttachmentStrategy abstract base: +// + +AttachmentStrategy::AttachmentStrategy() +{ + +} + +AttachmentStrategy::~AttachmentStrategy() +{ + +} + +const AttachmentStrategy *AttachmentStrategy::create(Type type) +{ + switch (type) { + case Iconic: return iconic(); + case Smart: return smart(); + case Inlined: return inlined(); + case Hidden: return hidden(); + case HeaderOnly: return headerOnly(); + } + qCCritical(MIMETREEPARSER_LOG) << "Unknown attachment startegy ( type ==" + << (int)type << ") requested!"; + return nullptr; // make compiler happy +} + +const AttachmentStrategy *AttachmentStrategy::create(const QString &type) +{ + const QString lowerType = type.toLower(); + if (lowerType == QLatin1String("iconic")) { + return iconic(); + } + //if ( lowerType == "smart" ) return smart(); // not needed, see below + if (lowerType == QLatin1String("inlined")) { + return inlined(); + } + if (lowerType == QLatin1String("hidden")) { + return hidden(); + } + if (lowerType == QLatin1String("headeronly")) { + return headerOnly(); + } + // don't kFatal here, b/c the strings are user-provided + // (KConfig), so fail gracefully to the default: + return smart(); +} + +static const AttachmentStrategy *iconicStrategy = nullptr; +static const AttachmentStrategy *smartStrategy = nullptr; +static const AttachmentStrategy *inlinedStrategy = nullptr; +static const AttachmentStrategy *hiddenStrategy = nullptr; +static const AttachmentStrategy *headerOnlyStrategy = nullptr; + +const AttachmentStrategy *AttachmentStrategy::iconic() +{ + if (!iconicStrategy) { + iconicStrategy = new IconicAttachmentStrategy(); + } + return iconicStrategy; +} + +const AttachmentStrategy *AttachmentStrategy::smart() +{ + if (!smartStrategy) { + smartStrategy = new SmartAttachmentStrategy(); + } + return smartStrategy; +} + +const AttachmentStrategy *AttachmentStrategy::inlined() +{ + if (!inlinedStrategy) { + inlinedStrategy = new InlinedAttachmentStrategy(); + } + return inlinedStrategy; +} + +const AttachmentStrategy *AttachmentStrategy::hidden() +{ + if (!hiddenStrategy) { + hiddenStrategy = new HiddenAttachmentStrategy(); + } + return hiddenStrategy; +} + +const AttachmentStrategy *AttachmentStrategy::headerOnly() +{ + if (!headerOnlyStrategy) { + headerOnlyStrategy = new HeaderOnlyAttachmentStrategy(); + } + return headerOnlyStrategy; +} + +bool AttachmentStrategy::requiresAttachmentListInHeader() const +{ + return false; +} diff --git a/framework/src/domain/mimetreeparser/otp/attachmentstrategy.h b/framework/src/domain/mimetreeparser/otp/attachmentstrategy.h new file mode 100644 index 00000000..a0b5dc81 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/attachmentstrategy.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- + attachmentstrategy.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__ +#define __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__ + +class QString; +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ + +class AttachmentStrategy +{ +protected: + AttachmentStrategy(); + virtual ~AttachmentStrategy(); + +public: + // + // Factory methods: + // + enum Type { Iconic, Smart, Inlined, Hidden, HeaderOnly }; + + static const AttachmentStrategy *create(Type type); + static const AttachmentStrategy *create(const QString &type); + + static const AttachmentStrategy *iconic(); + static const AttachmentStrategy *smart(); + static const AttachmentStrategy *inlined(); + static const AttachmentStrategy *hidden(); + static const AttachmentStrategy *headerOnly(); + + // + // Navigation methods: + // + + virtual const char *name() const = 0; + + // + // Bahavioural: + // + + enum Display { None, AsIcon, Inline }; + + virtual bool inlineNestedMessages() const = 0; + virtual Display defaultDisplay(KMime::Content *node) const = 0; + virtual bool requiresAttachmentListInHeader() const; +}; + +} + +#endif // __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__ diff --git a/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp b/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp new file mode 100644 index 00000000..364bc422 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp @@ -0,0 +1,108 @@ +/* + Copyright (c) 2013-2017 Montel Laurent + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + +#include "attachmenttemporaryfilesdirs.h" + +#include +#include +#include + +using namespace MimeTreeParser; + +class MimeTreeParser::AttachmentTemporaryFilesDirsPrivate +{ +public: + AttachmentTemporaryFilesDirsPrivate() + : mDelayRemoveAll(10000) + { + + } + QStringList mTempFiles; + QStringList mTempDirs; + int mDelayRemoveAll; +}; + +AttachmentTemporaryFilesDirs::AttachmentTemporaryFilesDirs(QObject *parent) + : QObject(parent), + d(new AttachmentTemporaryFilesDirsPrivate) +{ + +} + +AttachmentTemporaryFilesDirs::~AttachmentTemporaryFilesDirs() +{ + delete d; +} + +void AttachmentTemporaryFilesDirs::setDelayRemoveAllInMs(int ms) +{ + d->mDelayRemoveAll = (ms < 0) ? 0 : ms; +} + +void AttachmentTemporaryFilesDirs::removeTempFiles() +{ + QTimer::singleShot(d->mDelayRemoveAll, this, &AttachmentTemporaryFilesDirs::slotRemoveTempFiles); +} + +void AttachmentTemporaryFilesDirs::forceCleanTempFiles() +{ + QStringList::ConstIterator end = d->mTempFiles.constEnd(); + for (QStringList::ConstIterator it = d->mTempFiles.constBegin(); it != end; ++it) { + QFile::remove(*it); + } + d->mTempFiles.clear(); + end = d->mTempDirs.constEnd(); + for (QStringList::ConstIterator it = d->mTempDirs.constBegin(); it != end; ++it) { + QDir(*it).rmdir(*it); + } + d->mTempDirs.clear(); +} + +void AttachmentTemporaryFilesDirs::slotRemoveTempFiles() +{ + forceCleanTempFiles(); + //Delete it after cleaning + deleteLater(); +} + +void AttachmentTemporaryFilesDirs::addTempFile(const QString &file) +{ + if (!d->mTempFiles.contains(file)) { + d->mTempFiles.append(file); + } +} + +void AttachmentTemporaryFilesDirs::addTempDir(const QString &dir) +{ + if (!d->mTempDirs.contains(dir)) { + d->mTempDirs.append(dir); + } +} + +QStringList AttachmentTemporaryFilesDirs::temporaryFiles() const +{ + return d->mTempFiles; +} + +QStringList AttachmentTemporaryFilesDirs::temporaryDirs() const +{ + return d->mTempDirs; +} + diff --git a/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.h b/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.h new file mode 100644 index 00000000..bf65fcdb --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/attachmenttemporaryfilesdirs.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013-2016 Montel Laurent + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + +#ifndef ATTACHMENTTEMPORARYFILESDIRS_H +#define ATTACHMENTTEMPORARYFILESDIRS_H + +#include +#include + +namespace MimeTreeParser +{ +class AttachmentTemporaryFilesDirsPrivate; + +class AttachmentTemporaryFilesDirs : public QObject +{ + Q_OBJECT +public: + explicit AttachmentTemporaryFilesDirs(QObject *parent = nullptr); + ~AttachmentTemporaryFilesDirs(); + + void addTempFile(const QString &file); + void addTempDir(const QString &dir); + QStringList temporaryFiles() const; + void removeTempFiles(); + void forceCleanTempFiles(); + + QStringList temporaryDirs() const; + + void setDelayRemoveAllInMs(int ms); + +private Q_SLOTS: + void slotRemoveTempFiles(); + +private: + AttachmentTemporaryFilesDirsPrivate *const d; +}; + +} + +#endif // ATTACHMENTTEMPORARYFILESDIRS_H diff --git a/framework/src/domain/mimetreeparser/otp/bodypart.cpp b/framework/src/domain/mimetreeparser/otp/bodypart.cpp new file mode 100644 index 00000000..62e92d0c --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypart.cpp @@ -0,0 +1,41 @@ +/* + This file is part of KMail's plugin interface. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "bodypart.h" + +MimeTreeParser::Interface::BodyPartMemento::~BodyPartMemento() +{ +} + +MimeTreeParser::Interface::BodyPart::~BodyPart() +{ +} + diff --git a/framework/src/domain/mimetreeparser/otp/bodypart.h b/framework/src/domain/mimetreeparser/otp/bodypart.h new file mode 100644 index 00000000..f50c0360 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypart.h @@ -0,0 +1,209 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + bodypart.h + + This file is part of KMail's plugin interface. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_INTERFACES_BODYPART_H__ +#define __MIMETREEPARSER_INTERFACES_BODYPART_H__ + +#include +#include + +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ +class NodeHelper; +class ObjectTreeParser; +class ProcessResult; + +namespace Interface +{ + +class ObjectTreeSource; + +/*FIXME(Andras) review, port + class Observer; + class Observable; +*/ +/** + @short interface of classes that implement status for BodyPartFormatters. +*/ +class BodyPartMemento +{ +public: + virtual ~BodyPartMemento(); + + virtual void detach() = 0; +#if 0 +//FIXME(Andras) review, port + /** If your BodyPartMemento implementation also implements the + Observer interface, simply implement these as + return this;, else as return + 0;. This is needed to avoid forcing a dependency of + plugins on internal KMail classes. + */ + virtual Observer *asObserver() = 0; + + /** If your BodyPartMemento implementation also implements the + Observable interface, simply implement these as + return this;, else as return + 0;. This is needed to avoid forcing a dependency of + plugins on internal KMail classes. + */ + virtual Observable *asObservable() = 0; +#endif +}; + +/** + @short interface of message body parts. +*/ +class BodyPart +{ +public: + virtual ~BodyPart(); + + /** + @return a string respresentation of an URL that can be used + to invoke a BodyPartURLHandler for this body part. + */ + virtual QString makeLink(const QString &path) const = 0; + + /** + @return the decoded (CTE, canonicalisation, and charset + encoding undone) text contained in the body part, or + QString(), it the body part is not of type "text". + */ + virtual QString asText() const = 0; + + /** + @return the decoded (CTE undone) content of the body part, or + a null array if this body part instance is of type text. + */ + virtual QByteArray asBinary() const = 0; + + /** + @return the value of the content-type header field parameter + with name \a parameter, or QString(), if that that + parameter is not present in the body's content-type header + field. RFC 2231 encoding is removed first. + + Note that this method will suppress queries to certain + standard parameters (most notably "charset") to keep plugins + decent. + + Note2 that this method preserves the case of the parameter + value returned. So, if the parameter you want to use defines + the value to be case-insensitive (such as the smime-type + parameter), you need to make sure you do the casemap yourself + before comparing to a reference value. + */ + virtual QString contentTypeParameter(const char *parameter) const = 0; + + /** + @return the content of the content-description header field, + or QString() if that header is not present in this body + part. RFC 2047 encoding is decoded first. + */ + virtual QString contentDescription() const = 0; + + //virtual int contentDisposition() const = 0; + /** + @return the value of the content-disposition header field + parameter with name \a parameter, or QString() if that + parameter is not present in the body's content-disposition + header field. RFC 2231 encoding is removed first. + + The notes made for contentTypeParameter() above apply here as + well. + */ + virtual QString contentDispositionParameter(const char *parameter) const = 0; + + /** + @return whether this part already has it's complete body + fetched e.g. from an IMAP server. + */ + virtual bool hasCompleteBody() const = 0; + + /** + @return the BodyPartMemento set for this part, or null, if + none is set. + */ + virtual BodyPartMemento *memento() const = 0; + + /** + @return register an implementation of the BodyPartMemento + interface as a status object with this part. + */ + virtual void setBodyPartMemento(BodyPartMemento *) = 0; + + enum Display { None, AsIcon, Inline }; + /** + @return whether this body part should be displayed iconic or inline + */ + virtual Display defaultDisplay() const = 0; + + /** Returns the KMime::Content node represented here. Makes most of the above obsolete + and probably should be used in the interfaces in the first place. + */ + virtual KMime::Content *content() const = 0; + + /** + * Returns the top-level content. + * Note that this is _not_ necessarily the same as content()->topLevel(), for example the later + * will not work for "extra nodes", i.e. nodes in encrypted parts of the mail. + * topLevelContent() will return the correct result in this case. Also note that + * topLevelContent() + */ + virtual KMime::Content *topLevelContent() const = 0; + + /** + * Ok, this is ugly, exposing the node helper here, but there is too much useful stuff in there + * for real-world plugins. Still, there should be a nicer way for this. + */ + virtual MimeTreeParser::NodeHelper *nodeHelper() const = 0; + + /** + * For making it easier to refactor, add objectTreeParser + */ + virtual MimeTreeParser::ObjectTreeParser *objectTreeParser() const = 0; + virtual MimeTreeParser::Interface::ObjectTreeSource *source() const = 0; + virtual MimeTreeParser::ProcessResult *processResult() const = 0; +}; + +} // namespace Interface + +} + +#endif // __MIMETREEPARSER_INTERFACES_BODYPART_H__ diff --git a/framework/src/domain/mimetreeparser/otp/bodypartformatter.cpp b/framework/src/domain/mimetreeparser/otp/bodypartformatter.cpp new file mode 100644 index 00000000..63d7e92c --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypartformatter.cpp @@ -0,0 +1,147 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + bodypartformatter.cpp + + This file is part of KMail's plugin interface. + Copyright (c) 2016 Sandro Knauß + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "bodypartformatter.h" + +#include "bodypart.h" +#include "queuehtmlwriter.h" +#include "objecttreeparser.h" + +using namespace MimeTreeParser::Interface; + +namespace MimeTreeParser +{ +namespace Interface +{ + +class MessagePartPrivate +{ +public: + MessagePartPrivate(const BodyPart *part) + : mHtmlWriter(nullptr) + , mPart(part) + , mParentPart(nullptr) + , mCreatedWriter(false) + { + } + + ~MessagePartPrivate() + { + if (mCreatedWriter) { + delete mHtmlWriter; + } + } + + MimeTreeParser::HtmlWriter *htmlWriter() + { + if (!mHtmlWriter && mPart) { + mHtmlWriter = mPart->objectTreeParser()->htmlWriter(); + } + return mHtmlWriter; + } + + MimeTreeParser::HtmlWriter *mHtmlWriter; + const BodyPart *mPart; + MessagePart *mParentPart; + bool mCreatedWriter; + +}; +} +} + +MessagePart::MessagePart() + : QObject() + , d(new MessagePartPrivate(nullptr)) +{ +} + +MessagePart::MessagePart(const BodyPart &part) + : QObject() + , d(new MessagePartPrivate(&part)) +{ +} + +MessagePart::~MessagePart() +{ + delete d; +} + +void MessagePart::html(bool decorate) +{ + Q_UNUSED(decorate); + static_cast(d->mHtmlWriter)->replay(); +} + +QString MessagePart::text() const +{ + return QString(); +} + +MessagePart *MessagePart::parentPart() const +{ + return d->mParentPart; +} + +void MessagePart::setParentPart(MessagePart *parentPart) +{ + d->mParentPart = parentPart; +} + +QString MessagePart::htmlContent() const +{ + return text(); +} + +QString MessagePart::plaintextContent() const +{ + return text(); +} + +MimeTreeParser::HtmlWriter *MessagePart::htmlWriter() const +{ + return d->htmlWriter(); +} + +void MessagePart::setHtmlWriter(MimeTreeParser::HtmlWriter *htmlWriter) const +{ + if (d->mHtmlWriter) { + d->mHtmlWriter = htmlWriter; + } +} + +MessagePart::Ptr BodyPartFormatter::process(BodyPart &part) const +{ + auto mp = MessagePart::Ptr(new MessagePart(part)); + mp->setHtmlWriter(new QueueHtmlWriter(mp->htmlWriter())); + mp->d->mCreatedWriter = true; + return mp; +} diff --git a/framework/src/domain/mimetreeparser/otp/bodypartformatter.h b/framework/src/domain/mimetreeparser/otp/bodypartformatter.h new file mode 100644 index 00000000..682f9391 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypartformatter.h @@ -0,0 +1,149 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + bodypartformatter.h + + This file is part of KMail's plugin interface. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__ +#define __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__ + +#include +#include + +#include "mimetreeparser/objecttreeparser.h" + +namespace MimeTreeParser +{ +class HtmlWriter; + +namespace Interface +{ + +class BodyPartURLHandler; +class BodyPart; +class MessagePartPrivate; + +class MessagePart : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString plaintextContent READ plaintextContent) + Q_PROPERTY(QString htmlContent READ htmlContent) +public: + typedef QSharedPointer Ptr; + explicit MessagePart(); + explicit MessagePart(const BodyPart &part); + virtual ~MessagePart(); + + virtual void html(bool decorate); + virtual QString text() const; + + void setParentPart(MessagePart *parentPart); + MessagePart *parentPart() const; + + virtual QString plaintextContent() const; + virtual QString htmlContent() const; + + virtual MimeTreeParser::HtmlWriter *htmlWriter() const; + virtual void setHtmlWriter(MimeTreeParser::HtmlWriter *htmlWriter) const; +private: + MessagePartPrivate *d; + + friend class BodyPartFormatter; +}; + +class BodyPartFormatter +{ +public: + virtual ~BodyPartFormatter() {} + + /** + @li Ok returned when format() generated some HTML + @li NeedContent returned when format() needs the body of the part + @li AsIcon returned when the part should be shown iconified + @li Failed returned when formatting failed. Currently equivalent to Ok + */ + enum Result { Ok, NeedContent, AsIcon, Failed }; + + /** + Format body part \a part by generating some HTML and writing + that to \a writer. + + @return the result code (see above) + */ + virtual Result format(BodyPart *part, MimeTreeParser::HtmlWriter *writer) const = 0; + + /** + Variant of format that allows implementors to hook notifications up to + a listener interested in the result, for async operations. + + @return the result code (see above) + */ + virtual Result format(BodyPart *part, MimeTreeParser::HtmlWriter *writer, QObject *asyncResultObserver) const + { + Q_UNUSED(asyncResultObserver); + return format(part, writer); + } + + virtual void adaptProcessResult(ProcessResult &result) const + { + Q_UNUSED(result); + } + virtual MessagePart::Ptr process(BodyPart &part) const; +}; + +/** + @short interface for BodyPartFormatter plugins + + The interface is queried by for types, subtypes, and the + corresponding bodypart formatter, and the result inserted into + the bodypart formatter factory. + + Subtype alone or both type and subtype may be "*", which is + taken as a wildcard, so that e.g. type=text subtype=* matches + any text subtype, but with lesser specificity than a concrete + mimetype such as text/plain. type=* is only allowed when + subtype=*, too. +*/ +class BodyPartFormatterPlugin +{ +public: + virtual ~BodyPartFormatterPlugin() {} + + virtual const BodyPartFormatter *bodyPartFormatter(int idx) const = 0; + virtual const char *type(int idx) const = 0; + virtual const char *subtype(int idx) const = 0; + + virtual const BodyPartURLHandler *urlHandler(int idx) const = 0; +}; + +} // namespace Interface + +} +#endif // __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__ diff --git a/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.cpp b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.cpp new file mode 100644 index 00000000..a44576b8 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.cpp @@ -0,0 +1,208 @@ +/* + bodypartformatterfactory.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "bodypartformatterbasefactory.h" +#include "bodypartformatterbasefactory_p.h" +#include "mimetreeparser_debug.h" + +// Qt + +#include + +using namespace MimeTreeParser; + +BodyPartFormatterBaseFactoryPrivate::BodyPartFormatterBaseFactoryPrivate(BodyPartFormatterBaseFactory *factory) + : q(factory) + , all(nullptr) +{ +} + +BodyPartFormatterBaseFactoryPrivate::~BodyPartFormatterBaseFactoryPrivate() +{ + if (all) { + delete all; + all = nullptr; + } +} + +void BodyPartFormatterBaseFactoryPrivate::setup() +{ + if (!all) { + all = new TypeRegistry(); + messageviewer_create_builtin_bodypart_formatters(); + q->loadPlugins(); + } +} + +void BodyPartFormatterBaseFactoryPrivate::insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter) +{ + if (!type || !*type || !subtype || !*subtype || !formatter || !all) { + return; + } + + TypeRegistry::iterator type_it = all->find(type); + if (type_it == all->end()) { + qCDebug(MIMETREEPARSER_LOG) << "BodyPartFormatterBaseFactory: instantiating new Subtype Registry for \"" + << type << "\""; + type_it = all->insert(std::make_pair(type, SubtypeRegistry())).first; + assert(type_it != all->end()); + } + + SubtypeRegistry &subtype_reg = type_it->second; + + subtype_reg.insert(std::make_pair(subtype, formatter)); +} + +void BodyPartFormatterBaseFactoryPrivate::messageviewer_create_builtin_bodypart_formatters() +{ + //FIXME defined in bodypartformatter.cpp + // insert("application", "octet-stream", AnyTypeBodyPartFormatter::create()); + // insert("application", "pgp", ApplicationPgpBodyPartFormatter::create()); + // insert("application", "pkcs7-mime", ApplicationPkcs7MimeBodyPartFormatter::create()); + // insert("application", "x-pkcs7-mime", ApplicationPkcs7MimeBodyPartFormatter::create()); + // insert("application", "pgp-encrypted", ApplicationPGPEncryptedBodyPartFormatter::create()); + // insert("application", "*", AnyTypeBodyPartFormatter::create()); + + // insert("text", "html", TextHtmlBodyPartFormatter::create()); + // insert("text", "rtf", AnyTypeBodyPartFormatter::create()); + // insert("text", "plain", MailmanBodyPartFormatter::create()); + // insert("text", "plain", TextPlainBodyPartFormatter::create()); + // insert("text", "*", MailmanBodyPartFormatter::create()); + // insert("text", "*", TextPlainBodyPartFormatter::create()); + + // insert("image", "*", ImageTypeBodyPartFormatter::create()); + + // insert("message", "rfc822", MessageRfc822BodyPartFormatter::create()); + // insert("message", "*", AnyTypeBodyPartFormatter::create()); + + // insert("multipart", "alternative", MultiPartAlternativeBodyPartFormatter::create()); + // insert("multipart", "encrypted", MultiPartEncryptedBodyPartFormatter::create()); + // insert("multipart", "signed", MultiPartSignedBodyPartFormatter::create()); + // insert("multipart", "*", MultiPartMixedBodyPartFormatter::create()); + // insert("*", "*", AnyTypeBodyPartFormatter::create()); +} + +BodyPartFormatterBaseFactory::BodyPartFormatterBaseFactory() + : d(new BodyPartFormatterBaseFactoryPrivate(this)) +{ +} + +BodyPartFormatterBaseFactory::~BodyPartFormatterBaseFactory() +{ + delete d; +} + +void BodyPartFormatterBaseFactory::insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter) +{ + d->insert(type, subtype, formatter); +} + +const SubtypeRegistry &BodyPartFormatterBaseFactory::subtypeRegistry(const char *type) const +{ + if (!type || !*type) { + type = "*"; //krazy:exclude=doublequote_chars + } + + d->setup(); + assert(d->all); + + static SubtypeRegistry emptyRegistry; + if (d->all->empty()) { + return emptyRegistry; + } + + TypeRegistry::const_iterator type_it = d->all->find(type); + if (type_it == d->all->end()) { + type_it = d->all->find("*"); + } + if (type_it == d->all->end()) { + return emptyRegistry; + } + + const SubtypeRegistry &subtype_reg = type_it->second; + if (subtype_reg.empty()) { + return emptyRegistry; + } + return subtype_reg; +} + +SubtypeRegistry::const_iterator BodyPartFormatterBaseFactory::createForIterator(const char *type, const char *subtype) const +{ + if (!type || !*type) { + type = "*"; //krazy:exclude=doublequote_chars + } + if (!subtype || !*subtype) { + subtype = "*"; //krazy:exclude=doublequote_chars + } + + d->setup(); + assert(d->all); + + if (d->all->empty()) { + return SubtypeRegistry::const_iterator(); + } + + TypeRegistry::const_iterator type_it = d->all->find(type); + if (type_it == d->all->end()) { + type_it = d->all->find("*"); + } + if (type_it == d->all->end()) { + return SubtypeRegistry::const_iterator(); + } + + const SubtypeRegistry &subtype_reg = type_it->second; + if (subtype_reg.empty()) { + return SubtypeRegistry::const_iterator(); + } + + SubtypeRegistry::const_iterator subtype_it = subtype_reg.find(subtype); + qCWarning(MIMETREEPARSER_LOG) << type << subtype << subtype_reg.size(); + if (subtype_it == subtype_reg.end()) { + subtype_it = subtype_reg.find("*"); + } + if (subtype_it == subtype_reg.end()) { + return SubtypeRegistry::const_iterator(); + } + + if (!(*subtype_it).second) { + qCWarning(MIMETREEPARSER_LOG) << "BodyPartFormatterBaseFactory: a null bodypart formatter sneaked in for \"" + << type << "/" << subtype << "\"!"; + } + + return subtype_it; +} + +void BodyPartFormatterBaseFactory::loadPlugins() +{ + qCDebug(MIMETREEPARSER_LOG) << "plugin loading is not enabled in libmimetreeparser"; +} diff --git a/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.h b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.h new file mode 100644 index 00000000..2bba551d --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory.h @@ -0,0 +1,85 @@ +/* + bodypartformatterfactory.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_H__ +#define __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_H__ + +#include +#include + +namespace MimeTreeParser +{ + +namespace Interface +{ +class BodyPartFormatter; +} + +struct ltstr { + bool operator()(const char *s1, const char *s2) const + { + return qstricmp(s1, s2) < 0; + } +}; + +typedef std::multimap SubtypeRegistry; +typedef std::map TypeRegistry; + +class BodyPartFormatterBaseFactoryPrivate; + +class BodyPartFormatterBaseFactory +{ +public: + BodyPartFormatterBaseFactory(); + virtual ~BodyPartFormatterBaseFactory(); + + SubtypeRegistry::const_iterator createForIterator(const char *type, const char *subtype) const; + const SubtypeRegistry &subtypeRegistry(const char *type) const; + +protected: + void insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter); + virtual void loadPlugins(); +private: + static BodyPartFormatterBaseFactory *mSelf; + + BodyPartFormatterBaseFactoryPrivate *d; + friend class BodyPartFormatterBaseFactoryPrivate; +private: + // disabled + const BodyPartFormatterBaseFactory &operator=(const BodyPartFormatterBaseFactory &); + BodyPartFormatterBaseFactory(const BodyPartFormatterBaseFactory &); +}; + +} + +#endif // __MIMETREEPARSER_BODYPARTFORMATTERFACTORY_H__ diff --git a/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory_p.h b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory_p.h new file mode 100644 index 00000000..1f71f183 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/bodypartformatterbasefactory_p.h @@ -0,0 +1,57 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + bodypartformatterfactory.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_P_H__ +#define __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_P_H__ + +namespace MimeTreeParser +{ +class BodyPartFormatterBaseFactory; + +class BodyPartFormatterBaseFactoryPrivate +{ +public: + BodyPartFormatterBaseFactoryPrivate(BodyPartFormatterBaseFactory *factory); + ~BodyPartFormatterBaseFactoryPrivate(); + + void setup(); + void messageviewer_create_builtin_bodypart_formatters(); //defined in bodypartformatter.cpp + void insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter); + + BodyPartFormatterBaseFactory *q; + TypeRegistry *all; +}; + +} + +#endif // __MIMETREEPARSER_BODYPARTFORMATTERFACTORY_P_H__ diff --git a/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.cpp b/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.cpp new file mode 100644 index 00000000..a884ec36 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.cpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2014-2017 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "cryptobodypartmemento.h" + +using namespace GpgME; +using namespace MimeTreeParser; + +CryptoBodyPartMemento::CryptoBodyPartMemento() + : QObject(nullptr), + Interface::BodyPartMemento(), + m_running(false) +{ + +} + +CryptoBodyPartMemento::~CryptoBodyPartMemento() +{ + +} + +bool CryptoBodyPartMemento::isRunning() const +{ + return m_running; +} + +void CryptoBodyPartMemento::setAuditLog(const Error &err, const QString &log) +{ + m_auditLogError = err; + m_auditLog = log; +} + +void CryptoBodyPartMemento::setRunning(bool running) +{ + m_running = running; +} + +void CryptoBodyPartMemento::detach() +{ + disconnect(this, SIGNAL(update(MimeTreeParser::UpdateMode)), nullptr, nullptr); +} + diff --git a/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.h b/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.h new file mode 100644 index 00000000..076ed890 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/cryptobodypartmemento.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2014-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__ +#define __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__ + +#include + +#include +#include + +#include "bodypart.h" +#include "enums.h" + +namespace MimeTreeParser +{ + +class CryptoBodyPartMemento + : public QObject, + public Interface::BodyPartMemento +{ + Q_OBJECT +public: + CryptoBodyPartMemento(); + ~CryptoBodyPartMemento(); + + virtual bool start() = 0; + virtual void exec() = 0; + bool isRunning() const; + + const QString &auditLogAsHtml() const + { + return m_auditLog; + } + GpgME::Error auditLogError() const + { + return m_auditLogError; + } + + void detach() Q_DECL_OVERRIDE; + +Q_SIGNALS: + void update(MimeTreeParser::UpdateMode); + +protected Q_SLOTS: + void notify() + { + Q_EMIT update(MimeTreeParser::Force); + } + +protected: + void setAuditLog(const GpgME::Error &err, const QString &log); + void setRunning(bool running); + +private: + bool m_running; + QString m_auditLog; + GpgME::Error m_auditLogError; +}; +} +#endif // __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__ diff --git a/framework/src/domain/mimetreeparser/otp/cryptohelper.cpp b/framework/src/domain/mimetreeparser/otp/cryptohelper.cpp new file mode 100644 index 00000000..8e5df576 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/cryptohelper.cpp @@ -0,0 +1,150 @@ +/* + Copyright (C) 2015 Sandro Knauß + Copyright (C) 2001,2002 the KPGP authors + See file AUTHORS.kpgp for details + + Kmail 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) any later version. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "cryptohelper.h" + +using namespace MimeTreeParser; + +PGPBlockType Block::determineType() const +{ + const QByteArray data = text(); + if (data.startsWith("-----BEGIN PGP SIGNED")) { + return ClearsignedBlock; + } else if (data.startsWith("-----BEGIN PGP SIGNATURE")) { + return SignatureBlock; + } else if (data.startsWith("-----BEGIN PGP PUBLIC")) { + return PublicKeyBlock; + } else if (data.startsWith("-----BEGIN PGP PRIVATE") + || data.startsWith("-----BEGIN PGP SECRET")) { + return PrivateKeyBlock; + } else if (data.startsWith("-----BEGIN PGP MESSAGE")) { + if (data.startsWith("-----BEGIN PGP MESSAGE PART")) { + return MultiPgpMessageBlock; + } else { + return PgpMessageBlock; + } + } else if (data.startsWith("-----BEGIN PGP ARMORED FILE")) { + return PgpMessageBlock; + } else if (data.startsWith("-----BEGIN PGP ")) { + return UnknownBlock; + } else { + return NoPgpBlock; + } +} + +QList MimeTreeParser::prepareMessageForDecryption(const QByteArray &msg) +{ + PGPBlockType pgpBlock = NoPgpBlock; + QList blocks; + int start = -1; // start of the current PGP block + int lastEnd = -1; // end of the last PGP block + const int length = msg.length(); + + if (msg.isEmpty()) { + return blocks; + } + + if (msg.startsWith("-----BEGIN PGP ")) { + start = 0; + } else { + start = msg.indexOf("\n-----BEGIN PGP ") + 1; + if (start == 0) { + blocks.append(Block(msg, NoPgpBlock)); + return blocks; + } + } + + while (start != -1) { + int nextEnd, nextStart; + + // is the PGP block a clearsigned block? + if (!strncmp(msg.constData() + start + 15, "SIGNED", 6)) { + pgpBlock = ClearsignedBlock; + } else { + pgpBlock = UnknownBlock; + } + + nextEnd = msg.indexOf("\n-----END PGP ", start + 15); + nextStart = msg.indexOf("\n-----BEGIN PGP ", start + 15); + + if (nextEnd == -1) { // Missing END PGP line + if (lastEnd != -1) { + blocks.append(Block(msg.mid(lastEnd + 1), UnknownBlock)); + } else { + blocks.append(Block(msg.mid(start), UnknownBlock)); + } + break; + } + + if ((nextStart == -1) || (nextEnd < nextStart) || (pgpBlock == ClearsignedBlock)) { + // most likely we found a PGP block (but we don't check if it's valid) + + // store the preceding non-PGP block + if (start - lastEnd - 1 > 0) { + blocks.append(Block(msg.mid(lastEnd + 1, start - lastEnd - 1), NoPgpBlock)); + } + + lastEnd = msg.indexOf("\n", nextEnd + 14); + if (lastEnd == -1) { + if (start < length) { + blocks.append(Block(msg.mid(start))); + } + break; + } else { + blocks.append(Block(msg.mid(start, lastEnd + 1 - start))); + if ((nextStart != -1) && (nextEnd > nextStart)) { + nextStart = msg.indexOf("\n-----BEGIN PGP ", lastEnd + 1); + } + } + } + + start = nextStart; + + if (start == -1) { + if (lastEnd + 1 < length) { + //rest of mail is no PGP Block + blocks.append(Block(msg.mid(lastEnd + 1), NoPgpBlock)); + } + break; + } else { + start++; // move start behind the '\n' + } + } + + return blocks; +} + +Block::Block(const QByteArray &m) + : msg(m) +{ + mType = determineType(); +} + +Block::Block(const QByteArray &m, PGPBlockType t) + : msg(m) + , mType(t) +{ + +} + +QByteArray MimeTreeParser::Block::text() const +{ + return msg; +} + +PGPBlockType Block::type() const +{ + return mType; +} diff --git a/framework/src/domain/mimetreeparser/otp/cryptohelper.h b/framework/src/domain/mimetreeparser/otp/cryptohelper.h new file mode 100644 index 00000000..f09771c3 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/cryptohelper.h @@ -0,0 +1,62 @@ +/* + cryptohelper.h + + Copyright (C) 2015 Sandro Knauß + Copyright (C) 2001,2002 the KPGP authors + See file AUTHORS.kpgp for details + + KMail 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) any later version. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __MIMETREEPARSER_CRYPTOHELPER_H__ +#define __MIMETREEPARSER_CRYPTOHELPER_H__ + +#include +#include + +namespace MimeTreeParser +{ + +enum PGPBlockType { + UnknownBlock = -1, // BEGIN PGP ??? + NoPgpBlock = 0, + PgpMessageBlock = 1, // BEGIN PGP MESSAGE + MultiPgpMessageBlock = 2, // BEGIN PGP MESSAGE, PART X[/Y] + SignatureBlock = 3, // BEGIN PGP SIGNATURE + ClearsignedBlock = 4, // BEGIN PGP SIGNED MESSAGE + PublicKeyBlock = 5, // BEGIN PGP PUBLIC KEY BLOCK + PrivateKeyBlock = 6 // BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: ...SECRET...) +}; + +class Block +{ +public: + Block(const QByteArray &m); + + Block(const QByteArray &m, PGPBlockType t); + + QByteArray text() const; + PGPBlockType type() const; + PGPBlockType determineType() const; + + QByteArray msg; + PGPBlockType mType; +}; + +/** Parses the given message and splits it into OpenPGP blocks and + Non-OpenPGP blocks. +*/ +QList prepareMessageForDecryption(const QByteArray &msg); + +} // namespace MimeTreeParser + +Q_DECLARE_TYPEINFO(MimeTreeParser::Block, Q_MOVABLE_TYPE); + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.cpp b/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.cpp new file mode 100644 index 00000000..9810797a --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.cpp @@ -0,0 +1,86 @@ +/* + Copyright (c) 2014-2017 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "decryptverifybodypartmemento.h" + +#include + +#include + +using namespace QGpgME; +using namespace GpgME; +using namespace MimeTreeParser; + +DecryptVerifyBodyPartMemento::DecryptVerifyBodyPartMemento(DecryptVerifyJob *job, const QByteArray &cipherText) + : CryptoBodyPartMemento(), + m_cipherText(cipherText), + m_job(job) +{ + Q_ASSERT(m_job); +} + +DecryptVerifyBodyPartMemento::~DecryptVerifyBodyPartMemento() +{ + if (m_job) { + m_job->slotCancel(); + } +} + +bool DecryptVerifyBodyPartMemento::start() +{ + Q_ASSERT(m_job); + if (const Error err = m_job->start(m_cipherText)) { + m_dr = DecryptionResult(err); + return false; + } + connect(m_job.data(), &DecryptVerifyJob::result, + this, &DecryptVerifyBodyPartMemento::slotResult); + setRunning(true); + return true; +} + +void DecryptVerifyBodyPartMemento::exec() +{ + Q_ASSERT(m_job); + QByteArray plainText; + setRunning(true); + const std::pair p = m_job->exec(m_cipherText, plainText); + saveResult(p.first, p.second, plainText); + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = nullptr; +} + +void DecryptVerifyBodyPartMemento::saveResult(const DecryptionResult &dr, + const VerificationResult &vr, + const QByteArray &plainText) +{ + Q_ASSERT(m_job); + setRunning(false); + m_dr = dr; + m_vr = vr; + m_plainText = plainText; + setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml()); +} + +void DecryptVerifyBodyPartMemento::slotResult(const DecryptionResult &dr, + const VerificationResult &vr, + const QByteArray &plainText) +{ + saveResult(dr, vr, plainText); + m_job = nullptr; + notify(); +} diff --git a/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.h b/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.h new file mode 100644 index 00000000..4781abe2 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/decryptverifybodypartmemento.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2014-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__ +#define __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__ + +#include "cryptobodypartmemento.h" + +#include +#include + +#include + +#include "bodypart.h" + +namespace QGpgME +{ +class DecryptVerifyJob; +} + +namespace MimeTreeParser +{ + +class DecryptVerifyBodyPartMemento + : public CryptoBodyPartMemento +{ + Q_OBJECT +public: + DecryptVerifyBodyPartMemento(QGpgME::DecryptVerifyJob *job, const QByteArray &cipherText); + ~DecryptVerifyBodyPartMemento(); + + bool start() Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + + const QByteArray &plainText() const + { + return m_plainText; + } + const GpgME::DecryptionResult &decryptResult() const + { + return m_dr; + } + const GpgME::VerificationResult &verifyResult() const + { + return m_vr; + } + +private Q_SLOTS: + void slotResult(const GpgME::DecryptionResult &dr, + const GpgME::VerificationResult &vr, + const QByteArray &plainText); + +private: + void saveResult(const GpgME::DecryptionResult &, + const GpgME::VerificationResult &, + const QByteArray &); +private: + // input: + const QByteArray m_cipherText; + QPointer m_job; + // output: + GpgME::DecryptionResult m_dr; + GpgME::VerificationResult m_vr; + QByteArray m_plainText; +}; +} +#endif // __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__ diff --git a/framework/src/domain/mimetreeparser/otp/enums.h b/framework/src/domain/mimetreeparser/otp/enums.h new file mode 100644 index 00000000..bec5a028 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/enums.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_ENUMS_H__ +#define __MIMETREEPARSER_ENUMS_H__ + +namespace MimeTreeParser +{ + +/** + * The display update mode: Force updates the display immediately, Delayed updates + * after some time (150ms by default) + */ +enum UpdateMode { + Force = 0, + Delayed +}; + +/** Flags for the encryption state. */ +typedef enum { + KMMsgEncryptionStateUnknown = ' ', + KMMsgNotEncrypted = 'N', + KMMsgPartiallyEncrypted = 'P', + KMMsgFullyEncrypted = 'F', + KMMsgEncryptionProblematic = 'X' +} KMMsgEncryptionState; + +/** Flags for the signature state. */ +typedef enum { + KMMsgSignatureStateUnknown = ' ', + KMMsgNotSigned = 'N', + KMMsgPartiallySigned = 'P', + KMMsgFullySigned = 'F', + KMMsgSignatureProblematic = 'X' +} KMMsgSignatureState; + +} + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/filehtmlwriter.cpp b/framework/src/domain/mimetreeparser/otp/filehtmlwriter.cpp new file mode 100644 index 00000000..a143f944 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/filehtmlwriter.cpp @@ -0,0 +1,119 @@ +/* -*- c++ -*- + filehtmlwriter.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "filehtmlwriter.h" + +#include "mimetreeparser_debug.h" + +namespace MimeTreeParser +{ + +FileHtmlWriter::FileHtmlWriter(const QString &filename) + : HtmlWriter(), + mFile(filename.isEmpty() ? QStringLiteral("filehtmlwriter.out") : filename) +{ +} + +FileHtmlWriter::~FileHtmlWriter() +{ + if (mFile.isOpen()) { + qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: file still open!"; + mStream.setDevice(nullptr); + mFile.close(); + } +} + +void FileHtmlWriter::begin(const QString &css) +{ + openOrWarn(); + if (!css.isEmpty()) { + write(QLatin1String("\n")); + } +} + +void FileHtmlWriter::end() +{ + flush(); + mStream.setDevice(nullptr); + mFile.close(); +} + +void FileHtmlWriter::reset() +{ + if (mFile.isOpen()) { + mStream.setDevice(nullptr); + mFile.close(); + } +} + +void FileHtmlWriter::write(const QString &str) +{ + mStream << str; + flush(); +} + +void FileHtmlWriter::queue(const QString &str) +{ + write(str); +} + +void FileHtmlWriter::flush() +{ + mStream.flush(); + mFile.flush(); +} + +void FileHtmlWriter::openOrWarn() +{ + if (mFile.isOpen()) { + qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: file still open!"; + mStream.setDevice(nullptr); + mFile.close(); + } + if (!mFile.open(QIODevice::WriteOnly)) { + qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: Cannot open file" << mFile.fileName(); + } else { + mStream.setDevice(&mFile); + mStream.setCodec("UTF-8"); + } +} + +void FileHtmlWriter::embedPart(const QByteArray &contentId, const QString &url) +{ + mStream << "" << endl; + flush(); +} +void FileHtmlWriter::extraHead(const QString &) +{ + +} + +} // diff --git a/framework/src/domain/mimetreeparser/otp/filehtmlwriter.h b/framework/src/domain/mimetreeparser/otp/filehtmlwriter.h new file mode 100644 index 00000000..5dafb593 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/filehtmlwriter.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- + filehtmlwriter.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_FILEHTMLWRITER_H__ +#define __MIMETREEPARSER_FILEHTMLWRITER_H__ + +#include "mimetreeparser_export.h" +#include "mimetreeparser/htmlwriter.h" + +#include +#include + +class QString; + +namespace MimeTreeParser +{ + +class MIMETREEPARSER_EXPORT FileHtmlWriter : public HtmlWriter +{ +public: + explicit FileHtmlWriter(const QString &filename); + virtual ~FileHtmlWriter(); + + void begin(const QString &cssDefs) Q_DECL_OVERRIDE; + void end() Q_DECL_OVERRIDE; + void reset() Q_DECL_OVERRIDE; + void write(const QString &str) Q_DECL_OVERRIDE; + void queue(const QString &str) Q_DECL_OVERRIDE; + void flush() Q_DECL_OVERRIDE; + void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE; + void extraHead(const QString &str) Q_DECL_OVERRIDE; +private: + void openOrWarn(); + +private: + QFile mFile; + QTextStream mStream; +}; + +} // namespace MimeTreeParser + +#endif // __MIMETREEPARSER_FILEHTMLWRITER_H__ diff --git a/framework/src/domain/mimetreeparser/otp/htmlwriter.cpp b/framework/src/domain/mimetreeparser/otp/htmlwriter.cpp new file mode 100644 index 00000000..3c98d997 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/htmlwriter.cpp @@ -0,0 +1,40 @@ +/* + This file is part of KMail's plugin interface. + Copyright (c) 2003 Marc Mutz + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "htmlwriter.h" + +MimeTreeParser::Interface::HtmlWriter::~HtmlWriter() +{ +} + +MimeTreeParser::HtmlWriter::~HtmlWriter() +{ +} + diff --git a/framework/src/domain/mimetreeparser/otp/htmlwriter.h b/framework/src/domain/mimetreeparser/otp/htmlwriter.h new file mode 100644 index 00000000..382c80fb --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/htmlwriter.h @@ -0,0 +1,125 @@ +/* -*- c++ -*- + interfaces/htmlwriter.h + + This file is part of KMail's plugin interface. + Copyright (c) 2003 Marc Mutz + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__ +#define __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__ + +class QByteArray; +class QString; + +namespace MimeTreeParser +{ +/** + * @short An interface for HTML sinks. + * @author Marc Mutz + * + */ +namespace Interface +{ +class HtmlWriter +{ +public: + virtual ~HtmlWriter(); + + /** Signal the begin of stuff to write, and give the CSS definitions */ + virtual void begin(const QString &cssDefinitions) = 0; + /** Write out a chunk of text. No HTML escaping is performed. */ + virtual void write(const QString &html) = 0; + /** Signal the end of stuff to write. */ + virtual void end() = 0; +}; +} + +/** + * @short An interface to HTML sinks + * @author Marc Mutz + * + * @deprecated KMail should be ported to Interface::HtmlWriter. This + * interface exposes internal working models. The queuing + * vs. writing() issues exposed here should be hidden by using two + * different implementations of KHTMLPartHtmlWriter: one for + * queuing, and one for writing. This should be fixed before the + * release, so we an keep the plugin interface stable. + * + * Operate this interface in one and only one of the following two + * modes: + * + * @section Sync Mode + * + * In sync mode, use #begin() to initiate a session, then + * #write() some chunks of HTML code and finally #end() the session. + * + * @section Async Mode + * + * In async mode, use #begin() to initialize a session, then + * #queue() some chunks of HTML code and finally end the + * session by calling #flush(). + * + * Queued HTML code is fed to the html sink using a timer. For this + * to work, control must return to the event loop so timer events + * are delivered. + * + * @section Combined mode + * + * You may combine the two modes in the following way only. Any + * number of #write() calls can precede #queue() calls, + * but once a chunk has been queued, you @em must @em not + * #write() more data, only #queue() it. + * + * Naturally, whenever you queued data in a given session, that + * session must be ended by calling #flush(), not #end(). + */ +class HtmlWriter : public Interface::HtmlWriter +{ +public: + virtual ~HtmlWriter(); + + /** Stop all possibly pending processing in order to be able to + * call #begin() again. */ + virtual void reset() = 0; + + virtual void queue(const QString &str) = 0; + /** (Start) flushing internal buffers, if any. */ + virtual void flush() = 0; + + /** + * Embed a part with Content-ID @p contentId, using url @p url. + */ + virtual void embedPart(const QByteArray &contentId, const QString &url) = 0; + + virtual void extraHead(const QString &str) = 0; +}; + +} + +#endif // __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__ + diff --git a/framework/src/domain/mimetreeparser/otp/messagepart.cpp b/framework/src/domain/mimetreeparser/otp/messagepart.cpp new file mode 100644 index 00000000..3228a387 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/messagepart.cpp @@ -0,0 +1,1352 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "messagepart.h" +#include "mimetreeparser_debug.h" +#include "attachmentstrategy.h" +#include "cryptohelper.h" +#include "objecttreeparser.h" +#include "htmlwriter.h" +#include "qgpgmejobexecutor.h" + +#include "cryptobodypartmemento.h" +#include "decryptverifybodypartmemento.h" +#include "verifydetachedbodypartmemento.h" +#include "verifyopaquebodypartmemento.h" + +#include "utils.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +using namespace MimeTreeParser; + +//------MessagePart----------------------- +MessagePart::MessagePart(ObjectTreeParser *otp, + const QString &text) + : mText(text) + , mOtp(otp) + , mAttachmentNode(nullptr) + , mRoot(false) +{ +} + +MessagePart::~MessagePart() +{ +} + +PartMetaData *MessagePart::partMetaData() +{ + return &mMetaData; +} + +void MessagePart::setAttachmentFlag(KMime::Content *node) +{ + mAttachmentNode = node; +} + +bool MessagePart::isAttachment() const +{ + return mAttachmentNode; +} + +KMime::Content *MessagePart::attachmentNode() const +{ + return mAttachmentNode; +} + +void MessagePart::setIsRoot(bool root) +{ + mRoot = root; +} + +bool MessagePart::isRoot() const +{ + return mRoot; +} + +QString MessagePart::text() const +{ + return mText; +} + +void MessagePart::setText(const QString &text) +{ + mText = text; +} + +bool MessagePart::isHtml() const +{ + return false; +} + +bool MessagePart::isHidden() const +{ + return false; +} + +Interface::ObjectTreeSource *MessagePart::source() const +{ + Q_ASSERT(mOtp); + return mOtp->mSource; +} + +HtmlWriter *MessagePart::htmlWriter() const +{ + Q_ASSERT(mOtp); + return mOtp->htmlWriter(); +} + +void MessagePart::setHtmlWriter(HtmlWriter *htmlWriter) const +{ + mOtp->mHtmlWriter = htmlWriter; +} + +void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart) +{ + auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart); + mRoot = subMessagePart->isRoot(); + foreach (const auto &part, subMessagePart->subParts()) { + appendSubPart(part); + } +} + +QString MessagePart::renderInternalText() const +{ + QString text; + foreach (const auto &mp, subParts()) { + text += mp->text(); + } + return text; +} + +void MessagePart::copyContentFrom() const +{ + foreach (const auto &mp, subParts()) { + const auto m = mp.dynamicCast(); + if (m) { + m->copyContentFrom(); + } + } +} + +void MessagePart::fix() const +{ + foreach (const auto &mp, subParts()) { + const auto m = mp.dynamicCast(); + if (m) { + m->fix(); + } + } +} + +void MessagePart::appendSubPart(const Interface::MessagePart::Ptr &messagePart) +{ + messagePart->setParentPart(this); + mBlocks.append(messagePart); +} + +const QVector &MessagePart::subParts() const +{ + return mBlocks; +} + +bool MessagePart::hasSubParts() const +{ + return !mBlocks.isEmpty(); +} + +//-----MessagePartList---------------------- +MessagePartList::MessagePartList(ObjectTreeParser *otp) + : MessagePart(otp, QString()) +{ +} + +MessagePartList::~MessagePartList() +{ + +} + +QString MessagePartList::text() const +{ + return renderInternalText(); +} + +QString MessagePartList::plaintextContent() const +{ + return QString(); +} + +QString MessagePartList::htmlContent() const +{ + return QString(); +} + +//-----TextMessageBlock---------------------- + +TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage) + : MessagePartList(otp) + , mNode(node) + , mDrawFrame(drawFrame) + , mShowLink(showLink) + , mDecryptMessage(decryptMessage) + , mIsHidden(false) +{ + if (!mNode) { + qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; + return; + } + + mIsHidden = mOtp->nodeHelper()->isNodeDisplayedHidden(mNode); + + parseContent(); +} + +TextMessagePart::~TextMessagePart() +{ + +} + +bool TextMessagePart::decryptMessage() const +{ + return mDecryptMessage; +} + +void TextMessagePart::parseContent() +{ + const auto aCodec = mOtp->codecFor(mNode); + const QString &fromAddress = mOtp->nodeHelper()->fromAsString(mNode); + mSignatureState = KMMsgNotSigned; + mEncryptionState = KMMsgNotEncrypted; + const auto blocks = prepareMessageForDecryption(mNode->decodedContent()); + + const auto cryptProto = QGpgME::openpgp(); + + if (!blocks.isEmpty()) { + + /* The (overall) signature/encrypted status is broken + * if one unencrypted part is at the beginning or in the middle + * because mailmain adds an unencrypted part at the end this should not break the overall status + * + * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than + * the status is set to unencryped + */ + bool fullySignedOrEncrypted = true; + bool fullySignedOrEncryptedTmp = true; + + for (const auto &block : blocks) { + + if (!fullySignedOrEncryptedTmp) { + fullySignedOrEncrypted = false; + } + + if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) { + fullySignedOrEncryptedTmp = false; + appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(block.text())))); + } else if (block.type() == PgpMessageBlock) { + EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); + mp->setDecryptMessage(decryptMessage()); + mp->setIsEncrypted(true); + appendSubPart(mp); + if (!decryptMessage()) { + continue; + } + mp->startDecryption(block.text(), aCodec); + if (mp->partMetaData()->inProgress) { + continue; + } + } else if (block.type() == ClearsignedBlock) { + SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); + appendSubPart(mp); + mp->startVerification(block.text(), aCodec); + } else { + continue; + } + + const auto mp = subParts().last().staticCast(); + const PartMetaData *messagePart(mp->partMetaData()); + + if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { + mp->setText(aCodec->toUnicode(block.text())); + } + + if (messagePart->isEncrypted) { + mEncryptionState = KMMsgPartiallyEncrypted; + } + + if (messagePart->isSigned) { + mSignatureState = KMMsgPartiallySigned; + } + } + + //Do we have an fully Signed/Encrypted Message? + if (fullySignedOrEncrypted) { + if (mSignatureState == KMMsgPartiallySigned) { + mSignatureState = KMMsgFullySigned; + } + if (mEncryptionState == KMMsgPartiallyEncrypted) { + mEncryptionState = KMMsgFullyEncrypted; + } + } + } +} + +KMMsgEncryptionState TextMessagePart::encryptionState() const +{ + return mEncryptionState; +} + +KMMsgSignatureState TextMessagePart::signatureState() const +{ + return mSignatureState; +} + +bool TextMessagePart::isHidden() const +{ + return mIsHidden; +} + +bool TextMessagePart::showLink() const +{ + return mShowLink; +} + +bool TextMessagePart::showTextFrame() const +{ + return mDrawFrame; +} + +//-----AttachmentMessageBlock---------------------- + +AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage) + : TextMessagePart(otp, node, drawFrame, showLink, decryptMessage) + , mIsImage(false) + , mNeverDisplayInline(false) +{ + +} + +AttachmentMessagePart::~AttachmentMessagePart() +{ + +} + +bool AttachmentMessagePart::neverDisplayInline() const +{ + return mNeverDisplayInline; +} + +void AttachmentMessagePart::setNeverDisplayInline(bool displayInline) +{ + mNeverDisplayInline = displayInline; +} + +bool AttachmentMessagePart::isImage() const +{ + return mIsImage; +} + +void AttachmentMessagePart::setIsImage(bool image) +{ + mIsImage = image; +} + +IconType AttachmentMessagePart::asIcon() const +{ + const AttachmentStrategy *const as = mOtp->attachmentStrategy(); + const bool defaultHidden(as && as->defaultDisplay(mNode) == AttachmentStrategy::None); + const bool showOnlyOneMimePart(mOtp->showOnlyOneMimePart()); + auto preferredMode = source()->preferredMode(); + bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + + QByteArray mediaType("text"); + QByteArray subType("plain"); + if (mNode->contentType(false) && !mNode->contentType()->mediaType().isEmpty() && + !mNode->contentType()->subType().isEmpty()) { + mediaType = mNode->contentType()->mediaType(); + subType = mNode->contentType()->subType(); + } + const bool isTextPart = (mediaType == QByteArrayLiteral("text")); + + bool defaultAsIcon = true; + if (!neverDisplayInline()) { + if (as) { + defaultAsIcon = as->defaultDisplay(mNode) == AttachmentStrategy::AsIcon; + } + } + if (isImage() && showOnlyOneMimePart && !neverDisplayInline()) { + defaultAsIcon = false; + } + + // neither image nor text -> show as icon + if (!isImage() && !isTextPart) { + defaultAsIcon = true; + } + + if (isTextPart) { + if (as && as->defaultDisplay(mNode) != AttachmentStrategy::Inline) { + return MimeTreeParser::IconExternal; + } + return MimeTreeParser::NoIcon; + } else { + if (isImage() && isHtmlPreferred && + mNode->parent() && mNode->parent()->contentType()->subType() == "related") { + return MimeTreeParser::IconInline; + } + + if (defaultHidden && !showOnlyOneMimePart && mNode->parent()) { + return MimeTreeParser::IconInline; + } + + if (defaultAsIcon) { + return MimeTreeParser::IconExternal; + } else if (isImage()) { + return MimeTreeParser::IconInline; + } else { + return MimeTreeParser::NoIcon; + } + } +} + +bool AttachmentMessagePart::isHidden() const +{ + const AttachmentStrategy *const as = mOtp->attachmentStrategy(); + const bool defaultHidden(as && as->defaultDisplay(mNode) == AttachmentStrategy::None); + const bool showOnlyOneMimePart(mOtp->showOnlyOneMimePart()); + auto preferredMode = source()->preferredMode(); + bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + + QByteArray mediaType("text"); + QByteArray subType("plain"); + if (mNode->contentType(false) && !mNode->contentType()->mediaType().isEmpty() && + !mNode->contentType()->subType().isEmpty()) { + mediaType = mNode->contentType()->mediaType(); + subType = mNode->contentType()->subType(); + } + const bool isTextPart = (mediaType == QByteArrayLiteral("text")); + + bool defaultAsIcon = true; + if (!neverDisplayInline()) { + if (as) { + defaultAsIcon = as->defaultDisplay(mNode) == AttachmentStrategy::AsIcon; + } + } + if (isImage() && showOnlyOneMimePart && !neverDisplayInline()) { + defaultAsIcon = false; + } + + // neither image nor text -> show as icon + if (!isImage() && !isTextPart) { + defaultAsIcon = true; + } + + bool hidden(false); + if (isTextPart) { + hidden = defaultHidden && !showOnlyOneMimePart; + } else { + if (isImage() && isHtmlPreferred && + mNode->parent() && mNode->parent()->contentType()->subType() == "related") { + hidden = true; + } else { + hidden = defaultHidden && !showOnlyOneMimePart && mNode->parent(); + hidden |= defaultAsIcon && (defaultHidden || showOnlyOneMimePart); + } + } + mOtp->nodeHelper()->setNodeDisplayedHidden(mNode, hidden); + return hidden; +} + +//-----HtmlMessageBlock---------------------- + +HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node, Interface::ObjectTreeSource *source) + : MessagePart(otp, QString()) + , mNode(node) + , mSource(source) +{ + if (!mNode) { + qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; + return; + } + + const QByteArray partBody(mNode->decodedContent()); + mBodyHTML = mOtp->codecFor(mNode)->toUnicode(partBody); + mCharset = NodeHelper::charset(mNode); +} + +HtmlMessagePart::~HtmlMessagePart() +{ +} + +void HtmlMessagePart::fix() const +{ + mOtp->mHtmlContent += mBodyHTML; + mOtp->mHtmlContentCharset = mCharset; +} + +QString HtmlMessagePart::text() const +{ + return mBodyHTML; +} + +bool HtmlMessagePart::isHtml() const +{ + return true; +} + +//-----MimeMessageBlock---------------------- + +MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart) + : MessagePart(otp, QString()) + , mNode(node) + , mOnlyOneMimePart(onlyOneMimePart) +{ + if (!mNode) { + qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; + return; + } + + parseInternal(mNode, mOnlyOneMimePart); +} + +MimeMessagePart::~MimeMessagePart() +{ + +} + +QString MimeMessagePart::text() const +{ + return renderInternalText(); +} + +QString MimeMessagePart::plaintextContent() const +{ + return QString(); +} + +QString MimeMessagePart::htmlContent() const +{ + return QString(); +} + +//-----AlternativeMessagePart---------------------- + +AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode) + : MessagePart(otp, QString()) + , mNode(node) + , mPreferredMode(preferredMode) +{ + KMime::Content *dataIcal = findTypeInDirectChilds(mNode, "text/calendar"); + KMime::Content *dataHtml = findTypeInDirectChilds(mNode, "text/html"); + KMime::Content *dataText = findTypeInDirectChilds(mNode, "text/plain"); + + if (!dataHtml) { + // If we didn't find the HTML part as the first child of the multipart/alternative, it might + // be that this is a HTML message with images, and text/plain and multipart/related are the + // immediate children of this multipart/alternative node. + // In this case, the HTML node is a child of multipart/related. + dataHtml = findTypeInDirectChilds(mNode, "multipart/related"); + + // Still not found? Stupid apple mail actually puts the attachments inside of the + // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed + // here. + // Do this only when prefering HTML mail, though, since otherwise the attachments are hidden + // when displaying plain text. + if (!dataHtml) { + dataHtml = findTypeInDirectChilds(mNode, "multipart/mixed"); + } + } + + if (dataIcal) { + mChildNodes[Util::MultipartIcal] = dataIcal; + } + + if (dataText) { + mChildNodes[Util::MultipartPlain] = dataText; + } + + if (dataHtml) { + mChildNodes[Util::MultipartHtml] = dataHtml; + } + + if (mChildNodes.isEmpty()) { + qCWarning(MIMETREEPARSER_LOG) << "no valid nodes"; + return; + } + + QMapIterator i(mChildNodes); + while (i.hasNext()) { + i.next(); + mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true)); + } +} + +AlternativeMessagePart::~AlternativeMessagePart() +{ + +} + +Util::HtmlMode AlternativeMessagePart::preferredMode() const +{ + return mPreferredMode; +} + +QList AlternativeMessagePart::availableModes() +{ + return mChildParts.keys(); +} + +QString AlternativeMessagePart::text() const +{ + if (mChildParts.contains(Util::MultipartPlain)) { + return mChildParts[Util::MultipartPlain]->text(); + } + return QString(); +} + +void AlternativeMessagePart::fix() const +{ + if (mChildParts.contains(Util::MultipartPlain)) { + mChildParts[Util::MultipartPlain]->fix(); + } + + const auto mode = preferredMode(); + if (mode != Util::MultipartPlain && mChildParts.contains(mode)) { + mChildParts[mode]->fix(); + } +} + +void AlternativeMessagePart::copyContentFrom() const +{ + if (mChildParts.contains(Util::MultipartPlain)) { + mChildParts[Util::MultipartPlain]->copyContentFrom(); + } + + const auto mode = preferredMode(); + if (mode != Util::MultipartPlain && mChildParts.contains(mode)) { + mChildParts[mode]->copyContentFrom(); + } +} + +bool AlternativeMessagePart::isHtml() const +{ + return mChildParts.contains(Util::MultipartHtml); +} + +QString AlternativeMessagePart::plaintextContent() const +{ + return text(); +} + +QString AlternativeMessagePart::htmlContent() const +{ + if (mChildParts.contains(Util::MultipartHtml)) { + return mChildParts[Util::MultipartHtml]->text(); + } else { + return plaintextContent(); + } +} + +//-----CertMessageBlock---------------------- + +CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport) + : MessagePart(otp, QString()) + , mNode(node) + , mAutoImport(autoImport) + , mCryptoProto(cryptoProto) +{ + if (!mNode) { + qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; + return; + } + + if (!mAutoImport) { + return; + } + + const QByteArray certData = node->decodedContent(); + + QGpgME::ImportJob *import = mCryptoProto->importJob(); + QGpgMEJobExecutor executor; + mImportResult = executor.exec(import, certData); +} + +CertMessagePart::~CertMessagePart() +{ + +} + +QString CertMessagePart::text() const +{ + return QString(); +} + +//-----SignedMessageBlock--------------------- +SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, + const QString &text, + const QGpgME::Protocol *cryptoProto, + const QString &fromAddress, + KMime::Content *node) + : MessagePart(otp, text) + , mCryptoProto(cryptoProto) + , mFromAddress(fromAddress) + , mNode(node) +{ + mMetaData.technicalProblem = (mCryptoProto == nullptr); + mMetaData.isSigned = true; + mMetaData.isGoodSignature = false; + mMetaData.keyTrust = GpgME::Signature::Unknown; + mMetaData.status = i18n("Wrong Crypto Plug-In."); + mMetaData.status_code = GPGME_SIG_STAT_NONE; +} + +SignedMessagePart::~SignedMessagePart() +{ + +} + +void SignedMessagePart::setIsSigned(bool isSigned) +{ + mMetaData.isSigned = isSigned; +} + +bool SignedMessagePart::isSigned() const +{ + return mMetaData.isSigned; +} + +bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode) +{ + NodeHelper *nodeHelper = mOtp->nodeHelper(); + Interface::ObjectTreeSource *_source = source(); + + mMetaData.isSigned = false; + mMetaData.technicalProblem = (mCryptoProto == nullptr); + mMetaData.keyTrust = GpgME::Signature::Unknown; + mMetaData.status = i18n("Wrong Crypto Plug-In."); + mMetaData.status_code = GPGME_SIG_STAT_NONE; + + const QByteArray mementoName = "verification"; + + CryptoBodyPartMemento *m = dynamic_cast(nodeHelper->bodyPartMemento(mNode, mementoName)); + Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong + + if (!m && mCryptoProto) { + if (!signature.isEmpty()) { + QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob(); + if (job) { + m = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data); + } + } else { + QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob(); + if (job) { + m = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data); + } + } + if (m) { + if (mOtp->allowAsync()) { + QObject::connect(m, &CryptoBodyPartMemento::update, + nodeHelper, &NodeHelper::update); + QObject::connect(m, SIGNAL(update(MimeTreeParser::UpdateMode)), + _source->sourceObject(), SLOT(update(MimeTreeParser::UpdateMode))); + + if (m->start()) { + mMetaData.inProgress = true; + mOtp->mHasPendingAsyncJobs = true; + } + } else { + m->exec(); + } + nodeHelper->setBodyPartMemento(mNode, mementoName, m); + } + } else if (m->isRunning()) { + mMetaData.inProgress = true; + mOtp->mHasPendingAsyncJobs = true; + } else { + mMetaData.inProgress = false; + mOtp->mHasPendingAsyncJobs = false; + } + + if (m && !mMetaData.inProgress) { + if (!signature.isEmpty()) { + mVerifiedText = data; + } + setVerificationResult(m, textNode); + } + + if (!m && !mMetaData.inProgress) { + QString errorMsg; + QString cryptPlugLibName; + QString cryptPlugDisplayName; + if (mCryptoProto) { + cryptPlugLibName = mCryptoProto->name(); + cryptPlugDisplayName = mCryptoProto->displayName(); + } + + if (!mCryptoProto) { + if (cryptPlugDisplayName.isEmpty()) { + errorMsg = i18n("No appropriate crypto plug-in was found."); + } else { + errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'", + "No %1 plug-in was found.", + cryptPlugDisplayName); + } + } else { + errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.", + cryptPlugLibName); + } + mMetaData.errorText = i18n("The message is signed, but the " + "validity of the signature cannot be " + "verified.
" + "Reason: %1", + errorMsg); + } + + return mMetaData.isSigned; +} + +static int signatureToStatus(const GpgME::Signature &sig) +{ + switch (sig.status().code()) { + case GPG_ERR_NO_ERROR: + return GPGME_SIG_STAT_GOOD; + case GPG_ERR_BAD_SIGNATURE: + return GPGME_SIG_STAT_BAD; + case GPG_ERR_NO_PUBKEY: + return GPGME_SIG_STAT_NOKEY; + case GPG_ERR_NO_DATA: + return GPGME_SIG_STAT_NOSIG; + case GPG_ERR_SIG_EXPIRED: + return GPGME_SIG_STAT_GOOD_EXP; + case GPG_ERR_KEY_EXPIRED: + return GPGME_SIG_STAT_GOOD_EXPKEY; + default: + return GPGME_SIG_STAT_ERROR; + } +} + +QString prettifyDN(const char *uid) +{ + return QGpgME::DN(uid).prettyDN(); +} + +void SignedMessagePart::sigStatusToMetaData() +{ + GpgME::Key key; + if (mMetaData.isSigned) { + GpgME::Signature signature = mSignatures.front(); + mMetaData.status_code = signatureToStatus(signature); + mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD; + // save extended signature status flags + mMetaData.sigSummary = signature.summary(); + + if (mMetaData.isGoodSignature && !key.keyID()) { + // Search for the key by its fingerprint so that we can check for + // trust etc. + QGpgME::KeyListJob *job = mCryptoProto->keyListJob(false); // local, no sigs + if (!job) { + qCDebug(MIMETREEPARSER_LOG) << "The Crypto backend does not support listing keys. "; + } else { + std::vector found_keys; + // As we are local it is ok to make this synchronous + GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(signature.fingerprint())), false, found_keys); + if (res.error()) { + qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint(); + } + if (found_keys.size() > 1) { + // Should not Happen + qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); + } + if (found_keys.size() != 1) { + // Should not Happen at this point + qCDebug(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); + } else { + key = found_keys[0]; + } + delete job; + } + } + + if (key.keyID()) { + mMetaData.keyId = key.keyID(); + } + if (mMetaData.keyId.isEmpty()) { + mMetaData.keyId = signature.fingerprint(); + } + mMetaData.keyTrust = signature.validity(); + if (key.numUserIDs() > 0 && key.userID(0).id()) { + mMetaData.signer = prettifyDN(key.userID(0).id()); + } + for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { + // The following if /should/ always result in TRUE but we + // won't trust implicitely the plugin that gave us these data. + if (key.userID(iMail).email()) { + QString email = QString::fromUtf8(key.userID(iMail).email()); + // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the + // ### email addresses are specified as angle-addr, not addr-spec: + if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { + email = email.mid(1, email.length() - 2); + } + if (!email.isEmpty()) { + mMetaData.signerMailAddresses.append(email); + } + } + } + + if (signature.creationTime()) { + mMetaData.creationTime.setTime_t(signature.creationTime()); + } else { + mMetaData.creationTime = QDateTime(); + } + if (mMetaData.signer.isEmpty()) { + if (key.numUserIDs() > 0 && key.userID(0).name()) { + mMetaData.signer = prettifyDN(key.userID(0).name()); + } + if (!mMetaData.signerMailAddresses.empty()) { + if (mMetaData.signer.isEmpty()) { + mMetaData.signer = mMetaData.signerMailAddresses.front(); + } else { + mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>'); + } + } + } + } +} + +void SignedMessagePart::startVerification(const QByteArray &text, const QTextCodec *aCodec) +{ + startVerificationDetached(text, nullptr, QByteArray()); + + if (!mNode && mMetaData.isSigned) { + setText(aCodec->toUnicode(mVerifiedText)); + } +} + +void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature) +{ + mMetaData.isEncrypted = false; + mMetaData.isDecryptable = false; + + if (textNode) { + parseInternal(textNode, false); + } + + okVerify(text, signature, textNode); + + if (!mMetaData.isSigned) { + mMetaData.creationTime = QDateTime(); + } +} + +void SignedMessagePart::setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode) +{ + { + const auto vm = dynamic_cast(m); + if (vm) { + mSignatures = vm->verifyResult().signatures(); + } + } + { + const auto vm = dynamic_cast(m); + if (vm) { + mVerifiedText = vm->plainText(); + mSignatures = vm->verifyResult().signatures(); + } + } + { + const auto vm = dynamic_cast(m); + if (vm) { + mVerifiedText = vm->plainText(); + mSignatures = vm->verifyResult().signatures(); + } + } + mMetaData.auditLogError = m->auditLogError(); + mMetaData.auditLog = m->auditLogAsHtml(); + mMetaData.isSigned = !mSignatures.empty(); + + if (mMetaData.isSigned) { + sigStatusToMetaData(); + if (mNode) { + mOtp->nodeHelper()->setSignatureState(mNode, KMMsgFullySigned); + if (!textNode) { + mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData); + + if (!mVerifiedText.isEmpty()) { + auto tempNode = new KMime::Content(); + tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData())); + tempNode->parse(); + + if (!tempNode->head().isEmpty()) { + tempNode->contentDescription()->from7BitString("signed data"); + } + mOtp->mNodeHelper->attachExtraContent(mNode, tempNode); + + parseInternal(tempNode, false); + } + } + } + } +} + +QString SignedMessagePart::plaintextContent() const +{ + if (!mNode) { + return MessagePart::text(); + } else { + return QString(); + } +} + +QString SignedMessagePart::htmlContent() const +{ + if (!mNode) { + return MessagePart::text(); + } else { + return QString(); + } +} + +//-----CryptMessageBlock--------------------- +EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, + const QString &text, + const QGpgME::Protocol *cryptoProto, + const QString &fromAddress, + KMime::Content *node) + : MessagePart(otp, text) + , mPassphraseError(false) + , mNoSecKey(false) + , mCryptoProto(cryptoProto) + , mFromAddress(fromAddress) + , mNode(node) + , mDecryptMessage(false) +{ + mMetaData.technicalProblem = (mCryptoProto == nullptr); + mMetaData.isSigned = false; + mMetaData.isGoodSignature = false; + mMetaData.isEncrypted = false; + mMetaData.isDecryptable = false; + mMetaData.keyTrust = GpgME::Signature::Unknown; + mMetaData.status = i18n("Wrong Crypto Plug-In."); + mMetaData.status_code = GPGME_SIG_STAT_NONE; +} + +EncryptedMessagePart::~EncryptedMessagePart() +{ + +} + +void EncryptedMessagePart::setDecryptMessage(bool decrypt) +{ + mDecryptMessage = decrypt; +} + +bool EncryptedMessagePart::decryptMessage() const +{ + return mDecryptMessage; +} + +void EncryptedMessagePart::setIsEncrypted(bool encrypted) +{ + mMetaData.isEncrypted = encrypted; +} + +bool EncryptedMessagePart::isEncrypted() const +{ + return mMetaData.isEncrypted; +} + +bool EncryptedMessagePart::isDecryptable() const +{ + return mMetaData.isDecryptable; +} + +bool EncryptedMessagePart::passphraseError() const +{ + return mPassphraseError; +} + +void EncryptedMessagePart::startDecryption(const QByteArray &text, const QTextCodec *aCodec) +{ + KMime::Content *content = new KMime::Content; + content->setBody(text); + content->parse(); + + startDecryption(content); + + if (!mMetaData.inProgress && mMetaData.isDecryptable) { + if (hasSubParts()) { + auto _mp = (subParts()[0]).dynamicCast(); + if (_mp) { + _mp->setText(aCodec->toUnicode(mDecryptedData)); + } else { + setText(aCodec->toUnicode(mDecryptedData)); + } + } else { + setText(aCodec->toUnicode(mDecryptedData)); + } + } +} + +bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) +{ + mPassphraseError = false; + mMetaData.inProgress = false; + mMetaData.errorText.clear(); + mMetaData.auditLogError = GpgME::Error(); + mMetaData.auditLog.clear(); + bool bDecryptionOk = false; + bool cannotDecrypt = false; + Interface::ObjectTreeSource *_source = source(); + NodeHelper *nodeHelper = mOtp->nodeHelper(); + + Q_ASSERT(decryptMessage()); + + // Check whether the memento contains a result from last time: + const DecryptVerifyBodyPartMemento *m + = dynamic_cast(nodeHelper->bodyPartMemento(&data, "decryptverify")); + + Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong + + if (!m && mCryptoProto) { + QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob(); + if (!job) { + cannotDecrypt = true; + } else { + const QByteArray ciphertext = data.decodedContent(); + DecryptVerifyBodyPartMemento *newM + = new DecryptVerifyBodyPartMemento(job, ciphertext); + if (mOtp->allowAsync()) { + QObject::connect(newM, &CryptoBodyPartMemento::update, + nodeHelper, &NodeHelper::update); + QObject::connect(newM, SIGNAL(update(MimeTreeParser::UpdateMode)), _source->sourceObject(), + SLOT(update(MimeTreeParser::UpdateMode))); + if (newM->start()) { + mMetaData.inProgress = true; + mOtp->mHasPendingAsyncJobs = true; + } else { + m = newM; + } + } else { + newM->exec(); + m = newM; + } + nodeHelper->setBodyPartMemento(&data, "decryptverify", newM); + } + } else if (m->isRunning()) { + mMetaData.inProgress = true; + mOtp->mHasPendingAsyncJobs = true; + m = nullptr; + } + + if (m) { + const QByteArray &plainText = m->plainText(); + const GpgME::DecryptionResult &decryptResult = m->decryptResult(); + const GpgME::VerificationResult &verifyResult = m->verifyResult(); + mMetaData.isSigned = verifyResult.signatures().size() > 0; + + if (verifyResult.signatures().size() > 0) { + auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, MessagePart::text(), mCryptoProto, mFromAddress, mNode)); + subPart->setVerificationResult(m, nullptr); + appendSubPart(subPart); + } + + mDecryptRecipients = decryptResult.recipients(); + bDecryptionOk = !decryptResult.error(); +// std::stringstream ss; +// ss << decryptResult << '\n' << verifyResult; +// qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str(); + + if (!bDecryptionOk && mMetaData.isSigned) { + //Only a signed part + mMetaData.isEncrypted = false; + bDecryptionOk = true; + mDecryptedData = plainText; + } else { + mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; + mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; + mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); + if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) { + mMetaData.keyId = decryptResult.recipient(0).keyID(); + } + + if (bDecryptionOk) { + mDecryptedData = plainText; + } else { + mNoSecKey = true; + foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { + mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); + } + if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly + mPassphraseError = true; + } + } + } + } + + if (!bDecryptionOk) { + QString cryptPlugLibName; + if (mCryptoProto) { + cryptPlugLibName = mCryptoProto->name(); + } + + if (!mCryptoProto) { + mMetaData.errorText = i18n("No appropriate crypto plug-in was found."); + } else if (cannotDecrypt) { + mMetaData.errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", + cryptPlugLibName); + } else if (!passphraseError()) { + mMetaData.errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + + QLatin1String("
") + + i18n("Error: %1", mMetaData.errorText); + } + } + return bDecryptionOk; +} + +void EncryptedMessagePart::startDecryption(KMime::Content *data) +{ + if (!mNode && !data) { + return; + } + + if (!data) { + data = mNode; + } + + mMetaData.isEncrypted = true; + + bool bOkDecrypt = okDecryptMIME(*data); + + if (mMetaData.inProgress) { + return; + } + mMetaData.isDecryptable = bOkDecrypt; + + if (!mMetaData.isDecryptable) { + setText(QString::fromUtf8(mDecryptedData.constData())); + } + + if (mMetaData.isEncrypted && !decryptMessage()) { + mMetaData.isDecryptable = true; + } + + if (mNode && !mMetaData.isSigned) { + mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData); + + if (decryptMessage()) { + auto tempNode = new KMime::Content(); + tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData())); + tempNode->parse(); + + if (!tempNode->head().isEmpty()) { + tempNode->contentDescription()->from7BitString("encrypted data"); + } + mOtp->mNodeHelper->attachExtraContent(mNode, tempNode); + + parseInternal(tempNode, false); + } + } +} + +QString EncryptedMessagePart::plaintextContent() const +{ + if (!mNode) { + return MessagePart::text(); + } else { + return QString(); + } +} + +QString EncryptedMessagePart::htmlContent() const +{ + if (!mNode) { + return MessagePart::text(); + } else { + return QString(); + } +} + +QString EncryptedMessagePart::text() const +{ + if (hasSubParts()) { + auto _mp = (subParts()[0]).dynamicCast(); + if (_mp) { + return _mp->text(); + } else { + return MessagePart::text(); + } + } else { + return MessagePart::text(); + } +} + +EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) + : MessagePart(otp, QString()) + , mMessage(message) + , mNode(node) +{ + mMetaData.isEncrypted = false; + mMetaData.isSigned = false; + mMetaData.isEncapsulatedRfc822Message = true; + + mOtp->nodeHelper()->setNodeDisplayedEmbedded(mNode, true); + mOtp->nodeHelper()->setPartMetaData(mNode, mMetaData); + + if (!mMessage) { + qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!"; + return; + } + + // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists, + // since the user can click the link and expect to have normal attachment operations there. + mOtp->nodeHelper()->writeNodeToTempFile(message.data()); + + parseInternal(message.data(), false); +} + +EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() +{ + +} + +QString EncapsulatedRfc822MessagePart::text() const +{ + return renderInternalText(); +} + +void EncapsulatedRfc822MessagePart::copyContentFrom() const +{ +} + +void EncapsulatedRfc822MessagePart::fix() const +{ +} diff --git a/framework/src/domain/mimetreeparser/otp/messagepart.h b/framework/src/domain/mimetreeparser/otp/messagepart.h new file mode 100644 index 00000000..433f3f6b --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/messagepart.h @@ -0,0 +1,422 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_MESSAGEPART_H__ +#define __MIMETREEPARSER_MESSAGEPART_H__ + +#include "bodypartformatter.h" +#include "util.h" + +#include + +#include +#include +#include + +#include +#include + +class QTextCodec; +class PartPrivate; + +namespace GpgME +{ +class ImportResult; +} + +namespace QGpgME +{ +class Protocol; +} + +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ +class ObjectTreeParser; +class HtmlWriter; +class HTMLBlock; +typedef QSharedPointer HTMLBlockPtr; +class CryptoBodyPartMemento; +class MultiPartAlternativeBodyPartFormatter; +namespace Interface +{ +class ObjectTreeSource; +} + +class MessagePart : public Interface::MessagePart +{ + Q_OBJECT + Q_PROPERTY(bool attachment READ isAttachment) + Q_PROPERTY(bool root READ isRoot) + Q_PROPERTY(bool isHtml READ isHtml) + Q_PROPERTY(bool isHidden READ isHidden) +public: + typedef QSharedPointer Ptr; + MessagePart(ObjectTreeParser *otp, + const QString &text); + + virtual ~MessagePart(); + + virtual QString text() const Q_DECL_OVERRIDE; + void setText(const QString &text); + void setAttachmentFlag(KMime::Content *node); + bool isAttachment() const; + + void setIsRoot(bool root); + bool isRoot() const; + + virtual bool isHtml() const; + virtual bool isHidden() const; + + PartMetaData *partMetaData(); + + /* only a function that should be removed if the refactoring is over */ + virtual void fix() const; + virtual void copyContentFrom() const; + + void appendSubPart(const Interface::MessagePart::Ptr &messagePart); + const QVector &subParts() const; + bool hasSubParts() const; + + HtmlWriter *htmlWriter() const Q_DECL_OVERRIDE; + void setHtmlWriter(HtmlWriter *htmlWriter) const Q_DECL_OVERRIDE; + + Interface::ObjectTreeSource *source() const; + KMime::Content *attachmentNode() const; + +protected: + void parseInternal(KMime::Content *node, bool onlyOneMimePart); + QString renderInternalText() const; + + QString mText; + ObjectTreeParser *mOtp; + PartMetaData mMetaData; + +private: + QVector mBlocks; + + KMime::Content *mAttachmentNode; + bool mRoot; +}; + +class MimeMessagePart : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + MimeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart); + virtual ~MimeMessagePart(); + + QString text() const Q_DECL_OVERRIDE; + + QString plaintextContent() const Q_DECL_OVERRIDE; + QString htmlContent() const Q_DECL_OVERRIDE; +private: + KMime::Content *mNode; + bool mOnlyOneMimePart; + + friend class AlternativeMessagePart; + friend class ::PartPrivate; +}; + +class MessagePartList : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + MessagePartList(MimeTreeParser::ObjectTreeParser *otp); + virtual ~MessagePartList(); + + QString text() const Q_DECL_OVERRIDE; + + QString plaintextContent() const Q_DECL_OVERRIDE; + QString htmlContent() const Q_DECL_OVERRIDE; +private: +}; + +enum IconType { + NoIcon = 0, + IconExternal, + IconInline +}; + +class TextMessagePart : public MessagePartList +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + TextMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage); + virtual ~TextMessagePart(); + + KMMsgSignatureState signatureState() const; + KMMsgEncryptionState encryptionState() const; + + bool decryptMessage() const; + + bool isHidden() const Q_DECL_OVERRIDE; + + bool showLink() const; + bool showTextFrame() const; + +protected: + KMime::Content *mNode; + +private: + void parseContent(); + + KMMsgSignatureState mSignatureState; + KMMsgEncryptionState mEncryptionState; + bool mDrawFrame; + bool mShowLink; + bool mDecryptMessage; + bool mIsHidden; + + friend class DefaultRendererPrivate; + friend class ObjectTreeParser; + friend class ::PartPrivate; +}; + +class AttachmentMessagePart : public TextMessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + AttachmentMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage); + virtual ~AttachmentMessagePart(); + + IconType asIcon() const; + bool neverDisplayInline() const; + void setNeverDisplayInline(bool displayInline); + bool isImage() const; + void setIsImage(bool image); + + bool isHidden() const Q_DECL_OVERRIDE; + +private: + bool mIsImage; + bool mNeverDisplayInline; +}; + +class HtmlMessagePart : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + HtmlMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, MimeTreeParser::Interface::ObjectTreeSource *source); + virtual ~HtmlMessagePart(); + + QString text() const Q_DECL_OVERRIDE; + + void fix() const Q_DECL_OVERRIDE; + bool isHtml() const Q_DECL_OVERRIDE; + +private: + KMime::Content *mNode; + Interface::ObjectTreeSource *mSource; + QString mBodyHTML; + QByteArray mCharset; + + friend class DefaultRendererPrivate; + friend class ::PartPrivate; +}; + +class AlternativeMessagePart : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + AlternativeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode); + virtual ~AlternativeMessagePart(); + + QString text() const Q_DECL_OVERRIDE; + + Util::HtmlMode preferredMode() const; + + bool isHtml() const Q_DECL_OVERRIDE; + + QString plaintextContent() const Q_DECL_OVERRIDE; + QString htmlContent() const Q_DECL_OVERRIDE; + + QList availableModes(); + + void fix() const Q_DECL_OVERRIDE; + void copyContentFrom() const Q_DECL_OVERRIDE; +private: + KMime::Content *mNode; + + Util::HtmlMode mPreferredMode; + + QMap mChildNodes; + QMap mChildParts; + + friend class DefaultRendererPrivate; + friend class ObjectTreeParser; + friend class MultiPartAlternativeBodyPartFormatter; + friend class ::PartPrivate; +}; + +class CertMessagePart : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + CertMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport); + virtual ~CertMessagePart(); + + QString text() const Q_DECL_OVERRIDE; + +private: + KMime::Content *mNode; + bool mAutoImport; + GpgME::ImportResult mImportResult; + const QGpgME::Protocol *mCryptoProto; + friend class DefaultRendererPrivate; +}; + +class EncapsulatedRfc822MessagePart : public MessagePart +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + EncapsulatedRfc822MessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message); + virtual ~EncapsulatedRfc822MessagePart(); + + QString text() const Q_DECL_OVERRIDE; + + void copyContentFrom() const Q_DECL_OVERRIDE; + void fix() const Q_DECL_OVERRIDE; +private: + const KMime::Message::Ptr mMessage; + KMime::Content *mNode; + + friend class DefaultRendererPrivate; +}; + +class EncryptedMessagePart : public MessagePart +{ + Q_OBJECT + Q_PROPERTY(bool decryptMessage READ decryptMessage WRITE setDecryptMessage) + Q_PROPERTY(bool isEncrypted READ isEncrypted) + Q_PROPERTY(bool passphraseError READ passphraseError) +public: + typedef QSharedPointer Ptr; + EncryptedMessagePart(ObjectTreeParser *otp, + const QString &text, + const QGpgME::Protocol *cryptoProto, + const QString &fromAddress, + KMime::Content *node); + + virtual ~EncryptedMessagePart(); + + QString text() const Q_DECL_OVERRIDE; + + void setDecryptMessage(bool decrypt); + bool decryptMessage() const; + + void setIsEncrypted(bool encrypted); + bool isEncrypted() const; + + bool isDecryptable() const; + + bool passphraseError() const; + + void startDecryption(const QByteArray &text, const QTextCodec *aCodec); + void startDecryption(KMime::Content *data = nullptr); + + QByteArray mDecryptedData; + + QString plaintextContent() const Q_DECL_OVERRIDE; + QString htmlContent() const Q_DECL_OVERRIDE; + +private: + /** Handles the dectyptioon of a given content + * returns true if the decryption was successfull + * if used in async mode, check if mMetaData.inProgress is true, it inicates a running decryption process. + */ + bool okDecryptMIME(KMime::Content &data); + +protected: + bool mPassphraseError; + bool mNoSecKey; + const QGpgME::Protocol *mCryptoProto; + QString mFromAddress; + KMime::Content *mNode; + bool mDecryptMessage; + QByteArray mVerifiedText; + std::vector mDecryptRecipients; + + friend class DefaultRendererPrivate; + friend class ::PartPrivate; +}; + +class SignedMessagePart : public MessagePart +{ + Q_OBJECT + Q_PROPERTY(bool isSigned READ isSigned) +public: + typedef QSharedPointer Ptr; + SignedMessagePart(ObjectTreeParser *otp, + const QString &text, + const QGpgME::Protocol *cryptoProto, + const QString &fromAddress, + KMime::Content *node); + + virtual ~SignedMessagePart(); + + void setIsSigned(bool isSigned); + bool isSigned() const; + + void startVerification(const QByteArray &text, const QTextCodec *aCodec); + void startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature); + + QByteArray mDecryptedData; + std::vector mSignatures; + + QString plaintextContent() const Q_DECL_OVERRIDE; + QString htmlContent() const Q_DECL_OVERRIDE; + +private: + /** Handles the verification of data + * If signature is empty it is handled as inline signature otherwise as detached signature mode. + * Returns true if the verfication was successfull and the block is signed. + * If used in async mode, check if mMetaData.inProgress is true, it inicates a running verification process. + */ + bool okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode); + + void sigStatusToMetaData(); + + void setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode); +protected: + const QGpgME::Protocol *mCryptoProto; + QString mFromAddress; + KMime::Content *mNode; + QByteArray mVerifiedText; + + friend EncryptedMessagePart; + friend class DefaultRendererPrivate; + friend class ::PartPrivate; +}; + +} + +#endif //__MIMETREEPARSER_MESSAGEPART_H__ diff --git a/framework/src/domain/mimetreeparser/otp/messagepartrenderer.cpp b/framework/src/domain/mimetreeparser/otp/messagepartrenderer.cpp new file mode 100644 index 00000000..7f622268 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/messagepartrenderer.cpp @@ -0,0 +1,23 @@ +/* + Copyright (C) 2016 Sandro Knauß + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "messagepartrenderer.h" + +MimeTreeParser::Interface::MessagePartRenderer::~MessagePartRenderer() +{ +} diff --git a/framework/src/domain/mimetreeparser/otp/messagepartrenderer.h b/framework/src/domain/mimetreeparser/otp/messagepartrenderer.h new file mode 100644 index 00000000..a90c17e6 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/messagepartrenderer.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2016 Sandro Knauß + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_MESSAGEPARTRENDERER_IF_H__ +#define __MIMETREEPARSER_MESSAGEPARTRENDERER_IF_H__ + +#include + +namespace MimeTreeParser +{ +namespace Interface +{ +/** +* Interface for rendering messageparts to html. +* @author Andras Mantia +*/ +class MessagePartRenderer +{ +public: + typedef QSharedPointer Ptr; + + virtual ~MessagePartRenderer(); + + virtual QString html() const = 0; +}; +} +} +#endif diff --git a/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.cpp b/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.cpp new file mode 100644 index 00000000..f8ac36cd --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.cpp @@ -0,0 +1,3 @@ +#include "mimetreeparser_debug.h" + +Q_LOGGING_CATEGORY(MIMETREEPARSER_LOG, "mimetreeparser") diff --git a/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.h b/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.h new file mode 100644 index 00000000..ddfa6315 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/mimetreeparser_debug.h @@ -0,0 +1,4 @@ +#pragma once + +#include +Q_DECLARE_LOGGING_CATEGORY(MIMETREEPARSER_LOG) diff --git a/framework/src/domain/mimetreeparser/otp/nodehelper.cpp b/framework/src/domain/mimetreeparser/otp/nodehelper.cpp new file mode 100644 index 00000000..8e224f1b --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/nodehelper.cpp @@ -0,0 +1,1069 @@ +/* + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "nodehelper.h" +#include "mimetreeparser_debug.h" +#include "partmetadata.h" +#include "bodypart.h" +#include "attachmenttemporaryfilesdirs.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace MimeTreeParser +{ + +QStringList replySubjPrefixes(QStringList() << QStringLiteral("Re\\s*:") << QStringLiteral("Re\\[\\d+\\]:") << QStringLiteral("Re\\d+:")); +QStringList forwardSubjPrefixes(QStringList() << QStringLiteral("Fwd:") << QStringLiteral("FW:")); + +NodeHelper::NodeHelper() : + mAttachmentFilesDir(new AttachmentTemporaryFilesDirs()) +{ + //TODO(Andras) add methods to modify these prefixes + + mLocalCodec = QTextCodec::codecForLocale(); + + // In the case of Japan. Japanese locale name is "eucjp" but + // The Japanese mail systems normally used "iso-2022-jp" of locale name. + // We want to change locale name from eucjp to iso-2022-jp at KMail only. + + // (Introduction to i18n, 6.6 Limit of Locale technology): + // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP + // is the standard for Internet, and Shift-JIS is the encoding + // for Windows and Macintosh. + if (mLocalCodec) { + const QByteArray codecNameLower = mLocalCodec->name().toLower(); + if (codecNameLower == "eucjp" +#if defined Q_OS_WIN || defined Q_OS_MACX + || codecNameLower == "shift-jis" // OK? +#endif + ) { + mLocalCodec = QTextCodec::codecForName("jis7"); + // QTextCodec *cdc = QTextCodec::codecForName("jis7"); + // QTextCodec::setCodecForLocale(cdc); + // KLocale::global()->setEncoding(cdc->mibEnum()); + } + } +} + +NodeHelper::~NodeHelper() +{ + if (mAttachmentFilesDir) { + mAttachmentFilesDir->forceCleanTempFiles(); + delete mAttachmentFilesDir; + mAttachmentFilesDir = nullptr; + } + clear(); +} + +void NodeHelper::setNodeProcessed(KMime::Content *node, bool recurse) +{ + if (!node) { + return; + } + mProcessedNodes.append(node); + qCDebug(MIMETREEPARSER_LOG) << "Node processed: " << node->index().toString() << node->contentType()->as7BitString(); + //<< " decodedContent" << node->decodedContent(); + if (recurse) { + const auto contents = node->contents(); + for (KMime::Content *c : contents) { + setNodeProcessed(c, true); + } + } +} + +void NodeHelper::setNodeUnprocessed(KMime::Content *node, bool recurse) +{ + if (!node) { + return; + } + mProcessedNodes.removeAll(node); + + //avoid double addition of extra nodes, eg. encrypted attachments + const QMap >::iterator it = mExtraContents.find(node); + if (it != mExtraContents.end()) { + Q_FOREACH (KMime::Content *c, it.value()) { + KMime::Content *p = c->parent(); + if (p) { + p->removeContent(c); + } + } + qDeleteAll(it.value()); + qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); + mExtraContents.erase(it); + } + + qCDebug(MIMETREEPARSER_LOG) << "Node UNprocessed: " << node; + if (recurse) { + const auto contents = node->contents(); + for (KMime::Content *c : contents) { + setNodeUnprocessed(c, true); + } + } +} + +bool NodeHelper::nodeProcessed(KMime::Content *node) const +{ + if (!node) { + return true; + } + return mProcessedNodes.contains(node); +} + +static void clearBodyPartMemento(QMap &bodyPartMementoMap) +{ + for (QMap::iterator + it = bodyPartMementoMap.begin(), end = bodyPartMementoMap.end(); + it != end; ++it) { + Interface::BodyPartMemento *memento = it.value(); + memento->detach(); + delete memento; + } + bodyPartMementoMap.clear(); +} + +void NodeHelper::clear() +{ + mProcessedNodes.clear(); + mEncryptionState.clear(); + mSignatureState.clear(); + mOverrideCodecs.clear(); + std::for_each(mBodyPartMementoMap.begin(), mBodyPartMementoMap.end(), + &clearBodyPartMemento); + mBodyPartMementoMap.clear(); + QMap >::ConstIterator end(mExtraContents.constEnd()); + + for (QMap >::ConstIterator it = mExtraContents.constBegin(); it != end; ++it) { + Q_FOREACH (KMime::Content *c, it.value()) { + KMime::Content *p = c->parent(); + if (p) { + p->removeContent(c); + } + } + qDeleteAll(it.value()); + qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); + } + mExtraContents.clear(); + mDisplayEmbeddedNodes.clear(); + mDisplayHiddenNodes.clear(); +} + +void NodeHelper::setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state) +{ + mEncryptionState[node] = state; +} + +KMMsgEncryptionState NodeHelper::encryptionState(const KMime::Content *node) const +{ + return mEncryptionState.value(node, KMMsgNotEncrypted); +} + +void NodeHelper::setSignatureState(const KMime::Content *node, const KMMsgSignatureState state) +{ + mSignatureState[node] = state; +} + +KMMsgSignatureState NodeHelper::signatureState(const KMime::Content *node) const +{ + return mSignatureState.value(node, KMMsgNotSigned); +} + +PartMetaData NodeHelper::partMetaData(KMime::Content *node) +{ + return mPartMetaDatas.value(node, PartMetaData()); +} + +void NodeHelper::setPartMetaData(KMime::Content *node, const PartMetaData &metaData) +{ + mPartMetaDatas.insert(node, metaData); +} + +QString NodeHelper::writeNodeToTempFile(KMime::Content *node) +{ + // If the message part is already written to a file, no point in doing it again. + // This function is called twice actually, once from the rendering of the attachment + // in the body and once for the header. + QUrl existingFileName = tempFileUrlFromNode(node); + if (!existingFileName.isEmpty()) { + return existingFileName.toLocalFile(); + } + + QString fname = createTempDir(persistentIndex(node)); + if (fname.isEmpty()) { + return QString(); + } + + QString fileName = NodeHelper::fileName(node); + // strip off a leading path + int slashPos = fileName.lastIndexOf(QLatin1Char('/')); + if (-1 != slashPos) { + fileName = fileName.mid(slashPos + 1); + } + if (fileName.isEmpty()) { + fileName = QStringLiteral("unnamed"); + } + fname += QLatin1Char('/') + fileName; + + qCDebug(MIMETREEPARSER_LOG) << "Create temp file: " << fname; + QByteArray data = node->decodedContent(); + if (node->contentType()->isText() && !data.isEmpty()) { + // convert CRLF to LF before writing text attachments to disk + data = KMime::CRLFtoLF(data); + } + QFile f(fname); + if (!f.open(QIODevice::ReadWrite)) { + qCWarning(MIMETREEPARSER_LOG) << "Failed to write note to file:" << f.errorString(); + return QString(); + } + f.write(data); + mAttachmentFilesDir->addTempFile(fname); + // make file read-only so that nobody gets the impression that he might + // edit attached files (cf. bug #52813) + f.setPermissions(QFileDevice::ReadUser); + f.close(); + + return fname; +} + +QUrl NodeHelper::tempFileUrlFromNode(const KMime::Content *node) +{ + if (!node) { + return QUrl(); + } + + const QString index = persistentIndex(node); + + foreach (const QString &path, mAttachmentFilesDir->temporaryFiles()) { + const int right = path.lastIndexOf(QLatin1Char('/')); + int left = path.lastIndexOf(QLatin1String(".index."), right); + if (left != -1) { + left += 7; + } + + QStringRef storedIndex(&path, left, right - left); + if (left != -1 && storedIndex == index) { + return QUrl::fromLocalFile(path); + } + } + return QUrl(); +} + +QString NodeHelper::createTempDir(const QString ¶m) +{ + QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/messageviewer_XXXXXX") + QLatin1String(".index.") + param); + tempFile->open(); + const QString fname = tempFile->fileName(); + delete tempFile; + + QFile fFile(fname); + if (!(fFile.permissions() & QFileDevice::WriteUser)) { + // Not there or not writable + if (!QDir().mkpath(fname) || + !fFile.setPermissions(QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser)) { + return QString(); //failed create + } + } + + Q_ASSERT(!fname.isNull()); + + mAttachmentFilesDir->addTempDir(fname); + return fname; +} + +void NodeHelper::forceCleanTempFiles() +{ + mAttachmentFilesDir->forceCleanTempFiles(); + delete mAttachmentFilesDir; + mAttachmentFilesDir = nullptr; +} + +void NodeHelper::removeTempFiles() +{ + //Don't delete it it will delete in class + mAttachmentFilesDir->removeTempFiles(); + mAttachmentFilesDir = new AttachmentTemporaryFilesDirs(); +} + +void NodeHelper::addTempFile(const QString &file) +{ + mAttachmentFilesDir->addTempFile(file); +} + +bool NodeHelper::isInEncapsulatedMessage(KMime::Content *node) +{ + const KMime::Content *const topLevel = node->topLevel(); + const KMime::Content *cur = node; + while (cur && cur != topLevel) { + const bool parentIsMessage = cur->parent() && cur->parent()->contentType(false) && + cur->parent()->contentType()->mimeType().toLower() == "message/rfc822"; + if (parentIsMessage && cur->parent() != topLevel) { + return true; + } + cur = cur->parent(); + } + return false; +} + +QByteArray NodeHelper::charset(KMime::Content *node) +{ + if (node->contentType(false)) { + return node->contentType(false)->charset(); + } else { + return node->defaultCharset(); + } +} + +KMMsgEncryptionState NodeHelper::overallEncryptionState(KMime::Content *node) const +{ + KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown; + if (!node) { + return myState; + } + + KMime::Content *parent = node->parent(); + auto contents = parent ? parent->contents() : KMime::Content::List(); + if (contents.isEmpty()) { + contents.append(node); + } + int i = contents.indexOf(const_cast(node)); + for (; i < contents.size(); ++i) { + auto next = contents.at(i); + KMMsgEncryptionState otherState = encryptionState(next); + + // NOTE: children are tested ONLY when parent is not encrypted + if (otherState == KMMsgNotEncrypted && !next->contents().isEmpty()) { + otherState = overallEncryptionState(next->contents().at(0)); + } + + if (otherState == KMMsgNotEncrypted && !extraContents(next).isEmpty()) { + otherState = overallEncryptionState(extraContents(next).at(0)); + } + + if (next == node) { + myState = otherState; + } + + switch (otherState) { + case KMMsgEncryptionStateUnknown: + break; + case KMMsgNotEncrypted: + if (myState == KMMsgFullyEncrypted) { + myState = KMMsgPartiallyEncrypted; + } else if (myState != KMMsgPartiallyEncrypted) { + myState = KMMsgNotEncrypted; + } + break; + case KMMsgPartiallyEncrypted: + myState = KMMsgPartiallyEncrypted; + break; + case KMMsgFullyEncrypted: + if (myState != KMMsgFullyEncrypted) { + myState = KMMsgPartiallyEncrypted; + } + break; + case KMMsgEncryptionProblematic: + break; + } + } + + qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgEncryptionState:" << myState; + + return myState; +} + +KMMsgSignatureState NodeHelper::overallSignatureState(KMime::Content *node) const +{ + KMMsgSignatureState myState = KMMsgSignatureStateUnknown; + if (!node) { + return myState; + } + + KMime::Content *parent = node->parent(); + auto contents = parent ? parent->contents() : KMime::Content::List(); + if (contents.isEmpty()) { + contents.append(node); + } + int i = contents.indexOf(const_cast(node)); + for (; i < contents.size(); ++i) { + auto next = contents.at(i); + KMMsgSignatureState otherState = signatureState(next); + + // NOTE: children are tested ONLY when parent is not encrypted + if (otherState == KMMsgNotSigned && !next->contents().isEmpty()) { + otherState = overallSignatureState(next->contents().at(0)); + } + + if (otherState == KMMsgNotSigned && !extraContents(next).isEmpty()) { + otherState = overallSignatureState(extraContents(next).at(0)); + } + + if (next == node) { + myState = otherState; + } + + switch (otherState) { + case KMMsgSignatureStateUnknown: + break; + case KMMsgNotSigned: + if (myState == KMMsgFullySigned) { + myState = KMMsgPartiallySigned; + } else if (myState != KMMsgPartiallySigned) { + myState = KMMsgNotSigned; + } + break; + case KMMsgPartiallySigned: + myState = KMMsgPartiallySigned; + break; + case KMMsgFullySigned: + if (myState != KMMsgFullySigned) { + myState = KMMsgPartiallySigned; + } + break; + case KMMsgSignatureProblematic: + break; + } + } + + qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgSignatureState:" << myState; + + return myState; +} + +void NodeHelper::magicSetType(KMime::Content *node, bool aAutoDecode) +{ + const QByteArray body = (aAutoDecode) ? node->decodedContent() : node->body(); + QMimeDatabase db; + QMimeType mime = db.mimeTypeForData(body); + + QString mimetype = mime.name(); + node->contentType()->setMimeType(mimetype.toLatin1()); +} + +// static +QString NodeHelper::replacePrefixes(const QString &str, + const QStringList &prefixRegExps, + bool replace, + const QString &newPrefix) +{ + bool recognized = false; + // construct a big regexp that + // 1. is anchored to the beginning of str (sans whitespace) + // 2. matches at least one of the part regexps in prefixRegExps + QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*") + .arg(prefixRegExps.join(QStringLiteral(")|(?:"))); + QRegExp rx(bigRegExp, Qt::CaseInsensitive); + if (!rx.isValid()) { + qCWarning(MIMETREEPARSER_LOG) << "bigRegExp = \"" + << bigRegExp << "\"\n" + << "prefix regexp is invalid!"; + // try good ole Re/Fwd: + recognized = str.startsWith(newPrefix); + } else { // valid rx + QString tmp = str; + if (rx.indexIn(tmp) == 0) { + recognized = true; + if (replace) { + return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1Char(' ')); + } + } + } + if (!recognized) { + return newPrefix + QLatin1Char(' ') + str; + } else { + return str; + } +} + +QString NodeHelper::cleanSubject(KMime::Message *message) +{ + return cleanSubject(message, replySubjPrefixes + forwardSubjPrefixes, + true, QString()).trimmed(); +} + +QString NodeHelper::cleanSubject(KMime::Message *message, + const QStringList &prefixRegExps, + bool replace, + const QString &newPrefix) +{ + QString cleanStr; + if (message) { + cleanStr = + NodeHelper::replacePrefixes( + message->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix); + } + return cleanStr; +} + +void NodeHelper::setOverrideCodec(KMime::Content *node, const QTextCodec *codec) +{ + if (!node) { + return; + } + + mOverrideCodecs[node] = codec; +} + +const QTextCodec *NodeHelper::codec(KMime::Content *node) +{ + if (! node) { + return mLocalCodec; + } + + const QTextCodec *c = mOverrideCodecs.value(node, nullptr); + if (!c) { + // no override-codec set for this message, try the CT charset parameter: + QByteArray charset = node->contentType()->charset(); + + // utf-8 is a superset of us-ascii, so we don't loose anything, if we it insead + // utf-8 is nowadays that widely, that it is a good guess to use it to fix issus with broken clients. + if (charset.toLower() == "us-ascii") { + charset = "utf-8"; + } + c = codecForName(charset); + } + if (!c) { + // no charset means us-ascii (RFC 2045), so using local encoding should + // be okay + c = mLocalCodec; + } + return c; +} + +const QTextCodec *NodeHelper::codecForName(const QByteArray &_str) +{ + if (_str.isEmpty()) { + return nullptr; + } + QByteArray codec = _str.toLower(); + return KCharsets::charsets()->codecForName(QLatin1String(codec)); +} + +QString NodeHelper::fileName(const KMime::Content *node) +{ + QString name = const_cast(node)->contentDisposition()->filename(); + if (name.isEmpty()) { + name = const_cast(node)->contentType()->name(); + } + + name = name.trimmed(); + return name; +} + +//FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalBodyPartMemento replacement +Interface::BodyPartMemento *NodeHelper::bodyPartMemento(KMime::Content *node, + const QByteArray &which) const +{ + const QMap< QString, QMap >::const_iterator nit + = mBodyPartMementoMap.find(persistentIndex(node)); + if (nit == mBodyPartMementoMap.end()) { + return nullptr; + } + const QMap::const_iterator it = + nit->find(which.toLower()); + return it != nit->end() ? it.value() : nullptr; +} + +//FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalSetBodyPartMemento replacement +void NodeHelper::setBodyPartMemento(KMime::Content *node, const QByteArray &which, + Interface::BodyPartMemento *memento) +{ + QMap &mementos + = mBodyPartMementoMap[persistentIndex(node)]; + + const QByteArray whichLower = which.toLower(); + const QMap::iterator it = + mementos.lowerBound(whichLower); + + if (it != mementos.end() && it.key() == whichLower) { + delete it.value(); + if (memento) { + it.value() = memento; + } else { + mementos.erase(it); + } + } else { + mementos.insert(whichLower, memento); + } +} + +bool NodeHelper::isNodeDisplayedEmbedded(KMime::Content *node) const +{ + qCDebug(MIMETREEPARSER_LOG) << "IS NODE: " << mDisplayEmbeddedNodes.contains(node); + return mDisplayEmbeddedNodes.contains(node); +} + +void NodeHelper::setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded) +{ + qCDebug(MIMETREEPARSER_LOG) << "SET NODE: " << node << displayedEmbedded; + if (displayedEmbedded) { + mDisplayEmbeddedNodes.insert(node); + } else { + mDisplayEmbeddedNodes.remove(node); + } +} + +bool NodeHelper::isNodeDisplayedHidden(KMime::Content *node) const +{ + return mDisplayHiddenNodes.contains(node); +} + +void NodeHelper::setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden) +{ + if (displayedHidden) { + mDisplayHiddenNodes.insert(node); + } else { + mDisplayEmbeddedNodes.remove(node); + } +} + +/*! + Creates a persistent index string that bridges the gap between the + permanent nodes and the temporary ones. + + Used internally for robust indexing. +*/ +QString NodeHelper::persistentIndex(const KMime::Content *node) const +{ + if (!node) { + return QString(); + } + + QString indexStr = node->index().toString(); + if (indexStr.isEmpty()) { + QMapIterator > it(mExtraContents); + while (it.hasNext()) { + it.next(); + const auto &extraNodes = it.value(); + for (int i = 0; i < extraNodes.size(); i++) { + if (extraNodes[i] == node) { + indexStr = QString::fromLatin1("e%1").arg(i); + const QString parentIndex = persistentIndex(it.key()); + if (!parentIndex.isEmpty()) { + indexStr = QString::fromLatin1("%1:%2").arg(parentIndex, indexStr); + } + return indexStr; + } + } + } + } else { + const KMime::Content *const topLevel = node->topLevel(); + //if the node is an extra node, prepend the index of the extra node to the url + QMapIterator > it(mExtraContents); + while (it.hasNext()) { + it.next(); + const QList &extraNodes = extraContents(it.key()); + for (int i = 0; i < extraNodes.size(); ++i) { + KMime::Content *const extraNode = extraNodes[i]; + if (topLevel == extraNode) { + indexStr.prepend(QStringLiteral("e%1:").arg(i)); + const QString parentIndex = persistentIndex(it.key()); + if (!parentIndex.isEmpty()) { + indexStr = QStringLiteral("%1:%2").arg(parentIndex, indexStr); + } + return indexStr; + } + } + } + } + + return indexStr; +} + +KMime::Content *NodeHelper::contentFromIndex(KMime::Content *node, const QString &persistentIndex) const +{ + KMime::Content *c = node->topLevel(); + if (c) { + const QStringList pathParts = persistentIndex.split(QLatin1Char(':'), QString::SkipEmptyParts); + const int pathPartsSize(pathParts.size()); + for (int i = 0; i < pathPartsSize; ++i) { + const QString &path = pathParts[i]; + if (path.startsWith(QLatin1Char('e'))) { + const QList &extraParts = mExtraContents.value(c); + const int idx = path.midRef(1, -1).toInt(); + c = (idx < extraParts.size()) ? extraParts[idx] : nullptr; + } else { + c = c->content(KMime::ContentIndex(path)); + } + if (!c) { + break; + } + } + } + return c; +} + +QString NodeHelper::asHREF(const KMime::Content *node, const QString &place) const +{ + return QStringLiteral("attachment:%1?place=%2").arg(persistentIndex(node), place); +} + +KMime::Content *NodeHelper::fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &url) const +{ + if (url.isEmpty()) { + return mMessage.data(); + } + + if (!url.isLocalFile()) { + return contentFromIndex(mMessage.data(), url.adjusted(QUrl::StripTrailingSlash).path()); + } else { + const QString path = url.toLocalFile(); + // extract from //qttestn28554.index.2.3:0:2/unnamed -> "2.3:0:2" + // start of the index is something that is not a number followed by a dot: \D. + // index is only made of numbers,"." and ":": ([0-9.:]+) + // index is the last part of the folder name: / + const QRegExp rIndex(QStringLiteral("\\D\\.([e0-9.:]+)/")); + + //search the occurence at most at the end + if (rIndex.lastIndexIn(path) != -1) { + return contentFromIndex(mMessage.data(), rIndex.cap(1)); + } + return mMessage.data(); + } +} + +QString NodeHelper::fixEncoding(const QString &encoding) +{ + QString returnEncoding = encoding; + // According to http://www.iana.org/assignments/character-sets, uppercase is + // preferred in MIME headers + const QString returnEncodingToUpper = returnEncoding.toUpper(); + if (returnEncodingToUpper.contains(QStringLiteral("ISO "))) { + returnEncoding = returnEncodingToUpper; + returnEncoding.replace(QLatin1String("ISO "), QStringLiteral("ISO-")); + } + return returnEncoding; +} + +//----------------------------------------------------------------------------- +QString NodeHelper::encodingForName(const QString &descriptiveName) +{ + QString encoding = KCharsets::charsets()->encodingForName(descriptiveName); + return NodeHelper::fixEncoding(encoding); +} + +QStringList NodeHelper::supportedEncodings(bool usAscii) +{ + QStringList encodingNames = KCharsets::charsets()->availableEncodingNames(); + QStringList encodings; + QMap mimeNames; + QStringList::ConstIterator constEnd(encodingNames.constEnd()); + for (QStringList::ConstIterator it = encodingNames.constBegin(); + it != constEnd; ++it) { + QTextCodec *codec = KCharsets::charsets()->codecForName(*it); + QString mimeName = (codec) ? QString::fromLatin1(codec->name()).toLower() : (*it); + if (!mimeNames.contains(mimeName)) { + encodings.append(KCharsets::charsets()->descriptionForEncoding(*it)); + mimeNames.insert(mimeName, true); + } + } + encodings.sort(); + if (usAscii) { + encodings.prepend(KCharsets::charsets()->descriptionForEncoding(QStringLiteral("us-ascii"))); + } + return encodings; +} + +QString NodeHelper::fromAsString(KMime::Content *node) const +{ + if (auto topLevel = dynamic_cast(node->topLevel())) { + return topLevel->from()->asUnicodeString(); + } else { + auto realNode = std::find_if(mExtraContents.cbegin(), mExtraContents.cend(), + [node](const QList &nodes) { + return nodes.contains(node); + }); + if (realNode != mExtraContents.cend()) { + return fromAsString(realNode.key()); + } + } + + return QString(); +} + +void NodeHelper::attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content) +{ + qCDebug(MIMETREEPARSER_LOG) << "mExtraContents added for" << topLevelNode << " extra content: " << content; + mExtraContents[topLevelNode].append(content); +} + +QList< KMime::Content * > NodeHelper::extraContents(KMime::Content *topLevelnode) const +{ + return mExtraContents.value(topLevelnode); +} + +void NodeHelper::mergeExtraNodes(KMime::Content *node) +{ + if (!node) { + return; + } + + const QList extraNodes = extraContents(node); + for (KMime::Content *extra : extraNodes) { + if (node->bodyIsMessage()) { + qCWarning(MIMETREEPARSER_LOG) << "Asked to attach extra content to a kmime::message, this does not make sense. Attaching to:" << node << + node->encodedContent() << "\n====== with =======\n" << extra << extra->encodedContent(); + continue; + } + KMime::Content *c = new KMime::Content(node); + c->setContent(extra->encodedContent()); + c->parse(); + node->addContent(c); + } + + Q_FOREACH (KMime::Content *child, node->contents()) { + mergeExtraNodes(child); + } +} + +void NodeHelper::cleanFromExtraNodes(KMime::Content *node) +{ + if (!node) { + return; + } + const QList extraNodes = extraContents(node); + for (KMime::Content *extra : extraNodes) { + QByteArray s = extra->encodedContent(); + const auto children = node->contents(); + for (KMime::Content *c : children) { + if (c->encodedContent() == s) { + node->removeContent(c); + } + } + } + Q_FOREACH (KMime::Content *child, node->contents()) { + cleanFromExtraNodes(child); + } +} + +KMime::Message *NodeHelper::messageWithExtraContent(KMime::Content *topLevelNode) +{ + /*The merge is done in several steps: + 1) merge the extra nodes into topLevelNode + 2) copy the modified (merged) node tree into a new node tree + 3) restore the original node tree in topLevelNode by removing the extra nodes from it + + The reason is that extra nodes are assigned by pointer value to the nodes in the original tree. + */ + if (!topLevelNode) { + return nullptr; + } + + mergeExtraNodes(topLevelNode); + + KMime::Message *m = new KMime::Message; + m->setContent(topLevelNode->encodedContent()); + m->parse(); + + cleanFromExtraNodes(topLevelNode); +// qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITH EXTRA: " << m->encodedContent(); +// qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITHOUT EXTRA: " << topLevelNode->encodedContent(); + + return m; +} + +KMime::Content *NodeHelper::decryptedNodeForContent(KMime::Content *content) const +{ + const QList xc = extraContents(content); + if (!xc.empty()) { + if (xc.size() == 1) { + return xc.front(); + } else { + qCWarning(MIMETREEPARSER_LOG) << "WTF, encrypted node has multiple extra contents?"; + } + } + return nullptr; +} + +bool NodeHelper::unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders, + int recursionLevel) +{ + bool returnValue = false; + if (node) { + KMime::Content *curNode = node; + KMime::Content *decryptedNode = nullptr; + const QByteArray type = node->contentType(false) ? QByteArray(node->contentType()->mediaType()).toLower() : "text"; + const QByteArray subType = node->contentType(false) ? node->contentType()->subType().toLower() : "plain"; + const bool isMultipart = node->contentType(false) && node->contentType()->isMultipart(); + bool isSignature = false; + + qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") Looking at" << type << "/" << subType; + + if (isMultipart) { + if (subType == "signed") { + isSignature = true; + } else if (subType == "encrypted") { + decryptedNode = decryptedNodeForContent(curNode); + } + } else if (type == "application") { + if (subType == "octet-stream") { + decryptedNode = decryptedNodeForContent(curNode); + } else if (subType == "pkcs7-signature") { + isSignature = true; + } else if (subType == "pkcs7-mime") { + // note: subtype pkcs7-mime can also be signed + // and we do NOT want to remove the signature! + if (encryptionState(curNode) != KMMsgNotEncrypted) { + decryptedNode = decryptedNodeForContent(curNode); + } + } + } + + if (decryptedNode) { + qCDebug(MIMETREEPARSER_LOG) << "Current node has an associated decrypted node, adding a modified header " + "and then processing the children."; + + Q_ASSERT(addHeaders); + KMime::Content headers; + headers.setHead(curNode->head()); + headers.parse(); + if (decryptedNode->contentType(false)) { + headers.contentType()->from7BitString(decryptedNode->contentType()->as7BitString(false)); + } else { + headers.removeHeader(); + } + if (decryptedNode->contentTransferEncoding(false)) { + headers.contentTransferEncoding()->from7BitString(decryptedNode->contentTransferEncoding()->as7BitString(false)); + } else { + headers.removeHeader(); + } + if (decryptedNode->contentDisposition(false)) { + headers.contentDisposition()->from7BitString(decryptedNode->contentDisposition()->as7BitString(false)); + } else { + headers.removeHeader(); + } + if (decryptedNode->contentDescription(false)) { + headers.contentDescription()->from7BitString(decryptedNode->contentDescription()->as7BitString(false)); + } else { + headers.removeHeader(); + } + headers.assemble(); + + resultingData += headers.head() + '\n'; + unencryptedMessage_helper(decryptedNode, resultingData, false, recursionLevel + 1); + + returnValue = true; + } + + else if (isSignature) { + qCDebug(MIMETREEPARSER_LOG) << "Current node is a signature, adding it as-is."; + // We can't change the nodes under the signature, as that would invalidate it. Add the signature + // and its child as-is + if (addHeaders) { + resultingData += curNode->head() + '\n'; + } + resultingData += curNode->encodedBody(); + returnValue = false; + } + + else if (isMultipart) { + qCDebug(MIMETREEPARSER_LOG) << "Current node is a multipart node, adding its header and then processing all children."; + // Normal multipart node, add the header and all of its children + bool somethingChanged = false; + if (addHeaders) { + resultingData += curNode->head() + '\n'; + } + const QByteArray boundary = curNode->contentType()->boundary(); + foreach (KMime::Content *child, curNode->contents()) { + resultingData += "\n--" + boundary + '\n'; + const bool changed = unencryptedMessage_helper(child, resultingData, true, recursionLevel + 1); + if (changed) { + somethingChanged = true; + } + } + resultingData += "\n--" + boundary + "--\n\n"; + returnValue = somethingChanged; + } + + else if (curNode->bodyIsMessage()) { + qCDebug(MIMETREEPARSER_LOG) << "Current node is a message, adding the header and then processing the child."; + if (addHeaders) { + resultingData += curNode->head() + '\n'; + } + + returnValue = unencryptedMessage_helper(curNode->bodyAsMessage().data(), resultingData, true, recursionLevel + 1); + } + + else { + qCDebug(MIMETREEPARSER_LOG) << "Current node is an ordinary leaf node, adding it as-is."; + if (addHeaders) { + resultingData += curNode->head() + '\n'; + } + resultingData += curNode->body(); + returnValue = false; + } + } + + qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") done."; + return returnValue; +} + +KMime::Message::Ptr NodeHelper::unencryptedMessage(const KMime::Message::Ptr &originalMessage) +{ + QByteArray resultingData; + const bool messageChanged = unencryptedMessage_helper(originalMessage.data(), resultingData, true); + if (messageChanged) { +#if 0 + qCDebug(MIMETREEPARSER_LOG) << "Resulting data is:" << resultingData; + QFile bla("stripped.mbox"); + bla.open(QIODevice::WriteOnly); + bla.write(resultingData); + bla.close(); +#endif + KMime::Message::Ptr newMessage(new KMime::Message); + newMessage->setContent(resultingData); + newMessage->parse(); + return newMessage; + } else { + return KMime::Message::Ptr(); + } +} + +QVector NodeHelper::attachmentsOfExtraContents() const +{ + QVector result; + for (auto it = mExtraContents.begin(); it != mExtraContents.end(); ++it) { + foreach (auto content, it.value()) { + if (KMime::isAttachment(content)) { + result.push_back(content); + } else { + result += content->attachments(); + } + } + } + return result; +} + +} diff --git a/framework/src/domain/mimetreeparser/otp/nodehelper.h b/framework/src/domain/mimetreeparser/otp/nodehelper.h new file mode 100644 index 00000000..70253f21 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/nodehelper.h @@ -0,0 +1,290 @@ +/* + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_NODEHELPER_H__ +#define __MIMETREEPARSER_NODEHELPER_H__ + +#include "partmetadata.h" +#include "enums.h" + +#include + +#include +#include +#include + +class QUrl; +class QTextCodec; + +namespace MimeTreeParser +{ +class AttachmentTemporaryFilesDirs; +namespace Interface +{ +class BodyPartMemento; +} +} + +namespace MimeTreeParser +{ + +/** + * @author Andras Mantia + */ +class NodeHelper: public QObject +{ + Q_OBJECT +public: + NodeHelper(); + + ~NodeHelper(); + + void setNodeProcessed(KMime::Content *node, bool recurse); + void setNodeUnprocessed(KMime::Content *node, bool recurse); + bool nodeProcessed(KMime::Content *node) const; + void clear(); + void forceCleanTempFiles(); + + void setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state); + KMMsgEncryptionState encryptionState(const KMime::Content *node) const; + + void setSignatureState(const KMime::Content *node, const KMMsgSignatureState state); + KMMsgSignatureState signatureState(const KMime::Content *node) const; + + KMMsgSignatureState overallSignatureState(KMime::Content *node) const; + KMMsgEncryptionState overallEncryptionState(KMime::Content *node) const; + + void setPartMetaData(KMime::Content *node, const PartMetaData &metaData); + PartMetaData partMetaData(KMime::Content *node); + + /** + * Set the 'Content-Type' by mime-magic from the contents of the body. + * If autoDecode is true the decoded body will be used for mime type + * determination (this does not change the body itself). + */ + void magicSetType(KMime::Content *node, bool autoDecode = true); + + /** + * Return this mails subject, with all "forward" and "reply" + * prefixes removed + */ + static QString cleanSubject(KMime::Message *message); + + /** Attach an extra node to an existing node */ + void attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content); + + /** Get the extra nodes attached to the @param topLevelNode and all sub-nodes of @param topLevelNode */ + QList extraContents(KMime::Content *topLevelNode) const; + + /** Return a modified message (node tree) starting from @param topLevelNode that has the original nodes and the extra nodes. + The caller has the responsibility to delete the new message. + */ + KMime::Message *messageWithExtraContent(KMime::Content *topLevelNode); + + /** Get a QTextCodec suitable for this message part */ + const QTextCodec *codec(KMime::Content *node); + + /** Set the charset the user selected for the message to display */ + void setOverrideCodec(KMime::Content *node, const QTextCodec *codec); + + Interface::BodyPartMemento *bodyPartMemento(KMime::Content *node, const QByteArray &which) const; + + void setBodyPartMemento(KMime::Content *node, const QByteArray &which, + Interface::BodyPartMemento *memento); + + // A flag to remember if the node was embedded. This is useful for attachment nodes, the reader + // needs to know if they were displayed inline or not. + bool isNodeDisplayedEmbedded(KMime::Content *node) const; + void setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded); + + // Same as above, but this time determines if the node was hidden or not + bool isNodeDisplayedHidden(KMime::Content *node) const; + void setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden); + + /** + * Writes the given message part to a temporary file and returns the + * name of this file or QString() if writing failed. + */ + QString writeNodeToTempFile(KMime::Content *node); + + /** + * Returns the temporary file path and name where this node was saved, or an empty url + * if it wasn't saved yet with writeNodeToTempFile() + */ + QUrl tempFileUrlFromNode(const KMime::Content *node); + + /** + * Creates a temporary dir for saving attachments, etc. + * Will be automatically deleted when another message is viewed. + * @param param Optional part of the directory name. + */ + QString createTempDir(const QString ¶m = QString()); + + /** + * Cleanup the attachment temp files + */ + void removeTempFiles(); + + /** + * Add a file to the list of managed temporary files + */ + void addTempFile(const QString &file); + + // Get a href in the form attachment:?place=, used by ObjectTreeParser and + // UrlHandlerManager. + QString asHREF(const KMime::Content *node, const QString &place) const; + KMime::Content *fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &href) const; + + /** + * @return true if this node is a child or an encapsulated message + */ + static bool isInEncapsulatedMessage(KMime::Content *node); + + /** + * Returns the charset for the given node. If no charset is specified + * for the node, the defaultCharset() is returned. + */ + static QByteArray charset(KMime::Content *node); + + /** + * Check for prefixes @p prefixRegExps in @p str. If none + * is found, @p newPrefix + ' ' is prepended to @p str and the + * resulting string is returned. If @p replace is true, any + * sequence of whitespace-delimited prefixes at the beginning of + * @p str is replaced by @p newPrefix. + */ + static QString replacePrefixes(const QString &str, + const QStringList &prefixRegExps, + bool replace, + const QString &newPrefix); + + /** + * Return a QTextCodec for the specified charset. + * This function is a bit more tolerant, than QTextCodec::codecForName + */ + static const QTextCodec *codecForName(const QByteArray &_str); + + /** + * Returns a usable filename for a node, that can be the filename from the + * content disposition header, or if that one is empty, the name from the + * content type header. + */ + static QString fileName(const KMime::Content *node); + + /** + * Fixes an encoding received by a KDE function and returns the proper, + * MIME-compilant encoding name instead. + * @see encodingForName + */ + static QString fixEncoding(const QString &encoding); //TODO(Andras) move to a utility class? + + /** + * Drop-in replacement for KCharsets::encodingForName(). The problem with + * the KCharsets function is that it returns "human-readable" encoding names + * like "ISO 8859-15" instead of valid encoding names like "ISO-8859-15". + * This function fixes this by replacing whitespace with a hyphen. + */ + static QString encodingForName(const QString &descriptiveName); //TODO(Andras) move to a utility class? + + /** + * Return a list of the supported encodings + * @param usAscii if true, US-Ascii encoding will be prepended to the list. + */ + static QStringList supportedEncodings(bool usAscii); //TODO(Andras) move to a utility class? + + QString fromAsString(KMime::Content *node) const; + + KMime::Content *decryptedNodeForContent(KMime::Content *content) const; + + /** + * This function returns the unencrypted message that is based on @p originalMessage. + * All encrypted MIME parts are removed and replaced by their decrypted plain-text versions. + * Encrypted parts that are within signed parts are not replaced, since that would invalidate + * the signature. + * + * This only works if the message was run through ObjectTreeParser::parseObjectTree() with the + * currrent NodeHelper before, because parseObjectTree() actually decrypts the message and stores + * the decrypted nodes by calling attachExtraContent(). + * + * @return the unencrypted message or an invalid pointer if the original message didn't contain + * a part that needed to be modified. + */ + KMime::Message::Ptr unencryptedMessage(const KMime::Message::Ptr &originalMessage); + + /** + * Returns a list of attachments of attached extra content nodes. + * This is mainly useful is order to get attachments of encrypted messages. + * Note that this does not include attachments from the primary node tree. + * @see KMime::Content::attachments(). + */ + QVector attachmentsOfExtraContents() const; + +Q_SIGNALS: + void update(MimeTreeParser::UpdateMode); + +private: + Q_DISABLE_COPY(NodeHelper) + bool unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders, + int recursionLevel = 1); + + /** Check for prefixes @p prefixRegExps in #subject(). If none + is found, @p newPrefix + ' ' is prepended to the subject and the + resulting string is returned. If @p replace is true, any + sequence of whitespace-delimited prefixes at the beginning of + #subject() is replaced by @p newPrefix + **/ + static QString cleanSubject(KMime::Message *message, const QStringList &prefixRegExps, + bool replace, const QString &newPrefix); + + void mergeExtraNodes(KMime::Content *node); + void cleanFromExtraNodes(KMime::Content *node); + + /** Creates a persistent index string that bridges the gap between the + permanent nodes and the temporary ones. + + Used internally for robust indexing. + **/ + QString persistentIndex(const KMime::Content *node) const; + + /** Translates the persistentIndex into a node back + + node: any node of the actually message to what the persistentIndex is interpreded + **/ + KMime::Content *contentFromIndex(KMime::Content *node, const QString &persistentIndex) const; + +private: + QList mProcessedNodes; + QList mNodesUnderProcess; + QMap mEncryptionState; + QMap mSignatureState; + QSet mDisplayEmbeddedNodes; + QSet mDisplayHiddenNodes; + QTextCodec *mLocalCodec; + QMap mOverrideCodecs; + QMap > mBodyPartMementoMap; + QMap mPartMetaDatas; + QMap > mExtraContents; + AttachmentTemporaryFilesDirs *mAttachmentFilesDir; + + friend class NodeHelperTest; +}; + +} + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp b/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp new file mode 100644 index 00000000..b0d514b6 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp @@ -0,0 +1,495 @@ +/* + objecttreeparser.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + Copyright (C) 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + Copyright (c) 2015 Sandro Knauß + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +// MessageViewer includes + +#include "objecttreeparser.h" + +#include "attachmentstrategy.h" +#include "bodypartformatterbasefactory.h" +#include "nodehelper.h" +#include "messagepart.h" +#include "partnodebodypart.h" + +#include "mimetreeparser_debug.h" + +#include "utils.h" +#include "bodypartformatter.h" +#include "htmlwriter.h" +#include "messagepartrenderer.h" +#include "util.h" + +#include +#include + +// KDE includes + +// Qt includes +#include +#include +#include + +using namespace MimeTreeParser; + +ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser, + bool showOnlyOneMimePart, + const AttachmentStrategy *strategy) + : mSource(topLevelParser->mSource), + mNodeHelper(topLevelParser->mNodeHelper), + mHtmlWriter(topLevelParser->mHtmlWriter), + mTopLevelContent(topLevelParser->mTopLevelContent), + mShowOnlyOneMimePart(showOnlyOneMimePart), + mHasPendingAsyncJobs(false), + mAllowAsync(topLevelParser->mAllowAsync), + mAttachmentStrategy(strategy) +{ + init(); +} + +ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source, + MimeTreeParser::NodeHelper *nodeHelper, + bool showOnlyOneMimePart, + const AttachmentStrategy *strategy) + : mSource(source), + mNodeHelper(nodeHelper), + mHtmlWriter(nullptr), + mTopLevelContent(nullptr), + mShowOnlyOneMimePart(showOnlyOneMimePart), + mHasPendingAsyncJobs(false), + mAllowAsync(false), + mAttachmentStrategy(strategy) +{ + init(); +} + +void ObjectTreeParser::init() +{ + Q_ASSERT(mSource); + if (!attachmentStrategy()) { + mAttachmentStrategy = mSource->attachmentStrategy(); + } + + if (!mNodeHelper) { + mNodeHelper = new NodeHelper(); + mDeleteNodeHelper = true; + } else { + mDeleteNodeHelper = false; + } +} + +ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other) + : mSource(other.mSource), + mNodeHelper(other.nodeHelper()), //TODO(Andras) hm, review what happens if mDeleteNodeHelper was true in the source + mHtmlWriter(other.mHtmlWriter), + mTopLevelContent(other.mTopLevelContent), + mShowOnlyOneMimePart(other.showOnlyOneMimePart()), + mHasPendingAsyncJobs(other.hasPendingAsyncJobs()), + mAllowAsync(other.allowAsync()), + mAttachmentStrategy(other.attachmentStrategy()), + mDeleteNodeHelper(false) +{ + +} + +ObjectTreeParser::~ObjectTreeParser() +{ + if (mDeleteNodeHelper) { + delete mNodeHelper; + mNodeHelper = nullptr; + } +} + +void ObjectTreeParser::setAllowAsync(bool allow) +{ + Q_ASSERT(!mHasPendingAsyncJobs); + mAllowAsync = allow; +} + +bool ObjectTreeParser::allowAsync() const +{ + return mAllowAsync; +} + +bool ObjectTreeParser::hasPendingAsyncJobs() const +{ + return mHasPendingAsyncJobs; +} + +QString ObjectTreeParser::plainTextContent() const +{ + return mPlainTextContent; +} + +QString ObjectTreeParser::htmlContent() const +{ + return mHtmlContent; +} + +void ObjectTreeParser::copyContentFrom(const ObjectTreeParser *other) +{ + mPlainTextContent += other->plainTextContent(); + mHtmlContent += other->htmlContent(); + if (!other->plainTextContentCharset().isEmpty()) { + mPlainTextContentCharset = other->plainTextContentCharset(); + } + if (!other->htmlContentCharset().isEmpty()) { + mHtmlContentCharset = other->htmlContentCharset(); + } +} + +//----------------------------------------------------------------------------- + +void ObjectTreeParser::parseObjectTree(KMime::Content *node) +{ + mTopLevelContent = node; + mParsedPart = parseObjectTreeInternal(node, showOnlyOneMimePart()); + + if (mParsedPart) { + mParsedPart->fix(); + mParsedPart->copyContentFrom(); + if (auto mp = toplevelTextNode(mParsedPart)) { + if (auto _mp = mp.dynamicCast()) { + extractNodeInfos(_mp->mNode, true); + } else if (auto _mp = mp.dynamicCast()) { + if (_mp->mChildNodes.contains(Util::MultipartPlain)) { + extractNodeInfos(_mp->mChildNodes[Util::MultipartPlain], true); + } + } + setPlainTextContent(mp->text()); + } + + if (htmlWriter()) { + const auto renderer = mSource->messagePartTheme(mParsedPart); + if (renderer) { + mHtmlWriter->queue(renderer->html()); + } + } + } +} + +MessagePartPtr ObjectTreeParser::parsedPart() const +{ + return mParsedPart; +} + +bool ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart) +{ + bool bRendered = false; + const auto sub = mSource->bodyPartFormatterFactory()->subtypeRegistry(mediaType.constData()); + auto range = sub.equal_range(subType.constData()); + for (auto it = range.first; it != range.second; ++it) { + const auto formatter = (*it).second; + if (!formatter) { + continue; + } + PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper); + // Set the default display strategy for this body part relying on the + // identity of Interface::BodyPart::Display and AttachmentStrategy::Display + part.setDefaultDisplay((Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay(node)); + + mNodeHelper->setNodeDisplayedEmbedded(node, true); + + const Interface::MessagePart::Ptr result = formatter->process(part); + if (!result) { + continue; + } + + if (const auto mp = result.dynamicCast()) { + mp->setAttachmentFlag(node); + mpRet = result; + bRendered = true; + break; + } else if (dynamic_cast(result.data())) { + QObject *asyncResultObserver = allowAsync() ? mSource->sourceObject() : nullptr; + const auto r = formatter->format(&part, result->htmlWriter(), asyncResultObserver); + if (r == Interface::BodyPartFormatter::AsIcon) { + processResult.setNeverDisplayInline(true); + formatter->adaptProcessResult(processResult); + mNodeHelper->setNodeDisplayedEmbedded(node, false); + const Interface::MessagePart::Ptr mp = defaultHandling(node, processResult, onlyOneMimePart); + if (mp) { + if (auto _mp = mp.dynamicCast()) { + _mp->setAttachmentFlag(node); + } + mpRet = mp; + } + bRendered = true; + break; + } else if (r == Interface::BodyPartFormatter::Ok) { + processResult.setNeverDisplayInline(true); + formatter->adaptProcessResult(processResult); + mpRet = result; + bRendered = true; + break; + } + continue; + } else { + continue; + } + } + return bRendered; +} + +MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart) +{ + if (!node) { + return MessagePart::Ptr(); + } + + // reset pending async jobs state (we'll rediscover pending jobs as we go) + mHasPendingAsyncJobs = false; + + // reset "processed" flags for... + if (onlyOneMimePart) { + // ... this node and all descendants + mNodeHelper->setNodeUnprocessed(node, false); + if (!node->contents().isEmpty()) { + mNodeHelper->setNodeUnprocessed(node, true); + } + } else if (!node->parent()) { + // ...this node and all it's siblings and descendants + mNodeHelper->setNodeUnprocessed(node, true); + } + + const bool isRoot = node->isTopLevel(); + auto parsedPart = MessagePart::Ptr(new MessagePartList(this)); + parsedPart->setIsRoot(isRoot); + KMime::Content *parent = node->parent(); + auto contents = parent ? parent->contents() : KMime::Content::List(); + if (contents.isEmpty()) { + contents.append(node); + } + int i = contents.indexOf(const_cast(node)); + for (; i < contents.size(); ++i) { + node = contents.at(i); + if (mNodeHelper->nodeProcessed(node)) { + continue; + } + + ProcessResult processResult(mNodeHelper); + + QByteArray mediaType("text"); + QByteArray subType("plain"); + if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() && + !node->contentType()->subType().isEmpty()) { + mediaType = node->contentType()->mediaType(); + subType = node->contentType()->subType(); + } + + Interface::MessagePartPtr mp; + if (processType(node, processResult, mediaType, subType, mp, onlyOneMimePart)) { + if (mp) { + parsedPart->appendSubPart(mp); + } + } else if (processType(node, processResult, mediaType, "*", mp, onlyOneMimePart)) { + if (mp) { + parsedPart->appendSubPart(mp); + } + } else { + qCWarning(MIMETREEPARSER_LOG) << "THIS SHOULD NO LONGER HAPPEN:" << mediaType << '/' << subType; + const auto mp = defaultHandling(node, processResult, onlyOneMimePart); + if (mp) { + if (auto _mp = mp.dynamicCast()) { + _mp->setAttachmentFlag(node); + } + parsedPart->appendSubPart(mp); + } + } + mNodeHelper->setNodeProcessed(node, false); + + // adjust signed/encrypted flags if inline PGP was found + processResult.adjustCryptoStatesOfNode(node); + + if (onlyOneMimePart) { + break; + } + } + + return parsedPart; +} + +Interface::MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node, ProcessResult &result, bool onlyOneMimePart) +{ + Interface::MessagePart::Ptr mp; + ProcessResult processResult(mNodeHelper); + + if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream") && + (node->contentType()->name().endsWith(QLatin1String("p7m")) || + node->contentType()->name().endsWith(QLatin1String("p7s")) || + node->contentType()->name().endsWith(QLatin1String("p7c")) + ) && + processType(node, processResult, "application", "pkcs7-mime", mp, onlyOneMimePart)) { + return mp; + } + + const auto _mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node, false, true, mSource->decryptMessage())); + result.setInlineSignatureState(_mp->signatureState()); + result.setInlineEncryptionState(_mp->encryptionState()); + _mp->setNeverDisplayInline(result.neverDisplayInline()); + _mp->setIsImage(result.isImage()); + mp = _mp; + + // always show images in multipart/related when showing in html, not with an additional icon + auto preferredMode = mSource->preferredMode(); + bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + if (result.isImage() && node->parent() && + node->parent()->contentType()->subType() == "related" && isHtmlPreferred && !onlyOneMimePart) { + QString fileName = mNodeHelper->writeNodeToTempFile(node); + QString href = QUrl::fromLocalFile(fileName).url(); + QByteArray cid = node->contentID()->identifier(); + if (htmlWriter()) { + htmlWriter()->embedPart(cid, href); + } + nodeHelper()->setNodeDisplayedEmbedded(node, true); + mNodeHelper->setNodeDisplayedHidden(node, true); + return mp; + } + + // Show it inline if showOnlyOneMimePart(), which means the user clicked the image + // in the message structure viewer manually, and therefore wants to see the full image + if (result.isImage() && onlyOneMimePart && !result.neverDisplayInline()) { + mNodeHelper->setNodeDisplayedEmbedded(node, true); + } + + return mp; +} + +KMMsgSignatureState ProcessResult::inlineSignatureState() const +{ + return mInlineSignatureState; +} + +void ProcessResult::setInlineSignatureState(KMMsgSignatureState state) +{ + mInlineSignatureState = state; +} + +KMMsgEncryptionState ProcessResult::inlineEncryptionState() const +{ + return mInlineEncryptionState; +} + +void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state) +{ + mInlineEncryptionState = state; +} + +bool ProcessResult::neverDisplayInline() const +{ + return mNeverDisplayInline; +} + +void ProcessResult::setNeverDisplayInline(bool display) +{ + mNeverDisplayInline = display; +} + +bool ProcessResult::isImage() const +{ + return mIsImage; +} + +void ProcessResult::setIsImage(bool image) +{ + mIsImage = image; +} + +void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const +{ + if ((inlineSignatureState() != KMMsgNotSigned) || + (inlineEncryptionState() != KMMsgNotEncrypted)) { + mNodeHelper->setSignatureState(node, inlineSignatureState()); + mNodeHelper->setEncryptionState(node, inlineEncryptionState()); + } +} + +void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart) +{ + if (isFirstTextPart) { + mPlainTextContent += curNode->decodedText(); + mPlainTextContentCharset += NodeHelper::charset(curNode); + } +} + +void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent) +{ + mPlainTextContent = plainTextContent; +} + +const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const +{ + Q_ASSERT(node); + if (mSource->overrideCodec()) { + return mSource->overrideCodec(); + } + return mNodeHelper->codec(node); +} + +QByteArray ObjectTreeParser::plainTextContentCharset() const +{ + return mPlainTextContentCharset; +} + +QByteArray ObjectTreeParser::htmlContentCharset() const +{ + return mHtmlContentCharset; +} + +bool ObjectTreeParser::showOnlyOneMimePart() const +{ + return mShowOnlyOneMimePart; +} + +void ObjectTreeParser::setShowOnlyOneMimePart(bool show) +{ + mShowOnlyOneMimePart = show; +} + +const AttachmentStrategy *ObjectTreeParser::attachmentStrategy() const +{ + return mAttachmentStrategy; +} + +HtmlWriter *ObjectTreeParser::htmlWriter() const +{ + if (mHtmlWriter) { + return mHtmlWriter; + } + return mSource->htmlWriter(); +} + +MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const +{ + return mNodeHelper; +} diff --git a/framework/src/domain/mimetreeparser/otp/objecttreeparser.h b/framework/src/domain/mimetreeparser/otp/objecttreeparser.h new file mode 100644 index 00000000..3f29a673 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/objecttreeparser.h @@ -0,0 +1,406 @@ +/* + objecttreeparser.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz + Copyright (C) 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_OBJECTTREEPARSER_H__ +#define __MIMETREEPARSER_OBJECTTREEPARSER_H__ + +#include "nodehelper.h" +#include "objecttreesource.h" + +#include + +class QString; + +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ + +namespace Interface +{ +class MessagePart; +typedef QSharedPointer MessagePartPtr; +} + +class PartMetaData; +class ViewerPrivate; +class HtmlWriter; +class AttachmentStrategy; +class NodeHelper; +class MessagePart; +class MimeMessagePart; + +typedef QSharedPointer MessagePartPtr; +typedef QSharedPointer MimeMessagePartPtr; + +class ProcessResult +{ +public: + explicit ProcessResult(NodeHelper *nodeHelper, KMMsgSignatureState inlineSignatureState = KMMsgNotSigned, + KMMsgEncryptionState inlineEncryptionState = KMMsgNotEncrypted, + bool neverDisplayInline = false, + bool isImage = false) + : mInlineSignatureState(inlineSignatureState), + mInlineEncryptionState(inlineEncryptionState), + mNeverDisplayInline(neverDisplayInline), + mIsImage(isImage), + mNodeHelper(nodeHelper) {} + + KMMsgSignatureState inlineSignatureState() const; + void setInlineSignatureState(KMMsgSignatureState state); + + KMMsgEncryptionState inlineEncryptionState() const; + void setInlineEncryptionState(KMMsgEncryptionState state); + + bool neverDisplayInline() const; + void setNeverDisplayInline(bool display); + + bool isImage() const; + void setIsImage(bool image); + + void adjustCryptoStatesOfNode(const KMime::Content *node) const; + +private: + KMMsgSignatureState mInlineSignatureState; + KMMsgEncryptionState mInlineEncryptionState; + bool mNeverDisplayInline : 1; + bool mIsImage : 1; + NodeHelper *mNodeHelper; +}; + +/** +\brief Parses messages and generates HTML display code out of them + +\par Introduction + +First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate +to understand the broader picture. + +Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a +KMime::Content. + +\par Basics + +The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only +extracting the plainTextContent() for situations where only the message text is needed, for example +when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the +constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is +used. + +Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on +those nodes. Operating here means creating the HTML code for the node or extracting the textual +content from it. This process is started with parseObjectTree(), where we loop over the subnodes +of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can +handle the type of the node. This can either be an internal function, such as +processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin. +More on external plugins later. When no matching formatter is found, defaultHandling() is called +for that node. + +\par Multipart Nodes + +Those nodes that are of type multipart have subnodes. If one of those children needs to be +processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that +should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone +of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all +children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on +one of the children, as the other one doesn't need to be displayed. Similary, +processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the +signed node. + +\par Processed and Unprocessed Nodes + +When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being +not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a +list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other +related helper functions. + +It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the +related functions. This is important so that processing the same node twice can be prevented. The +check that prevents duplicate processing is in parseObjectTree(). + +An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(), +which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to +prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call +stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call +parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the +siblings is the plain text node, which shouldn't be processed! Therefore +processMultiPartAlternativeSubtype() sets the plain text node as been processed already. + +\par Plain Text Output + +Various nodes have plain text that should be displayed. This plain text is usually processed though +writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts +it if necessary. It also pushes the text through quotedHTML(), which does a number of things like +coloring quoted lines or detecting links and creating real link tags for them. + +\par Modifying the Message + +The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it +before displaying. This is for example the case when displaying a decrypted message: The original +message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts +that blob. After decryption, the current node is replaced with the decrypted node, which happens +in insertAndParseNewChildNode(). + +\par Crypto Operations + +For signature and decryption handling, there are functions which help with generating the HTML code +for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter() +and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause +the message to not be decrypted unless the user clicks a link. Whether the message should be +decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the +decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update +of the Viewer, which will cause parseObjectTree() to be called again. + +\par Async Crypto Operations + +The above case describes decryption the message in place. However, decryption and also verifying of +the signature can take a long time, so synchronous decryption and verifing would cause the Viewer to +block. Therefore it is possible to run these operations in async mode, see allowAsync(). +In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the +verify job, and informing the user that the operation is in progress with +writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a +BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can +have multiple mementos associated with it, which are differeniated by name. + +NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and +retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the +job pointer, the job input data and the job result. Mementos can be used for any async situation, +not just for crypto jobs, but I'll describe crypto jobs here. + +So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the +crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the +operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the +message is displayed. + +At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento +to be called. slotResult() then saves the result to some member variable and calls +BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update +will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it. +This is where the second run begins. + +The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or +processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with +the current node. Now it finds that memento, since it was created in the first run. It checks if the +memento's job has finished, and if so, the result can be written out (either the decrypted data or +the verified signature). + +When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to +note that the original MIME tree is never modified, and remains the same as the original one. The method +createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to +store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise +some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the +regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function +NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent. +Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent +node to the newly created temporary node. + +When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the +function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for +the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence +of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the +given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node. + +To conclude: For async operations, parseObjectTree() is called twice: The first call starts the +crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is +there and can use its result for writing out the HTML. + +\par PartMetaData and ProcessResult + +For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the +crypto state of a node. A PartMetaData can also be associated with a node by using +NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of +the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message. + +You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify +the ProcessResult to tell the callers something about the state of the nodes that were processed. +One example for its use is to tell the caller about the crypto state of the node. + +\par BodyPartFormatter Plugins + +As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp +contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from +a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro. + +The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not +yet happened at the time of writing. Therefore the code can seem a bit chaotic. + +External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins +can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento, +BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler, HtmlWriter and URLHandler. Therefore +external plugins have powerful capabilities, which are needed for example in the iCal formatter or +in the vCard formatter. + +\par Special HTML tags + +As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special +links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs. +Also, some special HTML tags are created, which the Viewer later uses for post-processing. For +example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which +is used to mark an attachment in the body with a yellow border when the user clicks the attachment +in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in +the Viewer to scroll to the attachment. +*/ +class ObjectTreeParser +{ + /** + * @internal + * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent. + */ + ObjectTreeParser(const ObjectTreeParser &other); + +public: + explicit ObjectTreeParser(Interface::ObjectTreeSource *source, + NodeHelper *nodeHelper = nullptr, + bool showOneMimePart = false, + const AttachmentStrategy *attachmentStrategy = nullptr); + + explicit ObjectTreeParser(const ObjectTreeParser *topLevelParser, + bool showOneMimePart = false, + const AttachmentStrategy *attachmentStrategy = nullptr); + virtual ~ObjectTreeParser(); + + void setAllowAsync(bool allow); + bool allowAsync() const; + + bool hasPendingAsyncJobs() const; + + /** + * The text of the message, ie. what would appear in the + * composer's text editor if this was edited or replied to. + * This is usually the content of the first text/plain MIME part. + */ + QString plainTextContent() const; + + /** + * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part. + * + * Not to be consfused with the HTML code that the message viewer widget displays, that HTML + * is written out by htmlWriter() and a totally different pair of shoes. + */ + QString htmlContent() const; + + /** + * The original charset of MIME part the plain text was extracted from. + * + * If there were more than one text/plain MIME parts in the mail, the this is the charset + * of the last MIME part processed. + */ + QByteArray plainTextContentCharset() const; + QByteArray htmlContentCharset() const; + + bool showOnlyOneMimePart() const; + void setShowOnlyOneMimePart(bool show); + + const AttachmentStrategy *attachmentStrategy() const; + + HtmlWriter *htmlWriter() const; + + NodeHelper *nodeHelper() const; + + /** Parse beginning at a given node and recursively parsing + the children of that node and it's next sibling. */ + void parseObjectTree(KMime::Content *node); + MessagePartPtr parsedPart() const; + +private: + void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart); + void setPlainTextContent(const QString &plainTextContent); + + /** + * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the + * top-level content. + */ + MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart); + bool processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart); + + Interface::MessagePartPtr defaultHandling(KMime::Content *node, MimeTreeParser::ProcessResult &result, bool onlyOneMimePart); + +private: + + /** ctor helper */ + void init(); + + const QTextCodec *codecFor(KMime::Content *node) const; + + void copyContentFrom(const ObjectTreeParser *other); + +private: + Interface::ObjectTreeSource *mSource; + NodeHelper *mNodeHelper; + HtmlWriter *mHtmlWriter; + QByteArray mPlainTextContentCharset; + QByteArray mHtmlContentCharset; + QString mPlainTextContent; + QString mHtmlContent; + KMime::Content *mTopLevelContent; + MessagePartPtr mParsedPart; + + /// Show only one mime part means that the user has selected some node in the message structure + /// viewer that is not the root, which means the user wants to only see the selected node and its + /// children. If that is the case, this variable is set to true. + /// The code needs to behave differently if this is set. For example, it should not process the + /// siblings. Also, consider inline images: Normally, those nodes are completely hidden, as the + /// HTML node embedds them. However, when showing only the node of the image, one has to show them, + /// as their is no HTML node in which they are displayed. There are many more cases where this + /// variable needs to be obeyed. + /// This variable is set to false again when processing the children in stdChildHandling(), as + /// the children can be completely displayed again. + bool mShowOnlyOneMimePart; + + bool mHasPendingAsyncJobs; + bool mAllowAsync; + const AttachmentStrategy *mAttachmentStrategy; + // DataUrl Icons cache + QString mCollapseIcon; + QString mExpandIcon; + bool mDeleteNodeHelper; + + friend class PartNodeBodyPart; + friend class MessagePart; + friend class EncryptedMessagePart; + friend class SignedMessagePart; + friend class EncapsulatedRfc822MessagePart; + friend class TextMessagePart; + friend class HtmlMessagePart; + friend class TextPlainBodyPartFormatter; + friend class MultiPartSignedBodyPartFormatter; + friend class ApplicationPkcs7MimeBodyPartFormatter; +}; + +} + +#endif // __MIMETREEPARSER_OBJECTTREEPARSER_H__ + diff --git a/framework/src/domain/mimetreeparser/otp/objecttreesource.cpp b/framework/src/domain/mimetreeparser/otp/objecttreesource.cpp new file mode 100644 index 00000000..45f96c58 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/objecttreesource.cpp @@ -0,0 +1,28 @@ +/* + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "objecttreesource.h" +#include "bodypartformatter.h" +#include "messagepart.h" + +using namespace MimeTreeParser; + +Interface::ObjectTreeSource::~ObjectTreeSource() +{ +} diff --git a/framework/src/domain/mimetreeparser/otp/objecttreesource.h b/framework/src/domain/mimetreeparser/otp/objecttreesource.h new file mode 100644 index 00000000..afada4c4 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/objecttreesource.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net + Copyright (c) 2009 Andras Mantia + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__ +#define __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__ + +#include "util.h" + +#include + +#include +class QTextCodec; + +namespace MimeTreeParser +{ +class HtmlWriter; +class AttachmentStrategy; +class BodyPartFormatterBaseFactory; +namespace Interface +{ +class MessagePart; +typedef QSharedPointer MessagePartPtr; +class MessagePartRenderer; +typedef QSharedPointer MessagePartRendererPtr; +} +} + +namespace MimeTreeParser +{ +namespace Interface +{ + +/** + * Interface for object tree sources. + * @author Andras Mantia + */ +class ObjectTreeSource +{ + +public: + virtual ~ObjectTreeSource(); + + /** + * Sets the type of mail that is currently displayed. Applications can display this + * information to the user, for example KMail displays a HTML status bar. + * Note: This is not called when the mode is "Normal". + */ + virtual void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) = 0; + + /** Return the mode that is the preferred to display */ + virtual MimeTreeParser::Util::HtmlMode preferredMode() const = 0; + + /** Return true if an encrypted mail should be decrypted */ + virtual bool decryptMessage() const = 0; + + /** Return true if external sources should be loaded in a html mail */ + virtual bool htmlLoadExternal() const = 0; + + /** Return true to include the signature details in the generated html */ + virtual bool showSignatureDetails() const = 0; + + virtual int levelQuote() const = 0; + + /** The override codec that should be used for the mail */ + virtual const QTextCodec *overrideCodec() = 0; + + virtual QString createMessageHeader(KMime::Message *message) = 0; + + /** Return the wanted attachment startegy */ + virtual const AttachmentStrategy *attachmentStrategy() = 0; + + /** Return the html write object */ + virtual HtmlWriter *htmlWriter() = 0; + + /** The source object behind the interface. */ + virtual QObject *sourceObject() = 0; + + /** should keys be imported automatically **/ + virtual bool autoImportKeys() const = 0; + + virtual bool showEmoticons() const = 0; + + virtual bool showExpandQuotesMark() const = 0; + + virtual const BodyPartFormatterBaseFactory *bodyPartFormatterFactory() = 0; + + virtual MessagePartRendererPtr messagePartTheme(MessagePartPtr msgPart) = 0; + + virtual bool isPrinting() const = 0; +}; +} +} +#endif diff --git a/framework/src/domain/mimetreeparser/otp/partmetadata.h b/framework/src/domain/mimetreeparser/otp/partmetadata.h new file mode 100644 index 00000000..41399837 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/partmetadata.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- + partmetadata.h + + KMail, the KDE mail client. + Copyright (c) 2002-2003 Karl-Heinz Zimmer + Copyright (c) 2003 Marc Mutz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2.0, as published by the Free Software Foundation. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#ifndef __MIMETREEPARSER_PARTMETADATA_H__ +#define __MIMETREEPARSER_PARTMETADATA_H__ + +#include +#include + +#include +#include + +namespace MimeTreeParser +{ + +class PartMetaData +{ +public: + PartMetaData() + : sigSummary(GpgME::Signature::None), + isSigned(false), + isGoodSignature(false), + isEncrypted(false), + isDecryptable(false), + inProgress(false), + technicalProblem(false), + isEncapsulatedRfc822Message(false) + { + } + GpgME::Signature::Summary sigSummary; + QString signClass; + QString signer; + QStringList signerMailAddresses; + QByteArray keyId; + GpgME::Signature::Validity keyTrust; + QString status; // to be used for unknown plug-ins + int status_code; // to be used for i18n of OpenPGP and S/MIME CryptPlugs + QString errorText; + QDateTime creationTime; + QString decryptionError; + QString auditLog; + GpgME::Error auditLogError; + bool isSigned : 1; + bool isGoodSignature : 1; + bool isEncrypted : 1; + bool isDecryptable : 1; + bool inProgress : 1; + bool technicalProblem : 1; + bool isEncapsulatedRfc822Message : 1; +}; + +} + +#endif // __MIMETREEPARSER_PARTMETADATA_H__ + diff --git a/framework/src/domain/mimetreeparser/otp/partnodebodypart.cpp b/framework/src/domain/mimetreeparser/otp/partnodebodypart.cpp new file mode 100644 index 00000000..ec509787 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/partnodebodypart.cpp @@ -0,0 +1,125 @@ +/* + partnodebodypart.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "partnodebodypart.h" +#include "nodehelper.h" +#include "objecttreeparser.h" +#include "mimetreeparser_debug.h" +#include + +#include +#include + +using namespace MimeTreeParser; + +static int serial = 0; + +PartNodeBodyPart::PartNodeBodyPart(ObjectTreeParser *otp, ProcessResult *result, KMime::Content *topLevelContent, KMime::Content *content, + NodeHelper *nodeHelper) + : Interface::BodyPart(), mTopLevelContent(topLevelContent), mContent(content), + mDefaultDisplay(Interface::BodyPart::None), mNodeHelper(nodeHelper) + , mObjectTreeParser(otp) + , mProcessResult(result) +{} + +QString PartNodeBodyPart::makeLink(const QString &path) const +{ + // FIXME: use a PRNG for the first arg, instead of a serial number + return QStringLiteral("x-kmail:/bodypart/%1/%2/%3") + .arg(serial++).arg(mContent->index().toString()) + .arg(QString::fromLatin1(QUrl::toPercentEncoding(path, "/"))); +} + +QString PartNodeBodyPart::asText() const +{ + if (!mContent->contentType()->isText()) { + return QString(); + } + return mContent->decodedText(); +} + +QByteArray PartNodeBodyPart::asBinary() const +{ + return mContent->decodedContent(); +} + +QString PartNodeBodyPart::contentTypeParameter(const char *param) const +{ + return mContent->contentType()->parameter(QString::fromLatin1(param)); +} + +QString PartNodeBodyPart::contentDescription() const +{ + return mContent->contentDescription()->asUnicodeString(); +} + +QString PartNodeBodyPart::contentDispositionParameter(const char *param) const +{ + return mContent->contentDisposition()->parameter(QString::fromLatin1(param)); +} + +bool PartNodeBodyPart::hasCompleteBody() const +{ + qCWarning(MIMETREEPARSER_LOG) << "Sorry, not yet implemented."; + return true; +} + +Interface::BodyPartMemento *PartNodeBodyPart::memento() const +{ + /*TODO(Andras) Volker suggests to use a ContentIndex->Mememnto mapping + Also review if the reader's bodyPartMemento should be returned or the NodeHelper's one + */ + return mNodeHelper->bodyPartMemento(mContent, "__plugin__"); +} + +void PartNodeBodyPart::setBodyPartMemento(Interface::BodyPartMemento *memento) +{ + /*TODO(Andras) Volker suggests to use a ContentIndex->Memento mapping + Also review if the reader's bodyPartMemento should be set or the NodeHelper's one */ + mNodeHelper->setBodyPartMemento(mContent, "__plugin__", memento); +} + +Interface::BodyPart::Display PartNodeBodyPart::defaultDisplay() const +{ + return mDefaultDisplay; +} + +void PartNodeBodyPart::setDefaultDisplay(Interface::BodyPart::Display d) +{ + mDefaultDisplay = d; +} + +Interface::ObjectTreeSource *PartNodeBodyPart::source() const +{ + return mObjectTreeParser->mSource; +} diff --git a/framework/src/domain/mimetreeparser/otp/partnodebodypart.h b/framework/src/domain/mimetreeparser/otp/partnodebodypart.h new file mode 100644 index 00000000..ded0ee2c --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/partnodebodypart.h @@ -0,0 +1,108 @@ +/* + partnodebodypart.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2004 Marc Mutz , + Ingo Kloecker + + KMail 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) any later version. + + KMail 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __MIMETREEPARSER_PARTNODEBODYPART_H__ +#define __MIMETREEPARSER_PARTNODEBODYPART_H__ + +#include "bodypart.h" + +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ +class NodeHelper; +} + +namespace MimeTreeParser +{ + +/** + @short an implementation of the BodyPart interface using KMime::Content's +*/ +class PartNodeBodyPart : public Interface::BodyPart +{ +public: + explicit PartNodeBodyPart(ObjectTreeParser *otp, ProcessResult *result, KMime::Content *topLevelContent, KMime::Content *content, + NodeHelper *nodeHelper); + + QString makeLink(const QString &path) const Q_DECL_OVERRIDE; + QString asText() const Q_DECL_OVERRIDE; + QByteArray asBinary() const Q_DECL_OVERRIDE; + QString contentTypeParameter(const char *param) const Q_DECL_OVERRIDE; + QString contentDescription() const Q_DECL_OVERRIDE; + QString contentDispositionParameter(const char *param) const Q_DECL_OVERRIDE; + bool hasCompleteBody() const Q_DECL_OVERRIDE; + + Interface::BodyPartMemento *memento() const Q_DECL_OVERRIDE; + void setBodyPartMemento(Interface::BodyPartMemento *memento) Q_DECL_OVERRIDE; + BodyPart::Display defaultDisplay() const Q_DECL_OVERRIDE; + void setDefaultDisplay(BodyPart::Display); + KMime::Content *content() const Q_DECL_OVERRIDE + { + return mContent; + } + KMime::Content *topLevelContent() const Q_DECL_OVERRIDE + { + return mTopLevelContent; + } + NodeHelper *nodeHelper() const Q_DECL_OVERRIDE + { + return mNodeHelper; + } + + ObjectTreeParser *objectTreeParser() const Q_DECL_OVERRIDE + { + return mObjectTreeParser; + } + + ProcessResult *processResult() const Q_DECL_OVERRIDE + { + return mProcessResult; + } + + Interface::ObjectTreeSource *source() const Q_DECL_OVERRIDE; +private: + KMime::Content *mTopLevelContent; + KMime::Content *mContent; + BodyPart::Display mDefaultDisplay; + NodeHelper *mNodeHelper; + ObjectTreeParser *mObjectTreeParser; + ProcessResult *mProcessResult; +}; + +} + +#endif // __MIMETREEPARSER_PARTNODEBODYPART_H__ diff --git a/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.cpp b/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.cpp new file mode 100644 index 00000000..1f453342 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.cpp @@ -0,0 +1,158 @@ +/* + Copyright (c) 2008 Volker Krause + + 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) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "qgpgmejobexecutor.h" +#include "mimetreeparser_debug.h" + +#include +#include +#include +#include + +#include + +#include + +using namespace GpgME; +using namespace MimeTreeParser; + +QGpgMEJobExecutor::QGpgMEJobExecutor(QObject *parent) : QObject(parent) +{ + setObjectName(QStringLiteral("KleoJobExecutor")); + mEventLoop = new QEventLoop(this); +} + +GpgME::VerificationResult QGpgMEJobExecutor::exec( + QGpgME::VerifyDetachedJob *job, + const QByteArray &signature, + const QByteArray &signedData) +{ + qCDebug(MIMETREEPARSER_LOG) << "Starting detached verification job"; + connect(job, SIGNAL(result(GpgME::VerificationResult)), SLOT(verificationResult(GpgME::VerificationResult))); + GpgME::Error err = job->start(signature, signedData); + if (err) { + return VerificationResult(err); + } + mEventLoop->exec(QEventLoop::ExcludeUserInputEvents); + return mVerificationResult; +} + +GpgME::VerificationResult QGpgMEJobExecutor::exec( + QGpgME::VerifyOpaqueJob *job, + const QByteArray &signedData, + QByteArray &plainText) +{ + qCDebug(MIMETREEPARSER_LOG) << "Starting opaque verification job"; + connect(job, SIGNAL(result(GpgME::VerificationResult,QByteArray)), SLOT(verificationResult(GpgME::VerificationResult,QByteArray))); + GpgME::Error err = job->start(signedData); + if (err) { + plainText.clear(); + return VerificationResult(err); + } + mEventLoop->exec(QEventLoop::ExcludeUserInputEvents); + plainText = mData; + return mVerificationResult; +} + +std::pair< GpgME::DecryptionResult, GpgME::VerificationResult > QGpgMEJobExecutor::exec( + QGpgME::DecryptVerifyJob *job, + const QByteArray &cipherText, + QByteArray &plainText) +{ + qCDebug(MIMETREEPARSER_LOG) << "Starting decryption job"; + connect(job, &QGpgME::DecryptVerifyJob::result, this, &QGpgMEJobExecutor::decryptResult); + GpgME::Error err = job->start(cipherText); + if (err) { + plainText.clear(); + return std::make_pair(DecryptionResult(err), VerificationResult(err)); + } + mEventLoop->exec(QEventLoop::ExcludeUserInputEvents); + plainText = mData; + return std::make_pair(mDecryptResult, mVerificationResult); +} + +GpgME::ImportResult QGpgMEJobExecutor::exec(QGpgME::ImportJob *job, const QByteArray &certData) +{ + connect(job, SIGNAL(result(GpgME::ImportResult)), SLOT(importResult(GpgME::ImportResult))); + GpgME::Error err = job->start(certData); + if (err) { + return ImportResult(err); + } + mEventLoop->exec(QEventLoop::ExcludeUserInputEvents); + return mImportResult; +} + +Error QGpgMEJobExecutor::auditLogError() const +{ + return mAuditLogError; +} + +void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result) +{ + qCDebug(MIMETREEPARSER_LOG) << "Detached verification job finished"; + QGpgME::Job *job = qobject_cast(sender()); + assert(job); + mVerificationResult = result; + mAuditLogError = job->auditLogError(); + mAuditLog = job->auditLogAsHtml(); + mEventLoop->quit(); +} + +void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText) +{ + qCDebug(MIMETREEPARSER_LOG) << "Opaque verification job finished"; + QGpgME::Job *job = qobject_cast(sender()); + assert(job); + mVerificationResult = result; + mData = plainText; + mAuditLogError = job->auditLogError(); + mAuditLog = job->auditLogAsHtml(); + mEventLoop->quit(); +} + +void QGpgMEJobExecutor::decryptResult( + const GpgME::DecryptionResult &decryptionresult, + const GpgME::VerificationResult &verificationresult, + const QByteArray &plainText) +{ + qCDebug(MIMETREEPARSER_LOG) << "Decryption job finished"; + QGpgME::Job *job = qobject_cast(sender()); + assert(job); + mVerificationResult = verificationresult; + mDecryptResult = decryptionresult; + mData = plainText; + mAuditLogError = job->auditLogError(); + mAuditLog = job->auditLogAsHtml(); + mEventLoop->quit(); +} + +void QGpgMEJobExecutor::importResult(const GpgME::ImportResult &result) +{ + QGpgME::Job *job = qobject_cast(sender()); + assert(job); + mImportResult = result; + mAuditLogError = job->auditLogError(); + mAuditLog = job->auditLogAsHtml(); + mEventLoop->quit(); +} + +QString QGpgMEJobExecutor::auditLogAsHtml() const +{ + return mAuditLog; +} + diff --git a/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.h b/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.h new file mode 100644 index 00000000..8a81b078 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/qgpgmejobexecutor.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2008 Volker Krause + + 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) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_KLEOJOBEXECUTOR_H__ +#define __MIMETREEPARSER_KLEOJOBEXECUTOR_H__ + +#include +#include +#include + +#include + +#include + +class QEventLoop; + +namespace QGpgME +{ +class DecryptVerifyJob; +class ImportJob; +class VerifyDetachedJob; +class VerifyOpaqueJob; +} + +namespace MimeTreeParser +{ + +/** + Helper class for synchronous execution of Kleo crypto jobs. +*/ +class QGpgMEJobExecutor : public QObject +{ + Q_OBJECT +public: + explicit QGpgMEJobExecutor(QObject *parent = nullptr); + + GpgME::VerificationResult exec(QGpgME::VerifyDetachedJob *job, + const QByteArray &signature, + const QByteArray &signedData); + GpgME::VerificationResult exec(QGpgME::VerifyOpaqueJob *job, + const QByteArray &signedData, + QByteArray &plainText); + std::pair exec(QGpgME::DecryptVerifyJob *job, + const QByteArray &cipherText, + QByteArray &plainText); + GpgME::ImportResult exec(QGpgME::ImportJob *job, const QByteArray &certData); + + GpgME::Error auditLogError() const; + QString auditLogAsHtml() const; + +private Q_SLOTS: + void verificationResult(const GpgME::VerificationResult &result); + void verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText); + void decryptResult(const GpgME::DecryptionResult &decryptionresult, + const GpgME::VerificationResult &verificationresult, + const QByteArray &plainText); + void importResult(const GpgME::ImportResult &result); + +private: + QEventLoop *mEventLoop; + GpgME::VerificationResult mVerificationResult; + GpgME::DecryptionResult mDecryptResult; + GpgME::ImportResult mImportResult; + QByteArray mData; + GpgME::Error mAuditLogError; + QString mAuditLog; +}; + +} + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.cpp b/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.cpp new file mode 100644 index 00000000..ea17bf5c --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.cpp @@ -0,0 +1,136 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "queuehtmlwriter.h" + +#include "mimetreeparser_debug.h" + +#include +#include + +using namespace MimeTreeParser; + +QueueHtmlWriter::QueueHtmlWriter(HtmlWriter *base) + : HtmlWriter() + , mBase(base) +{ +} + +QueueHtmlWriter::~QueueHtmlWriter() +{ +} + +void QueueHtmlWriter::setBase(HtmlWriter *base) +{ + mBase = base; +} + +void QueueHtmlWriter::begin(const QString &css) +{ + Command cmd; + cmd.type = Command::Begin; + cmd.s = css; + mQueue.append(cmd); +} + +void QueueHtmlWriter::end() +{ + Command cmd; + cmd.type = Command::End; + mQueue.append(cmd); +} + +void QueueHtmlWriter::reset() +{ + Command cmd; + cmd.type = Command::Reset; + mQueue.append(cmd); +} + +void QueueHtmlWriter::write(const QString &str) +{ + Command cmd; + cmd.type = Command::Write; + cmd.s = str; + mQueue.append(cmd); +} + +void QueueHtmlWriter::queue(const QString &str) +{ + Command cmd; + cmd.type = Command::Queue; + cmd.s = str; + mQueue.append(cmd); +} + +void QueueHtmlWriter::flush() +{ + Command cmd; + cmd.type = Command::Flush; + mQueue.append(cmd); +} + +void QueueHtmlWriter::replay() +{ + foreach (const auto &entry, mQueue) { + switch (entry.type) { + case Command::Begin: + mBase->begin(entry.s); + break; + case Command::End: + mBase->end(); + break; + case Command::Reset: + mBase->reset(); + break; + case Command::Write: + mBase->write(entry.s); + break; + case Command::Queue: + mBase->queue(entry.s); + break; + case Command::Flush: + mBase->flush(); + break; + case Command::EmbedPart: + mBase->embedPart(entry.b, entry.s); + break; + case Command::ExtraHead: + mBase->extraHead(entry.s); + break; + } + } +} + +void QueueHtmlWriter::embedPart(const QByteArray &contentId, const QString &url) +{ + Command cmd; + cmd.type = Command::EmbedPart; + cmd.s = url; + cmd.b = contentId; + mQueue.append(cmd); +} +void QueueHtmlWriter::extraHead(const QString &extra) +{ + Command cmd; + cmd.type = Command::ExtraHead; + cmd.s = extra; + mQueue.append(cmd); +} + diff --git a/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.h b/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.h new file mode 100644 index 00000000..9e7a4659 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/queuehtmlwriter.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_QUEUEHTMLWRITER_H__ +#define __MIMETREEPARSER_QUEUEHTMLWRITER_H__ + +#include "htmlwriter.h" + +#include +#include + +class QString; +class QByteArray; + +namespace MimeTreeParser +{ +/** +\brief Cache HTML output and not write them directy. + +This class is needed to make it possible to first process the mime tree and +afterwards render the HTML. + +Please do not use this class - it is only added to make it possible to slowly +move ObjectTreeParser to a process fist / render later. + +*/ +struct Command { + enum { Begin, End, Reset, Write, Queue, Flush, EmbedPart, ExtraHead } type; + QString s; + QByteArray b; +}; + +class QueueHtmlWriter : public HtmlWriter +{ +public: + explicit QueueHtmlWriter(MimeTreeParser::HtmlWriter *base); + virtual ~QueueHtmlWriter(); + + void setBase(HtmlWriter *base); + + void begin(const QString &cssDefs) Q_DECL_OVERRIDE; + void end() Q_DECL_OVERRIDE; + void reset() Q_DECL_OVERRIDE; + void write(const QString &str) Q_DECL_OVERRIDE; + void queue(const QString &str) Q_DECL_OVERRIDE; + void flush() Q_DECL_OVERRIDE; + void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE; + void extraHead(const QString &str) Q_DECL_OVERRIDE; + + void replay(); + +private: + HtmlWriter *mBase; + QVector mQueue; +}; + +} // namespace MimeTreeParser + +#endif // __MIMETREEPARSER_QUEUEHTMLWRITER_H__ diff --git a/framework/src/domain/mimetreeparser/otp/util.cpp b/framework/src/domain/mimetreeparser/otp/util.cpp new file mode 100644 index 00000000..5ca8d828 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/util.cpp @@ -0,0 +1,136 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "util.h" + +#include "mimetreeparser_debug.h" + +#include "nodehelper.h" + +#include + +#include +#include + +using namespace MimeTreeParser::Util; + +bool MimeTreeParser::Util::isTypeBlacklisted(KMime::Content *node) +{ + const QByteArray mediaTypeLower = node->contentType()->mediaType().toLower(); + bool typeBlacklisted = mediaTypeLower == "multipart"; + if (!typeBlacklisted) { + typeBlacklisted = KMime::isCryptoPart(node); + } + typeBlacklisted = typeBlacklisted || node == node->topLevel(); + const bool firstTextChildOfEncapsulatedMsg = + mediaTypeLower == "text" && + node->contentType()->subType().toLower() == "plain" && + node->parent() && node->parent()->contentType()->mediaType().toLower() == "message"; + return typeBlacklisted || firstTextChildOfEncapsulatedMsg; +} + +QString MimeTreeParser::Util::labelForContent(KMime::Content *node) +{ + const QString name = node->contentType()->name(); + QString label = name.isEmpty() ? NodeHelper::fileName(node) : name; + if (label.isEmpty()) { + label = node->contentDescription()->asUnicodeString(); + } + return label; +} + +QMimeType MimeTreeParser::Util::mimetype(const QString &name) +{ + QMimeDatabase db; + // consider the filename if mimetype cannot be found by content-type + const auto mimeTypes = db.mimeTypesForFileName(name); + for (const auto &mt : mimeTypes) { + if (mt.name() != QLatin1String("application/octet-stream")) { + return mt; + } + } + + // consider the attachment's contents if neither the Content-Type header + // nor the filename give us a clue + return db.mimeTypeForFile(name); +} + +QString MimeTreeParser::Util::iconNameForMimetype(const QString &mimeType, + const QString &fallbackFileName1, + const QString &fallbackFileName2) +{ + QString fileName; + QString tMimeType = mimeType; + + // convert non-registered types to registered types + if (mimeType == QLatin1String("application/x-vnd.kolab.contact")) { + tMimeType = QStringLiteral("text/x-vcard"); + } else if (mimeType == QLatin1String("application/x-vnd.kolab.event")) { + tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.event"); + } else if (mimeType == QLatin1String("application/x-vnd.kolab.task")) { + tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.todo"); + } else if (mimeType == QLatin1String("application/x-vnd.kolab.journal")) { + tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.journal"); + } else if (mimeType == QLatin1String("application/x-vnd.kolab.note")) { + tMimeType = QStringLiteral("application/x-vnd.akonadi.note"); + } else if (mimeType == QLatin1String("image/jpg")) { + tMimeType = QStringLiteral("image/jpeg"); + } + QMimeDatabase mimeDb; + auto mime = mimeDb.mimeTypeForName(tMimeType); + if (mime.isValid()) { + fileName = mime.iconName(); + } else { + fileName = QStringLiteral("unknown"); + if (!tMimeType.isEmpty()) { + qCWarning(MIMETREEPARSER_LOG) << "unknown mimetype" << tMimeType; + } + } + //WorkAround for #199083 + if (fileName == QLatin1String("text-vcard")) { + fileName = QStringLiteral("text-x-vcard"); + } + + if (fileName.isEmpty()) { + fileName = fallbackFileName1; + if (fileName.isEmpty()) { + fileName = fallbackFileName2; + } + if (!fileName.isEmpty()) { + fileName = mimeDb.mimeTypeForFile(QLatin1String("/tmp/") + fileName).iconName(); + } + } + + return fileName; +} + +QString MimeTreeParser::Util::iconNameForContent(KMime::Content *node) +{ + if (!node) { + return QString(); + } + + QByteArray mimeType = node->contentType()->mimeType(); + if (mimeType.isNull() || mimeType == "application/octet-stream") { + const QString mime = mimetype(node->contentDisposition()->filename()).name(); + mimeType = mime.toLatin1(); + } + mimeType = mimeType.toLower(); + return iconNameForMimetype(QLatin1String(mimeType), node->contentDisposition()->filename(), + node->contentType()->name()); +} diff --git a/framework/src/domain/mimetreeparser/otp/util.h b/framework/src/domain/mimetreeparser/otp/util.h new file mode 100644 index 00000000..099c647a --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/util.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_UTILS_UTIL_H__ +#define __MIMETREEPARSER_UTILS_UTIL_H__ + +#include + +class QMimeType; + +namespace KMime +{ +class Content; +} + +namespace MimeTreeParser +{ + +/** + * The Util namespace contains a collection of helper functions use in + * various places. + */ +namespace Util +{ + +/** + * Describes the type of the displayed message. This depends on the MIME structure + * of the mail and on whether HTML mode is enabled (which is decided by htmlMail()) + */ +enum HtmlMode { + Normal, ///< A normal plaintext message, non-multipart + Html, ///< A HTML message, non-multipart + MultipartPlain, ///< A multipart/alternative message, the plain text part is currently displayed + MultipartHtml, ///< A multipart/altervative message, the HTML part is currently displayed + MultipartIcal ///< A multipart/altervative message, the ICal part is currently displayed +}; + +bool isTypeBlacklisted(KMime::Content *node); + +QString labelForContent(KMime::Content *node); + +QMimeType mimetype(const QString &name); + +QString iconNameForMimetype(const QString &mimeType, + const QString &fallbackFileName1 = QString(), + const QString &fallbackFileName2 = QString()); + +QString iconNameForContent(KMime::Content *node); +} +} + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/utils.cpp b/framework/src/domain/mimetreeparser/otp/utils.cpp new file mode 100644 index 00000000..8f718143 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/utils.cpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2016 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "utils.h" + +using namespace MimeTreeParser; + +MimeMessagePart::Ptr MimeTreeParser::createAndParseTempNode(Interface::BodyPart &part, KMime::Content *parentNode, const char *content, const char *cntDesc) +{ + KMime::Content *newNode = new KMime::Content(); + newNode->setContent(KMime::CRLFtoLF(content)); + newNode->parse(); + + if (!newNode->head().isEmpty()) { + newNode->contentDescription()->from7BitString(cntDesc); + } + part.nodeHelper()->attachExtraContent(parentNode, newNode); + + return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, false)); +} + +KMime::Content *MimeTreeParser::findTypeInDirectChilds(KMime::Content *content, const QByteArray &mimeType) +{ + if (mimeType.isEmpty()) { + return content; + } + + foreach (auto child, content->contents()) { + if ((!child->contentType()->isEmpty()) + && (mimeType == child->contentType()->mimeType())) { + return child; + } + } + return nullptr; +} + +MessagePart::Ptr MimeTreeParser::toplevelTextNode(MessagePart::Ptr messageTree) +{ + foreach (const auto &mp, messageTree->subParts()) { + auto text = mp.dynamicCast(); + auto attach = mp.dynamicCast(); + if (text && !attach) { + return text; + } else if (const auto alternative = mp.dynamicCast()) { + return alternative; + } else if (const auto m = mp.dynamicCast()) { + auto ret = toplevelTextNode(m); + if (ret) { + return ret; + } + } + } + return MessagePart::Ptr(); +} diff --git a/framework/src/domain/mimetreeparser/otp/utils.h b/framework/src/domain/mimetreeparser/otp/utils.h new file mode 100644 index 00000000..d4aaa43a --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/utils.h @@ -0,0 +1,42 @@ +/* + Copyright (c) 2016 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef __MIMETREEPARSER_BODYFORAMATTER_UTILS_H__ +#define __MIMETREEPARSER_BODYFORAMATTER_UTILS_H__ + +#include "bodypart.h" +#include "messagepart.h" + +#include + +namespace MimeTreeParser +{ +/** + 1. Create a new partNode using 'content' data and Content-Description + found in 'cntDesc'. + 2. Parse the 'node' to display the content. +*/ +MimeMessagePart::Ptr createAndParseTempNode(Interface::BodyPart &part, KMime::Content *parentNode, const char *content, const char *cntDesc); + +KMime::Content *findTypeInDirectChilds(KMime::Content *content, const QByteArray &mimeType); + +MessagePart::Ptr toplevelTextNode(MessagePart::Ptr messageTree); +} + +#endif diff --git a/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.cpp b/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.cpp new file mode 100644 index 00000000..56c1d1a7 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.cpp @@ -0,0 +1,177 @@ +/* + Copyright (c) 2014-2017 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "verifydetachedbodypartmemento.h" +#include "mimetreeparser_debug.h" + +#include +#include + +#include + +#include + +#include + +using namespace QGpgME; +using namespace GpgME; +using namespace MimeTreeParser; + +VerifyDetachedBodyPartMemento::VerifyDetachedBodyPartMemento(VerifyDetachedJob *job, + KeyListJob *klj, + const QByteArray &signature, + const QByteArray &plainText) + : CryptoBodyPartMemento(), + m_signature(signature), + m_plainText(plainText), + m_job(job), + m_keylistjob(klj) +{ + assert(m_job); +} + +VerifyDetachedBodyPartMemento::~VerifyDetachedBodyPartMemento() +{ + if (m_job) { + m_job->slotCancel(); + } + if (m_keylistjob) { + m_keylistjob->slotCancel(); + } +} + +bool VerifyDetachedBodyPartMemento::start() +{ + assert(m_job); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento started"; +#endif + connect(m_job, SIGNAL(result(GpgME::VerificationResult)), + this, SLOT(slotResult(GpgME::VerificationResult))); + if (const Error err = m_job->start(m_signature, m_plainText)) { + m_vr = VerificationResult(err); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento stopped with error"; +#endif + return false; + } + setRunning(true); + return true; +} + +void VerifyDetachedBodyPartMemento::exec() +{ + assert(m_job); + setRunning(true); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento execed"; +#endif + saveResult(m_job->exec(m_signature, m_plainText)); + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = nullptr; +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento after execed"; +#endif + if (canStartKeyListJob()) { + std::vector keys; + m_keylistjob->exec(keyListPattern(), /*secretOnly=*/false, keys); + if (!keys.empty()) { + m_key = keys.back(); + } + } + if (m_keylistjob) { + m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves + } + m_keylistjob = nullptr; + setRunning(false); +} + +bool VerifyDetachedBodyPartMemento::canStartKeyListJob() const +{ + if (!m_keylistjob) { + return false; + } + const char *const fpr = m_vr.signature(0).fingerprint(); + return fpr && *fpr; +} + +QStringList VerifyDetachedBodyPartMemento::keyListPattern() const +{ + assert(canStartKeyListJob()); + return QStringList(QString::fromLatin1(m_vr.signature(0).fingerprint())); +} + +void VerifyDetachedBodyPartMemento::saveResult(const VerificationResult &vr) +{ + assert(m_job); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::saveResult called"; +#endif + m_vr = vr; + setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml()); +} + +void VerifyDetachedBodyPartMemento::slotResult(const VerificationResult &vr) +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotResult called"; +#endif + saveResult(vr); + m_job = nullptr; + if (canStartKeyListJob() && startKeyListJob()) { +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento: canStartKeyListJob && startKeyListJob"; +#endif + return; + } + if (m_keylistjob) { + m_keylistjob->deleteLater(); + } + m_keylistjob = nullptr; + setRunning(false); + notify(); +} + +bool VerifyDetachedBodyPartMemento::startKeyListJob() +{ + assert(canStartKeyListJob()); + if (const GpgME::Error err = m_keylistjob->start(keyListPattern())) { + return false; + } + connect(m_keylistjob, SIGNAL(done()), this, SLOT(slotKeyListJobDone())); + connect(m_keylistjob, SIGNAL(nextKey(GpgME::Key)), + this, SLOT(slotNextKey(GpgME::Key))); + return true; +} + +void VerifyDetachedBodyPartMemento::slotNextKey(const GpgME::Key &key) +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotNextKey called"; +#endif + m_key = key; +} + +void VerifyDetachedBodyPartMemento::slotKeyListJobDone() +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotKeyListJobDone called"; +#endif + m_keylistjob = nullptr; + setRunning(false); + notify(); +} diff --git a/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.h b/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.h new file mode 100644 index 00000000..f37dfe81 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/verifydetachedbodypartmemento.h @@ -0,0 +1,87 @@ +/* + Copyright (c) 2014-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__ +#define __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__ + +#include "cryptobodypartmemento.h" +#include +#include + +#include +#include + +#include "bodypart.h" + +namespace QGpgME +{ +class VerifyDetachedJob; +class KeyListJob; +} + +class QStringList; + +namespace MimeTreeParser +{ + +class VerifyDetachedBodyPartMemento + : public CryptoBodyPartMemento +{ + Q_OBJECT +public: + VerifyDetachedBodyPartMemento(QGpgME::VerifyDetachedJob *job, + QGpgME::KeyListJob *klj, + const QByteArray &signature, + const QByteArray &plainText); + ~VerifyDetachedBodyPartMemento(); + + bool start() Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + + const GpgME::VerificationResult &verifyResult() const + { + return m_vr; + } + const GpgME::Key &signingKey() const + { + return m_key; + } + +private Q_SLOTS: + void slotResult(const GpgME::VerificationResult &vr); + void slotKeyListJobDone(); + void slotNextKey(const GpgME::Key &); + +private: + void saveResult(const GpgME::VerificationResult &); + bool canStartKeyListJob() const; + QStringList keyListPattern() const; + bool startKeyListJob(); +private: + // input: + const QByteArray m_signature; + const QByteArray m_plainText; + QPointer m_job; + QPointer m_keylistjob; + // output: + GpgME::VerificationResult m_vr; + GpgME::Key m_key; +}; + +} + +#endif // __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__ diff --git a/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.cpp b/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.cpp new file mode 100644 index 00000000..99eb8b8e --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.cpp @@ -0,0 +1,179 @@ +/* + Copyright (c) 2014-2017 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "verifyopaquebodypartmemento.h" +#include "mimetreeparser_debug.h" + +#include +#include + +#include + +#include + +#include + +using namespace QGpgME; +using namespace GpgME; +using namespace MimeTreeParser; + +VerifyOpaqueBodyPartMemento::VerifyOpaqueBodyPartMemento(VerifyOpaqueJob *job, + KeyListJob *klj, + const QByteArray &signature) + : CryptoBodyPartMemento(), + m_signature(signature), + m_job(job), + m_keylistjob(klj) +{ + assert(m_job); +} + +VerifyOpaqueBodyPartMemento::~VerifyOpaqueBodyPartMemento() +{ + if (m_job) { + m_job->slotCancel(); + } + if (m_keylistjob) { + m_keylistjob->slotCancel(); + } +} + +bool VerifyOpaqueBodyPartMemento::start() +{ + assert(m_job); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento started"; +#endif + if (const Error err = m_job->start(m_signature)) { + m_vr = VerificationResult(err); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento stopped with error"; +#endif + return false; + } + connect(m_job, SIGNAL(result(GpgME::VerificationResult,QByteArray)), + this, SLOT(slotResult(GpgME::VerificationResult,QByteArray))); + setRunning(true); + return true; +} + +void VerifyOpaqueBodyPartMemento::exec() +{ + assert(m_job); + setRunning(true); + QByteArray plainText; +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento execed"; +#endif + saveResult(m_job->exec(m_signature, plainText), plainText); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento after execed"; +#endif + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = nullptr; + if (canStartKeyListJob()) { + std::vector keys; + m_keylistjob->exec(keyListPattern(), /*secretOnly=*/false, keys); + if (!keys.empty()) { + m_key = keys.back(); + } + } + if (m_keylistjob) { + m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves + } + m_keylistjob = nullptr; + setRunning(false); +} + +bool VerifyOpaqueBodyPartMemento::canStartKeyListJob() const +{ + if (!m_keylistjob) { + return false; + } + const char *const fpr = m_vr.signature(0).fingerprint(); + return fpr && *fpr; +} + +QStringList VerifyOpaqueBodyPartMemento::keyListPattern() const +{ + assert(canStartKeyListJob()); + return QStringList(QString::fromLatin1(m_vr.signature(0).fingerprint())); +} + +void VerifyOpaqueBodyPartMemento::saveResult(const VerificationResult &vr, + const QByteArray &plainText) +{ + assert(m_job); +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::saveResult called"; +#endif + m_vr = vr; + m_plainText = plainText; + setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml()); +} + +void VerifyOpaqueBodyPartMemento::slotResult(const VerificationResult &vr, + const QByteArray &plainText) +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotResult called"; +#endif + saveResult(vr, plainText); + m_job = nullptr; + if (canStartKeyListJob() && startKeyListJob()) { +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento: canStartKeyListJob && startKeyListJob"; +#endif + return; + } + if (m_keylistjob) { + m_keylistjob->deleteLater(); + } + m_keylistjob = nullptr; + setRunning(false); + notify(); +} + +bool VerifyOpaqueBodyPartMemento::startKeyListJob() +{ + assert(canStartKeyListJob()); + if (const GpgME::Error err = m_keylistjob->start(keyListPattern())) { + return false; + } + connect(m_keylistjob, SIGNAL(done()), this, SLOT(slotKeyListJobDone())); + connect(m_keylistjob, SIGNAL(nextKey(GpgME::Key)), + this, SLOT(slotNextKey(GpgME::Key))); + return true; +} + +void VerifyOpaqueBodyPartMemento::slotNextKey(const GpgME::Key &key) +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotNextKey called"; +#endif + m_key = key; +} + +void VerifyOpaqueBodyPartMemento::slotKeyListJobDone() +{ +#ifdef DEBUG_SIGNATURE + qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotKeyListJobDone called"; +#endif + m_keylistjob = nullptr; + setRunning(false); + notify(); +} diff --git a/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.h b/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.h new file mode 100644 index 00000000..02d30a13 --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/verifyopaquebodypartmemento.h @@ -0,0 +1,93 @@ +/* + Copyright (c) 2014-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__ +#define __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__ + +#include "cryptobodypartmemento.h" +#include +#include +#include + +#include +#include + +#include "bodypart.h" + +namespace QGpgME +{ +class VerifyOpaqueJob; +class KeyListJob; +} + +class QStringList; + +namespace MimeTreeParser +{ + +class VerifyOpaqueBodyPartMemento + : public CryptoBodyPartMemento +{ + Q_OBJECT +public: + VerifyOpaqueBodyPartMemento(QGpgME::VerifyOpaqueJob *job, + QGpgME::KeyListJob *klj, + const QByteArray &signature); + ~VerifyOpaqueBodyPartMemento(); + + bool start() Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + + const QByteArray &plainText() const + { + return m_plainText; + } + const GpgME::VerificationResult &verifyResult() const + { + return m_vr; + } + const GpgME::Key &signingKey() const + { + return m_key; + } + +private Q_SLOTS: + void slotResult(const GpgME::VerificationResult &vr, + const QByteArray &plainText); + void slotKeyListJobDone(); + void slotNextKey(const GpgME::Key &); + +private: + void saveResult(const GpgME::VerificationResult &, + const QByteArray &); + bool canStartKeyListJob() const; + QStringList keyListPattern() const; + bool startKeyListJob(); +private: + // input: + const QByteArray m_signature; + QPointer m_job; + QPointer m_keylistjob; + // output: + GpgME::VerificationResult m_vr; + QByteArray m_plainText; + GpgME::Key m_key; +}; + +} + +#endif // __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__ diff --git a/framework/src/domain/mimetreeparser/stringhtmlwriter.h b/framework/src/domain/mimetreeparser/stringhtmlwriter.h index fa5b760e..20f6763e 100644 --- a/framework/src/domain/mimetreeparser/stringhtmlwriter.h +++ b/framework/src/domain/mimetreeparser/stringhtmlwriter.h @@ -30,7 +30,7 @@ #ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ #define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ -#include +#include #include #include diff --git a/framework/src/domain/mimetreeparser/tests/CMakeLists.txt b/framework/src/domain/mimetreeparser/tests/CMakeLists.txt index 71afb903..a7f28bb1 100644 --- a/framework/src/domain/mimetreeparser/tests/CMakeLists.txt +++ b/framework/src/domain/mimetreeparser/tests/CMakeLists.txt @@ -11,13 +11,13 @@ include(ECMAddTests) add_executable(mimetreeparsertest interfacetest.cpp) add_gpg_crypto_test(mimetreeparsertest mimetreeparsertest) qt5_use_modules(mimetreeparsertest Core Test) -target_link_libraries(mimetreeparsertest mimetreeparser) +target_link_libraries(mimetreeparsertest mimetreeparser kube_otp) -find_package(Gpgmepp 1.7.1 CONFIG) -find_package(QGpgme 1.7.1 CONFIG) - -ecm_add_test(gpgerrortest.cpp - TEST_NAME "gpgerrortest" - NAME_PREFIX "mimetreeparser-" - LINK_LIBRARIES Qt5::Core Qt5::Test mimetreeparser Gpgmepp QGpgme -) +#find_package(Gpgmepp 1.7.1 CONFIG) +#find_package(QGpgme 1.7.1 CONFIG) +# +#ecm_add_test(gpgerrortest.cpp +# TEST_NAME "gpgerrortest" +# NAME_PREFIX "mimetreeparser-" +# LINK_LIBRARIES Qt5::Core Qt5::Test mimetreeparser Gpgmepp QGpgme +#) diff --git a/framework/src/domain/objecttreesource.cpp b/framework/src/domain/objecttreesource.cpp index 567f3516..186fdf80 100644 --- a/framework/src/domain/objecttreesource.cpp +++ b/framework/src/domain/objecttreesource.cpp @@ -19,10 +19,10 @@ #include "objecttreesource.h" -#include -#include -#include -#include +#include +#include +#include +#include class ObjectSourcePrivate { diff --git a/framework/src/domain/objecttreesource.h b/framework/src/domain/objecttreesource.h index 93812dc3..2167e06f 100644 --- a/framework/src/domain/objecttreesource.h +++ b/framework/src/domain/objecttreesource.h @@ -20,7 +20,7 @@ #ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H #define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H -#include +#include class QString; diff --git a/framework/src/domain/stringhtmlwriter.h b/framework/src/domain/stringhtmlwriter.h index fa5b760e..20f6763e 100644 --- a/framework/src/domain/stringhtmlwriter.h +++ b/framework/src/domain/stringhtmlwriter.h @@ -30,7 +30,7 @@ #ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ #define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ -#include +#include #include #include -- cgit v1.2.3