From c77f652a2831694aaae92c0d223b2a478b8b6f29 Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 4 Dec 2011 19:13:45 -0500 Subject: [PATCH] Add namespaced sequence number-based functions and remove some unnecessary try-catch blocks --- README.md | 2 + imap.js | 113 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 867882b..7beaf5e 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,8 @@ ImapConnection Functions * **removeDeleted**(Function) - _(void)_ - Permanently removes (EXPUNGEs) all messages flagged as Deleted in the mailbox that is currently open. The Function parameter is the callback with one parameter: the error (null if none). **Note:** At least on Gmail, performing this operation with any currently open mailbox that is not the Spam or Trash mailbox will merely archive any messages marked as Deleted (by moving them to the 'All Mail' mailbox). +**All functions below have sequence number-based counterparts that can be accessed by using the 'seq' namespace of the imap connection's instance (e.g. conn.seq.search() returns sequence numbers instead of unique ids, conn.seq.fetch() fetches by sequence number(s) instead of unique ids, etc):** + * **search**(Array, Function) - _(void)_ - Searches the currently open mailbox for messages using specific criterion. The Function parameter is the callback with two parameters: the error (null if none) and an Array containing the message IDs matching the search criterion. The Array parameter is a list of Arrays containing the criterion (and any required arguments) to be used. Prefix the criteria name with an "!" to negate. For example, to search for unread messages since April 20, 2010 you could use: [ 'UNSEEN', ['SINCE', 'April 20, 2010'] ]. To search for messages that are EITHER unread OR are dated April 20, 2010 or later, you could use: [ ['OR', 'UNSEEN', ['SINCE', 'April 20, 2010'] ] ]. * The following message flags are valid criterion and do not require values: * 'ALL' - All messages. diff --git a/imap.js b/imap.js index cd7cbae..2915677 100644 --- a/imap.js +++ b/imap.js @@ -68,6 +68,9 @@ function ImapConnection (options) { util.inherits(ImapConnection, EventEmitter); exports.ImapConnection = ImapConnection; +/* Namespace for seqno-based commands */ +ImapConnection.prototype.seq = {}; + ImapConnection.prototype.connect = function(loginCb) { var self = this, fnInit = function() { @@ -599,16 +602,28 @@ ImapConnection.prototype.renameBox = function(oldname, newname, cb) { this._send('RENAME "' + escape(oldname) + '" "' + escape(newname) + '"', cb); }; +ImapConnection.prototype.seq.search = function(options, cb) { + this._search('', options, cb); +}; ImapConnection.prototype.search = function(options, cb) { + this._search('UID ', options, cb); +}; +ImapConnection.prototype._search = function(which, options, cb) { if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); if (!Array.isArray(options)) throw new Error('Expected array for search options'); - this._send('UID SEARCH' + this._send(which + 'SEARCH' + buildSearchQuery(options, this.capabilities), cb); }; +ImapConnection.prototype.seq.fetch = function(seqnos, options) { + return this._fetch('', seqnos, options); +}; ImapConnection.prototype.fetch = function(uids, options) { + return this._fetch('UID ', uids, options); +}; +ImapConnection.prototype._fetch = function(which, uids, options) { if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); @@ -618,11 +633,7 @@ ImapConnection.prototype.fetch = function(uids, options) { if (!Array.isArray(uids)) uids = [uids]; - try { - validateUIDList(uids); - } catch(e) { - throw e; - } + validateUIDList(uids); var opts = { markSeen: false, @@ -630,7 +641,7 @@ ImapConnection.prototype.fetch = function(uids, options) { struct: true, headers: true, // \_______ at most one of these can be used for any given // _______ fetch request - body: false // / + body: false // / } }, toFetch, bodyRange = '', self = this; if (typeof options !== 'object') @@ -679,7 +690,8 @@ ImapConnection.prototype.fetch = function(uids, options) { if (this.capabilities.indexOf('X-GM-EXT-1') > -1) extensions = 'X-GM-THRID X-GM-MSGID X-GM-LABELS '; - this._send('UID FETCH ' + uids.join(',') + ' (' + extensions + 'FLAGS INTERNALDATE' + this._send(which + 'FETCH ' + uids.join(',') + ' (' + extensions + + 'FLAGS INTERNALDATE' + (opts.request.struct ? ' BODYSTRUCTURE' : '') + (typeof toFetch === 'string' ? ' BODY' + (!opts.markSeen ? '.PEEK' : '') @@ -698,54 +710,64 @@ ImapConnection.prototype.fetch = function(uids, options) { return imapFetcher; }; +ImapConnection.prototype.seq.addFlags = function(seqnos, flags, cb) { + this._store('', seqnos, flags, true, cb); +}; ImapConnection.prototype.addFlags = function(uids, flags, cb) { - try { - this._store(uids, flags, true, cb); - } catch (err) { - throw err; - } + this._store('UID ', uids, flags, true, cb); }; +ImapConnection.prototype.seq.delFlags = function(seqnos, flags, cb) { + this._store('', seqnos, flags, false, cb); +}; ImapConnection.prototype.delFlags = function(uids, flags, cb) { - try { - this._store(uids, flags, false, cb); - } catch (err) { - throw err; - } + this._store('UID ', uids, flags, false, cb); }; +ImapConnection.prototype.seq.addKeywords = function(seqnos, flags, cb) { + return this._addKeywords('', seqnos, flags, cb); +}; ImapConnection.prototype.addKeywords = function(uids, flags, cb) { + return this._addKeywords('UID ', uids, flags, cb); +}; +ImapConnection.prototype._addKeywords = function(which, uids, flags, cb) { if (!this._state.box._newKeywords) throw new Error('This mailbox does not allow new keywords to be added'); - try { - this._store(uids, flags, true, cb); - } catch (err) { - throw err; - } + this._store(which, uids, flags, true, cb); }; +ImapConnection.prototype.seq.delKeywords = function(seqnos, flags, cb) { + this._store('', seqnos, flags, false, cb); +}; ImapConnection.prototype.delKeywords = function(uids, flags, cb) { - try { - this._store(uids, flags, false, cb); - } catch (err) { - throw err; - } + this._store('UID ', uids, flags, false, cb); }; +ImapConnection.prototype.seq.copy = function(seqnos, boxTo, cb) { + return this._copy('', seqnos, boxTo, cb); +}; ImapConnection.prototype.copy = function(uids, boxTo, cb) { + return this._copy('UID ', uids, boxTo, cb); +}; +ImapConnection.prototype._copy = function(which, uids, boxTo, cb) { if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); + if (!Array.isArray(uids)) uids = [uids]; - try { - validateUIDList(uids); - } catch(e) { - throw e; - } - this._send('UID COPY ' + uids.join(',') + ' "' + escape(boxTo) + '"', cb); + + validateUIDList(uids); + + this._send(which + 'COPY ' + uids.join(',') + ' "' + escape(boxTo) + '"', cb); }; +ImapConnection.prototype.seq.move = function(seqnos, boxTo, cb) { + return this._move('', seqnos, boxTo, cb); +}; ImapConnection.prototype.move = function(uids, boxTo, cb) { + return this._move('UID ', uids, boxTo, cb); +}; +ImapConnection.prototype._move = function(which, uids, boxTo, cb) { var self = this; if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); @@ -753,7 +775,8 @@ ImapConnection.prototype.move = function(uids, boxTo, cb) { throw new Error('Cannot move message: ' + 'server does not allow deletion of messages'); } else { - self.copy(uids, boxTo, function(err, reentryCount, deletedUIDs, counter) { + self._copy(which, uids, boxTo, function(err, reentryCount, deletedUIDs, + counter) { if (err) { cb(err); return; @@ -806,20 +829,18 @@ ImapConnection.prototype._fnTmrConn = function(loginCb) { this._state.conn.destroy(); } -ImapConnection.prototype._store = function(uids, flags, isAdding, cb) { +ImapConnection.prototype._store = function(which, uids, flags, isAdding, cb) { var isKeywords = (arguments.callee.caller === this.addKeywords || arguments.callee.caller === this.delKeywords); if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); if (typeof uids === 'undefined') throw new Error('The message ID(s) must be specified'); + if (!Array.isArray(uids)) uids = [uids]; - try { - validateUIDList(uids); - } catch(e) { - throw e; - } + validateUIDList(uids); + if ((!Array.isArray(flags) && typeof flags !== 'string') || (Array.isArray(flags) && flags.length === 0)) throw new Error((isKeywords ? 'Keywords' : 'Flags') @@ -846,7 +867,7 @@ ImapConnection.prototype._store = function(uids, flags, isAdding, cb) { flags = flags.join(' '); cb = arguments[arguments.length-1]; - this._send('UID STORE ' + uids.join(',') + ' ' + (isAdding ? '+' : '-') + this._send(which + 'STORE ' + uids.join(',') + ' ' + (isAdding ? '+' : '-') + 'FLAGS.SILENT (' + flags + ')', cb); }; @@ -1046,11 +1067,7 @@ function buildSearchQuery(options, extensions, isOrChild) { if (!args) throw new Error('Incorrect number of arguments for search option: ' + criteria); - try { - validateUIDList(args); - } catch(e) { - throw e; - } + validateUIDList(args); searchargs += modifier + criteria + ' ' + args.join(','); break; // -- Extensions criteria -- @@ -1107,7 +1124,7 @@ function validateUIDList(uids) { } intval = parseInt(''+uids[i]); if (isNaN(intval)) { - throw new Error('Message ID must be an integer, "*", or a range: ' + throw new Error('Message ID/number must be an integer, "*", or a range: ' + uids[i]); } else if (typeof uids[i] !== 'number') uids[i] = intval;