mailfile 0.1.0

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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +203 -0
  3. package/dist/mailfile.cjs +2035 -0
  4. package/dist/mailfile.js +2037 -0
  5. package/dist/mailfile.min.js +30 -0
  6. package/package.json +84 -0
  7. package/src/cfb/index.js +4 -0
  8. package/src/cfb/read.js +221 -0
  9. package/src/cfb/write.js +262 -0
  10. package/src/errors.js +49 -0
  11. package/src/index.js +86 -0
  12. package/src/mapi/bag.js +195 -0
  13. package/src/mapi/index.js +6 -0
  14. package/src/mapi/tags.js +147 -0
  15. package/src/message.js +199 -0
  16. package/src/mime/build.js +100 -0
  17. package/src/mime/encodings.js +188 -0
  18. package/src/mime/headers.js +344 -0
  19. package/src/mime/index.js +12 -0
  20. package/src/mime/parse.js +153 -0
  21. package/src/msg/read.js +178 -0
  22. package/src/msg/write.js +124 -0
  23. package/src/rtf/deencapsulate.js +155 -0
  24. package/src/rtf/index.js +3 -0
  25. package/src/rtf/lzfu.js +67 -0
  26. package/src/util.js +59 -0
  27. package/types/cfb/index.d.ts +2 -0
  28. package/types/cfb/read.d.ts +70 -0
  29. package/types/cfb/write.d.ts +67 -0
  30. package/types/errors.d.ts +36 -0
  31. package/types/index.d.ts +73 -0
  32. package/types/mapi/bag.d.ts +77 -0
  33. package/types/mapi/index.d.ts +2 -0
  34. package/types/mapi/tags.d.ts +137 -0
  35. package/types/message.d.ts +94 -0
  36. package/types/mime/build.d.ts +27 -0
  37. package/types/mime/encodings.d.ts +52 -0
  38. package/types/mime/headers.d.ts +111 -0
  39. package/types/mime/index.d.ts +4 -0
  40. package/types/mime/parse.d.ts +43 -0
  41. package/types/msg/read.d.ts +26 -0
  42. package/types/msg/write.d.ts +6 -0
  43. package/types/rtf/deencapsulate.d.ts +23 -0
  44. package/types/rtf/index.d.ts +2 -0
  45. package/types/rtf/lzfu.d.ts +8 -0
  46. package/types/util.d.ts +25 -0
@@ -0,0 +1,2037 @@
1
+ var mailfile = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.js
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ErrorCode: () => ErrorCode,
24
+ Headers: () => Headers,
25
+ MailfileError: () => MailfileError,
26
+ Message: () => Message,
27
+ convert: () => convert,
28
+ detect: () => detect,
29
+ version: () => version
30
+ });
31
+
32
+ // src/mime/encodings.js
33
+ var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34
+ function bytesToBase64(bytes) {
35
+ let out = "";
36
+ let i;
37
+ const len = bytes.length;
38
+ for (i = 0; i + 2 < len; i += 3) {
39
+ const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
40
+ out += B64[n >> 18 & 63] + B64[n >> 12 & 63] + B64[n >> 6 & 63] + B64[n & 63];
41
+ }
42
+ const rem = len - i;
43
+ if (rem === 1) {
44
+ const a = bytes[i] << 16;
45
+ out += B64[a >> 18 & 63] + B64[a >> 12 & 63] + "==";
46
+ } else if (rem === 2) {
47
+ const b = bytes[i] << 16 | bytes[i + 1] << 8;
48
+ out += B64[b >> 18 & 63] + B64[b >> 12 & 63] + B64[b >> 6 & 63] + "=";
49
+ }
50
+ return out;
51
+ }
52
+ function base64ToBytes(str) {
53
+ let clean = String(str).replace(/[^A-Za-z0-9+/=]/g, "");
54
+ const pad = clean.indexOf("=");
55
+ if (pad >= 0) clean = clean.slice(0, pad);
56
+ const len = clean.length;
57
+ const out = new Uint8Array(Math.floor(len * 3 / 4));
58
+ let p = 0;
59
+ let buf = 0;
60
+ let bits = 0;
61
+ for (let i = 0; i < len; i++) {
62
+ const v = B64.indexOf(clean[i]);
63
+ if (v < 0) continue;
64
+ buf = buf << 6 | v;
65
+ bits += 6;
66
+ if (bits >= 8) {
67
+ bits -= 8;
68
+ out[p++] = buf >> bits & 255;
69
+ }
70
+ }
71
+ return out.subarray(0, p);
72
+ }
73
+ function wrap(str, width) {
74
+ const out = [];
75
+ for (let i = 0; i < str.length; i += width) out.push(str.slice(i, i + width));
76
+ return out.join("\r\n");
77
+ }
78
+ var encoder = new TextEncoder();
79
+ function utf8Encode(s) {
80
+ return encoder.encode(s);
81
+ }
82
+ function decodeBytes(bytes, charset) {
83
+ let cs = (charset || "utf-8").toLowerCase().replace(/^["']|["']$/g, "");
84
+ if (cs === "us-ascii" || cs === "ascii" || cs === "ansi_x3.4-1968") cs = "utf-8";
85
+ if (cs === "cp1252" || cs === "win-1252") cs = "windows-1252";
86
+ if (cs === "utf8") cs = "utf-8";
87
+ try {
88
+ return new TextDecoder(cs, { fatal: false }).decode(bytes);
89
+ } catch {
90
+ try {
91
+ return new TextDecoder("windows-1252").decode(bytes);
92
+ } catch {
93
+ let s = "";
94
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
95
+ return s;
96
+ }
97
+ }
98
+ }
99
+ function utf16Decode(bytes) {
100
+ try {
101
+ return new TextDecoder("utf-16le").decode(bytes);
102
+ } catch {
103
+ let s = "";
104
+ for (let i = 0; i + 1 < bytes.length; i += 2) {
105
+ s += String.fromCharCode(bytes[i] | bytes[i + 1] << 8);
106
+ }
107
+ return s;
108
+ }
109
+ }
110
+ function utf16Encode(str) {
111
+ const out = new Uint8Array(str.length * 2);
112
+ for (let i = 0; i < str.length; i++) {
113
+ const c = str.charCodeAt(i);
114
+ out[i * 2] = c & 255;
115
+ out[i * 2 + 1] = c >> 8;
116
+ }
117
+ return out;
118
+ }
119
+ function decodeQP(str, isHeaderWord) {
120
+ const out = [];
121
+ let i = 0;
122
+ while (i < str.length) {
123
+ const ch = str[i];
124
+ if (ch === "=") {
125
+ if (str[i + 1] === "\r" && str[i + 2] === "\n") {
126
+ i += 3;
127
+ continue;
128
+ }
129
+ if (str[i + 1] === "\n") {
130
+ i += 2;
131
+ continue;
132
+ }
133
+ const hex = str.substr(i + 1, 2);
134
+ if (/^[0-9A-Fa-f]{2}$/.test(hex)) {
135
+ out.push(parseInt(hex, 16));
136
+ i += 3;
137
+ continue;
138
+ }
139
+ out.push(61);
140
+ i++;
141
+ } else if (isHeaderWord && ch === "_") {
142
+ out.push(32);
143
+ i++;
144
+ } else {
145
+ out.push(str.charCodeAt(i) & 255);
146
+ i++;
147
+ }
148
+ }
149
+ return new Uint8Array(out);
150
+ }
151
+ function encodeQP(bytes) {
152
+ let out = "";
153
+ let lineLen = 0;
154
+ function push(tok) {
155
+ if (lineLen + tok.length > 75) {
156
+ out += "=\r\n";
157
+ lineLen = 0;
158
+ }
159
+ out += tok;
160
+ lineLen += tok.length;
161
+ }
162
+ const hex = (b) => "=" + ("0" + b.toString(16).toUpperCase()).slice(-2);
163
+ for (let i = 0; i < bytes.length; i++) {
164
+ const b = bytes[i];
165
+ if (b === 13 && bytes[i + 1] === 10) {
166
+ out += "\r\n";
167
+ lineLen = 0;
168
+ i++;
169
+ continue;
170
+ }
171
+ if (b === 10) {
172
+ out += "\r\n";
173
+ lineLen = 0;
174
+ continue;
175
+ }
176
+ if (b === 32 || b === 9) {
177
+ const next = bytes[i + 1];
178
+ if (next === 13 || next === 10 || next === void 0) push(hex(b));
179
+ else push(String.fromCharCode(b));
180
+ continue;
181
+ }
182
+ if (b === 61 || b < 32 || b > 126) push(hex(b));
183
+ else push(String.fromCharCode(b));
184
+ }
185
+ return out;
186
+ }
187
+
188
+ // src/mime/headers.js
189
+ function decodeWords(str) {
190
+ if (!str || str.indexOf("=?") < 0) return str || "";
191
+ const joined = str.replace(/(\?=)(?:\r?\n)?[ \t]+(=\?)/g, "$1$2");
192
+ return joined.replace(/=\?([^?]+)\?([BbQq])\?([^?]*)\?=/g, (m, cs, enc, text) => {
193
+ try {
194
+ const raw = enc.toUpperCase() === "B" ? base64ToBytes(text) : decodeQP(text, true);
195
+ return decodeBytes(raw, cs);
196
+ } catch {
197
+ return m;
198
+ }
199
+ });
200
+ }
201
+ function needsEncoding(s) {
202
+ return /[^\x20-\x7E]/.test(s);
203
+ }
204
+ function encodeWord(s) {
205
+ if (!needsEncoding(s)) return s;
206
+ const parts = [];
207
+ let cur = "";
208
+ let curBytes = 0;
209
+ const chars = Array.from(s);
210
+ for (const ch of chars) {
211
+ const n = utf8Encode(ch).length;
212
+ if (curBytes + n > 36) {
213
+ parts.push(cur);
214
+ cur = "";
215
+ curBytes = 0;
216
+ }
217
+ cur += ch;
218
+ curBytes += n;
219
+ }
220
+ if (cur) parts.push(cur);
221
+ return parts.map((p) => "=?UTF-8?B?" + bytesToBase64(utf8Encode(p)) + "?=").join("\r\n ");
222
+ }
223
+ function foldHeader(name, value) {
224
+ const line = name + ": " + value;
225
+ if (line.length <= 78 || /\r\n/.test(line)) return line;
226
+ let out = "";
227
+ let cur = name + ":";
228
+ const tokens = value.split(" ");
229
+ for (const tok of tokens) {
230
+ if (cur.length + 1 + tok.length > 78 && cur !== name + ":") {
231
+ out += cur + "\r\n";
232
+ cur = " " + tok;
233
+ } else {
234
+ cur += (cur === " " ? "" : " ") + tok;
235
+ }
236
+ }
237
+ return out + cur;
238
+ }
239
+ function formatAddress(name, email) {
240
+ email = (email || "").trim();
241
+ name = (name || "").trim();
242
+ if (!email) return name ? encodeWord(name) : "";
243
+ if (!name || name.toLowerCase() === email.toLowerCase()) return email;
244
+ if (needsEncoding(name)) return encodeWord(name) + " <" + email + ">";
245
+ if (/[()<>@,;:\\".\[\]]/.test(name)) {
246
+ return '"' + name.replace(/(["\\])/g, "\\$1") + '" <' + email + ">";
247
+ }
248
+ return name + " <" + email + ">";
249
+ }
250
+ function splitAddressList(str) {
251
+ const out = [];
252
+ let cur = "";
253
+ let inQuote = false;
254
+ let depth = 0;
255
+ for (let i = 0; i < str.length; i++) {
256
+ const c = str[i];
257
+ if (c === '"' && str[i - 1] !== "\\") inQuote = !inQuote;
258
+ if (!inQuote && (c === "<" || c === "(")) depth++;
259
+ if (!inQuote && (c === ">" || c === ")")) depth--;
260
+ if (c === "," && !inQuote && depth <= 0) {
261
+ out.push(cur);
262
+ cur = "";
263
+ continue;
264
+ }
265
+ cur += c;
266
+ }
267
+ if (cur.trim()) out.push(cur);
268
+ return out.map((s) => s.trim()).filter(Boolean);
269
+ }
270
+ function parseAddress(str) {
271
+ const s = decodeWords(String(str).trim());
272
+ const m = s.match(/^(.*)<([^>]*)>\s*$/);
273
+ if (m) {
274
+ const name = m[1].trim().replace(/^"(.*)"$/, "$1").replace(/\\(["\\])/g, "$1");
275
+ return { name, email: m[2].trim() };
276
+ }
277
+ return { name: "", email: s.replace(/^<|>$/g, "").trim() };
278
+ }
279
+ function parseAddressList(str) {
280
+ if (!str) return [];
281
+ return splitAddressList(String(str)).map(parseAddress).filter((a) => a.email || a.name);
282
+ }
283
+ function parseHeaderValue(raw) {
284
+ let value = "";
285
+ const params = {};
286
+ let i = 0;
287
+ const str = String(raw || "");
288
+ while (i < str.length && str[i] !== ";") {
289
+ value += str[i];
290
+ i++;
291
+ }
292
+ const rest = str.slice(i + 1);
293
+ const re = /\s*([^=\s;]+)\s*=\s*("(?:[^"\\]|\\.)*"|[^;]*)\s*(?:;|$)/g;
294
+ const raws = {};
295
+ let m;
296
+ while (m = re.exec(rest)) {
297
+ if (!m[1]) break;
298
+ let v = m[2].trim();
299
+ if (v[0] === '"') v = v.slice(1, -1).replace(/\\(.)/g, "$1");
300
+ raws[m[1].toLowerCase()] = v;
301
+ }
302
+ const pieces = {};
303
+ for (const k of Object.keys(raws)) {
304
+ const mm = k.match(/^([^*]+)(?:\*(\d+))?(\*)?$/);
305
+ if (!mm) {
306
+ params[k] = raws[k];
307
+ continue;
308
+ }
309
+ const base = mm[1];
310
+ const idx = mm[2] === void 0 ? -1 : parseInt(mm[2], 10);
311
+ const ext = !!mm[3];
312
+ if (idx < 0 && !ext) {
313
+ if (!(base in pieces)) params[base] = raws[k];
314
+ continue;
315
+ }
316
+ pieces[base] = pieces[base] || [];
317
+ pieces[base].push({ idx: idx < 0 ? 0 : idx, ext, v: raws[k] });
318
+ }
319
+ for (const base of Object.keys(pieces)) {
320
+ const list = pieces[base].sort((a, b) => a.idx - b.idx);
321
+ let charset = null;
322
+ let acc = "";
323
+ list.forEach((p, n) => {
324
+ let v = p.v;
325
+ if (p.ext) {
326
+ if (n === 0 && v.indexOf("'") >= 0) {
327
+ const bits = v.split("'");
328
+ charset = bits[0];
329
+ v = bits.slice(2).join("'");
330
+ }
331
+ acc += v;
332
+ } else {
333
+ acc += v;
334
+ }
335
+ });
336
+ if (charset || /%[0-9A-Fa-f]{2}/.test(acc)) {
337
+ const bytes = [];
338
+ for (let j = 0; j < acc.length; j++) {
339
+ if (acc[j] === "%" && /^[0-9A-Fa-f]{2}$/.test(acc.substr(j + 1, 2))) {
340
+ bytes.push(parseInt(acc.substr(j + 1, 2), 16));
341
+ j += 2;
342
+ } else {
343
+ bytes.push(acc.charCodeAt(j) & 255);
344
+ }
345
+ }
346
+ params[base] = decodeBytes(new Uint8Array(bytes), charset || "utf-8");
347
+ } else {
348
+ params[base] = decodeWords(acc);
349
+ }
350
+ }
351
+ return { value: value.trim(), params };
352
+ }
353
+ function encodeParamValue(name, val) {
354
+ if (!needsEncoding(val) && !/[";\\]/.test(val)) return name + '="' + val + '"';
355
+ if (!needsEncoding(val)) return name + '="' + val.replace(/(["\\])/g, "\\$1") + '"';
356
+ const bytes = utf8Encode(val);
357
+ let enc = "";
358
+ for (const b of bytes) {
359
+ if (b >= 48 && b <= 57 || b >= 65 && b <= 90 || b >= 97 && b <= 122 || b === 45 || b === 46 || b === 95) {
360
+ enc += String.fromCharCode(b);
361
+ } else {
362
+ enc += "%" + ("0" + b.toString(16).toUpperCase()).slice(-2);
363
+ }
364
+ }
365
+ const ascii = val.replace(/[^\x20-\x7E]/g, "_").replace(/(["\\])/g, "");
366
+ return name + '="' + ascii + '"; ' + name + "*=UTF-8''" + enc;
367
+ }
368
+ var Headers = class _Headers {
369
+ /** @param {Array<[string, string]>} [entries] */
370
+ constructor(entries) {
371
+ this.entries = entries ? entries.slice() : [];
372
+ }
373
+ /**
374
+ * Parse a raw header block. Continuation lines are unfolded.
375
+ * @param {string} text
376
+ * @returns {Headers}
377
+ */
378
+ static parse(text) {
379
+ const unfolded = String(text || "").replace(/\r?\n[ \t]+/g, " ");
380
+ const entries = [];
381
+ for (const line of unfolded.split(/\r?\n/)) {
382
+ if (!line.trim()) continue;
383
+ const idx = line.indexOf(":");
384
+ if (idx < 0) continue;
385
+ entries.push([line.slice(0, idx).trim(), line.slice(idx + 1).trim()]);
386
+ }
387
+ return new _Headers(entries);
388
+ }
389
+ /** @param {string} name @returns {string|null} First matching value. */
390
+ get(name) {
391
+ const lower = name.toLowerCase();
392
+ for (const [k, v] of this.entries) if (k.toLowerCase() === lower) return v;
393
+ return null;
394
+ }
395
+ /** @param {string} name @returns {string[]} Every matching value, in order. */
396
+ getAll(name) {
397
+ const lower = name.toLowerCase();
398
+ return this.entries.filter(([k]) => k.toLowerCase() === lower).map(([, v]) => v);
399
+ }
400
+ /** @param {string} name @returns {boolean} */
401
+ has(name) {
402
+ return this.get(name) !== null;
403
+ }
404
+ /** Replace all occurrences with a single value. @param {string} name @param {string} value */
405
+ set(name, value) {
406
+ this.delete(name);
407
+ this.entries.push([name, value]);
408
+ return this;
409
+ }
410
+ /** Append without removing existing occurrences. @param {string} name @param {string} value */
411
+ add(name, value) {
412
+ this.entries.push([name, value]);
413
+ return this;
414
+ }
415
+ /** @param {string} name */
416
+ delete(name) {
417
+ const lower = name.toLowerCase();
418
+ this.entries = this.entries.filter(([k]) => k.toLowerCase() !== lower);
419
+ return this;
420
+ }
421
+ /** Header block as text, with folding applied. @returns {string} */
422
+ get raw() {
423
+ return this.entries.map(([k, v]) => foldHeader(k, v)).join("\r\n");
424
+ }
425
+ [Symbol.iterator]() {
426
+ return this.entries[Symbol.iterator]();
427
+ }
428
+ };
429
+ function formatDate(d) {
430
+ const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
431
+ const months = [
432
+ "Jan",
433
+ "Feb",
434
+ "Mar",
435
+ "Apr",
436
+ "May",
437
+ "Jun",
438
+ "Jul",
439
+ "Aug",
440
+ "Sep",
441
+ "Oct",
442
+ "Nov",
443
+ "Dec"
444
+ ];
445
+ const p = (n) => (n < 10 ? "0" : "") + n;
446
+ let tz = -d.getTimezoneOffset();
447
+ const sign = tz >= 0 ? "+" : "-";
448
+ tz = Math.abs(tz);
449
+ return days[d.getDay()] + ", " + d.getDate() + " " + months[d.getMonth()] + " " + d.getFullYear() + " " + p(d.getHours()) + ":" + p(d.getMinutes()) + ":" + p(d.getSeconds()) + " " + sign + p(Math.floor(tz / 60)) + p(tz % 60);
450
+ }
451
+ function parseDate(str) {
452
+ if (!str) return null;
453
+ const d = new Date(String(str).replace(/\s*\([^)]*\)\s*$/, ""));
454
+ return isNaN(d.getTime()) ? null : d;
455
+ }
456
+
457
+ // src/mime/build.js
458
+ function randomBoundary(tag) {
459
+ let s = "";
460
+ for (let i = 0; i < 24; i++) s += "0123456789abcdef"[Math.floor(Math.random() * 16)];
461
+ return "----=_" + tag + "_" + s;
462
+ }
463
+ function textPart(mime, content) {
464
+ return [
465
+ "Content-Type: " + mime + '; charset="utf-8"',
466
+ "Content-Transfer-Encoding: quoted-printable",
467
+ "",
468
+ encodeQP(utf8Encode(content))
469
+ ].join("\r\n");
470
+ }
471
+ function attachmentPart(att) {
472
+ const mime = att.mime || "application/octet-stream";
473
+ const lines = [
474
+ "Content-Type: " + mime + "; " + encodeParamValue("name", att.filename),
475
+ "Content-Transfer-Encoding: base64"
476
+ ];
477
+ if (att.cid) lines.push("Content-ID: <" + att.cid + ">");
478
+ lines.push("Content-Disposition: " + (att.inline ? "inline" : "attachment") + "; " + encodeParamValue("filename", att.filename));
479
+ lines.push("");
480
+ lines.push(wrap(bytesToBase64(att.data), 76));
481
+ return lines.join("\r\n");
482
+ }
483
+ function multipart(subtype, parts, extraParams) {
484
+ const b = randomBoundary(subtype.toUpperCase());
485
+ const head = "Content-Type: multipart/" + subtype + '; boundary="' + b + '"' + (extraParams || "");
486
+ let body = "";
487
+ for (const p of parts) body += "--" + b + "\r\n" + p + "\r\n";
488
+ body += "--" + b + "--\r\n";
489
+ return { head, body };
490
+ }
491
+ function buildEml(msg) {
492
+ const inline = [];
493
+ const regular = [];
494
+ for (const a of msg.attachments || []) {
495
+ if (a.inline && a.cid) inline.push(a);
496
+ else regular.push(a);
497
+ }
498
+ const bodyParts = [];
499
+ if (msg.text) bodyParts.push(textPart("text/plain", msg.text));
500
+ if (msg.html) bodyParts.push(textPart("text/html", msg.html));
501
+ if (!bodyParts.length) bodyParts.push(textPart("text/plain", ""));
502
+ let current;
503
+ if (bodyParts.length > 1) {
504
+ const alt = multipart("alternative", bodyParts);
505
+ current = alt.head + "\r\n\r\n" + alt.body;
506
+ } else {
507
+ current = bodyParts[0];
508
+ }
509
+ if (inline.length) {
510
+ const rel = multipart(
511
+ "related",
512
+ [current].concat(inline.map(attachmentPart)),
513
+ '; type="text/html"'
514
+ );
515
+ current = rel.head + "\r\n\r\n" + rel.body;
516
+ }
517
+ if (regular.length) {
518
+ const mixed = multipart("mixed", [current].concat(regular.map(attachmentPart)));
519
+ current = mixed.head + "\r\n\r\n" + mixed.body;
520
+ }
521
+ const idx = current.indexOf("\r\n\r\n");
522
+ const partHeaders = current.slice(0, idx);
523
+ const partBody = current.slice(idx + 4);
524
+ const headerLines = [];
525
+ for (const [k, v] of msg.headers || []) headerLines.push(foldHeader(k, v));
526
+ headerLines.push("MIME-Version: 1.0");
527
+ for (const l of partHeaders.split("\r\n")) if (l) headerLines.push(l);
528
+ return utf8Encode(headerLines.join("\r\n") + "\r\n\r\n" + partBody);
529
+ }
530
+
531
+ // src/mime/parse.js
532
+ function findHeaderEnd(bytes) {
533
+ for (let i = 0; i + 1 < bytes.length; i++) {
534
+ if (bytes[i] === 10 && bytes[i + 1] === 10) return { end: i, bodyStart: i + 2 };
535
+ if (bytes[i] === 13 && bytes[i + 1] === 10 && bytes[i + 2] === 13 && bytes[i + 3] === 10) {
536
+ return { end: i, bodyStart: i + 4 };
537
+ }
538
+ }
539
+ return { end: bytes.length, bodyStart: bytes.length };
540
+ }
541
+ function splitOnBoundary(bytes, boundary) {
542
+ const marker = utf8Encode("--" + boundary);
543
+ const parts = [];
544
+ const starts = [];
545
+ for (let i = 0; i <= bytes.length - marker.length; i++) {
546
+ if (bytes[i] !== 45 || bytes[i + 1] !== 45) continue;
547
+ if (i > 0 && bytes[i - 1] !== 10) continue;
548
+ let ok = true;
549
+ for (let j = 0; j < marker.length; j++) {
550
+ if (bytes[i + j] !== marker[j]) {
551
+ ok = false;
552
+ break;
553
+ }
554
+ }
555
+ if (!ok) continue;
556
+ const after = i + marker.length;
557
+ const isEnd = bytes[after] === 45 && bytes[after + 1] === 45;
558
+ let k = after + (isEnd ? 2 : 0);
559
+ while (bytes[k] === 32 || bytes[k] === 9) k++;
560
+ if (!(bytes[k] === 13 || bytes[k] === 10 || k >= bytes.length)) continue;
561
+ let contentStart = k;
562
+ if (bytes[contentStart] === 13) contentStart++;
563
+ if (bytes[contentStart] === 10) contentStart++;
564
+ starts.push({ at: i, contentStart, isEnd });
565
+ if (isEnd) break;
566
+ }
567
+ for (let s = 0; s < starts.length - 1; s++) {
568
+ const from = starts[s].contentStart;
569
+ let to = starts[s + 1].at;
570
+ if (to > from && bytes[to - 1] === 10) to--;
571
+ if (to > from && bytes[to - 1] === 13) to--;
572
+ parts.push(bytes.subarray(from, Math.max(from, to)));
573
+ }
574
+ return parts;
575
+ }
576
+ function decodePart(bytes, encoding) {
577
+ const enc = (encoding || "7bit").trim().toLowerCase();
578
+ if (enc === "base64") return base64ToBytes(decodeBytes(bytes, "utf-8"));
579
+ if (enc === "quoted-printable") {
580
+ let s = "";
581
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
582
+ return decodeQP(s, false);
583
+ }
584
+ return bytes;
585
+ }
586
+ function parseNode(bytes, depth = 0) {
587
+ const split = findHeaderEnd(bytes);
588
+ const headers = Headers.parse(decodeBytes(bytes.subarray(0, split.end), "utf-8"));
589
+ const body = bytes.subarray(split.bodyStart);
590
+ const ct = parseHeaderValue(headers.get("content-type") || "text/plain");
591
+ const node = {
592
+ headers,
593
+ contentType: (ct.value || "text/plain").toLowerCase(),
594
+ params: ct.params,
595
+ children: []
596
+ };
597
+ if (node.contentType.startsWith("multipart/") && ct.params.boundary && depth < 20) {
598
+ for (const kid of splitOnBoundary(body, ct.params.boundary)) {
599
+ node.children.push(parseNode(kid, depth + 1));
600
+ }
601
+ } else {
602
+ node.content = decodePart(body, headers.get("content-transfer-encoding"));
603
+ }
604
+ return node;
605
+ }
606
+ var EXT_BY_MIME = {
607
+ "text/plain": "txt",
608
+ "text/html": "html",
609
+ "image/png": "png",
610
+ "image/jpeg": "jpg",
611
+ "image/gif": "gif",
612
+ "application/pdf": "pdf",
613
+ "message/rfc822": "eml"
614
+ };
615
+ function guessName(mime, n) {
616
+ return "attachment-" + (n + 1) + "." + (EXT_BY_MIME[mime] || "bin");
617
+ }
618
+ function collectParts(node, out, parentType) {
619
+ if (node.children.length) {
620
+ for (const c of node.children) collectParts(c, out, node.contentType);
621
+ return;
622
+ }
623
+ const disp = parseHeaderValue(node.headers.get("content-disposition") || "");
624
+ const dispType = (disp.value || "").toLowerCase();
625
+ const filename = disp.params.filename || node.params.name || "";
626
+ const cid = (node.headers.get("content-id") || "").replace(/^<|>$/g, "").trim();
627
+ const isText = node.contentType === "text/plain" || node.contentType === "text/html";
628
+ const isBody = isText && dispType !== "attachment" && !filename;
629
+ if (isBody) {
630
+ const text = decodeBytes(node.content, node.params.charset || "utf-8");
631
+ if (node.contentType === "text/html") out.html = out.html || text;
632
+ else out.text = out.text || text;
633
+ return;
634
+ }
635
+ out.attachments.push({
636
+ filename: decodeWords(filename) || guessName(node.contentType, out.attachments.length),
637
+ mime: node.contentType,
638
+ data: node.content || new Uint8Array(0),
639
+ cid,
640
+ inline: dispType === "inline" || !!cid && parentType === "multipart/related"
641
+ });
642
+ }
643
+ function parseEml(bytes) {
644
+ const root = parseNode(bytes, 0);
645
+ const out = { text: "", html: "", attachments: [], headers: root.headers, root };
646
+ collectParts(root, out, root.contentType);
647
+ return out;
648
+ }
649
+
650
+ // src/errors.js
651
+ var ErrorCode = {
652
+ /** The bytes are not an OLE2 compound file (wrong magic number). */
653
+ NOT_COMPOUND_FILE: "NOT_COMPOUND_FILE",
654
+ /** The compound file's internal structure is inconsistent or damaged. */
655
+ CORRUPT_CFB: "CORRUPT_CFB",
656
+ /** A stream ended before the declared length. */
657
+ TRUNCATED_STREAM: "TRUNCATED_STREAM",
658
+ /** The compound file is valid but is not an Outlook message. */
659
+ NOT_A_MESSAGE: "NOT_A_MESSAGE",
660
+ /** Input was empty. */
661
+ EMPTY_INPUT: "EMPTY_INPUT",
662
+ /** A structure is well-formed but uses a feature mailfile cannot represent. */
663
+ UNSUPPORTED: "UNSUPPORTED",
664
+ /** Strict mode only: the input deviates from the specification. */
665
+ MALFORMED: "MALFORMED"
666
+ };
667
+ var MailfileError = class extends Error {
668
+ /**
669
+ * @param {string} code One of {@link ErrorCode}.
670
+ * @param {string} message Human-readable description.
671
+ * @param {{cause?: unknown}} [options]
672
+ */
673
+ constructor(code, message, options) {
674
+ super(message);
675
+ this.name = "MailfileError";
676
+ this.code = code;
677
+ if (options && options.cause !== void 0) this.cause = options.cause;
678
+ }
679
+ };
680
+
681
+ // src/cfb/read.js
682
+ var SIG = [208, 207, 17, 224, 161, 177, 26, 225];
683
+ var FREESECT = 4294967295;
684
+ var ENDOFCHAIN = 4294967294;
685
+ var NOSTREAM = 4294967295;
686
+ var MINI_CUTOFF = 4096;
687
+ function read(bytes) {
688
+ if (!bytes || !bytes.length) {
689
+ throw new MailfileError(ErrorCode.EMPTY_INPUT, "No data to read");
690
+ }
691
+ if (bytes.length < 512) {
692
+ throw new MailfileError(
693
+ ErrorCode.NOT_COMPOUND_FILE,
694
+ "File is too small to be a compound file"
695
+ );
696
+ }
697
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
698
+ for (let i = 0; i < 8; i++) {
699
+ if (bytes[i] !== SIG[i]) {
700
+ throw new MailfileError(
701
+ ErrorCode.NOT_COMPOUND_FILE,
702
+ "Not a compound file (bad OLE signature)"
703
+ );
704
+ }
705
+ }
706
+ const sectorSize = 1 << dv.getUint16(30, true);
707
+ const miniSectorSize = 1 << dv.getUint16(32, true);
708
+ const numFatSectors = dv.getUint32(44, true);
709
+ const firstDirSector = dv.getUint32(48, true);
710
+ const miniCutoff = dv.getUint32(56, true) || MINI_CUTOFF;
711
+ const firstMiniFat = dv.getUint32(60, true);
712
+ const firstDifat = dv.getUint32(68, true);
713
+ if (sectorSize < 128 || sectorSize > 65536) {
714
+ throw new MailfileError(ErrorCode.CORRUPT_CFB, "Invalid sector size");
715
+ }
716
+ const maxSector = Math.floor((bytes.length - sectorSize) / sectorSize);
717
+ const sectorOffset = (s) => (s + 1) * sectorSize;
718
+ function checkSector(s) {
719
+ if (s > maxSector || s < 0) {
720
+ throw new MailfileError(
721
+ ErrorCode.CORRUPT_CFB,
722
+ "Sector reference outside the file (offset " + s + ")"
723
+ );
724
+ }
725
+ }
726
+ const fatSectors = [];
727
+ for (let d = 0; d < 109 && fatSectors.length < numFatSectors; d++) {
728
+ const v = dv.getUint32(76 + d * 4, true);
729
+ if (v === FREESECT || v === ENDOFCHAIN) break;
730
+ fatSectors.push(v);
731
+ }
732
+ let difSec = firstDifat;
733
+ let guard = 0;
734
+ while (difSec !== ENDOFCHAIN && difSec !== FREESECT && guard++ < 1e5 && fatSectors.length < numFatSectors) {
735
+ checkSector(difSec);
736
+ const base = sectorOffset(difSec);
737
+ for (let k = 0; k < sectorSize / 4 - 1; k++) {
738
+ const fv = dv.getUint32(base + k * 4, true);
739
+ if (fv === FREESECT || fv === ENDOFCHAIN) break;
740
+ fatSectors.push(fv);
741
+ }
742
+ difSec = dv.getUint32(base + sectorSize - 4, true);
743
+ }
744
+ const perSector = sectorSize / 4;
745
+ const fat = new Uint32Array(fatSectors.length * perSector);
746
+ for (let f = 0; f < fatSectors.length; f++) {
747
+ checkSector(fatSectors[f]);
748
+ const off = sectorOffset(fatSectors[f]);
749
+ for (let j = 0; j < perSector; j++) {
750
+ fat[f * perSector + j] = dv.getUint32(off + j * 4, true);
751
+ }
752
+ }
753
+ function chain(start) {
754
+ const out = [];
755
+ let s = start;
756
+ let n = 0;
757
+ while (s !== ENDOFCHAIN && s !== FREESECT && s !== void 0) {
758
+ if (n++ > fat.length + 8) {
759
+ throw new MailfileError(ErrorCode.CORRUPT_CFB, "Sector chain loops back on itself");
760
+ }
761
+ checkSector(s);
762
+ out.push(s);
763
+ s = fat[s];
764
+ }
765
+ return out;
766
+ }
767
+ function readSectors(list, size) {
768
+ const out = new Uint8Array(list.length * sectorSize);
769
+ for (let i = 0; i < list.length; i++) {
770
+ out.set(
771
+ bytes.subarray(sectorOffset(list[i]), sectorOffset(list[i]) + sectorSize),
772
+ i * sectorSize
773
+ );
774
+ }
775
+ return size == null ? out : out.subarray(0, size);
776
+ }
777
+ const miniFatBytes = readSectors(chain(firstMiniFat));
778
+ const miniFat = new Uint32Array(miniFatBytes.length >> 2);
779
+ const mdv = new DataView(miniFatBytes.buffer, miniFatBytes.byteOffset, miniFatBytes.byteLength);
780
+ for (let m = 0; m < miniFat.length; m++) miniFat[m] = mdv.getUint32(m * 4, true);
781
+ const dirBytes = readSectors(chain(firstDirSector));
782
+ const count = Math.floor(dirBytes.length / 128);
783
+ const ddv = new DataView(dirBytes.buffer, dirBytes.byteOffset, dirBytes.byteLength);
784
+ const entries = [];
785
+ for (let e = 0; e < count; e++) {
786
+ const o = e * 128;
787
+ const nameLen = ddv.getUint16(o + 64, true);
788
+ let name = "";
789
+ for (let c = 0; c + 1 < nameLen - 1; c += 2) {
790
+ const code = ddv.getUint16(o + c, true);
791
+ if (code === 0) break;
792
+ name += String.fromCharCode(code);
793
+ }
794
+ entries.push({
795
+ id: e,
796
+ name,
797
+ type: ddv.getUint8(o + 66),
798
+ left: ddv.getUint32(o + 68, true),
799
+ right: ddv.getUint32(o + 72, true),
800
+ child: ddv.getUint32(o + 76, true),
801
+ start: ddv.getUint32(o + 116, true),
802
+ size: ddv.getUint32(o + 120, true) + ddv.getUint32(o + 124, true) * 4294967296,
803
+ children: null
804
+ });
805
+ }
806
+ if (!entries.length) {
807
+ throw new MailfileError(ErrorCode.CORRUPT_CFB, "Compound file has no directory");
808
+ }
809
+ const root = entries[0];
810
+ const miniStream = readSectors(chain(root.start), root.size);
811
+ function readStream(entry) {
812
+ if (!entry || entry.type !== 2 || !entry.size) return new Uint8Array(0);
813
+ if (entry.size < miniCutoff) {
814
+ const out = new Uint8Array(entry.size);
815
+ let s = entry.start;
816
+ let pos = 0;
817
+ let n = 0;
818
+ while (s !== ENDOFCHAIN && s !== FREESECT && pos < entry.size) {
819
+ if (n++ > miniFat.length + 8) {
820
+ throw new MailfileError(ErrorCode.CORRUPT_CFB, "Mini sector chain loops back on itself");
821
+ }
822
+ const take = Math.min(miniSectorSize, entry.size - pos);
823
+ out.set(miniStream.subarray(s * miniSectorSize, s * miniSectorSize + take), pos);
824
+ pos += take;
825
+ s = miniFat[s];
826
+ }
827
+ return out;
828
+ }
829
+ return readSectors(chain(entry.start), entry.size);
830
+ }
831
+ function collect(nodeId, into, seen) {
832
+ if (nodeId === NOSTREAM || nodeId >= entries.length || seen[nodeId]) return;
833
+ seen[nodeId] = 1;
834
+ const n = entries[nodeId];
835
+ collect(n.left, into, seen);
836
+ into.push(n);
837
+ collect(n.right, into, seen);
838
+ }
839
+ function childrenOf(entry) {
840
+ if (entry.children) return entry.children;
841
+ const list = [];
842
+ collect(entry.child, list, {});
843
+ const map = /* @__PURE__ */ Object.create(null);
844
+ for (let i = 0; i < list.length; i++) map[list[i].name] = list[i];
845
+ entry.children = { list, map };
846
+ return entry.children;
847
+ }
848
+ return { root, entries, readStream, childrenOf };
849
+ }
850
+
851
+ // src/cfb/write.js
852
+ var SIG2 = [208, 207, 17, 224, 161, 177, 26, 225];
853
+ var FREESECT2 = 4294967295;
854
+ var ENDOFCHAIN2 = 4294967294;
855
+ var FATSECT = 4294967293;
856
+ var DIFSECT = 4294967292;
857
+ var NOSTREAM2 = 4294967295;
858
+ var MINI_CUTOFF2 = 4096;
859
+ function newRoot(clsid) {
860
+ return { name: "Root Entry", type: 5, clsid: clsid || null, children: [] };
861
+ }
862
+ function addStorage(parent, name, clsid) {
863
+ const n = { name, type: 1, clsid: clsid || null, children: [] };
864
+ parent.children.push(n);
865
+ return n;
866
+ }
867
+ function addStream(parent, name, data) {
868
+ const n = { name, type: 2, data: data || new Uint8Array(0) };
869
+ parent.children.push(n);
870
+ return n;
871
+ }
872
+ function cmpName(a, b) {
873
+ if (a.length !== b.length) return a.length - b.length;
874
+ const A = a.toUpperCase();
875
+ const B = b.toUpperCase();
876
+ for (let i = 0; i < A.length; i++) {
877
+ const d = A.charCodeAt(i) - B.charCodeAt(i);
878
+ if (d) return d;
879
+ }
880
+ return 0;
881
+ }
882
+ function write(root) {
883
+ const flat = [];
884
+ const assign = (node) => {
885
+ node.id = flat.length;
886
+ flat.push(node);
887
+ };
888
+ assign(root);
889
+ function buildTree(kids, lo, hi) {
890
+ if (lo >= hi) return NOSTREAM2;
891
+ const mid = lo + hi >> 1;
892
+ const n = kids[mid];
893
+ n.leftId = buildTree(kids, lo, mid);
894
+ n.rightId = buildTree(kids, mid + 1, hi);
895
+ return n.id;
896
+ }
897
+ function walk(node) {
898
+ if (!node.children || !node.children.length) {
899
+ node.childId = NOSTREAM2;
900
+ return;
901
+ }
902
+ const kids = node.children.slice().sort((a, b) => cmpName(a.name, b.name));
903
+ for (let i = 0; i < kids.length; i++) assign(kids[i]);
904
+ node.childId = buildTree(kids, 0, kids.length);
905
+ for (let j = 0; j < kids.length; j++) walk(kids[j]);
906
+ }
907
+ walk(root);
908
+ const SECTOR = 512;
909
+ const MINI = 64;
910
+ const PER = SECTOR / 4;
911
+ const bigStreams = [];
912
+ const miniStreams = [];
913
+ for (let i = 1; i < flat.length; i++) {
914
+ const n = flat[i];
915
+ if (n.type !== 2) {
916
+ n.startSector = ENDOFCHAIN2;
917
+ n.streamSize = 0;
918
+ continue;
919
+ }
920
+ n.streamSize = n.data.length;
921
+ if (n.data.length === 0) n.startSector = ENDOFCHAIN2;
922
+ else if (n.data.length >= MINI_CUTOFF2) bigStreams.push(n);
923
+ else miniStreams.push(n);
924
+ }
925
+ let miniSectorCount = 0;
926
+ for (const ms of miniStreams) {
927
+ ms.startSector = miniSectorCount;
928
+ miniSectorCount += Math.ceil(ms.data.length / MINI);
929
+ }
930
+ const miniStreamSize = miniSectorCount * MINI;
931
+ const miniStreamSectors = Math.ceil(miniStreamSize / SECTOR);
932
+ const miniStreamData = new Uint8Array(miniStreamSectors * SECTOR);
933
+ for (const ms of miniStreams) miniStreamData.set(ms.data, ms.startSector * MINI);
934
+ const bigSectorCounts = bigStreams.map((s) => Math.ceil(s.data.length / SECTOR));
935
+ const bigTotal = bigSectorCounts.reduce((x, y) => x + y, 0);
936
+ const miniFatSectors = Math.ceil(miniSectorCount * 4 / SECTOR) || 0;
937
+ const dirSectors = Math.ceil(flat.length / 4);
938
+ let fatSectors = 1;
939
+ let difatSectors = 0;
940
+ let total = 0;
941
+ for (let it = 0; it < 64; it++) {
942
+ total = bigTotal + miniStreamSectors + miniFatSectors + dirSectors + fatSectors + difatSectors;
943
+ const nf = Math.max(1, Math.ceil(total / PER));
944
+ const nd = nf <= 109 ? 0 : Math.ceil((nf - 109) / (PER - 1));
945
+ if (nf === fatSectors && nd === difatSectors) break;
946
+ fatSectors = nf;
947
+ difatSectors = nd;
948
+ }
949
+ total = bigTotal + miniStreamSectors + miniFatSectors + dirSectors + fatSectors + difatSectors;
950
+ let cursor = 0;
951
+ for (let i = 0; i < bigStreams.length; i++) {
952
+ bigStreams[i].startSector = cursor;
953
+ cursor += bigSectorCounts[i];
954
+ }
955
+ const miniStreamStart = miniStreamSectors ? cursor : ENDOFCHAIN2;
956
+ cursor += miniStreamSectors;
957
+ const miniFatStart = miniFatSectors ? cursor : ENDOFCHAIN2;
958
+ cursor += miniFatSectors;
959
+ const dirStart = cursor;
960
+ cursor += dirSectors;
961
+ const fatStart = cursor;
962
+ cursor += fatSectors;
963
+ const difatStart = difatSectors ? cursor : ENDOFCHAIN2;
964
+ cursor += difatSectors;
965
+ const fat = new Uint32Array(fatSectors * PER);
966
+ fat.fill(FREESECT2);
967
+ function runChain(start, count) {
968
+ for (let i = 0; i < count; i++) {
969
+ fat[start + i] = i === count - 1 ? ENDOFCHAIN2 : start + i + 1;
970
+ }
971
+ }
972
+ for (let i = 0; i < bigStreams.length; i++) runChain(bigStreams[i].startSector, bigSectorCounts[i]);
973
+ if (miniStreamSectors) runChain(miniStreamStart, miniStreamSectors);
974
+ if (miniFatSectors) runChain(miniFatStart, miniFatSectors);
975
+ runChain(dirStart, dirSectors);
976
+ for (let i = 0; i < fatSectors; i++) fat[fatStart + i] = FATSECT;
977
+ for (let i = 0; i < difatSectors; i++) fat[difatStart + i] = DIFSECT;
978
+ const miniFat = new Uint32Array(miniFatSectors * SECTOR / 4);
979
+ miniFat.fill(FREESECT2);
980
+ for (const ms of miniStreams) {
981
+ const cnt = Math.ceil(ms.data.length / MINI);
982
+ for (let q = 0; q < cnt; q++) {
983
+ miniFat[ms.startSector + q] = q === cnt - 1 ? ENDOFCHAIN2 : ms.startSector + q + 1;
984
+ }
985
+ }
986
+ root.startSector = miniStreamStart;
987
+ root.streamSize = miniStreamSize;
988
+ const out = new Uint8Array(SECTOR * (1 + total));
989
+ const dv = new DataView(out.buffer);
990
+ for (let i = 0; i < 8; i++) out[i] = SIG2[i];
991
+ dv.setUint16(24, 62, true);
992
+ dv.setUint16(26, 3, true);
993
+ dv.setUint16(28, 65534, true);
994
+ dv.setUint16(30, 9, true);
995
+ dv.setUint16(32, 6, true);
996
+ dv.setUint32(44, fatSectors, true);
997
+ dv.setUint32(48, dirStart, true);
998
+ dv.setUint32(56, MINI_CUTOFF2, true);
999
+ dv.setUint32(60, miniFatStart, true);
1000
+ dv.setUint32(64, miniFatSectors, true);
1001
+ dv.setUint32(68, difatStart, true);
1002
+ dv.setUint32(72, difatSectors, true);
1003
+ for (let h = 0; h < 109; h++) {
1004
+ dv.setUint32(76 + h * 4, h < fatSectors ? fatStart + h : FREESECT2, true);
1005
+ }
1006
+ const off = (sector) => SECTOR * (1 + sector);
1007
+ for (const s of bigStreams) out.set(s.data, off(s.startSector));
1008
+ if (miniStreamSectors) out.set(miniStreamData, off(miniStreamStart));
1009
+ if (miniFatSectors) {
1010
+ for (let i = 0; i < miniFat.length; i++) {
1011
+ dv.setUint32(off(miniFatStart) + i * 4, miniFat[i], true);
1012
+ }
1013
+ }
1014
+ for (let i = 0; i < fat.length; i++) dv.setUint32(off(fatStart) + i * 4, fat[i], true);
1015
+ for (let w = 0; w < difatSectors; w++) {
1016
+ const dbase = off(difatStart + w);
1017
+ for (let k = 0; k < PER - 1; k++) {
1018
+ const idx = 109 + w * (PER - 1) + k;
1019
+ dv.setUint32(dbase + k * 4, idx < fatSectors ? fatStart + idx : FREESECT2, true);
1020
+ }
1021
+ dv.setUint32(
1022
+ dbase + SECTOR - 4,
1023
+ w === difatSectors - 1 ? ENDOFCHAIN2 : difatStart + w + 1,
1024
+ true
1025
+ );
1026
+ }
1027
+ for (let e = 0; e < flat.length; e++) {
1028
+ const node = flat[e];
1029
+ const base = off(dirStart) + e * 128;
1030
+ const nlen = Math.min(node.name.length, 31);
1031
+ for (let p = 0; p < nlen; p++) dv.setUint16(base + p * 2, node.name.charCodeAt(p), true);
1032
+ dv.setUint16(base + 64, nlen * 2 + 2, true);
1033
+ dv.setUint8(base + 66, node.type);
1034
+ dv.setUint8(base + 67, 1);
1035
+ dv.setUint32(base + 68, node.leftId == null ? NOSTREAM2 : node.leftId, true);
1036
+ dv.setUint32(base + 72, node.rightId == null ? NOSTREAM2 : node.rightId, true);
1037
+ dv.setUint32(base + 76, node.childId == null ? NOSTREAM2 : node.childId, true);
1038
+ if (node.clsid) out.set(node.clsid, base + 80);
1039
+ dv.setUint32(base + 116, node.startSector == null ? ENDOFCHAIN2 : node.startSector, true);
1040
+ const sz = node.streamSize || 0;
1041
+ dv.setUint32(base + 120, sz >>> 0, true);
1042
+ dv.setUint32(base + 124, Math.floor(sz / 4294967296), true);
1043
+ }
1044
+ for (let e = flat.length; e < dirSectors * 4; e++) {
1045
+ const base = off(dirStart) + e * 128;
1046
+ dv.setUint32(base + 68, NOSTREAM2, true);
1047
+ dv.setUint32(base + 72, NOSTREAM2, true);
1048
+ dv.setUint32(base + 76, NOSTREAM2, true);
1049
+ dv.setUint32(base + 116, ENDOFCHAIN2, true);
1050
+ }
1051
+ return out;
1052
+ }
1053
+
1054
+ // src/mapi/tags.js
1055
+ var PropId = {
1056
+ SUBJECT: 55,
1057
+ NORMALIZED_SUBJECT: 3613,
1058
+ BODY: 4096,
1059
+ RTF_COMPRESSED: 4105,
1060
+ HTML: 4115,
1061
+ MESSAGE_CLASS: 26,
1062
+ TRANSPORT_HEADERS: 125,
1063
+ CLIENT_SUBMIT_TIME: 57,
1064
+ DELIVERY_TIME: 3590,
1065
+ LAST_MODIFICATION_TIME: 12296,
1066
+ CREATION_TIME: 12295,
1067
+ INTERNET_MESSAGE_ID: 4149,
1068
+ IN_REPLY_TO_ID: 4162,
1069
+ INTERNET_REFERENCES: 4153,
1070
+ DISPLAY_TO: 3588,
1071
+ DISPLAY_CC: 3587,
1072
+ DISPLAY_BCC: 3586,
1073
+ SENDER_NAME: 3098,
1074
+ SENDER_EMAIL: 3103,
1075
+ SENDER_ADDRTYPE: 3102,
1076
+ SENDER_SMTP: 23809,
1077
+ SENT_REP_NAME: 66,
1078
+ SENT_REP_EMAIL: 101,
1079
+ SENT_REP_ADDRTYPE: 100,
1080
+ SENT_REP_SMTP: 23810,
1081
+ MESSAGE_FLAGS: 3591,
1082
+ HASATTACH: 3611,
1083
+ STORE_SUPPORT_MASK: 13325,
1084
+ INTERNET_CPID: 16350,
1085
+ MESSAGE_CODEPAGE: 16381,
1086
+ PRIORITY: 38,
1087
+ IMPORTANCE: 23,
1088
+ OBJECT_TYPE: 4094,
1089
+ DISPLAY_TYPE: 14592,
1090
+ ROWID: 12288,
1091
+ RECIPIENT_TYPE: 3093,
1092
+ DISPLAY_NAME: 12289,
1093
+ EMAIL_ADDRESS: 12291,
1094
+ ADDRTYPE: 12290,
1095
+ SMTP_ADDRESS: 14846,
1096
+ ATTACH_METHOD: 14085,
1097
+ ATTACH_DATA_BIN: 14081,
1098
+ ATTACH_LONG_FILENAME: 14087,
1099
+ ATTACH_FILENAME: 14084,
1100
+ ATTACH_EXTENSION: 14083,
1101
+ ATTACH_MIME_TAG: 14094,
1102
+ ATTACH_CONTENT_ID: 14098,
1103
+ ATTACH_SIZE: 3616,
1104
+ ATTACH_NUM: 3617,
1105
+ ATTACH_FLAGS: 14100,
1106
+ ATTACHMENT_HIDDEN: 32766,
1107
+ RENDERING_POSITION: 14091
1108
+ };
1109
+ var PropType = {
1110
+ SHORT: 2,
1111
+ LONG: 3,
1112
+ FLOAT: 4,
1113
+ DOUBLE: 5,
1114
+ BOOLEAN: 11,
1115
+ I8: 20,
1116
+ STRING8: 30,
1117
+ UNICODE: 31,
1118
+ SYSTIME: 64,
1119
+ BINARY: 258,
1120
+ OBJECT: 13
1121
+ };
1122
+ var RecipientType = { TO: 1, CC: 2, BCC: 3 };
1123
+ var AttachMethod = { NONE: 0, BY_VALUE: 1, BY_REFERENCE: 2, EMBEDDED_MSG: 5 };
1124
+ var HeaderSize = {
1125
+ TOP_LEVEL: 32,
1126
+ EMBEDDED: 24,
1127
+ RECIPIENT: 8,
1128
+ ATTACHMENT: 8
1129
+ };
1130
+ var MSG_CLSID = new Uint8Array([
1131
+ 11,
1132
+ 13,
1133
+ 2,
1134
+ 0,
1135
+ 0,
1136
+ 0,
1137
+ 0,
1138
+ 0,
1139
+ 192,
1140
+ 0,
1141
+ 0,
1142
+ 0,
1143
+ 0,
1144
+ 0,
1145
+ 0,
1146
+ 70
1147
+ ]);
1148
+ var CODEPAGES = {
1149
+ 20127: "us-ascii",
1150
+ 28591: "iso-8859-1",
1151
+ 28592: "iso-8859-2",
1152
+ 28595: "iso-8859-5",
1153
+ 28597: "iso-8859-7",
1154
+ 28598: "iso-8859-8",
1155
+ 28599: "iso-8859-9",
1156
+ 28605: "iso-8859-15",
1157
+ 65e3: "utf-7",
1158
+ 65001: "utf-8",
1159
+ 1250: "windows-1250",
1160
+ 1251: "windows-1251",
1161
+ 1252: "windows-1252",
1162
+ 1253: "windows-1253",
1163
+ 1254: "windows-1254",
1164
+ 1255: "windows-1255",
1165
+ 1256: "windows-1256",
1166
+ 1257: "windows-1257",
1167
+ 1258: "windows-1258",
1168
+ 932: "shift_jis",
1169
+ 936: "gbk",
1170
+ 949: "euc-kr",
1171
+ 950: "big5",
1172
+ 874: "windows-874"
1173
+ };
1174
+ function hex4(n) {
1175
+ return ("000" + n.toString(16).toUpperCase()).slice(-4);
1176
+ }
1177
+ function hex8(n) {
1178
+ return ("0000000" + n.toString(16).toUpperCase()).slice(-8);
1179
+ }
1180
+ function tagName(id, type) {
1181
+ return "__substg1.0_" + hex4(id) + hex4(type);
1182
+ }
1183
+ function filetimeToDate(lo, hi) {
1184
+ if (!lo && !hi) return null;
1185
+ const ft = hi * 4294967296 + lo;
1186
+ const ms = ft / 1e4 - 116444736e5;
1187
+ if (!isFinite(ms) || ms < -22089888e5 || ms > 41024448e5) return null;
1188
+ return new Date(ms);
1189
+ }
1190
+ function dateToFiletime(date) {
1191
+ const ft = Math.round((date.getTime() + 116444736e5) * 1e4);
1192
+ return { lo: ft % 4294967296, hi: Math.floor(ft / 4294967296) };
1193
+ }
1194
+
1195
+ // src/mapi/bag.js
1196
+ var PropertyBag = class {
1197
+ /**
1198
+ * @param {import('../cfb/read.js').CfbFile} cfb
1199
+ * @param {object} storage Directory entry of the storage to read.
1200
+ * @param {number} headerSize One of `HeaderSize.*`.
1201
+ */
1202
+ constructor(cfb, storage, headerSize) {
1203
+ const kids = cfb.childrenOf(storage);
1204
+ this.vars = /* @__PURE__ */ Object.create(null);
1205
+ this.fixed = /* @__PURE__ */ Object.create(null);
1206
+ this.subStorages = [];
1207
+ this.cfb = cfb;
1208
+ this.childMap = kids.map;
1209
+ for (const k of kids.list) {
1210
+ const m = /^__substg1\.0_([0-9A-Fa-f]{8})$/.exec(k.name);
1211
+ if (m) {
1212
+ if (k.type === 2) this.vars[m[1].toUpperCase()] = cfb.readStream(k);
1213
+ else this.subStorages.push(k);
1214
+ continue;
1215
+ }
1216
+ if (k.type === 1) this.subStorages.push(k);
1217
+ }
1218
+ const propsEntry = kids.map["__properties_version1.0"];
1219
+ if (propsEntry) {
1220
+ const buf = cfb.readStream(propsEntry);
1221
+ const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
1222
+ for (let off = headerSize; off + 16 <= buf.length; off += 16) {
1223
+ this.fixed[dv.getUint16(off + 2, true)] = {
1224
+ type: dv.getUint16(off, true),
1225
+ lo: dv.getUint32(off + 8, true),
1226
+ hi: dv.getUint32(off + 12, true),
1227
+ i32: dv.getInt32(off + 8, true)
1228
+ };
1229
+ }
1230
+ }
1231
+ const cpid = this.fixed[PropId.INTERNET_CPID]?.i32 ?? this.fixed[PropId.MESSAGE_CODEPAGE]?.i32 ?? 0;
1232
+ this.charset = CODEPAGES[cpid] || "windows-1252";
1233
+ }
1234
+ /**
1235
+ * String property, preferring the Unicode variant.
1236
+ * @param {number} id @returns {string}
1237
+ */
1238
+ str(id) {
1239
+ const u = this.vars[hex4(id) + "001F"];
1240
+ if (u) return utf16Decode(u);
1241
+ const a = this.vars[hex4(id) + "001E"];
1242
+ if (a) return decodeBytes(a, this.charset);
1243
+ return "";
1244
+ }
1245
+ /** @param {number} id @returns {Uint8Array|null} */
1246
+ bin(id) {
1247
+ return this.vars[hex4(id) + "0102"] || null;
1248
+ }
1249
+ /** @param {number} id @returns {number|null} */
1250
+ int(id) {
1251
+ return this.fixed[id] ? this.fixed[id].i32 : null;
1252
+ }
1253
+ /** @param {number} id @returns {boolean|null} */
1254
+ bool(id) {
1255
+ return this.fixed[id] ? !!(this.fixed[id].lo & 1) : null;
1256
+ }
1257
+ /** @param {number} id @returns {Date|null} */
1258
+ date(id) {
1259
+ return this.fixed[id] ? filetimeToDate(this.fixed[id].lo, this.fixed[id].hi) : null;
1260
+ }
1261
+ /** @param {number} id @returns {boolean} */
1262
+ has(id) {
1263
+ const h = hex4(id);
1264
+ return !!(this.fixed[id] || this.vars[h + "001F"] || this.vars[h + "001E"] || this.vars[h + "0102"]);
1265
+ }
1266
+ /** Every property id present on this object. @returns {number[]} */
1267
+ ids() {
1268
+ const set = new Set(Object.keys(this.fixed).map(Number));
1269
+ for (const tag of Object.keys(this.vars)) set.add(parseInt(tag.slice(0, 4), 16));
1270
+ return [...set].sort((a, b) => a - b);
1271
+ }
1272
+ /** Nested storages whose names match a prefix, in directory order. */
1273
+ storagesMatching(re) {
1274
+ return this.subStorages.filter((s) => re.test(s.name)).sort((a, b) => a.name.localeCompare(b.name));
1275
+ }
1276
+ };
1277
+ var PropertyWriter = class {
1278
+ /**
1279
+ * @param {import('../cfb/write.js').CfbNode} node Storage to write into.
1280
+ * @param {number} headerSize One of `HeaderSize.*`.
1281
+ */
1282
+ constructor(node, headerSize) {
1283
+ this.node = node;
1284
+ this.headerSize = headerSize;
1285
+ this.entries = [];
1286
+ }
1287
+ _entry(id, type, size, lo, hi) {
1288
+ this.entries.push({ id, type, size, lo: lo || 0, hi: hi || 0 });
1289
+ }
1290
+ /** @param {number} id @param {string} value */
1291
+ str(id, value) {
1292
+ if (value == null || value === "") return this;
1293
+ const bytes = utf16Encode(String(value));
1294
+ addStream(this.node, tagName(id, PropType.UNICODE), bytes);
1295
+ this._entry(id, PropType.UNICODE, bytes.length + 2);
1296
+ return this;
1297
+ }
1298
+ /** @param {number} id @param {Uint8Array} bytes */
1299
+ bin(id, bytes) {
1300
+ if (!bytes) return this;
1301
+ addStream(this.node, tagName(id, PropType.BINARY), bytes);
1302
+ this._entry(id, PropType.BINARY, bytes.length);
1303
+ return this;
1304
+ }
1305
+ /** @param {number} id @param {number} v */
1306
+ int32(id, v) {
1307
+ if (v == null) return this;
1308
+ this._entry(id, PropType.LONG, null, v >>> 0, 0);
1309
+ return this;
1310
+ }
1311
+ /** @param {number} id @param {boolean} v */
1312
+ bool(id, v) {
1313
+ this._entry(id, PropType.BOOLEAN, null, v ? 1 : 0, 0);
1314
+ return this;
1315
+ }
1316
+ /** @param {number} id @param {Date} date */
1317
+ time(id, date) {
1318
+ if (!date) return this;
1319
+ const ft = dateToFiletime(date);
1320
+ this._entry(id, PropType.SYSTIME, null, ft.lo, ft.hi);
1321
+ return this;
1322
+ }
1323
+ /**
1324
+ * Write `__properties_version1.0`.
1325
+ * @param {(dv: DataView) => void} [headerFill] Fills the stream header.
1326
+ */
1327
+ finish(headerFill) {
1328
+ const buf = new Uint8Array(this.headerSize + this.entries.length * 16);
1329
+ const dv = new DataView(buf.buffer);
1330
+ if (headerFill) headerFill(dv);
1331
+ for (let i = 0; i < this.entries.length; i++) {
1332
+ const e = this.entries[i];
1333
+ const off = this.headerSize + i * 16;
1334
+ dv.setUint16(off, e.type, true);
1335
+ dv.setUint16(off + 2, e.id, true);
1336
+ dv.setUint32(off + 4, 6, true);
1337
+ if (e.size != null) {
1338
+ dv.setUint32(off + 8, e.size, true);
1339
+ dv.setUint32(off + 12, 0, true);
1340
+ } else {
1341
+ dv.setUint32(off + 8, e.lo, true);
1342
+ dv.setUint32(off + 12, e.hi, true);
1343
+ }
1344
+ }
1345
+ addStream(this.node, "__properties_version1.0", buf);
1346
+ }
1347
+ };
1348
+
1349
+ // src/rtf/lzfu.js
1350
+ var INIT_DICT = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
1351
+ var COMPRESSED = 1967544908;
1352
+ var UNCOMPRESSED = 1095517517;
1353
+ function decompress(bytes) {
1354
+ if (!bytes || bytes.length < 16) return null;
1355
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1356
+ const compSize = dv.getUint32(0, true);
1357
+ const rawSize = dv.getUint32(4, true);
1358
+ const magic = dv.getUint32(8, true);
1359
+ if (magic === UNCOMPRESSED) {
1360
+ return bytes.subarray(16, Math.min(bytes.length, 16 + rawSize));
1361
+ }
1362
+ if (magic !== COMPRESSED) return null;
1363
+ const dict = new Uint8Array(4096);
1364
+ for (let i = 0; i < INIT_DICT.length; i++) dict[i] = INIT_DICT.charCodeAt(i) & 255;
1365
+ let writeAt = INIT_DICT.length;
1366
+ const out = new Uint8Array(rawSize);
1367
+ let outPos = 0;
1368
+ let pos = 16;
1369
+ const end = Math.min(bytes.length, compSize + 4);
1370
+ while (pos < end && outPos < rawSize) {
1371
+ const control = bytes[pos++];
1372
+ for (let bit = 0; bit < 8 && pos < end && outPos < rawSize; bit++) {
1373
+ if (control & 1 << bit) {
1374
+ if (pos + 1 >= end) return out.subarray(0, outPos);
1375
+ const b1 = bytes[pos++];
1376
+ const b2 = bytes[pos++];
1377
+ const offset = b1 << 4 | b2 >> 4;
1378
+ const length = (b2 & 15) + 2;
1379
+ if (offset === writeAt % 4096) return out.subarray(0, outPos);
1380
+ for (let n = 0; n < length && outPos < rawSize; n++) {
1381
+ const byte = dict[(offset + n) % 4096];
1382
+ out[outPos++] = byte;
1383
+ dict[writeAt % 4096] = byte;
1384
+ writeAt++;
1385
+ }
1386
+ } else {
1387
+ const lit = bytes[pos++];
1388
+ out[outPos++] = lit;
1389
+ dict[writeAt % 4096] = lit;
1390
+ writeAt++;
1391
+ }
1392
+ }
1393
+ }
1394
+ return out.subarray(0, outPos);
1395
+ }
1396
+
1397
+ // src/rtf/deencapsulate.js
1398
+ var ENTITIES = { "&": "&amp;", "<": "&lt;", ">": "&gt;" };
1399
+ var esc = (s) => s.replace(/[&<>]/g, (c) => ENTITIES[c]);
1400
+ var SIMPLE = {
1401
+ lquote: "&lsquo;",
1402
+ rquote: "&rsquo;",
1403
+ ldblquote: "&ldquo;",
1404
+ rdblquote: "&rdquo;",
1405
+ emdash: "&mdash;",
1406
+ endash: "&ndash;",
1407
+ bullet: "&bull;",
1408
+ nbsp: "&nbsp;"
1409
+ };
1410
+ var SKIP_DESTINATIONS = /* @__PURE__ */ new Set([
1411
+ "fonttbl",
1412
+ "colortbl",
1413
+ "stylesheet",
1414
+ "info",
1415
+ "pntext",
1416
+ "generator"
1417
+ ]);
1418
+ function isEncapsulated(rtf) {
1419
+ return /\\from(html|text)1?/.test(rtf.slice(0, 4096));
1420
+ }
1421
+ function deencapsulate(rtf, decode) {
1422
+ const out = [];
1423
+ const suppressStack = [];
1424
+ let i = 0;
1425
+ const len = rtf.length;
1426
+ let suppress = false;
1427
+ let inHtmlTag = 0;
1428
+ let depth = 0;
1429
+ let skipDest = 0;
1430
+ let pendingSkipWord = false;
1431
+ let textBytes = [];
1432
+ const fromText = /\\fromtext/.test(rtf.slice(0, 4096));
1433
+ function emit(s) {
1434
+ if (skipDest || suppress && !inHtmlTag) return;
1435
+ out.push(s);
1436
+ }
1437
+ function flushBytes() {
1438
+ if (!textBytes.length) return;
1439
+ const s = decode(new Uint8Array(textBytes));
1440
+ textBytes = [];
1441
+ emit(inHtmlTag ? s : esc(s));
1442
+ }
1443
+ while (i < len) {
1444
+ const c = rtf[i];
1445
+ if (c === "\\") {
1446
+ const next = rtf[i + 1];
1447
+ if (next === "\\" || next === "{" || next === "}") {
1448
+ textBytes.push(next.charCodeAt(0));
1449
+ i += 2;
1450
+ continue;
1451
+ }
1452
+ if (next === "'") {
1453
+ textBytes.push(parseInt(rtf.substr(i + 2, 2), 16) || 0);
1454
+ i += 4;
1455
+ continue;
1456
+ }
1457
+ if (next === "*") {
1458
+ i += 2;
1459
+ pendingSkipWord = true;
1460
+ continue;
1461
+ }
1462
+ const m = /^\\([a-zA-Z]+)(-?\d+)? ?/.exec(rtf.slice(i));
1463
+ if (!m) {
1464
+ i += 2;
1465
+ continue;
1466
+ }
1467
+ const word = m[1];
1468
+ const param = m[2] === void 0 ? null : parseInt(m[2], 10);
1469
+ i += m[0].length;
1470
+ flushBytes();
1471
+ if (word === "htmltag") {
1472
+ pendingSkipWord = false;
1473
+ inHtmlTag = depth;
1474
+ continue;
1475
+ }
1476
+ if (word === "htmlrtf") {
1477
+ suppress = param !== 0;
1478
+ continue;
1479
+ }
1480
+ if (word === "mhtmltag") {
1481
+ skipDest = depth;
1482
+ pendingSkipWord = false;
1483
+ continue;
1484
+ }
1485
+ if (pendingSkipWord) {
1486
+ skipDest = depth;
1487
+ pendingSkipWord = false;
1488
+ continue;
1489
+ }
1490
+ if (word === "u" && param !== null) {
1491
+ if (!suppress || inHtmlTag) {
1492
+ const cp = param < 0 ? param + 65536 : param;
1493
+ emit(inHtmlTag ? String.fromCharCode(cp) : esc(String.fromCharCode(cp)));
1494
+ }
1495
+ if (rtf[i] === "?") i++;
1496
+ continue;
1497
+ }
1498
+ if (word === "par" || word === "line") {
1499
+ if (fromText) emit("\n");
1500
+ continue;
1501
+ }
1502
+ if (word === "tab") {
1503
+ emit(" ");
1504
+ continue;
1505
+ }
1506
+ if (SIMPLE[word]) {
1507
+ emit(SIMPLE[word]);
1508
+ continue;
1509
+ }
1510
+ if (SKIP_DESTINATIONS.has(word)) {
1511
+ skipDest = depth;
1512
+ continue;
1513
+ }
1514
+ continue;
1515
+ }
1516
+ if (c === "{") {
1517
+ flushBytes();
1518
+ depth++;
1519
+ suppressStack.push({ suppress, inHtmlTag });
1520
+ i++;
1521
+ continue;
1522
+ }
1523
+ if (c === "}") {
1524
+ flushBytes();
1525
+ if (skipDest && depth === skipDest) skipDest = 0;
1526
+ if (inHtmlTag && depth === inHtmlTag) inHtmlTag = 0;
1527
+ depth--;
1528
+ const st = suppressStack.pop();
1529
+ if (st) suppress = st.suppress;
1530
+ pendingSkipWord = false;
1531
+ i++;
1532
+ continue;
1533
+ }
1534
+ if (c === "\r" || c === "\n") {
1535
+ i++;
1536
+ continue;
1537
+ }
1538
+ textBytes.push(rtf.charCodeAt(i) & 255);
1539
+ i++;
1540
+ }
1541
+ flushBytes();
1542
+ const html = out.join("");
1543
+ if (fromText) {
1544
+ return '<html><body><pre style="font-family:inherit;white-space:pre-wrap">' + html + "</pre></body></html>";
1545
+ }
1546
+ return html;
1547
+ }
1548
+ function rtfToHtml(rtfBytes, decode) {
1549
+ if (!rtfBytes || !rtfBytes.length) return null;
1550
+ let rtf = "";
1551
+ for (let i = 0; i < rtfBytes.length; i++) rtf += String.fromCharCode(rtfBytes[i]);
1552
+ if (!isEncapsulated(rtf)) return null;
1553
+ try {
1554
+ const html = deencapsulate(rtf, decode);
1555
+ return html && html.trim() ? html : null;
1556
+ } catch {
1557
+ return null;
1558
+ }
1559
+ }
1560
+
1561
+ // src/util.js
1562
+ var MIME_BY_EXT = {
1563
+ pdf: "application/pdf",
1564
+ png: "image/png",
1565
+ jpg: "image/jpeg",
1566
+ jpeg: "image/jpeg",
1567
+ gif: "image/gif",
1568
+ bmp: "image/bmp",
1569
+ webp: "image/webp",
1570
+ svg: "image/svg+xml",
1571
+ txt: "text/plain",
1572
+ csv: "text/csv",
1573
+ html: "text/html",
1574
+ htm: "text/html",
1575
+ xml: "application/xml",
1576
+ json: "application/json",
1577
+ zip: "application/zip",
1578
+ doc: "application/msword",
1579
+ dot: "application/msword",
1580
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1581
+ xls: "application/vnd.ms-excel",
1582
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1583
+ ppt: "application/vnd.ms-powerpoint",
1584
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1585
+ eml: "message/rfc822",
1586
+ msg: "application/vnd.ms-outlook",
1587
+ ics: "text/calendar",
1588
+ rtf: "application/rtf",
1589
+ mp3: "audio/mpeg",
1590
+ mp4: "video/mp4"
1591
+ };
1592
+ function guessMime(name) {
1593
+ const m = /\.([A-Za-z0-9]+)$/.exec(name || "");
1594
+ return m && MIME_BY_EXT[m[1].toLowerCase()] || "application/octet-stream";
1595
+ }
1596
+ function safeName(s) {
1597
+ return String(s || "file").replace(/[\\/:*?"<>|\r\n\t]+/g, "_").replace(/^\.+/, "").slice(0, 120) || "file";
1598
+ }
1599
+ function shortName(name) {
1600
+ const m = /^(.*?)(\.[A-Za-z0-9]{1,3})?$/.exec(name || "file");
1601
+ const base = (m[1] || "file").replace(/[^A-Za-z0-9_-]/g, "").slice(0, 8) || "file";
1602
+ return base + (m[2] || "");
1603
+ }
1604
+ function replaceExt(name, ext) {
1605
+ return (String(name).replace(/\.[^.\\/]+$/, "") || "message") + ext;
1606
+ }
1607
+
1608
+ // src/msg/read.js
1609
+ function sniffHtmlCharset(bytes, fallback) {
1610
+ let head = "";
1611
+ for (let i = 0; i < Math.min(bytes.length, 2048); i++) head += String.fromCharCode(bytes[i]);
1612
+ const m = /charset\s*=\s*["']?\s*([\w-]+)/i.exec(head);
1613
+ return m ? m[1] : fallback;
1614
+ }
1615
+ function pickAddress(bag, nameId, emailId, addrTypeId, smtpId) {
1616
+ const name = bag.str(nameId);
1617
+ let email = bag.str(smtpId);
1618
+ if (!email) {
1619
+ const at = (bag.str(addrTypeId) || "").toUpperCase();
1620
+ const raw = bag.str(emailId);
1621
+ if (at === "SMTP" || raw && raw.indexOf("@") > 0 && raw.indexOf("/") < 0) email = raw;
1622
+ }
1623
+ return { name, email };
1624
+ }
1625
+ function readMessageStorage(cfb, storage, headerSize, opts, depth) {
1626
+ const bag = new PropertyBag(cfb, storage, headerSize);
1627
+ const data = {
1628
+ messageClass: bag.str(PropId.MESSAGE_CLASS),
1629
+ subject: bag.str(PropId.SUBJECT) || bag.str(PropId.NORMALIZED_SUBJECT),
1630
+ messageId: bag.str(PropId.INTERNET_MESSAGE_ID),
1631
+ inReplyTo: bag.str(PropId.IN_REPLY_TO_ID),
1632
+ references: bag.str(PropId.INTERNET_REFERENCES),
1633
+ date: bag.date(PropId.CLIENT_SUBMIT_TIME) || bag.date(PropId.DELIVERY_TIME) || bag.date(PropId.LAST_MODIFICATION_TIME) || null,
1634
+ importance: bag.int(PropId.IMPORTANCE),
1635
+ text: bag.str(PropId.BODY),
1636
+ html: "",
1637
+ to: [],
1638
+ cc: [],
1639
+ bcc: [],
1640
+ attachments: [],
1641
+ headers: Headers.parse(bag.str(PropId.TRANSPORT_HEADERS)),
1642
+ props: bag
1643
+ };
1644
+ let from = pickAddress(
1645
+ bag,
1646
+ PropId.SENT_REP_NAME,
1647
+ PropId.SENT_REP_EMAIL,
1648
+ PropId.SENT_REP_ADDRTYPE,
1649
+ PropId.SENT_REP_SMTP
1650
+ );
1651
+ if (!from.email && !from.name) {
1652
+ from = pickAddress(
1653
+ bag,
1654
+ PropId.SENDER_NAME,
1655
+ PropId.SENDER_EMAIL,
1656
+ PropId.SENDER_ADDRTYPE,
1657
+ PropId.SENDER_SMTP
1658
+ );
1659
+ } else if (!from.email) {
1660
+ const s = pickAddress(
1661
+ bag,
1662
+ PropId.SENDER_NAME,
1663
+ PropId.SENDER_EMAIL,
1664
+ PropId.SENDER_ADDRTYPE,
1665
+ PropId.SENDER_SMTP
1666
+ );
1667
+ if (s.email) from.email = s.email;
1668
+ }
1669
+ data.from = from;
1670
+ const htmlBin = bag.bin(PropId.HTML);
1671
+ if (htmlBin && htmlBin.length) {
1672
+ data.html = decodeBytes(htmlBin, sniffHtmlCharset(htmlBin, bag.charset));
1673
+ } else if (bag.vars[hex4(PropId.HTML) + "001F"]) {
1674
+ data.html = utf16Decode(bag.vars[hex4(PropId.HTML) + "001F"]);
1675
+ }
1676
+ if (!data.html) {
1677
+ const comp = bag.bin(PropId.RTF_COMPRESSED);
1678
+ if (comp) {
1679
+ const rtfBytes = decompress(comp);
1680
+ if (rtfBytes) {
1681
+ const cs = bag.charset;
1682
+ const html = rtfToHtml(rtfBytes, (b) => decodeBytes(b, cs));
1683
+ if (html) data.html = html;
1684
+ }
1685
+ }
1686
+ }
1687
+ for (const r of bag.storagesMatching(/^__recip_version1\.0_/)) {
1688
+ const rb = new PropertyBag(cfb, r, HeaderSize.RECIPIENT);
1689
+ const addr = pickAddress(
1690
+ rb,
1691
+ PropId.DISPLAY_NAME,
1692
+ PropId.EMAIL_ADDRESS,
1693
+ PropId.ADDRTYPE,
1694
+ PropId.SMTP_ADDRESS
1695
+ );
1696
+ if (!addr.email && !addr.name) continue;
1697
+ const type = rb.int(PropId.RECIPIENT_TYPE);
1698
+ if (type === RecipientType.CC) data.cc.push(addr);
1699
+ else if (type === RecipientType.BCC) data.bcc.push(addr);
1700
+ else data.to.push(addr);
1701
+ }
1702
+ if (!data.to.length && !data.cc.length) {
1703
+ data.to = parseAddressList(bag.str(PropId.DISPLAY_TO));
1704
+ data.cc = parseAddressList(bag.str(PropId.DISPLAY_CC));
1705
+ }
1706
+ for (const a of bag.storagesMatching(/^__attach_version1\.0_/)) {
1707
+ const ab = new PropertyBag(cfb, a, HeaderSize.ATTACHMENT);
1708
+ const method = ab.int(PropId.ATTACH_METHOD);
1709
+ const filename = ab.str(PropId.ATTACH_LONG_FILENAME) || ab.str(PropId.ATTACH_FILENAME);
1710
+ const cid = ab.str(PropId.ATTACH_CONTENT_ID);
1711
+ const mime = ab.str(PropId.ATTACH_MIME_TAG);
1712
+ const flags = ab.int(PropId.ATTACH_FLAGS) || 0;
1713
+ const hidden = ab.bool(PropId.ATTACHMENT_HIDDEN);
1714
+ if (method === AttachMethod.EMBEDDED_MSG) {
1715
+ const embedded = ab.childMap[tagName(PropId.ATTACH_DATA_BIN, PropType.OBJECT)];
1716
+ if (embedded && depth < 10) {
1717
+ try {
1718
+ const inner = readMessageStorage(cfb, embedded, HeaderSize.EMBEDDED, opts, depth + 1);
1719
+ data.attachments.push({
1720
+ filename: safeName(filename || inner.subject || "message") + ".eml",
1721
+ mime: "message/rfc822",
1722
+ embedded: inner,
1723
+ cid,
1724
+ inline: false
1725
+ });
1726
+ } catch {
1727
+ }
1728
+ }
1729
+ continue;
1730
+ }
1731
+ const name = filename || "attachment" + (data.attachments.length + 1) + (ab.str(PropId.ATTACH_EXTENSION) || ".bin");
1732
+ const att = {
1733
+ filename: name,
1734
+ mime: mime || guessMime(name),
1735
+ cid,
1736
+ inline: !!cid && ((flags & 4) !== 0 || hidden === true || !!data.html && data.html.indexOf("cid:" + cid) >= 0)
1737
+ };
1738
+ if (opts.lazy) {
1739
+ let cached = null;
1740
+ Object.defineProperty(att, "data", {
1741
+ enumerable: true,
1742
+ configurable: true,
1743
+ get() {
1744
+ if (!cached) cached = ab.bin(PropId.ATTACH_DATA_BIN) || new Uint8Array(0);
1745
+ return cached;
1746
+ }
1747
+ });
1748
+ if (!ab.has(PropId.ATTACH_DATA_BIN)) continue;
1749
+ } else {
1750
+ const bin = ab.bin(PropId.ATTACH_DATA_BIN);
1751
+ if (!bin) continue;
1752
+ att.data = bin;
1753
+ }
1754
+ data.attachments.push(att);
1755
+ }
1756
+ return data;
1757
+ }
1758
+ function readMsg(bytes, opts = {}) {
1759
+ const cfb = read(bytes);
1760
+ return readMessageStorage(cfb, cfb.root, HeaderSize.TOP_LEVEL, opts, 0);
1761
+ }
1762
+
1763
+ // src/msg/write.js
1764
+ var MSGFLAG_READ = 1;
1765
+ var STORE_UNICODE_OK = 262144;
1766
+ var CP_UTF8 = 65001;
1767
+ var ATT_MHTML_REF = 4;
1768
+ var displayName = (a) => a.name || a.email;
1769
+ function writeMsg(msg) {
1770
+ const root = newRoot(MSG_CLSID);
1771
+ const nameid = addStorage(root, "__nameid_version1.0");
1772
+ addStream(nameid, "__substg1.0_00020102", new Uint8Array(0));
1773
+ addStream(nameid, "__substg1.0_00030102", new Uint8Array(0));
1774
+ addStream(nameid, "__substg1.0_00040102", new Uint8Array(0));
1775
+ const to = msg.to || [];
1776
+ const cc = msg.cc || [];
1777
+ const bcc = msg.bcc || [];
1778
+ const attachments = msg.attachments || [];
1779
+ const from = msg.from || { name: "", email: "" };
1780
+ const p = new PropertyWriter(root, HeaderSize.TOP_LEVEL);
1781
+ p.str(PropId.MESSAGE_CLASS, msg.messageClass || "IPM.Note");
1782
+ p.str(PropId.SUBJECT, msg.subject || "");
1783
+ p.str(PropId.NORMALIZED_SUBJECT, (msg.subject || "").replace(/^\s*(re|fw|fwd)\s*:\s*/i, ""));
1784
+ p.str(PropId.BODY, msg.text || "");
1785
+ if (msg.html) p.bin(PropId.HTML, utf8Encode(msg.html));
1786
+ if (msg.headersRaw) p.str(PropId.TRANSPORT_HEADERS, msg.headersRaw);
1787
+ p.str(PropId.INTERNET_MESSAGE_ID, msg.messageId || "");
1788
+ p.str(PropId.IN_REPLY_TO_ID, msg.inReplyTo || "");
1789
+ p.str(PropId.INTERNET_REFERENCES, msg.references || "");
1790
+ const addrType = from.email ? "SMTP" : "";
1791
+ p.str(PropId.SENDER_NAME, from.name || from.email || "");
1792
+ p.str(PropId.SENDER_EMAIL, from.email || "");
1793
+ p.str(PropId.SENDER_ADDRTYPE, addrType);
1794
+ p.str(PropId.SENDER_SMTP, from.email || "");
1795
+ p.str(PropId.SENT_REP_NAME, from.name || from.email || "");
1796
+ p.str(PropId.SENT_REP_EMAIL, from.email || "");
1797
+ p.str(PropId.SENT_REP_ADDRTYPE, addrType);
1798
+ p.str(PropId.SENT_REP_SMTP, from.email || "");
1799
+ p.str(PropId.DISPLAY_TO, to.map(displayName).join("; "));
1800
+ p.str(PropId.DISPLAY_CC, cc.map(displayName).join("; "));
1801
+ p.str(PropId.DISPLAY_BCC, bcc.map(displayName).join("; "));
1802
+ p.time(PropId.CLIENT_SUBMIT_TIME, msg.date);
1803
+ p.time(PropId.DELIVERY_TIME, msg.date);
1804
+ p.time(PropId.CREATION_TIME, msg.date || /* @__PURE__ */ new Date());
1805
+ p.time(PropId.LAST_MODIFICATION_TIME, /* @__PURE__ */ new Date());
1806
+ p.int32(PropId.MESSAGE_FLAGS, MSGFLAG_READ);
1807
+ p.int32(PropId.STORE_SUPPORT_MASK, STORE_UNICODE_OK);
1808
+ p.int32(PropId.INTERNET_CPID, CP_UTF8);
1809
+ p.int32(PropId.MESSAGE_CODEPAGE, CP_UTF8);
1810
+ if (msg.importance != null) p.int32(PropId.IMPORTANCE, msg.importance);
1811
+ p.bool(PropId.HASATTACH, attachments.length > 0);
1812
+ const recipients = [
1813
+ ...to.map((a) => ({ a, t: RecipientType.TO })),
1814
+ ...cc.map((a) => ({ a, t: RecipientType.CC })),
1815
+ ...bcc.map((a) => ({ a, t: RecipientType.BCC }))
1816
+ ];
1817
+ recipients.forEach((r, i) => {
1818
+ const node = addStorage(root, "__recip_version1.0_#" + hex8(i));
1819
+ const rp = new PropertyWriter(node, HeaderSize.RECIPIENT);
1820
+ rp.int32(PropId.ROWID, i);
1821
+ rp.int32(PropId.RECIPIENT_TYPE, r.t);
1822
+ rp.int32(PropId.OBJECT_TYPE, 6);
1823
+ rp.int32(PropId.DISPLAY_TYPE, 0);
1824
+ rp.str(PropId.DISPLAY_NAME, r.a.name || r.a.email);
1825
+ rp.str(PropId.EMAIL_ADDRESS, r.a.email);
1826
+ rp.str(PropId.SMTP_ADDRESS, r.a.email);
1827
+ rp.str(PropId.ADDRTYPE, "SMTP");
1828
+ rp.finish();
1829
+ });
1830
+ attachments.forEach((att, i) => {
1831
+ const node = addStorage(root, "__attach_version1.0_#" + hex8(i));
1832
+ const ap = new PropertyWriter(node, HeaderSize.ATTACHMENT);
1833
+ ap.int32(PropId.ATTACH_NUM, i);
1834
+ ap.int32(PropId.ATTACH_METHOD, 1);
1835
+ ap.int32(PropId.OBJECT_TYPE, 7);
1836
+ ap.int32(PropId.ATTACH_SIZE, att.data.length);
1837
+ ap.int32(PropId.RENDERING_POSITION, -1);
1838
+ ap.bin(PropId.ATTACH_DATA_BIN, att.data);
1839
+ ap.str(PropId.ATTACH_LONG_FILENAME, att.filename);
1840
+ ap.str(PropId.ATTACH_FILENAME, shortName(att.filename));
1841
+ const ext = /\.[A-Za-z0-9]+$/.exec(att.filename);
1842
+ if (ext) ap.str(PropId.ATTACH_EXTENSION, ext[0]);
1843
+ ap.str(PropId.ATTACH_MIME_TAG, att.mime || guessMime(att.filename));
1844
+ if (att.cid) {
1845
+ ap.str(PropId.ATTACH_CONTENT_ID, att.cid);
1846
+ if (att.inline) {
1847
+ ap.int32(PropId.ATTACH_FLAGS, ATT_MHTML_REF);
1848
+ ap.bool(PropId.ATTACHMENT_HIDDEN, true);
1849
+ }
1850
+ }
1851
+ ap.finish();
1852
+ });
1853
+ p.finish((dv) => {
1854
+ dv.setUint32(8, recipients.length, true);
1855
+ dv.setUint32(12, attachments.length, true);
1856
+ dv.setUint32(16, recipients.length, true);
1857
+ dv.setUint32(20, attachments.length, true);
1858
+ });
1859
+ return write(root);
1860
+ }
1861
+
1862
+ // src/message.js
1863
+ var CONTENT_HEADERS = /* @__PURE__ */ new Set([
1864
+ "mime-version",
1865
+ "content-type",
1866
+ "content-transfer-encoding",
1867
+ "content-disposition",
1868
+ "content-id",
1869
+ "content-description",
1870
+ "content-language",
1871
+ "content-location"
1872
+ ]);
1873
+ var IMPORTANCE = { low: 0, normal: 1, high: 2 };
1874
+ var Message = class _Message {
1875
+ /** @param {Partial<Message>} [data] */
1876
+ constructor(data = {}) {
1877
+ this.subject = data.subject || "";
1878
+ this.from = data.from || { name: "", email: "" };
1879
+ this.to = data.to || [];
1880
+ this.cc = data.cc || [];
1881
+ this.bcc = data.bcc || [];
1882
+ this.date = data.date || null;
1883
+ this.messageId = data.messageId || "";
1884
+ this.inReplyTo = data.inReplyTo || "";
1885
+ this.references = data.references || "";
1886
+ this.importance = data.importance == null ? null : data.importance;
1887
+ this.messageClass = data.messageClass || "IPM.Note";
1888
+ this.text = data.text || "";
1889
+ this.html = data.html || "";
1890
+ this.attachments = data.attachments || [];
1891
+ this.headers = data.headers instanceof Headers ? data.headers : new Headers(data.headers || []);
1892
+ this.props = data.props || null;
1893
+ }
1894
+ /**
1895
+ * Parse an Outlook `.msg` file.
1896
+ * @param {Uint8Array} bytes
1897
+ * @param {{lazy?: boolean}} [opts] `lazy` defers attachment byte extraction
1898
+ * until `.data` is read — useful when you only want headers.
1899
+ * @returns {Message}
1900
+ */
1901
+ static fromMsg(bytes, opts = {}) {
1902
+ const data = readMsg(bytes, opts);
1903
+ data.attachments = data.attachments.map((a) => {
1904
+ if (!a.embedded) return a;
1905
+ const { embedded, ...rest } = a;
1906
+ return { ...rest, data: new _Message(embedded).toEml() };
1907
+ });
1908
+ return new _Message(data);
1909
+ }
1910
+ /**
1911
+ * Parse an RFC 5322 `.eml` file.
1912
+ * @param {Uint8Array} bytes
1913
+ * @returns {Message}
1914
+ */
1915
+ static fromEml(bytes) {
1916
+ const parsed = parseEml(bytes);
1917
+ const h = parsed.headers;
1918
+ return new _Message({
1919
+ subject: decodeWords(h.get("subject") || ""),
1920
+ from: parseAddressList(h.get("from") || "")[0] || { name: "", email: "" },
1921
+ to: parseAddressList(h.get("to") || ""),
1922
+ cc: parseAddressList(h.get("cc") || ""),
1923
+ bcc: parseAddressList(h.get("bcc") || ""),
1924
+ date: parseDate(h.get("date")),
1925
+ messageId: h.get("message-id") || "",
1926
+ inReplyTo: h.get("in-reply-to") || "",
1927
+ references: h.get("references") || "",
1928
+ importance: IMPORTANCE[(h.get("importance") || "").toLowerCase()] ?? null,
1929
+ text: parsed.text,
1930
+ html: parsed.html,
1931
+ attachments: parsed.attachments,
1932
+ headers: h
1933
+ });
1934
+ }
1935
+ /**
1936
+ * Serialise to an RFC 5322 `.eml` document.
1937
+ *
1938
+ * Headers carried in from the source are preserved verbatim — minus the
1939
+ * content headers, which are regenerated to match the body actually written.
1940
+ * @returns {Uint8Array}
1941
+ */
1942
+ toEml() {
1943
+ const out = [];
1944
+ const seen = /* @__PURE__ */ new Set();
1945
+ for (const [k, v] of this.headers) {
1946
+ if (CONTENT_HEADERS.has(k.toLowerCase())) continue;
1947
+ out.push([k, v]);
1948
+ seen.add(k.toLowerCase());
1949
+ }
1950
+ const put = (name, value) => {
1951
+ if (!value || seen.has(name.toLowerCase())) return;
1952
+ out.push([name, value]);
1953
+ seen.add(name.toLowerCase());
1954
+ };
1955
+ const list = (addrs) => addrs.map((a) => formatAddress(a.name, a.email)).filter(Boolean).join(", ");
1956
+ if (this.date) put("Date", formatDate(this.date));
1957
+ if (this.from.email || this.from.name) {
1958
+ put("From", formatAddress(this.from.name, this.from.email));
1959
+ }
1960
+ if (this.to.length) put("To", list(this.to));
1961
+ if (this.cc.length) put("Cc", list(this.cc));
1962
+ if (this.bcc.length) put("Bcc", list(this.bcc));
1963
+ if (!seen.has("subject")) put("Subject", encodeWord(this.subject || "") || " ");
1964
+ put("Message-ID", this.messageId);
1965
+ put("In-Reply-To", this.inReplyTo);
1966
+ put("References", this.references);
1967
+ return buildEml({
1968
+ headers: out,
1969
+ text: this.text,
1970
+ html: this.html,
1971
+ attachments: this.attachments
1972
+ });
1973
+ }
1974
+ /**
1975
+ * Serialise to an Outlook `.msg` file.
1976
+ * @returns {Uint8Array}
1977
+ */
1978
+ toMsg() {
1979
+ return writeMsg({
1980
+ messageClass: this.messageClass,
1981
+ subject: this.subject,
1982
+ from: this.from,
1983
+ to: this.to,
1984
+ cc: this.cc,
1985
+ bcc: this.bcc,
1986
+ date: this.date,
1987
+ messageId: this.messageId,
1988
+ inReplyTo: this.inReplyTo,
1989
+ references: this.references,
1990
+ importance: this.importance,
1991
+ text: this.text,
1992
+ html: this.html,
1993
+ attachments: this.attachments,
1994
+ // Keeping the original header block lets a later .msg -> .eml
1995
+ // conversion restore the full Received: trail.
1996
+ headersRaw: this.headers.entries.length ? this.headers.raw : ""
1997
+ });
1998
+ }
1999
+ };
2000
+
2001
+ // src/index.js
2002
+ var version = "0.1.0";
2003
+ var MSG_MAGIC = [208, 207, 17, 224, 161, 177, 26, 225];
2004
+ function detect(bytes, filename) {
2005
+ if (bytes && bytes.length >= 8) {
2006
+ let match = true;
2007
+ for (let i = 0; i < 8; i++) {
2008
+ if (bytes[i] !== MSG_MAGIC[i]) {
2009
+ match = false;
2010
+ break;
2011
+ }
2012
+ }
2013
+ if (match) return "msg";
2014
+ }
2015
+ const ext = (/\.([^.]+)$/.exec(filename || "") || [])[1];
2016
+ if (ext && ext.toLowerCase() === "msg") return "msg";
2017
+ return "eml";
2018
+ }
2019
+ function convert(bytes, opts = {}) {
2020
+ if (!bytes || !bytes.length) {
2021
+ throw new MailfileError(ErrorCode.EMPTY_INPUT, "File is empty");
2022
+ }
2023
+ const sourceFormat = opts.from || detect(bytes, opts.filename);
2024
+ const target = opts.to || (sourceFormat === "msg" ? "eml" : "msg");
2025
+ const message = sourceFormat === "msg" ? Message.fromMsg(bytes, { lazy: opts.lazy }) : Message.fromEml(bytes);
2026
+ const data = target === "msg" ? message.toMsg() : message.toEml();
2027
+ return {
2028
+ data,
2029
+ format: target,
2030
+ sourceFormat,
2031
+ mime: target === "msg" ? "application/vnd.ms-outlook" : "message/rfc822",
2032
+ filename: opts.filename ? replaceExt(opts.filename, "." + target) : void 0,
2033
+ message
2034
+ };
2035
+ }
2036
+ return __toCommonJS(index_exports);
2037
+ })();