Initial
This commit is contained in:
367
test/imaps/node_modules/node-imap/lib/utils.js
generated
vendored
Normal file
367
test/imaps/node_modules/node-imap/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
'use strict'
|
||||
|
||||
const RE_NUM_RANGE = /^(?:[\d]+|\*):(?:[\d]+|\*)$/,
|
||||
RE_BACKSLASH = /\\/g,
|
||||
RE_DBLQUOTE = /"/g,
|
||||
RE_INTEGER = /^\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, '\\"')
|
||||
}
|
||||
|
||||
module.exports.escape = escape
|
||||
|
||||
function validateUIDList(uids, noThrow) {
|
||||
for (var i = 0, len = uids.length, intval; i < len; ++i) {
|
||||
if (typeof uids[i] === 'string') {
|
||||
if (uids[i] === '*' || uids[i] === '*:*') {
|
||||
if (len > 1)
|
||||
uids = ['*']
|
||||
break
|
||||
} else if (RE_NUM_RANGE.test(uids[i]))
|
||||
continue
|
||||
}
|
||||
intval = parseInt('' + uids[i], 10)
|
||||
if (isNaN(intval)) {
|
||||
const err = new Error('UID/seqno must be an integer, "*", or a range: '
|
||||
+ uids[i])
|
||||
if (noThrow)
|
||||
return err
|
||||
else
|
||||
throw err
|
||||
} else if (intval <= 0) {
|
||||
const err = new Error('UID/seqno must be greater than zero')
|
||||
if (noThrow)
|
||||
return err
|
||||
else
|
||||
throw err
|
||||
} else if (typeof uids[i] !== 'number') {
|
||||
uids[i] = intval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.validateUIDList = validateUIDList
|
||||
|
||||
function hasNonASCII(str) {
|
||||
for (var i = 0, len = str.length; i < len; ++i) {
|
||||
if (str.charCodeAt(i) > 0x7F)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
module.exports.hasNonASCII = hasNonASCII
|
||||
|
||||
function buildString(str) {
|
||||
if (typeof str !== 'string')
|
||||
str = '' + str
|
||||
|
||||
if (hasNonASCII(str)) {
|
||||
var buf = Buffer.from(str, 'utf8') //new Buffer(str, 'utf8');
|
||||
return '{' + buf.length + '}\r\n' + buf.toString('binary')
|
||||
} else
|
||||
return '"' + escape(str) + '"'
|
||||
}
|
||||
|
||||
module.exports.buildString = buildString
|
||||
|
||||
function buildSearchQuery(options, extensions, info, isOrChild) {
|
||||
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 ? '' : ' ')
|
||||
if (typeof criteria === 'string')
|
||||
criteria = criteria.toUpperCase()
|
||||
else if (Array.isArray(criteria)) {
|
||||
if (criteria.length > 1)
|
||||
args = criteria.slice(1)
|
||||
if (criteria.length > 0)
|
||||
criteria = criteria[0].toUpperCase()
|
||||
} else
|
||||
throw new Error('Unexpected search option data type. '
|
||||
+ 'Expected string or array. Got: ' + typeof criteria)
|
||||
if (criteria === 'OR') {
|
||||
if (args.length !== 2)
|
||||
throw new Error('OR must have exactly two arguments')
|
||||
if (isOrChild)
|
||||
searchargs += 'OR ('
|
||||
else
|
||||
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)
|
||||
}
|
||||
switch (criteria) {
|
||||
// -- Standard criteria --
|
||||
case 'ALL':
|
||||
case 'ANSWERED':
|
||||
case 'DELETED':
|
||||
case 'DRAFT':
|
||||
case 'FLAGGED':
|
||||
case 'NEW':
|
||||
case 'SEEN':
|
||||
case 'RECENT':
|
||||
case 'OLD':
|
||||
case 'UNANSWERED':
|
||||
case 'UNDELETED':
|
||||
case 'UNDRAFT':
|
||||
case 'UNFLAGGED':
|
||||
case 'UNSEEN':
|
||||
searchargs += modifier + criteria
|
||||
break
|
||||
case 'BCC':
|
||||
case 'BODY':
|
||||
case 'CC':
|
||||
case 'FROM':
|
||||
case 'SUBJECT':
|
||||
case 'TEXT':
|
||||
case 'TO':
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ criteria)
|
||||
val = buildString(args[0])
|
||||
if (info && val[0] === '{')
|
||||
info.hasUTF8 = true
|
||||
searchargs += modifier + criteria + ' ' + val
|
||||
break
|
||||
case 'BEFORE':
|
||||
case 'ON':
|
||||
case 'SENTBEFORE':
|
||||
case 'SENTON':
|
||||
case 'SENTSINCE':
|
||||
case 'SINCE':
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ 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')
|
||||
}
|
||||
searchargs += modifier + criteria + ' ' + args[0].getDate() + '-'
|
||||
+ MONTHS[args[0].getMonth()] + '-'
|
||||
+ args[0].getFullYear()
|
||||
break
|
||||
case 'KEYWORD':
|
||||
case 'UNKEYWORD':
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ criteria)
|
||||
searchargs += modifier + criteria + ' ' + args[0]
|
||||
break
|
||||
case 'LARGER':
|
||||
case 'SMALLER':
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ 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
|
||||
case 'HEADER':
|
||||
if (!args || args.length !== 2)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ criteria)
|
||||
val = buildString(args[1])
|
||||
if (info && val[0] === '{')
|
||||
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)
|
||||
if (args.length === 0)
|
||||
throw new Error('Empty uid list')
|
||||
searchargs += modifier + criteria + ' ' + args.join(',')
|
||||
break
|
||||
// Extensions ==========================================================
|
||||
case 'X-GM-MSGID': // Gmail unique message ID
|
||||
case 'X-GM-THRID': // Gmail thread ID
|
||||
if (extensions.indexOf('X-GM-EXT-1') === -1)
|
||||
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)
|
||||
else {
|
||||
val = '' + args[0]
|
||||
if (!(RE_INTEGER.test(args[0])))
|
||||
throw new Error('Invalid value')
|
||||
}
|
||||
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)
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ criteria)
|
||||
val = buildString(args[0])
|
||||
if (info && val[0] === '{')
|
||||
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)
|
||||
if (!args || args.length !== 1)
|
||||
throw new Error('Incorrect number of arguments for search option: '
|
||||
+ criteria)
|
||||
searchargs += modifier + criteria + ' ' + args[0]
|
||||
break
|
||||
case 'MODSEQ':
|
||||
if (extensions.indexOf('CONDSTORE') === -1)
|
||||
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
|
||||
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])
|
||||
if (!validateUIDList(seqnos, true)) {
|
||||
if (seqnos.length === 0)
|
||||
throw new Error('Empty sequence number list')
|
||||
searchargs += modifier + seqnos.join(',')
|
||||
} else
|
||||
throw new Error('Unexpected search option: ' + criteria)
|
||||
}
|
||||
}
|
||||
if (isOrChild)
|
||||
break
|
||||
}
|
||||
return searchargs
|
||||
}
|
||||
|
||||
module.exports.buildSearchQuery = buildSearchQuery
|
||||
|
||||
// Pulled from assert.deepEqual:
|
||||
var pSlice = Array.prototype.slice
|
||||
function _deepEqual(actual, expected) {
|
||||
// 7.1. All identical values are equivalent, as determined by ===.
|
||||
if (actual === expected) {
|
||||
return true
|
||||
|
||||
} else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
|
||||
if (actual.length !== expected.length) return false
|
||||
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
if (actual[i] !== expected[i]) return false
|
||||
}
|
||||
|
||||
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.
|
||||
} else if (actual instanceof Date && expected instanceof Date) {
|
||||
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`).
|
||||
} 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports._deepEqual = _deepEqual
|
||||
|
||||
function isUndefinedOrNull(value) {
|
||||
return value === null || value === undefined
|
||||
}
|
||||
|
||||
module.exports.isUndefinedOrNull = isUndefinedOrNull
|
||||
|
||||
function isArguments(object) {
|
||||
return Object.prototype.toString.call(object) === '[object Arguments]'
|
||||
}
|
||||
|
||||
module.exports.isArguments = isArguments
|
||||
|
||||
function objEquiv(a, b) {
|
||||
var ka, kb, key, i
|
||||
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
|
||||
return false
|
||||
// an identical 'prototype' property.
|
||||
if (a.prototype !== b.prototype) return false
|
||||
//~~~I've managed to break Object.keys through screwy arguments passing.
|
||||
// Converting to array solves the problem.
|
||||
if (isArguments(a)) {
|
||||
if (!isArguments(b)) {
|
||||
return false
|
||||
}
|
||||
a = pSlice.call(a)
|
||||
b = pSlice.call(b)
|
||||
return _deepEqual(a, b)
|
||||
}
|
||||
try {
|
||||
ka = Object.keys(a)
|
||||
kb = Object.keys(b)
|
||||
} catch (e) {//happens when one is a string literal and the other isn't
|
||||
return false
|
||||
}
|
||||
// having the same number of owned properties (keys incorporates
|
||||
// hasOwnProperty)
|
||||
if (ka.length !== kb.length)
|
||||
return false
|
||||
//the same set of keys (although not necessarily the same order),
|
||||
ka.sort()
|
||||
kb.sort()
|
||||
//~~~cheap key test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
if (ka[i] != kb[i])
|
||||
return false
|
||||
}
|
||||
//equivalent values for every corresponding key, and
|
||||
//~~~possibly expensive deep test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
key = ka[i]
|
||||
if (!_deepEqual(a[key], b[key])) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports.objEquiv = objEquiv
|
||||
Reference in New Issue
Block a user