imapflow 1.0.89 → 1.0.90
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/CHANGELOG.md +4 -0
- package/lib/commands/idle.js +120 -91
- package/lib/imap-flow.js +4 -1
- package/lib/types.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/lib/commands/idle.js
CHANGED
|
@@ -2,112 +2,139 @@
|
|
|
2
2
|
|
|
3
3
|
const NOOP_INTERVAL = 2 * 60 * 1000;
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (connection.state !== connection.states.SELECTED) {
|
|
8
|
-
// nothing to do here
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
5
|
+
async function runIdle(connection) {
|
|
6
|
+
let response;
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let preCheckWaitQueue = [];
|
|
16
|
-
try {
|
|
17
|
-
connection.idling = true;
|
|
18
|
-
|
|
19
|
-
//let idleSent = false;
|
|
20
|
-
let doneRequested = false;
|
|
21
|
-
let doneSent = false;
|
|
22
|
-
let canEnd = false;
|
|
23
|
-
|
|
24
|
-
let preCheck = () => {
|
|
25
|
-
doneRequested = true;
|
|
26
|
-
if (canEnd && !doneSent) {
|
|
27
|
-
connection.log.debug({
|
|
28
|
-
src: 'c',
|
|
29
|
-
msg: `DONE`,
|
|
30
|
-
comment: `breaking IDLE`,
|
|
31
|
-
lockId: connection.currentLockId,
|
|
32
|
-
path: connection.mailbox && connection.mailbox.path
|
|
33
|
-
});
|
|
34
|
-
connection.write('DONE');
|
|
35
|
-
doneSent = true;
|
|
36
|
-
|
|
37
|
-
connection.idling = false;
|
|
38
|
-
connection.preCheck = false; // unset itself
|
|
39
|
-
|
|
40
|
-
while (preCheckWaitQueue.length) {
|
|
41
|
-
let { resolve } = preCheckWaitQueue.shift();
|
|
42
|
-
resolve();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
connection.preCheck = () => {
|
|
48
|
-
let handler = new Promise((resolve, reject) => {
|
|
49
|
-
preCheckWaitQueue.push({ resolve, reject });
|
|
50
|
-
});
|
|
8
|
+
let preCheckWaitQueue = [];
|
|
9
|
+
try {
|
|
10
|
+
connection.idling = true;
|
|
51
11
|
|
|
52
|
-
|
|
53
|
-
|
|
12
|
+
//let idleSent = false;
|
|
13
|
+
let doneRequested = false;
|
|
14
|
+
let doneSent = false;
|
|
15
|
+
let canEnd = false;
|
|
16
|
+
|
|
17
|
+
let preCheck = () => {
|
|
18
|
+
doneRequested = true;
|
|
19
|
+
if (canEnd && !doneSent) {
|
|
20
|
+
connection.log.debug({
|
|
21
|
+
src: 'c',
|
|
22
|
+
msg: `DONE`,
|
|
23
|
+
comment: `breaking IDLE`,
|
|
54
24
|
lockId: connection.currentLockId,
|
|
55
|
-
path: connection.mailbox && connection.mailbox.path
|
|
56
|
-
queued: preCheckWaitQueue.length,
|
|
57
|
-
doneRequested,
|
|
58
|
-
canEnd,
|
|
59
|
-
doneSent
|
|
25
|
+
path: connection.mailbox && connection.mailbox.path
|
|
60
26
|
});
|
|
27
|
+
connection.write('DONE');
|
|
28
|
+
doneSent = true;
|
|
61
29
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return handler;
|
|
65
|
-
};
|
|
30
|
+
connection.idling = false;
|
|
31
|
+
connection.preCheck = false; // unset itself
|
|
66
32
|
|
|
67
|
-
response = await connection.exec('IDLE', false, {
|
|
68
|
-
onPlusTag: async () => {
|
|
69
|
-
connection.log.debug({ msg: `Initiated IDLE, waiting for server input`, doneRequested });
|
|
70
|
-
canEnd = true;
|
|
71
|
-
if (doneRequested) {
|
|
72
|
-
preCheck();
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
onSend: () => {
|
|
76
|
-
//idleSent = true;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// unset before response.next()
|
|
81
|
-
if (typeof connection.preCheck === 'function') {
|
|
82
|
-
connection.log.trace({
|
|
83
|
-
msg: 'Clearing pre-check function',
|
|
84
|
-
lockId: connection.currentLockId,
|
|
85
|
-
path: connection.mailbox && connection.mailbox.path,
|
|
86
|
-
queued: preCheckWaitQueue.length,
|
|
87
|
-
doneRequested,
|
|
88
|
-
canEnd,
|
|
89
|
-
doneSent
|
|
90
|
-
});
|
|
91
|
-
connection.preCheck = false;
|
|
92
33
|
while (preCheckWaitQueue.length) {
|
|
93
34
|
let { resolve } = preCheckWaitQueue.shift();
|
|
94
35
|
resolve();
|
|
95
36
|
}
|
|
96
37
|
}
|
|
38
|
+
};
|
|
97
39
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
40
|
+
connection.preCheck = () => {
|
|
41
|
+
let handler = new Promise((resolve, reject) => {
|
|
42
|
+
preCheckWaitQueue.push({ resolve, reject });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
connection.log.trace({
|
|
46
|
+
msg: 'Requesting IDLE break',
|
|
47
|
+
lockId: connection.currentLockId,
|
|
48
|
+
path: connection.mailbox && connection.mailbox.path,
|
|
49
|
+
queued: preCheckWaitQueue.length,
|
|
50
|
+
doneRequested,
|
|
51
|
+
canEnd,
|
|
52
|
+
doneSent
|
|
53
|
+
});
|
|
103
54
|
|
|
104
|
-
|
|
55
|
+
preCheck();
|
|
56
|
+
|
|
57
|
+
return handler;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
response = await connection.exec('IDLE', false, {
|
|
61
|
+
onPlusTag: async () => {
|
|
62
|
+
connection.log.debug({ msg: `Initiated IDLE, waiting for server input`, doneRequested });
|
|
63
|
+
canEnd = true;
|
|
64
|
+
if (doneRequested) {
|
|
65
|
+
preCheck();
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
onSend: () => {
|
|
69
|
+
//idleSent = true;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// unset before response.next()
|
|
74
|
+
if (typeof connection.preCheck === 'function') {
|
|
75
|
+
connection.log.trace({
|
|
76
|
+
msg: 'Clearing pre-check function',
|
|
77
|
+
lockId: connection.currentLockId,
|
|
78
|
+
path: connection.mailbox && connection.mailbox.path,
|
|
79
|
+
queued: preCheckWaitQueue.length,
|
|
80
|
+
doneRequested,
|
|
81
|
+
canEnd,
|
|
82
|
+
doneSent
|
|
83
|
+
});
|
|
84
|
+
connection.preCheck = false;
|
|
105
85
|
while (preCheckWaitQueue.length) {
|
|
106
|
-
let {
|
|
107
|
-
|
|
86
|
+
let { resolve } = preCheckWaitQueue.shift();
|
|
87
|
+
resolve();
|
|
108
88
|
}
|
|
109
|
-
return false;
|
|
110
89
|
}
|
|
90
|
+
|
|
91
|
+
response.next();
|
|
92
|
+
return;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
connection.preCheck = false;
|
|
95
|
+
connection.idling = false;
|
|
96
|
+
|
|
97
|
+
connection.log.warn({ err, cid: connection.id });
|
|
98
|
+
while (preCheckWaitQueue.length) {
|
|
99
|
+
let { reject } = preCheckWaitQueue.shift();
|
|
100
|
+
reject(err);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Listes for changes in mailbox
|
|
107
|
+
module.exports = async (connection, maxIdleTime) => {
|
|
108
|
+
if (connection.state !== connection.states.SELECTED) {
|
|
109
|
+
// nothing to do here
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (connection.capabilities.has('IDLE')) {
|
|
114
|
+
let idleTimer;
|
|
115
|
+
let stillIdling = false;
|
|
116
|
+
let runIdleLoop = async () => {
|
|
117
|
+
if (maxIdleTime) {
|
|
118
|
+
idleTimer = setTimeout(() => {
|
|
119
|
+
if (connection.idling) {
|
|
120
|
+
if (typeof connection.preCheck === 'function') {
|
|
121
|
+
stillIdling = true;
|
|
122
|
+
// request IDLE break if IDLE has been running for allowed time
|
|
123
|
+
connection.log.trace({ msg: 'Max allowed IDLE time reached', cid: connection.id });
|
|
124
|
+
connection.preCheck().catch(err => connection.log.warn({ err, cid: connection.id }));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}, maxIdleTime);
|
|
128
|
+
}
|
|
129
|
+
let resp = await runIdle(connection);
|
|
130
|
+
clearTimeout(idleTimer);
|
|
131
|
+
if (stillIdling) {
|
|
132
|
+
stillIdling = false;
|
|
133
|
+
return runIdleLoop();
|
|
134
|
+
}
|
|
135
|
+
return resp;
|
|
136
|
+
};
|
|
137
|
+
return runIdleLoop();
|
|
111
138
|
}
|
|
112
139
|
|
|
113
140
|
let idleTimer;
|
|
@@ -127,11 +154,13 @@ module.exports = async connection => {
|
|
|
127
154
|
response.next();
|
|
128
155
|
};
|
|
129
156
|
|
|
157
|
+
let noopInterval = maxIdleTime ? Math.min(NOOP_INTERVAL, maxIdleTime) : NOOP_INTERVAL;
|
|
158
|
+
|
|
130
159
|
let runLoop = () => {
|
|
131
160
|
idleCheck()
|
|
132
161
|
.then(() => {
|
|
133
162
|
clearTimeout(idleTimer);
|
|
134
|
-
idleTimer = setTimeout(runLoop,
|
|
163
|
+
idleTimer = setTimeout(runLoop, noopInterval);
|
|
135
164
|
})
|
|
136
165
|
.catch(err => {
|
|
137
166
|
clearTimeout(idleTimer);
|
package/lib/imap-flow.js
CHANGED
|
@@ -129,6 +129,7 @@ class ImapFlow extends EventEmitter {
|
|
|
129
129
|
* @param {Boolean} [options.verifyOnly=false] If `true` then logs out automatically after successful authentication
|
|
130
130
|
* @param {Boolean} [options.proxy] Optional proxy URL. Supports HTTP CONNECT (`http://`, `https://`) and SOCKS (`socks://`, `socks4://`, `socks5://`) proxies
|
|
131
131
|
* @param {Boolean} [options.qresync=false] If true, then enables QRESYNC support. EXPUNGE notifications will include `uid` property instead of `seq`
|
|
132
|
+
* @param {Number} [options.maxIdleTime] If set, then breaks and restarts IDLE every maxIdleTime ms
|
|
132
133
|
*/
|
|
133
134
|
constructor(options) {
|
|
134
135
|
super({ captureRejections: true });
|
|
@@ -260,6 +261,8 @@ class ImapFlow extends EventEmitter {
|
|
|
260
261
|
this.currentLock = false;
|
|
261
262
|
this.locks = [];
|
|
262
263
|
|
|
264
|
+
this.maxIdleTime = options.maxIdleTime || false;
|
|
265
|
+
|
|
263
266
|
this.streamer.on('error', err => {
|
|
264
267
|
if (['Z_BUF_ERROR', 'ECONNRESET', 'EPIPE', 'ETIMEDOUT', 'EHOSTUNREACH'].includes(err.code)) {
|
|
265
268
|
// just close the connection, usually nothing but noise
|
|
@@ -1593,7 +1596,7 @@ class ImapFlow extends EventEmitter {
|
|
|
1593
1596
|
*/
|
|
1594
1597
|
async idle() {
|
|
1595
1598
|
if (!this.idling) {
|
|
1596
|
-
return await this.run('IDLE');
|
|
1599
|
+
return await this.run('IDLE', this.maxIdleTime);
|
|
1597
1600
|
}
|
|
1598
1601
|
}
|
|
1599
1602
|
|
package/lib/types.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ declare module "imapflow" {
|
|
|
28
28
|
* @param [options.verifyOnly = false] - If `true` then logs out automatically after successful authentication
|
|
29
29
|
* @param [options.proxy] - Optional proxy URL. Supports HTTP CONNECT (`http://`, `https://`) and SOCKS (`socks://`, `socks4://`, `socks5://`) proxies
|
|
30
30
|
* @param [options.qresync = false] - If true, then enables QRESYNC support. EXPUNGE notifications will include `uid` property instead of `seq`
|
|
31
|
+
* @param [options.maxIdleTime] - If set, then breaks and restarts IDLE every maxIdleTime ms
|
|
31
32
|
*/
|
|
32
33
|
class ImapFlow extends EventEmitter {
|
|
33
34
|
constructor(options: {
|
|
@@ -54,6 +55,7 @@ declare module "imapflow" {
|
|
|
54
55
|
verifyOnly?: boolean;
|
|
55
56
|
proxy?: boolean;
|
|
56
57
|
qresync?: boolean;
|
|
58
|
+
maxIdleTime?: number;
|
|
57
59
|
});
|
|
58
60
|
/**
|
|
59
61
|
* Current module version as a static class property
|