File indexing completed on 2025-03-09 04:54:40

0001 /*  -*- c++ -*-
0002     attachmentstrategy.cpp
0003 
0004     This file is part of KMail, the KDE mail client.
0005     SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
0006     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
0007     SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "attachmentstrategy.h"
0013 
0014 #include <MimeTreeParser/NodeHelper>
0015 #include <MimeTreeParser/Util>
0016 
0017 #include <KMime/Content>
0018 
0019 #include <QIcon>
0020 
0021 #include "messageviewer_debug.h"
0022 
0023 using namespace MessageViewer;
0024 
0025 static AttachmentStrategy::Display smartDisplay(KMime::Content *node)
0026 {
0027     const auto cd = node->contentDisposition(false);
0028     if (cd) {
0029         if (cd->disposition() == KMime::Headers::CDinline) {
0030             // explicit "inline" disposition:
0031             return AttachmentStrategy::Inline;
0032         }
0033         if (cd->disposition() == KMime::Headers::CDattachment) {
0034             // explicit "attachment" disposition:
0035             return AttachmentStrategy::AsIcon;
0036         }
0037     }
0038 
0039     const auto ct = node->contentType(false);
0040     if (ct && ct->isText() && ct->name().trimmed().isEmpty() && (!cd || cd->filename().trimmed().isEmpty())) {
0041         // text/* w/o filename parameter:
0042         return AttachmentStrategy::Inline;
0043     }
0044     return AttachmentStrategy::AsIcon;
0045 }
0046 
0047 //
0048 // IconicAttachmentStrategy:
0049 //   show everything but the first text/plain body as icons
0050 //
0051 
0052 class IconicAttachmentStrategy : public AttachmentStrategy
0053 {
0054     friend class AttachmentStrategy;
0055 
0056 protected:
0057     IconicAttachmentStrategy()
0058         : AttachmentStrategy()
0059     {
0060     }
0061 
0062     ~IconicAttachmentStrategy() override = default;
0063 
0064 public:
0065     [[nodiscard]] const char *name() const override
0066     {
0067         return "iconic";
0068     }
0069 
0070     [[nodiscard]] bool inlineNestedMessages() const override
0071     {
0072         return false;
0073     }
0074 
0075     Display defaultDisplay(KMime::Content *node) const override
0076     {
0077         if (node->contentType()->isText()
0078             && (!node->parent() || (node->contentDisposition()->filename().trimmed().isEmpty() && node->contentType(false)->name().trimmed().isEmpty()))) {
0079             // text/* w/o filename parameter:
0080             return Inline;
0081         }
0082         return AsIcon;
0083     }
0084 };
0085 
0086 //
0087 // SmartAttachmentStrategy:
0088 //   in addition to Iconic, show all body parts
0089 //   with content-disposition == "inline" and
0090 //   all text parts without a filename or name parameter inline
0091 //
0092 
0093 class SmartAttachmentStrategy : public AttachmentStrategy
0094 {
0095     friend class AttachmentStrategy;
0096 
0097 protected:
0098     SmartAttachmentStrategy()
0099         : AttachmentStrategy()
0100     {
0101     }
0102 
0103     ~SmartAttachmentStrategy() override = default;
0104 
0105 public:
0106     [[nodiscard]] const char *name() const override
0107     {
0108         return "smart";
0109     }
0110 
0111     [[nodiscard]] bool inlineNestedMessages() const override
0112     {
0113         return true;
0114     }
0115 
0116     Display defaultDisplay(KMime::Content *node) const override
0117     {
0118         return smartDisplay(node);
0119     }
0120 };
0121 
0122 //
0123 // InlinedAttachmentStrategy:
0124 //   show everything possible inline
0125 //
0126 
0127 class InlinedAttachmentStrategy : public AttachmentStrategy
0128 {
0129     friend class AttachmentStrategy;
0130 
0131 protected:
0132     InlinedAttachmentStrategy()
0133         : AttachmentStrategy()
0134     {
0135     }
0136 
0137     ~InlinedAttachmentStrategy() override = default;
0138 
0139 public:
0140     [[nodiscard]] const char *name() const override
0141     {
0142         return "inlined";
0143     }
0144 
0145     [[nodiscard]] bool inlineNestedMessages() const override
0146     {
0147         return true;
0148     }
0149 
0150     Display defaultDisplay(KMime::Content *) const override
0151     {
0152         return Inline;
0153     }
0154 };
0155 
0156 //
0157 // HiddenAttachmentStrategy
0158 //   show nothing except the first text/plain body part _at all_
0159 //
0160 
0161 class HiddenAttachmentStrategy : public AttachmentStrategy
0162 {
0163     friend class AttachmentStrategy;
0164 
0165 protected:
0166     HiddenAttachmentStrategy()
0167         : AttachmentStrategy()
0168     {
0169     }
0170 
0171     ~HiddenAttachmentStrategy() override = default;
0172 
0173 public:
0174     [[nodiscard]] const char *name() const override
0175     {
0176         return "hidden";
0177     }
0178 
0179     [[nodiscard]] bool inlineNestedMessages() const override
0180     {
0181         return false;
0182     }
0183 
0184     Display defaultDisplay(KMime::Content *node) const override
0185     {
0186         if (node->contentType()->isText() && node->contentDisposition()->filename().trimmed().isEmpty()
0187             && node->contentType(false)->name().trimmed().isEmpty()) {
0188             // text/* w/o filename parameter:
0189             return Inline;
0190         }
0191         if (!node->parent()) {
0192             return Inline;
0193         }
0194 
0195         if (node->parent() && node->parent()->contentType()->isMultipart() && node->parent()->contentType(false)->subType() == "related") {
0196             return Inline;
0197         }
0198 
0199         return None;
0200     }
0201 };
0202 
0203 class HeaderOnlyAttachmentStrategy : public AttachmentStrategy
0204 {
0205     friend class AttachmentStrategy;
0206 
0207 protected:
0208     HeaderOnlyAttachmentStrategy()
0209         : AttachmentStrategy()
0210     {
0211     }
0212 
0213     ~HeaderOnlyAttachmentStrategy() override = default;
0214 
0215 public:
0216     [[nodiscard]] const char *name() const override
0217     {
0218         return "headerOnly";
0219     }
0220 
0221     [[nodiscard]] bool inlineNestedMessages() const override
0222     {
0223         return true;
0224     }
0225 
0226     Display defaultDisplay(KMime::Content *node) const override
0227     {
0228         if (MimeTreeParser::NodeHelper::isInEncapsulatedMessage(node)) {
0229             return smartDisplay(node);
0230         }
0231 
0232         if (!MimeTreeParser::Util::labelForContent(node).isEmpty() && QIcon::hasThemeIcon(MimeTreeParser::Util::iconNameForContent(node))
0233             && !MimeTreeParser::Util::isTypeBlacklisted(node)) {
0234             return None;
0235         }
0236         return smartDisplay(node);
0237     }
0238 
0239     [[nodiscard]] bool requiresAttachmentListInHeader() const override
0240     {
0241         return true;
0242     }
0243 };
0244 
0245 //
0246 // AttachmentStrategy abstract base:
0247 //
0248 
0249 AttachmentStrategy::AttachmentStrategy() = default;
0250 
0251 AttachmentStrategy::~AttachmentStrategy() = default;
0252 
0253 const AttachmentStrategy *AttachmentStrategy::create(Type type)
0254 {
0255     switch (type) {
0256     case Iconic:
0257         return iconic();
0258     case Smart:
0259         return smart();
0260     case Inlined:
0261         return inlined();
0262     case Hidden:
0263         return hidden();
0264     case HeaderOnly:
0265         return headerOnly();
0266     }
0267     qCCritical(MESSAGEVIEWER_LOG) << "Unknown attachment strategy ( type ==" << static_cast<int>(type) << ") requested!";
0268     return nullptr; // make compiler happy
0269 }
0270 
0271 const AttachmentStrategy *AttachmentStrategy::create(const QString &type)
0272 {
0273     const QString lowerType = type.toLower();
0274     if (lowerType == QLatin1StringView("iconic")) {
0275         return iconic();
0276     }
0277     // if ( lowerType == "smart" )    return smart(); // not needed, see below
0278     if (lowerType == QLatin1StringView("inlined")) {
0279         return inlined();
0280     }
0281     if (lowerType == QLatin1StringView("hidden")) {
0282         return hidden();
0283     }
0284     if (lowerType == QLatin1StringView("headeronly")) {
0285         return headerOnly();
0286     }
0287     // don't kFatal here, b/c the strings are user-provided
0288     // (KConfig), so fail gracefully to the default:
0289     return smart();
0290 }
0291 
0292 static const AttachmentStrategy *iconicStrategy = nullptr;
0293 static const AttachmentStrategy *smartStrategy = nullptr;
0294 static const AttachmentStrategy *inlinedStrategy = nullptr;
0295 static const AttachmentStrategy *hiddenStrategy = nullptr;
0296 static const AttachmentStrategy *headerOnlyStrategy = nullptr;
0297 
0298 const AttachmentStrategy *AttachmentStrategy::iconic()
0299 {
0300     if (!iconicStrategy) {
0301         iconicStrategy = new IconicAttachmentStrategy();
0302     }
0303     return iconicStrategy;
0304 }
0305 
0306 const AttachmentStrategy *AttachmentStrategy::smart()
0307 {
0308     if (!smartStrategy) {
0309         smartStrategy = new SmartAttachmentStrategy();
0310     }
0311     return smartStrategy;
0312 }
0313 
0314 const AttachmentStrategy *AttachmentStrategy::inlined()
0315 {
0316     if (!inlinedStrategy) {
0317         inlinedStrategy = new InlinedAttachmentStrategy();
0318     }
0319     return inlinedStrategy;
0320 }
0321 
0322 const AttachmentStrategy *AttachmentStrategy::hidden()
0323 {
0324     if (!hiddenStrategy) {
0325         hiddenStrategy = new HiddenAttachmentStrategy();
0326     }
0327     return hiddenStrategy;
0328 }
0329 
0330 const AttachmentStrategy *AttachmentStrategy::headerOnly()
0331 {
0332     if (!headerOnlyStrategy) {
0333         headerOnlyStrategy = new HeaderOnlyAttachmentStrategy();
0334     }
0335     return headerOnlyStrategy;
0336 }
0337 
0338 bool AttachmentStrategy::requiresAttachmentListInHeader() const
0339 {
0340     return false;
0341 }