arn-rawmime 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/rawmimeBuilder.js
CHANGED
|
@@ -96,15 +96,17 @@ class MimeMessage {
|
|
|
96
96
|
currentLineLength = 0;
|
|
97
97
|
}
|
|
98
98
|
if (token.length > 75) {
|
|
99
|
-
// Hard split for giant tokens
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
result +=
|
|
106
|
-
currentLineLength =
|
|
99
|
+
// Hard split for giant tokens (Atom-aware)
|
|
100
|
+
// Find QP sequences (=XX) or single chars to avoid breaking a triplet
|
|
101
|
+
const atoms = token.match(/=[0-9A-F]{2}|./g) || [];
|
|
102
|
+
atoms.forEach((atom) => {
|
|
103
|
+
// Check if adding this atom exceeds the safety limit (75 to leave room for soft break '=')
|
|
104
|
+
if (currentLineLength + atom.length > 75) {
|
|
105
|
+
result += "=\r\n";
|
|
106
|
+
currentLineLength = 0;
|
|
107
107
|
}
|
|
108
|
+
result += atom;
|
|
109
|
+
currentLineLength += atom.length;
|
|
108
110
|
});
|
|
109
111
|
} else {
|
|
110
112
|
result += token;
|
|
@@ -123,6 +125,32 @@ class MimeMessage {
|
|
|
123
125
|
|
|
124
126
|
// ─── HELPER: Header Folding (RFC 5322) ──────────────────────────
|
|
125
127
|
_foldHeader(name, value) {
|
|
128
|
+
const hasNonAscii = /[^\x00-\x7F]/.test(value);
|
|
129
|
+
|
|
130
|
+
// 1. Unstructured headers (Subject, etc) -> Full Encode
|
|
131
|
+
const unstructured = ["subject", "x-report-abuse", "thread-topic"];
|
|
132
|
+
if (hasNonAscii && unstructured.includes(name.toLowerCase())) {
|
|
133
|
+
const encodedValue = Buffer.from(value, "utf8").toString("base64");
|
|
134
|
+
return `${name}: =?UTF-8?B?${encodedValue}?=`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 2. Structured headers (From, To) -> Smart Replace
|
|
138
|
+
// Finds quoted strings with special chars, e.g. "René", and encodes JUST that part.
|
|
139
|
+
if (hasNonAscii) {
|
|
140
|
+
const encodedStruct = value.replace(/"([^"]*)"/g, (match, content) => {
|
|
141
|
+
if (/[^\x00-\x7F]/.test(content)) {
|
|
142
|
+
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
143
|
+
return `=?UTF-8?B?${b64}?=`;
|
|
144
|
+
}
|
|
145
|
+
return match;
|
|
146
|
+
});
|
|
147
|
+
// If we changed anything, return it. If not (e.g. unquoted special chars), fallback to old folding
|
|
148
|
+
if (encodedStruct !== value) {
|
|
149
|
+
return `${name}: ${encodedStruct}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 3. Standard folding for ASCII-only (or unhandled) headers
|
|
126
154
|
const line = `${name}: ${value}`;
|
|
127
155
|
if (line.length <= 76) return line;
|
|
128
156
|
|
|
@@ -71,6 +71,8 @@ export interface ParsedMailData {
|
|
|
71
71
|
trafficSource: "dbr-w4m" | "dbr-m4w" | "other";
|
|
72
72
|
/** Status, set to 'bounce' if email is a bounce notification */
|
|
73
73
|
status: "bounce" | undefined;
|
|
74
|
+
/** First @username handle found in the HTML body (without @ prefix, trimmed), or null if none found */
|
|
75
|
+
username: string | null;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
/**
|
|
@@ -161,6 +161,22 @@ export const parseMail = async ({ rawData }) => {
|
|
|
161
161
|
}
|
|
162
162
|
if (!originalPostId) originalPostId = subjectPostId;
|
|
163
163
|
|
|
164
|
+
// --- 7b. Extract Username (first @handle in HTML, fallback to text) ---
|
|
165
|
+
let username = null;
|
|
166
|
+
const usernameRegex = /(?<![a-zA-Z0-9._%+-])@([a-zA-Z0-9_]+)/;
|
|
167
|
+
if (html) {
|
|
168
|
+
const usernameMatch = html.match(usernameRegex);
|
|
169
|
+
if (usernameMatch && usernameMatch[1]) {
|
|
170
|
+
username = usernameMatch[1].trim();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (!username && text) {
|
|
174
|
+
const usernameMatch = text.match(usernameRegex);
|
|
175
|
+
if (usernameMatch && usernameMatch[1]) {
|
|
176
|
+
username = usernameMatch[1].trim();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
// --- 8. Traffic Source ---
|
|
165
181
|
const isOfficialMailer = fromEmail === "mailer@mailersp.doublelist.com" || fromEmail === "robot@doublelist.com";
|
|
166
182
|
if (isOfficialMailer && mail.replyTo?.value[0]?.address) {
|
|
@@ -207,6 +223,7 @@ export const parseMail = async ({ rawData }) => {
|
|
|
207
223
|
envelopeToDomain,
|
|
208
224
|
trafficSource,
|
|
209
225
|
status,
|
|
226
|
+
username,
|
|
210
227
|
},
|
|
211
228
|
error: null,
|
|
212
229
|
};
|