@bobfrankston/rmfmail 1.0.544 → 1.0.546
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mailx.js +8 -1
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +7 -1
- package/client/app.js +38 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +37 -0
- package/client/styles/components.css +15 -0
- package/package.json +1 -1
- package/packages/mailx-service/charset.d.ts +15 -0
- package/packages/mailx-service/charset.d.ts.map +1 -0
- package/packages/mailx-service/charset.js +61 -0
- package/packages/mailx-service/charset.js.map +1 -0
- package/packages/mailx-service/charset.ts +45 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +10 -48
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +89 -252
- package/packages/mailx-service/local-store.d.ts +81 -0
- package/packages/mailx-service/local-store.d.ts.map +1 -0
- package/packages/mailx-service/local-store.js +159 -0
- package/packages/mailx-service/local-store.js.map +1 -0
- package/packages/mailx-service/local-store.ts +307 -0
package/client/app.ts
CHANGED
|
@@ -2625,6 +2625,43 @@ optSnippet?.addEventListener("change", () => {
|
|
|
2625
2625
|
localStorage.setItem("mailx-snippet", String(optSnippet.checked));
|
|
2626
2626
|
});
|
|
2627
2627
|
|
|
2628
|
+
// ── Search help button (?) ──
|
|
2629
|
+
// Opens a small modal showing docs/search.md so the user can see the
|
|
2630
|
+
// query syntax for each mode (FTS5 vs IMAP SEARCH vs Gmail API). The
|
|
2631
|
+
// markdown is fetched from the service via readConfigHelp("search"),
|
|
2632
|
+
// rendered as plain text inside a <pre> — no full markdown engine, just
|
|
2633
|
+
// preserves headings + tables visually. The doc itself is the source of
|
|
2634
|
+
// truth (lives at app/docs/search.md, deployed to GDrive on every
|
|
2635
|
+
// release like the other config help files).
|
|
2636
|
+
document.getElementById("search-help")?.addEventListener("click", async () => {
|
|
2637
|
+
const { readConfigHelp } = await import("./lib/api-client.js");
|
|
2638
|
+
const r = await readConfigHelp("search").catch(() => ({ content: "" }));
|
|
2639
|
+
const md = (r as any)?.content || "";
|
|
2640
|
+
const backdrop = document.createElement("div");
|
|
2641
|
+
backdrop.className = "mailx-modal-backdrop";
|
|
2642
|
+
backdrop.style.cssText = "position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center;";
|
|
2643
|
+
const panel = document.createElement("div");
|
|
2644
|
+
panel.style.cssText = "background:var(--color-bg,#fff);color:var(--color-text,#000);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.3);width:min(820px,92vw);max-height:85vh;display:flex;flex-direction:column;";
|
|
2645
|
+
panel.innerHTML = `
|
|
2646
|
+
<div style="padding:14px 18px;border-bottom:1px solid var(--color-border,#ddd);display:flex;justify-content:space-between;align-items:center">
|
|
2647
|
+
<span style="font-weight:600">Search syntax</span>
|
|
2648
|
+
<button type="button" id="search-help-close" style="background:none;border:0;font-size:18px;cursor:pointer">×</button>
|
|
2649
|
+
</div>
|
|
2650
|
+
<pre style="margin:0;padding:14px 18px;overflow:auto;font:13px/1.5 var(--font-ui);white-space:pre-wrap;word-wrap:break-word">${
|
|
2651
|
+
(md || "Help not available — check that docs/search.md is deployed.")
|
|
2652
|
+
.replace(/[&<>]/g, (c: string) => ({ "&": "&", "<": "<", ">": ">" }[c] || c))
|
|
2653
|
+
}</pre>
|
|
2654
|
+
`;
|
|
2655
|
+
backdrop.appendChild(panel);
|
|
2656
|
+
document.body.appendChild(backdrop);
|
|
2657
|
+
const close = (): void => backdrop.remove();
|
|
2658
|
+
panel.querySelector("#search-help-close")?.addEventListener("click", close);
|
|
2659
|
+
backdrop.addEventListener("click", e => { if (e.target === backdrop) close(); });
|
|
2660
|
+
document.addEventListener("keydown", function escClose(e) {
|
|
2661
|
+
if (e.key === "Escape") { close(); document.removeEventListener("keydown", escClose); }
|
|
2662
|
+
});
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2628
2665
|
// ── JSONC config file editor ──
|
|
2629
2666
|
document.getElementById("btn-edit-jsonc")?.addEventListener("click", async () => {
|
|
2630
2667
|
const settingsDropdown = document.getElementById("settings-dropdown");
|
|
@@ -1998,6 +1998,21 @@ body.calendar-sidebar-on .calendar-sidebar { display: flex; }
|
|
|
1998
1998
|
}
|
|
1999
1999
|
|
|
2000
2000
|
/* Server-search orthogonal checkbox */
|
|
2001
|
+
.search-help-btn {
|
|
2002
|
+
margin-left: 4px;
|
|
2003
|
+
width: 22px;
|
|
2004
|
+
height: 22px;
|
|
2005
|
+
border-radius: 50%;
|
|
2006
|
+
border: 1px solid var(--color-border, #bbb);
|
|
2007
|
+
background: var(--color-bg, transparent);
|
|
2008
|
+
color: var(--color-text-muted);
|
|
2009
|
+
font-size: 13px;
|
|
2010
|
+
line-height: 1;
|
|
2011
|
+
cursor: pointer;
|
|
2012
|
+
padding: 0;
|
|
2013
|
+
}
|
|
2014
|
+
.search-help-btn:hover { color: var(--color-text); border-color: var(--color-text); }
|
|
2015
|
+
|
|
2001
2016
|
.search-server-check {
|
|
2002
2017
|
display: inline-flex;
|
|
2003
2018
|
align-items: center;
|
package/package.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Charset normalization for incoming email bodies.
|
|
3
|
+
*
|
|
4
|
+
* Many senders (esp. PHPMailer-driven marketing) declare
|
|
5
|
+
* `charset=iso-8859-1` but emit UTF-8 bytes. simpleParser honors the
|
|
6
|
+
* declared charset and produces "â??" garbage for every non-ASCII
|
|
7
|
+
* codepoint (em-dash, smart quotes, …). When the raw body bytes are
|
|
8
|
+
* valid UTF-8, rewrite the charset header before parsing. We only
|
|
9
|
+
* override the obviously-wrong legacy declarations; explicit utf-8 /
|
|
10
|
+
* koi8 / etc. pass through.
|
|
11
|
+
*/
|
|
12
|
+
/** Returns either the original buffer (no change needed) or a copy with
|
|
13
|
+
* the leading charset declaration rewritten to utf-8. */
|
|
14
|
+
export declare function sniffAndFixCharset(raw: Buffer): Buffer;
|
|
15
|
+
//# sourceMappingURL=charset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charset.d.ts","sourceRoot":"","sources":["charset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;0DAC0D;AAC1D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQtD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Charset normalization for incoming email bodies.
|
|
3
|
+
*
|
|
4
|
+
* Many senders (esp. PHPMailer-driven marketing) declare
|
|
5
|
+
* `charset=iso-8859-1` but emit UTF-8 bytes. simpleParser honors the
|
|
6
|
+
* declared charset and produces "â??" garbage for every non-ASCII
|
|
7
|
+
* codepoint (em-dash, smart quotes, …). When the raw body bytes are
|
|
8
|
+
* valid UTF-8, rewrite the charset header before parsing. We only
|
|
9
|
+
* override the obviously-wrong legacy declarations; explicit utf-8 /
|
|
10
|
+
* koi8 / etc. pass through.
|
|
11
|
+
*/
|
|
12
|
+
/** Returns either the original buffer (no change needed) or a copy with
|
|
13
|
+
* the leading charset declaration rewritten to utf-8. */
|
|
14
|
+
export function sniffAndFixCharset(raw) {
|
|
15
|
+
const HEAD_LIMIT = 16384;
|
|
16
|
+
const head = raw.subarray(0, Math.min(HEAD_LIMIT, raw.length)).toString("latin1");
|
|
17
|
+
const re = /charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi;
|
|
18
|
+
if (!re.test(head))
|
|
19
|
+
return raw;
|
|
20
|
+
if (!isValidUtf8(raw))
|
|
21
|
+
return raw;
|
|
22
|
+
const fixed = head.replace(/charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi, "charset=utf-8");
|
|
23
|
+
return Buffer.concat([Buffer.from(fixed, "latin1"), raw.subarray(head.length)]);
|
|
24
|
+
}
|
|
25
|
+
/** Strict UTF-8 validity check: rejects overlong forms, invalid start
|
|
26
|
+
* bytes, and dangling continuations. Used to confirm the body is really
|
|
27
|
+
* UTF-8 before overriding a Latin-1 declaration. */
|
|
28
|
+
function isValidUtf8(buf) {
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < buf.length) {
|
|
31
|
+
const b = buf[i];
|
|
32
|
+
if (b < 0x80) {
|
|
33
|
+
i++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
let need;
|
|
37
|
+
if ((b & 0xE0) === 0xC0) {
|
|
38
|
+
if (b < 0xC2)
|
|
39
|
+
return false;
|
|
40
|
+
need = 1;
|
|
41
|
+
}
|
|
42
|
+
else if ((b & 0xF0) === 0xE0)
|
|
43
|
+
need = 2;
|
|
44
|
+
else if ((b & 0xF8) === 0xF0) {
|
|
45
|
+
if (b > 0xF4)
|
|
46
|
+
return false;
|
|
47
|
+
need = 3;
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
return false;
|
|
51
|
+
if (i + need >= buf.length)
|
|
52
|
+
return false;
|
|
53
|
+
for (let k = 1; k <= need; k++) {
|
|
54
|
+
if ((buf[i + k] & 0xC0) !== 0x80)
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
i += need + 1;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=charset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charset.js","sourceRoot":"","sources":["charset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;0DAC0D;AAC1D,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC1C,MAAM,UAAU,GAAG,KAAK,CAAC;IACzB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClF,MAAM,EAAE,GAAG,+DAA+D,CAAC;IAC3E,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,+DAA+D,EAAE,eAAe,CAAC,CAAC;IAC7G,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;qDAEqD;AACrD,SAAS,WAAW,CAAC,GAAW;IAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACjB,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAChC,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,IAAI;gBAAE,OAAO,KAAK,CAAC;YAAC,IAAI,GAAG,CAAC,CAAC;QAAC,CAAC;aAC7D,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI;YAAE,IAAI,GAAG,CAAC,CAAC;aAClC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,IAAI;gBAAE,OAAO,KAAK,CAAC;YAAC,IAAI,GAAG,CAAC,CAAC;QAAC,CAAC;;YAClE,OAAO,KAAK,CAAC;QAClB,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;QACnD,CAAC;QACD,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Charset normalization for incoming email bodies.
|
|
3
|
+
*
|
|
4
|
+
* Many senders (esp. PHPMailer-driven marketing) declare
|
|
5
|
+
* `charset=iso-8859-1` but emit UTF-8 bytes. simpleParser honors the
|
|
6
|
+
* declared charset and produces "â??" garbage for every non-ASCII
|
|
7
|
+
* codepoint (em-dash, smart quotes, …). When the raw body bytes are
|
|
8
|
+
* valid UTF-8, rewrite the charset header before parsing. We only
|
|
9
|
+
* override the obviously-wrong legacy declarations; explicit utf-8 /
|
|
10
|
+
* koi8 / etc. pass through.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Returns either the original buffer (no change needed) or a copy with
|
|
14
|
+
* the leading charset declaration rewritten to utf-8. */
|
|
15
|
+
export function sniffAndFixCharset(raw: Buffer): Buffer {
|
|
16
|
+
const HEAD_LIMIT = 16384;
|
|
17
|
+
const head = raw.subarray(0, Math.min(HEAD_LIMIT, raw.length)).toString("latin1");
|
|
18
|
+
const re = /charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi;
|
|
19
|
+
if (!re.test(head)) return raw;
|
|
20
|
+
if (!isValidUtf8(raw)) return raw;
|
|
21
|
+
const fixed = head.replace(/charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi, "charset=utf-8");
|
|
22
|
+
return Buffer.concat([Buffer.from(fixed, "latin1"), raw.subarray(head.length)]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Strict UTF-8 validity check: rejects overlong forms, invalid start
|
|
26
|
+
* bytes, and dangling continuations. Used to confirm the body is really
|
|
27
|
+
* UTF-8 before overriding a Latin-1 declaration. */
|
|
28
|
+
function isValidUtf8(buf: Buffer): boolean {
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < buf.length) {
|
|
31
|
+
const b = buf[i];
|
|
32
|
+
if (b < 0x80) { i++; continue; }
|
|
33
|
+
let need: number;
|
|
34
|
+
if ((b & 0xE0) === 0xC0) { if (b < 0xC2) return false; need = 1; }
|
|
35
|
+
else if ((b & 0xF0) === 0xE0) need = 2;
|
|
36
|
+
else if ((b & 0xF8) === 0xF0) { if (b > 0xF4) return false; need = 3; }
|
|
37
|
+
else return false;
|
|
38
|
+
if (i + need >= buf.length) return false;
|
|
39
|
+
for (let k = 1; k <= need; k++) {
|
|
40
|
+
if ((buf[i + k] & 0xC0) !== 0x80) return false;
|
|
41
|
+
}
|
|
42
|
+
i += need + 1;
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGpD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ import * as fs from "node:fs";
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
const __dirname = import.meta.dirname;
|
|
10
10
|
import * as gsync from "./google-sync.js";
|
|
11
|
+
import { sniffAndFixCharset } from "./charset.js";
|
|
11
12
|
import { loadSettings, saveSettings, loadAccounts, loadAccountsAsync, saveAccounts, initCloudConfig, loadAllowlist, saveAllowlist, loadAutocomplete, saveAutocomplete, loadKeys, getStorageInfo, getConfigDir } from "@bobfrankston/mailx-settings";
|
|
12
13
|
import { sanitizeHtml, encodeQuotedPrintable, htmlToPlainText } from "@bobfrankston/mailx-types";
|
|
13
14
|
import { simpleParser } from "mailparser";
|
|
@@ -21,53 +22,8 @@ import { simpleParser } from "mailparser";
|
|
|
21
22
|
* Operates byte-wise so MIME boundaries / base64 / etc. are preserved.
|
|
22
23
|
* The `utf-8` validity test rejects 0xC0–0xC1 / 0xF5–0xFF and continuation
|
|
23
24
|
* bytes out of place, which would be common in genuine Latin-1 text. */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const head = raw.subarray(0, Math.min(HEAD_LIMIT, raw.length)).toString("latin1");
|
|
27
|
-
const re = /charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi;
|
|
28
|
-
if (!re.test(head))
|
|
29
|
-
return raw;
|
|
30
|
-
if (!isValidUtf8(raw))
|
|
31
|
-
return raw;
|
|
32
|
-
const fixed = head.replace(/charset\s*=\s*"?(iso-8859-1|us-ascii|windows-1252|latin1)"?/gi, "charset=utf-8");
|
|
33
|
-
return Buffer.concat([Buffer.from(fixed, "latin1"), raw.subarray(head.length)]);
|
|
34
|
-
}
|
|
35
|
-
/** Strict UTF-8 validity check: rejects overlong forms, invalid start
|
|
36
|
-
* bytes, and dangling continuations. Used to confirm the body is really
|
|
37
|
-
* UTF-8 before overriding a Latin-1 declaration. */
|
|
38
|
-
function isValidUtf8(buf) {
|
|
39
|
-
let i = 0;
|
|
40
|
-
while (i < buf.length) {
|
|
41
|
-
const b = buf[i];
|
|
42
|
-
if (b < 0x80) {
|
|
43
|
-
i++;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
let need;
|
|
47
|
-
if ((b & 0xE0) === 0xC0) {
|
|
48
|
-
if (b < 0xC2)
|
|
49
|
-
return false;
|
|
50
|
-
need = 1;
|
|
51
|
-
}
|
|
52
|
-
else if ((b & 0xF0) === 0xE0)
|
|
53
|
-
need = 2;
|
|
54
|
-
else if ((b & 0xF8) === 0xF0) {
|
|
55
|
-
if (b > 0xF4)
|
|
56
|
-
return false;
|
|
57
|
-
need = 3;
|
|
58
|
-
}
|
|
59
|
-
else
|
|
60
|
-
return false;
|
|
61
|
-
if (i + need >= buf.length)
|
|
62
|
-
return false;
|
|
63
|
-
for (let k = 1; k <= need; k++) {
|
|
64
|
-
if ((buf[i + k] & 0xC0) !== 0x80)
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
i += need + 1;
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
25
|
+
// sniffAndFixCharset moved to ./charset.ts so LocalStore can use it
|
|
26
|
+
// without creating a circular dep with index.ts.
|
|
71
27
|
/** Parse `List-Unsubscribe` (RFC 2369) and `List-Unsubscribe-Post` (RFC 8058).
|
|
72
28
|
* mailparser only exposes ONE of mail/url even when both are present, so we
|
|
73
29
|
* also scan the raw header text for the full set of angle-bracketed URIs. */
|
|
@@ -2355,7 +2311,13 @@ export class MailxService {
|
|
|
2355
2311
|
* the legacy `config-help.md`-with-sections format if the per-file
|
|
2356
2312
|
* isn't available. */
|
|
2357
2313
|
async readConfigHelp(name) {
|
|
2358
|
-
|
|
2314
|
+
// Also serves topical help docs (search, editor, etc.) — same
|
|
2315
|
+
// mechanism as per-file config docs, just with no .jsonc suffix.
|
|
2316
|
+
const WHITELIST = [
|
|
2317
|
+
"accounts.jsonc", "allowlist.jsonc", "clients.jsonc",
|
|
2318
|
+
"config.jsonc", "contacts.jsonc", "preferences.jsonc",
|
|
2319
|
+
"search", "editor",
|
|
2320
|
+
];
|
|
2359
2321
|
if (!WHITELIST.includes(name))
|
|
2360
2322
|
return "";
|
|
2361
2323
|
const stem = name.replace(/\.jsonc$/, "");
|