Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-imap

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-imap - npm Package Compare versions

Comparing version 0.9.5 to 0.9.6

.eslintrc.js

188

deps/encoding/encoding.js

@@ -89,3 +89,3 @@ /*

/** @return {number} Get the next byte from the stream. */
this.get = function() {
this.get = function () {
return (pos >= bytes.length) ? EOF_byte : Number(bytes[pos]);

@@ -96,3 +96,3 @@ };

* offset the byte pointer. */
this.offset = function(n) {
this.offset = function (n) {
pos += n;

@@ -112,3 +112,3 @@ if (pos < 0) {

*/
this.match = function(test) {
this.match = function (test) {
if (test.length > pos + bytes.length) {

@@ -139,3 +139,3 @@ return false;

*/
this.emit = function(var_args) {
this.emit = function (var_args) {
/** @type {number} */

@@ -199,3 +199,3 @@ var last = EOF_byte;

* to advance the code point pointer by.*/
this.offset = function(n) {
this.offset = function (n) {
pos += n;

@@ -212,3 +212,3 @@ if (pos < 0) {

/** @return {number} Get the next code point from the stream. */
this.get = function() {
this.get = function () {
if (pos >= cps.length) {

@@ -229,3 +229,3 @@ return EOF_code_point;

/** @return {string} The accumulated string. */
this.string = function() {
this.string = function () {
return string;

@@ -235,3 +235,3 @@ };

/** @param {number} c The code point to encode into the stream. */
this.emit = function(c) {
this.emit = function (c) {
if (c <= 0xFFFF) {

@@ -275,3 +275,3 @@ string += String.fromCharCode(c);

throw new EncodingError('The code point ' + code_point +
' could not be encoded.');
' could not be encoded.');
}

@@ -763,6 +763,6 @@

var label_to_encoding = {};
encodings.forEach(function(category) {
category.encodings.forEach(function(encoding) {
encodings.forEach(function (category) {
category.encodings.forEach(function (encoding) {
name_to_encoding[encoding.name] = encoding;
encoding.labels.forEach(function(label) {
encoding.labels.forEach(function (label) {
label_to_encoding[label] = encoding;

@@ -870,3 +870,3 @@ });

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -913,3 +913,3 @@ if (bite === EOF_byte) {

utf8_code_point = utf8_code_point + (bite - 0x80) *
Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
if (utf8_bytes_seen !== utf8_bytes_needed) {

@@ -925,3 +925,3 @@ return null;

if (inRange(code_point, lower_boundary, 0x10FFFF) &&
!inRange(code_point, 0xD800, 0xDFFF)) {
!inRange(code_point, 0xD800, 0xDFFF)) {
return code_point;

@@ -944,3 +944,3 @@ }

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -969,3 +969,3 @@ if (code_point === EOF_code_point) {

var result = output_byte_stream.emit(
div(code_point, Math.pow(64, count)) + offset);
div(code_point, Math.pow(64, count)) + offset);
while (count > 0) {

@@ -980,6 +980,6 @@ var temp = div(code_point, Math.pow(64, count - 1));

name_to_encoding['utf-8'].getEncoder = function(options) {
name_to_encoding['utf-8'].getEncoder = function (options) {
return new UTF8Encoder(options);
};
name_to_encoding['utf-8'].getDecoder = function(options) {
name_to_encoding['utf-8'].getDecoder = function (options) {
return new UTF8Decoder(options);

@@ -1004,3 +1004,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -1034,3 +1034,3 @@ if (bite === EOF_byte) {

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1052,12 +1052,12 @@ if (code_point === EOF_code_point) {

(function() {
encodings.forEach(function(category) {
(function () {
encodings.forEach(function (category) {
if (category.heading !== 'Legacy single-byte encodings')
return;
category.encodings.forEach(function(encoding) {
category.encodings.forEach(function (encoding) {
var index = indexes[encoding.name];
encoding.getDecoder = function(options) {
encoding.getDecoder = function (options) {
return new SingleByteDecoder(index, options);
};
encoding.getEncoder = function(options) {
encoding.getEncoder = function (options) {
return new SingleByteEncoder(index, options);

@@ -1090,10 +1090,10 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();
if (bite === EOF_byte && gbk_first === 0x00 &&
gbk_second === 0x00 && gbk_third === 0x00) {
gbk_second === 0x00 && gbk_third === 0x00) {
return EOF_code_point;
}
if (bite === EOF_byte &&
(gbk_first !== 0x00 || gbk_second !== 0x00 || gbk_third !== 0x00)) {
(gbk_first !== 0x00 || gbk_second !== 0x00 || gbk_third !== 0x00)) {
gbk_first = 0x00;

@@ -1110,4 +1110,4 @@ gbk_second = 0x00;

code_point = indexGB18030CodePointFor(
(((gbk_first - 0x81) * 10 + (gbk_second - 0x30)) * 126 +
(gbk_third - 0x81)) * 10 + bite - 0x30);
(((gbk_first - 0x81) * 10 + (gbk_second - 0x30)) * 126 +
(gbk_third - 0x81)) * 10 + bite - 0x30);
}

@@ -1146,3 +1146,3 @@ gbk_first = 0x00;

code_point = pointer === null ? null :
indexCodePointFor(pointer, indexes['gbk']);
indexCodePointFor(pointer, indexes['gbk']);
if (pointer === null) {

@@ -1182,3 +1182,3 @@ byte_pointer.offset(-1);

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1210,12 +1210,12 @@ if (code_point === EOF_code_point) {

return output_byte_stream.emit(byte1 + 0x81,
byte2 + 0x30,
byte3 + 0x81,
byte4 + 0x30);
byte2 + 0x30,
byte3 + 0x81,
byte4 + 0x30);
};
}
name_to_encoding['gbk'].getEncoder = function(options) {
name_to_encoding['gbk'].getEncoder = function (options) {
return new GBKEncoder(false, options);
};
name_to_encoding['gbk'].getDecoder = function(options) {
name_to_encoding['gbk'].getDecoder = function (options) {
return new GBKDecoder(false, options);

@@ -1225,6 +1225,6 @@ };

// 9.2 gb18030
name_to_encoding['gb18030'].getEncoder = function(options) {
name_to_encoding['gb18030'].getEncoder = function (options) {
return new GBKEncoder(true, options);
};
name_to_encoding['gb18030'].getDecoder = function(options) {
name_to_encoding['gb18030'].getDecoder = function (options) {
return new GBKDecoder(true, options);

@@ -1248,3 +1248,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -1284,3 +1284,3 @@ if (bite === EOF_byte && hzgb2312_lead === 0x00) {

code_point = indexCodePointFor((lead - 1) * 190 +
(bite + 0x3F), indexes['gbk']);
(bite + 0x3F), indexes['gbk']);
}

@@ -1328,3 +1328,3 @@ if (bite === 0x0A) {

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1364,6 +1364,6 @@ if (code_point === EOF_code_point) {

name_to_encoding['hz-gb-2312'].getEncoder = function(options) {
name_to_encoding['hz-gb-2312'].getEncoder = function (options) {
return new HZGB2312Encoder(options);
};
name_to_encoding['hz-gb-2312'].getDecoder = function(options) {
name_to_encoding['hz-gb-2312'].getDecoder = function (options) {
return new HZGB2312Decoder(options);

@@ -1392,3 +1392,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
// NOTE: Hack to support emitting two code points

@@ -1434,3 +1434,3 @@ if (big5_pending !== null) {

var code_point = (pointer === null) ? null :
indexCodePointFor(pointer, indexes['big5']);
indexCodePointFor(pointer, indexes['big5']);
if (pointer === null) {

@@ -1466,3 +1466,3 @@ byte_pointer.offset(-1);

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1490,6 +1490,6 @@ if (code_point === EOF_code_point) {

name_to_encoding['big5'].getEncoder = function(options) {
name_to_encoding['big5'].getEncoder = function (options) {
return new Big5Encoder(options);
};
name_to_encoding['big5'].getDecoder = function(options) {
name_to_encoding['big5'].getDecoder = function (options) {
return new Big5Decoder(options);

@@ -1518,3 +1518,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -1538,3 +1538,3 @@ if (bite === EOF_byte) {

code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1,
indexes['jis0212']);
indexes['jis0212']);
}

@@ -1564,3 +1564,3 @@ if (!inRange(bite, 0xA1, 0xFE)) {

code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1,
indexes['jis0208']);
indexes['jis0208']);
}

@@ -1597,3 +1597,3 @@ if (!inRange(bite, 0xA1, 0xFE)) {

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1627,6 +1627,6 @@ if (code_point === EOF_code_point) {

name_to_encoding['euc-jp'].getEncoder = function(options) {
name_to_encoding['euc-jp'].getEncoder = function (options) {
return new EUCJPEncoder(options);
};
name_to_encoding['euc-jp'].getDecoder = function(options) {
name_to_encoding['euc-jp'].getDecoder = function (options) {
return new EUCJPDecoder(options);

@@ -1661,3 +1661,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -1760,6 +1760,6 @@ if (bite !== EOF_byte) {

if (inRange(iso2022jp_lead, 0x21, 0x7E) &&
inRange(bite, 0x21, 0x7E)) {
inRange(bite, 0x21, 0x7E)) {
code_point = (iso2022jp_jis0212 === false) ?
indexCodePointFor(pointer, indexes['jis0208']) :
indexCodePointFor(pointer, indexes['jis0212']);
indexCodePointFor(pointer, indexes['jis0208']) :
indexCodePointFor(pointer, indexes['jis0212']);
}

@@ -1805,3 +1805,3 @@ if (code_point === null) {

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1813,4 +1813,4 @@ if (code_point === EOF_code_point) {

if ((inRange(code_point, 0x0000, 0x007F) ||
code_point === 0x00A5 || code_point === 0x203E) &&
iso2022jp_state !== state.ASCII) {
code_point === 0x00A5 || code_point === 0x203E) &&
iso2022jp_state !== state.ASCII) {
code_point_pointer.offset(-1);

@@ -1830,3 +1830,3 @@ iso2022jp_state = state.ASCII;

if (inRange(code_point, 0xFF61, 0xFF9F) &&
iso2022jp_state !== state.Katakana) {
iso2022jp_state !== state.Katakana) {
code_point_pointer.offset(-1);

@@ -1854,6 +1854,6 @@ iso2022jp_state = state.Katakana;

name_to_encoding['iso-2022-jp'].getEncoder = function(options) {
name_to_encoding['iso-2022-jp'].getEncoder = function (options) {
return new ISO2022JPEncoder(options);
};
name_to_encoding['iso-2022-jp'].getDecoder = function(options) {
name_to_encoding['iso-2022-jp'].getDecoder = function (options) {
return new ISO2022JPDecoder(options);

@@ -1876,3 +1876,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -1894,3 +1894,3 @@ if (bite === EOF_byte && shiftjis_lead === 0x00) {

var code_point = indexCodePointFor((lead - lead_offset) * 188 +
bite - offset, indexes['jis0208']);
bite - offset, indexes['jis0208']);
if (code_point === null) {

@@ -1929,3 +1929,3 @@ return decoderError(fatal);

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -1960,6 +1960,6 @@ if (code_point === EOF_code_point) {

name_to_encoding['shift_jis'].getEncoder = function(options) {
name_to_encoding['shift_jis'].getEncoder = function (options) {
return new ShiftJISEncoder(options);
};
name_to_encoding['shift_jis'].getDecoder = function(options) {
name_to_encoding['shift_jis'].getDecoder = function (options) {
return new ShiftJISDecoder(options);

@@ -1986,3 +1986,3 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();

@@ -2015,7 +2015,7 @@ if (bite === EOF_byte && euckr_lead === 0) {

pointer = (26 + 26 + 126) * (0xC7 - 0x81) + (lead - 0xC7) * 94 +
(bite - 0xA1);
(bite - 0xA1);
}
var code_point = (pointer === null) ? null :
indexCodePointFor(pointer, indexes['euc-kr']);
indexCodePointFor(pointer, indexes['euc-kr']);
if (pointer === null) {

@@ -2054,3 +2054,3 @@ byte_pointer.offset(-1);

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
var code_point = code_point_pointer.get();

@@ -2082,6 +2082,6 @@ if (code_point === EOF_code_point) {

name_to_encoding['euc-kr'].getEncoder = function(options) {
name_to_encoding['euc-kr'].getEncoder = function (options) {
return new EUCKREncoder(options);
};
name_to_encoding['euc-kr'].getDecoder = function(options) {
name_to_encoding['euc-kr'].getDecoder = function (options) {
return new EUCKRDecoder(options);

@@ -2111,10 +2111,10 @@ };

*/
this.decode = function(byte_pointer) {
this.decode = function (byte_pointer) {
var bite = byte_pointer.get();
if (bite === EOF_byte && utf16_lead_byte === null &&
utf16_lead_surrogate === null) {
utf16_lead_surrogate === null) {
return EOF_code_point;
}
if (bite === EOF_byte && (utf16_lead_byte !== null ||
utf16_lead_surrogate !== null)) {
utf16_lead_surrogate !== null)) {
return decoderError(fatal);

@@ -2139,3 +2139,3 @@ }

return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
(code_point - 0xDC00);
(code_point - 0xDC00);
}

@@ -2168,3 +2168,3 @@ byte_pointer.offset(-2);

*/
this.encode = function(output_byte_stream, code_point_pointer) {
this.encode = function (output_byte_stream, code_point_pointer) {
function convert_to_bytes(code_unit) {

@@ -2196,6 +2196,6 @@ var byte1 = code_unit >> 8;

name_to_encoding['utf-16le'].getEncoder = function(options) {
name_to_encoding['utf-16le'].getEncoder = function (options) {
return new UTF16Encoder(false, options);
};
name_to_encoding['utf-16le'].getDecoder = function(options) {
name_to_encoding['utf-16le'].getDecoder = function (options) {
return new UTF16Decoder(false, options);

@@ -2205,6 +2205,6 @@ };

// 13.2 utf-16be
name_to_encoding['utf-16be'].getEncoder = function(options) {
name_to_encoding['utf-16be'].getEncoder = function (options) {
return new UTF16Encoder(true, options);
};
name_to_encoding['utf-16be'].getDecoder = function(options) {
name_to_encoding['utf-16be'].getDecoder = function (options) {
return new UTF16Decoder(true, options);

@@ -2256,4 +2256,4 @@ };

if (this._encoding === null || (this._encoding.name !== 'utf-8' &&
this._encoding.name !== 'utf-16le' &&
this._encoding.name !== 'utf-16be'))
this._encoding.name !== 'utf-16le' &&
this._encoding.name !== 'utf-16be'))
throw new TypeError('Unknown encoding: ' + opt_encoding);

@@ -2269,4 +2269,4 @@ /** @private @type {boolean} */

Object.defineProperty(
this, 'encoding',
{ get: function() { return this._encoding.name; } });
this, 'encoding',
{ get: function () { return this._encoding.name; } });
} else {

@@ -2337,4 +2337,4 @@ this.encoding = this._encoding.name;

Object.defineProperty(
this, 'encoding',
{ get: function() { return this._encoding.name; } });
this, 'encoding',
{ get: function () { return this._encoding.name; } });
} else {

@@ -2380,3 +2380,3 @@ this.encoding = this._encoding.name;

} while (code_point !== EOF_code_point &&
input_stream.get() != EOF_byte);
input_stream.get() != EOF_byte);
this._decoder = null;

@@ -2389,3 +2389,3 @@ }

if (UTFs.indexOf(this.encoding) !== -1 &&
result.charCodeAt(0) === 0xFEFF) {
result.charCodeAt(0) === 0xFEFF) {
result = result.substring(1);

@@ -2392,0 +2392,0 @@ }

@@ -1,56 +0,54 @@

'use strict';
'use strict'
const tls = require('tls'),
crypto = require('crypto'),
Socket = require('net').Socket,
EventEmitter = require('events').EventEmitter,
// inherits = require('util').inherits,
inspect = require('util').inspect,
isDate = require('util').isDate,
utf7 = require('utf7').imap;
crypto = require('crypto'),
Socket = require('net').Socket,
EventEmitter = require('events').EventEmitter,
// inherits = require('util').inherits,
inspect = require('util').inspect,
isDate = require('util').isDate,
utf7 = require('utf7').imap,
MONTHS = require('util').MONTHS
const Parser = require('./Parser').Parser,
parseExpr = require('./Parser').parseExpr,
parseHeader = require('./Parser').parseHeader;
parseExpr = require('./Parser').parseExpr,
parseHeader = require('./Parser').parseHeader
const validateUIDList = require('./utils').validateUIDList,
_deepEqual = require('./utils')._deepEqual,
escape = require('./utils').escape,
buildSearchQuery = require('./utils').buildSearchQuery
_deepEqual = require('./utils')._deepEqual,
escape = require('./utils').escape,
buildSearchQuery = require('./utils').buildSearchQuery
const MAX_INT = Number.MAX_SAFE_INTEGER,
KEEPALIVE_INTERVAL = 10000,
MAX_IDLE_WAIT = 300000, // 5 minutes
MONTHS = ['Jan', 'Feb', 'Mar',
'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'],
FETCH_ATTR_MAP = {
'RFC822.SIZE': 'size',
'BODY': 'struct',
'BODYSTRUCTURE': 'struct',
'ENVELOPE': 'envelope',
'INTERNALDATE': 'date'
},
SPECIAL_USE_ATTRIBUTES = [
'\\All',
'\\Archive',
'\\Drafts',
'\\Flagged',
'\\Important',
'\\Junk',
'\\Sent',
'\\Trash'
],
CRLF = '\r\n',
RE_CMD = /^([^ ]+)(?: |$)/,
RE_UIDCMD_HASRESULTS = /^UID (?:FETCH|SEARCH|SORT)/,
RE_IDLENOOPRES = /^(IDLE|NOOP) /,
RE_OPENBOX = /^EXAMINE|SELECT$/,
RE_BODYPART = /^BODY\[/,
RE_INVALID_KW_CHARS = /[\(\)\{\\\"\]\%\*\x00-\x20\x7F]/,
RE_ESCAPE = /\\\\/g
KEEPALIVE_INTERVAL = 10000,
MAX_IDLE_WAIT = 300000, // 5 minutes
FETCH_ATTR_MAP = {
'RFC822.SIZE': 'size',
'BODY': 'struct',
'BODYSTRUCTURE': 'struct',
'ENVELOPE': 'envelope',
'INTERNALDATE': 'date'
},
SPECIAL_USE_ATTRIBUTES = [
'\\All',
'\\Archive',
'\\Drafts',
'\\Flagged',
'\\Important',
'\\Junk',
'\\Sent',
'\\Trash'
],
CRLF = '\r\n',
RE_CMD = /^([^ ]+)(?: |$)/,
RE_UIDCMD_HASRESULTS = /^UID (?:FETCH|SEARCH|SORT)/,
RE_IDLENOOPRES = /^(IDLE|NOOP) /,
RE_OPENBOX = /^EXAMINE|SELECT$/,
RE_BODYPART = /^BODY\[/,
RE_INVALID_KW_CHARS = /[\(\)\{\\\"\]\%\*\x00-\x20\x7F]/,
RE_ESCAPE = /\\\\/g
/**

@@ -61,9 +59,9 @@ * Connection

class Connection extends EventEmitter {
constructor(config) {
super();
super()
if (!(this instanceof Connection))
return new Connection(config);
return new Connection(config)
// EventEmitter.call(this);
config || (config = {});
config || (config = {})
this._config = {

@@ -87,113 +85,113 @@ localAddress: config.localAddress,

: config.keepalive)
};
this._sock = config.socket || undefined;
this._tagcount = 0;
this._tmrConn = undefined;
this._tmrKeepalive = undefined;
this._tmrAuth = undefined;
this._queue = [];
this._box = undefined;
this._idle = { started: undefined, enabled: false };
this._parser = undefined;
this._curReq = undefined;
this.delimiter = undefined;
this.namespaces = undefined;
this.state = 'disconnected';
this.debug = config.debug;
}
this._sock = config.socket || undefined
this._tagcount = 0
this._tmrConn = undefined
this._tmrKeepalive = undefined
this._tmrAuth = undefined
this._queue = []
this._box = undefined
this._idle = { started: undefined, enabled: false }
this._parser = undefined
this._curReq = undefined
this.delimiter = undefined
this.namespaces = undefined
this.state = 'disconnected'
this.debug = config.debug
}
connect() {
const config = this._config, self = this;
let socket, parser, tlsOptions;
socket = config.socket || new Socket();
socket.setKeepAlive(true);
this._sock = undefined;
this._tagcount = 0;
this._tmrConn = undefined;
this._tmrKeepalive = undefined;
this._tmrAuth = undefined;
this._queue = [];
this._box = undefined;
this._idle = { started: undefined, enabled: false };
this._parser = undefined;
this._curReq = undefined;
this.delimiter = undefined;
this.namespaces = undefined;
this.state = 'disconnected';
const config = this._config, self = this
let socket, parser, tlsOptions
socket = config.socket || new Socket()
socket.setKeepAlive(true)
this._sock = undefined
this._tagcount = 0
this._tmrConn = undefined
this._tmrKeepalive = undefined
this._tmrAuth = undefined
this._queue = []
this._box = undefined
this._idle = { started: undefined, enabled: false }
this._parser = undefined
this._curReq = undefined
this.delimiter = undefined
this.namespaces = undefined
this.state = 'disconnected'
if (config.tls) {
tlsOptions = {};
tlsOptions.host = config.host;
tlsOptions.servername = config.host;
tlsOptions = {}
tlsOptions.host = config.host
tlsOptions.servername = config.host
// Host name may be overridden the tlsOptions
for (let k in config.tlsOptions)
tlsOptions[k] = config.tlsOptions[k];
tlsOptions.socket = socket;
tlsOptions[k] = config.tlsOptions[k]
tlsOptions.socket = socket
}
if (config.tls) {
this._sock = tls.connect(tlsOptions, onconnect);
this._isTsl = true;
this._sock = tls.connect(tlsOptions, onconnect)
this._isTsl = true
} else {
socket.once('connect', onconnect);
this._sock = socket;
socket.once('connect', onconnect)
this._sock = socket
}
function onconnect() {
clearTimeout(self._tmrConn);
self.state = 'connected';
self.debug && self.debug('[connection] Connected to host');
clearTimeout(self._tmrConn)
self.state = 'connected'
self.debug && self.debug('[connection] Connected to host')
self._tmrAuth = setTimeout(function () {
const err = new Error('Timed out while authenticating with server');
err.source = 'timeout-auth';
self.emit('error', err);
socket.destroy();
}, config.authTimeout);
const err = new Error('Timed out while authenticating with server')
err.source = 'timeout-auth'
self.emit('error', err)
socket.destroy()
}, config.authTimeout)
}
let additionalClose = !config.socket;
let additionalClose = !config.socket
this._onError = function (err) {
clearTimeout(self._tmrConn);
clearTimeout(self._tmrAuth);
self.debug && self.debug('[connection] Error: ' + err);
err.source = 'socket';
clearTimeout(self._tmrConn)
clearTimeout(self._tmrAuth)
self.debug && self.debug('[connection] Error: ' + err)
err.source = 'socket'
if (additionalClose && self._isTsl)
socket.destroy();
self.emit('error', err);
};
this._sock.on('error', this._onError);
socket.destroy()
self.emit('error', err)
}
this._sock.on('error', this._onError)
this._onSocketTimeout = function () {
clearTimeout(self._tmrConn);
clearTimeout(self._tmrAuth);
clearTimeout(self._tmrKeepalive);
self.state = 'disconnected';
self.debug && self.debug('[connection] Socket timeout');
const err = new Error('Socket timed out while talking to server');
err.source = 'socket-timeout';
self.emit('error', err);
socket.destroy();
};
this._sock.on('timeout', this._onSocketTimeout);
socket.setTimeout(config.socketTimeout);
clearTimeout(self._tmrConn)
clearTimeout(self._tmrAuth)
clearTimeout(self._tmrKeepalive)
self.state = 'disconnected'
self.debug && self.debug('[connection] Socket timeout')
const err = new Error('Socket timed out while talking to server')
err.source = 'socket-timeout'
self.emit('error', err)
socket.destroy()
}
this._sock.on('timeout', this._onSocketTimeout)
socket.setTimeout(config.socketTimeout)
socket.once('close', function (had_err) {
clearTimeout(self._tmrConn);
clearTimeout(self._tmrAuth);
clearTimeout(self._tmrKeepalive);
self.state = 'disconnected';
self.debug && self.debug('[connection] Closed');
self.emit('close', had_err);
});
clearTimeout(self._tmrConn)
clearTimeout(self._tmrAuth)
clearTimeout(self._tmrKeepalive)
self.state = 'disconnected'
self.debug && self.debug('[connection] Closed')
self.emit('close', had_err)
})
socket.once('end', function () {
clearTimeout(self._tmrConn);
clearTimeout(self._tmrAuth);
clearTimeout(self._tmrKeepalive);
self.state = 'disconnected';
self.debug && self.debug('[connection] Ended');
self.emit('end');
});
this._parser = parser = new Parser(this._sock, this.debug);
clearTimeout(self._tmrConn)
clearTimeout(self._tmrAuth)
clearTimeout(self._tmrKeepalive)
self.state = 'disconnected'
self.debug && self.debug('[connection] Ended')
self.emit('end')
})
this._parser = parser = new Parser(this._sock, this.debug)
parser.on('untagged', function (info) {
self._resUntagged(info);
});
self._resUntagged(info)
})
parser.on('tagged', function (info) {
self._resTagged(info);
});
self._resTagged(info)
})
parser.on('body', function (stream, info) {
let msg = self._curReq.fetchCache[info.seqno];
let toget;
let msg = self._curReq.fetchCache[info.seqno]
let toget
if (msg === undefined) {

@@ -205,6 +203,6 @@ msg = self._curReq.fetchCache[info.seqno] = {

ended: false
};
self._curReq.bodyEmitter.emit('message', msg.msgEmitter, info.seqno);
}
self._curReq.bodyEmitter.emit('message', msg.msgEmitter, info.seqno)
}
toget = msg.toget;
toget = msg.toget
// here we compare the parsed version of the expression inside BODY[]

@@ -214,14 +212,14 @@ // because 'HEADER.FIELDS (TO FROM)' really is equivalent to

// quoted form even if the client did not use quotes
const thisbody = parseExpr(info.which.toUpperCase());
const thisbody = parseExpr(info.which.toUpperCase())
for (let i = 0, len = toget.length; i < len; ++i) {
if (_deepEqual(thisbody, toget[i])) {
toget.splice(i, 1);
msg.msgEmitter.emit('body', stream, info);
return;
toget.splice(i, 1)
msg.msgEmitter.emit('body', stream, info)
return
}
}
stream.resume(); // a body we didn't ask for?
});
stream.resume() // a body we didn't ask for?
})
parser.on('continue', function (info) {
const type = self._curReq.type;
const type = self._curReq.type
if (type === 'IDLE') {

@@ -235,32 +233,32 @@ if (self._queue.length

&& !self._idle.enabled) {
self.debug && self.debug('=> DONE');
self._sock.write('DONE' + CRLF);
return;
self.debug && self.debug('=> DONE')
self._sock.write('DONE' + CRLF)
return
}
// now idling
self._idle.started = Date.now();
self._idle.started = Date.now()
}
else if (/^AUTHENTICATE XOAUTH/.test(self._curReq.fullcmd)) {
self._curReq.oauthError = Buffer.from(info.text, 'base64').toString('utf8'); //new Buffer(info.text, 'base64').toString('utf8');
self.debug && self.debug('=> ' + inspect(CRLF));
self._sock.write(CRLF);
self._curReq.oauthError = Buffer.from(info.text, 'base64').toString('utf8') //new Buffer(info.text, 'base64').toString('utf8');
self.debug && self.debug('=> ' + inspect(CRLF))
self._sock.write(CRLF)
} else if (/^AUTHENTICATE CRAM-MD5/.test(self._curReq.fullcmd)) {
self._authCRAMMD5(info.text);
self._authCRAMMD5(info.text)
} else if (type === 'APPEND') {
self._sockWriteAppendData(self._curReq.appendData);
self._sockWriteAppendData(self._curReq.appendData)
}
else if (self._curReq.lines && self._curReq.lines.length) {
const line = self._curReq.lines.shift() + '\r\n';
self.debug && self.debug('=> ' + inspect(line));
self._sock.write(line, 'binary');
const line = self._curReq.lines.shift() + '\r\n'
self.debug && self.debug('=> ' + inspect(line))
self._sock.write(line, 'binary')
}
});
})
parser.on('other', function (line) {
let m;
let m
if (m = RE_IDLENOOPRES.exec(line)) {
// no longer idling
self._idle.enabled = false;
self._idle.started = undefined;
clearTimeout(self._tmrKeepalive);
self._curReq = undefined;
self._idle.enabled = false
self._idle.started = undefined
clearTimeout(self._tmrKeepalive)
self._curReq = undefined
if (self._queue.length === 0

@@ -270,17 +268,17 @@ && self._config.keepalive

&& !self._idle.enabled) {
self._idle.enabled = true;
self._idle.enabled = true
if (m[1] === 'NOOP')
self._doKeepaliveTimer();
self._doKeepaliveTimer()
else
self._doKeepaliveTimer(true);
self._doKeepaliveTimer(true)
}
self._processQueue();
self._processQueue()
}
});
})
this._tmrConn = setTimeout(function () {
const err = new Error('Timed out while connecting to server');
err.source = 'timeout';
self.emit('error', err);
socket.destroy();
}, config.connTimeout);
const err = new Error('Timed out while connecting to server')
err.source = 'timeout'
self.emit('error', err)
socket.destroy()
}, config.connTimeout)
socket.connect({

@@ -290,43 +288,43 @@ port: config.port,

localAddress: config.localAddress
});
})
}
serverSupports(cap) {
return (this._caps && this._caps.indexOf(cap) > -1);
return (this._caps && this._caps.indexOf(cap) > -1)
}
destroy() {
this._queue = [];
this._curReq = undefined;
this._sock && this._sock.end();
this._queue = []
this._curReq = undefined
this._sock && this._sock.end()
}
end() {
const self = this;
const self = this
this._enqueue('LOGOUT', function () {
self._queue = [];
self._curReq = undefined;
self._sock.end();
});
self._queue = []
self._curReq = undefined
self._sock.end()
})
}
append(data, options, cb) {
const literal = this.serverSupports('LITERAL+');
const literal = this.serverSupports('LITERAL+')
if (typeof options === 'function') {
cb = options;
options = undefined;
cb = options
options = undefined
}
options = options || {};
options = options || {}
if (!options.mailbox) {
if (!this._box)
throw new Error('No mailbox specified or currently selected');
throw new Error('No mailbox specified or currently selected')
else
options.mailbox = this._box.name;
options.mailbox = this._box.name
}
const cmd = 'APPEND "' + escape(utf7.encode('' + options.mailbox)) + '"';
let cmd = 'APPEND "' + escape(utf7.encode('' + options.mailbox)) + '"'
if (options.flags) {
if (!Array.isArray(options.flags))
options.flags = [options.flags];
options.flags = [options.flags]
if (options.flags.length > 0) {
for (let i = 0, len = options.flags.length; i < len; ++i) {
if (options.flags[i][0] !== '$' && options.flags[i][0] !== '\\')
options.flags[i] = '\\' + options.flags[i];
options.flags[i] = '\\' + options.flags[i]
}
cmd += ' (' + options.flags.join(' ') + ')';
cmd += ' (' + options.flags.join(' ') + ')'
}

@@ -336,93 +334,93 @@ }

if (!isDate(options.date))
throw new Error('`date` is not a Date object');
cmd += ' "';
cmd += ('0' + options.date.getDate()).slice(-2);
cmd += '-';
cmd += MONTHS[options.date.getMonth()];
cmd += '-';
cmd += options.date.getFullYear();
cmd += ' ';
cmd += ('0' + options.date.getHours()).slice(-2);
cmd += ':';
cmd += ('0' + options.date.getMinutes()).slice(-2);
cmd += ':';
cmd += ('0' + options.date.getSeconds()).slice(-2);
cmd += ((options.date.getTimezoneOffset() > 0) ? ' -' : ' +');
cmd += ('0' + (-options.date.getTimezoneOffset() / 60)).slice(-2);
cmd += ('0' + (-options.date.getTimezoneOffset() % 60)).slice(-2);
cmd += '"';
throw new Error('`date` is not a Date object')
cmd += ' "'
cmd += ('0' + options.date.getDate()).slice(-2)
cmd += '-'
cmd += MONTHS[options.date.getMonth()]
cmd += '-'
cmd += options.date.getFullYear()
cmd += ' '
cmd += ('0' + options.date.getHours()).slice(-2)
cmd += ':'
cmd += ('0' + options.date.getMinutes()).slice(-2)
cmd += ':'
cmd += ('0' + options.date.getSeconds()).slice(-2)
cmd += ((options.date.getTimezoneOffset() > 0) ? ' -' : ' +')
cmd += ('0' + (-options.date.getTimezoneOffset() / 60)).slice(-2)
cmd += ('0' + (-options.date.getTimezoneOffset() % 60)).slice(-2)
cmd += '"'
}
cmd += ' {';
cmd += (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data));
cmd += (literal ? '+' : '') + '}';
this._enqueue(cmd, cb);
cmd += ' {'
cmd += (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data))
cmd += (literal ? '+' : '') + '}'
this._enqueue(cmd, cb)
if (literal)
this._queue[this._queue.length - 1].literalAppendData = data;
this._queue[this._queue.length - 1].literalAppendData = data
else
this._queue[this._queue.length - 1].appendData = data;
this._queue[this._queue.length - 1].appendData = data
}
getSpecialUseBoxes(cb) {
this._enqueue('XLIST "" "*"', cb);
this._enqueue('XLIST "" "*"', cb)
}
getBoxes(namespace, cb) {
if (typeof namespace === 'function') {
cb = namespace;
namespace = '';
cb = namespace
namespace = ''
}
namespace = escape(utf7.encode('' + namespace));
this._enqueue('LIST "' + namespace + '" "*"', cb);
namespace = escape(utf7.encode('' + namespace))
this._enqueue('LIST "' + namespace + '" "*"', cb)
}
id(identification, cb) {
if (!this.serverSupports('ID'))
throw new Error('Server does not support ID');
const cmd = 'ID';
throw new Error('Server does not support ID')
let cmd = 'ID'
if ((identification === null) || (Object.keys(identification).length === 0))
cmd += ' NIL';
cmd += ' NIL'
else {
if (Object.keys(identification).length > 30)
throw new Error('Max allowed number of keys is 30');
const kv = [];
throw new Error('Max allowed number of keys is 30')
const kv = []
for (let k in identification) {
if (Buffer.byteLength(k) > 30)
throw new Error('Max allowed key length is 30');
throw new Error('Max allowed key length is 30')
if (Buffer.byteLength(identification[k]) > 1024)
throw new Error('Max allowed value length is 1024');
kv.push('"' + escape(k) + '"');
kv.push('"' + escape(identification[k]) + '"');
throw new Error('Max allowed value length is 1024')
kv.push('"' + escape(k) + '"')
kv.push('"' + escape(identification[k]) + '"')
}
cmd += ' (' + kv.join(' ') + ')';
cmd += ' (' + kv.join(' ') + ')'
}
this._enqueue(cmd, cb);
this._enqueue(cmd, cb)
}
openBox(name, readOnly, cb) {
if (this.state !== 'authenticated')
throw new Error('Not authenticated');
throw new Error('Not authenticated')
if (typeof readOnly === 'function') {
cb = readOnly;
readOnly = false;
cb = readOnly
readOnly = false
}
name = '' + name;
const encname = escape(utf7.encode(name));
let cmd = (readOnly ? 'EXAMINE' : 'SELECT'), self = this;
cmd += ' "' + encname + '"';
name = '' + name
const encname = escape(utf7.encode(name))
let cmd = (readOnly ? 'EXAMINE' : 'SELECT'), self = this
cmd += ' "' + encname + '"'
if (this.serverSupports('CONDSTORE'))
cmd += ' (CONDSTORE)';
cmd += ' (CONDSTORE)'
this._enqueue(cmd, function (err) {
if (err) {
self._box = undefined;
cb(err);
self._box = undefined
cb(err)
}
else {
self._box.name = name;
cb(err, self._box);
self._box.name = name
cb(err, self._box)
}
});
})
}
closeBox(shouldExpunge, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
const self = this;
throw new Error('No mailbox is currently selected')
const self = this
if (typeof shouldExpunge === 'function') {
cb = shouldExpunge;
shouldExpunge = true;
cb = shouldExpunge
shouldExpunge = true
}

@@ -432,5 +430,5 @@ if (shouldExpunge) {

if (!err)
self._box = undefined;
cb(err);
});
self._box = undefined
cb(err)
})
}

@@ -443,5 +441,5 @@ else {

if (!err)
self._box = undefined;
cb(err);
});
self._box = undefined
cb(err)
})
}

@@ -451,7 +449,7 @@ else {

// non-existent mailbox
const badbox = 'NODEJSIMAPCLOSINGBOX' + Date.now();
const badbox = 'NODEJSIMAPCLOSINGBOX' + Date.now()
this._enqueue('SELECT "' + badbox + '"', function (err) {
self._box = undefined;
cb();
});
self._box = undefined
cb()
})
}

@@ -461,118 +459,119 @@ }

addBox(name, cb) {
this._enqueue('CREATE "' + escape(utf7.encode('' + name)) + '"', cb);
this._enqueue('CREATE "' + escape(utf7.encode('' + name)) + '"', cb)
}
delBox(name, cb) {
this._enqueue('DELETE "' + escape(utf7.encode('' + name)) + '"', cb);
this._enqueue('DELETE "' + escape(utf7.encode('' + name)) + '"', cb)
}
renameBox(oldname, newname, cb) {
const encoldname = escape(utf7.encode('' + oldname)), encnewname = escape(utf7.encode('' + newname)), self = this;
const encoldname = escape(utf7.encode('' + oldname)), encnewname = escape(utf7.encode('' + newname)), self = this
this._enqueue('RENAME "' + encoldname + '" "' + encnewname + '"', function (err) {
if (err)
return cb(err);
return cb(err)
if (self._box
&& self._box.name === oldname
&& oldname.toUpperCase() !== 'INBOX') {
self._box.name = newname;
cb(err, self._box);
self._box.name = newname
cb(err, self._box)
}
else
cb();
});
cb()
})
}
subscribeBox(name, cb) {
this._enqueue('SUBSCRIBE "' + escape(utf7.encode('' + name)) + '"', cb);
this._enqueue('SUBSCRIBE "' + escape(utf7.encode('' + name)) + '"', cb)
}
unsubscribeBox(name, cb) {
this._enqueue('UNSUBSCRIBE "' + escape(utf7.encode('' + name)) + '"', cb);
this._enqueue('UNSUBSCRIBE "' + escape(utf7.encode('' + name)) + '"', cb)
}
getSubscribedBoxes(namespace, cb) {
if (typeof namespace === 'function') {
cb = namespace;
namespace = '';
cb = namespace
namespace = ''
}
namespace = escape(utf7.encode('' + namespace));
this._enqueue('LSUB "' + namespace + '" "*"', cb);
namespace = escape(utf7.encode('' + namespace))
this._enqueue('LSUB "' + namespace + '" "*"', cb)
}
status(boxName, cb) {
if (this._box && this._box.name === boxName)
throw new Error('Cannot call status on currently selected mailbox');
boxName = escape(utf7.encode('' + boxName));
let info = ['MESSAGES', 'RECENT', 'UNSEEN', 'UIDVALIDITY', 'UIDNEXT'];
throw new Error('Cannot call status on currently selected mailbox')
boxName = escape(utf7.encode('' + boxName))
let info = ['MESSAGES', 'RECENT', 'UNSEEN', 'UIDVALIDITY', 'UIDNEXT']
if (this.serverSupports('CONDSTORE'))
info.push('HIGHESTMODSEQ');
info = info.join(' ');
this._enqueue('STATUS "' + boxName + '" (' + info + ')', cb);
info.push('HIGHESTMODSEQ')
info = info.join(' ')
this._enqueue('STATUS "' + boxName + '" (' + info + ')', cb)
}
expunge(uids, cb) {
if (typeof uids === 'function') {
cb = uids;
uids = undefined;
cb = uids
uids = undefined
}
if (uids !== undefined) {
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0)
throw new Error('Empty uid list');
uids = uids.join(',');
throw new Error('Empty uid list')
uids = uids.join(',')
if (!this.serverSupports('UIDPLUS'))
throw new Error('Server does not support this feature (UIDPLUS)');
this._enqueue('UID EXPUNGE ' + uids, cb);
throw new Error('Server does not support this feature (UIDPLUS)')
this._enqueue('UID EXPUNGE ' + uids, cb)
}
else
this._enqueue('EXPUNGE', cb);
this._enqueue('EXPUNGE', cb)
}
search(criteria, cb) {
this._search('UID ', criteria, cb);
this._search('UID ', criteria, cb)
}
_search(which, criteria, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
else if (!Array.isArray(criteria))
throw new Error('Expected array for search criteria');
let cmd = which + 'SEARCH', info = { hasUTF8: false /*output*/ }, query = buildSearchQuery(criteria, this._caps, info);
let lines;
throw new Error('Expected array for search criteria')
let cmd = which + 'SEARCH', info = { hasUTF8: false /*output*/ }, query = buildSearchQuery(criteria, this._caps, info)
let lines
if (info.hasUTF8) {
cmd += ' CHARSET UTF-8';
lines = query.split(CRLF);
query = lines.shift();
cmd += ' CHARSET UTF-8'
lines = query.split(CRLF)
query = lines.shift()
}
cmd += query;
this._enqueue(cmd, cb);
cmd += query
this._enqueue(cmd, cb)
if (info.hasUTF8) {
const req = this._queue[this._queue.length - 1];
req.lines = lines;
const req = this._queue[this._queue.length - 1]
req.lines = lines
}
}
addFlags(uids, flags, cb) {
this._store('UID ', uids, { mode: '+', flags: flags }, cb);
this._store('UID ', uids, { mode: '+', flags: flags }, cb)
}
delFlags(uids, flags, cb) {
this._store('UID ', uids, { mode: '-', flags: flags }, cb);
this._store('UID ', uids, { mode: '-', flags: flags }, cb)
}
setFlags(uids, flags, cb) {
this._store('UID ', uids, { mode: '', flags: flags }, cb);
this._store('UID ', uids, { mode: '', flags: flags }, cb)
}
addKeywords(uids, keywords, cb) {
this._store('UID ', uids, { mode: '+', keywords: keywords }, cb);
this._store('UID ', uids, { mode: '+', keywords: keywords }, cb)
}
delKeywords(uids, keywords, cb) {
this._store('UID ', uids, { mode: '-', keywords: keywords }, cb);
this._store('UID ', uids, { mode: '-', keywords: keywords }, cb)
}
setKeywords(uids, keywords, cb) {
this._store('UID ', uids, { mode: '', keywords: keywords }, cb);
this._store('UID ', uids, { mode: '', keywords: keywords }, cb)
}
_store(which, uids, cfg, cb) {
const mode = cfg.mode, isFlags = (cfg.flags !== undefined), items = (isFlags ? cfg.flags : cfg.keywords);
const mode = cfg.mode, isFlags = (cfg.flags !== undefined)
let items = (isFlags ? cfg.flags : cfg.keywords)
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
else if (uids === undefined)
throw new Error('No messages specified');
throw new Error('No messages specified')
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0) {
throw new Error('Empty '
+ (which === '' ? 'sequence number' : 'uid')
+ 'list');
+ 'list')
}

@@ -582,9 +581,9 @@ if ((!Array.isArray(items) && typeof items !== 'string')

throw new Error((isFlags ? 'Flags' : 'Keywords')
+ ' argument must be a string or a non-empty Array');
+ ' argument must be a string or a non-empty Array')
if (!Array.isArray(items))
items = [items];
items = [items]
for (let i = 0, len = items.length; i < len; ++i) {
if (isFlags) {
if (items[i][0] !== '\\')
items[i] = '\\' + items[i];
items[i] = '\\' + items[i]
}

@@ -596,50 +595,50 @@ else {

throw new Error('The keyword "' + items[i]
+ '" contains invalid characters');
+ '" contains invalid characters')
}
}
}
items = items.join(' ');
uids = uids.join(',');
let modifiers = '';
items = items.join(' ')
uids = uids.join(',')
let modifiers = ''
if (cfg.modseq !== undefined && !this._box.nomodseq)
modifiers += 'UNCHANGEDSINCE ' + cfg.modseq + ' ';
modifiers += 'UNCHANGEDSINCE ' + cfg.modseq + ' '
this._enqueue(which + 'STORE ' + uids + ' '
+ modifiers
+ mode + 'FLAGS.SILENT (' + items + ')', cb);
+ mode + 'FLAGS.SILENT (' + items + ')', cb)
}
copy(uids, boxTo, cb) {
this._copy('UID ', uids, boxTo, cb);
this._copy('UID ', uids, boxTo, cb)
}
_copy(which, uids, boxTo, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0) {
throw new Error('Empty '
+ (which === '' ? 'sequence number' : 'uid')
+ 'list');
+ 'list')
}
boxTo = escape(utf7.encode('' + boxTo));
this._enqueue(which + 'COPY ' + uids.join(',') + ' "' + boxTo + '"', cb);
boxTo = escape(utf7.encode('' + boxTo))
this._enqueue(which + 'COPY ' + uids.join(',') + ' "' + boxTo + '"', cb)
}
move(uids, boxTo, cb) {
this._move('UID ', uids, boxTo, cb);
this._move('UID ', uids, boxTo, cb)
}
_move(which, uids, boxTo, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
if (this.serverSupports('MOVE')) {
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0) {
throw new Error('Empty '
+ (which === '' ? 'sequence number' : 'uid')
+ 'list');
+ 'list')
}
uids = uids.join(',');
boxTo = escape(utf7.encode('' + boxTo));
this._enqueue(which + 'MOVE ' + uids + ' "' + boxTo + '"', cb);
uids = uids.join(',')
boxTo = escape(utf7.encode('' + boxTo))
this._enqueue(which + 'MOVE ' + uids + ' "' + boxTo + '"', cb)
}

@@ -649,10 +648,11 @@ else if (this._box.permFlags.indexOf('\\Deleted') === -1

throw new Error('Cannot move message: '
+ 'server does not allow deletion of messages');
+ 'server does not allow deletion of messages')
}
else {
let deletedUIDs;
const task = 0, self = this;
let deletedUIDs
let task = 0
const self = this
this._copy(which, uids, boxTo, function ccb(err, info) {
if (err)
return cb(err, info);
return cb(err, info)
if (task === 0 && which && self.serverSupports('UIDPLUS')) {

@@ -662,3 +662,3 @@ // UIDPLUS gives us a 'UID EXPUNGE n' command to expunge a subset of

// actions.
task = 2;
task = 2
}

@@ -669,6 +669,6 @@ // Make sure we don't expunge any messages marked as Deleted except the

self.search(['DELETED'], function (e, result) {
++task;
deletedUIDs = result;
ccb(e, info);
});
++task
deletedUIDs = result
ccb(e, info)
})
}

@@ -678,9 +678,9 @@ else if (task === 1) {

self.delFlags(deletedUIDs, '\\Deleted', function (e) {
++task;
ccb(e, info);
});
++task
ccb(e, info)
})
}
else {
++task;
ccb(err, info);
++task
ccb(err, info)
}

@@ -690,9 +690,9 @@ }

const cbMarkDel = function (e) {
++task;
ccb(e, info);
};
++task
ccb(e, info)
}
if (which)
self.addFlags(uids, '\\Deleted', cbMarkDel);
self.addFlags(uids, '\\Deleted', cbMarkDel)
else
self.seq.addFlags(uids, '\\Deleted', cbMarkDel);
self.seq.addFlags(uids, '\\Deleted', cbMarkDel)
}

@@ -702,10 +702,10 @@ else if (task === 3) {

self.expunge(uids, function (e) {
cb(e, info);
});
cb(e, info)
})
}
else {
self.expunge(function (e) {
++task;
ccb(e, info);
});
++task
ccb(e, info)
})
}

@@ -716,13 +716,13 @@ }

self.addFlags(deletedUIDs, '\\Deleted', function (e) {
cb(e, info);
});
cb(e, info)
})
}
else
cb(err, info);
cb(err, info)
}
});
})
}
}
fetch(uids, options) {
return this._fetch('UID ', uids, options);
return this._fetch('UID ', uids, options)
}

@@ -733,47 +733,47 @@ _fetch(which, uids, options) {

|| (Array.isArray(uids) && uids.length === 0))
throw new Error('Nothing to fetch');
throw new Error('Nothing to fetch')
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0) {
throw new Error('Empty '
+ (which === '' ? 'sequence number' : 'uid')
+ 'list');
+ 'list')
}
uids = uids.join(',');
let cmd = which + 'FETCH ' + uids + ' (';
const fetching = [];
let i, len, key;
uids = uids.join(',')
let cmd = which + 'FETCH ' + uids + ' ('
const fetching = []
let i, len, key
if (this.serverSupports('X-GM-EXT-1')) {
fetching.push('X-GM-THRID');
fetching.push('X-GM-MSGID');
fetching.push('X-GM-LABELS');
fetching.push('X-GM-THRID')
fetching.push('X-GM-MSGID')
fetching.push('X-GM-LABELS')
}
if (this.serverSupports('CONDSTORE') && !this._box.nomodseq)
fetching.push('MODSEQ');
fetching.push('UID');
fetching.push('FLAGS');
fetching.push('INTERNALDATE');
let modifiers;
fetching.push('MODSEQ')
fetching.push('UID')
fetching.push('FLAGS')
fetching.push('INTERNALDATE')
let modifiers
if (options) {
modifiers = options.modifiers;
modifiers = options.modifiers
if (options.envelope)
fetching.push('ENVELOPE');
fetching.push('ENVELOPE')
if (options.struct)
fetching.push('BODYSTRUCTURE');
fetching.push('BODYSTRUCTURE')
if (options.size)
fetching.push('RFC822.SIZE');
fetching.push('RFC822.SIZE')
if (Array.isArray(options.extensions)) {
options.extensions.forEach(function (extension) {
fetching.push(extension.toUpperCase());
});
fetching.push(extension.toUpperCase())
})
}
cmd += fetching.join(' ');
cmd += fetching.join(' ')
if (options.bodies !== undefined) {
let bodies = options.bodies, prefix = (options.markSeen ? '' : '.PEEK');
let bodies = options.bodies, prefix = (options.markSeen ? '' : '.PEEK')
if (!Array.isArray(bodies))
bodies = [bodies];
bodies = [bodies]
for (i = 0, len = bodies.length; i < len; ++i) {
fetching.push(parseExpr('' + bodies[i]));
cmd += ' BODY' + prefix + '[' + bodies[i] + ']';
fetching.push(parseExpr('' + bodies[i]))
cmd += ' BODY' + prefix + '[' + bodies[i] + ']'
}

@@ -783,74 +783,75 @@ }

else
cmd += fetching.join(' ');
cmd += ')';
const modkeys = (typeof modifiers === 'object' ? Object.keys(modifiers) : []), modstr = ' (';
cmd += fetching.join(' ')
cmd += ')'
const modkeys = (typeof modifiers === 'object' ? Object.keys(modifiers) : [])
let modstr = ' ('
for (i = 0, len = modkeys.length, key; i < len; ++i) {
key = modkeys[i].toUpperCase();
key = modkeys[i].toUpperCase()
if (key === 'CHANGEDSINCE' && this.serverSupports('CONDSTORE')
&& !this._box.nomodseq)
modstr += key + ' ' + modifiers[modkeys[i]] + ' ';
modstr += key + ' ' + modifiers[modkeys[i]] + ' '
}
if (modstr.length > 2) {
cmd += modstr.substring(0, modstr.length - 1);
cmd += ')';
cmd += modstr.substring(0, modstr.length - 1)
cmd += ')'
}
this._enqueue(cmd);
const req = this._queue[this._queue.length - 1];
req.fetchCache = {};
req.fetching = fetching;
return (req.bodyEmitter = new EventEmitter());
this._enqueue(cmd)
const req = this._queue[this._queue.length - 1]
req.fetchCache = {}
req.fetching = fetching
return (req.bodyEmitter = new EventEmitter())
}
// Extension methods ===========================================================
setLabels(uids, labels, cb) {
this._storeLabels('UID ', uids, labels, '', cb);
this._storeLabels('UID ', uids, labels, '', cb)
}
addLabels(uids, labels, cb) {
this._storeLabels('UID ', uids, labels, '+', cb);
this._storeLabels('UID ', uids, labels, '+', cb)
}
delLabels(uids, labels, cb) {
this._storeLabels('UID ', uids, labels, '-', cb);
this._storeLabels('UID ', uids, labels, '-', cb)
}
_storeLabels(which, uids, labels, mode, cb) {
if (!this.serverSupports('X-GM-EXT-1'))
throw new Error('Server must support X-GM-EXT-1 capability');
throw new Error('Server must support X-GM-EXT-1 capability')
else if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
else if (uids === undefined)
throw new Error('No messages specified');
throw new Error('No messages specified')
if (!Array.isArray(uids))
uids = [uids];
validateUIDList(uids);
uids = [uids]
validateUIDList(uids)
if (uids.length === 0) {
throw new Error('Empty '
+ (which === '' ? 'sequence number' : 'uid')
+ 'list');
+ 'list')
}
if ((!Array.isArray(labels) && typeof labels !== 'string')
|| (Array.isArray(labels) && labels.length === 0))
throw new Error('labels argument must be a string or a non-empty Array');
throw new Error('labels argument must be a string or a non-empty Array')
if (!Array.isArray(labels))
labels = [labels];
labels = [labels]
labels = labels.map(function (v) {
return '"' + escape(utf7.encode('' + v)) + '"';
}).join(' ');
uids = uids.join(',');
return '"' + escape(utf7.encode('' + v)) + '"'
}).join(' ')
uids = uids.join(',')
this._enqueue(which + 'STORE ' + uids + ' ' + mode
+ 'X-GM-LABELS.SILENT (' + labels + ')', cb);
+ 'X-GM-LABELS.SILENT (' + labels + ')', cb)
}
sort(sorts, criteria, cb) {
this._sort('UID ', sorts, criteria, cb);
this._sort('UID ', sorts, criteria, cb)
}
_sort(which, sorts, criteria, cb) {
let displaySupported = false;
let displaySupported = false
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
else if (!Array.isArray(sorts) || !sorts.length)
throw new Error('Expected array with at least one sort criteria');
throw new Error('Expected array with at least one sort criteria')
else if (!Array.isArray(criteria))
throw new Error('Expected array for search criteria');
throw new Error('Expected array for search criteria')
else if (!this.serverSupports('SORT'))
throw new Error('Sort is not supported on the server');
if(this.serverSupports("SORT=DISPLAY")){
displaySupported = true;
throw new Error('Sort is not supported on the server')
if (this.serverSupports('SORT=DISPLAY')) {
displaySupported = true
}

@@ -861,7 +862,7 @@

throw new Error('Unexpected sort criteria data type. '
+ 'Expected string. Got: ' + typeof criteria);
const modifier = '';
+ 'Expected string. Got: ' + typeof criteria)
let modifier = ''
if (c[0] === '-') {
modifier = 'REVERSE ';
c = c.substring(1);
modifier = 'REVERSE '
c = c.substring(1)
}

@@ -876,55 +877,59 @@ switch (c.toUpperCase()) {

case 'TO':
break;
break
case 'DISPLAYFROM':
case 'DISPLAYTO':
if(!displaySupported){
throw new Error("Unexpected sort criteria: " + c);
if (!displaySupported) {
throw new Error('Unexpected sort criteria: ' + c)
}
break;
break
default:
throw new Error('Unexpected sort criteria: ' + c);
throw new Error('Unexpected sort criteria: ' + c)
}
return modifier + c;
});
sorts = sorts.join(' ');
const info = { hasUTF8: false /*output*/ }, query = buildSearchQuery(criteria, this._caps, info), charset = 'US-ASCII';
let lines;
return modifier + c
})
sorts = sorts.join(' ')
const info = { hasUTF8: false /*output*/ }
let query = buildSearchQuery(criteria, this._caps, info)
let charset = 'US-ASCII'
let lines
if (info.hasUTF8) {
charset = 'UTF-8';
lines = query.split(CRLF);
query = lines.shift();
charset = 'UTF-8'
lines = query.split(CRLF)
query = lines.shift()
}
this._enqueue(which + 'SORT (' + sorts + ') ' + charset + query, cb);
this._enqueue(which + 'SORT (' + sorts + ') ' + charset + query, cb)
if (info.hasUTF8) {
const req = this._queue[this._queue.length - 1];
req.lines = lines;
const req = this._queue[this._queue.length - 1]
req.lines = lines
}
}
esearch(criteria, options, cb) {
this._esearch('UID ', criteria, options, cb);
this._esearch('UID ', criteria, options, cb)
}
_esearch(which, criteria, options, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
throw new Error('No mailbox is currently selected')
else if (!Array.isArray(criteria))
throw new Error('Expected array for search options');
const info = { hasUTF8: false /*output*/ }, query = buildSearchQuery(criteria, this._caps, info), charset = '';
let lines;
throw new Error('Expected array for search options')
const info = { hasUTF8: false /*output*/ }
let query = buildSearchQuery(criteria, this._caps, info)
let charset = ''
let lines
if (info.hasUTF8) {
charset = ' CHARSET UTF-8';
lines = query.split(CRLF);
query = lines.shift();
charset = ' CHARSET UTF-8'
lines = query.split(CRLF)
query = lines.shift()
}
if (typeof options === 'function') {
cb = options;
options = '';
cb = options
options = ''
}
else if (!options)
options = '';
options = ''
if (Array.isArray(options))
options = options.join(' ');
this._enqueue(which + 'SEARCH RETURN (' + options + ')' + charset + query, cb);
options = options.join(' ')
this._enqueue(which + 'SEARCH RETURN (' + options + ')' + charset + query, cb)
if (info.hasUTF8) {
const req = this._queue[this._queue.length - 1];
req.lines = lines;
const req = this._queue[this._queue.length - 1]
req.lines = lines
}

@@ -934,153 +939,155 @@ }

if (typeof limits === 'function') {
cb = limits;
limits = {};
cb = limits
limits = {}
}
const triplets = '';
let triplets = ''
for (let l in limits) {
if (triplets)
triplets += ' ';
triplets += l + ' ' + limits[l];
triplets += ' '
triplets += l + ' ' + limits[l]
}
quotaRoot = escape(utf7.encode('' + quotaRoot));
quotaRoot = escape(utf7.encode('' + quotaRoot))
this._enqueue('SETQUOTA "' + quotaRoot + '" (' + triplets + ')', function (err, quotalist) {
if (err)
return cb(err);
cb(err, quotalist ? quotalist[0] : limits);
});
return cb(err)
cb(err, quotalist ? quotalist[0] : limits)
})
}
getQuota(quotaRoot, cb) {
quotaRoot = escape(utf7.encode('' + quotaRoot));
quotaRoot = escape(utf7.encode('' + quotaRoot))
this._enqueue('GETQUOTA "' + quotaRoot + '"', function (err, quotalist) {
if (err)
return cb(err);
cb(err, quotalist[0]);
});
return cb(err)
cb(err, quotalist[0])
})
}
getQuotaRoot(boxName, cb) {
boxName = escape(utf7.encode('' + boxName));
boxName = escape(utf7.encode('' + boxName))
this._enqueue('GETQUOTAROOT "' + boxName + '"', function (err, quotalist) {
if (err)
return cb(err);
const quotas = {};
return cb(err)
const quotas = {}
if (quotalist) {
for (let i = 0, len = quotalist.length; i < len; ++i)
quotas[quotalist[i].root] = quotalist[i].resources;
quotas[quotalist[i].root] = quotalist[i].resources
}
cb(err, quotas);
});
cb(err, quotas)
})
}
thread(algorithm, criteria, cb) {
this._thread('UID ', algorithm, criteria, cb);
this._thread('UID ', algorithm, criteria, cb)
}
_thread(which, algorithm, criteria, cb) {
algorithm = algorithm.toUpperCase();
algorithm = algorithm.toUpperCase()
if (!this.serverSupports('THREAD=' + algorithm))
throw new Error('Server does not support that threading algorithm');
const info = { hasUTF8: false /*output*/ }, query = buildSearchQuery(criteria, this._caps, info), charset = 'US-ASCII';
let lines;
throw new Error('Server does not support that threading algorithm')
const info = { hasUTF8: false /*output*/ }
let query = buildSearchQuery(criteria, this._caps, info)
let charset = 'US-ASCII'
let lines
if (info.hasUTF8) {
charset = 'UTF-8';
lines = query.split(CRLF);
query = lines.shift();
charset = 'UTF-8'
lines = query.split(CRLF)
query = lines.shift()
}
this._enqueue(which + 'THREAD ' + algorithm + ' ' + charset + query, cb);
this._enqueue(which + 'THREAD ' + algorithm + ' ' + charset + query, cb)
if (info.hasUTF8) {
const req = this._queue[this._queue.length - 1];
req.lines = lines;
const req = this._queue[this._queue.length - 1]
req.lines = lines
}
}
addFlagsSince(uids, flags, modseq, cb) {
this._store('UID ', uids, { mode: '+', flags: flags, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '+', flags: flags, modseq: modseq }, cb)
}
delFlagsSince(uids, flags, modseq, cb) {
this._store('UID ', uids, { mode: '-', flags: flags, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '-', flags: flags, modseq: modseq }, cb)
}
setFlagsSince(uids, flags, modseq, cb) {
this._store('UID ', uids, { mode: '', flags: flags, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '', flags: flags, modseq: modseq }, cb)
}
addKeywordsSince(uids, keywords, modseq, cb) {
this._store('UID ', uids, { mode: '+', keywords: keywords, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '+', keywords: keywords, modseq: modseq }, cb)
}
delKeywordsSince(uids, keywords, modseq, cb) {
this._store('UID ', uids, { mode: '-', keywords: keywords, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '-', keywords: keywords, modseq: modseq }, cb)
}
setKeywordsSince(uids, keywords, modseq, cb) {
this._store('UID ', uids, { mode: '', keywords: keywords, modseq: modseq }, cb);
this._store('UID ', uids, { mode: '', keywords: keywords, modseq: modseq }, cb)
}
_resUntagged(info) {
const type = info.type;
let i, len, box, attrs, key;
const type = info.type
let i, len, box, attrs, key
if (type === 'bye')
this._sock.end();
this._sock.end()
else if (type === 'namespace')
this.namespaces = info.text;
this.namespaces = info.text
else if (type === 'id')
this._curReq.cbargs.push(info.text);
this._curReq.cbargs.push(info.text)
else if (type === 'capability')
this._caps = info.text.map(function (v) { return v.toUpperCase(); });
this._caps = info.text.map(function (v) { return v.toUpperCase() })
else if (type === 'preauth')
this.state = 'authenticated';
this.state = 'authenticated'
else if (type === 'sort' || type === 'thread' || type === 'esearch')
this._curReq.cbargs.push(info.text);
this._curReq.cbargs.push(info.text)
else if (type === 'search') {
if (info.text.results !== undefined) {
// CONDSTORE-modified search results
this._curReq.cbargs.push(info.text.results);
this._curReq.cbargs.push(info.text.modseq);
this._curReq.cbargs.push(info.text.results)
this._curReq.cbargs.push(info.text.modseq)
}
else
this._curReq.cbargs.push(info.text);
this._curReq.cbargs.push(info.text)
}
else if (type === 'quota') {
const cbargs = this._curReq.cbargs;
const cbargs = this._curReq.cbargs
if (!cbargs.length)
cbargs.push([]);
cbargs[0].push(info.text);
cbargs.push([])
cbargs[0].push(info.text)
}
else if (type === 'recent') {
if (!this._box && RE_OPENBOX.test(this._curReq.type))
this._createCurrentBox();
this._createCurrentBox()
if (this._box)
this._box.messages.new = info.num;
this._box.messages.new = info.num
}
else if (type === 'flags') {
if (!this._box && RE_OPENBOX.test(this._curReq.type))
this._createCurrentBox();
this._createCurrentBox()
if (this._box)
this._box.flags = info.text;
this._box.flags = info.text
}
else if (type === 'bad' || type === 'no') {
if (info.textCode
&& info.textCode.key
if (info.textCode
&& info.textCode.key
&& info.textCode.key === 'WEBALERT'
) {
this.emit('webalert', {
url: info.textCode.val,
message: info.text
});
}
) {
this.emit('webalert', {
url: info.textCode.val,
message: info.text
})
}
if (this.state === 'connected' && !this._curReq) {
clearTimeout(this._tmrConn);
clearTimeout(this._tmrAuth);
const err = new Error('Received negative welcome: ' + info.text);
err.source = 'protocol';
this.emit('error', err);
this._sock.end();
clearTimeout(this._tmrConn)
clearTimeout(this._tmrAuth)
const err = new Error('Received negative welcome: ' + info.text)
err.source = 'protocol'
this.emit('error', err)
this._sock.end()
}
}
else if (type === 'exists') {
if (!this._box && RE_OPENBOX.test(this._curReq.type))
this._createCurrentBox();
this._createCurrentBox()
if (this._box) {
let prev = this._box.messages.total, now = info.num;
this._box.messages.total = now;
let prev = this._box.messages.total, now = info.num
this._box.messages.total = now
if (this._box.name === '')
prev = now;
prev = now
if (now > prev && this.state === 'authenticated') {
this._box.messages.new = now - prev;
this.emit('mail', this._box.messages.new);
this._box.messages.new = now - prev
this.emit('mail', this._box.messages.new)
}

@@ -1092,4 +1099,4 @@ }

if (this._box.messages.total > 0)
--this._box.messages.total;
this.emit('expunge', info.num);
--this._box.messages.total
this.emit('expunge', info.num)
}

@@ -1099,6 +1106,6 @@ }

if (this.state === 'connected' && !this._curReq)
this._login();
this._login()
else if (typeof info.textCode === 'string'
&& info.textCode.toUpperCase() === 'ALERT')
this.emit('alert', info.text);
this.emit('alert', info.text)
else if (this._curReq

@@ -1109,41 +1116,41 @@ && info.textCode

if (!this._box)
this._createCurrentBox();
this._createCurrentBox()
if (info.textCode.key)
key = info.textCode.key.toUpperCase();
key = info.textCode.key.toUpperCase()
else
key = info.textCode;
key = info.textCode
if (key === 'UIDVALIDITY')
this._box.uidvalidity = info.textCode.val;
this._box.uidvalidity = info.textCode.val
else if (key === 'UIDNEXT')
this._box.uidnext = info.textCode.val;
this._box.uidnext = info.textCode.val
else if (key === 'HIGHESTMODSEQ')
this._box.highestmodseq = '' + info.textCode.val;
this._box.highestmodseq = '' + info.textCode.val
else if (key === 'PERMANENTFLAGS') {
let idx, permFlags, keywords;
this._box.permFlags = permFlags = info.textCode.val;
let idx, permFlags, keywords
this._box.permFlags = permFlags = info.textCode.val
if ((idx = this._box.permFlags.indexOf('\\*')) > -1) {
this._box.newKeywords = true;
permFlags.splice(idx, 1);
this._box.newKeywords = true
permFlags.splice(idx, 1)
}
this._box.keywords = keywords = permFlags.filter(function (f) {
return (f[0] !== '\\');
});
return (f[0] !== '\\')
})
for (i = 0, len = keywords.length; i < len; ++i)
permFlags.splice(permFlags.indexOf(keywords[i]), 1);
permFlags.splice(permFlags.indexOf(keywords[i]), 1)
}
else if (key === 'UIDNOTSTICKY')
this._box.persistentUIDs = false;
this._box.persistentUIDs = false
else if (key === 'NOMODSEQ')
this._box.nomodseq = true;
this._box.nomodseq = true
}
else if (typeof info.textCode === 'string'
&& info.textCode.toUpperCase() === 'UIDVALIDITY')
this.emit('uidvalidity', info.text);
this.emit('uidvalidity', info.text)
}
else if (type === 'list' || type === 'lsub' || type === 'xlist') {
if (this.delimiter === undefined)
this.delimiter = info.text.delimiter;
this.delimiter = info.text.delimiter
else {
if (this._curReq.cbargs.length === 0)
this._curReq.cbargs.push({});
this._curReq.cbargs.push({})
box = {

@@ -1154,23 +1161,25 @@ attribs: info.text.flags,

parent: null
};
}
for (i = 0, len = SPECIAL_USE_ATTRIBUTES.length; i < len; ++i)
if (box.attribs.indexOf(SPECIAL_USE_ATTRIBUTES[i]) > -1)
box.special_use_attrib = SPECIAL_USE_ATTRIBUTES[i];
const name = info.text.name, curChildren = this._curReq.cbargs[0];
box.special_use_attrib = SPECIAL_USE_ATTRIBUTES[i]
let name = info.text.name
let curChildren = this._curReq.cbargs[0]
if (box.delimiter) {
const path = name.split(box.delimiter), parent = null;
name = path.pop();
const path = name.split(box.delimiter)
let parent = null
name = path.pop()
for (i = 0, len = path.length; i < len; ++i) {
if (!curChildren[path[i]])
curChildren[path[i]] = {};
curChildren[path[i]] = {}
if (!curChildren[path[i]].children)
curChildren[path[i]].children = {};
parent = curChildren[path[i]];
curChildren = curChildren[path[i]].children;
curChildren[path[i]].children = {}
parent = curChildren[path[i]]
curChildren = curChildren[path[i]].children
}
box.parent = parent;
box.parent = parent
}
if (curChildren[name])
box.children = curChildren[name].children;
curChildren[name] = box;
box.children = curChildren[name].children
curChildren[name] = box
}

@@ -1188,19 +1197,19 @@ }

}
};
attrs = info.text.attrs;
}
attrs = info.text.attrs
if (attrs) {
if (attrs.recent !== undefined)
box.messages.new = attrs.recent;
box.messages.new = attrs.recent
if (attrs.unseen !== undefined)
box.messages.unseen = attrs.unseen;
box.messages.unseen = attrs.unseen
if (attrs.messages !== undefined)
box.messages.total = attrs.messages;
box.messages.total = attrs.messages
if (attrs.uidnext !== undefined)
box.uidnext = attrs.uidnext;
box.uidnext = attrs.uidnext
if (attrs.uidvalidity !== undefined)
box.uidvalidity = attrs.uidvalidity;
box.uidvalidity = attrs.uidvalidity
if (attrs.highestmodseq !== undefined) // CONDSTORE
box.highestmodseq = '' + attrs.highestmodseq;
box.highestmodseq = '' + attrs.highestmodseq
}
this._curReq.cbargs.push(box);
this._curReq.cbargs.push(box)
}

@@ -1210,31 +1219,31 @@ else if (type === 'fetch') {

// FETCH response sent as result of FETCH request
const msg = this._curReq.fetchCache[info.num], keys = Object.keys(info.text), keyslen = keys.length;
let toget, msgEmitter, j;
const msg = this._curReq.fetchCache[info.num], keys = Object.keys(info.text), keyslen = keys.length
let toget, msgEmitter, j
if (msg === undefined) {
// simple case -- no bodies were streamed
toget = this._curReq.fetching.slice(0);
toget = this._curReq.fetching.slice(0)
if (toget.length === 0)
return;
msgEmitter = new EventEmitter();
attrs = {};
this._curReq.bodyEmitter.emit('message', msgEmitter, info.num);
return
msgEmitter = new EventEmitter()
attrs = {}
this._curReq.bodyEmitter.emit('message', msgEmitter, info.num)
}
else {
toget = msg.toget;
msgEmitter = msg.msgEmitter;
attrs = msg.attrs;
toget = msg.toget
msgEmitter = msg.msgEmitter
attrs = msg.attrs
}
i = toget.length;
i = toget.length
if (i === 0) {
if (msg && !msg.ended) {
msg.ended = true;
msg.ended = true
process.nextTick(function () {
msgEmitter.emit('end');
});
msgEmitter.emit('end')
})
}
return;
return
}
if (keyslen > 0) {
while (--i >= 0) {
j = keyslen;
j = keyslen
while (--j >= 0) {

@@ -1244,13 +1253,13 @@ if (keys[j].toUpperCase() === toget[i]) {

if (toget[i] === 'X-GM-LABELS') {
const labels = info.text[keys[j]];
const labels = info.text[keys[j]]
for (let k = 0, lenk = labels.length; k < lenk; ++k)
labels[k] = utf7.decode(('' + labels[k]).replace(RE_ESCAPE, '\\'));
labels[k] = utf7.decode(('' + labels[k]).replace(RE_ESCAPE, '\\'))
}
key = FETCH_ATTR_MAP[toget[i]];
key = FETCH_ATTR_MAP[toget[i]]
if (!key)
key = toget[i].toLowerCase();
attrs[key] = info.text[keys[j]];
key = toget[i].toLowerCase()
attrs[key] = info.text[keys[j]]
}
toget.splice(i, 1);
break;
toget.splice(i, 1)
break
}

@@ -1262,7 +1271,7 @@ }

if (msg)
msg.ended = true;
msg.ended = true
process.nextTick(function () {
msgEmitter.emit('attributes', attrs);
msgEmitter.emit('end');
});
msgEmitter.emit('attributes', attrs)
msgEmitter.emit('end')
})
}

@@ -1275,3 +1284,3 @@ else if (msg === undefined) {

ended: false
};
}
}

@@ -1282,3 +1291,3 @@ }

// treat them as the same for now for simplicity
this.emit('update', info.num, info.text);
this.emit('update', info.num, info.text)
}

@@ -1288,17 +1297,17 @@ }

_resTagged(info) {
const req = this._curReq;
let err;
const req = this._curReq
let err
if (!req)
return;
this._curReq = undefined;
return
this._curReq = undefined
if (info.type === 'no' || info.type === 'bad') {
let errtext;
let errtext
if (info.text)
errtext = info.text;
errtext = info.text
else
errtext = req.oauthError;
err = new Error(errtext);
err.type = info.type;
err.textCode = info.textCode;
err.source = 'protocol';
errtext = req.oauthError
err = new Error(errtext)
err.type = info.type
err.textCode = info.textCode
err.source = 'protocol'
}

@@ -1308,3 +1317,3 @@ else if (this._box) {

this._box.readOnly = (typeof info.textCode === 'string'
&& info.textCode.toUpperCase() === 'READ-ONLY');
&& info.textCode.toUpperCase() === 'READ-ONLY')
}

@@ -1315,24 +1324,24 @@ // According to RFC 3501, UID commands do not give errors for

if (RE_UIDCMD_HASRESULTS.test(req.fullcmd) && req.cbargs.length === 0)
req.cbargs.push([]);
req.cbargs.push([])
}
if (req.bodyEmitter) {
const bodyEmitter = req.bodyEmitter;
const bodyEmitter = req.bodyEmitter
if (err) {
bodyEmitter.emit('error', err);
bodyEmitter.emit('error', err)
}
process.nextTick(function () {
bodyEmitter.emit('end');
});
bodyEmitter.emit('end')
})
}
else {
req.cbargs.unshift(err);
req.cbargs.unshift(err)
if (info.textCode && info.textCode.key) {
const key = info.textCode.key.toUpperCase();
const key = info.textCode.key.toUpperCase()
if (key === 'APPENDUID') // [uidvalidity, newUID]
req.cbargs.push(info.textCode.val[1]);
req.cbargs.push(info.textCode.val[1])
else if (key === 'COPYUID') // [uidvalidity, sourceUIDs, destUIDs]
req.cbargs.push(info.textCode.val[2]);
req.cbargs.push(info.textCode.val[2])
}
req.cb && req.cb.apply(this, req.cbargs);
req.cb && req.cb.apply(this, req.cbargs)
}

@@ -1343,6 +1352,6 @@ if (this._queue.length === 0

&& !this._idle.enabled) {
this._idle.enabled = true;
this._doKeepaliveTimer(true);
this._idle.enabled = true
this._doKeepaliveTimer(true)
}
this._processQueue();
this._processQueue()
}

@@ -1365,3 +1374,3 @@ _createCurrentBox() {

}
};
}
}

@@ -1375,46 +1384,46 @@ _doKeepaliveTimer(immediate) {

|| forceNoop)
self._enqueue('NOOP', true);
self._enqueue('NOOP', true)
else {
if (self._idle.started === undefined) {
self._idle.started = 0;
self._enqueue('IDLE', true);
self._idle.started = 0
self._enqueue('IDLE', true)
}
else if (self._idle.started > 0) {
const timeDiff = Date.now() - self._idle.started;
const timeDiff = Date.now() - self._idle.started
if (timeDiff >= idleWait) {
self._idle.enabled = false;
self.debug && self.debug('=> DONE');
self._sock.write('DONE' + CRLF);
return;
self._idle.enabled = false
self.debug && self.debug('=> DONE')
self._sock.write('DONE' + CRLF)
return
}
}
self._tmrKeepalive = setTimeout(timerfn, interval);
self._tmrKeepalive = setTimeout(timerfn, interval)
}
}
};
}
if (immediate)
timerfn();
timerfn()
else
this._tmrKeepalive = setTimeout(timerfn, interval);
this._tmrKeepalive = setTimeout(timerfn, interval)
}
_login() {
const self = this;
let checkedNS = false;
const self = this
let checkedNS = false
const reentry = function (err) {
clearTimeout(self._tmrAuth);
clearTimeout(self._tmrAuth)
if (err) {
self.emit('error', err);
return self._sock.end();
self.emit('error', err)
return self._sock.end()
}
// 2. Get the list of available namespaces (RFC2342)
if (!checkedNS && self.serverSupports('NAMESPACE')) {
checkedNS = true;
return self._enqueue('NAMESPACE', reentry);
checkedNS = true
return self._enqueue('NAMESPACE', reentry)
}
// 3. Get the top-level mailbox hierarchy delimiter used by the server
self._enqueue('LIST "" ""', function () {
self.state = 'authenticated';
self.emit('ready');
});
};
self.state = 'authenticated'
self.emit('ready')
})
}
// 1. Get the supported capabilities

@@ -1424,7 +1433,7 @@ self._enqueue('CAPABILITY', function () {

if (self.state === 'connected') {
let err;
let err
const checkCaps = function (error) {
if (error) {
error.source = 'authentication';
return reentry(error);
error.source = 'authentication'
return reentry(error)
}

@@ -1434,7 +1443,7 @@ if (self._caps === undefined) {

// provided after authentication
return self._enqueue('CAPABILITY', reentry);
return self._enqueue('CAPABILITY', reentry)
}
else
reentry();
};
reentry()
}
if (self.serverSupports('STARTTLS')

@@ -1444,116 +1453,116 @@ && (self._config.autotls === 'always'

&& self.serverSupports('LOGINDISABLED')))) {
self._starttls();
return;
self._starttls()
return
}
if (self.serverSupports('LOGINDISABLED')) {
err = new Error('Logging in is disabled on this server');
err.source = 'authentication';
return reentry(err);
err = new Error('Logging in is disabled on this server')
err.source = 'authentication'
return reentry(err)
}
let cmd;
let cmd
if (self.serverSupports('AUTH=XOAUTH') && self._config.xoauth) {
self._caps = undefined;
cmd = 'AUTHENTICATE XOAUTH';
self._caps = undefined
cmd = 'AUTHENTICATE XOAUTH'
// are there any servers that support XOAUTH/XOAUTH2 and not SASL-IR?
//if (self.serverSupports('SASL-IR'))
cmd += ' ' + escape(self._config.xoauth);
self._enqueue(cmd, checkCaps);
cmd += ' ' + escape(self._config.xoauth)
self._enqueue(cmd, checkCaps)
}
else if (self.serverSupports('AUTH=XOAUTH2') && self._config.xoauth2) {
self._caps = undefined;
cmd = 'AUTHENTICATE XOAUTH2';
self._caps = undefined
cmd = 'AUTHENTICATE XOAUTH2'
//if (self.serverSupports('SASL-IR'))
cmd += ' ' + escape(self._config.xoauth2);
self._enqueue(cmd, checkCaps);
cmd += ' ' + escape(self._config.xoauth2)
self._enqueue(cmd, checkCaps)
}
else if (self._config.user && self._config.password) {
if (self.serverSupports('AUTH=CRAM-MD5')) {
cmd = 'AUTHENTICATE CRAM-MD5';
cmd = 'AUTHENTICATE CRAM-MD5'
} else {
cmd = 'LOGIN "' + escape(self._config.user) + '" "'
+ escape(self._config.password) + '"';
+ escape(self._config.password) + '"'
}
self._caps = undefined;
self._enqueue(cmd, checkCaps);
self._caps = undefined
self._enqueue(cmd, checkCaps)
}
else {
err = new Error('No supported authentication method(s) available. '
+ 'Unable to login.');
err.source = 'authentication';
return reentry(err);
+ 'Unable to login.')
err.source = 'authentication'
return reentry(err)
}
}
else
reentry();
});
reentry()
})
}
_authCRAMMD5(secret) {
let decodedSecret, hmac, response;
decodedSecret = Buffer.from(secret, 'base64').toString('utf8');
let decodedSecret, hmac, response
decodedSecret = Buffer.from(secret, 'base64').toString('utf8')
hmac = crypto.createHmac('md5', this._config.password)
.update(decodedSecret)
.digest('hex');
response = new Buffer(this._config.user + ' ' + hmac).toString('base64');
this.debug && this.debug('=> ' + response);
this._sock.write(response + CRLF, 'utf8');
.update(decodedSecret)
.digest('hex')
response = new Buffer(this._config.user + ' ' + hmac).toString('base64')
this.debug && this.debug('=> ' + response)
this._sock.write(response + CRLF, 'utf8')
}
_starttls() {
const self = this;
const self = this
this._enqueue('STARTTLS', function (err) {
if (err) {
self.emit('error', err);
return self._sock.end();
self.emit('error', err)
return self._sock.end()
}
self._isTsl = true;
self._caps = undefined;
self._sock.removeAllListeners('error');
const tlsOptions = {};
tlsOptions.host = this._config.host;
self._isTsl = true
self._caps = undefined
self._sock.removeAllListeners('error')
const tlsOptions = {}
tlsOptions.host = this._config.host
// Host name may be overridden the tlsOptions
for (let k in this._config.tlsOptions)
tlsOptions[k] = this._config.tlsOptions[k];
tlsOptions.socket = self._sock;
tlsOptions[k] = this._config.tlsOptions[k]
tlsOptions.socket = self._sock
self._sock = tls.connect(tlsOptions, function () {
self._login();
});
self._sock.on('error', self._onError);
self._sock.on('timeout', self._onSocketTimeout);
self._sock.setTimeout(self._config.socketTimeout);
self._parser.setStream(self._sock);
});
self._login()
})
self._sock.on('error', self._onError)
self._sock.on('timeout', self._onSocketTimeout)
self._sock.setTimeout(self._config.socketTimeout)
self._parser.setStream(self._sock)
})
}
_processQueue() {
if (this._curReq || !this._queue.length || !this._sock || !this._sock.writable)
return;
this._curReq = this._queue.shift();
return
this._curReq = this._queue.shift()
if (this._tagcount === MAX_INT)
this._tagcount = 0;
let prefix;
this._tagcount = 0
let prefix
if (this._curReq.type === 'IDLE' || this._curReq.type === 'NOOP')
prefix = this._curReq.type;
prefix = this._curReq.type
else
prefix = 'A' + (this._tagcount++);
const out = prefix + ' ' + this._curReq.fullcmd;
this.debug && this.debug('=> ' + inspect(out));
this._sock.write(out + CRLF, 'utf8');
prefix = 'A' + (this._tagcount++)
const out = prefix + ' ' + this._curReq.fullcmd
this.debug && this.debug('=> ' + inspect(out))
this._sock.write(out + CRLF, 'utf8')
if (this._curReq.literalAppendData) {
// LITERAL+: we are appending a mesage, and not waiting for a reply
this._sockWriteAppendData(this._curReq.literalAppendData);
this._sockWriteAppendData(this._curReq.literalAppendData)
}
}
_sockWriteAppendData(appendData) {
const val = appendData;
let val = appendData
if (Buffer.isBuffer(appendData))
val = val.toString('utf8');
this.debug && this.debug('=> ' + inspect(val));
this._sock.write(val);
this._sock.write(CRLF);
val = val.toString('utf8')
this.debug && this.debug('=> ' + inspect(val))
this._sock.write(val)
this._sock.write(CRLF)
}
_enqueue(fullcmd, promote, cb) {
if (typeof promote === 'function') {
cb = promote;
promote = false;
cb = promote
promote = false
}

@@ -1565,7 +1574,7 @@ const info = {

cbargs: []
}, self = this;
}, self = this
if (promote)
this._queue.unshift(info);
this._queue.unshift(info)
else
this._queue.push(info);
this._queue.push(info)
if (!this._curReq

@@ -1576,3 +1585,3 @@ && this.state !== 'disconnected'

// the request object is needed immediately after enqueueing
process.nextTick(function () { self._processQueue(); });
process.nextTick(function () { self._processQueue() })
}

@@ -1584,8 +1593,8 @@ else if (this._curReq

&& this._idle.enabled) {
this._idle.enabled = false;
clearTimeout(this._tmrKeepalive);
this._idle.enabled = false
clearTimeout(this._tmrKeepalive)
if (this._idle.started > 0) {
// we've seen the continuation for our IDLE
this.debug && this.debug('=> DONE');
this._sock.write('DONE' + CRLF);
this.debug && this.debug('=> DONE')
this._sock.write('DONE' + CRLF)
}

@@ -1652,104 +1661,106 @@ }

// Namespace for seqno-based commands
Object.defineProperty(Connection.prototype, 'seq', { get: function() {
const self = this;
return {
delKeywords: function(seqnos, keywords, cb) {
self._store('', seqnos, { mode: '-', keywords: keywords }, cb);
},
addKeywords: function(seqnos, keywords, cb) {
self._store('', seqnos, { mode: '+', keywords: keywords }, cb);
},
setKeywords: function(seqnos, keywords, cb) {
self._store('', seqnos, { mode: '', keywords: keywords }, cb);
},
Object.defineProperty(Connection.prototype, 'seq', {
get: function () {
const self = this
return {
delKeywords: function (seqnos, keywords, cb) {
self._store('', seqnos, { mode: '-', keywords: keywords }, cb)
},
addKeywords: function (seqnos, keywords, cb) {
self._store('', seqnos, { mode: '+', keywords: keywords }, cb)
},
setKeywords: function (seqnos, keywords, cb) {
self._store('', seqnos, { mode: '', keywords: keywords }, cb)
},
delFlags: function(seqnos, flags, cb) {
self._store('', seqnos, { mode: '-', flags: flags }, cb);
},
addFlags: function(seqnos, flags, cb) {
self._store('', seqnos, { mode: '+', flags: flags }, cb);
},
setFlags: function(seqnos, flags, cb) {
self._store('', seqnos, { mode: '', flags: flags }, cb);
},
delFlags: function (seqnos, flags, cb) {
self._store('', seqnos, { mode: '-', flags: flags }, cb)
},
addFlags: function (seqnos, flags, cb) {
self._store('', seqnos, { mode: '+', flags: flags }, cb)
},
setFlags: function (seqnos, flags, cb) {
self._store('', seqnos, { mode: '', flags: flags }, cb)
},
move: function(seqnos, boxTo, cb) {
self._move('', seqnos, boxTo, cb);
},
copy: function(seqnos, boxTo, cb) {
self._copy('', seqnos, boxTo, cb);
},
fetch: function(seqnos, options) {
return self._fetch('', seqnos, options);
},
search: function(options, cb) {
self._search('', options, cb);
},
move: function (seqnos, boxTo, cb) {
self._move('', seqnos, boxTo, cb)
},
copy: function (seqnos, boxTo, cb) {
self._copy('', seqnos, boxTo, cb)
},
fetch: function (seqnos, options) {
return self._fetch('', seqnos, options)
},
search: function (options, cb) {
self._search('', options, cb)
},
// Extensions ==============================================================
delLabels: function(seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '-', cb);
},
addLabels: function(seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '+', cb);
},
setLabels: function(seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '', cb);
},
// Extensions ==============================================================
delLabels: function (seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '-', cb)
},
addLabels: function (seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '+', cb)
},
setLabels: function (seqnos, labels, cb) {
self._storeLabels('', seqnos, labels, '', cb)
},
esearch: function(criteria, options, cb) {
self._esearch('', criteria, options, cb);
},
esearch: function (criteria, options, cb) {
self._esearch('', criteria, options, cb)
},
sort: function(sorts, options, cb) {
self._sort('', sorts, options, cb);
},
thread: function(algorithm, criteria, cb) {
self._thread('', algorithm, criteria, cb);
},
sort: function (sorts, options, cb) {
self._sort('', sorts, options, cb)
},
thread: function (algorithm, criteria, cb) {
self._thread('', algorithm, criteria, cb)
},
delKeywordsSince: function(seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '-', keywords: keywords, modseq: modseq },
cb);
},
addKeywordsSince: function(seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '+', keywords: keywords, modseq: modseq },
cb);
},
setKeywordsSince: function(seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '', keywords: keywords, modseq: modseq },
cb);
},
delKeywordsSince: function (seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '-', keywords: keywords, modseq: modseq },
cb)
},
addKeywordsSince: function (seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '+', keywords: keywords, modseq: modseq },
cb)
},
setKeywordsSince: function (seqnos, keywords, modseq, cb) {
self._store('',
seqnos,
{ mode: '', keywords: keywords, modseq: modseq },
cb)
},
delFlagsSince: function(seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '-', flags: flags, modseq: modseq },
cb);
},
addFlagsSince: function(seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '+', flags: flags, modseq: modseq },
cb);
},
setFlagsSince: function(seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '', flags: flags, modseq: modseq },
cb);
delFlagsSince: function (seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '-', flags: flags, modseq: modseq },
cb)
},
addFlagsSince: function (seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '+', flags: flags, modseq: modseq },
cb)
},
setFlagsSince: function (seqnos, flags, modseq, cb) {
self._store('',
seqnos,
{ mode: '', flags: flags, modseq: modseq },
cb)
}
}
};
}});
}
})
Connection.parseHeader = parseHeader; // from Parser.js
Connection.parseHeader = parseHeader // from Parser.js
module.exports = Connection;
module.exports = Connection

@@ -1,108 +0,108 @@

'use strict';
'use strict'
const EventEmitter = require('events').EventEmitter,
ReadableStream = require('stream').Readable
|| require('readable-stream').Readable,
// inherits = require('util').inherits,
inspect = require('util').inspect;
ReadableStream = require('stream').Readable
|| require('readable-stream').Readable,
// inherits = require('util').inherits,
inspect = require('util').inspect
const utf7 = require('utf7').imap
let jsencoding; // lazy-loaded
let jsencoding // lazy-loaded
const CH_LF = 10,
LITPLACEHOLDER = String.fromCharCode(0),
EMPTY_READCB = function(n) {},
RE_INTEGER = /^\d+$/,
RE_PRECEDING = /^(?:\* |A\d+ |^\+ ?)/, // Before: /^(?:\* |A\d+ |\+ ?)/,
RE_BODYLITERAL = /BODY\[(.*)\] \{(\d+)\}$/i,
RE_BODYINLINEKEY = /^BODY\[(.*)\]$/i,
RE_SEQNO = /^\* (\d+)/,
RE_LISTCONTENT = /^\((.*)\)$/,
RE_LITERAL = /\{(\d+)\}$/,
RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|ID|LIST|XLIST|LSUB|SEARCH|STATUS|CAPABILITY|NAMESPACE|PREAUTH|SORT|THREAD|ESEARCH|QUOTA|QUOTAROOT)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?:(?: \[([^\]]+)\])?(?: (.+))?)?$/i,
RE_TAGGED = /^A(\d+) (OK|NO|BAD) ?(?:\[([^\]]+)\] )?(.*)$/i,
RE_CONTINUE = /^\+(?: (?:\[([^\]]+)\] )?(.+))?$/i,
RE_CRLF = /\r\n/g,
RE_HDR = /^([^:]+):[ \t]?(.+)?$/,
RE_ENCWORD = /=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=/gi,
RE_ENCWORD_END = /=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=$/i,
RE_ENCWORD_BEGIN = /^[ \t]=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=/i,
RE_QENC = /(?:=([a-fA-F0-9]{2}))|_/g,
RE_SEARCH_MODSEQ = /^(.+) \(MODSEQ (.+?)\)$/i,
RE_LWS_ONLY = /^[ \t]*$/;
LITPLACEHOLDER = String.fromCharCode(0),
EMPTY_READCB = function (n) { },
RE_INTEGER = /^\d+$/,
RE_PRECEDING = /^(?:\* |A\d+ |^\+ ?)/, // Before: /^(?:\* |A\d+ |\+ ?)/,
RE_BODYLITERAL = /BODY\[(.*)\] \{(\d+)\}$/i,
RE_BODYINLINEKEY = /^BODY\[(.*)\]$/i,
RE_SEQNO = /^\* (\d+)/,
RE_LISTCONTENT = /^\((.*)\)$/,
RE_LITERAL = /\{(\d+)\}$/,
RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|ID|LIST|XLIST|LSUB|SEARCH|STATUS|CAPABILITY|NAMESPACE|PREAUTH|SORT|THREAD|ESEARCH|QUOTA|QUOTAROOT)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?:(?: \[([^\]]+)\])?(?: (.+))?)?$/i,
RE_TAGGED = /^A(\d+) (OK|NO|BAD) ?(?:\[([^\]]+)\] )?(.*)$/i,
RE_CONTINUE = /^\+(?: (?:\[([^\]]+)\] )?(.+))?$/i,
RE_CRLF = /\r\n/g,
RE_HDR = /^([^:]+):[ \t]?(.+)?$/,
RE_ENCWORD = /=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=/gi,
RE_ENCWORD_END = /=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=$/i,
RE_ENCWORD_BEGIN = /^[ \t]=\?([^?*]*?)(?:\*.*?)?\?([qb])\?(.*?)\?=/i,
RE_QENC = /(?:=([a-fA-F0-9]{2}))|_/g,
RE_SEARCH_MODSEQ = /^(.+) \(MODSEQ (.+?)\)$/i,
RE_LWS_ONLY = /^[ \t]*$/
class Parser extends EventEmitter {
constructor(stream, debug) {
super();
super()
if (!(this instanceof Parser))
return new Parser(stream, debug);
return new Parser(stream, debug)
// EventEmitter.call(this);
this._stream = undefined;
this._body = undefined;
this._literallen = 0;
this._literals = [];
this._buffer = '';
this._ignoreReadable = false;
this.debug = debug;
const self = this;
this._stream = undefined
this._body = undefined
this._literallen = 0
this._literals = []
this._buffer = ''
this._ignoreReadable = false
this.debug = debug
const self = this
this._cbReadable = function () {
if (self._ignoreReadable)
return;
return
if (self._literallen > 0 && !self._body)
self._tryread(self._literallen);
self._tryread(self._literallen)
else
self._tryread();
};
this.setStream(stream);
process.nextTick(this._cbReadable);
self._tryread()
}
this.setStream(stream)
process.nextTick(this._cbReadable)
}
setStream(stream) {
if (this._stream)
this._stream.removeListener('readable', this._cbReadable);
this._stream.removeListener('readable', this._cbReadable)
if (/^v0\.8\./.test(process.version)) {
this._stream = (new ReadableStream()).wrap(stream);
this._stream = (new ReadableStream()).wrap(stream)
// since Readable.wrap() proxies events, we need to remove at least the
// proxied 'error' event since this can cause problems and Parser doesn't
// care about such events
stream._events.error.pop();
stream._events.error.pop()
}
else
this._stream = stream;
this._stream.on('readable', this._cbReadable);
this._stream = stream
this._stream.on('readable', this._cbReadable)
}
_tryread(n) {
if (this._stream.readable) {
const r = this._stream.read(n);
r && this._parse(r);
const r = this._stream.read(n)
r && this._parse(r)
}
}
_parse(data) {
let i = 0;
let idxlf;
const datalen = data.length;
let i = 0
let idxlf
const datalen = data.length
if (this._literallen > 0) {
if (this._body) {
const body = this._body;
const body = this._body
if (datalen >= this._literallen) {
const litlen = this._literallen;
i = litlen;
this._literallen = 0;
this._body = undefined;
body._read = EMPTY_READCB;
const litlen = this._literallen
i = litlen
this._literallen = 0
this._body = undefined
body._read = EMPTY_READCB
if (datalen > litlen)
body.push(data.slice(0, litlen));
body.push(data.slice(0, litlen))
else
body.push(data);
body.push(null);
body.push(data)
body.push(null)
}
else {
this._literallen -= datalen;
const r = body.push(data);
this._literallen -= datalen
const r = body.push(data)
if (!r) {
body._read = this._cbReadable;
return;
body._read = this._cbReadable
return
}
i = datalen;
i = datalen
}

@@ -112,37 +112,37 @@ }

if (datalen > this._literallen)
this._literals.push(data.slice(0, this._literallen));
this._literals.push(data.slice(0, this._literallen))
else
this._literals.push(data);
i = this._literallen;
this._literallen = 0;
this._literals.push(data)
i = this._literallen
this._literallen = 0
}
}
while (i < datalen) {
idxlf = indexOfCh(data, datalen, i, CH_LF);
idxlf = indexOfCh(data, datalen, i, CH_LF)
if (idxlf === -1) {
this._buffer += data.toString('utf8', i);
break;
this._buffer += data.toString('utf8', i)
break
}
else {
this._buffer += data.toString('utf8', i, idxlf);
this._buffer = this._buffer.trim();
i = idxlf + 1;
this.debug && this.debug('<= ' + inspect(this._buffer));
this._buffer += data.toString('utf8', i, idxlf)
this._buffer = this._buffer.trim()
i = idxlf + 1
this.debug && this.debug('<= ' + inspect(this._buffer))
if (RE_PRECEDING.test(this._buffer)) {
const firstChar = this._buffer[0];
const firstChar = this._buffer[0]
if (firstChar === '*')
this._resUntagged();
this._resUntagged()
else if (firstChar === 'A')
this._resTagged();
this._resTagged()
else if (firstChar === '+')
this._resContinue();
this._resContinue()
if (this._literallen > 0 && i < datalen) {
this._ignoreReadable = true;
this._ignoreReadable = true
// literal data included in this chunk -- put it back onto stream
this._stream.unshift(data.slice(i));
this._ignoreReadable = false;
i = datalen;
this._stream.unshift(data.slice(i))
this._ignoreReadable = false
i = datalen
if (!this._body) {
// check if unshifted contents satisfies non-body literal length
this._tryread(this._literallen);
this._tryread(this._literallen)
}

@@ -152,4 +152,4 @@ }

else {
this.emit('other', this._buffer);
this._buffer = '';
this.emit('other', this._buffer)
this._buffer = ''
}

@@ -159,14 +159,14 @@ }

if (this._literallen === 0 || this._body)
this._tryread();
this._tryread()
}
_resTagged() {
let m;
let m
if (m = RE_LITERAL.exec(this._buffer)) {
// non-BODY literal -- buffer it
this._buffer = this._buffer.replace(RE_LITERAL, LITPLACEHOLDER);
this._literallen = parseInt(m[1], 10);
this._buffer = this._buffer.replace(RE_LITERAL, LITPLACEHOLDER)
this._literallen = parseInt(m[1], 10)
}
else if (m = RE_TAGGED.exec(this._buffer)) {
this._buffer = '';
this._literals = [];
this._buffer = ''
this._literals = []
this.emit('tagged', {

@@ -177,20 +177,20 @@ type: m[2].toLowerCase(),

text: m[4]
});
})
}
else
this._buffer = '';
this._buffer = ''
}
_resUntagged() {
// console.log('_resUntagged')
let m;
let m
if (m = RE_BODYLITERAL.exec(this._buffer)) {
// console.log('RE_BODYLITERAL', this._buffer);
// BODY literal -- stream it
const which = m[1], size = parseInt(m[2], 10);
this._literallen = size;
this._body = new ReadableStream();
this._body._readableState.sync = false;
this._body._read = EMPTY_READCB;
m = RE_SEQNO.exec(this._buffer);
this._buffer = this._buffer.replace(RE_BODYLITERAL, '');
const which = m[1], size = parseInt(m[2], 10)
this._literallen = size
this._body = new ReadableStream()
this._body._readableState.sync = false
this._body._read = EMPTY_READCB
m = RE_SEQNO.exec(this._buffer)
this._buffer = this._buffer.replace(RE_BODYLITERAL, '')
this.emit('body', this._body, {

@@ -200,3 +200,3 @@ seqno: parseInt(m[1], 10),

size: size
});
})
}

@@ -206,8 +206,8 @@ else if (m = RE_LITERAL.exec(this._buffer)) {

// non-BODY literal -- buffer it
this._buffer = this._buffer.replace(RE_LITERAL, LITPLACEHOLDER);
this._literallen = parseInt(m[1], 10);
this._buffer = this._buffer.replace(RE_LITERAL, LITPLACEHOLDER)
this._literallen = parseInt(m[1], 10)
}
else if (m = RE_UNTAGGED.exec(this._buffer)) {
// console.log('RE_UNTAGGED', this._buffer);
this._buffer = '';
this._buffer = ''
// normal single line response

@@ -218,8 +218,8 @@ // m[1] or m[3] = response type

// m[5] = response text (optional)
let type, num, textCode, val;
let type, num, textCode, val
if (m[2] !== undefined)
num = parseInt(m[2], 10);
num = parseInt(m[2], 10)
if (m[4] !== undefined)
textCode = parseTextCode(m[4], this._literals);
type = (m[1] || m[3]).toLowerCase();
textCode = parseTextCode(m[4], this._literals)
type = (m[1] || m[3]).toLowerCase()
// console.log('Untagged Type: ', type);

@@ -233,19 +233,19 @@ if (type === 'flags'

// CONDSTORE search response
const p = RE_SEARCH_MODSEQ.exec(m[5]);
const p = RE_SEARCH_MODSEQ.exec(m[5])
val = {
results: p[1].split(' '),
modseq: p[2]
};
}
}
else {
if (m[5][0] === '(')
val = RE_LISTCONTENT.exec(m[5])[1].split(' ');
val = RE_LISTCONTENT.exec(m[5])[1].split(' ')
else
val = m[5].split(' ');
val = m[5].split(' ')
if (type === 'search' || type === 'sort')
val = val.map(function (v) { return parseInt(v, 10); });
val = val.map(function (v) { return parseInt(v, 10) })
}
}
else
val = [];
val = []
}

@@ -255,27 +255,27 @@ else if (type === 'thread') {

if (m[5])
val = parseExpr(m[5], this._literals);
val = parseExpr(m[5], this._literals)
else
val = [];
val = []
}
else if (type === 'list' || type === 'lsub' || type === 'xlist')
val = parseBoxList(m[5], this._literals);
val = parseBoxList(m[5], this._literals)
else if (type === 'id')
val = parseId(m[5], this._literals);
val = parseId(m[5], this._literals)
else if (type === 'status')
val = parseStatus(m[5], this._literals);
val = parseStatus(m[5], this._literals)
else if (type === 'fetch')
val = parseFetch.call(this, m[5], this._literals, num);
val = parseFetch.call(this, m[5], this._literals, num)
else if (type === 'namespace')
val = parseNamespaces(m[5], this._literals);
val = parseNamespaces(m[5], this._literals)
else if (type === 'esearch')
val = parseESearch(m[5], this._literals);
val = parseESearch(m[5], this._literals)
else if (type === 'quota')
val = parseQuota(m[5], this._literals);
val = parseQuota(m[5], this._literals)
else if (type === 'quotaroot')
val = parseQuotaRoot(m[5], this._literals);
val = parseQuotaRoot(m[5], this._literals)
else if (type === 'no') {
val = m[5]
} else
val = m[5];
this._literals = [];
val = m[5]
this._literals = []
this.emit('untagged', {

@@ -286,20 +286,20 @@ type: type,

text: val
});
})
}
else
this._buffer = '';
this._buffer = ''
}
_resContinue() {
const m = RE_CONTINUE.exec(this._buffer);
let textCode, text;
this._buffer = '';
const m = RE_CONTINUE.exec(this._buffer)
let textCode, text
this._buffer = ''
if (!m)
return;
text = m[2];
return
text = m[2]
if (m[1] !== undefined)
textCode = parseTextCode(m[1], this._literals);
textCode = parseTextCode(m[1], this._literals)
this.emit('continue', {
textCode: textCode,
text: text
});
})
}

@@ -316,18 +316,18 @@ }

function indexOfCh(buffer, len, i, ch) {
let r = -1;
let r = -1
for (; i < len; ++i) {
if (buffer[i] === ch) {
r = i;
break;
r = i
break
}
}
return r;
return r
}
function parseTextCode(text, literals) {
const r = parseExpr(text, literals);
const r = parseExpr(text, literals)
if (r.length === 1)
return r[0];
return r[0]
else
return { key: r[0], val: r.length === 2 ? r[1] : r.slice(1) };
return { key: r[0], val: r.length === 2 ? r[1] : r.slice(1) }
}

@@ -337,3 +337,3 @@

const r = parseExpr(text.toUpperCase().replace('UID', ''), literals),
attrs = {};
attrs = {}

@@ -345,10 +345,10 @@ // RFC4731 unfortunately is lacking on documentation, so we're going to

for (let i = 1, len = r.length, key, val; i < len; i += 2) {
key = r[i].toLowerCase();
val = r[i + 1];
key = r[i].toLowerCase()
val = r[i + 1]
if (key === 'all')
val = val.toString().split(',');
attrs[key] = val;
val = val.toString().split(',')
attrs[key] = val
}
return attrs;
return attrs
}

@@ -358,9 +358,9 @@

const r = parseExpr(text, literals),
id = {};
id = {}
if (r[0] === null)
return null;
return null
for (let i = 0, len = r[0].length; i < len; i += 2)
id[r[0][i].toLowerCase()] = r[0][i + 1];
id[r[0][i].toLowerCase()] = r[0][i + 1]
return id;
return id
}

@@ -370,3 +370,3 @@

const r = parseExpr(text, literals),
resources = {};
resources = {}

@@ -377,3 +377,3 @@ for (let i = 0, len = r[1].length; i < len; i += 3) {

limit: r[1][i + 2]
};
}
}

@@ -384,7 +384,7 @@

resources: resources
};
}
}
function parseQuotaRoot(text, literals) {
const r = parseExpr(text, literals);
const r = parseExpr(text, literals)

@@ -394,23 +394,23 @@ return {

mailbox: r[0]
};
}
}
function parseBoxList(text, literals) {
const r = parseExpr(text, literals);
const r = parseExpr(text, literals)
return {
flags: r[0],
delimiter: r[1],
name: utf7.decode(''+r[2])
};
name: utf7.decode('' + r[2])
}
}
function parseNamespaces(text, literals) {
const r = parseExpr(text, literals);
let i, len, j, len2, ns, nsobj, namespaces, n;
const r = parseExpr(text, literals)
let i, len, j, len2, ns, nsobj, namespaces, n
for (n = 0; n < 3; ++n) {
if (r[n]) {
namespaces = [];
namespaces = []
for (i = 0, len = r[n].length; i < len; ++i) {
ns = r[n][i];
ns = r[n][i]
nsobj = {

@@ -420,10 +420,10 @@ prefix: ns[0],

extensions: undefined
};
}
if (ns.length > 2)
nsobj.extensions = {};
nsobj.extensions = {}
for (j = 2, len2 = ns.length; j < len2; j += 2)
nsobj.extensions[ns[j]] = ns[j + 1];
namespaces.push(nsobj);
nsobj.extensions[ns[j]] = ns[j + 1]
namespaces.push(nsobj)
}
r[n] = namespaces;
r[n] = namespaces
}

@@ -436,37 +436,37 @@ }

shared: r[2]
};
}
}
function parseStatus(text, literals) {
const r = parseExpr(text, literals), attrs = {};
const r = parseExpr(text, literals), attrs = {}
// r[1] is [KEY1, VAL1, KEY2, VAL2, .... KEYn, VALn]
for (let i = 0, len = r[1].length; i < len; i += 2)
attrs[r[1][i].toLowerCase()] = r[1][i + 1];
attrs[r[1][i].toLowerCase()] = r[1][i + 1]
return {
name: utf7.decode(''+r[0]),
name: utf7.decode('' + r[0]),
attrs: attrs
};
}
}
function parseFetch(text, literals, seqno) {
const list = parseExpr(text, literals)[0], attrs = {};
let m, body;
const list = parseExpr(text, literals)[0], attrs = {}
let m, body
// list is [KEY1, VAL1, KEY2, VAL2, .... KEYn, VALn]
for (let i = 0, len = list.length, key, val; i < len; i += 2) {
key = list[i].toLowerCase();
val = list[i + 1];
key = list[i].toLowerCase()
val = list[i + 1]
if (key === 'envelope')
val = parseFetchEnvelope(val);
val = parseFetchEnvelope(val)
else if (key === 'internaldate')
val = new Date(val);
val = new Date(val)
else if (key === 'modseq') // always a list of one value
val = ''+val[0];
val = '' + val[0]
else if (key === 'body' || key === 'bodystructure')
val = parseBodyStructure(val);
val = parseBodyStructure(val)
else if (m = RE_BODYINLINEKEY.exec(list[i])) {
// a body was sent as a non-literal
val = Buffer.from(val); //new Buffer(''+val);
body = new ReadableStream();
body._readableState.sync = false;
body._read = EMPTY_READCB;
val = Buffer.from(val) //new Buffer(''+val);
body = new ReadableStream()
body._readableState.sync = false
body._read = EMPTY_READCB
this.emit('body', body, {

@@ -476,41 +476,41 @@ seqno: seqno,

size: val.length
});
body.push(val);
body.push(null);
continue;
})
body.push(val)
body.push(null)
continue
}
attrs[key] = val;
attrs[key] = val
}
return attrs;
return attrs
}
function parseBodyStructure(cur, literals, prefix, partID) {
let ret = [];
let i, len;
let ret = []
let i, len
if (prefix === undefined) {
const result = (Array.isArray(cur) ? cur : parseExpr(cur, literals));
const result = (Array.isArray(cur) ? cur : parseExpr(cur, literals))
if (result.length)
ret = parseBodyStructure(result, literals, '', 1);
ret = parseBodyStructure(result, literals, '', 1)
} else {
let part, partLen = cur.length, next;
let part, partLen = cur.length, next
if (Array.isArray(cur[0])) { // multipart
next = -1;
next = -1
while (Array.isArray(cur[++next])) {
ret.push(parseBodyStructure(cur[next],
literals,
prefix + (prefix !== '' ? '.' : '')
+ (partID++).toString(), 1));
literals,
prefix + (prefix !== '' ? '.' : '')
+ (partID++).toString(), 1))
}
part = { type: cur[next++].toLowerCase() };
part = { type: cur[next++].toLowerCase() }
if (partLen > next) {
if (Array.isArray(cur[next])) {
part.params = {};
part.params = {}
for (i = 0, len = cur[next].length; i < len; i += 2)
part.params[cur[next][i].toLowerCase()] = decodeWords(cur[next][i + 1]);
part.params[cur[next][i].toLowerCase()] = decodeWords(cur[next][i + 1])
} else
part.params = cur[next];
++next;
part.params = cur[next]
++next
}
} else { // single part
next = 7;
next = 7
if (typeof cur[1] === 'string') {

@@ -526,16 +526,16 @@ part = {

size: cur[6]
};
}
} else {
// type information for malformed multipart body
part = { type: cur[0] ? cur[0].toLowerCase() : null, params: null };
cur.splice(1, 0, null);
++partLen;
next = 2;
part = { type: cur[0] ? cur[0].toLowerCase() : null, params: null }
cur.splice(1, 0, null)
++partLen
next = 2
}
if (Array.isArray(cur[2])) {
part.params = {};
part.params = {}
for (i = 0, len = cur[2].length; i < len; i += 2)
part.params[cur[2][i].toLowerCase()] = decodeWords(cur[2][i + 1]);
part.params[cur[2][i].toLowerCase()] = decodeWords(cur[2][i + 1])
if (cur[1] === null)
++next;
++next
}

@@ -545,26 +545,26 @@ if (part.type === 'message' && part.subtype === 'rfc822') {

if (partLen > next && Array.isArray(cur[next]))
part.envelope = parseFetchEnvelope(cur[next]);
part.envelope = parseFetchEnvelope(cur[next])
else
part.envelope = null;
++next;
part.envelope = null
++next
// body
if (partLen > next && Array.isArray(cur[next]))
part.body = parseBodyStructure(cur[next], literals, prefix, 1);
part.body = parseBodyStructure(cur[next], literals, prefix, 1)
else
part.body = null;
++next;
part.body = null
++next
}
if ((part.type === 'text'
|| (part.type === 'message' && part.subtype === 'rfc822'))
&& partLen > next)
part.lines = cur[next++];
|| (part.type === 'message' && part.subtype === 'rfc822'))
&& partLen > next)
part.lines = cur[next++]
if (typeof cur[1] === 'string' && partLen > next)
part.md5 = cur[next++];
part.md5 = cur[next++]
}
// add any extra fields that may or may not be omitted entirely
parseStructExtra(part, partLen, cur, next);
ret.unshift(part);
parseStructExtra(part, partLen, cur, next)
ret.unshift(part)
}
return ret;
return ret
}

@@ -579,21 +579,21 @@

// ['Foo', ['Bar', 'Baz', 'Bam', 'Pow']]
const disposition = { type: null, params: null };
const disposition = { type: null, params: null }
if (Array.isArray(cur[next])) {
disposition.type = cur[next][0];
disposition.type = cur[next][0]
if (Array.isArray(cur[next][1])) {
disposition.params = {};
disposition.params = {}
for (let i = 0, len = cur[next][1].length, key; i < len; i += 2) {
key = cur[next][1][i].toLowerCase();
disposition.params[key] = decodeWords(cur[next][1][i + 1]);
key = cur[next][1][i].toLowerCase()
disposition.params[key] = decodeWords(cur[next][1][i + 1])
}
}
} else if (cur[next] !== null)
disposition.type = cur[next];
disposition.type = cur[next]
if (disposition.type === null)
part.disposition = null;
part.disposition = null
else
part.disposition = disposition;
part.disposition = disposition
++next;
++next
}

@@ -604,9 +604,9 @@ if (partLen > next) {

if (cur[next] !== null)
part.language = (Array.isArray(cur[next]) ? cur[next] : [cur[next]]);
part.language = (Array.isArray(cur[next]) ? cur[next] : [cur[next]])
else
part.language = null;
++next;
part.language = null
++next
}
if (partLen > next)
part.location = cur[next++];
part.location = cur[next++]
if (partLen > next) {

@@ -616,3 +616,3 @@ // extension stuff introduced by later RFCs

// let's not parse it for now ...
part.extensions = cur[next];
part.extensions = cur[next]
}

@@ -633,25 +633,25 @@ }

messageId: list[9]
};
}
}
function parseEnvelopeAddresses(list) {
let addresses = null;
let addresses = null
if (Array.isArray(list)) {
addresses = [];
let inGroup = false;
let curGroup;
addresses = []
let inGroup = false
let curGroup
for (let i = 0, len = list.length, addr; i < len; ++i) {
addr = list[i];
addr = list[i]
if (addr[2] === null) { // end of group addresses
inGroup = false;
inGroup = false
if (curGroup) {
addresses.push(curGroup);
curGroup = undefined;
addresses.push(curGroup)
curGroup = undefined
}
} else if (addr[3] === null) { // start of group addresses
inGroup = true;
inGroup = true
curGroup = {
group: addr[2],
addresses: []
};
}
} else { // regular user address

@@ -662,34 +662,34 @@ const info = {

host: addr[3]
};
}
if (inGroup)
curGroup.addresses.push(info);
curGroup.addresses.push(info)
else if (!inGroup)
addresses.push(info);
addresses.push(info)
}
list[i] = addr;
list[i] = addr
}
if (inGroup) {
// no end of group found, assume implicit end
addresses.push(curGroup);
addresses.push(curGroup)
}
}
return addresses;
return addresses
}
function parseExpr(o, literals, result, start, useBrackets) {
start = start || 0;
start = start || 0
let inQuote = false,
lastPos = start - 1,
isTop = false,
isBody = false,
escaping = false,
val;
lastPos = start - 1,
isTop = false,
isBody = false,
escaping = false,
val
if (useBrackets === undefined)
useBrackets = true;
useBrackets = true
if (!result)
result = [];
result = []
if (typeof o === 'string') {
o = { str: o };
isTop = true;
o = { str: o }
isTop = true
}

@@ -700,43 +700,43 @@ for (let i = start, len = o.str.length; i < len; ++i) {

if (o.str[i] === ']') {
val = convStr(o.str.substring(lastPos + 1, i + 1), literals);
result.push(val);
lastPos = i;
isBody = false;
val = convStr(o.str.substring(lastPos + 1, i + 1), literals)
result.push(val)
lastPos = i
isBody = false
}
} else if (o.str[i] === '"')
inQuote = true;
inQuote = true
else if (o.str[i] === ' '
|| o.str[i] === ')'
|| (useBrackets && o.str[i] === ']')) {
|| o.str[i] === ')'
|| (useBrackets && o.str[i] === ']')) {
if (i - (lastPos + 1) > 0) {
val = convStr(o.str.substring(lastPos + 1, i), literals);
result.push(val);
val = convStr(o.str.substring(lastPos + 1, i), literals)
result.push(val)
}
if ((o.str[i] === ')' || (useBrackets && o.str[i] === ']')) && !isTop)
return i;
lastPos = i;
return i
lastPos = i
} else if ((o.str[i] === '(' || (useBrackets && o.str[i] === '['))) {
if (o.str[i] === '['
&& i - 4 >= start
&& o.str.substring(i - 4, i).toUpperCase() === 'BODY') {
isBody = true;
lastPos = i - 5;
&& i - 4 >= start
&& o.str.substring(i - 4, i).toUpperCase() === 'BODY') {
isBody = true
lastPos = i - 5
} else {
const innerResult = [];
i = parseExpr(o, literals, innerResult, i + 1, useBrackets);
lastPos = i;
result.push(innerResult);
const innerResult = []
i = parseExpr(o, literals, innerResult, i + 1, useBrackets)
lastPos = i
result.push(innerResult)
}
}
} else if (o.str[i] === '\\')
escaping = !escaping;
escaping = !escaping
else if (o.str[i] === '"') {
if (!escaping)
inQuote = false;
escaping = false;
inQuote = false
escaping = false
}
if (i + 1 === len && len - (lastPos + 1) > 0)
result.push(convStr(o.str.substring(lastPos + 1), literals));
result.push(convStr(o.str.substring(lastPos + 1), literals))
}
return (isTop ? result : start);
return (isTop ? result : start)
}

@@ -746,20 +746,20 @@

if (str[0] === '"') {
str = str.substring(1, str.length - 1);
let newstr = '', isEscaping = false, p = 0;
str = str.substring(1, str.length - 1)
let newstr = '', isEscaping = false, p = 0
for (let i = 0, len = str.length; i < len; ++i) {
if (str[i] === '\\') {
if (!isEscaping)
isEscaping = true;
isEscaping = true
else {
isEscaping = false;
newstr += str.substring(p, i - 1);
p = i;
isEscaping = false
newstr += str.substring(p, i - 1)
p = i
}
} else if (str[i] === '"') {
if (isEscaping) {
isEscaping = false;
newstr += str.substring(p, i - 1);
p = i;
isEscaping = false
newstr += str.substring(p, i - 1)
p = i
}

@@ -769,29 +769,29 @@ }

if (p === 0)
return str;
return str
else {
newstr += str.substring(p);
return newstr;
newstr += str.substring(p)
return newstr
}
} else if (str === 'NIL')
return null;
return null
else if (RE_INTEGER.test(str)) {
// some IMAP extensions utilize large (64-bit) integers, which JavaScript
// can't handle natively, so we'll just keep it as a string if it's too big
const val = parseInt(str, 10);
return (val.toString() === str ? val : str);
const val = parseInt(str, 10)
return (val.toString() === str ? val : str)
} else if (literals && literals.length && str === LITPLACEHOLDER) {
let l = literals.shift();
let l = literals.shift()
if (Buffer.isBuffer(l))
l = l.toString('utf8');
return l;
l = l.toString('utf8')
return l
}
return str;
return str
}
function repeat(chr, len) {
let s = '';
let s = ''
for (let i = 0; i < len; ++i)
s += chr;
return s;
s += chr
return s
}

@@ -801,3 +801,3 @@

if (!jsencoding)
jsencoding = require('../deps/encoding/encoding');
jsencoding = require('../deps/encoding/encoding')
if (jsencoding.encodingExists(encoding)) {

@@ -808,6 +808,6 @@ if (state.buffer !== undefined) {

// something that's decodable
const newbuf = Buffer.alloc(state.buffer.length + buf.length); //new Buffer(state.buffer.length + buf.length);
state.buffer.copy(newbuf, 0);
buf.copy(newbuf, state.buffer.length);
buf = newbuf;
const newbuf = Buffer.alloc(state.buffer.length + buf.length) //new Buffer(state.buffer.length + buf.length);
state.buffer.copy(newbuf, 0)
buf.copy(newbuf, state.buffer.length)
buf = newbuf
} else {

@@ -819,17 +819,17 @@ // either:

// use different encodings
state.buffer = state.encoding = undefined;
state.curReplace = undefined;
state.buffer = state.encoding = undefined
state.curReplace = undefined
}
}
let ret, isPartial = false;
let ret, isPartial = false
if (state.remainder !== undefined) {
// use cached remainder from the previous lookahead
ret = state.remainder;
state.remainder = undefined;
ret = state.remainder
state.remainder = undefined
} else {
try {
ret = jsencoding.TextDecoder(encoding).decode(buf);
ret = jsencoding.TextDecoder(encoding).decode(buf)
} catch (e) {
if (e.message.indexOf('Seeking') === 0)
isPartial = true;
isPartial = true
}

@@ -841,8 +841,8 @@ }

// if not, the current buffer is partial
let lookahead, lookaheadBuf = Buffer.alloc(buf.length + nextBuf.length); //new Buffer(buf.length + nextBuf.length);
buf.copy(lookaheadBuf);
nextBuf.copy(lookaheadBuf, buf.length);
let lookahead, lookaheadBuf = Buffer.alloc(buf.length + nextBuf.length) //new Buffer(buf.length + nextBuf.length);
buf.copy(lookaheadBuf)
nextBuf.copy(lookaheadBuf, buf.length)
try {
lookahead = jsencoding.TextDecoder(encoding).decode(lookaheadBuf);
} catch(e) {
lookahead = jsencoding.TextDecoder(encoding).decode(lookaheadBuf)
} catch (e) {
// cannot decode the lookahead, do nothing

@@ -853,6 +853,6 @@ }

// the current buffer is whole, cache the lookahead's remainder
state.remainder = lookahead.substring(ret.length);
state.remainder = lookahead.substring(ret.length)
} else {
isPartial = true;
ret = undefined;
isPartial = true
ret = undefined
}

@@ -870,5 +870,5 @@ }

val: ret
});
state.replaces.splice(state.replaces.indexOf(state.curReplace), 1);
state.curReplace = undefined;
})
state.replaces.splice(state.replaces.indexOf(state.curReplace), 1)
state.curReplace = undefined
} else {

@@ -882,6 +882,6 @@ // normal case where there are no previous partials and we successfully

val: ret
});
})
}
state.buffer = state.encoding = undefined;
return;
state.buffer = state.encoding = undefined
return
} else if (isPartial) {

@@ -893,6 +893,6 @@ // RFC2047 says that each decoded encoded word "MUST represent an integral

// we can successfully decode or we see a change in encoding
state.encoding = encoding;
state.buffer = buf;
state.encoding = encoding
state.buffer = buf
if (!state.curReplace)
state.replaces.push(state.curReplace = []);
state.replaces.push(state.curReplace = [])
state.curReplace.push({

@@ -904,4 +904,4 @@ fromOffset: offset,

val: repeat('\uFFFD', buf.length)
});
return;
})
return
}

@@ -915,3 +915,3 @@ }

val: buf.toString('binary')
});
})
}

@@ -921,8 +921,8 @@

if (match === '_')
return ' ';
return ' '
else
return String.fromCharCode(parseInt(byte, 16));
return String.fromCharCode(parseInt(byte, 16))
}
function decodeWords(str, state) {
let pendoffset = -1;
let pendoffset = -1

@@ -937,9 +937,9 @@ if (!state) {

remainder: undefined
};
}
}
state.replaces = [];
state.replaces = []
let bytes, m, next, i, j, leni, lenj, seq;
let replaces = [], lastReplace = {};
let bytes, m, next, i, j, leni, lenj, seq
let replaces = [], lastReplace = {}

@@ -950,4 +950,4 @@ // join consecutive q-encoded words that have the same charset first

consecutive: (pendoffset > -1
? RE_LWS_ONLY.test(str.substring(pendoffset, m.index))
: false),
? RE_LWS_ONLY.test(str.substring(pendoffset, m.index))
: false),
charset: m[1].toLowerCase(),

@@ -960,15 +960,15 @@ encoding: m[2].toLowerCase(),

buf: undefined
};
lastReplace = replaces.length && replaces[replaces.length - 1];
}
lastReplace = replaces.length && replaces[replaces.length - 1]
if (seq.consecutive
&& seq.charset === lastReplace.charset
&& seq.encoding === lastReplace.encoding
&& seq.encoding === 'q') {
lastReplace.length += seq.length + seq.index - pendoffset;
lastReplace.chunk += seq.chunk;
&& seq.charset === lastReplace.charset
&& seq.encoding === lastReplace.encoding
&& seq.encoding === 'q') {
lastReplace.length += seq.length + seq.index - pendoffset
lastReplace.chunk += seq.chunk
} else {
replaces.push(seq);
lastReplace = seq;
replaces.push(seq)
lastReplace = seq
}
pendoffset = m.index + m[0].length;
pendoffset = m.index + m[0].length
}

@@ -978,12 +978,12 @@

for (i = 0, leni = replaces.length; i < leni; ++i) {
m = replaces[i];
state.consecutive = m.consecutive;
m = replaces[i]
state.consecutive = m.consecutive
if (m.encoding === 'q') {
// q-encoding, similar to quoted-printable
bytes = Buffer.from(m.chunk.replace(RE_QENC, qEncReplacer), 'binary'); //new Buffer(m.chunk.replace(RE_QENC, qEncReplacer), 'binary');
next = undefined;
bytes = Buffer.from(m.chunk.replace(RE_QENC, qEncReplacer), 'binary') //new Buffer(m.chunk.replace(RE_QENC, qEncReplacer), 'binary');
next = undefined
} else {
// base64
bytes = m.buf || Buffer.from(m.chunk, 'base64'); //new Buffer(m.chunk, 'base64');
next = replaces[i + 1];
bytes = m.buf || Buffer.from(m.chunk, 'base64') //new Buffer(m.chunk, 'base64');
next = replaces[i + 1]
if (next && next.consecutive && next.encoding === m.encoding

@@ -993,7 +993,7 @@ && next.charset === m.charset) {

// of the current chunk
next.buf = Buffer.from(next.chunk, 'base64'); //new Buffer(next.chunk, 'base64');
next.buf = Buffer.from(next.chunk, 'base64') //new Buffer(next.chunk, 'base64');
}
}
decodeBytes(bytes, m.charset, m.index, m.length, m.pendoffset, state,
next && next.buf);
next && next.buf)
}

@@ -1003,17 +1003,17 @@

for (i = state.replaces.length - 1; i >= 0; --i) {
seq = state.replaces[i];
seq = state.replaces[i]
if (Array.isArray(seq)) {
for (j = 0, lenj = seq.length; j < lenj; ++j) {
str = str.substring(0, seq[j].fromOffset)
+ seq[j].val
+ str.substring(seq[j].toOffset);
+ seq[j].val
+ str.substring(seq[j].toOffset)
}
} else {
str = str.substring(0, seq.fromOffset)
+ seq.val
+ str.substring(seq.toOffset);
+ seq.val
+ str.substring(seq.toOffset)
}
}
return str;
return str
}

@@ -1023,62 +1023,62 @@

const lines = str.split(RE_CRLF),
header = {},
state = {
buffer: undefined,
encoding: undefined,
consecutive: false,
replaces: undefined,
curReplace: undefined,
remainder: undefined
};
let len = lines.length;
let m, h, i, val;
header = {},
state = {
buffer: undefined,
encoding: undefined,
consecutive: false,
replaces: undefined,
curReplace: undefined,
remainder: undefined
}
let len = lines.length
let m, h, i, val
for (i = 0; i < len; ++i) {
if (lines[i].length === 0)
break; // empty line separates message's header and body
break // empty line separates message's header and body
if (lines[i][0] === '\t' || lines[i][0] === ' ') {
if (!Array.isArray(header[h]))
continue; // ignore invalid first line
continue // ignore invalid first line
// folded header content
val = lines[i];
val = lines[i]
if (!noDecode) {
if (RE_ENCWORD_END.test(lines[i - 1])
&& RE_ENCWORD_BEGIN.test(val)) {
&& RE_ENCWORD_BEGIN.test(val)) {
// RFC2047 says to *ignore* leading whitespace in folded header values
// for adjacent encoded-words ...
val = val.substring(1);
val = val.substring(1)
}
}
header[h][header[h].length - 1] += val;
header[h][header[h].length - 1] += val
} else {
m = RE_HDR.exec(lines[i]);
m = RE_HDR.exec(lines[i])
if (m) {
h = m[1].toLowerCase().trim();
h = m[1].toLowerCase().trim()
if (m[2]) {
if (header[h] === undefined)
header[h] = [m[2]];
header[h] = [m[2]]
else
header[h].push(m[2]);
header[h].push(m[2])
} else
header[h] = [''];
header[h] = ['']
} else
break;
break
}
}
if (!noDecode) {
let hvs;
let hvs
for (h in header) {
hvs = header[h];
hvs = header[h]
for (i = 0, len = header[h].length; i < len; ++i)
hvs[i] = decodeWords(hvs[i], state);
hvs[i] = decodeWords(hvs[i], state)
}
}
return header;
return header
}
exports.Parser = Parser;
exports.parseExpr = parseExpr;
exports.parseEnvelopeAddresses = parseEnvelopeAddresses;
exports.parseBodyStructure = parseBodyStructure;
exports.parseHeader = parseHeader;
exports.Parser = Parser
exports.parseExpr = parseExpr
exports.parseEnvelopeAddresses = parseEnvelopeAddresses
exports.parseBodyStructure = parseBodyStructure
exports.parseHeader = parseHeader

@@ -1,2 +0,2 @@

'use strict';
'use strict'

@@ -8,10 +8,18 @@ const RE_NUM_RANGE = /^(?:[\d]+|\*):(?:[\d]+|\*)$/,

module.exports.RE_NUM_RANGE = RE_NUM_RANGE
const MONTHS = ['Jan', 'Feb', 'Mar',
'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec']
module.exports.MONTH = MONTHS
// utilities -------------------------------------------------------------------
function escape(str) {
return str.replace(RE_BACKSLASH, '\\\\').replace(RE_DBLQUOTE, '\\"');
return str.replace(RE_BACKSLASH, '\\\\').replace(RE_DBLQUOTE, '\\"')
}
module.exports.escape = escape;
module.exports.escape = escape

@@ -23,23 +31,23 @@ function validateUIDList(uids, noThrow) {

if (len > 1)
uids = ['*'];
break;
uids = ['*']
break
} else if (RE_NUM_RANGE.test(uids[i]))
continue;
continue
}
intval = parseInt(''+uids[i], 10);
intval = parseInt('' + uids[i], 10)
if (isNaN(intval)) {
var err = new Error('UID/seqno must be an integer, "*", or a range: '
+ uids[i]);
const err = new Error('UID/seqno must be an integer, "*", or a range: '
+ uids[i])
if (noThrow)
return err;
return err
else
throw err;
throw err
} else if (intval <= 0) {
var err = new Error('UID/seqno must be greater than zero');
const err = new Error('UID/seqno must be greater than zero')
if (noThrow)
return err;
return err
else
throw err;
throw err
} else if (typeof uids[i] !== 'number') {
uids[i] = intval;
uids[i] = intval
}

@@ -49,3 +57,3 @@ }

module.exports.validateUIDList = validateUIDList;
module.exports.validateUIDList = validateUIDList

@@ -55,55 +63,57 @@ function hasNonASCII(str) {

if (str.charCodeAt(i) > 0x7F)
return true;
return true
}
return false;
return false
}
module.exports.hasNonASCII = hasNonASCII;
module.exports.hasNonASCII = hasNonASCII
function buildString(str) {
if (typeof str !== 'string')
str = ''+str;
str = '' + str
if (hasNonASCII(str)) {
var buf = Buffer.from(str, 'utf8'); //new Buffer(str, 'utf8');
return '{' + buf.length + '}\r\n' + buf.toString('binary');
var buf = Buffer.from(str, 'utf8') //new Buffer(str, 'utf8');
return '{' + buf.length + '}\r\n' + buf.toString('binary')
} else
return '"' + escape(str) + '"';
return '"' + escape(str) + '"'
}
module.exports.buildString = buildString;
module.exports.buildString = buildString
function buildSearchQuery(options, extensions, info, isOrChild) {
var searchargs = '', err, val;
let searchargs = ''
// let err
let val
for (var i = 0, len = options.length; i < len; ++i) {
var criteria = (isOrChild ? options : options[i]),
args = null,
modifier = (isOrChild ? '' : ' ');
args = null,
modifier = (isOrChild ? '' : ' ')
if (typeof criteria === 'string')
criteria = criteria.toUpperCase();
criteria = criteria.toUpperCase()
else if (Array.isArray(criteria)) {
if (criteria.length > 1)
args = criteria.slice(1);
args = criteria.slice(1)
if (criteria.length > 0)
criteria = criteria[0].toUpperCase();
criteria = criteria[0].toUpperCase()
} else
throw new Error('Unexpected search option data type. '
+ 'Expected string or array. Got: ' + typeof criteria);
+ 'Expected string or array. Got: ' + typeof criteria)
if (criteria === 'OR') {
if (args.length !== 2)
throw new Error('OR must have exactly two arguments');
throw new Error('OR must have exactly two arguments')
if (isOrChild)
searchargs += 'OR (';
searchargs += 'OR ('
else
searchargs += ' OR (';
searchargs += buildSearchQuery(args[0], extensions, info, true);
searchargs += ') (';
searchargs += buildSearchQuery(args[1], extensions, info, true);
searchargs += ')';
searchargs += ' OR ('
searchargs += buildSearchQuery(args[0], extensions, info, true)
searchargs += ') ('
searchargs += buildSearchQuery(args[1], extensions, info, true)
searchargs += ')'
} else {
if (criteria[0] === '!') {
modifier += 'NOT ';
criteria = criteria.substr(1);
modifier += 'NOT '
criteria = criteria.substr(1)
}
switch(criteria) {
switch (criteria) {
// -- Standard criteria --

@@ -124,4 +134,4 @@ case 'ALL':

case 'UNSEEN':
searchargs += modifier + criteria;
break;
searchargs += modifier + criteria
break
case 'BCC':

@@ -136,8 +146,8 @@ case 'BODY':

throw new Error('Incorrect number of arguments for search option: '
+ criteria);
val = buildString(args[0]);
+ criteria)
val = buildString(args[0])
if (info && val[0] === '{')
info.hasUTF8 = true;
searchargs += modifier + criteria + ' ' + val;
break;
info.hasUTF8 = true
searchargs += modifier + criteria + ' ' + val
break
case 'BEFORE':

@@ -151,12 +161,12 @@ case 'ON':

throw new Error('Incorrect number of arguments for search option: '
+ criteria);
+ criteria)
else if (!(args[0] instanceof Date)) {
if ((args[0] = new Date(args[0])).toString() === 'Invalid Date')
throw new Error('Search option argument must be a Date object'
+ ' or a parseable date string');
+ ' or a parseable date string')
}
searchargs += modifier + criteria + ' ' + args[0].getDate() + '-'
+ MONTHS[args[0].getMonth()] + '-'
+ args[0].getFullYear();
break;
+ MONTHS[args[0].getMonth()] + '-'
+ args[0].getFullYear()
break
case 'KEYWORD':

@@ -166,5 +176,5 @@ case 'UNKEYWORD':

throw new Error('Incorrect number of arguments for search option: '
+ criteria);
searchargs += modifier + criteria + ' ' + args[0];
break;
+ criteria)
searchargs += modifier + criteria + ' ' + args[0]
break
case 'LARGER':

@@ -174,27 +184,27 @@ case 'SMALLER':

throw new Error('Incorrect number of arguments for search option: '
+ criteria);
var num = parseInt(args[0], 10);
+ criteria)
var num = parseInt(args[0], 10)
if (isNaN(num))
throw new Error('Search option argument must be a number');
searchargs += modifier + criteria + ' ' + args[0];
break;
throw new Error('Search option argument must be a number')
searchargs += modifier + criteria + ' ' + args[0]
break
case 'HEADER':
if (!args || args.length !== 2)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
val = buildString(args[1]);
+ criteria)
val = buildString(args[1])
if (info && val[0] === '{')
info.hasUTF8 = true;
searchargs += modifier + criteria + ' "' + escape(''+args[0])
+ '" ' + val;
break;
info.hasUTF8 = true
searchargs += modifier + criteria + ' "' + escape('' + args[0])
+ '" ' + val
break
case 'UID':
if (!args)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
validateUIDList(args);
+ criteria)
validateUIDList(args)
if (args.length === 0)
throw new Error('Empty uid list');
searchargs += modifier + criteria + ' ' + args.join(',');
break;
throw new Error('Empty uid list')
searchargs += modifier + criteria + ' ' + args.join(',')
break
// Extensions ==========================================================

@@ -204,127 +214,127 @@ case 'X-GM-MSGID': // Gmail unique message ID

if (extensions.indexOf('X-GM-EXT-1') === -1)
throw new Error('IMAP extension not available for: ' + criteria);
throw new Error('IMAP extension not available for: ' + criteria)
if (!args || args.length !== 1)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
+ criteria)
else {
val = ''+args[0];
val = '' + args[0]
if (!(RE_INTEGER.test(args[0])))
throw new Error('Invalid value');
throw new Error('Invalid value')
}
searchargs += modifier + criteria + ' ' + val;
break;
searchargs += modifier + criteria + ' ' + val
break
case 'X-GM-RAW': // Gmail search syntax
if (extensions.indexOf('X-GM-EXT-1') === -1)
throw new Error('IMAP extension not available for: ' + criteria);
throw new Error('IMAP extension not available for: ' + criteria)
if (!args || args.length !== 1)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
val = buildString(args[0]);
+ criteria)
val = buildString(args[0])
if (info && val[0] === '{')
info.hasUTF8 = true;
searchargs += modifier + criteria + ' ' + val;
break;
info.hasUTF8 = true
searchargs += modifier + criteria + ' ' + val
break
case 'X-GM-LABELS': // Gmail labels
if (extensions.indexOf('X-GM-EXT-1') === -1)
throw new Error('IMAP extension not available for: ' + criteria);
throw new Error('IMAP extension not available for: ' + criteria)
if (!args || args.length !== 1)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
searchargs += modifier + criteria + ' ' + args[0];
break;
+ criteria)
searchargs += modifier + criteria + ' ' + args[0]
break
case 'MODSEQ':
if (extensions.indexOf('CONDSTORE') === -1)
throw new Error('IMAP extension not available for: ' + criteria);
throw new Error('IMAP extension not available for: ' + criteria)
if (!args || args.length !== 1)
throw new Error('Incorrect number of arguments for search option: '
+ criteria);
searchargs += modifier + criteria + ' ' + args[0];
break;
+ criteria)
searchargs += modifier + criteria + ' ' + args[0]
break
default:
// last hope it's a seqno set
// http://tools.ietf.org/html/rfc3501#section-6.4.4
var seqnos = (args ? [criteria].concat(args) : [criteria]);
var seqnos = (args ? [criteria].concat(args) : [criteria])
if (!validateUIDList(seqnos, true)) {
if (seqnos.length === 0)
throw new Error('Empty sequence number list');
searchargs += modifier + seqnos.join(',');
throw new Error('Empty sequence number list')
searchargs += modifier + seqnos.join(',')
} else
throw new Error('Unexpected search option: ' + criteria);
throw new Error('Unexpected search option: ' + criteria)
}
}
if (isOrChild)
break;
break
}
return searchargs;
return searchargs
}
module.exports.buildSearchQuery = buildSearchQuery;
module.exports.buildSearchQuery = buildSearchQuery
// Pulled from assert.deepEqual:
var pSlice = Array.prototype.slice;
var pSlice = Array.prototype.slice
function _deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
return true
} else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length !== expected.length) return false;
if (actual.length !== expected.length) return false
for (var i = 0; i < actual.length; i++) {
if (actual[i] !== expected[i]) return false;
if (actual[i] !== expected[i]) return false
}
return true;
return true
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
return actual.getTime() === expected.getTime()
// 7.3 If the expected value is a RegExp object, the actual value is
// equivalent if it is also a RegExp object with the same source and
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
// 7.3 If the expected value is a RegExp object, the actual value is
// equivalent if it is also a RegExp object with the same source and
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
} else if (actual instanceof RegExp && expected instanceof RegExp) {
return actual.source === expected.source &&
actual.global === expected.global &&
actual.multiline === expected.multiline &&
actual.lastIndex === expected.lastIndex &&
actual.ignoreCase === expected.ignoreCase;
actual.global === expected.global &&
actual.multiline === expected.multiline &&
actual.lastIndex === expected.lastIndex &&
actual.ignoreCase === expected.ignoreCase
// 7.4. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
// 7.4. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual !== 'object' && typeof expected !== 'object') {
return actual == expected;
return actual == expected
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
return objEquiv(actual, expected)
}
}
module.exports._deepEqual = _deepEqual;
module.exports._deepEqual = _deepEqual
function isUndefinedOrNull(value) {
return value === null || value === undefined;
return value === null || value === undefined
}
module.exports.isUndefinedOrNull = isUndefinedOrNull;
module.exports.isUndefinedOrNull = isUndefinedOrNull
function isArguments(object) {
return Object.prototype.toString.call(object) === '[object Arguments]';
return Object.prototype.toString.call(object) === '[object Arguments]'
}
module.exports.isArguments = isArguments;
module.exports.isArguments = isArguments
function objEquiv(a, b) {
var ka, kb, key, i;
var ka, kb, key, i
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
return false;
return false
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
if (a.prototype !== b.prototype) return false
//~~~I've managed to break Object.keys through screwy arguments passing.

@@ -334,13 +344,13 @@ // Converting to array solves the problem.

if (!isArguments(b)) {
return false;
return false
}
a = pSlice.call(a);
b = pSlice.call(b);
return _deepEqual(a, b);
a = pSlice.call(a)
b = pSlice.call(b)
return _deepEqual(a, b)
}
try {
ka = Object.keys(a);
kb = Object.keys(b);
ka = Object.keys(a)
kb = Object.keys(b)
} catch (e) {//happens when one is a string literal and the other isn't
return false;
return false
}

@@ -350,10 +360,10 @@ // having the same number of owned properties (keys incorporates

if (ka.length !== kb.length)
return false;
return false
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
ka.sort()
kb.sort()
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
return false
}

@@ -363,8 +373,8 @@ //equivalent values for every corresponding key, and

for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key])) return false;
key = ka[i]
if (!_deepEqual(a[key], b[key])) return false
}
return true;
return true
}
module.exports.objEquiv = objEquiv;
module.exports.objEquiv = objEquiv
{
"name": "node-imap",
"version": "0.9.5",
"version": "0.9.6",
"author": "Brian White <mscdex@mscdex.net>",

@@ -40,3 +40,6 @@ "contributors": [

"url": "https://github.com/mikebevz/node-imap"
},
"devDependencies": {
"eslint": "^7.1.0"
}
}
}
var assert = require('assert'),
net = require('net'),
Imap = require('../lib/Connection'),
result;
net = require('net'),
Imap = require('../lib/Connection'),
result
var CRLF = '\r\n';
var CRLF = '\r\n'
var RESPONSES = [
['* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA CHILDREN',
'A0 OK Thats all she wrote!',
''
'A0 OK Thats all she wrote!',
''
].join(CRLF),
['* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA CHILDREN UIDPLUS MOVE',
'A1 OK authenticated (Success)',
''
'A1 OK authenticated (Success)',
''
].join(CRLF),
['* NAMESPACE (("" "/")) NIL NIL',
'A2 OK Success',
''
'A2 OK Success',
''
].join(CRLF),
['* LIST (\\Noselect) "/" "/"',
'A3 OK Success',
''
'A3 OK Success',
''
].join(CRLF),
['* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)',
'* OK [PERMANENTFLAGS ()] Flags permitted.',
'* OK [UIDVALIDITY 2] UIDs valid.',
'* 685 EXISTS',
'* 0 RECENT',
'* OK [UIDNEXT 4422] Predicted next UID.',
'A4 OK [READ-ONLY] INBOX selected. (Success)',
''
'* OK [PERMANENTFLAGS ()] Flags permitted.',
'* OK [UIDVALIDITY 2] UIDs valid.',
'* 685 EXISTS',
'* 0 RECENT',
'* OK [UIDNEXT 4422] Predicted next UID.',
'A4 OK [READ-ONLY] INBOX selected. (Success)',
''
].join(CRLF),
['* 1 FETCH (UID 1)',
'* 1 FETCH (INTERNALDATE "05-Sep-2004 00:38:03 +0000" UID 1000)',
'* 1 FETCH (FLAGS (\\Seen))',
'A5 OK Success',
''
'* 1 FETCH (INTERNALDATE "05-Sep-2004 00:38:03 +0000" UID 1000)',
'* 1 FETCH (FLAGS (\\Seen))',
'A5 OK Success',
''
].join(CRLF),
['* BYE LOGOUT Requested',
'A6 OK good day (Success)',
''
'A6 OK good day (Success)',
''
].join(CRLF)
];
]
const srv = net.createServer(function(sock) {
sock.write('* OK asdf\r\n');
var buf = '', lines;
sock.on('data', function(data) {
buf += data.toString('utf8');
const srv = net.createServer(function (sock) {
sock.write('* OK asdf\r\n')
var buf = '', lines
sock.on('data', function (data) {
buf += data.toString('utf8')
if (buf.indexOf(CRLF) > -1) {
lines = buf.split(CRLF);
buf = lines.pop();
lines.forEach(function() {
sock.write(RESPONSES.shift());
});
lines = buf.split(CRLF)
buf = lines.pop()
lines.forEach(function () {
sock.write(RESPONSES.shift())
})
}
});
});
srv.listen(0, '127.0.0.1', function() {
var port = srv.address().port;
})
})
srv.listen(0, '127.0.0.1', function () {
var port = srv.address().port
var imap = new Imap({

@@ -68,26 +68,26 @@ user: 'foo',

keepalive: false
});
imap.on('ready', function() {
imap.openBox('INBOX', true, function() {
var f = imap.seq.fetch(1);
f.on('message', function(m) {
m.once('attributes', function(attrs) {
result = attrs;
});
});
f.on('end', function() {
srv.close();
imap.end();
});
});
});
imap.connect();
});
})
imap.on('ready', function () {
imap.openBox('INBOX', true, function () {
var f = imap.seq.fetch(1)
f.on('message', function (m) {
m.once('attributes', function (attrs) {
result = attrs
})
})
f.on('end', function () {
srv.close()
imap.end()
})
})
})
imap.connect()
})
process.once('exit', function() {
process.once('exit', function () {
assert.deepEqual(result, {
uid: 1,
date: new Date('05-Sep-2004 00:38:03 +0000'),
flags: [ '\\Seen' ]
});
});
flags: ['\\Seen']
})
})

@@ -1,6 +0,6 @@

const Parser = require('../lib/Parser').Parser;
const Parser = require('../lib/Parser').Parser
const assert = require('assert'),
crypto = require('crypto'),
inspect = require('util').inspect;
crypto = require('crypto'),
inspect = require('util').inspect

@@ -10,560 +10,643 @@ const CR = '\r', LF = '\n', CRLF = CR + LF;

[
{ source: ['A1 OK LOGIN completed', CRLF],
expected: [ { type: 'ok',
tagnum: 1,
textCode: undefined,
text: 'LOGIN completed'
}
],
{
source: ['A1 OK LOGIN completed', CRLF],
expected: [{
type: 'ok',
tagnum: 1,
textCode: undefined,
text: 'LOGIN completed'
}
],
what: 'Tagged OK'
},
{ source: ['IDLE OK IDLE terminated', CRLF],
expected: [ 'IDLE OK IDLE terminated' ],
{
source: ['IDLE OK IDLE terminated', CRLF],
expected: ['IDLE OK IDLE terminated'],
what: 'Unknown line'
},
{ source: ['IDLE OK Idle completed (0.002 + 1.783 + 1.783 secs).', CRLF],
expected: [ 'IDLE OK Idle completed (0.002 + 1.783 + 1.783 secs).' ],
{
source: ['IDLE OK Idle completed (0.002 + 1.783 + 1.783 secs).', CRLF],
expected: ['IDLE OK Idle completed (0.002 + 1.783 + 1.783 secs).'],
what: 'Unknown line with + char'
},
{ source: ['+ idling', CRLF],
expected: [ { textCode: undefined,
text: 'idling'
}
],
{
source: ['+ idling', CRLF],
expected: [{
textCode: undefined,
text: 'idling'
}
],
what: 'Continuation'
},
{ source: ['+ [ALERT] idling', CRLF],
expected: [ { textCode: 'ALERT',
text: 'idling'
}
],
{
source: ['+ [ALERT] idling', CRLF],
expected: [{
textCode: 'ALERT',
text: 'idling'
}
],
what: 'Continuation with text code'
},
{ source: ['A1 NO [ALERT] Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)', CRLF],
expected: [ {
type: 'no',
tagnum: 1,
textCode: 'ALERT',
text: 'Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)'
}
],
{
source: ['A1 NO [ALERT] Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)', CRLF],
expected: [{
type: 'no',
tagnum: 1,
textCode: 'ALERT',
text: 'Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)'
}
],
what: 'Continuation with text code'
},
{ source: ['* NO [WEBALERT https://someurl.com/continue?sarp=1&scc=1] Web login required.', CRLF],
expected: [ {
type: 'no',
num: undefined,
textCode: {
key: 'WEBALERT',
val: 'https://someurl.com/continue?sarp=1&scc=1'
},
text: 'Web login required.'
}
],
{
source: ['* NO [WEBALERT https://someurl.com/continue?sarp=1&scc=1] Web login required.', CRLF],
expected: [{
type: 'no',
num: undefined,
textCode: {
key: 'WEBALERT',
val: 'https://someurl.com/continue?sarp=1&scc=1'
},
text: 'Web login required.'
}
],
what: 'Handling of Webalert'
},
{ source: ['+', CRLF],
expected: [ { textCode: undefined,
text: undefined
}
],
{
source: ['+', CRLF],
expected: [{
textCode: undefined,
text: undefined
}
],
what: 'Continuation (broken -- RFC violation) sent by AOL IMAP'
},
{ source: ['* NAMESPACE ',
'(("" "/")) ',
'(("~" "/")) ',
'(("#shared/" "/")("#public/" "/")("#ftp/" "/")("#news." "."))',
CRLF],
expected: [ { type: 'namespace',
num: undefined,
textCode: undefined,
text: {
personal: [
{ prefix: '',
delimiter: '/',
extensions: undefined
}
],
other: [
{ prefix: '~',
delimiter: '/',
extensions: undefined
}
],
shared: [
{ prefix: '#shared/',
delimiter: '/',
extensions: undefined
},
{ prefix: '#public/',
delimiter: '/',
extensions: undefined
},
{ prefix: '#ftp/',
delimiter: '/',
extensions: undefined
},
{ prefix: '#news.',
delimiter: '.',
extensions: undefined
}
]
}
}
],
{
source: ['* NAMESPACE ',
'(("" "/")) ',
'(("~" "/")) ',
'(("#shared/" "/")("#public/" "/")("#ftp/" "/")("#news." "."))',
CRLF],
expected: [{
type: 'namespace',
num: undefined,
textCode: undefined,
text: {
personal: [
{
prefix: '',
delimiter: '/',
extensions: undefined
}
],
other: [
{
prefix: '~',
delimiter: '/',
extensions: undefined
}
],
shared: [
{
prefix: '#shared/',
delimiter: '/',
extensions: undefined
},
{
prefix: '#public/',
delimiter: '/',
extensions: undefined
},
{
prefix: '#ftp/',
delimiter: '/',
extensions: undefined
},
{
prefix: '#news.',
delimiter: '.',
extensions: undefined
}
]
}
}
],
what: 'Multiple namespaces'
},
{ source: ['* NAMESPACE ',
'(("" "/" "X-PARAM" ("FLAG1" "FLAG2"))) ',
'NIL ',
'NIL',
CRLF],
expected: [ { type: 'namespace',
num: undefined,
textCode: undefined,
text: {
personal: [
{ prefix: '',
delimiter: '/',
extensions: {
'X-PARAM': [ 'FLAG1', 'FLAG2' ]
}
}
],
other: null,
shared: null
}
}
],
{
source: ['* NAMESPACE ',
'(("" "/" "X-PARAM" ("FLAG1" "FLAG2"))) ',
'NIL ',
'NIL',
CRLF],
expected: [{
type: 'namespace',
num: undefined,
textCode: undefined,
text: {
personal: [
{
prefix: '',
delimiter: '/',
extensions: {
'X-PARAM': ['FLAG1', 'FLAG2']
}
}
],
other: null,
shared: null
}
}
],
what: 'Multiple namespaces'
},
{ source: ['* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)', CRLF],
expected: [ { type: 'flags',
num: undefined,
textCode: undefined,
text: [
'\\Answered',
'\\Flagged',
'\\Deleted',
'\\Seen',
'\\Draft'
]
}
],
{
source: ['* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)', CRLF],
expected: [{
type: 'flags',
num: undefined,
textCode: undefined,
text: [
'\\Answered',
'\\Flagged',
'\\Deleted',
'\\Seen',
'\\Draft'
]
}
],
what: 'Flags'
},
{ source: ['* SEARCH 2 3 6', CRLF],
expected: [ { type: 'search',
num: undefined,
textCode: undefined,
text: [ 2, 3, 6 ]
}
],
{
source: ['* SEARCH 2 3 6', CRLF],
expected: [{
type: 'search',
num: undefined,
textCode: undefined,
text: [2, 3, 6]
}
],
what: 'Search'
},
{ source: ['* XLIST (\\Noselect) "/" ~/Mail/foo', CRLF],
expected: [ { type: 'xlist',
num: undefined,
textCode: undefined,
text: {
flags: [ '\\Noselect' ],
delimiter: '/',
name: '~/Mail/foo'
}
}
],
{
source: ['* XLIST (\\Noselect) "/" ~/Mail/foo', CRLF],
expected: [{
type: 'xlist',
num: undefined,
textCode: undefined,
text: {
flags: ['\\Noselect'],
delimiter: '/',
name: '~/Mail/foo'
}
}
],
what: 'XList'
},
{ source: ['* LIST (\\Noselect) "/" ~/Mail/foo', CRLF],
expected: [ { type: 'list',
num: undefined,
textCode: undefined,
text: {
flags: [ '\\Noselect' ],
delimiter: '/',
name: '~/Mail/foo'
}
}
],
{
source: ['* LIST (\\Noselect) "/" ~/Mail/foo', CRLF],
expected: [{
type: 'list',
num: undefined,
textCode: undefined,
text: {
flags: ['\\Noselect'],
delimiter: '/',
name: '~/Mail/foo'
}
}
],
what: 'List'
},
{ source: ['* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)', CRLF],
expected: [ { type: 'status',
num: undefined,
textCode: undefined,
text: {
name: 'blurdybloop',
attrs: { messages: 231, uidnext: 44292 }
}
}
],
{
source: ['* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)', CRLF],
expected: [{
type: 'status',
num: undefined,
textCode: undefined,
text: {
name: 'blurdybloop',
attrs: { messages: 231, uidnext: 44292 }
}
}
],
what: 'Status'
},
{ source: ['* OK [UNSEEN 17] Message 17 is the first unseen message', CRLF],
expected: [ { type: 'ok',
num: undefined,
textCode: {
key: 'UNSEEN',
val: 17
},
text: 'Message 17 is the first unseen message'
}
],
{
source: ['* OK [UNSEEN 17] Message 17 is the first unseen message', CRLF],
expected: [{
type: 'ok',
num: undefined,
textCode: {
key: 'UNSEEN',
val: 17
},
text: 'Message 17 is the first unseen message'
}
],
what: 'Untagged OK (with text code, with text)'
},
{ source: ['* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited', CRLF],
expected: [ { type: 'ok',
num: undefined,
textCode: {
key: 'PERMANENTFLAGS',
val: [ '\\Deleted', '\\Seen', '\\*' ]
},
text: 'Limited'
}
],
{
source: ['* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited', CRLF],
expected: [{
type: 'ok',
num: undefined,
textCode: {
key: 'PERMANENTFLAGS',
val: ['\\Deleted', '\\Seen', '\\*']
},
text: 'Limited'
}
],
what: 'Untagged OK (with text code, with text)'
},
{ source: ['* OK [UNSEEN 17]', CRLF],
expected: [ { type: 'ok',
num: undefined,
textCode: {
key: 'UNSEEN',
val: 17
},
text: undefined
}
],
{
source: ['* OK [UNSEEN 17]', CRLF],
expected: [{
type: 'ok',
num: undefined,
textCode: {
key: 'UNSEEN',
val: 17
},
text: undefined
}
],
what: 'Untagged OK (no text code, with text) (RFC violation)'
},
{ source: ['* OK IMAP4rev1 Service Ready', CRLF],
expected: [ { type: 'ok',
num: undefined,
textCode: undefined,
text: 'IMAP4rev1 Service Ready'
}
],
{
source: ['* OK IMAP4rev1 Service Ready', CRLF],
expected: [{
type: 'ok',
num: undefined,
textCode: undefined,
text: 'IMAP4rev1 Service Ready'
}
],
what: 'Untagged OK (no text code, with text)'
},
{ source: ['* OK', CRLF], // I have seen servers that send stuff like this ..
expected: [ { type: 'ok',
num: undefined,
textCode: undefined,
text: undefined
}
],
{
source: ['* OK', CRLF], // I have seen servers that send stuff like this ..
expected: [{
type: 'ok',
num: undefined,
textCode: undefined,
text: undefined
}
],
what: 'Untagged OK (no text code, no text) (RFC violation)'
},
{ source: ['* 18 EXISTS', CRLF],
expected: [ { type: 'exists',
num: 18,
textCode: undefined,
text: undefined
}
],
{
source: ['* 18 EXISTS', CRLF],
expected: [{
type: 'exists',
num: 18,
textCode: undefined,
text: undefined
}
],
what: 'Untagged EXISTS'
},
{ source: ['* 2 RECENT', CRLF],
expected: [ { type: 'recent',
num: 2,
textCode: undefined,
text: undefined
}
],
{
source: ['* 2 RECENT', CRLF],
expected: [{
type: 'recent',
num: 2,
textCode: undefined,
text: undefined
}
],
what: 'Untagged RECENT'
},
{ source: ['* 12 FETCH (BODY[HEADER] {342}', CRLF,
'Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)', CRLF,
'From: Terry Gray <gray@cac.washington.edu>', CRLF,
'Subject: IMAP4rev1 WG mtg summary and minutes', CRLF,
'To: imap@cac.washington.edu', CRLF,
'cc: minutes@CNRI.Reston.VA.US, John Klensin <KLENSIN@MIT.EDU>', CRLF,
'Message-Id: <B27397-0100000@cac.washington.edu>', CRLF,
'MIME-Version: 1.0', CRLF,
'Content-Type: TEXT/PLAIN; CHARSET=US-ASCII', CRLF, CRLF,
')', CRLF],
expected: [ { seqno: 12,
which: 'HEADER',
size: 342
},
{ type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
{
source: ['* 12 FETCH (BODY[HEADER] {342}', CRLF,
'Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)', CRLF,
'From: Terry Gray <gray@cac.washington.edu>', CRLF,
'Subject: IMAP4rev1 WG mtg summary and minutes', CRLF,
'To: imap@cac.washington.edu', CRLF,
'cc: minutes@CNRI.Reston.VA.US, John Klensin <KLENSIN@MIT.EDU>', CRLF,
'Message-Id: <B27397-0100000@cac.washington.edu>', CRLF,
'MIME-Version: 1.0', CRLF,
'Content-Type: TEXT/PLAIN; CHARSET=US-ASCII', CRLF, CRLF,
')', CRLF],
expected: [{
seqno: 12,
which: 'HEADER',
size: 342
},
{
type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
bodySHA1s: ['1f96faf50f6410f99237791f9e3b89454bf93fa7'],
what: 'Untagged FETCH (body)'
},
{ source: ['* 12 FETCH (BODY[TEXT] "IMAP is terrible")', CRLF],
expected: [ { seqno: 12,
which: 'TEXT',
size: 16
},
{ type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
{
source: ['* 12 FETCH (BODY[TEXT] "IMAP is terrible")', CRLF],
expected: [{
seqno: 12,
which: 'TEXT',
size: 16
},
{
type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
bodySHA1s: ['bac8a1528c133787a6969a10a1ff453ebb9adfc8'],
what: 'Untagged FETCH (quoted body)'
},
{ source: ['* 12 FETCH (BODY[TEXT] "\\"IMAP\\" is terrible :\\\\")', CRLF],
expected: [ { seqno: 12,
which: 'TEXT',
size: 21
},
{ type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
{
source: ['* 12 FETCH (BODY[TEXT] "\\"IMAP\\" is terrible :\\\\")', CRLF],
expected: [{
seqno: 12,
which: 'TEXT',
size: 21
},
{
type: 'fetch',
num: 12,
textCode: undefined,
text: {}
}
],
bodySHA1s: ['7570c08150050a404603f63f60b65b42378d7d42'],
what: 'Untagged FETCH (quoted body with escaped chars)'
},
{ source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
'17-Jul-1996 02:44:25 -0700)' + CRLF],
expected: [ { type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
{
source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
'17-Jul-1996 02:44:25 -0700)' + CRLF],
expected: [{
type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
what: 'Untagged FETCH with non-body literal'
},
{ source: ['* 12 FETCH (INTERNALDATE {2',
'6}' + CRLF + '17-Jul-1996 02:44:25 -0700)' + CRLF],
expected: [ { type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
{
source: ['* 12 FETCH (INTERNALDATE {2',
'6}' + CRLF + '17-Jul-1996 02:44:25 -0700)' + CRLF],
expected: [{
type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
what: 'Untagged FETCH with non-body literal (length split)'
},
{ source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
'17-Jul-1996 02:44:25 -0700)' + CR,
LF],
expected: [ { type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
{
source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
'17-Jul-1996 02:44:25 -0700)' + CR,
LF],
expected: [{
type: 'fetch',
num: 12,
textCode: undefined,
text: {
internaldate: new Date('17-Jul-1996 02:44:25 -0700')
}
}
],
what: 'Untagged FETCH with non-body literal (split CRLF)'
},
{ source: ['* 12 FETCH (FLAGS (\\Seen)',
' INTERNALDATE "17-Jul-1996 02:44:25 -0700"',
' RFC822.SIZE 4286',
' ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)"',
' "IMAP4rev1 WG mtg summary and minutes"',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' ((NIL NIL "imap" "cac.washington.edu"))',
' ((NIL NIL "minutes" "CNRI.Reston.VA.US")',
'("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL',
' "<B27397-0100000@cac.washington.edu>")',
' BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028',
' 92))',
CRLF],
expected: [ { type: 'fetch',
num: 12,
textCode: undefined,
text: {
flags: [ '\\Seen' ],
internaldate: new Date('17-Jul-1996 02:44:25 -0700'),
'rfc822.size': 4286,
envelope: {
date: new Date('Wed, 17 Jul 1996 02:23:25 -0700 (PDT)'),
subject: 'IMAP4rev1 WG mtg summary and minutes',
from: [
{ name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
sender: [
{ name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
replyTo: [
{ name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
to: [
{ name: null,
mailbox: 'imap',
host: 'cac.washington.edu'
}
],
cc: [
{ name: null,
mailbox: 'minutes',
host: 'CNRI.Reston.VA.US'
},
{ name: 'John Klensin',
mailbox: 'KLENSIN',
host: 'MIT.EDU'
}
],
bcc: null,
inReplyTo: null,
messageId: '<B27397-0100000@cac.washington.edu>'
},
body: [
{ partID: '1',
type: 'text',
subtype: 'plain',
params: { charset: 'US-ASCII' },
id: null,
description: null,
encoding: '7BIT',
size: 3028,
lines: 92
}
]
}
}
],
{
source: ['* 12 FETCH (FLAGS (\\Seen)',
' INTERNALDATE "17-Jul-1996 02:44:25 -0700"',
' RFC822.SIZE 4286',
' ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)"',
' "IMAP4rev1 WG mtg summary and minutes"',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
' ((NIL NIL "imap" "cac.washington.edu"))',
' ((NIL NIL "minutes" "CNRI.Reston.VA.US")',
'("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL',
' "<B27397-0100000@cac.washington.edu>")',
' BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028',
' 92))',
CRLF],
expected: [{
type: 'fetch',
num: 12,
textCode: undefined,
text: {
flags: ['\\Seen'],
internaldate: new Date('17-Jul-1996 02:44:25 -0700'),
'rfc822.size': 4286,
envelope: {
date: new Date('Wed, 17 Jul 1996 02:23:25 -0700 (PDT)'),
subject: 'IMAP4rev1 WG mtg summary and minutes',
from: [
{
name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
sender: [
{
name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
replyTo: [
{
name: 'Terry Gray',
mailbox: 'gray',
host: 'cac.washington.edu'
}
],
to: [
{
name: null,
mailbox: 'imap',
host: 'cac.washington.edu'
}
],
cc: [
{
name: null,
mailbox: 'minutes',
host: 'CNRI.Reston.VA.US'
},
{
name: 'John Klensin',
mailbox: 'KLENSIN',
host: 'MIT.EDU'
}
],
bcc: null,
inReplyTo: null,
messageId: '<B27397-0100000@cac.washington.edu>'
},
body: [
{
partID: '1',
type: 'text',
subtype: 'plain',
params: { charset: 'US-ASCII' },
id: null,
description: null,
encoding: '7BIT',
size: 3028,
lines: 92
}
]
}
}
],
what: 'Untagged FETCH (flags, date, size, envelope, body[structure])'
},
// EXTENSIONS ================================================================
{ source: ['* ESEARCH (TAG "A285") UID MIN 7 MAX 3800', CRLF],
expected: [ { type: 'esearch',
num: undefined,
textCode: undefined,
text: { min: 7, max: 3800 }
}
],
{
source: ['* ESEARCH (TAG "A285") UID MIN 7 MAX 3800', CRLF],
expected: [{
type: 'esearch',
num: undefined,
textCode: undefined,
text: { min: 7, max: 3800 }
}
],
what: 'ESearch UID, 2 items'
},
{ source: ['* ESEARCH (TAG "A284") MIN 4', CRLF],
expected: [ { type: 'esearch',
num: undefined,
textCode: undefined,
text: { min: 4 }
}
],
{
source: ['* ESEARCH (TAG "A284") MIN 4', CRLF],
expected: [{
type: 'esearch',
num: undefined,
textCode: undefined,
text: { min: 4 }
}
],
what: 'ESearch 1 item'
},
{ source: ['* ESEARCH (TAG "A283") ALL 2,10:11', CRLF],
expected: [ { type: 'esearch',
num: undefined,
textCode: undefined,
text: { all: [ '2', '10:11' ] }
}
],
{
source: ['* ESEARCH (TAG "A283") ALL 2,10:11', CRLF],
expected: [{
type: 'esearch',
num: undefined,
textCode: undefined,
text: { all: ['2', '10:11'] }
}
],
what: 'ESearch ALL list'
},
{ source: ['* QUOTA "" (STORAGE 10 512)', CRLF],
expected: [ { type: 'quota',
num: undefined,
textCode: undefined,
text: {
root: '',
resources: {
storage: { usage: 10, limit: 512 }
}
}
}
],
{
source: ['* QUOTA "" (STORAGE 10 512)', CRLF],
expected: [{
type: 'quota',
num: undefined,
textCode: undefined,
text: {
root: '',
resources: {
storage: { usage: 10, limit: 512 }
}
}
}
],
what: 'Quota'
},
{ source: ['* QUOTAROOT INBOX ""', CRLF],
expected: [ { type: 'quotaroot',
num: undefined,
textCode: undefined,
text: {
roots: [ '' ],
mailbox: 'INBOX'
}
}
],
{
source: ['* QUOTAROOT INBOX ""', CRLF],
expected: [{
type: 'quotaroot',
num: undefined,
textCode: undefined,
text: {
roots: [''],
mailbox: 'INBOX'
}
}
],
what: 'QuotaRoot'
},
{ source: ['A1 OK', CRLF], // some servers like ppops.net sends such response
expected: [ { type: 'ok',
tagnum: 1,
textCode: undefined,
text: ''
}
],
{
source: ['A1 OK', CRLF], // some servers like ppops.net sends such response
expected: [{
type: 'ok',
tagnum: 1,
textCode: undefined,
text: ''
}
],
what: 'Tagged OK (no text code, no text)'
},
].forEach(function(v) {
const ss = new require('stream').Readable(), result = [];
let p;
ss._read = function(){};
].forEach(function (v) {
const ss = new require('stream').Readable(), result = []
let p
ss._read = function () { }
p = new Parser(ss);
p.on('tagged', function(info) {
result.push(info);
});
p.on('untagged', function(info) {
result.push(info);
});
p.on('continue', function(info) {
result.push(info);
});
p.on('other', function(line) {
result.push(line);
});
p.on('body', function(stream, info) {
result.push(info);
p = new Parser(ss)
p.on('tagged', function (info) {
result.push(info)
})
p.on('untagged', function (info) {
result.push(info)
})
p.on('continue', function (info) {
result.push(info)
})
p.on('other', function (line) {
result.push(line)
})
p.on('body', function (stream, info) {
result.push(info)
if (Array.isArray(v.bodySHA1s)) {
const hash = crypto.createHash('sha1');
stream.on('data', function(d) {
hash.update(d);
});
stream.on('end', function() {
const hash = crypto.createHash('sha1')
stream.on('data', function (d) {
hash.update(d)
})
stream.on('end', function () {
const calculated = hash.digest('hex'),
expected = v.bodySHA1s.shift();
expected = v.bodySHA1s.shift()
assert.equal(calculated,
expected,
makeMsg(v.what,
'Body SHA1 mismatch:'
+ '\nCalculated: ' + calculated
+ '\nExpected: ' + expected
)
);
});
expected,
makeMsg(v.what,
'Body SHA1 mismatch:'
+ '\nCalculated: ' + calculated
+ '\nExpected: ' + expected
)
)
})
} else
stream.resume();
});
stream.resume()
})
try {
v.source.forEach(function(chunk) {
ss.push(chunk);
});
v.source.forEach(function (chunk) {
ss.push(chunk)
})
} catch (e) {
console.log(makeMsg(v.what, 'JS Exception: ' + e.stack));
return;
console.log(makeMsg(v.what, 'JS Exception: ' + e.stack))
return
}
setImmediate(function() {
setImmediate(function () {
assert.deepEqual(result,
v.expected,
makeMsg(v.what,
'Result mismatch:'
+ '\nParsed: ' + inspect(result, false, 10)
+ '\nExpected: ' + inspect(v.expected, false, 10)
)
);
});
});
v.expected,
makeMsg(v.what,
'Result mismatch:'
+ '\nParsed: ' + inspect(result, false, 10)
+ '\nExpected: ' + inspect(v.expected, false, 10)
)
)
})
})
function makeMsg(what, msg) {
return '[' + what + ']: ' + msg;
return '[' + what + ']: ' + msg
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc