arn-rawmime 0.1.9 → 0.1.11
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.d.ts
CHANGED
|
@@ -38,7 +38,12 @@ export class MimeMessage {
|
|
|
38
38
|
/**
|
|
39
39
|
* Sets the style of the multipart boundary separator.
|
|
40
40
|
*/
|
|
41
|
-
setBoundaryStyle(style: "
|
|
41
|
+
setBoundaryStyle(style: "google_hex" | "outlook_exchange" | "outlook_nextpart" | "apple_mail_equal" | "apple_mail_dash" | "apple_webmail" | "yahoo_part" | "samsung_android" | "edomail" | "hex_segments" | "custom_dashes"): void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sets the style of the Message-ID generation.
|
|
45
|
+
*/
|
|
46
|
+
setMessageIdStyle(style: "apple_uuid" | "yahoo_mobile" | "yahoo_web" | "gmail_ca" | "outlook_exchange" | "outlook_web" | "samsung_android" | "generic_uuid" | "timestamp_dot" | "icloud_mail" | "android_email" | "google_mx_relay" | "generic" | "none"): void;
|
|
42
47
|
|
|
43
48
|
/**
|
|
44
49
|
* Sets whether to preserve the exact insertion order of MIME parts.
|
package/src/rawmimeBuilder.js
CHANGED
|
@@ -5,6 +5,8 @@ import { randomBytes } from "crypto";
|
|
|
5
5
|
import { convert } from "html-to-text";
|
|
6
6
|
import { DKIMSign } from "./dkim-signer.js";
|
|
7
7
|
import { processMarkDown } from "./processMarkDown.js";
|
|
8
|
+
import { generateBoundary, ALLOWED_BOUNDARY_STYLES } from "./utility/boundaryGenerator.js";
|
|
9
|
+
import { generateMessageId, ALLOWED_MESSAGE_ID_STYLES } from "./utility/messageIdGenerator.js";
|
|
8
10
|
|
|
9
11
|
const desiredOrder = [
|
|
10
12
|
"List-Unsubscribe-Post",
|
|
@@ -29,7 +31,8 @@ class MimeMessage {
|
|
|
29
31
|
this.attachmentPromises = [];
|
|
30
32
|
this.processingPromises = [];
|
|
31
33
|
this.removedHeaders = new Set();
|
|
32
|
-
this.boundaryStyle = "
|
|
34
|
+
this.boundaryStyle = "google_hex";
|
|
35
|
+
this.messageIdStyle = "gmail_ca";
|
|
33
36
|
this.preserveOrder = false;
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -38,13 +41,19 @@ class MimeMessage {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
setBoundaryStyle(style) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
throw new Error(`Invalid boundary style: ${style}. Allowed: ${allowed.join(", ")}`);
|
|
44
|
+
if (!ALLOWED_BOUNDARY_STYLES.includes(style)) {
|
|
45
|
+
throw new Error(`Invalid boundary style: ${style}. Allowed: ${ALLOWED_BOUNDARY_STYLES.join(", ")}`);
|
|
44
46
|
}
|
|
45
47
|
this.boundaryStyle = style;
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
setMessageIdStyle(style) {
|
|
51
|
+
if (!ALLOWED_MESSAGE_ID_STYLES.includes(style)) {
|
|
52
|
+
throw new Error(`Invalid Message-ID style: ${style}. Allowed: ${ALLOWED_MESSAGE_ID_STYLES.join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
this.messageIdStyle = style;
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
// ─── HELPER: HTML to Text Converter ─────────────────────────────
|
|
49
58
|
_htmlToText(html) {
|
|
50
59
|
if (!html) return "";
|
|
@@ -366,7 +375,6 @@ class MimeMessage {
|
|
|
366
375
|
|
|
367
376
|
messageId(options = {}) {
|
|
368
377
|
const { id, domain } = options;
|
|
369
|
-
let localPart = id ? id : "CA" + this._generateRandomString(49);
|
|
370
378
|
let msgDomain = domain;
|
|
371
379
|
if (!msgDomain) {
|
|
372
380
|
const fromHeader = this.headers.find((h) => h.name.toLowerCase() === "from");
|
|
@@ -379,8 +387,17 @@ class MimeMessage {
|
|
|
379
387
|
msgDomain = "mail.gmail.com";
|
|
380
388
|
}
|
|
381
389
|
}
|
|
382
|
-
|
|
383
|
-
|
|
390
|
+
if (id) {
|
|
391
|
+
const fullMessageId = `<${id.trim()}@${msgDomain.trim()}>`;
|
|
392
|
+
this.updateHeader("Message-ID", fullMessageId);
|
|
393
|
+
} else {
|
|
394
|
+
const generated = generateMessageId(this.messageIdStyle, msgDomain);
|
|
395
|
+
if (generated) {
|
|
396
|
+
this.updateHeader("Message-ID", generated);
|
|
397
|
+
} else {
|
|
398
|
+
this.removeHeaders(["Message-ID"]);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
384
401
|
}
|
|
385
402
|
|
|
386
403
|
_generateRandomString(length) {
|
|
@@ -597,41 +614,7 @@ class MimeMessage {
|
|
|
597
614
|
}
|
|
598
615
|
|
|
599
616
|
generateBoundary(oldBoundary) {
|
|
600
|
-
|
|
601
|
-
const prefix = oldBoundary.slice(0, 17);
|
|
602
|
-
let newPart;
|
|
603
|
-
do {
|
|
604
|
-
newPart = randomBytes(6).toString("hex").slice(0, 11);
|
|
605
|
-
} while (prefix + newPart === oldBoundary);
|
|
606
|
-
return prefix + newPart;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
if (this.boundaryStyle === "outlook") {
|
|
610
|
-
// Office 365/Outlook format: _000_HashedIDapcp_
|
|
611
|
-
const middle = randomBytes(24).toString("hex").toUpperCase();
|
|
612
|
-
return `_000_${middle}apcp_`;
|
|
613
|
-
} else if (this.boundaryStyle === "yahoo") {
|
|
614
|
-
// Yahoo style: ----=_Part_[Part1]_[Part2].[Timestamp]
|
|
615
|
-
// e.g. ----=_Part_1660954_1899761734.1782525050322
|
|
616
|
-
const part1 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
617
|
-
const part2 = Math.floor(Math.random() * 9000000000) + 1000000000;
|
|
618
|
-
const timestamp = Date.now();
|
|
619
|
-
return `----=_Part_${part1}_${part2}.${timestamp}`;
|
|
620
|
-
} else if (this.boundaryStyle === "apple-mail") {
|
|
621
|
-
// Apple Mail style: Apple-Mail=_UUID
|
|
622
|
-
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
623
|
-
const formattedUuid = [
|
|
624
|
-
uuid.slice(0, 8),
|
|
625
|
-
uuid.slice(8, 12),
|
|
626
|
-
uuid.slice(12, 16),
|
|
627
|
-
uuid.slice(16, 20),
|
|
628
|
-
uuid.slice(20),
|
|
629
|
-
].join("-");
|
|
630
|
-
return `Apple-Mail=_${formattedUuid}`;
|
|
631
|
-
} else {
|
|
632
|
-
// Default Google style
|
|
633
|
-
return "000000000000" + randomBytes(8).toString("hex");
|
|
634
|
-
}
|
|
617
|
+
return generateBoundary(this.boundaryStyle, oldBoundary);
|
|
635
618
|
}
|
|
636
619
|
|
|
637
620
|
// ─── BUILDING THE RAW EMAIL ───────────────────────────────────────
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { randomBytes } from "crypto";
|
|
2
|
+
|
|
3
|
+
export const ALLOWED_BOUNDARY_STYLES = [
|
|
4
|
+
"google_hex",
|
|
5
|
+
"outlook_exchange",
|
|
6
|
+
"outlook_nextpart",
|
|
7
|
+
"apple_mail_equal",
|
|
8
|
+
"apple_mail_dash",
|
|
9
|
+
"apple_webmail",
|
|
10
|
+
"yahoo_part",
|
|
11
|
+
"samsung_android",
|
|
12
|
+
"edomail",
|
|
13
|
+
"hex_segments",
|
|
14
|
+
"custom_dashes"
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function generateBoundary(style, oldBoundary) {
|
|
18
|
+
if (oldBoundary) {
|
|
19
|
+
const prefix = oldBoundary.slice(0, 17);
|
|
20
|
+
let newPart;
|
|
21
|
+
do {
|
|
22
|
+
newPart = randomBytes(6).toString("hex").slice(0, 11);
|
|
23
|
+
} while (prefix + newPart === oldBoundary);
|
|
24
|
+
return prefix + newPart;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
switch (style) {
|
|
28
|
+
case "google_hex":
|
|
29
|
+
return "000000000000" + randomBytes(8).toString("hex");
|
|
30
|
+
|
|
31
|
+
case "outlook_exchange": {
|
|
32
|
+
const middle = randomBytes(24).toString("hex").toUpperCase();
|
|
33
|
+
return `_000_${middle}apcp_`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
case "outlook_nextpart": {
|
|
37
|
+
const part1 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
38
|
+
const part2 = Math.floor(Math.random() * 9000000000) + 1000000000;
|
|
39
|
+
const timestamp = Date.now();
|
|
40
|
+
return `----=_NextPart_${part1}_${part2}.${timestamp}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case "apple_mail_equal": {
|
|
44
|
+
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
45
|
+
const formattedUuid = [
|
|
46
|
+
uuid.slice(0, 8),
|
|
47
|
+
uuid.slice(8, 12),
|
|
48
|
+
uuid.slice(12, 16),
|
|
49
|
+
uuid.slice(16, 20),
|
|
50
|
+
uuid.slice(20)
|
|
51
|
+
].join("-");
|
|
52
|
+
return `Apple-Mail=_${formattedUuid}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case "apple_mail_dash": {
|
|
56
|
+
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
57
|
+
const formattedUuid = [
|
|
58
|
+
uuid.slice(0, 8),
|
|
59
|
+
uuid.slice(8, 12),
|
|
60
|
+
uuid.slice(12, 16),
|
|
61
|
+
uuid.slice(16, 20),
|
|
62
|
+
uuid.slice(20)
|
|
63
|
+
].join("-");
|
|
64
|
+
return `Apple-Mail-_${formattedUuid}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case "apple_webmail": {
|
|
68
|
+
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
69
|
+
const formattedUuid = [
|
|
70
|
+
uuid.slice(0, 8),
|
|
71
|
+
uuid.slice(8, 12),
|
|
72
|
+
uuid.slice(12, 16),
|
|
73
|
+
uuid.slice(16, 20),
|
|
74
|
+
uuid.slice(20)
|
|
75
|
+
].join("-");
|
|
76
|
+
return `Apple-Webmail-_${formattedUuid}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case "yahoo_part": {
|
|
80
|
+
const part1 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
81
|
+
const part2 = Math.floor(Math.random() * 9000000000) + 1000000000;
|
|
82
|
+
const timestamp = Date.now();
|
|
83
|
+
return `----=_Part_${part1}_${part2}.${timestamp}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case "samsung_android": {
|
|
87
|
+
const timestamp = Date.now();
|
|
88
|
+
const rand = Math.floor(Math.random() * 900000000) + 100000000;
|
|
89
|
+
return `_com.samsung.android.email_${timestamp}_${rand}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case "edomail": {
|
|
93
|
+
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
94
|
+
return `EdoMail_${uuid}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case "hex_segments": {
|
|
98
|
+
const seg1 = randomBytes(4).toString("hex");
|
|
99
|
+
const seg2 = randomBytes(4).toString("hex");
|
|
100
|
+
const seg3 = randomBytes(2).toString("hex");
|
|
101
|
+
return `${seg1}_${seg2}_${seg3}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case "custom_dashes": {
|
|
105
|
+
const randStr = Math.random().toString(36).substring(2, 15).toUpperCase();
|
|
106
|
+
return `----${randStr}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
default:
|
|
110
|
+
// Default fallback
|
|
111
|
+
return "000000000000" + randomBytes(8).toString("hex");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { randomBytes } from "crypto";
|
|
2
|
+
|
|
3
|
+
export const ALLOWED_MESSAGE_ID_STYLES = [
|
|
4
|
+
"apple_uuid",
|
|
5
|
+
"yahoo_mobile",
|
|
6
|
+
"yahoo_web",
|
|
7
|
+
"gmail_ca",
|
|
8
|
+
"outlook_exchange",
|
|
9
|
+
"outlook_web",
|
|
10
|
+
"samsung_android",
|
|
11
|
+
"generic_uuid",
|
|
12
|
+
"timestamp_dot",
|
|
13
|
+
"icloud_mail",
|
|
14
|
+
"android_email",
|
|
15
|
+
"google_mx_relay",
|
|
16
|
+
"generic",
|
|
17
|
+
"none"
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function generateRandomString(length) {
|
|
21
|
+
const allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=";
|
|
22
|
+
const bytes = randomBytes(length);
|
|
23
|
+
let result = "";
|
|
24
|
+
for (let i = 0; i < length; i++) {
|
|
25
|
+
result += allowed[bytes[i] % allowed.length];
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function generateMessageId(style, domain) {
|
|
31
|
+
if (style === "none") return null;
|
|
32
|
+
|
|
33
|
+
const msgDomain = domain || "mail.gmail.com";
|
|
34
|
+
|
|
35
|
+
switch (style) {
|
|
36
|
+
case "apple_uuid": {
|
|
37
|
+
const uuid = randomBytes(16).toString("hex").toUpperCase();
|
|
38
|
+
const formattedUuid = [
|
|
39
|
+
uuid.slice(0, 8),
|
|
40
|
+
uuid.slice(8, 12),
|
|
41
|
+
uuid.slice(12, 16),
|
|
42
|
+
uuid.slice(16, 20),
|
|
43
|
+
uuid.slice(20)
|
|
44
|
+
].join("-");
|
|
45
|
+
return `<${formattedUuid}@${msgDomain}>`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case "yahoo_mobile": {
|
|
49
|
+
const part1 = Math.floor(Math.random() * 900000000) + 100000000;
|
|
50
|
+
const part2 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
51
|
+
const timestamp = Date.now();
|
|
52
|
+
return `<${part1}.${part2}.${timestamp}@mail.yahoo.com>`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case "yahoo_web": {
|
|
56
|
+
const part1 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
57
|
+
const part2 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
58
|
+
const timestamp = Date.now();
|
|
59
|
+
return `<Part_${part1}_${part2}.${timestamp}@mail.yahoo.com>`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "gmail_ca": {
|
|
63
|
+
const randStr = generateRandomString(49);
|
|
64
|
+
return `<CA${randStr}@${msgDomain}>`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case "outlook_exchange": {
|
|
68
|
+
const rand1 = randomBytes(12).toString("hex").toUpperCase();
|
|
69
|
+
const rand2 = randomBytes(6).toString("hex").toLowerCase();
|
|
70
|
+
return `<${rand1}apcp@${rand2}.prod.outlook.com>`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case "outlook_web": {
|
|
74
|
+
const rand = randomBytes(16).toString("hex").toUpperCase();
|
|
75
|
+
return `<${rand}@${msgDomain}>`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case "samsung_android": {
|
|
79
|
+
const timestamp = Date.now();
|
|
80
|
+
const rand1 = Math.floor(Math.random() * 9000000) + 1000000;
|
|
81
|
+
const rand2 = Math.floor(Math.random() * 900000000) + 100000000;
|
|
82
|
+
return `<${timestamp}.${rand1}.${rand2}@com.samsung.android.email>`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case "generic_uuid": {
|
|
86
|
+
const uuid = randomBytes(16).toString("hex").toLowerCase();
|
|
87
|
+
const formattedUuid = [
|
|
88
|
+
uuid.slice(0, 8),
|
|
89
|
+
uuid.slice(8, 12),
|
|
90
|
+
uuid.slice(12, 16),
|
|
91
|
+
uuid.slice(16, 20),
|
|
92
|
+
uuid.slice(20)
|
|
93
|
+
].join("-");
|
|
94
|
+
return `<${formattedUuid}@${msgDomain}>`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case "timestamp_dot": {
|
|
98
|
+
const timestamp = Date.now();
|
|
99
|
+
const rand = Math.floor(Math.random() * 900000) + 100000;
|
|
100
|
+
return `<${timestamp}.${rand}@${msgDomain}>`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
case "icloud_mail": {
|
|
104
|
+
const prefix = generateRandomString(12).toLowerCase().replace(/=/g, 'a');
|
|
105
|
+
return `<${prefix}@${msgDomain}>`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
case "android_email": {
|
|
109
|
+
const prefix = generateRandomString(24).toLowerCase().replace(/=/g, 'z');
|
|
110
|
+
const timestamp = Date.now();
|
|
111
|
+
return `<${prefix}.${timestamp}@email.android.com>`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case "google_mx_relay": {
|
|
115
|
+
const hex1 = randomBytes(4).toString("hex");
|
|
116
|
+
const hex2 = randomBytes(4).toString("hex");
|
|
117
|
+
const hex3 = randomBytes(3).toString("hex");
|
|
118
|
+
const hex4 = randomBytes(2).toString("hex");
|
|
119
|
+
return `<${hex1}.${hex2}.${hex3}.${hex4}@mx.google.com>`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "generic":
|
|
123
|
+
default: {
|
|
124
|
+
const randStr = generateRandomString(40);
|
|
125
|
+
return `<${randStr}@${msgDomain}>`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|