@redpop/apple-mail-mcp 0.0.0-stage → 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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -2
  3. package/SECURITY.md +102 -0
  4. package/bin/Mail MCP Helper.app/Contents/CodeResources +0 -0
  5. package/bin/Mail MCP Helper.app/Contents/Info.plist +26 -0
  6. package/bin/Mail MCP Helper.app/Contents/MacOS/mail-helper +0 -0
  7. package/bin/Mail MCP Helper.app/Contents/Resources/AppIcon.icns +0 -0
  8. package/bin/Mail MCP Helper.app/Contents/_CodeSignature/CodeResources +128 -0
  9. package/dist/bodyScan.js +259 -0
  10. package/dist/bodyScan.js.map +1 -0
  11. package/dist/cli/doctor.js +242 -0
  12. package/dist/cli/doctor.js.map +1 -0
  13. package/dist/cli/main.js +59 -0
  14. package/dist/cli/main.js.map +1 -0
  15. package/dist/cli/setup.js +107 -0
  16. package/dist/cli/setup.js.map +1 -0
  17. package/dist/config.js +74 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/context.js +43 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/cursor.js +39 -0
  22. package/dist/cursor.js.map +1 -0
  23. package/dist/dates.js +44 -0
  24. package/dist/dates.js.map +1 -0
  25. package/dist/errors.js +56 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/fdaPrompt.js +29 -0
  28. package/dist/fdaPrompt.js.map +1 -0
  29. package/dist/fold.js +8 -0
  30. package/dist/fold.js.map +1 -0
  31. package/dist/helper.js +45 -0
  32. package/dist/helper.js.map +1 -0
  33. package/dist/index.js +37 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/links.js +9 -0
  36. package/dist/links.js.map +1 -0
  37. package/dist/mailboxes.js +124 -0
  38. package/dist/mailboxes.js.map +1 -0
  39. package/dist/mime.js +151 -0
  40. package/dist/mime.js.map +1 -0
  41. package/dist/mimeTrim.js +222 -0
  42. package/dist/mimeTrim.js.map +1 -0
  43. package/dist/policy.js +84 -0
  44. package/dist/policy.js.map +1 -0
  45. package/dist/quickHtml.js +238 -0
  46. package/dist/quickHtml.js.map +1 -0
  47. package/dist/quotes.js +72 -0
  48. package/dist/quotes.js.map +1 -0
  49. package/dist/schemas.js +57 -0
  50. package/dist/schemas.js.map +1 -0
  51. package/dist/securityMail.data.js +34 -0
  52. package/dist/securityMail.data.js.map +1 -0
  53. package/dist/securityMail.js +82 -0
  54. package/dist/securityMail.js.map +1 -0
  55. package/dist/server.js +19 -0
  56. package/dist/server.js.map +1 -0
  57. package/dist/signing.js +64 -0
  58. package/dist/signing.js.map +1 -0
  59. package/dist/state.js +46 -0
  60. package/dist/state.js.map +1 -0
  61. package/dist/stdioRunner.js +206 -0
  62. package/dist/stdioRunner.js.map +1 -0
  63. package/dist/summary.js +57 -0
  64. package/dist/summary.js.map +1 -0
  65. package/dist/tools/getMessage.js +112 -0
  66. package/dist/tools/getMessage.js.map +1 -0
  67. package/dist/tools/getThread.js +52 -0
  68. package/dist/tools/getThread.js.map +1 -0
  69. package/dist/tools/index.js +11 -0
  70. package/dist/tools/index.js.map +1 -0
  71. package/dist/tools/listMailboxes.js +50 -0
  72. package/dist/tools/listMailboxes.js.map +1 -0
  73. package/dist/tools/respond.js +34 -0
  74. package/dist/tools/respond.js.map +1 -0
  75. package/dist/tools/searchMessages.js +159 -0
  76. package/dist/tools/searchMessages.js.map +1 -0
  77. package/package.json +60 -4
@@ -0,0 +1,124 @@
1
+ import { fold } from './fold.js';
2
+ // Namenslisten je Rolle (Deutsch, Englisch, Franzoesisch, Spanisch, Italienisch,
3
+ // Niederlaendisch, Gmail). Verglichen wird nach fold() gegen das letzte Pfadsegment.
4
+ export const SENT_NAMES = [
5
+ 'Sent', 'Sent Messages', 'Sent Items', 'Sent Mail',
6
+ 'Gesendet', 'Gesendete Objekte', 'Gesendete Elemente', 'Gesendete Nachrichten',
7
+ 'Envoyés', 'Éléments envoyés', 'Messages envoyés',
8
+ 'Enviados', 'Elementos enviados', 'Mensajes enviados', 'Enviado',
9
+ 'Posta inviata', 'Inviati', 'Elementi inviati', 'Inviata',
10
+ 'Verzonden', 'Verzonden items', 'Verzonden berichten',
11
+ ];
12
+ export const DRAFTS_NAMES = [
13
+ 'Drafts', 'Draft', 'Entwürfe', 'Entwurf', 'Brouillons', 'Borradores', 'Bozze', 'Concepten', 'Concept',
14
+ ];
15
+ export const ARCHIVE_NAMES = [
16
+ 'Archive', 'Archives', 'Archiv', 'Archivo', 'Archivio', 'Archief', 'Archivés', 'Archivierte Nachrichten',
17
+ ];
18
+ export const JUNK_NAMES = [
19
+ 'Junk', 'Junk E-mail', 'Junk Email', 'Junk Mail', 'Spam', 'Bulk Mail',
20
+ 'Junk-E-Mail', 'Werbung',
21
+ 'Courrier indésirable', 'Indésirables', 'Pourriel',
22
+ 'Correo no deseado', 'No deseado', 'Correo basura',
23
+ 'Posta indesiderata', 'Indesiderata', 'Posta indesiderati',
24
+ 'Ongewenst', 'Ongewenste e-mail', 'Ongewenste post',
25
+ ];
26
+ export const TRASH_NAMES = [
27
+ 'Trash', 'Deleted Messages', 'Deleted Items', 'Bin', 'Deleted',
28
+ 'Papierkorb', 'Gelöschte Objekte', 'Gelöschte Elemente', 'Gelöschte Nachrichten',
29
+ 'Corbeille', 'Éléments supprimés', 'Messages supprimés',
30
+ 'Papelera', 'Elementos eliminados', 'Elementos borrados', 'Eliminados',
31
+ 'Cestino', 'Posta eliminata', 'Elementi eliminati',
32
+ 'Prullenbak', 'Verwijderde items', 'Verwijderd',
33
+ ];
34
+ export const ALLMAIL_NAMES = [
35
+ 'All Mail', 'Alle Nachrichten', 'Tous les messages', 'Todos los mensajes',
36
+ 'Tutti i messaggi', 'Alle berichten',
37
+ ];
38
+ export const INBOX_NAME = 'INBOX';
39
+ const ROLE_LISTS = [
40
+ ['sent', new Set(SENT_NAMES.map(fold))],
41
+ ['drafts', new Set(DRAFTS_NAMES.map(fold))],
42
+ ['archive', new Set(ARCHIVE_NAMES.map(fold))],
43
+ ['junk', new Set(JUNK_NAMES.map(fold))],
44
+ ['trash', new Set(TRASH_NAMES.map(fold))],
45
+ ['allmail', new Set(ALLMAIL_NAMES.map(fold))],
46
+ ];
47
+ function typeOf(scheme) {
48
+ switch (scheme.toLowerCase()) {
49
+ case 'imap': return 'imap';
50
+ case 'local': return 'local';
51
+ case 'ews': return 'exchange';
52
+ default: return 'other';
53
+ }
54
+ }
55
+ // Dekodiert Pfadsegmente; null bei jedem unzulaessigen Segment.
56
+ function decodePath(tail) {
57
+ if (tail === '')
58
+ return null;
59
+ const out = [];
60
+ for (const seg of tail.split('/')) {
61
+ let d;
62
+ try {
63
+ d = decodeURIComponent(seg);
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ if (d === '' || d === '.' || d === '..' || d.includes('/') || d.includes('\0'))
69
+ return null;
70
+ out.push(d);
71
+ }
72
+ return out;
73
+ }
74
+ function roleOf(path) {
75
+ if (path.length === 0)
76
+ return 'other';
77
+ const last = fold(path[path.length - 1]);
78
+ if (path.length === 1 && last === fold(INBOX_NAME))
79
+ return 'inbox';
80
+ for (const [role, names] of ROLE_LISTS)
81
+ if (names.has(last))
82
+ return role;
83
+ return 'other';
84
+ }
85
+ // Rolle bei unzulaessigem Pfad: letztes nicht leeres Roh-Segment, nach Moeglichkeit
86
+ // dekodiert, sonst roh. Nie inbox. So bleiben Junk/Trash trotzdem ausgeschlossen.
87
+ function bestEffortRole(tail) {
88
+ const segs = tail.split('/');
89
+ for (let i = segs.length - 1; i >= 0; i--) {
90
+ let t = segs[i];
91
+ try {
92
+ t = decodeURIComponent(t);
93
+ }
94
+ catch {
95
+ // roher Text
96
+ }
97
+ const last = t.split('/').filter((x) => x !== '').pop();
98
+ if (last === undefined)
99
+ continue;
100
+ const r = roleOf([last, last]);
101
+ return r === 'inbox' ? 'other' : r;
102
+ }
103
+ return 'other';
104
+ }
105
+ export function parseMailbox(raw) {
106
+ const m = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^/]*)(?:\/(.*))?$/s.exec(raw.url);
107
+ const scheme = m?.[1] ?? '';
108
+ const accountId = m?.[2] ?? '';
109
+ const tail = m ? (m[3] ?? '') : raw.url;
110
+ const decoded = m ? decodePath(tail) : null;
111
+ const path = decoded ?? [];
112
+ return {
113
+ id: raw.id,
114
+ url: raw.url,
115
+ accountId,
116
+ type: m ? typeOf(scheme) : 'other',
117
+ path,
118
+ name: path.length > 0 ? path[path.length - 1] : tail,
119
+ role: decoded ? roleOf(path) : m ? bestEffortRole(tail) : 'other',
120
+ total: raw.total,
121
+ unread: raw.unread,
122
+ };
123
+ }
124
+ //# sourceMappingURL=mailboxes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailboxes.js","sourceRoot":"","sources":["../src/mailboxes.ts"],"names":[],"mappings":"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"}
package/dist/mime.js ADDED
@@ -0,0 +1,151 @@
1
+ import PostalMime from 'postal-mime';
2
+ import { convert } from 'html-to-text';
3
+ import { textParts, trimForScan } from './mimeTrim.js';
4
+ import { quickHtmlText } from './quickHtml.js';
5
+ /**
6
+ * Entfernt den .emlx-Rahmen: erste Zeile ist die Laenge der Nachricht in Bytes,
7
+ * der Rest (Plist) wird verworfen. Ohne Zahl oder bei zu grosser Laenge bleibt alles.
8
+ */
9
+ export function stripEmlxFrame(raw) {
10
+ const nl = raw.indexOf(0x0a);
11
+ if (nl < 1 || nl > 20)
12
+ return raw;
13
+ const first = raw.subarray(0, nl).toString('latin1').trim();
14
+ if (!/^\d+$/.test(first))
15
+ return raw;
16
+ const length = Number(first);
17
+ const body = raw.subarray(nl + 1);
18
+ if (!Number.isSafeInteger(length) || length > body.length)
19
+ return body;
20
+ return body.subarray(0, length);
21
+ }
22
+ function isKnownCharset(label) {
23
+ try {
24
+ new TextDecoder(label);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ function toAddress(a) {
32
+ if (!a.address)
33
+ return null;
34
+ return { address: a.address, name: a.name ? a.name : null };
35
+ }
36
+ function toAddresses(list) {
37
+ return (list ?? []).map(toAddress).filter((a) => a !== null);
38
+ }
39
+ export function normalise(s) {
40
+ return s
41
+ .normalize('NFC')
42
+ .replace(/\r\n?/g, '\n')
43
+ .replace(/\n{4,}/g, '\n\n\n')
44
+ .trim();
45
+ }
46
+ export function htmlToText(html) {
47
+ return convert(html, {
48
+ wordwrap: false,
49
+ selectors: [
50
+ { selector: 'a', options: { linkBrackets: ['[', ']'], hideLinkHrefIfSameAsText: true } },
51
+ { selector: 'img', format: 'skip' },
52
+ // Ueberschriften und Tabellenkoepfe in Originalschreibung, nicht in Grossbuchstaben.
53
+ ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].map((selector) => ({ selector, options: { uppercase: false } })),
54
+ { selector: 'table', options: { uppercaseHeaderCells: false } },
55
+ ],
56
+ });
57
+ }
58
+ /**
59
+ * Lesbar ist eine Textart (Klartext oder HTML), wenn sie keinen Teil hat oder mindestens einen
60
+ * ohne Zeichensatz oder mit bekanntem. Nur ein unbekannter Zeichensatz liefert sonst geratenen
61
+ * Text (postal-mime faellt dann auf UTF-8 oder Windows-1252 zurueck). Ohne erkannte Struktur
62
+ * (`parts` null) entscheidet postal-mime allein.
63
+ */
64
+ function readable(parts, type) {
65
+ if (parts === null)
66
+ return true;
67
+ const own = parts.filter((p) => p.type === type);
68
+ return own.length === 0 || own.some((p) => p.charset === null || isKnownCharset(p.charset));
69
+ }
70
+ /**
71
+ * MIME parsen und den Textteil waehlen: `text`, sonst `html`, jeweils nur mit lesbarem
72
+ * Zeichensatz (pro Teil geprueft). Null, wenn kein lesbarer Text vorliegt. Erwartet die
73
+ * Nachricht ohne .emlx-Rahmen.
74
+ */
75
+ async function decode(mime) {
76
+ const parts = textParts(mime);
77
+ const email = await PostalMime.parse(mime);
78
+ if (typeof email.text === 'string' && email.text.trim() !== '' && readable(parts, 'text/plain'))
79
+ return { email, source: { format: 'plain', text: email.text } };
80
+ if (typeof email.html === 'string' && email.html.trim() !== '' && readable(parts, 'text/html'))
81
+ return { email, source: { format: 'html', html: email.html } };
82
+ return null;
83
+ }
84
+ /** Rahmen entfernen, MIME parsen, HTML zu Text, normalisieren. Wirft nie. */
85
+ export async function parseMessage(raw) {
86
+ try {
87
+ const decoded = await decode(stripEmlxFrame(raw));
88
+ if (decoded === null)
89
+ return { ok: false };
90
+ const { email, source } = decoded;
91
+ const text = source.format === 'plain' ? source.text : htmlToText(source.html);
92
+ return {
93
+ ok: true,
94
+ text: normalise(text),
95
+ format: source.format,
96
+ headers: {
97
+ from: email.from ? toAddress(email.from) : null,
98
+ to: toAddresses(email.to),
99
+ cc: toAddresses(email.cc),
100
+ replyTo: toAddresses(email.replyTo),
101
+ date: email.date && !Number.isNaN(new Date(email.date).getTime()) ? new Date(email.date) : null,
102
+ },
103
+ };
104
+ }
105
+ catch {
106
+ return { ok: false };
107
+ }
108
+ }
109
+ export async function parseForScan(raw) {
110
+ try {
111
+ const mime = stripEmlxFrame(raw);
112
+ const trimmed = trimForScan(mime);
113
+ let decoded = trimmed === null ? null : await decode(trimmed).catch(() => null);
114
+ const approximate = decoded !== null;
115
+ // Ungekuerzt, oder gekuerzt kein Text (etwa leerer Klartext neben entferntem HTML).
116
+ decoded ??= await decode(mime);
117
+ if (decoded === null)
118
+ return { ok: false };
119
+ const { source } = decoded;
120
+ const full = async () => {
121
+ const parsed = await parseMessage(raw);
122
+ return parsed.ok ? parsed.text : null;
123
+ };
124
+ if (source.format === 'plain') {
125
+ const text = normalise(source.text);
126
+ if (approximate)
127
+ return { ok: true, exact: false, quick: text, sure: true, full };
128
+ return { ok: true, exact: true, quick: text, full: async () => text };
129
+ }
130
+ const quick = quickHtmlText(source.html);
131
+ return { ok: true, exact: false, quick: quick.text, sure: quick.sure, full };
132
+ }
133
+ catch {
134
+ return { ok: false };
135
+ }
136
+ }
137
+ /** Kuerzt an einer Wortgrenze. `totalChars` ist die Laenge vor dem Kuerzen. */
138
+ export function truncateText(text, maxChars) {
139
+ const totalChars = text.length;
140
+ if (totalChars <= maxChars)
141
+ return { text, truncated: false, totalChars };
142
+ let cut = text.slice(0, maxChars);
143
+ // Mitten im Wort? Dann bis zum letzten Leerraum zurueck.
144
+ if (!/\s/.test(text.charAt(maxChars))) {
145
+ const ws = cut.search(/\s\S*$/);
146
+ if (ws > 0)
147
+ cut = cut.slice(0, ws);
148
+ }
149
+ return { text: cut.trimEnd(), truncated: true, totalChars };
150
+ }
151
+ //# sourceMappingURL=mime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.js","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Kuerzt eine Nachricht fuer den Body-Scan: entfernt den Inhalt von Teilen, die postal-mime
3
+ * nie in den Text uebernimmt (Anhaenge, Bilder, application/*, sonstige text/*-Typen) sowie den
4
+ * HTML-Teil eines multipart/alternative, der einen Klartext-Teil daneben hat. postal-mime muss
5
+ * diese Bytes dann nicht dekodieren; bei einem 200-KB-Anhang ist das fast die ganze Parse-Zeit.
6
+ *
7
+ * Die Kopfzeilen bleiben, nur Koerper fallen weg. Im Zweifel wird nichts gekuerzt (null): ohne
8
+ * Multipart an der Wurzel, ohne Boundary, bei doppelt vergebener Boundary, doppelten
9
+ * Content-Type-Kopfzeilen oder zu tiefer Verschachtelung. Die Grenzen werden wie in postal-mime
10
+ * erkannt: Zeile beginnt mit "--" und einer aktiven Boundary (innerste zuerst), danach optional
11
+ * "--" und nur noch Leerzeichen oder Tabs; abschliessende CR gehoeren nicht zur Zeile.
12
+ *
13
+ * Der Scan verlaesst sich darauf nicht: jeder Treffer in einer gekuerzten Nachricht wird am
14
+ * ungekuerzten Text bestaetigt. Ein Fehler hier kann also einen Treffer verfehlen, aber nie
15
+ * einen falschen oder eine Sicherheitsmail liefern.
16
+ */
17
+ const MAX_DEPTH = 16;
18
+ const MAX_PARTS = 2000;
19
+ class Abort extends Error {
20
+ }
21
+ /** Zeilenende ohne LF; abschliessende CR werden wie in postal-mime abgeschnitten. */
22
+ function lineAt(s, start) {
23
+ const nl = s.indexOf('\n', start);
24
+ let end = nl < 0 ? s.length : nl;
25
+ const next = nl < 0 ? s.length : nl + 1;
26
+ while (end > start && s.charCodeAt(end - 1) === 0x0d)
27
+ end--;
28
+ return { text: s.slice(start, end), next };
29
+ }
30
+ /** Kopfblock ab `start` bis zur ersten Leerzeile vor `limit`; null, wenn keine Leerzeile kommt. */
31
+ function readHeaders(s, start, limit) {
32
+ const fields = [];
33
+ let p = start;
34
+ while (p < limit) {
35
+ const { text, next } = lineAt(s, p);
36
+ if (text === '') {
37
+ return toHeaders(fields, next);
38
+ }
39
+ if ((text.startsWith(' ') || text.startsWith('\t')) && fields.length > 0)
40
+ fields[fields.length - 1] += text;
41
+ else
42
+ fields.push(text);
43
+ p = next;
44
+ }
45
+ return null;
46
+ }
47
+ function toHeaders(fields, bodyStart) {
48
+ let type = null;
49
+ let disposition = null;
50
+ let boundary = null;
51
+ let charset = null;
52
+ let types = 0;
53
+ for (const f of fields) {
54
+ const colon = f.indexOf(':');
55
+ if (colon < 0)
56
+ continue;
57
+ const key = f.slice(0, colon).trim().toLowerCase();
58
+ const value = f.slice(colon + 1);
59
+ if (key === 'content-type') {
60
+ types++;
61
+ type = value.split(';')[0].trim().toLowerCase();
62
+ const b = /;\s*boundary\s*=\s*(?:"([^"]*)"|([^;\s]+))/i.exec(value);
63
+ boundary = b === null ? null : (b[1] ?? b[2] ?? null);
64
+ charset = /;\s*charset\s*=\s*"?([A-Za-z0-9_.:-]+)/i.exec(value)?.[1] ?? null;
65
+ }
66
+ else if (key === 'content-disposition') {
67
+ disposition = value.split(';')[0].trim().toLowerCase();
68
+ }
69
+ }
70
+ if (types > 1)
71
+ throw new Abort();
72
+ return { type, boundary, charset, disposition, bodyStart };
73
+ }
74
+ /** Naechste Grenzzeile einer aktiven Boundary ab dem Zeilenanfang `from`. */
75
+ function nextDelimiter(s, from, active) {
76
+ let ls = s.startsWith('--', from) ? from : -1;
77
+ if (ls < 0) {
78
+ const k = s.indexOf('\n--', from);
79
+ ls = k < 0 ? -1 : k + 1;
80
+ }
81
+ while (ls >= 0) {
82
+ const { text, next } = lineAt(s, ls);
83
+ for (let i = active.length - 1; i >= 0; i--) {
84
+ const b = active[i];
85
+ if (!text.startsWith(b, 2))
86
+ continue;
87
+ let rest = text.slice(2 + b.length);
88
+ const terminator = rest.startsWith('--');
89
+ if (terminator)
90
+ rest = rest.slice(2);
91
+ if (/^[ \t]*$/.test(rest))
92
+ return { start: ls, next, boundary: b, terminator };
93
+ }
94
+ const k = s.indexOf('\n--', ls);
95
+ ls = k < 0 ? -1 : k + 1;
96
+ }
97
+ return null;
98
+ }
99
+ const NEVER_TEXT = /^(?:application|image|audio|video|font|model)\//;
100
+ function isDroppable(h, htmlHasPlainSibling) {
101
+ const type = h.type ?? 'text/plain';
102
+ if (type.startsWith('multipart/') || type.startsWith('message/'))
103
+ return false;
104
+ if (h.disposition === 'attachment')
105
+ return true;
106
+ if (NEVER_TEXT.test(type))
107
+ return true;
108
+ if (type === 'text/html')
109
+ return htmlHasPlainSibling;
110
+ return type.startsWith('text/') && type !== 'text/plain';
111
+ }
112
+ /** Die Teile eines Multipart-Koerpers, der bei `from` beginnt, mit dem Ende ihres Koerpers. */
113
+ function children(s, from, boundary, outer, depth, w) {
114
+ if (depth > MAX_DEPTH || boundary === '' || outer.includes(boundary))
115
+ throw new Abort();
116
+ const active = [...outer, boundary];
117
+ const out = [];
118
+ let d = nextDelimiter(s, from, active);
119
+ // Vorspann bis zur ersten eigenen Grenze; endet sie vorher an einer aeusseren, gibt es keine Teile.
120
+ while (d !== null && d.boundary === boundary && !d.terminator) {
121
+ if (++w.parts > MAX_PARTS)
122
+ throw new Abort();
123
+ const next = nextDelimiter(s, d.next, active);
124
+ const end = next === null ? s.length : next.start;
125
+ const h = readHeaders(s, d.next, end);
126
+ if (h !== null)
127
+ out.push({ h, end });
128
+ d = next;
129
+ }
130
+ return out;
131
+ }
132
+ /** Geht die Teile eines Multipart-Koerpers durch, der bei `from` beginnt. */
133
+ function walk(s, from, boundary, subtype, outer, depth, w) {
134
+ const parts = children(s, from, boundary, outer, depth, w);
135
+ const active = [...outer, boundary];
136
+ const hasPlain = subtype === 'alternative' && parts.some(({ h }) => (h.type ?? 'text/plain') === 'text/plain' && h.disposition !== 'attachment');
137
+ for (const { h, end } of parts) {
138
+ const type = h.type ?? 'text/plain';
139
+ if (type.startsWith('multipart/')) {
140
+ if (h.boundary === null)
141
+ throw new Abort();
142
+ walk(s, h.bodyStart, h.boundary, type.slice('multipart/'.length), active, depth + 1, w);
143
+ }
144
+ else if (end > h.bodyStart && isDroppable(h, hasPlain)) {
145
+ w.cuts.push([h.bodyStart, end]);
146
+ }
147
+ }
148
+ }
149
+ export function trimForScan(mime) {
150
+ const s = mime.toString('latin1');
151
+ try {
152
+ const root = readHeaders(s, 0, s.length);
153
+ if (root === null || root.type === null || !root.type.startsWith('multipart/'))
154
+ return null;
155
+ if (root.boundary === null)
156
+ return null;
157
+ const w = { cuts: [], parts: 0 };
158
+ walk(s, root.bodyStart, root.boundary, root.type.slice('multipart/'.length), [], 0, w);
159
+ if (w.cuts.length === 0)
160
+ return null;
161
+ w.cuts.sort((a, b) => a[0] - b[0]);
162
+ let out = '';
163
+ let at = 0;
164
+ for (const [start, end] of w.cuts) {
165
+ if (start < at)
166
+ return null;
167
+ out += s.slice(at, start);
168
+ at = end;
169
+ }
170
+ out += s.slice(at);
171
+ return Buffer.from(out, 'latin1');
172
+ }
173
+ catch (e) {
174
+ if (e instanceof Abort)
175
+ return null;
176
+ throw e;
177
+ }
178
+ }
179
+ function collectText(s, h, end, active, depth, w, out) {
180
+ if (depth > MAX_DEPTH)
181
+ throw new Abort();
182
+ const type = h.type ?? 'text/plain';
183
+ if (type.startsWith('multipart/')) {
184
+ if (h.boundary === null)
185
+ throw new Abort();
186
+ for (const c of children(s, h.bodyStart, h.boundary, active, depth, w))
187
+ collectText(s, c.h, c.end, [...active, h.boundary], depth + 1, w, out);
188
+ }
189
+ else if (type === 'message/rfc822') {
190
+ // postal-mime liest eine eingebettete Nachricht mit, solange sie kein Anhang ist.
191
+ if (h.disposition === 'attachment')
192
+ return;
193
+ const inner = readHeaders(s, h.bodyStart, end);
194
+ if (inner !== null)
195
+ collectText(s, inner, end, active, depth + 1, w, out);
196
+ }
197
+ else if ((type === 'text/plain' || type === 'text/html') && h.disposition !== 'attachment') {
198
+ out.push({ type, charset: h.charset });
199
+ }
200
+ }
201
+ /**
202
+ * Die Teile, aus denen postal-mime Text oder HTML macht, mit ihrem Zeichensatz (null = keiner
203
+ * angegeben). Liest nur echte Kopfbloecke, nie Kopfzeilen, die im Text einer Nachricht stehen.
204
+ * null, wenn die Struktur nicht sicher erkannt wird (wie bei trimForScan).
205
+ */
206
+ export function textParts(mime) {
207
+ const s = mime.toString('latin1');
208
+ try {
209
+ const root = readHeaders(s, 0, s.length);
210
+ if (root === null)
211
+ return null;
212
+ const out = [];
213
+ collectText(s, root, s.length, [], 0, { cuts: [], parts: 0 }, out);
214
+ return out;
215
+ }
216
+ catch (e) {
217
+ if (e instanceof Abort)
218
+ return null;
219
+ throw e;
220
+ }
221
+ }
222
+ //# sourceMappingURL=mimeTrim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mimeTrim.js","sourceRoot":"","sources":["../src/mimeTrim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,SAAS,GAAG,EAAE,CAAA;AACpB,MAAM,SAAS,GAAG,IAAI,CAAA;AAEtB,MAAM,KAAM,SAAQ,KAAK;CAAG;AAU5B,qFAAqF;AACrF,SAAS,MAAM,CAAC,CAAS,EAAE,KAAa;IACtC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjC,IAAI,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAChC,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACvC,OAAO,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI;QAAE,GAAG,EAAE,CAAA;IAC3D,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAA;AAC5C,CAAC;AAED,mGAAmG;AACnG,SAAS,WAAW,CAAC,CAAS,EAAE,KAAa,EAAE,KAAa;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,CAAC,GAAG,KAAK,CAAA;IACb,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC;QACjB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACnC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAChC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAA;;YACtG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtB,CAAC,GAAG,IAAI,CAAA;IACV,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,SAAS,CAAC,MAAgB,EAAE,SAAiB;IACpD,IAAI,IAAI,GAAkB,IAAI,CAAA;IAC9B,IAAI,WAAW,GAAkB,IAAI,CAAA;IACrC,IAAI,QAAQ,GAAkB,IAAI,CAAA;IAClC,IAAI,OAAO,GAAkB,IAAI,CAAA;IACjC,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,KAAK,GAAG,CAAC;YAAE,SAAQ;QACvB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QAClD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAChC,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;YAC3B,KAAK,EAAE,CAAA;YACP,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;YAChD,MAAM,CAAC,GAAG,6CAA6C,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACnE,QAAQ,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;YACrD,OAAO,GAAG,yCAAyC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAC9E,CAAC;aAAM,IAAI,GAAG,KAAK,qBAAqB,EAAE,CAAC;YACzC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACzD,CAAC;IACH,CAAC;IACD,IAAI,KAAK,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,EAAE,CAAA;IAChC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAA;AAC5D,CAAC;AASD,6EAA6E;AAC7E,SAAS,aAAa,CAAC,CAAS,EAAE,IAAY,EAAE,MAAgB;IAC9D,IAAI,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7C,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACjC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACpC,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;YACpB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;gBAAE,SAAQ;YACpC,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;YACnC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YACxC,IAAI,UAAU;gBAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACpC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAA;QAChF,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAC/B,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,GAAG,iDAAiD,CAAA;AAEpE,SAAS,WAAW,CAAC,CAAU,EAAE,mBAA4B;IAC3D,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,YAAY,CAAA;IACnC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAA;IAC9E,IAAI,CAAC,CAAC,WAAW,KAAK,YAAY;QAAE,OAAO,IAAI,CAAA;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACtC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,mBAAmB,CAAA;IACpD,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,IAAI,KAAK,YAAY,CAAA;AAC1D,CAAC;AAOD,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,CAAS,EAAE,IAAY,EAAE,QAAgB,EAAE,KAAe,EAAE,KAAa,EAAE,CAAO;IAClG,IAAI,KAAK,GAAG,SAAS,IAAI,QAAQ,KAAK,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,EAAE,CAAA;IACvF,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,EAAE,QAAQ,CAAC,CAAA;IACnC,MAAM,GAAG,GAAuC,EAAE,CAAA;IAClD,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACtC,oGAAoG;IACpG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QAC9D,IAAI,EAAE,CAAC,CAAC,KAAK,GAAG,SAAS;YAAE,MAAM,IAAI,KAAK,EAAE,CAAA;QAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC7C,MAAM,GAAG,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;QACjD,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;QACpC,CAAC,GAAG,IAAI,CAAA;IACV,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,6EAA6E;AAC7E,SAAS,IAAI,CAAC,CAAS,EAAE,IAAY,EAAE,QAAgB,EAAE,OAAe,EAAE,KAAe,EAAE,KAAa,EAAE,CAAO;IAC/G,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAC1D,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,EAAE,QAAQ,CAAC,CAAA;IACnC,MAAM,QAAQ,GAAG,OAAO,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,KAAK,YAAY,IAAI,CAAC,CAAC,WAAW,KAAK,YAAY,CAAC,CAAA;IAChJ,KAAK,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,YAAY,CAAA;QACnC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI;gBAAE,MAAM,IAAI,KAAK,EAAE,CAAA;YAC1C,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;QACzF,CAAC;aAAM,IAAI,GAAG,GAAG,CAAC,CAAC,SAAS,IAAI,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC;YACzD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAA;QAC3F,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QACvC,MAAM,CAAC,GAAS,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;QACtC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACtF,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACpC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAClC,IAAI,GAAG,GAAG,EAAE,CAAA;QACZ,IAAI,EAAE,GAAG,CAAC,CAAA;QACV,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,KAAK,GAAG,EAAE;gBAAE,OAAO,IAAI,CAAA;YAC3B,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YACzB,EAAE,GAAG,GAAG,CAAA;QACV,CAAC;QACD,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAClB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,KAAK;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,CAAC,CAAA;IACT,CAAC;AACH,CAAC;AAOD,SAAS,WAAW,CAAC,CAAS,EAAE,CAAU,EAAE,GAAW,EAAE,MAAgB,EAAE,KAAa,EAAE,CAAO,EAAE,GAAe;IAChH,IAAI,KAAK,GAAG,SAAS;QAAE,MAAM,IAAI,KAAK,EAAE,CAAA;IACxC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,YAAY,CAAA;IACnC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,EAAE,CAAA;QAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;IAChJ,CAAC;SAAM,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACrC,kFAAkF;QAClF,IAAI,CAAC,CAAC,WAAW,KAAK,YAAY;YAAE,OAAM;QAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;QAC9C,IAAI,KAAK,KAAK,IAAI;YAAE,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;IAC3E,CAAC;SAAM,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;QAC7F,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;IACxC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;QACxC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QAC9B,MAAM,GAAG,GAAe,EAAE,CAAA;QAC1B,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;QAClE,OAAO,GAAG,CAAA;IACZ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,KAAK;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,CAAC,CAAA;IACT,CAAC;AACH,CAAC"}
package/dist/policy.js ADDED
@@ -0,0 +1,84 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { fold } from './fold.js';
3
+ import { parseMailbox } from './mailboxes.js';
4
+ function looksLikeEmail(s) {
5
+ if (s === null)
6
+ return false;
7
+ const parts = s.split('@');
8
+ return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
9
+ }
10
+ function stableJson(v) {
11
+ if (Array.isArray(v))
12
+ return `[${v.map(stableJson).join(',')}]`;
13
+ if (v !== null && typeof v === 'object') {
14
+ const o = v;
15
+ return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stableJson(o[k])}`).join(',')}}`;
16
+ }
17
+ return JSON.stringify(v);
18
+ }
19
+ export function fingerprintOf(config) {
20
+ const { helperPath: _h, debug: _d, ...effective } = config;
21
+ return createHash('sha256').update(stableJson(effective)).digest('hex').slice(0, 16);
22
+ }
23
+ export function computePolicy(config, raw) {
24
+ const namesAvailable = raw.accountNames === 'available';
25
+ const parsed = raw.mailboxes.map(parseMailbox);
26
+ // Konten aus den Mailbox-URLs, ergaenzt um Namen aus dem Helper.
27
+ const info = new Map(raw.accounts.map((a) => [a.uuid, a]));
28
+ const typeByAccount = new Map();
29
+ for (const mb of parsed)
30
+ if (!typeByAccount.has(mb.accountId))
31
+ typeByAccount.set(mb.accountId, mb.type);
32
+ const views = [...typeByAccount].map(([id, type]) => {
33
+ const i = namesAvailable ? info.get(id) : undefined;
34
+ return {
35
+ id,
36
+ name: i?.description ?? id,
37
+ address: looksLikeEmail(i?.username ?? null) ? i.username : null,
38
+ type,
39
+ };
40
+ });
41
+ // Allowlist: exakt gegen UUID, Adresse oder Name; ohne Namen nur UUID (fail-closed).
42
+ let allowedAccounts = null;
43
+ let unmatched = 0;
44
+ if (config.accounts.length > 0) {
45
+ allowedAccounts = new Set();
46
+ for (const entry of config.accounts) {
47
+ const e = fold(entry);
48
+ let matched = false;
49
+ for (const v of views) {
50
+ const hit = fold(v.id) === e || (namesAvailable && ((v.address !== null && fold(v.address) === e) || fold(v.name) === e));
51
+ if (hit) {
52
+ matched = true;
53
+ allowedAccounts.add(v.id);
54
+ }
55
+ }
56
+ if (!matched)
57
+ unmatched++;
58
+ }
59
+ }
60
+ const excluded = new Set(config.excludeMailboxes.map(fold));
61
+ const mailboxes = new Map();
62
+ for (const mb of parsed) {
63
+ if (allowedAccounts && !allowedAccounts.has(mb.accountId))
64
+ continue;
65
+ if (mb.role === 'junk' && !config.includeJunk)
66
+ continue;
67
+ if (mb.role === 'trash' && !config.includeTrash)
68
+ continue;
69
+ // Eintrag mit "/" passt auf den Pfad, sonst auf den Namen.
70
+ if (excluded.has(fold(mb.name)) || (mb.path.length > 0 && excluded.has(fold(mb.path.join('/')))))
71
+ continue;
72
+ mailboxes.set(mb.id, mb);
73
+ }
74
+ const accountsWithMail = new Set([...mailboxes.values()].map((m) => m.accountId));
75
+ return {
76
+ allowedMailboxIds: [...mailboxes.keys()],
77
+ mailboxes,
78
+ accounts: views.filter((v) => accountsWithMail.has(v.id)),
79
+ unmatchedAllowlistEntries: unmatched,
80
+ accountNamesAvailable: namesAvailable,
81
+ fingerprint: fingerprintOf(config),
82
+ };
83
+ }
84
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}