@bobfrankston/iflow 1.0.39 → 1.0.41
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/imaplib/imap-native.d.ts +3 -0
- package/imaplib/imap-native.js +50 -17
- package/package.json +1 -1
package/imaplib/imap-native.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export declare class NativeImapClient {
|
|
|
57
57
|
private verbose;
|
|
58
58
|
private selectedMailbox;
|
|
59
59
|
private mailboxInfo;
|
|
60
|
+
private greetingResolve;
|
|
60
61
|
constructor(config: ImapClientConfig, transportFactory: TransportFactory);
|
|
61
62
|
get connected(): boolean;
|
|
62
63
|
connect(): Promise<void>;
|
|
@@ -112,6 +113,8 @@ export declare class NativeImapClient {
|
|
|
112
113
|
appendMessage(mailbox: string, message: string | Uint8Array, flags?: string[]): Promise<number | null>;
|
|
113
114
|
startIdle(onNewMail: (count: number) => void): Promise<() => Promise<void>>;
|
|
114
115
|
getMessageCount(mailbox: string): Promise<number>;
|
|
116
|
+
/** Default timeout for IMAP commands (30s). Prevents hanging connections. */
|
|
117
|
+
private commandTimeout;
|
|
115
118
|
private sendCommand;
|
|
116
119
|
private waitForContinuation;
|
|
117
120
|
private waitForTagged;
|
package/imaplib/imap-native.js
CHANGED
|
@@ -20,6 +20,7 @@ export class NativeImapClient {
|
|
|
20
20
|
verbose;
|
|
21
21
|
selectedMailbox = null;
|
|
22
22
|
mailboxInfo = { exists: 0, recent: 0, uidNext: 0, uidValidity: 0, flags: [], permanentFlags: [] };
|
|
23
|
+
greetingResolve = null;
|
|
23
24
|
constructor(config, transportFactory) {
|
|
24
25
|
this.config = config;
|
|
25
26
|
this.transportFactory = transportFactory;
|
|
@@ -31,10 +32,24 @@ export class NativeImapClient {
|
|
|
31
32
|
async connect() {
|
|
32
33
|
const useTls = this.config.port === 993;
|
|
33
34
|
this.transport.onData((data) => this.handleData(data));
|
|
34
|
-
this.transport.onClose(() => {
|
|
35
|
+
this.transport.onClose(() => {
|
|
36
|
+
this._connected = false;
|
|
37
|
+
// Reject any pending command so it doesn't hang forever
|
|
38
|
+
if (this.pendingCommand) {
|
|
39
|
+
const { reject } = this.pendingCommand;
|
|
40
|
+
this.pendingCommand = null;
|
|
41
|
+
reject(new Error("Connection closed"));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
35
44
|
this.transport.onError((err) => {
|
|
36
45
|
if (this.verbose)
|
|
37
46
|
console.error(` [imap] Transport error: ${err.message}`);
|
|
47
|
+
// Reject any pending command on transport error
|
|
48
|
+
if (this.pendingCommand) {
|
|
49
|
+
const { reject } = this.pendingCommand;
|
|
50
|
+
this.pendingCommand = null;
|
|
51
|
+
reject(err);
|
|
52
|
+
}
|
|
38
53
|
});
|
|
39
54
|
await this.transport.connect(this.config.server, this.config.port, useTls, this.config.server);
|
|
40
55
|
// Read server greeting
|
|
@@ -57,19 +72,15 @@ export class NativeImapClient {
|
|
|
57
72
|
await this.authenticate();
|
|
58
73
|
}
|
|
59
74
|
async readGreeting() {
|
|
60
|
-
return new Promise((resolve) => {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else {
|
|
69
|
-
setTimeout(check, 10);
|
|
70
|
-
}
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const timeout = setTimeout(() => {
|
|
77
|
+
this.greetingResolve = null;
|
|
78
|
+
reject(new Error("Greeting timeout (10s)"));
|
|
79
|
+
}, 10000);
|
|
80
|
+
this.greetingResolve = (resp) => {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
resolve(resp);
|
|
71
83
|
};
|
|
72
|
-
check();
|
|
73
84
|
});
|
|
74
85
|
}
|
|
75
86
|
async authenticate() {
|
|
@@ -133,10 +144,15 @@ export class NativeImapClient {
|
|
|
133
144
|
try {
|
|
134
145
|
if (this._connected) {
|
|
135
146
|
const tag = proto.nextTag();
|
|
136
|
-
|
|
147
|
+
// Timeout the LOGOUT command — don't let it hang forever
|
|
148
|
+
await Promise.race([
|
|
149
|
+
this.sendCommand(tag, proto.logoutCommand(tag)),
|
|
150
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("LOGOUT timeout")), 5000))
|
|
151
|
+
]);
|
|
137
152
|
}
|
|
138
153
|
}
|
|
139
|
-
catch { /* ignore */ }
|
|
154
|
+
catch { /* ignore — always close transport below */ }
|
|
155
|
+
this.pendingCommand = null; // clear any hanging promise
|
|
140
156
|
this.transport.close();
|
|
141
157
|
this._connected = false;
|
|
142
158
|
}
|
|
@@ -399,13 +415,23 @@ export class NativeImapClient {
|
|
|
399
415
|
return status.messages || 0;
|
|
400
416
|
}
|
|
401
417
|
// ── Low-level command handling ──
|
|
418
|
+
/** Default timeout for IMAP commands (30s). Prevents hanging connections. */
|
|
419
|
+
commandTimeout = 30000;
|
|
402
420
|
sendCommand(tag, command) {
|
|
403
421
|
return new Promise((resolve, reject) => {
|
|
404
422
|
if (this.verbose && !command.includes("LOGIN") && !command.includes("AUTHENTICATE")) {
|
|
405
423
|
console.log(` [imap] > ${command.trimEnd()}`);
|
|
406
424
|
}
|
|
407
|
-
|
|
408
|
-
|
|
425
|
+
const timer = setTimeout(() => {
|
|
426
|
+
this.pendingCommand = null;
|
|
427
|
+
reject(new Error(`IMAP command timeout (${this.commandTimeout / 1000}s): ${command.split("\r")[0].substring(0, 50)}`));
|
|
428
|
+
}, this.commandTimeout);
|
|
429
|
+
this.pendingCommand = {
|
|
430
|
+
tag, responses: [],
|
|
431
|
+
resolve: (responses) => { clearTimeout(timer); resolve(responses); },
|
|
432
|
+
reject: (err) => { clearTimeout(timer); reject(err); },
|
|
433
|
+
};
|
|
434
|
+
this.transport.write(command).catch((err) => { clearTimeout(timer); reject(err); });
|
|
409
435
|
});
|
|
410
436
|
}
|
|
411
437
|
waitForContinuation(tag) {
|
|
@@ -482,6 +508,13 @@ export class NativeImapClient {
|
|
|
482
508
|
if (this.verbose && resp.raw.length < 200) {
|
|
483
509
|
console.log(` [imap] < ${resp.raw}`);
|
|
484
510
|
}
|
|
511
|
+
// Server greeting — resolve readGreeting() promise
|
|
512
|
+
if (this.greetingResolve && resp.tag === "*" && (resp.type === "OK" || resp.type === "PREAUTH")) {
|
|
513
|
+
const resolve = this.greetingResolve;
|
|
514
|
+
this.greetingResolve = null;
|
|
515
|
+
resolve(resp);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
485
518
|
// During IDLE, handle EXISTS notifications
|
|
486
519
|
if (this.idleTag && resp.tag === "*" && resp.type === "EXISTS") {
|
|
487
520
|
const count = parseInt(resp.text);
|