add support for ESEARCH

fork
mscdex 12 years ago
parent f176494188
commit 008332036f

@ -669,6 +669,24 @@ Extensions Supported
* 'TO' - The mailbox of the **first** "to" address.
* **ESEARCH**
* Server capability: ESEARCH
* Additional Connection functions
* **esearch**(< _array_ >criteria, < _array_ >options, < _function_ >callback) - _(void)_ - A variant of search() that can return metadata about results. `callback` has 2 parameters: < _Error_ >err, < _object_ >info. `info` has possible keys: 'all', 'min', 'max', 'count'. Valid `options`:
* 'ALL' - Retrieves sequence numbers or UIDs in a compact form (e.g. [2, '10:11'] instead of search()'s [2, 10, 11]) that match the criteria.
* 'MIN' - Retrieves the lowest sequence number or UID that satisfies the criteria.
* 'MAX' - Retrieves the highest sequence number or UID that satisfies the criteria.
* 'COUNT' - Retrieves the number of messages that satisfy the criteria.
Note: specifying no `options` or [] is the same as ['ALL']
TODO
----
@ -676,7 +694,6 @@ TODO
Several things not yet implemented in no particular order:
* Support STARTTLS
* Support AUTH=CRAM-MD5/AUTH=CRAM_MD5 authentication
* Support additional IMAP commands/extensions:
* NOTIFY (via NOTIFY extension -- http://tools.ietf.org/html/rfc5465)
* STATUS addition to LIST (via LIST-STATUS extension -- http://tools.ietf.org/html/rfc5819)

@ -375,6 +375,30 @@ Connection.prototype._search = function(which, options, cb) {
this._enqueue(which + 'SEARCH' + buildSearchQuery(options, this._caps), cb);
};
Connection.prototype.esearch = function(criteria, options, cb) {
this._search('UID ', criteria, options, cb);
};
Connection.prototype._esearch = function(which, criteria, options, cb) {
if (this._box === undefined)
throw new Error('No mailbox is currently selected');
else if (!Array.isArray(criteria))
throw new Error('Expected array for search options');
var searchQuery = buildSearchQuery(criteria, this._caps);
if (typeof options === 'function') {
cb = options;
options = '';
} else if (!options)
options = '';
if (Array.isArray(options))
options = options.join(' ');
this._enqueue(which + 'SEARCH RETURN (' + options + ') ' + searchQuery, cb);
};
Connection.prototype.sort = function(sorts, options, cb) {
this._sort('UID ', sorts, options, cb);
};
@ -717,6 +741,9 @@ Connection.prototype.__defineGetter__('seq', function() {
search: function(options, cb) {
self._search('', options, cb);
},
esearch: function(criteria, options, cb) {
self._esearch('', criteria, options, cb);
},
sort: function(sorts, options, cb) {
self._sort('', sorts, options, cb);
}

@ -13,7 +13,7 @@ var CH_LF = 10,
RE_SEQNO = /^\* (\d+)/,
RE_LISTCONTENT = /^\((.*)\)$/,
RE_LITERAL = /\{(\d+)\}$/,
RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|LIST|LSUB|SEARCH|STATUS|CAPABILITY|NAMESPACE|PREAUTH|SORT)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?: (?:\[([^\]]+)\] )?(.+))?$/i,
RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|LIST|LSUB|SEARCH|STATUS|CAPABILITY|NAMESPACE|PREAUTH|SORT|ESEARCH)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?: (?:\[([^\]]+)\] )?(.+))?$/i,
RE_TAGGED = /^A(\d+) (OK|NO|BAD) (?:\[([^\]]+)\] )?(.+)$/i,
RE_CONTINUE = /^\+ (?:\[([^\]]+)\] )?(.+)$/i,
RE_CRLF = /\r\n/g,
@ -219,6 +219,8 @@ Parser.prototype._resUntagged = function() {
val = parseFetch(m[5], this._literals);
else if (type === 'namespace')
val = parseNamespaces(m[5], this._literals);
else if (type === 'esearch')
val = parseESearch(m[5], this._literals);
else
val = m[5];
@ -269,6 +271,25 @@ function parseTextCode(text, literals) {
return { key: r[0], val: r[1] };
}
function parseESearch(text, literals) {
var r = parseExpr(text.toUpperCase().replace('UID', ''), literals),
attrs = {};
// RFC4731 unfortunately is lacking on documentation, so we're going to
// assume that the response text always begins with (TAG "A123") and skip that
// part ...
for (var i = 1, len = r.length, key, val; i < len; i += 2) {
key = r[i].toLowerCase();
val = r[i + 1];
if (key === 'all')
val = val.split(',');
attrs[key] = val;
}
return attrs;
}
function parseBoxList(text, literals) {
var r = parseExpr(text, literals);
return {

@ -360,6 +360,34 @@ var CR = '\r', LF = '\n', CRLF = CR + LF;
],
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 }
}
],
what: 'ESearch UID, 2 items'
},
{ 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' ] }
}
],
what: 'ESearch ALL list'
},
].forEach(function(v) {
var ss = new require('stream').Readable(), p, result = [];
ss._read = function(){};

Loading…
Cancel
Save