From 6bc92ab0ffc2c415470109ce86c3edec92cf10a7 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Tue, 4 Apr 2017 17:09:39 +0300 Subject: [PATCH] Fixed Compress --- README.md | 58 +++++++++++++++++------------- imap-core/lib/commands/compress.js | 25 ++++++++++--- imap-core/lib/imap-composer.js | 2 +- imap-core/lib/imap-connection.js | 2 +- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1928dbf0..8207f2a3 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Wild Duck IMAP server supports the following IMAP standards: - **APPENDLIMIT** (RFC7889) – maximum global allowed message size is advertised in CAPABILITY listing - **UTF8=ACCEPT** (RFC6855) – this also means that Wild Duck natively supports unicode email usernames. For example <андрис@уайлддак.орг> is a valid email address that is hosted by a test instance of Wild Duck - **QUOTA** (RFC2087) – Quota size is global for an account, using a single quota root. Be aware that quota size does not mean actual byte storage in disk, it is calculated as the sum of the rfc822 sources of stored messages. Actual disk usage is larger as there are database overhead per every message. +- **COMPRESS=DEFLATE** (RFC4978) – Compress traffic between the client and the server Wild Duck more or less passes the [ImapTest](https://www.imapwiki.org/ImapTest/TestFeatures). Common errors that arise in the test are unknown labels (Wild Duck doesn't send unsolicited FLAGS updates) and NO for STORE (messages deleted in one session can not be updated in another). @@ -379,26 +380,29 @@ Parameters - **id** is the mailbox ID - **size** is optional number to limit the length of the messages array (defaults to 20) -- **before** is an optional paging number (see *next* in response) -- **after** is an optional paging number (see *prev* in response) +- **before** is an optional paging number (see _next_ in response) +- **after** is an optional paging number (see _prev_ in response) Response includes the following fields - * **mailbox** is an object that lists some metadata about the current mailbox - * **id** is the mailbox ID - * **path** is the folder path - * **next** is an URL fragment for retrieving the next page (or false if there are no more pages) - * **prev** is an URL fragment for retrieving the previous page (or false if it is the first page) - * **messages** is an array of messages in the mailbox - * **id** is the message ID - * **date** is the date when this message was received - * **hasAttachments** is a boolean that indicates if this messages has attachments or not - * **intro** includes the first 256 characters from the message - * **subject** is the message title - * **from** is the From: field - * **to** is the To: field - * **cc** is the Cc: field - * **bcc** is the Bcc: field +- **mailbox** is an object that lists some metadata about the current mailbox + + - **id** is the mailbox ID + - **path** is the folder path + +- **next** is an URL fragment for retrieving the next page (or false if there are no more pages) +- **prev** is an URL fragment for retrieving the previous page (or false if it is the first page) +- **messages** is an array of messages in the mailbox + + - **id** is the message ID + - **date** is the date when this message was received + - **hasAttachments** is a boolean that indicates if this messages has attachments or not + - **intro** includes the first 256 characters from the message + - **subject** is the message title + - **from** is the From: field + - **to** is the To: field + - **cc** is the Cc: field + - **bcc** is the Bcc: field The response for successful listing should look like this: @@ -451,6 +455,7 @@ Response message includes the following fields - **date** is the receive date (not header Date: field) - **mailbox** is the id of the mailbox this messages belongs to + - **flags** is an array of IMAP flags for this message - **text** is the plaintext version of the message (derived from html if not present in message source) - **html** is the HTML version of the message (derived from plaintext if not present in message source) @@ -468,15 +473,21 @@ Response message includes the following fields HTML content has embedded images linked with the following URL structure: - attachment:MESSAGE_ID/ATTACHMENT_ID +``` +attachment:MESSAGE_ID/ATTACHMENT_ID +``` For example: - +``` + +``` To fetch the actual attachment contents for this image, use the following url: - http://localhost:8080/message/aaaaaa/attachment/bbbbbb +``` +http://localhost:8080/message/aaaaaa/attachment/bbbbbb +``` #### Example response @@ -588,10 +599,9 @@ Any other differences are most probably real bugs and unintentional. Wild Duck does not plan to be the most feature-rich IMAP client in the world. Most IMAP extensions are useless because there aren't too many clients that are able to benefit from these extensions. There are a few extensions though that would make sense to be added to Wild Duck -1. The IMAP COMPRESS Extension (RFC4978) -2. IMAP4 non-synchronizing literals, LITERAL- (RFC7888). Synchronized literals are needed for APPEND to check mailbox quota, small values could go with the non-synchronizing version. -3. LIST-STATUS (RFC5819) -4. _What else?_ (definitely not NOTIFY nor QRESYNC) +1. IMAP4 non-synchronizing literals, LITERAL- (RFC7888). Synchronized literals are needed for APPEND to check mailbox quota, small values could go with the non-synchronizing version. +2. LIST-STATUS (RFC5819) +3. _What else?_ (definitely not NOTIFY nor QRESYNC) ## Testing diff --git a/imap-core/lib/commands/compress.js b/imap-core/lib/commands/compress.js index 3556f413..ab54d3b9 100644 --- a/imap-core/lib/commands/compress.js +++ b/imap-core/lib/commands/compress.js @@ -32,17 +32,34 @@ module.exports = { setImmediate(() => { this.compression = true; - this._deflate = zlib.createDeflateRaw(); + this._deflate = zlib.createDeflateRaw({ + windowBits: 15 + }); this._inflate = zlib.createInflateRaw(); this._deflate.once('error', err => { - this._socket.emit('error', err); + this._server.logger.debug('[%s] Deflate error %s', this.id, err.message); + this.close(); }); - this._deflate.pipe(this._socket); + this._inflate.once('error', err => { + this._server.logger.debug('[%s] Inflate error %s', this.id, err.message); + this.close(); + }); this.writeStream.unpipe(this._socket); - this.writeStream.pipe(this._deflate); + this._deflate.pipe(this._socket); + let readNext = () => { + let chunk; + while ((chunk = this.writeStream.read()) !== null) { + if (this._deflate.write(chunk) === false) { + return this._deflate.once('drain', readNext); + } + } + // flush data to socket + this._deflate.flush(); + }; + this.writeStream.on('readable', readNext); this._socket.unpipe(this._parser); this._socket.pipe(this._inflate).pipe(this._parser); diff --git a/imap-core/lib/imap-composer.js b/imap-core/lib/imap-composer.js index 35c257b8..1cf64dd9 100644 --- a/imap-core/lib/imap-composer.js +++ b/imap-core/lib/imap-composer.js @@ -21,7 +21,7 @@ class IMAPComposer extends Transform { if (typeof obj.pipe === 'function') { // pipe stream to socket and wait until it finishes before continuing this.connection._server.logger.debug('[%s] S: ', this.connection.id); - obj.pipe(this.connection._socket, { + obj.pipe(this.connection[!this.connection.compression ? '_socket' : '_deflate'], { end: false }); obj.on('error', err => this.emit('error', err)); diff --git a/imap-core/lib/imap-connection.js b/imap-core/lib/imap-connection.js index 65ba108a..843ca2d0 100644 --- a/imap-core/lib/imap-connection.js +++ b/imap-core/lib/imap-connection.js @@ -122,7 +122,7 @@ class IMAPConnection extends EventEmitter { */ send(payload, callback) { if (this._socket && this._socket.writable) { - (!this.compression ? this._socket : this._deflate).write(payload + '\r\n', 'binary', callback); + this[!this.compression ? '_socket' : '_deflate'].write(payload + '\r\n', 'binary', callback); this._server.logger.debug('[%s] S:', this.id, payload); } }