Skip to content

Commit a5996e1

Browse files
mattsteinKMKoushik
authored andcommitted
fix: forward List-Unsubscribe headers through SMTP proxy
1 parent 89cba7f commit a5996e1

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

apps/smtp-server/src/server.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,39 @@ const SSL_KEY_PATH =
1616
const SSL_CERT_PATH =
1717
process.env.USESEND_API_CERT_PATH ?? process.env.UNSEND_API_CERT_PATH;
1818

19+
// Forwarded headers that mailparser's normalized Map can't be trusted for.
20+
// Read from headerLines (raw) instead.
21+
const FORWARDED_HEADERS = ["list-unsubscribe", "list-unsubscribe-post"];
22+
23+
function canonicalHeaderName(name: string): string {
24+
return name
25+
.split("-")
26+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
27+
.join("-");
28+
}
29+
30+
function extractForwardedHeaders(
31+
headerLines: readonly { key: string; line: string }[] | undefined,
32+
): Record<string, string> | undefined {
33+
const result: Record<string, string> = {};
34+
35+
for (const { key, line } of headerLines || []) {
36+
if (!FORWARDED_HEADERS.includes(key)) {
37+
continue;
38+
}
39+
const colonIndex = line.indexOf(":");
40+
if (colonIndex === -1) {
41+
continue;
42+
}
43+
const value = line.slice(colonIndex + 1).trim();
44+
if (value.length > 0) {
45+
result[canonicalHeaderName(key)] = value;
46+
}
47+
}
48+
49+
return Object.keys(result).length > 0 ? result : undefined;
50+
}
51+
1952
async function sendEmailToUseSend(emailData: any, apiKey: string) {
2053
try {
2154
const apiEndpoint = "/api/v1/emails";
@@ -88,6 +121,8 @@ const serverOptions: SMTPServerOptions = {
88121
return callback(new Error("No API key found in session"));
89122
}
90123

124+
const forwardedHeaders = extractForwardedHeaders(parsed.headerLines);
125+
91126
const emailObject = {
92127
to: Array.isArray(parsed.to)
93128
? parsed.to.map((addr) => addr.text).join(", ")
@@ -99,6 +134,7 @@ const serverOptions: SMTPServerOptions = {
99134
text: parsed.text,
100135
html: parsed.html,
101136
replyTo: parsed.replyTo?.text,
137+
headers: forwardedHeaders,
102138
attachments:
103139
parsed.attachments.length > 0
104140
? parsed.attachments.map((attachment, index) => ({

0 commit comments

Comments
 (0)