From b697dfe66d6cbfdded6ba6011effed05b1e17f88 Mon Sep 17 00:00:00 2001 From: Dominik Gehl Date: Fri, 4 Oct 2013 21:29:49 -0400 Subject: [PATCH 1/3] support for IMAP ID extension (RFC 2971) --- lib/Connection.js | 11 +++++++++++ lib/Parser.js | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Connection.js b/lib/Connection.js index 73a7e2e..afd5bf4 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -306,6 +306,15 @@ Connection.prototype.getBoxes = function(namespace, cb) { this._enqueue('LIST "' + namespace + '" "*"', cb); }; +Connection.prototype.id = function(identification, cb) { + if (!this.serverSupports('ID')) + throw new Error('Server does not support ID'); + cmd = ''; + for (var k in identification) + cmd += '"' + k + '" "' + identification[k] + '"'; + return this._enqueue('ID (' + cmd + ')', cb); +} + Connection.prototype.openBox = function(name, readOnly, cb) { if (this.state !== 'authenticated') throw new Error('Not authenticated'); @@ -1093,6 +1102,8 @@ Connection.prototype._resUntagged = function(info) { this._sock.end(); else if (type === 'namespace') this.namespaces = info.text; + else if (type === 'id') + this._curReq.cbargs.push(info.text); else if (type === 'capability') this._caps = info.text.map(function(v) { return v.toUpperCase(); }); else if (type === 'preauth') diff --git a/lib/Parser.js b/lib/Parser.js index 4e8c807..6658d12 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -14,7 +14,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|THREAD|ESEARCH|QUOTA|QUOTAROOT)|(\d+) (EXPUNGE|FETCH|RECENT|EXISTS))(?: (?:\[([^\]]+)\] )?(.+))?$/i, + RE_UNTAGGED = /^\* (?:(OK|NO|BAD|BYE|FLAGS|ID|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, @@ -249,6 +249,8 @@ Parser.prototype._resUntagged = function() { val = []; } else if (type === 'list' || type === 'lsub') val = parseBoxList(m[5], this._literals); + else if (type === 'id') + val = parseId(m[5], this._literals); else if (type === 'status') val = parseStatus(m[5], this._literals); else if (type === 'fetch') @@ -329,6 +331,17 @@ function parseESearch(text, literals) { return attrs; } +function parseId(text, literals) { + var r = parseExpr(text, literals), + id = {}; + + for (var i = 0, len = r[0].length; i < len; i += 2) { + id[r[0][i].toLowerCase()] = r[0][i + 1] + } + + return id; +} + function parseQuota(text, literals) { var r = parseExpr(text, literals), resources = {}; From 4ff1b82b9f954d39787f523aa8914d3bbcd32fa0 Mon Sep 17 00:00:00 2001 From: Dominik Gehl Date: Sat, 5 Oct 2013 06:59:07 -0400 Subject: [PATCH 2/3] ID support improvements --- lib/Connection.js | 23 ++++++++++++++++++----- lib/Parser.js | 6 +++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/Connection.js b/lib/Connection.js index afd5bf4..7bde6e7 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -309,11 +309,24 @@ Connection.prototype.getBoxes = function(namespace, cb) { Connection.prototype.id = function(identification, cb) { if (!this.serverSupports('ID')) throw new Error('Server does not support ID'); - cmd = ''; - for (var k in identification) - cmd += '"' + k + '" "' + identification[k] + '"'; - return this._enqueue('ID (' + cmd + ')', cb); -} + var cmd = 'ID'; + if (identification === null) + cmd += ' NIL'; + else { + if (Object.keys(identification).length > 30) + throw new Error('Max allowed number of keys is 30'); + cmd += ' ('; + for (var k in identification) { + if (k.length > 30) + throw new Error('Max allowed key length is 30'); + if (identification[k].length > 1024) + throw new Error('Max allowed value length is 1024'); + cmd += '"' + escape(k) + '" "' + escape(identification[k]) + '"'; + } + cmd += ')'; + } + this._enqueue(cmd, cb); +}; Connection.prototype.openBox = function(name, readOnly, cb) { if (this.state !== 'authenticated') diff --git a/lib/Parser.js b/lib/Parser.js index 6658d12..ea46c43 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -334,10 +334,10 @@ function parseESearch(text, literals) { function parseId(text, literals) { var r = parseExpr(text, literals), id = {}; - - for (var i = 0, len = r[0].length; i < len; i += 2) { + if (r[0] === null) + return null; + for (var i = 0, len = r[0].length; i < len; i += 2) id[r[0][i].toLowerCase()] = r[0][i + 1] - } return id; } From 93337c567f0d45999051c18f5ac03b8f9053bd46 Mon Sep 17 00:00:00 2001 From: Dominik Gehl Date: Sat, 5 Oct 2013 19:41:23 -0400 Subject: [PATCH 3/3] further improvements to ID extension support --- lib/Connection.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Connection.js b/lib/Connection.js index 7bde6e7..8a2e1a5 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -310,20 +310,21 @@ Connection.prototype.id = function(identification, cb) { if (!this.serverSupports('ID')) throw new Error('Server does not support ID'); var cmd = 'ID'; - if (identification === null) + if ((identification === null) || (Object.keys(identification).length === 0)) cmd += ' NIL'; else { if (Object.keys(identification).length > 30) throw new Error('Max allowed number of keys is 30'); - cmd += ' ('; + var kv = []; for (var k in identification) { - if (k.length > 30) + if (Buffer.byteLength(k) > 30) throw new Error('Max allowed key length is 30'); - if (identification[k].length > 1024) + if (Buffer.byteLength(identification[k]) > 1024) throw new Error('Max allowed value length is 1024'); - cmd += '"' + escape(k) + '" "' + escape(identification[k]) + '"'; + kv.push('"' + escape(k) + '"'); + kv.push('"' + escape(identification[k]) + '"'); } - cmd += ')'; + cmd += ' (' + kv.join(' ') + ')'; } this._enqueue(cmd, cb); };