From 39a7333a39499e4876abb29149f7c72f2f99a63d Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Tue, 16 Jan 2024 10:45:51 +0200 Subject: [PATCH] fix(logout): Fixed race condition for logout and TCP close. Fixes #161 --- lib/commands/logout.js | 3 +++ lib/imap-flow.js | 29 +++++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/commands/logout.js b/lib/commands/logout.js index 2468bf0..08c9a50 100644 --- a/lib/commands/logout.js +++ b/lib/commands/logout.js @@ -18,6 +18,9 @@ module.exports = async connection => { response = await connection.exec('LOGOUT'); return true; } catch (err) { + if (err.code === 'NoConnection') { + return true; + } connection.log.warn({ err, cid: connection.id }); return false; } finally { diff --git a/lib/imap-flow.js b/lib/imap-flow.js index 5928f2d..12264af 100644 --- a/lib/imap-flow.js +++ b/lib/imap-flow.js @@ -371,7 +371,9 @@ class ImapFlow extends EventEmitter { let request = this.requestTagMap.get(data.tag); if (request) { this.requestTagMap.delete(request.tag); - request.reject(new Error('Connection not available')); + const error = new Error('Connection not available'); + error.code = 'NoConnection'; + request.reject(error); } } return; @@ -595,6 +597,7 @@ class ImapFlow extends EventEmitter { default: { let err = new Error('Invalid server response'); + err.code = 'InvalidResponse'; err.response = parsed; request.reject(err); break; @@ -1393,7 +1396,7 @@ class ImapFlow extends EventEmitter { * await client.logout(); */ async logout() { - await this.run('LOGOUT'); + return await this.run('LOGOUT'); } /** @@ -1432,9 +1435,11 @@ class ImapFlow extends EventEmitter { // reject command that is currently processed if (this.currentRequest && this.requestTagMap.has(this.currentRequest.tag)) { let request = this.requestTagMap.get(this.currentRequest.tag); - if (request) { + if (request && ['LOGOUT'].includes(request.command)) { this.requestTagMap.delete(request.tag); - request.reject(new Error('Connection not available')); + const error = new Error('Connection not available'); + error.code = 'NoConnection'; + request.reject(error); } this.currentRequest = false; } @@ -1446,7 +1451,9 @@ class ImapFlow extends EventEmitter { let request = this.requestTagMap.get(req.tag); if (request) { this.requestTagMap.delete(request.tag); - request.reject(new Error('Connection not available')); + const error = new Error('Connection not available'); + error.code = 'NoConnection'; + request.reject(error); } } } @@ -2728,8 +2735,9 @@ class ImapFlow extends EventEmitter { } if (this.socket.destroyed) { - let err = new Error('Connection not available'); - throw err; + const error = new Error('Connection not available'); + error.code = 'NoConnection'; + throw error; } clearTimeout(this.idleStartTimer); @@ -2771,10 +2779,11 @@ class ImapFlow extends EventEmitter { const { resolve, reject, path, options, lockId } = this.locks.shift(); if (!this.usable || this.socket.destroyed) { - // reject all - let err = new Error('Connection not available'); this.log.trace({ msg: 'Failed to acquire mailbox lock', path, lockId }); - reject(err); + // reject all + let error = new Error('Connection not available'); + error.code = 'NoConnection'; + reject(error); return await this.processLocks(true); }