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 }