diff options
Diffstat (limited to 'comm/mailnews/base/src/LineReader.jsm')
-rw-r--r-- | comm/mailnews/base/src/LineReader.jsm | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/comm/mailnews/base/src/LineReader.jsm b/comm/mailnews/base/src/LineReader.jsm new file mode 100644 index 0000000000..2417457e3c --- /dev/null +++ b/comm/mailnews/base/src/LineReader.jsm @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const EXPORTED_SYMBOLS = ["LineReader"]; + +/** + * For a single request, mail servers may return several multi-line responses. A + * definition of multi-line responses can be found at rfc3977#section-3.1.1. + * + * This class helps dealing with multi-line responses by: + * - Break up a response to lines + * - Join incomplete line from a previous response with the current response + * - Remove stuffed dot (.. at the beginning of a line) + * - Detect the end of the response (\r\n.\r\n) + */ +class LineReader { + processingMultiLineResponse = false; + _data = ""; + + /** + * Read a multi-line response, emit each line through a callback. + * + * @param {string} data - A multi-line response received from the server. + * @param {Function} lineCallback - A line will be passed to the callback each + * time. + * @param {Function} doneCallback - A function to be called when data is ended. + */ + read(data, lineCallback, doneCallback) { + this._data += data; + if (this._data == ".\r\n" || this._data.endsWith("\r\n.\r\n")) { + this.processingMultiLineResponse = false; + this._data = this._data.slice(0, -3); + } else { + this.processingMultiLineResponse = true; + } + if (this._running) { + // This function can be called multiple times, but this._data should only + // be consumed once. + return; + } + + let i = 0; + this._running = true; + while (this._data) { + let index = this._data.indexOf("\r\n"); + if (index == -1) { + // Not enough data, save it for the next round. + break; + } + let line = this._data.slice(0, index + 2); + if (line.startsWith("..")) { + // Remove stuffed dot. + line = line.slice(1); + } + lineCallback(line); + this._data = this._data.slice(index + 2); + if (++i % 100 == 0) { + // Prevent blocking main process for too long. + Services.tm.spinEventLoopUntilEmpty(); + } + } + this._running = false; + if (!this.processingMultiLineResponse && !this._data) { + doneCallback(); + } + } +} |