File indexing completed on 2024-06-23 05:19:20
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sandro Knauß <sknauss@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "mailman.h" 0008 0009 #include "utils.h" 0010 0011 #include "messagepart.h" 0012 #include "objecttreeparser.h" 0013 0014 #include <KMime/Content> 0015 0016 #include "mimetreeparser_debug.h" 0017 0018 using namespace MimeTreeParser; 0019 0020 const MailmanBodyPartFormatter *MailmanBodyPartFormatter::self; 0021 0022 const Interface::BodyPartFormatter *MailmanBodyPartFormatter::create() 0023 { 0024 if (!self) { 0025 self = new MailmanBodyPartFormatter(); 0026 } 0027 return self; 0028 } 0029 0030 bool MailmanBodyPartFormatter::isMailmanMessage(KMime::Content *curNode) const 0031 { 0032 if (!curNode || curNode->head().isEmpty()) { 0033 return false; 0034 } 0035 if (curNode->hasHeader("X-Mailman-Version")) { 0036 return true; 0037 } 0038 if (KMime::Headers::Base *header = curNode->headerByType("X-Mailer")) { 0039 if (header->asUnicodeString().contains(QLatin1StringView("MAILMAN"), Qt::CaseInsensitive)) { 0040 return true; 0041 } 0042 } 0043 return false; 0044 } 0045 0046 MessagePart::Ptr MailmanBodyPartFormatter::process(Interface::BodyPart &part) const 0047 { 0048 KMime::Content *curNode = part.content(); 0049 0050 if (!isMailmanMessage(curNode)) { 0051 return {}; 0052 } 0053 0054 // Latin1 or utf8 ? 0055 const QString str = QString::fromLatin1(curNode->decodedContent()); 0056 0057 // ### 0058 const QLatin1StringView delim1("--__--__--\n\nMessage:"); 0059 const QLatin1StringView delim2("--__--__--\r\n\r\nMessage:"); 0060 const QLatin1StringView delimZ2("--__--__--\n\n_____________"); 0061 const QLatin1StringView delimZ1("--__--__--\r\n\r\n_____________"); 0062 QString partStr; 0063 QString digestHeaderStr; 0064 int thisDelim = str.indexOf(delim1, Qt::CaseInsensitive); 0065 if (thisDelim == -1) { 0066 thisDelim = str.indexOf(delim2, Qt::CaseInsensitive); 0067 } 0068 if (thisDelim == -1) { 0069 return {}; 0070 } 0071 0072 int nextDelim = str.indexOf(delim1, thisDelim + 1, Qt::CaseInsensitive); 0073 if (-1 == nextDelim) { 0074 nextDelim = str.indexOf(delim2, thisDelim + 1, Qt::CaseInsensitive); 0075 } 0076 if (-1 == nextDelim) { 0077 nextDelim = str.indexOf(delimZ1, thisDelim + 1, Qt::CaseInsensitive); 0078 } 0079 if (-1 == nextDelim) { 0080 nextDelim = str.indexOf(delimZ2, thisDelim + 1, Qt::CaseInsensitive); 0081 } 0082 if (nextDelim < 0) { 0083 return {}; 0084 } 0085 0086 // if ( curNode->mRoot ) 0087 // curNode = curNode->mRoot; 0088 0089 // at least one message found: build a mime tree 0090 digestHeaderStr = QStringLiteral("Content-Type: text/plain\nContent-Description: digest header\n\n"); 0091 digestHeaderStr += QStringView(str).mid(0, thisDelim); 0092 0093 MessagePartList::Ptr mpl(new MessagePartList(part.objectTreeParser())); 0094 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), digestHeaderStr.toLatin1().constData(), "Digest Header")); 0095 // mReader->queueHtml("<br><hr><br>"); 0096 // temporarily change current node's Content-Type 0097 // to get our embedded RfC822 messages properly inserted 0098 curNode->contentType()->setMimeType("multipart/digest"); 0099 while (-1 < nextDelim) { 0100 int thisEoL = str.indexOf(QLatin1StringView("\nMessage:"), thisDelim, Qt::CaseInsensitive); 0101 if (-1 < thisEoL) { 0102 thisDelim = thisEoL + 1; 0103 } else { 0104 thisEoL = str.indexOf(QLatin1StringView("\n_____________"), thisDelim, Qt::CaseInsensitive); 0105 if (-1 < thisEoL) { 0106 thisDelim = thisEoL + 1; 0107 } 0108 } 0109 thisEoL = str.indexOf(QLatin1Char('\n'), thisDelim); 0110 if (-1 < thisEoL) { 0111 thisDelim = thisEoL + 1; 0112 } else { 0113 thisDelim = thisDelim + 1; 0114 } 0115 // while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] ) 0116 // ++thisDelim; 0117 0118 partStr = QStringLiteral("Content-Type: message/rfc822\nContent-Description: embedded message\n\n"); 0119 partStr += QStringView(str).mid(thisDelim, nextDelim - thisDelim); 0120 QString subject = QStringLiteral("embedded message"); 0121 QString subSearch = QStringLiteral("\nSubject:"); 0122 int subPos = partStr.indexOf(subSearch, 0, Qt::CaseInsensitive); 0123 if (-1 < subPos) { 0124 subject = partStr.mid(subPos + subSearch.length()); 0125 thisEoL = subject.indexOf(QLatin1Char('\n')); 0126 if (-1 < thisEoL) { 0127 subject.truncate(thisEoL); 0128 } 0129 } 0130 qCDebug(MIMETREEPARSER_LOG) << " embedded message found: \"" << subject; 0131 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), partStr.toLatin1().constData(), subject.toLatin1().constData())); 0132 // mReader->queueHtml("<br><hr><br>"); 0133 thisDelim = nextDelim + 1; 0134 nextDelim = str.indexOf(delim1, thisDelim, Qt::CaseInsensitive); 0135 if (-1 == nextDelim) { 0136 nextDelim = str.indexOf(delim2, thisDelim, Qt::CaseInsensitive); 0137 } 0138 if (-1 == nextDelim) { 0139 nextDelim = str.indexOf(delimZ1, thisDelim, Qt::CaseInsensitive); 0140 } 0141 if (-1 == nextDelim) { 0142 nextDelim = str.indexOf(delimZ2, thisDelim, Qt::CaseInsensitive); 0143 } 0144 } 0145 // reset current node's Content-Type 0146 curNode->contentType()->setMimeType("text/plain"); 0147 int thisEoL = str.indexOf(QLatin1StringView("_____________"), thisDelim); 0148 if (-1 < thisEoL) { 0149 thisDelim = thisEoL; 0150 thisEoL = str.indexOf(QLatin1Char('\n'), thisDelim); 0151 if (-1 < thisEoL) { 0152 thisDelim = thisEoL + 1; 0153 } 0154 } else { 0155 thisDelim = thisDelim + 1; 0156 } 0157 partStr = QStringLiteral("Content-Type: text/plain\nContent-Description: digest footer\n\n"); 0158 partStr += QStringView(str).mid(thisDelim); 0159 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), partStr.toLatin1().constData(), "Digest Footer")); 0160 return mpl; 0161 }