From 46efaa5e281a5c5e50734686c6cee81d4f516475 Mon Sep 17 00:00:00 2001 From: mscdex Date: Sat, 29 Jun 2013 01:13:02 -0400 Subject: [PATCH] add support for THREAD --- README.md | 31 ++++++++++++++++++------------- lib/Connection.js | 21 ++++++++++++++++++++- lib/Parser.js | 7 ++++++- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c8f51eb..cafa6ab 100644 --- a/README.md +++ b/README.md @@ -602,23 +602,29 @@ Extensions Supported * **delLabels**(< _mixed_ >source, < _mixed_ >labels, < _function_ >callback) - _(void)_ - Removes `labels` from message(s). `source` can be a message UID, a message UID range (e.g. '2504:2507' or '\*' or '2504:\*'), or an _array_ of message UIDs and/or message UID ranges. `labels` is either a single label or an _array_ of labels. `callback` has 1 parameter: < _Error_ >err. -* **Sort** +* **RFC5256** * Server capability: SORT - * Additional Connection instance methods (seqno-based counterpart exists): + * Additional Connection instance methods (seqno-based counterpart exists): + + * **sort**(< _array_ >sortCriteria, < _array_ >searchCriteria, < _function_ >callback) - _(void)_ - Performs a sorted search(). A seqno-based counterpart also exists for this function. `callback` has 2 parameters: < _Error_ >err, < _array_ >UIDs. Valid `sortCriteria` are (reverse sorting of individual criteria is done by prefixing the criteria with '-'): + + * 'ARRIVAL' - Internal date and time of the message. This differs from the ON criteria in search(), which uses just the internal date. + * 'CC' - The mailbox of the **first** "cc" address. + * 'DATE' - Message sent date and time. + * 'FROM' - The mailbox of the **first** "from" address. + * 'SIZE' - Size of the message in octets. + * 'SUBJECT' - Base subject text. + * 'TO' - The mailbox of the **first** "to" address. + + * Server capability: THREAD=REFERENCES, THREAD=ORDEREDSUBJECT - * **sort**(< _array_ >sortCriteria, < _array_ >searchCriteria, < _function_ >callback) - _(void)_ - Performs a sorted search(). A seqno-based counterpart also exists for this function. `callback` has 2 parameters: < _Error_ >err, < _array_ >UIDs. Valid `sortCriteria` are (reverse sorting of individual criteria is done by prefixing the criteria with '-'): + * Additional Connection instance methods (seqno-based counterpart exists): - * 'ARRIVAL' - Internal date and time of the message. This differs from the ON criteria in search(), which uses just the internal date. - * 'CC' - The mailbox of the **first** "cc" address. - * 'DATE' - Message sent date and time. - * 'FROM' - The mailbox of the **first** "from" address. - * 'SIZE' - Size of the message in octets. - * 'SUBJECT' - Base subject text. - * 'TO' - The mailbox of the **first** "to" address. + * **thread**(< _string_ >algorithm, < _array_ >searchCriteria, < _function_ >callback) - _(void)_ - Performs a regular search with `searchCriteria` and groups the resulting search results using the given `algorithm` (e.g. 'references', 'orderedsubject'). `callback` has 2 parameters: < _Error_ >err, < _array_ >UIDs. `UIDs` is a nested array. -* **Extended Search** +* **RFC4731** * Server capability: ESEARCH @@ -633,7 +639,7 @@ Extensions Supported **Note:** specifying no `options` or [] for `options` is the same as ['ALL'] -* **Quota** +* **RFC2087** * Server capability: QUOTA @@ -673,4 +679,3 @@ Several things not yet implemented in no particular order: * 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) - * THREAD (via THREAD=ORDEREDSUBJECT and/or THREAD=REFERENCES extension(s) -- http://tools.ietf.org/html/rfc5256) diff --git a/lib/Connection.js b/lib/Connection.js index e21ce8c..6e966db 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -782,6 +782,22 @@ Connection.prototype.getQuotaRoot = function(boxName, cb) { cb(err, quotas); }); }; + +Connection.prototype.thread = function(algorithm, criteria, cb) { + this._thread('UID ', algorithm, criteria, cb); +}; + +Connection.prototype._thread = function(which, algorithm, criteria, cb) { + algorithm = algorithm.toUpperCase(); + + if (!this.serverSupports('THREAD=' + algorithm)) + throw new Error('Server does not support that threading algorithm'); + + var cmd = which + 'THREAD ' + algorithm + ' UTF-8 ' + + buildSearchQuery(criteria, this._caps); + + this._enqueue(cmd, cb); +}; // END Extension methods ======================================================= // Namespace for seqno-based commands @@ -826,6 +842,9 @@ Connection.prototype.__defineGetter__('seq', function() { }, sort: function(sorts, options, cb) { self._sort('', sorts, options, cb); + }, + thread: function(algorithm, criteria, cb) { + self._thread('', algorithm, criteria, cb); } }; }); @@ -841,7 +860,7 @@ Connection.prototype._resUntagged = function(info) { this._caps = info.text.map(function(v) { return v.toUpperCase(); }); else if (type === 'preauth') this.state = 'authenticated'; - else if (type === 'search' || type === 'sort') + else if (type === 'search' || type === 'sort' || type === 'thread') this._curReq.cbargs.push(info.text); else if (type === 'quota') { var cbargs = this._curReq.cbargs; diff --git a/lib/Parser.js b/lib/Parser.js index 323dac6..da9fb25 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -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|ESEARCH|QUOTA|QUOTAROOT)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?: (?:\[([^\]]+)\] )?(.+))?$/i, + RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|LIST|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, @@ -211,6 +211,11 @@ Parser.prototype._resUntagged = function() { val = val.map(function(v) { return parseInt(v, 10); }); } else val = []; + } else if (type === 'thread') { + if (m[5]) + val = parseExpr(m[5], this._literals); + else + val = []; } else if (type === 'list' || type === 'lsub') val = parseBoxList(m[5], this._literals); else if (type === 'status')