diff --git a/lib/imap.js b/lib/imap.js index 0ad3893b..4bf4928f 100644 --- a/lib/imap.js +++ b/lib/imap.js @@ -21,7 +21,7 @@ var CRLF = '\r\n', RE_LITHEADER = /(?:((?:BODY\[.*\])?|[^ ]+) )?\{(\d+)\}$/i, RE_UNRESP = /^\* (OK|PREAUTH|NO|BAD) (?:\[(.+)\] )?(.+)$/i, RE_CMD = /^([A-Za-z]+)(?: |$)/i, - REX_UNRESPDATA = XRegExp('^\\* (?:(?:(?NAMESPACE) (?(?:NIL|\\((?:\\(.+\\))+\\))) (?(?:NIL|\\((?:\\(.+\\))+\\))) (?(?:NIL|\\((?:\\(.+\\))+\\))))|(?:(?FLAGS) \\((?.*)\\))|(?:(?LIST|LSUB|XLIST) \\((?.*)\\) (?".+"|NIL) (?.+))|(?:(?SEARCH)(?: (?.+))?)|(?:(?STATUS) (?.+) \\((?.*)\\))|(?:(?CAPABILITY) (?.+))|(?:(?BYE) (?:\\[(?.+)\\] )?(?.+)))$', 'i'), + REX_UNRESPDATA = XRegExp('^\\* (?:(?:(?NAMESPACE) (?(?:NIL|\\((?:\\(.+\\))+\\))) (?(?:NIL|\\((?:\\(.+\\))+\\))) (?(?:NIL|\\((?:\\(.+\\))+\\))))|(?:(?FLAGS) \\((?.*)\\))|(?:(?LIST|LSUB|XLIST) \\((?.*)\\) (?".+"|NIL) (?.+))|(?:(?(SEARCH|SORT))(?: (?.+))?)|(?:(?STATUS) (?.+) \\((?.*)\\))|(?:(?CAPABILITY) (?.+))|(?:(?BYE) (?:\\[(?.+)\\] )?(?.+)))$', 'i'), REX_UNRESPNUM = XRegExp('^\\* (?\\d+) (?:(?EXISTS)|(?RECENT)|(?EXPUNGE)|(?:(?FETCH) \\((?.*)\\)))$', 'i'); // extension constants @@ -397,6 +397,7 @@ ImapConnection.prototype.connect = function(loginCb) { } break; case 'SEARCH': + case 'SORT': // m.results = list of 0+ uid/seq numbers (undefined if none) requests[0].cbargs.push((m.results ? m.results.split(' ') : [])); break; @@ -568,6 +569,7 @@ ImapConnection.prototype.connect = function(loginCb) { // if we unexpectedly received no untagged responses. else if ((cmdstr.indexOf('UID FETCH') === 0 || cmdstr.indexOf('UID SEARCH') === 0 + || cmdstr.indexOf('UID SORT') === 0 ) && args.length === 0) args.unshift([]); } @@ -779,6 +781,51 @@ ImapConnection.prototype._search = function(which, options, cb) { + utils.buildSearchQuery(options, this.capabilities), cb); }; +ImapConnection.prototype.sort = function(sorts, options, cb) { + this._sort('UID ', sorts, options, cb); +}; + +ImapConnection.prototype._sort = function(which, sorts, options, cb) { + if (this._state.status !== STATES.BOXSELECTED) + throw new Error('No mailbox is currently selected'); + if (!Array.isArray(sorts) || !sorts.length) + throw new Error('Expected array with at least one sort criteria'); + if (!Array.isArray(options)) + throw new Error('Expected array for search options'); + if (!this._serverSupports('SORT')) + return cb(new Error('Sorting is not supported on the server')); + + var criteria = sorts.map(function(criterion) { + if (typeof criterion !== 'string') + throw new Error('Unexpected sort criterion data type. ' + + 'Expected string. Got: ' + typeof criteria); + + var modifier = ''; + if (criterion.charAt(0) === '!') { + modifier = 'REVERSE '; + criterion = criterion.substring(1); + } + criterion = criterion.toUpperCase(); + switch (criterion) { + case 'ARRIVAL': + case 'CC': + case 'DATE': + case 'FROM': + case 'SIZE': + case 'SUBJECT': + case 'TO': + break; + default: + throw new Error('Unexpected sort criteria: ' + criterion); + } + + return modifier + criterion; + }); + + this._send(which + 'SORT (' + criteria.join(' ') + ') UTF-8' + + utils.buildSearchQuery(options, this.capabilities), cb); +}; + ImapConnection.prototype.fetch = function(uids, options) { return this._fetch('UID ', uids, options); }; @@ -1017,6 +1064,9 @@ ImapConnection.prototype.__defineGetter__('seq', function() { }, search: function(options, cb) { self._search('', options, cb); + }, + sort: function(sorts, options, cb) { + self._sort('', sorts, options, cb); } }; });