@bobfrankston/mailx 1.0.175 → 1.0.176
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"height":1047,"width":1844,"x":
|
|
1
|
+
{"height":1047,"width":1844,"x":531,"y":264}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.176",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@bobfrankston/iflow-node": "^0.1.1",
|
|
25
25
|
"@bobfrankston/miscinfo": "^1.0.7",
|
|
26
26
|
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
27
|
-
"@bobfrankston/msger": "^0.1.
|
|
27
|
+
"@bobfrankston/msger": "^0.1.225",
|
|
28
28
|
"@capacitor/android": "^8.3.0",
|
|
29
29
|
"@capacitor/cli": "^8.3.0",
|
|
30
30
|
"@capacitor/core": "^8.3.0",
|
|
@@ -1624,7 +1624,7 @@ export class ImapManager extends EventEmitter {
|
|
|
1624
1624
|
/** Save a debug copy of outgoing mail to the sending directory */
|
|
1625
1625
|
saveSendingCopy(accountId, rawMessage, label) {
|
|
1626
1626
|
try {
|
|
1627
|
-
const sendingDir = path.join(
|
|
1627
|
+
const sendingDir = path.join(getConfigDir(), "sending", accountId);
|
|
1628
1628
|
fs.mkdirSync(sendingDir, { recursive: true });
|
|
1629
1629
|
const now = new Date();
|
|
1630
1630
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
@@ -1665,7 +1665,7 @@ export class ImapManager extends EventEmitter {
|
|
|
1665
1665
|
console.error(` [outbox] IMAP queue failed: ${e.message} — saving locally`);
|
|
1666
1666
|
}
|
|
1667
1667
|
// Fallback: save to local file queue
|
|
1668
|
-
const localQueue = path.join(
|
|
1668
|
+
const localQueue = path.join(getConfigDir(), "outbox", accountId);
|
|
1669
1669
|
fs.mkdirSync(localQueue, { recursive: true });
|
|
1670
1670
|
const now = new Date();
|
|
1671
1671
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
@@ -1675,7 +1675,7 @@ export class ImapManager extends EventEmitter {
|
|
|
1675
1675
|
}
|
|
1676
1676
|
/** Process local file queue — move to IMAP Outbox when server is reachable */
|
|
1677
1677
|
async processLocalQueue(accountId) {
|
|
1678
|
-
const localQueue = path.join(
|
|
1678
|
+
const localQueue = path.join(getConfigDir(), "outbox", accountId);
|
|
1679
1679
|
if (!fs.existsSync(localQueue))
|
|
1680
1680
|
return;
|
|
1681
1681
|
const files = fs.readdirSync(localQueue).filter(f => f.endsWith(".ltr"));
|
|
@@ -6,6 +6,42 @@
|
|
|
6
6
|
import * as dns from "node:dns/promises";
|
|
7
7
|
import { loadSettings, saveSettings, loadAccounts, loadAccountsAsync, saveAccounts, initCloudConfig, loadAllowlist, saveAllowlist, loadAutocomplete, saveAutocomplete, getStorePath, getStorageInfo } from "@bobfrankston/mailx-settings";
|
|
8
8
|
import { simpleParser } from "mailparser";
|
|
9
|
+
// ── Quoted-printable encoding (readable in debug .eml files) ──
|
|
10
|
+
function encodeQuotedPrintable(text) {
|
|
11
|
+
const bytes = Buffer.from(text, "utf-8");
|
|
12
|
+
let line = "";
|
|
13
|
+
let result = "";
|
|
14
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
15
|
+
const b = bytes[i];
|
|
16
|
+
let encoded;
|
|
17
|
+
if (b === 0x0D && bytes[i + 1] === 0x0A) {
|
|
18
|
+
// CRLF — output as-is
|
|
19
|
+
result += line + "\r\n";
|
|
20
|
+
line = "";
|
|
21
|
+
i++; // skip LF
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
else if (b === 0x0A) {
|
|
25
|
+
// Bare LF — normalize to CRLF
|
|
26
|
+
result += line + "\r\n";
|
|
27
|
+
line = "";
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
else if ((b >= 33 && b <= 126 && b !== 61) || b === 9 || b === 32) {
|
|
31
|
+
encoded = String.fromCharCode(b);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
encoded = "=" + b.toString(16).toUpperCase().padStart(2, "0");
|
|
35
|
+
}
|
|
36
|
+
if (line.length + encoded.length > 75) {
|
|
37
|
+
result += line + "=\r\n";
|
|
38
|
+
line = "";
|
|
39
|
+
}
|
|
40
|
+
line += encoded;
|
|
41
|
+
}
|
|
42
|
+
result += line;
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
9
45
|
// ── Email provider detection (MX-based) ──
|
|
10
46
|
const GOOGLE_DOMAINS = ["gmail.com", "googlemail.com"];
|
|
11
47
|
const MS_DOMAINS = ["outlook.com", "hotmail.com", "live.com"];
|
|
@@ -308,7 +344,7 @@ export class MailxService {
|
|
|
308
344
|
const cc = msg.cc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
|
|
309
345
|
const bcc = msg.bcc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
|
|
310
346
|
const body = msg.bodyHtml || msg.bodyText || "";
|
|
311
|
-
const
|
|
347
|
+
const bodyEncoded = encodeQuotedPrintable(body);
|
|
312
348
|
// Generate a unique Message-ID (required for threading, dedup, and RFC compliance)
|
|
313
349
|
const domain = account.email.split("@")[1] || "mailx.local";
|
|
314
350
|
const messageId = `<${Date.now()}.${Math.random().toString(36).slice(2)}@${domain}>`;
|
|
@@ -319,9 +355,9 @@ export class MailxService {
|
|
|
319
355
|
`Message-ID: ${messageId}`,
|
|
320
356
|
msg.inReplyTo ? `In-Reply-To: ${msg.inReplyTo}` : null,
|
|
321
357
|
msg.references?.length ? `References: ${msg.references.join(" ")}` : null,
|
|
322
|
-
`MIME-Version: 1.0`, `Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding:
|
|
358
|
+
`MIME-Version: 1.0`, `Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`,
|
|
323
359
|
].filter(h => h !== null).join("\r\n");
|
|
324
|
-
const rawMessage = `${headers}\r\n\r\n${
|
|
360
|
+
const rawMessage = `${headers}\r\n\r\n${bodyEncoded}`;
|
|
325
361
|
this.imapManager.queueOutgoingLocal(account.id, rawMessage);
|
|
326
362
|
console.log(` Queued locally: ${msg.subject} via ${account.id} from ${fromHeader}`);
|
|
327
363
|
for (const addr of msg.to)
|
|
@@ -491,15 +527,15 @@ export class MailxService {
|
|
|
491
527
|
// Generate or reuse a stable draft ID for dedup
|
|
492
528
|
const id = draftId || `mailx-draft-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
493
529
|
const body = bodyHtml || bodyText || "";
|
|
494
|
-
const
|
|
530
|
+
const bodyEncoded = encodeQuotedPrintable(body);
|
|
495
531
|
const headers = [
|
|
496
532
|
`From: ${account.name} <${account.email}>`,
|
|
497
533
|
to ? `To: ${to}` : null, cc ? `Cc: ${cc}` : null,
|
|
498
534
|
`Subject: ${subject || "(no subject)"}`, `Date: ${new Date().toUTCString()}`,
|
|
499
535
|
`X-Mailx-Draft-ID: ${id}`,
|
|
500
|
-
`MIME-Version: 1.0`, `Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding:
|
|
536
|
+
`MIME-Version: 1.0`, `Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`,
|
|
501
537
|
].filter(h => h !== null).join("\r\n");
|
|
502
|
-
const raw = `${headers}\r\n\r\n${
|
|
538
|
+
const raw = `${headers}\r\n\r\n${bodyEncoded}`;
|
|
503
539
|
const uid = await this.imapManager.saveDraft(accountId, raw, previousDraftUid, id);
|
|
504
540
|
return { uid, draftId: id };
|
|
505
541
|
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
From: Bob Frankston <Bob.Frankston@Gmail.com>
|
|
2
|
-
To: David P. Reed <dpreed@deepplum.com>
|
|
3
|
-
Cc: Brian DeLacey <bdelacey@gmail.com>
|
|
4
|
-
Bcc: bob@bob.ma
|
|
5
|
-
Subject: Re: Converting POPFile from perl to Python
|
|
6
|
-
Date: Wed, 08 Apr 2026 01:02:13 GMT
|
|
7
|
-
Message-ID: <1775610133517.ug0o0gii4wm@Gmail.com>
|
|
8
|
-
MIME-Version: 1.0
|
|
9
|
-
Content-Type: text/html; charset=UTF-8
|
|
10
|
-
Content-Transfer-Encoding: base64
|
|
11
|
-
|
|
12
|
-
PHA+SSdtIHJlc3BvbmRpbmcgdXNpbmcgbXkgZW1haWwgcHJvZ3JhbS48L3A+PHA+PGJyPjwvcD48
|
|
13
|
-
cD5UaGVyZSBpcyBzbyBtdWNoIHRvIGNhdGNoIHVwIHdpdGggLS0gdGhlIGdvb2QgYW5kIHRoZSBi
|
|
14
|
-
YWQuIE9uZSBvYnNlcnZhdGlvbiBpcyB0aGF0IGl0IGxlYXJuZWQgdG8gbXVjaCB0byBwcm9ncmFt
|
|
15
|
-
IGxpa2UgYSBodW1hbiBhbmQgbm90IGVub3VnaC4gT25lIGh1bW9yb3VzIHRoaW5nIGlzIHdoZW4g
|
|
16
|
-
aXQgZXN0aW1hdGVzIGEgdGFzayB3aWxsIHRha2UgYSB3ZWVrIGJ1dCBpdCBkb2VzIGl0IGluIGZp
|
|
17
|
-
dmUgbWludXRlcyBiZWNhdXNlIGl0IGlzIHVzaW5nIHN0YW5kYXJkIG1lYXN1cmVzLiBJdCdzIGRl
|
|
18
|
-
c2lnbiBzZW5zZSBpcyBmdXJzaGl0IGJ1dCBpZiB5b3Ugd29yayB3aXRoIGl0IGFuZCBnZXQgcGFz
|
|
19
|
-
dCB0aGUgSmVuZ2EgbW9kZSB5b3UgY2FuIGdldCBsb3RzIGRvbmUuIEZvciBleGFtcGxlLCBpbWFw
|
|
20
|
-
IGRvZXNuJ3Qgd29yayB3ZWxsIHdpdGggZ21haWwgYnV0IGl0IHNhaWQgdGhhdCBhZGRpbmcgZ21h
|
|
21
|
-
aWwgd2FzIGEgbWFqb3IgcHJvamVjdCBidXQgSSBzYWlkIGRvIGl0IGFueXdheSBhbmQgZml2ZSBt
|
|
22
|
-
aW51dGVzIGxhdGVyIGl0IHdhcyBkb25lLiBucG0gaW5zdGFsbCAtZyBAYm9iZnJhbmtzdG9uL21h
|
|
23
|
-
aWx4IGlzbid0IHF1aXQgcmVhZHkgZm9yIHByaW1lIHRpbWUuPC9wPjxwPjxicj48L3A+PHA+U3Rp
|
|
24
|
-
bGwgYSBodWdlIHRvZG8gbGlzdCAoaW4gdGhlIG1haWx4IGRpciBpZiB5b3UncmUgY3VyaW91cy4g
|
|
25
|
-
SXQgd2lsbCB3YW50IHRvIHN0YXJ0IHdpdGggYSBnbWFpbCBhZGRyZXNzIGFuZCBjcmVhdGVzIGEg
|
|
26
|
-
bWFpbHggZGlyZWN0b3J5IG9uIGdkcml2ZS4gQSBnYWluLCBzdGlsbCBsb3RzIG9mIGlzc3VlcyB3
|
|
27
|
-
aGljaCB5b3UgY2FuIHJlcG9ydCB0byBtZSBidXQgaXQgaXMgY29taW5nIHRvZ2V0aGVyLjwvcD48
|
|
28
|
-
cD48YnI+PC9wPjxwPk9uIDQvNy8yMDI2LCA1OjA0OjU3IFBNLCBEYXZpZCBQLiBSZWVkICZsdDtk
|
|
29
|
-
cHJlZWRAZGVlcHBsdW0uY29tJmd0OyB3cm90ZTo8L3A+PGRpdiBjbGFzcz0icWwtY29kZS1ibG9j
|
|
30
|
-
ay1jb250YWluZXIiIHNwZWxsY2hlY2s9ImZhbHNlIj48ZGl2IGNsYXNzPSJxbC1jb2RlLWJsb2Nr
|
|
31
|
-
IiBkYXRhLWxhbmd1YWdlPSJwbGFpbiI+Rmlyc3Qgb3ZlcmFsbCB0ZXN0IGJlZ2FuIGxvb3Bpbmcg
|
|
32
|
-
Zm9yZXZlci4gSSBzdG9wcGVkIGl0LCBhbmQgaW5xdWlyZWQgd2hhdCB0aGUgaGVsbCwgYW5kPC9k
|
|
33
|
-
aXY+PGRpdiBjbGFzcz0icWwtY29kZS1ibG9jayIgZGF0YS1sYW5ndWFnZT0icGxhaW4iPmhlcmUg
|
|
34
|
-
aXMgd2hhdCBpdCBzYXlzLiBHYWFoLiBUaGlzIGlzIG5vdCBleHBsYWluaW5nIG11Y2gsIGFuZCBp
|
|
35
|
-
dCB3cm90ZSB0aGlzIGNvZGUuLi48L2Rpdj48ZGl2IGNsYXNzPSJxbC1jb2RlLWJsb2NrIiBkYXRh
|
|
36
|
-
LWxhbmd1YWdlPSJwbGFpbiI+PGJyPjwvZGl2PjxkaXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRh
|
|
37
|
-
dGEtbGFuZ3VhZ2U9InBsYWluIj4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTwvZGl2Pjxk
|
|
38
|
-
aXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRhdGEtbGFuZ3VhZ2U9InBsYWluIj5UaGUgdGVzdCBo
|
|
39
|
-
YW5ncyBiZWNhdXNlICNoYW5kbGVDbGllbnQgaW4gUE9QM1Byb3h5IHRocm93cyBiZWZvcmUgc2Vu
|
|
40
|
-
ZGluZyB0aGUgYmFubmVyLiBUaGUgY3VscHJpdCBpcyB0aGlzIGRlYWQgY29kZTo8L2Rpdj48ZGl2
|
|
41
|
-
IGNsYXNzPSJxbC1jb2RlLWJsb2NrIiBkYXRhLWxhbmd1YWdlPSJwbGFpbiI+PGJyPjwvZGl2Pjxk
|
|
42
|
-
aXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRhdGEtbGFuZ3VhZ2U9InBsYWluIj4gIGNvbnN0IGNs
|
|
43
|
-
aWVudFdyaXRlciA9IG5ldyBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXIoPC9kaXY+PGRpdiBj
|
|
44
|
-
bGFzcz0icWwtY29kZS1ibG9jayIgZGF0YS1sYW5ndWFnZT0icGxhaW4iPiAgICBjbGllbnQud3Jp
|
|
45
|
-
dGFibGUuZ2V0V3JpdGVyKCkucmVsZWFzZUxvY2soKSBhcyB1bmtub3duIGFzIFdyaXRhYmxlU3Ry
|
|
46
|
-
ZWFtPC9kaXY+PGRpdiBjbGFzcz0icWwtY29kZS1ibG9jayIgZGF0YS1sYW5ndWFnZT0icGxhaW4i
|
|
47
|
-
PiAgKTs8L2Rpdj48ZGl2IGNsYXNzPSJxbC1jb2RlLWJsb2NrIiBkYXRhLWxhbmd1YWdlPSJwbGFp
|
|
48
|
-
biI+PGJyPjwvZGl2PjxkaXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRhdGEtbGFuZ3VhZ2U9InBs
|
|
49
|
-
YWluIj4gIHJlbGVhc2VMb2NrKCkgcmV0dXJucyB2b2lkLiBQYXNzaW5nIHZvaWQgdG8gV3JpdGFi
|
|
50
|
-
bGVTdHJlYW1EZWZhdWx0V3JpdGVyIHRocm93cyBhIFR5cGVFcnJvciwgd2hpY2ggaXMgc3dhbGxv
|
|
51
|
-
d2VkIGJ5IHRoZTwvZGl2PjxkaXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRhdGEtbGFuZ3VhZ2U9
|
|
52
|
-
InBsYWluIj4gIGFjY2VwdCBsb29wJ3MgLmNhdGNoIOKAlCBzbyB0aGUgY2xpZW50IG5ldmVyIHJl
|
|
53
|
-
Y2VpdmVzIHRoZSBiYW5uZXIgYW5kIHdhaXRzIGZvcmV2ZXIuIGNsaWVudFdyaXRlciBpcyBhbHNv
|
|
54
|
-
IG5ldmVyIHVzZWQuPC9kaXY+PGRpdiBjbGFzcz0icWwtY29kZS1ibG9jayIgZGF0YS1sYW5ndWFn
|
|
55
|
-
ZT0icGxhaW4iPiAgRml4OjwvZGl2PjxkaXYgY2xhc3M9InFsLWNvZGUtYmxvY2siIGRhdGEtbGFu
|
|
56
|
-
Z3VhZ2U9InBsYWluIj48YnI+PC9kaXY+PGRpdiBjbGFzcz0icWwtY29kZS1ibG9jayIgZGF0YS1s
|
|
57
|
-
YW5ndWFnZT0icGxhaW4iPjxicj48L2Rpdj48ZGl2IGNsYXNzPSJxbC1jb2RlLWJsb2NrIiBkYXRh
|
|
58
|
-
LWxhbmd1YWdlPSJwbGFpbiI+PGJyPjwvZGl2PjwvZGl2Pg==
|