summaryrefslogtreecommitdiffstats
path: root/src/cidschemehandler.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-02 17:46:21 +0200
committerDanilo M. <danix@danix.xyz>2026-08-02 17:46:21 +0200
commit2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd (patch)
treefaec2dc7b5d9be72fac5e091b81eed3491d3c2d6 /src/cidschemehandler.cpp
parent8ecd5a8e83380f543b856ff28226a8e15fd7dc96 (diff)
downloadqtmaildir-2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd.tar.gz
qtmaildir-2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd.zip
feat: add HTML builder and cid: scheme handler
HtmlBuilder renders parsed messages (and whole threads, as one document, so newsletter threads don't spawn one Chromium process per message) into the HTML string the web view loads. Plain text is escaped and quote lines marked; the cid: rewrite is namespaced per message ("<prefix>!<id>") so two thread messages sharing a Content-ID don't collide. Hardened namespaceCids beyond the initial sketch after attacking it: handles unquoted cid: attribute values, background=/poster= (not just src/href), and CSS url(cid:...) in both style="" attributes and <style> blocks, all case-insensitively. Replaced the greedy [^"']+ capture with per-quote-style alternation so two cid: refs on one line can't bleed into each other. CidSchemeHandler serves cid: requests from the thread's inline-parts map, keyed by the same namespaced string, replaced wholesale per thread.
Diffstat (limited to 'src/cidschemehandler.cpp')
-rw-r--r--src/cidschemehandler.cpp36
1 files changed, 36 insertions, 0 deletions
diff --git a/src/cidschemehandler.cpp b/src/cidschemehandler.cpp
new file mode 100644
index 0000000..b4bb836
--- /dev/null
+++ b/src/cidschemehandler.cpp
@@ -0,0 +1,36 @@
+#include "cidschemehandler.h"
+
+#include <QBuffer>
+#include <QWebEngineUrlRequestJob>
+
+CidSchemeHandler::CidSchemeHandler(QObject *parent)
+ : QWebEngineUrlSchemeHandler(parent)
+{
+}
+
+void CidSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
+{
+ // QUrl::path() returns the percent-DECODED body of a cid: URL (confirmed
+ // in Task 5's RequestInterceptor tests, and re-verified here for the
+ // namespaced "<prefix>!<id>" form specifically: '!' needs no percent
+ // escaping per RFC 3986 sub-delims, and even when a Content-ID's own
+ // characters are percent-encoded by the sender, decoding is idempotent
+ // with how the prefix was concatenated in HtmlBuilder::namespaceCids, so
+ // the string handed to path() here is exactly the map key that was
+ // inserted for this part).
+ const QString id = job->requestUrl().path();
+
+ if (!m_parts.contains(id)) {
+ job->fail(QWebEngineUrlRequestJob::UrlNotFound);
+ return;
+ }
+
+ const InlinePart part = m_parts.value(id);
+
+ // The buffer is parented to the job so it lives exactly as long as needed.
+ auto *buffer = new QBuffer(job);
+ buffer->setData(part.data);
+ buffer->open(QIODevice::ReadOnly);
+
+ job->reply(part.mimeType.toUtf8(), buffer);
+}