summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mimeparser.cpp15
-rw-r--r--src/mimeparser.h11
-rw-r--r--tests/test_mimeparser.cpp23
3 files changed, 44 insertions, 5 deletions
diff --git a/src/mimeparser.cpp b/src/mimeparser.cpp
index ecf56f1..2782a4a 100644
--- a/src/mimeparser.cpp
+++ b/src/mimeparser.cpp
@@ -272,20 +272,25 @@ QString recipientSummary(const QString &rawTo, int maxNames)
return summary;
}
-QString attachmentFolderName(const QString &rfc822Date, const QString &subject)
+QDateTime MimeParser::parseDate(const QString &rfc822Date)
{
- // The date prefix sorts chronologically in a file manager. A Date: header
- // that does not parse is simply dropped rather than guessed at.
// A trailing timezone comment, "... +0200 (CEST)", is legal per RFC 5322
// and common in the wild, but Qt::RFC2822Date rejects the whole string
// when one is present (verified on Qt 6.11). Strip comments before
- // parsing, or every such message silently loses its date prefix.
+ // parsing, or every such message silently loses its date.
QString cleaned = rfc822Date;
cleaned.remove(QRegularExpression(QStringLiteral("\\s*\\([^)]*\\)")));
cleaned = cleaned.trimmed();
+ return QDateTime::fromString(cleaned, Qt::RFC2822Date);
+}
+
+QString attachmentFolderName(const QString &rfc822Date, const QString &subject)
+{
+ // The date prefix sorts chronologically in a file manager. A Date: header
+ // that does not parse is simply dropped rather than guessed at.
QString prefix;
- const QDateTime parsed = QDateTime::fromString(cleaned, Qt::RFC2822Date);
+ const QDateTime parsed = MimeParser::parseDate(rfc822Date);
if (parsed.isValid())
prefix = parsed.toString(QStringLiteral("yyyy-MM-dd"));
diff --git a/src/mimeparser.h b/src/mimeparser.h
index 9fceb2f..da54434 100644
--- a/src/mimeparser.h
+++ b/src/mimeparser.h
@@ -19,6 +19,7 @@
#pragma once
#include <QByteArray>
+#include <QDateTime>
#include <QHash>
#include <QList>
#include <QString>
@@ -143,4 +144,14 @@ public:
MimeParser();
ParsedMessage parse(const QString &filePath) const;
+
+ /// Parses an RFC 2822 `Date:` header, returning an invalid QDateTime when
+ /// nothing usable is there.
+ ///
+ /// **Strips comments before parsing**, because `Qt::RFC2822Date` rejects
+ /// the entire string when a trailing timezone comment is present, and
+ /// `... +0200 (CEST)` is legal per RFC 5322 and common in the wild
+ /// (verified on Qt 6.11). A parser without this silently loses the date on
+ /// a large share of real mail.
+ static QDateTime parseDate(const QString &rfc822Date);
};
diff --git a/tests/test_mimeparser.cpp b/tests/test_mimeparser.cpp
index bac71f6..af6307e 100644
--- a/tests/test_mimeparser.cpp
+++ b/tests/test_mimeparser.cpp
@@ -47,6 +47,7 @@ private slots:
void recipientSummarySurvivesUnusableInput();
void folderNameSurvivesATimezoneComment();
void savingABatchNeverOverwrites();
+ void parsesADateWithATimezoneComment();
private:
QString fixture(const QString &name) const
@@ -485,5 +486,27 @@ void TestMimeParser::recipientSummarySurvivesUnusableInput()
recipientSummary(QStringLiteral("\"unterminated <a@example.org>"));
}
+void TestMimeParser::parsesADateWithATimezoneComment()
+{
+ // Qt::RFC2822Date rejects the WHOLE string when a trailing comment is
+ // present (verified on Qt 6.11), and "+0200 (CEST)" is both legal and
+ // common. Without the comment stripped, every such message loses its date
+ // silently: the attachment folder loses its prefix, and a date search
+ // offers nothing with no indication why.
+ const QDateTime withComment = MimeParser::parseDate(
+ QStringLiteral("Fri, 14 Aug 2026 09:30:00 +0200 (CEST)"));
+ QVERIFY(withComment.isValid());
+ QCOMPARE(withComment.date(), QDate(2026, 8, 14));
+
+ const QDateTime plain = MimeParser::parseDate(
+ QStringLiteral("Fri, 14 Aug 2026 09:30:00 +0200"));
+ QVERIFY(plain.isValid());
+ QCOMPARE(plain.date(), QDate(2026, 8, 14));
+
+ // Nothing usable is an invalid QDateTime, never a guess.
+ QVERIFY(!MimeParser::parseDate(QStringLiteral("last Tuesday")).isValid());
+ QVERIFY(!MimeParser::parseDate(QString()).isValid());
+}
+
QTEST_MAIN(TestMimeParser)
#include "test_mimeparser.moc"