From d551c118287ad46e8dd584ff72acc98d7e024fc1 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Tue, 4 Apr 2017 16:35:56 +0300 Subject: [PATCH] Trying COMPRESS --- imap-core/lib/commands/capability.js | 1 + imap-core/lib/commands/compress.js | 56 ++++++++++++++++++++++++++++ imap-core/lib/imap-command.js | 3 +- imap-core/lib/imap-connection.js | 14 ++++++- 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 imap-core/lib/commands/compress.js diff --git a/imap-core/lib/commands/capability.js b/imap-core/lib/commands/capability.js index ed37ba5..6d187a9 100644 --- a/imap-core/lib/commands/capability.js +++ b/imap-core/lib/commands/capability.js @@ -31,6 +31,7 @@ module.exports = { capabilities.push('QUOTA'); capabilities.push('MOVE'); + capabilities.push('COMPRESS=DEFLATE'); if (this._server.options.maxMessage) { capabilities.push('APPENDLIMIT=' + this._server.options.maxMessage); diff --git a/imap-core/lib/commands/compress.js b/imap-core/lib/commands/compress.js new file mode 100644 index 0000000..82f0005 --- /dev/null +++ b/imap-core/lib/commands/compress.js @@ -0,0 +1,56 @@ +'use strict'; + +const zlib = require('zlib'); + +// tag COMPRESS DEFLATE +module.exports = { + state: ['Authenticated', 'Selected'], + + schema: [{ + name: 'mechanism', + type: 'string' + }], + + handler(command, callback) { + + let mechanism = (command.attributes[0] && command.attributes[0].value || '').toString().toUpperCase().trim(); + + if (!mechanism) { + return callback(null, { + response: 'BAD' + }); + } + + if (mechanism !== 'DEFLATE') { + return callback(null, { + response: 'BAD', + code: 'CANNOT', + message: 'Unsupported compression mechanism' + }); + } + + setImmediate(() => { + this.compression = true; + + this._deflate = zlib.createDeflateRaw(); + this._inflate = zlib.createDeflateRaw(); + + this._deflate.once('error', err => { + this._socket.emit('error', err); + }); + + this._deflate.pipe(this._socket); + + this.writeStream.unpipe(this._socket); + this.writeStream.pipe(this._deflate); + + this._socket.unpipe(this._parser); + this._socket.pipe(this._inflate).pipe(this._parser); + }); + + callback(null, { + response: 'OK', + message: 'DEFLATE active' + }); + } +}; diff --git a/imap-core/lib/imap-command.js b/imap-core/lib/imap-command.js index 0bc6c7b..fdd4b87 100644 --- a/imap-core/lib/imap-command.js +++ b/imap-core/lib/imap-command.js @@ -45,7 +45,8 @@ let commands = new Map([ ['ENABLE', require('./commands/enable')], ['GETQUOTAROOT', require('./commands/getquotaroot')], ['SETQUOTA', require('./commands/setquota')], - ['GETQUOTA', require('./commands/getquota')] + ['GETQUOTA', require('./commands/getquota')], + ['COMPRESS', require('./commands/compress')] /*eslint-enable global-require*/ ]); diff --git a/imap-core/lib/imap-connection.js b/imap-core/lib/imap-connection.js index 1a344d7..65ba108 100644 --- a/imap-core/lib/imap-connection.js +++ b/imap-core/lib/imap-connection.js @@ -28,6 +28,10 @@ class IMAPConnection extends EventEmitter { // Random session ID, used for logging this.id = crypto.randomBytes(9).toString('base64'); + this.compression = false; + this._deflate = false; + this._inflate = false; + this._server = server; this._socket = socket; @@ -118,7 +122,7 @@ class IMAPConnection extends EventEmitter { */ send(payload, callback) { if (this._socket && this._socket.writable) { - this._socket.write(payload + '\r\n', 'binary', callback); + (!this.compression ? this._socket : this._deflate).write(payload + '\r\n', 'binary', callback); this._server.logger.debug('[%s] S:', this.id, payload); } } @@ -178,6 +182,14 @@ class IMAPConnection extends EventEmitter { this._dataStream = null; } + if (this._deflate) { + this._deflate = null; + } + + if (this._inflate) { + this._inflate = null; + } + if (this._listenerData) { this._listenerData.clear(); }