summaryrefslogtreecommitdiffstats
path: root/src/mimeparser.h
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-02 17:27:07 +0200
committerDanilo M. <danix@danix.xyz>2026-08-04 12:52:21 +0200
commit5f22f80b6b5cef5e768086338ae4b22120f3e67c (patch)
treee127827da5a248fd8e6c80397f896a1f6d302ac0 /src/mimeparser.h
parent72b56d0beca6d640c724eabc73a3ee45cf04bfea (diff)
downloadqtmaildir-5f22f80b6b5cef5e768086338ae4b22120f3e67c.tar.gz
qtmaildir-5f22f80b6b5cef5e768086338ae4b22120f3e67c.zip
feat: add MimeParser with GMime and safe attachment naming
Diffstat (limited to 'src/mimeparser.h')
-rw-r--r--src/mimeparser.h64
1 files changed, 64 insertions, 0 deletions
diff --git a/src/mimeparser.h b/src/mimeparser.h
new file mode 100644
index 0000000..27a239f
--- /dev/null
+++ b/src/mimeparser.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <QByteArray>
+#include <QHash>
+#include <QList>
+#include <QString>
+
+/// An inline part referenced by a cid: URL from the HTML body.
+struct InlinePart
+{
+ QString mimeType;
+ QByteArray data;
+};
+
+struct Attachment
+{
+ QString filename; ///< As it appeared in the message. Untrusted.
+ QString mimeType;
+ QByteArray data;
+
+ /// filename reduced to a basename safe to join onto a directory.
+ /// Attacker-controlled input: a filename may contain path separators or
+ /// "..", so anything that could escape the target directory is stripped.
+ /// Returns a generated name when nothing usable remains.
+ QString safeFilename() const;
+
+ /// Writes the attachment into directory. Returns the full path written, or
+ /// an empty string on failure with *error set.
+ QString saveTo(const QString &directory, QString *error) const;
+};
+
+struct ParsedMessage
+{
+ bool ok = false;
+ QString error;
+
+ QString subject;
+ QString from;
+ QString to;
+ QString cc;
+ QString date;
+ QString messageId;
+
+ QString plainBody;
+ QString htmlBody;
+
+ QHash<QString, InlinePart> inlineParts; ///< Keyed by Content-ID, no <>.
+ QList<Attachment> attachments;
+
+ bool hasHtml() const { return !htmlBody.isEmpty(); }
+};
+
+/// Parses a single message file using GMime.
+///
+/// Hand-rolling this would mean reimplementing RFC 2047 encoded words, RFC 2231
+/// parameter continuations, transfer encodings, and charset conversion, plus
+/// tolerance for malformed real-world mail.
+class MimeParser
+{
+public:
+ MimeParser();
+
+ ParsedMessage parse(const QString &filePath) const;
+};