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 }