Comparing version 0.9.5 to 0.9.6
@@ -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 |
304
lib/utils.js
@@ -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 | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23
9005
812783
1