@bobfrankston/rmfmail 1.2.270 → 1.2.274

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 (52) hide show
  1. package/client/android-bootstrap.bundle.js +28 -2
  2. package/client/android-bootstrap.bundle.js.map +2 -2
  3. package/client/app.bundle.js +125 -10
  4. package/client/app.bundle.js.map +4 -4
  5. package/client/app.js +19 -0
  6. package/client/app.js.map +1 -1
  7. package/client/app.ts +20 -0
  8. package/client/components/message-list.js +9 -1
  9. package/client/components/message-list.js.map +1 -1
  10. package/client/components/message-list.ts +9 -1
  11. package/client/components/message-viewer.js +40 -2
  12. package/client/components/message-viewer.js.map +1 -1
  13. package/client/components/message-viewer.ts +37 -2
  14. package/client/compose/compose.bundle.js +4 -0
  15. package/client/compose/compose.bundle.js.map +2 -2
  16. package/client/lib/api-client.js +6 -0
  17. package/client/lib/api-client.js.map +1 -1
  18. package/client/lib/api-client.ts +6 -0
  19. package/client/lib/approved-senders.js +66 -0
  20. package/client/lib/approved-senders.js.map +1 -0
  21. package/client/lib/approved-senders.ts +63 -0
  22. package/client/lib/mailxapi.js +3 -0
  23. package/client/package.json +1 -1
  24. package/package.json +5 -5
  25. package/packages/mailx-imap/package-lock.json +2 -2
  26. package/packages/mailx-imap/package.json +1 -1
  27. package/packages/mailx-service/index.d.ts +15 -0
  28. package/packages/mailx-service/index.d.ts.map +1 -1
  29. package/packages/mailx-service/index.js +18 -0
  30. package/packages/mailx-service/index.js.map +1 -1
  31. package/packages/mailx-service/index.ts +19 -0
  32. package/packages/mailx-service/jsonrpc.js +2 -0
  33. package/packages/mailx-service/jsonrpc.js.map +1 -1
  34. package/packages/mailx-service/jsonrpc.ts +2 -0
  35. package/packages/mailx-service/package.json +1 -1
  36. package/packages/mailx-settings/package.json +1 -1
  37. package/packages/mailx-store/package.json +1 -1
  38. package/packages/mailx-store-web/android-bootstrap.js +1 -0
  39. package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
  40. package/packages/mailx-store-web/android-bootstrap.ts +1 -0
  41. package/packages/mailx-store-web/package.json +1 -1
  42. package/packages/mailx-store-web/web-service.d.ts +11 -0
  43. package/packages/mailx-store-web/web-service.d.ts.map +1 -1
  44. package/packages/mailx-store-web/web-service.js +14 -0
  45. package/packages/mailx-store-web/web-service.js.map +1 -1
  46. package/packages/mailx-store-web/web-service.ts +15 -0
  47. package/packages/mailx-types/index.d.ts.map +1 -1
  48. package/packages/mailx-types/index.js +25 -2
  49. package/packages/mailx-types/index.js.map +1 -1
  50. package/packages/mailx-types/index.ts +21 -1
  51. package/packages/mailx-types/package.json +1 -1
  52. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-18100 → node_modules.npmglobalize-stash-31304}/.package-lock.json +0 -0
@@ -56,6 +56,7 @@ __export(api_client_exports, {
56
56
  flagSenderOrDomain: () => flagSenderOrDomain,
57
57
  formatJsonc: () => formatJsonc,
58
58
  getAccounts: () => getAccounts,
59
+ getAllowlist: () => getAllowlist,
59
60
  getAttachment: () => getAttachment,
60
61
  getAutocompleteSettings: () => getAutocompleteSettings,
61
62
  getCalendarEvents: () => getCalendarEvents,
@@ -350,6 +351,9 @@ function addUserDictWords(words) {
350
351
  function removeUserDictWord(word) {
351
352
  return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
352
353
  }
354
+ function getAllowlist() {
355
+ return ipc().getAllowlist?.() ?? Promise.resolve({ senders: [], domains: [], recipients: [], flaggedSenders: [], flaggedDomains: [] });
356
+ }
353
357
  function flagSenderOrDomain(type, value) {
354
358
  return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
355
359
  }
@@ -1310,6 +1314,61 @@ var init_message_state = __esm({
1310
1314
  }
1311
1315
  });
1312
1316
 
1317
+ // client/lib/approved-senders.js
1318
+ var approved_senders_exports = {};
1319
+ __export(approved_senders_exports, {
1320
+ approvedCounts: () => approvedCounts,
1321
+ isSenderApproved: () => isSenderApproved,
1322
+ noteApprovedLocally: () => noteApprovedLocally,
1323
+ refreshApprovedSenders: () => refreshApprovedSenders
1324
+ });
1325
+ async function refreshApprovedSenders() {
1326
+ try {
1327
+ const list = await getAllowlist();
1328
+ senders = new Set((list.senders || []).map(norm));
1329
+ domains = new Set((list.domains || []).map(norm));
1330
+ loaded = true;
1331
+ } catch (e) {
1332
+ console.warn("[approved-senders] refresh failed:", e?.message || e);
1333
+ }
1334
+ }
1335
+ function isSenderApproved(address) {
1336
+ if (!loaded)
1337
+ return false;
1338
+ const a = norm(address);
1339
+ if (!a)
1340
+ return false;
1341
+ if (senders.has(a))
1342
+ return true;
1343
+ const d = domainOf(a);
1344
+ return !!d && domains.has(d);
1345
+ }
1346
+ function noteApprovedLocally(type, value) {
1347
+ const v = norm(value);
1348
+ if (!v)
1349
+ return;
1350
+ if (type === "sender")
1351
+ senders.add(v);
1352
+ else
1353
+ domains.add(v);
1354
+ loaded = true;
1355
+ }
1356
+ function approvedCounts() {
1357
+ return { senders: senders.size, domains: domains.size, loaded };
1358
+ }
1359
+ var senders, domains, loaded, norm, domainOf;
1360
+ var init_approved_senders = __esm({
1361
+ "client/lib/approved-senders.js"() {
1362
+ "use strict";
1363
+ init_api_client();
1364
+ senders = /* @__PURE__ */ new Set();
1365
+ domains = /* @__PURE__ */ new Set();
1366
+ loaded = false;
1367
+ norm = (s) => (s || "").trim().toLowerCase();
1368
+ domainOf = (addr) => norm(addr).split("@").pop() || "";
1369
+ }
1370
+ });
1371
+
1313
1372
  // packages/mailx-types/contact-rules.js
1314
1373
  var init_contact_rules = __esm({
1315
1374
  "packages/mailx-types/contact-rules.js"() {
@@ -1382,9 +1441,20 @@ function displayNameClaimsOtherAddress(name, address) {
1382
1441
  const m = n.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g);
1383
1442
  if (!m)
1384
1443
  return null;
1444
+ const realDomain = real.split("@").pop() || "";
1385
1445
  for (const claimed of m) {
1386
- if (claimed.toLowerCase() !== real)
1387
- return claimed;
1446
+ if (claimed.toLowerCase() === real)
1447
+ continue;
1448
+ const claimedDomain = claimed.toLowerCase().split("@").pop() || "";
1449
+ if (claimedDomain && realDomain) {
1450
+ if (claimedDomain === realDomain)
1451
+ continue;
1452
+ if (claimedDomain.endsWith("." + realDomain))
1453
+ continue;
1454
+ if (realDomain.endsWith("." + claimedDomain))
1455
+ continue;
1456
+ }
1457
+ return claimed;
1388
1458
  }
1389
1459
  return null;
1390
1460
  }
@@ -2682,6 +2752,36 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
2682
2752
  items.push({ label: "", action: () => {
2683
2753
  }, separator: true });
2684
2754
  }
2755
+ if (el === fromEl && msg.from?.address) {
2756
+ const addr = String(msg.from.address);
2757
+ const dom = addr.split("@").pop() || "";
2758
+ const claimed = displayNameClaimsOtherAddress(String(msg.from.name || ""), addr);
2759
+ if (claimed && !isSenderApproved(addr)) {
2760
+ const approve = async (type, value) => {
2761
+ noteApprovedLocally(type, value);
2762
+ try {
2763
+ await allowRemoteContent(type, value);
2764
+ await refreshApprovedSenders();
2765
+ document.dispatchEvent(new CustomEvent("mailx-allowlist-changed"));
2766
+ const st = document.getElementById("status-sync");
2767
+ if (st)
2768
+ st.textContent = `Marked valid: ${value}`;
2769
+ } catch (e2) {
2770
+ const st = document.getElementById("status-sync");
2771
+ if (st) {
2772
+ st.textContent = `Couldn't save "${value}" to the allowlist \u2014 ${e2?.message || e2}`;
2773
+ st.style.color = "oklch(0.65 0.2 25)";
2774
+ }
2775
+ console.error("[allowlist] approve failed:", e2);
2776
+ }
2777
+ };
2778
+ items.push({ label: `\u2713 Mark ${addr} as a valid sender`, action: () => void approve("sender", addr) });
2779
+ if (dom)
2780
+ items.push({ label: `\u2713 Trust all senders at ${dom}`, action: () => void approve("domain", dom) });
2781
+ items.push({ label: "", action: () => {
2782
+ }, separator: true });
2783
+ }
2784
+ }
2685
2785
  if (omitted > 0) {
2686
2786
  items.push({
2687
2787
  label: `\u2026 ${omitted} more recipients \u2014 copy the whole list`,
@@ -3271,7 +3371,7 @@ ${escapeText(reputationBlind)}"` : ""}>${reputationText}</div>` : "") + `<div cl
3271
3371
  }
3272
3372
  function formatAddr(addr) {
3273
3373
  const { text, claimed } = formatSender(addr);
3274
- if (!claimed)
3374
+ if (!claimed || isSenderApproved(addr.address))
3275
3375
  return text;
3276
3376
  return `${text} \u2014 display name claims ${claimed}`;
3277
3377
  }
@@ -4096,6 +4196,7 @@ var init_message_viewer = __esm({
4096
4196
  "client/components/message-viewer.js"() {
4097
4197
  "use strict";
4098
4198
  init_api_client();
4199
+ init_approved_senders();
4099
4200
  init_context_menu();
4100
4201
  init_message_state();
4101
4202
  init_message_list();
@@ -5519,6 +5620,7 @@ var init_message_list = __esm({
5519
5620
  init_folder_picker();
5520
5621
  init_fold_text();
5521
5622
  init_mailx_types();
5623
+ init_approved_senders();
5522
5624
  currentSpecialUse = "";
5523
5625
  lastClickedRow = null;
5524
5626
  loading = false;
@@ -5633,7 +5735,7 @@ var init_message_list = __esm({
5633
5735
  from.textContent = msg.to.map((a) => a.name || a.address).join(", ");
5634
5736
  } else {
5635
5737
  const fromName2 = msg.from.name || msg.from.address;
5636
- const fromClaim = displayNameClaimsOtherAddress(msg.from.name || "", msg.from.address || "");
5738
+ const fromClaim = isSenderApproved(msg.from.address || "") ? null : displayNameClaimsOtherAddress(msg.from.name || "", msg.from.address || "");
5637
5739
  from.textContent = fromClaim ? `${fromName2} \u2014 actually ${msg.from.address}` : fromName2;
5638
5740
  if (fromClaim) {
5639
5741
  from.classList.add("ml-from-spoofed");
@@ -9891,12 +9993,12 @@ async function openCompose(mode, overrideMsg, overrideAccountId) {
9891
9993
  }
9892
9994
  if (msg && (mode === "editAsNew" || mode === "forward") && Array.isArray(msg.attachments) && msg.attachments.length) {
9893
9995
  const msgAccountId = current?.accountId || accountId;
9894
- const loaded = [];
9996
+ const loaded2 = [];
9895
9997
  for (const att of msg.attachments) {
9896
9998
  try {
9897
9999
  const data = await getAttachment(msgAccountId, msg.uid, att.id, msg.folderId);
9898
10000
  if (data?.content) {
9899
- loaded.push({
10001
+ loaded2.push({
9900
10002
  filename: data.filename || att.filename || "attachment",
9901
10003
  mimeType: data.contentType || att.contentType || "application/octet-stream",
9902
10004
  dataBase64: data.content
@@ -9906,7 +10008,7 @@ async function openCompose(mode, overrideMsg, overrideAccountId) {
9906
10008
  console.error(`[compose] ${mode}: failed to load attachment ${att.id}: ${e?.message || e}`);
9907
10009
  }
9908
10010
  }
9909
- if (loaded.length) init.attachments = loaded;
10011
+ if (loaded2.length) init.attachments = loaded2;
9910
10012
  }
9911
10013
  if (preferWindow && !frame) {
9912
10014
  try {
@@ -12368,6 +12470,19 @@ function applyThreadFilter() {
12368
12470
  console.error("pending mailto pickup failed:", e?.message || e);
12369
12471
  }
12370
12472
  })();
12473
+ (async () => {
12474
+ try {
12475
+ const { refreshApprovedSenders: refreshApprovedSenders2 } = await Promise.resolve().then(() => (init_approved_senders(), approved_senders_exports));
12476
+ await refreshApprovedSenders2();
12477
+ document.dispatchEvent(new CustomEvent("mailx-allowlist-changed"));
12478
+ } catch (e) {
12479
+ console.warn("approved-sender index load failed:", e?.message || e);
12480
+ }
12481
+ })();
12482
+ document.addEventListener("mailx-allowlist-changed", () => {
12483
+ void Promise.resolve().then(() => (init_message_list(), message_list_exports)).then((m) => m.reloadCurrentFolder?.()).catch(() => {
12484
+ });
12485
+ });
12371
12486
  (async () => {
12372
12487
  try {
12373
12488
  const { consumePendingShare: consumePendingShare2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -13619,12 +13734,12 @@ document.addEventListener("mailx-popout-message", (async (e) => {
13619
13734
  draftId: msg.mailxDraftId || ""
13620
13735
  };
13621
13736
  if (Array.isArray(msg.attachments) && msg.attachments.length) {
13622
- const loaded = [];
13737
+ const loaded2 = [];
13623
13738
  for (const att of msg.attachments) {
13624
13739
  try {
13625
13740
  const data = await getAttachment(accountId, msg.uid, att.id, msg.folderId);
13626
13741
  if (data?.content) {
13627
- loaded.push({
13742
+ loaded2.push({
13628
13743
  filename: data.filename || att.filename || "attachment",
13629
13744
  mimeType: data.contentType || att.contentType || "application/octet-stream",
13630
13745
  dataBase64: data.content
@@ -13634,7 +13749,7 @@ document.addEventListener("mailx-popout-message", (async (e) => {
13634
13749
  console.error(`[compose] edit draft: failed to load attachment ${att.id} ("${att.filename}"): ${e2?.message || e2}`);
13635
13750
  }
13636
13751
  }
13637
- if (loaded.length) init.attachments = loaded;
13752
+ if (loaded2.length) init.attachments = loaded2;
13638
13753
  }
13639
13754
  handOffComposeInit(showComposeOverlay(msg.subject ? `Edit: ${msg.subject}` : "Edit draft"), init);
13640
13755
  return;